@vellumai/assistant 0.6.4 → 0.6.5

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 (717) hide show
  1. package/.prettierignore +5 -0
  2. package/ARCHITECTURE.md +32 -36
  3. package/Dockerfile +12 -0
  4. package/README.md +3 -4
  5. package/bun.lock +8 -3
  6. package/docs/architecture/integrations.md +1 -20
  7. package/docs/architecture/security.md +16 -16
  8. package/docs/error-handling.md +111 -0
  9. package/docs/skills.md +10 -10
  10. package/docs/stt-provider-onboarding.md +2 -1
  11. package/knip.json +9 -2
  12. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  13. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  14. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  15. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  16. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  17. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  18. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  19. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  20. package/openapi.yaml +123 -11
  21. package/package.json +6 -3
  22. package/scripts/generate-openapi.ts +50 -11
  23. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  24. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  25. package/src/__tests__/agent-loop.test.ts +112 -1
  26. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  27. package/src/__tests__/anthropic-provider.test.ts +171 -2
  28. package/src/__tests__/approval-cascade.test.ts +31 -10
  29. package/src/__tests__/approval-routes-http.test.ts +134 -10
  30. package/src/__tests__/assistant-attachments.test.ts +44 -0
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  32. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  33. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  34. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  35. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  36. package/src/__tests__/btw-routes.test.ts +47 -1
  37. package/src/__tests__/call-controller.test.ts +1 -2
  38. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  39. package/src/__tests__/catalog-cache.test.ts +27 -4
  40. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  41. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  42. package/src/__tests__/checker.test.ts +428 -501
  43. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  44. package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
  45. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  46. package/src/__tests__/config-analysis.test.ts +11 -28
  47. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  48. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  49. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  50. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  51. package/src/__tests__/config-schema.test.ts +427 -114
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  54. package/src/__tests__/contacts-write.test.ts +4 -4
  55. package/src/__tests__/context-token-estimator.test.ts +191 -1
  56. package/src/__tests__/context-window-manager.test.ts +530 -2
  57. package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
  58. package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
  59. package/src/__tests__/conversation-agent-loop.test.ts +412 -82
  60. package/src/__tests__/conversation-attachments.test.ts +1 -1
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
  62. package/src/__tests__/conversation-error.test.ts +37 -6
  63. package/src/__tests__/conversation-history-web-search.test.ts +6 -0
  64. package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
  65. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  66. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  67. package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
  68. package/src/__tests__/conversation-process-callsite.test.ts +306 -0
  69. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
  70. package/src/__tests__/conversation-queue.test.ts +41 -26
  71. package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
  72. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
  74. package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
  75. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  76. package/src/__tests__/conversation-slash-queue.test.ts +34 -19
  77. package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
  78. package/src/__tests__/conversation-speed-override.test.ts +30 -11
  79. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  80. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  81. package/src/__tests__/conversation-title-service.test.ts +2 -2
  82. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  83. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  84. package/src/__tests__/conversation-usage.test.ts +3 -1
  85. package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
  86. package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
  87. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
  88. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  89. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  90. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  91. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  92. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  93. package/src/__tests__/credentials-cli.test.ts +1 -9
  94. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  95. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  96. package/src/__tests__/delete-propagation.test.ts +437 -0
  97. package/src/__tests__/dm-backfill.test.ts +417 -0
  98. package/src/__tests__/dm-persistence.test.ts +227 -0
  99. package/src/__tests__/edit-propagation.test.ts +280 -0
  100. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  101. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  102. package/src/__tests__/estimator-calibration.test.ts +213 -0
  103. package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
  104. package/src/__tests__/file-write-tool.test.ts +151 -1
  105. package/src/__tests__/filing-service.test.ts +255 -0
  106. package/src/__tests__/gemini-provider.test.ts +0 -3
  107. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  108. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  109. package/src/__tests__/heartbeat-service.test.ts +96 -15
  110. package/src/__tests__/host-shell-tool.test.ts +124 -18
  111. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  112. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  113. package/src/__tests__/intent-routing.test.ts +1 -40
  114. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  115. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  116. package/src/__tests__/llm-resolver.test.ts +214 -0
  117. package/src/__tests__/llm-schema.test.ts +223 -0
  118. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  119. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  120. package/src/__tests__/migration-import-from-url.test.ts +684 -0
  121. package/src/__tests__/model-intents.test.ts +9 -83
  122. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  123. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  124. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  125. package/src/__tests__/oauth-store.test.ts +10 -7
  126. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  127. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  128. package/src/__tests__/openai-provider.test.ts +7 -0
  129. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  130. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  131. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  132. package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
  133. package/src/__tests__/permission-mode.test.ts +16 -0
  134. package/src/__tests__/permission-types.test.ts +0 -1
  135. package/src/__tests__/persona-resolver.test.ts +13 -13
  136. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  137. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  138. package/src/__tests__/pricing.test.ts +50 -3
  139. package/src/__tests__/profiler-routes.test.ts +1 -1
  140. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  141. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  143. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  144. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  145. package/src/__tests__/proxy-approval-callback.test.ts +0 -1
  146. package/src/__tests__/reaction-persistence.test.ts +560 -0
  147. package/src/__tests__/relay-server.test.ts +1 -1
  148. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  149. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  150. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  151. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  152. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  153. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  154. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  156. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  157. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  158. package/src/__tests__/server-history-render.test.ts +31 -0
  159. package/src/__tests__/shell-parser-property.test.ts +13 -13
  160. package/src/__tests__/skill-cache-store.test.ts +182 -0
  161. package/src/__tests__/skills.test.ts +19 -33
  162. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  163. package/src/__tests__/slack-skill.test.ts +3 -8
  164. package/src/__tests__/starter-bundle.test.ts +35 -0
  165. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  166. package/src/__tests__/suggestion-routes.test.ts +160 -3
  167. package/src/__tests__/system-prompt.test.ts +22 -35
  168. package/src/__tests__/task-runner.test.ts +3 -1
  169. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  170. package/src/__tests__/terminal-tools.test.ts +8 -0
  171. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  172. package/src/__tests__/thread-backfill.test.ts +941 -0
  173. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
  174. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  175. package/src/__tests__/tool-executor.test.ts +60 -94
  176. package/src/__tests__/trust-store.test.ts +442 -109
  177. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  178. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  179. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  180. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  181. package/src/__tests__/volume-security-guard.test.ts +3 -2
  182. package/src/__tests__/web-search-history.test.ts +337 -0
  183. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  184. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  185. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  186. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  187. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  188. package/src/__tests__/workspace-policy.test.ts +1 -13
  189. package/src/acp/client-handler.ts +1 -2
  190. package/src/agent/loop.ts +209 -17
  191. package/src/avatar/resvg-lazy.test.ts +136 -0
  192. package/src/avatar/resvg-lazy.ts +82 -9
  193. package/src/avatar/traits-png-sync.ts +21 -1
  194. package/src/browser/__tests__/operations.test.ts +163 -0
  195. package/src/browser/identifiers.ts +51 -0
  196. package/src/browser/operations.ts +660 -0
  197. package/src/browser/types.ts +81 -0
  198. package/src/calls/guardian-question-copy.ts +2 -2
  199. package/src/calls/telephony-stt-routing.ts +1 -1
  200. package/src/calls/voice-session-bridge.ts +1 -0
  201. package/src/cli/AGENTS.md +1 -1
  202. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  203. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  204. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  205. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  206. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  207. package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
  208. package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
  209. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  210. package/src/cli/commands/__tests__/task.test.ts +913 -0
  211. package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
  212. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  213. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  214. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  215. package/src/cli/commands/attachment.ts +182 -0
  216. package/src/cli/commands/browser.ts +350 -0
  217. package/src/cli/commands/cache.ts +341 -0
  218. package/src/cli/commands/completions.ts +0 -3
  219. package/src/cli/commands/config.ts +6 -6
  220. package/src/cli/commands/conversations-import.ts +347 -0
  221. package/src/cli/commands/conversations.ts +14 -1
  222. package/src/cli/commands/email.ts +234 -194
  223. package/src/cli/commands/image-generation.ts +300 -0
  224. package/src/cli/commands/inference.ts +200 -0
  225. package/src/cli/commands/memory.ts +127 -17
  226. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  227. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  228. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  229. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  230. package/src/cli/commands/stt.ts +339 -0
  231. package/src/cli/commands/task.ts +795 -0
  232. package/src/cli/commands/trust.ts +50 -19
  233. package/src/cli/commands/tts.ts +273 -0
  234. package/src/cli/commands/ui.ts +670 -0
  235. package/src/cli/commands/watchers.ts +509 -0
  236. package/src/cli/lib/daemon-credential-client.ts +0 -19
  237. package/src/cli/program.ts +23 -4
  238. package/src/cli.ts +0 -37
  239. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
  240. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  241. package/src/config/bundled-skills/messaging/SKILL.md +2 -2
  242. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  243. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
  244. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  245. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  246. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
  247. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  248. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  249. package/src/config/bundled-tool-registry.ts +0 -175
  250. package/src/config/env.ts +7 -2
  251. package/src/config/feature-flag-registry.json +25 -9
  252. package/src/config/llm-resolver.ts +128 -0
  253. package/src/config/loader.ts +194 -10
  254. package/src/config/raw-config-utils.ts +30 -2
  255. package/src/config/sanitize-for-transfer.ts +35 -0
  256. package/src/config/schema.ts +30 -41
  257. package/src/config/schemas/analysis.ts +3 -22
  258. package/src/config/schemas/calls.ts +0 -4
  259. package/src/config/schemas/filing.ts +2 -7
  260. package/src/config/schemas/heartbeat.ts +0 -5
  261. package/src/config/schemas/inference.ts +3 -23
  262. package/src/config/schemas/llm.ts +318 -0
  263. package/src/config/schemas/memory-processing.ts +1 -9
  264. package/src/config/schemas/notifications.ts +4 -11
  265. package/src/config/schemas/platform.ts +3 -9
  266. package/src/config/schemas/security.ts +33 -0
  267. package/src/config/schemas/services.ts +9 -4
  268. package/src/config/schemas/stt.ts +1 -0
  269. package/src/config/schemas/tts.ts +53 -0
  270. package/src/config/schemas/updates.ts +1 -1
  271. package/src/config/schemas/workspace-git.ts +3 -40
  272. package/src/config/skills.ts +2 -2
  273. package/src/context/__tests__/compact-prompt.test.ts +45 -0
  274. package/src/context/__tests__/microcompact.test.ts +805 -0
  275. package/src/context/estimator-calibration.ts +136 -0
  276. package/src/context/microcompact.ts +443 -0
  277. package/src/context/prompts/compact.md +12 -0
  278. package/src/context/token-estimator.ts +61 -3
  279. package/src/context/window-manager.ts +229 -25
  280. package/src/credential-execution/approval-bridge.ts +0 -1
  281. package/src/credential-execution/executable-discovery.ts +19 -8
  282. package/src/credential-execution/process-manager.test.ts +109 -0
  283. package/src/credential-execution/process-manager.ts +65 -2
  284. package/src/daemon/approval-generators.ts +29 -4
  285. package/src/daemon/assistant-attachments.ts +24 -13
  286. package/src/daemon/classifier.ts +2 -2
  287. package/src/daemon/config-watcher.ts +0 -1
  288. package/src/daemon/context-overflow-reducer.ts +4 -1
  289. package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
  290. package/src/daemon/conversation-agent-loop.ts +462 -80
  291. package/src/daemon/conversation-attachments.ts +2 -6
  292. package/src/daemon/conversation-error.ts +36 -1
  293. package/src/daemon/conversation-lifecycle.ts +30 -6
  294. package/src/daemon/conversation-messaging.ts +73 -4
  295. package/src/daemon/conversation-process.ts +10 -4
  296. package/src/daemon/conversation-queue-manager.ts +3 -0
  297. package/src/daemon/conversation-runtime-assembly.ts +760 -29
  298. package/src/daemon/conversation-slash.ts +2 -2
  299. package/src/daemon/conversation-surfaces.ts +389 -1
  300. package/src/daemon/conversation-tool-setup.ts +10 -5
  301. package/src/daemon/conversation-usage.ts +1 -1
  302. package/src/daemon/conversation.ts +118 -30
  303. package/src/daemon/external-skills-bootstrap.ts +41 -0
  304. package/src/daemon/guardian-action-generators.ts +34 -14
  305. package/src/daemon/handlers/config-model.test.ts +86 -0
  306. package/src/daemon/handlers/config-model.ts +54 -12
  307. package/src/daemon/handlers/conversations.ts +9 -2
  308. package/src/daemon/handlers/shared.ts +39 -11
  309. package/src/daemon/handlers/skills.ts +2 -2
  310. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  311. package/src/daemon/lifecycle.ts +76 -14
  312. package/src/daemon/message-types/conversations.ts +14 -0
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/trust.ts +0 -2
  315. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  316. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  317. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  318. package/src/daemon/pkb-context-tracker.ts +125 -0
  319. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  320. package/src/daemon/pkb-reminder-builder.ts +31 -0
  321. package/src/daemon/providers-setup.ts +6 -0
  322. package/src/daemon/server.ts +117 -9
  323. package/src/daemon/tool-side-effects.ts +0 -9
  324. package/src/daemon/watch-handler.ts +4 -4
  325. package/src/daemon/web-search-history.ts +126 -0
  326. package/src/events/domain-events.ts +0 -1
  327. package/src/filing/filing-service.ts +9 -10
  328. package/src/heartbeat/heartbeat-service.ts +76 -28
  329. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  330. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  331. package/src/home/assistant-feed-authoring.ts +4 -0
  332. package/src/home/emit-feed-event.ts +4 -0
  333. package/src/home/feed-scheduler.ts +20 -4
  334. package/src/home/feed-types.ts +56 -2
  335. package/src/home/relationship-state-writer.ts +2 -2
  336. package/src/home/rollup-producer.ts +34 -5
  337. package/src/home/suggested-prompts.ts +101 -0
  338. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  339. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  340. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  341. package/src/ipc/__tests__/socket-path.test.ts +73 -0
  342. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  343. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  344. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  345. package/src/ipc/cli-client.ts +2 -1
  346. package/src/ipc/cli-server.ts +26 -8
  347. package/src/ipc/gateway-client.ts +4 -4
  348. package/src/ipc/routes/attachment.ts +114 -0
  349. package/src/ipc/routes/browser-context.ts +61 -0
  350. package/src/ipc/routes/browser.ts +96 -0
  351. package/src/ipc/routes/cache.ts +96 -0
  352. package/src/ipc/routes/index.ts +17 -1
  353. package/src/ipc/routes/task-queue.ts +226 -0
  354. package/src/ipc/routes/task.ts +173 -0
  355. package/src/ipc/routes/ui-request.ts +50 -0
  356. package/src/ipc/routes/watcher.ts +203 -0
  357. package/src/ipc/socket-path.ts +100 -0
  358. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  359. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  360. package/src/memory/admin.ts +18 -0
  361. package/src/memory/conversation-analyze-job.ts +14 -13
  362. package/src/memory/conversation-attention-store.ts +13 -6
  363. package/src/memory/conversation-crud.ts +103 -3
  364. package/src/memory/conversation-group-migration.ts +38 -6
  365. package/src/memory/conversation-title-service.ts +7 -4
  366. package/src/memory/db-init.ts +2 -0
  367. package/src/memory/embedding-backend.ts +1 -1
  368. package/src/memory/graph/compaction.ts +299 -0
  369. package/src/memory/graph/consolidation.ts +4 -4
  370. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  371. package/src/memory/graph/extraction.test.ts +272 -2
  372. package/src/memory/graph/extraction.ts +173 -51
  373. package/src/memory/graph/graph-search.test.ts +92 -0
  374. package/src/memory/graph/graph-search.ts +4 -1
  375. package/src/memory/graph/narrative.ts +2 -2
  376. package/src/memory/graph/pattern-scan.ts +2 -2
  377. package/src/memory/graph/retriever.test.ts +459 -0
  378. package/src/memory/graph/retriever.ts +230 -48
  379. package/src/memory/graph/store.ts +41 -0
  380. package/src/memory/graph/tool-handlers.ts +27 -0
  381. package/src/memory/graph/tools.ts +6 -1
  382. package/src/memory/indexer.ts +5 -5
  383. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  384. package/src/memory/job-handlers/summarization.ts +2 -2
  385. package/src/memory/job-utils.ts +7 -1
  386. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  387. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  388. package/src/memory/jobs-store.ts +44 -3
  389. package/src/memory/jobs-worker.ts +4 -0
  390. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  391. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  392. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  393. package/src/memory/migrations/index.ts +1 -0
  394. package/src/memory/pkb/pkb-index.test.ts +368 -0
  395. package/src/memory/pkb/pkb-index.ts +255 -0
  396. package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
  397. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  398. package/src/memory/pkb/pkb-search.test.ts +438 -0
  399. package/src/memory/pkb/pkb-search.ts +137 -0
  400. package/src/memory/pkb/types.ts +53 -0
  401. package/src/memory/qdrant-client.ts +122 -1
  402. package/src/memory/slack-thread-store.ts +37 -0
  403. package/src/messaging/providers/gmail/adapter.ts +6 -16
  404. package/src/messaging/providers/gmail/client.ts +22 -0
  405. package/src/messaging/providers/gmail/types.ts +7 -0
  406. package/src/messaging/providers/slack/adapter.ts +14 -2
  407. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  408. package/src/messaging/providers/slack/backfill.ts +101 -0
  409. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  410. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  411. package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
  412. package/src/messaging/providers/slack/render-transcript.ts +443 -0
  413. package/src/messaging/style-analyzer.ts +5 -2
  414. package/src/notifications/README.md +9 -5
  415. package/src/notifications/decision-engine.ts +3 -9
  416. package/src/notifications/preference-extractor.ts +2 -6
  417. package/src/oauth/oauth-store.ts +1 -0
  418. package/src/oauth/platform-connection.test.ts +47 -0
  419. package/src/oauth/platform-connection.ts +15 -5
  420. package/src/oauth/seed-providers.ts +4 -2
  421. package/src/permissions/approval-policy.test.ts +948 -0
  422. package/src/permissions/approval-policy.ts +257 -0
  423. package/src/permissions/bash-risk-classifier.test.ts +1208 -0
  424. package/src/permissions/bash-risk-classifier.ts +707 -0
  425. package/src/permissions/checker.ts +217 -708
  426. package/src/permissions/command-registry.test.ts +535 -0
  427. package/src/permissions/command-registry.ts +825 -0
  428. package/src/permissions/defaults.ts +26 -78
  429. package/src/permissions/file-risk-classifier.test.ts +535 -0
  430. package/src/permissions/file-risk-classifier.ts +274 -0
  431. package/src/permissions/risk-types.ts +205 -0
  432. package/src/permissions/secret-prompter.ts +53 -2
  433. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  434. package/src/permissions/skill-risk-classifier.ts +214 -0
  435. package/src/permissions/trust-client.ts +52 -25
  436. package/src/permissions/trust-store-interface.ts +1 -6
  437. package/src/permissions/trust-store.ts +161 -62
  438. package/src/permissions/types.ts +23 -14
  439. package/src/permissions/web-risk-classifier.test.ts +170 -0
  440. package/src/permissions/web-risk-classifier.ts +89 -0
  441. package/src/permissions/workspace-policy.ts +1 -16
  442. package/src/platform/client.ts +19 -1
  443. package/src/prompts/persona-resolver.ts +3 -3
  444. package/src/prompts/system-prompt.ts +19 -20
  445. package/src/prompts/templates/SOUL.md +2 -2
  446. package/src/prompts/update-bulletin-job.ts +190 -0
  447. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  448. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  449. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  450. package/src/providers/anthropic/client.ts +183 -14
  451. package/src/providers/call-site-routing.ts +71 -0
  452. package/src/providers/gemini/client.ts +65 -2
  453. package/src/providers/managed-proxy/constants.ts +2 -1
  454. package/src/providers/model-catalog.ts +501 -33
  455. package/src/providers/model-intents.ts +4 -4
  456. package/src/providers/openai/chat-completions-provider.ts +57 -1
  457. package/src/providers/openai/responses-provider.ts +86 -9
  458. package/src/providers/openrouter/client.ts +76 -9
  459. package/src/providers/provider-env-vars.ts +56 -0
  460. package/src/providers/provider-send-message.ts +22 -5
  461. package/src/providers/ratelimit.ts +4 -0
  462. package/src/providers/registry.ts +19 -8
  463. package/src/providers/retry.ts +174 -39
  464. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  465. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  466. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  467. package/src/providers/speech-to-text/resolve.ts +7 -0
  468. package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
  469. package/src/providers/speech-to-text/xai-realtime.ts +796 -0
  470. package/src/providers/speech-to-text/xai.test.ts +155 -0
  471. package/src/providers/speech-to-text/xai.ts +97 -0
  472. package/src/providers/types.ts +93 -3
  473. package/src/runtime/AGENTS.md +2 -2
  474. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  475. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  476. package/src/runtime/agent-wake.ts +63 -22
  477. package/src/runtime/auth/route-policy.ts +4 -0
  478. package/src/runtime/btw-sidechain.ts +13 -3
  479. package/src/runtime/channel-reply-delivery.ts +106 -2
  480. package/src/runtime/decision-token.ts +116 -0
  481. package/src/runtime/gateway-client.ts +2 -2
  482. package/src/runtime/http-router.ts +32 -0
  483. package/src/runtime/http-server.ts +52 -1
  484. package/src/runtime/http-types.ts +23 -1
  485. package/src/runtime/interactive-ui.ts +362 -0
  486. package/src/runtime/invite-instruction-generator.ts +2 -2
  487. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  488. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  489. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  490. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  491. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  492. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  493. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  494. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  495. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  496. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  497. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  498. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  499. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  500. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  501. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  502. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  503. package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
  504. package/src/runtime/routes/approval-routes.ts +12 -17
  505. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  506. package/src/runtime/routes/avatar-routes.ts +20 -4
  507. package/src/runtime/routes/btw-routes.ts +1 -4
  508. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  509. package/src/runtime/routes/conversation-routes.ts +133 -27
  510. package/src/runtime/routes/debug-routes.ts +1 -1
  511. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  512. package/src/runtime/routes/events-routes.ts +16 -0
  513. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  514. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  515. package/src/runtime/routes/home-feed-routes.ts +120 -2
  516. package/src/runtime/routes/inbound-message-handler.ts +912 -2
  517. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  518. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  519. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  520. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  521. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  522. package/src/runtime/routes/migration-routes.ts +720 -124
  523. package/src/runtime/routes/settings-routes.ts +4 -2
  524. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  525. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  526. package/src/runtime/routes/work-items-routes.ts +3 -2
  527. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  528. package/src/runtime/services/analyze-conversation.ts +12 -16
  529. package/src/runtime/skill-route-registry.ts +28 -6
  530. package/src/schedule/scheduler.ts +8 -0
  531. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  532. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  533. package/src/security/oauth2.ts +98 -35
  534. package/src/security/secure-keys.ts +7 -8
  535. package/src/security/token-manager.ts +27 -13
  536. package/src/security/untrusted-content.ts +102 -0
  537. package/src/skills/catalog-cache.ts +26 -7
  538. package/src/skills/catalog-install.ts +31 -3
  539. package/src/skills/skill-cache-store.ts +97 -0
  540. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  541. package/src/stt/daemon-batch-transcriber.ts +33 -0
  542. package/src/stt/stt-stream-session.ts +8 -1
  543. package/src/stt/types.ts +5 -1
  544. package/src/subagent/manager.ts +41 -13
  545. package/src/tasks/ephemeral-permissions.ts +9 -4
  546. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  547. package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
  548. package/src/tools/browser/browser-execution.ts +65 -38
  549. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  550. package/src/tools/credentials/tool-policy.ts +39 -5
  551. package/src/tools/credentials/vault.ts +9 -4
  552. package/src/tools/executor.ts +4 -0
  553. package/src/tools/filesystem/write.ts +52 -0
  554. package/src/tools/host-terminal/host-shell.ts +45 -5
  555. package/src/tools/memory/register.test.ts +185 -0
  556. package/src/tools/memory/register.ts +3 -1
  557. package/src/tools/network/web-fetch.ts +20 -10
  558. package/src/tools/network/web-search.ts +19 -4
  559. package/src/tools/permission-checker.ts +36 -15
  560. package/src/tools/policy-context.ts +25 -8
  561. package/src/tools/registry.ts +55 -3
  562. package/src/tools/side-effects.ts +0 -11
  563. package/src/tools/skills/execute.ts +2 -2
  564. package/src/tools/skills/sandbox-runner.ts +5 -2
  565. package/src/tools/terminal/backends/native.ts +51 -2
  566. package/src/tools/terminal/safe-env.ts +3 -2
  567. package/src/tools/terminal/shell.ts +1 -0
  568. package/src/tools/tool-manifest.ts +6 -21
  569. package/src/tools/types.ts +12 -3
  570. package/src/tools/verification-control-plane-policy.ts +1 -1
  571. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  572. package/src/tts/provider-catalog.ts +18 -0
  573. package/src/tts/providers/index.ts +2 -0
  574. package/src/tts/providers/xai-provider.ts +224 -0
  575. package/src/tts/types.ts +46 -0
  576. package/src/types/tar-stream.d.ts +66 -0
  577. package/src/util/json.ts +17 -0
  578. package/src/util/platform.ts +2 -2
  579. package/src/util/pricing.ts +15 -5
  580. package/src/watcher/engine.ts +1 -1
  581. package/src/watcher/providers/google-calendar.ts +134 -8
  582. package/src/watcher/providers/outlook-calendar.ts +42 -2
  583. package/src/workspace/git-service.ts +23 -4
  584. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  585. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  586. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  587. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
  588. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  589. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  590. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  591. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  592. package/src/workspace/migrations/AGENTS.md +1 -1
  593. package/src/workspace/migrations/registry.ts +16 -0
  594. package/src/workspace/provider-commit-message-generator.ts +19 -38
  595. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  596. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  597. package/src/__tests__/gmail-preferences.test.ts +0 -117
  598. package/src/__tests__/outlook-attachments.test.ts +0 -301
  599. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  600. package/src/__tests__/outlook-categories.test.ts +0 -212
  601. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  602. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  603. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  604. package/src/__tests__/outlook-trash.test.ts +0 -77
  605. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  606. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  607. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  608. package/src/__tests__/update-bulletin.test.ts +0 -478
  609. package/src/__tests__/update-template-contract.test.ts +0 -29
  610. package/src/cli/commands/doctor.ts +0 -341
  611. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  612. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  613. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  614. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  615. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  616. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  617. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  618. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  619. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  620. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  621. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  622. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  623. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  624. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  625. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  626. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  627. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  628. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  629. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  630. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  631. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  632. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  633. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  634. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  635. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  636. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  637. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  638. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  639. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  640. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  641. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  642. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  643. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  644. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  645. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  646. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  647. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  648. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  649. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  650. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  651. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  652. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  653. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  654. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  655. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  656. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  657. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  658. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  659. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  660. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  661. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  662. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  663. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  664. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  665. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  666. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  667. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  668. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  669. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  670. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  671. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  672. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  673. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  674. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  675. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  676. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  677. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  678. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  679. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  680. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  681. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  682. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  683. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  684. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  685. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  686. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  687. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  688. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  689. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  690. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  691. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  692. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  693. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  694. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  695. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  696. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  697. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  698. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  699. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  700. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  701. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  702. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  703. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  704. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  705. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  706. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  707. package/src/prompts/templates/UPDATES.md +0 -50
  708. package/src/prompts/update-bulletin-format.ts +0 -85
  709. package/src/prompts/update-bulletin-state.ts +0 -58
  710. package/src/prompts/update-bulletin-template-path.ts +0 -13
  711. package/src/prompts/update-bulletin.ts +0 -139
  712. package/src/shared/provider-env-vars.ts +0 -19
  713. package/src/tools/watcher/create.ts +0 -86
  714. package/src/tools/watcher/delete.ts +0 -36
  715. package/src/tools/watcher/digest.ts +0 -54
  716. package/src/tools/watcher/list.ts +0 -83
  717. package/src/tools/watcher/update.ts +0 -71
