@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
@@ -4,16 +4,14 @@
4
4
  * Periodically flushes LLM usage events and turn events (user messages) from
5
5
  * the local SQLite database and POSTs them to the platform telemetry endpoint.
6
6
  *
7
- * Two auth modes:
8
- * - Authenticated: Api-Key header via managed proxy context
9
- * - Anonymous: unauthenticated POST (telemetry endpoints are public)
7
+ * Authenticated-only: events are sent via the managed proxy context
8
+ * (Api-Key header). When no platform credentials are available, or when
9
+ * platform features are disabled (VELLUM_DISABLE_PLATFORM in local mode), the
10
+ * flush is skipped and retried next cycle.
10
11
  */
11
12
 
12
- import {
13
- getPlatformBaseUrl,
14
- getPlatformOrganizationId,
15
- getPlatformUserId,
16
- } from "../config/env.js";
13
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
14
+ import { getPlatformOrganizationId, getPlatformUserId } from "../config/env.js";
17
15
  import { getConfig } from "../config/loader.js";
18
16
  import { queryUnreportedAuthFallbackEvents } from "../memory/auth-fallback-events-store.js";
19
17
  import {
@@ -26,7 +24,17 @@ import { queryUnreportedOnboardingEvents } from "../memory/onboarding-events-sto
26
24
  import { queryUnreportedSkillLoadedEvents } from "../memory/skill-loaded-events-store.js";
27
25
  import { queryUnreportedToolExecutedEvents } from "../memory/tool-executed-events-store.js";
28
26
  import { queryUnreportedTurnEvents } from "../memory/turn-events-store.js";
27
+ import {
28
+ assembleBoundedTurnTrace,
29
+ isTurnSettled,
30
+ } from "../memory/turn-trace-store.js";
29
31
  import { VellumPlatformClient } from "../platform/client.js";
32
+ import {
33
+ getCachedShareAnalytics,
34
+ getCachedShareDiagnostics,
35
+ getCachedShareDiagnosticsVersion,
36
+ } from "../platform/consent-cache.js";
37
+ import { arePlatformFeaturesEnabled } from "../platform/feature-gate.js";
30
38
  import type { UsageAttributionProfileSource } from "../usage/types.js";
31
39
  import { getDeviceId } from "../util/device-id.js";
32
40
  import { getLogger } from "../util/logger.js";
@@ -35,6 +43,7 @@ import {
35
43
  type ActivationStepName,
36
44
  buildActivationDaemonEventId,
37
45
  } from "./activation-funnel.js";
46
+ import { isDiagnosticsConsentVersionEligible } from "./trace-collection-policy.js";
38
47
  import type { TelemetryEvent, TurnTelemetryClientInfo } from "./types.js";
39
48
 
40
49
  const log = getLogger("usage-telemetry");
@@ -128,7 +137,7 @@ export class UsageTelemetryReporter {
128
137
  // reporter shipping its rows: an absent watermark means no flush (opted
129
138
  // in or out) has ever advanced it, so rows recorded before this build —
130
139
  // including any opted-out period under older builds that gated the
131
- // reporter on collectUsageData — would otherwise ship retroactively.
140
+ // reporter on the usage-data opt-out — would otherwise ship retroactively.
132
141
  // Initialize an absent watermark to "now" at construction. Construction
133
142
  // happens during daemon startup before any tool runs, so no legitimate
134
143
  // row falls behind the watermark — initializing at first FLUSH instead
@@ -137,8 +146,8 @@ export class UsageTelemetryReporter {
137
146
  // absent and re-initialize later. An EXISTING watermark is never touched:
138
147
  // opted-out sessions keep it advancing via the opt-out flush branch, and
139
148
  // overwriting it here would drop a legitimate unshipped backlog.
140
- // `skill_loaded` needs no init: recording is gated on collectUsageData,
141
- // so opt-out rows never exist and its standard 0 default is safe.
149
+ // `skill_loaded` needs no init: recording is gated on share_analytics
150
+ // consent, so opt-out rows never exist and its standard 0 default is safe.
142
151
  //
143
152
  // Best-effort: DB init failures are tolerated at daemon startup (degraded
144
153
  // mode), so this must never throw out of the constructor — matching
@@ -162,7 +171,8 @@ export class UsageTelemetryReporter {
162
171
  // Delay the first flush to allow the credential infrastructure (CES
163
172
  // handshake) to complete. Without this delay, VellumPlatformClient.create()
164
173
  // returns null because the credential backend hasn't resolved yet, causing
165
- // telemetry to fall back to anonymous mode permanently.
174
+ // the initial flush to be skipped (we send authenticated-only); the
175
+ // delay lets credentials resolve so the first flush can actually ship.
166
176
  this.initialFlushTimer = setTimeout(() => {
167
177
  this.initialFlushTimer = null;
168
178
  this.flush().catch((err) => {
@@ -205,22 +215,32 @@ export class UsageTelemetryReporter {
205
215
  try {
206
216
  if (batchCount >= MAX_CONSECUTIVE_BATCHES) return;
207
217
 
208
- // Respect opt-out: if the user has disabled usage data collection, skip
209
- // the flush and advance watermarks so events recorded during the
210
- // opt-out window are never sent retroactively. The daemon runs the
211
- // reporter even when opted out specifically so this branch keeps
212
- // executing every cycle plus the final flush in stop() — which is
213
- // what lets a later opt-in (runtime or via restart) resume from a
214
- // watermark that already covers the opt-out window. One caveat: a
215
- // RUNTIME false→true flip can still ship up to one flush interval
216
- // (≤5 min) of pre-toggle rows recorded since the last opted-out flush;
218
+ // Skip when platform features are disabled (VELLUM_DISABLE_PLATFORM in
219
+ // local mode; the flag is ignored when IS_PLATFORM is set, matching
220
+ // VellumPlatformClient.create()). Watermarks are NOT advanced here: this
221
+ // is a deployment/local-mode toggle, not a privacy opt-out, so the unsent
222
+ // backlog ships once the flag is cleared.
223
+ if (!arePlatformFeaturesEnabled()) {
224
+ return;
225
+ }
226
+
227
+ // Respect opt-out: if the platform owner has not granted
228
+ // `share_analytics` consent, skip the flush and advance watermarks so
229
+ // events recorded during the opt-out window are never sent
230
+ // retroactively. The daemon runs the reporter even when opted out
231
+ // specifically so this branch keeps executing — every cycle plus the
232
+ // final flush in stop() — which is what lets a later opt-in (runtime or
233
+ // via restart) resume from a watermark that already covers the opt-out
234
+ // window. One caveat: a RUNTIME false→true flip can still ship up to one
235
+ // flush interval (≤5 min) of pre-toggle rows recorded since the last
236
+ // opted-out flush;
217
237
  // the restart path is fully covered by the final flush in stop(). The
218
238
  // caveat applies to the always-on tables without a write-time opt-out
219
239
  // gate (llm_usage, turn events) and to tool_invocations rows recorded
220
240
  // under builds predating the audit listener's write-time gate — new
221
241
  // opted-out tool_invocations rows persist NULL telemetry columns and
222
242
  // are unreportable by construction regardless of watermark timing.
223
- if (!getConfig().collectUsageData) {
243
+ if (!getCachedShareAnalytics()) {
224
244
  // Advance the timestamp watermarks and pin the ID watermarks to a
225
245
  // sentinel that sorts above any real UUID. The sentinel (rather than
226
246
  // "") keeps the compound-cursor branch active — a falsy ID would
@@ -332,9 +352,68 @@ export class UsageTelemetryReporter {
332
352
  BATCH_SIZE,
333
353
  );
334
354
 
355
+ // Trace completeness barrier (trace-eligible owners only).
356
+ //
357
+ // When trace collection is on, a turn's trace must only be sent once that
358
+ // turn is COMPLETE — otherwise a flush that races an in-progress reply
359
+ // would capture a partial transcript and the watermark would advance past
360
+ // the turn, leaving it permanently partial. So we report only the leading
361
+ // run of complete turns and STOP at the first incomplete (in-flight) one:
362
+ // the turn watermark is a single monotonic `(createdAt, id)` cursor, so a
363
+ // later complete turn cannot be reported past an earlier deferred one
364
+ // without skipping it. The deferred turn (and everything after it) is
365
+ // picked up on a later flush once its response settles.
366
+ //
367
+ // When trace collection is OFF, no turn is deferred and reporting timing
368
+ // is unchanged for everyone else — `turn_raw` behavior is untouched.
369
+ //
370
+ // Trace eligibility is composed daemon-side to mirror the platform's
371
+ // authoritative owner-based ingest gate, so traces for ineligible owners
372
+ // never leave the device. Three parts, fail-closed (all must be true):
373
+ // 1. the `trace-collection` LaunchDarkly flag (delivered via the
374
+ // assistant-tagged flag sync, already evaluated server-side for this
375
+ // assistant's owner),
376
+ // 2. the owner's cached `share_diagnostics` consent, and
377
+ // 3. the owner's cached `share_diagnostics_accepted_version` being at or
378
+ // past the disclosing version — the platform applies the identical
379
+ // check, so an old consent never yields a trace here or there.
380
+ const traceEligible =
381
+ isAssistantFeatureFlagEnabled("trace-collection", getConfig()) &&
382
+ getCachedShareDiagnostics() &&
383
+ isDiagnosticsConsentVersionEligible(getCachedShareDiagnosticsVersion());
384
+ let reportableTurnEvents = turnEvents;
385
+ if (traceEligible && turnEvents.length > 0) {
386
+ let barrier = turnEvents.length;
387
+ for (let i = 0; i < turnEvents.length; i++) {
388
+ const t = turnEvents[i];
389
+ if (
390
+ !isTurnSettled({
391
+ conversationId: t.conversationId,
392
+ userMessageId: t.id,
393
+ userMessageCreatedAt: t.createdAt,
394
+ })
395
+ ) {
396
+ barrier = i;
397
+ break;
398
+ }
399
+ }
400
+ if (barrier < turnEvents.length) {
401
+ reportableTurnEvents = turnEvents.slice(0, barrier);
402
+ log.debug(
403
+ {
404
+ deferredTurnId: turnEvents[barrier].id,
405
+ deferredConversationId: turnEvents[barrier].conversationId,
406
+ reportedTurns: barrier,
407
+ deferredTurns: turnEvents.length - barrier,
408
+ },
409
+ "Deferring in-progress turn(s) from telemetry until complete",
410
+ );
411
+ }
412
+ }
413
+
335
414
  if (
336
415
  events.length === 0 &&
337
- turnEvents.length === 0 &&
416
+ reportableTurnEvents.length === 0 &&
338
417
  lifecycleEvents.length === 0 &&
339
418
  onboardingEvents.length === 0 &&
340
419
  authFallbackEvents.length === 0 &&
@@ -343,14 +422,21 @@ export class UsageTelemetryReporter {
343
422
  )
344
423
  return;
345
424
 
346
- // Resolve auth context authenticated path uses client, anonymous path
347
- // sends unauthenticated (telemetry endpoints are public).
425
+ // Resolve auth context. We send authenticated-only: if no platform
426
+ // credentials are available yet, skip without advancing watermarks so the
427
+ // backlog ships on a later cycle once credentials resolve.
348
428
  const client = await VellumPlatformClient.create();
429
+ if (!client) {
430
+ log.debug(
431
+ { pendingEventCount: events.length + turnEvents.length },
432
+ "Telemetry flush: no platform credentials — skipping, will retry next cycle",
433
+ );
434
+ return;
435
+ }
349
436
  log.debug(
350
437
  {
351
- authenticated: !!client,
352
438
  usageCount: events.length,
353
- turnCount: turnEvents.length,
439
+ turnCount: reportableTurnEvents.length,
354
440
  lifecycleCount: lifecycleEvents.length,
355
441
  onboardingCount: onboardingEvents.length,
356
442
  authFallbackCount: authFallbackEvents.length,
@@ -396,7 +482,23 @@ export class UsageTelemetryReporter {
396
482
  assistant_version: e.assistantVersion ?? APP_VERSION,
397
483
  }),
398
484
  ),
399
- ...turnEvents.map((e): TelemetryEvent => {
485
+ ...reportableTurnEvents.map((e): TelemetryEvent => {
486
+ // Per-turn trace collection gate. `traceEligible` (computed above)
487
+ // requires the `trace-collection` flag AND the owner's cached
488
+ // `share_diagnostics` consent AND an eligible accepted consent
489
+ // version. Fail-closed: when any is off the trace is omitted and the
490
+ // trace-free turn row flushes as before. The
491
+ // `share_analytics` gate above already passed, so this is an
492
+ // additional, independent gate specific to trace PII. Every turn
493
+ // reaching here is settled (the completeness barrier dropped any
494
+ // in-flight turns), so the trace is never a partial mid-turn snapshot.
495
+ const trace = traceEligible
496
+ ? assembleBoundedTurnTrace({
497
+ conversationId: e.conversationId,
498
+ userMessageId: e.id,
499
+ userMessageCreatedAt: e.createdAt,
500
+ })
501
+ : null;
400
502
  // `messages.metadata.client` is a nested JSON object extracted
401
503
  // via `json_extract`; sqlite returns it as a text representation.
402
504
  // Parse defensively — a corrupted blob in the JSON column should
@@ -431,6 +533,11 @@ export class UsageTelemetryReporter {
431
533
  interface_id: e.interfaceId,
432
534
  channel_id: e.channelId,
433
535
  client,
536
+ // Only attach `trace` when consent is on AND a bounded trace was
537
+ // assembled. Omitting the key entirely when there's no trace keeps
538
+ // the wire shape byte-identical to pre-trace turn events for the
539
+ // common (no-consent) path.
540
+ ...(trace ? { trace } : {}),
434
541
  // Turn events derive from `messages` + `conversations`
435
542
  // rather than a dedicated table. Adding `assistant_version`
436
543
  // to `messages` is a separate (larger) migration; until
@@ -588,18 +695,12 @@ export class UsageTelemetryReporter {
588
695
  body: JSON.stringify(payload),
589
696
  };
590
697
 
591
- let resp: Response;
592
- if (client) {
593
- resp = await client.fetch(TELEMETRY_PATH, fetchInit);
594
- } else {
595
- const url = `${getPlatformBaseUrl()}${TELEMETRY_PATH}`;
596
- resp = await fetch(url, fetchInit);
597
- }
698
+ const resp = await client.fetch(TELEMETRY_PATH, fetchInit);
598
699
 
599
700
  if (!resp.ok) {
600
701
  const body = await resp.text();
601
702
  log.warn(
602
- { status: resp.status, authenticated: !!client, body },
703
+ { status: resp.status, body },
603
704
  "Usage telemetry POST failed — will retry next cycle",
604
705
  );
605
706
  return;
@@ -616,9 +717,11 @@ export class UsageTelemetryReporter {
616
717
  setMemoryCheckpoint(CHECKPOINT_KEY_WATERMARK_ID, lastEvent.id);
617
718
  }
618
719
 
619
- // Advance turn watermark (compound cursor)
620
- if (turnEvents.length > 0) {
621
- const lastTurn = turnEvents[turnEvents.length - 1];
720
+ // Advance turn watermark (compound cursor) — only to the last REPORTED
721
+ // turn. Deferred (in-flight) turns sit beyond this cursor and are
722
+ // re-evaluated on a later flush, so the watermark never skips them.
723
+ if (reportableTurnEvents.length > 0) {
724
+ const lastTurn = reportableTurnEvents[reportableTurnEvents.length - 1];
622
725
  setMemoryCheckpoint(
623
726
  CHECKPOINT_KEY_TURN_WATERMARK,
624
727
  String(lastTurn.createdAt),
@@ -693,10 +796,14 @@ export class UsageTelemetryReporter {
693
796
  );
694
797
  }
695
798
 
696
- // If we got a full batch of any type, there may be more — recurse
799
+ // If we got a full batch of any type, there may be more — recurse.
800
+ // Turns use the REPORTED count: when the completeness barrier truncates
801
+ // the batch, the deferred turns must wait for a later flush (by which
802
+ // point they've settled) rather than being re-queried and re-deferred in
803
+ // a tight recursion.
697
804
  if (
698
805
  events.length === BATCH_SIZE ||
699
- turnEvents.length === BATCH_SIZE ||
806
+ reportableTurnEvents.length === BATCH_SIZE ||
700
807
  lifecycleEvents.length === BATCH_SIZE ||
701
808
  onboardingEvents.length === BATCH_SIZE ||
702
809
  authFallbackEvents.length === BATCH_SIZE ||
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## New Non-Skill Tools Are Strongly Discouraged
4
4
 
5
- **Prefer skills over new non-skill tool registrations.** Non-skill tools require approval from Team Jarvis.
5
+ **Prefer skills over new non-skill tool registrations.** Non-skill tools require approval from the core team.
6
6
 
7
7
  Skills are the preferred approach for adding new capabilities — they are progressively disclosed into context, more portable, and can be iterated on independently. New non-skill tool registrations (`class ... implements Tool` + `registerTool()`) carry additional costs:
8
8
 
@@ -48,7 +48,7 @@ See [`assistant/docs/credential-execution-service.md`](../../docs/credential-exe
48
48
 
49
49
  ## If You Have Approval
50
50
 
51
- If Team Jarvis has approved your new tool:
51
+ If the core team has approved your new tool:
52
52
 
53
53
  1. The pre-commit hook will block your commit by default
54
54
  2. Use `git commit --no-verify` to bypass the hook
@@ -56,4 +56,4 @@ If Team Jarvis has approved your new tool:
56
56
 
57
57
  ## Questions?
58
58
 
59
- Contact Team Jarvis before shipping a new tool.
59
+ Contact the core team before shipping a new tool.
@@ -54,6 +54,9 @@ const setPreferredBackendKindMock = mock(
54
54
  (_conversationId: string, _kind: CdpClientKind) => {},
55
55
  );
56
56
  const clearPreferredBackendKindMock = mock((_conversationId: string) => {});
57
+ const waitForExtensionClientMock = mock(
58
+ async (_actor?: string, _targetClientId?: string) => true,
59
+ );
57
60
 
58
61
  // ---------------------------------------------------------------------------
59
62
  // Module mocks (must be declared before dynamic import)
@@ -98,6 +101,7 @@ mock.module("../../../daemon/host-browser-proxy.js", () => ({
98
101
  return {
99
102
  isAvailable: () => false,
100
103
  hasExtensionClient: () => false,
104
+ waitForExtensionClient: waitForExtensionClientMock,
101
105
  request: () => Promise.reject(new Error("no extension")),
102
106
  };
103
107
  },
@@ -141,9 +145,36 @@ describe("acquireCdpClientWithMode: target_client_id overrides sticky backend mo
141
145
  getCdpClientMock.mockClear();
142
146
  setPreferredBackendKindMock.mockClear();
143
147
  clearPreferredBackendKindMock.mockClear();
148
+ waitForExtensionClientMock.mockClear();
144
149
  stickyKind = null;
145
150
  });
146
151
 
152
+ test("extension-pinned acquisition awaits the reconnect grace window", async () => {
153
+ await executeBrowserAttach(
154
+ { target_client_id: "host-client-grace" },
155
+ makeContext("grace-window"),
156
+ );
157
+
158
+ // The grace wait runs before backend selection so a flapping extension
159
+ // SSE reconnect doesn't hard-fail the dispatch, and it targets the exact
160
+ // requested client.
161
+ expect(waitForExtensionClientMock).toHaveBeenCalledTimes(1);
162
+ expect(waitForExtensionClientMock).toHaveBeenCalledWith(
163
+ undefined,
164
+ "host-client-grace",
165
+ );
166
+ expect(getCdpClientCalls[0].mode).toBe("extension");
167
+ });
168
+
169
+ test("non-extension (sticky local, no target) does not wait on the extension", async () => {
170
+ stickyKind = "local";
171
+
172
+ await executeBrowserAttach({}, makeContext("no-grace-for-local"));
173
+
174
+ expect(waitForExtensionClientMock).not.toHaveBeenCalled();
175
+ expect(getCdpClientCalls[0].mode).toBe("local");
176
+ });
177
+
147
178
  test("sticky local + target_client_id → getCdpClient receives mode:extension, not local", async () => {
148
179
  // Simulate a prior turn that pinned the conversation to the local backend.
149
180
  stickyKind = "local";
@@ -375,16 +375,17 @@ function isRestrictedChromePageProbeError(error: CdpError): boolean {
375
375
  * The returned client is wrapped so its first successful `send()`
376
376
  * writes the resolved kind back to the conversation memo.
377
377
  */
378
- function acquireCdpClientWithMode(
378
+ async function acquireCdpClientWithMode(
379
379
  input: Record<string, unknown>,
380
380
  context: ToolContext,
381
- ):
381
+ ): Promise<
382
382
  | {
383
383
  cdp: ReturnType<typeof getCdpClient>;
384
384
  browserMode: BrowserMode;
385
385
  errorResult?: never;
386
386
  }
387
- | { cdp?: never; browserMode?: never; errorResult: ToolExecutionResult } {
387
+ | { cdp?: never; browserMode?: never; errorResult: ToolExecutionResult }
388
+ > {
388
389
  const modeResult = parseBrowserMode(input);
389
390
  if (!modeResult.ok) {
390
391
  return {
@@ -412,6 +413,16 @@ function acquireCdpClientWithMode(
412
413
  ? rememberedKind
413
414
  : browserMode;
414
415
 
416
+ // Extension-pinned dispatch (explicit `--browser-mode extension` or a
417
+ // `target_client_id`) hard-fails when the extension is momentarily
418
+ // absent. Absorb a brief reconnect blip before selecting the backend.
419
+ if (effectiveMode === "extension") {
420
+ await HostBrowserProxy.instance.waitForExtensionClient(
421
+ context.sourceActorPrincipalId,
422
+ targetClientId,
423
+ );
424
+ }
425
+
415
426
  try {
416
427
  const raw = getCdpClient(context, { mode: effectiveMode, targetClientId });
417
428
  const cdp = wrapWithKindMemo(raw, context.conversationId);
@@ -655,7 +666,7 @@ export async function executeBrowserNavigate(
655
666
  }
656
667
 
657
668
  // URL validation passed — acquire the CDP client.
658
- const acquired = acquireCdpClientWithMode(input, context);
669
+ const acquired = await acquireCdpClientWithMode(input, context);
659
670
  if (acquired.errorResult) return acquired.errorResult;
660
671
  const { cdp, browserMode } = acquired;
661
672
 
@@ -1093,7 +1104,7 @@ export async function executeBrowserNavigate(
1093
1104
  }
1094
1105
  } else {
1095
1106
  // Login / 2FA / OAuth - the agent should handle these itself
1096
- // using browser operations + credential_store. Don't hand off.
1107
+ // using browser operations + stored credentials. Don't hand off.
1097
1108
  lines.push("");
1098
1109
  lines.push(formatAuthChallenge(challenge));
1099
1110
  lines.push("");
@@ -1157,7 +1168,7 @@ export async function executeBrowserSnapshot(
1157
1168
  _input: Record<string, unknown>,
1158
1169
  context: ToolContext,
1159
1170
  ): Promise<ToolExecutionResult> {
1160
- const acquired = acquireCdpClientWithMode(_input, context);
1171
+ const acquired = await acquireCdpClientWithMode(_input, context);
1161
1172
  if (acquired.errorResult) return acquired.errorResult;
1162
1173
  const { cdp, browserMode } = acquired;
1163
1174
 
@@ -1209,7 +1220,7 @@ export async function executeBrowserScreenshot(
1209
1220
  input: Record<string, unknown>,
1210
1221
  context: ToolContext,
1211
1222
  ): Promise<ToolExecutionResult> {
1212
- const acquired = acquireCdpClientWithMode(input, context);
1223
+ const acquired = await acquireCdpClientWithMode(input, context);
1213
1224
  if (acquired.errorResult) return acquired.errorResult;
1214
1225
  const { cdp, browserMode } = acquired;
1215
1226
  const fullPage = input.full_page === true;
@@ -1266,7 +1277,7 @@ export async function executeBrowserAttach(
1266
1277
  _input: Record<string, unknown>,
1267
1278
  context: ToolContext,
1268
1279
  ): Promise<ToolExecutionResult> {
1269
- const acquired = acquireCdpClientWithMode(_input, context);
1280
+ const acquired = await acquireCdpClientWithMode(_input, context);
1270
1281
  if (acquired.errorResult) return acquired.errorResult;
1271
1282
  const cdp = acquired.cdp;
1272
1283
  try {
@@ -1319,7 +1330,7 @@ export async function executeBrowserDetach(
1319
1330
  _input: Record<string, unknown>,
1320
1331
  context: ToolContext,
1321
1332
  ): Promise<ToolExecutionResult> {
1322
- const acquired = acquireCdpClientWithMode(_input, context);
1333
+ const acquired = await acquireCdpClientWithMode(_input, context);
1323
1334
  if (acquired.errorResult) return acquired.errorResult;
1324
1335
  const cdp = acquired.cdp;
1325
1336
  try {
@@ -1369,7 +1380,7 @@ export async function executeBrowserClose(
1369
1380
  input: Record<string, unknown>,
1370
1381
  context: ToolContext,
1371
1382
  ): Promise<ToolExecutionResult> {
1372
- const acquired = acquireCdpClientWithMode(input, context);
1383
+ const acquired = await acquireCdpClientWithMode(input, context);
1373
1384
  if (acquired.errorResult) return acquired.errorResult;
1374
1385
  const cdp = acquired.cdp;
1375
1386
  try {
@@ -1440,7 +1451,7 @@ export async function executeBrowserClick(
1440
1451
  const { resolved, error } = resolveElement(context.conversationId, input);
1441
1452
  if (error) return { content: error, isError: true };
1442
1453
 
1443
- const acquired = acquireCdpClientWithMode(input, context);
1454
+ const acquired = await acquireCdpClientWithMode(input, context);
1444
1455
  if (acquired.errorResult) return acquired.errorResult;
1445
1456
  const cdp = acquired.cdp;
1446
1457
  try {
@@ -1561,7 +1572,7 @@ export async function executeBrowserType(
1561
1572
  ? `element_id "${resolved!.eid}"`
1562
1573
  : resolved!.selector;
1563
1574
 
1564
- const acquired = acquireCdpClientWithMode(input, context);
1575
+ const acquired = await acquireCdpClientWithMode(input, context);
1565
1576
  if (acquired.errorResult) return acquired.errorResult;
1566
1577
  const cdp = acquired.cdp;
1567
1578
  try {
@@ -1638,7 +1649,7 @@ export async function executeBrowserPressKey(
1638
1649
  : resolved!.selector;
1639
1650
  }
1640
1651
 
1641
- const acquired = acquireCdpClientWithMode(input, context);
1652
+ const acquired = await acquireCdpClientWithMode(input, context);
1642
1653
  if (acquired.errorResult) return acquired.errorResult;
1643
1654
  const cdp = acquired.cdp;
1644
1655
  try {
@@ -1715,7 +1726,7 @@ export async function executeBrowserScroll(
1715
1726
  break;
1716
1727
  }
1717
1728
 
1718
- const acquired = acquireCdpClientWithMode(input, context);
1729
+ const acquired = await acquireCdpClientWithMode(input, context);
1719
1730
  if (acquired.errorResult) return acquired.errorResult;
1720
1731
  const cdp = acquired.cdp;
1721
1732
  try {
@@ -1778,7 +1789,7 @@ export async function executeBrowserSelectOption(
1778
1789
  ? `element_id "${resolved!.eid}"`
1779
1790
  : resolved!.selector;
1780
1791
 
1781
- const acquired = acquireCdpClientWithMode(input, context);
1792
+ const acquired = await acquireCdpClientWithMode(input, context);
1782
1793
  if (acquired.errorResult) return acquired.errorResult;
1783
1794
  const cdp = acquired.cdp;
1784
1795
  try {
@@ -1894,7 +1905,7 @@ export async function executeBrowserHover(
1894
1905
  const { resolved, error } = resolveElement(context.conversationId, input);
1895
1906
  if (error) return { content: error, isError: true };
1896
1907
 
1897
- const acquired = acquireCdpClientWithMode(input, context);
1908
+ const acquired = await acquireCdpClientWithMode(input, context);
1898
1909
  if (acquired.errorResult) return acquired.errorResult;
1899
1910
  const cdp = acquired.cdp;
1900
1911
  try {
@@ -1988,7 +1999,7 @@ export async function executeBrowserWaitFor(
1988
1999
  return { content: `Waited ${waitMs}ms.`, isError: false };
1989
2000
  }
1990
2001
 
1991
- const acquired = acquireCdpClientWithMode(input, context);
2002
+ const acquired = await acquireCdpClientWithMode(input, context);
1992
2003
  if (acquired.errorResult) return acquired.errorResult;
1993
2004
  const cdp = acquired.cdp;
1994
2005
  try {
@@ -2037,7 +2048,7 @@ export async function executeBrowserExtract(
2037
2048
  ): Promise<ToolExecutionResult> {
2038
2049
  const includeLinks = input.include_links === true;
2039
2050
 
2040
- const acquired = acquireCdpClientWithMode(input, context);
2051
+ const acquired = await acquireCdpClientWithMode(input, context);
2041
2052
  if (acquired.errorResult) return acquired.errorResult;
2042
2053
  const cdp = acquired.cdp;
2043
2054
  try {
@@ -2120,7 +2131,7 @@ export async function executeBrowserFillCredential(
2120
2131
  ? `element_id "${resolved!.eid}"`
2121
2132
  : resolved!.selector;
2122
2133
 
2123
- const acquired = acquireCdpClientWithMode(input, context);
2134
+ const acquired = await acquireCdpClientWithMode(input, context);
2124
2135
  if (acquired.errorResult) return acquired.errorResult;
2125
2136
  const cdp = acquired.cdp;
2126
2137
  try {
@@ -13,12 +13,11 @@ import {
13
13
  searchDocumentsByTitle,
14
14
  updateDocumentContent,
15
15
  } from "../../documents/document-store.js";
16
+ import { canActOnPrivilegedDocuments } from "../../runtime/effective-capabilities.js";
16
17
  import type { ToolContext, ToolExecutionResult } from "../types.js";
17
18
 
18
19
  function isPrivilegedDocumentActor(context: ToolContext): boolean {
19
- return (
20
- context.trustClass === "guardian" || context.executionChannel === "vellum"
21
- );
20
+ return canActOnPrivilegedDocuments(context);
22
21
  }
23
22
 
24
23
  export function documentNotFound(surfaceId: string): ToolExecutionResult {
@@ -5,7 +5,7 @@ import { bridgeCesApproval } from "../credential-execution/approval-bridge.js";
5
5
  import { isCesShellLockdownEnabled } from "../credential-execution/feature-gates.js";
6
6
  import { PermissionPrompter } from "../permissions/prompter.js";
7
7
  import { RiskLevel } from "../permissions/types.js";
8
- import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
8
+ import { isUntrustedShellLockdownActive } from "../runtime/effective-capabilities.js";
9
9
  import { redactSensitiveFields } from "../security/redaction.js";
10
10
  import { getCesClient } from "../security/secure-keys.js";
11
11
  import { TokenExpiredError } from "../security/token-manager.js";
@@ -128,8 +128,10 @@ export class ToolExecutor {
128
128
  // skip this assignment entirely - defeating the lockdown.
129
129
  if (
130
130
  name === "host_bash" &&
131
- isCesShellLockdownEnabled(getConfig()) &&
132
- isUntrustedTrustClass(context.trustClass)
131
+ isUntrustedShellLockdownActive({
132
+ trustClass: context.trustClass,
133
+ lockdownEnabled: isCesShellLockdownEnabled(getConfig()),
134
+ })
133
135
  ) {
134
136
  context.forcePromptSideEffects = true;
135
137
  }
@@ -23,9 +23,9 @@ import { getConfig } from "../../config/loader.js";
23
23
  import { isCesShellLockdownEnabled } from "../../credential-execution/feature-gates.js";
24
24
  import { HostBashProxy } from "../../daemon/host-bash-proxy.js";
25
25
  import { RiskLevel } from "../../permissions/types.js";
26
- import { isUntrustedTrustClass } from "../../runtime/actor-trust-resolver.js";
27
26
  import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
28
27
  import { assistantEventHub } from "../../runtime/assistant-event-hub.js";
28
+ import { isUntrustedShellLockdownActive } from "../../runtime/effective-capabilities.js";
29
29
  import { redactSecrets } from "../../security/secret-scanner.js";
30
30
  import { getLogger } from "../../util/logger.js";
31
31
  import {
@@ -204,9 +204,10 @@ export const hostShellTool = {
204
204
  // NOTE: forcePromptSideEffects is set in executor.ts BEFORE the
205
205
  // permission check runs, not here. Setting it here would be too late
206
206
  // because execute() is called after permissions have already been evaluated.
207
- const hostLockdownActive =
208
- isCesShellLockdownEnabled(config) &&
209
- isUntrustedTrustClass(context.trustClass);
207
+ const hostLockdownActive = isUntrustedShellLockdownActive({
208
+ trustClass: context.trustClass,
209
+ lockdownEnabled: isCesShellLockdownEnabled(config),
210
+ });
210
211
 
211
212
  // Guard: non-host-proxy interfaces need an explicit target when multiple
212
213
  // capable clients are connected to avoid ambiguous untargeted broadcasts.
@@ -10,7 +10,7 @@ import {
10
10
  graphRememberDefinition,
11
11
  } from "../../memory/graph/tools.js";
12
12
  import { RiskLevel } from "../../permissions/types.js";
13
- import { isUntrustedTrustClass } from "../../runtime/actor-trust-resolver.js";
13
+ import { resolveCapabilities } from "../../runtime/capabilities.js";
14
14
  import type {
15
15
  ToolContext,
16
16
  ToolDefinition,
@@ -60,7 +60,7 @@ export const recallTool = {
60
60
  input: Record<string, unknown>,
61
61
  context: ToolContext,
62
62
  ): Promise<ToolExecutionResult> {
63
- if (isUntrustedTrustClass(context.trustClass)) {
63
+ if (!resolveCapabilities(context.trustClass).canAccessMemory) {
64
64
  return {
65
65
  content:
66
66
  "Recall is only available to the guardian because it can read sensitive local context.",