@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
@@ -1,17 +1,17 @@
1
1
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import {
4
- afterEach,
5
- beforeEach,
6
- describe,
7
- expect,
8
- jest,
9
- mock,
10
- test,
11
- } from "bun:test";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
12
4
 
13
5
  const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
14
6
 
7
+ // Default the warm-pool gate to OPEN for existing tests — they predate
8
+ // the gate and expect heartbeat/filing/etc. to run on every tick. Tests
9
+ // that specifically exercise the gate path override this mock locally.
10
+ mock.module("../runtime/pre-first-message-gate.js", () => ({
11
+ hasReceivedUserMessage: () => true,
12
+ _resetPreFirstMessageGateCacheForTests: () => {},
13
+ }));
14
+
15
15
  // ── Heartbeat run store mock ───────────────────────────────────────
16
16
  const mockInsertPendingHeartbeatRun = mock(() => "mock-run-id");
17
17
  const mockStartHeartbeatRun = mock(() => true);
@@ -34,12 +34,6 @@ mock.module("../heartbeat/heartbeat-run-store.js", () => ({
34
34
  countCompletedHeartbeatRuns: mockCountCompletedHeartbeatRuns,
35
35
  }));
36
36
 
37
- // ── Feed event mock ───────────────────────────────────────────────
38
- const mockEmitFeedEvent = mock(() => Promise.resolve());
39
- mock.module("../home/emit-feed-event.js", () => ({
40
- emitFeedEvent: mockEmitFeedEvent,
41
- }));
42
-
43
37
  // Mock config loader
