@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,52 @@
1
+ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
2
+ import { withCrashRecovery } from "./validate-migration-state.js";
3
+
4
+ /**
5
+ * Rename `thread_starters` table to `conversation_starters` and recreate
6
+ * indexes with new names, aligning with the thread → conversation
7
+ * terminology unification.
8
+ */
9
+ export function migrateRenameThreadStartersTable(database: DrizzleDb): void {
10
+ withCrashRecovery(
11
+ database,
12
+ "migration_rename_thread_starters_table_v1",
13
+ () => {
14
+ const raw = getSqliteFrom(database);
15
+
16
+ // Check the old table exists before attempting anything
17
+ const oldTableExists = raw
18
+ .query(
19
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'thread_starters'`,
20
+ )
21
+ .get();
22
+ if (!oldTableExists) return;
23
+
24
+ // Rename the physical table
25
+ raw.exec(
26
+ /*sql*/ `ALTER TABLE thread_starters RENAME TO conversation_starters`,
27
+ );
28
+
29
+ // Drop old indexes and recreate with new names.
30
+ // SQLite automatically updates index table references on RENAME, but the
31
+ // index names still reference the old naming convention — drop and recreate
32
+ // with consistent names pointing at the new table.
33
+
34
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_thread_starters_batch`);
35
+ raw.exec(
36
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_conversation_starters_batch ON conversation_starters(generation_batch, created_at)`,
37
+ );
38
+
39
+ raw.exec(/*sql*/ `DROP INDEX IF EXISTS idx_thread_starters_card_type`);
40
+ raw.exec(
41
+ /*sql*/ `CREATE INDEX IF NOT EXISTS idx_conversation_starters_card_type ON conversation_starters(card_type, scope_id)`,
42
+ );
43
+
44
+ // Migrate checkpoint keys from old thread_starters: prefix to
45
+ // conversation_starters: so existing checkpoint data is found by
46
+ // the renamed code paths and unnecessary re-generation is avoided.
47
+ raw.exec(
48
+ /*sql*/ `UPDATE memory_checkpoints SET key = replace(key, 'thread_starters:', 'conversation_starters:') WHERE key LIKE 'thread_starters:%'`,
49
+ );
50
+ },
51
+ );
52
+ }
@@ -0,0 +1,15 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+
3
+ /**
4
+ * Create the lifecycle_events table for tracking app lifecycle telemetry
5
+ * (app_open, hatch, etc.).
6
+ */
7
+ export function createLifecycleEventsTable(database: DrizzleDb): void {
8
+ database.run(/*sql*/ `
9
+ CREATE TABLE IF NOT EXISTS lifecycle_events (
10
+ id TEXT PRIMARY KEY,
11
+ event_name TEXT NOT NULL,
12
+ created_at INTEGER NOT NULL
13
+ )
14
+ `);
15
+ }
@@ -0,0 +1,36 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { withCrashRecovery } from "./validate-migration-state.js";
4
+
5
+ const CHECKPOINT_KEY = "migration_drop_capability_card_state_v1";
6
+
7
+ /**
8
+ * Remove persisted capability-card state after the card feed was deleted.
9
+ *
10
+ * Conversation starters remain in place, but card rows, category relevance
11
+ * state, and queued generation jobs are dead data and should not survive.
12
+ */
13
+ export function migrateDropCapabilityCardState(database: DrizzleDb): void {
14
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
15
+ const raw = getSqliteFrom(database);
16
+
17
+ const conversationStartersExists = raw
18
+ .query(
19
+ `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'conversation_starters'`,
20
+ )
21
+ .get();
22
+ if (conversationStartersExists) {
23
+ raw.exec(
24
+ /*sql*/ `DELETE FROM conversation_starters WHERE card_type = 'card'`,
25
+ );
26
+ }
27
+
28
+ raw.exec(
29
+ /*sql*/ `DELETE FROM memory_jobs WHERE type = 'generate_capability_cards'`,
30
+ );
31
+ raw.exec(
32
+ /*sql*/ `DELETE FROM memory_checkpoints WHERE key LIKE 'capability_cards:%'`,
33
+ );
34
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS capability_card_categories`);
35
+ });
36
+ }
@@ -0,0 +1,40 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { withCrashRecovery } from "./validate-migration-state.js";
4
+
5
+ const CHECKPOINT_KEY = "migration_create_trace_events_table_v1";
6
+
7
+ /**
8
+ * Create the trace_events table for persisting trace/activity events
9
+ * so the Logs panel has data across sessions.
10
+ */
11
+ export function migrateCreateTraceEventsTable(database: DrizzleDb): void {
12
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
13
+ const raw = getSqliteFrom(database);
14
+
15
+ raw.exec(/*sql*/ `
16
+ CREATE TABLE IF NOT EXISTS trace_events (
17
+ event_id TEXT PRIMARY KEY,
18
+ conversation_id TEXT NOT NULL,
19
+ request_id TEXT,
20
+ timestamp_ms INTEGER NOT NULL,
21
+ sequence INTEGER NOT NULL,
22
+ kind TEXT NOT NULL,
23
+ status TEXT,
24
+ summary TEXT NOT NULL,
25
+ attributes_json TEXT,
26
+ created_at INTEGER NOT NULL
27
+ )
28
+ `);
29
+
30
+ raw.exec(/*sql*/ `
31
+ CREATE INDEX IF NOT EXISTS idx_trace_events_conversation_id
32
+ ON trace_events (conversation_id)
33
+ `);
34
+
35
+ raw.exec(/*sql*/ `
36
+ CREATE INDEX IF NOT EXISTS idx_trace_events_conversation_timestamp
37
+ ON trace_events (conversation_id, timestamp_ms)
38
+ `);
39
+ });
40
+ }
@@ -113,6 +113,12 @@ export { migrateRenameSequenceStepsReplyKey } from "./168-rename-sequence-steps-
113
113
  export { migrateRenameGmailProviderKeyToGoogle } from "./169-rename-gmail-provider-key-to-google.js";
