@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
@@ -0,0 +1,438 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
5
+
6
+ const testDir = mkdtempSync(join(tmpdir(), "conv-wipe-test-"));
7
+
8
+ mock.module("../util/platform.js", () => ({
9
+ getDataDir: () => testDir,
10
+ isMacOS: () => process.platform === "darwin",
11
+ isLinux: () => process.platform === "linux",
12
+ isWindows: () => process.platform === "win32",
13
+ getPidPath: () => join(testDir, "test.pid"),
14
+ getDbPath: () => join(testDir, "test.db"),
15
+ getLogPath: () => join(testDir, "test.log"),
16
+ ensureDataDir: () => {},
17
+ }));
18
+
19
+ mock.module("../util/logger.js", () => ({
20
+ getLogger: () =>
21
+ new Proxy({} as Record<string, unknown>, {
22
+ get: () => () => {},
23
+ }),
24
+ }));
25
+
26
+ import {
27
+ addMessage,
28
+ createConversation,
29
+ getConversation,
30
+ getMessages,
31
+ wipeConversation,
32
+ } from "../memory/conversation-crud.js";
33
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
34
+ import { enqueueMemoryJob } from "../memory/jobs-store.js";
35
+
36
+ // Initialize db once before all tests
37
+ initializeDb();
38
+
39
+ afterAll(() => {
40
+ resetDb();
41
+ try {
42
+ rmSync(testDir, { recursive: true });
43
+ } catch {
44
+ /* best effort */
45
+ }
46
+ });
47
+
48
+ describe("wipeConversation", () => {
49
+ beforeEach(() => {
50
+ const db = getDb();
51
+ db.run(`DELETE FROM memory_item_sources`);
52
+ db.run(`DELETE FROM memory_segments`);
53
+ db.run(`DELETE FROM memory_items`);
54
+ db.run(`DELETE FROM memory_summaries`);
55
+ db.run(`DELETE FROM memory_embeddings`);
56
+ db.run(`DELETE FROM memory_jobs`);
57
+ db.run(`DELETE FROM tool_invocations`);
58
+ db.run(`DELETE FROM llm_request_logs`);
59
+ db.run(`DELETE FROM messages`);
60
+ db.run(`DELETE FROM conversations`);
61
+ });
62
+
63
+ test("wipes conversation and all messages", async () => {
64
+ const conv = createConversation("test");
65
+ await addMessage(conv.id, "user", "first message");
66
+ await addMessage(conv.id, "assistant", "second message");
67
+ await addMessage(conv.id, "user", "third message");
68
+
69
+ wipeConversation(conv.id);
70
+
71
+ expect(getConversation(conv.id)).toBeNull();
72
+ expect(getMessages(conv.id)).toEqual([]);
73
+ });
74
+
75
+ test("restores explicitly superseded memory items", async () => {
76
+ const convA = createConversation("conversation A");
77
+ const msgA = await addMessage(convA.id, "user", "I like blue");
78
+
79
+ const convB = createConversation("conversation B");
80
+ const msgB = await addMessage(convB.id, "user", "I like red");
81
+
82
+ const db = getDb();
83
+ const now = Date.now();
84
+
85
+ // Insert itemA: active preference about color
86
+ db.run(
87
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
88
+ VALUES ('itemA', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-a', 'default', ${now}, ${now})`,
89
+ );
90
+
91
+ // Insert itemB: active preference about color, supersedes itemA
92
+ db.run(
93
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
94
+ VALUES ('itemB', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-b', 'default', 'itemA', ${now}, ${now})`,
95
+ );
96
+
97
+ // Mark itemA as superseded by itemB
98
+ db.run(
99
+ `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemB' WHERE id = 'itemA'`,
100
+ );
101
+
102
+ // Link itemA to convA's message, itemB to convB's message
103
+ db.run(
104
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemA', '${msgA.id}', ${now})`,
105
+ );
106
+ db.run(
107
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
108
+ );
109
+
110
+ const result = wipeConversation(convB.id);
111
+
112
+ // itemA should be restored to active with superseded_by cleared
113
+ const raw = (
114
+ getDb() as unknown as {
115
+ $client: import("bun:sqlite").Database;
116
+ }
117
+ ).$client;
118
+ const itemARow = raw
119
+ .query(
120
+ "SELECT status, superseded_by FROM memory_items WHERE id = 'itemA'",
121
+ )
122
+ .get() as { status: string; superseded_by: string | null } | null;
123
+ expect(itemARow).not.toBeNull();
124
+ expect(itemARow!.status).toBe("active");
125
+ expect(itemARow!.superseded_by).toBeNull();
126
+
127
+ // itemB should no longer exist (orphaned and deleted by deleteConversation)
128
+ const itemBRow = (
129
+ getDb() as unknown as {
130
+ $client: import("bun:sqlite").Database;
131
+ }
132
+ ).$client
133
+ .query("SELECT * FROM memory_items WHERE id = 'itemB'")
134
+ .get();
135
+ expect(itemBRow).toBeNull();
136
+
137
+ expect(result.unsupersededItemIds).toContain("itemA");
138
+ });
139
+
140
+ test("does not restore superseded items when superseding item has other sources", async () => {
141
+ const convA = createConversation("conversation A");
142
+ const msgA = await addMessage(convA.id, "user", "I like red in A");
143
+
144
+ const convB = createConversation("conversation B");
145
+ const msgB = await addMessage(convB.id, "user", "I like red in B");
146
+
147
+ const db = getDb();
148
+ const now = Date.now();
149
+
150
+ // Insert itemOld (will be superseded)
151
+ db.run(
152
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
153
+ VALUES ('itemOld', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-old', 'default', ${now}, ${now})`,
154
+ );
155
+
156
+ // Insert itemNew (supersedes itemOld)
157
+ db.run(
158
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
159
+ VALUES ('itemNew', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-new', 'default', 'itemOld', ${now}, ${now})`,
160
+ );
161
+
162
+ // Mark itemOld as superseded
163
+ db.run(
164
+ `UPDATE memory_items SET status = 'superseded', superseded_by = 'itemNew' WHERE id = 'itemOld'`,
165
+ );
166
+
167
+ // Link itemNew to BOTH conversations
168
+ db.run(
169
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgA.id}', ${now})`,
170
+ );
171
+ db.run(
172
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
173
+ );
174
+
175
+ wipeConversation(convA.id);
176
+
177
+ const raw = (
178
+ getDb() as unknown as {
179
+ $client: import("bun:sqlite").Database;
180
+ }
181
+ ).$client;
182
+
183
+ // itemOld should still be superseded because itemNew has another source (convB)
184
+ const itemOldRow = raw
185
+ .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
186
+ .get() as { status: string } | null;
187
+ expect(itemOldRow).not.toBeNull();
188
+ expect(itemOldRow!.status).toBe("superseded");
189
+
190
+ // itemNew should still exist (has source from convB)
191
+ const itemNewRow = raw
192
+ .query("SELECT * FROM memory_items WHERE id = 'itemNew'")
193
+ .get();
194
+ expect(itemNewRow).not.toBeNull();
195
+ });
196
+
197
+ test("restores orphaned subject-match superseded items", async () => {
198
+ const convB = createConversation("conversation B");
199
+ const msgB = await addMessage(convB.id, "user", "I use vim");
200
+
201
+ const db = getDb();
202
+ const now = Date.now();
203
+
204
+ // Insert itemOld: superseded with no superseded_by link
205
+ db.run(
206
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
207
+ VALUES ('itemOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-old', 'default', ${now}, ${now})`,
208
+ );
209
+
210
+ // Insert itemNew: active, same kind/subject/scope_id
211
+ db.run(
212
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
213
+ VALUES ('itemNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-new', 'default', ${now}, ${now})`,
214
+ );
215
+
216
+ // Link itemNew to convB's message
217
+ db.run(
218
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
219
+ );
220
+
221
+ wipeConversation(convB.id);
222
+
223
+ const raw = (
224
+ getDb() as unknown as {
225
+ $client: import("bun:sqlite").Database;
226
+ }
227
+ ).$client;
228
+
229
+ // itemOld should now be active (restored as orphaned subject-match superseded item)
230
+ const itemOldRow = raw
231
+ .query("SELECT status FROM memory_items WHERE id = 'itemOld'")
232
+ .get() as { status: string } | null;
233
+ expect(itemOldRow).not.toBeNull();
234
+ expect(itemOldRow!.status).toBe("active");
235
+ });
236
+
237
+ test("does not restore superseded items from unrelated conversations", async () => {
238
+ // convA has an item that superseded an older item — convA was previously
239
+ // deleted via regular deleteConversation, leaving the old item superseded
240
+ // with superseded_by = NULL. When we later wipe convB, Step F should NOT
241
+ // restore that unrelated item.
242
+ const convA = createConversation("conversation A");
243
+ const _msgA = await addMessage(convA.id, "user", "I use dark theme");
244
+
245
+ const convB = createConversation("conversation B");
246
+ const msgB = await addMessage(convB.id, "user", "I use vim");
247
+
248
+ const db = getDb();
249
+ const now = Date.now();
250
+
251
+ // unrelatedOld: superseded item from an old conversation (e.g. "uses light theme")
252
+ // Its superseder was deleted in a prior deleteConversation, leaving
253
+ // superseded_by = NULL.
254
+ db.run(
255
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
256
+ VALUES ('unrelatedOld', 'superseded', 'preference', 'theme', 'uses light theme', 0.7, 'fp-unrelated', 'default', ${now}, ${now})`,
257
+ );
258
+
259
+ // convA's active item that superseded unrelatedOld — we simulate the
260
+ // case where convA was already deleted, leaving unrelatedOld with
261
+ // superseded_by = NULL and no active replacement.
262
+ // (We don't actually insert the superseder — just leave unrelatedOld
263
+ // as a superseded item with no superseded_by and no active match.)
264
+
265
+ // convB's items — itemOld is superseded by itemNew (subject: editor)
266
+ db.run(
267
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
268
+ VALUES ('editorOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-editor-old', 'default', ${now}, ${now})`,
269
+ );
270
+ db.run(
271
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
272
+ VALUES ('editorNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-editor-new', 'default', ${now}, ${now})`,
273
+ );
274
+ db.run(
275
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('editorNew', '${msgB.id}', ${now})`,
276
+ );
277
+
278
+ const result = wipeConversation(convB.id);
279
+
280
+ const raw = (
281
+ getDb() as unknown as {
282
+ $client: import("bun:sqlite").Database;
283
+ }
284
+ ).$client;
285
+
286
+ // editorOld SHOULD be restored (its kind+subject matches an orphaned item from convB)
287
+ const editorOldRow = raw
288
+ .query("SELECT status FROM memory_items WHERE id = 'editorOld'")
289
+ .get() as { status: string } | null;
290
+ expect(editorOldRow).not.toBeNull();
291
+ expect(editorOldRow!.status).toBe("active");
292
+
293
+ // unrelatedOld should NOT be restored — it was superseded by a different
294
+ // conversation's item (theme, not editor) and has nothing to do with convB
295
+ const unrelatedOldRow = raw
296
+ .query("SELECT status FROM memory_items WHERE id = 'unrelatedOld'")
297
+ .get() as { status: string } | null;
298
+ expect(unrelatedOldRow).not.toBeNull();
299
+ expect(unrelatedOldRow!.status).toBe("superseded");
300
+
301
+ // Only editorOld should be in the unsuperseded list, not unrelatedOld
302
+ expect(result.unsupersededItemIds).toContain("editorOld");
303
+ expect(result.unsupersededItemIds).not.toContain("unrelatedOld");
304
+ });
305
+
306
+ test("deletes conversation summaries", async () => {
307
+ const conv = createConversation("test");
308
+ await addMessage(conv.id, "user", "hello");
309
+
310
+ const now = Date.now();
311
+
312
+ const raw = (
313
+ getDb() as unknown as {
314
+ $client: import("bun:sqlite").Database;
315
+ }
316
+ ).$client;
317
+
318
+ // Insert a conversation-scoped summary
319
+ raw
320
+ .query(
321
+ `INSERT INTO memory_summaries (id, scope, scope_key, summary, token_estimate, version, scope_id, start_at, end_at, created_at, updated_at)
322
+ VALUES ('sum-1', 'conversation', ?, 'test summary', 100, 1, 'default', ?, ?, ?, ?)`,
323
+ )
324
+ .run(conv.id, now, now, now, now);
325
+
326
+ // Insert a corresponding embedding
327
+ raw
328
+ .query(
329
+ `INSERT INTO memory_embeddings (id, target_type, target_id, provider, model, dimensions, created_at, updated_at)
330
+ VALUES ('emb-sum-1', 'summary', 'sum-1', 'test', 'test', 384, ?, ?)`,
331
+ )
332
+ .run(now, now);
333
+
334
+ const result = wipeConversation(conv.id);
335
+
336
+ // Summary should be deleted
337
+ const summaryRow = raw
338
+ .query("SELECT * FROM memory_summaries WHERE id = 'sum-1'")
339
+ .get();
340
+ expect(summaryRow).toBeNull();
341
+
342
+ // Embedding should be deleted
343
+ const embeddingRow = raw
344
+ .query("SELECT * FROM memory_embeddings WHERE id = 'emb-sum-1'")
345
+ .get();
346
+ expect(embeddingRow).toBeNull();
347
+
348
+ expect(result.deletedSummaryIds).toContain("sum-1");
349
+ });
350
+
351
+ test("cancels pending memory jobs", async () => {
352
+ const conv = createConversation("test");
353
+ const msg = await addMessage(conv.id, "user", "hello", undefined, {
354
+ skipIndexing: true,
355
+ });
356
+
357
+ // Clear any jobs that might have been created by prior operations
358
+ const db = getDb();
359
+ db.run(`DELETE FROM memory_jobs`);
360
+
361
+ enqueueMemoryJob("extract_items", { messageId: msg.id });
362
+ enqueueMemoryJob("build_conversation_summary", {
363
+ conversationId: conv.id,
364
+ });
365
+
366
+ const result = wipeConversation(conv.id);
367
+
368
+ const raw = (
369
+ getDb() as unknown as {
370
+ $client: import("bun:sqlite").Database;
371
+ }
372
+ ).$client;
373
+
374
+ // Both jobs should be failed with conversation_wiped error
375
+ const jobs = raw
376
+ .query("SELECT status, last_error FROM memory_jobs")
377
+ .all() as Array<{ status: string; last_error: string | null }>;
378
+
379
+ for (const job of jobs) {
380
+ // Skip embed_item jobs enqueued by wipeConversation's unsupersede logic
381
+ if (job.status === "pending") continue;
382
+ expect(job.status).toBe("failed");
383
+ expect(job.last_error).toContain("conversation_wiped");
384
+ }
385
+
386
+ expect(result.cancelledJobCount).toBeGreaterThanOrEqual(2);
387
+ });
388
+
389
+ test("wipe of empty conversation succeeds", () => {
390
+ const conv = createConversation("empty");
391
+
392
+ const result = wipeConversation(conv.id);
393
+
394
+ expect(getConversation(conv.id)).toBeNull();
395
+ expect(result.segmentIds).toEqual([]);
396
+ expect(result.orphanedItemIds).toEqual([]);
397
+ expect(result.unsupersededItemIds).toEqual([]);
398
+ expect(result.deletedSummaryIds).toEqual([]);
399
+ expect(result.cancelledJobCount).toBe(0);
400
+ });
401
+
402
+ test("does not affect other conversations", async () => {
403
+ const convA = createConversation("conversation A");
404
+ await addMessage(convA.id, "user", "message in A");
405
+
406
+ const convB = createConversation("conversation B");
407
+ const msgB = await addMessage(convB.id, "user", "message in B");
408
+
409
+ const db = getDb();
410
+ const now = Date.now();
411
+
412
+ // Insert a memory item sourced from convB's message
413
+ db.run(
414
+ `INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
415
+ VALUES ('itemB', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-b', 'default', ${now}, ${now})`,
416
+ );
417
+ db.run(
418
+ `INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
419
+ );
420
+
421
+ wipeConversation(convA.id);
422
+
423
+ // convB should still exist
424
+ expect(getConversation(convB.id)).not.toBeNull();
425
+ expect(getMessages(convB.id)).toHaveLength(1);
426
+
427
+ // convB's memory item should still exist
428
+ const raw = (
429
+ getDb() as unknown as {
430
+ $client: import("bun:sqlite").Database;
431
+ }
432
+ ).$client;
433
+ const itemBRow = raw
434
+ .query("SELECT * FROM memory_items WHERE id = 'itemB'")
435
+ .get();
436
+ expect(itemBRow).not.toBeNull();
437
+ });
438
+ });
@@ -35,7 +35,7 @@ mock.module("../config/loader.js", () => ({
35
35
  compactThreshold: 0.8,
36
36
  summaryBudgetRatio: 0.05,
37
37
  },
38
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
38
+ rateLimit: { maxRequestsPerMinute: 0 },
39
39
  memory: { enabled: false },
40
40
  }),
41
41
  loadRawConfig: () => ({}),
@@ -105,8 +105,7 @@ mock.module("../memory/attachments-store.js", () => ({
105
105
 
106
106
  mock.module("../memory/retriever.js", () => ({
107
107
  buildMemoryRecall: async () => null,
108
- injectMemoryRecallAsSeparateMessage: (msgs: Message[]) => msgs,
109
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
108
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
110
109
  }));
111
110
 
112
111
  mock.module("../memory/query-builder.js", () => ({
@@ -55,7 +55,7 @@ mock.module("../config/loader.js", () => ({
55
55
  nonInteractiveLatestTurnCompression: "truncate",
56
56
  },
57
57
  },
58
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
58
+ rateLimit: { maxRequestsPerMinute: 0 },
59
59
  memory: { enabled: false },
60
60
  daemon: {
61
61
  startupSocketWaitMs: 5000,
@@ -73,9 +73,9 @@ mock.module("../config/loader.js", () => ({
73
73
  "image-generation": {
74
74
  mode: "your-own",
75
75
  provider: "gemini",
76
- model: "gemini-2.5-flash-image",
76
+ model: "gemini-3.1-flash-image-preview",
77
77
  },
78
- "web-search": { mode: "your-own", provider: "anthropic-native" },
78
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
79
79
  },
80
80
  }),
81
81
  loadRawConfig: () => ({}),
@@ -154,8 +154,7 @@ mock.module("../memory/retriever.js", () => ({
154
154
  latencyMs: 0,
155
155
  topCandidates: [],
156
156
  }),
157
- injectMemoryRecallAsSeparateMessage: (msgs: Message[]) => msgs,
158
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
157
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
159
158
  }));
160
159
  mock.module("../memory/query-builder.js", () => ({
161
160
  buildMemoryQuery: () => "",
@@ -53,7 +53,7 @@ mock.module("../config/loader.js", () => ({
53
53
  nonInteractiveLatestTurnCompression: "truncate",
54
54
  },
55
55
  },
56
- rateLimit: { maxRequestsPerMinute: 0, maxTokensPerSession: 0 },
56
+ rateLimit: { maxRequestsPerMinute: 0 },
57
57
  memory: { enabled: false },
58
58
  daemon: {
59
59
  startupSocketWaitMs: 5000,
@@ -71,9 +71,9 @@ mock.module("../config/loader.js", () => ({
71
71
  "image-generation": {
72
72
  mode: "your-own",
73
73
  provider: "gemini",
74
- model: "gemini-2.5-flash-image",
74
+ model: "gemini-3.1-flash-image-preview",
75
75
  },
76
- "web-search": { mode: "your-own", provider: "anthropic-native" },
76
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
77
77
  },
78
78
  }),
79
79
  loadRawConfig: () => ({}),
@@ -152,8 +152,7 @@ mock.module("../memory/retriever.js", () => ({
152
152
  latencyMs: 0,
153
153
  topCandidates: [],
154
154
  }),
155
- injectMemoryRecallAsSeparateMessage: (msgs: Message[]) => msgs,
156
- stripMemoryRecallMessages: (msgs: Message[]) => msgs,
155
+ injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
157
156
  }));
158
157
  mock.module("../memory/query-builder.js", () => ({
159
158
  buildMemoryQuery: () => "",
@@ -15,9 +15,29 @@ const mockConfig = {
15
15
  timeouts: { permissionTimeoutSec: 300 },
16
16
  };
17
17
 
18
+ function setMockNestedValue(
19
+ obj: Record<string, unknown>,
20
+ path: string,
21
+ value: unknown,
22
+ ): void {
23
+ const keys = path.split(".");
24
+ let current = obj;
25
+ for (let i = 0; i < keys.length - 1; i++) {
26
+ const key = keys[i];
27
+ if (current[key] == null || typeof current[key] !== "object") {
28
+ current[key] = {};
29
+ }
30
+ current = current[key] as Record<string, unknown>;
31
+ }
32
+ current[keys[keys.length - 1]] = value;
33
+ }
34
+
18
35
  mock.module("../config/loader.js", () => ({
19
36
  getConfig: () => mockConfig,
20
37
  loadConfig: () => mockConfig,
38
+ loadRawConfig: () => ({}),
39
+ saveRawConfig: () => {},
40
+ setNestedValue: setMockNestedValue,
21
41
  invalidateConfigCache: () => {},
22
42
  }));
23
43
 
@@ -250,6 +250,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
250
250
  "daemon/providers-setup.ts", // provider initialization API key lookup
251
251
  "tools/claude-code/claude-code.ts", // Claude Code tool API key lookup
252
252
  "workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
253
+ "cli/commands/avatar.ts", // CLI avatar generation API key lookup
253
254
  ]);
254
255
 
255
256
  const thisDir = dirname(fileURLToPath(import.meta.url));