@vellumai/assistant 0.7.3 → 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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -61,14 +61,10 @@ type IncludeValidationResult =
61
61
  | IncludeValidationError
62
62
  | IncludeValidationCycleError;
63
63
 
64
- /**
65
- * Validate the include graph starting from the given root skill ID.
66
- * Uses three-state DFS (unseen/visiting/done) to detect both missing children
67
- * and cycles. Returns the first error encountered in DFS order.
68
- */
69
- export function validateIncludes(
64
+ function validateIncludeGraph(
70
65
  rootId: string,
71
66
  catalogIndex: Map<string, SkillSummary>,
67
+ options: { failOnMissing: boolean },
72
68
  ): IncludeValidationResult {
73
69
  const visited: string[] = [];
74
70
  type State = "unseen" | "visiting" | "done";
@@ -97,13 +93,16 @@ export function validateIncludes(
97
93
  if (skill?.includes) {
98
94
  for (const childId of skill.includes) {
99
95
  if (!catalogIndex.has(childId)) {
100
- return {
101
- ok: false,
102
- error: "missing",
103
- missingChildId: childId,
104
- parentId: id,
105
- path: [...ancestry],
106
- };
96
+ if (options.failOnMissing) {
97
+ return {
98
+ ok: false,
99
+ error: "missing",
100
+ missingChildId: childId,
101
+ parentId: id,
102
+ path: [...ancestry],
103
+ };
104
+ }
105
+ continue;
107
106
  }
108
107
  const childError = dfs(childId);
109
108
  if (childError) return childError;
@@ -120,6 +119,29 @@ export function validateIncludes(
120
119
  return { ok: true, visited };
121
120
  }
122
121
 
122
+ /**
123
+ * Validate the include graph starting from the given root skill ID.
124
+ * Uses three-state DFS (unseen/visiting/done) to detect both missing children
125
+ * and cycles. Returns the first error encountered in DFS order.
126
+ */
127
+ export function validateIncludes(
128
+ rootId: string,
129
+ catalogIndex: Map<string, SkillSummary>,
130
+ ): IncludeValidationResult {
131
+ return validateIncludeGraph(rootId, catalogIndex, { failOnMissing: true });
132
+ }
133
+
134
+ /**
135
+ * Validate only cycle safety for a graph that may intentionally have missing
136
+ * advisory includes. Missing child IDs are skipped during traversal.
137
+ */
138
+ export function validateIncludeCycles(
139
+ rootId: string,
140
+ catalogIndex: Map<string, SkillSummary>,
141
+ ): IncludeValidationResult {
142
+ return validateIncludeGraph(rootId, catalogIndex, { failOnMissing: false });
143
+ }
144
+
123
145
  /**
124
146
  * Recursively traverse the include graph starting from the given root skill ID.
125
147
  * Returns all visited skill IDs in DFS pre-order.
@@ -10,14 +10,15 @@
10
10
 
11
11
  import { v4 as uuid } from "uuid";
12
12
 
13
+ import { resolveCallSiteConfig } from "../config/llm-resolver.js";
13
14
  import { getConfig } from "../config/loader.js";
14
15
  import { Conversation } from "../daemon/conversation.js";
15
16
  import { findConversation } from "../daemon/conversation-store.js";
16
17
  import type { ServerMessage } from "../daemon/message-protocol.js";
17
18
  import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
18
- import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
19
+ import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
20
+ import { resolveDefaultProvider } from "../providers/connection-resolution.js";
19
21
  import { RateLimitProvider } from "../providers/ratelimit.js";
20
- import { getProvider } from "../providers/registry.js";
21
22
  import { createAbortReason } from "../util/abort-reasons.js";
22
23
  import { getLogger } from "../util/logger.js";
23
24
  import { getSandboxWorkingDir } from "../util/platform.js";
@@ -181,19 +182,23 @@ export class SubagentManager {
181
182
 
182
183
  // ── Build conversation dependencies ─────────────────────────────
183
184
  const appConfig = getConfig();
184
- let provider = getProvider(appConfig.llm.default.provider);
185
+ // Connection-aware default-provider resolution. Throws
186
+ // `ConnectionResolutionError` if `llm.default.provider_connection` is
187
+ // unset or the connection row is missing/mismatched (config bugs).
188
+ // Returns null on soft credential failures (vault miss, transient
189
+ // auth) — handled below as "no provider available". Per-call
190
+ // `callSite` routing is layered next.
191
+ const baseProvider = await resolveDefaultProvider(appConfig);
192
+ if (!baseProvider) {
193
+ throw new Error(
194
+ `Subagent: default provider '${resolveCallSiteConfig("mainAgent", appConfig.llm).provider}' is not registered`,
195
+ );
196
+ }
185
197
  // Per-call `options.config.callSite` (e.g. `subagentSpawn`) can resolve
186
- // to a provider name that differs from `llm.default.provider`. Wrap the
187
- // default provider so the actual transport routes correctly per call,
188
- // rather than only forwarding metadata to the default's HTTP client.
189
- // See `providers/call-site-routing.ts`.
190
- provider = new CallSiteRoutingProvider(provider, (name) => {
191
- try {
192
- return getProvider(name);
193
- } catch {
194
- return undefined;
195
- }
196
- });
198
+ // to a profile that differs from `llm.default`. The shared wrapper
199
+ // threads `appConfig` through so per-call alternate-profile routing is
200
+ // also connection-aware (matches the canonical dispatch path).
201
+ let provider = wrapWithCallSiteRouting(baseProvider, appConfig);
197
202
  const { rateLimit } = appConfig;
198
203
  if (rateLimit.maxRequestsPerMinute > 0) {
199
204
  provider = new RateLimitProvider(
@@ -225,7 +230,7 @@ export class SubagentManager {
225
230
  config.systemPromptOverride ??
226
231
  buildSubagentSystemPrompt({ ...config, id: subagentId }, role);
227
232
  }
228
- const maxTokens = appConfig.llm.default.maxTokens;
233
+ const maxTokens = resolveCallSiteConfig("subagentSpawn", appConfig.llm).maxTokens;
229
234
  const workingDir = getSandboxWorkingDir();
230
235
 
231
236
  // ── Initialise state ────────────────────────────────────────────
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Tests for the target_client_id sticky-mode override in
3
+ * acquireCdpClientWithMode (browser-execution.ts).
4
+ *
5
+ * Fix 1: when target_client_id is provided, the sticky backend kind
6
+ * remembered from prior turns in the conversation must NOT be applied.
7
+ * The factory must receive mode:"extension" so the request reaches the
8
+ * host-browser proxy regardless of any prior local/cdp-inspect preference.
9
+ */
10
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
11
+
12
+ import type { ToolContext } from "../../types.js";
13
+ import type { BrowserMode, CdpClientKind } from "../cdp-client/types.js";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Captured call state
17
+ // ---------------------------------------------------------------------------
18
+
19
+ interface CdpClientCallOpts {
20
+ mode?: BrowserMode;
21
+ targetClientId?: string;
22
+ }
23
+
24
+ const getCdpClientCalls: CdpClientCallOpts[] = [];
25
+
26
+ function makeFakeScopedClient(kind: CdpClientKind, conversationId: string) {
27
+ return {
28
+ kind,
29
+ conversationId,
30
+ send: mock(async () => ({})),
31
+ dispose: mock(() => {}),
32
+ };
33
+ }
34
+
35
+ const getCdpClientMock = mock(
36
+ (ctx: ToolContext, opts?: CdpClientCallOpts) => {
37
+ getCdpClientCalls.push({
38
+ mode: opts?.mode,
39
+ targetClientId: opts?.targetClientId,
40
+ });
41
+ return makeFakeScopedClient("extension", ctx.conversationId);
42
+ },
43
+ );
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Mutable sticky-kind control
47
+ // ---------------------------------------------------------------------------
48
+
49
+ let stickyKind: CdpClientKind | null = null;
50
+
51
+ const setPreferredBackendKindMock = mock(
52
+ (_conversationId: string, _kind: CdpClientKind) => {},
53
+ );
54
+ const clearPreferredBackendKindMock = mock((_conversationId: string) => {});
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Module mocks (must be declared before dynamic import)
58
+ // ---------------------------------------------------------------------------
59
+
60
+ mock.module("../cdp-client/factory.js", () => ({
61
+ getCdpClient: getCdpClientMock,
62
+ buildCandidateList: mock(() => []),
63
+ isDesktopAutoCooldownActive: () => false,
64
+ }));
65
+
66
+ mock.module("../browser-manager.js", () => ({
67
+ browserManager: {
68
+ getPreferredBackendKind: (_conversationId: string) => stickyKind,
69
+ setPreferredBackendKind: setPreferredBackendKindMock,
70
+ clearPreferredBackendKind: clearPreferredBackendKindMock,
71
+ storeSnapshotBackendNodeMap: () => {},
72
+ clearSnapshotBackendNodeMap: () => {},
73
+ resolveSnapshotBackendNodeId: () => undefined,
74
+ isInteractive: () => false,
75
+ supportsRouteInterception: false,
76
+ },
77
+ }));
78
+
79
+ mock.module("../../../config/loader.js", () => ({
80
+ getConfig: () => ({
81
+ hostBrowser: {
82
+ cdpInspect: {
83
+ enabled: false,
84
+ host: "localhost",
85
+ port: 9222,
86
+ probeTimeoutMs: 500,
87
+ desktopAuto: { enabled: false, cooldownMs: 30_000 },
88
+ },
89
+ },
90
+ }),
91
+ }));
92
+
93
+ mock.module("../../../daemon/host-browser-proxy.js", () => ({
94
+ HostBrowserProxy: {
95
+ get instance() {
96
+ return {
97
+ isAvailable: () => false,
98
+ hasExtensionClient: () => false,
99
+ request: () => Promise.reject(new Error("no extension")),
100
+ };
101
+ },
102
+ },
103
+ }));
104
+
105
+ mock.module("../runtime-check.js", () => ({
106
+ checkBrowserRuntime: async () => ({
107
+ playwrightAvailable: true,
108
+ chromiumInstalled: true,
109
+ chromiumPath: "/tmp/chromium",
110
+ error: null,
111
+ }),
112
+ }));
113
+
114
+ mock.module("../../../util/logger.js", () => ({
115
+ getLogger: () => ({
116
+ debug: () => {},
117
+ warn: () => {},
118
+ info: () => {},
119
+ error: () => {},
120
+ }),
121
+ }));
122
+
123
+ // Import under test after all mock.module calls.
124
+ const { executeBrowserAttach } = await import("../browser-execution.js");
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Helpers
128
+ // ---------------------------------------------------------------------------
129
+
130
+ function makeContext(conversationId: string): ToolContext {
131
+ return {
132
+ conversationId,
133
+ workingDir: "/tmp",
134
+ trustClass: "guardian",
135
+ signal: new AbortController().signal,
136
+ } as unknown as ToolContext;
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Tests
141
+ // ---------------------------------------------------------------------------
142
+
143
+ describe("acquireCdpClientWithMode: target_client_id overrides sticky backend mode", () => {
144
+ beforeEach(() => {
145
+ getCdpClientCalls.length = 0;
146
+ getCdpClientMock.mockClear();
147
+ setPreferredBackendKindMock.mockClear();
148
+ clearPreferredBackendKindMock.mockClear();
149
+ stickyKind = null;
150
+ });
151
+
152
+ test("sticky local + target_client_id → getCdpClient receives mode:extension, not local", async () => {
153
+ // Simulate a prior turn that pinned the conversation to the local backend.
154
+ stickyKind = "local";
155
+
156
+ await executeBrowserAttach(
157
+ { target_client_id: "host-client-abc" },
158
+ makeContext("sticky-local-override"),
159
+ );
160
+
161
+ expect(getCdpClientMock).toHaveBeenCalledTimes(1);
162
+ // Fix 1: sticky "local" must be bypassed when target_client_id is present.
163
+ expect(getCdpClientCalls[0].mode).toBe("extension");
164
+ expect(getCdpClientCalls[0].targetClientId).toBe("host-client-abc");
165
+ });
166
+
167
+ test("sticky cdp-inspect + target_client_id → getCdpClient receives mode:extension, not cdp-inspect", async () => {
168
+ stickyKind = "cdp-inspect";
169
+
170
+ await executeBrowserAttach(
171
+ { target_client_id: "host-client-xyz" },
172
+ makeContext("sticky-inspect-override"),
173
+ );
174
+
175
+ expect(getCdpClientMock).toHaveBeenCalledTimes(1);
176
+ expect(getCdpClientCalls[0].mode).toBe("extension");
177
+ expect(getCdpClientCalls[0].targetClientId).toBe("host-client-xyz");
178
+ });
179
+
180
+ test("sticky local + no target_client_id → getCdpClient receives mode:local (sticky honored)", async () => {
181
+ // Without target_client_id, the sticky preference must still apply.
182
+ stickyKind = "local";
183
+
184
+ await executeBrowserAttach(
185
+ {}, // no target_client_id
186
+ makeContext("sticky-honored"),
187
+ );
188
+
189
+ expect(getCdpClientMock).toHaveBeenCalledTimes(1);
190
+ expect(getCdpClientCalls[0].mode).toBe("local");
191
+ expect(getCdpClientCalls[0].targetClientId).toBeUndefined();
192
+ });
193
+
194
+ test("no sticky + target_client_id → getCdpClient receives mode:extension", async () => {
195
+ stickyKind = null; // No prior sticky preference
196
+
197
+ await executeBrowserAttach(
198
+ { target_client_id: "host-client-fresh" },
199
+ makeContext("no-sticky-targeted"),
200
+ );
201
+
202
+ expect(getCdpClientMock).toHaveBeenCalledTimes(1);
203
+ expect(getCdpClientCalls[0].mode).toBe("extension");
204
+ expect(getCdpClientCalls[0].targetClientId).toBe("host-client-fresh");
205
+ });
206
+ });
@@ -378,16 +378,25 @@ function acquireCdpClientWithMode(
378
378
  }
379
379
  const browserMode = modeResult.mode;
380
380
 
381
+ const targetClientId =
382
+ typeof input.target_client_id === "string" && input.target_client_id !== ""
383
+ ? input.target_client_id
384
+ : undefined;
385
+
381
386
  const rememberedKind = browserManager.getPreferredBackendKind(
382
387
  context.conversationId,
383
388
  );
389
+ // target_client_id requires the extension proxy path — bypass any sticky
390
+ // backend remembered from prior turns so the explicit target always wins.
384
391
  const effectiveMode: BrowserMode =
385
- browserMode === "auto" && rememberedKind !== null
392
+ targetClientId != null
393
+ ? "extension"
394
+ : browserMode === "auto" && rememberedKind !== null
386
395
  ? rememberedKind
387
396
  : browserMode;
388
397
 
389
398
  try {
390
- const raw = getCdpClient(context, { mode: effectiveMode });
399
+ const raw = getCdpClient(context, { mode: effectiveMode, targetClientId });
391
400
  const cdp = wrapWithKindMemo(raw, context.conversationId);
392
401
  return { cdp, browserMode };
393
402
  } catch (err) {
@@ -395,10 +404,12 @@ function acquireCdpClientWithMode(
395
404
  // a remembered backend kind that has since become unavailable. Drop
396
405
  // the stale memo and retry with fresh auto selection so a dead
397
406
  // sticky preference doesn't surface as a hard failure.
398
- if (browserMode === "auto" && effectiveMode !== "auto") {
407
+ // Do not apply this fallback when target_client_id is set — a targeting
408
+ // failure must surface as an error, not silently route elsewhere.
409
+ if (browserMode === "auto" && effectiveMode !== "auto" && targetClientId == null) {
399
410
  browserManager.clearPreferredBackendKind(context.conversationId);
400
411
  try {
401
- const raw = getCdpClient(context, { mode: "auto" });
412
+ const raw = getCdpClient(context, { mode: "auto", targetClientId });
402
413
  const cdp = wrapWithKindMemo(raw, context.conversationId);
403
414
  return { cdp, browserMode };
404
415
  } catch (retryErr) {
@@ -78,13 +78,26 @@ let desktopAutoConfig = { enabled: true, cooldownMs: 30_000 };
78
78
  const logWarnCalls: Array<{ args: unknown[] }> = [];
79
79
  const logDebugCalls: Array<{ args: unknown[] }> = [];
80
80
 
81
+ // Spread-real-module pattern: bun's `mock.module` is process-global, so a
82
+ // factory that returns ONLY `{ createExtensionCdpClient }` would clobber
83
+ // the `ExtensionCdpClient` class export for every later test file that
84
+ // imports from this module path (e.g. extension-cdp-client.test.ts). We
85
+ // snapshot the real exports first and override only the symbols this
86
+ // suite stubs out.
87
+ import * as realCdpInspectClient from "../cdp-inspect-client.js";
88
+ import * as realExtensionCdpClient from "../extension-cdp-client.js";
89
+ import * as realLocalCdpClient from "../local-cdp-client.js";
90
+
81
91
  mock.module("../extension-cdp-client.js", () => ({
92
+ ...realExtensionCdpClient,
82
93
  createExtensionCdpClient: createExtensionCdpClientMock,
83
94
  }));
84
95
  mock.module("../local-cdp-client.js", () => ({
96
+ ...realLocalCdpClient,
85
97
  createLocalCdpClient: createLocalCdpClientMock,
86
98
  }));
87
99
  mock.module("../cdp-inspect-client.js", () => ({
100
+ ...realCdpInspectClient,
88
101
  createCdpInspectClient: createCdpInspectClientMock,
89
102
  }));
90
103
  mock.module("../../../../config/loader.js", () => ({
@@ -235,9 +248,15 @@ describe("getCdpClient", () => {
235
248
  );
236
249
  expect(result).toEqual({ ok: true, via: "extension" });
237
250
  expect(createExtensionCdpClientMock).toHaveBeenCalledTimes(1);
251
+ // Call signature includes optional cdpSessionId, sourceActorPrincipalId,
252
+ // and targetClientId (all undefined here — no pinned session id, no actor
253
+ // binding, no explicit client target in this legacy ctx).
238
254
  expect(createExtensionCdpClientMock).toHaveBeenCalledWith(
239
255
  fakeProxy,
240
256
  "test-convo",
257
+ undefined,
258
+ undefined,
259
+ undefined,
241
260
  );
242
261
  expect(createLocalCdpClientMock).not.toHaveBeenCalled();
243
262
  expect(createCdpInspectClientMock).not.toHaveBeenCalled();
@@ -658,6 +677,105 @@ describe("getCdpClient", () => {
658
677
  await client.send("Page.navigate");
659
678
  expect(createCdpInspectClientMock).toHaveBeenCalledTimes(1);
660
679
  });
680
+
681
+ test("threads sourceActorPrincipalId from ToolContext into createExtensionCdpClient", async () => {
682
+ // The proxy uses sourceActorPrincipalId to refuse cross-user dispatch
683
+ // when host_browser is exposed cross-client (web/iOS turn → connected
684
+ // extension/macOS bridge). The factory must thread the value from the
685
+ // ToolContext through to ExtensionCdpClient on every candidate-list path
686
+ // so the actor identity reaches the proxy at request time.
687
+ const fakeProxy = makeAvailableProxy();
688
+ mockSingletonProxy = fakeProxy;
689
+ const ctx = makeContext({
690
+ conversationId: "actor-bound",
691
+ sourceActorPrincipalId: "user-actor-1",
692
+ });
693
+
694
+ const client = getCdpClient(ctx);
695
+ await client.send("Page.navigate");
696
+
697
+ expect(createExtensionCdpClientMock).toHaveBeenCalledTimes(1);
698
+ expect(createExtensionCdpClientMock).toHaveBeenCalledWith(
699
+ fakeProxy,
700
+ "actor-bound",
701
+ undefined,
702
+ "user-actor-1",
703
+ undefined,
704
+ );
705
+ });
706
+
707
+ test("threads targetClientId from options into createExtensionCdpClient", async () => {
708
+ const fakeProxy = makeAvailableProxy();
709
+ mockSingletonProxy = fakeProxy;
710
+ const ctx = makeContext({
711
+ conversationId: "targeted-client",
712
+ sourceActorPrincipalId: "user-actor-1",
713
+ });
714
+
715
+ const client = getCdpClient(ctx, { targetClientId: "specific-ext-client" });
716
+ await client.send("Page.navigate");
717
+
718
+ expect(createExtensionCdpClientMock).toHaveBeenCalledTimes(1);
719
+ expect(createExtensionCdpClientMock).toHaveBeenCalledWith(
720
+ fakeProxy,
721
+ "targeted-client",
722
+ undefined,
723
+ "user-actor-1",
724
+ "specific-ext-client",
725
+ );
726
+ });
727
+
728
+ // ── auto mode + targetClientId: no fallback (Fix 2) ─────────────────
729
+
730
+ test("auto mode + targetClientId + no extension → throws targeting error, does not fall through to local", () => {
731
+ // No extension connected — without targetClientId, local would be the
732
+ // fallback. With targetClientId, the factory must fail immediately.
733
+ const ctx = makeContext({ conversationId: "auto-targeted-no-ext" });
734
+
735
+ expect(() =>
736
+ getCdpClient(ctx, { mode: "auto", targetClientId: "specific-host-client" }),
737
+ ).toThrow(
738
+ expect.objectContaining({
739
+ code: "transport_error",
740
+ message: expect.stringContaining("specific-host-client"),
741
+ }),
742
+ );
743
+ expect(createLocalCdpClientMock).not.toHaveBeenCalled();
744
+ expect(createCdpInspectClientMock).not.toHaveBeenCalled();
745
+ });
746
+
747
+ test("auto mode + targetClientId + extension available → routes only to extension, no other backends tried", async () => {
748
+ cdpInspectEnabled = true; // cdp-inspect would normally be in the candidate list
749
+ const fakeProxy = makeAvailableProxy();
750
+ mockSingletonProxy = fakeProxy;
751
+ const ctx = makeContext({
752
+ conversationId: "auto-targeted-ext-available",
753
+ sourceActorPrincipalId: "actor-42",
754
+ });
755
+
756
+ const client = getCdpClient(ctx, {
757
+ mode: "auto",
758
+ targetClientId: "specific-host-client",
759
+ });
760
+ expect(client.kind).toBe("extension");
761
+
762
+ const result = await client.send<{ ok: boolean; via: string }>(
763
+ "Page.navigate",
764
+ );
765
+ expect(result).toEqual({ ok: true, via: "extension" });
766
+
767
+ expect(createExtensionCdpClientMock).toHaveBeenCalledTimes(1);
768
+ expect(createExtensionCdpClientMock).toHaveBeenCalledWith(
769
+ fakeProxy,
770
+ "auto-targeted-ext-available",
771
+ undefined,
772
+ "actor-42",
773
+ "specific-host-client",
774
+ );
775
+ // cdp-inspect and local must NOT have been tried — no fallback.
776
+ expect(createCdpInspectClientMock).not.toHaveBeenCalled();
777
+ expect(createLocalCdpClientMock).not.toHaveBeenCalled();
778
+ });
661
779
  });
662
780
 
663
781
  // ── buildCandidateList tests ─────────────────────────────────────────────
@@ -748,6 +866,62 @@ describe("buildCandidateList", () => {
748
866
  expect(candidates.length).toBe(1);
749
867
  expect(candidates[0].kind).toBe("local");
750
868
  });
869
+
870
+ // ── targetClientId: no-fallback single-extension list (Fix 2) ────────
871
+
872
+ test("targetClientId: returns a single extension candidate when extension is available", () => {
873
+ const fakeProxy = makeAvailableProxy();
874
+ mockSingletonProxy = fakeProxy;
875
+ const ctx = makeContext({ conversationId: "target-with-ext" });
876
+
877
+ const candidates = buildCandidateList(ctx, "host-client-42");
878
+
879
+ // Must be exactly one candidate — no cdp-inspect or local fallbacks.
880
+ expect(candidates.length).toBe(1);
881
+ expect(candidates[0].kind).toBe("extension");
882
+ expect(candidates[0].reason).toContain("host-client-42");
883
+ });
884
+
885
+ test("targetClientId: throws transport_error immediately when no Chrome Extension is connected", () => {
886
+ // No extension — local would normally be the fallback, but targeting
887
+ // requires the proxy path and must fail loudly instead of routing elsewhere.
888
+ const ctx = makeContext({ conversationId: "target-no-ext" });
889
+
890
+ expect(() => buildCandidateList(ctx, "host-client-42")).toThrow(
891
+ expect.objectContaining({
892
+ code: "transport_error",
893
+ message: expect.stringContaining("host-client-42"),
894
+ }),
895
+ );
896
+ // Verify we did NOT silently fall through to a local candidate.
897
+ expect(createLocalCdpClientMock).not.toHaveBeenCalled();
898
+ });
899
+
900
+ test("targetClientId: threads targetClientId into the extension candidate's create() call", async () => {
901
+ const fakeProxy = makeAvailableProxy();
902
+ mockSingletonProxy = fakeProxy;
903
+ const ctx = makeContext({
904
+ conversationId: "target-threads-id",
905
+ sourceActorPrincipalId: "actor-1",
906
+ });
907
+
908
+ const candidates = buildCandidateList(ctx, "host-client-99");
909
+ expect(candidates.length).toBe(1);
910
+
911
+ // Clear accumulated calls from prior tests in this describe block before
912
+ // materialising the candidate so the call count is unambiguous.
913
+ createExtensionCdpClientMock.mockClear();
914
+ candidates[0].create();
915
+
916
+ expect(createExtensionCdpClientMock).toHaveBeenCalledTimes(1);
917
+ expect(createExtensionCdpClientMock).toHaveBeenCalledWith(
918
+ fakeProxy,
919
+ "target-threads-id",
920
+ undefined,
921
+ "actor-1",
922
+ "host-client-99",
923
+ );
924
+ });
751
925
  });
752
926
 
753
927
  // ── buildChainedClient failover tests ────────────────────────────────────
@@ -249,18 +249,22 @@ describe("connectCdpWsTransport", () => {
249
249
  });
250
250
 
251
251
  test("fans out events (frames with no id) to listeners", async () => {
252
+ // The server pushes the unsolicited event in direct response to
253
+ // a client trigger, sending the event frame BEFORE the response
254
+ // ack. WebSocket message ordering then guarantees the event
255
+ // arrives at the client (and is fanned out) before the trigger
256
+ // resolves — no setTimeout race between the server's open
257
+ // callback and the test attaching its listener.
252
258
  const server = startFakeWsServer({
253
- onOpen(ws) {
254
- // Push an unsolicited event shortly after open.
255
- setTimeout(() => {
256
- ws.send(
257
- JSON.stringify({
258
- method: "Target.targetCreated",
259
- params: { targetId: "abc" },
260
- sessionId: "S1",
261
- }),
262
- );
263
- }, 5);
259
+ onMessage(ws, frame) {
260
+ ws.send(
261
+ JSON.stringify({
262
+ method: "Target.targetCreated",
263
+ params: { targetId: "abc" },
264
+ sessionId: "S1",
265
+ }),
266
+ );
267
+ ws.send(JSON.stringify({ id: frame.id, result: {} }));
264
268
  },
265
269
  });
266
270
  try {
@@ -273,8 +277,7 @@ describe("connectCdpWsTransport", () => {
273
277
  transport.addEventListener((ev) => {
274
278
  received.push(ev);
275
279
  });
276
- // Wait for the event to arrive.
277
- await new Promise((r) => setTimeout(r, 50));
280
+ await transport.send("Test.trigger");
278
281
  expect(received).toEqual([
279
282
  {
280
283
  method: "Target.targetCreated",
@@ -44,6 +44,19 @@ export class ExtensionCdpClient implements ScopedCdpClient {
44
44
  private readonly proxy: HostBrowserProxy,
45
45
  public readonly conversationId: string,
46
46
  private readonly cdpSessionId?: string,
47
+ /**
48
+ * Caller's actor principal id. When provided, the proxy will refuse to
49
+ * dispatch this CDP command to a host_browser-capable client owned by a
50
+ * different actor — closing the cross-client exposure path's same-user
51
+ * boundary.
52
+ */
53
+ private readonly sourceActorPrincipalId?: string,
54
+ /**
55
+ * Explicit target client id. When provided, the proxy routes directly
56
+ * to that client instead of auto-resolving to the most-recently-active
57
+ * same-actor host_browser client.
58
+ */
59
+ private readonly targetClientId?: string,
47
60
  ) {}
48
61
 
49
62
  async send<T = unknown>(
@@ -74,6 +87,8 @@ export class ExtensionCdpClient implements ScopedCdpClient {
74
87
  },
75
88
  this.conversationId,
76
89
  signal,
90
+ this.sourceActorPrincipalId,
91
+ this.targetClientId,
77
92
  );
78
93
  } catch (err) {
79
94
  throw new CdpError(
@@ -194,6 +209,14 @@ export function createExtensionCdpClient(
194
209
  proxy: HostBrowserProxy,
195
210
  conversationId: string,
196
211
  cdpSessionId?: string,
212
+ sourceActorPrincipalId?: string,
213
+ targetClientId?: string,
197
214
  ): ExtensionCdpClient {
198
- return new ExtensionCdpClient(proxy, conversationId, cdpSessionId);
215
+ return new ExtensionCdpClient(
216
+ proxy,
217
+ conversationId,
218
+ cdpSessionId,
219
+ sourceActorPrincipalId,
220
+ targetClientId,
221
+ );
199
222
  }