@@ -1,8 +1,8 @@
1
1
  /**
2
- * End-state verification test for the browser skill migration.
2
+ * End-state verification test for the browser CLI-only architecture.
3
3
  *
4
- * Locks the final invariants from the BROWSER_SKILL plan so that future
5
- * changes cannot silently regress any of the migration guarantees.
4
+ * Locks the invariants of the CLI-only browser contract so that future
5
+ * changes cannot silently regress any of the architectural guarantees.
6
6
  */
7
7
  import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
8
8
 
@@ -10,23 +10,21 @@ mock.module("../config/loader.js", () => ({
10
10
  getConfig: () => ({}),
11
11
  }));
12
12
 
13
+ import { BROWSER_OPERATION_META } from "../browser/operations.js";
14
+ import { BROWSER_OPERATIONS } from "../browser/types.js";
13
15
  import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
14
16
  import {
15
17
  projectSkillTools,
16
18
  resetSkillToolProjection,
17
19
  } from "../daemon/conversation-skill-tools.js";
18
- import { getDefaultRuleTemplates } from "../permissions/defaults.js";
19
20
  import {
20
21
  __resetRegistryForTesting,
21
22
  getAllToolDefinitions,
22
23
  getAllTools,
23
24
  initializeTools,
24
25
  } from "../tools/registry.js";
