@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
@@ -36,10 +36,6 @@ import {
36
36
  import * as approvalMessageComposer from "../runtime/approval-message-composer.js";
37
37
  import * as gatewayClient from "../runtime/gateway-client.js";
38
38
  import * as pendingInteractions from "../runtime/pending-interactions.js";
39
- import {
40
- _clearApprovalPromptTsTrackerForTesting,
41
- trackApprovalPromptTs,
42
- } from "../runtime/routes/approval-prompt-ts-tracker.js";
43
39
  import { handleApprovalInterception } from "../runtime/routes/guardian-approval-interception.js";
44
40
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
45
41
 
@@ -69,7 +65,6 @@ function resetTables(): void {
69
65
  /* tables may not exist yet */
70
66
  }
71
67
  pendingInteractions.clear();
72
- _clearApprovalPromptTsTrackerForTesting();
73
68
  }
74
69
 
75
70
  function createTestGuardianApproval(
@@ -224,36 +219,9 @@ describe("guardian grant minting on tool-approval decisions", () => {
224
219
  composeSpy.mockRestore();
225
220
  });
226
221
 
227
- test("guardian reaction white_check_mark maps to approve_once (legacy compat)", async () => {
228
- const requestId = "req-grant-reaction-1";
229
- createTestGuardianApproval(requestId, {
230
- conversationId: CONVERSATION_ID,
231
- channel: "slack",
232
- guardianChatId: GUARDIAN_CHAT,
233
- });
234
- registerPendingInteraction(requestId, CONVERSATION_ID, TOOL_NAME);
235
- const approvalMessageTs = "1700000000.000100";
236
- trackApprovalPromptTs("slack", GUARDIAN_CHAT, approvalMessageTs);
237
-
238
- const result = await handleApprovalInterception({
239
- conversationId: "guardian-conv-1",
240
- callbackData: "reaction:white_check_mark",
241
- content: "",
242
- conversationExternalId: GUARDIAN_CHAT,
243
- sourceChannel: "slack",
244
- actorExternalId: GUARDIAN_USER,
245
- replyCallbackUrl: "https://gateway.test/deliver",
246
- trustCtx: makeTrustContext(),
247
- assistantId: ASSISTANT_ID,
248
- approvalMessageTs,
249
- });
250
-
251
- // white_check_mark is mapped to approve_once (backward compat) — the
252
- // pending approval is resolved and a grant is minted.
253
- expect(result.handled).toBe(true);
254
- expect(result.type).toBe("guardian_decision_applied");
255
- expect(countGrants()).toBe(1);
256
- });
222
+ // Reaction-based approvals are exercised against the canonical pipeline in
223
+ // slack-reaction-canonical-approval.test.ts — reactions route through
224
+ // routeGuardianReply, not handleApprovalInterception.
257
225
 
258
226
  // ── 2. approve_once for non-tool-approval does NOT mint a grant ──
259
227
 
@@ -828,7 +828,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
828
828
  replyCtx({
829
829
  messageText: "approve",
830
830
  conversationId: "conv-guardian-conversation",
831
- pendingRequestIds: [req.id],
831
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
832
832
  approvalConversationGenerator: undefined,
833
833
  }),
834
834
  );
@@ -857,7 +857,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
857
857
  replyCtx({
858
858
  messageText: "ok, what is this for?",
859
859
  conversationId: "conv-guardian-conversation",
860
- pendingRequestIds: [req.id],
860
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
861
861
  approvalConversationGenerator: undefined,
862
862
  }),
863
863
  );
@@ -870,7 +870,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
870
870
  expect(unchanged!.status).toBe("pending");
871
871
  });
872
872
 
