@vellumai/assistant 0.7.3 → 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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /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,8 +660,28 @@ 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
 
674
+ /**
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.
681
+ */
682
+ const INJECTION_HEADER =
683
+ "**CRITICAL:** These are page summaries. Read the page file if it looks relevant.";
684
+
363
685
  /**
364
686
  * Render the inner content of the `<memory>` block for a list of slugs.
365
687
  * The caller wraps the result in `<memory>...</memory>` exactly once at
@@ -370,11 +692,15 @@ interface RenderInjectionBlockResult {
370
692
  * trailing `### Skills You Can Use` subsection; everything else is read
371
693
  * from disk via `readPage` and rendered as a concept-page section.
372
694
  *
373
- * Concept pages are read in parallel via `readPage`. Pages whose file has
374
- * gone missing between selection and render (e.g. consolidation deleted
375
- * them, folder reorg renamed the slug) are dropped from the rendered
376
- * block but reported back via `missingSlugs` so callers can surface the
377
- * 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).
378
704
  *
379
705
  * Skill slugs whose entry the cache no longer knows (e.g. uninstalled
380
706
  * mid-run) are silently dropped, mirroring the missing-pages behavior but
@@ -383,23 +709,24 @@ interface RenderInjectionBlockResult {
383
709
  * skill is an expected catalog-level outcome rather than a stale-index
384
710
  * bug.
385
711
  *
386
- * The block shape mirrors the §5 layout concept-page sections first,
387
- * skills subsection last preserving the prompt format the agent sees:
712
+ * Each concept-page section is rendered as a path header followed by either
713
+ * the page's `summary` (when present in frontmatter) or the full page (the
714
+ * fallback for pages predating the summary field). Skills sit at the end
715
+ * under `### Skills You Can Use`, unchanged. The leading `**CRITICAL:**`
716
+ * line tells the agent how to read the block.
388
717
  *
389
- * ### <concept-slug-1>
718
+ * **CRITICAL:** These are page summaries. Read the page file if it looks relevant.
719
+ *
720
+ * # memory/concepts/<concept-slug-1>.md
721
+ * <summary-1>
722
+ *
723
+ * # memory/concepts/<concept-slug-2>.md
390
724
  * ---
391
725
  * edges:
392
726
  * - <neighbor-slug>
393
727
  * ref_files:
394
728
  * - <path/to/asset>
395
729
  * ---
396
- * <body-1>
397
- *
398
- * ### <concept-slug-2>
399
- * ---
400
- * edges: []
401
- * ref_files: []
402
- * ---
403
730
  * <body-2>
404
731
  *
405
732
  * ### Skills You Can Use
@@ -413,23 +740,43 @@ async function renderInjectionBlock(
413
740
  const conceptSlugs = slugs.filter((s) => !isSkillSlug(s));
414
741
  const skillSlugs = slugs.filter((s) => isSkillSlug(s));
415
742
 
416
- const pages = await Promise.all(
417
- conceptSlugs.map(async (slug) => {
418
- const page = await readPage(workspaceDir, slug);
419
- return { slug, page };
420
- }),
743
+ const settled = await Promise.allSettled(
744
+ conceptSlugs.map((slug) => readPage(workspaceDir, slug)),
421
745
  );
422
746
 
423
747
  const sections: string[] = [];
424
748
  const missingSlugs: string[] = [];
425
- 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;
426
763
  if (!page) {
427
764
  missingSlugs.push(slug);
428
765
  continue;
429
766
  }
767
+ const summary = page.frontmatter.summary?.trim();
768
+ const path = `memory/concepts/${slug}.md`;
769
+ if (summary && summary.length > 0) {
770
+ sections.push(`# ${path}\n${summary}`);
771
+ anySummarySection = true;
772
+ continue;
773
+ }
774
+ // Fallback: page predates the `summary` field (or the field was set to
775
+ // empty). Render the full page — frontmatter + body — so retrieval
776
+ // still surfaces the same content the agent saw before this change.
430
777
  const content = renderPageContent(page).trim();
431
778
  if (content.length === 0) continue;
432
- sections.push(`### ${slug}\n${content}`);
779
+ sections.push(`# ${path}\n${content}`);
433
780
  }
434
781
 
435
782
  const skillLines: string[] = [];
@@ -442,10 +789,14 @@ async function renderInjectionBlock(
442
789
  sections.push(`### Skills You Can Use\n${skillLines.join("\n")}`);
443
790
  }
444
791
 
445
- if (sections.length === 0) return { block: null, missingSlugs };
792
+ if (sections.length === 0) {
793
+ return { block: null, missingSlugs, corruptSlugs };
794
+ }
446
795
 
796
+ const body = sections.join("\n\n");
447
797
  return {
448
- block: sections.join("\n\n"),
798
+ block: anySummarySection ? `${INJECTION_HEADER}\n\n${body}` : body,
449
799
  missingSlugs,
800
+ corruptSlugs,
450
801
  };
451
802
  }