@vellumai/assistant 0.8.0 → 0.8.1

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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -0,0 +1,213 @@
1
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ mock.module("../../util/logger.js", () => ({
4
+ getLogger: () =>
5
+ new Proxy({} as Record<string, unknown>, {
6
+ get: () => () => {},
7
+ }),
8
+ }));
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Mock state — reset between tests.
12
+ // ---------------------------------------------------------------------------
13
+
14
+ type ConvRow = {
15
+ id: string;
16
+ source: string | null;
17
+ last_message_at: number | null;
18
+ };
19
+ type JobRow = {
20
+ type: string;
21
+ status: string;
22
+ payload: string;
23
+ };
24
+
25
+ let mockConversations: ConvRow[] = [];
26
+ let mockJobs: JobRow[] = [];
27
+ let deletedIds: string[] = [];
28
+
29
+ mock.module("../db-connection.js", () => ({
30
+ getDb: () => ({
31
+ select: (cols?: Record<string, unknown>) => ({
32
+ from: (_table: { _: { name: string } } | unknown) => ({
33
+ where: (..._args: unknown[]) => ({
34
+ all: () => {
35
+ // Heuristic: tests only construct two query shapes — the jobs
36
+ // query and the conversations query. Distinguish by the first
37
+ // requested column shape.
38
+ const colKeys = cols ? Object.keys(cols) : [];
39
+ if (colKeys.includes("conversationId")) {
40
+ return mockJobs
41
+ .filter(
42
+ (j) =>
43
+ j.type === "memory_retrospective" &&
44
+ (j.status === "pending" || j.status === "running"),
45
+ )
46
+ .map((j) => {
47
+ let convId: string | null = null;
48
+ try {
49
+ const parsed = JSON.parse(j.payload) as {
50
+ conversationId?: unknown;
51
+ };
52
+ if (typeof parsed.conversationId === "string") {
53
+ convId = parsed.conversationId;
54
+ }
55
+ } catch {
56
+ // Ignore malformed payload
57
+ }
58
+ return { conversationId: convId };
59
+ });
60
+ }
61
+ // Otherwise, this is the conversation query.
62
+ // The test harness applies its OWN filter logic below since the
63
+ // production code uses drizzle's combinator. We expose all rows
64
+ // tagged with the right source/last_message_at, and the test
65
+ // post-filters them to mirror the production query.
66
+ return mockConversations
67
+ .filter((c) => c.source === "memory-retrospective")
68
+ .filter(
69
+ (c) =>
70
+ c.last_message_at !== null &&
71
+ c.last_message_at < injectedNowMinusOrphanAgeMs,
72
+ )
73
+ .filter((c) => !activeJobConvIds.has(c.id))
74
+ .map((c) => ({ id: c.id }));
75
+ },
76
+ }),
77
+ }),
78
+ }),
79
+ }),
80
+ }));
81
+
82
+ let activeJobConvIds = new Set<string>();
83
+ let injectedNowMinusOrphanAgeMs = 0;
84
+
85
+ mock.module("../conversation-crud.js", () => ({
86
+ deleteConversation: (id: string) => {
87
+ deletedIds.push(id);
88
+ mockConversations = mockConversations.filter((c) => c.id !== id);
89
+ },
90
+ }));
91
+
92
+ import { sweepOrphanMemoryRetrospectiveConversations } from "../memory-retrospective-startup-cleanup.js";
93
+
94
+ const ORPHAN_AGE_MS = 60 * 60 * 1000;
95
+
96
+ function rebuildActiveJobSet(): void {
97
+ activeJobConvIds = new Set();
98
+ for (const j of mockJobs) {
99
+ if (
100
+ j.type !== "memory_retrospective" ||
101
+ (j.status !== "pending" && j.status !== "running")
102
+ ) {
103
+ continue;
104
+ }
105
+ try {
106
+ const parsed = JSON.parse(j.payload) as { conversationId?: unknown };
107
+ if (typeof parsed.conversationId === "string") {
108
+ activeJobConvIds.add(parsed.conversationId);
109
+ }
110
+ } catch {
111
+ // ignore
112
+ }
113
+ }
114
+ }
115
+
116
+ describe("sweepOrphanMemoryRetrospectiveConversations", () => {
117
+ beforeEach(() => {
118
+ mockConversations = [];
119
+ mockJobs = [];
120
+ deletedIds = [];
121
+ activeJobConvIds = new Set();
122
+ injectedNowMinusOrphanAgeMs = 0;
123
+ });
124
+
125
+ afterEach(() => {
126
+ mockConversations = [];
127
+ mockJobs = [];
128
+ });
129
+
130
+ test("sweeps an old memory-retrospective conversation with no active job", () => {
131
+ const now = Date.now();
132
+ injectedNowMinusOrphanAgeMs = now - ORPHAN_AGE_MS;
133
+ mockConversations = [
134
+ {
135
+ id: "old-orphan",
136
+ source: "memory-retrospective",
137
+ last_message_at: now - 2 * ORPHAN_AGE_MS,
138
+ },
139
+ ];
140
+ rebuildActiveJobSet();
141
+
142
+ const result = sweepOrphanMemoryRetrospectiveConversations(now);
143
+
144
+ expect(result.swept).toBe(1);
145
+ expect(deletedIds).toEqual(["old-orphan"]);
146
+ });
147
+
148
+ test("does NOT sweep recent memory-retrospective conversations", () => {
149
+ const now = Date.now();
150
+ injectedNowMinusOrphanAgeMs = now - ORPHAN_AGE_MS;
151
+ mockConversations = [
152
+ {
153
+ id: "fresh-bg",
154
+ source: "memory-retrospective",
155
+ last_message_at: now - 60_000,
156
+ },
157
+ ];
158
+ rebuildActiveJobSet();
159
+
160
+ const result = sweepOrphanMemoryRetrospectiveConversations(now);
161
+
162
+ expect(result.swept).toBe(0);
163
+ expect(deletedIds).toEqual([]);
164
+ });
165
+
166
+ test("does NOT sweep conversations of OTHER sources, even when old", () => {
167
+ const now = Date.now();
168
+ injectedNowMinusOrphanAgeMs = now - ORPHAN_AGE_MS;
169
+ mockConversations = [
170
+ {
171
+ id: "auto-analysis-old",
172
+ source: "auto-analysis",
173
+ last_message_at: now - 2 * ORPHAN_AGE_MS,
174
+ },
175
+ ];
176
+ rebuildActiveJobSet();
177
+
178
+ const result = sweepOrphanMemoryRetrospectiveConversations(now);
179
+
180
+ expect(result.swept).toBe(0);
181
+ });
182
+
183
+ test("does NOT sweep an orphan whose source conversation has an active job", () => {
184
+ const now = Date.now();
185
+ injectedNowMinusOrphanAgeMs = now - ORPHAN_AGE_MS;
186
+ mockConversations = [
187
+ {
188
+ id: "orphan-but-protected",
189
+ source: "memory-retrospective",
190
+ last_message_at: now - 2 * ORPHAN_AGE_MS,
191
+ },
192
+ ];
193
+ mockJobs = [
194
+ {
195
+ type: "memory_retrospective",
196
+ status: "pending",
197
+ payload: JSON.stringify({ conversationId: "orphan-but-protected" }),
198
+ },
199
+ ];
200
+ rebuildActiveJobSet();
201
+
202
+ const result = sweepOrphanMemoryRetrospectiveConversations(now);
203
+
204
+ expect(result.swept).toBe(0);
205
+ expect(deletedIds).toEqual([]);
206
+ });
207
+
208
+ test("running across an empty workspace returns swept=0 without errors", () => {
209
+ const result = sweepOrphanMemoryRetrospectiveConversations();
210
+ expect(result.swept).toBe(0);
211
+ expect(deletedIds).toEqual([]);
212
+ });
213
+ });
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { shouldEnqueueRetrospective } from "../memory-retrospective-trigger-check.js";
4
+
5
+ const THRESHOLDS = {
6
+ timeThresholdMs: 30 * 60 * 1000, // 30 min
7
+ messageThreshold: 10,
8
+ minCooldownMs: 5 * 60 * 1000, // 5 min
9
+ };
10
+
11
+ describe("shouldEnqueueRetrospective", () => {
12
+ test("no state — returns 'interval' regardless of message count", () => {
13
+ const result = shouldEnqueueRetrospective({
14
+ state: null,
15
+ newMessageCount: 0,
16
+ now: Date.now(),
17
+ ...THRESHOLDS,
18
+ });
19
+ expect(result).toBe("interval");
20
+ });
21
+
22
+ test("cooldown gate — within minCooldownMs, returns null even if other thresholds would trip", () => {
23
+ const now = Date.now();
24
+ const result = shouldEnqueueRetrospective({
25
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 60_000 }, // 1 min ago
26
+ newMessageCount: 50, // way over threshold
27
+ now,
28
+ ...THRESHOLDS,
29
+ });
30
+ expect(result).toBeNull();
31
+ });
32
+
33
+ test("cooldown elapsed + time threshold reached — returns 'interval'", () => {
34
+ const now = Date.now();
35
+ const result = shouldEnqueueRetrospective({
36
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 31 * 60_000 },
37
+ newMessageCount: 1,
38
+ now,
39
+ ...THRESHOLDS,
40
+ });
41
+ expect(result).toBe("interval");
42
+ });
43
+
44
+ test("cooldown elapsed + time threshold not reached + message threshold met — returns 'message_count'", () => {
45
+ const now = Date.now();
46
+ const result = shouldEnqueueRetrospective({
47
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 6 * 60_000 }, // past cooldown
48
+ newMessageCount: 10, // exactly at threshold
49
+ now,
50
+ ...THRESHOLDS,
51
+ });
52
+ expect(result).toBe("message_count");
53
+ });
54
+
55
+ test("cooldown elapsed + neither threshold met — returns null", () => {
56
+ const now = Date.now();
57
+ const result = shouldEnqueueRetrospective({
58
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 6 * 60_000 },
59
+ newMessageCount: 5, // below threshold
60
+ now,
61
+ ...THRESHOLDS,
62
+ });
63
+ expect(result).toBeNull();
64
+ });
65
+
66
+ test("time threshold at exact boundary — returns 'interval'", () => {
67
+ const now = Date.now();
68
+ const result = shouldEnqueueRetrospective({
69
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 30 * 60_000 },
70
+ newMessageCount: 1,
71
+ now,
72
+ ...THRESHOLDS,
73
+ });
74
+ expect(result).toBe("interval");
75
+ });
76
+
77
+ test("message threshold prefers 'message_count' label when both could fire (interval also at boundary)", () => {
78
+ // When the interval is also at threshold AND there are also enough
79
+ // new messages, interval wins because it's evaluated first — the
80
+ // trigger label is for observability only, the action is the same.
81
+ const now = Date.now();
82
+ const result = shouldEnqueueRetrospective({
83
+ state: { lastProcessedMessageId: "m1", lastRunAt: now - 31 * 60_000 },
84
+ newMessageCount: 20,
85
+ now,
86
+ ...THRESHOLDS,
87
+ });
88
+ expect(result).toBe("interval");
89
+ });
90
+ });
@@ -23,6 +23,7 @@ import { initializeDb } from "../db-init.js";
23
23
  import {
24
24
  backfillMemoryV2ActivationMessageId,
25
25
  getMemoryV2ActivationLogByMessageIds,
26
+ type MemoryV2ConceptRowRecord,
26
27
  recordMemoryV2ActivationLog,
27
28
  } from "../memory-v2-activation-log-store.js";