873
- test("explicit empty pendingRequestIds hint stays fail-closed for desktop actors", async () => {
873
+ test("explicit blocked scope stays fail-closed for desktop actors", async () => {
874
874
  createCanonicalGuardianRequest({
875
875
  kind: "tool_approval",
876
876
  sourceType: "channel",
@@ -887,7 +887,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
887
887
  messageText: "approve",
888
888
  actor: trustedActor(),
889
889
  conversationId: "conv-unrelated",
890
- pendingRequestIds: [],
890
+ pendingScope: { mode: "blocked" },
891
891
  approvalConversationGenerator: undefined,
892
892
  }),
893
893
  );
@@ -897,6 +897,38 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
897
897
  expect(result.decisionApplied).toBe(false);
898
898
  });
899
899
 
900
+ test("explicit request code still resolves under a blocked scope (cross-chat carve-out)", async () => {
901
+ // The Slack cross-chat guard blocks identity fallback, but an explicit
902
+ // request code carries its own target and must still resolve — otherwise a
903
+ // guardian could not approve-by-code from a chat where no card was delivered.
904
+ const req = createCanonicalGuardianRequest({
905
+ kind: "tool_approval",
906
+ sourceType: "channel",
907
+ conversationId: "conv-other",
908
+ guardianExternalUserId: "guardian-1",
909
+ guardianPrincipalId: TEST_PRINCIPAL_ID,
910
+ requestCode: "ABC123",
911
+ toolName: "shell",
912
+ expiresAt: Date.now() + 60_000,
913
+ });
914
+ registerPendingToolApprovalInteraction(req.id, "conv-other", "shell");
915
+
916
+ const result = await routeGuardianReply(
917
+ replyCtx({
918
+ messageText: "ABC123 approve",
919
+ actor: trustedActor(),
920
+ conversationId: "conv-unrelated",
921
+ pendingScope: { mode: "blocked" },
922
+ approvalConversationGenerator: undefined,
923
+ }),
924
+ );
925
+
926
+ expect(result.consumed).toBe(true);
927
+ expect(result.requestId).toBe(req.id);
928
+ expect(result.decisionApplied).toBe(true);
929
+ expect(getCanonicalGuardianRequest(req.id)!.status).toBe("approved");
930
+ });
931
+
900
932
  test("multiple hinted pending requests with plain-text approve returns disambiguation", async () => {
901
933
  const req1 = createCanonicalGuardianRequest({
902
934
  kind: "tool_approval",
@@ -924,7 +956,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
924
956
  replyCtx({
925
957
  messageText: "approve",
926
958
  conversationId: "conv-guardian-conversation",
927
- pendingRequestIds: [req1.id, req2.id],
959
+ pendingScope: { mode: "scoped", requestIds: [req1.id, req2.id] },
928
960
  approvalConversationGenerator: undefined,
929
961
  }),
930
962
  );
@@ -977,7 +1009,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
977
1009
  replyCtx({
978
1010
  messageText: "yes approve it",
979
1011
  conversationId: "conv-1",
980
- pendingRequestIds: [req1.id, req2.id],
1012
+ pendingScope: { mode: "scoped", requestIds: [req1.id, req2.id] },
981
1013
  approvalConversationGenerator: mockGenerator as any,
982
1014
  }),
983
1015
  );
@@ -1031,7 +1063,10 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
1031
1063
  replyCtx({
1032
1064
  messageText: "approve",
1033
1065
  conversationId: "conv-guardian-conversation",
1034
- pendingRequestIds: [answerRequest.id, approvalRequest.id],
1066
+ pendingScope: {
1067
+ mode: "scoped",
1068
+ requestIds: [answerRequest.id, approvalRequest.id],
1069
+ },
1035
1070
  approvalConversationGenerator: undefined,
1036
1071
  }),
1037
1072
  );
@@ -1075,7 +1110,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
1075
1110
  replyCtx({
1076
1111
  messageText: "yes",
1077
1112
  conversationId: "conv-1",
1078
- pendingRequestIds: [req.id],
1113
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1079
1114
  approvalConversationGenerator: mockGenerator as any,
1080
1115
  }),
1081
1116
  );
@@ -1104,7 +1139,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
1104
1139
  replyCtx({
1105
1140
  messageText: "go for it",
1106
1141
  conversationId: "conv-1",
1107
- pendingRequestIds: [req.id],
1142
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1108
1143
  approvalConversationGenerator: undefined,
1109
1144
  }),
1110
1145
  );
@@ -1117,7 +1152,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
1117
1152
  expect(resolved!.status).toBe("approved");
1118
1153
  });
1119
1154
 
