@vellumai/assistant 0.6.5 → 0.7.0

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 (1520) hide show
  1. package/AGENTS.md +29 -1
  2. package/ARCHITECTURE.md +60 -53
  3. package/Dockerfile +25 -3
  4. package/README.md +8 -10
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +277 -0
  6. package/bun.lock +306 -119
  7. package/docs/architecture/integrations.md +32 -39
  8. package/docs/architecture/memory.md +26 -120
  9. package/docs/architecture/security.md +22 -36
  10. package/docs/browser-use-architecture-phase2.md +63 -20
  11. package/docs/credential-execution-service.md +7 -5
  12. package/docs/plugins.md +761 -0
  13. package/docs/skills.md +10 -10
  14. package/docs/stt-provider-onboarding.md +17 -45
  15. package/examples/plugins/echo/README.md +132 -0
  16. package/examples/plugins/echo/bun.lock +25 -0
  17. package/examples/plugins/echo/package.json +17 -0
  18. package/examples/plugins/echo/register.ts +187 -0
  19. package/knip.json +8 -22
  20. package/node_modules/@vellumai/ces-client/bun.lock +33 -0
  21. package/node_modules/@vellumai/ces-client/package.json +25 -0
  22. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +631 -0
  23. package/node_modules/@vellumai/ces-client/src/__tests__/package-boundary.test.ts +138 -0
  24. package/node_modules/@vellumai/ces-client/src/credential-rpc.ts +13 -0
  25. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +296 -0
  26. package/node_modules/@vellumai/ces-client/src/http-log-export.ts +111 -0
  27. package/node_modules/@vellumai/ces-client/src/index.ts +43 -0
  28. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +445 -0
  29. package/node_modules/@vellumai/credential-storage/src/__tests__/package-boundary.test.ts +32 -6
  30. package/node_modules/@vellumai/egress-proxy/src/__tests__/package-boundary.test.ts +32 -1
  31. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  32. package/node_modules/@vellumai/gateway-client/bun.lock +39 -0
  33. package/node_modules/@vellumai/gateway-client/package.json +23 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +343 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/package-boundary.test.ts +140 -0
  36. package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +422 -0
  37. package/node_modules/@vellumai/gateway-client/src/index.ts +35 -0
  38. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +331 -0
  39. package/node_modules/@vellumai/gateway-client/src/types.ts +131 -0
  40. package/node_modules/@vellumai/gateway-client/tsconfig.json +20 -0
  41. package/node_modules/@vellumai/{ces-contracts → service-contracts}/bun.lock +1 -1
  42. package/node_modules/@vellumai/{ces-contracts → service-contracts}/package.json +4 -2
  43. package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/contracts.test.ts +5 -1
  44. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +155 -0
  45. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +23 -0
  46. package/node_modules/@vellumai/service-contracts/src/index.ts +25 -0
  47. package/node_modules/@vellumai/{ces-contracts/src/index.ts → service-contracts/src/transport.ts} +6 -28
  48. package/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  49. package/node_modules/@vellumai/service-contracts/tsconfig.json +20 -0
  50. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +891 -0
  51. package/node_modules/@vellumai/skill-host-contracts/bun.lock +24 -0
  52. package/node_modules/@vellumai/skill-host-contracts/package.json +18 -0
  53. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +91 -0
  54. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +1348 -0
  55. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +6 -0
  56. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +11 -0
  57. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +32 -0
  58. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +333 -0
  59. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +444 -0
  60. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +20 -0
  61. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +12 -0
  62. package/openapi.yaml +3135 -692
  63. package/package.json +13 -7
  64. package/scripts/check-circular-deps.ts +80 -0
  65. package/scripts/generate-openapi.ts +24 -7
  66. package/{src/memory/graph/inspect.ts → scripts/memory-inspect.ts} +28 -28
  67. package/src/__tests__/access-request-decision.test.ts +2 -11
  68. package/src/__tests__/acp-session.test.ts +4 -150
  69. package/src/__tests__/actor-token-service.test.ts +17 -678
  70. package/src/__tests__/agent-loop-callsite-precedence.test.ts +2 -6
  71. package/src/__tests__/agent-loop-override-profile.test.ts +404 -0
  72. package/src/__tests__/agent-loop-thinking.test.ts +4 -4
  73. package/src/__tests__/agent-wake-override-profile.test.ts +261 -0
  74. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -1
  75. package/src/__tests__/anthropic-provider.test.ts +127 -15
  76. package/src/__tests__/app-compiler.test.ts +57 -0
  77. package/src/__tests__/app-routes-csp.test.ts +106 -55
  78. package/src/__tests__/approval-cascade.test.ts +10 -357
  79. package/src/__tests__/approval-conversation-turn.test.ts +3 -8
  80. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
  81. package/src/__tests__/approval-primitive.test.ts +2 -1
  82. package/src/__tests__/approval-routes-http.test.ts +34 -451
  83. package/src/__tests__/assistant-events-sse-hardening.test.ts +73 -80
  84. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -3
  85. package/src/__tests__/attachment-upload-trusted-source.test.ts +139 -0
  86. package/src/__tests__/attachments-store.test.ts +46 -1
  87. package/src/__tests__/audit-log-rotation.test.ts +2 -1
  88. package/src/__tests__/auto-analysis-end-to-end.test.ts +9 -20
  89. package/src/__tests__/avatar-generator.test.ts +4 -2
  90. package/src/__tests__/background-shell-bash.test.ts +227 -0
  91. package/src/__tests__/background-shell-host-bash.test.ts +474 -0
  92. package/src/__tests__/background-tool-registry.test.ts +145 -0
  93. package/src/__tests__/background-tool-routes.test.ts +175 -0
  94. package/src/__tests__/btw-routes.test.ts +147 -183
  95. package/src/__tests__/bundled-asset.test.ts +6 -6
  96. package/src/__tests__/call-controller.test.ts +15 -2
  97. package/src/__tests__/call-conversation-messages.test.ts +2 -1
  98. package/src/__tests__/call-domain.test.ts +2 -2
  99. package/src/__tests__/call-pointer-messages.test.ts +11 -13
  100. package/src/__tests__/call-recovery.test.ts +2 -1
  101. package/src/__tests__/call-routes-http.test.ts +3 -14
  102. package/src/__tests__/call-store.test.ts +2 -1
  103. package/src/__tests__/cancel-resolves-conversation-key.test.ts +31 -62
  104. package/src/__tests__/canonical-guardian-store.test.ts +2 -2
  105. package/src/__tests__/catalog-cache.test.ts +69 -0
  106. package/src/__tests__/catalog-files.test.ts +0 -26
  107. package/src/__tests__/ces-rpc-credential-backend.test.ts +1 -1
  108. package/src/__tests__/channel-approval-routes.test.ts +79 -49
  109. package/src/__tests__/channel-approval.test.ts +9 -7
  110. package/src/__tests__/channel-approvals.test.ts +9 -180
  111. package/src/__tests__/channel-delivery-store.test.ts +11 -10
  112. package/src/__tests__/channel-guardian.test.ts +14 -25
  113. package/src/__tests__/channel-readiness-service.test.ts +8 -6
  114. package/src/__tests__/channel-reply-delivery.test.ts +3 -19
  115. package/src/__tests__/channel-retry-sweep.test.ts +2 -5
  116. package/src/__tests__/checker.test.ts +447 -3806
  117. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  118. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +208 -0
  119. package/src/__tests__/cli.test.ts +1 -38
  120. package/src/__tests__/compaction-events.test.ts +500 -0
  121. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  122. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  123. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  124. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -7
  125. package/src/__tests__/config-model-image-provider.test.ts +109 -0
  126. package/src/__tests__/config-schema-cmd.test.ts +1 -1
  127. package/src/__tests__/config-schema.test.ts +25 -203
  128. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  129. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -25
  130. package/src/__tests__/contact-store-user-file.test.ts +2 -1
  131. package/src/__tests__/contacts-tools.test.ts +71 -18
  132. package/src/__tests__/contacts-write.test.ts +6 -61
  133. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  134. package/src/__tests__/context-search-agent-protocol.test.ts +230 -0
  135. package/src/__tests__/context-search-agent-runner.test.ts +998 -0
  136. package/src/__tests__/context-search-conversations-source.test.ts +320 -0
  137. package/src/__tests__/context-search-fanout.test.ts +380 -0
  138. package/src/__tests__/context-search-memory-source.test.ts +311 -0
  139. package/src/__tests__/context-search-pkb-source.test.ts +444 -0
  140. package/src/__tests__/context-search-types.test.ts +95 -0
  141. package/src/__tests__/context-search-workspace-source.test.ts +545 -0
  142. package/src/__tests__/context-window-manager.test.ts +380 -4
  143. package/src/__tests__/conversation-abort-tool-results.test.ts +14 -2
  144. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +631 -0
  145. package/src/__tests__/conversation-agent-loop-overflow.test.ts +41 -32
  146. package/src/__tests__/conversation-agent-loop.test.ts +54 -143
  147. package/src/__tests__/conversation-analysis-routes.test.ts +60 -82
  148. package/src/__tests__/conversation-attachments.test.ts +9 -20
  149. package/src/__tests__/conversation-attention-store.test.ts +2 -1
  150. package/src/__tests__/conversation-attention-telegram.test.ts +4 -2
  151. package/src/__tests__/conversation-clear-safety.test.ts +53 -95
  152. package/src/__tests__/conversation-confirmation-signals.test.ts +7 -40
  153. package/src/__tests__/conversation-crud-inference-profile.test.ts +54 -0
  154. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +63 -157
  155. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  156. package/src/__tests__/conversation-disk-view.test.ts +5 -4
  157. package/src/__tests__/conversation-fork-crud.test.ts +26 -55
  158. package/src/__tests__/conversation-fork-route.test.ts +5 -74
  159. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  160. package/src/__tests__/conversation-inference-profile-list.test.ts +128 -0
  161. package/src/__tests__/conversation-inference-profile-route.test.ts +216 -0
  162. package/src/__tests__/conversation-init.benchmark.test.ts +4 -95
  163. package/src/__tests__/conversation-key-store-disk-view.test.ts +2 -1
  164. package/src/__tests__/conversation-lifecycle.test.ts +0 -1
  165. package/src/__tests__/conversation-list-source.test.ts +2 -2
  166. package/src/__tests__/conversation-load-history-repair.test.ts +0 -1
  167. package/src/__tests__/conversation-pairing.test.ts +174 -11
  168. package/src/__tests__/conversation-pre-run-repair.test.ts +137 -294
  169. package/src/__tests__/conversation-process-callsite.test.ts +3 -1
  170. package/src/__tests__/conversation-provider-retry-repair.test.ts +22 -8
  171. package/src/__tests__/conversation-queue.test.ts +30 -47
  172. package/src/__tests__/conversation-routes-disk-view.test.ts +131 -103
  173. package/src/__tests__/conversation-routes-guardian-reply.test.ts +80 -55
  174. package/src/__tests__/conversation-routes-slash-commands.test.ts +83 -12
  175. package/src/__tests__/conversation-runtime-assembly.test.ts +196 -194
  176. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  177. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  178. package/src/__tests__/conversation-slash-commands.test.ts +6 -43
  179. package/src/__tests__/conversation-slash-queue.test.ts +7 -3
  180. package/src/__tests__/conversation-slash-unknown.test.ts +25 -3
  181. package/src/__tests__/conversation-speed-override.test.ts +6 -2
  182. package/src/__tests__/conversation-starter-routes.test.ts +177 -55
  183. package/src/__tests__/conversation-starters-cadence.test.ts +2 -2
  184. package/src/__tests__/conversation-store.test.ts +2 -375
  185. package/src/__tests__/conversation-title-service.test.ts +116 -0
  186. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +42 -3
  187. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +6 -6
  188. package/src/__tests__/conversation-unread-route.test.ts +1 -1
  189. package/src/__tests__/conversation-usage.test.ts +3 -2
  190. package/src/__tests__/conversation-wipe.test.ts +2 -103
  191. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -2
  192. package/src/__tests__/conversation-workspace-injection.test.ts +3 -1
  193. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -2
  194. package/src/__tests__/conversations-defer-cli.test.ts +150 -0
  195. package/src/__tests__/credential-execution-admin-cli.test.ts +1 -1
  196. package/src/__tests__/credential-execution-api-key-propagation.test.ts +2 -2
  197. package/src/__tests__/credential-execution-approval-bridge.test.ts +22 -289
  198. package/src/__tests__/credential-execution-client.test.ts +1 -1
  199. package/src/__tests__/credential-execution-managed-contract.test.ts +1 -1
  200. package/src/__tests__/credential-health-service.test.ts +78 -9
  201. package/src/__tests__/credential-security-invariants.test.ts +16 -2
  202. package/src/__tests__/credentials-cli.test.ts +45 -21
  203. package/src/__tests__/daemon-credential-client.test.ts +23 -108
  204. package/src/__tests__/db-acp-history.test.ts +284 -0
  205. package/src/__tests__/db-activation-state.test.ts +240 -0
  206. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +2 -1
  207. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +248 -0
  208. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +2 -1
  209. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +116 -0
  210. package/src/__tests__/db-rename-inference-profile-snake-case-migration.test.ts +132 -0
  211. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  212. package/src/__tests__/delete-propagation.test.ts +3 -2
  213. package/src/__tests__/deterministic-verification-control-plane.test.ts +39 -32
  214. package/src/__tests__/dm-backfill.test.ts +3 -2
  215. package/src/__tests__/edit-propagation.test.ts +5 -7
  216. package/src/__tests__/embedding-managed-proxy-selection.test.ts +1 -1
  217. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  218. package/src/__tests__/events-client-registration.test.ts +297 -0
  219. package/src/__tests__/file-write-tool.test.ts +2 -4
  220. package/src/__tests__/filing-service.test.ts +144 -17
  221. package/src/__tests__/first-greeting.test.ts +247 -5
  222. package/src/__tests__/followup-tools.test.ts +2 -1
  223. package/src/__tests__/gateway-client-managed-outbound.test.ts +8 -12
  224. package/src/__tests__/gateway-only-enforcement.test.ts +2 -6
  225. package/src/__tests__/gateway-only-guard.test.ts +4 -3
  226. package/src/__tests__/gemini-provider.test.ts +276 -10
  227. package/src/__tests__/graph-extraction-event-date.test.ts +30 -0
  228. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -1
  229. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -2
  230. package/src/__tests__/guardian-action-followup-store.test.ts +2 -1
  231. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +9 -9
  232. package/src/__tests__/guardian-action-late-reply.test.ts +2 -1
  233. package/src/__tests__/guardian-action-store.test.ts +2 -1
  234. package/src/__tests__/guardian-action-sweep.test.ts +9 -8
  235. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -1
  236. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +21 -118
  237. package/src/__tests__/guardian-dispatch.test.ts +14 -11
  238. package/src/__tests__/guardian-grant-minting.test.ts +9 -15
  239. package/src/__tests__/guardian-outbound-http.test.ts +71 -106
  240. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -2
  241. package/src/__tests__/guardian-routing-invariants.test.ts +34 -90
  242. package/src/__tests__/guardian-routing-state.test.ts +14 -22
  243. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -2
  244. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +253 -0
  245. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +8 -4
  246. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  247. package/src/__tests__/heartbeat-service.test.ts +39 -21
  248. package/src/__tests__/helpers/call-route-handler.ts +72 -0
  249. package/src/__tests__/helpers/channel-test-adapter.ts +161 -0
  250. package/src/__tests__/helpers/gateway-classify-mock.ts +67 -0
  251. package/src/__tests__/helpers/mock-logger.ts +36 -0
  252. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  253. package/src/__tests__/home-state-routes.test.ts +10 -31
  254. package/src/__tests__/host-browser-e2e-cloud.test.ts +309 -1
  255. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +12 -2
  256. package/src/__tests__/host-browser-routes.test.ts +36 -91
  257. package/src/__tests__/host-browser-ws-events-e2e.test.ts +10 -2
  258. package/src/__tests__/host-proxy-interface.test.ts +38 -4
  259. package/src/__tests__/host-shell-tool.test.ts +2 -4
  260. package/src/__tests__/host-transfer-pending-interactions.test.ts +160 -0
  261. package/src/__tests__/host-transfer-proxy.test.ts +733 -0
  262. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  263. package/src/__tests__/http-user-message-parity.test.ts +20 -11
  264. package/src/__tests__/image-credentials.test.ts +137 -0
  265. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  266. package/src/__tests__/inbound-invite-redemption.test.ts +3 -2
  267. package/src/__tests__/injector-chain.test.ts +525 -0
  268. package/src/__tests__/inline-skill-load-permissions.test.ts +41 -206
  269. package/src/__tests__/install-skill-routing.test.ts +1 -1
  270. package/src/__tests__/intent-routing.test.ts +0 -26
  271. package/src/__tests__/invite-redemption-service.test.ts +2 -1
  272. package/src/__tests__/invite-routes-http.test.ts +80 -12
  273. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -1
  274. package/src/__tests__/jobs-store-upsert-debounced.test.ts +2 -1
  275. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +157 -0
  276. package/src/__tests__/list-messages-attachments.test.ts +52 -55
  277. package/src/__tests__/list-messages-page-latest.test.ts +283 -0
  278. package/src/__tests__/list-messages-tool-merge.test.ts +16 -17
  279. package/src/__tests__/llm-call-pipeline.test.ts +284 -0
  280. package/src/__tests__/llm-context-normalization.test.ts +69 -4
  281. package/src/__tests__/llm-context-route-provider.test.ts +39 -113
  282. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -1
  283. package/src/__tests__/llm-resolver.test.ts +211 -0
  284. package/src/__tests__/llm-schema.test.ts +56 -0
  285. package/src/__tests__/llm-usage-store.test.ts +2 -1
  286. package/src/__tests__/log-export-workspace.test.ts +28 -17
  287. package/src/__tests__/mcp-abort-signal.test.ts +2 -3
  288. package/src/__tests__/mcp-client-auth.test.ts +2 -3
  289. package/src/__tests__/media-generate-image.test.ts +119 -13
  290. package/src/__tests__/memory-admin-recall.test.ts +221 -0
  291. package/src/__tests__/memory-recall-log-store.test.ts +2 -1
  292. package/src/__tests__/memory-retrieval-pipeline.test.ts +399 -0
  293. package/src/__tests__/memory-upsert-concurrency.test.ts +3 -1
  294. package/src/__tests__/migration-cross-version-compatibility.test.ts +14 -13
  295. package/src/__tests__/migration-export-http.test.ts +17 -17
  296. package/src/__tests__/migration-export-to-gcs.test.ts +491 -0
  297. package/src/__tests__/migration-import-commit-http.test.ts +16 -16
  298. package/src/__tests__/migration-import-from-gcs.test.ts +533 -0
  299. package/src/__tests__/migration-import-from-url.test.ts +21 -91
  300. package/src/__tests__/migration-import-preflight-http.test.ts +13 -13
  301. package/src/__tests__/migration-jobs-status.test.ts +164 -0
  302. package/src/__tests__/migration-validate-http.test.ts +48 -83
  303. package/src/__tests__/mock-gateway-ipc.ts +32 -62
  304. package/src/__tests__/model-intents.test.ts +16 -1
  305. package/src/__tests__/nl-approval-parser.test.ts +13 -17
  306. package/src/__tests__/non-member-access-request.test.ts +13 -5
  307. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  308. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  309. package/src/__tests__/notification-guardian-path.test.ts +15 -8
  310. package/src/__tests__/notification-schedule-notify-dedup.test.ts +109 -0
  311. package/src/__tests__/notification-telegram-adapter.test.ts +57 -55
  312. package/src/__tests__/oauth-apps-routes.test.ts +77 -123
  313. package/src/__tests__/oauth-cli.test.ts +28 -13
  314. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  315. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  316. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  317. package/src/__tests__/oauth-provider-visibility.test.ts +6 -6
  318. package/src/__tests__/oauth-providers-routes.test.ts +81 -103
  319. package/src/__tests__/oauth-store.test.ts +44 -77
  320. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -3
  321. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  322. package/src/__tests__/openai-image-service.test.ts +368 -0
  323. package/src/__tests__/openai-provider.test.ts +105 -6
  324. package/src/__tests__/openai-responses-provider.test.ts +146 -4
  325. package/src/__tests__/openrouter-provider-only.test.ts +22 -4
  326. package/src/__tests__/overflow-reduce-pipeline.test.ts +671 -0
  327. package/src/__tests__/permission-types.test.ts +3 -18
  328. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  329. package/src/__tests__/persistence-pipeline.test.ts +378 -0
  330. package/src/__tests__/pipeline-runner.test.ts +565 -0
  331. package/src/__tests__/platform-bash-auto-approve.test.ts +27 -20
  332. package/src/__tests__/platform.test.ts +10 -59
  333. package/src/__tests__/playbook-execution.test.ts +2 -1
  334. package/src/__tests__/playbook-tools.test.ts +2 -1
  335. package/src/__tests__/plugin-bootstrap.test.ts +529 -0
  336. package/src/__tests__/plugin-registry.test.ts +303 -0
  337. package/src/__tests__/plugin-route-contribution.test.ts +294 -0
  338. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  339. package/src/__tests__/plugin-tool-contribution.test.ts +292 -0
  340. package/src/__tests__/plugin-types.test.ts +320 -0
  341. package/src/__tests__/pricing.test.ts +195 -14
  342. package/src/__tests__/profiler-routes.test.ts +112 -177
  343. package/src/__tests__/provider-send-message-override-profile.test.ts +223 -0
  344. package/src/__tests__/proxy-approval-callback.test.ts +6 -493
  345. package/src/__tests__/qdrant-collection-migration.test.ts +7 -7
  346. package/src/__tests__/reaction-persistence.test.ts +4 -2
  347. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  348. package/src/__tests__/recording-handler.test.ts +0 -2
  349. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  350. package/src/__tests__/registry.test.ts +1 -2
  351. package/src/__tests__/relay-server.test.ts +19 -4
  352. package/src/__tests__/require-fresh-approval.test.ts +19 -168
  353. package/src/__tests__/resolve-trust-class.test.ts +2 -1
  354. package/src/__tests__/retry-thinking-tool-choice.test.ts +19 -7
  355. package/src/__tests__/retry-verbosity-normalization.test.ts +139 -0
  356. package/src/__tests__/runtime-attachment-metadata.test.ts +26 -6
  357. package/src/__tests__/runtime-events-sse-parity.test.ts +12 -13
  358. package/src/__tests__/runtime-events-sse.test.ts +13 -21
  359. package/src/__tests__/schedule-routes.test.ts +304 -77
  360. package/src/__tests__/schedule-store.test.ts +119 -1
  361. package/src/__tests__/schedule-tools.test.ts +2 -1
  362. package/src/__tests__/scheduler-recurrence.test.ts +16 -71
  363. package/src/__tests__/scheduler-reuse-conversation.test.ts +12 -51
  364. package/src/__tests__/scheduler-wake.test.ts +356 -0
  365. package/src/__tests__/scoped-approval-grants.test.ts +2 -1
  366. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -1
  367. package/src/__tests__/secret-detection-handler.test.ts +2 -19
  368. package/src/__tests__/secret-ingress-http.test.ts +38 -21
  369. package/src/__tests__/secret-routes-managed-proxy.test.ts +46 -102
  370. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  371. package/src/__tests__/send-endpoint-busy.test.ts +38 -25
  372. package/src/__tests__/sequence-store.test.ts +2 -1
  373. package/src/__tests__/server-history-render.test.ts +2 -2
  374. package/src/__tests__/service-contracts-import-guard.test.ts +185 -0
  375. package/src/__tests__/set-permission-mode.test.ts +0 -10
  376. package/src/__tests__/settings-routes.test.ts +35 -68
  377. package/src/__tests__/skill-boundary-guard.test.ts +105 -0
  378. package/src/__tests__/skill-load-inline-command.test.ts +2 -2
  379. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  380. package/src/__tests__/skill-runtime-path.test.ts +64 -0
  381. package/src/__tests__/skills-file-content-endpoint.test.ts +0 -2
  382. package/src/__tests__/slack-inbound-verification.test.ts +11 -2
  383. package/src/__tests__/slack-messaging-token-resolution.test.ts +1 -3
  384. package/src/__tests__/slack-reaction-approvals.test.ts +4 -4
  385. package/src/__tests__/slack-share-routes.test.ts +37 -72
  386. package/src/__tests__/subagent-call-site-routing.test.ts +79 -0
  387. package/src/__tests__/subagent-fork-spawn.test.ts +20 -28
  388. package/src/__tests__/subagent-notify-parent.test.ts +6 -29
  389. package/src/__tests__/subagent-role-registry.test.ts +3 -3
  390. package/src/__tests__/subagent-spawn-tool-fork.test.ts +52 -104
  391. package/src/__tests__/subagent-tools.test.ts +0 -1
  392. package/src/__tests__/suggestion-routes.test.ts +149 -57
  393. package/src/__tests__/task-compiler.test.ts +2 -1
  394. package/src/__tests__/task-management-tools.test.ts +2 -1
  395. package/src/__tests__/task-memory-cleanup.test.ts +3 -1
  396. package/src/__tests__/task-scheduler.test.ts +5 -16
  397. package/src/__tests__/telegram-config.test.ts +0 -1
  398. package/src/__tests__/terminal-tools.test.ts +5 -314
  399. package/src/__tests__/thread-backfill.test.ts +3 -2
  400. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  401. package/src/__tests__/token-estimate-pipeline.test.ts +484 -0
  402. package/src/__tests__/tool-approval-handler.test.ts +21 -63
  403. package/src/__tests__/tool-audit-listener.test.ts +3 -3
  404. package/src/__tests__/tool-domain-event-publisher.test.ts +3 -3
  405. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  406. package/src/__tests__/tool-execute-pipeline.test.ts +429 -0
  407. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +61 -4
  408. package/src/__tests__/tool-executor-lifecycle-events.test.ts +28 -56
  409. package/src/__tests__/tool-executor.test.ts +434 -1604
  410. package/src/__tests__/tool-grant-request-escalation.test.ts +90 -311
  411. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  412. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  413. package/src/__tests__/trust-context-guards.test.ts +1 -1
  414. package/src/__tests__/trusted-contact-approval-notifier.test.ts +7 -15
  415. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +178 -354
  416. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  417. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  418. package/src/__tests__/trusted-contact-verification.test.ts +2 -1
  419. package/src/__tests__/turn-boundary-resolution.test.ts +2 -1
  420. package/src/__tests__/twilio-routes.test.ts +25 -66
  421. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -7
  422. package/src/__tests__/usage-routes.test.ts +73 -90
  423. package/src/__tests__/user-plugin-loader.test.ts +233 -0
  424. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -2
  425. package/src/__tests__/verification-control-plane-policy.test.ts +95 -14
  426. package/src/__tests__/voice-ingress-preflight.test.ts +5 -5
  427. package/src/__tests__/voice-invite-redemption.test.ts +2 -1
  428. package/src/__tests__/voice-scoped-grant-consumer.test.ts +3 -3
  429. package/src/__tests__/voice-session-bridge.test.ts +285 -106
  430. package/src/__tests__/volume-security-guard.test.ts +0 -2
  431. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -1
  432. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +3 -1
  433. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +2 -1
  434. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +1 -1
  435. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  436. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  437. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  438. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  439. package/src/__tests__/workspace-migration-052-seed-default-inference-profiles.test.ts +260 -0
  440. package/src/__tests__/workspace-migration-053-release-notes-acp-codex.test.ts +225 -0
  441. package/src/__tests__/workspace-migration-054-seed-recall-callsite.test.ts +235 -0
  442. package/src/__tests__/workspace-migration-055-release-notes-agentic-recall.test.ts +128 -0
  443. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +232 -0
  444. package/src/__tests__/workspace-migration-acp-sessions-ui.test.ts +144 -0
  445. package/src/__tests__/workspace-migration-drop-user-md.test.ts +1 -1
  446. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +274 -0
  447. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  448. package/src/__tests__/workspace-policy.test.ts +21 -3
  449. package/src/acp/__tests__/client-handler.test.ts +64 -0
  450. package/src/acp/__tests__/helpers/acp-config-stub.ts +62 -0
  451. package/src/acp/__tests__/helpers/which-stub.ts +45 -0
  452. package/src/acp/__tests__/session-manager-persistence.test.ts +366 -0
  453. package/src/acp/__tests__/session-manager-startup.test.ts +159 -0
  454. package/src/acp/__tests__/session-manager.test.ts +83 -0
  455. package/src/acp/client-handler.ts +23 -139
  456. package/src/acp/resolve-agent.test.ts +291 -0
  457. package/src/acp/resolve-agent.ts +176 -0
  458. package/src/acp/session-manager.ts +166 -7
  459. package/src/acp/types.ts +2 -50
  460. package/src/agent/loop.ts +365 -104
  461. package/src/agent/message-types.ts +0 -2
  462. package/src/approvals/AGENTS.md +1 -1
  463. package/src/approvals/__tests__/guardian-feed-event.test.ts +296 -0
  464. package/src/approvals/approval-primitive.ts +3 -20
  465. package/src/approvals/guardian-decision-primitive.ts +37 -68
  466. package/src/approvals/guardian-request-resolvers.ts +109 -103
  467. package/src/avatar/character-components.ts +6 -6
  468. package/src/{config/bundled-skills/settings/tools → avatar}/identity-avatar.ts +1 -1
  469. package/src/backup/__tests__/backup-worker.test.ts +2 -15
  470. package/src/backup/__tests__/paths.test.ts +3 -2
  471. package/src/backup/backup-worker.ts +3 -24
  472. package/src/backup/paths.ts +2 -18
  473. package/src/backup/restore.ts +7 -11
  474. package/src/browser/__tests__/operations.test.ts +0 -35
  475. package/src/browser/operations.ts +1 -47
  476. package/src/bundler/app-compiler.ts +84 -1
  477. package/src/bundler/package-resolver.ts +2 -6
  478. package/src/calls/active-call-lease.ts +1 -1
  479. package/src/calls/call-constants.ts +1 -1
  480. package/src/calls/call-controller.ts +1 -5
  481. package/src/calls/call-domain.ts +14 -14
  482. package/src/calls/call-pointer-messages.ts +4 -9
  483. package/src/calls/call-state.ts +2 -2
  484. package/src/calls/call-store.ts +2 -1
  485. package/src/calls/guardian-action-sweep.ts +9 -25
  486. package/src/calls/guardian-dispatch.ts +1 -20
  487. package/src/calls/media-stream-audio-transcode.ts +2 -41
  488. package/src/calls/media-stream-server.ts +2 -3
  489. package/src/calls/media-stream-stt-session.ts +1 -3
  490. package/src/calls/relay-access-wait.ts +5 -8
  491. package/src/calls/relay-server.ts +15 -18
  492. package/src/calls/relay-setup-router.ts +2 -2
  493. package/src/calls/relay-verification.ts +4 -4
  494. package/src/calls/twilio-rest.ts +1 -1
  495. package/src/calls/twilio-routes.ts +160 -78
  496. package/src/calls/voice-control-protocol.ts +10 -10
  497. package/src/calls/voice-ingress-preflight.ts +2 -2
  498. package/src/calls/voice-session-bridge.ts +137 -42
  499. package/src/channels/__tests__/types.test.ts +28 -6
  500. package/src/channels/permission-profiles.ts +2 -72
  501. package/src/channels/types.ts +48 -30
  502. package/src/cli/AGENTS.md +1 -0
  503. package/src/cli/__tests__/notifications.test.ts +92 -214
  504. package/src/cli/commands/__tests__/attachment.test.ts +14 -8
  505. package/src/cli/commands/__tests__/backup.test.ts +4 -15
  506. package/src/cli/commands/__tests__/browser.test.ts +36 -31
  507. package/src/cli/commands/__tests__/cache.test.ts +23 -18
  508. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  509. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  510. package/src/cli/commands/__tests__/memory-v2.test.ts +396 -0
  511. package/src/cli/commands/__tests__/task.test.ts +36 -35
  512. package/src/cli/commands/__tests__/trust.test.ts +602 -0
  513. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  514. package/src/cli/commands/__tests__/ui-confirm.test.ts +14 -14
  515. package/src/cli/commands/__tests__/ui.test.ts +17 -17
  516. package/src/cli/commands/__tests__/watchers.test.ts +29 -29
  517. package/src/cli/commands/__tests__/webhooks.test.ts +544 -0
  518. package/src/cli/commands/attachment.ts +12 -8
  519. package/src/cli/commands/auth.ts +1 -1
  520. package/src/cli/commands/avatar.ts +192 -9
  521. package/src/cli/commands/backup.ts +16 -46
  522. package/src/cli/commands/browser.ts +52 -4
  523. package/src/cli/commands/cache.ts +7 -5
  524. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  525. package/src/cli/commands/clients.ts +137 -0
  526. package/src/cli/commands/completions.ts +3 -10
  527. package/src/cli/commands/contacts.ts +10 -10
  528. package/src/cli/commands/conversations-defer.ts +364 -0
  529. package/src/cli/commands/conversations-import.ts +2 -3
  530. package/src/cli/commands/conversations.ts +115 -57
  531. package/src/cli/commands/credential-execution.ts +1 -1
  532. package/src/cli/commands/credentials.ts +139 -5
  533. package/src/cli/commands/default-action.ts +1 -1
  534. package/src/cli/commands/domain.ts +2 -2
  535. package/src/cli/commands/email.ts +7 -7
  536. package/src/cli/commands/image-generation.ts +33 -34
  537. package/src/cli/commands/keys.ts +2 -2
  538. package/src/cli/commands/mcp.ts +1 -1
  539. package/src/cli/commands/memory-v2.ts +343 -0
  540. package/src/cli/commands/memory.ts +8 -8
  541. package/src/cli/commands/notifications.ts +87 -121
  542. package/src/cli/commands/oauth/__tests__/connect.test.ts +23 -5
  543. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +1 -1
  544. package/src/cli/commands/oauth/__tests__/mode.test.ts +1 -1
  545. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  546. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  547. package/src/cli/commands/oauth/__tests__/status.test.ts +1 -1
  548. package/src/cli/commands/oauth/__tests__/token.test.ts +1 -1
  549. package/src/cli/commands/oauth/connect.ts +4 -4
  550. package/src/cli/commands/oauth/providers.ts +176 -8
  551. package/src/cli/commands/oauth/shared.ts +29 -2
  552. package/src/cli/commands/oauth/status.ts +46 -36
  553. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -6
  554. package/src/cli/commands/platform/__tests__/connect.test.ts +23 -11
  555. package/src/cli/commands/platform/__tests__/disconnect.test.ts +22 -10
  556. package/src/cli/commands/platform/__tests__/status.test.ts +22 -10
  557. package/src/cli/commands/platform/connect.ts +3 -3
  558. package/src/cli/commands/platform/disconnect.ts +4 -6
  559. package/src/cli/commands/platform/index.ts +12 -10
  560. package/src/cli/commands/routes.ts +7 -1
  561. package/src/cli/commands/sequence.ts +7 -7
  562. package/src/cli/commands/skills.ts +189 -84
  563. package/src/cli/commands/task.ts +12 -10
  564. package/src/cli/commands/trust.ts +460 -162
  565. package/src/cli/commands/ui.ts +3 -3
  566. package/src/cli/commands/usage.ts +10 -5
  567. package/src/cli/commands/watchers.ts +8 -8
  568. package/src/cli/commands/webhooks.ts +270 -0
  569. package/src/cli/lib/daemon-avatar-client.ts +37 -0
  570. package/src/cli/lib/daemon-credential-client.ts +27 -189
  571. package/src/cli/lib/ipc-params.ts +22 -0
  572. package/src/cli/program.ts +29 -29
  573. package/src/cli.ts +1 -61
  574. package/src/config/__tests__/backup-schema.test.ts +7 -2
  575. package/src/config/acp-defaults.test.ts +57 -0
  576. package/src/config/acp-defaults.ts +40 -0
  577. package/src/config/acp-schema.ts +1 -1
  578. package/src/config/assistant-feature-flags.ts +18 -142
  579. package/src/config/bundled-skills/acp/SKILL.md +44 -16
  580. package/src/config/bundled-skills/acp/TOOLS.json +45 -1
  581. package/src/config/bundled-skills/{screen-watch/tools/start-screen-watch.ts → acp/tools/acp-list-agents.ts} +2 -2
  582. package/src/config/bundled-skills/acp/tools/acp-steer.ts +12 -0
  583. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  584. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  585. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  586. package/src/config/bundled-skills/contacts/tools/contact-search.ts +25 -51
  587. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +31 -44
  588. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  589. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  590. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  591. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +6 -6
  592. package/src/config/bundled-skills/media-processing/services/reduce.ts +0 -13
  593. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  594. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  595. package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +1 -1
  596. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  597. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  598. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  599. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -1
  600. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +1 -1
  601. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -1
  602. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  603. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  604. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  605. package/src/config/bundled-skills/settings/SKILL.md +2 -17
  606. package/src/config/bundled-skills/settings/TOOLS.json +0 -56
  607. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  608. package/src/config/bundled-tool-registry.ts +4 -21
  609. package/src/config/env.ts +7 -8
  610. package/src/config/feature-flag-registry.json +25 -17
  611. package/src/config/llm-resolver.ts +51 -33
  612. package/src/config/loader.ts +12 -15
  613. package/src/config/schema.ts +22 -70
  614. package/src/config/schemas/__tests__/filing.test.ts +58 -0
  615. package/src/config/schemas/__tests__/memory-v2.test.ts +186 -0
  616. package/src/config/schemas/backup.ts +1 -1
  617. package/src/config/schemas/conversations.ts +16 -0
  618. package/src/config/schemas/filing.ts +12 -0
  619. package/src/config/schemas/host-browser.ts +2 -2
  620. package/src/config/schemas/inference.ts +0 -2
  621. package/src/config/schemas/ingress.ts +1 -1
  622. package/src/config/schemas/llm.ts +51 -10
  623. package/src/config/schemas/memory-storage.ts +1 -1
  624. package/src/config/schemas/memory-v2.ts +176 -0
  625. package/src/config/schemas/memory.ts +2 -0
  626. package/src/config/schemas/security.ts +0 -60
  627. package/src/config/schemas/services.ts +46 -7
  628. package/src/config/schemas/tts.ts +11 -0
  629. package/src/config/skill-state.ts +6 -2
  630. package/src/config/skills.ts +95 -6
  631. package/src/config/types.ts +0 -41
  632. package/src/contacts/contact-store.ts +2 -2
  633. package/src/contacts/contacts-write.ts +0 -38
  634. package/src/contacts/types.ts +8 -10
  635. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  636. package/src/context/prompts/compact.md +26 -12
  637. package/src/context/token-estimator.ts +1 -1
  638. package/src/context/tool-result-truncation.ts +4 -64
  639. package/src/context/window-manager.ts +191 -17
  640. package/src/credential-execution/approval-bridge.ts +7 -69
  641. package/src/credential-execution/client.ts +17 -422
  642. package/src/credential-execution/feature-gates.ts +1 -2
  643. package/src/credential-execution/managed-catalog.ts +1 -1
  644. package/src/credential-health/credential-health-service.ts +20 -7
  645. package/src/daemon/__tests__/conversation-feed-event.test.ts +304 -0
  646. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  647. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  648. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  649. package/src/daemon/__tests__/daemon-skill-host.test.ts +272 -0
  650. package/src/daemon/__tests__/meet-host-supervisor.test.ts +587 -0
  651. package/src/daemon/__tests__/meet-manifest-loader.test.ts +463 -0
  652. package/src/daemon/approval-generators.ts +2 -14
  653. package/src/daemon/classifier.ts +0 -106
  654. package/src/daemon/config-watcher.ts +14 -56
  655. package/src/daemon/connection-policy.ts +0 -14
  656. package/src/daemon/context-overflow-policy.ts +4 -13
  657. package/src/daemon/conversation-agent-loop-handlers.ts +120 -28
  658. package/src/daemon/conversation-agent-loop.ts +1113 -701
  659. package/src/daemon/conversation-attachments.ts +5 -81
  660. package/src/daemon/conversation-error.ts +9 -5
  661. package/src/daemon/conversation-history.ts +11 -20
  662. package/src/daemon/conversation-launch.ts +1 -1
  663. package/src/daemon/conversation-lifecycle.ts +37 -19
  664. package/src/daemon/conversation-messaging.ts +1 -1
  665. package/src/daemon/conversation-notifiers.ts +3 -111
  666. package/src/daemon/conversation-process.ts +23 -20
  667. package/src/daemon/conversation-runtime-assembly.ts +530 -471
  668. package/src/daemon/conversation-slash.ts +4 -160
  669. package/src/daemon/conversation-store.ts +368 -0
  670. package/src/daemon/conversation-surfaces.ts +5 -4
  671. package/src/daemon/conversation-tool-setup.ts +49 -161
  672. package/src/daemon/conversation.ts +126 -217
  673. package/src/daemon/daemon-control.ts +3 -3
  674. package/src/daemon/daemon-skill-host.ts +262 -0
  675. package/src/daemon/external-plugins-bootstrap.ts +532 -0
  676. package/src/daemon/first-greeting.ts +191 -14
  677. package/src/daemon/handlers/config-channels.ts +2 -2
  678. package/src/daemon/handlers/config-embeddings.ts +1 -1
  679. package/src/daemon/handlers/config-ingress.ts +24 -2
  680. package/src/daemon/handlers/config-model.test.ts +17 -0
  681. package/src/daemon/handlers/config-model.ts +18 -52
  682. package/src/daemon/handlers/config-telegram.ts +6 -53
  683. package/src/daemon/handlers/config-voice.ts +1 -1
  684. package/src/daemon/handlers/conversations.ts +22 -156
  685. package/src/daemon/handlers/recording.ts +1 -1
  686. package/src/daemon/handlers/shared.ts +34 -35
  687. package/src/daemon/handlers/skills.ts +20 -24
  688. package/src/daemon/host-transfer-proxy.ts +500 -0
  689. package/src/daemon/lifecycle.ts +56 -326
  690. package/src/daemon/meet-host-startup.ts +51 -0
  691. package/src/daemon/meet-host-supervisor.ts +781 -0
  692. package/src/daemon/meet-manifest-loader.ts +410 -0
  693. package/src/daemon/memory-v2-startup.ts +35 -0
  694. package/src/daemon/message-protocol.ts +4 -7
  695. package/src/daemon/message-types/acp.ts +1 -0
  696. package/src/daemon/message-types/computer-use.ts +2 -34
  697. package/src/daemon/message-types/conversations.ts +65 -2
  698. package/src/daemon/message-types/host-transfer.ts +41 -0
  699. package/src/daemon/message-types/integrations.ts +6 -0
  700. package/src/daemon/message-types/messages.ts +26 -14
  701. package/src/daemon/message-types/schedules.ts +1 -0
  702. package/src/daemon/message-types/settings.ts +0 -6
  703. package/src/daemon/message-types/shared.ts +5 -2
  704. package/src/daemon/message-types/subagents.ts +2 -1
  705. package/src/daemon/message-types/workspace.ts +0 -2
  706. package/src/daemon/pkb-reminder-builder.test.ts +13 -12
  707. package/src/daemon/pkb-reminder-builder.ts +8 -16
  708. package/src/daemon/process-message.ts +616 -0
  709. package/src/daemon/providers-setup.ts +14 -6
  710. package/src/daemon/server.ts +79 -1272
  711. package/src/daemon/shutdown-handlers.ts +3 -13
  712. package/src/daemon/startup-error.ts +1 -1
  713. package/src/daemon/tool-side-effects.ts +14 -56
  714. package/src/daemon/trust-context.ts +32 -0
  715. package/src/daemon/wake-target-adapter.ts +223 -0
  716. package/src/email/feature-gate.ts +1 -1
  717. package/src/events/domain-events.ts +1 -8
  718. package/src/events/tool-audit-listener.ts +2 -8
  719. package/src/events/tool-metrics-listener.ts +1 -4
  720. package/src/filing/filing-service.ts +194 -54
  721. package/src/followups/followup-store.ts +3 -71
  722. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +228 -0
  723. package/src/heartbeat/heartbeat-service.ts +52 -8
  724. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  725. package/src/home/__tests__/phase5-exit-criteria.test.ts +18 -1
  726. package/src/home/__tests__/rollup-producer.test.ts +67 -2
  727. package/src/home/assistant-feed-authoring.ts +8 -1
  728. package/src/home/emit-feed-event.ts +7 -0
  729. package/src/home/feed-types.ts +42 -3
  730. package/src/home/relationship-state-writer.ts +1 -1
  731. package/src/home/rewrite-command-preview.ts +66 -0
  732. package/src/home/rewrite-feed-title.ts +58 -0
  733. package/src/home/rollup-producer.ts +16 -3
  734. package/src/inbound/platform-callback-registration.ts +1 -17
  735. package/src/ipc/__tests__/attachment-ipc.test.ts +128 -66
  736. package/src/ipc/__tests__/browser-ipc.test.ts +75 -51
  737. package/src/ipc/__tests__/cache-ipc.test.ts +52 -107
  738. package/src/ipc/__tests__/cli-ipc.test.ts +9 -6
  739. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +254 -0
  740. package/src/ipc/__tests__/skill-server.test.ts +182 -0
  741. package/src/ipc/__tests__/socket-path.test.ts +44 -37
  742. package/src/ipc/__tests__/ui-request-route.test.ts +241 -216
  743. package/src/ipc/__tests__/watcher-ipc.test.ts +33 -33
  744. package/src/ipc/assistant-server.ts +450 -0
  745. package/src/ipc/cli-client.ts +3 -3
  746. package/src/ipc/gateway-client.test.ts +131 -0
  747. package/src/ipc/gateway-client.ts +98 -120
  748. package/src/ipc/ipc-framing.ts +281 -0
  749. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +152 -0
  750. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +219 -0
  751. package/src/ipc/routes/db-proxy.ts +73 -0
  752. package/src/ipc/routes/route-adapter.ts +32 -0
  753. package/src/ipc/routes/trust-rules.test.ts +218 -0
  754. package/src/ipc/skill-ipc-types.ts +13 -0
  755. package/src/ipc/skill-routes/__tests__/config.test.ts +146 -0
  756. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +402 -0
  757. package/src/ipc/skill-routes/__tests__/identity.test.ts +81 -0
  758. package/src/ipc/skill-routes/__tests__/log.test.ts +133 -0
  759. package/src/ipc/skill-routes/__tests__/memory.test.ts +178 -0
  760. package/src/ipc/skill-routes/__tests__/platform.test.ts +111 -0
  761. package/src/ipc/skill-routes/__tests__/providers.test.ts +265 -0
  762. package/src/ipc/skill-routes/__tests__/registries.test.ts +361 -0
  763. package/src/ipc/skill-routes/config.ts +47 -0
  764. package/src/ipc/skill-routes/events.ts +131 -0
  765. package/src/ipc/skill-routes/identity.ts +34 -0
  766. package/src/ipc/skill-routes/index.ts +37 -0
  767. package/src/ipc/skill-routes/log.ts +40 -0
  768. package/src/ipc/skill-routes/memory.ts +76 -0
  769. package/src/ipc/skill-routes/platform.ts +39 -0
  770. package/src/ipc/skill-routes/providers.ts +163 -0
  771. package/src/ipc/skill-routes/registries.ts +393 -0
  772. package/src/ipc/skill-server.ts +771 -0
  773. package/src/ipc/skill-socket-path.ts +20 -0
  774. package/src/ipc/socket-cleanup.ts +92 -0
  775. package/src/ipc/socket-path.ts +55 -48
  776. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +374 -0
  777. package/src/live-voice/__tests__/live-voice-archive.test.ts +525 -0
  778. package/src/live-voice/__tests__/live-voice-events.test.ts +473 -0
  779. package/src/live-voice/__tests__/live-voice-integration.test.ts +359 -0
  780. package/src/live-voice/__tests__/live-voice-metrics.test.ts +179 -0
  781. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +349 -0
  782. package/src/live-voice/__tests__/live-voice-stt.test.ts +244 -0
  783. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +337 -0
  784. package/src/live-voice/__tests__/live-voice-tts.test.ts +337 -0
  785. package/src/live-voice/__tests__/protocol.test.ts +295 -0
  786. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +421 -0
  787. package/src/live-voice/live-voice-archive.ts +758 -0
  788. package/src/live-voice/live-voice-metrics.ts +472 -0
  789. package/src/live-voice/live-voice-session-manager.ts +222 -0
  790. package/src/live-voice/live-voice-session.ts +1144 -0
  791. package/src/live-voice/live-voice-tts.ts +260 -0
  792. package/src/live-voice/protocol.ts +524 -0
  793. package/src/mcp/client.ts +2 -2
  794. package/src/media/app-icon-generator.ts +23 -46
  795. package/src/media/avatar-router.ts +26 -41
  796. package/src/media/gemini-image-service.ts +8 -41
  797. package/src/media/image-credentials.ts +73 -0
  798. package/src/media/image-service.ts +85 -0
  799. package/src/media/openai-image-service.ts +131 -0
  800. package/src/media/types.ts +46 -0
  801. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +4 -28
  802. package/src/memory/__tests__/auto-analysis-guard.test.ts +2 -2
  803. package/src/memory/__tests__/conversation-analyze-job.test.ts +7 -62
  804. package/src/memory/__tests__/conversation-group-migration.test.ts +2 -2
  805. package/src/memory/__tests__/find-analysis-conversation.test.ts +2 -1
  806. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +235 -0
  807. package/src/memory/admin.ts +65 -7
  808. package/src/memory/app-git-service.ts +0 -14
  809. package/src/memory/attachments-store.ts +14 -16
  810. package/src/memory/auto-analysis-enqueue.ts +2 -17
  811. package/src/memory/canonical-guardian-store.ts +2 -1
  812. package/src/memory/channel-verification-sessions.ts +1 -1
  813. package/src/memory/checkpoints.ts +1 -1
  814. package/src/memory/context-search/agent-protocol.ts +424 -0
  815. package/src/memory/context-search/agent-runner.ts +1295 -0
  816. package/src/memory/context-search/format.ts +160 -0
  817. package/src/memory/context-search/limits.ts +106 -0
  818. package/src/memory/context-search/search.ts +387 -0
  819. package/src/memory/context-search/sources/conversations.ts +278 -0
  820. package/src/memory/context-search/sources/memory.ts +90 -0
  821. package/src/memory/context-search/sources/pkb.ts +468 -0
  822. package/src/memory/context-search/sources/workspace.ts +1255 -0
  823. package/src/memory/context-search/types.ts +49 -0
  824. package/src/memory/conversation-analyze-job.ts +3 -24
  825. package/src/memory/conversation-attention-store.ts +1 -1
  826. package/src/memory/conversation-bootstrap.ts +1 -1
  827. package/src/memory/conversation-crud.ts +117 -145
  828. package/src/memory/conversation-directories.ts +1 -11
  829. package/src/memory/conversation-display-order-migration.ts +11 -2
  830. package/src/memory/conversation-group-migration.ts +20 -4
  831. package/src/memory/conversation-key-store.ts +3 -4
  832. package/src/memory/conversation-queries.ts +69 -29
  833. package/src/memory/conversation-starter-validation.ts +88 -0
  834. package/src/memory/conversation-starters-cadence.ts +1 -1
  835. package/src/memory/conversation-title-service.ts +27 -1
  836. package/src/memory/db-init.ts +22 -4
  837. package/src/memory/db-maintenance.ts +1 -1
  838. package/src/memory/delivery-channels.ts +1 -14
  839. package/src/memory/delivery-crud.ts +2 -32
  840. package/src/memory/delivery-status.ts +1 -1
  841. package/src/memory/embedding-gemini.test.ts +44 -5
  842. package/src/memory/embedding-gemini.ts +6 -1
  843. package/src/memory/external-conversation-store.ts +1 -1
  844. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +412 -0
  845. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +225 -0
  846. package/src/memory/graph/bootstrap.test.ts +277 -0
  847. package/src/memory/graph/bootstrap.ts +10 -6
  848. package/src/memory/graph/capability-seed.ts +3 -3
  849. package/src/memory/graph/compaction.ts +1 -1
  850. package/src/memory/graph/consolidation.ts +13 -10
  851. package/src/memory/graph/conversation-graph-memory.ts +151 -1
  852. package/src/memory/graph/decay.ts +1 -1
  853. package/src/memory/graph/extraction.ts +63 -23
  854. package/src/memory/graph/graph-memory-state-store.ts +1 -1
  855. package/src/memory/graph/graph-search.test.ts +95 -2
  856. package/src/memory/graph/graph-search.ts +22 -7
  857. package/src/memory/graph/image-ref-utils.ts +1 -1
  858. package/src/memory/graph/retriever.test.ts +158 -4
  859. package/src/memory/graph/retriever.ts +27 -8
  860. package/src/memory/graph/store.test.ts +2 -1
  861. package/src/memory/graph/store.ts +1 -1
  862. package/src/memory/graph/tool-handlers.ts +73 -247
  863. package/src/memory/graph/tools.ts +35 -53
  864. package/src/memory/group-crud.ts +1 -2
  865. package/src/memory/guardian-action-store.ts +2 -1
  866. package/src/memory/guardian-approvals.ts +1 -1
  867. package/src/memory/guardian-rate-limits.ts +1 -1
  868. package/src/memory/indexer.ts +43 -17
  869. package/src/memory/invite-store.ts +1 -1
  870. package/src/memory/job-handlers/backfill.ts +1 -1
  871. package/src/memory/job-handlers/cleanup.ts +2 -1
  872. package/src/memory/job-handlers/conversation-starters.ts +18 -10
  873. package/src/memory/job-handlers/embedding.test.ts +2 -1
  874. package/src/memory/job-handlers/embedding.ts +1 -1
  875. package/src/memory/job-handlers/index-maintenance.ts +1 -1
  876. package/src/memory/job-handlers/summarization.ts +3 -3
  877. package/src/memory/job-utils.ts +3 -3
  878. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +362 -0
  879. package/src/memory/jobs/embed-concept-page.ts +210 -0
  880. package/src/memory/jobs/embed-pkb-file.test.ts +2 -1
  881. package/src/memory/jobs-store.ts +10 -2
  882. package/src/memory/jobs-worker.ts +58 -5
  883. package/src/memory/lifecycle-events-store.ts +1 -1
  884. package/src/memory/llm-request-log-store.ts +1 -1
  885. package/src/memory/llm-usage-store.ts +1 -1
  886. package/src/memory/media-store.ts +1 -1
  887. package/src/memory/memory-recall-log-store.ts +1 -1
  888. package/src/memory/migrations/038-actor-token-records.ts +3 -0
  889. package/src/memory/migrations/039-actor-refresh-token-records.ts +3 -0
  890. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  891. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  892. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  893. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  894. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  895. package/src/memory/migrations/226-schedule-wake-conversation-id.ts +11 -0
  896. package/src/memory/migrations/227-add-conversation-inference-profile.ts +18 -0
  897. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +27 -0
  898. package/src/memory/migrations/229-delete-private-conversations.test.ts +1087 -0
  899. package/src/memory/migrations/229-delete-private-conversations.ts +210 -0
  900. package/src/memory/migrations/230-acp-session-history.ts +41 -0
  901. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +128 -0
  902. package/src/memory/migrations/232-activation-state.ts +38 -0
  903. package/src/memory/migrations/index.ts +14 -0
  904. package/src/memory/migrations/registry.ts +7 -0
  905. package/src/memory/pkb/pkb-index.test.ts +5 -5
  906. package/src/memory/pkb/pkb-reconcile.test.ts +5 -5
  907. package/src/memory/pkb/pkb-search.test.ts +148 -7
  908. package/src/memory/pkb/pkb-search.ts +65 -30
  909. package/src/memory/published-pages-store.ts +1 -1
  910. package/src/memory/qdrant-client.test.ts +60 -0
  911. package/src/memory/qdrant-client.ts +25 -0
  912. package/src/memory/schema/acp.ts +30 -0
  913. package/src/memory/schema/conversations.ts +1 -1
  914. package/src/memory/schema/index.ts +1 -0
  915. package/src/memory/schema/infrastructure.ts +2 -32
  916. package/src/memory/schema/memory-graph.ts +36 -14
  917. package/src/memory/schema/oauth.ts +4 -1
  918. package/src/memory/scoped-approval-grants.ts +2 -1
  919. package/src/memory/search/semantic.ts +2 -2
  920. package/src/memory/shared-app-links-store.ts +2 -1
  921. package/src/memory/tool-usage-store.ts +1 -1
  922. package/src/memory/trace-event-store.ts +2 -1
  923. package/src/memory/turn-events-store.ts +1 -1
  924. package/src/memory/v2/__tests__/activation-store.test.ts +202 -0
  925. package/src/memory/v2/__tests__/activation.test.ts +956 -0
  926. package/src/memory/v2/__tests__/backfill-jobs.test.ts +610 -0
  927. package/src/memory/v2/__tests__/consolidation-job.test.ts +395 -0
  928. package/src/memory/v2/__tests__/edges.test.ts +435 -0
  929. package/src/memory/v2/__tests__/injection.test.ts +792 -0
  930. package/src/memory/v2/__tests__/migration.test.ts +812 -0
  931. package/src/memory/v2/__tests__/page-store.test.ts +334 -0
  932. package/src/memory/v2/__tests__/qdrant.test.ts +438 -0
  933. package/src/memory/v2/__tests__/sim.test.ts +549 -0
  934. package/src/memory/v2/__tests__/skill-content.test.ts +85 -0
  935. package/src/memory/v2/__tests__/skill-qdrant.test.ts +657 -0
  936. package/src/memory/v2/__tests__/skill-store.test.ts +351 -0
  937. package/src/memory/v2/__tests__/sweep-job.test.ts +441 -0
  938. package/src/memory/v2/activation-store.ts +109 -0
  939. package/src/memory/v2/activation.ts +490 -0
  940. package/src/memory/v2/backfill-jobs.ts +442 -0
  941. package/src/memory/v2/consolidation-job.ts +304 -0
  942. package/src/memory/v2/edges.ts +217 -0
  943. package/src/memory/v2/injection.ts +307 -0
  944. package/src/memory/v2/migration.ts +654 -0
  945. package/src/memory/v2/now-text.ts +38 -0
  946. package/src/memory/v2/page-store.ts +245 -0
  947. package/src/memory/v2/prompts/consolidation.ts +185 -0
  948. package/src/memory/v2/prompts/sweep.ts +56 -0
  949. package/src/memory/v2/qdrant.ts +342 -0
  950. package/src/memory/v2/sim.ts +206 -0
  951. package/src/memory/v2/skill-content.ts +42 -0
  952. package/src/memory/v2/skill-qdrant.ts +395 -0
  953. package/src/memory/v2/skill-store.ts +128 -0
  954. package/src/memory/v2/sweep-job.ts +298 -0
  955. package/src/memory/v2/types.ts +116 -0
  956. package/src/memory/validation.ts +1 -1
  957. package/src/messaging/providers/index.ts +262 -0
  958. package/src/messaging/providers/slack/api.ts +242 -0
  959. package/src/messaging/providers/slack/message-metadata.ts +1 -1
  960. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  961. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  962. package/src/messaging/providers/slack/send.ts +383 -0
  963. package/src/messaging/providers/telegram-bot/adapter.ts +4 -42
  964. package/src/messaging/providers/telegram-bot/api.ts +253 -0
  965. package/src/messaging/providers/telegram-bot/client.ts +17 -58
  966. package/src/messaging/providers/telegram-bot/send.ts +232 -0
  967. package/src/messaging/providers/whatsapp/adapter.ts +4 -36
  968. package/src/messaging/providers/whatsapp/api.ts +319 -0
  969. package/src/messaging/providers/whatsapp/client.ts +4 -48
  970. package/src/messaging/providers/whatsapp/send.ts +209 -0
  971. package/src/notifications/adapters/slack.ts +5 -23
  972. package/src/notifications/adapters/telegram.ts +8 -29
  973. package/src/notifications/conversation-candidates.ts +1 -1
  974. package/src/notifications/conversation-pairing.ts +78 -19
  975. package/src/notifications/conversation-seed-composer.ts +12 -6
  976. package/src/notifications/copy-composer.ts +1 -6
  977. package/src/notifications/decision-engine.ts +1 -1
  978. package/src/notifications/decisions-store.ts +1 -1
  979. package/src/notifications/deliveries-store.ts +2 -1
  980. package/src/notifications/deterministic-checks.ts +1 -1
  981. package/src/notifications/emit-signal.ts +1 -1
  982. package/src/notifications/events-store.ts +1 -13
  983. package/src/notifications/preferences-store.ts +1 -1
  984. package/src/notifications/signal.ts +1 -11
  985. package/src/oauth/AGENTS.md +1 -1
  986. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  987. package/src/oauth/connect-orchestrator.ts +8 -34
  988. package/src/oauth/connect-types.ts +6 -10
  989. package/src/oauth/connection-resolver.ts +11 -2
  990. package/src/oauth/manual-token-connection.ts +23 -0
  991. package/src/oauth/oauth-store.ts +32 -15
  992. package/src/oauth/provider-serializer.ts +6 -1
  993. package/src/oauth/seed-providers.ts +56 -108
  994. package/src/outbound-proxy/http-forwarder.ts +9 -0
  995. package/src/outbound-proxy/index.ts +0 -1
  996. package/src/permissions/approval-policy.test.ts +398 -106
  997. package/src/permissions/approval-policy.ts +134 -108
  998. package/src/permissions/checker.test.ts +632 -0
  999. package/src/permissions/checker.ts +280 -345
  1000. package/src/permissions/gateway-threshold-reader.ts +177 -0
  1001. package/src/permissions/ipc-risk-types.ts +95 -0
  1002. package/src/permissions/prompter.ts +8 -9
  1003. package/src/permissions/risk-types.ts +24 -153
  1004. package/src/permissions/types.ts +19 -47
  1005. package/src/permissions/workspace-policy.ts +10 -7
  1006. package/src/playbooks/playbook-compiler.ts +1 -1
  1007. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  1008. package/src/plugins/defaults/compaction.ts +145 -0
  1009. package/src/plugins/defaults/empty-response.ts +126 -0
  1010. package/src/plugins/defaults/history-repair.ts +85 -0
  1011. package/src/plugins/defaults/index.ts +116 -0
  1012. package/src/plugins/defaults/injectors.ts +488 -0
  1013. package/src/plugins/defaults/llm-call.ts +79 -0
  1014. package/src/plugins/defaults/memory-retrieval.ts +221 -0
  1015. package/src/plugins/defaults/overflow-reduce.ts +185 -0
  1016. package/src/plugins/defaults/persistence.ts +129 -0
  1017. package/src/plugins/defaults/title-generate.ts +95 -0
  1018. package/src/plugins/defaults/token-estimate.ts +103 -0
  1019. package/src/plugins/defaults/tool-error.ts +126 -0
  1020. package/src/plugins/defaults/tool-execute.ts +89 -0
  1021. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  1022. package/src/plugins/pipeline.ts +316 -0
  1023. package/src/plugins/plugin-skill-contributions.ts +292 -0
  1024. package/src/plugins/registry.ts +301 -0
  1025. package/src/plugins/types.ts +1133 -0
  1026. package/src/plugins/user-loader.ts +203 -0
  1027. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +197 -0
  1028. package/src/prompts/persona-resolver.ts +2 -4
  1029. package/src/prompts/system-prompt.ts +39 -0
  1030. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  1031. package/src/prompts/templates/SOUL.md +3 -1
  1032. package/src/providers/__tests__/provider-env-vars.test.ts +0 -21
  1033. package/src/providers/__tests__/retry-callsite.test.ts +3 -6
  1034. package/src/providers/anthropic/client.ts +71 -19
  1035. package/src/providers/call-site-routing.ts +7 -3
  1036. package/src/providers/fireworks/client.ts +3 -0
  1037. package/src/providers/gemini/client.ts +96 -22
  1038. package/src/providers/managed-proxy/context.ts +0 -12
  1039. package/src/providers/model-catalog.ts +123 -25
  1040. package/src/providers/model-intents.ts +6 -7
  1041. package/src/providers/openai/chat-completions-provider.ts +37 -7
  1042. package/src/providers/openai/responses-provider.ts +39 -4
  1043. package/src/providers/openrouter/client.ts +9 -6
  1044. package/src/providers/provider-env-vars.ts +4 -12
  1045. package/src/providers/provider-send-message.ts +16 -11
  1046. package/src/providers/registry.ts +1 -1
  1047. package/src/providers/retry.ts +52 -23
  1048. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  1049. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  1050. package/src/providers/speech-to-text/openai-whisper-stream.ts +1 -1
  1051. package/src/providers/speech-to-text/openai-whisper.ts +3 -6
  1052. package/src/providers/speech-to-text/provider-catalog.ts +75 -0
  1053. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  1054. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  1055. package/src/providers/speech-to-text/xai.ts +5 -5
  1056. package/src/providers/thinking-config.ts +34 -0
  1057. package/src/providers/types.ts +22 -10
  1058. package/src/runtime/AGENTS.md +27 -17
  1059. package/src/runtime/__tests__/agent-wake.test.ts +33 -9
  1060. package/src/runtime/__tests__/client-registry.test.ts +271 -0
  1061. package/src/runtime/__tests__/interactive-ui.test.ts +157 -246
  1062. package/src/runtime/access-request-helper.ts +9 -20
  1063. package/src/runtime/actor-trust-resolver.ts +2 -2
  1064. package/src/runtime/agent-wake.ts +174 -68
  1065. package/src/runtime/approval-conversation-turn.ts +2 -15
  1066. package/src/runtime/approval-message-composer.ts +11 -60
  1067. package/src/runtime/assistant-event.ts +18 -66
  1068. package/src/runtime/auth/__tests__/guard-tests.test.ts +6 -30
  1069. package/src/runtime/auth/__tests__/middleware.test.ts +10 -10
  1070. package/src/runtime/auth/__tests__/route-policy.test.ts +0 -8
  1071. package/src/runtime/auth/context.ts +9 -0
  1072. package/src/runtime/auth/middleware.ts +4 -4
  1073. package/src/runtime/auth/route-policy.ts +195 -4
  1074. package/src/runtime/auth/token-service.ts +1 -100
  1075. package/src/runtime/capability-tokens.ts +89 -313
  1076. package/src/runtime/channel-approval-types.ts +1 -6
  1077. package/src/runtime/channel-approvals.ts +7 -79
  1078. package/src/runtime/channel-readiness-service.ts +2 -2
  1079. package/src/runtime/channel-reply-delivery.ts +2 -8
  1080. package/src/runtime/channel-retry-sweep.ts +20 -17
  1081. package/src/runtime/client-registry.ts +254 -0
  1082. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -7
  1083. package/src/runtime/gateway-client.ts +37 -378
  1084. package/src/runtime/guardian-action-grant-minter.ts +2 -3
  1085. package/src/runtime/guardian-action-message-composer.ts +11 -52
  1086. package/src/runtime/guardian-action-service.ts +19 -7
  1087. package/src/runtime/guardian-decision-types.ts +4 -65
  1088. package/src/runtime/guardian-reply-router.ts +10 -19
  1089. package/src/runtime/guardian-vellum-migration.ts +5 -64
  1090. package/src/runtime/http-errors.ts +3 -0
  1091. package/src/runtime/http-router.ts +50 -7
  1092. package/src/runtime/http-server.ts +345 -1041
  1093. package/src/runtime/http-types.ts +15 -100
  1094. package/src/runtime/interactive-ui-types.ts +145 -0
  1095. package/src/runtime/interactive-ui.ts +38 -196
  1096. package/src/runtime/invite-redemption-service.ts +1 -1
  1097. package/src/runtime/invite-redemption-templates.ts +1 -1
  1098. package/src/runtime/local-actor-identity.ts +13 -43
  1099. package/src/runtime/message-composer-types.ts +134 -0
  1100. package/src/runtime/middleware/rate-limiter.ts +1 -1
  1101. package/src/runtime/middleware/request-logger.ts +5 -2
  1102. package/src/runtime/migrations/__tests__/job-registry.test.ts +346 -0
  1103. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +16 -0
  1104. package/src/runtime/migrations/job-registry.ts +281 -0
  1105. package/src/runtime/migrations/vbundle-builder.ts +4 -26
  1106. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  1107. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -13
  1108. package/src/runtime/migrations/vbundle-tar-stream.ts +11 -3
  1109. package/src/runtime/nl-approval-parser.ts +16 -21
  1110. package/src/runtime/pending-interactions.ts +29 -12
  1111. package/src/runtime/routes/__tests__/acp-routes.test.ts +395 -0
  1112. package/src/runtime/routes/__tests__/backup-routes.test.ts +204 -320
  1113. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +72 -4
  1114. package/src/runtime/routes/__tests__/stt-routes.test.ts +182 -223
  1115. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +230 -0
  1116. package/src/{ipc/__tests__/task-ipc.test.ts → runtime/routes/__tests__/task-routes.test.ts} +116 -96
  1117. package/src/runtime/routes/__tests__/tts-routes.test.ts +185 -289
  1118. package/src/runtime/routes/access-request-decision.ts +25 -50
  1119. package/src/runtime/routes/acp-routes.test.ts +371 -0
  1120. package/src/runtime/routes/acp-routes.ts +392 -166
  1121. package/src/runtime/routes/app-management-routes.ts +464 -660
  1122. package/src/runtime/routes/app-routes.ts +192 -177
  1123. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  1124. package/src/runtime/routes/approval-routes.ts +133 -434
  1125. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +24 -84
  1126. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +3 -10
  1127. package/src/runtime/routes/attachment-routes.ts +409 -253
  1128. package/src/runtime/routes/audio-routes.ts +51 -18
  1129. package/src/runtime/routes/avatar-routes.ts +82 -75
  1130. package/src/runtime/routes/background-tool-routes.ts +94 -0
  1131. package/src/runtime/routes/backup-routes.ts +154 -336
  1132. package/src/runtime/routes/brain-graph-routes.ts +83 -110
  1133. package/src/runtime/routes/browser-routes.ts +141 -0
  1134. package/src/runtime/routes/btw-routes.ts +62 -106
  1135. package/src/runtime/routes/cache-routes.ts +96 -0
  1136. package/src/runtime/routes/call-routes.ts +208 -247
  1137. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +1 -1
  1138. package/src/runtime/routes/channel-delivery-routes.ts +25 -27
  1139. package/src/runtime/routes/channel-readiness-routes.ts +83 -120
  1140. package/src/runtime/routes/channel-route-definitions.ts +62 -0
  1141. package/src/runtime/routes/channel-route-shared.ts +14 -18
  1142. package/src/runtime/routes/channel-verification-routes.ts +207 -187
  1143. package/src/runtime/routes/client-routes.ts +48 -0
  1144. package/src/runtime/routes/contact-routes.ts +533 -407
  1145. package/src/runtime/routes/conversation-analysis-routes.ts +48 -49
  1146. package/src/runtime/routes/conversation-attention-routes.ts +55 -67
  1147. package/src/runtime/routes/conversation-list-routes.ts +265 -0
  1148. package/src/runtime/routes/conversation-management-routes.ts +626 -715
  1149. package/src/runtime/routes/conversation-query-routes.ts +510 -460
  1150. package/src/runtime/routes/conversation-routes.ts +652 -457
  1151. package/src/runtime/routes/conversation-starter-routes.ts +121 -71
  1152. package/src/runtime/routes/credential-prompt-routes.ts +124 -0
  1153. package/src/runtime/routes/debug-routes.ts +34 -39
  1154. package/src/runtime/routes/defer-routes.ts +230 -0
  1155. package/src/runtime/routes/diagnostics-routes.ts +79 -70
  1156. package/src/runtime/routes/documents-routes.ts +117 -106
  1157. package/src/runtime/routes/errors.ts +132 -0
  1158. package/src/runtime/routes/events-routes.ts +97 -58
  1159. package/src/runtime/routes/filing-routes.ts +65 -78
  1160. package/src/runtime/routes/global-search-routes.ts +51 -57
  1161. package/src/runtime/routes/group-routes.ts +199 -181
  1162. package/src/runtime/routes/guardian-action-routes.ts +103 -169
  1163. package/src/runtime/routes/guardian-approval-interception.ts +27 -58
  1164. package/src/runtime/routes/guardian-approval-prompt.ts +10 -21
  1165. package/src/runtime/routes/guardian-approval-reply-helpers.ts +2 -6
  1166. package/src/runtime/routes/guardian-expiry-sweep.ts +19 -36
  1167. package/src/runtime/routes/heartbeat-routes.ts +194 -209
  1168. package/src/runtime/routes/home-feed-routes.ts +85 -187
  1169. package/src/runtime/routes/home-state-routes.ts +27 -24
  1170. package/src/runtime/routes/host-bash-routes.ts +42 -52
  1171. package/src/runtime/routes/host-browser-routes.ts +38 -69
  1172. package/src/runtime/routes/host-cu-routes.ts +74 -70
  1173. package/src/runtime/routes/host-file-routes.ts +50 -60
  1174. package/src/runtime/routes/host-transfer-routes.ts +220 -0
  1175. package/src/runtime/routes/http-adapter.ts +172 -0
  1176. package/src/runtime/routes/identity-routes.ts +83 -79
  1177. package/src/runtime/routes/inbound-conversation.ts +11 -18
  1178. package/src/runtime/routes/inbound-message-handler.ts +162 -123
  1179. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +79 -138
  1180. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +2 -3
  1181. package/src/runtime/routes/inbound-stages/background-dispatch.ts +54 -90
  1182. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +25 -50
  1183. package/src/runtime/routes/inbound-stages/edit-intercept.ts +7 -7
  1184. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  1185. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +5 -6
  1186. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -24
  1187. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -10
  1188. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
  1189. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +3 -3
  1190. package/src/runtime/routes/inbound-stages/verification-intercept.ts +19 -26
  1191. package/src/runtime/routes/index.ts +197 -0
  1192. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +25 -32
  1193. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +22 -31
  1194. package/src/runtime/routes/integrations/slack/channel.ts +69 -66
  1195. package/src/runtime/routes/integrations/slack/share.ts +49 -58
  1196. package/src/runtime/routes/integrations/telegram.ts +91 -74
  1197. package/src/runtime/routes/integrations/twilio.ts +163 -240
  1198. package/src/runtime/routes/integrations/vercel.ts +57 -54
  1199. package/src/runtime/routes/interface-routes.ts +43 -0
  1200. package/src/runtime/routes/internal-oauth-routes.ts +56 -0
  1201. package/src/runtime/routes/internal-twilio-routes.ts +46 -0
  1202. package/src/runtime/routes/llm-context-normalization.ts +4 -2
  1203. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  1204. package/src/runtime/routes/log-export-routes.ts +90 -100
  1205. package/src/runtime/routes/memory-item-routes.test.ts +153 -175
  1206. package/src/runtime/routes/memory-item-routes.ts +243 -323
  1207. package/src/runtime/routes/memory-v2-routes.ts +193 -0
  1208. package/src/runtime/routes/migration-rollback-routes.ts +167 -212
  1209. package/src/runtime/routes/migration-routes.ts +877 -377
  1210. package/src/runtime/routes/notification-routes.ts +199 -70
  1211. package/src/runtime/routes/oauth-apps.ts +254 -251
  1212. package/src/runtime/routes/oauth-providers.ts +66 -57
  1213. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +224 -0
  1214. package/src/runtime/routes/playground/__tests__/guard.test.ts +60 -0
  1215. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +250 -0
  1216. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +195 -0
  1217. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +159 -0
  1218. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +207 -0
  1219. package/src/runtime/routes/playground/__tests__/state.test.ts +175 -0
  1220. package/src/runtime/routes/playground/conversation-not-found.ts +27 -0
  1221. package/src/runtime/routes/playground/force-compact.ts +60 -0
  1222. package/src/runtime/routes/playground/guard.ts +36 -0
  1223. package/src/runtime/routes/playground/helpers.ts +103 -0
  1224. package/src/runtime/routes/playground/index.ts +18 -0
  1225. package/src/runtime/routes/playground/inject-failures.ts +143 -0
  1226. package/src/runtime/routes/playground/reset-circuit.ts +89 -0
  1227. package/src/runtime/routes/playground/seed-conversation.ts +113 -0
  1228. package/src/runtime/routes/playground/seeded-conversations.ts +74 -0
  1229. package/src/runtime/routes/playground/state.ts +77 -0
  1230. package/src/runtime/routes/profiler-routes.ts +132 -167
  1231. package/src/runtime/routes/ps-routes.ts +120 -0
  1232. package/src/runtime/routes/recording-routes.ts +197 -258
  1233. package/src/runtime/routes/rename-conversation-routes.ts +89 -0
  1234. package/src/runtime/routes/schedule-routes.ts +284 -207
  1235. package/src/runtime/routes/secret-routes.ts +219 -265
  1236. package/src/runtime/routes/secrets-deps.ts +24 -0
  1237. package/src/runtime/routes/settings-routes.ts +361 -441
  1238. package/src/runtime/routes/skills-routes.ts +434 -469
  1239. package/src/runtime/routes/stt-routes.ts +196 -206
  1240. package/src/runtime/routes/subagents-routes.ts +125 -141
  1241. package/src/runtime/routes/suggest-trust-rule-routes.ts +244 -0
  1242. package/src/runtime/routes/surface-action-routes.ts +135 -190
  1243. package/src/runtime/routes/surface-content-routes.ts +84 -118
  1244. package/src/runtime/routes/task-routes.ts +354 -0
  1245. package/src/runtime/routes/telemetry-routes.ts +33 -49
  1246. package/src/runtime/routes/trace-event-routes.ts +55 -74
  1247. package/src/runtime/routes/trust-rules-routes.ts +147 -239
  1248. package/src/runtime/routes/tts-routes.ts +187 -169
  1249. package/src/runtime/routes/types.ts +139 -0
  1250. package/src/{ipc/routes/ui-request.ts → runtime/routes/ui-request-routes.ts} +23 -17
  1251. package/src/runtime/routes/upgrade-broadcast-routes.ts +156 -197
  1252. package/src/runtime/routes/usage-routes.ts +143 -169
  1253. package/src/runtime/routes/user-routes.ts +102 -18
  1254. package/src/runtime/routes/wake-conversation-routes.ts +49 -0
  1255. package/src/{ipc/routes/watcher.ts → runtime/routes/watcher-routes.ts} +84 -39
  1256. package/src/runtime/routes/wipe-conversation-routes.ts +89 -0
  1257. package/src/runtime/routes/work-items-routes.test.ts +10 -20
  1258. package/src/runtime/routes/work-items-routes.ts +418 -433
  1259. package/src/runtime/routes/workspace-commit-routes.ts +30 -61
  1260. package/src/runtime/routes/workspace-routes.test.ts +254 -381
  1261. package/src/runtime/routes/workspace-routes.ts +238 -246
  1262. package/src/runtime/runtime-mode.ts +8 -1
  1263. package/src/runtime/services/__tests__/analyze-conversation.test.ts +80 -118
  1264. package/src/runtime/services/analyze-conversation.ts +14 -41
  1265. package/src/runtime/services/conversation-serializer.ts +181 -0
  1266. package/src/runtime/skill-route-registry.ts +75 -15
  1267. package/src/runtime/trust-context-resolver.ts +3 -2
  1268. package/src/runtime/verification-outbound-actions.ts +13 -49
  1269. package/src/schedule/run-script.ts +68 -0
  1270. package/src/schedule/schedule-store.ts +70 -2
  1271. package/src/schedule/scheduler.ts +149 -8
  1272. package/src/security/ces-credential-client.ts +32 -169
  1273. package/src/security/ces-rpc-credential-backend.ts +1 -1
  1274. package/src/security/credential-backend.ts +6 -6
  1275. package/src/security/oauth-completion-page.ts +1 -1
  1276. package/src/security/oauth2.ts +3 -6
  1277. package/src/sequence/analytics.ts +1 -1
  1278. package/src/sequence/guardrails.ts +3 -3
  1279. package/src/sequence/store.ts +2 -1
  1280. package/src/signals/bash.ts +1 -1
  1281. package/src/signals/event-stream.ts +1 -1
  1282. package/src/skills/catalog-cache.ts +19 -5
  1283. package/src/skills/catalog-files.ts +0 -5
  1284. package/src/skills/catalog-install.ts +28 -18
  1285. package/src/skills/category-inference.ts +0 -11
  1286. package/src/skills/clawhub.ts +2 -2
  1287. package/src/skills/managed-store.ts +2 -2
  1288. package/src/skills/remote-skill-policy.ts +6 -7
  1289. package/src/subagent/index.ts +2 -6
  1290. package/src/subagent/manager.ts +27 -23
  1291. package/src/subagent/types.ts +9 -0
  1292. package/src/tasks/SPEC.md +2 -2
  1293. package/src/tasks/task-compiler.ts +1 -1
  1294. package/src/tasks/task-runner.ts +2 -22
  1295. package/src/tasks/task-store.ts +1 -1
  1296. package/src/tools/acp/list-agents.test.ts +115 -0
  1297. package/src/tools/acp/list-agents.ts +31 -0
  1298. package/src/tools/acp/spawn.test.ts +379 -0
  1299. package/src/tools/acp/spawn.ts +142 -62
  1300. package/src/tools/acp/steer.test.ts +101 -0
  1301. package/src/tools/acp/steer.ts +38 -0
  1302. package/src/tools/background-tool-registry.ts +98 -0
  1303. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  1304. package/src/tools/browser/browser-execution.ts +122 -26
  1305. package/src/tools/browser/browser-manager.ts +1 -8
  1306. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  1307. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  1308. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +1 -1
  1309. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +3 -1
  1310. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  1311. package/src/tools/browser/cdp-client/factory.ts +15 -4
  1312. package/src/tools/browser/cdp-client/types.ts +4 -1
  1313. package/src/tools/computer-use/definitions.ts +1 -1
  1314. package/src/tools/credential-execution/make-authenticated-request.ts +2 -2
  1315. package/src/tools/credential-execution/manage-secure-command-tool.ts +1 -1
  1316. package/src/tools/credential-execution/run-authenticated-command.ts +2 -2
  1317. package/src/tools/credentials/broker-types.ts +2 -1
  1318. package/src/tools/document/editor-template.ts +1 -1
  1319. package/src/tools/execution-timeout.ts +1 -1
  1320. package/src/tools/executor.ts +123 -76
  1321. package/src/tools/host-filesystem/transfer.test.ts +268 -0
  1322. package/src/tools/host-filesystem/transfer.ts +234 -0
  1323. package/src/tools/host-terminal/host-shell.ts +189 -11
  1324. package/src/tools/mcp/mcp-tool-factory.ts +1 -1
  1325. package/src/tools/memory/register.test.ts +161 -1
  1326. package/src/tools/memory/register.ts +19 -34
  1327. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  1328. package/src/tools/permission-checker.ts +103 -255
  1329. package/src/tools/policy-context.ts +5 -8
  1330. package/src/tools/registry.ts +156 -4
  1331. package/src/tools/schedule/create.ts +23 -8
  1332. package/src/tools/schedule/update.ts +3 -1
  1333. package/src/tools/secret-detection-handler.ts +13 -154
  1334. package/src/tools/shared/shell-output.ts +4 -1
  1335. package/src/tools/side-effects.ts +2 -2
  1336. package/src/tools/skills/execute.ts +1 -1
  1337. package/src/tools/subagent/spawn.ts +35 -11
  1338. package/src/tools/system/avatar-generator.ts +6 -2
  1339. package/src/tools/terminal/safe-env.ts +9 -1
  1340. package/src/tools/terminal/shell.ts +161 -31
  1341. package/src/tools/tool-approval-handler.ts +4 -70
  1342. package/src/tools/tool-input-summary.ts +10 -0
  1343. package/src/tools/types.ts +157 -151
  1344. package/src/tools/ui-surface/definitions.ts +2 -2
  1345. package/src/util/debounce.ts +0 -21
  1346. package/src/util/errors.ts +0 -8
  1347. package/src/util/log-redact.ts +0 -1
  1348. package/src/util/platform.ts +85 -119
  1349. package/src/util/pricing.ts +135 -9
  1350. package/src/watcher/engine.ts +42 -20
  1351. package/src/watcher/watcher-store.ts +2 -1
  1352. package/src/work-items/work-item-store.ts +1 -1
  1353. package/src/workspace/git-service.ts +1 -6
  1354. package/src/workspace/migrations/006-services-config.ts +11 -4
  1355. package/src/workspace/migrations/017-seed-persona-dirs.ts +1 -1
  1356. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +1 -1
  1357. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  1358. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +1 -1
  1359. package/src/workspace/migrations/031-drop-user-md.ts +1 -1
  1360. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  1361. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +3 -4
  1362. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  1363. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  1364. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  1365. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  1366. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  1367. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  1368. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +150 -0
  1369. package/src/workspace/migrations/053-release-notes-acp-codex.ts +107 -0
  1370. package/src/workspace/migrations/054-seed-recall-callsite.ts +102 -0
  1371. package/src/workspace/migrations/055-release-notes-agentic-recall.ts +63 -0
  1372. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +65 -0
  1373. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +98 -0
  1374. package/src/workspace/migrations/058-release-notes-acp-sessions-ui.ts +71 -0
  1375. package/src/workspace/migrations/059-move-pid-to-workspace.ts +53 -0
  1376. package/src/workspace/migrations/060-memory-v2-init.ts +53 -0
  1377. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +1 -1
  1378. package/src/workspace/migrations/registry.ts +30 -0
  1379. package/src/workspace/migrations/runner.ts +2 -2
  1380. package/src/workspace/provider-commit-message-generator.ts +1 -1
  1381. package/tsconfig.json +1 -1
  1382. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  1383. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  1384. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +0 -471
  1385. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +0 -436
  1386. package/src/__tests__/cli-command-risk-guard.test.ts +0 -368
  1387. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  1388. package/src/__tests__/config-watcher-feature-flags.test.ts +0 -211
  1389. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  1390. package/src/__tests__/conversation-approval-overrides.test.ts +0 -207
  1391. package/src/__tests__/conversation-host-access-routes.test.ts +0 -229
  1392. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +0 -226
  1393. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +0 -167
  1394. package/src/__tests__/ephemeral-permissions.test.ts +0 -474
  1395. package/src/__tests__/extension-id-sync-guard.test.ts +0 -241
  1396. package/src/__tests__/hooks-blocking.test.ts +0 -178
  1397. package/src/__tests__/hooks-cli.test.ts +0 -182
  1398. package/src/__tests__/hooks-config.test.ts +0 -108
  1399. package/src/__tests__/hooks-discovery.test.ts +0 -211
  1400. package/src/__tests__/hooks-integration.test.ts +0 -196
  1401. package/src/__tests__/hooks-manager.test.ts +0 -226
  1402. package/src/__tests__/hooks-runner.test.ts +0 -175
  1403. package/src/__tests__/hooks-settings.test.ts +0 -160
  1404. package/src/__tests__/hooks-templates.test.ts +0 -169
  1405. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  1406. package/src/__tests__/hooks-watch.test.ts +0 -112
  1407. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +0 -374
  1408. package/src/__tests__/native-host-marker-sync-guard.test.ts +0 -157
  1409. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  1410. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  1411. package/src/__tests__/pairing-concurrent.test.ts +0 -84
  1412. package/src/__tests__/pairing-routes.test.ts +0 -181
  1413. package/src/__tests__/parser.test.ts +0 -595
  1414. package/src/__tests__/permission-checker-host-gate.test.ts +0 -512
  1415. package/src/__tests__/permission-controls-v2-flag.test.ts +0 -55
  1416. package/src/__tests__/permission-mode.test.ts +0 -89
  1417. package/src/__tests__/provider-env-vars-scope.test.ts +0 -52
  1418. package/src/__tests__/risk-classifier-parity.test.ts +0 -230
  1419. package/src/__tests__/send-notification-tool.test.ts +0 -83
  1420. package/src/__tests__/shell-identity.test.ts +0 -370
  1421. package/src/__tests__/shell-parser-fuzz.test.ts +0 -629
  1422. package/src/__tests__/shell-parser-property.test.ts +0 -936
  1423. package/src/__tests__/starter-bundle.test.ts +0 -173
  1424. package/src/__tests__/stt-catalog-parity.test.ts +0 -282
  1425. package/src/__tests__/task-runner.test.ts +0 -224
  1426. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -357
  1427. package/src/__tests__/trust-store-pattern-matches.test.ts +0 -29
  1428. package/src/__tests__/trust-store.test.ts +0 -2013
  1429. package/src/__tests__/v2-consent-policy.test.ts +0 -103
  1430. package/src/browser/identifiers.ts +0 -51
  1431. package/src/cli/commands/shotgun.ts +0 -266
  1432. package/src/cli/db.ts +0 -1
  1433. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  1434. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  1435. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  1436. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  1437. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  1438. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  1439. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  1440. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  1441. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  1442. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  1443. package/src/config/bundled-skills/settings/tools/avatar-get.ts +0 -40
  1444. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +0 -64
  1445. package/src/config/bundled-skills/settings/tools/avatar-update.ts +0 -88
  1446. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  1447. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +0 -127
  1448. package/src/daemon/approved-devices-store.ts +0 -110
  1449. package/src/daemon/context-overflow-approval.ts +0 -52
  1450. package/src/daemon/external-skills-bootstrap.ts +0 -41
  1451. package/src/daemon/message-types/trust.ts +0 -71
  1452. package/src/daemon/pairing-store.ts +0 -229
  1453. package/src/daemon/watch-handler.ts +0 -399
  1454. package/src/hooks/cli.ts +0 -253
  1455. package/src/hooks/config.ts +0 -100
  1456. package/src/hooks/discovery.ts +0 -135
  1457. package/src/hooks/manager.ts +0 -179
  1458. package/src/hooks/runner.ts +0 -117
  1459. package/src/hooks/templates.ts +0 -77
  1460. package/src/hooks/types.ts +0 -75
  1461. package/src/ipc/cli-server.ts +0 -252
  1462. package/src/ipc/routes/attachment.ts +0 -114
  1463. package/src/ipc/routes/browser-context.ts +0 -61
  1464. package/src/ipc/routes/browser.ts +0 -96
  1465. package/src/ipc/routes/cache.ts +0 -96
  1466. package/src/ipc/routes/index.ts +0 -21
  1467. package/src/ipc/routes/task-queue.ts +0 -226
  1468. package/src/ipc/routes/task.ts +0 -173
  1469. package/src/ipc/routes/wake-conversation.ts +0 -19
  1470. package/src/memory/db.ts +0 -23
  1471. package/src/oauth/scope-policy.ts +0 -89
  1472. package/src/permissions/bash-risk-classifier.test.ts +0 -1208
  1473. package/src/permissions/bash-risk-classifier.ts +0 -707
  1474. package/src/permissions/command-registry.test.ts +0 -535
  1475. package/src/permissions/command-registry.ts +0 -825
  1476. package/src/permissions/defaults.ts +0 -313
  1477. package/src/permissions/file-risk-classifier.test.ts +0 -535
  1478. package/src/permissions/file-risk-classifier.ts +0 -274
  1479. package/src/permissions/permission-mode.ts +0 -24
  1480. package/src/permissions/shell-identity.ts +0 -337
  1481. package/src/permissions/skill-risk-classifier.test.ts +0 -311
  1482. package/src/permissions/skill-risk-classifier.ts +0 -214
  1483. package/src/permissions/trust-client.ts +0 -359
  1484. package/src/permissions/trust-store-interface.ts +0 -100
  1485. package/src/permissions/trust-store.ts +0 -1330
  1486. package/src/permissions/v2-consent-policy.ts +0 -87
  1487. package/src/permissions/web-risk-classifier.test.ts +0 -170
  1488. package/src/permissions/web-risk-classifier.ts +0 -89
  1489. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +0 -715
  1490. package/src/runtime/__tests__/capability-tokens.test.ts +0 -258
  1491. package/src/runtime/actor-refresh-token-store.ts +0 -156
  1492. package/src/runtime/actor-token-store.ts +0 -207
  1493. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -264
  1494. package/src/runtime/auth/credential-service.ts +0 -352
  1495. package/src/runtime/conversation-approval-overrides.ts +0 -86
  1496. package/src/runtime/gateway-internal-client.ts +0 -94
  1497. package/src/runtime/routes/browser-extension-pair-routes.ts +0 -556
  1498. package/src/runtime/routes/channel-routes.ts +0 -112
  1499. package/src/runtime/routes/contact-routes.test.ts +0 -298
  1500. package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -175
  1501. package/src/runtime/routes/guardian-refresh-routes.ts +0 -79
  1502. package/src/runtime/routes/invite-routes.ts +0 -280
  1503. package/src/runtime/routes/pairing-routes.ts +0 -431
  1504. package/src/runtime/routes/watch-routes.ts +0 -156
  1505. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +0 -67
  1506. package/src/runtime/services/analyze-deps-singleton.ts +0 -32
  1507. package/src/signals/shotgun.ts +0 -203
  1508. package/src/tasks/ephemeral-permissions.ts +0 -55
  1509. package/src/tools/terminal/parser.ts +0 -623
  1510. package/src/tools/watch/screen-watch.ts +0 -144
  1511. package/src/tools/watch/watch-state.ts +0 -142
  1512. package/src/types/qrcode.d.ts +0 -13
  1513. package/src/util/network-info.ts +0 -55
  1514. /package/node_modules/@vellumai/{ces-contracts → ces-client}/tsconfig.json +0 -0
  1515. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/grants.test.ts +0 -0
  1516. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/error.ts +0 -0
  1517. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/grants.ts +0 -0
  1518. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/handles.ts +0 -0
  1519. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rendering.ts +0 -0
  1520. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rpc.ts +0 -0
