@vellumai/assistant 0.4.56 → 0.5.0

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 (457) hide show
  1. package/ARCHITECTURE.md +10 -10
  2. package/Dockerfile +3 -0
  3. package/README.md +11 -11
  4. package/docs/architecture/integrations.md +2 -2
  5. package/docs/architecture/memory.md +3 -4
  6. package/docs/credential-execution-service.md +13 -20
  7. package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
  8. package/package.json +1 -1
  9. package/src/__tests__/actor-token-service.test.ts +7 -7
  10. package/src/__tests__/anthropic-provider.test.ts +172 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
  12. package/src/__tests__/approval-cascade.test.ts +2 -2
  13. package/src/__tests__/approval-routes-http.test.ts +3 -4
  14. package/src/__tests__/asset-materialize-tool.test.ts +5 -5
  15. package/src/__tests__/asset-search-tool.test.ts +1 -1
  16. package/src/__tests__/assistant-attachments.test.ts +5 -5
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
  19. package/src/__tests__/attachments-store.test.ts +2 -2
  20. package/src/__tests__/avatar-e2e.test.ts +5 -3
  21. package/src/__tests__/browser-skill-endstate.test.ts +0 -1
  22. package/src/__tests__/call-routes-http.test.ts +2 -2
  23. package/src/__tests__/callback-handoff-copy.test.ts +1 -1
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
  25. package/src/__tests__/channel-readiness-routes.test.ts +0 -1
  26. package/src/__tests__/channel-readiness-service.test.ts +0 -1
  27. package/src/__tests__/checker.test.ts +31 -32
  28. package/src/__tests__/chrome-cdp.test.ts +47 -18
  29. package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
  30. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  31. package/src/__tests__/config-schema.test.ts +9 -18
  32. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
  34. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
  35. package/src/__tests__/conversation-agent-loop.test.ts +11 -4
  36. package/src/__tests__/conversation-attachments.test.ts +1 -1
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
  38. package/src/__tests__/conversation-error.test.ts +33 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
  40. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  41. package/src/__tests__/conversation-pairing.test.ts +1 -1
  42. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
  44. package/src/__tests__/conversation-queue.test.ts +23 -14
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +204 -185
  47. package/src/__tests__/conversation-seed-composer.test.ts +1 -1
  48. package/src/__tests__/conversation-slash-queue.test.ts +4 -4
  49. package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
  50. package/src/__tests__/conversation-starter-routes.test.ts +291 -0
  51. package/src/__tests__/conversation-wipe.test.ts +438 -0
  52. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
  53. package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
  54. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
  55. package/src/__tests__/credential-security-e2e.test.ts +20 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault-unit.test.ts +227 -0
  58. package/src/__tests__/credentials-cli.test.ts +3 -0
  59. package/src/__tests__/date-context.test.ts +59 -377
  60. package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
  61. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
  62. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  63. package/src/__tests__/encrypted-store.test.ts +249 -15
  64. package/src/__tests__/ephemeral-permissions.test.ts +4 -5
  65. package/src/__tests__/event-bus.test.ts +3 -3
  66. package/src/__tests__/file-read-tool.test.ts +40 -0
  67. package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
  68. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  69. package/src/__tests__/gemini-image-service.test.ts +4 -4
  70. package/src/__tests__/gemini-provider.test.ts +6 -9
  71. package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
  72. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  73. package/src/__tests__/host-file-read-tool.test.ts +87 -0
  74. package/src/__tests__/host-shell-tool.test.ts +6 -6
  75. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  76. package/src/__tests__/identity-intro-cache.test.ts +209 -0
  77. package/src/__tests__/intent-routing.test.ts +51 -99
  78. package/src/__tests__/invite-routes-http.test.ts +5 -0
  79. package/src/__tests__/list-messages-attachments.test.ts +1 -1
  80. package/src/__tests__/managed-proxy-context.test.ts +2 -5
  81. package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
  82. package/src/__tests__/media-generate-image.test.ts +32 -15
  83. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  84. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
  86. package/src/__tests__/memory-recall-quality.test.ts +4 -3
  87. package/src/__tests__/memory-regressions.test.ts +86 -90
  88. package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
  89. package/src/__tests__/migration-export-http.test.ts +26 -27
  90. package/src/__tests__/migration-import-commit-http.test.ts +165 -37
  91. package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
  92. package/src/__tests__/migration-validate-http.test.ts +16 -16
  93. package/src/__tests__/model-intents.test.ts +2 -2
  94. package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
  95. package/src/__tests__/non-member-access-request.test.ts +3 -3
  96. package/src/__tests__/notification-broadcaster.test.ts +1 -1
  97. package/src/__tests__/notification-decision-fallback.test.ts +2 -2
  98. package/src/__tests__/notification-decision-identity.test.ts +8 -9
  99. package/src/__tests__/notification-decision-strategy.test.ts +1 -1
  100. package/src/__tests__/notification-deep-link.test.ts +1 -1
  101. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  102. package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
  103. package/src/__tests__/oauth-store.test.ts +1 -3
  104. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
  105. package/src/__tests__/onboarding-template-contract.test.ts +23 -59
  106. package/src/__tests__/provider-error-scenarios.test.ts +154 -0
  107. package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
  108. package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
  109. package/src/__tests__/provider-registry-ollama.test.ts +5 -2
  110. package/src/__tests__/qdrant-manager.test.ts +7 -7
  111. package/src/__tests__/ratelimit.test.ts +0 -74
  112. package/src/__tests__/recording-handler.test.ts +0 -1
  113. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
  115. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  116. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  117. package/src/__tests__/scheduler-recurrence.test.ts +46 -2
  118. package/src/__tests__/schema-transforms.test.ts +114 -54
  119. package/src/__tests__/secret-onetime-send.test.ts +20 -0
  120. package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
  121. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  122. package/src/__tests__/send-endpoint-busy.test.ts +63 -4
  123. package/src/__tests__/send-notification-tool.test.ts +2 -2
  124. package/src/__tests__/shell-credential-ref.test.ts +0 -1
  125. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
  126. package/src/__tests__/skill-memory.test.ts +549 -0
  127. package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
  128. package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
  129. package/src/__tests__/slack-channel-config.test.ts +109 -94
  130. package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
  131. package/src/__tests__/swarm-recursion.test.ts +2 -2
  132. package/src/__tests__/swarm-tool.test.ts +2 -2
  133. package/src/__tests__/system-prompt.test.ts +19 -66
  134. package/src/__tests__/telegram-config.test.ts +121 -0
  135. package/src/__tests__/terminal-tools.test.ts +1 -1
  136. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  138. package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
  139. package/src/__tests__/tool-executor.test.ts +1 -1
  140. package/src/__tests__/trace-emitter.test.ts +8 -1
  141. package/src/__tests__/trust-store.test.ts +7 -8
  142. package/src/__tests__/twilio-routes.test.ts +1 -18
  143. package/src/__tests__/user-reference.test.ts +82 -2
  144. package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
  145. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  146. package/src/approvals/guardian-request-resolvers.ts +3 -3
  147. package/src/avatar/ascii-renderer.ts +2 -2
  148. package/src/avatar/png-renderer.ts +2 -2
  149. package/src/avatar/resvg-lazy.ts +21 -0
  150. package/src/calls/guardian-dispatch.ts +1 -1
  151. package/src/calls/relay-access-wait.ts +2 -2
  152. package/src/calls/twilio-rest.ts +0 -248
  153. package/src/cli/AGENTS.md +5 -8
  154. package/src/cli/__tests__/notifications.test.ts +5 -5
  155. package/src/cli/commands/avatar.ts +64 -2
  156. package/src/cli/commands/conversations.ts +131 -1
  157. package/src/cli/commands/credentials.ts +2 -0
  158. package/src/cli/commands/notifications.ts +3 -3
  159. package/src/cli.ts +10 -0
  160. package/src/config/bundled-skills/acp/SKILL.md +5 -5
  161. package/src/config/bundled-skills/acp/TOOLS.json +6 -6
  162. package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
  163. package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
  164. package/src/config/bundled-skills/browser/SKILL.md +15 -15
  165. package/src/config/bundled-skills/browser/TOOLS.json +14 -14
  166. package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
  167. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
  168. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
  169. package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
  170. package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
  171. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
  172. package/src/config/bundled-skills/contacts/SKILL.md +3 -3
  173. package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
  174. package/src/config/bundled-skills/document/SKILL.md +4 -4
  175. package/src/config/bundled-skills/document/TOOLS.json +2 -2
  176. package/src/config/bundled-skills/followups/TOOLS.json +3 -3
  177. package/src/config/bundled-skills/gmail/SKILL.md +32 -32
  178. package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
  179. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
  180. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
  181. package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
  182. package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
  183. package/src/config/bundled-skills/google-calendar/types.ts +1 -1
  184. package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
  185. package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
  186. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
  187. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
  188. package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
  189. package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
  190. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
  191. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
  192. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
  193. package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
  194. package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
  195. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
  196. package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
  197. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
  198. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
  199. package/src/config/bundled-skills/messaging/SKILL.md +29 -25
  200. package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
  201. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  202. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  203. package/src/config/bundled-skills/notifications/SKILL.md +3 -3
  204. package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
  205. package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
  206. package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
  207. package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
  208. package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
  209. package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
  210. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
  211. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
  212. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
  213. package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
  214. package/src/config/bundled-skills/schedule/SKILL.md +26 -26
  215. package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
  216. package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
  217. package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
  218. package/src/config/bundled-skills/sequences/SKILL.md +2 -2
  219. package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
  220. package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
  221. package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
  222. package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
  223. package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
  224. package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
  225. package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
  226. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
  227. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  228. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
  229. package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
  230. package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
  231. package/src/config/bundled-skills/slack/SKILL.md +2 -2
  232. package/src/config/bundled-skills/slack/TOOLS.json +8 -8
  233. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
  234. package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
  235. package/src/config/bundled-skills/tasks/SKILL.md +1 -1
  236. package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
  237. package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
  238. package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
  239. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
  240. package/src/config/bundled-skills/watcher/SKILL.md +4 -4
  241. package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
  242. package/src/config/feature-flag-registry.json +33 -17
  243. package/src/config/schemas/sandbox.ts +1 -1
  244. package/src/config/schemas/services.ts +13 -3
  245. package/src/config/schemas/timeouts.ts +0 -10
  246. package/src/contacts/contact-store.ts +63 -0
  247. package/src/contacts/contacts-write.ts +1 -1
  248. package/src/daemon/assistant-attachments.ts +2 -2
  249. package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
  250. package/src/daemon/conversation-agent-loop.ts +7 -30
  251. package/src/daemon/conversation-error.ts +24 -0
  252. package/src/daemon/conversation-memory.ts +8 -7
  253. package/src/daemon/conversation-runtime-assembly.ts +141 -275
  254. package/src/daemon/conversation-slash.ts +7 -26
  255. package/src/daemon/conversation-surfaces.ts +14 -0
  256. package/src/daemon/conversation-tool-setup.ts +9 -8
  257. package/src/daemon/conversation.ts +2 -0
  258. package/src/daemon/daemon-control.ts +1 -1
  259. package/src/daemon/date-context.ts +10 -83
  260. package/src/daemon/handlers/config-channels.ts +12 -2
  261. package/src/daemon/handlers/config-slack-channel.ts +7 -1
  262. package/src/daemon/handlers/config-telegram.ts +6 -1
  263. package/src/daemon/handlers/conversations.ts +2 -2
  264. package/src/daemon/handlers/skills.ts +4 -0
  265. package/src/daemon/lifecycle.ts +28 -4
  266. package/src/daemon/providers-setup.ts +1 -1
  267. package/src/daemon/server.ts +1 -5
  268. package/src/daemon/shutdown-handlers.ts +9 -3
  269. package/src/daemon/tool-side-effects.ts +40 -0
  270. package/src/daemon/trace-emitter.ts +26 -2
  271. package/src/events/domain-events.ts +1 -1
  272. package/src/events/tool-permission-telemetry-listener.ts +46 -0
  273. package/src/inbound/platform-callback-registration.ts +0 -18
  274. package/src/media/app-icon-generator.ts +15 -8
  275. package/src/media/avatar-router.ts +15 -8
  276. package/src/media/gemini-image-service.ts +125 -21
  277. package/src/memory/attachments-store.ts +3 -3
  278. package/src/memory/channel-verification-sessions.ts +6 -6
  279. package/src/memory/conversation-crud.ts +196 -1
  280. package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
  281. package/src/memory/conversation-title-service.ts +2 -3
  282. package/src/memory/db-init.ts +25 -1
  283. package/src/memory/invite-store.ts +4 -4
  284. package/src/memory/items-extractor.ts +4 -4
  285. package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
  286. package/src/memory/jobs-store.ts +3 -2
  287. package/src/memory/jobs-worker.ts +7 -5
  288. package/src/memory/lifecycle-events-store.ts +63 -0
  289. package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
  290. package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
  291. package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
  292. package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
  293. package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
  294. package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
  295. package/src/memory/migrations/index.ts +6 -0
  296. package/src/memory/migrations/registry.ts +13 -0
  297. package/src/memory/retriever.test.ts +223 -96
  298. package/src/memory/retriever.ts +115 -138
  299. package/src/memory/schema/calls.ts +1 -1
  300. package/src/memory/schema/contacts.ts +1 -1
  301. package/src/memory/schema/infrastructure.ts +29 -0
  302. package/src/memory/schema/memory-core.ts +7 -17
  303. package/src/memory/schema/notifications.ts +1 -1
  304. package/src/memory/search/formatting.ts +23 -6
  305. package/src/memory/search/lexical.ts +2 -0
  306. package/src/memory/search/semantic.ts +2 -0
  307. package/src/memory/search/staleness.ts +5 -1
  308. package/src/memory/search/types.ts +4 -0
  309. package/src/memory/task-memory-cleanup.ts +96 -6
  310. package/src/memory/trace-event-store.ts +148 -0
  311. package/src/notifications/README.md +1 -1
  312. package/src/notifications/decision-engine.ts +45 -4
  313. package/src/notifications/emit-signal.ts +5 -4
  314. package/src/notifications/events-store.ts +4 -4
  315. package/src/notifications/signal.ts +1 -1
  316. package/src/oauth/manual-token-connection.ts +49 -25
  317. package/src/permissions/checker.ts +6 -5
  318. package/src/permissions/defaults.ts +4 -4
  319. package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
  320. package/src/prompts/cache-boundary.ts +8 -0
  321. package/src/prompts/system-prompt.ts +105 -634
  322. package/src/prompts/templates/BOOTSTRAP.md +172 -33
  323. package/src/prompts/templates/IDENTITY.md +8 -24
  324. package/src/prompts/templates/SOUL.md +20 -41
  325. package/src/prompts/templates/USER.md +3 -19
  326. package/src/prompts/user-reference.ts +14 -16
  327. package/src/providers/anthropic/client.ts +51 -19
  328. package/src/providers/gemini/client.ts +6 -9
  329. package/src/providers/managed-proxy/constants.ts +1 -7
  330. package/src/providers/managed-proxy/context.ts +0 -1
  331. package/src/providers/model-intents.ts +5 -5
  332. package/src/providers/openai/client.ts +10 -1
  333. package/src/providers/openrouter/client.ts +1 -0
  334. package/src/providers/ratelimit.ts +0 -35
  335. package/src/providers/registry.ts +3 -5
  336. package/src/providers/retry.ts +18 -1
  337. package/src/runtime/access-request-helper.ts +16 -2
  338. package/src/runtime/auth/route-policy.ts +7 -0
  339. package/src/runtime/channel-verification-service.ts +1 -1
  340. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  341. package/src/runtime/guardian-vellum-migration.ts +61 -1
  342. package/src/runtime/http-server.ts +8 -4
  343. package/src/runtime/migrations/vbundle-builder.ts +212 -32
  344. package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
  345. package/src/runtime/migrations/vbundle-importer.ts +66 -1
  346. package/src/runtime/migrations/vbundle-validator.ts +17 -3
  347. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
  348. package/src/runtime/routes/attachment-routes.ts +2 -2
  349. package/src/runtime/routes/btw-routes.ts +93 -0
  350. package/src/runtime/routes/channel-verification-routes.ts +19 -2
  351. package/src/runtime/routes/conversation-management-routes.ts +55 -1
  352. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-routes.ts +49 -5
  354. package/src/runtime/routes/conversation-starter-routes.ts +207 -0
  355. package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
  356. package/src/runtime/routes/identity-intro-cache.ts +105 -0
  357. package/src/runtime/routes/identity-routes.ts +51 -0
  358. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
  359. package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
  360. package/src/runtime/routes/migration-routes.ts +25 -13
  361. package/src/runtime/routes/secret-routes.ts +18 -0
  362. package/src/runtime/routes/settings-routes.ts +9 -9
  363. package/src/runtime/routes/telemetry-routes.ts +53 -0
  364. package/src/runtime/routes/trace-event-routes.ts +62 -0
  365. package/src/runtime/tool-grant-request-helper.ts +1 -1
  366. package/src/runtime/verification-outbound-actions.ts +47 -31
  367. package/src/security/encrypted-store.ts +262 -78
  368. package/src/skills/catalog-install.ts +10 -0
  369. package/src/skills/managed-store.ts +2 -0
  370. package/src/skills/skill-memory.ts +222 -0
  371. package/src/subagent/manager.ts +1 -4
  372. package/src/telemetry/types.ts +10 -1
  373. package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
  374. package/src/telemetry/usage-telemetry-reporter.ts +53 -4
  375. package/src/tools/AGENTS.md +11 -11
  376. package/src/tools/acp/spawn.ts +1 -1
  377. package/src/tools/apps/executors.ts +8 -8
  378. package/src/tools/apps/registry.ts +1 -1
  379. package/src/tools/assets/materialize.ts +6 -6
  380. package/src/tools/assets/search.ts +10 -10
  381. package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
  382. package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
  383. package/src/tools/browser/auth-detector.ts +6 -6
  384. package/src/tools/browser/browser-execution.ts +13 -13
  385. package/src/tools/browser/browser-manager.ts +3 -3
  386. package/src/tools/browser/chrome-cdp.ts +5 -5
  387. package/src/tools/browser/jit-auth.ts +2 -2
  388. package/src/tools/browser/network-recorder.test.ts +2 -2
  389. package/src/tools/browser/network-recorder.ts +3 -3
  390. package/src/tools/browser/runtime-check.ts +3 -3
  391. package/src/tools/claude-code/claude-code.ts +2 -2
  392. package/src/tools/computer-use/definitions.ts +18 -18
  393. package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
  394. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
  395. package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
  396. package/src/tools/credentials/broker-types.ts +5 -5
  397. package/src/tools/credentials/broker.ts +15 -15
  398. package/src/tools/credentials/metadata-store.ts +2 -2
  399. package/src/tools/credentials/resolve.ts +1 -1
  400. package/src/tools/credentials/selection.ts +1 -1
  401. package/src/tools/credentials/tool-policy.ts +1 -1
  402. package/src/tools/credentials/vault.ts +115 -25
  403. package/src/tools/execution-target.ts +2 -2
  404. package/src/tools/executor.ts +7 -7
  405. package/src/tools/filesystem/edit.ts +2 -2
  406. package/src/tools/filesystem/read.ts +15 -4
  407. package/src/tools/filesystem/write.ts +1 -1
  408. package/src/tools/host-filesystem/edit.ts +2 -1
  409. package/src/tools/host-filesystem/read.ts +18 -1
  410. package/src/tools/host-filesystem/write.ts +1 -1
  411. package/src/tools/host-terminal/host-shell.ts +9 -8
  412. package/src/tools/mcp/mcp-tool-factory.ts +7 -6
  413. package/src/tools/memory/definitions.ts +6 -5
  414. package/src/tools/memory/handlers.test.ts +1 -1
  415. package/src/tools/network/__tests__/web-search.test.ts +3 -3
  416. package/src/tools/network/domain-normalize.ts +2 -2
  417. package/src/tools/network/script-proxy/session-manager.ts +10 -10
  418. package/src/tools/network/web-fetch.ts +1 -1
  419. package/src/tools/network/web-search.ts +3 -3
  420. package/src/tools/permission-checker.ts +8 -8
  421. package/src/tools/registry.ts +7 -7
  422. package/src/tools/schedule/list.ts +2 -2
  423. package/src/tools/schema-transforms.ts +31 -21
  424. package/src/tools/secret-detection-handler.ts +1 -1
  425. package/src/tools/sensitive-output-placeholders.ts +1 -1
  426. package/src/tools/shared/filesystem/edit-engine.ts +1 -1
  427. package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
  428. package/src/tools/shared/filesystem/image-read.ts +25 -5
  429. package/src/tools/shared/filesystem/path-policy.ts +2 -2
  430. package/src/tools/shared/shell-output.ts +1 -1
  431. package/src/tools/side-effects.ts +1 -1
  432. package/src/tools/skills/execute.ts +1 -1
  433. package/src/tools/skills/load.ts +3 -3
  434. package/src/tools/skills/sandbox-runner.ts +3 -3
  435. package/src/tools/subagent/read.ts +1 -1
  436. package/src/tools/subagent/spawn.ts +2 -2
  437. package/src/tools/swarm/delegate.ts +3 -3
  438. package/src/tools/system/request-permission.ts +5 -4
  439. package/src/tools/terminal/backends/native.ts +4 -4
  440. package/src/tools/terminal/parser.ts +6 -6
  441. package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
  442. package/src/tools/terminal/shell.ts +16 -16
  443. package/src/tools/tool-approval-handler.ts +21 -12
  444. package/src/tools/tool-manifest.ts +4 -4
  445. package/src/tools/types.ts +3 -3
  446. package/src/tools/ui-surface/definitions.ts +9 -37
  447. package/src/tools/watcher/list.ts +1 -1
  448. package/src/util/logger.ts +7 -2
  449. package/src/util/pricing.ts +4 -0
  450. package/src/util/retry.ts +29 -1
  451. package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
  452. package/src/workspace/migrations/registry.ts +2 -0
  453. package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
  454. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
  455. package/src/cli/reference.ts +0 -38
  456. package/src/memory/job-handlers/capability-cards.ts +0 -420
  457. package/src/runtime/routes/thread-starter-routes.ts +0 -294
