@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
@@ -40,6 +40,7 @@ import {
40
40
  import { hydrate, save } from "./activation-store.js";
41
41
  import { getEdgeIndex } from "./edge-index.js";
42
42
  import { readPage, renderPageContent } from "./page-store.js";
43
+ import { runRouter } from "./router.js";
43
44
  import { getSkillCapability, isSkillSlug } from "./skill-store.js";
44
45
  import type { ActivationState, EverInjectedEntry } from "./types.js";
45
46
 
@@ -58,6 +59,16 @@ const log = getLogger("memory-v2-injection");
58
59
  */
59
60
  export type InjectMemoryV2Mode = "context-load" | "per-turn";
60
61
 
62
+ /**
63
+ * Internal mode union for `finalizeInjection`. Extends the public
64
+ * `InjectMemoryV2Mode` with `"router"` (router-driven success path) and
65
+ * `"errored"` (caller-supplied failure path or the helper's own
66
+ * try/catch promotion). The public surface intentionally only carries
67
+ * the caller-facing modes — these two are persistence/telemetry concerns
68
+ * that don't belong on `InjectMemoryV2BlockParams`.
69
+ */
70
+ type FinalizeInjectionMode = InjectMemoryV2Mode | "router" | "errored";
71
+
61
72
  export interface InjectMemoryV2BlockParams {
62
73
  /** SQLite database handle for activation_state hydrate/save. */
63
74
  database: DrizzleDb;
@@ -94,8 +105,12 @@ export interface InjectMemoryV2BlockResult {
94
105
  */
95
106
  block: string | null;
96
107
  /**
97
- * Slugs that were freshly attached on this turn. Empty when `block` is
98
- * null. Returned for telemetry / debug logging by the call site.
108
+ * Slugs we attempted to attach this turn (top-K minus everInjected).
109
+ * Always populated even when `block` is `null` phantom slugs whose
110
+ * backing page is missing on disk land here and are recorded in
111
+ * `everInjected` so we don't infinite-retry next turn. Callers using
112
+ * this for "we injected N slugs" telemetry should cross-reference
113
+ * `block !== null` (or the activation log's `page_missing` status).
99
114
  */
100
115
  toInject: string[];
101
116
  }
@@ -135,6 +150,27 @@ export async function injectMemoryV2Block(
135
150
  throwIfAborted(signal);
136
151
  const priorState = await hydrate(database, conversationId);
137
152
 
153
+ // Flag-gated router dispatch: when the LLM router is enabled, route the
154
+ // per-turn page selection through `runRouter` and reuse `finalizeInjection`
155
+ // for persistence, render, and telemetry. The activation pipeline below
156
+ // remains the default (flag-off) behavior — every code path past this
157
+ // branch only runs when the router is disabled.
158
+ if (config.memory.v2.router.enabled) {
159
+ return injectViaRouter({
160
+ workspaceDir,
161
+ database,
162
+ conversationId,
163
+ currentTurn,
164
+ userMessage,
165
+ assistantMessage,
166
+ nowText,
167
+ messageId,
168
+ config,
169
+ priorState,
170
+ signal,
171
+ });
172
+ }
173
+
138
174
  // (2) Topology. `getEdgeIndex` walks concept-page frontmatter and caches
139
175
  // the result module-locally; an empty workspace yields an empty index.
140
176
  throwIfAborted(signal);
@@ -178,7 +214,7 @@ export async function injectMemoryV2Block(
178
214
  // prior cached attachments don't exist or have been thrown away. The user
179
215
  // message gets a complete top-K dump alongside the static
180
216
  // essentials/threads/recent block, then per-turn turns just add deltas.
181
- const mode = params.mode ?? "per-turn";
217
+ const mode: InjectMemoryV2Mode = params.mode ?? "per-turn";
182
218
  const priorEverInjected: readonly EverInjectedEntry[] =
183
219
  priorState?.everInjected ?? [];
184
220
  const { topNow, toInject } = selectInjections({
@@ -192,11 +228,108 @@ export async function injectMemoryV2Block(
192
228
  // even on a "no new injection" turn, prior-state activations decay via the
193
229
  // candidate-set carry-forward and need to be rewritten so `epsilon`-trimmed
194
230
  // slugs drop out of consideration next turn.
195
- const nextState: Record<string, number> = {};
231
+ const nextStateMap: Record<string, number> = {};
196
232
  for (const [slug, value] of finalActivation) {
197
- if (value > epsilon) nextState[slug] = value;
233
+ if (value > epsilon) nextStateMap[slug] = value;
198
234
  }
199
235
 
236
+ // Build the rich per-candidate telemetry rows up front (status assigned
237
+ // later by `finalizeInjection` once we know what actually rendered).
238
+ const telemetryRows: MemoryV2ConceptRowRecord[] = [...candidates].map(
239
+ (slug) => {
240
+ const breakdown = ownBreakdown.get(slug);
241
+ const inPrior = fromPrior.has(slug);
242
+ const inAnn = fromAnn.has(slug);
243
+ return {
244
+ slug,
245
+ finalActivation: finalActivation.get(slug) ?? 0,
246
+ ownActivation: ownActivation.get(slug) ?? 0,
247
+ priorActivation: breakdown?.priorContribution ?? 0,
248
+ simUser: breakdown?.simUser ?? 0,
249
+ simAssistant: breakdown?.simAssistant ?? 0,
250
+ simNow: breakdown?.simNow ?? 0,
251
+ simUserRerankBoost: breakdown?.simUserRerankBoost ?? 0,
252
+ simAssistantRerankBoost: breakdown?.simAssistantRerankBoost ?? 0,
253
+ inRerankPool: breakdown?.inRerankPool ?? false,
254
+ spreadContribution: spreadContribution.get(slug) ?? 0,
255
+ source:
256
+ inPrior && inAnn ? "both" : inPrior ? "prior_state" : "ann_top50",
257
+ status: "not_injected",
258
+ };
259
+ },
260
+ );
261
+
262
+ return finalizeInjection({
263
+ workspaceDir,
264
+ database,
265
+ conversationId,
266
+ mode,
267
+ currentTurn,
268
+ messageId,
269
+ priorEverInjected,
270
+ slugsToRender,
271
+ telemetryRows,
272
+ config,
273
+ nextStateMap,
274
+ });
275
+ }
276
+
277
+ /**
278
+ * Tail of `injectMemoryV2Block` extracted as a private helper so the
279
+ * router branch (PR 10) can reuse the same persistence + render +
280
+ * telemetry-finalization pipeline. Performs:
281
+ *
282
+ * 1. Build `nextEverInjected` from `slugsToRender`, filtering out skill
283
+ * slugs whose capability cache entry is missing so future turns
284
+ * re-attempt attachment once the cache is populated.
285
+ * 2. Persist the next activation_state row.
286
+ * 3. Render the injection block.
287
+ * 4. Finalize per-row `status` (`injected | in_context | not_injected |
288
+ * page_missing | corrupt`) on the caller-provided telemetry rows
289
+ * using the render result.
290
+ * 5. Sort rows by `finalActivation` descending and flush the activation
291
+ * log — even when an error is thrown partway through, so silent
292
+ * failures remain observable.
293
+ * 6. Return the rendered block plus `toInject = newlyInjected`.
294
+ *
295
+ * The caller pre-builds `telemetryRows` with all per-candidate breakdown
296
+ * fields filled in (router-mode callers can pass zeros where the
297
+ * breakdown doesn't apply) and a placeholder `status: "not_injected"`
298
+ * which this helper overwrites. `nextStateMap` is the activation
299
+ * pipeline's sparse next-state; router-mode callers pass an empty map.
300
+ */
301
+ async function finalizeInjection(args: {
302
+ workspaceDir: string;
303
+ database: DrizzleDb;
304
+ conversationId: string;
305
+ mode: FinalizeInjectionMode;
306
+ currentTurn: number;
307
+ messageId: string;
308
+ priorEverInjected: readonly EverInjectedEntry[];
309
+ slugsToRender: string[];
310
+ telemetryRows: MemoryV2ConceptRowRecord[];
311
+ config: AssistantConfig;
312
+ nextStateMap: Record<string, number>;
313
+ }): Promise<InjectMemoryV2BlockResult> {
314
+ const {
315
+ workspaceDir,
316
+ database,
317
+ conversationId,
318
+ currentTurn,
319
+ messageId,
320
+ priorEverInjected,
321
+ slugsToRender,
322
+ telemetryRows,
323
+ config,
324
+ nextStateMap,
325
+ } = args;
326
+
327
+ // `mode` is `let` because the trailing try/finally promotes it to "errored"
328
+ // when the render/telemetry path throws — we still want a log row written
329
+ // (with whatever rows we managed to build) so silent failures are
330
+ // observable in the database.
331
+ let mode: FinalizeInjectionMode = args.mode;
332
+
200
333
  // Mark every rendered slug as ever-injected so future per-turn deltas don't
201
334
  // re-attach the same content. On context-load this is the full top-K (we
202
335
  // just rendered all of them); on per-turn it's just the newly added slugs.
@@ -206,9 +339,21 @@ export async function injectMemoryV2Block(
206
339
  // like concept slugs — once attached on a turn, the cached attachment lives
207
340
  // on that user message and the agent keeps seeing it across subsequent turns
208
341
  // until compaction evicts the turn.
342
+ //
343
+ // Skill slugs whose in-process cache entry is missing (e.g. startup race
344
+ // between the skill seed and the first turn, or stale Qdrant index pointing
345
+ // at an uninstalled skill) are excluded from `everInjected` so future
346
+ // per-turn runs re-attempt attachment once the cache is populated. Without
347
+ // this, the slug would be marked injected even though `renderInjectionBlock`
348
+ // silently dropped it.
349
+ const missingSkillSlugs = new Set(
350
+ slugsToRender.filter(
351
+ (slug) => isSkillSlug(slug) && !getSkillCapability(slug),
352
+ ),
353
+ );
209
354
  const everInjectedSet = new Set(priorEverInjected.map((entry) => entry.slug));
210
355
  const newlyInjected = slugsToRender.filter(
211
- (slug) => !everInjectedSet.has(slug),
356
+ (slug) => !everInjectedSet.has(slug) && !missingSkillSlugs.has(slug),
212
357
  );
213
358
  const nextEverInjected: EverInjectedEntry[] = [
214
359
  ...priorEverInjected,
@@ -217,62 +362,76 @@ export async function injectMemoryV2Block(
217
362
 
218
363
  const nextActivationState: ActivationState = {
219
364
  messageId,
220
- state: nextState,
365
+ state: nextStateMap,
221
366
  everInjected: nextEverInjected,
222
367
  currentTurn,
223
368
  updatedAt: Date.now(),
224
369
  };
225
370
 
226
- await save(database, conversationId, nextActivationState);
371
+ // `block` and `conceptRowsForLog` are declared outside the try so the
372
+ // finally block can flush activation telemetry even if rendering, status
373
+ // finalization, or the activation-state save throws partway through.
374
+ // Without this, a Zod failure on a single concept page (e.g. unrecognized
375
+ // frontmatter key) silently dropped the entire turn's activation log row,
376
+ // masking the underlying data-corruption bug.
377
+ //
378
+ // `conceptRowsForLog` only receives the caller-provided `telemetryRows`
379
+ // *after* status finalization succeeds — matching the prior behavior where
380
+ // an early `save()` / `renderInjectionBlock()` throw produced an empty
381
+ // `concepts` array on the log row.
382
+ let block: string | null = null;
383
+ let conceptRowsForLog: MemoryV2ConceptRowRecord[] = [];
384
+ let caughtErr: unknown = undefined;
227
385
 
228
- // Render before recording telemetry so the activation log can mark slugs
229
- // whose backing file is gone — those are no-op renders that would otherwise
230
- // be indistinguishable from successful "injected" rows in the log.
231
- // `renderInjectionBlock` itself short-circuits on empty inputs.
232
- const { block, missingSlugs } = await renderInjectionBlock(
233
- workspaceDir,
234
- slugsToRender,
235
- );
236
- const missingSlugSet = new Set(missingSlugs);
237
- if (missingSlugs.length > 0) {
238
- log.warn(
239
- {
240
- conversationId,
241
- turn: currentTurn,
242
- missingSlugs,
243
- renderedCount: slugsToRender.length - missingSlugs.length,
244
- },
245
- "Memory v2 injection skipped slugs whose page was missing on disk — Qdrant index may be stale; consider reembed",
246
- );
247
- }
386
+ try {
387
+ await save(database, conversationId, nextActivationState);
388
+
389
+ // Render before recording telemetry so the activation log can mark slugs
390
+ // whose backing file is gone or failed to load — those are no-op renders
391
+ // that would otherwise be indistinguishable from successful "injected"
392
+ // rows in the log. `renderInjectionBlock` itself short-circuits on empty
393
+ // inputs and emits per-slug `log.warn` for each corrupt page.
394
+ const rendered = await renderInjectionBlock(workspaceDir, slugsToRender);
395
+ block = rendered.block;
396
+ const { missingSlugs, corruptSlugs } = rendered;
397
+ const missingSlugSet = new Set(missingSlugs);
398
+ const corruptSlugSet = new Set(corruptSlugs);
399
+ if (missingSlugs.length > 0) {
400
+ log.warn(
401
+ {
402
+ conversationId,
403
+ turn: currentTurn,
404
+ missingSlugs,
405
+ renderedCount:
406
+ slugsToRender.length - missingSlugs.length - corruptSlugs.length,
407
+ },
408
+ "Memory v2 injection skipped slugs whose page was missing on disk — Qdrant index may be stale; consider reembed",
409
+ );
410
+ }
248
411
 
249
- // Record per-turn activation telemetry. Failures are warn-logged and never
250
- // block memory injection.
251
- const toInjectSet = new Set(toInject);
252
- const renderedSet = new Set(slugsToRender);
253
- const conceptRows: MemoryV2ConceptRowRecord[] = [...candidates].map(
254
- (slug) => {
255
- const breakdown = ownBreakdown.get(slug);
256
- const inPrior = fromPrior.has(slug);
257
- const inAnn = fromAnn.has(slug);
258
- // Status reflects what was rendered for *this* turn:
259
- // - context-load: cache was wiped (turn 1 / post-compaction), so
260
- // `slugsToRender = topNow` and every rendered slug is freshly
261
- // injected on this turn. `in_context` is unreachable because there
262
- // is no prior cached attachment for the inspector to point at.
263
- // - per-turn: cached attachments from prior turns are still on the
264
- // user message, so prior-everInjected slugs are `in_context` and
265
- // the delta (`toInject`) is `injected`.
266
- // `page_missing` overrides any "would-have-been-injected" status when
267
- // `readPage` returned null for the slug — telemetry surfaces stale
268
- // ANN/edge entries instead of silently masquerading as a successful
269
- // injection.
412
+ // Finalize per-row status onto the caller-provided telemetry rows.
413
+ // - context-load: cache was wiped (turn 1 / post-compaction), so
414
+ // `slugsToRender = topNow` and every rendered slug is freshly
415
+ // injected on this turn. `in_context` is unreachable because there
416
+ // is no prior cached attachment for the inspector to point at.
417
+ // - per-turn: cached attachments from prior turns are still on the
418
+ // user message, so prior-everInjected slugs are `in_context` and
419
+ // the delta (`slugsToRender`, which equals `toInject` in this mode)
420
+ // is `injected`.
421
+ // `page_missing` and `corrupt` override any "would-have-been-injected"
422
+ // status when `readPage` returned null or threw telemetry surfaces
423
+ // stale ANN/edge entries and malformed pages instead of silently
424
+ // masquerading as successful injections. `corrupt` takes priority over
425
+ // `page_missing` since they're mutually exclusive per slug.
426
+ const renderedSet = new Set(slugsToRender);
427
+ for (const row of telemetryRows) {
428
+ const slug = row.slug;
270
429
  let status: MemoryV2ConceptRowRecord["status"];
271
430
  if (mode === "context-load") {
272
431
  status = renderedSet.has(slug) ? "injected" : "not_injected";
273
432
  } else if (everInjectedSet.has(slug)) {
274
433
  status = "in_context";
275
- } else if (toInjectSet.has(slug)) {
434
+ } else if (renderedSet.has(slug)) {
276
435
  status = "injected";
277
436
  } else {
278
437
  status = "not_injected";
@@ -280,52 +439,195 @@ export async function injectMemoryV2Block(
280
439
  if (status === "injected" && missingSlugSet.has(slug)) {
281
440
  status = "page_missing";
282
441
  }
283
- return {
284
- slug,
285
- finalActivation: finalActivation.get(slug) ?? 0,
286
- ownActivation: ownActivation.get(slug) ?? 0,
287
- priorActivation: breakdown?.priorContribution ?? 0,
288
- simUser: breakdown?.simUser ?? 0,
289
- simAssistant: breakdown?.simAssistant ?? 0,
290
- simNow: breakdown?.simNow ?? 0,
291
- simUserRerankBoost: breakdown?.simUserRerankBoost ?? 0,
292
- simAssistantRerankBoost: breakdown?.simAssistantRerankBoost ?? 0,
293
- inRerankPool: breakdown?.inRerankPool ?? false,
294
- spreadContribution: spreadContribution.get(slug) ?? 0,
295
- source:
296
- inPrior && inAnn ? "both" : inPrior ? "prior_state" : "ann_top50",
297
- status,
298
- };
299
- },
300
- );
301
- conceptRows.sort((a, b) => b.finalActivation - a.finalActivation);
302
-
303
- const v2Cfg = config.memory.v2;
304
- try {
305
- recordMemoryV2ActivationLog({
306
- conversationId,
307
- turn: currentTurn,
308
- mode,
309
- concepts: conceptRows,
310
- config: {
311
- d: v2Cfg.d,
312
- c_user: v2Cfg.c_user,
313
- c_assistant: v2Cfg.c_assistant,
314
- c_now: v2Cfg.c_now,
315
- k: v2Cfg.k,
316
- hops: v2Cfg.hops,
317
- top_k: v2Cfg.top_k,
318
- epsilon: v2Cfg.epsilon,
319
- },
320
- });
442
+ if (corruptSlugSet.has(slug)) {
443
+ status = "corrupt";
444
+ }
445
+ row.status = status;
446
+ }
447
+ telemetryRows.sort((a, b) => b.finalActivation - a.finalActivation);
448
+ conceptRowsForLog = telemetryRows;
321
449
  } catch (err) {
450
+ // Stash the error and let `finally` flush a best-effort telemetry row
451
+ // before we re-throw to the caller. `mode = "errored"` flags the row
452
+ // for observability dashboards / inspector queries.
453
+ caughtErr = err;
454
+ mode = "errored";
455
+ } finally {
456
+ try {
457
+ recordMemoryV2ActivationLog({
458
+ conversationId,
459
+ turn: currentTurn,
460
+ mode,
461
+ concepts: conceptRowsForLog,
462
+ config: configSnapshot(config),
463
+ });
464
+ } catch (telemetryErr) {
465
+ log.warn(
466
+ { err: telemetryErr, conversationId, turn: currentTurn },
467
+ "Failed to record memory v2 activation telemetry — continuing",
468
+ );
469
+ }
470
+ }
471
+
472
+ if (caughtErr !== undefined) throw caughtErr;
473
+ return { block, toInject: newlyInjected };
474
+ }
475
+
476
+ /**
477
+ * Router-mode dispatch path. Replaces the spreading-activation pipeline with
478
+ * a single LLM call that picks the per-turn concept-page set. On success we
479
+ * reuse `finalizeInjection` so the persistence/render/telemetry contract
480
+ * stays identical to the activation path; on `runRouter` failure we still
481
+ * advance `activation_state` (so `currentTurn` and `messageId` move forward)
482
+ * and emit a `mode: "errored"` telemetry row so the failure is observable.
483
+ *
484
+ * Failure rows are tagged `errored`, not `router`, because router-mode rows
485
+ * are reserved for successful selections — keeping the two visually distinct
486
+ * in inspector queries. `nextStateMap` is always empty in router mode: the
487
+ * router does not compute spreading-activation scores, so there is no sparse
488
+ * activation map to persist.
489
+ */
490
+ async function injectViaRouter(args: {
491
+ workspaceDir: string;
492
+ database: DrizzleDb;
493
+ conversationId: string;
494
+ currentTurn: number;
495
+ userMessage: string;
496
+ assistantMessage: string;
497
+ nowText: string;
498
+ messageId: string;
499
+ config: AssistantConfig;
500
+ priorState: ActivationState | null;
501
+ signal?: AbortSignal;
502
+ }): Promise<InjectMemoryV2BlockResult> {
503
+ const {
504
+ workspaceDir,
505
+ database,
506
+ conversationId,
507
+ currentTurn,
508
+ userMessage,
509
+ assistantMessage,
510
+ nowText,
511
+ messageId,
512
+ config,
513
+ priorState,
514
+ signal,
515
+ } = args;
516
+
517
+ const priorEverInjected: readonly EverInjectedEntry[] =
518
+ priorState?.everInjected ?? [];
519
+
520
+ const routerResult = await runRouter({
521
+ workspaceDir,
522
+ userMessage,
523
+ assistantMessage,
524
+ nowText,
525
+ priorEverInjected,
526
+ config,
527
+ ...(signal ? { signal } : {}),
528
+ });
529
+
530
+ if (routerResult.failureReason !== null) {
322
531
  log.warn(
323
- { err, conversationId, turn: currentTurn },
324
- "Failed to record memory v2 activation telemetry continuing",
532
+ { failureReason: routerResult.failureReason },
533
+ "memory v2 router failure; skipping injection",
325
534
  );
535
+ // Delegate the failure path to `finalizeInjection` with empty inputs
536
+ // and `mode: "errored"`. The helper persists a stub activation_state
537
+ // (preserving `priorEverInjected` so future turns still subtract
538
+ // previously-attached slugs) and writes the telemetry row through the
539
+ // same code path as the success branch — no inline duplication of
540
+ // `save` + `recordMemoryV2ActivationLog`.
541
+ return finalizeInjection({
542
+ workspaceDir,
543
+ database,
544
+ conversationId,
545
+ mode: "errored",
546
+ currentTurn,
547
+ messageId,
548
+ priorEverInjected,
549
+ slugsToRender: [],
550
+ telemetryRows: [],
551
+ config,
552
+ nextStateMap: {},
553
+ });
326
554
  }
327
555
 
328
- return { block, toInject: newlyInjected };
556
+ // Dedupe router-picked slugs against `priorEverInjected` BEFORE rendering.
557
+ // The router prompt explicitly invites the model to re-pick already-injected
558
+ // pages "to re-anchor"; if we passed those through, `renderInjectionBlock`
559
+ // would re-emit the slug into a fresh `<memory>` block while the prior
560
+ // turn's cached attachment is still on the prior user message — duplicate
561
+ // content. Activation per-turn mode does not have this issue because
562
+ // `selectInjections()` returns `toInject = topNow - everInjected`.
563
+ //
564
+ // Telemetry rows for prior-everInjected slugs are still emitted below,
565
+ // but tagged `source: "carry_over"` (not `"router"`) so inspector queries
566
+ // can attribute selections correctly.
567
+ const everInjectedSet = new Set(priorEverInjected.map((e) => e.slug));
568
+ const slugsToRender = routerResult.selectedSlugs.filter(
569
+ (s) => !everInjectedSet.has(s),
570
+ );
571
+
572
+ // Build minimal telemetry rows for the union of router-selected slugs and
573
+ // prior `everInjected` slugs. Router-mode rows zero out every activation
574
+ // value (no spreading activation runs). Slugs the router picked this turn
575
+ // get `source: "router"`; prior-everInjected slugs the router did NOT
576
+ // re-pick get `source: "carry_over"`. The `status` placeholder is
577
+ // overwritten by `finalizeInjection`.
578
+ const routerPicked = new Set(routerResult.selectedSlugs);
579
+ const telemetrySlugs = new Set<string>(routerPicked);
580
+ for (const entry of priorEverInjected) telemetrySlugs.add(entry.slug);
581
+ const telemetryRows: MemoryV2ConceptRowRecord[] = [...telemetrySlugs].map(
582
+ (slug) => ({
583
+ slug,
584
+ finalActivation: 0,
585
+ ownActivation: 0,
586
+ priorActivation: 0,
587
+ simUser: 0,
588
+ simAssistant: 0,
589
+ simNow: 0,
590
+ simUserRerankBoost: 0,
591
+ simAssistantRerankBoost: 0,
592
+ inRerankPool: false,
593
+ spreadContribution: 0,
594
+ source: routerPicked.has(slug) ? "router" : "carry_over",
595
+ status: "not_injected",
596
+ }),
597
+ );
598
+
599
+ return finalizeInjection({
600
+ workspaceDir,
601
+ database,
602
+ conversationId,
603
+ mode: "router",
604
+ currentTurn,
605
+ messageId,
606
+ priorEverInjected,
607
+ slugsToRender,
608
+ telemetryRows,
609
+ config,
610
+ nextStateMap: {},
611
+ });
612
+ }
613
+
614
+ /**
615
+ * Snapshot the v2 config tunables in the shape `recordMemoryV2ActivationLog`
616
+ * persists. Pulled out so the router-failure path does not duplicate the
617
+ * field list inline.
618
+ */
619
+ function configSnapshot(config: AssistantConfig) {
620
+ const v2Cfg = config.memory.v2;
621
+ return {
622
+ d: v2Cfg.d,
623
+ c_user: v2Cfg.c_user,
624
+ c_assistant: v2Cfg.c_assistant,
625
+ c_now: v2Cfg.c_now,
626
+ k: v2Cfg.k,
627
+ hops: v2Cfg.hops,
628
+ top_k: v2Cfg.top_k,
629
+ epsilon: v2Cfg.epsilon,
630
+ };
329
631
  }
330
632
 
331
633
  function throwIfAborted(signal: AbortSignal | undefined): void {
@@ -358,14 +660,24 @@ interface RenderInjectionBlockResult {
358
660
  * edge-index entries that pointed at pages no longer on disk.
359
661
  */
360
662
  missingSlugs: string[];
663
+ /**
664
+ * Slugs whose `readPage` call threw (e.g. invalid frontmatter that fails
665
+ * Zod validation, unreadable file). These are reported separately from
666
+ * `missingSlugs` because they're a different failure mode — the file
667
+ * exists but is malformed, not absent — and surfaced so the caller can
668
+ * mark them in the activation log (`status: "corrupt"`). Per-page errors
669
+ * are isolated: one bad page no longer rejects the whole batch.
670
+ */
671
+ corruptSlugs: string[];
361
672
  }
362
673
 
363
674
  /**
364
- * Leading instruction line emitted at the top of every non-empty injection
365
- * block. Tells the agent that what follows are page summaries and that it
366
- * should read the underlying file when a summary looks relevant. Pages
367
- * without a `summary` field render in full instead the agent treats
368
- * those as inline content and doesn't need to follow up.
675
+ * Leading instruction line emitted at the top of an injection block when at
676
+ * least one section was rendered from a page's `summary` field. Tells the
677
+ * agent the truncated entries are summaries and to read the underlying file
678
+ * if relevant. Suppressed when every section is a full-page fallback
679
+ * claiming "these are summaries" over already-complete content would mislead
680
+ * the agent into wasted reads.
369
681
  */
370
682
  const INJECTION_HEADER =
371
683
  "**CRITICAL:** These are page summaries. Read the page file if it looks relevant.";
@@ -380,11 +692,15 @@ const INJECTION_HEADER =
380
692
  * trailing `### Skills You Can Use` subsection; everything else is read
381
693
  * from disk via `readPage` and rendered as a concept-page section.
382
694
  *
383
- * Concept pages are read in parallel via `readPage`. Pages whose file has
384
- * gone missing between selection and render (e.g. consolidation deleted
385
- * them, folder reorg renamed the slug) are dropped from the rendered
386
- * block but reported back via `missingSlugs` so callers can surface the
387
- * divergence.
695
+ * Concept pages are read in parallel via `Promise.allSettled`. Per-page
696
+ * errors are isolated: a `readPage` rejection (e.g. invalid frontmatter
697
+ * failing Zod validation) collects the slug into `corruptSlugs` and the
698
+ * remaining pages still render normally. Pages whose file has gone missing
699
+ * between selection and render (e.g. consolidation deleted them, folder
700
+ * reorg renamed the slug) are dropped from the rendered block but reported
701
+ * back via `missingSlugs`. The two buckets are kept separate so callers can
702
+ * distinguish "file vanished" (stale index) from "file is malformed"
703
+ * (data-corruption / programmer error).
388
704
  *
389
705
  * Skill slugs whose entry the cache no longer knows (e.g. uninstalled
390
706
  * mid-run) are silently dropped, mirroring the missing-pages behavior but
@@ -424,16 +740,26 @@ async function renderInjectionBlock(
424
740
  const conceptSlugs = slugs.filter((s) => !isSkillSlug(s));
425
741
  const skillSlugs = slugs.filter((s) => isSkillSlug(s));
426
742
 
427
- const pages = await Promise.all(
428
- conceptSlugs.map(async (slug) => {
429
- const page = await readPage(workspaceDir, slug);
430
- return { slug, page };
431
- }),
743
+ const settled = await Promise.allSettled(
744
+ conceptSlugs.map((slug) => readPage(workspaceDir, slug)),
432
745
  );
433
746
 
434
747
  const sections: string[] = [];
435
748
  const missingSlugs: string[] = [];
436
- for (const { slug, page } of pages) {
749
+ const corruptSlugs: string[] = [];
750
+ let anySummarySection = false;
751
+ for (let i = 0; i < settled.length; i++) {
752
+ const slug = conceptSlugs[i]!;
753
+ const result = settled[i]!;
754
+ if (result.status === "rejected") {
755
+ corruptSlugs.push(slug);
756
+ log.warn(
757
+ { slug, err: result.reason },
758
+ "Memory v2 injection skipped slug whose page failed to load — frontmatter may be malformed",
759
+ );
760
+ continue;
761
+ }
762
+ const page = result.value;
437
763
  if (!page) {
438
764
  missingSlugs.push(slug);
439
765
  continue;
@@ -442,6 +768,7 @@ async function renderInjectionBlock(
442
768
  const path = `memory/concepts/${slug}.md`;
443
769
  if (summary && summary.length > 0) {
444
770
  sections.push(`# ${path}\n${summary}`);
771
+ anySummarySection = true;
445
772
  continue;
446
773
  }
447
774
  // Fallback: page predates the `summary` field (or the field was set to
@@ -462,10 +789,14 @@ async function renderInjectionBlock(
462
789
  sections.push(`### Skills You Can Use\n${skillLines.join("\n")}`);
463
790
  }
464
791
 
465
- if (sections.length === 0) return { block: null, missingSlugs };
792
+ if (sections.length === 0) {
793
+ return { block: null, missingSlugs, corruptSlugs };
794
+ }
466
795
 
796
+ const body = sections.join("\n\n");
467
797
  return {
468
- block: `${INJECTION_HEADER}\n\n${sections.join("\n\n")}`,
798
+ block: anySummarySection ? `${INJECTION_HEADER}\n\n${body}` : body,
469
799
  missingSlugs,
800
+ corruptSlugs,
470
801
  };
471
802
  }