28
29
  import { memoryV2ActivationLogs } from "../schema.js";
@@ -66,6 +67,74 @@ describe("memory-v2-activation-log-store", () => {
66
67
  expect(result!.config).toEqual(sampleConfig);
67
68
  });
68
69
 
70
+ test("round-trip: router-mode log row with zeroed activations and source: 'router'", () => {
71
+ const conversationId = "conv-router";
72
+ const messageId = "msg-router";
73
+
74
+ const routerConcepts: MemoryV2ConceptRowRecord[] = [
75
+ {
76
+ slug: "concept-router-a",
77
+ finalActivation: 0,
78
+ ownActivation: 0,
79
+ priorActivation: 0,
80
+ simUser: 0,
81
+ simAssistant: 0,
82
+ simNow: 0,
83
+ simUserRerankBoost: 0,
84
+ simAssistantRerankBoost: 0,
85
+ inRerankPool: false,
86
+ spreadContribution: 0,
87
+ source: "router",
88
+ status: "injected",
89
+ },
90
+ {
91
+ slug: "concept-router-b",
92
+ finalActivation: 0,
93
+ ownActivation: 0,
94
+ priorActivation: 0,
95
+ simUser: 0,
96
+ simAssistant: 0,
97
+ simNow: 0,
98
+ simUserRerankBoost: 0,
99
+ simAssistantRerankBoost: 0,
100
+ inRerankPool: false,
101
+ spreadContribution: 0,
102
+ source: "router",
103
+ status: "not_injected",
104
+ },
105
+ ];
106
+
107
+ recordMemoryV2ActivationLog({
108
+ conversationId,
109
+ turn: 7,
110
+ mode: "router",
111
+ concepts: routerConcepts,
112
+ config: sampleConfig,
113
+ });
114
+
115
+ backfillMemoryV2ActivationMessageId(conversationId, messageId);
116
+
117
+ const result = getMemoryV2ActivationLogByMessageIds([messageId]);
118
+ expect(result).not.toBeNull();
119
+ expect(result!.conversationId).toBe(conversationId);
120
+ expect(result!.turn).toBe(7);
121
+ expect(result!.mode).toBe("router");
122
+ expect(result!.concepts).toEqual(routerConcepts);
123
+ expect(result!.config).toEqual(sampleConfig);
124
+ for (const concept of result!.concepts) {
125
+ expect(concept.source).toBe("router");
126
+ expect(concept.finalActivation).toBe(0);
127
+ expect(concept.ownActivation).toBe(0);
128
+ expect(concept.priorActivation).toBe(0);
129
+ expect(concept.simUser).toBe(0);
130
+ expect(concept.simAssistant).toBe(0);
131
+ expect(concept.simNow).toBe(0);
132
+ expect(concept.simUserRerankBoost).toBe(0);
133
+ expect(concept.simAssistantRerankBoost).toBe(0);
134
+ expect(concept.spreadContribution).toBe(0);
135
+ }
136
+ });
137
+
69
138
  test("returns null for empty messageIds array", () => {
70
139
  const result = getMemoryV2ActivationLogByMessageIds([]);
71
140
  expect(result).toBeNull();
@@ -117,6 +117,7 @@ describe("memory-v2-concept-frequency", () => {
117
117
  in_context: 1,
118
118
  not_injected: 0,
119
119
  page_missing: 0,
120
+ corrupt: 0,
120
121
  });
121
122
  expect(bySlug.get("alice")!.totalEvaluations).toBe(3);
122
123
  expect(bySlug.get("alice")!.onDisk).toBe(true);
@@ -127,6 +128,7 @@ describe("memory-v2-concept-frequency", () => {
127
128
  in_context: 0,
128
129
  not_injected: 1,
129
130
  page_missing: 0,
131
+ corrupt: 0,
130
132
  });
131
133
  expect(bySlug.get("bob")!.totalEvaluations).toBe(2);
132
134
  expect(bySlug.get("bob")!.onDisk).toBe(true);
@@ -136,6 +138,7 @@ describe("memory-v2-concept-frequency", () => {
136
138
  in_context: 0,
137
139
  not_injected: 0,
138
140
  page_missing: 1,
141
+ corrupt: 0,
139
142
  });
140
143
  expect(bySlug.get("charlie")!.onDisk).toBe(false);
141
144
  expect(bySlug.get("charlie")!.lastInjectedAt).toBeNull();
@@ -0,0 +1,179 @@
1
+ import { desc, eq } from "drizzle-orm";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ import type { DrizzleDb } from "./db-connection.js";
5
+ import { stringifyMessageContent } from "./message-content.js";
6
+ import { conversations, messageBookmarks, messages } from "./schema.js";
7
+
8
+ /**
9
+ * Wire-shape representation of a bookmark, joined with the bookmarked
10
+ * message and its parent conversation. Mirrors
11
+ * `clients/shared/Network/BookmarkSummary.swift` — dates are emitted as
12
+ * unix-millisecond integers, and the message preview is capped to keep
13
+ * the list payload bounded.
14
+ */
15
+ export interface BookmarkSummary {
16
+ id: string;
17
+ messageId: string;
18
+ conversationId: string;
19
+ conversationTitle: string | null;
20
+ messagePreview: string;
21
+ /** "user" | "assistant" — kept as a free-form string so it round-trips raw. */
22
+ messageRole: string;
23
+ /** Unix milliseconds. */
24
+ messageCreatedAt: number;
25
+ /** Unix milliseconds. */
26
+ createdAt: number;
27
+ }
28
+
29
+ const PREVIEW_MAX_CHARS = 240;
30
+
31
+ /**
32
+ * Decode the on-disk message content (legacy plain string OR JSON-serialized
33
+ * `ContentBlock[]`) into a single text string and cap it at
34
+ * `PREVIEW_MAX_CHARS`. Without the decode step, modern rows would render as
35
+ * raw JSON in the bookmark list.
36
+ */
37
+ function buildPreview(content: string): string {
38
+ const text = stringifyMessageContent(content);
39
+ return text.length > PREVIEW_MAX_CHARS
40
+ ? text.slice(0, PREVIEW_MAX_CHARS)
41
+ : text;
42
+ }
43
+
44
+ /**
45
+ * Shared SELECT shape used by the JOIN-based readers. Pulling this out
46
+ * avoids duplicating the column list and the row-mapping below.
47
+ */
48
+ const BOOKMARK_JOIN_COLUMNS = {
49
+ id: messageBookmarks.id,
50
+ messageId: messageBookmarks.messageId,
51
+ conversationId: messageBookmarks.conversationId,
52
+ createdAt: messageBookmarks.createdAt,
53
+ conversationTitle: conversations.title,
54
+ messageContent: messages.content,
55
+ messageRole: messages.role,
56
+ messageCreatedAt: messages.createdAt,
57
+ } as const;
58
+
59
+ type BookmarkJoinRow = {
60
+ id: string;
61
+ messageId: string;
62
+ conversationId: string;
63
+ createdAt: number;
64
+ conversationTitle: string | null;
65
+ messageContent: string;
66
+ messageRole: string;
67
+ messageCreatedAt: number;
68
+ };
69
+
70
+ function rowToSummary(row: BookmarkJoinRow): BookmarkSummary {
71
+ return {
72
+ id: row.id,
73
+ messageId: row.messageId,
74
+ conversationId: row.conversationId,
75
+ conversationTitle: row.conversationTitle,
76
+ messagePreview: buildPreview(row.messageContent),
77
+ messageRole: row.messageRole,
78
+ messageCreatedAt: row.messageCreatedAt,
79
+ createdAt: row.createdAt,
80
+ };
81
+ }
82
+
83
+ function selectBookmarkJoin(db: DrizzleDb) {
84
+ return db
85
+ .select(BOOKMARK_JOIN_COLUMNS)
86
+ .from(messageBookmarks)
87
+ .innerJoin(messages, eq(messages.id, messageBookmarks.messageId))
88
+ .innerJoin(
89
+ conversations,
90
+ eq(conversations.id, messageBookmarks.conversationId),
91
+ );
92
+ }
93
+
94
+ /**
95
+ * List all bookmarks newest-first, joined against `messages` and
96
+ * `conversations`. Bookmarks whose parent message or conversation has
97
+ * been deleted are naturally excluded by the inner-join semantics; the
98
+ * `ON DELETE CASCADE` on the FKs means rows should never end up in this
99
+ * orphan state, but the join provides a defense-in-depth guarantee.
100
+ */
101
+ export function listBookmarks(db: DrizzleDb): BookmarkSummary[] {
102
+ const rows = selectBookmarkJoin(db)
103
+ .orderBy(desc(messageBookmarks.createdAt))
104
+ .all();
105
+ return rows.map(rowToSummary);
106
+ }
107
+
108
+ /**
109
+ * Create a bookmark for the given message and return its JOIN-shaped
110
+ * {@link BookmarkSummary}. Idempotent on the unique `message_id` index —
111
+ * if a bookmark already exists for `messageId`, the existing summary is
112
+ * returned and no new row is inserted.
113
+ */
114
+ export function createBookmark(
115
+ db: DrizzleDb,
116
+ params: { messageId: string; conversationId: string },
117
+ ): BookmarkSummary {
118
+ const { messageId, conversationId } = params;
119
+ const existing = db
120
+ .select({ id: messageBookmarks.id })
121
+ .from(messageBookmarks)
122
+ .where(eq(messageBookmarks.messageId, messageId))
123
+ .get();
124
+ if (existing) return readBookmarkSummaryOrThrow(db, existing.id);
125
+
126
+ const id = uuid();
127
+ try {
128
+ db.insert(messageBookmarks)
129
+ .values({ id, messageId, conversationId, createdAt: Date.now() })
130
+ .run();
131
+ } catch (err) {
132
+ // Lost a race against a concurrent create — fall back to fetch by
133
+ // messageId so we still return the winning row.
134
+ const winner = db
135
+ .select({ id: messageBookmarks.id })
136
+ .from(messageBookmarks)
137
+ .where(eq(messageBookmarks.messageId, messageId))
138
+ .get();
139
+ if (!winner) throw err;
140
+ return readBookmarkSummaryOrThrow(db, winner.id);
141
+ }
142
+ return readBookmarkSummaryOrThrow(db, id);
143
+ }
144
+
145
+ function readBookmarkSummaryOrThrow(
146
+ db: DrizzleDb,
147
+ id: string,
148
+ ): BookmarkSummary {
149
+ const row = selectBookmarkJoin(db).where(eq(messageBookmarks.id, id)).get();
150
+ if (!row) {
151
+ // Unreachable: caller just observed (or inserted) this id.
152
+ throw new Error(`Bookmark ${id} disappeared between insert and read`);
153
+ }
154
+ return rowToSummary(row);
155
+ }
156
+
157
+ /**
158
+ * Delete the bookmark (if any) attached to the given `messageId`.
159
+ * Returns true iff a row was removed.
160
+ *
161
+ * Drizzle's high-level `.run()` is typed as `void` for the sync sqlite
162
+ * driver, so we check existence with a follow-up SELECT instead of
163
+ * relying on a row-count from the delete statement.
164
+ */
165
+ export function deleteBookmarkByMessageId(
166
+ db: DrizzleDb,
167
+ messageId: string,
168
+ ): boolean {
169
+ const existed = db
170
+ .select({ id: messageBookmarks.id })
171
+ .from(messageBookmarks)
172
+ .where(eq(messageBookmarks.messageId, messageId))
173
+ .get();
174
+ if (!existed) return false;
175
+ db.delete(messageBookmarks)
176
+ .where(eq(messageBookmarks.messageId, messageId))
177
+ .run();
178
+ return true;
179
+ }
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * `redactWorkspaceEvidence` scrubs secrets from workspace-sourced evidence
6
6
  * excerpts before they are serialised into the prompt that is sent to the
7
- * external recall LLM provider. Memory/PKB/conversation evidence is left
7
+ * external recall LLM provider. Memory/conversation evidence is left
8
8
  * untouched — those sources contain intentionally stored user content.
9
9
  */
10
10
 
@@ -65,7 +65,10 @@ describe("redactWorkspaceEvidence", () => {
65
65
 
66
66
  test("does NOT modify non-secret workspace excerpts", () => {
67
67
  const safeContent = "This is a normal comment explaining the architecture.";
68
- const original = makeEvidence({ source: "workspace", excerpt: safeContent });
68
+ const original = makeEvidence({
69
+ source: "workspace",
70
+ excerpt: safeContent,
71
+ });
69
72
  const [result] = redactWorkspaceEvidence([original]);
70
73
 
71
74
  expect(result.excerpt).toBe(safeContent);
@@ -74,11 +77,11 @@ describe("redactWorkspaceEvidence", () => {
74
77
  });
75
78
 
76
79
  test("does NOT redact non-workspace sources", () => {
77
- // Memory/PKB/conversation evidence is intentionally stored user content —
80
+ // Memory/conversation evidence is intentionally stored user content —
78
81
  // redacting it would break recall for things the user deliberately noted.
79
82
  const secretLike = "token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0In0.abc";
80
83
 
81
- for (const source of ["memory", "pkb", "conversations"] as const) {
84
+ for (const source of ["memory", "conversations"] as const) {
82
85
  const original = makeEvidence({ source, excerpt: secretLike });
83
86
  const [result] = redactWorkspaceEvidence([original]);
84
87
 
@@ -89,8 +92,16 @@ describe("redactWorkspaceEvidence", () => {
89
92
 
90
93
  test("redacts multiple secrets across multiple workspace evidence items", () => {
91
94
  const results = redactWorkspaceEvidence([
92
- makeEvidence({ id: "ev-1", source: "workspace", excerpt: `key=${ANTHROPIC_KEY}` }),
93
- makeEvidence({ id: "ev-2", source: "workspace", excerpt: GENERIC_SECRET_EXCERPT }),
95
+ makeEvidence({
96
+ id: "ev-1",
97
+ source: "workspace",
98
+ excerpt: `key=${ANTHROPIC_KEY}`,
99
+ }),
100
+ makeEvidence({
101
+ id: "ev-2",
102
+ source: "workspace",
103
+ excerpt: GENERIC_SECRET_EXCERPT,
104
+ }),
94
105
  ]);
95
106
 
96
107
  expect(results[0].excerpt).not.toContain(ANTHROPIC_KEY);
@@ -101,7 +112,10 @@ describe("redactWorkspaceEvidence", () => {
101
112
 
102
113
  test("does not mutate the original evidence objects", () => {
103
114
  const secret = ANTHROPIC_KEY;
104
- const original = makeEvidence({ source: "workspace", excerpt: `key=${secret}` });
115
+ const original = makeEvidence({
116
+ source: "workspace",
117
+ excerpt: `key=${secret}`,
118
+ });
105
119
  const originalExcerpt = original.excerpt;
106
120
 
107
121
  redactWorkspaceEvidence([original]);
@@ -111,8 +125,16 @@ describe("redactWorkspaceEvidence", () => {
111
125
 
112
126
  test("handles mixed sources in one batch correctly", () => {
113
127
  const secret = ANTHROPIC_KEY;
114
- const wsItem = makeEvidence({ id: "ev-ws", source: "workspace", excerpt: `key=${secret}` });
115
- const memItem = makeEvidence({ id: "ev-mem", source: "memory", excerpt: `key=${secret}` });
128
+ const wsItem = makeEvidence({
129
+ id: "ev-ws",
130
+ source: "workspace",
131
+ excerpt: `key=${secret}`,
132
+ });
133
+ const memItem = makeEvidence({
134
+ id: "ev-mem",
135
+ source: "memory",
136
+ excerpt: `key=${secret}`,
137
+ });
116
138
 
117
139
  const [wsResult, memResult] = redactWorkspaceEvidence([wsItem, memItem]);
118
140
 
@@ -34,6 +34,7 @@ type RecallFinishFallbackReason =
34
34
  | "malformed_finish_payload"
35
35
  | "invalid_confidence"
36
36
  | "invalid_citation_ids"
37
+ | "missing_citations"
37
38
  | "unknown_citation_ids"
38
39
  | "empty_answer";
39
40
 
@@ -57,7 +58,6 @@ const DEFAULT_MAX_SEARCH_CALLS = 4;
57
58
 
58
59
  const RECALL_SOURCE_DESCRIPTIONS: Record<RecallSource, string> = {
59
60
  memory: "durable memory graph facts and relationship/context memories",
60
- pkb: "personal knowledge base notes, NOW context, and pinned context",
61
61
  conversations: "past assistant conversations and conversation summaries",
62
62
  workspace: "files and text available in the current workspace",
63
63
  };
@@ -293,6 +293,10 @@ export function validateFinishRecallPayload(
293
293
  return fallbackFinish("invalid_citation_ids");
294
294
  }
295
295
 
296
+ if (payload.citation_ids.length === 0) {
297
+ return fallbackFinish("missing_citations");
298
+ }
299
+
296
300
  const citationValidation = validateRecallCitationIds(
297
301
  payload.citation_ids,
298
302
  evidence,