@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
@@ -1,3 +1,7 @@
1
+ import { validateInferenceProfileKey } from "../../config/inference-profile-validation.js";
2
+ import { resolveDefaultProfileKey } from "../../config/llm-resolver.js";
3
+ import { getConfig } from "../../config/loader.js";
4
+ import { AUTO_PROFILE_KEY } from "../../config/seed-inference-profiles.js";
1
5
  import { findConversation } from "../../daemon/conversation-registry.js";
2
6
  import { getConversationOverrideProfile } from "../../memory/conversation-crud.js";
3
7
  import type { Message } from "../../providers/types.js";
@@ -14,6 +18,7 @@ export async function executeSubagentSpawn(
14
18
  const extraContext = input.context as string | undefined;
15
19
  const fork = input.fork === true;
16
20
  const role = (input.role as string | undefined) ?? undefined;
21
+ const inferenceProfile = input.inference_profile;
17
22
 
18
23
  // For fork mode, sendResultToUser defaults to false unless explicitly set to true.
19
24
  // For regular mode, sendResultToUser defaults to true (existing behavior).
@@ -28,6 +33,26 @@ export async function executeSubagentSpawn(
28
33
  };
29
34
  }
30
35
 
36
+ let requestedOverrideProfile: string | undefined;
37
+ let forceOverrideProfile = false;
38
+ if (inferenceProfile !== undefined) {
39
+ if (typeof inferenceProfile !== "string") {
40
+ return {
41
+ content: "Error: inference_profile must be a string",
42
+ isError: true,
43
+ };
44
+ }
45
+ const profileError = validateInferenceProfileKey(inferenceProfile);
46
+ if (profileError) {
47
+ return {
48
+ content: `Error: ${profileError}`,
49
+ isError: true,
50
+ };
51
+ }
52
+ requestedOverrideProfile = inferenceProfile;
53
+ forceOverrideProfile = true;
54
+ }
55
+
31
56
  const manager = getSubagentManager();
32
57
  const sendToClient = context.sendToClient as
33
58
  | ((msg: { type: string; [key: string]: unknown }) => void)