25
- import { eagerModuleToolNames } from "../tools/tool-manifest.js";
26
26
  import {
27
27
  BROWSER_SKILL_ID,
28
- BROWSER_TOOL_COUNT,
29
- BROWSER_TOOL_NAMES,
30
28
  buildSkillLoadHistory,
31
29
  } from "./test-support/browser-skill-harness.js";
32
30
 
@@ -35,7 +33,7 @@ afterAll(() => {
35
33
  _setOverridesForTesting({});
36
34
  });
37
35
 
38
- describe("browser skill migration end-state", () => {
36
+ describe("browser CLI-only architecture end-state", () => {
39
37
  beforeAll(async () => {
40
38
  __resetRegistryForTesting();
41
39
  _setOverridesForTesting({
@@ -44,201 +42,65 @@ describe("browser skill migration end-state", () => {
44
42
  await initializeTools();
45
43
  });
46
44
 
47
- const BROWSER_TOOLS = [
48
- "browser_navigate",
49
- "browser_snapshot",
50
- "browser_screenshot",
51
- "browser_close",
52
- "browser_attach",
53
- "browser_detach",
54
- "browser_click",
55
- "browser_type",
56
- "browser_press_key",
57
- "browser_scroll",
58
- "browser_select_option",
59
- "browser_hover",
60
- "browser_wait_for",
61
- "browser_extract",
62
- "browser_wait_for_download",
63
- "browser_fill_credential",
64
- "browser_status",
65
- ] as const;
45
+ // ── 1. No browser_* tools in startup payload ─────────────────────
66
46
 
67
- // ── 1. Startup payload excludes browser tools ──────────────────────
68
-
69
- test("browser tools are NOT in startup core registry", () => {
47
+ test("no browser_* tools are registered at startup", () => {
70
48
  const toolNames = getAllTools().map((t) => t.name);
71
- for (const name of BROWSER_TOOLS) {
72
- expect(toolNames).not.toContain(name);
73
- }
49
+ const browserTools = toolNames.filter((n) => n.startsWith("browser_"));
50
+ expect(browserTools).toHaveLength(0);
74
51
  });
75
52
 
76
- test("browser tool names are NOT in eagerModuleToolNames", () => {
77
- for (const name of BROWSER_TOOLS) {
78
- expect(eagerModuleToolNames).not.toContain(name);
79
- }
80
- });
81
-
82
- test("startup tool definition count is reduced (no browser tools)", () => {
53
+ test("no browser_* tool definitions at startup", () => {
83
54
  const definitions = getAllToolDefinitions();
84
- // Startup has ~20 definitions after moving scaffold/settings/skill-management
85
- // tools to bundled skills (no browser tools).
86
- // Allow wider drift for unrelated tool additions while still failing if
87
- // browser tools are reintroduced at startup (+many definitions).
88
- expect(definitions.length).toBeGreaterThanOrEqual(15);
89
- expect(definitions.length).toBeLessThanOrEqual(50);
90
-
91
- const defNames = definitions.map((d) => d.name);
92
- for (const name of BROWSER_TOOLS) {
93
- expect(defNames).not.toContain(name);
94
- }
95
-
96
- // Payload ceiling: startup payload is ~22 000 chars. Browser tools
97
- // contribute ~4 640 chars — if they leak back in, the total would exceed
98
- // 35 000. The margin absorbs minor tool additions.
99
- const payloadSize = JSON.stringify(definitions).length;
100
- expect(payloadSize).toBeLessThan(35_000);
55
+ const browserDefs = definitions.filter((d) =>
56
+ d.name.startsWith("browser_"),
57
+ );
58
+ expect(browserDefs).toHaveLength(0);
101
59
  });
102
60
 
103
- // ── 2. Browser skill exists and is active ──────────────────────────
61
+ // ── 2. Browser skill directory exists with SKILL.md ──────────────
104
62
 
105
- test("bundled browser skill directory exists with SKILL.md and TOOLS.json", async () => {
63
+ test("managed browser skill directory exists with SKILL.md but no TOOLS.json", async () => {
106
64
  const path = await import("node:path");
107
65
  const fs = await import("node:fs");
66
+ // Browser skill lives in skills/vellum-browser-use/ (managed), not bundled-skills/.
108
67
  const skillDir = path.resolve(
109
68
  import.meta.dirname,
110
- "../config/bundled-skills/browser",
69
+ "../../../skills/vellum-browser-use",
111
70
  );
112
71
  expect(fs.existsSync(path.join(skillDir, "SKILL.md"))).toBe(true);
113
- expect(fs.existsSync(path.join(skillDir, "TOOLS.json"))).toBe(true);
114
- });
115
-
116
- test("browser TOOLS.json contains all browser tools", async () => {
117
- const path = await import("node:path");
118
- const fs = await import("node:fs");
119
- const toolsPath = path.resolve(
120
- import.meta.dirname,
121
- "../config/bundled-skills/browser/TOOLS.json",
122
- );
123
- const manifest = JSON.parse(fs.readFileSync(toolsPath, "utf-8"));
124
- expect(manifest.version).toBe(1);
125
- expect(manifest.tools).toHaveLength(BROWSER_TOOLS.length);
126
- const toolNames = manifest.tools.map((t: { name: string }) => t.name);
127
- for (const name of BROWSER_TOOLS) {
128
- expect(toolNames).toContain(name);
129
- }
130
- });
131
-
132
- test("every browser tool schema exposes an optional browser_mode property", async () => {
133
- const path = await import("node:path");
134
- const fs = await import("node:fs");
135
- const toolsPath = path.resolve(
136
- import.meta.dirname,
137
- "../config/bundled-skills/browser/TOOLS.json",
138
- );
139
- const manifest = JSON.parse(fs.readFileSync(toolsPath, "utf-8"));
140
- for (const tool of manifest.tools) {
141
- const props = tool.input_schema?.properties ?? {};
142
- expect(props.browser_mode).toBeDefined();
143
- expect(props.browser_mode.type).toBe("string");
144
- // browser_mode must NOT be required
145
- const required: string[] = tool.input_schema?.required ?? [];
146
- expect(required).not.toContain("browser_mode");
147
- }
148
- });
149
-
150
- // ── 3. Permission defaults align with PR 08/09 ────────────────────
151
-
152
- test("skill_load has default allow rule", () => {
153
- const templates = getDefaultRuleTemplates();
154
- const rule = templates.find(
155
- (t) => t.id === "default:allow-skill_load-global",
156
- );
157
- expect(rule).toBeDefined();
158
- expect(rule!.decision).toBe("allow");
159
- });
160
-
161
- test("all browser tools have default allow rules", () => {
162
- const templates = getDefaultRuleTemplates();
163
- for (const tool of BROWSER_TOOLS) {
164
- const rule = templates.find(
165
- (t) => t.id === `default:allow-${tool}-global`,
166
- );
167
- expect(rule).toBeDefined();
168
- expect(rule!.decision).toBe("allow");
169
- // browser_navigate uses standalone "**" globstar because navigate
170
- // candidates contain URLs with "/" (e.g. "browser_navigate:https://example.com/path").
171
- const expectedPattern = tool === "browser_navigate" ? "**" : `${tool}:*`;
172
- expect(rule!.pattern).toBe(expectedPattern);
173
- }
72
+ // Browser operations are dispatched via the CLI, not via skill tools.
73
+ expect(fs.existsSync(path.join(skillDir, "TOOLS.json"))).toBe(false);
174
74
  });
175
75
 
176
- // ── 4. Tool wrapper scripts exist ──────────────────────────────────
76
+ // ── 3. Browser tool wrapper directory does not exist ─────────────
177
77
 
178
- test("all browser tool wrapper scripts exist", async () => {
78
+ test("browser tool wrapper scripts directory does not exist", async () => {
179
79
  const path = await import("node:path");
180
80
  const fs = await import("node:fs");
181
81
  const toolsDir = path.resolve(
182
82
  import.meta.dirname,
183
- "../config/bundled-skills/browser/tools",
83
+ "../../../skills/vellum-browser-use/tools",
184
84
  );
185
- const wrapperFiles = [
186
- "browser-navigate.ts",
187
- "browser-snapshot.ts",
188
- "browser-screenshot.ts",
189
- "browser-close.ts",
190
- "browser-attach.ts",
191
- "browser-detach.ts",
192
- "browser-click.ts",
193
- "browser-type.ts",
194
- "browser-press-key.ts",
195
- "browser-scroll.ts",
196
- "browser-select-option.ts",
197
- "browser-hover.ts",
198
- "browser-wait-for.ts",
199
- "browser-extract.ts",
200
- "browser-wait-for-download.ts",
201
- "browser-fill-credential.ts",
202
- "browser-status.ts",
203
- ];
204
- for (const file of wrapperFiles) {
205
- expect(fs.existsSync(path.join(toolsDir, file))).toBe(true);
206
- }
85
+ // Browser operations are dispatched via CLI commands,
86
+ // not via per-tool executor files.
87
+ expect(fs.existsSync(toolsDir)).toBe(false);
207
88
  });
208
89
 
209
- // ── 5. Execution extraction is in place ────────────────────────────
90
+ // ── 4. Browser operations have CLI metadata ──────────────────────
210
91
 
211
- test("browser-execution.ts exists with exported execute functions", async () => {
212
- const path = await import("node:path");
213
- const fs = await import("node:fs");
214
- const execPath = path.resolve(
215
- import.meta.dirname,
216
- "../tools/browser/browser-execution.ts",
217
- );
218
- expect(fs.existsSync(execPath)).toBe(true);
219
- const content = fs.readFileSync(execPath, "utf-8");
220
- // browser_wait_for_download uses a standalone wrapper that calls
221
- // browserManager.waitForDownload() directly — no execute* function.
222
- const TOOLS_WITH_EXECUTE_FN = BROWSER_TOOLS.filter(
223
- (name) => name !== "browser_wait_for_download",
224
- );
225
- for (const name of TOOLS_WITH_EXECUTE_FN) {
226
- // Derive expected function name: browser_navigate -> executeBrowserNavigate
227
- const fnName =
228
- "execute" +
229
- name
230
- .split("_")
231
- .map((s, i) =>
232
- i === 0 ? "Browser" : s.charAt(0).toUpperCase() + s.slice(1),
233
- )
234
- .join("");
235
- expect(content).toContain(fnName);
92
+ test("every browser operation has CLI subcommand metadata", () => {
93
+ for (const op of BROWSER_OPERATIONS) {
94
+ const meta = BROWSER_OPERATION_META.find((m) => m.operation === op);
95
+ expect(meta).toBeDefined();
96
+ expect(meta!.helpText).toBeDefined();
97
+ expect(meta!.helpText).toContain("assistant browser");
236
98
  }
237
99
  });
238
100
 
239
- // ── 6. Runtime projection adds all browser tools ──────────
101
+ // ── 5. Skill projection emits no tool definitions ────────────────
240
102
 
241
- test("skill_load projection adds all browser tools", () => {
103
+ test("skill_load projection registers no browser tools", () => {
242
104
  const history = buildSkillLoadHistory(BROWSER_SKILL_ID);
243
105
  const tracking = new Map<string, string>();
244
106
 
@@ -247,17 +109,24 @@ describe("browser skill migration end-state", () => {
247
109
  previouslyActiveSkillIds: tracking,
248
110
  });
249
111
 
250
- // Tool definitions are no longer sent to the LLM (dispatched via
251
- // skill_execute), so toolDefinitions is expected to be empty.
112
+ // No tool definitions sent to the LLM browser operations are
113
+ // dispatched via `assistant browser` CLI commands.
252
114
  expect(projection.toolDefinitions).toHaveLength(0);
253
-
254
- // All browser tools should be registered and tracked in allowedToolNames
255
- expect(projection.allowedToolNames.size).toBe(BROWSER_TOOL_COUNT);
256
- for (const name of BROWSER_TOOL_NAMES) {
257
- expect(projection.allowedToolNames.has(name)).toBe(true);
258
- }
115
+ expect(projection.allowedToolNames.size).toBe(0);
259
116
  } finally {
260
117
  resetSkillToolProjection(tracking);
261
118
  }
262
119
  });
120
+
121
+ // ── 6. Execution module exists ───────────────────────────────────
122
+
123
+ test("browser-execution.ts exists with exported execute functions", async () => {
124
+ const path = await import("node:path");
125
+ const fs = await import("node:fs");
126
+ const execPath = path.resolve(
127
+ import.meta.dirname,
128
+ "../tools/browser/browser-execution.ts",
129
+ );
130
+ expect(fs.existsSync(execPath)).toBe(true);
131
+ });
263
132
  });
@@ -73,6 +73,15 @@ mock.module("../prompts/persona-resolver.js", () => ({
73
73
  resolveUserSlug: () => null,
74
74
  }));
75
75
 
76
+ // Force the identity-intro fast-path to miss so the handler always falls
77
+ // through to the LLM call — the call-site assertions need a real provider
78
+ // invocation to inspect.
79
+ mock.module("../runtime/routes/identity-intro-cache.js", () => ({
80
+ getCachedIntro: () => null,
81
+ setCachedIntro: () => {},
82
+ computeIdentityContentHash: () => "test-hash",
83
+ }));
84
+
76
85
  // ---------------------------------------------------------------------------
77
86
  // Imports (after mocks)
78
87
  // ---------------------------------------------------------------------------
@@ -324,7 +333,44 @@ describe("POST /v1/btw", () => {
324
333
 
325
334
  // Options: tool_choice must be "none"
326
335
  expect(options!.config!.tool_choice).toEqual({ type: "none" });
327
- expect(options!.config!.modelIntent).toBe("latency-optimized");
336
+ // Default call-site for the BTW side-chain is the identity intro path.
337
+ // `callSite` and `modelIntent` are mutually exclusive (PR 5 contract);
338
+ // when a caller passes neither, the default `callSite` is set and
339
+ // `modelIntent` stays undefined.
340
+ expect(options!.config!.callSite).toBe("identityIntro");
341
+ expect(options!.config!.modelIntent).toBeUndefined();
342
+ });
343
+
344
+ test("greeting requests pass callSite: 'emptyStateGreeting'", async () => {
345
+ const provider = makeMockProvider();
346
+ const session = makeMockSession(provider);
347
+ const deps = makeSendMessageDeps(session);
348
+
349
+ const res = await callHandler(
350
+ { conversationKey: "greeting", content: "Generate a greeting" },
351
+ { sendMessageDeps: deps },
352
+ );
353
+ await readStream(res);
354
+
355
+ expect(provider.sendMessage).toHaveBeenCalledTimes(1);
356
+ const [, , , options] = provider.sendMessage.mock.calls[0];
357
+ expect(options!.config!.callSite).toBe("emptyStateGreeting");
358
+ });
359
+
360
+ test("identity intro requests pass callSite: 'identityIntro'", async () => {
361
+ const provider = makeMockProvider();
362
+ const session = makeMockSession(provider);
363
+ const deps = makeSendMessageDeps(session);
364
+
365
+ const res = await callHandler(
366
+ { conversationKey: "identity-intro", content: "Generate an intro" },
367
+ { sendMessageDeps: deps },
368
+ );
369
+ await readStream(res);
370
+
371
+ expect(provider.sendMessage).toHaveBeenCalledTimes(1);
372
+ const [, , , options] = provider.sendMessage.mock.calls[0];
373
+ expect(options!.config!.callSite).toBe("identityIntro");
328
374
  });
329
375
 
330
376
  // -- No persistence --
@@ -35,10 +35,9 @@ mock.module("../config/loader.js", () => {
35
35
  silenceTimeoutSeconds: 30,
36
36
  disclosure: { enabled: false, text: "" },
37
37
  safety: { denyCategories: [] },
38
- model: undefined,
39
38
  },
40
39
  memory: { enabled: false },
41
- notifications: { decisionModelIntent: "latency-optimized" },
40
+ notifications: {},
42
41
  services: {
43
42
  tts: {
44
43
  mode: "your-own" as const,
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Verifies that `CallSiteRoutingProvider` selects the right underlying
3
+ * provider transport per call based on `options.config.callSite`.
4
+ *
5
+ * The wrapper exists so per-call-site `llm.callSites.<id>.provider`
6
+ * overrides actually swap the HTTP transport, not just the request
7
+ * metadata. The conversation's transport is fixed at construction time;
8
+ * without this wrapper a memoryRetrieval call configured to run on OpenAI
9
+ * but originating from an Anthropic-default conversation would still hit
10
+ * the Anthropic transport.
11
+ */
12
+
13
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
14
+
15
+ mock.module("../util/logger.js", () => ({
16
+ getLogger: () =>
17
+ new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
18
+ }));
19
+
20
+ // Mutable LLM config consumed by the resolver via `getConfig()`.
21
+ let mockLlmConfig: Record<string, unknown> = {};
22
+
23
+ mock.module("../config/loader.js", () => ({
24
+ getConfig: () => ({ llm: mockLlmConfig }),
25
+ }));
26
+
27
+ import { LLMSchema } from "../config/schemas/llm.js";
28
+ import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
29
+ import type {
30
+ Message,
31
+ Provider,
32
+ ProviderResponse,
33
+ SendMessageOptions,
34
+ } from "../providers/types.js";
35
+
36
+ const DUMMY_MESSAGES: Message[] = [
37
+ { role: "user", content: [{ type: "text", text: "hi" }] },
38
+ ];
39
+
40
+ function makeResponse(model: string): ProviderResponse {
41
+ return {
42
+ content: [{ type: "text", text: "ok" }],
43
+ model,
44
+ usage: { inputTokens: 1, outputTokens: 1 },
45
+ stopReason: "end_turn",
46
+ };
47
+ }
48
+
49
+ function makeProvider(
50
+ name: string,
51
+ onCall: (options: SendMessageOptions | undefined) => void,
52
+ ): Provider {
53
+ return {
54
+ name,
55
+ async sendMessage(_messages, _tools, _systemPrompt, options) {
56
+ onCall(options);
57
+ return makeResponse(name);
58
+ },
59
+ };
60
+ }
61
+
62
+ function setLlmConfig(raw: unknown): void {
63
+ mockLlmConfig = LLMSchema.parse(raw) as Record<string, unknown>;
64
+ }
65
+
66
+ beforeEach(() => {
67
+ mockLlmConfig = LLMSchema.parse({}) as Record<string, unknown>;
68
+ });
69
+
70
+ describe("CallSiteRoutingProvider", () => {
71
+ test("routes to default provider when callSite is absent", async () => {
72
+ setLlmConfig({
73
+ default: { provider: "anthropic", model: "claude-opus-4-7" },
74
+ callSites: {
75
+ memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
76
+ },
77
+ });
78
+
79
+ const calls = { default: 0, alt: 0 };
80
+ const defaultProvider = makeProvider("anthropic", () => {
81
+ calls.default++;
82
+ });
83
+ const altProvider = makeProvider("openai", () => {
84
+ calls.alt++;
85
+ });
86
+
87
+ const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
88
+ name === "openai" ? altProvider : undefined,
89
+ );
90
+
91
+ const response = await wrapped.sendMessage(
92
+ DUMMY_MESSAGES,
93
+ undefined,
94
+ undefined,
95
+ // No callSite — must hit default even though `memoryRetrieval` is
96
+ // configured for openai.
97
+ { config: {} },
98
+ );
99
+
100
+ expect(calls.default).toBe(1);
101
+ expect(calls.alt).toBe(0);
102
+ expect(response.model).toBe("anthropic");
103
+ });
104
+
105
+ test("routes to default provider when callSite resolves to same provider name", async () => {
106
+ setLlmConfig({
107
+ default: { provider: "anthropic", model: "claude-opus-4-7" },
108
+ callSites: {
109
+ // Same provider as default — no transport swap needed.
110
+ memoryRetrieval: { model: "claude-haiku-4-5-20251001" },
111
+ },
112
+ });
113
+
114
+ const calls = { default: 0, alt: 0 };
115
+ const defaultProvider = makeProvider("anthropic", () => {
116
+ calls.default++;
117
+ });
118
+ const altProvider = makeProvider("openai", () => {
119
+ calls.alt++;
120
+ });
121
+
122
+ const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
123
+ name === "openai" ? altProvider : undefined,
124
+ );
125
+
126
+ await wrapped.sendMessage(DUMMY_MESSAGES, undefined, undefined, {
127
+ config: { callSite: "memoryRetrieval" },
128
+ });
129
+
130
+ expect(calls.default).toBe(1);
131
+ expect(calls.alt).toBe(0);
132
+ });
133
+
134
+ test("routes to alternative provider when callSite resolves to a different provider name", async () => {
135
+ setLlmConfig({
136
+ default: { provider: "anthropic", model: "claude-opus-4-7" },
137
+ callSites: {
138
+ memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
139
+ },
140
+ });
141
+
142
+ const calls = { default: 0, alt: 0 };
143
+ const defaultProvider = makeProvider("anthropic", () => {
144
+ calls.default++;
145
+ });
146
+ const altProvider = makeProvider("openai", () => {
147
+ calls.alt++;
148
+ });
149
+
150
+ const wrapped = new CallSiteRoutingProvider(defaultProvider, (name) =>
151
+ name === "openai" ? altProvider : undefined,
152
+ );
153
+
154
+ const response = await wrapped.sendMessage(
155
+ DUMMY_MESSAGES,
156
+ undefined,
157
+ undefined,
158
+ { config: { callSite: "memoryRetrieval" } },
159
+ );
160
+
161
+ expect(calls.default).toBe(0);
162
+ expect(calls.alt).toBe(1);
163
+ expect(response.model).toBe("openai");
164
+ });
165
+
166
+ test("falls back to default when alternative provider is not registered", async () => {
167
+ setLlmConfig({
168
+ default: { provider: "anthropic", model: "claude-opus-4-7" },
169
+ callSites: {
170
+ memoryRetrieval: { provider: "openai", model: "gpt-5.4" },
171
+ },
172
+ });
173
+
174
+ const calls = { default: 0 };
175
+ const defaultProvider = makeProvider("anthropic", () => {
176
+ calls.default++;
177
+ });
178
+
179
+ // Lookup always returns undefined — simulating a missing/uninitialized
180
+ // provider in the registry.
181
+ const wrapped = new CallSiteRoutingProvider(
182
+ defaultProvider,
183
+ () => undefined,
184
+ );
185
+
186
+ const response = await wrapped.sendMessage(
187
+ DUMMY_MESSAGES,
188
+ undefined,
189
+ undefined,
190
+ { config: { callSite: "memoryRetrieval" } },
191
+ );
192
+
193
+ expect(calls.default).toBe(1);
194
+ expect(response.model).toBe("anthropic");
195
+ });
196
+
197
+ test("delegates `name` and `tokenEstimationProvider` to the default provider", () => {
198
+ const defaultProvider: Provider = {
199
+ name: "anthropic",
200
+ tokenEstimationProvider: "anthropic",
201
+ async sendMessage() {
202
+ return makeResponse("anthropic");
203
+ },
204
+ };
205
+
206
+ const wrapped = new CallSiteRoutingProvider(
207
+ defaultProvider,
208
+ () => undefined,
209
+ );
210
+
211
+ expect(wrapped.name).toBe("anthropic");
212
+ expect(wrapped.tokenEstimationProvider).toBe("anthropic");
213
+ });
214
+ });
@@ -22,6 +22,7 @@ mock.module("../util/logger.js", () => ({
22
22
  }));
23
23
 
24
24
  let mockRepoSkillsDir: string | undefined = undefined;
25
+ let mockLocalCatalog: CatalogSkill[] = [];
25
26
  let mockFetchCatalogResult: CatalogSkill[] = [];
26
27
  let mockFetchCatalogError: Error | null = null;
27
28
  let fetchCatalogCallCount = 0;
@@ -31,7 +32,7 @@ mock.module("../skills/catalog-install.js", () => ({
31
32
  getRepoSkillsDir: () => mockRepoSkillsDir,
32
33
  readLocalCatalog: (_dir: string) => {
33
34
  readLocalCatalogCallCount++;
34
- return mockFetchCatalogResult;
35
+ return mockLocalCatalog;
35
36
  },
36
37
  fetchCatalog: async () => {
37
38
  fetchCatalogCallCount++;
@@ -64,6 +65,7 @@ const updatedCatalog: CatalogSkill[] = [
64
65
  function resetState(): void {
65
66
  invalidateCatalogCache();
66
67
  mockRepoSkillsDir = undefined;
68
+ mockLocalCatalog = [];
67
69
  mockFetchCatalogResult = [];
68
70
  mockFetchCatalogError = null;
69
71
  fetchCatalogCallCount = 0;
@@ -152,13 +154,34 @@ describe("getCatalog", () => {
152
154
  expect(fetchCatalogCallCount).toBe(2);
153
155
  });
154
156
 
155
- test("uses local catalog when repoSkillsDir is set", async () => {
157
+ test("merges local and remote catalogs when repoSkillsDir is set", async () => {
156
158
  mockRepoSkillsDir = "/mock/repo/skills";
157
- mockFetchCatalogResult = sampleCatalog;
159
+ mockLocalCatalog = [
160
+ { id: "web-search", name: "Local Web Search", description: "Local" },
161
+ ];
162
+ mockFetchCatalogResult = [
163
+ { id: "web-search", name: "Remote Web Search", description: "Remote" },
164
+ { id: "remote-only", name: "Remote Only", description: "Remote only" },
165
+ ];
166
+
167
+ const result = await getCatalog();
168
+ expect(readLocalCatalogCallCount).toBe(1);
169
+ expect(fetchCatalogCallCount).toBe(1); // still merges with remote
170
+ // Local entry takes precedence for overlapping id
171
+ expect(result).toEqual([
172
+ { id: "web-search", name: "Local Web Search", description: "Local" },
173
+ { id: "remote-only", name: "Remote Only", description: "Remote only" },
174
+ ]);
175
+ });
176
+
177
+ test("falls back to local bundled catalog when remote fetch fails", async () => {
178
+ mockRepoSkillsDir = "/mock/repo/skills";
179
+ mockLocalCatalog = sampleCatalog;
180
+ mockFetchCatalogError = new Error("Network timeout");
158
181
 
159
182
  const result = await getCatalog();
160
183
  expect(result).toEqual(sampleCatalog);
161
184
  expect(readLocalCatalogCallCount).toBe(1);
162
- expect(fetchCatalogCallCount).toBe(0); // no remote fetch
185
+ expect(fetchCatalogCallCount).toBe(1); // attempted remote fetch
163
186
  });
164
187
  });