@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a

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 (624) hide show
  1. package/ARCHITECTURE.md +19 -35
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +1128 -119
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  28. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  29. package/src/__tests__/app-compiler.test.ts +15 -1
  30. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  31. package/src/__tests__/approval-cascade.test.ts +6 -0
  32. package/src/__tests__/approval-routes-http.test.ts +128 -0
  33. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  34. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  35. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  36. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  37. package/src/__tests__/btw-routes.test.ts +6 -8
  38. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  39. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  40. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  41. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  42. package/src/__tests__/checker.test.ts +0 -3
  43. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  44. package/src/__tests__/compaction-events.test.ts +6 -0
  45. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  46. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  47. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  48. package/src/__tests__/config-schema.test.ts +35 -0
  49. package/src/__tests__/config-watcher.test.ts +0 -18
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  51. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  52. package/src/__tests__/contacts-tools.test.ts +29 -0
  53. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  57. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  59. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  60. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  61. package/src/__tests__/conversation-process-callsite.test.ts +6 -0
  62. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  63. package/src/__tests__/conversation-queue.test.ts +6 -0
  64. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  65. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  66. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  68. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  69. package/src/__tests__/conversation-title-service.test.ts +62 -0
  70. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  71. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  72. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  73. package/src/__tests__/credential-broker.test.ts +449 -1
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  75. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  76. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  77. package/src/__tests__/credential-routes.test.ts +360 -0
  78. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  79. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  80. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  81. package/src/__tests__/disk-usage.test.ts +65 -0
  82. package/src/__tests__/document-tool-security.test.ts +9 -6
  83. package/src/__tests__/document-update-default-surface.test.ts +202 -0
  84. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  85. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  86. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  87. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  88. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  89. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  90. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  91. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  92. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  93. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  94. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  95. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  96. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  97. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  98. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  99. package/src/__tests__/identity-routes.test.ts +0 -189
  100. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  101. package/src/__tests__/injector-disk-pressure.test.ts +10 -5
  102. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  103. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  104. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  105. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  106. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  107. package/src/__tests__/llm-resolver.test.ts +70 -24
  108. package/src/__tests__/llm-schema.test.ts +1 -0
  109. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  110. package/src/__tests__/mcp-health-check.test.ts +6 -7
  111. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  112. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  113. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  114. package/src/__tests__/path-policy.test.ts +34 -0
  115. package/src/__tests__/persona-resolver.test.ts +49 -14
  116. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  118. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  119. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  120. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  121. package/src/__tests__/reaction-persistence.test.ts +150 -29
  122. package/src/__tests__/registry.test.ts +2 -7
  123. package/src/__tests__/relay-server.test.ts +285 -0
  124. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  125. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  126. package/src/__tests__/schedule-routes.test.ts +0 -30
  127. package/src/__tests__/schedule-tools.test.ts +2 -18
  128. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  129. package/src/__tests__/secret-response-routing.test.ts +35 -0
  130. package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
  131. package/src/__tests__/skill-execute-input.test.ts +177 -1
  132. package/src/__tests__/skill-load-tool.test.ts +72 -0
  133. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  134. package/src/__tests__/skills.test.ts +51 -0
  135. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  136. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  137. package/src/__tests__/subagent-tools.test.ts +266 -0
  138. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  139. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  140. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  141. package/src/__tests__/title-generate-hook.test.ts +100 -3
  142. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  143. package/src/__tests__/token-manager.test.ts +519 -0
  144. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  145. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  146. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  147. package/src/__tests__/tool-executor.test.ts +0 -79
  148. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  149. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  150. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  151. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  152. package/src/__tests__/twilio-routes.test.ts +81 -1
  153. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  154. package/src/__tests__/weak-open-model.test.ts +30 -0
  155. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  156. package/src/__tests__/workspace-greetings.test.ts +152 -0
  157. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  158. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  159. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  160. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  161. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  162. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  163. package/src/agent/loop.ts +75 -29
  164. package/src/api/README.md +6 -6
  165. package/src/api/events/tool-result.ts +6 -0
  166. package/src/api/events/workflow-completed.ts +53 -0
  167. package/src/api/events/workflow-leaf-finished.ts +38 -0
  168. package/src/api/events/workflow-leaf-started.ts +35 -0
  169. package/src/api/events/workflow-progress.ts +32 -0
  170. package/src/api/events/workflow-started.ts +31 -0
  171. package/src/api/index.ts +40 -0
  172. package/src/api/responses/conversation-message.ts +28 -4
  173. package/src/api/responses/home.ts +26 -4
  174. package/src/api/responses/workflow-journal.ts +53 -0
  175. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  176. package/src/approvals/guardian-decision-primitive.ts +26 -3
  177. package/src/approvals/guardian-request-resolvers.ts +183 -80
  178. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  179. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  180. package/src/calls/call-pointer-messages.ts +10 -4
  181. package/src/calls/channel-admission-reader.ts +104 -0
  182. package/src/calls/guardian-dispatch.ts +17 -45
  183. package/src/calls/media-stream-server.ts +84 -2
  184. package/src/calls/relay-access-wait.ts +1 -1
  185. package/src/calls/relay-server.ts +66 -0
  186. package/src/calls/relay-setup-router.ts +82 -1
  187. package/src/calls/twilio-routes.ts +17 -8
  188. package/src/calls/voice-session-bridge.ts +2 -2
  189. package/src/cli/commands/clients.ts +3 -0
  190. package/src/cli/commands/db/status.ts +37 -3
  191. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  192. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  193. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  194. package/src/cli/commands/memory/index.ts +30 -0
  195. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  196. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  197. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  198. package/src/cli/commands/oauth/status.test.ts +36 -0
  199. package/src/cli/commands/oauth/status.ts +23 -3
  200. package/src/cli/commands/plugins.ts +400 -14
  201. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  202. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  203. package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
  204. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  205. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  206. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  207. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  208. package/src/cli/lib/diff-plugin.ts +346 -0
  209. package/src/cli/lib/inspect-plugin.ts +12 -1
  210. package/src/cli/lib/install-from-github.ts +128 -19
  211. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  212. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  213. package/src/cli/lib/plugin-marketplace.ts +11 -0
  214. package/src/cli/lib/plugin-pin-history.ts +257 -0
  215. package/src/cli/lib/plugin-surfaces.ts +104 -0
  216. package/src/cli/lib/upgrade-plugin.ts +298 -10
  217. package/src/cli/program.ts +2 -6
  218. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  219. package/src/config/assistant-feature-flags.ts +22 -7
  220. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  221. package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
  222. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  223. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  224. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  225. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  226. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  227. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  228. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  229. package/src/config/bundled-tool-registry.ts +2 -7
  230. package/src/config/call-site-defaults.ts +15 -2
  231. package/src/config/feature-flag-registry.json +46 -31
  232. package/src/config/inference-profile-validation.ts +26 -0
  233. package/src/config/llm-resolver.ts +3 -0
  234. package/src/config/loader.ts +4 -0
  235. package/src/config/memory-v3-gate.ts +11 -0
  236. package/src/config/profile-order.ts +28 -0
  237. package/src/config/schema.ts +8 -6
  238. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  239. package/src/config/schemas/call-site-catalog.ts +7 -0
  240. package/src/config/schemas/channels.ts +11 -0
  241. package/src/config/schemas/elevenlabs.ts +0 -1
  242. package/src/config/schemas/llm.ts +31 -0
  243. package/src/config/schemas/memory-lifecycle.ts +3 -7
  244. package/src/config/schemas/memory-v3.ts +6 -0
  245. package/src/config/schemas/platform.ts +0 -8
  246. package/src/config/schemas/services.ts +18 -0
  247. package/src/config/seed-inference-profiles.ts +109 -44
  248. package/src/config/skills.ts +21 -0
  249. package/src/config/sync-gated-profiles.ts +220 -0
  250. package/src/contacts/contact-store.ts +89 -106
  251. package/src/contacts/contacts-write.ts +5 -22
  252. package/src/contacts/types.ts +0 -1
  253. package/src/context/compactor.ts +88 -54
  254. package/src/context/strip-injections.ts +58 -10
  255. package/src/context/token-estimator.ts +1 -1
  256. package/src/credential-execution/process-manager.ts +55 -14
  257. package/src/credential-execution/prompted-credential.ts +2 -3
  258. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  259. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  260. package/src/daemon/config-watcher.ts +0 -4
  261. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  262. package/src/daemon/conversation-agent-loop.ts +114 -22
  263. package/src/daemon/conversation-history.ts +1 -1
  264. package/src/daemon/conversation-lifecycle.ts +3 -5
  265. package/src/daemon/conversation-process.ts +13 -5
  266. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  267. package/src/daemon/conversation-slash.ts +2 -23
  268. package/src/daemon/conversation-surfaces.ts +26 -0
  269. package/src/daemon/conversation-tool-setup.ts +53 -19
  270. package/src/daemon/conversation.ts +131 -20
  271. package/src/daemon/disk-pressure-policy.ts +5 -3
  272. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  273. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  274. package/src/daemon/handlers/config-a2a.ts +0 -2
  275. package/src/daemon/handlers/config-channels.ts +15 -16
  276. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  277. package/src/daemon/handlers/conversations.ts +107 -0
  278. package/src/daemon/host-browser-proxy.ts +41 -0
  279. package/src/daemon/lifecycle.ts +55 -27
  280. package/src/daemon/memory-v2-startup.test.ts +131 -0
  281. package/src/daemon/memory-v2-startup.ts +100 -1
  282. package/src/daemon/message-provenance.ts +2 -0
  283. package/src/daemon/message-types/contacts.ts +0 -1
  284. package/src/daemon/message-types/conversations.ts +3 -3
  285. package/src/daemon/message-types/sync.ts +0 -1
  286. package/src/daemon/message-types/web-activity.ts +7 -1
  287. package/src/daemon/message-types/workflows.ts +83 -1
  288. package/src/daemon/orphan-reaper.test.ts +0 -19
  289. package/src/daemon/orphan-reaper.ts +2 -24
  290. package/src/daemon/server.ts +0 -10
  291. package/src/daemon/tool-setup-types.ts +4 -0
  292. package/src/daemon/trust-context.ts +1 -1
  293. package/src/daemon/wake-conversation-ops.ts +1 -0
  294. package/src/events/tool-audit-listener.ts +2 -2
  295. package/src/home/feed-source-enrichment.test.ts +151 -0
  296. package/src/home/feed-source-enrichment.ts +176 -0
  297. package/src/home/relationship-state.ts +2 -4
  298. package/src/instrument.ts +18 -6
  299. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  300. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  301. package/src/ipc/assistant-server.ts +37 -4
  302. package/src/ipc/gateway-flag-listener.ts +18 -2
  303. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  304. package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
  305. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  306. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  307. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  308. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  309. package/src/memory/auth-fallback-events-store.ts +2 -2
  310. package/src/memory/auto-analysis-enqueue.ts +3 -5
  311. package/src/memory/bookmark-crud.ts +1 -2
  312. package/src/memory/canonical-guardian-store.ts +39 -1
  313. package/src/memory/conversation-crud.ts +9 -4
  314. package/src/memory/conversation-key-store.ts +17 -2
  315. package/src/memory/conversation-title-service.ts +64 -7
  316. package/src/memory/db-async-query.ts +20 -2
  317. package/src/memory/db-connection.ts +49 -5
  318. package/src/memory/db-init.ts +17 -17
  319. package/src/memory/embedding-backend.ts +38 -1
  320. package/src/memory/embedding-billing-breaker.ts +96 -0
  321. package/src/memory/jobs-store.ts +25 -13
  322. package/src/memory/jobs-worker.ts +54 -1
  323. package/src/memory/lifecycle-events-store.ts +2 -2
  324. package/src/memory/memory-retrospective-constants.ts +4 -4
  325. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  326. package/src/memory/memory-retrospective-job.ts +28 -227
  327. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  328. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  329. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  330. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  331. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  332. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  333. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  334. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  335. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  336. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  337. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  338. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  339. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  340. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  341. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  342. package/src/memory/migrations/index.ts +6 -0
  343. package/src/memory/migrations/run-migrations.ts +41 -0
  344. package/src/memory/migrations/validate-migration-state.ts +1 -1
  345. package/src/memory/onboarding-events-store.ts +3 -3
  346. package/src/memory/schema/contacts.ts +0 -5
  347. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  348. package/src/memory/skill-loaded-events-store.ts +2 -2
  349. package/src/memory/tool-executed-events-store.test.ts +7 -7
  350. package/src/memory/turn-trace-store.test.ts +736 -0
  351. package/src/memory/turn-trace-store.ts +364 -0
  352. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  353. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  354. package/src/memory/v2/consolidation-job.ts +2 -2
  355. package/src/memory/v2/skill-content.ts +25 -7
  356. package/src/memory/v2/skill-store.ts +7 -1
  357. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  358. package/src/memory/v3-eval/eval-packets.ts +546 -0
  359. package/src/messaging/providers/slack/adapter.ts +1 -1
  360. package/src/messaging/providers/slack/api.ts +31 -0
  361. package/src/messaging/providers/slack/send.test.ts +114 -2
  362. package/src/messaging/providers/slack/send.ts +30 -7
  363. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  364. package/src/messaging/providers/slack/withdraw.ts +161 -0
  365. package/src/notifications/AGENTS.md +2 -0
  366. package/src/notifications/access-request-copy.ts +72 -59
  367. package/src/notifications/adapters/shared.ts +29 -0
  368. package/src/notifications/adapters/slack.ts +58 -103
  369. package/src/notifications/adapters/telegram.ts +2 -20
  370. package/src/notifications/approval-card-data.ts +333 -0
  371. package/src/notifications/broadcaster.ts +16 -3
  372. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  373. package/src/notifications/copy-composer.ts +3 -3
  374. package/src/notifications/decision-engine.ts +4 -2
  375. package/src/notifications/destination-resolver.ts +4 -6
  376. package/src/notifications/guardian-question-mode.ts +10 -0
  377. package/src/notifications/home-feed-side-effect.ts +7 -16
  378. package/src/notifications/notification-utils.ts +19 -20
  379. package/src/notifications/signal.ts +79 -43
  380. package/src/notifications/types.ts +98 -121
  381. package/src/oauth/AGENTS.md +5 -24
  382. package/src/permissions/checker.test.ts +51 -0
  383. package/src/permissions/checker.ts +185 -26
  384. package/src/permissions/ipc-risk-types.ts +24 -0
  385. package/src/permissions/question-prompter.test.ts +27 -0
  386. package/src/permissions/question-prompter.ts +4 -0
  387. package/src/permissions/secret-prompter.ts +15 -1
  388. package/src/platform/client.test.ts +119 -0
  389. package/src/platform/client.ts +66 -0
  390. package/src/platform/consent-cache.test.ts +267 -0
  391. package/src/platform/consent-cache.ts +174 -0
  392. package/src/plugin-api/constants.ts +1 -1
  393. package/src/plugin-api/index.ts +33 -1
  394. package/src/plugin-api/model-profiles.ts +33 -0
  395. package/src/plugin-api/types.ts +50 -2
  396. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  397. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  398. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  399. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  400. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  401. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  402. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  403. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  404. package/src/plugins/defaults/advisor/config.ts +21 -0
  405. package/src/plugins/defaults/advisor/consult.ts +93 -0
  406. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  407. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  408. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  409. package/src/plugins/defaults/advisor/package.json +14 -0
  410. package/src/plugins/defaults/advisor/steering.ts +67 -0
  411. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  412. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  413. package/src/plugins/defaults/compaction/window-manager.ts +18 -0
  414. package/src/plugins/defaults/index.ts +60 -0
  415. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  416. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  417. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  418. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  419. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  420. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  421. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  422. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  423. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  424. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  425. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  426. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  427. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  428. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  429. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  430. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  431. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  432. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  433. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  434. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  435. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  436. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  437. package/src/prompts/persona-resolver.ts +14 -4
  438. package/src/prompts/templates/system-sections.ts +7 -2
  439. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  440. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  441. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  442. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  443. package/src/providers/anthropic/client.ts +230 -175
  444. package/src/providers/atlascloud/client.ts +85 -0
  445. package/src/providers/call-site-routing.ts +10 -0
  446. package/src/providers/fetch-provider-catalog.ts +85 -0
  447. package/src/providers/gemini/client.ts +43 -0
  448. package/src/providers/inference/adapter-factory.ts +3 -0
  449. package/src/providers/model-catalog.ts +58 -0
  450. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  451. package/src/providers/openai/chat-completions-provider.ts +7 -0
  452. package/src/providers/openai/responses-provider.ts +10 -0
  453. package/src/providers/provider-send-message.ts +11 -3
  454. package/src/providers/ratelimit.ts +9 -0
  455. package/src/providers/retry.ts +62 -12
  456. package/src/providers/search-provider-catalog.ts +10 -0
  457. package/src/providers/types.ts +16 -0
  458. package/src/providers/usage-tracking.ts +7 -0
  459. package/src/providers/weak-open-model.ts +22 -0
  460. package/src/runtime/AGENTS.md +0 -1
  461. package/src/runtime/__tests__/agent-wake.test.ts +227 -0
  462. package/src/runtime/__tests__/client-health.test.ts +44 -0
  463. package/src/runtime/access-request-helper.ts +21 -53
  464. package/src/runtime/actor-trust-resolver.ts +59 -63
  465. package/src/runtime/agent-wake.ts +71 -0
  466. package/src/runtime/assistant-event-hub.ts +18 -4
  467. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  468. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  469. package/src/runtime/btw-sidechain.ts +3 -6
  470. package/src/runtime/capabilities.test.ts +120 -0
  471. package/src/runtime/capabilities.ts +197 -0
  472. package/src/runtime/channel-approval-types.ts +22 -45
  473. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  474. package/src/runtime/channel-retry-sweep.ts +1 -0
  475. package/src/runtime/channel-verification-service.ts +3 -3
  476. package/src/runtime/client-health.ts +26 -0
  477. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  478. package/src/runtime/effective-capabilities.test.ts +128 -0
  479. package/src/runtime/effective-capabilities.ts +84 -0
  480. package/src/runtime/guardian-reply-router.ts +106 -21
  481. package/src/runtime/invite-redemption-service.ts +9 -25
  482. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
  483. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  484. package/src/runtime/pending-interactions.ts +35 -0
  485. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  486. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  487. package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
  488. package/src/runtime/routes/app-routes.ts +1 -1
  489. package/src/runtime/routes/approval-routes.ts +40 -9
  490. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  491. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  492. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  493. package/src/runtime/routes/btw-routes.ts +1 -27
  494. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  495. package/src/runtime/routes/client-routes.ts +10 -0
  496. package/src/runtime/routes/contact-routes.ts +31 -8
  497. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  498. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  499. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  500. package/src/runtime/routes/conversation-routes.ts +39 -14
  501. package/src/runtime/routes/credential-routes.ts +40 -16
  502. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  503. package/src/runtime/routes/events-routes.ts +1 -3
  504. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  505. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  506. package/src/runtime/routes/home-feed-routes.ts +8 -3
  507. package/src/runtime/routes/identity-routes.ts +1 -296
  508. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  509. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  510. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  511. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  512. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  513. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  514. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  515. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  516. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  517. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  518. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  519. package/src/runtime/routes/index.ts +2 -0
  520. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  521. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  522. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  523. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  524. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  525. package/src/runtime/routes/notification-routes.ts +122 -133
  526. package/src/runtime/routes/platform-routes.ts +2 -2
  527. package/src/runtime/routes/plugins-routes.ts +366 -11
  528. package/src/runtime/routes/schedule-routes.ts +0 -22
  529. package/src/runtime/routes/secret-routes.ts +15 -0
  530. package/src/runtime/routes/surface-action-routes.ts +2 -1
  531. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  532. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  533. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  534. package/src/runtime/routes/workflow-routes.ts +131 -29
  535. package/src/runtime/routes/workspace-greetings.ts +55 -0
  536. package/src/runtime/sync/resource-sync-events.ts +1 -11
  537. package/src/runtime/tool-grant-request-helper.ts +18 -16
  538. package/src/runtime/trust-context-resolver.ts +8 -5
  539. package/src/schedule/inference-profile.ts +2 -14
  540. package/src/schedule/schedule-store.ts +1 -1
  541. package/src/schedule/scheduler-types.ts +5 -1
  542. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  543. package/src/security/secret-patterns.ts +3 -0
  544. package/src/subagent/manager.ts +17 -4
  545. package/src/subagent/types.ts +6 -0
  546. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  547. package/src/telemetry/trace-collection-policy.ts +30 -0
  548. package/src/telemetry/types.ts +89 -0
  549. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  550. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  551. package/src/tools/AGENTS.md +3 -3
  552. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  553. package/src/tools/browser/browser-execution.ts +30 -19
  554. package/src/tools/document/document-tool.ts +30 -6
  555. package/src/tools/executor.ts +5 -3
  556. package/src/tools/host-terminal/host-shell.ts +5 -4
  557. package/src/tools/memory/register.ts +2 -2
  558. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  559. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  560. package/src/tools/network/web-fetch.ts +372 -1
  561. package/src/tools/network/web-search-error.ts +1 -1
  562. package/src/tools/network/web-search.ts +213 -10
  563. package/src/tools/permission-checker.ts +4 -3
  564. package/src/tools/registry.ts +20 -0
  565. package/src/tools/schedule/create.ts +7 -12
  566. package/src/tools/schedule/update.ts +4 -11
  567. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  568. package/src/tools/side-effects.ts +2 -17
  569. package/src/tools/skills/execute.ts +99 -2
  570. package/src/tools/skills/load.ts +43 -2
  571. package/src/tools/subagent/spawn.ts +64 -15
  572. package/src/tools/terminal/shell.ts +10 -4
  573. package/src/tools/tool-approval-handler.ts +18 -13
  574. package/src/tools/tool-manifest.ts +0 -2
  575. package/src/tools/types.ts +10 -0
  576. package/src/tools/ui-surface/definitions.ts +64 -3
  577. package/src/tools/verification-control-plane-policy.ts +3 -1
  578. package/src/tools/workflows/run-workflow.test.ts +8 -18
  579. package/src/tools/workflows/run-workflow.ts +1 -0
  580. package/src/util/disk-usage.ts +78 -23
  581. package/src/util/logs-db-path.ts +22 -0
  582. package/src/util/platform.ts +10 -3
  583. package/src/watcher/telemetry.ts +2 -2
  584. package/src/workflows/capabilities.ts +2 -3
  585. package/src/workflows/engine.test.ts +175 -1
  586. package/src/workflows/engine.ts +82 -0
  587. package/src/workflows/journal-store.test.ts +70 -0
  588. package/src/workflows/journal-store.ts +18 -3
  589. package/src/workflows/run-manager.test.ts +171 -28
  590. package/src/workflows/run-manager.ts +66 -24
  591. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  592. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  593. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  594. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  595. package/src/workspace/migrations/registry.ts +8 -0
  596. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  597. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  598. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  599. package/src/__tests__/credential-vault.test.ts +0 -1706
  600. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  601. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  602. package/src/cli/commands/__tests__/task.test.ts +0 -914
  603. package/src/cli/commands/task.ts +0 -771
  604. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  605. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  606. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  607. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  608. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  609. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  610. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  611. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  612. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  613. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  614. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  615. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  616. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  617. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  618. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  619. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  620. package/src/memory/preloaded-apps.ts +0 -116
  621. package/src/notifications/tool-approval-copy.ts +0 -142
  622. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  623. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  624. package/src/tools/credentials/vault.ts +0 -712
