@vellumai/assistant 0.9.1-staging.1 → 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 (433) hide show
  1. package/docs/activation-funnel-telemetry.md +24 -18
  2. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  3. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  4. package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
  5. package/openapi.yaml +852 -15
  6. package/package.json +1 -1
  7. package/src/__tests__/access-request-card-view.test.ts +98 -0
  8. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  9. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
  10. package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  12. package/src/__tests__/app-compiler.test.ts +15 -1
  13. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  14. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  15. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  16. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  18. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  19. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  20. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +174 -30
  22. package/src/__tests__/config-schema.test.ts +35 -0
  23. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  24. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  25. package/src/__tests__/contacts-tools.test.ts +29 -0
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  27. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  28. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  29. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  30. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  31. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  32. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  33. package/src/__tests__/conversation-title-service.test.ts +62 -0
  34. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  35. package/src/__tests__/credential-prompt-route.test.ts +1 -0
  36. package/src/__tests__/credential-security-invariants.test.ts +2 -0
  37. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  38. package/src/__tests__/disk-usage.test.ts +65 -0
  39. package/src/__tests__/dynamic-page-surface.test.ts +51 -0
  40. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  41. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  42. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  43. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  44. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  45. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  46. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  47. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  48. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  49. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  50. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  51. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  52. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  53. package/src/__tests__/invite-redemption-service.test.ts +0 -3
  54. package/src/__tests__/llm-catalog-parity.test.ts +30 -1
  55. package/src/__tests__/llm-resolver.test.ts +21 -0
  56. package/src/__tests__/llm-schema.test.ts +1 -0
  57. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  58. package/src/__tests__/mcp-health-check.test.ts +6 -7
  59. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  60. package/src/__tests__/path-policy.test.ts +34 -0
  61. package/src/__tests__/persona-resolver.test.ts +38 -0
  62. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  63. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  64. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  65. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  66. package/src/__tests__/reaction-persistence.test.ts +150 -29
  67. package/src/__tests__/relay-server.test.ts +285 -0
  68. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  69. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  70. package/src/__tests__/skill-execute-input.test.ts +5 -0
  71. package/src/__tests__/skills.test.ts +51 -0
  72. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  73. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  74. package/src/__tests__/subagent-tools.test.ts +150 -0
  75. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  76. package/src/__tests__/title-generate-hook.test.ts +100 -3
  77. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  78. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  79. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  80. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  81. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  82. package/src/__tests__/trusted-contact-verification.test.ts +2 -4
  83. package/src/__tests__/twilio-routes.test.ts +81 -1
  84. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  85. package/src/__tests__/weak-open-model.test.ts +30 -0
  86. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  87. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  88. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  89. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  90. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  91. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  92. package/src/agent/loop.ts +33 -33
  93. package/src/api/events/tool-result.ts +6 -0
  94. package/src/api/events/workflow-completed.ts +53 -0
  95. package/src/api/events/workflow-leaf-finished.ts +38 -0
  96. package/src/api/events/workflow-leaf-started.ts +35 -0
  97. package/src/api/events/workflow-progress.ts +32 -0
  98. package/src/api/events/workflow-started.ts +31 -0
  99. package/src/api/index.ts +40 -0
  100. package/src/api/responses/conversation-message.ts +26 -0
  101. package/src/api/responses/home.ts +26 -0
  102. package/src/api/responses/workflow-journal.ts +53 -0
  103. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  104. package/src/approvals/guardian-decision-primitive.ts +26 -3
  105. package/src/approvals/guardian-request-resolvers.ts +181 -78
  106. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  107. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  108. package/src/calls/call-pointer-messages.ts +10 -4
  109. package/src/calls/channel-admission-reader.ts +104 -0
  110. package/src/calls/guardian-dispatch.ts +17 -45
  111. package/src/calls/media-stream-server.ts +84 -2
  112. package/src/calls/relay-server.ts +66 -0
  113. package/src/calls/relay-setup-router.ts +82 -1
  114. package/src/calls/twilio-routes.ts +17 -8
  115. package/src/cli/commands/clients.ts +3 -0
  116. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  117. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  118. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  119. package/src/cli/commands/memory/index.ts +30 -0
  120. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  121. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  122. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  123. package/src/cli/commands/oauth/status.test.ts +36 -0
  124. package/src/cli/commands/oauth/status.ts +23 -3
  125. package/src/cli/commands/plugins.ts +57 -5
  126. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  127. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
  128. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  129. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
  130. package/src/cli/lib/inspect-plugin.ts +12 -1
  131. package/src/cli/lib/merge-plugin-tree.ts +149 -49
  132. package/src/cli/lib/plugin-surfaces.ts +104 -0
  133. package/src/cli/lib/upgrade-plugin.ts +64 -36
  134. package/src/cli/program.ts +2 -4
  135. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  136. package/src/config/assistant-feature-flags.ts +22 -7
  137. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  138. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  139. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  140. package/src/config/bundled-skills/workflows/SKILL.md +14 -7
  141. package/src/config/call-site-defaults.ts +3 -0
  142. package/src/config/feature-flag-registry.json +49 -18
  143. package/src/config/llm-resolver.ts +3 -0
  144. package/src/config/memory-v3-gate.ts +11 -0
  145. package/src/config/schema.ts +8 -6
  146. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  147. package/src/config/schemas/call-site-catalog.ts +7 -0
  148. package/src/config/schemas/channels.ts +11 -0
  149. package/src/config/schemas/llm.ts +31 -0
  150. package/src/config/schemas/memory-lifecycle.ts +3 -7
  151. package/src/config/schemas/memory-v3.ts +6 -0
  152. package/src/config/schemas/services.ts +18 -0
  153. package/src/config/seed-inference-profiles.ts +94 -34
  154. package/src/config/skills.ts +21 -0
  155. package/src/config/sync-gated-profiles.ts +220 -0
  156. package/src/contacts/contact-store.ts +2 -10
  157. package/src/contacts/contacts-write.ts +1 -2
  158. package/src/contacts/types.ts +0 -1
  159. package/src/context/compactor.ts +86 -52
  160. package/src/context/strip-injections.ts +58 -10
  161. package/src/context/token-estimator.ts +1 -1
  162. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  163. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  164. package/src/daemon/conversation-agent-loop.ts +100 -19
  165. package/src/daemon/conversation-history.ts +1 -1
  166. package/src/daemon/conversation-lifecycle.ts +3 -5
  167. package/src/daemon/conversation-process.ts +13 -5
  168. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  169. package/src/daemon/conversation-surfaces.ts +26 -0
  170. package/src/daemon/conversation-tool-setup.ts +16 -11
  171. package/src/daemon/conversation.ts +64 -14
  172. package/src/daemon/disk-pressure-policy.ts +5 -3
  173. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  174. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  175. package/src/daemon/handlers/config-a2a.ts +0 -2
  176. package/src/daemon/handlers/config-channels.ts +5 -10
  177. package/src/daemon/handlers/config-slack-channel.ts +20 -0
  178. package/src/daemon/handlers/conversations.ts +107 -0
  179. package/src/daemon/host-browser-proxy.ts +41 -0
  180. package/src/daemon/lifecycle.ts +55 -20
  181. package/src/daemon/message-provenance.ts +2 -0
  182. package/src/daemon/message-types/contacts.ts +0 -1
  183. package/src/daemon/message-types/web-activity.ts +7 -1
  184. package/src/daemon/message-types/workflows.ts +83 -1
  185. package/src/daemon/tool-setup-types.ts +4 -0
  186. package/src/daemon/trust-context.ts +1 -1
  187. package/src/events/tool-audit-listener.ts +2 -2
  188. package/src/home/feed-source-enrichment.test.ts +151 -0
  189. package/src/home/feed-source-enrichment.ts +176 -0
  190. package/src/instrument.ts +18 -6
  191. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  192. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  193. package/src/ipc/assistant-server.ts +37 -4
  194. package/src/ipc/gateway-flag-listener.ts +18 -2
  195. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  196. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  197. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  198. package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
  199. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  200. package/src/memory/auth-fallback-events-store.ts +2 -2
  201. package/src/memory/auto-analysis-enqueue.ts +3 -5
  202. package/src/memory/canonical-guardian-store.ts +39 -1
  203. package/src/memory/conversation-crud.ts +9 -4
  204. package/src/memory/conversation-key-store.ts +17 -2
  205. package/src/memory/conversation-title-service.ts +64 -7
  206. package/src/memory/db-init.ts +10 -0
  207. package/src/memory/embedding-backend.ts +15 -1
  208. package/src/memory/jobs-worker.ts +2 -1
  209. package/src/memory/lifecycle-events-store.ts +2 -2
  210. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  211. package/src/memory/memory-retrospective-job.ts +9 -0
  212. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  213. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  214. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  215. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
  216. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  217. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  218. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  219. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  220. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  221. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  222. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  223. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  224. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  225. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
  226. package/src/memory/migrations/index.ts +5 -0
  227. package/src/memory/onboarding-events-store.ts +3 -3
  228. package/src/memory/schema/contacts.ts +0 -1
  229. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  230. package/src/memory/skill-loaded-events-store.ts +2 -2
  231. package/src/memory/tool-executed-events-store.test.ts +7 -7
  232. package/src/memory/turn-trace-store.test.ts +736 -0
  233. package/src/memory/turn-trace-store.ts +364 -0
  234. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  235. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  236. package/src/memory/v2/consolidation-job.ts +2 -2
  237. package/src/memory/v2/skill-content.ts +25 -7
  238. package/src/memory/v2/skill-store.ts +7 -1
  239. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  240. package/src/memory/v3-eval/eval-packets.ts +546 -0
  241. package/src/messaging/providers/slack/api.ts +31 -0
  242. package/src/messaging/providers/slack/send.test.ts +114 -2
  243. package/src/messaging/providers/slack/send.ts +30 -7
  244. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  245. package/src/messaging/providers/slack/withdraw.ts +161 -0
  246. package/src/notifications/AGENTS.md +2 -0
  247. package/src/notifications/access-request-copy.ts +72 -59
  248. package/src/notifications/adapters/slack.ts +55 -73
  249. package/src/notifications/approval-card-data.ts +333 -0
  250. package/src/notifications/broadcaster.ts +6 -2
  251. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  252. package/src/notifications/copy-composer.ts +3 -3
  253. package/src/notifications/decision-engine.ts +4 -2
  254. package/src/notifications/destination-resolver.ts +4 -6
  255. package/src/notifications/guardian-question-mode.ts +10 -0
  256. package/src/notifications/home-feed-side-effect.ts +3 -13
  257. package/src/notifications/notification-utils.ts +2 -1
  258. package/src/notifications/signal.ts +79 -43
  259. package/src/notifications/types.ts +98 -128
  260. package/src/permissions/checker.test.ts +51 -0
  261. package/src/permissions/checker.ts +185 -26
  262. package/src/permissions/ipc-risk-types.ts +24 -0
  263. package/src/permissions/question-prompter.test.ts +27 -0
  264. package/src/permissions/question-prompter.ts +4 -0
  265. package/src/platform/client.test.ts +119 -0
  266. package/src/platform/client.ts +66 -0
  267. package/src/platform/consent-cache.test.ts +267 -0
  268. package/src/platform/consent-cache.ts +174 -0
  269. package/src/plugin-api/index.ts +27 -0
  270. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  271. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  272. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  273. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  274. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  275. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  276. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  277. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  278. package/src/plugins/defaults/advisor/config.ts +21 -0
  279. package/src/plugins/defaults/advisor/consult.ts +93 -0
  280. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  281. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  282. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  283. package/src/plugins/defaults/advisor/package.json +14 -0
  284. package/src/plugins/defaults/advisor/steering.ts +67 -0
  285. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  286. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  287. package/src/plugins/defaults/index.ts +35 -0
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  290. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  291. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  298. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  299. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
  300. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  301. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  302. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  303. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
  304. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  305. package/src/prompts/persona-resolver.ts +12 -2
  306. package/src/prompts/templates/system-sections.ts +7 -2
  307. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  308. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  309. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  310. package/src/providers/atlascloud/client.ts +85 -0
  311. package/src/providers/fetch-provider-catalog.ts +85 -0
  312. package/src/providers/inference/adapter-factory.ts +3 -0
  313. package/src/providers/model-catalog.ts +58 -0
  314. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  315. package/src/providers/openai/chat-completions-provider.ts +7 -0
  316. package/src/providers/openai/responses-provider.ts +10 -0
  317. package/src/providers/provider-send-message.ts +11 -3
  318. package/src/providers/retry.ts +53 -12
  319. package/src/providers/search-provider-catalog.ts +10 -0
  320. package/src/providers/weak-open-model.ts +22 -0
  321. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  322. package/src/runtime/__tests__/client-health.test.ts +44 -0
  323. package/src/runtime/access-request-helper.ts +21 -53
  324. package/src/runtime/actor-trust-resolver.ts +49 -21
  325. package/src/runtime/agent-wake.ts +52 -0
  326. package/src/runtime/assistant-event-hub.ts +18 -4
  327. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  328. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  329. package/src/runtime/capabilities.test.ts +120 -0
  330. package/src/runtime/capabilities.ts +197 -0
  331. package/src/runtime/channel-approval-types.ts +5 -1
  332. package/src/runtime/channel-retry-sweep.ts +1 -0
  333. package/src/runtime/channel-verification-service.ts +1 -2
  334. package/src/runtime/client-health.ts +26 -0
  335. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  336. package/src/runtime/effective-capabilities.test.ts +128 -0
  337. package/src/runtime/effective-capabilities.ts +84 -0
  338. package/src/runtime/guardian-reply-router.ts +106 -21
  339. package/src/runtime/invite-redemption-service.ts +6 -22
  340. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  341. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  342. package/src/runtime/pending-interactions.ts +15 -0
  343. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  344. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  345. package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
  346. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  347. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  348. package/src/runtime/routes/client-routes.ts +10 -0
  349. package/src/runtime/routes/contact-routes.ts +31 -8
  350. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  351. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  352. package/src/runtime/routes/conversation-routes.ts +37 -12
  353. package/src/runtime/routes/events-routes.ts +1 -3
  354. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  355. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  356. package/src/runtime/routes/home-feed-routes.ts +8 -3
  357. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  358. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
  359. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  360. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  361. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  362. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  363. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  364. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  365. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  366. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  367. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  368. package/src/runtime/routes/index.ts +2 -0
  369. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  370. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  371. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  372. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  373. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  374. package/src/runtime/routes/notification-routes.ts +122 -133
  375. package/src/runtime/routes/platform-routes.ts +2 -2
  376. package/src/runtime/routes/plugins-routes.ts +40 -7
  377. package/src/runtime/routes/secret-routes.ts +10 -0
  378. package/src/runtime/routes/surface-action-routes.ts +2 -1
  379. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  380. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  381. package/src/runtime/routes/workflow-routes.test.ts +225 -1
  382. package/src/runtime/routes/workflow-routes.ts +131 -1
  383. package/src/runtime/tool-grant-request-helper.ts +18 -16
  384. package/src/runtime/trust-context-resolver.ts +8 -5
  385. package/src/schedule/schedule-store.ts +1 -1
  386. package/src/schedule/scheduler-types.ts +5 -1
  387. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  388. package/src/security/secret-patterns.ts +3 -0
  389. package/src/subagent/manager.ts +11 -4
  390. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  391. package/src/telemetry/trace-collection-policy.ts +30 -0
  392. package/src/telemetry/types.ts +89 -0
  393. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  394. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  395. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  396. package/src/tools/browser/browser-execution.ts +29 -18
  397. package/src/tools/document/document-tool.ts +2 -3
  398. package/src/tools/executor.ts +5 -3
  399. package/src/tools/host-terminal/host-shell.ts +5 -4
  400. package/src/tools/memory/register.ts +2 -2
  401. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  402. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  403. package/src/tools/network/web-fetch.ts +372 -1
  404. package/src/tools/network/web-search.ts +213 -10
  405. package/src/tools/permission-checker.ts +3 -2
  406. package/src/tools/registry.ts +20 -0
  407. package/src/tools/schedule/create.ts +4 -3
  408. package/src/tools/schedule/update.ts +2 -1
  409. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  410. package/src/tools/skills/execute.ts +1 -2
  411. package/src/tools/subagent/spawn.ts +37 -13
  412. package/src/tools/terminal/shell.ts +10 -4
  413. package/src/tools/tool-approval-handler.ts +17 -10
  414. package/src/tools/types.ts +9 -0
  415. package/src/tools/ui-surface/definitions.ts +25 -2
  416. package/src/tools/verification-control-plane-policy.ts +3 -1
  417. package/src/tools/workflows/run-workflow.ts +1 -0
  418. package/src/util/disk-usage.ts +78 -23
  419. package/src/util/platform.ts +8 -1
  420. package/src/watcher/telemetry.ts +2 -2
  421. package/src/workflows/engine.test.ts +175 -1
  422. package/src/workflows/engine.ts +82 -0
  423. package/src/workflows/journal-store.test.ts +70 -0
  424. package/src/workflows/journal-store.ts +18 -3
  425. package/src/workflows/run-manager.test.ts +171 -3
  426. package/src/workflows/run-manager.ts +64 -0
  427. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  428. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  429. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  430. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  431. package/src/workspace/migrations/registry.ts +8 -0
  432. package/src/notifications/tool-approval-copy.ts +0 -142
  433. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