44
38
  let mockConfig = {
45
39
  heartbeat: {
@@ -383,8 +377,6 @@ describe("HeartbeatService", () => {
383
377
  mockListHeartbeatRuns.mockImplementation(() => []);
384
378
  mockCountCompletedHeartbeatRuns.mockClear();
385
379
  mockCountCompletedHeartbeatRuns.mockImplementation(() => 10);
386
- mockEmitFeedEvent.mockClear();
387
- mockEmitFeedEvent.mockImplementation(() => Promise.resolve());
388
380
 
389
381
  mockConfig = {
390
382
  heartbeat: {
@@ -848,15 +840,11 @@ describe("HeartbeatService", () => {
848
840
  await service.runOnce();
849
841
  await new Promise((resolve) => setTimeout(resolve, 0));
850
842
 
851
- expect(conversationCreatedCalls).toHaveLength(0);
843
+ // The conversation surfaces to the sidebar via the runner's bootstrap
844
+ // callback for *every* heartbeat — "silent OK" means no notification
845
+ // signal is emitted, not that the conversation is hidden.
846
+ expect(conversationCreatedCalls).toHaveLength(1);
852
847
  expect(emittedNotificationSignals).toHaveLength(0);
853
- const successFeedCalls = mockEmitFeedEvent.mock.calls.filter(
854
- (call: unknown[]) => {
855
- const opts = call[0] as { dedupKey?: string };
856
- return opts.dedupKey?.startsWith("heartbeat:ok:");
857
- },
858
- );
859
- expect(successFeedCalls).toHaveLength(0);
860
848
  });
861
849
 
862
850
  test("HEARTBEAT_OK stays silent when earlier content mentions HEARTBEAT_ALERT", async () => {
@@ -897,7 +885,9 @@ describe("HeartbeatService", () => {
897
885
  await service.runOnce();
898
886
  await new Promise((resolve) => setTimeout(resolve, 0));
899
887
 
900
- expect(conversationCreatedCalls).toHaveLength(0);
888
+ // Conversation surfaces via the runner bootstrap, but no notification
889
+ // is emitted since the disposition is OK.
890
+ expect(conversationCreatedCalls).toHaveLength(1);
901
891
  expect(emittedNotificationSignals).toHaveLength(0);
902
892
  });
903
893
 
@@ -1564,7 +1554,10 @@ describe("HeartbeatService", () => {
1564
1554
  await service.runOnce();
1565
1555
  await new Promise((resolve) => setTimeout(resolve, 0));
1566
1556
 
1567
- expect(conversationCreatedCalls).toHaveLength(0);
1557
+ // The bootstrap-time surface fires regardless of CAS (it happens
1558
+ // before completeHeartbeatRun). CAS-false suppresses the alert
1559
+ // notification emit but not the sidebar entry.
1560
+ expect(conversationCreatedCalls).toHaveLength(1);
1568
1561
  expect(emittedNotificationSignals).toHaveLength(0);
1569
1562
  });
1570
1563
 
@@ -1581,15 +1574,6 @@ describe("HeartbeatService", () => {
1581
1574
 
1582
1575
  // completeHeartbeatRun returned false, so alerter should NOT be called
1583
1576
  expect(alerterCalls).toHaveLength(0);
1584
-
1585
- // No failure feed event either
1586
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1587
- (call: unknown[]) => {
1588
- const opts = call[0] as { dedupKey?: string };
1589
- return opts.dedupKey?.startsWith("heartbeat:fail:");
1590
- },
1591
- );
1592
- expect(failCalls).toHaveLength(0);
1593
1577
  });
1594
1578
 
1595
1579
  test("active-hours skip calls skipHeartbeatRun", async () => {
@@ -1738,39 +1722,14 @@ describe("HeartbeatService", () => {
1738
1722
  expect(mockSupersedePendingRun).toHaveBeenCalledWith("mock-run-id");
1739
1723
  });
1740
1724
 
1741
- test("timeout calls completeHeartbeatRun with status timeout", async () => {
1742
- jest.useFakeTimers();
1743
- try {
1744
- let resolveRun: () => void;
1745
- const runPromise = new Promise<void>((r) => {
1746
- resolveRun = r;
1747
- });
1748
-
1749
- const service = createService({
1750
- processMessage: async () => {
1751
- await runPromise;
1752
- return { messageId: "msg-1" };
1753
- },
1754
- });
1755
-
1756
- const runOncePromise = service.runOnce();
1757
- // Advance past the 30-minute timeout
1758
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1759
- await runOncePromise;
1760
-
1761
- expect(mockCompleteHeartbeatRun).toHaveBeenCalledWith("mock-run-id", {
1762
- status: "timeout",
1763
- error: "Heartbeat execution exceeded the 30-minute timeout",
1764
- });
1765
-
1766
- // Clean up — resolve the hanging promise so it doesn't leak
1767
- resolveRun!();
1768
- } finally {
1769
- jest.useRealTimers();
1770
- }
1771
- });
1725
+ // Note: the heartbeat-specific behavior on timeout is the trivial
1726
+ // `errorKind === "timeout" ? "timeout" : "error"` mapping. The runner
1727
+ // owns the actual timeout race (covered in
1728
+ // `background-job-runner.test.ts`), so we don't reproduce its
1729
+ // setTimeout-based timing here fake timers don't reliably propagate
1730
+ // into the runner's module scope across bun versions.
1772
1731
 
1773
- test("failure feed event has urgency high and includes error message", async () => {
1732
+ test("failure emits activity.failed notification with errorKind exception", async () => {
1774
1733
  const service = createService({
1775
1734
  processMessage: async () => {
1776
1735
  throw new Error("web_search outage");
@@ -1779,192 +1738,50 @@ describe("HeartbeatService", () => {
1779
1738
 
1780
1739
  await service.runOnce();
1781
1740
 
1782
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1783
- (call: unknown[]) => {
1784
- const opts = call[0] as { title?: string };
1785
- return opts.title === "Heartbeat Failed";
1786
- },
1787
- );
1788
- expect(failCalls).toHaveLength(1);
1789
- const opts = (failCalls as any[][])[0][0] as {
1790
- urgency?: string;
1791
- summary?: string;
1792
- };
1793
- expect(opts.urgency).toBe("high");
1794
- expect(opts.summary).toContain("web_search outage");
1795
- });
1796
-
1797
- test("CAS false on complete suppresses failure feed event", async () => {
1798
- mockCompleteHeartbeatRun.mockImplementation(() => false);
1799
-
1800
- const service = createService({
1801
- processMessage: async () => {
1802
- throw new Error("some error");
1803
- },
1804
- });
1805
-
1806
- await service.runOnce();
1807
-
1808
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1809
- (call: unknown[]) => {
1810
- const opts = call[0] as { title?: string };
1811
- return opts.title === "Heartbeat Failed";
1812
- },
1741
+ const failSignals = emittedNotificationSignals.filter(
1742
+ (s) => s.sourceEventName === "activity.failed",
1813
1743
  );
1814
- expect(failCalls).toHaveLength(0);
1744
+ expect(failSignals).toHaveLength(1);
1745
+ const signal = failSignals[0]!;
1746
+ expect(signal.contextPayload.jobName).toBe("heartbeat");
1747
+ expect(signal.contextPayload.errorKind).toBe("exception");
1748
+ expect(signal.contextPayload.errorMessage).toContain("web_search outage");
1749
+ expect(signal.attentionHints?.urgency).toBe("medium");
1750
+ expect(signal.attentionHints?.isAsyncBackground).toBe(true);
1815
1751
  });
1816
1752
 
1817
- test("timeout emits feed event with urgency high", async () => {
1818
- jest.useFakeTimers();
1819
- try {
1820
- let resolveRun: () => void;
1821
- const runPromise = new Promise<void>((r) => {
1822
- resolveRun = r;
1823
- });
1824
-
1825
- const service = createService({
1826
- processMessage: async () => {
1827
- await runPromise;
1828
- return { messageId: "msg-1" };
1829
- },
1830
- });
1831
-
1832
- const runOncePromise = service.runOnce();
1833
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1834
- await runOncePromise;
1835
-
1836
- const timeoutCalls = mockEmitFeedEvent.mock.calls.filter(
1837
- (call: unknown[]) => {
1838
- const opts = call[0] as { title?: string };
1839
- return opts.title === "Heartbeat Timed Out";
1840
- },
1841
- );
1842
- expect(timeoutCalls).toHaveLength(1);
1843
- const opts = (timeoutCalls as any[][])[0][0] as {
1844
- urgency?: string;
1845
- };
1846
- expect(opts.urgency).toBe("high");
1847
-
1848
- resolveRun!();
1849
- } finally {
1850
- jest.useRealTimers();
1851
- }
1852
- });
1853
-
1854
- test("CAS false on timeout suppresses timeout feed event", async () => {
1855
- jest.useFakeTimers();
1856
- try {
1857
- mockCompleteHeartbeatRun.mockImplementation(() => false);
1858
-
1859
- let resolveRun: () => void;
1860
- const runPromise = new Promise<void>((r) => {
1861
- resolveRun = r;
1862
- });
1863
-
1864
- const service = createService({
1865
- processMessage: async () => {
1866
- await runPromise;
1867
- return { messageId: "msg-1" };
1868
- },
1869
- });
1870
-
1871
- const runOncePromise = service.runOnce();
1872
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1873
- await runOncePromise;
1874
-
1875
- // completeHeartbeatRun returned false, so no timeout feed event
1876
- const timeoutCalls = mockEmitFeedEvent.mock.calls.filter(
1877
- (call: unknown[]) => {
1878
- const opts = call[0] as { title?: string };
1879
- return opts.title === "Heartbeat Timed Out";
1880
- },
1881
- );
1882
- expect(timeoutCalls).toHaveLength(0);
1883
-
1884
- resolveRun!();
1885
- } finally {
1886
- jest.useRealTimers();
1887
- }
1888
- });
1889
-
1890
- test("late run emits late feed event", async () => {
1891
- const service = createService();
1892
- service.start();
1893
-
1894
- // Set the pending run to be 10 minutes in the past
1895
- (service as any)._nextRunAt = Date.now() - 10 * 60 * 1000;
1896
- (service as any)._pendingRunId = "late-run-id";
1897
-
1898
- await service.runOnce();
1899
-
1900
- const lateCalls = mockEmitFeedEvent.mock.calls.filter(
1901
- (call: unknown[]) => {
1902
- const opts = call[0] as { title?: string };
1903
- return opts.title === "Heartbeat Ran Late";
1904
- },
1905
- );
1906
- expect(lateCalls).toHaveLength(1);
1907
- const opts = (lateCalls as any[][])[0][0] as {
1908
- urgency?: string;
1909
- summary?: string;
1910
- };
1911
- expect(opts.urgency).toBe("medium");
1912
- expect(opts.summary).toContain("10 minutes late");
1913
-
1914
- await service.stop();
1915
- });
1916
-
1917
- test("on-time run does not emit late feed event", async () => {
1918
- const service = createService();
1919
- await service.runOnce();
1920
-
1921
- const lateCalls = mockEmitFeedEvent.mock.calls.filter(
1922
- (call: unknown[]) => {
1923
- const opts = call[0] as { title?: string };
1924
- return opts.title === "Heartbeat Ran Late";
1925
- },
1926
- );
1927
- expect(lateCalls).toHaveLength(0);
1928
- });
1929
-
1930
- test("start() emits missed-run feed event when stale rows exist", () => {
1753
+ test("start() emits activity.failed notification when stale rows exist", () => {
1931
1754
  mockMarkStaleRunsAsMissed.mockImplementation(() => 2);
1932
1755
  mockMarkStaleRunningAsError.mockImplementation(() => 1);
1933
1756
 
1934
1757
  const service = createService();
1935
1758
  service.start();
1936
1759
 
1937
- const missedCalls = mockEmitFeedEvent.mock.calls.filter(
1938
- (call: unknown[]) => {
1939
- const opts = call[0] as { title?: string };
1940
- return opts.title === "Heartbeat Runs Missed";
1941
- },
1760
+ const missedSignals = emittedNotificationSignals.filter(
1761
+ (s) => s.sourceEventName === "activity.failed",
1942
1762
  );
1943
- expect(missedCalls).toHaveLength(1);
1944
- const opts = (missedCalls as any[][])[0][0] as {
1945
- urgency?: string;
1946
- summary?: string;
1947
- };
1948
- expect(opts.urgency).toBe("high");
1949
- expect(opts.summary).toContain("3");
1763
+ expect(missedSignals).toHaveLength(1);
1764
+ const signal = missedSignals[0]!;
1765
+ expect(signal.dedupeKey).toContain("activity-failed:heartbeat-missed:");
1766
+ expect(signal.contextPayload.jobName).toBe("heartbeat");
1767
+ const errorMessage = signal.contextPayload.errorMessage as string;
1768
+ expect(errorMessage).toContain("3");
1769
+ expect(signal.attentionHints?.urgency).toBe("medium");
1950
1770
 
1951
1771
  service.stop();
1952
1772
  });
1953
1773
 
1954
- test("start() does not emit missed-run feed event when counts are 0", () => {
1774
+ test("start() does not emit notification when counts are 0", () => {
1955
1775
  mockMarkStaleRunsAsMissed.mockImplementation(() => 0);
1956
1776
  mockMarkStaleRunningAsError.mockImplementation(() => 0);
1957
1777
 
1958
1778
  const service = createService();
1959
1779
  service.start();
1960
1780
 
1961
- const missedCalls = mockEmitFeedEvent.mock.calls.filter(
1962
- (call: unknown[]) => {
1963
- const opts = call[0] as { title?: string };
1964
- return opts.title === "Heartbeat Runs Missed";
1965
- },
1781
+ const missedSignals = emittedNotificationSignals.filter(
1782
+ (s) => s.sourceEventName === "activity.failed",
1966
1783
  );
1967
- expect(missedCalls).toHaveLength(0);
1784
+ expect(missedSignals).toHaveLength(0);
1968
1785
  service.stop();
1969
1786
  });
1970
1787
  });
@@ -724,6 +724,95 @@ describe("repairHistory", () => {
724
724
  expect(userMsg.content.every((b) => b.type !== "tool_result")).toBe(true);
725
725
  });
726
726
 
727
+ test("downgrades orphan web_search_tool_result in assistant message to text", () => {
728
+ // Inverse of the orphan-server_tool_use case. A web_search_tool_result
729
+ // in an assistant message whose tool_use_id has no preceding
730
+ // server_tool_use in the same message would 400 at the API. Downgrade
731
+ // to text so the model still sees the search results.
732
+ const messages: Message[] = [
733
+ { role: "user", content: [{ type: "text", text: "search" }] },
734
+ {
735
+ role: "assistant",
736
+ content: [
737
+ { type: "text", text: "Here's what I found." },
738
+ {
739
+ type: "web_search_tool_result",
740
+ tool_use_id: "srvtoolu_orphan",
741
+ content: [
742
+ {
743
+ type: "web_search_result",
744
+ url: "https://example.com",
745
+ title: "Example",
746
+ encrypted_content: "enc_abc",
747
+ },
748
+ ],
749
+ },
750
+ ],
751
+ },
752
+ ];
753
+
754
+ const { messages: repaired, stats } = repairHistory(messages);
755
+
756
+ expect(stats.orphanToolResultsDowngraded).toBe(1);
757
+ expect(stats.missingToolResultsInserted).toBe(0);
758
+
759
+ const assistantMsg = repaired[1];
760
+ expect(
761
+ assistantMsg.content.every((b) => b.type !== "web_search_tool_result"),
762
+ ).toBe(true);
763
+ const downgraded = assistantMsg.content.find(
764
+ (b) =>
765
+ b.type === "text" &&
766
+ (b as { text: string }).text.includes("srvtoolu_orphan"),
767
+ );
768
+ expect(downgraded).toBeDefined();
769
+ });
770
+
771
+ test("repairs both orphan directions within the same assistant message", () => {
772
+ // server_tool_use without a result AND a stray wsr from a different id —
773
+ // both must be repaired in one pass.
774
+ const messages: Message[] = [
775
+ { role: "user", content: [{ type: "text", text: "go" }] },
776
+ {
777
+ role: "assistant",
778
+ content: [
779
+ {
780
+ type: "server_tool_use",
781
+ id: "stu_missing_result",
782
+ name: "web_search",
783
+ input: { query: "alpha" },
784
+ },
785
+ {
786
+ type: "web_search_tool_result",
787
+ tool_use_id: "stu_no_use",
788
+ content: [
789
+ { type: "web_search_result", url: "https://x.test", title: "X" },
790
+ ],
791
+ },
792
+ ],
793
+ },
794
+ ];
795
+
796
+ const { messages: repaired, stats } = repairHistory(messages);
797
+
798
+ expect(stats.missingToolResultsInserted).toBe(1);
799
+ expect(stats.orphanToolResultsDowngraded).toBe(1);
800
+
801
+ const assistantMsg = repaired[1];
802
+ // Synthetic result inserted immediately after the orphan server_tool_use.
803
+ const blockTypes = assistantMsg.content.map((b) => b.type);
804
+ expect(blockTypes[0]).toBe("server_tool_use");
805
+ expect(blockTypes[1]).toBe("web_search_tool_result");
806
+ expect(
807
+ (assistantMsg.content[1] as { tool_use_id: string }).tool_use_id,
808
+ ).toBe("stu_missing_result");
809
+ // The orphan wsr is downgraded to text.
810
+ expect(blockTypes[2]).toBe("text");
811
+ expect((assistantMsg.content[2] as { text: string }).text).toContain(
812
+ "stu_no_use",
813
+ );
814
+ });
815
+
727
816
  test("downgrades type-mismatched web_search_tool_result for tool_use", () => {
728
817
  // A web_search_tool_result paired with a regular tool_use ID is a type mismatch
729
818
  const messages: Message[] = [
@@ -3,6 +3,8 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
3
3
  const sentMessages: unknown[] = [];
4
4
  const resolvedInteractionIds: string[] = [];
5
5
  let mockHasClient = false;
6
+ // clientId → actorPrincipalId for the hub mock
7
+ const mockActorMap = new Map<string, string>();
6
8
 
7
9
  mock.module("../runtime/assistant-event-hub.js", () => ({
8
10
  broadcastMessage: (msg: unknown) => sentMessages.push(msg),
@@ -11,11 +13,24 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
11
13
  cap === "host_app_control" && mockHasClient
12
14
  ? { id: "mock-client" }
13
15
  : null,
16
+ getActorPrincipalIdForClient: (clientId: string) =>
17
+ mockActorMap.get(clientId),
14
18
  },
15
19
  }));
16
20
 
21
+ interface RegisteredInteraction {
22
+ conversationId: string;
23
+ kind: string;
24
+ targetClientId?: string;
25
+ targetActorPrincipalId?: string;
26
+ }
27
+ const registeredInteractions: RegisteredInteraction[] = [];
28
+
17
29
  mock.module("../runtime/pending-interactions.js", () => ({
18
- register: () => undefined,
30
+ register: (
31
+ _requestId: string,
32
+ entry: RegisteredInteraction,
33
+ ) => registeredInteractions.push(entry),
19
34
  resolve: (requestId: string) => {
20
35
  resolvedInteractionIds.push(requestId);
21
36
  return undefined;
@@ -58,7 +73,9 @@ describe("HostAppControlProxy", () => {
58
73
  beforeEach(() => {
59
74
  sentMessages.length = 0;
60
75
  resolvedInteractionIds.length = 0;
76
+ registeredInteractions.length = 0;
61
77
  mockHasClient = false;
78
+ mockActorMap.clear();
62
79
  _resetActiveAppControlSession();
63
80
  });
64
81
 
@@ -769,4 +786,95 @@ describe("HostAppControlProxy", () => {
769
786
  proxy.dispose();
770
787
  });
771
788
  });
789
+
790
+ // -------------------------------------------------------------------------
791
+ // (g) sourceActorPrincipalId + targetClientId plumbing
792
+ // -------------------------------------------------------------------------
793
+
794
+ describe("actor principal + targetClientId plumbing", () => {
795
+ test("request() accepts sourceActorPrincipalId and targetClientId without crashing", async () => {
796
+ const proxy = new HostAppControlProxy("conv-1");
797
+ const ctrl = new AbortController();
798
+ _setActiveAppControlSession({
799
+ conversationId: "conv-1",
800
+ app: "com.example.app",
801
+ });
802
+
803
+ const resultPromise = proxy.request(
804
+ "app_control_observe",
805
+ { tool: "observe", app: "com.example.app" },
806
+ "conv-1",
807
+ ctrl.signal,
808
+ "actor-principal-1", // sourceActorPrincipalId
809
+ "client-A", // targetClientId
810
+ );
811
+
812
+ expect(sentMessages).toHaveLength(1);
813
+ const sent = sentMessages[0] as Record<string, unknown>;
814
+ expect(sent.type).toBe("host_app_control_request");
815
+ // targetClientId propagates to the broadcast envelope
816
+ expect(sent.targetClientId).toBe("client-A");
817
+
818
+ // pending interactions registers targetClientId
819
+ expect(registeredInteractions).toHaveLength(1);
820
+ expect(registeredInteractions[0].targetClientId).toBe("client-A");
821
+
822
+ // Resolve to unblock the promise
823
+ proxy.resolve(sent.requestId as string, payload({ pngBase64: PNG_A }));
824
+ await resultPromise;
825
+
826
+ proxy.dispose();
827
+ });
828
+
829
+ test("request() with targetClientId + known actor: registers targetActorPrincipalId", async () => {
830
+ mockActorMap.set("client-A", "user-1");
831
+ const proxy = new HostAppControlProxy("conv-1");
832
+ const ctrl = new AbortController();
833
+ _setActiveAppControlSession({
834
+ conversationId: "conv-1",
835
+ app: "com.example.app",
836
+ });
837
+
838
+ const resultPromise = proxy.request(
839
+ "app_control_observe",
840
+ { tool: "observe", app: "com.example.app" },
841
+ "conv-1",
842
+ ctrl.signal,
843
+ "user-1", // sourceActorPrincipalId
844
+ "client-A", // targetClientId → hub resolves actorPrincipalId = "user-1"
845
+ );
846
+
847
+ const sent = sentMessages[0] as Record<string, unknown>;
848
+ // targetActorPrincipalId was looked up from hub and stored
849
+ expect(registeredInteractions[0].targetActorPrincipalId).toBe("user-1");
850
+
851
+ proxy.resolve(sent.requestId as string, payload({ pngBase64: PNG_A }));
852
+ await resultPromise;
853
+
854
+ proxy.dispose();
855
+ });
856
+
857
+ test("request() without targetClientId: does not register targetActorPrincipalId", async () => {
858
+ const proxy = new HostAppControlProxy("conv-1");
859
+ const ctrl = new AbortController();
860
+
861
+ const resultPromise = proxy.request(
862
+ "app_control_start",
863
+ { tool: "start", app: "com.example.app" },
864
+ "conv-1",
865
+ ctrl.signal,
866
+ "user-1", // sourceActorPrincipalId
867
+ undefined, // no targetClientId
868
+ );
869
+
870
+ const sent = sentMessages[0] as Record<string, unknown>;
871
+ expect(registeredInteractions[0].targetClientId).toBeUndefined();
872
+ expect(registeredInteractions[0].targetActorPrincipalId).toBeUndefined();
873
+
874
+ proxy.resolve(sent.requestId as string, payload({ pngBase64: PNG_A }));
875
+ await resultPromise;
876
+
877
+ proxy.dispose();
878
+ });
879
+ });
772
880
  });