@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
@@ -10,7 +10,6 @@ import {
10
10
  import { emitContactChange } from "./contact-events.js";
11
11
  import type {
12
12
  AssistantContactMetadata,
13
- AssistantSpecies,
14
13
  ChannelPolicy,
15
14
  ChannelStatus,
16
15
  Contact,
@@ -1013,66 +1012,6 @@ function parseAssistantMetadata(
1013
1012
  } as AssistantContactMetadata;
1014
1013
  }
1015
1014
 
1016
- /**
1017
- * Validate that metadata matches the expected shape for the given species.
1018
- * Enforces the invariant that makes the discriminated union cast in
1019
- * parseAssistantMetadata safe.
1020
- */
1021
- export function validateSpeciesMetadata(
1022
- species: AssistantSpecies,
1023
- metadata: Record<string, unknown> | null | undefined,
1024
- ): void {
1025
- if (metadata == null) return;
1026
-
1027
- if (species === "vellum") {
1028
- if (typeof metadata.assistantId !== "string" || !metadata.assistantId) {
1029
- throw new Error(
1030
- 'Vellum assistant metadata requires a non-empty "assistantId" string',
1031
- );
1032
- }
1033
- if (typeof metadata.gatewayUrl !== "string" || !metadata.gatewayUrl) {
1034
- throw new Error(
1035
- 'Vellum assistant metadata requires a non-empty "gatewayUrl" string',
1036
- );
1037
- }
1038
- }
1039
- }
1040
-
1041
- export function upsertAssistantContactMetadata(params: {
1042
- contactId: string;
1043
- species: AssistantSpecies;
1044
- metadata?: Record<string, unknown> | null;
1045
- }): AssistantContactMetadata {
1046
- validateSpeciesMetadata(params.species, params.metadata);
1047
-
1048
- const db = getDb();
1049
- const metadataJson =
1050
- params.metadata != null ? JSON.stringify(params.metadata) : null;
1051
-
1052
- db.insert(assistantContactMetadata)
1053
- .values({
1054
- contactId: params.contactId,
1055
- species: params.species,
1056
- metadata: metadataJson,
1057
- })
1058
- .onConflictDoUpdate({
1059
- target: assistantContactMetadata.contactId,
1060
- set: {
1061
- species: params.species,
1062
- metadata: metadataJson,
1063
- },
1064
- })
1065
- .run();
1066
-
1067
- const row = db
1068
- .select()
1069
- .from(assistantContactMetadata)
1070
- .where(eq(assistantContactMetadata.contactId, params.contactId))
1071
- .get();
1072
-
1073
- return parseAssistantMetadata(row!);
1074
- }
1075
-
1076
1015
  export function getAssistantContactMetadata(
1077
1016
  contactId: string,
1078
1017
  ): AssistantContactMetadata | null {
@@ -32,6 +32,19 @@ const COMPACTION_TOOL_RESULT_MAX_CHARS = 6_000;
32
32
  const MIN_COMPACTABLE_PERSISTED_MESSAGES = 2;
33
33
  const INTERNAL_CONTEXT_SUMMARY_MESSAGES = new WeakSet<Message>();
34
34
 
35
+ /**
36
+ * Hard cap on the verbatim tail-anchor block we splice into the
37
+ * post-compaction summary message (see `extractTailAssistantText`). 1500
38
+ * chars (~375 tokens) covers a few paragraphs of recent assistant
39
+ * narration without bloating the summary. When the tail exceeds this
40
+ * size we keep the END (most recent text), since "next step" / "now I'll
41
+ * …" statements typically live at the end of the assistant's last text
42
+ * block and that's the part the post-compaction model needs most.
43
+ */
44
+ const TAIL_ANCHOR_MAX_CHARS = 1500;
45
+ const TAIL_ANCHOR_OPEN_TAG = "<verbatim_tail>";
46
+ const TAIL_ANCHOR_CLOSE_TAG = "</verbatim_tail>";
47
+
35
48
  /**
36
49
  * When the existing summary is this fraction or more of the per-summary
37
50
  * token budget, inject a "compress older content aggressively" instruction
@@ -488,13 +501,45 @@ export class ContextWindowManager {
488
501
  };
489
502
  }
490
503
 
491
- const keepPlan = this.pickKeepBoundary(messages, userTurnStarts, {
504
+ const keepPlanInitial = this.pickKeepBoundary(messages, userTurnStarts, {
492
505
  minKeepRecentUserTurns: options?.minKeepRecentUserTurns,
493
506
  targetInputTokensOverride: options?.targetInputTokensOverride,
494
507
  conversationOriginChannel: options?.conversationOriginChannel,
495
508
  force: options?.force,
496
509
  previousEstimatedInputTokens,
497
510
  });
511
+ // Under force (user-explicit `/compact`), never route through the
512
+ // "already fits" / "truncated tool results without summarization"
513
+ // early-return — those are no-op responses to a direct user command.
514
+ // The boundary can collapse to the summary in two cases the
515
+ // projection-optimism clamp in pickKeepBoundary does not cover:
516
+ // 1. `adjustForToolPairs` walked the boundary back through a
517
+ // tool_use/tool_result chain at the start of the conversation.
518
+ // 2. The binary search settled below `userTurnStarts.length` (so
519
+ // the clamp at the top of pickKeepBoundary did not fire) but
520
+ // `adjustForToolPairs` still walked the resulting boundary
521
+ // backwards past `summaryOffset`.
522
+ // Rescue: restore the binary search's intended keep depth (capped at
523
+ // `length - 1` so we always summarize at least one turn) and bypass
524
+ // `adjustForToolPairs`. The kept region's first message may then
525
+ // contain a `tool_result` whose matching `tool_use` lives in the
526
+ // compacted region; we strip such orphans below before assembling
527
+ // the final messages array so the next agent turn does not fail
528
+ // when sending to the LLM.
529
+ const forceRescueApplied =
530
+ options?.force === true &&
531
+ keepPlanInitial.keepFromIndex <= summaryOffset &&
532
+ userTurnStarts.length >= 2;
533
+ const safeKeepTurns = Math.max(
534
+ 1,
535
+ Math.min(keepPlanInitial.keepTurns, userTurnStarts.length - 1),
536
+ );
537
+ const keepPlan = forceRescueApplied
538
+ ? {
539
+ keepFromIndex: userTurnStarts[userTurnStarts.length - safeKeepTurns],
540
+ keepTurns: safeKeepTurns,
541
+ }
542
+ : keepPlanInitial;
498
543
  if (keepPlan.keepFromIndex <= summaryOffset) {
499
544
  // All turns fit after truncation projection, but the real in-memory
500
545
  // messages may still contain un-truncated tool results. Apply truncation
@@ -511,6 +556,14 @@ export class ContextWindowManager {
511
556
  toolTokenBudget: this.toolTokenBudget,
512
557
  })
513
558
  : previousEstimatedInputTokens;
559
+ // Under force with only one user turn, the rescue above could not
560
+ // fire — there is nothing earlier to summarize. Surface that
561
+ // explicitly instead of "conversation already fits..." so the user
562
+ // knows why `/compact` did not produce a summary.
563
+ const noSummarizationReason =
564
+ options?.force && userTurnStarts.length < 2
565
+ ? "only one user turn — nothing earlier to compact"
566
+ : "conversation already fits within the compaction target";
514
567
  return {
515
568
  messages: truncatedMessages,
516
569
  compacted: didTruncate,
@@ -527,7 +580,7 @@ export class ContextWindowManager {
527
580
  summaryText: existingSummary ?? "",
528
581
  reason: didTruncate
529
582
  ? "truncated tool results without summarization"
530
- : "conversation already fits within the compaction target",
583
+ : noSummarizationReason,
531
584
  };
532
585
  }
533
586
 
@@ -645,9 +698,15 @@ export class ContextWindowManager {
645
698
  };
646
699
  }
647
700
 
701
+ // `severePressure` already bypasses this guard to keep context from
702
+ // overflowing. Forced compaction also bypasses: when the user
703
+ // explicitly types `/compact` we must summarize whatever is
704
+ // available rather than return "insufficient compactable persisted
705
+ // messages" — that is a no-op response to a direct user command.
648
706
  if (
649
707
  compactedPersistedMessages < MIN_COMPACTABLE_PERSISTED_MESSAGES &&
650
- !severePressure
708
+ !severePressure &&
709
+ !options?.force
651
710
  ) {
652
711
  return {
653
712
  messages,
@@ -688,7 +747,6 @@ export class ContextWindowManager {
688
747
  signal,
689
748
  options?.overrideProfile ?? null,
690
749
  );
691
- const summary = summaryUpdate.summary;
692
750
  const summaryInputTokens = summaryUpdate.inputTokens;
693
751
  const summaryOutputTokens = summaryUpdate.outputTokens;
694
752
  const summaryModel = summaryUpdate.model;
@@ -704,6 +762,19 @@ export class ContextWindowManager {
704
762
  }
705
763
  const summaryCalls = 1;
706
764
 
765
+ // Force-keep the most recent assistant text from the compactable region
766
+ // by splicing it verbatim into the summary message. This is independent
767
+ // of what the LLM summarizer chose to surface — when compaction
768
+ // interrupts a long assistant work span, this anchor preserves the
769
+ // model's last self-narration ("Next step: …", "About to …") so the
770
+ // post-compaction model has unambiguous continuity instead of falling
771
+ // back to a "where am I?" recovery shape.
772
+ const tailAnchorText = extractTailAssistantText(compactableMessages);
773
+ const summary =
774
+ tailAnchorText != null
775
+ ? appendTailAnchorToSummary(summaryUpdate.summary, tailAnchorText)
776
+ : summaryUpdate.summary;
777
+
707
778
  // Media (images, files) in kept turns is preserved naturally — those
708
779
  // turns are carried forward as-is and their token cost is already
709
780
  // accounted for by pickKeepBoundary's estimatePromptTokens call.
@@ -716,7 +787,14 @@ export class ContextWindowManager {
716
787
  messages.slice(keepPlan.keepFromIndex),
717
788
  COMPACTION_TOOL_RESULT_MAX_CHARS,
718
789
  );
719
- const compactedMessages = [summaryMessage, ...truncatedKeptMessages];
790
+ // The force-rescue boundary bypasses `adjustForToolPairs`, so the
791
+ // kept region may contain `tool_result` blocks whose matching
792
+ // `tool_use` is in the (now-compacted) prefix. Strip those orphans
793
+ // so the next agent turn does not fail with an LLM API error.
794
+ const keptMessages = forceRescueApplied
795
+ ? stripOrphanToolResults(truncatedKeptMessages)
796
+ : truncatedKeptMessages;
797
+ const compactedMessages = [summaryMessage, ...keptMessages];
720
798
  const estimatedInputTokens = estimatePromptTokens(
721
799
  compactedMessages,
722
800
  this.systemPrompt,
@@ -1251,6 +1329,53 @@ function adjustForToolPairs(
1251
1329
  return idx;
1252
1330
  }
1253
1331
 
1332
+ /**
1333
+ * Strip `tool_result` blocks whose matching `tool_use` is not present in
1334
+ * the message array. Used by the force-rescue path in `_maybeCompact`
1335
+ * which bypasses `adjustForToolPairs` to honor user-explicit `/compact`
1336
+ * commands — the kept region's first user message can otherwise contain
1337
+ * an orphan `tool_result`, which the LLM API rejects.
1338
+ *
1339
+ * A user message that contains only orphan `tool_result` blocks is
1340
+ * dropped entirely; partial messages keep the surviving content blocks.
1341
+ */
1342
+ function stripOrphanToolResults(messages: Message[]): Message[] {
1343
+ const knownToolUseIds = new Set<string>();
1344
+ for (const msg of messages) {
1345
+ if (msg.role !== "assistant") continue;
1346
+ for (const block of msg.content) {
1347
+ if (
1348
+ (block.type === "tool_use" || block.type === "server_tool_use") &&
1349
+ "id" in block
1350
+ ) {
1351
+ knownToolUseIds.add((block as { id: string }).id);
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ return messages.flatMap((msg) => {
1357
+ if (msg.role !== "user") return [msg];
1358
+ let stripped = false;
1359
+ const filtered = msg.content.filter((block) => {
1360
+ if (
1361
+ (block.type === "tool_result" ||
1362
+ block.type === "web_search_tool_result") &&
1363
+ "tool_use_id" in block
1364
+ ) {
1365
+ const id = (block as { tool_use_id: string }).tool_use_id;
1366
+ if (!knownToolUseIds.has(id)) {
1367
+ stripped = true;
1368
+ return false;
1369
+ }
1370
+ }
1371
+ return true;
1372
+ });
1373
+ if (!stripped) return [msg];
1374
+ if (filtered.length === 0) return [];
1375
+ return [{ ...msg, content: filtered }];
1376
+ });
1377
+ }
1378
+
1254
1379
  export function getSummaryFromContextMessage(
1255
1380
  message: Message | undefined,
1256
1381
  ): string | null {
@@ -1286,6 +1411,67 @@ export function createContextSummaryMessage(summary: string): Message {
1286
1411
  return message;
1287
1412
  }
1288
1413
 
1414
+ /**
1415
+ * Walk `messages` backward and return the concatenated text content of the
1416
+ * most recent assistant message that contains at least one non-empty text
1417
+ * block. tool_use / tool_result / image / unknown blocks are skipped. The
1418
+ * result is trimmed and (if longer than `maxChars`) clamped from the START
1419
+ * so the END — where "next step" / "now I'll …" narration tends to land —
1420
+ * is preserved.
1421
+ *
1422
+ * Returns `null` when no eligible assistant text is found (e.g. compactable
1423
+ * region was all user/tool messages, or all assistant messages were
1424
+ * tool_use-only). The caller treats `null` as "no anchor to splice".
1425
+ *
1426
+ * Used by `_maybeCompact` to force-keep the last assistant text from the
1427
+ * compactable region into the post-compaction summary message, so the
1428
+ * model's most recent self-narration survives summarization regardless of
1429
+ * whether the LLM summarizer chose to surface it.
1430
+ */
1431
+ export function extractTailAssistantText(
1432
+ messages: Message[],
1433
+ maxChars: number = TAIL_ANCHOR_MAX_CHARS,
1434
+ ): string | null {
1435
+ for (let i = messages.length - 1; i >= 0; i--) {
1436
+ const message = messages[i];
1437
+ if (message?.role !== "assistant") continue;
1438
+ const text = extractText(message.content).trim();
1439
+ if (text.length === 0) continue;
1440
+ if (text.length <= maxChars) return text;
1441
+ // Keep the END — most recent narration wins.
1442
+ const truncated = safeStringSlice(
1443
+ text,
1444
+ text.length - maxChars,
1445
+ text.length,
1446
+ );
1447
+ return `[...truncated] ${truncated}`;
1448
+ }
1449
+ return null;
1450
+ }
1451
+
1452
+ /**
1453
+ * Splice a verbatim tail-anchor block onto the end of the LLM-produced
1454
+ * summary text. The tag-wrapped block is structurally distinct from any
1455
+ * `## ` section the LLM might generate, so it survives section-boundary
1456
+ * clamping in `clampSummaryAtSectionBoundary` (which only runs on the LLM
1457
+ * summary itself, before this splice).
1458
+ *
1459
+ * Idempotent: if the summary already ends with a `<verbatim_tail>…` block
1460
+ * (e.g. from a prior compaction whose summary was carried forward as
1461
+ * `existingSummary`), it is replaced rather than stacked, so successive
1462
+ * compactions don't accumulate stale tails.
1463
+ */
1464
+ export function appendTailAnchorToSummary(
1465
+ summary: string,
1466
+ tailText: string,
1467
+ ): string {
1468
+ const trimmed = summary.trimEnd();
1469
+ const existingOpen = trimmed.lastIndexOf(TAIL_ANCHOR_OPEN_TAG);
1470
+ const base =
1471
+ existingOpen >= 0 ? trimmed.slice(0, existingOpen).trimEnd() : trimmed;
1472
+ return `${base}\n\n${TAIL_ANCHOR_OPEN_TAG}\n${tailText.trim()}\n${TAIL_ANCHOR_CLOSE_TAG}`;
1473
+ }
1474
+
1289
1475
  /**
1290
1476
  * Build content blocks for the summary prompt. Returns a mix of text blocks
1291
1477
  * (for the scaffolding, existing summary, and serialized non-image content)
@@ -91,6 +91,27 @@ mock.module("../../memory/auto-analysis-enqueue.js", () => ({
91
91
  },
92
92
  }));
93
93
 
94
+ let memoryRetroEnabled = false;
95
+ const memoryRetroCalls: Array<{
96
+ conversationId: string;
97
+ trigger: string;
98
+ }> = [];
99
+
100
+ mock.module("../../memory/memory-retrospective-enqueue.js", () => ({
101
+ enqueueMemoryRetrospectiveIfEnabled: (args: {
102
+ conversationId: string;
103
+ trigger: string;
104
+ }) => {
105
+ if (!memoryRetroEnabled) return;
106
+ memoryRetroCalls.push(args);
107
+ },
108
+ // Also export sibling functions other modules import from this file, so
109
+ // mocking it here doesn't break transitive imports loaded during the
110
+ // `disposeConversation` dynamic-import chain.
111
+ enqueueMemoryRetrospectiveOnCompaction: () => {},
112
+ isMemoryRetrospectiveConversation: (_id: string) => false,
113
+ }));
114
+
94
115
  // Stub all side-effecting cleanup helpers that disposeConversation chains
95
116
  // into after the enqueue block. We assert on enqueue behavior only.
96
117
  mock.module("../../tools/browser/browser-screencast.js", () => ({
@@ -168,7 +189,9 @@ describe("disposeConversation — auto-analysis enqueue", () => {
168
189
  beforeEach(() => {
169
190
  memoryJobCalls.length = 0;
170
191
  autoAnalyzeCalls.length = 0;
192
+ memoryRetroCalls.length = 0;
171
193
  autoAnalyzeEnabled = true;
194
+ memoryRetroEnabled = false;
172
195
  autoAnalysisConversations.clear();
173
196
  v2Enabled = false;
174
197
  });
@@ -346,3 +369,59 @@ describe("disposeConversation — auto-analysis enqueue", () => {
346
369
  });
347
370
  });
348
371
  });
372
+
373
+ describe("disposeConversation — memory-retrospective lifecycle safety net", () => {
374
+ beforeEach(() => {
375
+ memoryJobCalls.length = 0;
376
+ autoAnalyzeCalls.length = 0;
377
+ memoryRetroCalls.length = 0;
378
+ autoAnalyzeEnabled = false;
379
+ memoryRetroEnabled = false;
380
+ autoAnalysisConversations.clear();
381
+ v2Enabled = false;
382
+ });
383
+
384
+ test("guardian conversation + flag on — enqueues memory-retrospective with trigger 'lifecycle'", () => {
385
+ memoryRetroEnabled = true;
386
+ const ctx = makeDisposeContext({
387
+ conversationId: "conv-retro",
388
+ trustClass: "guardian",
389
+ });
390
+
391
+ disposeConversation(ctx);
392
+
393
+ expect(memoryRetroCalls).toHaveLength(1);
394
+ expect(memoryRetroCalls[0]).toEqual({
395
+ conversationId: "conv-retro",
396
+ trigger: "lifecycle",
397
+ });
398
+ });
399
+
400
+ test("flag off — no memory-retrospective enqueue", () => {
401
+ memoryRetroEnabled = false;
402
+ const ctx = makeDisposeContext({
403
+ conversationId: "conv-retro-off",
404
+ trustClass: "guardian",
405
+ });
406
+
407
+ disposeConversation(ctx);
408
+
409
+ expect(memoryRetroCalls).toHaveLength(0);
410
+ });
411
+
412
+ test("untrusted actor — no memory-retrospective enqueue even when flag is on", () => {
413
+ memoryRetroEnabled = true;
414
+ const ctx = makeDisposeContext({
415
+ conversationId: "conv-retro-untrusted",
416
+ trustClass: "unknown",
417
+ });
418
+
419
+ disposeConversation(ctx);
420
+
421
+ // The outer trust-class guard in disposeConversation gates ALL three
422
+ // enqueues (graph_extract, auto-analyze, memory-retrospective). When
423
+ // the actor is untrusted, none of them fire.
424
+ expect(memoryRetroCalls).toHaveLength(0);
425
+ expect(autoAnalyzeCalls).toHaveLength(0);
426
+ });
427
+ });
@@ -20,10 +20,11 @@
20
20
  * hasNoClient flag.
21
21
  *
22
22
  * Cross-client exception: tools whose capabilities are in
23
- * CROSS_CLIENT_EXPOSED_CAPABILITIES (host_bash, host_file) are allowed for
24
- * non-host-proxy interfaces (e.g. "web") when at least one capable client
25
- * is connected via the event hub. host_browser is excluded (chrome-extension
26
- * is its own executor; web turns have no CDP target model).
23
+ * CROSS_CLIENT_EXPOSED_CAPABILITIES (host_bash, host_file, host_browser)
24
+ * are allowed for non-host-proxy interactive interfaces ("web", "ios")
25
+ * when at least one capable client is connected via the event hub.
26
+ * chrome-extension is excluded as a security boundary, regardless of
27
+ * whether the capability is technically supported elsewhere.
27
28
  */
28
29
 
29
30
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -358,6 +359,110 @@ describe("isToolActiveForContext — cross-client exposure for host_file_*", ()
358
359
  });
359
360
  });
360
361
 
362
+ describe("isToolActiveForContext — cross-client exposure for host_browser", () => {
363
+ // host_browser cross-client routing was shipped in PR #27489 (host-
364
+ // browser-via-macos-host-proxy); LLM-exposure for non-host-proxy
365
+ // transports is added by including "host_browser" in
366
+ // CROSS_CLIENT_EXPOSED_CAPABILITIES. Web and iOS turns can now drive a
367
+ // connected macOS or chrome-extension client via the event hub.
368
+ test("host_browser is exposed for web transport when a host_browser client is connected", () => {
369
+ mockClientCountByCapability.set("host_browser", 1);
370
+ expect(
371
+ isToolActiveForContext(
372
+ "host_browser",
373
+ makeCtx({ hasNoClient: false, transportInterface: "web" }),
374
+ ),
375
+ ).toBe(true);
376
+ });
377
+
378
+ test("host_browser is exposed for ios transport when a host_browser client is connected", () => {
379
+ // INTERACTIVE_INTERFACES = {macos, ios, web}; ios goes through the same
380
+ // cross-client branch as web because supportsHostProxy("ios", *) is
381
+ // false. This pins the parity guarantee.
382
+ mockClientCountByCapability.set("host_browser", 1);
383
+ expect(
384
+ isToolActiveForContext(
385
+ "host_browser",
386
+ makeCtx({ hasNoClient: false, transportInterface: "ios" }),
387
+ ),
388
+ ).toBe(true);
389
+ });
390
+
391
+ test("host_browser is NOT exposed for web when no host_browser client is connected", () => {
392
+ mockClientCountByCapability.set("host_browser", 0);
393
+ expect(
394
+ isToolActiveForContext(
395
+ "host_browser",
396
+ makeCtx({ hasNoClient: false, transportInterface: "web" }),
397
+ ),
398
+ ).toBe(false);
399
+ });
400
+
401
+ test("host_browser is NOT exposed for ios when no host_browser client is connected", () => {
402
+ mockClientCountByCapability.set("host_browser", 0);
403
+ expect(
404
+ isToolActiveForContext(
405
+ "host_browser",
406
+ makeCtx({ hasNoClient: false, transportInterface: "ios" }),
407
+ ),
408
+ ).toBe(false);
409
+ });
410
+
411
+ test("host_browser is NOT exposed when hasNoClient is true (no approval UI)", () => {
412
+ // hasNoClient gate: cross-client exception must not bypass this.
413
+ mockClientCountByCapability.set("host_browser", 1);
414
+ expect(
415
+ isToolActiveForContext(
416
+ "host_browser",
417
+ makeCtx({ hasNoClient: true, transportInterface: "web" }),
418
+ ),
419
+ ).toBe(false);
420
+ });
421
+
422
+ test("host_browser for macos transport is unaffected by the cross-client exception", () => {
423
+ // macos natively supports host_browser via host proxy — the
424
+ // supportsHostProxy check passes, so the cross-client branch is never
425
+ // reached.
426
+ mockClientCountByCapability.set("host_browser", 0);
427
+ expect(
428
+ isToolActiveForContext(
429
+ "host_browser",
430
+ makeCtx({ hasNoClient: false, transportInterface: "macos" }),
431
+ ),
432
+ ).toBe(true);
433
+ });
434
+
435
+ test("host_browser for chrome-extension transport is unaffected by the cross-client exception", () => {
436
+ // chrome-extension natively supports host_browser via its own
437
+ // executor (supportsHostProxy("chrome-extension", "host_browser")
438
+ // returns true), so the cross-client branch is never reached. The
439
+ // hasNoClient gate is also bypassed for chrome-extension transports
440
+ // because the extension provides its own approval UI.
441
+ mockClientCountByCapability.set("host_browser", 0);
442
+ expect(
443
+ isToolActiveForContext(
444
+ "host_browser",
445
+ makeCtx({ hasNoClient: true, transportInterface: "chrome-extension" }),
446
+ ),
447
+ ).toBe(true);
448
+ });
449
+
450
+ test("listClientsByCapability is queried with host_browser, not host_bash or host_file (per-capability invariant)", () => {
451
+ // Defense against any future regression that hardcodes a different
452
+ // capability in the cross-client check. Only host_browser-capable
453
+ // clients should satisfy host_browser exposure.
454
+ mockClientCountByCapability.set("host_bash", 1);
455
+ mockClientCountByCapability.set("host_file", 1);
456
+ mockClientCountByCapability.set("host_browser", 0);
457
+ expect(
458
+ isToolActiveForContext(
459
+ "host_browser",
460
+ makeCtx({ hasNoClient: false, transportInterface: "web" }),
461
+ ),
462
+ ).toBe(false);
463
+ });
464
+ });
465
+
361
466
  describe("HOST_TOOL_NAMES derivation", () => {
362
467
  test("HOST_TOOL_NAMES is derived from HOST_TOOL_TO_CAPABILITY", () => {
363
468
  // Sanity check: every tool in the names set has a capability mapping.
@@ -23,8 +23,9 @@ const loggerSpy = {
23
23
  warn: mock(() => {}),
24
24
  error: mock(() => {}),
25
25
  };
26
+ const getLoggerSpy = mock((_name: string) => loggerSpy);
26
27
  mock.module("../../util/logger.js", () => ({
27
- getLogger: () => loggerSpy,
28
+ getLogger: getLoggerSpy,
28
29
  }));
29
30
 
30
31
  mock.module("../../config/loader.js", () => ({
@@ -163,12 +164,13 @@ describe("createDaemonSkillHost", () => {
163
164
  expect(host.speakers).toBeDefined();
164
165
  });
165
166
 
166
- test("logger.get returns an object with the four severity methods", () => {
167
+ test("logger.get prefixes the scope with the skillId", () => {
167
168
  const log = host.logger.get("test-scope");
168
169
  log.debug("d", { k: "v" });
169
170
  log.info("i");
170
171
  log.warn("w");
171
172
  log.error("e");
173
+ expect(getLoggerSpy).toHaveBeenCalledWith("meet-join:test-scope");
172
174
  expect(loggerSpy.debug).toHaveBeenCalled();
173
175
  expect(loggerSpy.info).toHaveBeenCalled();
174
176
  expect(loggerSpy.warn).toHaveBeenCalled();
@@ -255,8 +257,12 @@ describe("createDaemonSkillHost", () => {
255
257
  });
256
258
  expect(handle).toBeDefined();
257
259
  expect(registerSkillRouteSpy).toHaveBeenCalled();
258
- host.registries.registerShutdownHook("test-hook", async () => {});
259
- expect(registerShutdownHookSpy).toHaveBeenCalled();
260
+ const hook = async () => {};
261
+ host.registries.registerShutdownHook("test-hook", hook);
262
+ expect(registerShutdownHookSpy).toHaveBeenCalledWith(
263
+ "meet-join:test-hook",
264
+ hook,
265
+ );
260
266
  });
261
267
 
262
268
  test("speakers.createTracker yields a concrete tracker instance", () => {