@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
@@ -31,7 +31,7 @@ import type { Provider } from "../../providers/types.js";
31
31
  import { getLogger } from "../../util/logger.js";
32
32
  import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
33
33
  import { enqueueMemoryJob } from "../jobs-store.js";
34
- import { slugify, writePage } from "./page-store.js";
34
+ import { deletePage, listPages, slugify, writePage } from "./page-store.js";
35
35
  import type { ConceptPage } from "./types.js";
36
36
 
37
37
  const log = getLogger("memory-v2-migration");
@@ -111,6 +111,9 @@ export function gatherV1State(
111
111
  // domain layer (which v2 will eventually replace). We only need id /
112
112
  // content / type / significance / event_date — everything else is unused
113
113
  // by the migration.
114
+ // Soft-deleted nodes (`fidelity = 'gone'`) are excluded so deleted memories
115
+ // don't get resurrected into v2 concept pages. The rest of the codebase uses
116
+ // the same filter when reading live graph state.
114
117
  const nodeRows = raw
115
118
  .query<
116
119
  {
@@ -122,10 +125,12 @@ export function gatherV1State(
122
125
  },
123
126
  []
124
127
  >(
125
- /*sql*/ `SELECT id, content, type, significance, event_date FROM memory_graph_nodes`,
128
+ /*sql*/ `SELECT id, content, type, significance, event_date FROM memory_graph_nodes WHERE fidelity != 'gone'`,
126
129
  )
127
130
  .all();
131
+ const liveNodeIds = new Set<string>();
128
132
  for (const row of nodeRows) {
133
+ liveNodeIds.add(row.id);
129
134
  items.push({
130
135
  id: row.id,
131
136
  text: row.content,
@@ -138,16 +143,23 @@ export function gatherV1State(
138
143
  }
139
144
 
140
145
  // -- Graph edges --
146
+ // Edges with either endpoint pointing at a soft-deleted node are dropped to
147
+ // stay consistent with the node filter above.
141
148
  const edgeRows = raw
142
149
  .query<
143
150
  { source_node_id: string; target_node_id: string },
144
151
  []
145
152
  >(/*sql*/ `SELECT source_node_id, target_node_id FROM memory_graph_edges`)
146
153
  .all();
147
- const edges: V1Edge[] = edgeRows.map((row) => ({
148
- sourceNodeId: row.source_node_id,
149
- targetNodeId: row.target_node_id,
150
- }));
154
+ const edges: V1Edge[] = [];
155
+ for (const row of edgeRows) {
156
+ if (!liveNodeIds.has(row.source_node_id)) continue;
157
+ if (!liveNodeIds.has(row.target_node_id)) continue;
158
+ edges.push({
159
+ sourceNodeId: row.source_node_id,
160
+ targetNodeId: row.target_node_id,
161
+ });
162
+ }
151
163
 
152
164
  // -- PKB content --
153
165
  const pkbDir = join(workspaceDir, "pkb");
@@ -234,10 +246,10 @@ export interface Cluster {
234
246
  /**
235
247
  * Group v1 items into proposed concept pages.
236
248
  *
237
- * The heuristic is intentionally simple — embedding-cluster is on the
238
- * "follow-up improvements" list (see plan §11), but for the v1-of-v2 cutover
239
- * file-based grouping for PKB plus per-graph-node singletons gives a
240
- * reasonable starting set that the LLM can refine in stage 3:
249
+ * The heuristic is intentionally simple — embedding-based clustering is a
250
+ * planned follow-up. For the v1-of-v2 cutover, file-based grouping for PKB
251
+ * plus per-graph-node singletons gives a reasonable starting set that the LLM
252
+ * can refine in stage 3:
241
253
  *
242
254
  * - Each `pkb_topic` file becomes its own cluster (slug derived from
243
255
  * filename — that's literally what topic files were already keyed on).
@@ -333,7 +345,7 @@ export async function synthesizeConceptPage(
333
345
 
334
346
  return {
335
347
  slug: slugify(cluster.slugHint),
336
- frontmatter: { edges: [], ref_files: [] },
348
+ frontmatter: { edges: [], ref_files: [], ref_urls: [] },
337
349
  body: body.endsWith("\n") ? body : `${body}\n`,
338
350
  };
339
351
  }
@@ -447,13 +459,23 @@ export function collapseEdges(
447
459
  // ---------------------------------------------------------------------------
448
460
 
449
461
  /**
450
- * Enqueue an `embed_concept_page` job for each newly-written slug. The job
451
- * itself lands in PR 13 — we just stage the queue here, so the embeddings
462
+ * Enqueue an `embed_concept_page` job for each newly-written slug. The handler
463
+ * is implemented separately — we just stage the queue here so the embeddings
452
464
  * are ready by the time activation needs them.
465
+ *
466
+ * `database` is threaded through to `enqueueMemoryJob` as the override DB
467
+ * handle. Without this, jobs would be written to the global `getDb()` instead
468
+ * of the migration's DB — which is wrong for tests, isolated runners, and
469
+ * multi-workspace processes that pass an explicit `database`.
453
470
  */
454
- export function enqueueEmbeds(slugs: string[]): number {
471
+ export function enqueueEmbeds(slugs: string[], database: DrizzleDb): number {
455
472
  for (const slug of slugs) {
456
- enqueueMemoryJob("embed_concept_page", { slug });
473
+ enqueueMemoryJob(
474
+ "embed_concept_page",
475
+ { slug },
476
+ undefined,
477
+ database as never,
478
+ );
457
479
  }
458
480
  return slugs.length;
459
481
  }
@@ -570,15 +592,37 @@ export async function runMemoryV2Migration(
570
592
  };
571
593
  });
572
594
 
595
+ // On force-rerun, drop pre-existing pages whose slugs aren't produced by
596
+ // this run. `writePage` is an atomic per-slug overwrite, so without this
597
+ // step a force rerun would leave orphan pages on disk from earlier slugs
598
+ // that no longer match any v1 cluster.
599
+ if (force) {
600
+ const newSlugs = new Set(finalizedPages.map((p) => p.slug));
601
+ const existingSlugs = await listPages(workspaceDir);
602
+ await Promise.all(
603
+ existingSlugs
604
+ .filter((slug) => !newSlugs.has(slug))
605
+ .map((slug) => deletePage(workspaceDir, slug)),
606
+ );
607
+ }
608
+
573
609
  // Page writes hit different filenames so they're safe to fan out.
574
610
  await Promise.all(
575
611
  finalizedPages.map((page) => writePage(workspaceDir, page)),
576
612
  );
577
613
 
578
614
  const promotions = derivePromotions(items);
615
+ if (force) {
616
+ // Strip any prior migration block so force-reruns re-emit fresh
617
+ // promotions instead of being skipped by the in-file marker guard.
618
+ await stripPromotionMarkerBlocks(workspaceDir);
619
+ }
579
620
  await appendPromotions(workspaceDir, promotions);
580
621
 
581
- const embedsEnqueued = enqueueEmbeds(finalizedPages.map((p) => p.slug));
622
+ const embedsEnqueued = enqueueEmbeds(
623
+ finalizedPages.map((p) => p.slug),
624
+ database,
625
+ );
582
626
 
583
627
  await writeSentinel(workspaceDir);
584
628
 
@@ -598,10 +642,18 @@ export async function runMemoryV2Migration(
598
642
  };
599
643
  }
600
644
 
645
+ /**
646
+ * HTML marker embedded in each appended block so a crash-recovery rerun can
647
+ * detect already-applied promotions and skip the append. `appendLines` is a
648
+ * read-modify-write — without this, a crash between `appendPromotions` and
649
+ * `writeSentinel` would let the next boot duplicate every promotion line.
650
+ */
651
+ const PROMOTION_MARKER = "<!-- migration:v1-to-v2 -->";
652
+
601
653
  /**
602
654
  * Append each promotion bucket to its target file. Files are created if
603
- * absent — the workspace migration in PR 3 (`060-memory-v2-init`) seeds
604
- * empty placeholders, so this is mostly belt-and-suspenders.
655
+ * absent — the `060-memory-v2-init` workspace migration seeds empty
656
+ * placeholders, so this is mostly belt-and-suspenders.
605
657
  */
606
658
  async function appendPromotions(
607
659
  workspaceDir: string,
@@ -626,7 +678,11 @@ async function appendPromotions(
626
678
  }
627
679
  }
628
680
 
629
- /** Append `lines` to `path`, creating it (with a trailing newline) if absent. */
681
+ /**
682
+ * Append `lines` to `path`, creating it (with a trailing newline) if absent.
683
+ * If the file already contains `PROMOTION_MARKER`, the append is skipped — a
684
+ * prior partially-completed migration already wrote this block.
685
+ */
630
686
  async function appendLines(path: string, lines: string[]): Promise<void> {
631
687
  let existing = "";
632
688
  try {
@@ -634,8 +690,49 @@ async function appendLines(path: string, lines: string[]): Promise<void> {
634
690
  } catch (err) {
635
691
  if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
636
692
  }
693
+ if (existing.includes(PROMOTION_MARKER)) return;
637
694
  const trailing = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
638
- const next = `${existing}${trailing}${lines.join("\n")}\n`;
695
+ const next = `${existing}${trailing}${PROMOTION_MARKER}\n${lines.join("\n")}\n`;
696
+ await writeFile(path, next, "utf-8");
697
+ }
698
+
699
+ /**
700
+ * Strip any prior migration-block (everything from the marker line through
701
+ * end of file) from each promotion target. Called on force-reruns so the
702
+ * marker guard in `appendLines` doesn't skip the new promotions.
703
+ */
704
+ async function stripPromotionMarkerBlocks(workspaceDir: string): Promise<void> {
705
+ const memoryDir = join(workspaceDir, "memory");
706
+ const candidates: string[] = [
707
+ join(memoryDir, "essentials.md"),
708
+ join(memoryDir, "threads.md"),
709
+ ];
710
+ const archiveDir = join(memoryDir, "archive");
711
+ if (existsSync(archiveDir)) {
712
+ for (const name of readdirSync(archiveDir)) {
713
+ if (name.startsWith("migrated-") && name.endsWith(".md")) {
714
+ candidates.push(join(archiveDir, name));
715
+ }
716
+ }
717
+ }
718
+ await Promise.all(candidates.map(stripMarkerBlock));
719
+ }
720
+
721
+ async function stripMarkerBlock(path: string): Promise<void> {
722
+ let existing: string;
723
+ try {
724
+ existing = await readFile(path, "utf-8");
725
+ } catch (err) {
726
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") return;
727
+ throw err;
728
+ }
729
+ const idx = existing.indexOf(PROMOTION_MARKER);
730
+ if (idx === -1) return;
731
+ // Cut from the start of the marker line. `idx` already points at the marker,
732
+ // which `appendLines` always wrote at the start of its own line, so a plain
733
+ // slice here also drops the leading newline that preceded it (if any).
734
+ const trimmed = existing.slice(0, idx).replace(/\n+$/, "");
735
+ const next = trimmed.length === 0 ? "" : `${trimmed}\n`;
639
736
  await writeFile(path, next, "utf-8");
640
737
  }
641
738
 
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Memory v2 — Numbered page index for the router prompt.
3
+ *
4
+ * Renders a compact catalog of every concept page plus every seeded skill
5
+ * entry, sorted by slug ASCII for deterministic IDs, with each entry's
6
+ * outgoing edges resolved to numeric IDs. The router prompt consumes the
7
+ * pre-rendered block to choose which slugs to activate per turn.
8
+ *
9
+ * Skill entries (those in the `skills/<id>` namespace) participate alongside
10
+ * concept pages so the router can reach them through the same mechanism.
11
+ * Skill entries always have `edges: []` — the cross-page edge graph is a
12
+ * concept-page-only construct.
13
+ *
14
+ * The build is cached module-locally per `workspaceDir`, mirroring
15
+ * `edge-index.ts`. Callers must invalidate via `invalidatePageIndex` when
16
+ * concept pages or seeded skill entries change.
17
+ */
18
+
19
+ import { getLogger } from "../../util/logger.js";
20
+ import { listPages, readPage } from "./page-store.js";
21
+
22
+ // Dynamic import for `./skill-store.js` happens inside `getPageIndex` so that
23
+ // modules that only need `invalidatePageIndex` (page-store.ts,
24
+ // tool-side-effects.ts) don't transitively pull in the embedding-backend
25
+ // chain via skill-store. Loading it at call time keeps the invalidation hook
26
+ // cheap and avoids cross-module import cycles in tests that mock jobs-store
27
+ // or embedding-backend.
28
+
29
+ const log = getLogger("memory-v2-page-index");
30
+
31
+ const SUMMARY_MAX_LENGTH = 200;
32
+
33
+ /**
34
+ * One row in the rendered page index. `id` is a 1-based dense integer that is
35
+ * stable within a single index version (i.e. a single build). It changes when
36
+ * the index is rebuilt because IDs are derived from the slug-sorted position;
37
+ * callers must not persist them across builds.
38
+ */
39
+ export interface PageIndexEntry {
40
+ /** 1-based dense numeric id, stable within an index version. */
41
+ id: number;
42
+ /** Concept-page slug or `skills/<id>`. */
43
+ slug: string;
44
+ /** Truncated to {@link SUMMARY_MAX_LENGTH} characters. */
45
+ summary: string;
46
+ /** Numeric IDs of outgoing edges, in sorted order. */
47
+ edges: number[];
48
+ }
49
+
50
+ /**
51
+ * Snapshot of the page index for one workspace. `entries` is sorted by slug
52
+ * ASCII so IDs are deterministic across rebuilds with the same input. The
53
+ * `bySlug` and `byId` maps are convenience lookups; `rendered` is the prompt
54
+ * block consumed by the router.
55
+ */
56
+ export interface PageIndex {
57
+ entries: PageIndexEntry[];
58
+ bySlug: Map<string, PageIndexEntry>;
59
+ byId: Map<number, PageIndexEntry>;
60
+ rendered: string;
61
+ }
62
+
63
+ interface CachedIndex {
64
+ workspaceDir: string;
65
+ index: PageIndex;
66
+ }
67
+
68
+ let cache: CachedIndex | null = null;
69
+
70
+ /**
71
+ * Return a `PageIndex` for `workspaceDir`. Cached module-locally; the cache
72
+ * is invalidated by `invalidatePageIndex` (called by daemon-side hooks when
73
+ * concept pages or skill entries change).
74
+ *
75
+ * Cold builds list every concept page in parallel, drop pages whose read
76
+ * rejects, append seeded skill entries from `listSkillEntries()`, sort by
77
+ * slug for deterministic IDs, then resolve outgoing edges to numeric IDs.
78
+ */
79
+ export async function getPageIndex(workspaceDir: string): Promise<PageIndex> {
80
+ if (cache && cache.workspaceDir === workspaceDir) {
81
+ return cache.index;
82
+ }
83
+
84
+ const slugs = await listPages(workspaceDir);
85
+
86
+ // Read pages in parallel; pages whose read rejects are dropped with a warn
87
+ // so a single broken page never blocks the rest of the index.
88
+ const settled = await Promise.allSettled(
89
+ slugs.map((slug) => readPage(workspaceDir, slug)),
90
+ );
91
+
92
+ // Intermediate shape used while we still need the raw outgoing slugs to
93
+ // resolve into numeric IDs after sorting.
94
+ interface DraftEntry {
95
+ slug: string;
96
+ summary: string;
97
+ outgoingSlugs: string[];
98
+ }
99
+
100
+ const drafts: DraftEntry[] = [];
101
+ for (let i = 0; i < settled.length; i++) {
102
+ const result = settled[i];
103
+ const slug = slugs[i];
104
+ if (result.status === "rejected") {
105
+ log.warn(
106
+ { slug, err: result.reason },
107
+ "Dropping concept page from index — read failed",
108
+ );
109
+ continue;
110
+ }
111
+ const page = result.value;
112
+ if (!page) continue;
113
+ const summarySource = page.frontmatter.summary?.trim() || page.body.trim();
114
+ drafts.push({
115
+ slug,
116
+ summary: summarySource.slice(0, SUMMARY_MAX_LENGTH),
117
+ outgoingSlugs: page.frontmatter.edges,
118
+ });
119
+ }
120
+
121
+ const { listSkillEntries, SKILL_SLUG_PREFIX } =
122
+ await import("./skill-store.js");
123
+ for (const entry of listSkillEntries()) {
124
+ drafts.push({
125
+ slug: `${SKILL_SLUG_PREFIX}${entry.id}`,
126
+ summary: entry.content.trim().slice(0, SUMMARY_MAX_LENGTH),
127
+ outgoingSlugs: [],
128
+ });
129
+ }
130
+
131
+ drafts.sort((a, b) => (a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0));
132
+
133
+ // Assign 1-based dense IDs in sort order so entries[i].id === i + 1.
134
+ const bySlug = new Map<string, PageIndexEntry>();
135
+ const byId = new Map<number, PageIndexEntry>();
136
+ const entries: PageIndexEntry[] = drafts.map((draft, i) => {
137
+ const entry: PageIndexEntry = {
138
+ id: i + 1,
139
+ slug: draft.slug,
140
+ summary: draft.summary,
141
+ edges: [],
142
+ };
143
+ bySlug.set(entry.slug, entry);
144
+ byId.set(entry.id, entry);
145
+ return entry;
146
+ });
147
+
148
+ // Edges whose target slug isn't in the index are dropped silently — the
149
+ // frontmatter sweep is responsible for surfacing those as warnings.
150
+ for (let i = 0; i < entries.length; i++) {
151
+ const draft = drafts[i];
152
+ const resolved: number[] = [];
153
+ for (const targetSlug of draft.outgoingSlugs) {
154
+ const target = bySlug.get(targetSlug);
155
+ if (target) resolved.push(target.id);
156
+ }
157
+ resolved.sort((a, b) => a - b);
158
+ entries[i].edges = resolved;
159
+ }
160
+
161
+ const rendered = renderIndex(entries);
162
+ const index: PageIndex = { entries, bySlug, byId, rendered };
163
+ cache = { workspaceDir, index };
164
+ return index;
165
+ }
166
+
167
+ /**
168
+ * Clear the cached index. Pass `workspaceDir` to scope invalidation to a
169
+ * specific cache entry; omit it to clear unconditionally.
170
+ */
171
+ export function invalidatePageIndex(workspaceDir?: string): void {
172
+ if (!cache) return;
173
+ if (workspaceDir === undefined || cache.workspaceDir === workspaceDir) {
174
+ cache = null;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Render the prompt block: one line per entry shaped
180
+ * `[id] slug — summary (edges: a, b, c)`. Lines without outgoing edges drop
181
+ * the parenthetical entirely. Trailing newline so the block can be
182
+ * concatenated into a larger prompt without manual padding.
183
+ */
184
+ function renderIndex(entries: readonly PageIndexEntry[]): string {
185
+ const lines = entries.map((entry) => {
186
+ const head = `[${entry.id}] ${entry.slug} — ${entry.summary}`;
187
+ if (entry.edges.length === 0) return head;
188
+ return `${head} (edges: ${entry.edges.join(", ")})`;
189
+ });
190
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
191
+ }
@@ -33,6 +33,7 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
33
33
 
34
34
  import { FRONTMATTER_REGEX } from "../../skills/frontmatter.js";
35
35
  import { invalidateEdgeIndex } from "./edge-index.js";
36
+ import { invalidatePageIndex } from "./page-index.js";
36
37
  import { type ConceptPage, ConceptPageFrontmatterSchema } from "./types.js";
37
38
 
38
39
  /** Filename suffix for concept pages. */
@@ -287,6 +288,7 @@ export async function writePage(
287
288
  throw err;
288
289
  }
289
290
  invalidateEdgeIndex(workspaceDir);
291
+ invalidatePageIndex(workspaceDir);
290
292
  }
291
293
 
292
294
  /**
@@ -397,6 +399,7 @@ export async function deletePage(
397
399
  throw err;
398
400
  }
399
401
  invalidateEdgeIndex(workspaceDir);
402
+ invalidatePageIndex(workspaceDir);
400
403
  }
401
404
 
402
405
  /**
@@ -6,9 +6,11 @@
6
6
  * runs with its full system prompt + tool surface; the text below is supplied
7
7
  * as the wake hint.
8
8
  *
9
- * The single placeholder `{{CUTOFF}}` is substituted at runtime with an
10
- * ISO-8601 timestamp captured at job dispatch. Anything appended to
11
- * `memory/buffer.md` after that timestamp is the next pass's problem.
9
+ * The single placeholder `{{CUTOFF}}` is substituted at runtime with a
10
+ * timestamp captured at job dispatch in the same `Mon D, h:mm AM/PM` shape
11
+ * that `buffer.md` entries use, so the agent's "timestamp ≥ cutoff" check
12
+ * compares like-with-like. Anything appended after that minute is the next
13
+ * pass's problem.
12
14
  *
13
15
  * Kept under `prompts/` rather than inlined in `consolidation-job.ts` so the
14
16
  * prompt body is reviewable on its own and the job module stays focused on
@@ -432,10 +434,10 @@ For each article you touched:
432
434
  This is the engine that decides who you are tomorrow. Be ORGANIZED. Care, judgment, voice. Your voice. Your wiki.`;
433
435
 
434
436
  /**
435
- * Resolve `CONSOLIDATION_PROMPT` with `{{CUTOFF}}` substituted. The cutoff
436
- * format is the caller's choicethe prompt treats it as opaque text and
437
- * uses string comparison, so any total-order timestamp format works (ISO-8601
438
- * is the convention).
437
+ * Resolve `CONSOLIDATION_PROMPT` with `{{CUTOFF}}` substituted. The prompt
438
+ * treats the cutoff as opaque text callers pass a `Mon D, h:mm AM/PM`
439
+ * timestamp matching the `buffer.md` entry format so the agent compares
440
+ * like-with-like.
439
441
  */
440
442
  export function renderConsolidationPrompt(cutoff: string): string {
441
443
  return CONSOLIDATION_PROMPT.replaceAll(CUTOFF_PLACEHOLDER, cutoff);
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Memory v2 — router prompt template.
3
+ *
4
+ * The router runs once per assistant turn and decides which concept pages (if
5
+ * any) should be injected on top of the always-on essentials/threads/recent
6
+ * block. The body lives here (under `prompts/`) so it is reviewable on its
7
+ * own, mirroring the convention established in `sweep.ts`.
8
+ *
9
+ * Three placeholders are substituted at runtime:
10
+ *
11
+ * - `{{ASSISTANT_NAME}}` — assistant display name (from IDENTITY.md when
12
+ * available, else the neutral fallback "the assistant").
13
+ * - `{{USER_NAME}}` — guardian display name (from the guardian persona when
14
+ * available, else "the user").
15
+ * - `{{PAGE_INDEX}}` — pre-rendered page index. Each line has the shape
16
+ * `[id] slug — summary (edges: a, b, c)` where edges are numeric IDs into
17
+ * the same list. The caller renders this so the prompt module stays
18
+ * stateless.
19
+ *
20
+ * Operators may replace the bundled body via
21
+ * `memory.v2.router.router_prompt_path` and {@link resolveRouterPrompt} — the
22
+ * same placeholder substitution applies to overrides.
23
+ */
24
+
25
+ import { lstatSync, readFileSync } from "node:fs";
26
+ import { homedir } from "node:os";
27
+ import { isAbsolute, join } from "node:path";
28
+
29
+ import { getLogger } from "../../../util/logger.js";
30
+ import { getWorkspaceDir } from "../../../util/platform.js";
31
+
32
+ const log = getLogger("memory-v2-router-prompt");
33
+
34
+ /**
35
+ * Hard upper bound on the override file size. The bundled prompt is well
36
+ * under 4 KiB; 1 MiB is generous-enough for any reasonable hand-edit while
37
+ * still preventing pathological inputs from being slurped into memory on
38
+ * every router call.
39
+ */
40
+ const MAX_PROMPT_BYTES = 1 * 1024 * 1024;
41
+
42
+ /** Sentinel substituted with the assistant's display name at runtime. */
43
+ const ASSISTANT_NAME_PLACEHOLDER = "{{ASSISTANT_NAME}}";
44
+
45
+ /** Sentinel substituted with the guardian's display name at runtime. */
46
+ const USER_NAME_PLACEHOLDER = "{{USER_NAME}}";
47
+
48
+ /** Sentinel substituted with the rendered page index block at runtime. */
49
+ const PAGE_INDEX_PLACEHOLDER = "{{PAGE_INDEX}}";
50
+
51
+ /**
52
+ * Router prompt — picks at most a handful of concept pages to inject for the
53
+ * next assistant turn. The model emits a `select_pages_to_inject` tool call
54
+ * with a `page_ids` array; the runtime parses the response via the tool
55
+ * definition declared in the router job module.
56
+ *
57
+ * Recent message context and `<now>` / `<already_injected_ids>` blocks are
58
+ * appended at the call site so we don't inadvertently expand `{{` inside
59
+ * dynamic content.
60
+ */
61
+ const ROUTER_PROMPT = `You are a background helper for ${ASSISTANT_NAME_PLACEHOLDER}. Your job is to route memory pages for the next assistant turn between ${ASSISTANT_NAME_PLACEHOLDER} and ${USER_NAME_PLACEHOLDER}.
62
+
63
+ You will be shown the recent conversation, a \`<now>\` marker for the current time, an \`<already_injected_ids>\` block listing pages picked on the previous turn, and a \`# Concept Page Index\` listing every routable page on this workspace.
64
+
65
+ Pick the concept pages whose contents would help ${ASSISTANT_NAME_PLACEHOLDER} respond well on this turn. Lean toward inclusion when in doubt — missing a relevant page is a worse error than surfacing a few unused ones, because the assistant can ignore extras but can't summon context that wasn't loaded. Abstain (return an empty list) only when nothing in the index plausibly bears on the turn.
66
+
67
+ Index format. Each line of the index has the shape:
68
+
69
+ [id] slug — summary (edges: a, b, c)
70
+
71
+ \`id\` is a small integer used to refer to this page. \`edges\` are numeric IDs into the same list, pointing at related pages; you may follow them when one page strongly implies another.
72
+
73
+ Already-injected pages. Pages whose IDs appear in \`<already_injected_ids>\` were picked on the previous turn. Do not pick them again unless ${ASSISTANT_NAME_PLACEHOLDER} should re-anchor on that material — e.g., the topic genuinely returns after drifting away. Routine continuity does not require re-picking; the prior turn's pages are already in the assistant's working context.
74
+
75
+ Time. Bias toward pages that match the current state implied by \`<now>\` and the active conversational threads (what is happening today, what was just decided, who is being discussed). Stale pages with no bearing on the live conversation should be skipped even if their summaries look superficially relevant.
76
+
77
+ Emit your selection by calling \`select_pages_to_inject\` with the chosen \`page_ids\`. Return an empty array to abstain.
78
+
79
+ # Concept Page Index
80
+
81
+ ${PAGE_INDEX_PLACEHOLDER}`;
82
+
83
+ interface RenderRouterPromptOpts {
84
+ assistantName: string | null;
85
+ userName: string | null;
86
+ pageIndexBlock: string;
87
+ }
88
+
89
+ /**
90
+ * Resolve `ROUTER_PROMPT` with assistant name, user name, and the rendered
91
+ * page index substituted in. Falls back to neutral defaults so the prompt
92
+ * still produces well-formed English when either name is unavailable on this
93
+ * workspace. The page index is substituted verbatim — callers are responsible
94
+ * for trimming/formatting it.
95
+ */
96
+ export function renderRouterPrompt(opts: RenderRouterPromptOpts): string {
97
+ return substitutePlaceholders(ROUTER_PROMPT, opts);
98
+ }
99
+
100
+ /**
101
+ * Load the router prompt template, optionally overridden from the file
102
+ * referenced by `memory.v2.router.router_prompt_path`, then substitute the
103
+ * standard placeholders. Path-resolution rules mirror the consolidation
104
+ * prompt override: absolute paths used as-is, leading `~/` expanded to home,
105
+ * relative paths resolved under the workspace root.
106
+ *
107
+ * Failure handling is intentionally permissive — missing file, read error,
108
+ * oversized file, or empty/whitespace-only body all log a warning and fall
109
+ * back to the bundled prompt. Router selection must never break because of
110
+ * a bad override.
111
+ */
112
+ export function resolveRouterPrompt(
113
+ overridePath: string | null,
114
+ opts: RenderRouterPromptOpts,
115
+ ): string {
116
+ if (overridePath === null) return renderRouterPrompt(opts);
117
+
118
+ const resolvedPath = resolveOverridePath(overridePath);
119
+ let contents: string;
120
+ try {
121
+ const stat = lstatSync(resolvedPath);
122
+ if (!stat.isFile()) {
123
+ log.warn(
124
+ {
125
+ configuredPath: overridePath,
126
+ resolvedPath,
127
+ reason: "not_regular_file",
128
+ fallback: "bundled",
129
+ },
130
+ "router prompt override is not a regular file; using bundled prompt",
131
+ );
132
+ return renderRouterPrompt(opts);
133
+ }
134
+ if (stat.size > MAX_PROMPT_BYTES) {
135
+ log.warn(
136
+ {
137
+ configuredPath: overridePath,
138
+ resolvedPath,
139
+ size: stat.size,
140
+ limit: MAX_PROMPT_BYTES,
141
+ reason: "oversized_override",
142
+ fallback: "bundled",
143
+ },
144
+ "router prompt override exceeds size limit; using bundled prompt",
145
+ );
146
+ return renderRouterPrompt(opts);
147
+ }
148
+ contents = readFileSync(resolvedPath, "utf-8");
149
+ } catch (err) {
150
+ const code = (err as NodeJS.ErrnoException).code;
151
+ log.warn(
152
+ { configuredPath: overridePath, resolvedPath, code, fallback: "bundled" },
153
+ "router prompt override unreadable; using bundled prompt",
154
+ );
155
+ return renderRouterPrompt(opts);
156
+ }
157
+
158
+ if (contents.trim().length === 0) {
159
+ log.warn(
160
+ {
161
+ configuredPath: overridePath,
162
+ resolvedPath,
163
+ reason: "empty_override",
164
+ fallback: "bundled",
165
+ },
166
+ "router prompt override is empty; using bundled prompt",
167
+ );
168
+ return renderRouterPrompt(opts);
169
+ }
170
+
171
+ return substitutePlaceholders(contents, opts);
172
+ }
173
+
174
+ function substitutePlaceholders(
175
+ template: string,
176
+ opts: RenderRouterPromptOpts,
177
+ ): string {
178
+ const assistant = opts.assistantName?.trim() || "the assistant";
179
+ const user = opts.userName?.trim() || "the user";
180
+ return template
181
+ .replaceAll(ASSISTANT_NAME_PLACEHOLDER, assistant)
182
+ .replaceAll(USER_NAME_PLACEHOLDER, user)
183
+ .replaceAll(PAGE_INDEX_PLACEHOLDER, opts.pageIndexBlock);
184
+ }
185
+
186
+ function resolveOverridePath(overridePath: string): string {
187
+ if (overridePath.startsWith("~/")) {
188
+ return join(homedir(), overridePath.slice(2));
189
+ }
190
+ if (isAbsolute(overridePath)) return overridePath;
191
+ return join(getWorkspaceDir(), overridePath);
192
+ }