114
114
  export { migrateCreateThreadStartersTable } from "./170-thread-starters-table.js";
115
115
  export { migrateCapabilityCardColumns } from "./171-capability-card-columns.js";
116
+ export { migrateRenameCreatedBySessionIdColumns } from "./172-rename-created-by-session-id.js";
117
+ export { migrateRenameSourceSessionIdColumn } from "./173-rename-source-session-id.js";
118
+ export { migrateRenameThreadStartersTable } from "./174-rename-thread-starters-table.js";
119
+ export { createLifecycleEventsTable } from "./175-create-lifecycle-events.js";
120
+ export { migrateDropCapabilityCardState } from "./176-drop-capability-card-state.js";
121
+ export { migrateCreateTraceEventsTable } from "./177-create-trace-events-table.js";
116
122
  export {
117
123
  MIGRATION_REGISTRY,
118
124
  type MigrationRegistryEntry,
@@ -215,6 +215,19 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
215
215
  description:
216
216
  "Rename integration:gmail provider key to integration:google across oauth_providers, oauth_apps, and oauth_connections",
217
217
  },
218
+ {
219
+ key: "migration_rename_thread_starters_table_v1",
220
+ version: 32,
221
+ description:
222
+ "Rename thread_starters table to conversation_starters and recreate indexes with new names",
223
+ },
224
+ {
225
+ key: "migration_drop_capability_card_state_v1",
226
+ version: 33,
227
+ dependsOn: ["migration_rename_thread_starters_table_v1"],
228
+ description:
229
+ "Remove deleted capability-card rows, jobs, checkpoints, and category state",
230
+ },
218
231
  ];
219
232
 
