@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a

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 (624) hide show
  1. package/ARCHITECTURE.md +19 -35
  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 +1128 -119
  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__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  28. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  29. package/src/__tests__/app-compiler.test.ts +15 -1
  30. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  31. package/src/__tests__/approval-cascade.test.ts +6 -0
  32. package/src/__tests__/approval-routes-http.test.ts +128 -0
  33. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  34. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  35. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  36. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  37. package/src/__tests__/btw-routes.test.ts +6 -8
  38. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  39. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  40. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  41. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  42. package/src/__tests__/checker.test.ts +0 -3
  43. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  44. package/src/__tests__/compaction-events.test.ts +6 -0
  45. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  46. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  47. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  48. package/src/__tests__/config-schema.test.ts +35 -0
  49. package/src/__tests__/config-watcher.test.ts +0 -18
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  51. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  52. package/src/__tests__/contacts-tools.test.ts +29 -0
  53. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  57. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  59. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  60. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  61. package/src/__tests__/conversation-process-callsite.test.ts +6 -0
  62. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  63. package/src/__tests__/conversation-queue.test.ts +6 -0
  64. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  65. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  66. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  68. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  69. package/src/__tests__/conversation-title-service.test.ts +62 -0
  70. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  71. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  72. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  73. package/src/__tests__/credential-broker.test.ts +449 -1
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  75. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  76. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  77. package/src/__tests__/credential-routes.test.ts +360 -0
  78. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  79. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  80. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  81. package/src/__tests__/disk-usage.test.ts +65 -0
  82. package/src/__tests__/document-tool-security.test.ts +9 -6
  83. package/src/__tests__/document-update-default-surface.test.ts +202 -0
  84. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  85. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  86. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  87. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  88. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  89. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  90. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  91. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  92. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  93. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  94. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  95. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  96. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  97. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  98. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  99. package/src/__tests__/identity-routes.test.ts +0 -189
  100. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  101. package/src/__tests__/injector-disk-pressure.test.ts +10 -5
  102. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  103. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  104. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  105. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  106. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  107. package/src/__tests__/llm-resolver.test.ts +70 -24
  108. package/src/__tests__/llm-schema.test.ts +1 -0
  109. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  110. package/src/__tests__/mcp-health-check.test.ts +6 -7
  111. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  112. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  113. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  114. package/src/__tests__/path-policy.test.ts +34 -0
  115. package/src/__tests__/persona-resolver.test.ts +49 -14
  116. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  118. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  119. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  120. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  121. package/src/__tests__/reaction-persistence.test.ts +150 -29
  122. package/src/__tests__/registry.test.ts +2 -7
  123. package/src/__tests__/relay-server.test.ts +285 -0
  124. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  125. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  126. package/src/__tests__/schedule-routes.test.ts +0 -30
  127. package/src/__tests__/schedule-tools.test.ts +2 -18
  128. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  129. package/src/__tests__/secret-response-routing.test.ts +35 -0
  130. package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
  131. package/src/__tests__/skill-execute-input.test.ts +177 -1
  132. package/src/__tests__/skill-load-tool.test.ts +72 -0
  133. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  134. package/src/__tests__/skills.test.ts +51 -0
  135. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  136. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  137. package/src/__tests__/subagent-tools.test.ts +266 -0
  138. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  139. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  140. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  141. package/src/__tests__/title-generate-hook.test.ts +100 -3
  142. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  143. package/src/__tests__/token-manager.test.ts +519 -0
  144. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  145. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  146. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  147. package/src/__tests__/tool-executor.test.ts +0 -79
  148. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  149. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  150. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  151. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  152. package/src/__tests__/twilio-routes.test.ts +81 -1
  153. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  154. package/src/__tests__/weak-open-model.test.ts +30 -0
  155. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  156. package/src/__tests__/workspace-greetings.test.ts +152 -0
  157. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  158. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  159. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  160. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  161. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  162. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  163. package/src/agent/loop.ts +75 -29
  164. package/src/api/README.md +6 -6
  165. package/src/api/events/tool-result.ts +6 -0
  166. package/src/api/events/workflow-completed.ts +53 -0
  167. package/src/api/events/workflow-leaf-finished.ts +38 -0
  168. package/src/api/events/workflow-leaf-started.ts +35 -0
  169. package/src/api/events/workflow-progress.ts +32 -0
  170. package/src/api/events/workflow-started.ts +31 -0
  171. package/src/api/index.ts +40 -0
  172. package/src/api/responses/conversation-message.ts +28 -4
  173. package/src/api/responses/home.ts +26 -4
  174. package/src/api/responses/workflow-journal.ts +53 -0
  175. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  176. package/src/approvals/guardian-decision-primitive.ts +26 -3
  177. package/src/approvals/guardian-request-resolvers.ts +183 -80
  178. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  179. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  180. package/src/calls/call-pointer-messages.ts +10 -4
  181. package/src/calls/channel-admission-reader.ts +104 -0
  182. package/src/calls/guardian-dispatch.ts +17 -45
  183. package/src/calls/media-stream-server.ts +84 -2
  184. package/src/calls/relay-access-wait.ts +1 -1
  185. package/src/calls/relay-server.ts +66 -0
  186. package/src/calls/relay-setup-router.ts +82 -1
  187. package/src/calls/twilio-routes.ts +17 -8
  188. package/src/calls/voice-session-bridge.ts +2 -2
  189. package/src/cli/commands/clients.ts +3 -0
  190. package/src/cli/commands/db/status.ts +37 -3
  191. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  192. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  193. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  194. package/src/cli/commands/memory/index.ts +30 -0
  195. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  196. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  197. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  198. package/src/cli/commands/oauth/status.test.ts +36 -0
  199. package/src/cli/commands/oauth/status.ts +23 -3
  200. package/src/cli/commands/plugins.ts +400 -14
  201. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  202. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  203. package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
  204. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  205. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  206. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  207. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  208. package/src/cli/lib/diff-plugin.ts +346 -0
  209. package/src/cli/lib/inspect-plugin.ts +12 -1
  210. package/src/cli/lib/install-from-github.ts +128 -19
  211. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  212. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  213. package/src/cli/lib/plugin-marketplace.ts +11 -0
  214. package/src/cli/lib/plugin-pin-history.ts +257 -0
  215. package/src/cli/lib/plugin-surfaces.ts +104 -0
  216. package/src/cli/lib/upgrade-plugin.ts +298 -10
  217. package/src/cli/program.ts +2 -6
  218. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  219. package/src/config/assistant-feature-flags.ts +22 -7
  220. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  221. package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
  222. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  223. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  224. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  225. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  226. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  227. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  228. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  229. package/src/config/bundled-tool-registry.ts +2 -7
  230. package/src/config/call-site-defaults.ts +15 -2
  231. package/src/config/feature-flag-registry.json +46 -31
  232. package/src/config/inference-profile-validation.ts +26 -0
  233. package/src/config/llm-resolver.ts +3 -0
  234. package/src/config/loader.ts +4 -0
  235. package/src/config/memory-v3-gate.ts +11 -0
  236. package/src/config/profile-order.ts +28 -0
  237. package/src/config/schema.ts +8 -6
  238. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  239. package/src/config/schemas/call-site-catalog.ts +7 -0
  240. package/src/config/schemas/channels.ts +11 -0
  241. package/src/config/schemas/elevenlabs.ts +0 -1
  242. package/src/config/schemas/llm.ts +31 -0
  243. package/src/config/schemas/memory-lifecycle.ts +3 -7
  244. package/src/config/schemas/memory-v3.ts +6 -0
  245. package/src/config/schemas/platform.ts +0 -8
  246. package/src/config/schemas/services.ts +18 -0
  247. package/src/config/seed-inference-profiles.ts +109 -44
  248. package/src/config/skills.ts +21 -0
  249. package/src/config/sync-gated-profiles.ts +220 -0
  250. package/src/contacts/contact-store.ts +89 -106
  251. package/src/contacts/contacts-write.ts +5 -22
  252. package/src/contacts/types.ts +0 -1
  253. package/src/context/compactor.ts +88 -54
  254. package/src/context/strip-injections.ts +58 -10
  255. package/src/context/token-estimator.ts +1 -1
  256. package/src/credential-execution/process-manager.ts +55 -14
  257. package/src/credential-execution/prompted-credential.ts +2 -3
  258. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  259. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  260. package/src/daemon/config-watcher.ts +0 -4
  261. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  262. package/src/daemon/conversation-agent-loop.ts +114 -22
  263. package/src/daemon/conversation-history.ts +1 -1
  264. package/src/daemon/conversation-lifecycle.ts +3 -5
  265. package/src/daemon/conversation-process.ts +13 -5
  266. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  267. package/src/daemon/conversation-slash.ts +2 -23
  268. package/src/daemon/conversation-surfaces.ts +26 -0
  269. package/src/daemon/conversation-tool-setup.ts +53 -19
  270. package/src/daemon/conversation.ts +131 -20
  271. package/src/daemon/disk-pressure-policy.ts +5 -3
  272. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  273. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  274. package/src/daemon/handlers/config-a2a.ts +0 -2
  275. package/src/daemon/handlers/config-channels.ts +15 -16
  276. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  277. package/src/daemon/handlers/conversations.ts +107 -0
  278. package/src/daemon/host-browser-proxy.ts +41 -0
  279. package/src/daemon/lifecycle.ts +55 -27
  280. package/src/daemon/memory-v2-startup.test.ts +131 -0
  281. package/src/daemon/memory-v2-startup.ts +100 -1
  282. package/src/daemon/message-provenance.ts +2 -0
  283. package/src/daemon/message-types/contacts.ts +0 -1
  284. package/src/daemon/message-types/conversations.ts +3 -3
  285. package/src/daemon/message-types/sync.ts +0 -1
  286. package/src/daemon/message-types/web-activity.ts +7 -1
  287. package/src/daemon/message-types/workflows.ts +83 -1
  288. package/src/daemon/orphan-reaper.test.ts +0 -19
  289. package/src/daemon/orphan-reaper.ts +2 -24
  290. package/src/daemon/server.ts +0 -10
  291. package/src/daemon/tool-setup-types.ts +4 -0
  292. package/src/daemon/trust-context.ts +1 -1
  293. package/src/daemon/wake-conversation-ops.ts +1 -0
  294. package/src/events/tool-audit-listener.ts +2 -2
  295. package/src/home/feed-source-enrichment.test.ts +151 -0
  296. package/src/home/feed-source-enrichment.ts +176 -0
  297. package/src/home/relationship-state.ts +2 -4
  298. package/src/instrument.ts +18 -6
  299. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  300. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  301. package/src/ipc/assistant-server.ts +37 -4
  302. package/src/ipc/gateway-flag-listener.ts +18 -2
  303. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  304. package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
  305. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  306. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  307. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  308. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  309. package/src/memory/auth-fallback-events-store.ts +2 -2
  310. package/src/memory/auto-analysis-enqueue.ts +3 -5
  311. package/src/memory/bookmark-crud.ts +1 -2
  312. package/src/memory/canonical-guardian-store.ts +39 -1
  313. package/src/memory/conversation-crud.ts +9 -4
  314. package/src/memory/conversation-key-store.ts +17 -2
  315. package/src/memory/conversation-title-service.ts +64 -7
  316. package/src/memory/db-async-query.ts +20 -2
  317. package/src/memory/db-connection.ts +49 -5
  318. package/src/memory/db-init.ts +17 -17
  319. package/src/memory/embedding-backend.ts +38 -1
  320. package/src/memory/embedding-billing-breaker.ts +96 -0
  321. package/src/memory/jobs-store.ts +25 -13
  322. package/src/memory/jobs-worker.ts +54 -1
  323. package/src/memory/lifecycle-events-store.ts +2 -2
  324. package/src/memory/memory-retrospective-constants.ts +4 -4
  325. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  326. package/src/memory/memory-retrospective-job.ts +28 -227
  327. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  328. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  329. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  330. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  331. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  332. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  333. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  334. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  335. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  336. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  337. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  338. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  339. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  340. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  341. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  342. package/src/memory/migrations/index.ts +6 -0
  343. package/src/memory/migrations/run-migrations.ts +41 -0
  344. package/src/memory/migrations/validate-migration-state.ts +1 -1
  345. package/src/memory/onboarding-events-store.ts +3 -3
  346. package/src/memory/schema/contacts.ts +0 -5
  347. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  348. package/src/memory/skill-loaded-events-store.ts +2 -2
  349. package/src/memory/tool-executed-events-store.test.ts +7 -7
  350. package/src/memory/turn-trace-store.test.ts +736 -0
  351. package/src/memory/turn-trace-store.ts +364 -0
  352. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  353. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  354. package/src/memory/v2/consolidation-job.ts +2 -2
  355. package/src/memory/v2/skill-content.ts +25 -7
  356. package/src/memory/v2/skill-store.ts +7 -1
  357. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  358. package/src/memory/v3-eval/eval-packets.ts +546 -0
  359. package/src/messaging/providers/slack/adapter.ts +1 -1
  360. package/src/messaging/providers/slack/api.ts +31 -0
  361. package/src/messaging/providers/slack/send.test.ts +114 -2
  362. package/src/messaging/providers/slack/send.ts +30 -7
  363. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  364. package/src/messaging/providers/slack/withdraw.ts +161 -0
  365. package/src/notifications/AGENTS.md +2 -0
  366. package/src/notifications/access-request-copy.ts +72 -59
  367. package/src/notifications/adapters/shared.ts +29 -0
  368. package/src/notifications/adapters/slack.ts +58 -103
  369. package/src/notifications/adapters/telegram.ts +2 -20
  370. package/src/notifications/approval-card-data.ts +333 -0
  371. package/src/notifications/broadcaster.ts +16 -3
  372. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  373. package/src/notifications/copy-composer.ts +3 -3
  374. package/src/notifications/decision-engine.ts +4 -2
  375. package/src/notifications/destination-resolver.ts +4 -6
  376. package/src/notifications/guardian-question-mode.ts +10 -0
  377. package/src/notifications/home-feed-side-effect.ts +7 -16
  378. package/src/notifications/notification-utils.ts +19 -20
  379. package/src/notifications/signal.ts +79 -43
  380. package/src/notifications/types.ts +98 -121
  381. package/src/oauth/AGENTS.md +5 -24
  382. package/src/permissions/checker.test.ts +51 -0
  383. package/src/permissions/checker.ts +185 -26
  384. package/src/permissions/ipc-risk-types.ts +24 -0
  385. package/src/permissions/question-prompter.test.ts +27 -0
  386. package/src/permissions/question-prompter.ts +4 -0
  387. package/src/permissions/secret-prompter.ts +15 -1
  388. package/src/platform/client.test.ts +119 -0
  389. package/src/platform/client.ts +66 -0
  390. package/src/platform/consent-cache.test.ts +267 -0
  391. package/src/platform/consent-cache.ts +174 -0
  392. package/src/plugin-api/constants.ts +1 -1
  393. package/src/plugin-api/index.ts +33 -1
  394. package/src/plugin-api/model-profiles.ts +33 -0
  395. package/src/plugin-api/types.ts +50 -2
  396. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  397. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  398. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  399. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  400. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  401. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  402. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  403. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  404. package/src/plugins/defaults/advisor/config.ts +21 -0
  405. package/src/plugins/defaults/advisor/consult.ts +93 -0
  406. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  407. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  408. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  409. package/src/plugins/defaults/advisor/package.json +14 -0
  410. package/src/plugins/defaults/advisor/steering.ts +67 -0
  411. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  412. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  413. package/src/plugins/defaults/compaction/window-manager.ts +18 -0
  414. package/src/plugins/defaults/index.ts +60 -0
  415. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  416. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  417. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  418. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  419. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  420. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  421. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  422. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  423. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  424. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  425. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  426. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  427. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  428. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  429. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  430. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  431. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  432. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  433. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  434. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  435. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  436. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  437. package/src/prompts/persona-resolver.ts +14 -4
  438. package/src/prompts/templates/system-sections.ts +7 -2
  439. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  440. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  441. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  442. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  443. package/src/providers/anthropic/client.ts +230 -175
  444. package/src/providers/atlascloud/client.ts +85 -0
  445. package/src/providers/call-site-routing.ts +10 -0
  446. package/src/providers/fetch-provider-catalog.ts +85 -0
  447. package/src/providers/gemini/client.ts +43 -0
  448. package/src/providers/inference/adapter-factory.ts +3 -0
  449. package/src/providers/model-catalog.ts +58 -0
  450. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  451. package/src/providers/openai/chat-completions-provider.ts +7 -0
  452. package/src/providers/openai/responses-provider.ts +10 -0
  453. package/src/providers/provider-send-message.ts +11 -3
  454. package/src/providers/ratelimit.ts +9 -0
  455. package/src/providers/retry.ts +62 -12
  456. package/src/providers/search-provider-catalog.ts +10 -0
  457. package/src/providers/types.ts +16 -0
  458. package/src/providers/usage-tracking.ts +7 -0
  459. package/src/providers/weak-open-model.ts +22 -0
  460. package/src/runtime/AGENTS.md +0 -1
  461. package/src/runtime/__tests__/agent-wake.test.ts +227 -0
  462. package/src/runtime/__tests__/client-health.test.ts +44 -0
  463. package/src/runtime/access-request-helper.ts +21 -53
  464. package/src/runtime/actor-trust-resolver.ts +59 -63
  465. package/src/runtime/agent-wake.ts +71 -0
  466. package/src/runtime/assistant-event-hub.ts +18 -4
  467. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  468. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  469. package/src/runtime/btw-sidechain.ts +3 -6
  470. package/src/runtime/capabilities.test.ts +120 -0
  471. package/src/runtime/capabilities.ts +197 -0
  472. package/src/runtime/channel-approval-types.ts +22 -45
  473. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  474. package/src/runtime/channel-retry-sweep.ts +1 -0
  475. package/src/runtime/channel-verification-service.ts +3 -3
  476. package/src/runtime/client-health.ts +26 -0
  477. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  478. package/src/runtime/effective-capabilities.test.ts +128 -0
  479. package/src/runtime/effective-capabilities.ts +84 -0
  480. package/src/runtime/guardian-reply-router.ts +106 -21
  481. package/src/runtime/invite-redemption-service.ts +9 -25
  482. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
  483. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  484. package/src/runtime/pending-interactions.ts +35 -0
  485. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  486. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  487. package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
  488. package/src/runtime/routes/app-routes.ts +1 -1
  489. package/src/runtime/routes/approval-routes.ts +40 -9
  490. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  491. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  492. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  493. package/src/runtime/routes/btw-routes.ts +1 -27
  494. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  495. package/src/runtime/routes/client-routes.ts +10 -0
  496. package/src/runtime/routes/contact-routes.ts +31 -8
  497. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  498. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  499. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  500. package/src/runtime/routes/conversation-routes.ts +39 -14
  501. package/src/runtime/routes/credential-routes.ts +40 -16
  502. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  503. package/src/runtime/routes/events-routes.ts +1 -3
  504. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  505. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  506. package/src/runtime/routes/home-feed-routes.ts +8 -3
  507. package/src/runtime/routes/identity-routes.ts +1 -296
  508. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  509. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  510. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  511. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  512. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  513. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  514. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  515. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  516. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  517. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  518. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  519. package/src/runtime/routes/index.ts +2 -0
  520. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  521. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  522. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  523. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  524. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  525. package/src/runtime/routes/notification-routes.ts +122 -133
  526. package/src/runtime/routes/platform-routes.ts +2 -2
  527. package/src/runtime/routes/plugins-routes.ts +366 -11
  528. package/src/runtime/routes/schedule-routes.ts +0 -22
  529. package/src/runtime/routes/secret-routes.ts +15 -0
  530. package/src/runtime/routes/surface-action-routes.ts +2 -1
  531. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  532. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  533. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  534. package/src/runtime/routes/workflow-routes.ts +131 -29
  535. package/src/runtime/routes/workspace-greetings.ts +55 -0
  536. package/src/runtime/sync/resource-sync-events.ts +1 -11
  537. package/src/runtime/tool-grant-request-helper.ts +18 -16
  538. package/src/runtime/trust-context-resolver.ts +8 -5
  539. package/src/schedule/inference-profile.ts +2 -14
  540. package/src/schedule/schedule-store.ts +1 -1
  541. package/src/schedule/scheduler-types.ts +5 -1
  542. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  543. package/src/security/secret-patterns.ts +3 -0
  544. package/src/subagent/manager.ts +17 -4
  545. package/src/subagent/types.ts +6 -0
  546. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  547. package/src/telemetry/trace-collection-policy.ts +30 -0
  548. package/src/telemetry/types.ts +89 -0
  549. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  550. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  551. package/src/tools/AGENTS.md +3 -3
  552. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  553. package/src/tools/browser/browser-execution.ts +30 -19
  554. package/src/tools/document/document-tool.ts +30 -6
  555. package/src/tools/executor.ts +5 -3
  556. package/src/tools/host-terminal/host-shell.ts +5 -4
  557. package/src/tools/memory/register.ts +2 -2
  558. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  559. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  560. package/src/tools/network/web-fetch.ts +372 -1
  561. package/src/tools/network/web-search-error.ts +1 -1
  562. package/src/tools/network/web-search.ts +213 -10
  563. package/src/tools/permission-checker.ts +4 -3
  564. package/src/tools/registry.ts +20 -0
  565. package/src/tools/schedule/create.ts +7 -12
  566. package/src/tools/schedule/update.ts +4 -11
  567. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  568. package/src/tools/side-effects.ts +2 -17
  569. package/src/tools/skills/execute.ts +99 -2
  570. package/src/tools/skills/load.ts +43 -2
  571. package/src/tools/subagent/spawn.ts +64 -15
  572. package/src/tools/terminal/shell.ts +10 -4
  573. package/src/tools/tool-approval-handler.ts +18 -13
  574. package/src/tools/tool-manifest.ts +0 -2
  575. package/src/tools/types.ts +10 -0
  576. package/src/tools/ui-surface/definitions.ts +64 -3
  577. package/src/tools/verification-control-plane-policy.ts +3 -1
  578. package/src/tools/workflows/run-workflow.test.ts +8 -18
  579. package/src/tools/workflows/run-workflow.ts +1 -0
  580. package/src/util/disk-usage.ts +78 -23
  581. package/src/util/logs-db-path.ts +22 -0
  582. package/src/util/platform.ts +10 -3
  583. package/src/watcher/telemetry.ts +2 -2
  584. package/src/workflows/capabilities.ts +2 -3
  585. package/src/workflows/engine.test.ts +175 -1
  586. package/src/workflows/engine.ts +82 -0
  587. package/src/workflows/journal-store.test.ts +70 -0
  588. package/src/workflows/journal-store.ts +18 -3
  589. package/src/workflows/run-manager.test.ts +171 -28
  590. package/src/workflows/run-manager.ts +66 -24
  591. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  592. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  593. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  594. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  595. package/src/workspace/migrations/registry.ts +8 -0
  596. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  597. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  598. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  599. package/src/__tests__/credential-vault.test.ts +0 -1706
  600. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  601. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  602. package/src/cli/commands/__tests__/task.test.ts +0 -914
  603. package/src/cli/commands/task.ts +0 -771
  604. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  605. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  606. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  607. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  608. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  609. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  610. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  611. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  612. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  613. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  614. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  615. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  616. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  617. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  618. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  619. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  620. package/src/memory/preloaded-apps.ts +0 -116
  621. package/src/notifications/tool-approval-copy.ts +0 -142
  622. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  623. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  624. package/src/tools/credentials/vault.ts +0 -712
