@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
@@ -28,6 +28,33 @@ import type { Message } from "../providers/types.js";
28
28
  */
29
29
  export type InjectionMatcher = string | { prefix: string; suffix: string };
30
30
 
31
+ /**
32
+ * Whether an injected text block matches any of the given matchers. A plain
33
+ * string matches by prefix; a `{ prefix, suffix }` wrapper requires both ends
34
+ * so user-authored text merely opening with an injection-like tag is kept.
35
+ */
36
+ function textBlockMatchesInjection(
37
+ text: string,
38
+ matchers: InjectionMatcher[],
39
+ ): boolean {
40
+ return matchers.some((m) =>
41
+ typeof m === "string"
42
+ ? text.startsWith(m)
43
+ : text.startsWith(m.prefix) && text.endsWith(m.suffix),
44
+ );
45
+ }
46
+
47
+ /** Drop the matching injection text blocks from a single content array. */
48
+ function filterInjectionBlocks(
49
+ content: Message["content"],
50
+ matchers: InjectionMatcher[],
51
+ ): Message["content"] {
52
+ return content.filter(
53
+ (block) =>
54
+ block.type !== "text" || !textBlockMatchesInjection(block.text, matchers),
55
+ );
56
+ }
57
+
31
58
  /**
32
59
  * Remove text blocks from user messages that match any of the given matchers.
33
60
  * If stripping removes all content blocks from a message, the message itself
@@ -43,14 +70,7 @@ export function stripUserTextBlocksByPrefix(
43
70
  return messages
44
71
  .map((message) => {
45
72
  if (message.role !== "user") return message;
46
- const nextContent = message.content.filter((block) => {
47
- if (block.type !== "text") return true;
48
- return !matchers.some((m) =>
49
- typeof m === "string"
50
- ? block.text.startsWith(m)
51
- : block.text.startsWith(m.prefix) && block.text.endsWith(m.suffix),
52
- );
53
- });
73
+ const nextContent = filterInjectionBlocks(message.content, matchers);
54
74
  if (nextContent.length === message.content.length) return message;
55
75
  if (nextContent.length === 0) return null;
56
76
  return { ...message, content: nextContent };
@@ -60,6 +80,30 @@ export function stripUserTextBlocksByPrefix(
60
80
  );
61
81
  }
62
82
 
83
+ /**
84
+ * Tail-scoped variant of {@link stripUserTextBlocksByPrefix}: remove matching
85
+ * text blocks from ONLY the last message, and only when that message is a user
86
+ * message. This mirrors the runtime injection step, which applies every
87
+ * per-turn block to the tail user message, so clearing the tail's stale copies
88
+ * before re-injection makes re-injection idempotent.
89
+ *
90
+ * The tail message is retained even if all of its blocks are removed, so the
91
+ * role-`"user"` tail invariant the re-injection step relies on is preserved.
92
+ * Earlier messages are untouched, so per-message historical grounding (e.g.
93
+ * each turn's `<turn_context>`) survives and the stable cached prefix — which
94
+ * lives entirely before the tail — is never disturbed.
95
+ */
96
+ export function stripTailUserTextBlocksByPrefix(
97
+ messages: Message[],
98
+ matchers: InjectionMatcher[],
99
+ ): Message[] {
100
+ const last = messages[messages.length - 1];
101
+ if (!last || last.role !== "user") return messages;
102
+ const nextContent = filterInjectionBlocks(last.content, matchers);
103
+ if (nextContent.length === last.content.length) return messages;
104
+ return [...messages.slice(0, -1), { ...last, content: nextContent }];
105
+ }
106
+
63
107
  /**
64
108
  * Full-wrapper matcher for the memory-v3 ephemeral `<memory_spotlight>` block.
65
109
  * Shared by the per-turn scoped strip ({@link stripSpotlightInjections}) and
@@ -88,8 +132,12 @@ export const NOW_SCRATCHPAD_STRIP_PREFIXES: InjectionMatcher[] = [
88
132
  "<now_scratchpad>",
89
133
  ];
90
134
 
91
- /** Matchers stripped by the pipeline (order doesn't matter — single pass). */
92
- const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
135
+ /**
136
+ * Matchers stripped by the compaction pipeline (order doesn't matter — single
137
+ * pass). Exported so the post-compaction re-injection path can reuse this set
138
+ * as the base of its idempotency strip without duplicating it.
139
+ */
140
+ export const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
93
141
  "<channel_capabilities>",
