@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -18,6 +18,9 @@
18
18
  * handles registration, touch (heartbeat), and unregistration (dispose).
19
19
  */
20
20
 
21
+ import { type IntervalHistogram, monitorEventLoopDelay } from "node:perf_hooks";
22
+
23
+ import * as Sentry from "@sentry/node";
21
24
  import { z } from "zod";
22
25
 
23
26
  import type { HostProxyCapability } from "../../channels/types.js";
@@ -44,6 +47,175 @@ const log = getLogger("events-routes");
44
47
  /** Keep-alive comment sent to idle clients every 7 s by default. */
45
48
  const DEFAULT_HEARTBEAT_INTERVAL_MS = 7_000;
46
49
 
50
+ /**
51
+ * Resolution of the event-loop delay histogram, per
52
+ * https://nodejs.org/api/perf_hooks.html#perf_hooksmonitoreventloopdelayoptions.
53
+ * A 20 ms resolution gives sub-tick visibility while keeping overhead near zero.
54
+ */
55
+ const EVENT_LOOP_DELAY_RESOLUTION_MS = 20;
56
+
57
+ /**
58
+ * How often we reset the cumulative event-loop delay histogram so subsequent
59
+ * percentile snapshots reflect recent behavior rather than the entire process
60
+ * lifetime. Matches the default window used by `@fastify/under-pressure` and
61
+ * `prom-client` for runtime-pressure metrics.
62
+ */
63
+ const EVENT_LOOP_DELAY_RESET_INTERVAL_MS = 60_000;
64
+
65
+ let eventLoopDelay: IntervalHistogram | null = null;
66
+ let eventLoopResetTimer: ReturnType<typeof setInterval> | null = null;
67
+
68
+ /**
69
+ * Lazily start a cumulative event-loop delay histogram on the first SSE
70
+ * subscriber, and schedule a periodic reset so percentile readings stay
71
+ * meaningful across long-lived daemon processes.
72
+ *
73
+ * Guarded with try/catch because `node:perf_hooks.monitorEventLoopDelay`
74
+ * was a stub in some older Bun versions; if the runtime ever regresses,
75
+ * we still emit the shed log + Sentry capture without lag stats rather
76
+ * than crashing the SSE handler.
77
+ */
78
+ function ensureEventLoopDelayMonitorStarted(): void {
79
+ if (eventLoopDelay !== null) return;
80
+ try {
81
+ const histogram = monitorEventLoopDelay({
82
+ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS,
83
+ });
84
+ histogram.enable();
85
+ eventLoopDelay = histogram;
86
+ eventLoopResetTimer = setInterval(() => {
87
+ try {
88
+ histogram.reset();
89
+ } catch {
90
+ if (eventLoopResetTimer) {
91
+ clearInterval(eventLoopResetTimer);
92
+ eventLoopResetTimer = null;
93
+ }
94
+ }
95
+ }, EVENT_LOOP_DELAY_RESET_INTERVAL_MS);
96
+ eventLoopResetTimer.unref?.();
97
+ } catch (err) {
98
+ log.warn({ err }, "failed to start event-loop delay monitor");
99
+ eventLoopDelay = null;
100
+ }
101
+ }
102
+
103
+ export interface EventLoopDelaySnapshot {
104
+ mean_ms: number | null;
105
+ p99_ms: number | null;
106
+ max_ms: number | null;
107
+ }
108
+
109
+ function nsToMs(ns: number): number | null {
110
+ if (!Number.isFinite(ns)) return null;
111
+ // Round to the nearest microsecond, then express in ms (3 decimal places).
112
+ return Math.round(ns / 1e3) / 1e3;
113
+ }
114
+
115
+ function sampleEventLoopDelay(): EventLoopDelaySnapshot {
116
+ const histogram = eventLoopDelay;
117
+ if (histogram === null) {
118
+ return { mean_ms: null, p99_ms: null, max_ms: null };
119
+ }
120
+ try {
121
+ return {
122
+ mean_ms: nsToMs(histogram.mean),
123
+ p99_ms: nsToMs(histogram.percentile(99)),
124
+ max_ms: nsToMs(histogram.max),
125
+ };
126
+ } catch {
127
+ return { mean_ms: null, p99_ms: null, max_ms: null };
128
+ }
129
+ }
130
+
131
+ export interface SseSubscriberInstrumentation {
132
+ subscribedAtMs: number;
133
+ eventsDelivered: number;
134
+ heartbeatsSent: number;
135
+ clientId: string | null;
136
+ interfaceId: string | null;
137
+ conversationKey: string | null;
138
+ }
139
+
140
+ export type SseShedReason = "callback_backpressure" | "heartbeat_backpressure";
141
+
142
+ export type SseShedReporter = (
143
+ reason: SseShedReason,
144
+ inst: SseSubscriberInstrumentation,
145
+ ) => void;
146
+
147
+ /**
148
+ * Build the structured payload sent to Sentry when an SSE subscriber is
149
+ * shed under backpressure.
150
+ *
151
+ * The conversation key is deliberately excluded: for channel-backed
152
+ * conversations (WhatsApp, Telegram, etc.) the key embeds external
153
+ * identifiers — phone numbers, chat IDs — and Sentry contexts are not
154
+ * run through the PII redactor in `instrument.ts` (only
155
+ * `exception.values`, `breadcrumbs`, and `extra` are). Correlation
156
+ * with the client-side `sse_watchdog_fired` event is achieved via the
157
+ * `client_id` tag + timestamp instead.
158
+ */
159
+ export function buildSseShedSentryContext(
160
+ reason: SseShedReason,
161
+ inst: SseSubscriberInstrumentation,
162
+ elDelay: EventLoopDelaySnapshot,
163
+ nowMs: number,
164
+ ): Record<string, unknown> {
165
+ return {
166
+ reason,
167
+ subscription_age_ms: nowMs - inst.subscribedAtMs,
168
+ events_delivered: inst.eventsDelivered,
169
+ heartbeats_sent: inst.heartbeatsSent,
170
+ client_id: inst.clientId,
171
+ interface_id: inst.interfaceId,
172
+ event_loop_delay_mean_ms: elDelay.mean_ms,
173
+ event_loop_delay_p99_ms: elDelay.p99_ms,
174
+ event_loop_delay_max_ms: elDelay.max_ms,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Report a backpressure-shed event from an SSE subscriber to logs and Sentry.
180
+ *
181
+ * SSE subscribers are shed when `controller.desiredSize <= 0`: the consumer
182
+ * has stopped reading and the stream's bounded queue is full. From the
183
+ * daemon's side this looks identical to a hung client — and the visible
184
+ * symptom on the client side is the 45 s idle-watchdog firing (Sentry
185
+ * issue `sse_watchdog_fired`). Surfacing the shed lets us time-correlate
186
+ * the two sides and attribute stalls to either backpressure or another
187
+ * cause (network drop, event-loop starvation, etc.).
188
+ *
189
+ * The Sentry call uses level="warning" intentionally: a shed is a
190
+ * saturation event, not an internal error.
191
+ */
192
+ const defaultSseShedReporter: SseShedReporter = (reason, inst) => {
193
+ const elDelay = sampleEventLoopDelay();
194
+ const sentryContext = buildSseShedSentryContext(
195
+ reason,
196
+ inst,
197
+ elDelay,
198
+ Date.now(),
199
+ );
200
+ log.warn(
201
+ { ...sentryContext, conversation_key: inst.conversationKey },
202
+ "sse subscriber shed under backpressure",
203
+ );
204
+
205
+ try {
206
+ Sentry.withScope((scope) => {
207
+ scope.setLevel("warning");
208
+ scope.setTag("sse_shed_reason", reason);
209
+ if (inst.clientId) scope.setTag("client_id", inst.clientId);
210
+ if (inst.interfaceId) scope.setTag("interface_id", inst.interfaceId);
211
+ scope.setContext("sse_shed", sentryContext);
212
+ Sentry.captureMessage(`sse_subscriber_shed:${reason}`);
213
+ });
214
+ } catch {
215
+ // Never let a telemetry failure break the SSE path.
216
+ }
217
+ };
218
+
47
219
  /**
48
220
  * Stream assistant events as Server-Sent Events.
49
221
  *
@@ -63,12 +235,15 @@ const DEFAULT_HEARTBEAT_INTERVAL_MS = 7_000;
63
235
  * Options (for testing):
64
236
  * hub -- override the event hub (defaults to process singleton).
65
237
  * heartbeatIntervalMs -- how often to emit keep-alive comments (default 7 s).
238
+ * shedReporter -- override the callback invoked when a subscriber is shed
239
+ * under backpressure (defaults to log + Sentry capture).
66
240
  */
67
241
  export function handleSubscribeAssistantEvents(
68
242
  args: RouteHandlerArgs,
69
243
  options?: {
70
244
  hub?: AssistantEventHub;
71
245
  heartbeatIntervalMs?: number;
246
+ shedReporter?: SseShedReporter;
72
247
  },
73
248
  ): ReadableStream<Uint8Array> {
74
249
  const { queryParams, headers, abortSignal } = args;
@@ -108,6 +283,7 @@ export function handleSubscribeAssistantEvents(
108
283
  const hub = options?.hub ?? assistantEventHub;
109
284
  const heartbeatIntervalMs =
110
285
  options?.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
286
+ const shedReporter = options?.shedReporter ?? defaultSseShedReporter;
111
287
 
112
288
  const ALL_CAPABILITIES: HostProxyCapability[] = [
113
289
  "host_bash",
@@ -134,6 +310,17 @@ export function handleSubscribeAssistantEvents(
134
310
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
135
311
  let sub!: AssistantEventSubscription;
136
312
 
313
+ const instrumentation: SseSubscriberInstrumentation = {
314
+ subscribedAtMs: Date.now(),
315
+ eventsDelivered: 0,
316
+ heartbeatsSent: 0,
317
+ clientId,
318
+ interfaceId,
319
+ conversationKey: conversationKey ?? null,
320
+ };
321
+
322
+ ensureEventLoopDelayMonitorStarted();
323
+
137
324
  function cleanup() {
138
325
  if (heartbeatTimer) {
139
326
  clearInterval(heartbeatTimer);
@@ -151,11 +338,13 @@ export function handleSubscribeAssistantEvents(
151
338
  if (!controller) return;
152
339
  try {
153
340
  if (controller.desiredSize != null && controller.desiredSize <= 0) {
341
+ shedReporter("callback_backpressure", instrumentation);
154
342
  sub.dispose();
155
343
  cleanup();
156
344
  return;
157
345
  }
158
346
  controller.enqueue(encoder.encode(formatSseFrame(event)));
347
+ instrumentation.eventsDelivered += 1;
159
348
  } catch {
160
349
  sub.dispose();
161
350
  cleanup();
@@ -205,10 +394,12 @@ export function handleSubscribeAssistantEvents(
205
394
  }
206
395
 
207
396
  controller.enqueue(encoder.encode(formatSseHeartbeat()));
397
+ instrumentation.heartbeatsSent += 1;
208
398
 
209
399
  heartbeatTimer = setInterval(() => {
210
400
  try {
211
401
  if (controller.desiredSize != null && controller.desiredSize <= 0) {
402
+ shedReporter("heartbeat_backpressure", instrumentation);
212
403
  sub.dispose();
213
404
  cleanup();
214
405
  return;
@@ -217,6 +408,7 @@ export function handleSubscribeAssistantEvents(
217
408
  hub.touchClient(clientId);
218
409
  }
219
410
  controller.enqueue(encoder.encode(formatSseHeartbeat()));
411
+ instrumentation.heartbeatsSent += 1;
220
412
  } catch {
221
413
  sub.dispose();
222
414
  cleanup();
@@ -26,11 +26,9 @@ import {
26
26
  type FeedItem,
27
27
  feedItemSchema,
28
28
  type FeedItemStatus,
29
- lowPriorityCollapsedSchema,
30
29
  suggestedPromptSchema,
31
30
  } from "../../home/feed-types.js";
32
31
  import { patchFeedItemStatus, readHomeFeed } from "../../home/feed-writer.js";
33
- import { runRollupProducer } from "../../home/rollup-producer.js";
34
32
  import { getSuggestedPrompts } from "../../home/suggested-prompts.js";
35
33
  import {
36
34
  addMessage,
@@ -42,26 +40,6 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
42
40
 
43
41
  const log = getLogger("home-feed-routes");
44
42
 
45
- /**
46
- * Debounce window for the on-visit rollup refresh. A GET on the feed
47
- * route fires the rollup producer fire-and-forget at most once per
48
- * window — repeat GETs within this interval (e.g. from a client that
49
- * polls aggressively, or from multiple panels opening in rapid
50
- * succession) skip the trigger and just return the cached feed.
51
- */
52
- const ON_VISIT_REFRESH_DEBOUNCE_MS = 10 * 60 * 1000;
53
-
54
- let lastOnVisitRefreshAt = 0;
55
-
56
- /**
57
- * Reset the on-visit debounce gate. Test-only — production callers
58
- * should never touch this. Exported with an underscore-prefixed name
59
- * so lint rules can flag misuse.
60
- */
61
- export function __resetOnVisitRefreshStateForTests(): void {
62
- lastOnVisitRefreshAt = 0;
63
- }
64
-
65
43
  // ---------------------------------------------------------------------------
66
44
  // Response / request schemas
67
45
  // ---------------------------------------------------------------------------
@@ -77,7 +55,6 @@ const getHomeFeedResponseSchema = z.object({
77
55
  updatedAt: z.string(),
78
56
  contextBanner: contextBannerSchema,
79
57
  suggestedPrompts: z.array(suggestedPromptSchema),
80
- lowPriorityCollapsed: lowPriorityCollapsedSchema,
81
58
  });
82
59
 
83
60
  const patchFeedItemRequestSchema = z.object({
@@ -139,15 +116,11 @@ export async function handleGetHomeFeed({
139
116
  const timeAwaySeconds = parsed;
140
117
 
141
118
  const feed = readHomeFeed();
142
- const filtered = feed.items.filter((item) => {
143
- if (item.minTimeAway === undefined) return true;
144
- return item.minTimeAway <= timeAwaySeconds;
145
- });
146
-
147
- const LOW_PRIORITY_THRESHOLD = 30;
148
- const lowPriorityItems = filtered.filter(
149
- (item) => item.priority < LOW_PRIORITY_THRESHOLD,
150
- );
119
+ // v2 schema dropped per-item `minTimeAway` gating; surface every item
120
+ // and let the client decide what to render based on its own
121
+ // session state. `timeAwaySeconds` survives only to feed the
122
+ // context-banner relative-time label.
123
+ const filtered = feed.items;
151
124
 
152
125
  const now = new Date();
153
126
  const contextBanner = {
@@ -156,11 +129,6 @@ export async function handleGetHomeFeed({
156
129
  newCount: filtered.filter((i) => i.status === "new").length,
157
130
  };
158
131
 
159
- const lowPriorityCollapsed = {
160
- count: lowPriorityItems.length,
161
- itemIds: lowPriorityItems.map((item) => item.id),
162
- };
163
-
164
132
  const suggestedPrompts = await getSuggestedPrompts();
165
133
 
166
134
  log.debug(
@@ -168,60 +136,20 @@ export async function handleGetHomeFeed({
168
136
  timeAwayBucket: timeAwayBucket(timeAwaySeconds),
169
137
  totalItems: feed.items.length,
170
138
  filteredItems: filtered.length,
171
- lowPriorityCount: lowPriorityItems.length,
172
139
  newCount: contextBanner.newCount,
173
140
  suggestedPromptsCount: suggestedPrompts.length,
174
141
  },
175
142
  "GET /v1/home/feed",
176
143
  );
177
144
 
178
- maybeTriggerOnVisitRollupRefresh(now);
179
-
180
145
  return {
181
146
  items: filtered,
182
147
  updatedAt: feed.updatedAt,
183
148
  contextBanner,
184
149
  suggestedPrompts,
185
- lowPriorityCollapsed,
186
150
  };
187
151
  }
188
152
 
189
- function maybeTriggerOnVisitRollupRefresh(now: Date): void {
190
- const nowMs = now.getTime();
191
- if (nowMs - lastOnVisitRefreshAt < ON_VISIT_REFRESH_DEBOUNCE_MS) return;
192
- const previousRefreshAt = lastOnVisitRefreshAt;
193
- lastOnVisitRefreshAt = nowMs;
194
- void runRollupProducer(now)
195
- .then((result) => {
196
- const skippedBeforeLLM =
197
- result.skippedReason === "no_provider" ||
198
- result.skippedReason === "no_actions" ||
199
- result.skippedReason === "in_flight";
200
- if (skippedBeforeLLM) {
201
- if (lastOnVisitRefreshAt === nowMs) {
202
- lastOnVisitRefreshAt = previousRefreshAt;
203
- }
204
- log.debug(
205
- { skippedReason: result.skippedReason },
206
- "On-visit rollup refresh skipped; debounce gate rolled back",
207
- );
208
- } else if (result.skippedReason !== null) {
209
- log.debug(
210
- { skippedReason: result.skippedReason },
211
- "On-visit rollup refresh skipped",
212
- );
213
- } else {
214
- log.info(
215
- { wroteCount: result.wroteCount },
216
- "On-visit rollup refresh completed",
217
- );
218
- }
219
- })
220
- .catch((err) => {
221
- log.warn({ err }, "On-visit rollup refresh failed");
222
- });
223
- }
224
-
225
153
  export async function handlePatchFeedItem({
226
154
  pathParams = {},
227
155
  body,
@@ -309,7 +237,7 @@ export const ROUTES: RouteDefinition[] = [
309
237
  type: "integer",
310
238
  required: true,
311
239
  description:
312
- "Seconds since the user was last active in the client. Used to filter items with a `minTimeAway` gate and to compute the context-banner relative-time label.",
240
+ "Seconds since the user was last active in the client. Used to compute the context-banner relative-time label.",
313
241
  },
314
242
  ],
315
243
  responseBody: getHomeFeedResponseSchema,
@@ -19,8 +19,13 @@ import type {
19
19
  HostAppControlResultPayload,
20
20
  HostAppControlState,
21
21
  } from "../../daemon/message-types/host-app-control.js";
22
+ import {
23
+ enforceSameActorOrThrow,
24
+ SAME_ACTOR_FORBIDDEN_DESCRIPTION,
25
+ } from "../auth/same-actor.js";
26
+ import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
22
27
  import * as pendingInteractions from "../pending-interactions.js";
23
- import { BadRequestError } from "./errors.js";
28
+ import { BadRequestError, ForbiddenError } from "./errors.js";
24
29
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
25
30
 
26
31
  const VALID_STATES: ReadonlySet<HostAppControlState> = new Set([
@@ -33,7 +38,7 @@ const VALID_STATES: ReadonlySet<HostAppControlState> = new Set([
33
38
  // POST /v1/host-app-control-result
34
39
  // ---------------------------------------------------------------------------
35
40
 
36
- function handleHostAppControlResult({ body }: RouteHandlerArgs) {
41
+ function handleHostAppControlResult({ body, headers }: RouteHandlerArgs) {
37
42
  if (!body || typeof body !== "object") {
38
43
  throw new BadRequestError("Request body is required");
39
44
  }
@@ -72,6 +77,34 @@ function handleHostAppControlResult({ body }: RouteHandlerArgs) {
72
77
  return { accepted: true };
73
78
  }
74
79
 
80
+ // Same-actor binding: when the pending interaction has a targetClientId,
81
+ // validate the submitting client matches and the actor principals align.
82
+ // Mirrors host-browser / host-cu / host-bash result routes.
83
+ if (peeked.targetClientId != null) {
84
+ const headerMap = headers ?? {};
85
+ const submittingClientId =
86
+ headerMap["x-vellum-client-id"]?.trim() || undefined;
87
+ if (!submittingClientId) {
88
+ throw new BadRequestError(
89
+ "x-vellum-client-id header is missing for a targeted host app-control request.",
90
+ );
91
+ }
92
+ if (submittingClientId !== peeked.targetClientId) {
93
+ throw new ForbiddenError(
94
+ `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
95
+ );
96
+ }
97
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
98
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
99
+ );
100
+ enforceSameActorOrThrow({
101
+ sourceActorPrincipalId: submittingActorPrincipalId,
102
+ targetActorPrincipalId: peeked.targetActorPrincipalId,
103
+ targetClientId: peeked.targetClientId,
104
+ op: "host_app_control",
105
+ });
106
+ }
107
+
75
108
  const interaction = pendingInteractions.resolve(requestId)!;
76
109
  const conversation = findConversation(interaction.conversationId);
77
110
  if (!conversation) {
@@ -129,6 +162,15 @@ export const ROUTES: RouteDefinition[] = [
129
162
  responseBody: z.object({
130
163
  accepted: z.boolean(),
131
164
  }),
165
+ additionalResponses: {
166
+ "400": {
167
+ description:
168
+ "x-vellum-client-id header is missing for a targeted host app-control request.",
169
+ },
170
+ "403": {
171
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
172
+ },
173
+ },
132
174
  handler: handleHostAppControlResult,
133
175
  },
134
176
  ];
@@ -10,15 +10,22 @@ import {
10
10
  markTargetInvalidated,
11
11
  publishCdpEvent,
12
12
  } from "../../browser-session/events.js";
13
- import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
13
+ import {
14
+ enforceSameActorOrThrow,
15
+ SAME_ACTOR_FORBIDDEN_DESCRIPTION,
16
+ } from "../auth/same-actor.js";
17
+ import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
14
18
  import * as pendingInteractions from "../pending-interactions.js";
15
- import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
19
+ import {
20
+ BadRequestError,
21
+ ConflictError,
22
+ ForbiddenError,
23
+ NotFoundError,
24
+ } from "./errors.js";
16
25
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
17
26
 
18
27
  /**
19
- * Result of attempting to resolve a host browser result frame. Used by both
20
- * the HTTP endpoint and the WS relay path so they share the same validation
21
- * and resolution semantics.
28
+ * Result of attempting to resolve a host browser result frame.
22
29
  *
23
30
  * Success → the pending interaction was consumed and the conversation was
24
31
  * notified.
@@ -30,8 +37,8 @@ export type HostBrowserResultResolution =
30
37
  | { ok: true }
31
38
  | {
32
39
  ok: false;
33
- code: "BAD_REQUEST" | "NOT_FOUND" | "CONFLICT";
34
- status: 400 | 404 | 409;
40
+ code: "BAD_REQUEST" | "FORBIDDEN" | "NOT_FOUND" | "CONFLICT";
41
+ status: 400 | 403 | 404 | 409;
35
42
  message: string;
36
43
  };
37
44
 
@@ -40,22 +47,26 @@ export type HostBrowserResultResolution =
40
47
  * the pending interaction by requestId, validates its kind is
41
48
  * `host_browser`, and forwards the response to the owning conversation.
42
49
  *
43
- * NOTE: The WebSocket `host_browser_result` frame path does NOT go
44
- * through this function it is handled by `HostBrowserProxy.resolveResult`
45
- * directly, which only consults `pendingInteractions` and does not
46
- * currently perform a kind check. That asymmetry is pre-existing; if
47
- * the WS path is ever opened to less-trusted clients, it should adopt
48
- * the same kind-check guard added here.
50
+ * Same-actor binding: when the pending interaction has a
51
+ * `targetClientId` (set by the proxy at request time when an actor is
52
+ * known), the submitting client must (a) identify itself via
53
+ * `x-vellum-client-id` matching the captured target, and (b) the
54
+ * submitting actor's principal must match the actor captured for that
55
+ * client at registration time. Mirrors the host-cu / host-bash result
56
+ * routes.
49
57
  *
50
58
  * This function does NOT perform auth — callers are expected to have
51
59
  * already authenticated the caller (the HTTP route uses
52
60
  * `requireBoundGuardian`).
53
61
  */
54
- export function resolveHostBrowserResultByRequestId(frame: {
55
- requestId?: unknown;
56
- content?: unknown;
57
- isError?: unknown;
58
- }): HostBrowserResultResolution {
62
+ export function resolveHostBrowserResultByRequestId(
63
+ frame: {
64
+ requestId?: unknown;
65
+ content?: unknown;
66
+ isError?: unknown;
67
+ },
68
+ headers?: Record<string, string | undefined>,
69
+ ): HostBrowserResultResolution {
59
70
  const { requestId, content, isError } = frame;
60
71
 
61
72
  if (!requestId || typeof requestId !== "string") {
@@ -86,11 +97,60 @@ export function resolveHostBrowserResultByRequestId(frame: {
86
97
  };
87
98
  }
88
99
 
100
+ // Validate submitting client matches the targeted client (if any).
101
+ if (peeked.targetClientId != null) {
102
+ const headerMap = headers ?? {};
103
+ const submittingClientId =
104
+ headerMap["x-vellum-client-id"]?.trim() || undefined;
105
+ if (!submittingClientId) {
106
+ return {
107
+ ok: false,
108
+ code: "BAD_REQUEST",
109
+ status: 400,
110
+ message:
111
+ "x-vellum-client-id header is missing for a targeted host browser request.",
112
+ };
113
+ }
114
+ if (submittingClientId !== peeked.targetClientId) {
115
+ return {
116
+ ok: false,
117
+ code: "FORBIDDEN",
118
+ status: 403,
119
+ message: `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
120
+ };
121
+ }
122
+
123
+ // Defense-in-depth: require the submitting actor's principal id to match
124
+ // the actor principal id captured when the target client opened its SSE
125
+ // stream. This prevents a different authenticated user with knowledge of
126
+ // both the requestId and target clientId from submitting a result on
127
+ // behalf of the targeted client.
128
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
129
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
130
+ );
131
+ try {
132
+ enforceSameActorOrThrow({
133
+ sourceActorPrincipalId: submittingActorPrincipalId,
134
+ targetActorPrincipalId: peeked.targetActorPrincipalId,
135
+ targetClientId: peeked.targetClientId,
136
+ op: "host_browser",
137
+ });
138
+ } catch (err) {
139
+ // enforceSameActorOrThrow throws ForbiddenError on rejection.
140
+ return {
141
+ ok: false,
142
+ code: "FORBIDDEN",
143
+ status: 403,
144
+ message: err instanceof Error ? err.message : "Same-actor check failed",
145
+ };
146
+ }
147
+ }
148
+
89
149
  const normalizedContent = typeof content === "string" ? content : "";
90
150
  const normalizedIsError = typeof isError === "boolean" ? isError : false;
91
151
 
92
- const proxy = HostBrowserProxy.instance;
93
- proxy.resolveResult(requestId, {
152
+ const interaction = pendingInteractions.resolve(requestId);
153
+ interaction?.rpcResolve?.({
94
154
  content: normalizedContent,
95
155
  isError: normalizedIsError,
96
156
  });
@@ -188,13 +248,18 @@ export function resolveHostBrowserSessionInvalidated(frame: {
188
248
  // POST /v1/host-browser-result
189
249
  // ---------------------------------------------------------------------------
190
250
 
191
- function handleHostBrowserResult({ body }: RouteHandlerArgs) {
251
+ function handleHostBrowserResult({ body, headers }: RouteHandlerArgs) {
192
252
  if (!body || typeof body !== "object") {
193
253
  throw new BadRequestError("Request body is required");
194
254
  }
195
255
 
196
- const resolution = resolveHostBrowserResultByRequestId(body);
256
+ const resolution = resolveHostBrowserResultByRequestId(
257
+ body,
258
+ headers as Record<string, string | undefined> | undefined,
259
+ );
197
260
  if (!resolution.ok) {
261
+ if (resolution.code === "FORBIDDEN")
262
+ throw new ForbiddenError(resolution.message);
198
263
  if (resolution.code === "NOT_FOUND")
199
264
  throw new NotFoundError(resolution.message);
200
265
  if (resolution.code === "CONFLICT")
@@ -260,6 +325,22 @@ export const ROUTES: RouteDefinition[] = [
260
325
  responseBody: z.object({
261
326
  accepted: z.boolean(),
262
327
  }),
328
+ additionalResponses: {
329
+ "400": {
330
+ description:
331
+ "x-vellum-client-id header is missing for a targeted host browser request.",
332
+ },
333
+ "403": {
334
+ description: SAME_ACTOR_FORBIDDEN_DESCRIPTION,
335
+ },
336
+ "404": {
337
+ description: "No pending browser request for the given requestId.",
338
+ },
339
+ "409": {
340
+ description:
341
+ "Pending interaction kind is not host_browser (mismatched proxy ID space).",
342
+ },
343
+ },
263
344
  handler: handleHostBrowserResult,
264
345
  },
265
346
  {
@@ -35,6 +35,7 @@ export function routeDefinitionsToHTTPRoutes(
35
35
  return routes.map((r) => ({
36
36
  endpoint: r.endpoint,
37
37
  method: r.method,
38
+ operationId: r.operationId,
38
39
  policyKey:
39
40
  r.policyKey ?? r.endpoint.replace(/\/:[^/]+/g, "").replace(/^:/, ""),
40
41
  pathParams: r.pathParams,
@@ -47,6 +48,7 @@ export function routeDefinitionsToHTTPRoutes(
47
48
  responseStatus:
48
49
  typeof r.responseStatus === "string" ? r.responseStatus : undefined,
49
50
  additionalResponses: r.additionalResponses,
51
+ logging: r.logging,
50
52
  handler: async ({ req, url, params, authContext }) => {
51
53
  try {
52
54
  if (r.requireGuardian) {