@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
@@ -8,10 +8,10 @@ mock.module("../../util/logger.js", () => ({
8
8
  }),
9
9
  }));
10
10
 
11
- // Mutable usage-data gate, flipped per-test.
12
- let collectUsageData = true;
13
- mock.module("../../config/loader.js", () => ({
14
- getConfig: () => ({ collectUsageData }),
11
+ // Mutable consent gate, flipped per-test.
12
+ let shareAnalytics = true;
13
+ mock.module("../../platform/consent-cache.js", () => ({
14
+ getCachedShareAnalytics: () => shareAnalytics,
15
15
  }));
16
16
 
17
17
  import { getDb } from "../db-connection.js";
@@ -30,7 +30,7 @@ function resetTable(): void {
30
30
 
31
31
  describe("onboarding-events-store: recordActivationEvent", () => {
32
32
  beforeEach(() => {
33
- collectUsageData = true;
33
+ shareAnalytics = true;
34
34
  resetTable();
35
35
  });
36
36
 
@@ -66,8 +66,8 @@ describe("onboarding-events-store: recordActivationEvent", () => {
66
66
  expect(rows[0]!.stepIndex).toBe(2);
67
67
  });
68
68
 
69
- test("returns null and writes no row when collectUsageData is disabled", () => {
70
- collectUsageData = false;
69
+ test("returns null and writes no row when share_analytics is disabled", () => {
70
+ shareAnalytics = false;
71
71
  const event = recordActivationEvent({
72
72
  stepName: "activation_moment_1_complete",
73
73
  sessionId: "conv-3",
@@ -1,7 +1,7 @@
1
1
  import { and, asc, eq, gt, or } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
- import { getConfig } from "../config/loader.js";
4
+ import { getCachedShareAnalytics } from "../platform/consent-cache.js";
5
5
  import { getDb } from "./db-connection.js";
6
6
  import { authFallbackEvents } from "./schema.js";
7
7
 
@@ -36,7 +36,7 @@ export function recordAuthFallbackCounts(
36
36
  windowEnd: number,
37
37
  counts: AuthFallbackCount[],
38
38
  ): number {
39
- if (!getConfig().collectUsageData) return 0;
39
+ if (!getCachedShareAnalytics()) return 0;
40
40
  if (counts.length === 0) return 0;
41
41
  const db = getDb();
42
42
  const createdAt = Date.now();
@@ -1,9 +1,7 @@
1
1
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
2
2
  import { getConfig } from "../config/loader.js";
3
- import {
4
- isUntrustedTrustClass,
5
- type TrustClass,
6
- } from "../runtime/actor-trust-resolver.js";
3
+ import { type TrustClass } from "../runtime/actor-trust-resolver.js";
4
+ import { resolveCapabilities } from "../runtime/capabilities.js";
7
5
  import { getLogger } from "../util/logger.js";
8
6
  import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
9
7
  import { isMemoryEnabled, upsertAutoAnalysisJob } from "./jobs-store.js";
@@ -118,7 +116,7 @@ export function enqueueAutoAnalysisOnCompaction(
118
116
  conversationId: string,
119
117
  trustClass: TrustClass | undefined,
120
118
  ): void {
121
- if (isUntrustedTrustClass(trustClass)) {
119
+ if (!resolveCapabilities(trustClass).canAccessMemory) {
122
120
  return;
123
121
  }
124
122
  try {
@@ -7,8 +7,7 @@ import { conversations, messageBookmarks, messages } from "./schema.js";
7
7
 
8
8
  /**
9
9
  * Wire-shape representation of a bookmark, joined with the bookmarked
10
- * message and its parent conversation. Mirrors
11
- * `clients/shared/Network/BookmarkSummary.swift` — dates are emitted as
10
+ * message and its parent conversation. Dates are emitted as
12
11
  * unix-millisecond integers, and the message preview is capped to keep
13
12
  * the list payload bounded.
14
13
  */
@@ -74,7 +74,7 @@ export interface CanonicalGuardianRequest {
74
74
  updatedAt: number;
75
75
  }
76
76
 
77
- interface CanonicalGuardianDelivery {
77
+ export interface CanonicalGuardianDelivery {
78
78
  id: string;
79
79
  requestId: string;
80
80
  destinationChannel: string;
@@ -674,6 +674,44 @@ export function listPendingCanonicalGuardianRequestsByDestinationChat(
674
674
  return pendingRequests;
675
675
  }
676
676
 
677
+ /**
678
+ * Find the pending canonical request whose guardian-facing delivery landed on a
679
+ * specific channel message (channel + chat + message id).
680
+ *
681
+ * This is the addressing key for emoji-reaction decisions: a reaction carries
682
+ * only the message it is attached to, so the delivered card's message id is how
683
+ * we recover which request the guardian acted on — even when several cards are
684
+ * pending in the same chat. Returns null when no delivery matches or the matched
685
+ * request is no longer pending.
686
+ */
687
+ export function getPendingCanonicalRequestByDestinationMessage(
688
+ destinationChannel: string,
689
+ destinationChatId: string,
690
+ destinationMessageId: string,
691
+ ): CanonicalGuardianRequest | null {
692
+ const db = getDb();
693
+
694
+ const delivery = db
695
+ .select()
696
+ .from(canonicalGuardianDeliveries)
697
+ .where(
698
+ and(
699
+ eq(canonicalGuardianDeliveries.destinationChannel, destinationChannel),
700
+ eq(canonicalGuardianDeliveries.destinationChatId, destinationChatId),
701
+ eq(
702
+ canonicalGuardianDeliveries.destinationMessageId,
703
+ destinationMessageId,
704
+ ),
705
+ ),
706
+ )
707
+ .get();
708
+
709
+ if (!delivery) return null;
710
+
711
+ const request = getCanonicalGuardianRequest(delivery.requestId);
712
+ return request && request.status === "pending" ? request : null;
713
+ }
714
+
677
715
  // ---------------------------------------------------------------------------
678
716
  // Conversation scope helpers
679
717
  // ---------------------------------------------------------------------------
@@ -129,7 +129,7 @@ export const messageMetadataSchema = z
129
129
  * and read gate (conversation history loading) to enforce trust-aware access.
130
130
  */
131
131
  provenanceTrustClass: z
132
- .enum(["guardian", "trusted_contact", "unknown"])
132
+ .enum(["guardian", "trusted_contact", "unverified_contact", "unknown"])
133
133
  .optional(),
134
134
  provenanceSourceChannel: channelIdSchema.optional(),
135
135
  provenanceGuardianExternalUserId: z.string().optional(),
@@ -2575,12 +2575,17 @@ export function getConversationOriginInterface(
2575
2575
  * in the given conversation, or `undefined` if none is found.
2576
2576
  *
2577
2577
  * Used by the pointer message trust resolver to detect conversations
2578
- * whose audience is a guardian or trusted_contact outside desktop-origin
2579
- * conversations.
2578
+ * whose audience is a guardian, trusted_contact, or unverified_contact
2579
+ * outside desktop-origin conversations.
2580
2580
  */
2581
2581
  export function getConversationRecentProvenanceTrustClass(
2582
2582
  conversationId: string,
2583
- ): "guardian" | "trusted_contact" | "unknown" | undefined {
2583
+ ):
2584
+ | "guardian"
2585
+ | "trusted_contact"
2586
+ | "unverified_contact"
2587
+ | "unknown"
2588
+ | undefined {
2584
2589
  const row = rawGet<{ metadata: string | null }>(
2585
2590
  `SELECT metadata FROM messages
2586
2591
  WHERE conversation_id = ? AND role = 'user' AND metadata IS NOT NULL
@@ -135,7 +135,16 @@ export function resolveConversationId(idOrKey: string): string | null {
135
135
  */
136
136
  export function getOrCreateConversation(
137
137
  conversationKey: string,
138
- opts?: { conversationType?: "standard" },
138
+ opts?: {
139
+ conversationType?: "standard";
140
+ /**
141
+ * Caller-supplied title for the conversation, used only when this call
142
+ * actually creates the row. Treated as a user-set title (`isAutoTitle = 0`)
143
+ * so the async LLM title generator's safe-overwrite check leaves it
144
+ * untouched. Ignored when the conversation already exists.
145
+ */
146
+ title?: string;
147
+ },
139
148
  ): {
140
149
  conversationId: string;
141
150
  conversationType: string;
@@ -205,13 +214,19 @@ export function getOrCreateConversation(
205
214
 
206
215
  const now = Date.now();
207
216
  const conversationId = uuid();
208
- const title = GENERATING_TITLE;
217
+ const customTitle = opts?.title?.trim();
218
+ const title = customTitle || GENERATING_TITLE;
209
219
  const memoryScopeId = "default";
210
220
 
211
221
  tx.insert(conversations)
212
222
  .values({
213
223
  id: conversationId,
214
224
  title,
225
+ // A caller-supplied title is user-set: mark it non-auto (0) so the
226
+ // async LLM title generator's `canReplaceTitle` check won't overwrite
227
+ // it. Without one, omit the column so it takes its default
228
+ // (AUTO_TITLE_LLM) and follows the auto-generated placeholder flow.
229
+ ...(customTitle ? { isAutoTitle: 0 } : {}),
215
230
  createdAt: now,
216
231
  updatedAt: now,
217
232
  totalInputTokens: 0,
@@ -166,6 +166,7 @@ export async function generateAndPersistConversationTitle(
166
166
  const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
167
167
  updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
168
168
  publishConversationTitleChanged(conversationId, fallback);
169
+ logRetryableFallback(params, "no_provider");
169
170
  return { title: fallback, updated: true };
170
171
  }
171
172
 
@@ -206,6 +207,7 @@ export async function generateAndPersistConversationTitle(
206
207
  const fallback = deriveFallbackTitle(context) ?? UNTITLED_FALLBACK;
207
208
  updateConversationTitle(conversationId, fallback, AUTO_TITLE_DETERMINISTIC);
208
209
  publishConversationTitleChanged(conversationId, fallback);
210
+ logRetryableFallback(params, "empty_output");
209
211
  return { title: fallback, updated: true };
210
212
  }
211
213
 
@@ -230,7 +232,7 @@ export const titleMutex = new Mutex();
230
232
  /**
231
233
  * Fire-and-forget wrapper for title generation. Failures are logged
232
234
  * but do not propagate. On failure, replaces loading placeholder with
233
- * a stable fallback title so loading state is never permanent.
235
+ * a retryable fallback title so loading state is never permanent.
234
236
  *
235
237
  * Calls are serialized via {@link titleMutex} so burst conversation
236
238
  * creation does not overwhelm the LLM provider.
@@ -244,16 +246,20 @@ export function queueGenerateConversationTitle(
244
246
  })
245
247
  .catch((err) => {
246
248
  log.warn(
247
- { err, conversationId: params.conversationId },
248
- "Failed to generate conversation title (non-fatal)",
249
+ retryableFallbackLogFields(params, "generation_error", err),
250
+ "Conversation title generation used retryable fallback",
249
251
  );
250
- // Replace loading placeholder with stable fallback
252
+ // Replace loading placeholder with a retryable fallback.
251
253
  try {
252
254
  const conversation = getConversation(params.conversationId);
253
255
  if (conversation && conversation.title === GENERATING_TITLE) {
254
256
  const fallback =
255
257
  deriveFallbackTitle(params.context) ?? UNTITLED_FALLBACK;
256
- updateConversationTitle(params.conversationId, fallback);
258
+ updateConversationTitle(
259
+ params.conversationId,
260
+ fallback,
261
+ AUTO_TITLE_DETERMINISTIC,
262
+ );
257
263
  publishConversationTitleChanged(params.conversationId, fallback);
258
264
  }
259
265
  } catch {
@@ -268,6 +274,11 @@ export interface RegenerateTitleParams {
268
274
  conversationId: string;
269
275
  provider?: Provider;
270
276
  signal?: AbortSignal;
277
+ /**
278
+ * Limit regeneration to placeholder or deterministic titles. Used for retrying
279
+ * failed initial generation without racing against a successful initial title.
280
+ */
281
+ onlyIfReplaceable?: boolean;
271
282
  }
272
283
 
273
284
  /**
@@ -278,12 +289,15 @@ export interface RegenerateTitleParams {
278
289
  export async function regenerateConversationTitle(
279
290
  params: RegenerateTitleParams,
280
291
  ): Promise<{ title: string; updated: boolean }> {
281
- const { conversationId, signal } = params;
292
+ const { conversationId, onlyIfReplaceable, signal } = params;
282
293
 
283
294
  const conversation = getConversation(conversationId);
284
295
  if (!conversation || !conversation.isAutoTitle) {
285
296
  return { title: conversation?.title ?? UNTITLED_FALLBACK, updated: false };
286
297
  }
298
+ if (onlyIfReplaceable && !canReplaceTitle(conversation)) {
299
+ return { title: conversation.title ?? UNTITLED_FALLBACK, updated: false };
300
+ }
287
301
 
288
302
  const provider =
289
303
  params.provider ?? (await getConfiguredProvider("conversationTitle"));
@@ -317,7 +331,11 @@ export async function regenerateConversationTitle(
317
331
  if (title) {
318
332
  // Re-check isAutoTitle before persisting (race guard against manual rename)
319
333
  const current = getConversation(conversationId);
320
- if (!current || !current.isAutoTitle) {
334
+ if (
335
+ !current ||
336
+ !current.isAutoTitle ||
337
+ (onlyIfReplaceable && !canReplaceTitle(current))
338
+ ) {
321
339
  return { title: current?.title ?? UNTITLED_FALLBACK, updated: false };
322
340
  }
323
341
 
@@ -408,6 +426,45 @@ function buildTitlePrompt(
408
426
  return parts.join("\n");
409
427
  }
410
428
 
429
+ function titleGenerationLogFields(params: GenerateTitleParams) {
430
+ return {
431
+ conversationId: params.conversationId,
432
+ contextOrigin: params.context?.origin,
433
+ hasContext: Boolean(params.context),
434
+ userMessageLength: params.userMessage?.length ?? 0,
435
+ assistantResponseLength: params.assistantResponse?.length ?? 0,
436
+ };
437
+ }
438
+
439
+ type TitleGenerationFallbackReason =
440
+ | "no_provider"
441
+ | "empty_output"
442
+ | "generation_error";
443
+
444
+ function retryableFallbackLogFields(
445
+ params: GenerateTitleParams,
446
+ reason: TitleGenerationFallbackReason,
447
+ err?: unknown,
448
+ ): Record<string, unknown> {
449
+ const fields: Record<string, unknown> = {
450
+ ...titleGenerationLogFields(params),
451
+ reason,
452
+ fallbackSource: params.context ? "context" : "untitled",
453
+ };
454
+ if (err) fields.err = err;
455
+ return fields;
456
+ }
457
+
458
+ function logRetryableFallback(
459
+ params: GenerateTitleParams,
460
+ reason: TitleGenerationFallbackReason,
461
+ ): void {
462
+ log.warn(
463
+ retryableFallbackLogFields(params, reason),
464
+ "Conversation title generation used retryable fallback",
465
+ );
466
+ }
467
+
411
468
  const META_FAILURE_TITLES = new Set([
412
469
  "missing context",
413
470
  "no context",
@@ -40,6 +40,7 @@ import {
40
40
  createSkillLoadedEventsTable,
41
41
  createTasksAndWorkItemsTables,
42
42
  createWatchersAndLogsTables,
43
+ dropApprovalPromptTsTrackerTable,
43
44
  migrate230AcpSessionHistory,
44
45
  migrate231RepairMemoryGraphEventDates,
45
46
  migrateA2ATasks,
@@ -68,6 +69,7 @@ import {
68
69
  migrateChannelInboundDeliveryAttempts,
69
70
  migrateChannelInteractionColumns,
70
71
  migrateContactChannelsAccessFields,
72
+ migrateContactChannelsRenormalizeAddresses,
71
73
  migrateContactChannelsTypeChatIdIndex,
72
74
  migrateContactChannelsUniqueExtUser,
73
75
  migrateContactsAssistantId,
@@ -101,6 +103,7 @@ import {
101
103
  migrateDropConflicts,
102
104
  migrateDropContactInteractionColumns,
103
105
  migrateDropEntityTables,
106
+ migrateDropExternalUserId,
104
107
  migrateDropLegacyMemberGuardianTables,
105
108
  migrateDropLoopbackPortColumn,
106
109
  migrateDropMemoryItemsTables,
@@ -194,7 +197,9 @@ import {
194
197
  migrateRenameVerificationSessionIdColumn,
195
198
  migrateRenameVerificationTable,
196
199
  migrateRenameVoiceToPhone,
200
+ migrateRewriteBalancedEconomyProfilePins,
197
201
  migrateScheduleCapabilities,
202
+ migrateScheduleDefaultNoReuseConversation,
198
203
  migrateScheduleDescription,
199
204
  migrateScheduleInferenceProfile,
200
205
  migrateScheduleOneShotRouting,
@@ -222,6 +227,7 @@ import {
222
227
  migrateUsageLlmCallCount,
223
228
  migrateVoiceInviteColumns,
224
229
  migrateVoiceInviteDisplayMetadata,
230
+ migrateWorkflowJournalLeafTokens,
225
231
  migrateWorkflowRuns,
226
232
  migrateWorkflowRunTrust,
227
233
  recoverCrashedMigrations,
@@ -229,6 +235,7 @@ import {
229
235
  runLateMigrations,
230
236
  validateMigrationState,
231
237
  } from "./migrations/index.js";
238
+ import { runMigrationSteps } from "./migrations/run-migrations.js";
232
239
 
233
240
  // ---------------------------------------------------------------------------
234
241
  // Test DB template — run migrations once, reuse across test files
@@ -518,29 +525,22 @@ export function initializeDb(): void {
518
525
  migrateBackfillOriginChannelFromBindings,
519
526
  migrateContactChannelsUniqueExtUser,
520
527
  migrateScheduleCapabilities,
528
+ migrateContactChannelsRenormalizeAddresses,
529
+ migrateScheduleDefaultNoReuseConversation,
530
+ migrateWorkflowJournalLeafTokens,
531
+ migrateDropExternalUserId,
532
+ dropApprovalPromptTsTrackerTable,
533
+ migrateRewriteBalancedEconomyProfilePins,
521
534
  ];
522
535
 
523
536
  // Run each migration step, catching and logging individual failures so one
524
537
  // broken migration doesn't prevent independent later ones from succeeding.
525
- const failures: string[] = [];
526
- for (const step of migrationSteps) {
527
- try {
528
- log.debug({ migration: step.name }, `Starting migration: ${step.name}`);
529
- step(database);
530
- log.debug({ migration: step.name }, `Migration succeeded: ${step.name}`);
531
- } catch (err) {
532
- failures.push(step.name);
533
- log.error(
534
- { err, migration: step.name },
535
- `Migration failed: ${step.name}`,
536
- );
537
- }
538
- }
538
+ const { failed } = runMigrationSteps(database, migrationSteps);
539
539
 
540
- if (failures.length > 0) {
540
+ if (failed.length > 0) {
541
541
  log.error(
542
- { failedMigrations: failures, count: failures.length },
543
- `DB initialization completed with ${failures.length} failed migration(s)`,
542
+ { failedMigrations: failed, count: failed.length },
543
+ `DB initialization completed with ${failed.length} failed migration(s)`,
544
544
  );
545
545
  }
546
546
 
@@ -7,6 +7,13 @@ import { PLATFORM_PROVIDER_META } from "../providers/platform-proxy/constants.js
7
7
  import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
8
8
  import { getProviderKeyAsync } from "../security/secure-keys.js";
9
9
  import { getLogger } from "../util/logger.js";
10
+ import {
11
+ EmbeddingBillingBlockError,
12
+ extractHttpStatus,
13
+ isEmbeddingBillingBreakerOpen,
14
+ recordBillingBlock,
15
+ recordBillingSuccess,
16
+ } from "./embedding-billing-breaker.js";
10
17
  import { GeminiEmbeddingBackend } from "./embedding-gemini.js";
11
18
  import { OllamaEmbeddingBackend } from "./embedding-ollama.js";
12
19
  import { OpenAIEmbeddingBackend } from "./embedding-openai.js";
@@ -490,6 +497,20 @@ export async function getMemoryBackendStatus(config: AssistantConfig): Promise<{
490
497
  };
491
498
  }
492
499
 
500
+ /**
501
+ * Thrown by {@link embedWithBackend} when no embedding backend is configured or
502
+ * available. This is a PROCESS-WIDE condition (backend selection is effectively
503
+ * cached for the run), so a caller embedding many items should treat the first
504
+ * occurrence as fatal to the whole batch rather than a per-item failure — see
505
+ * `backfillAllSections`, which aborts on it instead of churning through deletes.
506
+ */
507
+ export class EmbeddingBackendUnavailableError extends Error {
508
+ constructor(message = "No memory embedding backend configured") {
509
+ super(message);
510
+ this.name = "EmbeddingBackendUnavailableError";
511
+ }
512
+ }
513
+
493
514
  export async function embedWithBackend(
494
515
  config: AssistantConfig,
495
516
  inputs: EmbeddingInput[],
@@ -499,9 +520,15 @@ export async function embedWithBackend(
499
520
  model: string;
500
521
  vectors: number[][];
501
522
  }> {
523
+ // Fail-fast when the billing breaker is open — avoids burning a network
524
+ // round-trip on every caller (embed lane jobs, activation recompute, etc.).
525
+ if (isEmbeddingBillingBreakerOpen()) {
526
+ throw new EmbeddingBillingBlockError();
527
+ }
528
+
502
529
  const selection = await selectEmbeddingBackend(config);
503
530
  if (!selection.backend) {
504
- throw new Error(
531
+ throw new EmbeddingBackendUnavailableError(
505
532
  selection.reason ?? "No memory embedding backend configured",
506
533
  );
507
534
  }
@@ -612,6 +639,10 @@ export async function embedWithBackend(
612
639
  );
613
640
  }
614
641
 
642
+ // A successful backend call proves billing is active — close the
643
+ // breaker if it was in the probe window.
644
+ recordBillingSuccess();
645
+
615
646
  if (isPrimary) {
616
647
  const merged = [...cached] as number[][];
617
648
  for (let i = 0; i < uncachedIndices.length; i++) {
@@ -626,6 +657,12 @@ export async function embedWithBackend(
626
657
  return { provider: backend.provider, model: backend.model, vectors };
627
658
  } catch (err) {
628
659
  lastErr = err;
660
+ // If ANY backend in the chain returns 402, trip the billing breaker
661
+ // immediately — fallbacks will hit the same depleted balance.
662
+ if (extractHttpStatus(err) === 402) {
663
+ recordBillingBlock();
664
+ throw err;
665
+ }
629
666
  if (backends.length > 1) {
630
667
  log.warn(
631
668
  { err, provider: backend.provider },
@@ -0,0 +1,96 @@
1
+ import { getLogger } from "../util/logger.js";
2
+
3
+ const log = getLogger("embedding-billing-breaker");
4
+
5
+ /**
6
+ * Lightweight circuit breaker for embedding billing blocks (HTTP 402).
7
+ *
8
+ * Unlike the Qdrant circuit breaker (which needs a failure threshold and
9
+ * half-open probe logic), billing exhaustion is deterministic — a single
10
+ * 402 means the org is depleted and every subsequent call will fail
11
+ * identically. The breaker opens immediately on the first 402 and stays
12
+ * open for COOLDOWN_MS, then allows one probe through. A successful
13
+ * probe (no 402) closes the breaker; a failed probe re-opens it.
14
+ */
15
+
16
+ const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes
17
+
18
+ type BreakerState = "closed" | "open";
19
+
20
+ let breakerState: BreakerState = "closed";
21
+ let openedAt = 0;
22
+
23
+ export class EmbeddingBillingBlockError extends Error {
24
+ constructor() {
25
+ super("Embedding billing breaker open — org balance depleted");
26
+ this.name = "EmbeddingBillingBlockError";
27
+ }
28
+ }
29
+
30
+ /** Trip the breaker after a 402 billing block. */
31
+ export function recordBillingBlock(): void {
32
+ if (breakerState === "closed") {
33
+ log.warn(
34
+ { cooldownMs: COOLDOWN_MS },
35
+ "Embedding billing breaker opened — embedding jobs paused until probe succeeds",
36
+ );
37
+ }
38
+ breakerState = "open";
39
+ openedAt = Date.now();
40
+ }
41
+
42
+ /**
43
+ * Clear the breaker after a successful embedding call during the probe
44
+ * window. Ignored when the breaker is closed or when the cooldown has not
45
+ * yet elapsed — this prevents a concurrent in-flight embed job (started
46
+ * before the breaker opened) from prematurely closing a freshly-tripped
47
+ * breaker.
48
+ */
49
+ export function recordBillingSuccess(): void {
50
+ if (breakerState !== "open") return;
51
+ if (Date.now() - openedAt < COOLDOWN_MS) return;
52
+ log.info("Embedding billing breaker closed — billing probe succeeded");
53
+ breakerState = "closed";
54
+ openedAt = 0;
55
+ }
56
+
57
+ /** True when the breaker is open and the cooldown has NOT yet elapsed. */
58
+ export function isEmbeddingBillingBreakerOpen(): boolean {
59
+ if (breakerState === "closed") return false;
60
+ if (Date.now() - openedAt >= COOLDOWN_MS) return false;
61
+ return true;
62
+ }
63
+
64
+ /**
65
+ * True when the breaker is open but the cooldown has elapsed, meaning the
66
+ * next embed job should be allowed through as a probe to check whether
67
+ * the org has been re-funded.
68
+ */
69
+ export function shouldAllowBillingProbe(): boolean {
70
+ return breakerState === "open" && Date.now() - openedAt >= COOLDOWN_MS;
71
+ }
72
+
73
+ /** Extract an HTTP status code from an error, if present. */
74
+ export function extractHttpStatus(err: unknown): number | undefined {
75
+ if (err == null || typeof err !== "object") return undefined;
76
+
77
+ // SDK-style errors (OpenAI, Anthropic) carry `.status` directly.
78
+ if ("status" in err) {
79
+ const s = (err as { status?: unknown }).status;
80
+ if (typeof s === "number") return s;
81
+ }
82
+
83
+ // Gemini/Ollama backends embed the status in the message: "... (402): ..."
84
+ if (err instanceof Error) {
85
+ const match = err.message.match(/\((\d{3})\)/);
86
+ if (match) return parseInt(match[1], 10);
87
+ }
88
+
89
+ return undefined;
90
+ }
91
+
92
+ /** @internal Test-only: reset breaker state. */
93
+ export function _resetEmbeddingBillingBreaker(): void {
94
+ breakerState = "closed";
95
+ openedAt = 0;
96
+ }
@@ -5,6 +5,10 @@ import { getConfig } from "../config/loader.js";
5
5
  import { getLogger } from "../util/logger.js";
6
6
  import { truncate } from "../util/truncate.js";
7
7
  import { getDb } from "./db-connection.js";
8
+ import {
9
+ isEmbeddingBillingBreakerOpen,
10
+ shouldAllowBillingProbe,
11
+ } from "./embedding-billing-breaker.js";
8
12
  import {
9
13
  isQdrantBreakerOpen,
10
14
  shouldAllowQdrantProbe,
@@ -533,24 +537,32 @@ export function claimMemoryJobs(limits: LaneBudgets): MemoryJob[] {
533
537
  .all()
534
538
  : [];
535
539
 
536
- // Embed lane: gated by the Qdrant circuit breaker. When the breaker is open,
537
- // skip embed jobs entirely — they would just be claimed → fail → deferred,
538
- // wasting CPU cycles. Exception: if the cooldown has elapsed (breaker ready
539
- // for half-open probe), allow exactly 1 embed job through so the breaker
540
- // can self-heal. Note: this gate applies ONLY to the embed lane; slow and
541
- // fast lanes run unimpeded.
542
- const breakerOpen = isQdrantBreakerOpen();
543
- const probeAllowed = breakerOpen && shouldAllowQdrantProbe();
544
- const skipEmbedJobs = breakerOpen && !probeAllowed;
540
+ // Embed lane: gated by both the Qdrant circuit breaker and the billing
541
+ // breaker. When either breaker is open, skip embed jobs entirely — they
542
+ // would just be claimed fail deferred, wasting CPU cycles. Exception:
543
+ // if the cooldown has elapsed (breaker ready for probe), allow exactly
544
+ // 1 embed job through so the breaker can self-heal.
545
+ const qdrantBreakerOpen = isQdrantBreakerOpen();
546
+ const qdrantProbeAllowed = qdrantBreakerOpen && shouldAllowQdrantProbe();
547
+ const billingBreakerOpen = isEmbeddingBillingBreakerOpen();
548
+ const billingProbeAllowed = !billingBreakerOpen && shouldAllowBillingProbe();
549
+ const skipEmbedJobs =
550
+ (qdrantBreakerOpen && !qdrantProbeAllowed) ||
551
+ (billingBreakerOpen && !billingProbeAllowed);
552
+ const probeAllowed = qdrantProbeAllowed || billingProbeAllowed;
545
553
  const embedLimit = probeAllowed ? Math.min(1, limits.embed) : limits.embed;
546
554
 
547
555
  if (skipEmbedJobs && limits.embed > 0) {
548
- log.debug("Skipping embed job claims — Qdrant circuit breaker is open");
556
+ if (billingBreakerOpen) {
557
+ log.debug(
558
+ "Skipping embed job claims — embedding billing breaker is open",
559
+ );
560
+ } else {
561
+ log.debug("Skipping embed job claims — Qdrant circuit breaker is open");
562
+ }
549
563
  }
550
564
  if (probeAllowed && limits.embed > 0) {
551
- log.debug(
552
- "Allowing 1 embed probe job — Qdrant circuit breaker cooldown elapsed",
553
- );
565
+ log.debug("Allowing 1 embed probe job — breaker cooldown elapsed");
554
566
  }
555
567
 
556
568
  const embedCandidates =