1120
- test("code-based routing is constrained to caller-provided pendingRequestIds scope", async () => {
1155
+ test("code-based routing is constrained to caller-provided scope", async () => {
1121
1156
  const inScope = createCanonicalGuardianRequest({
1122
1157
  kind: "tool_approval",
1123
1158
  sourceType: "channel",
@@ -1145,7 +1180,7 @@ describe("routing invariant: disambiguation stays fail-closed", () => {
1145
1180
  replyCtx({
1146
1181
  messageText: "222BBB approve",
1147
1182
  conversationId: "conv-guardian-conversation",
1148
- pendingRequestIds: [inScope.id],
1183
+ pendingScope: { mode: "scoped", requestIds: [inScope.id] },
1149
1184
  approvalConversationGenerator: undefined,
1150
1185
  }),
1151
1186
  );
@@ -1438,7 +1473,7 @@ describe("routing invariant: directResolve interactions resolve via guardian dec
1438
1473
  describe("routing invariant: destination hints do not bypass tool_approval principal binding", () => {
1439
1474
  beforeEach(() => resetTables());
1440
1475
 
1441
- test("explicit pendingRequestIds still fail closed when guardianPrincipalId does not match", async () => {
1476
+ test("explicit scope still fails closed when guardianPrincipalId does not match", async () => {
1442
1477
  // Voice-originated tool approval with a different principal than the actor.
1443
1478
  const req = createCanonicalGuardianRequest({
1444
1479
  kind: "tool_approval",
@@ -1452,15 +1487,15 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
1452
1487
  });
1453
1488
  registerPendingToolApprovalInteraction(req.id, "conv-voice-1", "shell");
1454
1489
 
1455
- // The channel inbound router would compute pendingRequestIds from
1456
- // delivery-scoped lookup and pass them here. Simulate that.
1490
+ // The channel inbound router would compute the pending scope from
1491
+ // delivery-scoped lookup and pass it here. Simulate that.
1457
1492
  const result = await routeGuardianReply(
1458
1493
  replyCtx({
1459
1494
  messageText: "approve",
1460
1495
  channel: "telegram",
1461
1496
  actor: guardianActor({ guardianPrincipalId: "different-principal" }),
1462
1497
  conversationId: "conv-guardian-chat",
1463
- pendingRequestIds: [req.id],
1498
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1464
1499
  approvalConversationGenerator: undefined,
1465
1500
  }),
1466
1501
  );
@@ -1486,7 +1521,7 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
1486
1521
  expiresAt: Date.now() + 60_000,
1487
1522
  });
1488
1523
 
1489
- // No pendingRequestIds passed — identity-based fallback uses
1524
+ // No pendingScope passed — identity-based fallback uses
1490
1525
  // actor.actorExternalUserId which does not match any request's
1491
1526
  // guardianExternalUserId (since it's null).
1492
1527
  const result = await routeGuardianReply(
@@ -1495,7 +1530,7 @@ describe("routing invariant: destination hints do not bypass tool_approval princ
1495
1530
  channel: "telegram",
1496
1531
  actor: guardianActor({ actorExternalUserId: "guardian-tg-user" }),
1497
1532
  conversationId: "conv-guardian-chat",
1498
- // pendingRequestIds: undefined — no delivery hints
1533
+ // pendingScope omitted — no delivery hints
1499
1534
  approvalConversationGenerator: undefined,
1500
1535
  }),
1501
1536
  );
@@ -1534,7 +1569,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1534
1569
  replyCtx({
1535
1570
  messageText: "open invite flow",
1536
1571
  conversationId: "conv-guardian-conversation",
1537
- pendingRequestIds: [req.id],
1572
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1538
1573
  approvalConversationGenerator: undefined,
1539
1574
  }),
1540
1575
  );
@@ -1569,7 +1604,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1569
1604
  replyCtx({
1570
1605
  messageText: phrase,
1571
1606
  conversationId: "conv-test",
1572
- pendingRequestIds: [req.id],
1607
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1573
1608
  approvalConversationGenerator: undefined,
1574
1609
  }),
1575
1610
  );
@@ -1595,7 +1630,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1595
1630
  replyCtx({
1596
1631
  messageText: "open invite flow",
1597
1632
  conversationId: "conv-guardian-conversation",
1598
- pendingRequestIds: [req.id],
1633
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1599
1634
  approvalConversationGenerator: undefined,
1600
1635
  }),
1601
1636
  );
@@ -1625,7 +1660,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1625
1660
  replyCtx({
1626
1661
  messageText: "A00B01 approve",
1627
1662
  conversationId: "conv-guardian-conversation",
1628
- pendingRequestIds: [req.id],
1663
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1629
1664
  approvalConversationGenerator: undefined,
1630
1665
  }),
1631
1666
  );
@@ -1655,7 +1690,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1655
1690
  channel: "vellum",
1656
1691
  actor: trustedActor({ channel: "vellum" }),
1657
1692
  conversationId: "conv-guardian-conversation",
1658
- pendingRequestIds: [req.id],
1693
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1659
1694
  approvalConversationGenerator: undefined,
1660
1695
  });
1661
1696
 
@@ -1694,7 +1729,7 @@ describe("routing invariant: invite handoff bypass for access requests", () => {
1694
1729
  channel: "vellum",
1695
1730
  actor: trustedActor({ channel: "vellum" }),
1696
1731
  conversationId: "conv-guardian-conversation",
1697
- pendingRequestIds: [req.id],
1732
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1698
1733
  approvalConversationGenerator: approvalConversationGenerator as any,
1699
1734
  });
1700
1735
 
@@ -1746,7 +1781,7 @@ describe("routing invariant: expired requests are excluded from pending discover
1746
1781
  replyCtx({
1747
1782
  messageText: "approve",
1748
1783
  conversationId: "conv-guardian-conversation",
1749
- pendingRequestIds: [expired.id, active.id],
1784
+ pendingScope: { mode: "scoped", requestIds: [expired.id, active.id] },
1750
1785
  approvalConversationGenerator: undefined,
1751
1786
  }),
1752
1787
  );
@@ -1781,7 +1816,7 @@ describe("routing invariant: expired requests are excluded from pending discover
1781
1816
  replyCtx({
1782
1817
  messageText: "`approve`",
1783
1818
  conversationId: "conv-guardian-conversation",
1784
- pendingRequestIds: [req.id],
1819
+ pendingScope: { mode: "scoped", requestIds: [req.id] },
1785
1820
  approvalConversationGenerator: undefined,
1786
1821
  }),
1787
1822
  );
@@ -1821,7 +1856,10 @@ describe("routing invariant: expired requests are excluded from pending discover
1821
1856
  replyCtx({
1822
1857
  messageText: "approve",
1823
1858
  conversationId: "conv-guardian-conversation",
1824
- pendingRequestIds: [expired1.id, expired2.id],
1859
+ pendingScope: {
1860
+ mode: "scoped",
1861
+ requestIds: [expired1.id, expired2.id],
1862
+ },
1825
1863
  approvalConversationGenerator: undefined,
1826
1864
  }),
1827
1865
  );
@@ -162,7 +162,6 @@ describe("inbound-message-handler trusted-contact interactivity", () => {
162
162
  {
163
163
  type: "telegram",
164
164
  address: "telegram-user-default",
165
- externalUserId: "telegram-user-default",
166
165
  status: "active",
167
166
  policy: "allow",
168
167
  },
@@ -75,6 +75,16 @@ mock.module("../tools/browser/cdp-client/factory.js", () => ({
75
75
  },
76
76
  }));
77
77
 
78
+ // The real proxy is an always-present singleton; stub the grace wait so
79
+ // extension-pinned acquisition doesn't poll for the full window here.
80
+ mock.module("../daemon/host-browser-proxy.js", () => ({
81
+ HostBrowserProxy: {
82
+ get instance() {
83
+ return { waitForExtensionClient: async () => true };
84
+ },
85
+ },
86
+ }));
87
+
78
88
  // ── Minimal browserManager stub ──────────────────────────────────────
79
89
 
80
90
  /** Mutable memo shared between mock methods and tests. */
@@ -58,9 +58,14 @@ let mockExtensionAvailable = false;
58
58
  mock.module("../daemon/host-browser-proxy.js", () => ({
59
59
  HostBrowserProxy: {
60
60
  get instance() {
61
- return mockExtensionAvailable
62
- ? { isAvailable: () => true, request: mock(async () => ({})) }
63
- : undefined;
61
+ // The real proxy is a lazily-created singleton that always exists;
62
+ // `isAvailable`/`waitForExtensionClient` reflect whether an extension
63
+ // is connected rather than the instance being absent.
64
+ return {
65
+ isAvailable: () => mockExtensionAvailable,
66
+ waitForExtensionClient: async () => mockExtensionAvailable,
67
+ request: mock(async () => ({})),
68
+ };
64
69
  },
65
70
  },
66
71
  }));
@@ -55,7 +55,6 @@ export function createGuardianBinding(params: {
55
55
  {
56
56
  type: params.channel,
57
57
  address: canonicalId,
58
- externalUserId: canonicalId,
59
58
  externalChatId: params.guardianDeliveryChatId,
60
59
  status: "active",
61
60
  verifiedAt: Date.now(),
@@ -46,6 +46,8 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
46
46
  mockClients.filter((c) => c.capabilities.includes(cap)),
47
47
  listClientsByInterface: (interfaceId: string) =>
48
48
  mockClients.filter((c) => c.interfaceId === interfaceId),
49
+ getClientById: (clientId: string) =>
50
+ mockClients.find((c) => c.clientId === clientId),
49
51
  getActorPrincipalIdForClient: (clientId: string) =>
50
52
  mockClients.find((c) => c.clientId === clientId)?.actorPrincipalId,
51
53
  },
@@ -108,6 +110,91 @@ describe("HostBrowserProxy", () => {
108
110
  pendingInteractions.clear();
109
111
  });
110
112
 
113
+ describe("waitForExtensionClient", () => {
114
+ const EXTENSION: MockClient = {
115
+ clientId: "ext-client",
116
+ interfaceId: "chrome-extension",
117
+ capabilities: ["host_browser"],
118
+ };
119
+
120
+ test("returns true immediately when an extension is connected", async () => {
121
+ mockClients = [EXTENSION];
122
+ expect(await proxy.waitForExtensionClient(undefined, undefined, 1_000)).toBe(
123
+ true,
124
+ );
125
+ });
126
+
127
+ test("returns false after the timeout when none connects", async () => {
128
+ mockClients = [DEFAULT_CLIENT]; // macos only, no extension
129
+ expect(await proxy.waitForExtensionClient(undefined, undefined, 100)).toBe(
130
+ false,
131
+ );
132
+ });
133
+
134
+ test("returns true when an extension appears within the window", async () => {
135
+ mockClients = [DEFAULT_CLIENT];
136
+ setTimeout(() => {
137
+ mockClients = [DEFAULT_CLIENT, EXTENSION];
138
+ }, 50);
139
+ expect(await proxy.waitForExtensionClient(undefined, undefined, 2_000)).toBe(
140
+ true,
141
+ );
142
+ });
143
+
144
+ test("respects sourceActorPrincipalId", async () => {
145
+ mockClients = [
146
+ {
147
+ clientId: "other-actor-ext",
148
+ interfaceId: "chrome-extension",
149
+ actorPrincipalId: "actor-b",
150
+ capabilities: ["host_browser"],
151
+ },
152
+ ];
153
+ // Caller is actor-a; the connected extension belongs to actor-b.
154
+ expect(await proxy.waitForExtensionClient("actor-a", undefined, 100)).toBe(
155
+ false,
156
+ );
157
+ });
158
+
159
+ test("with a targetClientId, waits for that exact client (not a sibling)", async () => {
160
+ // A sibling extension is connected, but the targeted client is not —
161
+ // the wait must not return early on the sibling's presence.
162
+ mockClients = [
163
+ {
164
+ clientId: "sibling-ext",
165
+ interfaceId: "chrome-extension",
166
+ capabilities: ["host_browser"],
167
+ },
168
+ ];
169
+ expect(
170
+ await proxy.waitForExtensionClient(undefined, "target-ext", 100),
171
+ ).toBe(false);
172
+ });
173
+
174
+ test("with a targetClientId, returns true once that client appears", async () => {
175
+ mockClients = [
176
+ {
177
+ clientId: "sibling-ext",
178
+ interfaceId: "chrome-extension",
179
+ capabilities: ["host_browser"],
180
+ },
181
+ ];
182
+ setTimeout(() => {
183
+ mockClients = [
184
+ ...mockClients,
185
+ {
186
+ clientId: "target-ext",
187
+ interfaceId: "chrome-extension",
188
+ capabilities: ["host_browser"],
189
+ },
190
+ ];
191
+ }, 50);
192
+ expect(
193
+ await proxy.waitForExtensionClient(undefined, "target-ext", 2_000),
194
+ ).toBe(true);
195
+ });
196
+ });
197
+
111
198
  describe("request/resolve lifecycle (happy path)", () => {
112
199
  test("sends host_browser_request and resolves with content", async () => {
113
200
  const resultPromise = proxy.request(