220
233
  export interface MigrationValidationResult {
@@ -95,8 +95,7 @@ import {
95
95
  } from "../memory/qdrant-circuit-breaker.js";
96
96
  import {
97
97
  buildMemoryRecall,
98
- injectMemoryRecallAsSeparateMessage,
99
- stripMemoryRecallMessages,
98
+ injectMemoryRecallAsUserBlock,
100
99
  } from "../memory/retriever.js";
101
100
  import {
102
101
  conversations,
@@ -104,15 +103,24 @@ import {
104
103
  memoryItemSources,
105
104
  messages,
106
105
  } from "../memory/schema.js";
106
+ import type { ContentBlock, Message } from "../providers/types.js";
107
107
 
108
108
  // ---------------------------------------------------------------------------
109
109
  // Helpers
110
110
  // ---------------------------------------------------------------------------
111
111
 
112
+ /** Extract text from a content block, asserting it is a text block. */
113
+ function textOf(block: ContentBlock): string {
114
+ if (block.type !== "text")
115
+ throw new Error(`Expected text block, got ${block.type}`);
116
+ return block.text;
117
+ }
118
+
112
119
  function insertConversation(
113
120
  db: ReturnType<typeof getDb>,
114
121
  id: string,
115
122
  createdAt: number,
123
+ opts?: { contextCompactedMessageCount?: number },
116
124
  ) {
117
125
  db.insert(conversations)
118
126
  .values({
@@ -124,7 +132,7 @@ function insertConversation(
124
132
  totalOutputTokens: 0,
125
133
  totalEstimatedCost: 0,
126
134
  contextSummary: null,
127
- contextCompactedMessageCount: 0,
135
+ contextCompactedMessageCount: opts?.contextCompactedMessageCount ?? 0,
128
136
  contextCompactedAt: null,
129
137
  })
130
138
  .run();
@@ -318,8 +326,178 @@ describe("Memory Retriever Pipeline", () => {
318
326
  expect(result.tier1Count).toBeDefined();
319
327
  expect(result.tier2Count).toBeDefined();
320
328
  expect(result.hybridSearchMs).toBeDefined();
321
- // Recency search finds candidates even though they don't pass tier classification
329
+ // Recency search finds raw candidates from this conversation…
330
+ expect(result.recencyHits).toBeGreaterThan(0);
331
+ // …but they are filtered out because they belong to the active
332
+ // conversation and are already present in the conversation history.
333
+ expect(result.mergedCount).toBe(0);
334
+ });
335
+
336
+ // -----------------------------------------------------------------------
337
+ // Current-conversation segment filtering
338
+ // -----------------------------------------------------------------------
339
+
340
+ test("current-conversation segments are filtered from recency results", async () => {
341
+ const db = getDb();
342
+ const now = Date.now();
343
+ const activeConv = "conv-active";
344
+ const otherConv = "conv-other";
345
+
346
+ insertConversation(db, activeConv, now - 60_000);
347
+ insertConversation(db, otherConv, now - 120_000);
348
+
349
+ // Messages and segments in the active conversation (should be filtered)
350
+ insertMessage(
351
+ db,
352
+ "msg-a1",
353
+ activeConv,
354
+ "user",
355
+ "hello world",
356
+ now - 50_000,
357
+ );
358
+ insertSegment(
359
+ db,
360
+ "seg-a1",
361
+ "msg-a1",
362
+ activeConv,
363
+ "user",
364
+ "hello world",
365
+ now - 50_000,
366
+ );
367
+
368
+ // Messages and segments in a different conversation (should be kept)
369
+ insertMessage(
370
+ db,
371
+ "msg-o1",
372
+ otherConv,
373
+ "user",
374
+ "hello world from other",
375
+ now - 100_000,
376
+ );
377
+ insertSegment(
378
+ db,
379
+ "seg-o1",
380
+ "msg-o1",
381
+ otherConv,
382
+ "user",
383
+ "hello world from other",
384
+ now - 100_000,
385
+ );
386
+
387
+ // Query from the active conversation
388
+ const result = await buildMemoryRecall(
389
+ "hello world",
390
+ activeConv,
391
+ TEST_CONFIG,
392
+ );
393
+
394
+ expect(result.enabled).toBe(true);
395
+ // Recency search finds segments from the active conversation
396
+ expect(result.recencyHits).toBeGreaterThan(0);
397
+ // But they are filtered out of merged results; only other-conversation
398
+ // segments would survive (none in this case since recency is scoped to
399
+ // the active conversation).
400
+ expect(result.mergedCount).toBe(0);
401
+ });
402
+
403
+ test("compacted segments from current conversation are preserved in memory", async () => {
404
+ const db = getDb();
405
+ const now = Date.now();
406
+ const convId = "conv-compacted";
407
+
408
+ // Create a conversation where 2 messages have been compacted away
409
+ insertConversation(db, convId, now - 120_000, {
410
+ contextCompactedMessageCount: 2,
411
+ });
412
+
413
+ // Older messages (compacted out of context window) — their segments
414
+ // should NOT be filtered because the model can no longer see them
415
+ insertMessage(
416
+ db,
417
+ "msg-old-1",
418
+ convId,
419
+ "user",
420
+ "old discussion topic",
421
+ now - 100_000,
422
+ );
423
+ insertMessage(
424
+ db,
425
+ "msg-old-2",
426
+ convId,
427
+ "assistant",
428
+ "old response",
429
+ now - 90_000,
430
+ );
431
+
432
+ // Newer messages (still in context window) — their segments should
433
+ // be filtered since the model can still see them
434
+ insertMessage(
435
+ db,
436
+ "msg-new-1",
437
+ convId,
438
+ "user",
439
+ "recent discussion",
440
+ now - 50_000,
441
+ );
442
+ insertMessage(
443
+ db,
444
+ "msg-new-2",
445
+ convId,
446
+ "assistant",
447
+ "recent response",
448
+ now - 40_000,
449
+ );
450
+
451
+ // Segments from compacted messages (should survive filtering)
452
+ insertSegment(
453
+ db,
454
+ "seg-old-1",
455
+ "msg-old-1",
456
+ convId,
457
+ "user",
458
+ "old discussion topic details",
459
+ now - 100_000,
460
+ );
461
+ insertSegment(
462
+ db,
463
+ "seg-old-2",
464
+ "msg-old-2",
465
+ convId,
466
+ "assistant",
467
+ "old response details",
468
+ now - 90_000,
469
+ );
470
+
471
+ // Segments from in-context messages (should be filtered)
472
+ insertSegment(
473
+ db,
474
+ "seg-new-1",
475
+ "msg-new-1",
476
+ convId,
477
+ "user",
478
+ "recent discussion details",
479
+ now - 50_000,
480
+ );
481
+ insertSegment(
482
+ db,
483
+ "seg-new-2",
484
+ "msg-new-2",
485
+ convId,
486
+ "assistant",
487
+ "recent response details",
488
+ now - 40_000,
489
+ );
490
+
491
+ const result = await buildMemoryRecall(
492
+ "discussion topic",
493
+ convId,
494
+ TEST_CONFIG,
495
+ );
496
+
497
+ expect(result.enabled).toBe(true);
498
+ // Recency search finds segments from this conversation
322
499
  expect(result.recencyHits).toBeGreaterThan(0);
500
+ // Compacted segments survive filtering — they are no longer in context
323
501
  expect(result.mergedCount).toBeGreaterThan(0);
324
502
  });
325
503
 
@@ -527,10 +705,10 @@ describe("Memory Retriever Pipeline", () => {
527
705
  expect(result.enabled).toBe(true);
528
706
  // Semantic/hybrid search should be skipped
529
707
  expect(result.semanticHits).toBe(0);
530
- // Recency search finds candidates (but they may not pass tier thresholds
531
- // since recency-only candidates have no semantic score component)
708
+ // Recency search finds raw candidates
532
709
  expect(result.recencyHits).toBeGreaterThan(0);
533
- expect(result.mergedCount).toBeGreaterThan(0);
710
+ // …but current-conversation segments are filtered out
711
+ expect(result.mergedCount).toBe(0);
534
712
  });
535
713
 
536
714
  // -----------------------------------------------------------------------
@@ -588,115 +766,62 @@ describe("Memory Retriever Pipeline", () => {
588
766
  });
589
767
 
590
768
  // -----------------------------------------------------------------------
591
- // stripMemoryRecallMessages with <memory_context> format
769
+ // injectMemoryRecallAsUserBlock
592
770
  // -----------------------------------------------------------------------
593
771
 
594
- test("stripMemoryRecallMessages: strips <memory_context> XML format", () => {
595
- type Msg = {
596
- role: "user" | "assistant";
597
- content: Array<{ type: string; text?: string }>;
598
- };
599
- const recallText =
600
- "<memory_context>\n\n<relevant_context>\nsome context\n</relevant_context>\n\n</memory_context>";
601
-
602
- const msgs: Msg[] = [
603
- {
604
- role: "user",
605
- content: [{ type: "text", text: recallText }],
606
- },
607
- {
608
- role: "assistant",
609
- content: [{ type: "text", text: "[Memory context loaded.]" }],
610
- },
772
+ test("injectMemoryRecallAsUserBlock: prepends memory context to last user message", () => {
773
+ const msgs: Message[] = [
611
774
  {
612
775
  role: "user",
613
- content: [{ type: "text", text: "Hello, what do you know about me?" }],
776
+ content: [{ type: "text", text: "Hello" }],
614
777
  },
615
778
  ];
616
779
 
617
- const cleaned = stripMemoryRecallMessages(msgs, recallText);
618
- expect(cleaned).toHaveLength(1);
619
- expect(cleaned[0].role).toBe("user");
620
- expect(cleaned[0].content[0].text).toBe(
621
- "Hello, what do you know about me?",
622
- );
623
- });
780
+ const recallText =
781
+ "<memory_context __injected>\n\n<relevant_context>\ntest\n</relevant_context>\n\n</memory_context>";
782
+ const result = injectMemoryRecallAsUserBlock(msgs, recallText);
624
783
 
625
- test("stripMemoryRecallMessages: handles <memory_context> with slightly different content", () => {
626
- type Msg = {
627
- role: "user" | "assistant";
628
- content: Array<{ type: string; text?: string }>;
629
- };
630
- const originalRecall =
631
- "<memory_context>\n\n<relevant_context>\noriginal\n</relevant_context>\n\n</memory_context>";
632
- const actualRecall =
633
- "<memory_context>\n\n<relevant_context>\nslightly different\n</relevant_context>\n\n</memory_context>";
784
+ // Same number of messages no synthetic pair added
785
+ expect(result).toHaveLength(1);
786
+ expect(result[0].role).toBe("user");
787
+ // Memory context prepended as first content block
788
+ expect(result[0].content).toHaveLength(2);
789
+ expect(textOf(result[0].content[0])).toBe(recallText);
790
+ // Original user text preserved as second block
791
+ expect(textOf(result[0].content[1])).toBe("Hello");
792
+ });
634
793
 
635
- const msgs: Msg[] = [
636
- {
637
- role: "user",
638
- content: [{ type: "text", text: actualRecall }],
639
- },
640
- {
641
- role: "assistant",
642
- content: [{ type: "text", text: "[Memory context loaded.]" }],
643
- },
794
+ test("injectMemoryRecallAsUserBlock: no-op for empty text", () => {
795
+ const msgs: Message[] = [
644
796
  {
645
797
  role: "user",
646
- content: [{ type: "text", text: "follow-up question" }],
798
+ content: [{ type: "text", text: "Hello" }],
647
799
  },
648
800
  ];
649
801
 
650
- // The <memory_context> tag-based matching should work even when exact text differs
651
- const cleaned = stripMemoryRecallMessages(msgs, originalRecall);
652
- expect(cleaned).toHaveLength(1);
653
- expect(cleaned[0].content[0].text).toBe("follow-up question");
802
+ const result = injectMemoryRecallAsUserBlock(msgs, "");
803
+ expect(result).toHaveLength(1);
804
+ expect(textOf(result[0].content[0])).toBe("Hello");
654
805
  });
655
806
 
656
- // -----------------------------------------------------------------------
657
- // injectMemoryRecallAsSeparateMessage
658
- // -----------------------------------------------------------------------
659
-
660
- test("injectMemoryRecallAsSeparateMessage: injects context + ack before last user message", () => {
661
- type Msg = {
662
- role: "user" | "assistant";
663
- content: Array<{ type: string; text?: string }>;
664
- };
665
- const msgs: Msg[] = [
666
- {
667
- role: "user",
668
- content: [{ type: "text", text: "Hello" }],
669
- },
807
+ test("injectMemoryRecallAsUserBlock: preserves history before last user message", () => {
808
+ const msgs: Message[] = [
809
+ { role: "user", content: [{ type: "text", text: "First" }] },
810
+ { role: "assistant", content: [{ type: "text", text: "Response" }] },
811
+ { role: "user", content: [{ type: "text", text: "Second" }] },
670
812
  ];
671
813
 
672
814
  const recallText =
673
- "<memory_context>\n\n<relevant_context>\ntest\n</relevant_context>\n\n</memory_context>";
674
- const result = injectMemoryRecallAsSeparateMessage(msgs, recallText);
815
+ "<memory_context __injected>\n\n<relevant_context>\nfact\n</relevant_context>\n\n</memory_context>";
816
+ const result = injectMemoryRecallAsUserBlock(msgs, recallText);
675
817
 
676
818
  expect(result).toHaveLength(3);
677
- expect(result[0].role).toBe("user");
678
- expect(result[0].content[0].text).toBe(recallText);
679
- expect(result[1].role).toBe("assistant");
680
- expect(result[1].content[0].text).toBe("[Memory context loaded.]");
681
- expect(result[2].role).toBe("user");
682
- expect(result[2].content[0].text).toBe("Hello");
683
- });
684
-
685
- test("injectMemoryRecallAsSeparateMessage: no-op for empty text", () => {
686
- type Msg = {
687
- role: "user" | "assistant";
688
- content: Array<{ type: string; text?: string }>;
689
- };
690
- const msgs: Msg[] = [
691
- {
692
- role: "user",
693
- content: [{ type: "text", text: "Hello" }],
694
- },
695
- ];
696
-
697
- const result = injectMemoryRecallAsSeparateMessage(msgs, "");
698
- expect(result).toHaveLength(1);
699
- expect(result[0].content[0].text).toBe("Hello");
819
+ // Earlier messages unchanged
820
+ expect(result[0]).toBe(msgs[0]);
821
+ expect(result[1]).toBe(msgs[1]);
822
+ // Last user message has memory prepended
823
+ expect(textOf(result[2].content[0])).toBe(recallText);
824
+ expect(textOf(result[2].content[1])).toBe("Second");
700
825
  });
701
826
 
702
827
  // -----------------------------------------------------------------------
@@ -728,7 +853,9 @@ describe("Memory Retriever Pipeline", () => {
728
853
  // pipeline proceeds non-degraded end-to-end.
729
854
  expect(result.enabled).toBe(true);
730
855
  expect(result.degraded).toBe(false);
731
- // Recency search finds candidates; hybrid search returns empty from mock
856
+ // Recency search finds raw candidates; hybrid search returns empty from mock
732
857
  expect(result.recencyHits).toBeGreaterThan(0);
858
+ // Current-conversation segments are filtered out of merged results
859
+ expect(result.mergedCount).toBe(0);
733
860
  });
734
861
  });