@@ -37,13 +37,13 @@ export async function generateAppIcon(
37
37
  appDescription?: string,
38
38
  ): Promise<void> {
39
39
  const config = getConfig();
40
- const apiKey = await getProviderKeyAsync("gemini");
40
+ const imageGenMode = config.services["image-generation"].mode;
41
41
 
42
+ // Resolve credentials strictly based on mode — no cross-mode fallbacks
42
43
  let credentials: ImageGenCredentials | undefined;
43
- if (apiKey) {
44
- credentials = { type: "direct", apiKey };
45
- } else {
46
- const managedBaseUrl = await buildManagedBaseUrl("vertex");
44
+
45
+ if (imageGenMode === "managed") {
46
+ const managedBaseUrl = await buildManagedBaseUrl("gemini");
47
47
  if (managedBaseUrl) {
48
48
  const ctx = await resolveManagedProxyContext();
49
49
  credentials = {
@@ -52,12 +52,19 @@ export async function generateAppIcon(
52
52
  baseUrl: managedBaseUrl,
53
53
  };
54
54
  }
55
+ } else {
56
+ const apiKey = await getProviderKeyAsync("gemini");
57
+ if (apiKey) {
58
+ credentials = { type: "direct", apiKey };
59
+ }
55
60
  }
56
61
 
57
62
  if (!credentials) {
58
- log.debug(
59
- "No Gemini API key or managed proxy — skipping app icon generation",
60
- );
63
+ const reason =
64
+ imageGenMode === "managed"
65
+ ? "Managed proxy is not available"
66
+ : "No Gemini API key configured";
67
+ log.debug(`${reason} — skipping app icon generation`);
61
68
  return;
62
69
  }
63
70
 
@@ -14,13 +14,13 @@ export async function generateAvatar(
14
14
  prompt: string,
15
15
  ): Promise<{ imageBase64: string; mimeType: string }> {
16
16
  const config = getConfig();
17
- const geminiKey = await getProviderKeyAsync("gemini");
17
+ const imageGenMode = config.services["image-generation"].mode;
18
18
 
19
+ // Resolve credentials strictly based on mode — no cross-mode fallbacks
19
20
  let credentials: ImageGenCredentials | undefined;
20
- if (geminiKey) {
21
- credentials = { type: "direct", apiKey: geminiKey };
22
- } else {
23
- const managedBaseUrl = await buildManagedBaseUrl("vertex");
21
+
22
+ if (imageGenMode === "managed") {
23
+ const managedBaseUrl = await buildManagedBaseUrl("gemini");
24
24
  if (managedBaseUrl) {
25
25
  const ctx = await resolveManagedProxyContext();
26
26
  credentials = {
@@ -29,12 +29,19 @@ export async function generateAvatar(
29
29
  baseUrl: managedBaseUrl,
30
30
  };
31
31
  }
32
+ } else {
33
+ const geminiKey = await getProviderKeyAsync("gemini");
34
+ if (geminiKey) {
35
+ credentials = { type: "direct", apiKey: geminiKey };
36
+ }
32
37
  }
33
38
 
34
39
  if (!credentials) {
35
- throw new ConfigError(
36
- "Gemini API key is not configured. Set it via `keys set gemini <key>`.",
37
- );
40
+ const hint =
41
+ imageGenMode === "managed"
42
+ ? "Managed proxy is not available. Please log in to Vellum or switch to Your Own mode."
43
+ : "Gemini API key is not configured. Please set your Gemini API key in Settings > Models & Services.";
44
+ throw new ConfigError(hint);
38
45
  }
39
46
 
40
47
  const result = await generateImage(credentials, {
@@ -7,7 +7,7 @@ interface ImageGenerationRequest {
7
7
  mode: "generate" | "edit";
8
8
  /** Base64-encoded source images for edit mode */
9
9
  sourceImages?: Array<{ mimeType: string; dataBase64: string }>;
10
- /** Model override; defaults to 'gemini-2.5-flash-image' */
10
+ /** Model override; defaults to 'gemini-3.1-flash-image-preview' */
11
11
  model?: string;
12
12
  /** Number of output variants (1-4, default 1) */
13
13
  variants?: number;
@@ -19,7 +19,7 @@ interface DirectCredentials {
19
19
  apiKey: string;
20
20
  }
21
21
 
22
- /** Credentials for managed proxy access via Vertex AI. */
22
+ /** Credentials for managed proxy access (platform translates to Vertex AI). */
23
23
  interface ManagedProxyCredentials {
24
24
  type: "managed-proxy";
25
25
  assistantApiKey: string;
@@ -43,10 +43,9 @@ interface ImageGenerationResult {
43
43
 
44
44
  // --- Constants ---
45
45
 
46
- const DEFAULT_MODEL = "gemini-2.5-flash-image";
46
+ const DEFAULT_MODEL = "gemini-3.1-flash-image-preview";
47
47
  const ALLOWED_MODELS = new Set([
48
- "gemini-2.5-flash-image",
49
- "gemini-3-pro-image",
48
+ "gemini-3.1-flash-image-preview",
50
49
  "gemini-3-pro-image-preview",
51
50
  ]);
52
51
  const MAX_VARIANTS = 4;
@@ -76,6 +75,77 @@ export function mapGeminiError(error: unknown): string {
76
75
  return "An unexpected error occurred during image generation.";
77
76
  }
78
77
 
78
+ // --- Managed proxy direct HTTP call ---
79
+
80
+ /**
81
+ * Call the managed proxy directly via fetch, using the Gemini API URL format.
82
+ * The platform proxy translates this to Vertex AI internally with ADC auth.
83
+ *
84
+ * Uses the Gemini API format:
85
+ * POST {baseUrl}/v1beta/models/{model}:generateContent
86
+ */
87
+ async function generateImageViaProxy(
88
+ credentials: ManagedProxyCredentials,
89
+ model: string,
90
+ contents: unknown[],
91
+ config: Record<string, unknown>,
92
+ ): Promise<{
93
+ images: GeneratedImage[];
94
+ text?: string;
95
+ }> {
96
+ const url = `${credentials.baseUrl}/v1beta/models/${model}:generateContent`;
97
+
98
+ const response = await fetch(url, {
99
+ method: "POST",
100
+ headers: {
101
+ "Content-Type": "application/json",
102
+ Authorization: `Bearer ${credentials.assistantApiKey}`,
103
+ },
104
+ body: JSON.stringify({
105
+ contents,
106
+ generationConfig: config,
107
+ }),
108
+ });
109
+
110
+ if (!response.ok) {
111
+ const body = await response.text();
112
+ throw new Error(
113
+ `Managed proxy request failed (${response.status}): ${body}`,
114
+ );
115
+ }
116
+
117
+ const data = (await response.json()) as {
118
+ candidates?: Array<{
119
+ content?: {
120
+ parts?: Array<{
121
+ text?: string;
122
+ inlineData?: { mimeType?: string; data?: string };
123
+ }>;
124
+ };
125
+ }>;
126
+ };
127
+
128
+ const images: GeneratedImage[] = [];
129
+ let text: string | undefined;
130
+
131
+ const responseParts = data.candidates?.[0]?.content?.parts;
132
+ if (responseParts) {
133
+ for (const part of responseParts) {
134
+ if (part.inlineData) {
135
+ images.push({
136
+ mimeType: part.inlineData.mimeType ?? "image/png",
137
+ dataBase64: part.inlineData.data ?? "",
138
+ });
139
+ }
140
+ if (part.text) {
141
+ text = text ? `${text}\n${part.text}` : part.text;
142
+ }
143
+ }
144
+ }
145
+
146
+ return { images, text };
147
+ }
148
+
79
149
  // --- Core function ---
80
150
 
81
151
  export async function generateImage(
@@ -89,19 +159,6 @@ export async function generateImage(
89
159
 
90
160
  const variants = Math.max(1, Math.min(request.variants ?? 1, MAX_VARIANTS));
91
161
 
92
- const client =
93
- credentials.type === "managed-proxy"
94
- ? new GoogleGenAI({
95
- vertexai: true,
96
- project: "proxy",
97
- location: "us-central1",
98
- httpOptions: {
99
- baseUrl: credentials.baseUrl,
100
- headers: { Authorization: `Bearer ${credentials.assistantApiKey}` },
101
- },
102
- })
103
- : new GoogleGenAI({ apiKey: credentials.apiKey });
104
-
105
162
  // Build contents array — append a title request so the model's text
106
163
  // response contains a short filename-safe title for the generated image.
107
164
  const promptWithTitle = `${request.prompt}\n\nAlso respond with a short title (max 6 words) for the image on its own line, prefixed with "Title: ".`;
@@ -118,11 +175,60 @@ export async function generateImage(
118
175
  }
119
176
 
120
177
  const config = { responseModalities: ["TEXT", "IMAGE"] as string[] };
178
+ const contents = [{ role: "user" as const, parts }];
179
+
180
+ // For the managed proxy, bypass the @google/genai SDK and make direct HTTP
181
+ // calls. The SDK's generateContent doesn't support responseModalities for
182
+ // image generation. Direct fetch lets us use the Gemini API format with
183
+ // the managed proxy translating to Vertex internally.
184
+ if (credentials.type === "managed-proxy") {
185
+ const makeSingleCall = () =>
186
+ generateImageViaProxy(credentials, model, contents, config);
187
+
188
+ if (variants === 1) {
189
+ const result = await makeSingleCall();
190
+ const title = extractTitle(result.text);
191
+ if (title) {
192
+ for (const img of result.images) img.title = title;
193
+ }
194
+ return {
195
+ images: result.images,
196
+ text: stripTitleLine(result.text),
197
+ resolvedModel: model,
198
+ };
199
+ }
200
+
201
+ const results = await Promise.all(
202
+ Array.from({ length: variants }, () => makeSingleCall()),
203
+ );
204
+ const allImages: GeneratedImage[] = [];
205
+ let combinedText: string | undefined;
206
+ for (const result of results) {
207
+ const title = extractTitle(result.text);
208
+ if (title) {
209
+ for (const img of result.images) img.title = title;
210
+ }
211
+ allImages.push(...result.images);
212
+ if (result.text) {
213
+ combinedText = combinedText
214
+ ? `${combinedText}\n${result.text}`
215
+ : result.text;
216
+ }
217
+ }
218
+ return {
219
+ images: allImages,
220
+ text: stripTitleLine(combinedText),
221
+ resolvedModel: model,
222
+ };
223
+ }
224
+
225
+ // Direct Gemini API path — use the SDK with API key auth.
226
+ const client = new GoogleGenAI({ apiKey: credentials.apiKey });
121
227
 
122
228
  const makeSingleCall = async () => {
123
229
  const response = await client.models.generateContent({
124
230
  model,
125
- contents: [{ role: "user" as const, parts }],
231
+ contents,
126
232
  config,
127
233
  });
128
234
 
@@ -144,7 +250,6 @@ export async function generateImage(
144
250
  }
145
251
  }
146
252
 
147
- // Extract title from the text response and apply to images
148
253
  const title = extractTitle(text);
149
254
  if (title) {
150
255
  for (const img of images) {
@@ -160,7 +265,6 @@ export async function generateImage(
160
265
  return { ...result, resolvedModel: model };
161
266
  }
162
267
 
163
- // Parallel calls for multiple variants
164
268
  const results = await Promise.all(
165
269
  Array.from({ length: variants }, () => makeSingleCall()),
166
270
  );
@@ -48,8 +48,8 @@ function formatBytes(bytes: number): string {
48
48
  // Size and encoding limits
49
49
  // ---------------------------------------------------------------------------
50
50
 
51
- /** Hard ceiling on a single uploaded attachment (50 MB, matching assistant limits). */
52
- export const MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
51
+ /** Hard ceiling on a single uploaded attachment (100 MB, matching assistant limits). */
52
+ export const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
53
53
 
54
54
  /** Attachments larger than this are stored on disk instead of inline in SQLite. */
55
55
  export const FILE_BACKED_THRESHOLD_BYTES = 5 * 1024 * 1024;
@@ -226,7 +226,7 @@ function computeContentHash(dataBase64: string): string {
226
226
  /**
227
227
  * Store a file-backed attachment by path reference, without reading the file
228
228
  * into memory. This avoids OOM risk for large recordings that exceed the
229
- * normal 50 MB upload limit.
229
+ * normal 100 MB upload limit.
230
230
  *
231
231
  * The file stays on disk; the attachment row stores an empty dataBase64 and
232
232
  * records the on-disk path in a `file_path` column (added via DB migration
@@ -59,7 +59,7 @@ export interface VerificationSession {
59
59
  challengeHash: string;
60
60
  expiresAt: number;
61
61
  status: SessionStatus;
62
- createdByConversationId: string | null;
62
+ sourceConversationId: string | null;
63
63
  consumedByExternalUserId: string | null;
64
64
  consumedByChatId: string | null;
65
65
  // Outbound session: expected-identity binding
@@ -96,7 +96,7 @@ function rowToSession(
96
96
  challengeHash: row.challengeHash,
97
97
  expiresAt: row.expiresAt,
98
98
  status: row.status as SessionStatus,
99
- createdByConversationId: row.createdByConversationId,
99
+ sourceConversationId: row.sourceConversationId,
100
100
  consumedByExternalUserId: row.consumedByExternalUserId,
101
101
  consumedByChatId: row.consumedByChatId,
102
102
  expectedExternalUserId: row.expectedExternalUserId ?? null,
@@ -127,7 +127,7 @@ export function createInboundSession(params: {
127
127
  channel: string;
128
128
  challengeHash: string;
129
129
  expiresAt: number;
130
- createdByConversationId?: string;
130
+ sourceConversationId?: string;
131
131
  }): VerificationSession {
132
132
  const db = getDb();
133
133
  const now = Date.now();
@@ -150,7 +150,7 @@ export function createInboundSession(params: {
150
150
  challengeHash: params.challengeHash,
151
151
  expiresAt: params.expiresAt,
152
152
  status: "pending" as const,
153
- createdByConversationId: params.createdByConversationId ?? null,
153
+ sourceConversationId: params.sourceConversationId ?? null,
154
154
  consumedByExternalUserId: null,
155
155
  consumedByChatId: null,
156
156
  expectedExternalUserId: null,
@@ -278,7 +278,7 @@ export function createVerificationSession(params: {
278
278
  challengeHash: string;
279
279
  expiresAt: number;
280
280
  status: SessionStatus;
281
- createdByConversationId?: string;
281
+ sourceConversationId?: string;
282
282
  expectedExternalUserId?: string | null;
283
283
  expectedChatId?: string | null;
284
284
  expectedPhoneE164?: string | null;
@@ -313,7 +313,7 @@ export function createVerificationSession(params: {
313
313
  challengeHash: params.challengeHash,
314
314
  expiresAt: params.expiresAt,
315
315
  status: params.status as string,
316
- createdByConversationId: params.createdByConversationId ?? null,
316
+ sourceConversationId: params.sourceConversationId ?? null,
317
317
  consumedByExternalUserId: null,
318
318
  consumedByChatId: null,
319
319
  expectedExternalUserId: params.expectedExternalUserId ?? null,
@@ -12,8 +12,9 @@ import { createRowMapper } from "../util/row-mapper.js";
12
12
  import { deleteOrphanAttachments } from "./attachments-store.js";
13
13
  import { projectAssistantMessage } from "./conversation-attention-store.js";
14
14
  import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
15
- import { getDb, rawExec, rawGet, rawRun } from "./db.js";
15
+ import { getDb, rawAll, rawExec, rawGet, rawRun } from "./db.js";
16
16
  import { indexMessageNow } from "./indexer.js";
17
+ import { enqueueMemoryJob } from "./jobs-store.js";
17
18
  import {
18
19
  channelInboundEvents,
19
20
  conversations,
@@ -22,10 +23,12 @@ import {
22
23
  memoryItems,
23
24
  memoryItemSources,
24
25
  memorySegments,
26
+ memorySummaries,
25
27
  messageAttachments,
26
28
  messages,
27
29
  toolInvocations,
28
30
  } from "./schema.js";
31
+ import { cancelPendingJobsForConversation } from "./task-memory-cleanup.js";
29
32
 
30
33
  const log = getLogger("conversation-store");
31
34
 
@@ -357,6 +360,192 @@ export function deleteConversation(id: string): DeletedMemoryIds {
357
360
  return result;
358
361
  }
359
362
 
363
+ /**
364
+ * Wipe a conversation and revert all memory changes it caused.
365
+ *
366
+ * Extends `deleteConversation` with:
367
+ * - Cancelling pending memory jobs before deletion
368
+ * - Restoring memory items that were explicitly superseded by items from this conversation
369
+ * - Restoring orphaned subject-match superseded items after deletion
370
+ * - Deleting conversation-scoped memory summaries and their embeddings
371
+ * - Enqueuing `embed_item` jobs for all restored items
372
+ */
373
+ export function wipeConversation(id: string): WipeConversationResult {
374
+ const db = getDb();
375
+ const unsupersededItemIds: string[] = [];
376
+ const deletedSummaryIds: string[] = [];
377
+
378
+ // Step A — Cancel pending memory jobs (before deleting messages, since
379
+ // the cancellation queries join on `messages`).
380
+ const cancelledJobCount = cancelPendingJobsForConversation(id);
381
+
382
+ // Step B — Un-supersede memory items with explicit `supersededBy` links.
383
+ // Find memory items whose `superseded_by` points to an item sourced
384
+ // exclusively from this conversation.
385
+ const explicitSuperseded = rawAll<{ oldItemId: string }>(
386
+ `SELECT DISTINCT mi_old.id AS oldItemId
387
+ FROM memory_items mi_old
388
+ JOIN memory_items mi_new ON mi_old.superseded_by = mi_new.id
389
+ WHERE mi_old.status = 'superseded'
390
+ AND mi_new.id IN (
391
+ SELECT mis.memory_item_id
392
+ FROM memory_item_sources mis
393
+ JOIN messages m ON m.id = mis.message_id
394
+ WHERE m.conversation_id = ?
395
+ )
396
+ AND NOT EXISTS (
397
+ SELECT 1 FROM memory_item_sources mis2
398
+ JOIN messages m2 ON m2.id = mis2.message_id
399
+ WHERE mis2.memory_item_id = mi_new.id
400
+ AND m2.conversation_id != ?
401
+ )
402
+ AND NOT EXISTS (
403
+ SELECT 1 FROM memory_items mi_active
404
+ WHERE mi_active.kind = mi_old.kind
405
+ AND mi_active.subject = mi_old.subject
406
+ AND mi_active.scope_id = mi_old.scope_id
407
+ AND mi_active.status = 'active'
408
+ AND mi_active.id != mi_old.id
409
+ -- Exclude items sourced exclusively from the conversation being
410
+ -- wiped — deleteConversation will remove them, so they should not
411
+ -- block restoration of mi_old.
412
+ AND NOT (
413
+ EXISTS (
414
+ SELECT 1 FROM memory_item_sources mis_a
415
+ JOIN messages m_a ON m_a.id = mis_a.message_id
416
+ WHERE mis_a.memory_item_id = mi_active.id
417
+ AND m_a.conversation_id = ?
418
+ )
419
+ AND NOT EXISTS (
420
+ SELECT 1 FROM memory_item_sources mis_b
421
+ JOIN messages m_b ON m_b.id = mis_b.message_id
422
+ WHERE mis_b.memory_item_id = mi_active.id
423
+ AND m_b.conversation_id != ?
424
+ )
425
+ )
426
+ )`,
427
+ id,
428
+ id,
429
+ id,
430
+ id,
431
+ );
432
+ for (const { oldItemId } of explicitSuperseded) {
433
+ rawRun(
434
+ "UPDATE memory_items SET status = 'active', superseded_by = NULL WHERE id = ?",
435
+ oldItemId,
436
+ );
437
+ enqueueMemoryJob("embed_item", { itemId: oldItemId });
438
+ unsupersededItemIds.push(oldItemId);
439
+ }
440
+
441
+ // Step C — Delete conversation-scoped memory summaries and their embeddings.
442
+ const summaryRows = db
443
+ .select({ id: memorySummaries.id })
444
+ .from(memorySummaries)
445
+ .where(
446
+ and(
447
+ eq(memorySummaries.scope, "conversation"),
448
+ eq(memorySummaries.scopeKey, id),
449
+ ),
450
+ )
451
+ .all();
452
+ const summaryIds = summaryRows.map((r) => r.id);
453
+ if (summaryIds.length > 0) {
454
+ db.delete(memoryEmbeddings)
455
+ .where(
456
+ and(
457
+ eq(memoryEmbeddings.targetType, "summary"),
458
+ inArray(memoryEmbeddings.targetId, summaryIds),
459
+ ),
460
+ )
461
+ .run();
462
+ db.delete(memorySummaries)
463
+ .where(inArray(memorySummaries.id, summaryIds))
464
+ .run();
465
+ }
466
+ deletedSummaryIds.push(...summaryIds);
467
+
468
+ // Step D — Get the conversation's memoryScopeId before deletion.
469
+ const scopeId = getConversationMemoryScopeId(id);
470
+
471
+ // Step D.5 — Collect kind + subject pairs of items that will be orphaned
472
+ // by deleteConversation. These are items sourced from this conversation's
473
+ // messages that have NO sources from any other conversation. We need this
474
+ // before deletion so we can scope Step F to only restore superseded items
475
+ // matching the specific kind + subject pairs that just lost their active
476
+ // replacement.
477
+ const orphanedKindSubjects = rawAll<{ kind: string; subject: string }>(
478
+ `SELECT DISTINCT mi.kind, mi.subject
479
+ FROM memory_items mi
480
+ JOIN memory_item_sources mis ON mis.memory_item_id = mi.id
481
+ JOIN messages m ON m.id = mis.message_id
482
+ WHERE m.conversation_id = ?
483
+ AND NOT EXISTS (
484
+ SELECT 1 FROM memory_item_sources mis2
485
+ JOIN messages m2 ON m2.id = mis2.message_id
486
+ WHERE mis2.memory_item_id = mi.id
487
+ AND m2.conversation_id != ?
488
+ )`,
489
+ id,
490
+ id,
491
+ );
492
+
493
+ // Step E — Delegate to deleteConversation which handles messages (cascade
494
+ // segments, item_sources, attachments), llmRequestLogs, toolInvocations,
495
+ // orphaned memory items + embeddings, and the conversation row.
496
+ const deletedMemoryIds = deleteConversation(id);
497
+
498
+ // Step F — Restore orphaned subject-match superseded items. After
499
+ // deleteConversation removes superseding items, find superseded items
500
+ // with no supersededBy link where no active item with the same
501
+ // kind + subject + scope_id exists. Scoped to only the kind + subject
502
+ // pairs of items that were just orphaned by deleteConversation, so we
503
+ // don't accidentally restore items superseded by unrelated conversations.
504
+ let orphanedSuperseded: Array<{ id: string }> = [];
505
+ if (orphanedKindSubjects.length > 0) {
506
+ const placeholders = orphanedKindSubjects.map(() => "(?, ?)").join(", ");
507
+ const params: Array<string> = [scopeId];
508
+ for (const { kind, subject } of orphanedKindSubjects) {
509
+ params.push(kind, subject);
510
+ }
511
+ orphanedSuperseded = rawAll<{ id: string }>(
512
+ `SELECT id FROM (
513
+ SELECT id, ROW_NUMBER() OVER (
514
+ PARTITION BY kind, subject, scope_id
515
+ ORDER BY last_seen_at DESC
516
+ ) AS rn
517
+ FROM memory_items
518
+ WHERE status = 'superseded'
519
+ AND superseded_by IS NULL
520
+ AND scope_id = ?
521
+ AND (kind, subject) IN (VALUES ${placeholders})
522
+ AND NOT EXISTS (
523
+ SELECT 1 FROM memory_items mi2
524
+ WHERE mi2.kind = memory_items.kind
525
+ AND mi2.subject = memory_items.subject
526
+ AND mi2.scope_id = memory_items.scope_id
527
+ AND mi2.status = 'active'
528
+ AND mi2.id != memory_items.id
529
+ )
530
+ ) WHERE rn = 1`,
531
+ ...params,
532
+ );
533
+ }
534
+ for (const { id: itemId } of orphanedSuperseded) {
535
+ rawRun("UPDATE memory_items SET status = 'active' WHERE id = ?", itemId);
536
+ enqueueMemoryJob("embed_item", { itemId });
537
+ unsupersededItemIds.push(itemId);
538
+ }
539
+
540
+ // Step G — Return the combined result.
541
+ return {
542
+ ...deletedMemoryIds,
543
+ unsupersededItemIds,
544
+ deletedSummaryIds,
545
+ cancelledJobCount,
546
+ };
547
+ }
548
+
360
549
  /**
361
550
  * Delete all private (temporary) conversations and their associated data.
362
551
  * Called at daemon startup to clean up ephemeral conversations from previous sessions.
@@ -755,6 +944,12 @@ export interface DeletedMemoryIds {
755
944
  orphanedItemIds: string[];
756
945
  }
757
946
 
947
+ export interface WipeConversationResult extends DeletedMemoryIds {
948
+ unsupersededItemIds: string[];
949
+ deletedSummaryIds: string[];
950
+ cancelledJobCount: number;
951
+ }
952
+
758
953
  /**
759
954
  * Update the content of an existing message. Used when consolidating
760
955
  * multiple assistant messages into one.
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Cadence logic for thread starters and capability cards generation.
2
+ * Cadence logic for conversation starters generation.
3
3
  *
4
- * Decides whether new generation jobs should be enqueued based on how many
4
+ * Decides whether a new generation job should be enqueued based on how many
5
5
  * active memory items have accumulated since the last generation.
6
6
  */
7
7
 
@@ -9,18 +9,17 @@ import { and, eq, inArray, like } from "drizzle-orm";
9
9
 
10
10
  import { getLogger } from "../util/logger.js";
11
11
  import { getDb } from "./db.js";
12
- import { CAPABILITY_CARD_CATEGORIES } from "./job-handlers/capability-cards.js";
13
12
  import { enqueueMemoryJob } from "./jobs-store.js";
14
13
  import { rawGet } from "./raw-query.js";
15
14
  import { memoryCheckpoints, memoryJobs } from "./schema.js";
16
15
 
17
- const log = getLogger("thread-starters-cadence");
16
+ const log = getLogger("conversation-starters-cadence");
18
17
 
19
18
  /**
20
19
  * Check whether enough new memory items have accumulated to justify
21
- * generating a fresh batch of thread starters and capability cards.
20
+ * generating a fresh batch of conversation starters.
22
21
  */
23
- export function maybeEnqueueThreadStartersJob(scopeId: string): void {
22
+ export function maybeEnqueueConversationStartersJob(scopeId: string): void {
24
23
  const db = getDb();
25
24
 
26
25
  // Count total active memory items
@@ -32,7 +31,7 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
32
31
  if (totalActive === 0) return;
33
32
 
34
33
  // Read checkpoint: item count at last generation (scoped so each scope tracks independently)
35
- const checkpointKey = `thread_starters:item_count_at_last_gen:${scopeId}`;
34
+ const checkpointKey = `conversation_starters:item_count_at_last_gen:${scopeId}`;
36
35
  const checkpoint = db
37
36
  .select({ value: memoryCheckpoints.value })
38
37
  .from(memoryCheckpoints)
@@ -59,7 +58,7 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
59
58
  .from(memoryJobs)
60
59
  .where(
61
60
  and(
62
- eq(memoryJobs.type, "generate_thread_starters"),
61
+ eq(memoryJobs.type, "generate_conversation_starters"),
63
62
  inArray(memoryJobs.status, ["pending", "running"]),
64
63
  like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
65
64
  ),
@@ -67,41 +66,9 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
67
66
  .get();
68
67
  if (existing) return;
69
68
 
70
- enqueueMemoryJob("generate_thread_starters", { scopeId });
69
+ enqueueMemoryJob("generate_conversation_starters", { scopeId });
71
70
  log.info(
72
71
  { totalActive, lastCount, delta, threshold, scopeId },
73
- "Enqueued thread starters generation job",
72
+ "Enqueued conversation starters generation job",
74
73
  );
75
-
76
- // Also enqueue capability card regeneration for all categories
77
- maybeEnqueueCapabilityCardJobs(scopeId);
78
- }
79
-
80
- /**
81
- * Enqueue capability card generation jobs for all categories.
82
- * Skips categories that already have pending/running jobs.
83
- */
84
- function maybeEnqueueCapabilityCardJobs(scopeId: string): void {
85
- const db = getDb();
86
-
87
- for (const category of CAPABILITY_CARD_CATEGORIES) {
88
- const existing = db
89
- .select({ id: memoryJobs.id })
90
- .from(memoryJobs)
91
- .where(
92
- and(
93
- eq(memoryJobs.type, "generate_capability_cards"),
94
- inArray(memoryJobs.status, ["pending", "running"]),
95
- like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
96
- like(memoryJobs.payload, `%"category":"${category}"%`),
97
- ),
98
- )
99
- .get();
100
-
101
- if (!existing) {
102
- enqueueMemoryJob("generate_capability_cards", { scopeId, category });
103
- }
104
- }
105
-
106
- log.info({ scopeId }, "Enqueued capability card generation jobs");
107
74
  }