@@ -0,0 +1,267 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { OwnerConsent } from "./client.js";
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Mutable mock state
7
+ // ---------------------------------------------------------------------------
8
+
9
+ let mockClient: {
10
+ platformAssistantId: string;
11
+ getOwnerConsent: () => Promise<OwnerConsent | null>;
12
+ } | null = null;
13
+ let createCallCount = 0;
14
+ // Legacy fail-closed opt-out marker surfaced via getConfigReadOnly(). Default
15
+ // off/absent so existing behavior is unchanged.
16
+ let mockLegacyTelemetryOptOut: boolean | undefined = false;
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Module mocks (must precede the import under test)
20
+ // ---------------------------------------------------------------------------
21
+
22
+ mock.module("./client.js", () => ({
23
+ VellumPlatformClient: {
24
+ create: async () => {
25
+ createCallCount += 1;
26
+ return mockClient;
27
+ },
28
+ },
29
+ }));
30
+
31
+ mock.module("../config/loader.js", () => ({
32
+ getConfigReadOnly: () => ({
33
+ legacyTelemetryOptOut: mockLegacyTelemetryOptOut,
34
+ }),
35
+ }));
36
+
37
+ mock.module("../util/logger.js", () => ({
38
+ getLogger: () =>
39
+ new Proxy({} as Record<string, unknown>, {
40
+ get: () => () => {},
41
+ }),
42
+ }));
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Import under test (after mocks)
46
+ // ---------------------------------------------------------------------------
47
+
48
+ import {
49
+ __setCachedShareAnalyticsForTest,
50
+ __setCachedShareDiagnosticsForTest,
51
+ __setCachedShareDiagnosticsVersionForTest,
52
+ getCachedShareAnalytics,
53
+ getCachedShareDiagnostics,
54
+ getCachedShareDiagnosticsVersion,
55
+ refreshConsentCache,
56
+ startConsentRefresh,
57
+ stopConsentRefresh,
58
+ } from "./consent-cache.js";
59
+
60
+ /**
61
+ * Build a mock owner consent. Fields default to `false` so existing
62
+ * share_analytics-focused cases don't have to spell them out.
63
+ */
64
+ function makeConsent(overrides: Partial<OwnerConsent> = {}): OwnerConsent {
65
+ return {
66
+ shareAnalytics: false,
67
+ shareDiagnostics: false,
68
+ shareDiagnosticsAcceptedVersion: "",
69
+ ...overrides,
70
+ };
71
+ }
72
+
73
+ function makeClient(consent: OwnerConsent | null, assistantId = "asst_1") {
74
+ return {
75
+ platformAssistantId: assistantId,
76
+ getOwnerConsent: async () => consent,
77
+ };
78
+ }
79
+
80
+ describe("consent-cache", () => {
81
+ beforeEach(() => {
82
+ mockClient = null;
83
+ createCallCount = 0;
84
+ mockLegacyTelemetryOptOut = false;
85
+ __setCachedShareAnalyticsForTest(false);
86
+ __setCachedShareDiagnosticsForTest(false);
87
+ __setCachedShareDiagnosticsVersionForTest("");
88
+ });
89
+
90
+ afterEach(async () => {
91
+ await stopConsentRefresh();
92
+ });
93
+
94
+ test("defaults to false before any refresh", () => {
95
+ expect(getCachedShareAnalytics()).toBe(false);
96
+ });
97
+
98
+ test("stays false when no platform client is available", async () => {
99
+ mockClient = null;
100
+ await refreshConsentCache();
101
+ expect(getCachedShareAnalytics()).toBe(false);
102
+ });
103
+
104
+ test("becomes true after a successful fetch reporting shareAnalytics: true", async () => {
105
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }));
106
+ await refreshConsentCache();
107
+ expect(getCachedShareAnalytics()).toBe(true);
108
+ });
109
+
110
+ test("a null fetch keeps the last known value", async () => {
111
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }));
112
+ await refreshConsentCache();
113
+ expect(getCachedShareAnalytics()).toBe(true);
114
+
115
+ // Transient failure: consent endpoint returns null.
116
+ mockClient = makeClient(null);
117
+ await refreshConsentCache();
118
+ expect(getCachedShareAnalytics()).toBe(true);
119
+ });
120
+
121
+ test("a missing client flips a prior opt-in back to false", async () => {
122
+ __setCachedShareAnalyticsForTest(true);
123
+ mockClient = null;
124
+ await refreshConsentCache();
125
+ expect(getCachedShareAnalytics()).toBe(false);
126
+ });
127
+
128
+ test("a client without a resolvable assistant identity fails closed", async () => {
129
+ __setCachedShareAnalyticsForTest(true);
130
+ // Identity cleared mid-session (e.g. assistantId emptied while base URL +
131
+ // API key persist). getOwnerConsent must not be relied upon to preserve the
132
+ // prior opt-in here — fail closed instead.
133
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }), "");
134
+ await refreshConsentCache();
135
+ expect(getCachedShareAnalytics()).toBe(false);
136
+ });
137
+
138
+ test("legacy opt-out marker keeps analytics off despite platform opt-in", async () => {
139
+ mockLegacyTelemetryOptOut = true;
140
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }));
141
+ await refreshConsentCache();
142
+ // Platform reports opt-in, but the fail-closed marker forces off.
143
+ expect(getCachedShareAnalytics()).toBe(false);
144
+ });
145
+
146
+ test("a fetch reporting shareAnalytics: false turns the cache off", async () => {
147
+ __setCachedShareAnalyticsForTest(true);
148
+ mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
149
+ await refreshConsentCache();
150
+ expect(getCachedShareAnalytics()).toBe(false);
151
+ });
152
+
153
+ test("startConsentRefresh is idempotent and runs an immediate refresh", async () => {
154
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }));
155
+
156
+ startConsentRefresh();
157
+ startConsentRefresh(); // no-op: timer already running
158
+ // Let the fire-and-forget immediate refresh settle.
159
+ await new Promise((resolve) => setTimeout(resolve, 0));
160
+
161
+ expect(getCachedShareAnalytics()).toBe(true);
162
+ // One immediate refresh from the first call; the second is a no-op.
163
+ expect(createCallCount).toBe(1);
164
+ });
165
+
166
+ test("stopConsentRefresh clears the timer (start can run again)", async () => {
167
+ startConsentRefresh();
168
+ await stopConsentRefresh();
169
+ // A fresh start after stop performs another immediate refresh.
170
+ mockClient = makeClient(makeConsent({ shareAnalytics: true }));
171
+ startConsentRefresh();
172
+ await new Promise((resolve) => setTimeout(resolve, 0));
173
+ expect(getCachedShareAnalytics()).toBe(true);
174
+ });
175
+
176
+ // share_diagnostics tracks the same refresh as share_analytics; Sentry's
177
+ // beforeSend re-reads it per event so a revocation is honored within a cycle.
178
+ test("diagnostics defaults to false before any refresh", () => {
179
+ expect(getCachedShareDiagnostics()).toBe(false);
180
+ });
181
+
182
+ test("diagnostics becomes true after a fetch reporting shareDiagnostics: true", async () => {
183
+ mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
184
+ await refreshConsentCache();
185
+ expect(getCachedShareDiagnostics()).toBe(true);
186
+ });
187
+
188
+ test("a later fetch reporting shareDiagnostics: false honors the revocation", async () => {
189
+ mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
190
+ await refreshConsentCache();
191
+ expect(getCachedShareDiagnostics()).toBe(true);
192
+
193
+ mockClient = makeClient(makeConsent({ shareDiagnostics: false }));
194
+ await refreshConsentCache();
195
+ expect(getCachedShareDiagnostics()).toBe(false);
196
+ });
197
+
198
+ test("a null diagnostics fetch keeps the last known value", async () => {
199
+ mockClient = makeClient(makeConsent({ shareDiagnostics: true }));
200
+ await refreshConsentCache();
201
+ expect(getCachedShareDiagnostics()).toBe(true);
202
+
203
+ mockClient = makeClient(null);
204
+ await refreshConsentCache();
205
+ expect(getCachedShareDiagnostics()).toBe(true);
206
+ });
207
+
208
+ test("a missing client flips a prior diagnostics opt-in back to false", async () => {
209
+ __setCachedShareDiagnosticsForTest(true);
210
+ mockClient = null;
211
+ await refreshConsentCache();
212
+ expect(getCachedShareDiagnostics()).toBe(false);
213
+ });
214
+
215
+ test("a client without a resolvable assistant identity fails diagnostics closed", async () => {
216
+ __setCachedShareDiagnosticsForTest(true);
217
+ mockClient = makeClient(makeConsent({ shareDiagnostics: true }), "");
218
+ await refreshConsentCache();
219
+ expect(getCachedShareDiagnostics()).toBe(false);
220
+ });
221
+
222
+ test("caches the accepted diagnostics-consent version from a successful fetch", async () => {
223
+ mockClient = makeClient(
224
+ makeConsent({
225
+ shareDiagnostics: true,
226
+ shareDiagnosticsAcceptedVersion: "2026-06-18",
227
+ }),
228
+ );
229
+ await refreshConsentCache();
230
+ expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
231
+ });
232
+
233
+ test("a missing client clears a prior cached diagnostics version", async () => {
234
+ __setCachedShareDiagnosticsVersionForTest("2026-06-18");
235
+ mockClient = null;
236
+ await refreshConsentCache();
237
+ expect(getCachedShareDiagnosticsVersion()).toBe("");
238
+ });
239
+
240
+ test("a client without a resolvable assistant identity clears the diagnostics version", async () => {
241
+ __setCachedShareDiagnosticsVersionForTest("2026-06-18");
242
+ mockClient = makeClient(
243
+ makeConsent({
244
+ shareDiagnostics: true,
245
+ shareDiagnosticsAcceptedVersion: "2026-06-18",
246
+ }),
247
+ "",
248
+ );
249
+ await refreshConsentCache();
250
+ expect(getCachedShareDiagnosticsVersion()).toBe("");
251
+ });
252
+
253
+ test("a null fetch keeps the last known diagnostics version", async () => {
254
+ mockClient = makeClient(
255
+ makeConsent({
256
+ shareDiagnostics: true,
257
+ shareDiagnosticsAcceptedVersion: "2026-06-18",
258
+ }),
259
+ );
260
+ await refreshConsentCache();
261
+ expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
262
+
263
+ mockClient = makeClient(null);
264
+ await refreshConsentCache();
265
+ expect(getCachedShareDiagnosticsVersion()).toBe("2026-06-18");
266
+ });
267
+ });
@@ -0,0 +1,174 @@
1
+ /**
2
+ * In-memory cache of the platform owner's telemetry consent.
3
+ *
4
+ * Three values are cached, all refreshed from the same owner-consent fetch:
5
+ * - `share_analytics`: gates usage telemetry collection.
6
+ * - `share_diagnostics`: gates crash diagnostics (read by Sentry `beforeSend`),
7
+ * and composes the per-turn trace-collection gate alongside the
8
+ * `trace-collection` feature flag.
9
+ * - `share_diagnostics_accepted_version`: the consent version the owner
10
+ * accepted; the disclosing-version half of the trace-collection gate (see
11
+ * telemetry/trace-collection-policy.ts).
12
+ *
13
+ * Hot-path gates (record-time telemetry writes, Sentry `beforeSend`) need a
14
+ * synchronous, I/O-free read, so this module owns the values and refreshes them
15
+ * periodically in the background. Default-off until the first successful fetch:
16
+ * an absent session, a disabled platform, or a transient fetch failure all leave
17
+ * the values untouched (initial `false`), so we never report analytics or send
18
+ * crash diagnostics without a confirmed opt-in.
19
+ */
20
+
21
+ import { getConfigReadOnly } from "../config/loader.js";
22
+ import { getLogger } from "../util/logger.js";
23
+ import { VellumPlatformClient } from "./client.js";
24
+
25
+ const log = getLogger("consent-cache");
26
+
27
+ const REFRESH_INTERVAL_MS = 5 * 60_000; // refresh consent every 5 min
28
+
29
+ let cachedShareAnalytics = false; // default-off until first success
30
+ let cachedShareDiagnostics = false; // default-off until first success
31
+ let cachedShareDiagnosticsVersion = ""; // "" fails the trace disclosure gate
32
+ // Fail-closed marker for a workspace that locally opted out of usage data
33
+ // before telemetry moved to platform `share_analytics` consent (migration 106).
34
+ // While set, telemetry stays off regardless of platform consent. Cleared by a
35
+ // future cross-repo reconciliation once the platform exposes an explicit
36
+ // re-consent signal; not auto-cleared here.
37
+ let legacyOptOut = false;
38
+ let refreshTimer: ReturnType<typeof setInterval> | null = null;
39
+
40
+ /**
41
+ * Synchronous hot-path accessor for the effective `share_analytics` consent.
42
+ * Never does I/O; returns `false` until a successful refresh proves otherwise,
43
+ * and stays `false` while the legacy fail-closed opt-out marker is set.
44
+ */
45
+ export function getCachedShareAnalytics(): boolean {
46
+ return cachedShareAnalytics && !legacyOptOut;
47
+ }
48
+
49
+ /**
50
+ * Synchronous hot-path accessor for the `share_diagnostics` consent (read by
51
+ * Sentry `beforeSend`). Never does I/O; returns `false` until a successful
52
+ * refresh proves otherwise. Because every Sentry event re-reads this, a
53
+ * mid-session opt-out is honored within one refresh cycle.
54
+ */
55
+ export function getCachedShareDiagnostics(): boolean {
56
+ return cachedShareDiagnostics;
57
+ }
58
+
59
+ /**
60
+ * Synchronous hot-path accessor for the owner's accepted diagnostics-consent
61
+ * version ("YYYY-MM-DD", or "" until a successful refresh / if never accepted).
62
+ * The disclosing-version half of the per-turn trace-collection gate.
63
+ */
64
+ export function getCachedShareDiagnosticsVersion(): string {
65
+ return cachedShareDiagnosticsVersion;
66
+ }
67
+
68
+ /**
69
+ * Refresh the cached consent from the platform.
70
+ *
71
+ * No platform session / features disabled (`create()` is null) → default-off.
72
+ * No resolvable assistant identity (no owner whose consent we can attest to) →
73
+ * fail closed. A successful fetch adopts the reported values. A `null` fetch
74
+ * (transient failure / undeployed endpoint) leaves the previous values
75
+ * unchanged so a known opt-in is not flipped off mid-session.
76
+ */
77
+ export async function refreshConsentCache(): Promise<void> {
78
+ legacyOptOut = getConfigReadOnly().legacyTelemetryOptOut === true;
79
+
80
+ const client = await VellumPlatformClient.create();
81
+ if (!client) {
82
+ setCachedShareAnalytics(false);
83
+ setCachedShareDiagnostics(false);
84
+ setCachedShareDiagnosticsVersion("");
85
+ return;
86
+ }
87
+
88
+ // No resolvable owner identity → fail closed (don't ride a stale opt-in).
89
+ if (!client.platformAssistantId) {
90
+ setCachedShareAnalytics(false);
91
+ setCachedShareDiagnostics(false);
92
+ setCachedShareDiagnosticsVersion("");
93
+ return;
94
+ }
95
+
96
+ const consent = await client.getOwnerConsent();
97
+ if (consent) {
98
+ setCachedShareAnalytics(consent.shareAnalytics);
99
+ setCachedShareDiagnostics(consent.shareDiagnostics);
100
+ setCachedShareDiagnosticsVersion(consent.shareDiagnosticsAcceptedVersion);
101
+ }
102
+ }
103
+
104
+ function setCachedShareAnalytics(value: boolean): void {
105
+ if (value !== cachedShareAnalytics) {
106
+ log.debug(
107
+ { from: cachedShareAnalytics, to: value },
108
+ "share_analytics consent changed",
109
+ );
110
+ cachedShareAnalytics = value;
111
+ }
112
+ }
113
+
114
+ function setCachedShareDiagnostics(value: boolean): void {
115
+ if (value !== cachedShareDiagnostics) {
116
+ log.debug(
117
+ { from: cachedShareDiagnostics, to: value },
118
+ "share_diagnostics consent changed",
119
+ );
120
+ cachedShareDiagnostics = value;
121
+ }
122
+ }
123
+
124
+ function setCachedShareDiagnosticsVersion(value: string): void {
125
+ if (value !== cachedShareDiagnosticsVersion) {
126
+ log.debug(
127
+ { from: cachedShareDiagnosticsVersion, to: value },
128
+ "share_diagnostics_accepted_version changed",
129
+ );
130
+ cachedShareDiagnosticsVersion = value;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Begin periodic consent refresh. Idempotent — a second call is a no-op while a
136
+ * timer is already running. Runs one immediate refresh, then every 5 minutes.
137
+ */
138
+ export function startConsentRefresh(): void {
139
+ if (refreshTimer) {
140
+ return;
141
+ }
142
+
143
+ refreshConsentCache().catch((err) => {
144
+ log.debug({ err }, "initial consent refresh failed");
145
+ });
146
+ refreshTimer = setInterval(() => {
147
+ refreshConsentCache().catch((err) => {
148
+ log.debug({ err }, "consent refresh failed");
149
+ });
150
+ }, REFRESH_INTERVAL_MS);
151
+ }
152
+
153
+ /** Stop periodic consent refresh and clear the timer. */
154
+ export async function stopConsentRefresh(): Promise<void> {
155
+ if (refreshTimer) {
156
+ clearInterval(refreshTimer);
157
+ refreshTimer = null;
158
+ }
159
+ }
160
+
161
+ /** Test-only: override the cached analytics value without going through a refresh. */
162
+ export function __setCachedShareAnalyticsForTest(value: boolean): void {
163
+ cachedShareAnalytics = value;
164
+ }
165
+
166
+ /** Test-only: override the cached diagnostics value without going through a refresh. */
167
+ export function __setCachedShareDiagnosticsForTest(value: boolean): void {
168
+ cachedShareDiagnostics = value;
169
+ }
170
+
171
+ /** Test-only: override the cached diagnostics-consent version directly. */
172
+ export function __setCachedShareDiagnosticsVersionForTest(value: string): void {
173
+ cachedShareDiagnosticsVersion = value;
174
+ }
@@ -34,6 +34,9 @@
34
34
  * - {@link getSecureKeyAsync} — read a secret from secure storage
35
35
  * - {@link getModelProfiles} — list the workspace inference profiles a plugin
36
36
  * can route to (e.g. a model router building its category → profile map)
37
+ * - {@link getConfiguredProvider} — resolve a {@link Provider} for a call site
38
+ * (optionally overriding the profile) and run inference through the
39
+ * workspace's configured profiles and credentials — no plugin-supplied API key
37
40
  *
38
41
  * - {@link PluginInitContext} — passed to `init` hook at bootstrap
39
42
  * - {@link PluginShutdownContext} — passed to `shutdown` hook at teardown
@@ -80,6 +83,20 @@ export type {
80
83
  ToolUseContent,
81
84
  WebSearchToolResultContent,
82
85
  } from "../providers/types.js";
86
+ // Provider + inference types. A plugin that runs its own inference through
87
+ // `getConfiguredProvider` names these to type the provider handle it gets back,
88
+ // the request options it passes to `sendMessage`, and the response.
89
+ export type {
90
+ Provider,
91
+ ProviderEvent,
92
+ ProviderResponse,
93
+ SendMessageConfig,
94
+ SendMessageOptions,
95
+ } from "../providers/types.js";
96
+ // Call-site identifier accepted by `getConfiguredProvider`. Plugins typically
97
+ // pass `"inference"` (the general-purpose call site) and pick the model via the
98
+ // `overrideProfile` option.
99
+ export type { LLMCallSite } from "../config/schemas/llm.js";
83
100
  export type {
84
101
  AgentLoopExitReason,
85
102
  ModelProfileInfo,
@@ -115,3 +132,13 @@ export type {
115
132
  export { assistantEventHub } from "../runtime/assistant-event-hub.js";
116
133
  export { getSecureKeyAsync } from "../security/secure-keys.js";
117
134
  export { getModelProfiles } from "./model-profiles.js";
135
+ // Resolve a provider for a call site (optionally overriding the profile) so a
136
+ // plugin can run inference through the workspace's configured profiles and
137
+ // credentials — managed-proxy or BYOK — without supplying its own API key.
138
+ // Pair with `getModelProfiles` to pick a profile. Returns `null` when no
139
+ // provider is configured. By default `overrideProfile` layers below any
140
+ // per-call-site config the workspace has pinned (e.g. a cheap `inference`
141
+ // profile), so it loses to that pin; pass `forceOverrideProfile: true` to
142
+ // float the chosen profile above the call-site layers when the plugin must
143
+ // run on a specific profile regardless of workspace tuning.
144
+ export { getConfiguredProvider } from "../providers/provider-send-message.js";
@@ -0,0 +1,56 @@
1
+ import { describe, expect, mock, test } from "bun:test";
2
+
3
+ // Drive the gate off a controllable llm config + a stubbed default-profile
4
+ // resolver, so we can assert the default-on semantics precisely.
5
+ let mockLlm: {
6
+ profiles: Record<string, { advisorEnabled?: boolean | null }>;
7
+ activeProfile?: string;
8
+ } = { profiles: {} };
9
+
10
+ mock.module("../../../../config/loader.js", () => ({
11
+ getConfig: () => ({ llm: mockLlm }),
12
+ }));
13
+ mock.module("../../../../config/llm-resolver.js", () => ({
14
+ resolveDefaultProfileKey: () => "balanced",
15
+ }));
16
+
17
+ const { advisorEnabledForProfile } = await import("../advisor-gate.js");
18
+
19
+ describe("advisorEnabledForProfile", () => {
20
+ test("default-on when the profile omits the flag", () => {
21
+ mockLlm = { profiles: { p: {} }, activeProfile: "p" };
22
+ expect(advisorEnabledForProfile("p")).toBe(true);
23
+ });
24
+
25
+ test("default-on when the flag is null", () => {
26
+ mockLlm = { profiles: { p: { advisorEnabled: null } }, activeProfile: "p" };
27
+ expect(advisorEnabledForProfile("p")).toBe(true);
28
+ });
29
+
30
+ test("disabled only on an explicit false", () => {
31
+ mockLlm = {
32
+ profiles: { p: { advisorEnabled: false } },
33
+ activeProfile: "p",
34
+ };
35
+ expect(advisorEnabledForProfile("p")).toBe(false);
36
+ });
37
+
38
+ test("enabled on an explicit true", () => {
39
+ mockLlm = { profiles: { p: { advisorEnabled: true } }, activeProfile: "p" };
40
+ expect(advisorEnabledForProfile("p")).toBe(true);
41
+ });
42
+
43
+ test("falls back to the active profile when modelProfile is null", () => {
44
+ mockLlm = {
45
+ profiles: { a: { advisorEnabled: false } },
46
+ activeProfile: "a",
47
+ };
48
+ expect(advisorEnabledForProfile(null)).toBe(false);
49
+ });
50
+
51
+ test("falls back to the call-site default profile when neither is set", () => {
52
+ // resolveDefaultProfileKey is stubbed to "balanced".
53
+ mockLlm = { profiles: { balanced: { advisorEnabled: false } } };
54
+ expect(advisorEnabledForProfile(null)).toBe(false);
55
+ });
56
+ });
@@ -0,0 +1,43 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+
3
+ import type { Message } from "../../../../providers/types.js";
4
+ import {
5
+ getCapture,
6
+ recordMessages,
7
+ recordSystemPrompt,
8
+ resetAdvisorStateForTests,
9
+ seedCapture,
10
+ } from "../advisor-state-store.js";
11
+
12
+ const userMsg = (t: string): Message => ({
13
+ role: "user",
14
+ content: [{ type: "text", text: t }],
15
+ });
16
+
17
+ afterEach(() => {
18
+ resetAdvisorStateForTests();
19
+ });
20
+
21
+ describe("advisor state store", () => {
22
+ test("records system prompt and messages independently per conversation", () => {
23
+ recordSystemPrompt("c1", "system A");
24
+ recordMessages("c1", [userMsg("hello")]);
25
+ recordSystemPrompt("c2", "system B");
26
+
27
+ expect(getCapture("c1")?.systemPrompt).toBe("system A");
28
+ expect(getCapture("c1")?.messages).toEqual([userMsg("hello")]);
29
+ expect(getCapture("c2")?.systemPrompt).toBe("system B");
30
+ expect(getCapture("c2")?.messages).toEqual([]);
31
+ });
32
+
33
+ test("seedCapture snapshots (copies) the array", () => {
34
+ const live: Message[] = [userMsg("one")];
35
+ seedCapture("c1", live);
36
+ live.push(userMsg("two"));
37
+ expect(getCapture("c1")?.messages).toEqual([userMsg("one")]);
38
+ });
39
+
40
+ test("getCapture returns undefined for an unseen conversation", () => {
41
+ expect(getCapture("nope")).toBeUndefined();
42
+ });
43
+ });