@@ -18,6 +18,7 @@ import type {
18
18
  Provider,
19
19
  ProviderResponse,
20
20
  SendMessageOptions,
21
+ ToolDefinition,
21
22
  } from "../types.js";
22
23
  import {
23
24
  ContextOverflowError,
@@ -823,181 +824,7 @@ export class AnthropicProvider implements Provider {
823
824
  // edge LB, NAT idle) — only the latter should be retried.
824
825
  let innerTimeoutSignal: AbortSignal | undefined;
825
826
  try {
826
- const formatted = messages
827
- .map((m) => {
828
- // Track whether an unknown block was dropped during filtering
829
- let droppedUnknownBlock = false;
830
-
831
- const content = m.content
832
- .map((block) => {
833
- const result = this.toAnthropicBlockSafe(block);
834
- if (result == null) {
835
- droppedUnknownBlock = true;
836
- }
837
- return result;
838
- })
839
- .filter(
840
- (block): block is Anthropic.ContentBlockParam => block != null,
841
- )
842
- .filter(
843
- (block) =>
844
- !(
845
- block.type === "text" &&
846
- !(block as { text?: string }).text?.trim()
847
- ),
848
- );
849
-
850
- // Preserve assistant turns that would otherwise become empty after filtering
851
- // unknown block types (e.g. ui_surface). Dropping these messages can violate
852
- // Anthropic's role alternation requirement.
853
- if (
854
- content.length === 0 &&
855
- m.role === "assistant" &&
856
- droppedUnknownBlock
857
- ) {
858
- return {
859
- role: m.role as "assistant",
860
- content: [
861
- { type: "text" as const, text: PLACEHOLDER_BLOCKS_OMITTED },
862
- ],
863
- };
864
- }
865
-
866
- return {
867
- role: m.role,
868
- content,
869
- } as Anthropic.MessageParam;
870
- })
871
- .reduce<Anthropic.MessageParam[]>((acc, m) => {
872
- if (m.content.length > 0) {
873
- acc.push(m);
874
- return acc;
875
- }
876
- // Dropping an empty assistant message between two user messages (or vice
877
- // versa) would create consecutive same-role messages, violating
878
- // Anthropic's role alternation requirement. Inject a placeholder instead.
879
- const prev = acc[acc.length - 1];
880
- if (m.role === "assistant" && prev && prev.role !== "assistant") {
881
- acc.push({
882
- role: "assistant" as const,
883
- content: [
884
- { type: "text" as const, text: PLACEHOLDER_EMPTY_TURN },
885
- ],
886
- });
887
- }
888
- return acc;
889
- }, []);
890
-
891
- // Post-processing: merge consecutive same-role messages that violate
892
- // Anthropic's strict user/assistant alternation requirement. These can
893
- // arise from:
894
- // - Dropping empty messages in the reduce above (placeholder-adjacent)
895
- // - History reconstruction artifacts that bypass repairHistory
896
- //
897
- // Walk backwards so splice indices stay valid. After a merge+splice
898
- // the element that was at i+1 shifts to i, potentially creating a
899
- // new adjacent pair — bump i back up to recheck that position.
900
- {
901
- let i = formatted.length - 1;
902
- while (i > 0 && i < formatted.length) {
903
- if (formatted[i].role !== formatted[i - 1].role) {
904
- i--;
905
- continue;
906
- }
907
-
908
- const iContent = (
909
- Array.isArray(formatted[i].content) ? formatted[i].content : []
910
- ) as Anthropic.ContentBlockParam[];
911
- const prevContent = (
912
- Array.isArray(formatted[i - 1].content)
913
- ? formatted[i - 1].content
914
- : []
915
- ) as Anthropic.ContentBlockParam[];
916
- const isPlaceholder = (c: Anthropic.ContentBlockParam[]): boolean => {
917
- if (
918
- c.length !== 1 ||
919
- typeof c[0] === "string" ||
920
- c[0].type !== "text"
921
- )
922
- return false;
923
- const text = (c[0] as { text?: string }).text;
924
- return typeof text === "string" && isPlaceholderSentinelText(text);
925
- };
926
-
927
- if (isPlaceholder(iContent)) {
928
- formatted.splice(i, 1);
929
- // Removed the later element. The new formatted[i] (formerly
930
- // i+1) may now be same-role as i-1, so decrement once to
931
- // recheck from the correct position.
932
- i--;
933
- } else if (isPlaceholder(prevContent)) {
934
- formatted.splice(i - 1, 1);
935
- // Removed the earlier element — everything shifted down by 1.
936
- // The element that was at i is now at i-1. Decrement so the
937
- // next iteration compares the new i-1 with i-2 (or exits if
938
- // i-1 is 0).
939
- i--;
940
- } else {
941
- // Neither is a placeholder — merge content blocks into the
942
- // earlier message and remove the later one. Skip the merge
943
- // when either message carries tool_use or tool_result blocks;
944
- // those require structural alternation for ensureToolPairing
945
- // to inject the correct synthetic results downstream.
946
- const hasToolBlock = (c: Anthropic.ContentBlockParam[]): boolean =>
947
- c.some(
948
- (b) =>
949
- typeof b !== "string" &&
950
- (b.type === "tool_use" || b.type === "tool_result"),
951
- );
952
- if (!hasToolBlock(prevContent) && !hasToolBlock(iContent)) {
953
- formatted[i - 1] = {
954
- ...formatted[i - 1],
955
- content: [...prevContent, ...iContent],
956
- };
957
- formatted.splice(i, 1);
958
- // Clamp i to the new last index — the splice may have put
959
- // us past the end. If there's a new element at i (formerly
960
- // i+1), it will be rechecked against the merged i-1.
961
- if (i >= formatted.length) {
962
- i = formatted.length - 1;
963
- }
964
- } else {
965
- // Can't merge (tool blocks present) — leave for
966
- // ensureToolPairing which handles tool_use/tool_result
967
- // alternation in its own forward walk.
968
- i--;
969
- }
970
- }
971
- }
972
- }
973
-
974
- // Strip thinking/redacted_thinking blocks from completed historical
975
- // assistant turns. Anthropic only requires these blocks for active
976
- // tool-use continuation (the tail span where assistant tool_use is
977
- // followed by user tool_result). Replaying stale thinking blocks from
978
- // earlier turns causes 400 errors when the signature is no longer
979
- // valid (e.g. after a provider/model/profile switch).
980
- const activeToolUseStart = findActiveToolUseContinuationStart(formatted);
981
- for (let i = 0; i < activeToolUseStart; i++) {
982
- const msg = formatted[i];
983
- if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
984
- const stripped = (msg.content as Anthropic.ContentBlockParam[]).filter(
985
- (b) =>
986
- typeof b === "string" ||
987
- (b.type !== "thinking" && b.type !== "redacted_thinking"),
988
- );
989
- if (stripped.length === 0) {
990
- stripped.push({
991
- type: "text" as const,
992
- text: PLACEHOLDER_BLOCKS_OMITTED,
993
- });
994
- }
995
- formatted[i] = { ...msg, content: stripped };
996
- }
997
-
998
- sentMessages = ensureToolPairing(
999
- repairOrphanedServerToolBlocks(formatted),
1000
- );
827
+ sentMessages = this.buildSentMessages(messages);
1001
828
  const {
1002
829
  effort,
1003
830
  speed,
@@ -1681,6 +1508,234 @@ export class AnthropicProvider implements Provider {
1681
1508
  }
1682
1509
  }
1683
1510
 
1511
+ /**
1512
+ * Exact prompt-token count via Anthropic's `/v1/messages/count_tokens`
1513
+ * endpoint — the real tokenizer, no inference. Serializes `messages` /
1514
+ * `systemPrompt` / `tools` the same way {@link sendMessage} does (so the
1515
+ * count tracks what the next call would actually send), minus the
1516
+ * `cache_control` breakpoints, which don't affect token counts.
1517
+ *
1518
+ * The serialization here is intentionally simpler than `sendMessage`'s
1519
+ * (no role-alternation merge / placeholder injection): on a pathological
1520
+ * history `count_tokens` may reject the request, which surfaces as a thrown
1521
+ * error the caller turns into a local-estimator fallback. The common path —
1522
+ * a well-formed history — counts exactly.
1523
+ */
1524
+ async countInputTokens(
1525
+ messages: Message[],
1526
+ systemPrompt: string,
1527
+ tools?: ToolDefinition[],
1528
+ ): Promise<number> {
1529
+ const sentMessages = this.buildSentMessages(messages);
1530
+
1531
+ const system = systemPrompt
1532
+ ? systemPrompt
1533
+ .split(SYSTEM_PROMPT_CACHE_BOUNDARY)
1534
+ .filter((text) => text.length > 0)
1535
+ .map((text) => ({ type: "text" as const, text }))
1536
+ : [];
1537
+ const toolsParam =
1538
+ tools && tools.length > 0
1539
+ ? tools.map((t) => ({
1540
+ name: t.name,
1541
+ description: t.description,
1542
+ input_schema: t.input_schema as Anthropic.Tool["input_schema"],
1543
+ }))
1544
+ : undefined;
1545
+
1546
+ const res = await this.client.messages.countTokens({
1547
+ model: this.model,
1548
+ messages: sentMessages,
1549
+ ...(system.length > 0 ? { system } : {}),
1550
+ ...(toolsParam ? { tools: toolsParam } : {}),
1551
+ });
1552
+ return res.input_tokens;
1553
+ }
1554
+
1555
+ /**
1556
+ * Serialize internal `Message[]` into the Anthropic `MessageParam[]` the
1557
+ * Messages API expects: drop unknown/empty blocks, preserve role
1558
+ * alternation (placeholder injection + same-role merge), strip stale
1559
+ * thinking blocks outside the active tool-use continuation, and repair
1560
+ * tool_use/tool_result pairing. Shared by {@link sendMessage} and
1561
+ * {@link countInputTokens} so both send the identical message payload —
1562
+ * `cache_control` breakpoints (which don't affect token counts) are the
1563
+ * only thing layered on top in `sendMessage`.
1564
+ */
1565
+ private buildSentMessages(messages: Message[]): Anthropic.MessageParam[] {
1566
+ const formatted = messages
1567
+ .map((m) => {
1568
+ // Track whether an unknown block was dropped during filtering
1569
+ let droppedUnknownBlock = false;
1570
+
1571
+ const content = m.content
1572
+ .map((block) => {
1573
+ const result = this.toAnthropicBlockSafe(block);
1574
+ if (result == null) {
1575
+ droppedUnknownBlock = true;
1576
+ }
1577
+ return result;
1578
+ })
1579
+ .filter(
1580
+ (block): block is Anthropic.ContentBlockParam => block != null,
1581
+ )
1582
+ .filter(
1583
+ (block) =>
1584
+ !(
1585
+ block.type === "text" &&
1586
+ !(block as { text?: string }).text?.trim()
1587
+ ),
1588
+ );
1589
+
1590
+ // Preserve assistant turns that would otherwise become empty after filtering
1591
+ // unknown block types (e.g. ui_surface). Dropping these messages can violate
1592
+ // Anthropic's role alternation requirement.
1593
+ if (
1594
+ content.length === 0 &&
1595
+ m.role === "assistant" &&
1596
+ droppedUnknownBlock
1597
+ ) {
1598
+ return {
1599
+ role: m.role as "assistant",
1600
+ content: [
1601
+ { type: "text" as const, text: PLACEHOLDER_BLOCKS_OMITTED },
1602
+ ],
1603
+ };
1604
+ }
1605
+
1606
+ return {
1607
+ role: m.role,
1608
+ content,
1609
+ } as Anthropic.MessageParam;
1610
+ })
1611
+ .reduce<Anthropic.MessageParam[]>((acc, m) => {
1612
+ if (m.content.length > 0) {
1613
+ acc.push(m);
1614
+ return acc;
1615
+ }
1616
+ // Dropping an empty assistant message between two user messages (or vice
1617
+ // versa) would create consecutive same-role messages, violating
1618
+ // Anthropic's role alternation requirement. Inject a placeholder instead.
1619
+ const prev = acc[acc.length - 1];
1620
+ if (m.role === "assistant" && prev && prev.role !== "assistant") {
1621
+ acc.push({
1622
+ role: "assistant" as const,
1623
+ content: [{ type: "text" as const, text: PLACEHOLDER_EMPTY_TURN }],
1624
+ });
1625
+ }
1626
+ return acc;
1627
+ }, []);
1628
+
1629
+ // Post-processing: merge consecutive same-role messages that violate
1630
+ // Anthropic's strict user/assistant alternation requirement. These can
1631
+ // arise from:
1632
+ // - Dropping empty messages in the reduce above (placeholder-adjacent)
1633
+ // - History reconstruction artifacts that bypass repairHistory
1634
+ //
1635
+ // Walk backwards so splice indices stay valid. After a merge+splice
1636
+ // the element that was at i+1 shifts to i, potentially creating a
1637
+ // new adjacent pair — bump i back up to recheck that position.
1638
+ {
1639
+ let i = formatted.length - 1;
1640
+ while (i > 0 && i < formatted.length) {
1641
+ if (formatted[i].role !== formatted[i - 1].role) {
1642
+ i--;
1643
+ continue;
1644
+ }
1645
+
1646
+ const iContent = (
1647
+ Array.isArray(formatted[i].content) ? formatted[i].content : []
1648
+ ) as Anthropic.ContentBlockParam[];
1649
+ const prevContent = (
1650
+ Array.isArray(formatted[i - 1].content)
1651
+ ? formatted[i - 1].content
1652
+ : []
1653
+ ) as Anthropic.ContentBlockParam[];
1654
+ const isPlaceholder = (c: Anthropic.ContentBlockParam[]): boolean => {
1655
+ if (
1656
+ c.length !== 1 ||
1657
+ typeof c[0] === "string" ||
1658
+ c[0].type !== "text"
1659
+ )
1660
+ return false;
1661
+ const text = (c[0] as { text?: string }).text;
1662
+ return typeof text === "string" && isPlaceholderSentinelText(text);
1663
+ };
1664
+
1665
+ if (isPlaceholder(iContent)) {
1666
+ formatted.splice(i, 1);
1667
+ // Removed the later element. The new formatted[i] (formerly
1668
+ // i+1) may now be same-role as i-1, so decrement once to
1669
+ // recheck from the correct position.
1670
+ i--;
1671
+ } else if (isPlaceholder(prevContent)) {
1672
+ formatted.splice(i - 1, 1);
1673
+ // Removed the earlier element — everything shifted down by 1.
1674
+ // The element that was at i is now at i-1. Decrement so the
1675
+ // next iteration compares the new i-1 with i-2 (or exits if
1676
+ // i-1 is 0).
1677
+ i--;
1678
+ } else {
1679
+ // Neither is a placeholder — merge content blocks into the
1680
+ // earlier message and remove the later one. Skip the merge
1681
+ // when either message carries tool_use or tool_result blocks;
1682
+ // those require structural alternation for ensureToolPairing
1683
+ // to inject the correct synthetic results downstream.
1684
+ const hasToolBlock = (c: Anthropic.ContentBlockParam[]): boolean =>
1685
+ c.some(
1686
+ (b) =>
1687
+ typeof b !== "string" &&
1688
+ (b.type === "tool_use" || b.type === "tool_result"),
1689
+ );
1690
+ if (!hasToolBlock(prevContent) && !hasToolBlock(iContent)) {
1691
+ formatted[i - 1] = {
1692
+ ...formatted[i - 1],
1693
+ content: [...prevContent, ...iContent],
1694
+ };
1695
+ formatted.splice(i, 1);
1696
+ // Clamp i to the new last index — the splice may have put
1697
+ // us past the end. If there's a new element at i (formerly
1698
+ // i+1), it will be rechecked against the merged i-1.
1699
+ if (i >= formatted.length) {
1700
+ i = formatted.length - 1;
1701
+ }
1702
+ } else {
1703
+ // Can't merge (tool blocks present) — leave for
1704
+ // ensureToolPairing which handles tool_use/tool_result
1705
+ // alternation in its own forward walk.
1706
+ i--;
1707
+ }
1708
+ }
1709
+ }
1710
+ }
1711
+
1712
+ // Strip thinking/redacted_thinking blocks from completed historical
1713
+ // assistant turns. Anthropic only requires these blocks for active
1714
+ // tool-use continuation (the tail span where assistant tool_use is
1715
+ // followed by user tool_result). Replaying stale thinking blocks from
1716
+ // earlier turns causes 400 errors when the signature is no longer
1717
+ // valid (e.g. after a provider/model/profile switch).
1718
+ const activeToolUseStart = findActiveToolUseContinuationStart(formatted);
1719
+ for (let i = 0; i < activeToolUseStart; i++) {
1720
+ const msg = formatted[i];
1721
+ if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
1722
+ const stripped = (msg.content as Anthropic.ContentBlockParam[]).filter(
1723
+ (b) =>
1724
+ typeof b === "string" ||
1725
+ (b.type !== "thinking" && b.type !== "redacted_thinking"),
1726
+ );
1727
+ if (stripped.length === 0) {
1728
+ stripped.push({
1729
+ type: "text" as const,
1730
+ text: PLACEHOLDER_BLOCKS_OMITTED,
1731
+ });
1732
+ }
1733
+ formatted[i] = { ...msg, content: stripped };
1734
+ }
1735
+
1736
+ return ensureToolPairing(repairOrphanedServerToolBlocks(formatted));
1737
+ }
1738
+
1684
1739
  /**
1685
1740
  * Convert a content block to Anthropic format, returning null for unknown
1686
1741
  * block types instead of throwing. Unknown types (e.g. ui_surface stored
@@ -0,0 +1,85 @@
1
+ import OpenAI from "openai";
2
+
3
+ import { getLogger } from "../../util/logger.js";
4
+ import { OpenAIChatCompletionsProvider } from "../openai/chat-completions-provider.js";
5
+
6
+ const log = getLogger("atlascloud-client");
7
+
8
+ /** Validation-specific timeout (10s) so a stalled network doesn't block key submission. */
9
+ const VALIDATION_TIMEOUT_MS = 10_000;
10
+
11
+ export interface AtlasCloudProviderOptions {
12
+ apiKey?: string;
13
+ streamTimeoutMs?: number;
14
+ }
15
+
16
+ /**
17
+ * Atlas Cloud exposes a single OpenAI-compatible endpoint. Unlike MiniMax it
18
+ * has no regional fallback host, so validation targets this URL only.
19
+ */
20
+ const DEFAULT_ATLASCLOUD_BASE_URL = "https://api.atlascloud.ai/v1";
21
+
22
+ /**
23
+ * Validate an Atlas Cloud API key by listing models against the OpenAI-compatible
24
+ * endpoint. Definitive auth failures (401/403) reject the key; transient errors
25
+ * (429, 5xx, network) allow the key to be stored so a flaky network doesn't
26
+ * block setup.
27
+ */
28
+ export async function validateAtlasCloudApiKey(
29
+ apiKey: string,
30
+ ): Promise<{ valid: true } | { valid: false; reason: string }> {
31
+ try {
32
+ const client = new OpenAI({
33
+ apiKey,
34
+ baseURL: DEFAULT_ATLASCLOUD_BASE_URL,
35
+ timeout: VALIDATION_TIMEOUT_MS,
36
+ maxRetries: 0,
37
+ });
38
+ await client.models.list();
39
+ return { valid: true };
40
+ } catch (error) {
41
+ if (error instanceof OpenAI.APIError) {
42
+ if (error.status === 401) {
43
+ return { valid: false, reason: "API key is invalid or expired." };
44
+ }
45
+ if (error.status === 403) {
46
+ return {
47
+ valid: false,
48
+ reason: `Atlas Cloud API error (${error.status}): ${error.message}`,
49
+ };
50
+ }
51
+ // Transient errors (429, 5xx, etc.) — allow the key to be stored.
52
+ log.warn(
53
+ { status: error.status },
54
+ "Atlas Cloud API returned a transient error during key validation — allowing key",
55
+ );
56
+ return { valid: true };
57
+ }
58
+ // Network errors — allow the key to be stored.
59
+ log.warn(
60
+ { error: error instanceof Error ? error.message : String(error) },
61
+ "Network error during Atlas Cloud key validation — allowing key",
62
+ );
63
+ return { valid: true };
64
+ }
65
+ }
66
+
67
+ export class AtlasCloudProvider extends OpenAIChatCompletionsProvider {
68
+ constructor(
69
+ apiKey: string,
70
+ model: string,
71
+ options: AtlasCloudProviderOptions = {},
72
+ ) {
73
+ super(apiKey, model, {
74
+ baseURL: DEFAULT_ATLASCLOUD_BASE_URL,
75
+ providerName: "atlascloud",
76
+ providerLabel: "Atlas Cloud",
77
+ streamTimeoutMs: options.streamTimeoutMs,
78
+ // Atlas Cloud hosts reasoning models (e.g. DeepSeek) that emit chain of
79
+ // thought via `reasoning_content` rather than inline `<think>` tags. The
80
+ // base provider parses this field into thinking blocks and replays it on
81
+ // multi-turn requests.
82
+ assistantReasoningField: "reasoning_content",
83
+ });
84
+ }
85
+ }
@@ -60,6 +60,12 @@ export class CallSiteRoutingProvider implements Provider {
60
60
  return this._activeProviderContext.getStore() ?? this.defaultProvider.name;
61
61
  }
62
62
 
63
+ // Forward the optional token-counting endpoint from the default provider —
64
+ // the same one whose `tokenEstimationProvider` this wrapper surfaces, and
65
+ // the provider that handles the main agent turn that `/compact` sizes
66
+ // against. Per-call connection routing only affects `sendMessage`.
67
+ public readonly countInputTokens?: NonNullable<Provider["countInputTokens"]>;
68
+
63
69
  constructor(
64
70
  private readonly defaultProvider: Provider,
65
71
  /**
@@ -88,6 +94,10 @@ export class CallSiteRoutingProvider implements Provider {
88
94
  ) => Promise<Provider | null>,
89
95
  ) {
90
96
  this.tokenEstimationProvider = defaultProvider.tokenEstimationProvider;
97
+ if (defaultProvider.countInputTokens) {
98
+ this.countInputTokens =
99
+ defaultProvider.countInputTokens.bind(defaultProvider);
100
+ }
91
101
  }
92
102
 
93
103
  async sendMessage(
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Canonical catalog of built-in web-fetch providers.
3
+ *
4
+ * This is the single source of truth that drives the config-schema enum
5
+ * (`VALID_WEB_FETCH_PROVIDERS` in `assistant/src/config/schemas/services.ts`)
6
+ * and the client picker mirror in
7
+ * `clients/web/src/assistant/generated/web-fetch-provider-catalog.gen.ts`.
8
+ *
9
+ * Mirrors `search-provider-catalog.ts`. Where web-search distinguishes
10
+ * `managed` (platform proxy) from `byok` (user key), web-fetch has no managed
11
+ * proxy yet, so the two kinds are:
12
+ *
13
+ * - `builtin` — the daemon's own HTTP fetch + extract path (`default`). No
14
+ * key, always available, the default.
15
+ * - `byok` — an external provider that needs a user-supplied key (e.g.
16
+ * `firecrawl`, which scrapes via its hosted API and returns clean
17
+ * markdown, including for JavaScript-rendered pages the builtin fetcher
18
+ * can't see).
19
+ *
20
+ * BYOK fetch providers intentionally reuse the SAME bare-name credential as
21
+ * their search counterpart (e.g. `firecrawl` → `FIRECRAWL_API_KEY`), so a
22
+ * single stored key powers both `web_search` and `web_fetch`. The key is
23
+ * already registered in `API_KEY_PROVIDERS` via `SEARCH_PROVIDER_CATALOG`;
24
+ * this catalog deliberately does NOT re-register it.
25
+ */
26
+
27
+ export type FetchProviderKind = "builtin" | "byok";
28
+
29
+ export interface FetchProviderCatalogEntry {
30
+ /** Stable provider identifier. Matches config values. */
31
+ readonly id: string;
32
+ /** Short display name used by picker UIs. */
33
+ readonly displayName: string;
34
+ /** Optional long display name for prose contexts. */
35
+ readonly displayNameLong?: string;
36
+ /** `builtin` (no key) or `byok` (user-supplied key). */
37
+ readonly kind: FetchProviderKind;
38
+ /** Placeholder shown in the API-key input. BYOK providers only. */
39
+ readonly apiKeyPrefix?: string;
40
+ /** Environment variable name carrying the API key. BYOK providers only. */
41
+ readonly envVar?: string;
42
+ /** Secret-catalog key (the bare provider name accepted by
43
+ * `getProviderKeyAsync`). BYOK providers only. */
44
+ readonly secretKey?: string;
45
+ /** Privacy-policy URL surfaced in marketing data-sharing docs.
46
+ * BYOK providers only. */
47
+ readonly privacyPolicyUrl?: string;
48
+ }
49
+
50
+ export const FETCH_PROVIDER_CATALOG: readonly FetchProviderCatalogEntry[] = [
51
+ {
52
+ id: "default",
53
+ displayName: "Built-in",
54
+ displayNameLong: "Built-in fetcher",
55
+ kind: "builtin",
56
+ },
57
+ {
58
+ id: "firecrawl",
59
+ displayName: "Firecrawl",
60
+ kind: "byok",
61
+ apiKeyPrefix: "fc-...",
62
+ envVar: "FIRECRAWL_API_KEY",
63
+ secretKey: "firecrawl",
64
+ privacyPolicyUrl: "https://www.firecrawl.dev/privacy-policy",
65
+ },
66
+ ];
67
+
68
+ /** Provider ids accepted by the web-fetch config schema. */
69
+ export const FETCH_PROVIDER_IDS: readonly string[] =
70
+ FETCH_PROVIDER_CATALOG.map((p) => p.id);
71
+
72
+ /** Catalog entries that require a user-supplied API key. */
73
+ export const BYOK_FETCH_PROVIDERS: readonly FetchProviderCatalogEntry[] =
74
+ FETCH_PROVIDER_CATALOG.filter((p) => p.kind === "byok");
75
+
76
+ /** BYOK fetch-provider ids. */
77
+ export const BYOK_FETCH_PROVIDER_IDS: readonly string[] =
78
+ BYOK_FETCH_PROVIDERS.map((p) => p.id);
79
+
80
+ /** Look up a single catalog entry by id. Returns `undefined` if unknown. */
81
+ export function getFetchProvider(
82
+ id: string,
83
+ ): FetchProviderCatalogEntry | undefined {
84
+ return FETCH_PROVIDER_CATALOG.find((p) => p.id === id);
85
+ }
@@ -17,6 +17,7 @@ import type {
17
17
  Provider,
18
18
  ProviderResponse,
19
19
  SendMessageOptions,
20
+ ToolDefinition,
20
21
  } from "../types.js";
21
22
  import {
22
23
  ContextOverflowError,
@@ -518,6 +519,48 @@ export class GeminiProvider implements Provider {
518
519
  }
519
520
  }
520
521
 
522
+ /**
523
+ * Exact prompt-token count via Gemini's `models.countTokens` — the real
524
+ * tokenizer, no generation. Mirrors {@link sendMessage}'s composition
525
+ * (contents + `systemInstruction` + tool function declarations) so the count
526
+ * tracks what a real call would send. Throws when the endpoint returns no
527
+ * `totalTokens`, so the caller falls back to the local estimate.
528
+ */
529
+ async countInputTokens(
530
+ messages: Message[],
531
+ systemPrompt: string,
532
+ tools?: ToolDefinition[],
533
+ ): Promise<number> {
534
+ const contents = this.toGeminiContents(messages, this.model);
535
+ const config: genai.CountTokensConfig = {};
536
+ if (systemPrompt) {
537
+ config.systemInstruction = systemPrompt.replaceAll(
538
+ SYSTEM_PROMPT_CACHE_BOUNDARY,
539
+ "\n\n",
540
+ );
541
+ }
542
+ if (tools && tools.length > 0) {
543
+ config.tools = [
544
+ {
545
+ functionDeclarations: tools.map((t) => ({
546
+ name: t.name,
547
+ description: t.description,
548
+ parametersJsonSchema: t.input_schema,
549
+ })),
550
+ },
551
+ ];
552
+ }
553
+ const res = await this.client.models.countTokens({
554
+ model: this.model,
555
+ contents,
556
+ ...(Object.keys(config).length > 0 ? { config } : {}),
557
+ });
558
+ if (typeof res.totalTokens !== "number") {
559
+ throw new Error("Gemini countTokens returned no totalTokens");
560
+ }
561
+ return res.totalTokens;
562
+ }
563
+
521
564
  /** Convert neutral messages to Gemini Content[] format. */
522
565
  private toGeminiContents(
523
566
  messages: Message[],
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  import { AnthropicProvider } from "../anthropic/client.js";
22
+ import { AtlasCloudProvider } from "../atlascloud/client.js";
22
23
  import { FireworksProvider } from "../fireworks/client.js";
23
24
  import { GeminiProvider } from "../gemini/client.js";
24
25
  import { MinimaxProvider } from "../minimax/client.js";
@@ -114,6 +115,8 @@ const ADAPTER_FACTORIES: Record<string, AdapterFactory> = {
114
115
  }),
115
116
  minimax: ({ apiKey, model, streamTimeoutMs }) =>
116
117
  new MinimaxProvider(apiKey, model, { streamTimeoutMs }),
118
+ atlascloud: ({ apiKey, model, streamTimeoutMs }) =>
119
+ new AtlasCloudProvider(apiKey, model, { streamTimeoutMs }),
117
120
  };
118
121
 
119
122
  /**