@@ -1,7 +1,13 @@
1
1
  /**
2
2
  * Route handlers for conversation messages and suggestions.
3
3
  */
4
- import { existsSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
+ import {
5
+ existsSync,
6
+ readdirSync,
7
+ readFileSync,
8
+ statSync,
9
+ writeFileSync,
10
+ } from "node:fs";
5
11
  import { join, relative } from "node:path";
6
12
 
7
13
  import { z } from "zod";
@@ -13,6 +19,7 @@ import {
13
19
  } from "../../agent/message-types.js";
14
20
  import {
15
21
  canServiceRegistryBrowser,
22
+ canServiceSseBrowser,
16
23
  CHANNEL_IDS,
17
24
  INTERFACE_IDS,
18
25
  type InterfaceId,
@@ -23,6 +30,7 @@ import {
23
30
  } from "../../channels/types.js";
24
31
  import { isHttpAuthDisabled } from "../../config/env.js";
25
32
  import { getConfig } from "../../config/loader.js";
33
+ import { createApprovalConversationGenerator } from "../../daemon/approval-generators.js";
26
34
  import type { Conversation } from "../../daemon/conversation.js";
27
35
  import {
28
36
  buildModelInfoEvent,
@@ -33,6 +41,7 @@ import {
33
41
  resolveSlash,
34
42
  type SlashContext,
35
43
  } from "../../daemon/conversation-slash.js";
44
+ import { getOrCreateConversation as getOrCreateConversationInstance } from "../../daemon/conversation-store.js";
36
45
  import {
37
46
  getCannedFirstGreeting,
38
47
  isWakeUpGreeting,
@@ -42,17 +51,26 @@ import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
42
51
  import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
43
52
  import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
44
53
  import { HostFileProxy } from "../../daemon/host-file-proxy.js";
54
+ import { HostTransferProxy } from "../../daemon/host-transfer-proxy.js";
45
55
  import type { ServerMessage } from "../../daemon/message-protocol.js";
46
56
  import type {
47
57
  HostProxyTransportMetadata,
48
58
  NonHostProxyTransportMetadata,
49
59
  } from "../../daemon/message-types/conversations.js";
50
- import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
60
+ import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
61
+ import { emitFeedEvent } from "../../home/emit-feed-event.js";
51
62
  import {
52
63
  writeOnboardingSidecar,
53
64
  writeRelationshipState,
54
65
  } from "../../home/relationship-state-writer.js";
55
- import * as attachmentsStore from "../../memory/attachments-store.js";
66
+ import { rewriteCommandPreview } from "../../home/rewrite-command-preview.js";
67
+ import { ipcCall } from "../../ipc/gateway-client.js";
68
+ import {
69
+ getAttachmentById,
70
+ getAttachmentMetadataForMessage,
71
+ getAttachmentsByIds,
72
+ getSourcePathsForAttachments,
73
+ } from "../../memory/attachments-store.js";
56
74
  import {
57
75
  createCanonicalGuardianRequest,
58
76
  generateCanonicalRequestCode,
@@ -68,6 +86,7 @@ import {
68
86
  hasMessages,
69
87
  type MessageRow,
70
88
  provenanceFromTrustContext,
89
+ setConversationInferenceProfile,
71
90
  setConversationOriginChannelIfUnset,
72
91
  setConversationOriginInterfaceIfUnset,
73
92
  } from "../../memory/conversation-crud.js";
@@ -82,17 +101,19 @@ import { checkIngressForSecrets } from "../../security/secret-ingress.js";
82
101
  import { redactSecrets } from "../../security/secret-scanner.js";
83
102
  import { summarizeToolInput } from "../../tools/tool-input-summary.js";
84
103
  import { getLogger } from "../../util/logger.js";
85
- import { getWorkspacePromptPath } from "../../util/platform.js";
104
+ import {
105
+ getInterfacesDir,
106
+ getWorkspacePromptPath,
107
+ } from "../../util/platform.js";
86
108
  import { silentlyWithLog } from "../../util/silently.js";
87
109
  import { buildAssistantEvent } from "../assistant-event.js";
110
+ import { assistantEventHub } from "../assistant-event-hub.js";
88
111
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
89
- import type { AuthContext } from "../auth/types.js";
90
112
  import { getChromeExtensionRegistry } from "../chrome-extension-registry.js";
113
+ import { getClientRegistry } from "../client-registry.js";
91
114
  import { bridgeConfirmationRequestToGuardian } from "../confirmation-request-guardian-bridge.js";
92
115
  import { routeGuardianReply } from "../guardian-reply-router.js";
93
116
  import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
94
- import { httpError } from "../http-errors.js";
95
- import type { RouteDefinition } from "../http-router.js";
96
117
  import type {
97
118
  ApprovalConversationGenerator,
98
119
  RuntimeAttachmentMetadata,
@@ -105,6 +126,9 @@ import {
105
126
  resolveTrustContext,
106
127
  withSourceChannel,
107
128
  } from "../trust-context-resolver.js";
129
+ import { BadRequestError, InternalError, RouteError } from "./errors.js";
130
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
131
+ import { RouteResponse } from "./types.js";
108
132
 
109
133
  const log = getLogger("conversation-routes");
110
134
 
@@ -112,6 +136,15 @@ const log = getLogger("conversation-routes");
112
136
  const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
113
137
 
114
138
  const SUGGESTION_CACHE_MAX = 100;
139
+ const VALID_RISK_THRESHOLDS = ["none", "low", "medium", "high"] as const;
140
+ type RiskThreshold = (typeof VALID_RISK_THRESHOLDS)[number];
141
+
142
+ function isValidRiskThreshold(value: unknown): value is RiskThreshold {
143
+ return (
144
+ typeof value === "string" &&
145
+ VALID_RISK_THRESHOLDS.includes(value as RiskThreshold)
146
+ );
147
+ }
115
148
 
116
149
  function collectCanonicalGuardianRequestHintIds(
117
150
  conversationId: string,
@@ -359,11 +392,11 @@ function getInterfaceFilesWithMtimes(
359
392
  }
360
393
 
361
394
  export function handleListMessages(
362
- url: URL,
395
+ { queryParams }: RouteHandlerArgs,
363
396
  interfacesDir: string | null,
364
- ): Response {
365
- const conversationId = url.searchParams.get("conversationId");
366
- const conversationKey = url.searchParams.get("conversationKey");
397
+ ): Record<string, unknown> {
398
+ const conversationId = queryParams?.conversationId;
399
+ const conversationKey = queryParams?.conversationKey;
367
400
 
368
401
  let resolvedConversationId: string | undefined;
369
402
  if (conversationId) {
@@ -372,30 +405,40 @@ export function handleListMessages(
372
405
  const mapping = getConversationByKey(conversationKey);
373
406
  resolvedConversationId = mapping?.conversationId;
374
407
  } else {
375
- return httpError(
376
- "BAD_REQUEST",
408
+ throw new BadRequestError(
377
409
  "conversationKey or conversationId query parameter is required",
378
- 400,
379
410
  );
380
411
  }
381
412
 
382
- if (!resolvedConversationId) {
383
- return Response.json({ messages: [] });
384
- }
385
-
386
- const beforeTimestampRaw = url.searchParams.get("beforeTimestamp");
387
- const limitRaw = url.searchParams.get("limit");
413
+ const beforeTimestampRaw = queryParams?.beforeTimestamp;
414
+ const limitRaw = queryParams?.limit;
415
+ const pageRaw = queryParams?.page;
388
416
 
389
417
  // Validate: reject NaN values with 400
390
- if (beforeTimestampRaw !== null && isNaN(Number(beforeTimestampRaw))) {
391
- return httpError(
392
- "BAD_REQUEST",
393
- "beforeTimestamp must be a valid number",
394
- 400,
395
- );
418
+ if (beforeTimestampRaw != null && isNaN(Number(beforeTimestampRaw))) {
419
+ throw new BadRequestError("beforeTimestamp must be a valid number");
420
+ }
421
+ if (limitRaw != null && isNaN(Number(limitRaw))) {
422
+ throw new BadRequestError("limit must be a valid number");
396
423
  }
397
- if (limitRaw !== null && isNaN(Number(limitRaw))) {
398
- return httpError("BAD_REQUEST", "limit must be a valid number", 400);
424
+ if (pageRaw != null && pageRaw !== "latest") {
425
+ throw new BadRequestError("page must be 'latest' when provided");
426
+ }
427
+ const isLatestPage = pageRaw === "latest";
428
+
429
+ if (!resolvedConversationId) {
430
+ // Unresolved conversation keys still need to advertise the stable
431
+ // `page=latest` contract so the web client can rely on metadata fields
432
+ // being present even before any message is persisted.
433
+ if (isLatestPage && beforeTimestampRaw == null) {
434
+ return {
435
+ messages: [],
436
+ hasMore: false,
437
+ oldestTimestamp: null,
438
+ oldestMessageId: null,
439
+ };
440
+ }
441
+ return { messages: [] };
399
442
  }
400
443
 
401
444
  const beforeTimestamp = beforeTimestampRaw
@@ -406,10 +449,12 @@ export function handleListMessages(
406
449
  ? Math.min(Math.max(Math.floor(Number(limitRaw)), 1), 500)
407
450
  : undefined;
408
451
 
409
- // Option A: only paginate when beforeTimestamp is present.
410
- // Initial load and reconnect send limit but no beforeTimestamp — those must continue
411
- // returning all messages for zero regression risk.
412
- const isPaginated = beforeTimestamp != null;
452
+ // Paginate when either `beforeTimestamp` (older-page request) or
453
+ // `page=latest` (initial newest-N request) is set. When both are sent,
454
+ // `beforeTimestamp` wins because the caller is explicitly asking for an
455
+ // older page; `getMessagesPaginated` ignores `beforeTimestamp === undefined`
456
+ // and returns the newest `limit` messages in chronological order.
457
+ const isPaginated = beforeTimestamp != null || isLatestPage;
413
458
 
414
459
  let rawMessages: MessageRow[];
415
460
  let hasMore = false;
@@ -574,12 +619,12 @@ export function handleListMessages(
574
619
  // aren't lost before DB compaction relinks them.
575
620
  const idsToQuery = [m.id, ...(mergedIdMap.get(m.id) ?? [])];
576
621
  const linked = idsToQuery.flatMap((id) =>
577
- attachmentsStore.getAttachmentMetadataForMessage(id),
622
+ getAttachmentMetadataForMessage(id),
578
623
  );
579
624
  if (linked.length > 0) {
580
625
  msgAttachments = linked.map((a) => {
581
626
  if (a.mimeType.startsWith("image/")) {
582
- const full = attachmentsStore.getAttachmentById(a.id, {
627
+ const full = getAttachmentById(a.id, {
583
628
  hydrateFileData: true,
584
629
  });
585
630
  return {
@@ -655,15 +700,28 @@ export function handleListMessages(
655
700
  rawMessages.length > 0 ? rawMessages[0].createdAt : undefined;
656
701
  const oldestMessageId =
657
702
  rawMessages.length > 0 ? rawMessages[0].id : undefined;
658
- return Response.json({
703
+ // `page=latest` always emits both metadata fields so the web client has
704
+ // a stable contract; emit `null` when the conversation is empty.
705
+ // The existing `beforeTimestamp` branch keeps its conditional shape to
706
+ // avoid disturbing current callers.
707
+ if (isLatestPage && beforeTimestamp == null) {
708
+ return {
709
+ messages,
710
+ hasMore,
711
+ oldestTimestamp: oldestTimestamp ?? null,
712
+ oldestMessageId: oldestMessageId ?? null,
713
+ };
714
+ }
715
+
716
+ return {
659
717
  messages,
660
718
  hasMore,
661
719
  ...(oldestTimestamp != null ? { oldestTimestamp } : {}),
662
720
  ...(oldestMessageId != null ? { oldestMessageId } : {}),
663
- });
721
+ };
664
722
  }
665
723
 
666
- return Response.json({ messages });
724
+ return { messages };
667
725
  }
668
726
 
669
727
  // ── Tool-result merging ─────────────────────────────────────────────
@@ -994,10 +1052,59 @@ function makeHubPublisher(
994
1052
  allowlistOptions: msg.allowlistOptions,
995
1053
  scopeOptions: msg.scopeOptions,
996
1054
  persistentDecisionsAllowed: msg.persistentDecisionsAllowed,
997
- temporaryOptionsAvailable: msg.temporaryOptionsAvailable,
998
1055
  },
999
1056
  });
1000
1057
 
1058
+ const inputRecord = msg.input as Record<string, unknown>;
1059
+ const commandPreview =
1060
+ redactSecrets(summarizeToolInput(msg.toolName, inputRecord)) ||
1061
+ undefined;
1062
+ const technicalTitle = commandPreview
1063
+ ? `Requesting permission: ${commandPreview}`
1064
+ : `Requesting approval to use ${msg.toolName}.`;
1065
+ const dedupKey = `tool-approval:${msg.requestId}`;
1066
+
1067
+ // Emit immediately with the technical preview.
1068
+ void emitFeedEvent({
1069
+ source: "assistant",
1070
+ title: technicalTitle,
1071
+ summary: technicalTitle,
1072
+ dedupKey,
1073
+ urgency: msg.riskLevel === "high" ? "high" : "medium",
1074
+ conversationId,
1075
+ detailPanel: { kind: "toolPermission" },
1076
+ }).catch((err) => {
1077
+ log.warn(
1078
+ { err, requestId: msg.requestId },
1079
+ "Failed to emit tool approval request feed event",
1080
+ );
1081
+ });
1082
+
1083
+ // Background: rewrite into prose and update the feed item.
1084
+ if (commandPreview) {
1085
+ void rewriteCommandPreview(msg.toolName, commandPreview)
1086
+ .then((prose) => {
1087
+ if (prose) {
1088
+ const proseTitle = `Requesting permission: ${prose}`;
1089
+ return emitFeedEvent({
1090
+ source: "assistant",
1091
+ title: proseTitle,
1092
+ summary: proseTitle,
1093
+ dedupKey,
1094
+ urgency: msg.riskLevel === "high" ? "high" : "medium",
1095
+ conversationId,
1096
+ detailPanel: { kind: "toolPermission" },
1097
+ });
1098
+ }
1099
+ })
1100
+ .catch((err) => {
1101
+ log.warn(
1102
+ { err, requestId: msg.requestId },
1103
+ "Failed to update feed event with prose rewrite",
1104
+ );
1105
+ });
1106
+ }
1107
+
1001
1108
  // Create a canonical guardian request so HTTP handlers can find it
1002
1109
  // via applyCanonicalGuardianDecision.
1003
1110
  try {
@@ -1168,83 +1275,93 @@ function registerHostProxyPendingInteraction(
1168
1275
  });
1169
1276
  return msg.requestId;
1170
1277
  }
1278
+ if (msg.type === "host_transfer_request") {
1279
+ pendingInteractions.register(msg.requestId, {
1280
+ conversation,
1281
+ conversationId,
1282
+ kind: "host_transfer",
1283
+ });
1284
+ return msg.requestId;
1285
+ }
1171
1286
  return undefined;
1172
1287
  }
1173
1288
 
1174
1289
  /**
1175
1290
  * Resolve the host_browser sender function for a conversation turn.
1176
1291
  *
1177
- * When the guardian has an active extension connection in the
1178
- * ChromeExtensionRegistry, returns a registry-routed sender that forwards
1179
- * `host_browser_request` / `host_browser_cancel` frames through the
1180
- * WebSocket to the connected extension. Otherwise returns the SSE hub
1181
- * emitter (`onEvent`).
1292
+ * Transport selection:
1293
+ * 1. **WebSocket registry** when the guardian has an active entry in
1294
+ * ChromeExtensionRegistry (self-hosted direct WS connection), the
1295
+ * registry-routed sender is returned. Frames go directly over the
1296
+ * WebSocket to the extension.
1297
+ * 2. **SSE event hub** — when no WebSocket connection exists but a
1298
+ * chrome-extension client is connected via SSE (cloud/platform mode),
1299
+ * the SSE hub sender (`onEvent`) is returned. The extension receives
1300
+ * `host_browser_request` frames as SSE events and POSTs results back
1301
+ * to `/v1/host-browser-result`.
1182
1302
  *
1183
- * For `chrome-extension` turns the registry sender is **always** returned
1184
- * regardless of the POST-time connection check. The chrome-extension
1185
- * interface has no SSE consumer for `host_browser_request` frames, so
1186
- * falling back to `onEvent` would cause CDP calls to stall until the proxy
1187
- * timeout (30 s) instead of failing immediately at send time when the
1188
- * registry throws on a missing connection.
1189
- *
1190
- * This helper is interface-agnostic: both chrome-extension and macOS turns
1191
- * can obtain a registry-routed sender when extension connectivity exists.
1192
- * The `isRegistryRouted` flag lets the caller decide whether to set
1193
- * `hostBrowserSenderOverride` and whether to provision a `HostBrowserProxy`
1194
- * for interfaces that don't statically support host_browser (e.g. macOS).
1303
+ * When neither transport is available, `onEvent` is returned as the
1304
+ * default sender (used by macOS for its native host_browser path).
1305
+ * `hasSseExtension` is `false` in that case so the caller can avoid
1306
+ * provisioning a stale `HostBrowserProxy` for interfaces that don't
1307
+ * natively support host_browser.
1195
1308
  */
1196
1309
  function resolveHostBrowserSender(
1197
1310
  conversation: Conversation,
1198
1311
  conversationId: string,
1199
- authContext: AuthContext,
1312
+ actorPrincipalId: string | undefined,
1200
1313
  onEvent: (msg: ServerMessage) => void,
1201
1314
  sourceInterface: InterfaceId,
1202
- ): { sender: (msg: ServerMessage) => void; isRegistryRouted: boolean } {
1203
- // Check whether the guardian has any active extension connection.
1315
+ ): {
1316
+ sender: (msg: ServerMessage) => void;
1317
+ isRegistryRouted: boolean;
1318
+ hasSseExtension: boolean;
1319
+ } {
1204
1320
  const guardianId =
1205
- conversation.trustContext?.guardianPrincipalId ??
1206
- authContext.actorPrincipalId;
1321
+ conversation.trustContext?.guardianPrincipalId ?? actorPrincipalId;
1207
1322
  const hasExtensionConnection =
1208
1323
  !!guardianId && !!getChromeExtensionRegistry().get(guardianId);
1209
1324
 
1210
- // For chrome-extension, always use the registry sender so that send-time
1211
- // failures produce immediate errors rather than 30-second proxy timeouts.
1212
- // The SSE hub has no extension consumer, so falling back to onEvent is
1213
- // never correct for this interface.
1214
- if (!hasExtensionConnection && sourceInterface !== "chrome-extension") {
1215
- return { sender: onEvent, isRegistryRouted: false };
1216
- }
1217
-
1218
- // Build a registry-routed sender. The guardian principal ID is resolved
1219
- // at send time rather than captured here so that queue-drain restores
1220
- // (which re-fire this closure outside the original POST context) follow
1221
- // the conversation's bound guardian identity rather than a stale
1222
- // authContext.actorPrincipalId.
1223
- const registrySender = (msg: ServerMessage): void => {
1224
- const requestId = registerHostProxyPendingInteraction(
1225
- msg,
1226
- conversation,
1227
- conversationId,
1228
- );
1229
- const gid =
1230
- conversation.trustContext?.guardianPrincipalId ??
1231
- authContext.actorPrincipalId;
1232
- if (!gid) {
1233
- if (requestId) pendingInteractions.resolve(requestId);
1234
- throw new Error(
1235
- "host_browser send skipped: no guardianId on AuthContext",
1236
- );
1237
- }
1238
- const ok = getChromeExtensionRegistry().send(gid, msg);
1239
- if (!ok) {
1240
- if (requestId) pendingInteractions.resolve(requestId);
1241
- throw new Error(
1242
- `host_browser send failed: no active connection for guardian ${gid}`,
1325
+ // Priority 1: WebSocket registry direct WS to the extension.
1326
+ if (hasExtensionConnection) {
1327
+ const registrySender = (msg: ServerMessage): void => {
1328
+ const requestId = registerHostProxyPendingInteraction(
1329
+ msg,
1330
+ conversation,
1331
+ conversationId,
1243
1332
  );
1244
- }
1245
- };
1333
+ const gid =
1334
+ conversation.trustContext?.guardianPrincipalId ?? actorPrincipalId;
1335
+ if (!gid) {
1336
+ if (requestId) pendingInteractions.resolve(requestId);
1337
+ throw new Error(
1338
+ "host_browser send skipped: no guardianId on AuthContext",
1339
+ );
1340
+ }
1341
+ const ok = getChromeExtensionRegistry().send(gid, msg);
1342
+ if (!ok) {
1343
+ if (requestId) pendingInteractions.resolve(requestId);
1344
+ throw new Error(
1345
+ `host_browser send failed: no active connection for guardian ${gid}`,
1346
+ );
1347
+ }
1348
+ };
1349
+ return {
1350
+ sender: registrySender,
1351
+ isRegistryRouted: true,
1352
+ hasSseExtension: false,
1353
+ };
1354
+ }
1246
1355
 
1247
- return { sender: registrySender, isRegistryRouted: true };
1356
+ // Priority 2: SSE-connected chrome extension (cloud/platform mode).
1357
+ // Check the ClientRegistry for a chrome-extension client specifically —
1358
+ // getMostRecentByCapability("host_browser") would also match macOS
1359
+ // clients, which handle browser frames through their own native path.
1360
+ const hasSseExtension =
1361
+ canServiceSseBrowser(sourceInterface) &&
1362
+ !!getClientRegistry().getMostRecentByInterface("chrome-extension");
1363
+
1364
+ return { sender: onEvent, isRegistryRouted: false, hasSseExtension };
1248
1365
  }
1249
1366
 
1250
1367
  /**
@@ -1271,7 +1388,7 @@ function resolveHostBrowserSender(
1271
1388
  * failure. The route handler path must never reject because of a
1272
1389
  * best-effort persistence step.
1273
1390
  */
1274
- function persistOnboardingArtifacts(onboarding: {
1391
+ export function persistOnboardingArtifacts(onboarding: {
1275
1392
  tools: string[];
1276
1393
  tasks: string[];
1277
1394
  tone: string;
@@ -1283,31 +1400,49 @@ function persistOnboardingArtifacts(onboarding: {
1283
1400
  const assistantName = onboarding.assistantName?.trim();
1284
1401
  if (assistantName) {
1285
1402
  const identityPath = getWorkspacePromptPath("IDENTITY.md");
1286
- if (!existsSync(identityPath)) {
1287
- try {
1403
+ try {
1404
+ if (existsSync(identityPath)) {
1405
+ const content = readFileSync(identityPath, "utf-8");
1406
+ const updated = content.replace(
1407
+ /^- (?:\*\*)?Name:(?:\*\*)?\s*.*$/m,
1408
+ () => `- **Name:** ${assistantName}`,
1409
+ );
1410
+ if (updated !== content) {
1411
+ writeFileSync(identityPath, updated, "utf-8");
1412
+ }
1413
+ } else {
1288
1414
  writeFileSync(
1289
1415
  identityPath,
1290
- `# Identity\n\n- Name: ${assistantName}\n`,
1416
+ `# Identity\n\n- **Name:** ${assistantName}\n`,
1291
1417
  "utf-8",
1292
1418
  );
1293
- } catch (err) {
1294
- log.warn(
1295
- { err, identityPath },
1296
- "Failed to seed IDENTITY.md from onboarding",
1297
- );
1298
1419
  }
1420
+ } catch (err) {
1421
+ log.warn(
1422
+ { err, identityPath },
1423
+ "Failed to seed IDENTITY.md from onboarding",
1424
+ );
1299
1425
  }
1300
1426
  }
1301
1427
 
1302
1428
  const userName = onboarding.userName?.trim();
1303
1429
  if (userName) {
1304
1430
  const userPath = getWorkspacePromptPath("USER.md");
1305
- if (!existsSync(userPath)) {
1306
- try {
1307
- writeFileSync(userPath, `# User\n\n- Name: ${userName}\n`, "utf-8");
1308
- } catch (err) {
1309
- log.warn({ err, userPath }, "Failed to seed USER.md from onboarding");
1431
+ try {
1432
+ if (existsSync(userPath)) {
1433
+ const content = readFileSync(userPath, "utf-8");
1434
+ const updated = content.replace(
1435
+ /^- (?:\*\*)?Name:(?:\*\*)?\s*.*$/m,
1436
+ () => `- **Name:** ${userName}`,
1437
+ );
1438
+ if (updated !== content) {
1439
+ writeFileSync(userPath, updated, "utf-8");
1440
+ }
1441
+ } else {
1442
+ writeFileSync(userPath, `# User\n\n- **Name:** ${userName}\n`, "utf-8");
1310
1443
  }
1444
+ } catch (err) {
1445
+ log.warn({ err, userPath }, "Failed to seed USER.md from onboarding");
1311
1446
  }
1312
1447
  }
1313
1448
 
@@ -1320,15 +1455,13 @@ function persistOnboardingArtifacts(onboarding: {
1320
1455
  }
1321
1456
 
1322
1457
  export async function handleSendMessage(
1323
- req: Request,
1458
+ { body: rawBody, headers }: RouteHandlerArgs,
1324
1459
  deps: {
1325
1460
  sendMessageDeps?: SendMessageDeps;
1326
1461
  approvalConversationGenerator?: ApprovalConversationGenerator;
1327
- heartbeatService?: HeartbeatService;
1328
1462
  },
1329
- authContext: AuthContext,
1330
- ): Promise<Response> {
1331
- const body = (await req.json()) as {
1463
+ ): Promise<unknown> {
1464
+ const body = (rawBody ?? {}) as {
1332
1465
  conversationKey?: string;
1333
1466
  content?: string;
1334
1467
  attachmentIds?: string[];
@@ -1339,7 +1472,10 @@ export async function handleSendMessage(
1339
1472
  bypassSecretCheck?: boolean;
1340
1473
  hostHomeDir?: string;
1341
1474
  hostUsername?: string;
1475
+ clientId?: string;
1342
1476
  clientMessageId?: string;
1477
+ inferenceProfile?: string | null;
1478
+ riskThreshold?: string;
1343
1479
  onboarding?: {
1344
1480
  tools: string[];
1345
1481
  tasks: string[];
@@ -1349,35 +1485,70 @@ export async function handleSendMessage(
1349
1485
  };
1350
1486
  };
1351
1487
 
1488
+ const actorPrincipalId = headers?.["x-vellum-actor-principal-id"];
1489
+ const principalType = headers?.["x-vellum-principal-type"];
1490
+
1352
1491
  const { conversationKey, content, attachmentIds } = body;
1353
1492
  const clientMessageId =
1354
1493
  typeof body.clientMessageId === "string" ? body.clientMessageId : undefined;
1494
+ const requestedInferenceProfile =
1495
+ typeof body.inferenceProfile === "string"
1496
+ ? body.inferenceProfile
1497
+ : undefined;
1498
+ const requestedRiskThreshold = body.riskThreshold;
1499
+ if (
1500
+ body.inferenceProfile != null &&
1501
+ typeof body.inferenceProfile !== "string"
1502
+ ) {
1503
+ throw new BadRequestError(
1504
+ "inferenceProfile must be a non-empty string or null",
1505
+ );
1506
+ }
1507
+ if (requestedInferenceProfile === "") {
1508
+ throw new BadRequestError(
1509
+ "inferenceProfile must be a non-empty string or null",
1510
+ );
1511
+ }
1512
+ if (requestedInferenceProfile !== undefined) {
1513
+ const profiles = getConfig().llm.profiles ?? {};
1514
+ if (
1515
+ !Object.prototype.hasOwnProperty.call(profiles, requestedInferenceProfile)
1516
+ ) {
1517
+ throw new BadRequestError(
1518
+ `Profile "${requestedInferenceProfile}" is not defined in llm.profiles`,
1519
+ );
1520
+ }
1521
+ }
1522
+ if (
1523
+ requestedRiskThreshold !== undefined &&
1524
+ !isValidRiskThreshold(requestedRiskThreshold)
1525
+ ) {
1526
+ throw new BadRequestError(
1527
+ `riskThreshold must be one of: ${VALID_RISK_THRESHOLDS.join(", ")}`,
1528
+ );
1529
+ }
1355
1530
  if (!body.sourceChannel || typeof body.sourceChannel !== "string") {
1356
- return httpError("BAD_REQUEST", "sourceChannel is required", 400);
1531
+ throw new BadRequestError("sourceChannel is required");
1357
1532
  }
1358
1533
  const sourceChannel = parseChannelId(body.sourceChannel);
1359
1534
 
1360
1535
  if (!sourceChannel) {
1361
- return httpError(
1362
- "BAD_REQUEST",
1536
+ throw new BadRequestError(
1363
1537
  `Invalid sourceChannel: ${
1364
1538
  body.sourceChannel
1365
1539
  }. Valid values: ${CHANNEL_IDS.join(", ")}`,
1366
- 400,
1367
1540
  );
1368
1541
  }
1369
1542
 
1370
1543
  if (!body.interface || typeof body.interface !== "string") {
1371
- return httpError("BAD_REQUEST", "interface is required", 400);
1544
+ throw new BadRequestError("interface is required");
1372
1545
  }
1373
1546
  const sourceInterface = parseInterfaceId(body.interface);
1374
1547
  if (!sourceInterface) {
1375
- return httpError(
1376
- "BAD_REQUEST",
1548
+ throw new BadRequestError(
1377
1549
  `Invalid interface: ${body.interface}. Valid values: ${INTERFACE_IDS.join(
1378
1550
  ", ",
1379
1551
  )}`,
1380
- 400,
1381
1552
  );
1382
1553
  }
1383
1554
 
@@ -1389,7 +1560,7 @@ export async function handleSendMessage(
1389
1560
 
1390
1561
  // Reject non-string content values (numbers, objects, etc.)
1391
1562
  if (content != null && typeof content !== "string") {
1392
- return httpError("BAD_REQUEST", "content must be a string", 400);
1563
+ throw new BadRequestError("content must be a string");
1393
1564
  }
1394
1565
 
1395
1566
  const trimmedContent = typeof content === "string" ? content.trim() : "";
@@ -1397,23 +1568,17 @@ export async function handleSendMessage(
1397
1568
  Array.isArray(attachmentIds) && attachmentIds.length > 0;
1398
1569
 
1399
1570
  if (trimmedContent.length === 0 && !hasAttachments) {
1400
- return httpError(
1401
- "BAD_REQUEST",
1402
- "content or attachmentIds is required",
1403
- 400,
1404
- );
1571
+ throw new BadRequestError("content or attachmentIds is required");
1405
1572
  }
1406
1573
 
1407
1574
  // Validate that all attachment IDs resolve
1408
1575
  if (hasAttachments) {
1409
- const resolved = attachmentsStore.getAttachmentsByIds(attachmentIds);
1576
+ const resolved = getAttachmentsByIds(attachmentIds);
1410
1577
  if (resolved.length !== attachmentIds.length) {
1411
1578
  const resolvedIds = new Set(resolved.map((a) => a.id));
1412
1579
  const missing = attachmentIds.filter((id) => !resolvedIds.has(id));
1413
- return httpError(
1414
- "BAD_REQUEST",
1580
+ throw new BadRequestError(
1415
1581
  `Attachment IDs not found: ${missing.join(", ")}`,
1416
- 400,
1417
1582
  );
1418
1583
  }
1419
1584
  }
@@ -1422,36 +1587,52 @@ export async function handleSendMessage(
1422
1587
  if (trimmedContent.length > 0 && !body.bypassSecretCheck) {
1423
1588
  const ingressResult = checkIngressForSecrets(trimmedContent);
1424
1589
  if (ingressResult.blocked) {
1425
- return Response.json(
1426
- {
1590
+ return new RouteResponse(
1591
+ JSON.stringify({
1427
1592
  accepted: false,
1428
1593
  error: "secret_blocked",
1429
1594
  message: ingressResult.userNotice,
1430
1595
  detectedTypes: ingressResult.detectedTypes,
1431
- },
1432
- { status: 422 },
1596
+ }),
1597
+ { "content-type": "application/json" },
1598
+ 422,
1433
1599
  );
1434
1600
  }
1435
1601
  }
1436
1602
 
1437
1603
  if (!deps.sendMessageDeps) {
1438
- return httpError(
1439
- "SERVICE_UNAVAILABLE",
1604
+ throw new RouteError(
1440
1605
  "Message processing is not available",
1606
+ "SERVICE_UNAVAILABLE",
1441
1607
  503,
1442
1608
  );
1443
1609
  }
1444
1610
 
1445
1611
  // Desktop messages are always from the guardian — reset the heartbeat
1446
1612
  // timer so the next heartbeat is a full interval after this interaction.
1447
- deps.heartbeatService?.resetTimer();
1613
+ HeartbeatService.getInstance()?.resetTimer();
1448
1614
 
1449
- const conversationType =
1450
- body.conversationType === "private" ? ("private" as const) : undefined;
1451
1615
  const mapping = getOrCreateConversation(resolvedConversationKey, {
1452
- conversationType,
1616
+ conversationType: "standard",
1453
1617
  });
1454
1618
 
1619
+ if (requestedRiskThreshold !== undefined) {
1620
+ const result = await ipcCall("set_conversation_threshold", {
1621
+ conversationId: mapping.conversationId,
1622
+ threshold: requestedRiskThreshold,
1623
+ });
1624
+ if (result === undefined) {
1625
+ log.error(
1626
+ {
1627
+ conversationId: mapping.conversationId,
1628
+ threshold: requestedRiskThreshold,
1629
+ },
1630
+ "Failed to set conversation risk threshold override via gateway IPC",
1631
+ );
1632
+ throw new InternalError("Failed to persist risk threshold override");
1633
+ }
1634
+ }
1635
+
1455
1636
  const smDeps = deps.sendMessageDeps;
1456
1637
 
1457
1638
  // Notify all connected clients that the conversation list changed when
@@ -1500,25 +1681,33 @@ export async function handleSendMessage(
1500
1681
  { transport },
1501
1682
  );
1502
1683
 
1684
+ if (requestedInferenceProfile !== undefined) {
1685
+ setConversationInferenceProfile(
1686
+ mapping.conversationId,
1687
+ requestedInferenceProfile,
1688
+ );
1689
+ }
1690
+
1503
1691
  // Store pre-chat onboarding context on the conversation when this is the
1504
- // very first message (no prior messages loaded). Also persist the
1505
- // onboarding selections so the Home page shows onboarding-sourced
1506
- // chips immediately, and seed IDENTITY.md / USER.md so subsequent
1507
- // turn-boundary recomputes of relationship-state have a stable
1508
- // persona source beyond the in-memory conversation object.
1509
- if (body.onboarding && conversation.messages.length === 0) {
1510
- conversation.setOnboardingContext(body.onboarding);
1511
- persistOnboardingArtifacts(body.onboarding);
1692
+ // very first message (no prior messages loaded). Artifact persistence
1693
+ // (IDENTITY.md, USER.md, sidecar) is deferred: on the canned greeting
1694
+ // path it runs inside the setTimeout right before warmPromptCache() so
1695
+ // the warmed system prompt includes the identity; on the normal LLM
1696
+ // path it runs immediately before inference starts.
1697
+ const isFirstOnboarding =
1698
+ !!body.onboarding && conversation.messages.length === 0;
1699
+ if (isFirstOnboarding) {
1700
+ conversation.setOnboardingContext(body.onboarding!);
1512
1701
  }
1513
1702
 
1514
1703
  // Resolve guardian context from the AuthContext's actorPrincipalId.
1515
1704
  // The JWT-verified principal is used as the sender identity through
1516
1705
  // the same trust resolution pipeline that channel ingress uses.
1517
- if (authContext.actorPrincipalId) {
1706
+ if (actorPrincipalId) {
1518
1707
  // Dev bypass (HTTP auth disabled): the synthetic "dev-bypass" principal
1519
1708
  // won't match any guardian binding. Resolve from the local guardian
1520
1709
  // binding instead, which produces the correct guardian trust context.
1521
- if (isHttpAuthDisabled() && authContext.actorPrincipalId === "dev-bypass") {
1710
+ if (isHttpAuthDisabled() && actorPrincipalId === "dev-bypass") {
1522
1711
  conversation.setTrustContext(resolveLocalTrustContext(sourceChannel));
1523
1712
  } else {
1524
1713
  const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
@@ -1526,24 +1715,24 @@ export async function handleSendMessage(
1526
1715
  assistantId,
1527
1716
  sourceChannel: "vellum",
1528
1717
  conversationExternalId: "local",
1529
- actorExternalId: authContext.actorPrincipalId,
1718
+ actorExternalId: actorPrincipalId,
1530
1719
  });
1531
1720
  if (trustCtx.trustClass === "unknown") {
1532
1721
  // Attempt to heal guardian binding drift: after a DB reset the
1533
1722
  // guardian binding gets a new vellum-principal-* UUID while the
1534
1723
  // client still holds a valid JWT with the old one. The signing
1535
1724
  // key survives the reset, so the JWT is authentic — just stale.
1536
- const healed = healGuardianBindingDrift(authContext.actorPrincipalId);
1725
+ const healed = healGuardianBindingDrift(actorPrincipalId);
1537
1726
  if (healed) {
1538
1727
  trustCtx = resolveTrustContext({
1539
1728
  assistantId,
1540
1729
  sourceChannel: "vellum",
1541
1730
  conversationExternalId: "local",
1542
- actorExternalId: authContext.actorPrincipalId,
1731
+ actorExternalId: actorPrincipalId,
1543
1732
  });
1544
1733
  log.info(
1545
1734
  {
1546
- actorPrincipalId: authContext.actorPrincipalId,
1735
+ actorPrincipalId: actorPrincipalId,
1547
1736
  trustClass: trustCtx.trustClass,
1548
1737
  },
1549
1738
  "Trust re-resolved after guardian binding drift heal",
@@ -1551,10 +1740,10 @@ export async function handleSendMessage(
1551
1740
  } else {
1552
1741
  log.warn(
1553
1742
  {
1554
- actorPrincipalId: authContext.actorPrincipalId,
1743
+ actorPrincipalId: actorPrincipalId,
1555
1744
  sourceChannel,
1556
1745
  trustClass: trustCtx.trustClass,
1557
- principalType: authContext.principalType,
1746
+ principalType: principalType,
1558
1747
  },
1559
1748
  "JWT-verified actor resolved to unknown trust class — possible guardian binding drift (e.g. DB reset without re-bootstrap)",
1560
1749
  );
@@ -1593,17 +1782,19 @@ export async function handleSendMessage(
1593
1782
  conversation.setHostBashProxy(undefined);
1594
1783
  }
1595
1784
  // Resolve the host_browser sender — registry-routed when the guardian has
1596
- // an active extension connection, SSE hub otherwise. This applies to both
1597
- // chrome-extension and macOS interfaces so that macOS turns can route
1598
- // browser automation through the user's real Chrome session when available.
1599
- const { sender: browserProxySendToClient, isRegistryRouted } =
1600
- resolveHostBrowserSender(
1601
- conversation,
1602
- mapping.conversationId,
1603
- authContext,
1604
- onEvent,
1605
- sourceInterface,
1606
- );
1785
+ // an active WS extension connection, SSE hub when a chrome-extension is
1786
+ // connected via SSE (cloud mode), or SSE hub as default for macOS.
1787
+ const {
1788
+ sender: browserProxySendToClient,
1789
+ isRegistryRouted,
1790
+ hasSseExtension,
1791
+ } = resolveHostBrowserSender(
1792
+ conversation,
1793
+ mapping.conversationId,
1794
+ actorPrincipalId,
1795
+ onEvent,
1796
+ sourceInterface,
1797
+ );
1607
1798
 
1608
1799
  // Stash the registry-routed sender on the conversation so queue-drain
1609
1800
  // restores (which run outside of conversation-routes.ts and only have
@@ -1618,17 +1809,18 @@ export async function handleSendMessage(
1618
1809
  conversation.hostBrowserSenderOverride = undefined;
1619
1810
  }
1620
1811
 
1621
- // Provision the host browser proxy. For interfaces that natively support
1622
- // host_browser (chrome-extension), always provision it. For macOS, the
1623
- // static capability check returns false (supportsHostProxy("macos",
1624
- // "host_browser") === false) because the extension isn't guaranteed to be
1625
- // attached — but when the registry confirms an active extension
1626
- // connection, we provision the proxy anyway so macOS turns can drive the
1627
- // user's real Chrome session. When no extension is connected, macOS skips
1628
- // provisioning and browser tools fall through to cdp-inspect/local.
1812
+ // Provision the host browser proxy when a viable transport exists:
1813
+ // - macOS: natively supports host_browser via its own SSE path
1814
+ // - WS registry: extension connected via direct WebSocket
1815
+ // - SSE extension: chrome extension connected via SSE (cloud mode)
1816
+ //
1817
+ // For chrome-extension, require an active transport (WS or SSE). Without
1818
+ // one, host_browser_request frames would be emitted to the SSE hub with
1819
+ // no consumer, causing a 30s proxy timeout instead of failing fast.
1629
1820
  const shouldProvisionBrowserProxy =
1630
- supportsHostProxy(sourceInterface, "host_browser") ||
1631
- (canServiceRegistryBrowser(sourceInterface) && isRegistryRouted);
1821
+ supportsHostProxy(sourceInterface) ||
1822
+ (canServiceRegistryBrowser(sourceInterface) && isRegistryRouted) ||
1823
+ hasSseExtension;
1632
1824
  if (shouldProvisionBrowserProxy) {
1633
1825
  if (!conversation.isProcessing() || !conversation.hostBrowserProxy) {
1634
1826
  const browserProxy = new HostBrowserProxy(
@@ -1649,8 +1841,15 @@ export async function handleSendMessage(
1649
1841
  });
1650
1842
  conversation.setHostFileProxy(fileProxy);
1651
1843
  }
1844
+ if (!conversation.isProcessing() || !conversation.getHostTransferProxy()) {
1845
+ const transferProxy = new HostTransferProxy(onEvent, (requestId) => {
1846
+ pendingInteractions.resolve(requestId);
1847
+ });
1848
+ conversation.setHostTransferProxy(transferProxy);
1849
+ }
1652
1850
  } else if (!conversation.isProcessing()) {
1653
1851
  conversation.setHostFileProxy(undefined);
1852
+ conversation.setHostTransferProxy(undefined);
1654
1853
  }
1655
1854
  if (supportsHostProxy(sourceInterface, "host_cu")) {
1656
1855
  if (!conversation.isProcessing() || !conversation.hostCuProxy) {
@@ -1688,9 +1887,10 @@ export async function handleSendMessage(
1688
1887
  skipProxySenderUpdate: preservingProxies,
1689
1888
  });
1690
1889
  // Re-enable the browser proxy for turns that provisioned one. This covers:
1890
+ // - macOS: always provisioned (SSE sender or registry-routed when extension
1891
+ // is connected)
1691
1892
  // - chrome-extension: natively supports host_browser (non-interactive but
1692
1893
  // has a connected client for host_browser_request events)
1693
- // - macOS with extension: provisioned above when isRegistryRouted is true
1694
1894
  //
1695
1895
  // The helper bypasses the `hasNoClient` gate so chrome-extension turns can
1696
1896
  // drive the browser via CDP without leaking host_bash/host_file tool
@@ -1703,97 +1903,103 @@ export async function handleSendMessage(
1703
1903
 
1704
1904
  // ── Canned first-greeting fast path ──
1705
1905
  // On a completely fresh workspace, skip LLM inference for the macOS
1706
- // wake-up greeting and return a pre-written response. This eliminates
1707
- // 10-30s of inference latency on first boot.
1906
+ // wake-up greeting and return a pre-written response. When onboarding
1907
+ // context is present the greeting is personalized using the selections;
1908
+ // otherwise a generic greeting is served. Both paths are instant.
1708
1909
  if (isWakeUpGreeting(trimmedContent, conversation.getMessages().length)) {
1709
- const cannedGreeting = getCannedFirstGreeting();
1710
- if (cannedGreeting) {
1711
- conversation.processing = true;
1712
- let cleanupDeferred = false;
1713
- try {
1714
- const provenance = provenanceFromTrustContext(
1715
- conversation.trustContext,
1716
- );
1717
- const channelMeta = {
1718
- ...provenance,
1719
- userMessageChannel: sourceChannel,
1720
- assistantMessageChannel: sourceChannel,
1721
- userMessageInterface: sourceInterface,
1722
- assistantMessageInterface: sourceInterface,
1723
- };
1724
-
1725
- const rawContent = content ?? "";
1726
- const attachments = hasAttachments
1727
- ? smDeps.resolveAttachments(attachmentIds)
1728
- : [];
1729
- const userMsg = createUserMessage(rawContent, attachments);
1730
- const persisted = await addMessage(
1731
- mapping.conversationId,
1732
- "user",
1733
- JSON.stringify(userMsg.content),
1734
- channelMeta,
1735
- );
1736
- conversation.getMessages().push(userMsg);
1910
+ const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
1737
1911
 
1738
- setConversationOriginChannelIfUnset(
1739
- mapping.conversationId,
1740
- sourceChannel,
1741
- );
1742
- setConversationOriginInterfaceIfUnset(
1743
- mapping.conversationId,
1744
- sourceInterface,
1745
- );
1912
+ conversation.processing = true;
1913
+ let cleanupDeferred = false;
1914
+ try {
1915
+ const provenance = provenanceFromTrustContext(conversation.trustContext);
1916
+ const channelMeta = {
1917
+ ...provenance,
1918
+ userMessageChannel: sourceChannel,
1919
+ assistantMessageChannel: sourceChannel,
1920
+ userMessageInterface: sourceInterface,
1921
+ assistantMessageInterface: sourceInterface,
1922
+ };
1746
1923
 
1747
- const assistantMsg = createAssistantMessage(cannedGreeting);
1748
- await addMessage(
1749
- mapping.conversationId,
1750
- "assistant",
1751
- JSON.stringify(assistantMsg.content),
1752
- channelMeta,
1753
- );
1754
- conversation.getMessages().push(assistantMsg);
1924
+ const rawContent = content ?? "";
1925
+ const attachments = hasAttachments
1926
+ ? smDeps.resolveAttachments(attachmentIds)
1927
+ : [];
1928
+ const userMsg = createUserMessage(rawContent, attachments);
1929
+ const persisted = await addMessage(
1930
+ mapping.conversationId,
1931
+ "user",
1932
+ JSON.stringify(userMsg.content),
1933
+ channelMeta,
1934
+ );
1935
+ conversation.getMessages().push(userMsg);
1755
1936
 
1756
- const conversationId = mapping.conversationId;
1757
- const response = Response.json(
1758
- { accepted: true, messageId: persisted.id, conversationId },
1759
- { status: 202 },
1760
- );
1937
+ setConversationOriginChannelIfUnset(
1938
+ mapping.conversationId,
1939
+ sourceChannel,
1940
+ );
1941
+ setConversationOriginInterfaceIfUnset(
1942
+ mapping.conversationId,
1943
+ sourceInterface,
1944
+ );
1761
1945
 
1762
- // Defer event publishing to next tick (same pattern as unknown-slash
1763
- // fast path) so the HTTP response reaches the client before SSE
1764
- // events arrive.
1765
- setTimeout(() => {
1766
- onEvent({
1767
- type: "user_message_echo",
1768
- text: rawContent,
1769
- conversationId,
1770
- messageId: persisted.id,
1771
- clientMessageId,
1772
- });
1773
- onEvent({ type: "assistant_text_delta", text: cannedGreeting });
1774
- onEvent({ type: "message_complete", conversationId });
1775
- conversation.processing = false;
1776
- silentlyWithLog(
1777
- conversation.drainQueue(),
1778
- "canned-greeting queue drain",
1779
- );
1780
- }, 0);
1946
+ const conversationId = mapping.conversationId;
1947
+
1948
+ const assistantMsg = createAssistantMessage(cannedGreeting);
1949
+ await addMessage(
1950
+ mapping.conversationId,
1951
+ "assistant",
1952
+ JSON.stringify(assistantMsg.content),
1953
+ channelMeta,
1954
+ );
1955
+ conversation.getMessages().push(assistantMsg);
1956
+
1957
+ const response = {
1958
+ accepted: true,
1959
+ messageId: persisted.id,
1960
+ conversationId,
1961
+ };
1781
1962
 
1782
- log.info(
1783
- { conversationId },
1784
- "Served canned first greeting — skipped LLM inference",
1963
+ setTimeout(() => {
1964
+ onEvent({
1965
+ type: "user_message_echo",
1966
+ text: rawContent,
1967
+ conversationId,
1968
+ messageId: persisted.id,
1969
+ clientMessageId,
1970
+ });
1971
+ onEvent({ type: "assistant_text_delta", text: cannedGreeting });
1972
+ onEvent({ type: "message_complete", conversationId });
1973
+ conversation.processing = false;
1974
+ silentlyWithLog(
1975
+ conversation.drainQueue(),
1976
+ "canned-greeting queue drain",
1785
1977
  );
1786
- cleanupDeferred = true;
1787
- return response;
1788
- } finally {
1789
- if (!cleanupDeferred && conversation.processing) {
1790
- conversation.processing = false;
1791
- silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1978
+
1979
+ if (isFirstOnboarding) {
1980
+ persistOnboardingArtifacts(body.onboarding!);
1792
1981
  }
1982
+ conversation.warmPromptCache();
1983
+ }, 0);
1984
+
1985
+ log.info(
1986
+ { conversationId, personalized: !!body.onboarding },
1987
+ "Served canned first greeting — skipped LLM inference",
1988
+ );
1989
+ cleanupDeferred = true;
1990
+ return response;
1991
+ } finally {
1992
+ if (!cleanupDeferred && conversation.processing) {
1993
+ conversation.processing = false;
1994
+ silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1793
1995
  }
1794
1996
  }
1795
1997
  }
1796
1998
 
1999
+ if (isFirstOnboarding) {
2000
+ persistOnboardingArtifacts(body.onboarding!);
2001
+ }
2002
+
1797
2003
  const attachments = hasAttachments
1798
2004
  ? smDeps.resolveAttachments(attachmentIds)
1799
2005
  : [];
@@ -1828,16 +2034,13 @@ export async function handleSendMessage(
1828
2034
  verifiedActorPrincipalId,
1829
2035
  });
1830
2036
  if (inlineReplyResult.consumed) {
1831
- return Response.json(
1832
- {
1833
- accepted: true,
1834
- conversationId: mapping.conversationId,
1835
- ...(inlineReplyResult.messageId
1836
- ? { messageId: inlineReplyResult.messageId }
1837
- : {}),
1838
- },
1839
- { status: 202 },
1840
- );
2037
+ return {
2038
+ accepted: true,
2039
+ conversationId: mapping.conversationId,
2040
+ ...(inlineReplyResult.messageId
2041
+ ? { messageId: inlineReplyResult.messageId }
2042
+ : {}),
2043
+ };
1841
2044
  }
1842
2045
  } catch (err) {
1843
2046
  log.warn(
@@ -1869,9 +2072,10 @@ export async function handleSendMessage(
1869
2072
  clientMessageId,
1870
2073
  );
1871
2074
  if (enqueueResult.rejected) {
1872
- return Response.json(
1873
- { accepted: false, error: "queue_full" },
1874
- { status: 429 },
2075
+ return new RouteResponse(
2076
+ JSON.stringify({ accepted: false, error: "queue_full" }),
2077
+ { "content-type": "application/json" },
2078
+ 429,
1875
2079
  );
1876
2080
  }
1877
2081
 
@@ -1920,10 +2124,11 @@ export async function handleSendMessage(
1920
2124
  );
1921
2125
  }
1922
2126
 
1923
- return Response.json(
1924
- { accepted: true, queued: true, conversationId: mapping.conversationId },
1925
- { status: 202 },
1926
- );
2127
+ return {
2128
+ accepted: true,
2129
+ queued: true,
2130
+ conversationId: mapping.conversationId,
2131
+ };
1927
2132
  }
1928
2133
 
1929
2134
  // Auto-deny pending confirmations for idle conversations. The legacy
@@ -2044,14 +2249,11 @@ export async function handleSendMessage(
2044
2249
  ? await buildModelInfoEvent()
2045
2250
  : null;
2046
2251
 
2047
- const response = Response.json(
2048
- {
2049
- accepted: true,
2050
- messageId: persisted.id,
2051
- conversationId: mapping.conversationId,
2052
- },
2053
- { status: 202 },
2054
- );
2252
+ const response = {
2253
+ accepted: true,
2254
+ messageId: persisted.id,
2255
+ conversationId: mapping.conversationId,
2256
+ };
2055
2257
 
2056
2258
  // Defer event publishing to next tick so the HTTP response reaches the
2057
2259
  // client first. This ensures the client's serverToLocalConversationMap is
@@ -2165,14 +2367,11 @@ export async function handleSendMessage(
2165
2367
  }
2166
2368
  })();
2167
2369
 
2168
- return Response.json(
2169
- {
2170
- accepted: true,
2171
- messageId: persisted.id,
2172
- conversationId,
2173
- },
2174
- { status: 202 },
2175
- );
2370
+ return {
2371
+ accepted: true,
2372
+ messageId: persisted.id,
2373
+ conversationId,
2374
+ };
2176
2375
  }
2177
2376
 
2178
2377
  const resolvedContent = slashResult.content;
@@ -2212,10 +2411,11 @@ export async function handleSendMessage(
2212
2411
  );
2213
2412
  });
2214
2413
 
2215
- return Response.json(
2216
- { accepted: true, messageId, conversationId: mapping.conversationId },
2217
- { status: 202 },
2218
- );
2414
+ return {
2415
+ accepted: true,
2416
+ messageId,
2417
+ conversationId: mapping.conversationId,
2418
+ };
2219
2419
  }
2220
2420
 
2221
2421
  function escapeXmlContent(text: string): string {
@@ -2251,11 +2451,15 @@ async function generateLlmSuggestion(
2251
2451
  `Write the user's next reply, focusing on the LAST question or call-to-action in the assistant message. Keep it short (under 15 words), casual, and in the user's voice. Respond in this exact format:\n\n` +
2252
2452
  `<reply>YOUR_REPLY_HERE</reply>`;
2253
2453
 
2454
+ // Single user message only — no assistant-role prefill. Anthropic
2455
+ // rejects assistant prefill whenever the request triggers extended
2456
+ // thinking (e.g. Opus 4.x at `effort: "xhigh"`), and the call-site
2457
+ // config is user-controlled, so we can't statically guarantee a
2458
+ // prefill-safe model. Keep `stop_sequences: ["</reply>"]` as an
2459
+ // early-termination hint; the parser below handles both tagged and
2460
+ // untagged responses so untagged "casual answer" replies still work.
2254
2461
  const response = await provider.sendMessage(
2255
- [
2256
- { role: "user", content: [{ type: "text", text: userPrompt }] },
2257
- { role: "assistant", content: [{ type: "text", text: "<reply>" }] },
2258
- ],
2462
+ [{ role: "user", content: [{ type: "text", text: userPrompt }] }],
2259
2463
  [], // no tools
2260
2464
  systemPrompt,
2261
2465
  {
@@ -2270,7 +2474,12 @@ async function generateLlmSuggestion(
2270
2474
 
2271
2475
  const textBlock = response.content.find((b) => b.type === "text");
2272
2476
  const raw = textBlock && "text" in textBlock ? textBlock.text : "";
2273
- const stripped = raw
2477
+ // Prefer the content inside <reply>…</reply> when the model honors the
2478
+ // tag format. If the response has no tags, fall back to the raw text —
2479
+ // a plain "Sure, tomorrow works" without tags is still a valid chip.
2480
+ const tagMatch = raw.match(/<reply>([\s\S]*?)(?:<\/reply>|$)/i);
2481
+ const extracted = tagMatch ? tagMatch[1] : raw;
2482
+ const stripped = extracted
2274
2483
  .replace(/<\/?reply>/gi, "")
2275
2484
  .replace(/^["'`]+|["'`]+$/g, "")
2276
2485
  .trim();
@@ -2293,54 +2502,39 @@ async function generateLlmSuggestion(
2293
2502
  }
2294
2503
 
2295
2504
  export async function handleGetSuggestion(
2296
- url: URL,
2505
+ { queryParams }: RouteHandlerArgs,
2297
2506
  deps: {
2298
2507
  suggestionCache: Map<string, string>;
2299
2508
  suggestionInFlight: Map<string, Promise<string | null>>;
2300
2509
  },
2301
- ): Promise<Response> {
2302
- const conversationKey = url.searchParams.get("conversationKey");
2510
+ ): Promise<Record<string, unknown>> {
2511
+ const noSuggestion = {
2512
+ suggestion: null,
2513
+ messageId: null,
2514
+ source: "none" as const,
2515
+ };
2516
+
2517
+ const conversationKey = queryParams?.conversationKey;
2303
2518
  if (!conversationKey) {
2304
- return httpError(
2305
- "BAD_REQUEST",
2306
- "conversationKey query parameter is required",
2307
- 400,
2308
- );
2519
+ throw new BadRequestError("conversationKey query parameter is required");
2309
2520
  }
2310
2521
 
2311
2522
  const mapping = getConversationByKey(conversationKey);
2312
- if (!mapping) {
2313
- return Response.json({
2314
- suggestion: null,
2315
- messageId: null,
2316
- source: "none" as const,
2317
- });
2318
- }
2523
+ if (!mapping) return noSuggestion;
2319
2524
 
2320
2525
  const rawMessages = getMessages(mapping.conversationId);
2321
- if (rawMessages.length === 0) {
2322
- return Response.json({
2323
- suggestion: null,
2324
- messageId: null,
2325
- source: "none" as const,
2326
- });
2327
- }
2526
+ if (rawMessages.length === 0) return noSuggestion;
2328
2527
 
2329
2528
  // Staleness check: compare requested messageId against the latest
2330
2529
  // assistant message BEFORE filtering by text content. This ensures
2331
2530
  // that a newer tool-only assistant turn (empty text) still causes
2332
2531
  // older messageId requests to be correctly marked as stale.
2333
- const requestedMessageId = url.searchParams.get("messageId");
2532
+ const requestedMessageId = queryParams?.messageId;
2334
2533
  if (requestedMessageId) {
2335
2534
  for (let i = rawMessages.length - 1; i >= 0; i--) {
2336
2535
  if (rawMessages[i].role === "assistant") {
2337
2536
  if (rawMessages[i].id !== requestedMessageId) {
2338
- return Response.json({
2339
- suggestion: null,
2340
- messageId: null,
2341
- source: "none" as const,
2342
- stale: true,
2343
- });
2537
+ return { ...noSuggestion, stale: true };
2344
2538
  }
2345
2539
  break;
2346
2540
  }
@@ -2368,22 +2562,13 @@ export async function handleGetSuggestion(
2368
2562
  // If a messageId was requested and the first text-bearing assistant
2369
2563
  // message is a *different* message, the request is stale.
2370
2564
  if (requestedMessageId && msg.id !== requestedMessageId) {
2371
- return Response.json({
2372
- suggestion: null,
2373
- messageId: null,
2374
- source: "none" as const,
2375
- stale: true,
2376
- });
2565
+ return { ...noSuggestion, stale: true };
2377
2566
  }
2378
2567
 
2379
2568
  // Return cached suggestion if we already generated one for this message
2380
2569
  const cached = suggestionCache.get(msg.id);
2381
2570
  if (cached !== undefined) {
2382
- return Response.json({
2383
- suggestion: cached,
2384
- messageId: msg.id,
2385
- source: "llm" as const,
2386
- });
2571
+ return { suggestion: cached, messageId: msg.id, source: "llm" as const };
2387
2572
  }
2388
2573
 
2389
2574
  // Find the most recent user message preceding this assistant turn so the
@@ -2427,11 +2612,11 @@ export async function handleGetSuggestion(
2427
2612
  }
2428
2613
  suggestionCache.set(msg.id, llmSuggestion);
2429
2614
 
2430
- return Response.json({
2615
+ return {
2431
2616
  suggestion: llmSuggestion,
2432
2617
  messageId: msg.id,
2433
2618
  source: "llm" as const,
2434
- });
2619
+ };
2435
2620
  }
2436
2621
  } catch (err) {
2437
2622
  suggestionInFlight.delete(msg.id);
@@ -2447,18 +2632,10 @@ export async function handleGetSuggestion(
2447
2632
  );
2448
2633
  }
2449
2634
 
2450
- return Response.json({
2451
- suggestion: null,
2452
- messageId: null,
2453
- source: "none" as const,
2454
- });
2635
+ return noSuggestion;
2455
2636
  }
2456
2637
 
2457
- return Response.json({
2458
- suggestion: null,
2459
- messageId: null,
2460
- source: "none" as const,
2461
- });
2638
+ return noSuggestion;
2462
2639
  }
2463
2640
 
2464
2641
  /**
@@ -2467,19 +2644,17 @@ export async function handleGetSuggestion(
2467
2644
  * Full-text search across all conversations (message content + titles).
2468
2645
  * Returns ranked results grouped by conversation, each with matching message excerpts.
2469
2646
  */
2470
- function handleSearchConversations(url: URL): Response {
2471
- const query = url.searchParams.get("q") ?? "";
2647
+ function handleSearchConversations({
2648
+ queryParams,
2649
+ }: RouteHandlerArgs): Record<string, unknown> {
2650
+ const query = queryParams?.q ?? "";
2472
2651
  if (!query.trim()) {
2473
- return httpError("BAD_REQUEST", "q query parameter is required", 400);
2652
+ throw new BadRequestError("q query parameter is required");
2474
2653
  }
2475
2654
 
2476
- const limit = url.searchParams.has("limit")
2477
- ? Number(url.searchParams.get("limit"))
2478
- : undefined;
2479
- const maxMessagesPerConversation = url.searchParams.has(
2480
- "maxMessagesPerConversation",
2481
- )
2482
- ? Number(url.searchParams.get("maxMessagesPerConversation"))
2655
+ const limit = queryParams?.limit ? Number(queryParams.limit) : undefined;
2656
+ const maxMessagesPerConversation = queryParams?.maxMessagesPerConversation
2657
+ ? Number(queryParams.maxMessagesPerConversation)
2483
2658
  : undefined;
2484
2659
 
2485
2660
  const results = searchConversations(query, {
@@ -2490,105 +2665,125 @@ function handleSearchConversations(url: URL): Response {
2490
2665
  : {}),
2491
2666
  });
2492
2667
 
2493
- return Response.json({ query, results });
2668
+ return { query, results };
2669
+ }
2670
+
2671
+ // ---------------------------------------------------------------------------
2672
+ // Module-level state
2673
+ // ---------------------------------------------------------------------------
2674
+
2675
+ const suggestionCache = new Map<string, string>();
2676
+ const suggestionInFlight = new Map<string, Promise<string | null>>();
2677
+
2678
+ function resolveAttachments(attachmentIds: string[]) {
2679
+ const resolved = getAttachmentsByIds(attachmentIds, {
2680
+ hydrateFileData: true,
2681
+ });
2682
+ const sourcePaths = getSourcePathsForAttachments(attachmentIds);
2683
+ return resolved.map((a) => ({
2684
+ id: a.id,
2685
+ filename: a.originalFilename,
2686
+ mimeType: a.mimeType,
2687
+ data: a.dataBase64,
2688
+ ...(sourcePaths.has(a.id) ? { filePath: sourcePaths.get(a.id) } : {}),
2689
+ }));
2494
2690
  }
2495
2691
 
2496
2692
  // ---------------------------------------------------------------------------
2497
2693
  // Route definitions
2498
2694
  // ---------------------------------------------------------------------------
2499
2695
 
2500
- export function conversationRouteDefinitions(deps: {
2501
- interfacesDir: string | null;
2502
- sendMessageDeps?: SendMessageDeps;
2503
- approvalConversationGenerator?: ApprovalConversationGenerator;
2504
- suggestionCache: Map<string, string>;
2505
- suggestionInFlight: Map<string, Promise<string | null>>;
2506
- getHeartbeatService?: () => HeartbeatService | undefined;
2507
- }): RouteDefinition[] {
2508
- return [
2509
- {
2510
- endpoint: "messages",
2511
- method: "GET",
2512
- summary: "List messages",
2513
- description:
2514
- "Return messages for a conversation, including attachments and interface file metadata.",
2515
- tags: ["messages"],
2516
- responseBody: z.object({
2517
- messages: z.array(z.unknown()).describe("Array of message objects"),
2518
- hasMore: z
2519
- .boolean()
2520
- .optional()
2521
- .describe("Whether older messages exist beyond this page"),
2522
- oldestTimestamp: z
2523
- .number()
2524
- .optional()
2525
- .describe(
2526
- "Timestamp of the oldest message in this page (ms since epoch)",
2527
- ),
2528
- oldestMessageId: z
2529
- .string()
2530
- .optional()
2531
- .describe("ID of the oldest message in this page"),
2532
- }),
2533
- handler: ({ url }) => handleListMessages(url, deps.interfacesDir),
2534
- },
2535
- {
2536
- endpoint: "messages",
2537
- method: "POST",
2538
- summary: "Send a message",
2539
- description:
2540
- "Send a user message to a conversation and trigger the assistant response.",
2541
- tags: ["messages"],
2542
- requestBody: z.object({
2543
- conversationKey: z.string().optional(),
2544
- content: z.string().describe("Message text content"),
2545
- attachments: z
2546
- .array(z.unknown())
2547
- .describe("Optional file attachments")
2548
- .optional(),
2549
- conversationType: z.string().optional(),
2550
- slashCommand: z.string().optional(),
2551
- }),
2552
- handler: async ({ req, authContext }) =>
2553
- handleSendMessage(
2554
- req,
2555
- {
2556
- sendMessageDeps: deps.sendMessageDeps,
2557
- approvalConversationGenerator: deps.approvalConversationGenerator,
2558
- heartbeatService: deps.getHeartbeatService?.(),
2559
- },
2560
- authContext,
2696
+ export const ROUTES: RouteDefinition[] = [
2697
+ {
2698
+ operationId: "messages_get",
2699
+ endpoint: "messages",
2700
+ method: "GET",
2701
+ summary: "List messages",
2702
+ description:
2703
+ "Return messages for a conversation, including attachments and interface file metadata.",
2704
+ tags: ["messages"],
2705
+ responseBody: z.object({
2706
+ messages: z.array(z.unknown()).describe("Array of message objects"),
2707
+ hasMore: z
2708
+ .boolean()
2709
+ .optional()
2710
+ .describe("Whether older messages exist beyond this page"),
2711
+ oldestTimestamp: z
2712
+ .number()
2713
+ .nullable()
2714
+ .optional()
2715
+ .describe(
2716
+ "Timestamp of the oldest message in this page (ms since epoch). Null when page=latest is used on an empty conversation.",
2561
2717
  ),
2562
- },
2563
- {
2564
- endpoint: "search",
2565
- method: "GET",
2566
- summary: "Search conversations",
2567
- description: "Full-text search across all conversations.",
2568
- tags: ["conversations"],
2569
- responseBody: z.object({
2570
- query: z.string(),
2571
- results: z.array(z.unknown()),
2718
+ oldestMessageId: z
2719
+ .string()
2720
+ .nullable()
2721
+ .optional()
2722
+ .describe("ID of the oldest message in this page"),
2723
+ }),
2724
+ handler: (args) => handleListMessages(args, getInterfacesDir()),
2725
+ },
2726
+ {
2727
+ operationId: "messages_post",
2728
+ endpoint: "messages",
2729
+ method: "POST",
2730
+ summary: "Send a message",
2731
+ description:
2732
+ "Send a user message to a conversation and trigger the assistant response.",
2733
+ tags: ["messages"],
2734
+ responseStatus: "202",
2735
+ requestBody: z.object({
2736
+ conversationKey: z.string().optional(),
2737
+ content: z.string().describe("Message text content"),
2738
+ attachments: z
2739
+ .array(z.unknown())
2740
+ .describe("Optional file attachments")
2741
+ .optional(),
2742
+ conversationType: z.string().optional(),
2743
+ slashCommand: z.string().optional(),
2744
+ inferenceProfile: z.string().nullable().optional(),
2745
+ riskThreshold: z.enum(VALID_RISK_THRESHOLDS).optional(),
2746
+ }),
2747
+ handler: async (args) =>
2748
+ handleSendMessage(args, {
2749
+ sendMessageDeps: {
2750
+ getOrCreateConversation: getOrCreateConversationInstance,
2751
+ assistantEventHub,
2752
+ resolveAttachments,
2753
+ },
2754
+ approvalConversationGenerator: createApprovalConversationGenerator(),
2572
2755
  }),
2573
- handler: ({ url }) => handleSearchConversations(url),
2574
- },
2575
- {
2576
- endpoint: "suggestion",
2577
- method: "GET",
2578
- summary: "Get reply suggestion",
2579
- description:
2580
- "Return an LLM-generated follow-up suggestion for the most recent assistant message.",
2581
- tags: ["messages"],
2582
- responseBody: z.object({
2583
- suggestion: z.string(),
2584
- messageId: z.string(),
2585
- source: z.string(),
2756
+ },
2757
+ {
2758
+ operationId: "search_get",
2759
+ endpoint: "search",
2760
+ method: "GET",
2761
+ summary: "Search conversations",
2762
+ description: "Full-text search across all conversations.",
2763
+ tags: ["conversations"],
2764
+ responseBody: z.object({
2765
+ query: z.string(),
2766
+ results: z.array(z.unknown()),
2767
+ }),
2768
+ handler: handleSearchConversations,
2769
+ },
2770
+ {
2771
+ operationId: "suggestion_get",
2772
+ endpoint: "suggestion",
2773
+ method: "GET",
2774
+ summary: "Get reply suggestion",
2775
+ description:
2776
+ "Return an LLM-generated follow-up suggestion for the most recent assistant message.",
2777
+ tags: ["messages"],
2778
+ responseBody: z.object({
2779
+ suggestion: z.string(),
2780
+ messageId: z.string(),
2781
+ source: z.string(),
2782
+ }),
2783
+ handler: async (args) =>
2784
+ handleGetSuggestion(args, {
2785
+ suggestionCache,
2786
+ suggestionInFlight,
2586
2787
  }),
2587
- handler: async ({ url }) =>
2588
- handleGetSuggestion(url, {
2589
- suggestionCache: deps.suggestionCache,
2590
- suggestionInFlight: deps.suggestionInFlight,
2591
- }),
2592
- },
2593
- ];
2594
- }
2788
+ },
2789
+ ];