@@ -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.
@@ -9,9 +9,9 @@ import { statSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
 
11
11
  import { type ChannelId, parseInterfaceId } from "../channels/types.js";
12
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
13
12
  import { resolveCallSiteConfig } from "../config/llm-resolver.js";
14
13
  import { getConfig } from "../config/loader.js";
14
+ import { isMemoryV3Live } from "../config/memory-v3-gate.js";
15
15
  import type { LLMCallSite, LLMConfig } from "../config/schemas/llm.js";
16
16
  import {
17
17
  NOW_SCRATCHPAD_STRIP_PREFIXES,
@@ -62,11 +62,11 @@ import type {
62
62
  import type { ContentBlock, Message } from "../providers/types.js";
63
63
  import {
64
64
  type ActorTrustContext,
65
- isUntrustedTrustClass,
66
65
  resolveActorTrust,
67
66
  type TrustClass,
68
67
  } from "../runtime/actor-trust-resolver.js";
69
68
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
69
+ import { resolveCapabilities } from "../runtime/capabilities.js";
70
70
  import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
71
71
  import { getSubagentManager } from "../subagent/index.js";
72
72
  import type { SubagentState } from "../subagent/types.js";
@@ -125,8 +125,8 @@ export interface InboundActorContext {
125
125
  actorSenderDisplayName?: string;
126
126
  /** Guardian-managed display name from the contact record. */
127
127
  actorMemberDisplayName?: string;
128
- /** Trust classification: guardian, trusted_contact, or unknown. */
129
- trustClass: "guardian" | "trusted_contact" | "unknown";
128
+ /** Trust classification: see TrustClass. */
129
+ trustClass: TrustClass;
130
130
  /** Guardian identity for this (assistant, channel) binding. */
131
131
  guardianIdentity?: string;
132
132
  /** Member status when the actor has a contact record. */
@@ -916,7 +916,7 @@ function filterSlackConversationRowsForActor(
916
916
  rows: MessageRow[],
917
917
  trustClass: TrustClass | undefined,
918
918
  ): MessageRow[] {
919
- if (!isUntrustedTrustClass(trustClass)) return rows;
919
+ if (resolveCapabilities(trustClass).canAccessMemory) return rows;
920
920
  const nonSlackVisibleRows = filterMessagesForUntrustedActor(rows);
921
921
  const nonSlackVisibleIds = new Set(nonSlackVisibleRows.map((row) => row.id));
922
922
  return rows.filter((row) => {
@@ -1009,6 +1009,7 @@ function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
1009
1009
  if (
1010
1010
  outer.provenanceTrustClass === "guardian" ||
1011
1011
  outer.provenanceTrustClass === "trusted_contact" ||
1012
+ outer.provenanceTrustClass === "unverified_contact" ||
1012
1013
  outer.provenanceTrustClass === "unknown"
1013
1014
  ) {
1014
1015
  provenanceTrustClass = outer.provenanceTrustClass;
@@ -1322,9 +1323,9 @@ export function loadSlackChronologicalContext(
1322
1323
  options,
1323
1324
  );
1324
1325
  return assembleSlackChronologicalContext(rows, capabilities, {
1325
- contextSummary: isUntrustedTrustClass(options.trustClass)
1326
- ? null
1327
- : options.contextSummary,
1326
+ contextSummary: resolveCapabilities(options.trustClass).canAccessMemory
1327
+ ? options.contextSummary
1328
+ : null,
1328
1329
  });
1329
1330
  }
1330
1331
 
@@ -1536,8 +1537,8 @@ export interface RuntimeInjectionBlocks {
1536
1537
  */
1537
1538
  memoryV3InjectedBlock?: string;
1538
1539
  /**
1539
- * True when memory-v3 superseded v2 as this turn's `<memory>` source — the
1540
- * `memory-v3-live` flag is on AND the v3 injector produced a block (possibly
1540
+ * True when memory-v3 superseded v2 as this turn's `<memory>` source —
1541
+ * `memory.v3.live` is on AND the v3 injector produced a block (possibly
1541
1542
  * empty-text on an all-repeat turn), i.e. exactly when assembly stripped
1542
1543
  * v2's fresh tail block. The user-prompt-submit hook keys v2's
1543
1544
  * `memoryInjectedBlock` metadata persist off this so a stripped v2 block is
@@ -2177,7 +2178,7 @@ export async function applyRuntimeInjections(
2177
2178
  // no-op, keeping the v2 path bit-for-bit identical.
2178
2179
  let runMessagesForAssembly = stripSpotlightInjections(runMessages);
2179
2180
 
2180
- // v2 suppression: when the `memory-v3-live` flag is on AND the v3 injector
2181
+ // v2 suppression: when `memory.v3.live` is on AND the v3 injector
2181
2182
  // produced a block this turn (possibly empty-text on an all-repeat turn), v3
2182
2183
  // owns the `<memory>` layer. v2's `prepareMemory` already prepended its own
2183
2184
  // fresh `<memory>` block to the tail user message — strip the TAIL's v2
@@ -2196,10 +2197,7 @@ export async function applyRuntimeInjections(
2196
2197
  // (`produce()` → null) leaves v2's block intact — fallback rather than a
2197
2198
  // memory-less turn. Idempotent: re-injection sites that already stripped
2198
2199
  // see no change. Flag off → bit-for-bit identical to the v2 path.
2199
- const suppressV2MemoryForV3 = isAssistantFeatureFlagEnabled(
2200
- "memory-v3-live",
2201
- getConfig(),
2202
- );
2200
+ const suppressV2MemoryForV3 = isMemoryV3Live(getConfig());
2203
2201
  const v3ProducedBlock = afterMemory.some((b) => b.id === MEMORY_V3_BLOCK_ID);
2204
2202
  const memoryV3Active = suppressV2MemoryForV3 && v3ProducedBlock;
2205
2203
  if (memoryV3Active) {
@@ -7,6 +7,7 @@ import {
7
7
  loadRawConfig,
8
8
  saveRawConfig,
9
9
  } from "../config/loader.js";
10
+ import { orderProfileKeys } from "../config/profile-order.js";
10
11
  import { getConversationOverrideProfile } from "../memory/conversation-crud.js";
11
12
  import { getConfiguredProviders } from "../providers/provider-availability.js";
12
13
  import { getVisibleProviderCatalog } from "../providers/provider-catalog-visibility.js";
@@ -130,28 +131,6 @@ function parseModelCommand(trimmed: string): ModelCommandParse | null {
130
131
  return { kind: "switch", profileName: rest };
131
132
  }
132
133
 
133
- function orderedProfileNames(
134
- profiles: Record<
135
- string,
136
- { label?: string; description?: string; status?: "active" | "disabled" }
137
- >,
138
- profileOrder: readonly string[] | undefined,
139
- ): string[] {
140
- const order = profileOrder ?? [];
141
- const seen = new Set<string>();
142
- const ordered: string[] = [];
143
- for (const name of order) {
144
- if (profiles[name] != null && !seen.has(name)) {
145
- ordered.push(name);
146
- seen.add(name);
147
- }
148
- }
149
- const tail = Object.keys(profiles)
150
- .filter((n) => !seen.has(n))
151
- .sort();
152
- return [...ordered, ...tail];
153
- }
154
-
155
134
  async function resolveModelCommand(
156
135
  parse: ModelCommandParse,
157
136
  ): Promise<SlashResolution> {
@@ -160,7 +139,7 @@ async function resolveModelCommand(
160
139
  string,
161
140
  { label?: string; description?: string; status?: "active" | "disabled" }
162
141
  >;
163
- const profileNames = orderedProfileNames(profiles, config.llm.profileOrder);
142
+ const profileNames = orderProfileKeys(profiles, config.llm.profileOrder);
164
143
  const activeProfile = config.llm.activeProfile;
165
144
 
166
145
  if (parse.kind === "list") {
@@ -291,6 +291,32 @@ export function markSurfaceCompleted(
291
291
  }
292
292
  }
293
293
 
294
+ /**
295
+ * Complete a `ui_surface` card and notify live clients, addressed only by
296
+ * conversation + surface id.
297
+ *
298
+ * Unlike {@link completeSurfaceFromAction}, this needs no live `Conversation`
299
+ * instance, so it can run from flows that don't own one — projecting a
300
+ * terminal guardian-request status onto its in-app approval card when the
301
+ * request was resolved on another surface (or by the expiry sweep). Persists
302
+ * the completion (reload-safe) and broadcasts `ui_surface_complete` so every
303
+ * connected client of this guardian converges. No-ops when the surface block
304
+ * isn't found in the conversation.
305
+ */
306
+ export function completeSurfaceAndNotify(
307
+ conversationId: string,
308
+ surfaceId: string,
309
+ summary: string,
310
+ ): void {
311
+ markSurfaceCompleted({ conversationId }, surfaceId, summary);
312
+ broadcastMessage({
313
+ type: "ui_surface_complete",
314
+ conversationId,
315
+ surfaceId,
316
+ summary,
317
+ });
318
+ }
319
+
294
320
  /**
295
321
  * Remove a `ui_surface` content block from history so a passively dismissed
296
322
  * surface does not survive a reload. The live client drops a dismissed surface
@@ -17,16 +17,21 @@ import type { LLMCallSite } from "../config/schemas/llm.js";
17
17
  import { getBindingByConversation } from "../memory/external-conversation-store.js";
18
18
  import type { PermissionPrompter } from "../permissions/prompter.js";
19
19
  import type { SecretPrompter } from "../permissions/secret-prompter.js";
20
+ import { advisorEnabledForProfile } from "../plugins/defaults/advisor/advisor-gate.js";
20
21
  import type { Message, ToolDefinition } from "../providers/types.js";
21
22
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
22
23
  import { registerConversationSender } from "../tools/browser/browser-screencast.js";
23
24
  import type { ToolExecutor } from "../tools/executor.js";
24
- import { getMcpToolDefinitions } from "../tools/registry.js";
25
+ import { getMcpToolDefinitions, getTool } from "../tools/registry.js";
25
26
  import {
26
27
  ACTIVITY_SKIP_SET,
27
28
  injectActivityField,
28
29
  } from "../tools/schema-transforms.js";
29
- import { resolveSkillExecuteInput } from "../tools/skills/execute.js";
30
+ import {
31
+ augmentSkillExecuteError,
32
+ recoverSkillExecuteEnvelope,
33
+ resolveSkillExecuteInput,
34
+ } from "../tools/skills/execute.js";
30
35
  import { resolveToolInvocationAlias } from "../tools/tool-name-aliases.js";
31
36
  import {
32
37
  isDiskPressureCleanupToolName,
@@ -52,7 +57,7 @@ import {
52
57
  } from "./doordash-steps.js";
53
58
  import type { ServerMessage, UiSurfaceShow } from "./message-protocol.js";
54
59
  import { runPostExecutionSideEffects } from "./tool-side-effects.js";
55
- import { resolveTrustClass } from "./trust-context.js";
60
+ import { FALLBACK_TURN_TRUST, resolveTrustClass } from "./trust-context.js";
56
61
 
57
62
  const log = getLogger("conversation-tool-setup");
58
63
 
@@ -88,8 +93,10 @@ export type {
88
93
  * agent's). The conversation id is threaded as the mix selection seed so
89
94
  * mix-profile arms match what the dispatch path actually ran.
90
95
  *
91
- * Returns `null` on any failure: attribution is telemetry-only and must
92
- * never break tool execution (or skill loads, which reuse this helper).
96
+ * Returns `null` on any failure: attribution must never break tool execution
97
+ * (or skill loads, which reuse this helper). Consumers read it best-effort —
98
+ * usage telemetry, and `subagent_spawn`, which inherits the resolved
99
+ * `appliedProfile` so a child defaults to the invoking turn's profile.
93
100
  */
94
101
  export function resolveConversationAttribution(
95
102
  ctx: Pick<
@@ -178,28 +185,32 @@ export function createToolExecutor(
178
185
  markDoordashStepInProgress(ctx, executionInput);
179
186
  }
180
187
 
181
- // Build the context object shared by both the skill_execute interception
182
- // path and the regular executor path.
188
+ // Per-turn trust snapshot: prefer the snapshot captured at turn start so
189
+ // a concurrent owner meta command (/status, /clean) that mutates the live
190
+ // trustContext cannot elevate the in-flight turn to guardian.
191
+ const turnTrust =
192
+ ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
193
+
183
194
  const toolContext: ToolContext = {
184
195
  workingDir: ctx.workingDir,
185
196
  conversationId: ctx.conversationId,
186
197
  assistantId: ctx.assistantId,
187
198
  requestId: ctx.currentRequestId,
188
199
  taskRunId: ctx.taskRunId,
189
- trustClass: resolveTrustClass(ctx.trustContext),
190
- executionChannel: ctx.trustContext?.sourceChannel,
191
- sourceActorPrincipalId: ctx.trustContext?.guardianPrincipalId,
200
+ trustClass: resolveTrustClass(turnTrust),
201
+ executionChannel: turnTrust.sourceChannel,
202
+ sourceActorPrincipalId: turnTrust.guardianPrincipalId,
192
203
  callSessionId: ctx.callSessionId,
193
204
  triggeredBySurfaceAction:
194
205
  ctx.surfaceActionRequestIds?.has(ctx.currentRequestId ?? "") ?? false,
195
206
  approvedViaPrompt: ctx.approvedViaPromptThisTurn || undefined,
196
207
  batchAuthorizedByTask: false,
197
- requesterExternalUserId: ctx.trustContext?.requesterExternalUserId,
198
- requesterChatId: ctx.trustContext?.requesterChatId,
199
- requesterIdentifier: ctx.trustContext?.requesterIdentifier,
200
- requesterDisplayName: ctx.trustContext?.requesterDisplayName,
208
+ requesterExternalUserId: turnTrust.requesterExternalUserId,
209
+ requesterChatId: turnTrust.requesterChatId,
210
+ requesterIdentifier: turnTrust.requesterIdentifier,
211
+ requesterDisplayName: turnTrust.requesterDisplayName,
201
212
  channelPermissionChannelId:
202
- ctx.trustContext?.sourceChannel === "slack"
213
+ turnTrust.sourceChannel === "slack"
203
214
  ? getBindingByConversation(ctx.conversationId)?.externalChatId
204
215
  : undefined,
205
216
  onOutput,
@@ -211,6 +222,7 @@ export function createToolExecutor(
211
222
  isPlatformHosted: getIsPlatform(),
212
223
  transportInterface: ctx.transportInterface,
213
224
  overrideProfile: ctx.currentTurnOverrideProfile,
225
+ invokingCallSite: ctx.currentCallSite ?? "mainAgent",
214
226
  attribution: resolveConversationAttribution(ctx),
215
227
  onToolLifecycleEvent: handleToolLifecycleEvent,
216
228
  sendToClient: (msg) => {
@@ -295,9 +307,16 @@ export function createToolExecutor(
295
307
  // risk level, permission checks, hooks, and lifecycle events all fire
296
308
  // with the real tool name.
297
309
  if (executionName === "skill_execute") {
310
+ // Recover an envelope the provider wrapped as unparseable when MiniMax's
311
+ // coercion failed to JSON-decode a bare-string `input` (see
312
+ // recoverSkillExecuteEnvelope), then resolve the inner tool + params.
313
+ const envelope = recoverSkillExecuteEnvelope(executionInput);
298
314
  const rawToolName =
299
- typeof executionInput.tool === "string" ? executionInput.tool : "";
300
- const rawToolInput = resolveSkillExecuteInput(executionInput);
315
+ typeof envelope.tool === "string" ? envelope.tool : "";
316
+ const innerSchema = rawToolName
317
+ ? getTool(rawToolName)?.input_schema
318
+ : undefined;
319
+ const rawToolInput = resolveSkillExecuteInput(envelope, innerSchema);
301
320
 
302
321
  // Clone to avoid mutating shared input objects
303
322
  const { name: toolName, input: toolInput } = resolveToolInvocationAlias(
@@ -320,7 +339,12 @@ export function createToolExecutor(
320
339
  const innerRejection = rejectNonAllowlistedTool(toolName);
321
340
  if (innerRejection) return innerRejection;
322
341
 
323
- const result = await executor.execute(toolName, toolInput, toolContext);
342
+ const rawResult = await executor.execute(
343
+ toolName,
344
+ toolInput,
345
+ toolContext,
346
+ );
347
+ const result = augmentSkillExecuteError(toolName, toolInput, rawResult);
324
348
  if (toolContext.approvedViaPrompt) {
325
349
  ctx.approvedViaPromptThisTurn = true;
326
350
  }
@@ -371,7 +395,7 @@ export function createProxyApprovalCallback(
371
395
  * history or explicit preactivation. Without this, their tools are
372
396
  * unavailable in fresh conversations until `skill_load` is called.
373
397
  */
374
- const DEFAULT_PREACTIVATED_SKILL_IDS = ["tasks", "notifications", "subagent"];
398
+ const DEFAULT_PREACTIVATED_SKILL_IDS = ["notifications", "subagent"];
375
399
 
376
400
  /**
377
401
  * Subset of Conversation state that the resolveTools callback reads at each
@@ -576,6 +600,16 @@ export function isToolActiveForContext(
576
600
  return true;
577
601
  }
578
602
  }
603
+ if (name === "advisor") {
604
+ // Gated per chat-profile (`ProfileEntry.advisorEnabled`): when the resolved
605
+ // profile disables the advisor, omit the tool from the wire list so the
606
+ // model never sees a tool it can only no-op on. Resolves the profile the
607
+ // same way the advisor's execution-time guard does (the per-turn override,
608
+ // else the active profile). The wire list is fixed before PRE_MODEL_CALL
609
+ // hooks run, so a hook that re-routes the profile mid-turn (the model-router
610
+ // lever on `PreModelCallContext.modelProfile`) is not reflected here.
611
+ return advisorEnabledForProfile(ctx.currentTurnOverrideProfile ?? null);
612
+ }
579
613
  if (UI_SURFACE_TOOL_NAMES.has(name)) {
580
614
  if (
581
615
  channelCapabilities?.channel === "slack" &&
@@ -80,12 +80,10 @@ import {
80
80
  } from "../prompts/system-prompt.js";
81
81
  import type { ContentBlock, Message } from "../providers/types.js";
82
82
  import type { Provider } from "../providers/types.js";
83
- import {
84
- isUntrustedTrustClass,
85
- type TrustClass,
86
- } from "../runtime/actor-trust-resolver.js";
83
+ import { type TrustClass } from "../runtime/actor-trust-resolver.js";
87
84
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
88
85
  import type { AuthContext } from "../runtime/auth/types.js";
86
+ import { resolveCapabilities } from "../runtime/capabilities.js";
89
87
  import type { InteractiveUiResult } from "../runtime/interactive-ui.js";
90
88
  import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
91
89
  import {
@@ -107,7 +105,7 @@ import {
107
105
  runAgentLoopImpl,
108
106
  } from "./conversation-agent-loop.js";
109
107
  import type { HistoryConversationContext } from "./conversation-history.js";
110
- import { undo as undoImpl } from "./conversation-history.js";
108
+ import { isToolResultBlock, undo as undoImpl } from "./conversation-history.js";
111
109
  import {
112
110
  abortConversation,
113
111
  disposeConversation,
@@ -175,6 +173,36 @@ import { TraceEmitter } from "./trace-emitter.js";
175
173
 
176
174
  const log = getLogger("conversation");
177
175
 
176
+ /**
177
+ * Whether a persisted message starts a new conversation turn. A turn is
178
+ * delimited by a "real" user message; a user message whose content is entirely
179
+ * tool_result blocks is a continuation within the current turn, and assistant
180
+ * messages never start one. Mirrors the turn-boundary definition used by
181
+ * `getAssistantMessageIdsInTurn`/`getTurnTimeBounds` and the agent loop's
182
+ * per-turn `turnCount++`, so counting these reconstructs `turnCount` on load.
183
+ */
184
+ function startsNewTurn(role: string, content: string): boolean {
185
+ if (role !== "user") return false;
186
+ try {
187
+ const parsed = JSON.parse(content);
188
+ if (
189
+ Array.isArray(parsed) &&
190
+ parsed.length > 0 &&
191
+ parsed.every(
192
+ (block: unknown) =>
193
+ block != null &&
194
+ typeof block === "object" &&
195
+ isToolResultBlock(block as Record<string, unknown>),
196
+ )
197
+ ) {
198
+ return false;
199
+ }
200
+ } catch {
201
+ // Non-JSON content is a plain user message — a turn boundary.
202
+ }
203
+ return true;
204
+ }
205
+
178
206
  export interface CleanResult {
179
207
  previousEstimatedInputTokens: number;
180
208
  estimatedInputTokens: number;
@@ -366,6 +394,13 @@ export class Conversation {
366
394
  workspaceDir: string,
367
395
  ) => Pick<WorkspaceGitService, "ensureInitialized">;
368
396
  /** @internal */ commitTurnChanges?: typeof commitTurnChanges;
397
+ /**
398
+ * Abort-watchdog timeout (ms) for the agent loop's bounded-unwind backstop.
399
+ * Overridable in tests to fire the watchdog quickly; defaults to the
400
+ * production constant in the agent loop when unset.
401
+ * @internal
402
+ */
403
+ abortWatchdogMs?: number;
369
404
  /**
370
405
  * The conversation's immutable creation type (`interactive`, `background`,
371
406
  * `scheduled`, …) as stored on the DB row. Cached on load (and set directly
@@ -824,10 +859,25 @@ export class Conversation {
824
859
 
825
860
  async loadFromDb(): Promise<void> {
826
861
  const trustClass = this.trustContext?.trustClass;
862
+ const canAccessMemory = resolveCapabilities(trustClass).canAccessMemory;
827
863
  const allDbMessages = getMessages(this.conversationId);
828
- const dbMessages = isUntrustedTrustClass(trustClass)
829
- ? filterMessagesForUntrustedActor(allDbMessages)
830
- : allDbMessages;
864
+ const dbMessages = canAccessMemory
865
+ ? allDbMessages
866
+ : filterMessagesForUntrustedActor(allDbMessages);
867
+
868
+ // Rehydrate the in-memory turn counter from persisted history. `turnCount`
869
+ // is otherwise a fresh-zero field, so a reloaded conversation (eviction,
870
+ // restart, fork) would restart its turn numbering at 0 and the next turn
871
+ // would reuse `turnIndex` 0 — colliding with the conversation's first turn.
872
+ // The memory-v3 selector memoizes per (conversationId, turnIndex) for the
873
+ // life of the daemon process, so a collided turnIndex serves a stale
874
+ // selection and skips retrieval. One turn per real (turn-starting) user
875
+ // message, matching the agent loop's per-turn `turnCount++`. Counted from
876
+ // the full unsliced history so it survives compaction and is independent of
877
+ // the viewer's trust class.
878
+ this.turnCount = allDbMessages.filter((m) =>
879
+ startsNewTurn(m.role, m.content),
880
+ ).length;
831
881
 
832
882
  const conv = getConversation(this.conversationId);
833
883
  this.conversationType = conv?.conversationType ?? undefined;
@@ -855,12 +905,12 @@ export class Conversation {
855
905
  // than exist. Slack chronological context is a separate consumer that
856
906
  // applies its own trust filtering downstream, so it reads the raw
857
907
  // mirrored count rather than this in-context boundary.
858
- const inContextCompactedCount = isUntrustedTrustClass(trustClass)
859
- ? 0
860
- : Math.min(this.contextCompactedMessageCount, dbMessages.length);
861
- const contextSummaryForHistory = isUntrustedTrustClass(trustClass)
862
- ? null
863
- : this.contextSummary?.trim() || null;
908
+ const inContextCompactedCount = canAccessMemory
909
+ ? Math.min(this.contextCompactedMessageCount, dbMessages.length)
910
+ : 0;
911
+ const contextSummaryForHistory = canAccessMemory
912
+ ? this.contextSummary?.trim() || null
913
+ : null;
864
914
 
865
915
  // Every injection-strip event (`/clean` or compaction) updates
866
916
  // `historyStrippedAt`. Messages older than this should skip metadata
@@ -1556,8 +1606,65 @@ export class Conversation {
1556
1606
  }
1557
1607
  }
1558
1608
 
1609
+ /**
1610
+ * Token count for `messages` used to render the user-facing `/compact` and
1611
+ * `/clean` figures. Prefers the provider's real `count_tokens` tokenizer (so
1612
+ * the numbers match the context-window indicator's provider-reported usage)
1613
+ * and falls back to the context-window manager's local estimate when the
1614
+ * provider has no count endpoint or the count call fails — both measure the
1615
+ * same system-prompt + tools composition the manager sizes against.
1616
+ *
1617
+ * The count is a network round-trip with its own rate limit, so this is for
1618
+ * user-initiated actions only, never the per-turn auto-compaction gate.
1619
+ */
1620
+ private async calculateTokens(messages: Message[]): Promise<number> {
1621
+ const countInputTokens = this.provider.countInputTokens;
1622
+ if (!countInputTokens) {
1623
+ return this.contextWindowManager.estimateInputTokens(messages);
1624
+ }
1625
+ try {
1626
+ const { systemPrompt, tools } =
1627
+ this.contextWindowManager.tokenCountInputs;
1628
+ return await countInputTokens.call(
1629
+ this.provider,
1630
+ messages,
1631
+ systemPrompt,
1632
+ tools,
1633
+ );
1634
+ } catch (err) {
1635
+ log.warn(
1636
+ { err, conversationId: this.conversationId },
1637
+ "Provider token count failed — falling back to local estimate",
1638
+ );
1639
+ return this.contextWindowManager.estimateInputTokens(messages);
1640
+ }
1641
+ }
1642
+
1559
1643
  async forceCompact(): Promise<ContextWindowResult> {
1560
- return this.runCompaction(true);
1644
+ // Report the user-facing before/after using the provider's real tokenizer
1645
+ // (count_tokens) so the `/compact` line matches the context-window
1646
+ // indicator, which reflects the provider's actual reported usage — rather
1647
+ // than the local chars/4 estimate the compaction pipeline runs internally
1648
+ // (it under-counts by ~25% on typical histories). `calculateTokens`
1649
+ // falls back to that estimate when the provider has no count endpoint or
1650
+ // the count call fails, so behavior degrades gracefully.
1651
+ //
1652
+ // Only the *displayed* numbers are overridden — the compaction log and
1653
+ // circuit-breaker accounting inside `runCompaction` keep the estimate-based
1654
+ // figures, leaving calibration and historical logs untouched.
1655
+ const before = await this.calculateTokens(this.messages);
1656
+ const result = await this.runCompaction(true);
1657
+ // `runCompaction` applies the compacted history to `this.messages` in
1658
+ // place, so after a successful compaction this re-counts the new history;
1659
+ // a no-op leaves the context unchanged, so before === after.
1660
+ const after = result.compacted
1661
+ ? await this.calculateTokens(this.messages)
1662
+ : before;
1663
+ return {
1664
+ ...result,
1665
+ previousEstimatedInputTokens: before,
1666
+ estimatedInputTokens: after,
1667
+ };
1561
1668
  }
1562
1669
 
1563
1670
  /**
@@ -1676,15 +1783,17 @@ export class Conversation {
1676
1783
  * activations are no longer deduped against the prior session.
1677
1784
  */
1678
1785
  async forceClean(): Promise<CleanResult> {
1679
- const previousEstimatedInputTokens =
1680
- this.contextWindowManager.estimateInputTokens(this.messages);
1786
+ // Use the provider's real tokenizer for the displayed before/after (see
1787
+ // `forceCompact` for why); falls back to the local estimate when count
1788
+ // isn't available.
1789
+ const previousEstimatedInputTokens = await this.calculateTokens(
1790
+ this.messages,
1791
+ );
1681
1792
  const stripped = stripInjectionsForCompaction(this.messages);
1682
1793
  this.messages = stripped;
1683
1794
  await this.graphMemory.onCompacted(0);
1684
1795
  setConversationHistoryStrippedAt(this.conversationId, Date.now());
1685
- const estimatedInputTokens = this.contextWindowManager.estimateInputTokens(
1686
- this.messages,
1687
- );
1796
+ const estimatedInputTokens = await this.calculateTokens(this.messages);
1688
1797
  return {
1689
1798
  previousEstimatedInputTokens,
1690
1799
  estimatedInputTokens,
@@ -1847,6 +1956,8 @@ export class Conversation {
1847
1956
  * this value via {@link SubagentManager.spawn}.
1848
1957
  */
1849
1958
  overrideProfile?: string;
1959
+ /** Float `overrideProfile` above call-site layers for this run. */
1960
+ forceOverrideProfile?: boolean;
1850
1961
  },
1851
1962
  ): Promise<void> {
1852
1963
  const { onEvent, ...rest } = options ?? {};
@@ -1,5 +1,6 @@
1
1
  import type { InterfaceId } from "../channels/types.js";
2
2
  import type { LLMCallSite } from "../config/schemas/llm.js";
3
+ import { resolveCapabilities } from "../runtime/capabilities.js";
3
4
  import type { DiskPressureStatus } from "./disk-pressure-guard.js";
4
5
  import type { ConversationType } from "./message-types/shared.js";
5
6
  import type { TrustContext } from "./trust-context.js";
@@ -74,11 +75,11 @@ export function classifyDiskPressureTurnPolicy(
74
75
  }
75
76
 
76
77
  const trustClass = metadata.trustContext?.trustClass;
77
- if (trustClass === "guardian") {
78
+ if (resolveCapabilities(trustClass).canActUnderDiskPressureCleanup) {
78
79
  return { action: "allow-cleanup-mode", reason: "guardian" };
79
80
  }
80
81
 
81
- if (trustClass === "trusted_contact") {
82
+ if (trustClass === "trusted_contact" || trustClass === "unverified_contact") {
82
83
  return { action: "block", reason: "trusted-contact" };
83
84
  }
84
85
 
@@ -156,6 +157,7 @@ function isExplicitLocalOwnerCleanupTurn(
156
157
  }
157
158
  return (
158
159
  metadata.trustContext == null ||
159
- metadata.trustContext.trustClass === "guardian"
160
+ resolveCapabilities(metadata.trustContext.trustClass)
161
+ .canActUnderDiskPressureCleanup
160
162
  );
161
163
  }
@@ -121,7 +121,6 @@ describe("completeA2AInvite", () => {
121
121
  const invite = findById(created.inviteId!);
122
122
  const contact = getContact(invite!.contactId);
123
123
  expect(contact!.channels[0]!.address).toBe("upper-case-id-123");
124
- expect(contact!.channels[0]!.externalUserId).toBe("UPPER-Case-ID-123");
125
124
  });
126
125
 
127
126
  test("assistantContactMetadata has correct assistantId and gatewayUrl", () => {
@@ -120,7 +120,6 @@ describe("redeemA2AInvite", () => {
120
120
 
121
121
  const contact = getContact(result.contactId!);
122
122
  expect(contact!.channels[0]!.address).toBe("upper-case-sender-id");
123
- expect(contact!.channels[0]!.externalUserId).toBe("UPPER-Case-SENDER-ID");
124
123
  });
125
124
 
126
125
  test("does not make outbound fetch calls", () => {
@@ -221,7 +221,6 @@ export function completeA2AInvite(params: {
221
221
  {
222
222
  type: "a2a",
223
223
  address: params.acceptor.assistantId.toLowerCase(),
224
- externalUserId: params.acceptor.assistantId,
225
224
  status: "active",
226
225
  policy: "allow",
227
226
  },
@@ -289,7 +288,6 @@ export function redeemA2AInvite(params: {
289
288
  {
290
289
  type: "a2a",
291
290
  address: params.sender.assistantId.toLowerCase(),
292
- externalUserId: params.sender.assistantId,
293
291
  status: "active",
294
292
  policy: "allow",
295
293
  },