@vellumai/assistant 0.9.0 → 0.10.0-staging.2

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 (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  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 +976 -63
  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__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -35,8 +35,9 @@ const BRAVE_SEARCH_PATH = "/res/v1/web/search";
35
35
  const BRAVE_API_URL = `https://api.search.brave.com${BRAVE_SEARCH_PATH}`;
36
36
  const PERPLEXITY_API_URL = "https://api.perplexity.ai/chat/completions";
37
37
  const TAVILY_API_URL = "https://api.tavily.com/search";
38
+ const FIRECRAWL_API_URL = "https://api.firecrawl.dev/v2/search";
38
39
 
39
- type WebSearchProvider = "perplexity" | "brave" | "tavily";
40
+ type WebSearchProvider = "perplexity" | "brave" | "tavily" | "firecrawl";
40
41
  type WebSearchMode = "managed" | "your-own";
41
42
 
42
43
  /**
@@ -108,6 +109,28 @@ interface TavilySearchResponse {
108
109
  results?: TavilySearchResult[];
109
110
  }
110
111
 
112
+ interface FirecrawlSearchResult {
113
+ title?: string;
114
+ description?: string;
115
+ url?: string;
116
+ category?: string;
117
+ }
118
+
119
+ /**
120
+ * Firecrawl `/v2/search` returns one array per requested source under `data`
121
+ * (defaults to `web`). We request only `web`, so that is the array we read;
122
+ * `news`/`images` are typed loosely since we never request them here.
123
+ */
124
+ interface FirecrawlSearchResponse {
125
+ success?: boolean;
126
+ data?: {
127
+ web?: FirecrawlSearchResult[];
128
+ news?: FirecrawlSearchResult[];
129
+ images?: unknown[];
130
+ };
131
+ warning?: string | null;
132
+ }
133
+
111
134
  function getWebSearchProvider(): WebSearchProvider {
112
135
  const config = getConfig();
113
136
  const configured = config.services["web-search"].provider ?? "perplexity";
@@ -229,6 +252,34 @@ function formatTavilyResults(
229
252
  return lines.join("\n");
230
253
  }
231
254
 
255
+ function formatFirecrawlResults(
256
+ data: FirecrawlSearchResponse,
257
+ query: string,
258
+ ): string {
259
+ const results = data.data?.web ?? [];
260
+
261
+ if (results.length === 0) {
262
+ return `No results found for "${query}".`;
263
+ }
264
+
265
+ const lines: string[] = [`Web search results for "${query}":\n`];
266
+
267
+ for (let i = 0; i < results.length; i++) {
268
+ const r = results[i];
269
+ const title = r.title?.trim() || r.url?.trim() || "Untitled result";
270
+ lines.push(`${i + 1}. ${title}`);
271
+ if (r.url) {
272
+ lines.push(` URL: ${r.url}`);
273
+ }
274
+ if (r.description) {
275
+ lines.push(` ${r.description}`);
276
+ }
277
+ lines.push("");
278
+ }
279
+
280
+ return lines.join("\n");
281
+ }
282
+
232
283
  function buildBraveMetadata(
233
284
  results: BraveSearchResult[],
234
285
  query: string,
@@ -365,6 +416,55 @@ function tavilyTimeRangeForFreshness(
365
416
  }
366
417
  }
367
418
 
419
+ function buildFirecrawlMetadata(
420
+ data: FirecrawlSearchResponse,
421
+ query: string,
422
+ durationMs: number,
423
+ ): WebSearchMetadata {
424
+ const results = data.data?.web ?? [];
425
+ const items: WebSearchResultItem[] = results.map((r, i) => {
426
+ const url = r.url ?? "";
427
+ const domain = extractDomain(url);
428
+ return {
429
+ rank: i + 1,
430
+ title: r.title?.trim() || url.trim() || "Untitled result",
431
+ url,
432
+ domain,
433
+ faviconUrl: faviconUrlForDomain(domain),
434
+ snippet: r.description,
435
+ };
436
+ });
437
+ return {
438
+ query,
439
+ provider: "firecrawl",
440
+ resultCount: items.length,
441
+ durationMs,
442
+ results: items,
443
+ };
444
+ }
445
+
446
+ /**
447
+ * Map the tool's `freshness` window onto Firecrawl's `tbs` time filter
448
+ * (`qdr:d` / `qdr:w` / `qdr:m` / `qdr:y`). Returns `undefined` for an
449
+ * unrecognized value so the request omits `tbs` entirely.
450
+ */
451
+ function firecrawlTbsForFreshness(
452
+ freshness: string | undefined,
453
+ ): "qdr:d" | "qdr:w" | "qdr:m" | "qdr:y" | undefined {
454
+ switch (freshness) {
455
+ case "pd":
456
+ return "qdr:d";
457
+ case "pw":
458
+ return "qdr:w";
459
+ case "pm":
460
+ return "qdr:m";
461
+ case "py":
462
+ return "qdr:y";
463
+ default:
464
+ return undefined;
465
+ }
466
+ }
467
+
368
468
  function errorResult(
369
469
  query: string,
370
470
  provider: WebSearchProvider,
@@ -396,8 +496,7 @@ function errorResult(
396
496
  */
397
497
  function rawBodyDetail(body: unknown): { message: string } | undefined {
398
498
  if (body == null) return undefined;
399
- const text =
400
- typeof body === "string" ? body : safeStringifyBody(body);
499
+ const text = typeof body === "string" ? body : safeStringifyBody(body);
401
500
  const trimmed = text.trim();
402
501
  return trimmed ? { message: trimmed } : undefined;
403
502
  }
@@ -602,10 +701,7 @@ async function executeManagedBraveSearch(
602
701
  if (!proxyResult.ok) {
603
702
  // Keep billing/auth/unavailable mapping as specific copy; route genuine
604
703
  // platform 5xx (transport-level failures) to the friendly backend helper.
605
- if (
606
- proxyResult.kind === "platform-error" &&
607
- proxyResult.status >= 500
608
- ) {
704
+ if (proxyResult.kind === "platform-error" && proxyResult.status >= 500) {
609
705
  return backendFailureResult(
610
706
  query,
611
707
  "brave",
@@ -868,6 +964,104 @@ async function executeTavilySearch(
868
964
  );
869
965
  }
870
966
 
967
+ async function executeFirecrawlSearch(
968
+ query: string,
969
+ count: number,
970
+ freshness: string | undefined,
971
+ apiKey: string,
972
+ signal?: AbortSignal,
973
+ ): Promise<ToolExecutionResult> {
974
+ const tbs = firecrawlTbsForFreshness(freshness);
975
+ const body: Record<string, unknown> = {
976
+ query,
977
+ limit: count,
978
+ sources: ["web"],
979
+ };
980
+ if (tbs) {
981
+ body.tbs = tbs;
982
+ }
983
+
984
+ const startedAt = Date.now();
985
+
986
+ for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
987
+ let response: Response;
988
+ try {
989
+ response = await fetch(FIRECRAWL_API_URL, {
990
+ method: "POST",
991
+ headers: {
992
+ "Content-Type": "application/json",
993
+ Authorization: `Bearer ${apiKey}`,
994
+ "X-Client-Source": "vellum-assistant",
995
+ },
996
+ body: JSON.stringify(body),
997
+ signal,
998
+ });
999
+ } catch (err) {
1000
+ return networkFailureResult(query, "firecrawl", startedAt, err, signal);
1001
+ }
1002
+
1003
+ if (response.ok) {
1004
+ const data = (await response.json()) as FirecrawlSearchResponse;
1005
+ const durationMs = Date.now() - startedAt;
1006
+ return {
1007
+ content:
1008
+ wrapUntrustedContent(formatFirecrawlResults(data, query), {
1009
+ source: "search",
1010
+ sourceDetail: "firecrawl",
1011
+ }) + CITATION_INSTRUCTION,
1012
+ isError: false,
1013
+ activityMetadata: {
1014
+ webSearch: buildFirecrawlMetadata(data, query, durationMs),
1015
+ },
1016
+ };
1017
+ }
1018
+
1019
+ const bodyText = await response.text();
1020
+
1021
+ if (response.status === 401 || response.status === 403) {
1022
+ return errorResult(
1023
+ query,
1024
+ "firecrawl",
1025
+ startedAt,
1026
+ "Invalid or expired Firecrawl API key",
1027
+ );
1028
+ }
1029
+
1030
+ if (response.status === 429 && attempt < DEFAULT_MAX_RETRIES) {
1031
+ const delayMs = getHttpRetryDelay(
1032
+ response,
1033
+ attempt,
1034
+ DEFAULT_BASE_DELAY_MS,
1035
+ );
1036
+ log.warn(
1037
+ { attempt: attempt + 1, delayMs },
1038
+ "Firecrawl Search rate limited, retrying",
1039
+ );
1040
+ await sleep(delayMs);
1041
+ continue;
1042
+ }
1043
+
1044
+ log.warn({ status: response.status }, "Firecrawl Search API error");
1045
+ return backendFailureResult(
1046
+ query,
1047
+ "firecrawl",
1048
+ startedAt,
1049
+ { statusCode: response.status, error: rawBodyDetail(bodyText) },
1050
+ response.status === 429
1051
+ ? "Firecrawl Search rate limit exceeded after retries. Try again shortly."
1052
+ : `Firecrawl Search API returned status ${response.status}`,
1053
+ );
1054
+ }
1055
+
1056
+ return backendFailureResult(
1057
+ query,
1058
+ "firecrawl",
1059
+ startedAt,
1060
+ { statusCode: 429 },
1061
+ "Firecrawl Search rate limit exceeded after retries. Try again shortly.",
1062
+ );
1063
+ }
1064
+
871
1065
  // ----------------------------------------------------------------------------
872
1066
  // Adapter registry
873
1067
  //
@@ -909,6 +1103,14 @@ const tavilySearchAdapter: WebSearchAdapter = {
909
1103
  executeTavilySearch(query, count, freshness, apiKey, signal),
910
1104
  };
911
1105
 
1106
+ const firecrawlSearchAdapter: WebSearchAdapter = {
1107
+ id: "firecrawl",
1108
+ providerKeyName: "firecrawl",
1109
+ fallbackOrder: 4,
1110
+ execute: ({ query, count, freshness, apiKey, signal }) =>
1111
+ executeFirecrawlSearch(query, count, freshness, apiKey, signal),
1112
+ };
1113
+
912
1114
  /**
913
1115
  * All built-in web-search adapters keyed by provider id. The
914
1116
  * `Record<WebSearchProvider, ...>` shape forces TypeScript to flag any
@@ -918,6 +1120,7 @@ const WEB_SEARCH_ADAPTERS: Record<WebSearchProvider, WebSearchAdapter> = {
918
1120
  perplexity: perplexitySearchAdapter,
919
1121
  brave: braveSearchAdapter,
920
1122
  tavily: tavilySearchAdapter,
1123
+ firecrawl: firecrawlSearchAdapter,
921
1124
  };
922
1125
 
923
1126
  /**
@@ -949,7 +1152,7 @@ export const webSearchTool = {
949
1152
  count: {
950
1153
  type: "number",
951
1154
  description:
952
- "Number of results to return (1-20, default 10). Used with Brave and Tavily providers.",
1155
+ "Number of results to return (1-20, default 10). Used with Brave, Tavily, and Firecrawl providers.",
953
1156
  },
954
1157
  offset: {
955
1158
  type: "number",
@@ -959,7 +1162,7 @@ export const webSearchTool = {
959
1162
  freshness: {
960
1163
  type: "string",
961
1164
  description:
962
- 'Filter by recency: "pd" (past day), "pw" (past week), "pm" (past month), "py" (past year). Used with Brave and Tavily providers.',
1165
+ 'Filter by recency: "pd" (past day), "pw" (past week), "pm" (past month), "py" (past year). Used with Brave, Tavily, and Firecrawl providers.',
963
1166
  },
964
1167
  },
965
1168
  required: ["query"],
@@ -1039,7 +1242,7 @@ export const webSearchTool = {
1039
1242
  query,
1040
1243
  provider,
1041
1244
  startedAt,
1042
- "No web search API key configured. Set it via `keys set perplexity <key>`, `keys set brave <key>`, or `keys set tavily <key>`, or configure it from the Settings page under API Keys.",
1245
+ "No web search API key configured. Set it via `keys set perplexity <key>`, `keys set brave <key>`, `keys set tavily <key>`, or `keys set firecrawl <key>`, or configure it from the Settings page under API Keys.",
1043
1246
  );
1044
1247
  }
1045
1248
  }
@@ -16,6 +16,7 @@ import type {
16
16
  RiskThreshold,
17
17
  } from "../permissions/types.js";
18
18
  import { RiskLevel } from "../permissions/types.js";
19
+ import { resolveCapabilities } from "../runtime/capabilities.js";
19
20
  import { getLogger } from "../util/logger.js";
20
21
  import { buildPolicyContext } from "./policy-context.js";
21
22
  import { isSideEffectTool } from "./side-effects.js";
@@ -173,7 +174,7 @@ export class PermissionChecker {
173
174
  if (
174
175
  context.forcePromptSideEffects &&
175
176
  result.decision === "allow" &&
176
- isSideEffectTool(name, input)
177
+ isSideEffectTool(name)
177
178
  ) {
178
179
  result.decision = "prompt";
179
180
  result.reason = "Side-effect tool requires explicit approval";
@@ -238,7 +239,7 @@ export class PermissionChecker {
238
239
  result.decision === "prompt" &&
239
240
  context.isPlatformHosted &&
240
241
  name === "bash" &&
241
- context.trustClass === "guardian" &&
242
+ resolveCapabilities(context.trustClass).canSelfApproveTools &&
242
243
  !context.requireFreshApproval
243
244
  ) {
244
245
  log.info(
@@ -274,7 +275,7 @@ export class PermissionChecker {
274
275
  true;
275
276
  if (
276
277
  context.isInteractive === false &&
277
- context.trustClass === "guardian" &&
278
+ resolveCapabilities(context.trustClass).canSelfApproveTools &&
278
279
  !context.requireFreshApproval &&
279
280
  !isDynamicSkillLoad
280
281
  ) {
@@ -548,6 +548,26 @@ export function getMcpToolDefinitions(): Tool[] {
548
548
  );
549
549
  }
550
550
 
551
+ /**
552
+ * Return MCP tools grouped by their owning server ID. Each entry contains
553
+ * the server ID and the tool definitions registered by that server.
554
+ */
555
+ export function getMcpToolsByServer(): Map<string, Tool[]> {
556
+ const byServer = new Map<string, Tool[]>();
557
+ for (const [name, owner] of ownersByName) {
558
+ if (owner.kind !== "mcp") continue;
559
+ const tool = tools.get(name);
560
+ if (!tool) continue;
561
+ let list = byServer.get(owner.id);
562
+ if (!list) {
563
+ list = [];
564
+ byServer.set(owner.id, list);
565
+ }
566
+ list.push(tool);
567
+ }
568
+ return byServer;
569
+ }
570
+
551
571
  /**
552
572
  * Return the names of all currently registered skill-origin tools.
553
573
  */
@@ -1,5 +1,4 @@
1
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
2
- import { getConfig } from "../../config/loader.js";
1
+ import { resolveCapabilities } from "../../runtime/capabilities.js";
3
2
  import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
4
3
  import { formatIntegrationSummary } from "../../schedule/integration-status.js";
5
4
  import { validateRruleSetLines } from "../../schedule/recurrence-engine.js";
@@ -17,7 +16,7 @@ import {
17
16
  } from "../../schedule/schedule-store.js";
18
17
  import {
19
18
  CapabilityManifestSchema,
20
- resolveCapabilities,
19
+ resolveCapabilities as resolveWorkflowCapabilities,
21
20
  } from "../../workflows/capabilities.js";
22
21
  import type { ToolContext, ToolExecutionResult } from "../types.js";
23
22
 
@@ -32,7 +31,7 @@ export async function executeScheduleCreate(
32
31
  input: Record<string, unknown>,
33
32
  context: ToolContext,
34
33
  ): Promise<ToolExecutionResult> {
35
- if (context.trustClass !== "guardian") {
34
+ if (!resolveCapabilities(context.trustClass).canManageSchedules) {
36
35
  return {
37
36
  content:
38
37
  "Error: schedule_create is restricted to guardian actors because schedules execute with elevated privileges.",
@@ -121,13 +120,9 @@ export async function executeScheduleCreate(
121
120
  };
122
121
  }
123
122
  } else if (mode === "workflow") {
124
- // Workflow mode is gated by the `workflows` flag (a scheduled run would
125
- // otherwise hard-fail at trigger time) and requires a saved workflow name —
126
- // mirrors the HTTP route's create-side validation so the assistant-facing
127
- // path and the settings route enforce the same shape.
128
- if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
129
- return { content: "Error: workflows are not enabled.", isError: true };
130
- }
123
+ // Workflow mode requires a saved workflow name mirrors the HTTP route's
124
+ // create-side validation so the assistant-facing path and the settings route
125
+ // enforce the same shape.
131
126
  if (!workflowName) {
132
127
  return {
133
128
  content:
@@ -144,7 +139,7 @@ export async function executeScheduleCreate(
144
139
  if (input.capabilities !== undefined) {
145
140
  try {
146
141
  const manifest = CapabilityManifestSchema.parse(input.capabilities);
147
- resolveCapabilities(manifest);
142
+ resolveWorkflowCapabilities(manifest);
148
143
  capabilities = manifest;
149
144
  } catch (err) {
150
145
  const msg = err instanceof Error ? err.message : String(err);
@@ -1,5 +1,4 @@
1
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
2
- import { getConfig } from "../../config/loader.js";
1
+ import { resolveCapabilities } from "../../runtime/capabilities.js";
3
2
  import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
4
3
  import { validateRruleSetLines } from "../../schedule/recurrence-engine.js";
5
4
  import {
@@ -31,7 +30,7 @@ export async function executeScheduleUpdate(
31
30
  input: Record<string, unknown>,
32
31
  context: ToolContext,
33
32
  ): Promise<ToolExecutionResult> {
34
- if (context.trustClass !== "guardian") {
33
+ if (!resolveCapabilities(context.trustClass).canManageSchedules) {
35
34
  return {
36
35
  content:
37
36
  "Error: schedule_update is restricted to guardian actors because schedules execute with elevated privileges.",
@@ -205,8 +204,8 @@ export async function executeScheduleUpdate(
205
204
  };
206
205
  }
207
206
 
208
- // Mirror the HTTP route: a schedule whose RESULTING mode is `workflow` must be
209
- // flag-enabled and carry a non-empty workflowName. Compute the post-update
207
+ // Mirror the HTTP route: a schedule whose RESULTING mode is `workflow` must
208
+ // carry a non-empty workflowName. Compute the post-update
210
209
  // state (the update's value if present, else the persisted one) so both
211
210
  // "switch to workflow without a name" and "clear the name on a workflow
212
211
  // schedule" are rejected — otherwise the scheduler hits the `!job.workflowName`
@@ -217,12 +216,6 @@ export async function executeScheduleUpdate(
217
216
  const resultingMode =
218
217
  updates.mode !== undefined ? (updates.mode as string) : existing.mode;
219
218
  if (resultingMode === "workflow") {
220
- if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
221
- return {
222
- content: "Error: workflows are not enabled.",
223
- isError: true,
224
- };
225
- }
226
219
  const resultingWorkflowName =
227
220
  updates.workflowName !== undefined
228
221
  ? ((updates.workflowName as string | null) ?? "")
@@ -31,6 +31,40 @@ export type PathResult =
31
31
  const CONTAINER_WORKSPACE_PREFIX = "/workspace/";
32
32
  const CONTAINER_WORKSPACE_EXACT = "/workspace";
33
33
 
34
+ // ---------------------------------------------------------------------------
35
+ // Symlink resolution
36
+ // ---------------------------------------------------------------------------
37
+
38
+ /**
39
+ * Resolve symlinks in an absolute path.
40
+ *
41
+ * For an existing path, returns its `realpathSync`. For a path that does not
42
+ * exist yet (e.g. a `file_write` target), walks up to the nearest existing
43
+ * ancestor, resolves that ancestor via `realpathSync`, then re-appends the
44
+ * trailing (non-existent) components — so a symlink anywhere in the existing
45
+ * prefix is still followed. Falls back to the lexical input when nothing on
46
+ * the path resolves (e.g. the path lives on a filesystem this process cannot
47
+ * see, as with host_file paths proxied to a remote client).
48
+ *
49
+ * Used both for sandbox-boundary enforcement and to canonicalize paths before
50
+ * security risk classification, so a symlink cannot mask the true target of a
51
+ * file operation.
52
+ */
53
+ export function resolveRealPath(absolutePath: string): string {
54
+ let current = absolutePath;
55
+ const trailing: string[] = [];
56
+ while (current !== dirname(current)) {
57
+ try {
58
+ const real = realpathSync(current);
59
+ return trailing.length > 0 ? join(real, ...trailing) : real;
60
+ } catch {
61
+ trailing.unshift(basename(current));
62
+ current = dirname(current);
63
+ }
64
+ }
65
+ return absolutePath;
66
+ }
67
+
34
68
  // ---------------------------------------------------------------------------
35
69
  // Sandbox policy
36
70
  // ---------------------------------------------------------------------------
@@ -82,18 +116,7 @@ export function sandboxPolicy(
82
116
  realResolved = resolved;
83
117
  }
84
118
  } else {
85
- let current = resolved;
86
- const trailing: string[] = [];
87
- while (current !== dirname(current)) {
88
- try {
89
- const real = realpathSync(current);
90
- realResolved = trailing.length > 0 ? join(real, ...trailing) : real;
91
- break;
92
- } catch {
93
- trailing.unshift(basename(current));
94
- current = dirname(current);
95
- }
96
- }
119
+ realResolved = resolveRealPath(resolved);
97
120
  }
98
121
 
99
122
  // Resolve the boundary directory's real path too (in case it's a symlink)
@@ -115,7 +138,10 @@ export function sandboxPolicy(
115
138
 
116
139
  // Check both the logical path and the symlink-resolved path so a symlink
117
140
  // with a non-denied name pointing at a denied file is still caught.
118
- if (DENIED_BASENAMES.has(basename(resolved)) || DENIED_BASENAMES.has(basename(realResolved))) {
141
+ if (
142
+ DENIED_BASENAMES.has(basename(resolved)) ||
143
+ DENIED_BASENAMES.has(basename(realResolved))
144
+ ) {
119
145
  return {
120
146
  ok: false,
121
147
  reason: "denied",
@@ -25,22 +25,7 @@ const SIDE_EFFECT_TOOLS: ReadonlySet<string> = new Set([
25
25
  * Returns `true` if the given tool name is classified as having side effects
26
26
  * (i.e. it can modify the filesystem, execute arbitrary commands, or trigger
27
27
  * external actions). Read-only and informational tools return `false`.
28
- *
29
- * For mixed-action tools (e.g. credential_store), the optional
30
- * `input` parameter is inspected to distinguish mutating actions (create,
31
- * update, cancel) from read-only ones (list, get).
32
28
  */
33
- export function isSideEffectTool(
34
- toolName: string,
35
- input?: Record<string, unknown>,
36
- ): boolean {
37
- if (SIDE_EFFECT_TOOLS.has(toolName)) return true;
38
-
39
- // Action-aware checks for mixed-action tools
40
- if (toolName === "credential_store") {
41
- const action = input?.action;
42
- return action === "store" || action === "delete" || action === "prompt";
43
- }
44
-
45
- return false;
29
+ export function isSideEffectTool(toolName: string): boolean {
30
+ return SIDE_EFFECT_TOOLS.has(toolName);
46
31
  }
@@ -62,6 +62,39 @@ export function resolveSkillExecuteInput(
62
62
  return {};
63
63
  }
64
64
 
65
+ /**
66
+ * Augment an inner-tool error with `skill_execute` envelope guidance when the
67
+ * call carried no inner parameters.
68
+ *
69
+ * {@link resolveSkillExecuteInput} rescues *misplaced* parameters (siblings, a
70
+ * JSON-encoded string). It cannot rescue a genuinely empty call — there is
71
+ * nothing to relocate — so the inner tool runs with `{}` and rejects it with a
72
+ * field-level message ("<field> is required") that says nothing about the
73
+ * envelope. Weak models then retry the identical empty shape, oscillating over
74
+ * whether parameters belong under `input`, as siblings, or as a JSON string.
75
+ *
76
+ * Appending the canonical envelope shape gives the next attempt a concrete
77
+ * template. Fires only when the resolved inner input was empty AND the tool
78
+ * errored, so well-formed calls and tools that legitimately accept no
79
+ * parameters are untouched.
80
+ */
81
+ export function augmentSkillExecuteError(
82
+ toolName: string,
83
+ resolvedInput: Record<string, unknown>,
84
+ result: ToolExecutionResult,
85
+ ): ToolExecutionResult {
86
+ if (!result.isError || Object.keys(resolvedInput).length > 0) return result;
87
+
88
+ const guidance =
89
+ `\n\nThis skill_execute call carried no parameters for "${toolName}". ` +
90
+ `Put the tool's parameters inside \`input\`. For example: ` +
91
+ `{"tool": "${toolName}", "input": { /* the tool's parameters */ }, ` +
92
+ `"activity": "..."}. The skill's instructions (from skill_load) list ` +
93
+ `"${toolName}"'s required fields.`;
94
+
95
+ return { ...result, content: result.content + guidance };
96
+ }
97
+
65
98
  export const skillExecuteTool = {
66
99
  name: "skill_execute",
67
100
  description: