@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
@@ -12,6 +12,7 @@ import {
12
12
  createUserMessage,
13
13
  } from "../agent/message-types.js";
14
14
  import {
15
+ type InterfaceId,
15
16
  parseChannelId,
16
17
  parseInterfaceId,
17
18
  type TurnChannelContext,
@@ -179,7 +180,9 @@ export interface ProcessConversationContext {
179
180
  statusText?: string,
180
181
  ): void;
181
182
  /** Force context compaction regardless of threshold/cooldown. */
182
- forceCompact(): Promise<ContextWindowResult>;
183
+ forceCompact(options?: {
184
+ targetInputTokensOverride?: number;
185
+ }): Promise<ContextWindowResult>;
183
186
  /** Set transport-derived hints for the conversation. */
184
187
  setTransportHints(hints: string[] | undefined): void;
185
188
  /** IANA timezone reported by the active client for the current turn. */
@@ -195,6 +198,13 @@ export interface ProcessConversationContext {
195
198
  applyClientTimezoneFromTransport(
196
199
  transport: ConversationTransportMetadata,
197
200
  ): void;
201
+ /**
202
+ * Instantiate host proxies for capabilities that have become reachable
203
+ * mid-queue (e.g. a macOS client connected after a web turn was enqueued
204
+ * without a proxy). Called from drain paths before preactivation so skills
205
+ * are only activated when the proxy that services them is present.
206
+ */
207
+ ensureHostProxiesForTurn(sourceInterface: InterfaceId | undefined): void;
198
208
  }
199
209
 
200
210
  function resolveQueuedTurnContext(
@@ -432,19 +442,19 @@ async function drainSingleMessage(
432
442
  conversation.applyClientTimezoneFromTransport(next.transport);
433
443
  }
434
444
 
435
- // Re-preactivate host-proxy skills for interactive desktop turns. The
436
- // dequeue path reset `preactivatedSkillIds` above, so without these
437
- // re-adds the relevant skill tools wouldn't be projected to the LLM
438
- // for queued messages 2+ even though the underlying proxies (HostCuProxy,
439
- // HostAppControlProxy) are still attached. Mirrors the per-message
440
- // instantiation block in `conversation-routes.ts` / `process-message.ts`.
445
+ // Re-attach and re-preactivate host-proxy skills for interactive turns.
446
+ // The dequeue path reset `preactivatedSkillIds` above; without these
447
+ // re-adds the relevant skill tools won't be projected to the LLM for
448
+ // queued messages 2+. Also instantiates proxies that may not have been
449
+ // present when the message was first enqueued (e.g. a macOS client
450
+ // connects between enqueue and drain). Mirrors the per-message block in
451
+ // `conversation-routes.ts` / `process-message.ts`.
441
452
  if (next.isInteractive !== false) {
442
453
  const interfaceCtx =
443
454
  queuedInterfaceCtx ?? conversation.getTurnInterfaceContext();
444
- preactivateHostProxySkills(
445
- conversation,
446
- interfaceCtx?.userMessageInterface,
447
- );
455
+ const sourceInterface = interfaceCtx?.userMessageInterface;
456
+ conversation.ensureHostProxiesForTurn(sourceInterface);
457
+ preactivateHostProxySkills(conversation, sourceInterface);
448
458
  }
449
459
 
450
460
  // Snapshot persona context at turn start so later tool turns can't pick up
@@ -628,7 +638,9 @@ async function drainSingleMessage(
628
638
  "assistant_turn",
629
639
  next.requestId,
630
640
  );
631
- const result = await conversation.forceCompact();
641
+ const result = await conversation.forceCompact({
642
+ targetInputTokensOverride: slashResult.targetInputTokensOverride,
643
+ });
632
644
  const responseText = formatCompactResult(result);
633
645
 
634
646
  const assistantMsg = createAssistantMessage(responseText);
@@ -875,15 +887,14 @@ async function drainBatch(
875
887
  conversation.applyClientTimezoneFromTransport(head.transport);
876
888
  }
877
889
 
878
- // Re-preactivate host-proxy skills for interactive desktop turns.
890
+ // Re-attach and re-preactivate host-proxy skills for interactive turns.
879
891
  // Mirrors the single-message path exactly — sourced from `head`.
880
892
  if (head.isInteractive !== false) {
881
893
  const interfaceCtx =
882
894
  queuedInterfaceCtx ?? conversation.getTurnInterfaceContext();
883
- preactivateHostProxySkills(
884
- conversation,
885
- interfaceCtx?.userMessageInterface,
886
- );
895
+ const sourceInterface = interfaceCtx?.userMessageInterface;
896
+ conversation.ensureHostProxiesForTurn(sourceInterface);
897
+ preactivateHostProxySkills(conversation, sourceInterface);
887
898
  }
888
899
 
889
900
  // Snapshot persona context at turn start so later tool turns can't pick up
@@ -1314,7 +1325,11 @@ export async function processMessage(
1314
1325
  );
1315
1326
  conversation.messages.push(assistantMsg);
1316
1327
 
1317
- onEvent({ type: "assistant_text_delta", text: replyText });
1328
+ onEvent({
1329
+ type: "assistant_text_delta",
1330
+ text: replyText,
1331
+ conversationId: conversation.conversationId,
1332
+ });
1318
1333
  onEvent({
1319
1334
  type: "message_complete",
1320
1335
  conversationId: conversation.conversationId,
@@ -1474,7 +1489,9 @@ export async function processMessage(
1474
1489
  "assistant_turn",
1475
1490
  requestId,
1476
1491
  );
1477
- const result = await conversation.forceCompact();
1492
+ const result = await conversation.forceCompact({
1493
+ targetInputTokensOverride: slashResult.targetInputTokensOverride,
1494
+ });
1478
1495
  const responseText = formatCompactResult(result);
1479
1496
 
1480
1497
  const assistantMsg = createAssistantMessage(responseText);
@@ -1641,12 +1641,15 @@ const RUNTIME_INJECTION_PREFIXES = [
1641
1641
  // blocks persist in history so the assistant retains temporal/actor grounding.
1642
1642
  "<memory_context __injected>",
1643
1643
  "<memory_context>", // backward-compat: strip legacy blocks from pre-__injected history
1644
- // NOTE: `<memory>` blocks (both the dynamic activation block from
1645
- // `injectTextBlock` and the static `memory-v2-static` injector) are
1646
- // intentionally NOT stripped memory injections persist in history so
1647
- // the assistant retains intra-turn memory state. The activation pipeline
1648
- // dedupes via `everInjected`, and compaction handles aggregate growth, so
1644
+ // The static `memory-v2-static` block (opens `<memory>\n…`) IS stripped
1645
+ // so each compaction re-injects the freshest essentials/threads/recent/
1646
+ // buffer view, matching the `<knowledge_base>` cadence. The dynamic
1647
+ // activation block (opens `<memory __injected>…`) is intentionally NOT
1648
+ // stripped `startsWith("<memory>\n")` does not match it so per-turn
1649
+ // memory activations persist in history. The activation pipeline dedupes
1650
+ // via `everInjected`, and compaction handles aggregate growth, so
1649
1651
  // accumulation does not cause unbounded context growth.
1652
+ "<memory>\n",
1650
1653
  "<voice_call_control>",
1651
1654
  "<workspace_top_level>", // backward-compat: strip legacy workspace blocks
1652
1655
  // NOTE: <workspace> is intentionally NOT stripped — workspace context
@@ -1736,6 +1739,7 @@ export interface RuntimeInjectionBlocks {
1736
1739
  workspaceBlock?: string;
1737
1740
  nowScratchpadBlock?: string;
1738
1741
  pkbContextBlock?: string;
1742
+ memoryV2StaticBlock?: string;
1739
1743
  /**
1740
1744
  * Composed output of every plugin-registered {@link Injector}, concatenated
1741
1745
  * in ascending `order`. Empty string when every injector opted out (returned
@@ -2153,6 +2157,7 @@ export async function applyRuntimeInjections(
2153
2157
  let nowScratchpadCaptured: string | undefined;
2154
2158
  let pkbContextCaptured: string | undefined;
2155
2159
  let pkbSystemReminderCaptured: string | undefined;
2160
+ let memoryV2StaticCaptured: string | undefined;
2156
2161
  const initialTail = runMessages[runMessages.length - 1];
2157
2162
  const initialTailIsUser = !!initialTail && initialTail.role === "user";
2158
2163
  if (initialTailIsUser) {
@@ -2173,6 +2178,9 @@ export async function applyRuntimeInjections(
2173
2178
  case "pkb-reminder":
2174
2179
  pkbSystemReminderCaptured = block.text;
2175
2180
  break;
2181
+ case "memory-v2-static":
2182
+ memoryV2StaticCaptured = block.text;
2183
+ break;
2176
2184
  }
2177
2185
  }
2178
2186
  }
@@ -2354,6 +2362,7 @@ export async function applyRuntimeInjections(
2354
2362
  workspaceBlock: workspaceCaptured,
2355
2363
  nowScratchpadBlock: nowScratchpadCaptured,
2356
2364
  pkbContextBlock: pkbContextCaptured,
2365
+ memoryV2StaticBlock: memoryV2StaticCaptured,
2357
2366
  injectorChainBlock,
2358
2367
  },
2359
2368
  };
@@ -1,6 +1,12 @@
1
1
  import type { InterfaceId } from "../channels/types.js";
2
2
  import { resolveEffectiveContextWindow } from "../config/llm-context-resolution.js";
3
- import { getConfig } from "../config/loader.js";
3
+ import { resolveCallSiteConfig } from "../config/llm-resolver.js";
4
+ import {
5
+ getConfig,
6
+ invalidateConfigCache,
7
+ loadRawConfig,
8
+ saveRawConfig,
9
+ } from "../config/loader.js";
4
10
  import { getConversationOverrideProfile } from "../memory/conversation-crud.js";
5
11
  import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
6
12
  import { getConfiguredProviders } from "../providers/provider-availability.js";
@@ -8,7 +14,43 @@ import { getConfiguredProviders } from "../providers/provider-availability.js";
8
14
  export type SlashResolution =
9
15
  | { kind: "passthrough"; content: string }
10
16
  | { kind: "unknown"; message: string }
11
- | { kind: "compact" };
17
+ | { kind: "compact"; targetInputTokensOverride?: number };
18
+
19
+ const COMPACT_USAGE_HINT =
20
+ "Usage: `/compact [<tokens>]` (e.g. `/compact 30000`, `/compact 30k`, `/compact 1m`).";
21
+
22
+ type CompactParse =
23
+ | { kind: "compact"; targetInputTokensOverride?: number }
24
+ | { kind: "unknown"; message: string };
25
+
26
+ const TOKEN_COUNT_PATTERN = /^(\d+(?:\.\d+)?)([km])?$/i;
27
+ const COMPACT_COMMAND_PATTERN = /^\/compact(?:\s+(.+?))?\s*$/i;
28
+
29
+ function parseTokenCount(input: string): number | null {
30
+ const match = input.match(TOKEN_COUNT_PATTERN);
31
+ if (!match) return null;
32
+ const value = Number.parseFloat(match[1]);
33
+ if (!Number.isFinite(value) || value <= 0) return null;
34
+ const suffix = match[2]?.toLowerCase();
35
+ const multiplier = suffix === "m" ? 1_000_000 : suffix === "k" ? 1_000 : 1;
36
+ const tokens = Math.floor(value * multiplier);
37
+ return tokens > 0 ? tokens : null;
38
+ }
39
+
40
+ function parseCompactCommand(trimmed: string): CompactParse | null {
41
+ const match = trimmed.match(COMPACT_COMMAND_PATTERN);
42
+ if (!match) return null;
43
+ const rest = match[1]?.trim();
44
+ if (!rest) return { kind: "compact" };
45
+ const tokens = parseTokenCount(rest);
46
+ if (tokens == null) {
47
+ return {
48
+ kind: "unknown",
49
+ message: `Unrecognized argument to \`/compact\`: \`${rest}\`. ${COMPACT_USAGE_HINT}`,
50
+ };
51
+ }
52
+ return { kind: "compact", targetInputTokensOverride: tokens };
53
+ }
12
54
 
13
55
  // ── /context and /status commands ────────────────────────────────────
14
56
 
@@ -71,10 +113,125 @@ const DEPRECATED_MODEL_SHORTCUTS = new Set([
71
113
  "grok-multi",
72
114
  ]);
73
115
 
116
+ // ── /model command (inference profile switcher) ──────────────────────
117
+
118
+ type ModelCommandParse =
119
+ | { kind: "list" }
120
+ | { kind: "switch"; profileName: string };
121
+
122
+ /**
123
+ * Parse `/model` and `/model <name>` forms. Returns `null` for any input
124
+ * that isn't a `/model` invocation (so the caller can fall through).
125
+ */
126
+ function parseModelCommand(trimmed: string): ModelCommandParse | null {
127
+ if (trimmed === "/model") return { kind: "list" };
128
+ if (!trimmed.startsWith("/model ")) return null;
129
+ const rest = trimmed.slice("/model ".length).trim();
130
+ if (rest.length === 0) return { kind: "list" };
131
+ return { kind: "switch", profileName: rest };
132
+ }
133
+
134
+ function orderedProfileNames(
135
+ profiles: Record<string, { label?: string; description?: string; status?: "active" | "disabled" }>,
136
+ profileOrder: readonly string[] | undefined,
137
+ ): string[] {
138
+ const order = profileOrder ?? [];
139
+ const seen = new Set<string>();
140
+ const ordered: string[] = [];
141
+ for (const name of order) {
142
+ if (profiles[name] != null && !seen.has(name)) {
143
+ ordered.push(name);
144
+ seen.add(name);
145
+ }
146
+ }
147
+ const tail = Object.keys(profiles)
148
+ .filter((n) => !seen.has(n))
149
+ .sort();
150
+ return [...ordered, ...tail];
151
+ }
152
+
153
+ async function resolveModelCommand(
154
+ parse: ModelCommandParse,
155
+ ): Promise<SlashResolution> {
156
+ const config = getConfig();
157
+ const profiles = (config.llm.profiles ?? {}) as Record<
158
+ string,
159
+ { label?: string; description?: string; status?: "active" | "disabled" }
160
+ >;
161
+ const profileNames = orderedProfileNames(profiles, config.llm.profileOrder);
162
+ const activeProfile = config.llm.activeProfile;
163
+
164
+ if (parse.kind === "list") {
165
+ if (profileNames.length === 0) {
166
+ return {
167
+ kind: "unknown",
168
+ message:
169
+ "No inference profiles are defined. Use **Settings → Models & Services** to create one.",
170
+ };
171
+ }
172
+ const lines = ["Inference profiles:\n"];
173
+ for (const name of profileNames) {
174
+ const profile = profiles[name];
175
+ const label = profile.label ?? name;
176
+ const isCurrent = name === activeProfile;
177
+ const isDisabled = profile.status === "disabled";
178
+ const marker = isCurrent ? " **[current]**" : "";
179
+ const disabled = isDisabled ? " *(disabled)*" : "";
180
+ const description = profile.description ? ` — ${profile.description}` : "";
181
+ lines.push(` - \`${name}\` (${label})${marker}${disabled}${description}`);
182
+ }
183
+ lines.push("\nSwitch with `/model <name>`.");
184
+ return { kind: "unknown", message: lines.join("\n") };
185
+ }
186
+
187
+ const target = parse.profileName;
188
+ if (!(target in profiles)) {
189
+ const available = profileNames.map((n) => `\`${n}\``).join(", ");
190
+ const hint = available.length > 0 ? ` Available: ${available}.` : "";
191
+ return {
192
+ kind: "unknown",
193
+ message: `Profile \`${target}\` not found.${hint}`,
194
+ };
195
+ }
196
+ if (profiles[target].status === "disabled") {
197
+ return {
198
+ kind: "unknown",
199
+ message: `Profile \`${target}\` is disabled. Enable it in **Settings → Models & Services** first.`,
200
+ };
201
+ }
202
+ if (target === activeProfile) {
203
+ const label = profiles[target].label ?? target;
204
+ return {
205
+ kind: "unknown",
206
+ message: `Already using profile \`${target}\` (${label}).`,
207
+ };
208
+ }
209
+
210
+ // Write `llm.activeProfile` directly to the raw config file. We invalidate
211
+ // the in-process cache so the very next `getConfig()` reflects the switch;
212
+ // the file watcher will also pick this up but its debounce can lag a tick.
213
+ const raw = loadRawConfig();
214
+ const llm: Record<string, unknown> =
215
+ raw.llm != null && typeof raw.llm === "object" && !Array.isArray(raw.llm)
216
+ ? (raw.llm as Record<string, unknown>)
217
+ : {};
218
+ llm.activeProfile = target;
219
+ raw.llm = llm;
220
+ saveRawConfig(raw);
221
+ invalidateConfigCache();
222
+
223
+ const label = profiles[target].label ?? target;
224
+ return {
225
+ kind: "unknown",
226
+ message: `Switched to profile \`${target}\` (${label}).`,
227
+ };
228
+ }
229
+
74
230
  // ── /models command ──────────────────────────────────────────────────
75
231
 
76
232
  async function resolveModelList(): Promise<SlashResolution> {
77
233
  const config = getConfig();
234
+ const resolvedMainAgent = resolveCallSiteConfig("mainAgent", config.llm);
78
235
  const configuredProviders = new Set<string>(await getConfiguredProviders());
79
236
 
80
237
  const lines = ["Available models:\n"];
@@ -89,8 +246,8 @@ async function resolveModelList(): Promise<SlashResolution> {
89
246
  lines.push(`**${providerName}** ${status}`);
90
247
  for (const { id, displayName } of models) {
91
248
  const isCurrent =
92
- config.llm.default.provider === provider &&
93
- config.llm.default.model === id;
249
+ resolvedMainAgent.provider === provider &&
250
+ resolvedMainAgent.model === id;
94
251
  const current = isCurrent ? " **[current]**" : "";
95
252
  lines.push(` - ${displayName} (\`${id}\`)${current}`);
96
253
  }
@@ -146,6 +303,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
146
303
  if (context) {
147
304
  fallbackLines.push("/context — Show conversation context usage");
148
305
  }
306
+ fallbackLines.push("/model — List or switch inference profile");
149
307
  fallbackLines.push("/models — List all available models");
150
308
  if (context) {
151
309
  fallbackLines.push("/status — Show conversation status and context usage");
@@ -158,6 +316,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
158
316
  "/commands — List all available commands",
159
317
  "/compact — Force context compaction immediately",
160
318
  "/context — Show conversation context usage",
319
+ "/model — List or switch inference profile",
161
320
  "/models — List all available models",
162
321
  "/status — Show conversation status and context usage",
163
322
  "/btw — Ask a side question while the assistant is working",
@@ -170,6 +329,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
170
329
  "/commands — List all available commands",
171
330
  "/compact — Force context compaction immediately",
172
331
  "/context — Show conversation context usage",
332
+ "/model — List or switch inference profile",
173
333
  "/models — List all available models",
174
334
  "/status — Show conversation status and context usage",
175
335
  "/btw — Ask a side question while the assistant is working",
@@ -181,6 +341,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
181
341
  "/commands — List all available commands",
182
342
  "/compact — Force context compaction immediately",
183
343
  "/context — Show conversation context usage",
344
+ "/model — List or switch inference profile",
184
345
  "/models — List all available models",
185
346
  "/status — Show conversation status and context usage",
186
347
  "/btw — Ask a side question while the assistant is working",
@@ -200,10 +361,7 @@ export function classifySlash(
200
361
  content: string,
201
362
  ): "passthrough" | "compact" | "unknown" {
202
363
  const trimmed = content.trim();
203
- if (
204
- trimmed === "/model" ||
205
- (trimmed.startsWith("/model ") && trimmed !== "/models")
206
- ) {
364
+ if (parseModelCommand(trimmed) != null) {
207
365
  return "unknown";
208
366
  }
209
367
  const shortcutMatch = trimmed.match(/^\/([a-z0-9-]+)(\s|$)/i);
@@ -214,7 +372,8 @@ export function classifySlash(
214
372
  return "unknown";
215
373
  }
216
374
  if (trimmed === "/models") return "unknown";
217
- if (trimmed === "/compact") return "compact";
375
+ const compactParse = parseCompactCommand(trimmed);
376
+ if (compactParse) return compactParse.kind;
218
377
  if (trimmed === "/context") return "unknown";
219
378
  if (trimmed === "/status") return "unknown";
220
379
  if (trimmed === "/commands") return "unknown";
@@ -230,17 +389,11 @@ export async function resolveSlash(
230
389
  content: string,
231
390
  context?: SlashContext,
232
391
  ): Promise<SlashResolution> {
233
- // Handle deprecated model-switching commands direct users to Settings
392
+ // Handle `/model`list profiles (no arg) or switch active profile.
234
393
  const trimmed = content.trim();
235
- if (
236
- trimmed === "/model" ||
237
- (trimmed.startsWith("/model ") && trimmed !== "/models")
238
- ) {
239
- return {
240
- kind: "unknown",
241
- message:
242
- "The `/model` command has been removed. Use **Settings → Models & Services** to change your model and provider.",
243
- };
394
+ const modelParse = parseModelCommand(trimmed);
395
+ if (modelParse != null) {
396
+ return await resolveModelCommand(modelParse);
244
397
  }
245
398
 
246
399
  // Reject deprecated provider shortcut commands (/opus, /sonnet, /haiku, etc.)
@@ -260,10 +413,9 @@ export async function resolveSlash(
260
413
  return await resolveModelList();
261
414
  }
262
415
 
263
- // Handle /compact command
264
- if (trimmed === "/compact") {
265
- return { kind: "compact" };
266
- }
416
+ // Handle /compact command (with optional `<tokens>` override).
417
+ const compactParse = parseCompactCommand(trimmed);
418
+ if (compactParse) return compactParse;
267
419
 
268
420
  // Handle /context and legacy /status commands
269
421
  if (trimmed === "/context" || trimmed === "/status") {
@@ -14,12 +14,13 @@
14
14
  * shared rate-limit timestamps, broadcast).
15
15
  */
16
16
 
17
+ import { resolveCallSiteConfig } from "../config/llm-resolver.js";
17
18
  import { getConfig } from "../config/loader.js";
18
19
  import type { CesClient } from "../credential-execution/client.js";
19
20
  import { buildSystemPrompt } from "../prompts/system-prompt.js";
20
- import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
21
+ import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
22
+ import { resolveDefaultProvider } from "../providers/connection-resolution.js";
21
23
  import { RateLimitProvider } from "../providers/ratelimit.js";
22
- import { getProvider } from "../providers/registry.js";
23
24
  import { getSubagentManager } from "../subagent/index.js";
24
25
  import { getSandboxWorkingDir } from "../util/platform.js";
25
26
  import { Conversation } from "./conversation.js";
@@ -222,14 +223,20 @@ export async function getOrCreateConversation(
222
223
 
223
224
  const createPromise = (async () => {
224
225
  const config = getConfig();
225
- let provider = getProvider(config.llm.default.provider);
226
- provider = new CallSiteRoutingProvider(provider, (name) => {
227
- try {
228
- return getProvider(name);
229
- } catch {
230
- return undefined;
231
- }
232
- });
226
+ // Connection-aware default-provider resolution. Throws
227
+ // `ConnectionResolutionError` when the default profile's
228
+ // `provider_connection` is unset / unknown / mismatched (config
229
+ // bugs). Returns null on soft credential failures (handled below
230
+ // as "default provider not registered").
231
+ const baseProvider = await resolveDefaultProvider(config);
232
+ if (!baseProvider) {
233
+ throw new Error(
234
+ `Conversation: default provider '${resolveCallSiteConfig("mainAgent", config.llm).provider}' is not registered`,
235
+ );
236
+ }
237
+ // Per-call `callSite` routing layered on top, with connection-awareness
238
+ // for alternate profiles (matches the canonical dispatch path).
239
+ let provider = wrapWithCallSiteRouting(baseProvider, config);
233
240
  const { rateLimit } = config;
234
241
  if (rateLimit.maxRequestsPerMinute > 0) {
235
242
  provider = new RateLimitProvider(
@@ -2023,14 +2023,6 @@ export async function surfaceProxyResolver(
2023
2023
 
2024
2024
  // Route app-control proxy tools (all app_control_* tool variants)
2025
2025
  if (toolName.startsWith("app_control_")) {
2026
- if (!ctx.hostAppControlProxy || !ctx.hostAppControlProxy.isAvailable()) {
2027
- return {
2028
- content:
2029
- "App control is not available — enable the `app-control` feature flag and connect a macOS client.",
2030
- isError: true,
2031
- };
2032
- }
2033
-
2034
2026
  // `app_control_stop` resolves immediately: tear down the proxy without
2035
2027
  // a client round-trip. Mirrors CU's terminal-tool short-circuit
2036
2028
  // (`computer_use_done` / `computer_use_respond`). Clear the
@@ -2039,15 +2031,85 @@ export async function surfaceProxyResolver(
2039
2031
  // instead of dispatching against a torn-down proxy, and so a sibling
2040
2032
  // conversation can acquire the released singleton lock without the
2041
2033
  // disposed proxy still being addressable.
2034
+ //
2035
+ // Run this BEFORE the isAvailable() gate so a disconnected client
2036
+ // doesn't strand the singleton lock — stop is local-only.
2042
2037
  if (toolName === "app_control_stop") {
2043
- if (ctx.setHostAppControlProxy) {
2044
- ctx.setHostAppControlProxy(undefined);
2045
- } else {
2046
- ctx.hostAppControlProxy.dispose();
2038
+ if (ctx.hostAppControlProxy) {
2039
+ if (ctx.setHostAppControlProxy) {
2040
+ ctx.setHostAppControlProxy(undefined);
2041
+ } else {
2042
+ ctx.hostAppControlProxy.dispose();
2043
+ }
2047
2044
  }
2048
2045
  return { content: "App control stopped.", isError: false };
2049
2046
  }
2050
2047
 
2048
+ if (!ctx.hostAppControlProxy || !ctx.hostAppControlProxy.isAvailable()) {
2049
+ return {
2050
+ content:
2051
+ "App control is not available — enable the `app-control` feature flag and connect a macOS client.",
2052
+ isError: true,
2053
+ };
2054
+ }
2055
+
2056
+ // Resolve target client. Mirrors the host_cu block above: validate
2057
+ // explicit target_client_id (existence, capability, same-actor), then
2058
+ // multi-client guard when no target is supplied. App-control is
2059
+ // single-client-only at the host (one active session per macOS
2060
+ // machine), so a broadcast across multiple capable clients would fire
2061
+ // the same input on every machine.
2062
+ let targetClientId: string | undefined =
2063
+ typeof input.target_client_id === "string" &&
2064
+ input.target_client_id !== ""
2065
+ ? input.target_client_id
2066
+ : undefined;
2067
+
2068
+ const sourceActorPrincipalId = ctx.trustContext?.guardianPrincipalId;
2069
+ if (targetClientId != null) {
2070
+ const client = assistantEventHub.getClientById(targetClientId);
2071
+ if (!client) {
2072
+ return {
2073
+ content: `No connected client with id '${targetClientId}'. Run \`assistant clients list --capability host_app_control\` to see available clients.`,
2074
+ isError: true,
2075
+ };
2076
+ }
2077
+ if (!client.capabilities.includes("host_app_control")) {
2078
+ return {
2079
+ content: `Client '${targetClientId}' does not support host_app_control. Run \`assistant clients list --capability host_app_control\` to see available clients.`,
2080
+ isError: true,
2081
+ };
2082
+ }
2083
+ const rejection = enforceSameActorOrErrorResult({
2084
+ hub: assistantEventHub,
2085
+ sourceActorPrincipalId,
2086
+ targetClientId,
2087
+ op: "host_app_control",
2088
+ });
2089
+ if (rejection) return rejection;
2090
+ }
2091
+
2092
+ if (targetClientId == null) {
2093
+ const allAcClients =
2094
+ assistantEventHub.listClientsByCapability("host_app_control");
2095
+ const sameUserAcClients = allAcClients.filter(
2096
+ (c) => c.actorPrincipalId === sourceActorPrincipalId,
2097
+ );
2098
+ if (sameUserAcClients.length > 1) {
2099
+ return {
2100
+ content: `Error: multiple clients support host_app_control. Specify which client to target with \`target_client_id\`. Run \`assistant clients list --capability host_app_control\` to see client IDs and labels.`,
2101
+ isError: true,
2102
+ };
2103
+ }
2104
+ // When cross-user host_app_control clients are connected, auto-
2105
+ // resolve to the unique same-user client. Otherwise the proxy would
2106
+ // dispatch untargeted and the action could reach a cross-user
2107
+ // client. Belt-and-suspenders: the proxy re-checks same-user.
2108
+ if (sameUserAcClients.length === 1 && allAcClients.length > 1) {
2109
+ targetClientId = sameUserAcClients[0].clientId;
2110
+ }
2111
+ }
2112
+
2051
2113
  // The TS `HostAppControlInput` (and the Swift mirror) is a discriminated
2052
2114
  // union on `tool` ("start" | "observe" | "press" | …). The agent's raw
2053
2115
  // tool input only carries the action-specific payload (app, x/y, text,
@@ -2066,6 +2128,8 @@ export async function surfaceProxyResolver(
2066
2128
  inputWithTool,
2067
2129
  ctx.conversationId,
2068
2130
  signal ?? new AbortController().signal,
2131
+ sourceActorPrincipalId,
2132
+ targetClientId,
2069
2133
  );
2070
2134
  }
2071
2135