@@ -75,18 +100,41 @@ export async function executeSubagentSpawn(
75
100
  // `SubagentManager.spawn` forwards it back into the subagent's
76
101
  // `runAgentLoop` call as `options.overrideProfile`.
77
102
  //
78
- // Prefer the per-turn `context.overrideProfile` (populated by
79
- // `runAgentLoopImpl` from its resolved `turnOverrideProfile`) over a
80
- // row read so nested spawns inherit correctly. The current subagent's
81
- // own conversation row never has `inferenceProfile` set its override
82
- // arrived via in-memory `SubagentConfig.overrideProfile` and even if it
83
- // were set, `getConversationOverrideProfile` short-circuits for
84
- // background conversations and returns `undefined`. Falling back to the
85
- // row read preserves behavior for tool calls that originate outside an
86
- // agent-loop turn.
87
- const inheritedOverrideProfile =
88
- context.overrideProfile ??
89
- getConversationOverrideProfile(context.conversationId);
103
+ // Resolution order: an explicit spawn-time profile, then the per-turn
104
+ // `context.overrideProfile` (populated by `runAgentLoopImpl` from its
105
+ // resolved `turnOverrideProfile`, covering per-conversation overrides and
106
+ // tool-routed switches), then a row read, and finally the resolved DEFAULT
107
+ // profile of the call site that invoked us. That last fallback is what makes
108
+ // a subagent match its invoker when the invoking turn ran purely on its
109
+ // call-site default the workspace `activeProfile` for `mainAgent`, or the
110
+ // call site's own catalog default (e.g. `cost-optimized`) for a
111
+ // heartbeat/background invoker. `resolveDefaultProfileKey` does not reflect a
112
+ // static `llm.callSites.<callSite>` profile override (only the
113
+ // activeProfile-for-mainAgent path plus the catalog default), a narrow gap
114
+ // that only surfaces with no `activeProfile` set and a hand-tuned call-site
115
+ // profile.
116
+ //
117
+ // The fallback is forwarded NON-forced, so an explicit
118
+ // `llm.callSites.subagentSpawn` profile still wins; an explicit
119
+ // `inference_profile` argument keeps `forceOverrideProfile` and wins
120
+ // outright. (The row read short-circuits to `undefined` for the background
121
+ // subagent conversation and for tool calls outside an agent-loop turn.)
122
+ let inheritedOverrideProfile = requestedOverrideProfile;
123
+ if (inheritedOverrideProfile === undefined) {
124
+ const inheritedCandidate =
125
+ context.overrideProfile ??
126
+ getConversationOverrideProfile(context.conversationId) ??
127
+ resolveDefaultProfileKey(
128
+ context.invokingCallSite ?? "mainAgent",
129
+ getConfig().llm,
130
+ );
131
+ // Skip the metadata-only "auto" key: forwarding it collapses the child to
132
+ // `llm.default`, whereas the invoker's own auto base IS the `subagentSpawn`
133
+ // default — so leaving this undefined keeps the child on that default.
134
+ if (inheritedCandidate !== AUTO_PROFILE_KEY) {
135
+ inheritedOverrideProfile = inheritedCandidate;
136
+ }
137
+ }
90
138
 
91
139
  try {
92
140
  const subagentId = await manager.spawn(
@@ -102,6 +150,7 @@ export async function executeSubagentSpawn(
102
150
  ...(inheritedOverrideProfile
103
151
  ? { overrideProfile: inheritedOverrideProfile }
104
152
  : {}),
153
+ ...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
105
154
  ...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
106
155
  ...forkFields,
107
156
  },
@@ -4,8 +4,8 @@ import { spawn } from "node:child_process";
4
4
  import { getConfig } from "../../config/loader.js";
5
5
  import { isCesShellLockdownEnabled } from "../../credential-execution/feature-gates.js";
6
6
  import { RiskLevel } from "../../permissions/types.js";
7
- import { isUntrustedTrustClass } from "../../runtime/actor-trust-resolver.js";
8
7
  import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
8
+ import { isUntrustedShellLockdownActive } from "../../runtime/effective-capabilities.js";
9
9
  import { redactSecrets } from "../../security/secret-scanner.js";
10
10
  import { getLogger } from "../../util/logger.js";
11
11
  import { getDataDir } from "../../util/platform.js";
@@ -117,9 +117,10 @@ export const shellTool = {
117
117
  }
118
118
 
119
119
  const config = getConfig();
120
- const shellLockdownActive =
121
- isCesShellLockdownEnabled(config) &&
122
- isUntrustedTrustClass(context.trustClass);
120
+ const shellLockdownActive = isUntrustedShellLockdownActive({
121
+ trustClass: context.trustClass,
122
+ lockdownEnabled: isCesShellLockdownEnabled(config),
123
+ });
123
124
 
124
125
  const networkMode: "off" | "proxied" =
125
126
  input.network_mode === "proxied" ? "proxied" : "off";
@@ -307,6 +308,11 @@ export const shellTool = {
307
308
 
308
309
  const env = buildSanitizedEnv();
309
310
  env.__CONVERSATION_ID = context.conversationId;
311
+ // Surface the resolving model to assistant CLI commands so they can tailor
312
+ // remediation guidance for weak open models (see isWeakOpenModel).
313
+ if (context.attribution?.resolvedModel) {
314
+ env.__RESOLVED_MODEL = context.attribution.resolvedModel;
315
+ }
310
316
  if (proxyEnv) {
311
317
  Object.assign(env, proxyEnv);
312
318
  }
@@ -9,7 +9,7 @@ import {
9
9
  isUnparseableToolArgs,
10
10
  unparseableToolArgsMessage,
11
11
  } from "../providers/unparseable-tool-args.js";
12
- import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
12
+ import { resolveCapabilities } from "../runtime/capabilities.js";
13
13
  import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-helper.js";
14
14
  import { redactSecrets } from "../security/secret-scanner.js";
15
15
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
@@ -157,7 +157,6 @@ const UI_SURFACE_TOOLS = new Set(["ui_show", "ui_update", "ui_dismiss"]);
157
157
 
158
158
  function requiresGuardianApprovalForActor(
159
159
  toolName: string,
160
- input: Record<string, unknown>,
161
160
  executionTarget: ExecutionTarget,
162
161
  ): boolean {
163
162
  // UI surface tools are passive, user-visible operations (cards, forms,
@@ -170,14 +169,14 @@ function requiresGuardianApprovalForActor(
170
169
  // Side-effect tools always require guardian approval for untrusted actors.
171
170
  // Read-only host execution is also blocked because it can leak sensitive
172
171
  // local information (e.g. shell/file reads).
173
- return isSideEffectTool(toolName, input) || executionTarget === "host";
172
+ return isSideEffectTool(toolName) || executionTarget === "host";
174
173
  }
175
174
 
176
175
  function guardianApprovalDeniedMessage(
177
176
  trustClass: ToolContext["trustClass"],
178
177
  toolName: string,
179
178
  ): string {
180
- if (trustClass === "unknown") {
179
+ if (resolveCapabilities(trustClass).sensitiveToolApproval === "deny") {
181
180
  return `Permission denied for "${toolName}": this action requires guardian approval from a verified channel identity.`;
182
181
  }
183
182
  return `Permission denied for "${toolName}": this action requires guardian approval and the current actor is not the guardian.`;
@@ -320,11 +319,14 @@ export class ToolApprovalHandler {
320
319
 
321
320
  const guardianApprovalRequired = requiresGuardianApprovalForActor(
322
321
  name,
323
- input,
324
322
  executionTarget,
325
323
  );
326
324
 
327
- if (isUntrustedTrustClass(context.trustClass) && guardianApprovalRequired) {
325
+ if (
326
+ resolveCapabilities(context.trustClass).sensitiveToolApproval !==
327
+ "self" &&
328
+ guardianApprovalRequired
329
+ ) {
328
330
  const inputDigest = computeToolApprovalDigest(name, input);
329
331
  needsGrantConsumption = true;
330
332
  deferredConsumeParams = {
@@ -532,15 +534,18 @@ export class ToolApprovalHandler {
532
534
 
533
535
  // No matching grant or race condition - deny or wait inline.
534
536
  //
535
- // For verified non-guardian actors (trusted_contact) with sufficient
536
- // context, escalate to the guardian by creating a canonical
537
- // tool_grant_request. Then wait bounded for the grant to become
538
- // available - this lets the tool call succeed inline after guardian
539
- // approval without the requester having to retry manually.
537
+ // For non-guardian actors with established identity (trusted_contact
538
+ // or unverified_contact) and sufficient context, escalate to the
539
+ // guardian by creating a canonical tool_grant_request. Then wait
540
+ // bounded for the grant to become available - this lets the tool call
541
+ // succeed inline after guardian approval without the requester having
542
+ // to retry manually.
540
543
  //
541
- // Unverified actors remain fail-closed with no escalation or wait.
544
+ // Actors with no identity (unknown) remain fail-closed with no
545
+ // escalation or wait.
542
546
  if (
543
- context.trustClass === "trusted_contact" &&
547
+ resolveCapabilities(context.trustClass).sensitiveToolApproval ===
548
+ "escalate-and-wait" &&
544
549
  context.assistantId &&
545
550
  context.executionChannel &&
546
551
  context.requesterExternalUserId
@@ -15,7 +15,6 @@ import { askQuestionTool } from "./ask-question/ask-question-tool.js";
15
15
  import { makeAuthenticatedRequestTool } from "./credential-execution/make-authenticated-request.js";
16
16
  import { manageSecureCommandTool } from "./credential-execution/manage-secure-command-tool.js";
17
17
  import { runAuthenticatedCommandTool } from "./credential-execution/run-authenticated-command.js";
18
- import { credentialStoreTool } from "./credentials/vault.js";
19
18
  import { fileEditTool } from "./filesystem/edit.js";
20
19
  import { fileListTool } from "./filesystem/list.js";
21
20
  import { fileReadTool } from "./filesystem/read.js";
@@ -90,7 +89,6 @@ export const explicitTools: ToolDefinition[] = [
90
89
  // Always-explicit tools
91
90
  rememberTool,
92
91
  recallTool,
93
- credentialStoreTool,
94
92
  notifyParentTool,
95
93
  askQuestionTool,
96
94
  // NOTE: external skill tools (registered via registerExternalTools in
@@ -13,6 +13,7 @@ import { RiskLevel } from "@vellumai/skill-host-contracts";
13
13
  import { z } from "zod";
14
14
 
15
15
  import type { InterfaceId } from "../channels/types.js";
16
+ import type { LLMCallSite } from "../config/schemas/llm.js";
16
17
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
17
18
  import type { SecretPromptResult } from "../permissions/secret-prompter.js";
18
19
  import type { ContentBlock } from "../providers/types.js";
@@ -363,6 +364,14 @@ export interface ToolContext {
363
364
  * `executeSubagentSpawn` in tools/subagent/spawn.ts.
364
365
  */
365
366
  overrideProfile?: string;
367
+ /**
368
+ * The LLM call site of the turn currently executing this tool (`mainAgent`,
369
+ * `heartbeatAgent`, scheduled work, etc.). `subagent_spawn` reads it to
370
+ * default a spawned subagent's inference profile to the profile the invoking
371
+ * turn resolved to, so subagents match whatever agent invoked them rather
372
+ * than always falling back to the static `subagentSpawn` call-site default.
373
+ */
374
+ invokingCallSite?: LLMCallSite;
366
375
  /**
367
376
  * Canonical principal ID of the actor on whose behalf this tool invocation
368
377
  * is running. Sourced from `conversation.trustContext.guardianPrincipalId`.
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { RiskLevel } from "../../permissions/types.js";
11
+ import { isWeakOpenModel } from "../../providers/weak-open-model.js";
11
12
  import { ACTIVATION_MOMENT_PARAMS } from "../../telemetry/activation-funnel.js";
12
13
  import type {
13
14
  ToolContext,
@@ -37,8 +38,9 @@ function proxyExecute(toolName: string) {
37
38
  ): Promise<ToolExecutionResult> => {
38
39
  if (toolName === "ui_show" && isEmptyDynamicPage(input)) {
39
40
  return {
40
- content:
41
- "Error: ui_show dynamic_page requires non-empty HTML in `data.html`. The surface was not displayed because no content was provided — the user would see a blank box. Resend ui_show with the full HTML markup in `data.html`.",
41
+ content: isWeakOpenModel(context.attribution?.resolvedModel)
42
+ ? EMPTY_DYNAMIC_PAGE_DECLARATIVE_REDIRECT
43
+ : EMPTY_DYNAMIC_PAGE_HTML_HINT,
42
44
  isError: true,
43
45
  };
44
46
  }
@@ -59,6 +61,14 @@ function proxyExecute(toolName: string) {
59
61
  };
60
62
  }
61
63
 
64
+ if (toolName === "ui_update" && isEmptyUpdate(input)) {
65
+ return {
66
+ content:
67
+ 'Error: ui_update received an empty `data` payload, so the surface was unchanged — the user still sees its previous state. The provided data is merged into the surface\'s current data, and merging nothing is a no-op. To advance a task_progress card, send the full step list: ui_update { surface_id: "<id>", data: { templateData: { steps: [{ label: "<step>", status: "completed" }, { label: "<step>", status: "in_progress" }] } } }. Resend ui_update with the fields you intend to change under `data`.',
68
+ isError: true,
69
+ };
70
+ }
71
+
62
72
  if (!context.proxyToolResolver) {
63
73
  return {
64
74
  content: `No proxy resolver configured for proxy tool "${toolName}". This tool requires an external resolver (e.g. a connected macOS client).`,
@@ -81,6 +91,27 @@ function proxyExecute(toolName: string) {
81
91
  };
82
92
  }
83
93
 
94
+ /**
95
+ * Rejection envelope for an empty `dynamic_page` ui_show from a capable model:
96
+ * the surface carried no `data.html`, so it would render as a blank box. These
97
+ * models reliably author HTML, so the fix is simply to resend it inline.
98
+ */
99
+ const EMPTY_DYNAMIC_PAGE_HTML_HINT =
100
+ "Error: ui_show dynamic_page requires non-empty HTML in `data.html`. The surface was not displayed because no content was provided — the user would see a blank box. Resend ui_show with the full HTML markup in `data.html`.";
101
+
102
+ /**
103
+ * Rejection envelope for an empty `dynamic_page` ui_show from a weak open model.
104
+ * Authoring full HTML inline is the generation task these models fail at (an
105
+ * empty `data: {}` here, or broken markup otherwise), so steering them to
106
+ * "resend the HTML" just repeats the failure. Most widget requests are really
107
+ * structured data — comparisons, results, metrics — which render reliably via a
108
+ * field-based surface the model populates without writing any HTML, and which
109
+ * cannot render blank. dynamic_page stays available for genuinely custom visual
110
+ * HTML.
111
+ */
112
+ const EMPTY_DYNAMIC_PAGE_DECLARATIVE_REDIRECT =
113
+ 'Error: ui_show dynamic_page was not displayed — `data.html` was empty, so the user would see a blank box. Authoring full HTML inline is error-prone; for data, comparisons, results, or metrics prefer a structured surface, which you fill with fields (no HTML) and which never renders blank. Re-show the content as one of: a `table` (ui_show { surface_type: "table", data: { columns: [{ id, label }], rows: [{ id, cells: { <columnId>: "<value>" } }] } }), a `card` ({ title, body, metadata: [{ label, value }] }), or `work_result` ({ summary, metrics: [{ label, value }] }). Only use dynamic_page when you genuinely need custom visual HTML, in which case include the complete markup in `data.html` now.';
114
+
84
115
  /**
85
116
  * Worked ui_update example, appended to a successful task_progress `ui_show`
86
117
  * result so the model learns the update pattern at the point of use (with the
@@ -98,6 +129,36 @@ function isTaskProgressCardShow(input: Record<string, unknown>): boolean {
98
129
  return data?.template === "task_progress";
99
130
  }
100
131
 
132
+ /**
133
+ * A `ui_update` whose `data` merge would change nothing: missing, not an
134
+ * object, or containing only (recursively) empty objects. Merging such a
135
+ * payload is a silent no-op — the surface keeps its prior state while the
136
+ * client still reports "Surface updated" — so the model never learns its
137
+ * update was hollow and a live card (e.g. task_progress) appears frozen.
138
+ * Arrays (e.g. `templateData.steps`) and any non-empty primitive leaf count
139
+ * as content.
140
+ */
141
+ function isEmptyUpdate(input: Record<string, unknown>): boolean {
142
+ const data = asRecord(input.data);
143
+ return data === null || !hasContent(data);
144
+ }
145
+
146
+ function hasContent(value: unknown): boolean {
147
+ if (value === null || value === undefined) {
148
+ return false;
149
+ }
150
+ if (Array.isArray(value)) {
151
+ return value.length > 0;
152
+ }
153
+ if (typeof value === "object") {
154
+ return Object.values(value).some(hasContent);
155
+ }
156
+ if (typeof value === "string") {
157
+ return value.trim().length > 0;
158
+ }
159
+ return true;
160
+ }
161
+
101
162
  function isEmptyDynamicPage(input: Record<string, unknown>): boolean {
102
163
  if (input.surface_type !== "dynamic_page") {
103
164
  return false;
@@ -311,7 +372,7 @@ export const uiShowTool = {
311
372
  // ui_update
312
373
  // ---------------------------------------------------------------------------
313
374
 
314
- const uiUpdateTool = {
375
+ export const uiUpdateTool = {
315
376
  name: "ui_update",
316
377
  description:
317
378
  "Update an existing surface's data. The provided data object is merged into the surface's current data.\n" +
@@ -10,6 +10,8 @@
10
10
  * /v1/channel-verification-sessions/revoke
11
11
  */
12
12
 
13
+ import { resolveCapabilities } from "../runtime/capabilities.js";
14
+
13
15
  const VERIFICATION_ENDPOINT_PATHS = [
14
16
  "/v1/channel-verification-sessions",
15
17
  "/v1/channel-verification-sessions/resend",
@@ -128,7 +130,7 @@ export function enforceVerificationControlPlanePolicy(
128
130
  return { denied: false };
129
131
  }
130
132
 
131
- if (trustClass === "guardian") {
133
+ if (resolveCapabilities(trustClass).canUseVerificationControlPlane) {
132
134
  return { denied: false };
133
135
  }
134
136
 
@@ -11,11 +11,9 @@ mock.module("../../util/logger.js", () => ({
11
11
  }));
12
12
 
13
13
  // ── Mutable mock state ────────────────────────────────────────────────
14
- // `flagEnabled` toggles the `workflows` feature flag; `configThrows`
15
- // simulates config not yet loaded (test-setup race). The run-manager mock
16
- // records the args of the last `start()` call for assertion.
14
+ // `configThrows` simulates config not yet loaded (test-setup race). The
15
+ // run-manager mock records the args of the last `start()` call for assertion.
17
16
 
18
- let flagEnabled = true;
19
17
  let configThrows = false;
20
18
 
21
19
  const realLoader = await import("../../config/loader.js");
@@ -35,13 +33,6 @@ mock.module("../../config/loader.js", () => ({
35
33
  }) as unknown as ReturnType<typeof realLoader.loadConfig>,
36
34
  }));
37
35
 
38
- const realFlags = await import("../../config/assistant-feature-flags.js");
39
- mock.module("../../config/assistant-feature-flags.js", () => ({
40
- ...realFlags,
41
- isAssistantFeatureFlagEnabled: (key: string) =>
42
- key === "workflows" ? flagEnabled : false,
43
- }));
44
-
45
36
  // No live conversation in tests — the tool falls back to a synthetic trust
46
37
  // context built from the tool context's trustClass.
47
38
  const realRegistry = await import("../../daemon/conversation-registry.js");
@@ -92,7 +83,6 @@ function makeContext(): Parameters<typeof executeRunWorkflow>[1] {
92
83
  }
93
84
 
94
85
  beforeEach(() => {
95
- flagEnabled = true;
96
86
  configThrows = false;
97
87
  startThrows = null;
98
88
  lastStartArgs = null;
@@ -105,10 +95,10 @@ beforeEach(() => {
105
95
  });
106
96
 
107
97
  describe("workflow tools are served by the workflows skill", () => {
108
- // The cutover moved run_workflow / manage_workflows out of the always-on tool
109
- // manifest into the flag-gated `workflows` bundled skill (loaded via
110
- // skill_load, invoked via skill_execute). The skill manifest is the
111
- // source of truth; assert it declares both as in-process host tools.
98
+ // run_workflow / manage_workflows are served by the `workflows` bundled skill
99
+ // (loaded via skill_load, invoked via skill_execute), not the always-on tool
100
+ // manifest. The skill manifest is the source of truth; assert it declares both
101
+ // as in-process host tools.
112
102
  test("the workflows skill TOOLS.json declares both host-executed tools", async () => {
113
103
  const { readFileSync } = await import("node:fs");
114
104
  const { join } = await import("node:path");
@@ -211,13 +201,13 @@ describe("run_workflow launch", () => {
211
201
  });
212
202
 
213
203
  test("surfaces a run-manager start error as a tool error", async () => {
214
- startThrows = new Error("Workflows are not enabled.");
204
+ startThrows = new Error("run manager exploded");
215
205
  const res = await executeRunWorkflow(
216
206
  { script: "export const meta = {};" },
217
207
  makeContext(),
218
208
  );
219
209
  expect(res.isError).toBe(true);
220
- expect(res.content).toContain("Workflows are not enabled.");
210
+ expect(res.content).toContain("run manager exploded");
221
211
  });
222
212
  });
223
213
 
@@ -68,6 +68,7 @@ export async function executeRunWorkflow(
68
68
  args,
69
69
  manifest,
70
70
  conversationId: context.conversationId,
71
+ ...(context.toolUseId ? { toolUseId: context.toolUseId } : {}),
71
72
  ...(label ? { label } : {}),
72
73
  trustContext,
73
74
  });
@@ -1,7 +1,11 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, statfsSync } from "node:fs";
3
3
 
4
- import { getMinikubeStorageSize } from "../config/env-registry.js";
4
+ import {
5
+ getIsContainerized,
6
+ getIsPlatform,
7
+ getMinikubeStorageSize,
8
+ } from "../config/env-registry.js";
5
9
  import { getWorkspaceDir } from "./platform.js";
6
10
 
7
11
  export interface DiskUsageInfo {
@@ -58,6 +62,57 @@ export function __resetDiskUsageCacheForTests(): void {
58
62
  duCachePaths = null;
59
63
  }
60
64
 
65
+ /**
66
+ * How the workspace volume's capacity should be reported when statfsSync
67
+ * cannot see the volume directly.
68
+ *
69
+ * - `fixed-cap`: minikube hostPath PVC — the PVC request is the hard
70
+ * capacity, so usage is measured with `du` and free space is whatever
71
+ * remains within the quota.
72
+ * - `host-free`: local Docker named volume — the volume is backed by the
73
+ * host (or Colima VM) filesystem and can grow into its free space, so
74
+ * usage is measured with `du` and the effective capacity is current usage
75
+ * plus the host's remaining headroom.
76
+ * - `none`: statfsSync already reports the volume accurately (bare metal, or
77
+ * a platform-managed instance on a CSI-backed PVC), so use it directly.
78
+ */
79
+ type WorkspaceVolumeReporting =
80
+ | { kind: "none" }
81
+ | { kind: "fixed-cap"; totalBytes: number }
82
+ | { kind: "host-free" };
83
+
84
+ /**
85
+ * Decide how to report the workspace volume's capacity. Both minikube
86
+ * hostPath PVCs and local Docker named volumes are backed by the host
87
+ * filesystem, so statfsSync reports the host's entire disk rather than the
88
+ * workspace volume — in both cases we measure actual usage with `du` instead.
89
+ */
90
+ function classifyWorkspaceVolume(
91
+ fsTotalBytes: number,
92
+ ): WorkspaceVolumeReporting {
93
+ // Minikube mode: the platform passes the PVC storage size so we can report
94
+ // accurate capacity. Detect the hostPath case by comparing filesystem size
95
+ // against the PVC size — if the filesystem is larger, statfsSync is seeing
96
+ // the host disk and we should measure the directory instead.
97
+ const storageSizeRaw = getMinikubeStorageSize();
98
+ if (storageSizeRaw) {
99
+ const pvcTotalBytes = parseK8sMemoryBytes(storageSizeRaw);
100
+ if (pvcTotalBytes !== null && fsTotalBytes > pvcTotalBytes * 1.1) {
101
+ return { kind: "fixed-cap", totalBytes: pvcTotalBytes };
102
+ }
103
+ return { kind: "none" };
104
+ }
105
+
106
+ // Local Docker hatch: the workspace is a Docker named volume backed by the
107
+ // host filesystem. Platform-managed remote instances run on CSI-backed PVCs
108
+ // where statfsSync already reports the volume, so they are excluded.
109
+ if (getIsContainerized() && !getIsPlatform()) {
110
+ return { kind: "host-free" };
111
+ }
112
+
113
+ return { kind: "none" };
114
+ }
115
+
61
116
  export function getDiskUsageInfo(): DiskUsageInfo | null {
62
117
  try {
63
118
  const wsDir = getWorkspaceDir();
@@ -68,28 +123,28 @@ export function getDiskUsageInfo(): DiskUsageInfo | null {
68
123
  const bytesToMb = (b: number) =>
69
124
  Math.round((b / (1024 * 1024)) * 100) / 100;
70
125
 
71
- // Minikube mode: the platform passes the PVC storage size so we can
72
- // report accurate capacity. On hostPath-backed PVCs statfsSync reports
73
- // the host's entire filesystem rather than the PVC. Detect this by
74
- // comparing filesystem size against PVC size — if the filesystem is
75
- // larger, measure actual directory usage with `du` instead.
76
- const storageSizeRaw = getMinikubeStorageSize();
77
- if (storageSizeRaw) {
78
- const pvcTotalBytes = parseK8sMemoryBytes(storageSizeRaw);
79
- if (pvcTotalBytes !== null && fsTotalBytes > pvcTotalBytes * 1.1) {
80
- const volumePaths = [diskPath];
81
- if (diskPath !== "/data" && existsSync("/data")) {
82
- volumePaths.push("/data");
83
- }
84
- const usedBytes = getCachedDirectorySizeBytes(volumePaths);
85
- if (usedBytes !== null) {
86
- return {
87
- path: diskPath,
88
- totalMb: bytesToMb(pvcTotalBytes),
89
- usedMb: bytesToMb(usedBytes),
90
- freeMb: bytesToMb(Math.max(0, pvcTotalBytes - usedBytes)),
91
- };
92
- }
126
+ const reporting = classifyWorkspaceVolume(fsTotalBytes);
127
+ if (reporting.kind !== "none") {
128
+ const volumePaths = [diskPath];
129
+ if (diskPath !== "/data" && existsSync("/data")) {
130
+ volumePaths.push("/data");
131
+ }
132
+ const usedBytes = getCachedDirectorySizeBytes(volumePaths);
133
+ if (usedBytes !== null) {
134
+ const totalBytes =
135
+ reporting.kind === "fixed-cap"
136
+ ? reporting.totalBytes
137
+ : usedBytes + fsFreeBytes;
138
+ const freeBytes =
139
+ reporting.kind === "fixed-cap"
140
+ ? Math.max(0, reporting.totalBytes - usedBytes)
141
+ : fsFreeBytes;
142
+ return {
143
+ path: diskPath,
144
+ totalMb: bytesToMb(totalBytes),
145
+ usedMb: bytesToMb(usedBytes),
146
+ freeMb: bytesToMb(freeBytes),
147
+ };
93
148
  }
94
149
  }
95
150
 
@@ -314,7 +314,14 @@ export function getWorkspaceToolsDir(): string {
314
314
  return join(getWorkspaceDir(), "tools");
315
315
  }
316
316
 
317
- /** Returns $VELLUM_WORKSPACE_DIR/routes — user-defined HTTP route handlers. */
317
+ /**
318
+ * Returns $VELLUM_WORKSPACE_DIR/routes — user-defined HTTP route handlers.
319
+ *
320
+ * Handler modules under this directory are dynamic-imported by the user-route
321
+ * dispatcher and their exported HTTP-method functions are executed on the
322
+ * next matching request, so the file risk classifier escalates writes under
323
+ * this path to High for the same reason `plugins/` and `tools/` are escalated.
324
+ */
318
325
  export function getWorkspaceRoutesDir(): string {
319
326
  return join(getWorkspaceDir(), "routes");
320
327
  }
@@ -402,8 +409,8 @@ export function getSkillRuntimePath(
402
409
  *
403
410
  * Resolution order:
404
411
  *
405
- * 1. macOS `.app` bundle: `Contents/Resources/bun` — shipped by
406
- * `clients/macos/build.sh` at a version that matches `.tool-versions`.
412
+ * 1. macOS `.app` bundle: `Contents/Resources/bun` — bundled at a version
413
+ * that matches `.tool-versions`.
407
414
  * 2. Next-to-binary: `<execDir>/bun` for Docker/generic compiled layouts
408
415
  * that stage a bun binary alongside the daemon (PR 29 wires this up).
409
416
  *
@@ -4,8 +4,8 @@
4
4
  * ahead of the planned watcher → skills-and-schedules migration.
5
5
  *
6
6
  * Both event shapes ship through the existing lifecycle-event telemetry
7
- * pipeline, so they inherit its `collectUsageData` opt-out gate and need
8
- * no wire-contract or platform-side changes:
7
+ * pipeline, so they inherit its platform `share_analytics` consent gate and
8
+ * need no wire-contract or platform-side changes:
9
9
  *
10
10
  * - `watcher_enabled:<providerId>` — one per enabled watcher, at most
11
11
  * once per 24h per daemon. Counts devices that have watchers
@@ -29,9 +29,8 @@
29
29
  * (If host execution is ever wanted, the deliberate path is to thread the
30
30
  * originating tool context through the engine — not to relax this gate.)
31
31
  *
32
- * This module is pure logic: it performs no feature-flag checks and no I/O
33
- * beyond the synchronous tool-registry lookup. The `workflows` flag gates the
34
- * callers, not this code.
32
+ * This module is pure logic: it performs no I/O beyond the synchronous
33
+ * tool-registry lookup.
35
34
  */
36
35
 
37
36
  import { z } from "zod";