94
142
  "<channel_command_context>",
95
143
  "<disk_pressure_warning>",
@@ -305,7 +305,7 @@ export function estimateMessagesTokens(
305
305
  }
306
306
 
307
307
  /** Estimate token cost for a single tool definition. */
308
- function estimateToolDefinitionTokens(tool: ToolDefinition): number {
308
+ export function estimateToolDefinitionTokens(tool: ToolDefinition): number {
309
309
  return (
310
310
  TOOL_DEFINITION_OVERHEAD_TOKENS +
311
311
  estimateTextTokens(tool.name) +
@@ -312,6 +312,43 @@ function buildLocalCesEnv(): Record<string, string | undefined> {
312
312
  };
313
313
  }
314
314
 
315
+ // ---------------------------------------------------------------------------
316
+ // Close notification (shared by both transports)
317
+ // ---------------------------------------------------------------------------
318
+
319
+ /**
320
+ * Tracks the transport `alive` state and notifies registered handlers exactly
321
+ * once when the transport dies, so the RPC client can fail-fast any in-flight
322
+ * calls instead of waiting out their timeouts.
323
+ */
324
+ function createCloseNotifier(): {
325
+ isAlive: () => boolean;
326
+ markDead: () => void;
327
+ onClose: (handler: () => void) => void;
328
+ } {
329
+ let alive = true;
330
+ let notified = false;
331
+ const handlers: Array<() => void> = [];
332
+ return {
333
+ isAlive: () => alive,
334
+ markDead() {
335
+ alive = false;
336
+ if (notified) return;
337
+ notified = true;
338
+ for (const handler of handlers) {
339
+ try {
340
+ handler();
341
+ } catch {
342
+ // a close handler must never throw back into the transport
343
+ }
344
+ }
345
+ },
346
+ onClose(handler) {
347
+ handlers.push(handler);
348
+ },
349
+ };
350
+ }
351
+
315
352
  // ---------------------------------------------------------------------------
316
353
  // Stdio transport (local mode)
317
354
  // ---------------------------------------------------------------------------
@@ -319,7 +356,7 @@ function buildLocalCesEnv(): Record<string, string | undefined> {
319
356
  function createStdioTransport(proc: Subprocess): CesTransport {
320
357
  const messageHandlers: Array<(message: string) => void> = [];
321
358
  let buffer = "";
322
- let alive = true;
359
+ const death = createCloseNotifier();
323
360
 
324
361
  // Read stdout line by line — narrow past `number` union arm from Subprocess type
325
362
  if (proc.stdout && typeof proc.stdout !== "number") {
@@ -350,9 +387,9 @@ function createStdioTransport(proc: Subprocess): CesTransport {
350
387
  endReason = "error";
351
388
  readError = err;
352
389
  } finally {
353
- // Preserve existing semantics: the transport reports dead the moment
354
- // the stdout stream ends, regardless of why.
355
- alive = false;
390
+ // Report dead the moment the stdout stream ends (regardless of why),
391
+ // and notify the client so it can fail-fast any in-flight calls.
392
+ death.markDead();
356
393
 
357
394
  // DIAGNOSTIC (observation only). The reconnection path (secure-keys.ts)
358
395
  // bounces CES whenever isAlive() goes false. This classifies WHY the
@@ -384,7 +421,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
384
421
 
385
422
  // Track process exit
386
423
  void proc.exited.then((exitCode) => {
387
- alive = false;
424
+ death.markDead();
388
425
  log.info(
389
426
  { pid: proc.pid, exitCode },
390
427
  "CES stdio transport: process exited",
@@ -393,7 +430,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
393
430
 
394
431
  return {
395
432
  write(line: string): void {
396
- if (!alive || !proc.stdin || typeof proc.stdin === "number") {
433
+ if (!death.isAlive() || !proc.stdin || typeof proc.stdin === "number") {
397
434
  throw new Error("CES stdio transport is not alive");
398
435
  }
399
436
  proc.stdin.write(line + "\n");
@@ -404,11 +441,13 @@ function createStdioTransport(proc: Subprocess): CesTransport {
404
441
  },
405
442
 
406
443
  isAlive(): boolean {
407
- return alive;
444
+ return death.isAlive();
408
445
  },
409
446
 
447
+ onClose: death.onClose,
448
+
410
449
  close(): void {
411
- alive = false;
450
+ death.markDead();
412
451
  if (proc.stdin && typeof proc.stdin !== "number") {
413
452
  proc.stdin.end();
414
453
  }
@@ -423,7 +462,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
423
462
  function createSocketTransport(socket: Socket): CesTransport {
424
463
  const messageHandlers: Array<(message: string) => void> = [];
425
464
  let buffer = "";
426
- let alive = true;
465
+ const death = createCloseNotifier();
427
466
 
428
467
  const decoder = new StringDecoder("utf8");
429
468
 
@@ -442,17 +481,17 @@ function createSocketTransport(socket: Socket): CesTransport {
442
481
  });
443
482
 
444
483
  socket.on("close", () => {
445
- alive = false;
484
+ death.markDead();
446
485
  });
447
486
 
448
487
  socket.on("error", (err) => {
449
488
  log.warn({ err }, "CES socket transport error");
450
- alive = false;
489
+ death.markDead();
451
490
  });
452
491
 
453
492
  return {
454
493
  write(line: string): void {
455
- if (!alive || socket.destroyed) {
494
+ if (!death.isAlive() || socket.destroyed) {
456
495
  throw new Error("CES socket transport is not alive");
457
496
  }
458
497
  socket.write(line + "\n");
@@ -463,11 +502,13 @@ function createSocketTransport(socket: Socket): CesTransport {
463
502
  },
464
503
 
465
504
  isAlive(): boolean {
466
- return alive && !socket.destroyed;
505
+ return death.isAlive() && !socket.destroyed;
467
506
  },
468
507
 
508
+ onClose: death.onClose,
509
+
469
510
  close(): void {
470
- alive = false;
511
+ death.markDead();
471
512
  socket.destroy();
472
513
  },
473
514
  };
@@ -8,9 +8,8 @@
8
8
  * channel actually connects, not just stored;
9
9
  * - any other credential: written to secure storage.
10
10
  *
11
- * Both the `credential_store` tool's prompt action and the CLI
12
- * `credentials prompt` route share this logic so the two entry points behave
13
- * identically.
11
+ * The CLI `credentials prompt` route uses this logic to persist a credential
12
+ * collected through a secure prompt.
14
13
  */
15
14
 
16
15
  import { getConfig } from "../config/loader.js";
@@ -236,8 +236,9 @@ describe("disposeConversation — auto-analysis enqueue", () => {
236
236
 
237
237
  test("untrusted conversation — enqueues neither graph_extract nor conversation_analyze", () => {
238
238
  // `unknown` is the trust class used for untrusted actors. The disposal
239
- // code short-circuits on `isUntrustedTrustClass()` so neither enqueue
240
- // path should fire. This preserves the memory trust boundary.
239
+ // code short-circuits when `resolveCapabilities(trustClass).canAccessMemory`
240
+ // is false, so neither enqueue path should fire. This preserves the
241
+ // memory trust boundary.
241
242
  const ctx = makeDisposeContext({
242
243
  conversationId: "conv-untrusted",
243
244
  trustClass: "unknown",
@@ -178,8 +178,6 @@ export class ConfigWatcher {
178
178
  * Start all file watchers. `onConversationEvict` is called when watched
179
179
  * files change and conversations need to be evicted for reload.
180
180
  * `onIdentityChanged` is called when IDENTITY.md changes on disk.
181
- * `onIdentityIntroChanged` is called when SOUL.md changes and identity
182
- * intro subscribers should refetch.
183
181
  * `onSkillsChanged` is called after skill directory changes evict
184
182
  * conversations.
185
183
  */
@@ -190,7 +188,6 @@ export class ConfigWatcher {
190
188
  onAvatarChanged?: () => void,
191
189
  onConfigChanged?: () => void,
192
190
  onSkillsChanged?: () => void,
193
- onIdentityIntroChanged?: () => void,
194
191
  ): void {
195
192
  // Reset the stopped flag so a stop()→start() cycle on the same
196
193
  // instance resumes hot-reload instead of silently bailing in every
@@ -226,7 +223,6 @@ export class ConfigWatcher {
226
223
  },
227
224
  "SOUL.md": () => {
228
225
  onConversationEvict();
229
- onIdentityIntroChanged?.();
230
226
  },
231
227
  "IDENTITY.md": () => {
232
228
  onConversationEvict();
@@ -1184,6 +1184,7 @@ export async function finalizePendingToolResultRow(
1184
1184
  let provenanceTrustClass:
1185
1185
  | "guardian"
1186
1186
  | "trusted_contact"
1187
+ | "unverified_contact"
1187
1188
  | "unknown"
1188
1189
  | undefined;
1189
1190
  let automated: boolean | undefined;
@@ -1845,6 +1846,7 @@ export async function handleMessageComplete(
1845
1846
  let provenanceTrustClass:
1846
1847
  | "guardian"
1847
1848
  | "trusted_contact"
1849
+ | "unverified_contact"
1848
1850
  | "unknown"
1849
1851
  | undefined;
1850
1852
  let automated: boolean | undefined;
@@ -70,9 +70,9 @@ import type { UserPromptSubmitContext } from "../plugin-api/types.js";
70
70
  import { runHook } from "../plugins/pipeline.js";
71
71
  import type { ContentBlock, Message } from "../providers/types.js";
72
72
  import type { Provider } from "../providers/types.js";
73
- import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
74
73
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
75
74
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
75
+ import { resolveCapabilities } from "../runtime/capabilities.js";
76
76
  import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
77
77
  import type { ActivationMomentParam } from "../telemetry/activation-funnel.js";
78
78
  import type { UsageActor } from "../usage/actors.js";
@@ -187,6 +187,67 @@ export interface AssistantSurface {
187
187
  activationMoment?: ActivationMomentParam;
188
188
  }
189
189
 
190
+ // ── abort watchdog ───────────────────────────────────────────────────
191
+
192
+ /**
193
+ * Generous backstop that drives an aborted turn to its `finally` even if some
194
+ * awaited operation fails to observe the abort signal.
195
+ *
196
+ * Abort is otherwise cooperative and already wired into the slow paths: the
197
+ * provider call forwards the signal to its HTTP/streaming fetch, and tool
198
+ * execution races the signal so a stuck tool can't block cancellation. This
199
+ * watchdog only fires when a future code path silently ignores abort — without
200
+ * it, such a path would hang the loop forever and latch the conversation's
201
+ * `processing` flag true (the wedged "Thinking…" indicator). It is
202
+ * defense-in-depth, not the primary mechanism, so the timeout is deliberately
203
+ * generous; in the common case abort settles in-flight work well before it.
204
+ */
205
+ const ABORT_WATCHDOG_MS = 45_000;
206
+
207
+ /**
208
+ * Race `work` against an abort watchdog. The watchdog stays disarmed until the
209
+ * signal fires; once it does, the turn has `timeoutMs` to settle before the
210
+ * watchdog rejects with the signal's own abort reason so the caller unwinds to
211
+ * its `finally` and the reason classifies as a user cancellation downstream.
212
+ * The abandoned `work` promise keeps running detached — its eventual rejection
213
+ * is swallowed so it can't surface as an unhandled rejection.
214
+ */
215
+ async function withAbortWatchdog<T>(
216
+ work: Promise<T>,
217
+ signal: AbortSignal,
218
+ timeoutMs: number,
219
+ onFire: () => void,
220
+ ): Promise<T> {
221
+ let timer: ReturnType<typeof setTimeout> | undefined;
222
+ let onAbort: (() => void) | undefined;
223
+ const watchdog = new Promise<never>((_, reject) => {
224
+ const arm = () => {
225
+ timer = setTimeout(() => {
226
+ onFire();
227
+ // Propagate the signal's own reason (an `AbortReason` for daemon-owned
228
+ // cancels) so the loop's catch classifies this as a user cancellation;
229
+ // fall back to a tagged AbortError when the signal carries no reason.
230
+ reject(
231
+ signal.reason ??
232
+ new DOMException("The operation was aborted", "AbortError"),
233
+ );
234
+ }, timeoutMs);
235
+ };
236
+ if (signal.aborted) arm();
237
+ else {
238
+ onAbort = arm;
239
+ signal.addEventListener("abort", onAbort, { once: true });
240
+ }
241
+ });
242
+ try {
243
+ return await Promise.race([work, watchdog]);
244
+ } finally {
245
+ if (timer) clearTimeout(timer);
246
+ if (onAbort) signal.removeEventListener("abort", onAbort);
247
+ void work.catch(() => {});
248
+ }
249
+ }
250
+
190
251
  // ── runAgentLoop ─────────────────────────────────────────────────────
191
252
 
192
253
  export async function runAgentLoopImpl(
@@ -215,6 +276,11 @@ export async function runAgentLoopImpl(
215
276
  * spawns).
216
277
  */
217
278
  overrideProfile?: string;
279
+ /**
280
+ * Float `overrideProfile` above call-site layers for non-main-agent call
281
+ * sites. Used when a caller explicitly pins a background run to a profile.
282
+ */
283
+ forceOverrideProfile?: boolean;
218
284
  },
219
285
  ): Promise<void> {
220
286
  if (!ctx.abortController) {
@@ -290,6 +356,7 @@ export async function runAgentLoopImpl(
290
356
  ctx.toolRoutedProfile = undefined;
291
357
 
292
358
  const turnOverrideProfile = userExplicitOverride;
359
+ const forceOverrideProfile = options?.forceOverrideProfile === true;
293
360
 
294
361
  const readCurrentOverrideProfile = (): string | undefined =>
295
362
  options?.overrideProfile ??
@@ -300,6 +367,7 @@ export async function runAgentLoopImpl(
300
367
  llm: config.llm,
301
368
  callSite: turnCallSite,
302
369
  overrideProfile: turnOverrideProfile ?? undefined,
370
+ forceOverrideProfile,
303
371
  selectionSeed: ctx.conversationId,
304
372
  });
305
373
  let currentEffectiveContextWindow: EffectiveContextWindow =
@@ -307,6 +375,7 @@ export async function runAgentLoopImpl(
307
375
  let currentContextWindowConfig = contextWindowConfigFromEffective(
308
376
  resolveCallSiteConfig(turnCallSite, config.llm, {
309
377
  overrideProfile: turnOverrideProfile ?? undefined,
378
+ forceOverrideProfile,
310
379
  selectionSeed: ctx.conversationId,
311
380
  }).contextWindow,
312
381
  currentEffectiveContextWindow,
@@ -322,11 +391,13 @@ export async function runAgentLoopImpl(
322
391
  llm: config.llm,
323
392
  callSite: turnCallSite,
324
393
  overrideProfile: currentOverrideProfile,
394
+ forceOverrideProfile,
325
395
  selectionSeed: ctx.conversationId,
326
396
  });
327
397
  currentContextWindowConfig = contextWindowConfigFromEffective(
328
398
  resolveCallSiteConfig(turnCallSite, config.llm, {
329
399
  overrideProfile: currentOverrideProfile,
400
+ forceOverrideProfile,
330
401
  selectionSeed: ctx.conversationId,
331
402
  }).contextWindow,
332
403
  currentEffectiveContextWindow,
@@ -892,21 +963,36 @@ export async function runAgentLoopImpl(
892
963
  msgs: Message[],
893
964
  compactInPlace = false,
894
965
  ): Promise<Message[]> => {
895
- const { history, exitReason, newMessages } = await ctx.agentLoop.run({
896
- messages: msgs,
897
- onEvent: eventHandler,
898
- signal: abortController.signal,
899
- requestId: reqId,
900
- onCheckpoint,
901
- callSite: turnCallSite,
902
- trust: loopTrust,
903
- overrideProfile: turnOverrideProfile,
904
- resolveOverrideProfile: resolveCurrentOverrideProfile,
905
- resolveContextWindow,
906
- compactInPlace,
907
- isNonInteractive,
908
- modelProfileKey,
909
- });
966
+ const watchdogMs = ctx.abortWatchdogMs ?? ABORT_WATCHDOG_MS;
967
+ const { history, exitReason, newMessages } = await withAbortWatchdog(
968
+ ctx.agentLoop.run({
969
+ messages: msgs,
970
+ onEvent: eventHandler,
971
+ signal: abortController.signal,
972
+ requestId: reqId,
973
+ onCheckpoint,
974
+ callSite: turnCallSite,
975
+ trust: loopTrust,
976
+ overrideProfile: turnOverrideProfile,
977
+ ...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
978
+ resolveOverrideProfile: resolveCurrentOverrideProfile,
979
+ resolveContextWindow,
980
+ compactInPlace,
981
+ isNonInteractive,
982
+ modelProfileKey,
983
+ }),
984
+ abortController.signal,
985
+ watchdogMs,
986
+ () =>
987
+ rlog.error(
988
+ {
989
+ conversationId: ctx.conversationId,
990
+ requestId: reqId,
991
+ timeoutMs: watchdogMs,
992
+ },
993
+ "Abort watchdog fired — agent loop did not settle after cancel; forcing turn to finally",
994
+ ),
995
+ );
910
996
  lastRunNewMessages = newMessages;
911
997
  if (exitReason === "handoff") {
912
998
  yieldedForHandoff = true;
@@ -1439,6 +1525,12 @@ export async function runAgentLoopImpl(
1439
1525
 
1440
1526
  ctx.profiler.emitSummary(ctx.traceEmitter, reqId);
1441
1527
 
1528
+ // Tear down this turn's per-turn state. Abort reliably drives the loop to
1529
+ // this `finally` within a bounded time — cooperative signal propagation
1530
+ // (provider fetch + tool race) backed by the abort watchdog — so a
1531
+ // cancelled turn always unwinds before any resend can start a new one.
1532
+ // There is therefore only ever one turn alive, and clearing the shared
1533
+ // state below cannot clobber a concurrent turn.
1442
1534
  ctx.abortController = null;
1443
1535
  ctx.setProcessing(false);
1444
1536
  ctx.onConfirmationOutcome = undefined;
@@ -1578,9 +1670,9 @@ export async function applyCompactionResult(
1578
1670
  // in-context boundary rather than the raw mirror so the persisted count
1579
1671
  // stays consistent with what the new summary represents and never
1580
1672
  // double-counts an unsliced untrusted view.
1581
- const inContextCompactedCount = isUntrustedTrustClass(
1673
+ const inContextCompactedCount = !resolveCapabilities(
1582
1674
  ctx.trustContext?.trustClass,
1583
- )
1675
+ ).canAccessMemory
1584
1676
  ? 0
1585
1677
  : ctx.contextCompactedMessageCount;
1586
1678
  ctx.contextCompactedMessageCount =
@@ -1657,10 +1749,10 @@ function collapseRawResponses(rawResponses?: unknown[]): unknown | undefined {
1657
1749
 
1658
1750
  /**
1659
1751
  * Matches any runtime-injection tag that should never appear inside a
1660
- * generated summary. If the regex hits, either the compaction strip logic
1661
- * failed to drop an injected block from the summarizer input, or the
1662
- * summarizer invented tag-like text on its own both are quality bugs
1663
- * worth surfacing via telemetry.
1752
+ * generated summary. A hit means the summary echoed an injection tag
1753
+ * either parroted from history the summarizer read or invented outright.
1754
+ * The durable summary should be clean prose, so the match is surfaced via
1755
+ * telemetry.
1664
1756
  */
1665
1757
  const SUMMARY_MEMORY_ECHO_PATTERN =
1666
1758
  /<(?:memory|memory_context|memory_image|turn_context|workspace|workspace_top_level|knowledge_base|pkb|system_reminder|now_scratchpad|NOW\.md|active_thread|active_subagents|active_workspace|active_dynamic_page|channel_capabilities|transport_hints|system_notice|non_interactive_context|temporal_context|guardian_context|inbound_actor_context|channel_turn_context|interface_turn_context|channel_command_context|voice_call_control)\b/i;
@@ -23,7 +23,7 @@ const log = getLogger("conversation-history");
23
23
 
24
24
  // ── Helpers ──────────────────────────────────────────────────────────
25
25
 
26
- function isToolResultBlock(
26
+ export function isToolResultBlock(
27
27
  block: ContentBlock | Record<string, unknown>,
28
28
  ): boolean {
29
29
  return (
@@ -16,10 +16,8 @@ import type { PermissionPrompter } from "../permissions/prompter.js";
16
16
  import type { SecretPrompter } from "../permissions/secret-prompter.js";
17
17
  import { disposeContextWindowManager } from "../plugins/defaults/compaction/manager-store.js";
18
18
  import type { ContentBlock, Message } from "../providers/types.js";
19
- import {
20
- isUntrustedTrustClass,
21
- type TrustClass,
22
- } from "../runtime/actor-trust-resolver.js";
19
+ import { type TrustClass } from "../runtime/actor-trust-resolver.js";
20
+ import { resolveCapabilities } from "../runtime/capabilities.js";
23
21
  import { unregisterConversationSender } from "../tools/browser/browser-screencast.js";
24
22
  import { type AbortReason, createAbortReason } from "../util/abort-reasons.js";
25
23
  import { getLogger } from "../util/logger.js";
@@ -147,7 +145,7 @@ export function disposeConversation(ctx: DisposeContext): void {
147
145
  // Trigger graph extraction for end-of-conversation sweep.
148
146
  // Only extract from guardian conversations to preserve the memory trust
149
147
  // boundary — untrusted content must not influence future memory retrieval.
150
- if (!isUntrustedTrustClass(ctx.trustContext?.trustClass)) {
148
+ if (resolveCapabilities(ctx.trustContext?.trustClass).canAccessMemory) {
151
149
  // Recursion guard: skip graph_extract for auto-analysis conversations.
152
150
  // The analysis agent writes memory directly via tools, so extracting
153
151
  // from its reflective musings would double-write into the memory graph.
@@ -28,7 +28,10 @@ import {
28
28
  import { extractPreferences } from "../notifications/preference-extractor.js";
29
29
  import { createPreference } from "../notifications/preferences-store.js";
30
30
  import type { ContextWindowResult } from "../plugins/defaults/compaction/window-manager.js";
31
- import { routeGuardianReply } from "../runtime/guardian-reply-router.js";
31
+ import {
32
+ type GuardianPendingScope,
33
+ routeGuardianReply,
34
+ } from "../runtime/guardian-reply-router.js";
32
35
  import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
33
36
  import { getLogger } from "../util/logger.js";
34
37
  import type { CleanResult, Conversation } from "./conversation.js";
@@ -153,7 +156,7 @@ function resolveQueuedTurnInterfaceContext(
153
156
  }
154
157
 
155
158
  /** Build a SlashContext from the current conversation state and config. */
156
- function buildSlashContext(
159
+ export function buildSlashContext(
157
160
  content: string,
158
161
  conversation: Conversation,
159
162
  ): SlashContext | undefined {
@@ -1407,9 +1410,14 @@ export async function processMessage(
1407
1410
  "vellum",
1408
1411
  ).map((request) => request.id)
1409
1412
  : [];
1410
- const canonicalPendingRequestIdsForConversation =
1413
+ // Empty hints → leave the scope unset (identity-fallback): the desktop
1414
+ // guardian can still resolve their pending work by identity/principal.
1415
+ const pendingScope: GuardianPendingScope | undefined =
1411
1416
  canonicalPendingRequestHintIdsForConversation.length > 0
1412
- ? canonicalPendingRequestHintIdsForConversation
1417
+ ? {
1418
+ mode: "scoped",
1419
+ requestIds: canonicalPendingRequestHintIdsForConversation,
1420
+ }
1413
1421
  : undefined;
1414
1422
 
1415
1423
  // ── Canonical guardian reply router (desktop/conversation path) ──
@@ -1428,7 +1436,7 @@ export async function processMessage(
1428
1436
  conversation.trustContext?.guardianPrincipalId ?? undefined,
1429
1437
  },
1430
1438
  conversationId: conversation.conversationId,
1431
- pendingRequestIds: canonicalPendingRequestIdsForConversation,
1439
+ pendingScope,
1432
1440
  // Desktop path: disable NL classification to avoid consuming non-decision
1433
1441
  // messages while a tool confirmation is pending. Deterministic code-prefix
1434
1442
  // and callback parsing remain active.