@vellumai/assistant 0.8.6 → 0.8.7

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 (891) hide show
  1. package/AGENTS.md +4 -4
  2. package/Dockerfile +1 -0
  3. package/bun.lock +11 -2
  4. package/docker-entrypoint.sh +8 -6
  5. package/docs/plugins.md +63 -28
  6. package/examples/plugins/echo/register.ts +4 -7
  7. package/knip.json +1 -0
  8. package/node_modules/@vellumai/environments/bun.lock +24 -0
  9. package/node_modules/@vellumai/environments/package.json +18 -0
  10. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  11. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  12. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  13. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  14. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  15. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  16. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
  17. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
  18. package/openapi.yaml +3735 -353
  19. package/package.json +7 -3
  20. package/scripts/generate-openapi.ts +20 -13
  21. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  22. package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
  23. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  24. package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
  25. package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
  26. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  27. package/src/__tests__/agent-loop.test.ts +207 -341
  28. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
  29. package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
  30. package/src/__tests__/anthropic-provider.test.ts +201 -55
  31. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  32. package/src/__tests__/app-control-flow.test.ts +5 -0
  33. package/src/__tests__/approval-cascade.test.ts +4 -11
  34. package/src/__tests__/approval-routes-http.test.ts +4 -2
  35. package/src/__tests__/assistant-event.test.ts +15 -0
  36. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
  37. package/src/__tests__/avatar-e2e.test.ts +7 -37
  38. package/src/__tests__/avatar-generator.test.ts +12 -42
  39. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  40. package/src/__tests__/background-shell-bash.test.ts +3 -7
  41. package/src/__tests__/btw-routes.test.ts +7 -12
  42. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  43. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  44. package/src/__tests__/catalog-files.test.ts +1 -0
  45. package/src/__tests__/channel-approval-routes.test.ts +48 -20
  46. package/src/__tests__/channel-approvals.test.ts +3 -1
  47. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  48. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  49. package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
  50. package/src/__tests__/channel-retry-sweep.test.ts +71 -79
  51. package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
  52. package/src/__tests__/clawhub-files.test.ts +1 -0
  53. package/src/__tests__/compaction-events.test.ts +5 -17
  54. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  55. package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
  56. package/src/__tests__/compaction-trail-store.test.ts +1 -79
  57. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  58. package/src/__tests__/computer-use-tools.test.ts +2 -2
  59. package/src/__tests__/config-watcher.test.ts +28 -0
  60. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  61. package/src/__tests__/context-token-estimator.test.ts +34 -0
  62. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  63. package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
  64. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
  65. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
  66. package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
  67. package/src/__tests__/conversation-agent-loop.test.ts +581 -62
  68. package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
  69. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  70. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  71. package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
  72. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  73. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  74. package/src/__tests__/conversation-fork-crud.test.ts +86 -172
  75. package/src/__tests__/conversation-fork-route.test.ts +16 -14
  76. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  77. package/src/__tests__/conversation-lifecycle.test.ts +3 -2
  78. package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
  79. package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
  80. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  81. package/src/__tests__/conversation-pairing.test.ts +34 -4
  82. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  83. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
  84. package/src/__tests__/conversation-process-callsite.test.ts +27 -30
  85. package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
  86. package/src/__tests__/conversation-queue.test.ts +270 -164
  87. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
  88. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  89. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
  90. package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
  91. package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
  92. package/src/__tests__/conversation-slash-queue.test.ts +37 -31
  93. package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
  94. package/src/__tests__/conversation-speed-override.test.ts +8 -22
  95. package/src/__tests__/conversation-stream-state.test.ts +484 -0
  96. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
  97. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  98. package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
  99. package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
  100. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
  101. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  102. package/src/__tests__/conversation-usage.test.ts +0 -2
  103. package/src/__tests__/conversation-wipe.test.ts +1 -1
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
  105. package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
  107. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  108. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  109. package/src/__tests__/cross-provider-web-search.test.ts +6 -2
  110. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  111. package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
  112. package/src/__tests__/disk-pressure-guard.test.ts +66 -0
  113. package/src/__tests__/disk-pressure-routes.test.ts +9 -2
  114. package/src/__tests__/dm-persistence.test.ts +7 -2
  115. package/src/__tests__/dynamic-page-surface.test.ts +68 -0
  116. package/src/__tests__/edit-propagation.test.ts +1 -2
  117. package/src/__tests__/empty-response-pipeline.test.ts +127 -5
  118. package/src/__tests__/filing-service.test.ts +2 -2
  119. package/src/__tests__/first-greeting.test.ts +55 -14
  120. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  121. package/src/__tests__/gemini-provider.test.ts +351 -28
  122. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  123. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
  124. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  125. package/src/__tests__/heartbeat-service.test.ts +2 -1
  126. package/src/__tests__/history-repair-hook.test.ts +161 -0
  127. package/src/__tests__/history-repair-observability.test.ts +1 -1
  128. package/src/__tests__/history-repair.test.ts +2 -1
  129. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  130. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  131. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  132. package/src/__tests__/host-file-proxy.test.ts +31 -0
  133. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  134. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  135. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  136. package/src/__tests__/host-shell-tool.test.ts +9 -4
  137. package/src/__tests__/http-user-message-parity.test.ts +2 -2
  138. package/src/__tests__/identity-intro-cache.test.ts +35 -14
  139. package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
  140. package/src/__tests__/injector-background-turn.test.ts +1 -1
  141. package/src/__tests__/injector-chain.test.ts +1 -1
  142. package/src/__tests__/injector-disk-pressure.test.ts +1 -1
  143. package/src/__tests__/injector-document-comments.test.ts +1 -1
  144. package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
  145. package/src/__tests__/injector-v3-suppression.test.ts +220 -0
  146. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  147. package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
  148. package/src/__tests__/list-messages-page-latest.test.ts +0 -1
  149. package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
  150. package/src/__tests__/llm-call-pipeline.test.ts +21 -15
  151. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  152. package/src/__tests__/llm-resolver.test.ts +23 -47
  153. package/src/__tests__/llm-usage-store.test.ts +45 -0
  154. package/src/__tests__/log-export-routes.test.ts +59 -0
  155. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  156. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  157. package/src/__tests__/mcp-health-check.test.ts +18 -13
  158. package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
  159. package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
  160. package/src/__tests__/messaging-send-tool.test.ts +8 -4
  161. package/src/__tests__/migration-export-http.test.ts +12 -12
  162. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  163. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  164. package/src/__tests__/migration-validate-http.test.ts +3 -3
  165. package/src/__tests__/native-web-search.test.ts +14 -20
  166. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  167. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  168. package/src/__tests__/oauth-commands-routes.test.ts +1 -1
  169. package/src/__tests__/onboarding-template-contract.test.ts +10 -0
  170. package/src/__tests__/openai-provider.test.ts +66 -70
  171. package/src/__tests__/openai-responses-provider.test.ts +21 -77
  172. package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
  173. package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
  174. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  175. package/src/__tests__/persistence-pipeline.test.ts +15 -26
  176. package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
  177. package/src/__tests__/pipeline-runner.test.ts +2 -3
  178. package/src/__tests__/plugin-bootstrap.test.ts +51 -25
  179. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  180. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  181. package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
  182. package/src/__tests__/plugin-types.test.ts +7 -14
  183. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  184. package/src/__tests__/process-message-background-slack.test.ts +17 -16
  185. package/src/__tests__/process-message-display-content.test.ts +30 -42
  186. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  187. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  188. package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
  189. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  190. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  191. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  192. package/src/__tests__/ratelimit.test.ts +9 -4
  193. package/src/__tests__/relay-server.test.ts +20 -13
  194. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  195. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  196. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  197. package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
  198. package/src/__tests__/schedule-routes.test.ts +80 -10
  199. package/src/__tests__/schedule-store.test.ts +67 -0
  200. package/src/__tests__/schedule-tools.test.ts +125 -0
  201. package/src/__tests__/secret-ingress-http.test.ts +2 -2
  202. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  203. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  204. package/src/__tests__/secret-response-routing.test.ts +13 -11
  205. package/src/__tests__/send-endpoint-busy.test.ts +2 -1
  206. package/src/__tests__/shell-observability.test.ts +249 -0
  207. package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
  208. package/src/__tests__/skill-feature-flags.test.ts +6 -6
  209. package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
  210. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  211. package/src/__tests__/skillssh-files.test.ts +1 -0
  212. package/src/__tests__/starter-task-flow.test.ts +6 -6
  213. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  214. package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
  215. package/src/__tests__/suggestion-routes.test.ts +3 -3
  216. package/src/__tests__/sync-message-contract.test.ts +19 -16
  217. package/src/__tests__/system-prompt.test.ts +54 -0
  218. package/src/__tests__/terminal-tools.test.ts +3 -24
  219. package/src/__tests__/thread-backfill.test.ts +4 -9
  220. package/src/__tests__/title-generate-pipeline.test.ts +1 -1
  221. package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
  222. package/src/__tests__/tool-error-pipeline.test.ts +2 -2
  223. package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
  224. package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
  225. package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
  226. package/src/__tests__/tool-result-truncation.test.ts +3 -1
  227. package/src/__tests__/tools-audio-read.test.ts +113 -0
  228. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  229. package/src/__tests__/turn-events-store.test.ts +11 -7
  230. package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
  231. package/src/__tests__/voice-session-bridge.test.ts +13 -7
  232. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  233. package/src/acp/prepare-agent-env.ts +52 -11
  234. package/src/agent/compaction-circuit.ts +140 -0
  235. package/src/agent/loop.ts +409 -85
  236. package/src/api/README.md +19 -17
  237. package/src/api/constants/tool-execution.ts +21 -0
  238. package/src/api/events/assistant-activity-state.ts +75 -0
  239. package/src/api/events/assistant-outbound-attachment.ts +25 -27
  240. package/src/api/events/assistant-text-delta.ts +6 -8
  241. package/src/api/events/assistant-turn-start.ts +5 -7
  242. package/src/api/events/avatar-updated.ts +24 -0
  243. package/src/api/events/compaction-circuit-closed.ts +26 -0
  244. package/src/api/events/compaction-circuit-open.ts +28 -0
  245. package/src/api/events/confirmation-request.ts +114 -0
  246. package/src/api/events/contact-request.ts +33 -0
  247. package/src/api/events/conversation-error.ts +77 -0
  248. package/src/api/events/conversation-list-invalidated.ts +38 -0
  249. package/src/api/events/conversation-title-updated.ts +24 -0
  250. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  251. package/src/api/events/document-comment-created.ts +24 -28
  252. package/src/api/events/document-comment-deleted.ts +6 -8
  253. package/src/api/events/document-comment-reopened.ts +6 -8
  254. package/src/api/events/document-comment-resolved.ts +8 -10
  255. package/src/api/events/document-editor-update.ts +27 -0
  256. package/src/api/events/error.ts +32 -0
  257. package/src/api/events/generation-cancelled.ts +4 -6
  258. package/src/api/events/generation-handoff.ts +13 -15
  259. package/src/api/events/home-feed-updated.ts +26 -0
  260. package/src/api/events/identity-changed.ts +32 -0
  261. package/src/api/events/interaction-resolved.ts +50 -0
  262. package/src/api/events/message-complete.ts +10 -12
  263. package/src/api/events/message-dequeued.ts +21 -0
  264. package/src/api/events/message-queued-deleted.ts +23 -0
  265. package/src/api/events/message-queued.ts +22 -0
  266. package/src/api/events/message-request-complete.ts +29 -0
  267. package/src/api/events/navigate-settings.ts +20 -0
  268. package/src/api/events/notification-intent.ts +33 -0
  269. package/src/api/events/open-url.ts +6 -8
  270. package/src/api/events/question-request.ts +67 -0
  271. package/src/api/events/relationship-state-updated.ts +4 -6
  272. package/src/api/events/secret-request.ts +42 -0
  273. package/src/api/events/subagent-event.ts +79 -0
  274. package/src/api/events/subagent-spawned.ts +40 -0
  275. package/src/api/events/subagent-status-changed.ts +65 -0
  276. package/src/api/events/sync-changed.ts +29 -0
  277. package/src/api/events/tool-result.ts +129 -0
  278. package/src/api/events/tool-use-start.ts +8 -10
  279. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  280. package/src/api/events/ui-surface-complete.ts +30 -0
  281. package/src/api/events/ui-surface-dismiss.ts +22 -0
  282. package/src/api/events/ui-surface-show.ts +67 -0
  283. package/src/api/events/ui-surface-update.ts +26 -0
  284. package/src/api/events/usage-update.ts +34 -0
  285. package/src/api/events/user-message-echo.ts +35 -0
  286. package/src/api/index.ts +354 -0
  287. package/src/api/requests/dictation.ts +45 -0
  288. package/src/api/responses/disk-pressure-status.ts +26 -0
  289. package/src/api/responses/home.ts +217 -0
  290. package/src/api/responses/llm-context-response.ts +2 -0
  291. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  292. package/src/api/responses/subagent-detail.ts +48 -0
  293. package/src/approvals/guardian-decision-primitive.ts +7 -15
  294. package/src/approvals/guardian-request-resolvers.ts +6 -9
  295. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  296. package/src/avatar/__tests__/avatar-store.test.ts +193 -0
  297. package/src/avatar/avatar-manifest.ts +195 -0
  298. package/src/avatar/avatar-store.ts +113 -0
  299. package/src/avatar/traits-png-sync.ts +8 -2
  300. package/src/background-wake/next-wake.test.ts +31 -1
  301. package/src/background-wake/next-wake.ts +4 -1
  302. package/src/calls/call-conversation-messages.ts +6 -4
  303. package/src/calls/guardian-action-sweep.ts +6 -4
  304. package/src/calls/relay-server.ts +12 -8
  305. package/src/calls/voice-session-bridge.ts +13 -27
  306. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  307. package/src/cli/commands/avatar.ts +17 -11
  308. package/src/cli/commands/conversations.ts +15 -1
  309. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  310. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  311. package/src/cli/commands/db/format.ts +48 -0
  312. package/src/cli/commands/db/index.ts +29 -0
  313. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  314. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  315. package/src/cli/commands/db/repair-steps.ts +164 -0
  316. package/src/cli/commands/db/repair.ts +141 -0
  317. package/src/cli/commands/db/status.ts +366 -0
  318. package/src/cli/commands/memory-v3.ts +159 -445
  319. package/src/cli/lib/cli-colors.ts +24 -6
  320. package/src/cli/program.ts +4 -5
  321. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  322. package/src/config/assistant-feature-flags.ts +2 -2
  323. package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
  324. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  325. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  326. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  327. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  328. package/src/config/call-site-defaults.ts +2 -7
  329. package/src/config/feature-flag-registry.json +25 -9
  330. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
  331. package/src/config/schemas/call-site-catalog.ts +8 -15
  332. package/src/config/schemas/llm.ts +2 -3
  333. package/src/config/schemas/memory-lifecycle.ts +24 -0
  334. package/src/config/schemas/memory-v2.ts +0 -253
  335. package/src/config/schemas/memory-v3.ts +39 -0
  336. package/src/config/schemas/memory.ts +6 -1
  337. package/src/config/schemas/timeouts.ts +3 -1
  338. package/src/context/compactor.ts +54 -31
  339. package/src/context/token-estimator.ts +19 -0
  340. package/src/context/tool-result-truncation.ts +1 -43
  341. package/src/context/window-manager.ts +138 -20
  342. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
  343. package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
  344. package/src/daemon/approval-generators.ts +4 -4
  345. package/src/daemon/config-watcher.ts +7 -1
  346. package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
  347. package/src/daemon/conversation-agent-loop.ts +284 -584
  348. package/src/daemon/conversation-error.ts +7 -7
  349. package/src/daemon/conversation-history.ts +22 -6
  350. package/src/daemon/conversation-launch.ts +4 -8
  351. package/src/daemon/conversation-lifecycle.ts +10 -38
  352. package/src/daemon/conversation-messaging.ts +1 -3
  353. package/src/daemon/conversation-notifiers.ts +7 -5
  354. package/src/daemon/conversation-process.ts +100 -79
  355. package/src/daemon/conversation-runtime-assembly.ts +47 -21
  356. package/src/daemon/conversation-store.ts +6 -5
  357. package/src/daemon/conversation-surfaces.ts +55 -69
  358. package/src/daemon/conversation-tool-setup.ts +3 -0
  359. package/src/daemon/conversation.ts +91 -126
  360. package/src/daemon/daemon-skill-host.ts +2 -6
  361. package/src/daemon/disk-pressure-guard.ts +35 -29
  362. package/src/daemon/external-plugins-bootstrap.ts +46 -24
  363. package/src/daemon/first-greeting.ts +26 -4
  364. package/src/daemon/guardian-action-generators.ts +2 -2
  365. package/src/daemon/handlers/conversations.ts +6 -22
  366. package/src/daemon/handlers/shared.ts +4 -0
  367. package/src/daemon/handlers/skills.ts +15 -14
  368. package/src/daemon/host-app-control-proxy.ts +54 -1
  369. package/src/daemon/host-cu-proxy.ts +46 -22
  370. package/src/daemon/host-file-proxy.ts +25 -1
  371. package/src/daemon/host-proxy-preactivation.ts +25 -6
  372. package/src/daemon/lifecycle.ts +28 -55
  373. package/src/daemon/message-protocol.ts +2 -3
  374. package/src/daemon/message-provenance.ts +49 -0
  375. package/src/daemon/message-types/contacts.ts +3 -20
  376. package/src/daemon/message-types/conversations.ts +13 -111
  377. package/src/daemon/message-types/documents.ts +3 -9
  378. package/src/daemon/message-types/home.ts +4 -17
  379. package/src/daemon/message-types/integrations.ts +2 -6
  380. package/src/daemon/message-types/messages.ts +28 -343
  381. package/src/daemon/message-types/notifications.ts +2 -32
  382. package/src/daemon/message-types/settings.ts +3 -8
  383. package/src/daemon/message-types/skills.ts +2 -0
  384. package/src/daemon/message-types/surfaces.ts +2 -0
  385. package/src/daemon/message-types/sync.ts +12 -25
  386. package/src/daemon/message-types/workspace.ts +3 -11
  387. package/src/daemon/process-message.ts +49 -46
  388. package/src/daemon/server.ts +12 -0
  389. package/src/daemon/tool-side-effects.ts +10 -7
  390. package/src/daemon/trust-context.ts +13 -0
  391. package/src/daemon/wake-target-adapter.ts +11 -1
  392. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
  393. package/src/heartbeat/heartbeat-run-store.ts +31 -0
  394. package/src/heartbeat/heartbeat-service.ts +16 -0
  395. package/src/home/feature-gate.ts +22 -0
  396. package/src/home/feed-types.ts +36 -221
  397. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  398. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  399. package/src/ipc/routes/route-adapter.ts +45 -6
  400. package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
  401. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  402. package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
  403. package/src/ipc/skill-routes/memory.ts +26 -13
  404. package/src/ipc/skill-routes/providers.ts +5 -6
  405. package/src/ipc/skill-routes/registries.ts +13 -61
  406. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  407. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  408. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  409. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  410. package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
  411. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  412. package/src/memory/context-search/agent-runner.ts +2 -4
  413. package/src/memory/conversation-crud.ts +39 -8
  414. package/src/memory/conversation-queries.ts +78 -22
  415. package/src/memory/db-init.ts +8 -0
  416. package/src/memory/db-maintenance.ts +18 -2
  417. package/src/memory/graph/consolidation.ts +8 -11
  418. package/src/memory/graph/conversation-graph-memory.ts +41 -8
  419. package/src/memory/graph/extraction.ts +6 -9
  420. package/src/memory/graph/narrative.ts +2 -2
  421. package/src/memory/graph/pattern-scan.ts +2 -2
  422. package/src/memory/graph/retriever.ts +20 -26
  423. package/src/memory/graph/tools.ts +4 -4
  424. package/src/memory/job-handlers/conversation-starters.ts +32 -32
  425. package/src/memory/job-handlers/summarization.ts +1 -2
  426. package/src/memory/jobs-store.ts +3 -1
  427. package/src/memory/jobs-worker.ts +51 -39
  428. package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
  429. package/src/memory/llm-request-log-source-local.ts +0 -11
  430. package/src/memory/llm-request-log-source.ts +9 -25
  431. package/src/memory/llm-request-log-store.ts +0 -41
  432. package/src/memory/llm-usage-store.ts +10 -0
  433. package/src/memory/memory-marker.ts +17 -0
  434. package/src/memory/memory-retrospective-job.ts +6 -2
  435. package/src/memory/memory-v2-activation-log-store.ts +1 -83
  436. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  437. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  438. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  439. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  440. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  441. package/src/memory/migrations/index.ts +4 -0
  442. package/src/memory/schema/infrastructure.ts +11 -0
  443. package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
  444. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  445. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  446. package/src/memory/v2/__tests__/router.test.ts +14 -4
  447. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
  448. package/src/memory/v2/backfill-jobs.ts +6 -0
  449. package/src/memory/v2/consolidation-job.ts +89 -9
  450. package/src/memory/v2/migration.ts +5 -3
  451. package/src/memory/v2/page-index.ts +11 -0
  452. package/src/memory/v2/router.ts +8 -11
  453. package/src/memory/v2/sweep-job.ts +8 -11
  454. package/src/memory/v2/types.ts +1 -0
  455. package/src/memory/v3/__tests__/assign.test.ts +242 -0
  456. package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
  457. package/src/memory/v3/__tests__/core.test.ts +39 -0
  458. package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
  459. package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
  460. package/src/memory/v3/__tests__/health.test.ts +203 -0
  461. package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
  462. package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
  463. package/src/memory/v3/__tests__/needle.test.ts +107 -0
  464. package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
  465. package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
  466. package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
  467. package/src/memory/v3/__tests__/router.test.ts +260 -0
  468. package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
  469. package/src/memory/v3/__tests__/selector.test.ts +404 -0
  470. package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
  471. package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
  472. package/src/memory/v3/__tests__/tree.test.ts +192 -0
  473. package/src/memory/v3/__tests__/types.test.ts +54 -0
  474. package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
  475. package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
  476. package/src/memory/v3/assign.ts +268 -0
  477. package/src/memory/v3/capabilities.ts +124 -0
  478. package/src/memory/v3/core.ts +26 -0
  479. package/src/memory/v3/data/README.md +84 -0
  480. package/src/memory/v3/data/assignments.json +5 -0
  481. package/src/memory/v3/data/core.json +1 -0
  482. package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
  483. package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
  484. package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
  485. package/src/memory/v3/health.ts +0 -0
  486. package/src/memory/v3/maintain-job.ts +314 -0
  487. package/src/memory/v3/needle.ts +115 -0
  488. package/src/memory/v3/orchestrate.ts +114 -0
  489. package/src/memory/v3/page-content.ts +34 -0
  490. package/src/memory/v3/provider-blocks.ts +16 -0
  491. package/src/memory/v3/reconcile.ts +523 -0
  492. package/src/memory/v3/render-injection.ts +32 -0
  493. package/src/memory/v3/router.ts +184 -0
  494. package/src/memory/v3/selection-log-store.ts +84 -0
  495. package/src/memory/v3/selector.ts +211 -0
  496. package/src/memory/v3/shadow-plugin.ts +379 -0
  497. package/src/memory/v3/snapshot.ts +209 -0
  498. package/src/memory/v3/tree.ts +174 -0
  499. package/src/memory/v3/types.ts +46 -60
  500. package/src/memory/v3/working-set.ts +88 -0
  501. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  502. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  503. package/src/messaging/style-analyzer.ts +8 -11
  504. package/src/notifications/conversation-pairing.ts +8 -6
  505. package/src/notifications/decision-engine.ts +10 -13
  506. package/src/notifications/preference-extractor.ts +11 -14
  507. package/src/permissions/prompter.ts +42 -36
  508. package/src/permissions/question-prompter.test.ts +35 -26
  509. package/src/permissions/question-prompter.ts +6 -10
  510. package/src/plugin-api/index.ts +2 -0
  511. package/src/plugin-api/types.ts +25 -3
  512. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
  513. package/src/plugins/defaults/circuit-breaker/package.json +15 -0
  514. package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
  515. package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
  516. package/src/plugins/defaults/compaction/package.json +15 -0
  517. package/src/plugins/defaults/compaction/register.ts +35 -0
  518. package/src/plugins/defaults/compaction/terminal.ts +73 -0
  519. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
  520. package/src/plugins/defaults/empty-response/package.json +15 -0
  521. package/src/plugins/defaults/empty-response/register.ts +28 -0
  522. package/src/plugins/defaults/empty-response/terminal.ts +106 -0
  523. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  524. package/src/plugins/defaults/history-repair/package.json +15 -0
  525. package/src/plugins/defaults/history-repair/register.ts +24 -0
  526. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  527. package/src/plugins/defaults/index.ts +29 -40
  528. package/src/plugins/defaults/injectors/package.json +15 -0
  529. package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
  530. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
  531. package/src/plugins/defaults/llm-call/package.json +15 -0
  532. package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
  533. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
  534. package/src/plugins/defaults/memory-retrieval/package.json +15 -0
  535. package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
  536. package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
  537. package/src/plugins/defaults/overflow-reduce/package.json +15 -0
  538. package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
  539. package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
  540. package/src/plugins/defaults/persistence/package.json +15 -0
  541. package/src/plugins/defaults/persistence/register.ts +38 -0
  542. package/src/plugins/defaults/persistence/terminal.ts +83 -0
  543. package/src/plugins/defaults/title-generate/package.json +15 -0
  544. package/src/plugins/defaults/title-generate/register.ts +35 -0
  545. package/src/plugins/defaults/title-generate/terminal.ts +31 -0
  546. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
  547. package/src/plugins/defaults/token-estimate/package.json +15 -0
  548. package/src/plugins/defaults/token-estimate/register.ts +34 -0
  549. package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
  550. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
  551. package/src/plugins/defaults/tool-error/package.json +15 -0
  552. package/src/plugins/defaults/tool-error/register.ts +35 -0
  553. package/src/plugins/defaults/tool-error/terminal.ts +47 -0
  554. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
  555. package/src/plugins/defaults/tool-execute/package.json +15 -0
  556. package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
  557. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
  558. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  559. package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
  560. package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
  561. package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
  562. package/src/plugins/external-plugin-loader.ts +2 -2
  563. package/src/plugins/pipeline.ts +0 -12
  564. package/src/plugins/types.ts +51 -90
  565. package/src/plugins/user-loader.ts +4 -3
  566. package/src/proactive-artifact/aux-message-injector.ts +0 -1
  567. package/src/proactive-artifact/job.test.ts +20 -8
  568. package/src/proactive-artifact/job.ts +3 -1
  569. package/src/prompts/sections.ts +20 -7
  570. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  571. package/src/prompts/templates/BOOTSTRAP.md +5 -1
  572. package/src/prompts/templates/system-sections.ts +6 -0
  573. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  574. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  575. package/src/providers/anthropic/client.ts +24 -5
  576. package/src/providers/call-site-routing.ts +1 -9
  577. package/src/providers/gemini/client.ts +152 -34
  578. package/src/providers/gemini/inline-media.ts +74 -0
  579. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
  580. package/src/providers/openai/chat-completions-provider.ts +1 -4
  581. package/src/providers/openai/responses-provider.ts +1 -4
  582. package/src/providers/openrouter/client.ts +1 -6
  583. package/src/providers/provider-send-message.ts +6 -6
  584. package/src/providers/ratelimit.ts +1 -9
  585. package/src/providers/retry.ts +0 -5
  586. package/src/providers/types.ts +11 -2
  587. package/src/providers/usage-tracking.ts +1 -9
  588. package/src/runtime/__tests__/agent-wake.test.ts +131 -26
  589. package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
  590. package/src/runtime/agent-wake.ts +93 -18
  591. package/src/runtime/assistant-event-hub.ts +2 -2
  592. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  593. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  594. package/src/runtime/auth/route-policy.ts +42 -1079
  595. package/src/runtime/background-job-runner.ts +1 -4
  596. package/src/runtime/btw-sidechain.ts +3 -1
  597. package/src/runtime/channel-approvals.ts +3 -14
  598. package/src/runtime/channel-invite-transport.ts +5 -6
  599. package/src/runtime/channel-readiness-service.ts +2 -5
  600. package/src/runtime/channel-retry-sweep.ts +12 -16
  601. package/src/runtime/conversation-stream-state.ts +294 -0
  602. package/src/runtime/http-router.ts +19 -22
  603. package/src/runtime/http-types.ts +12 -6
  604. package/src/runtime/invite-instruction-generator.ts +3 -3
  605. package/src/runtime/pending-interactions.ts +2 -2
  606. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  607. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  608. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
  609. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  610. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
  611. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
  612. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  613. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  614. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  615. package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
  616. package/src/runtime/routes/acp-routes.test.ts +97 -75
  617. package/src/runtime/routes/acp-routes.ts +29 -6
  618. package/src/runtime/routes/app-management-routes.ts +97 -24
  619. package/src/runtime/routes/app-routes.ts +25 -5
  620. package/src/runtime/routes/approval-routes.ts +16 -4
  621. package/src/runtime/routes/attachment-routes.ts +25 -1
  622. package/src/runtime/routes/audio-routes.ts +1 -0
  623. package/src/runtime/routes/audit-routes.ts +5 -0
  624. package/src/runtime/routes/auth-routes.ts +5 -0
  625. package/src/runtime/routes/avatar-routes.ts +238 -59
  626. package/src/runtime/routes/background-tool-routes.ts +9 -0
  627. package/src/runtime/routes/background-wake-routes.ts +13 -3
  628. package/src/runtime/routes/backup-routes.ts +45 -0
  629. package/src/runtime/routes/bookmark-routes.ts +13 -0
  630. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  631. package/src/runtime/routes/browser-routes.ts +5 -0
  632. package/src/runtime/routes/browser-tabs-routes.ts +5 -0
  633. package/src/runtime/routes/btw-routes.ts +5 -1
  634. package/src/runtime/routes/cache-routes.ts +13 -0
  635. package/src/runtime/routes/call-routes.ts +21 -10
  636. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  637. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  638. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  639. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  640. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  641. package/src/runtime/routes/client-routes.ts +9 -0
  642. package/src/runtime/routes/consolidation-routes.ts +13 -5
  643. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  644. package/src/runtime/routes/contact-routes.ts +90 -23
  645. package/src/runtime/routes/content-source-routes.ts +5 -1
  646. package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
  647. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  648. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  649. package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
  650. package/src/runtime/routes/conversation-list-routes.ts +81 -12
  651. package/src/runtime/routes/conversation-management-routes.ts +57 -14
  652. package/src/runtime/routes/conversation-query-routes.ts +88 -41
  653. package/src/runtime/routes/conversation-routes.ts +74 -19
  654. package/src/runtime/routes/conversation-starter-routes.ts +22 -13
  655. package/src/runtime/routes/conversations-import-routes.ts +6 -1
  656. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  657. package/src/runtime/routes/credential-routes.ts +25 -6
  658. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  659. package/src/runtime/routes/debug-routes.ts +11 -2
  660. package/src/runtime/routes/defer-routes.ts +13 -0
  661. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  662. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  663. package/src/runtime/routes/document-comments-routes.ts +46 -27
  664. package/src/runtime/routes/documents-routes.ts +21 -10
  665. package/src/runtime/routes/domain-routes.ts +61 -28
  666. package/src/runtime/routes/email-routes.ts +33 -0
  667. package/src/runtime/routes/events-routes.ts +114 -9
  668. package/src/runtime/routes/filing-routes.ts +9 -4
  669. package/src/runtime/routes/gateway-log-routes.ts +5 -0
  670. package/src/runtime/routes/global-search-routes.ts +53 -50
  671. package/src/runtime/routes/group-routes.ts +21 -5
  672. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  673. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  674. package/src/runtime/routes/heartbeat-routes.ts +25 -9
  675. package/src/runtime/routes/home-feed-routes.ts +23 -19
  676. package/src/runtime/routes/home-state-routes.ts +8 -40
  677. package/src/runtime/routes/host-app-control-routes.ts +5 -0
  678. package/src/runtime/routes/host-bash-routes.ts +5 -0
  679. package/src/runtime/routes/host-browser-routes.ts +13 -0
  680. package/src/runtime/routes/host-cu-routes.ts +5 -0
  681. package/src/runtime/routes/host-file-routes.ts +26 -6
  682. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  683. package/src/runtime/routes/http-adapter.ts +1 -2
  684. package/src/runtime/routes/identity-intro-cache.ts +17 -6
  685. package/src/runtime/routes/identity-routes.ts +12 -2
  686. package/src/runtime/routes/image-generation-routes.ts +5 -0
  687. package/src/runtime/routes/inbound-message-handler.ts +15 -11
  688. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
  689. package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
  690. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  691. package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
  692. package/src/runtime/routes/inference-send-routes.ts +11 -11
  693. package/src/runtime/routes/integrations/a2a.ts +30 -7
  694. package/src/runtime/routes/integrations/slack/channel.ts +19 -3
  695. package/src/runtime/routes/integrations/slack/share.ts +9 -2
  696. package/src/runtime/routes/integrations/telegram.ts +28 -9
  697. package/src/runtime/routes/integrations/twilio.ts +35 -7
  698. package/src/runtime/routes/integrations/vercel.ts +3 -3
  699. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  700. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  701. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  702. package/src/runtime/routes/log-export-routes.ts +28 -10
  703. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  704. package/src/runtime/routes/memory-item-routes.ts +21 -10
  705. package/src/runtime/routes/memory-v2-routes.ts +90 -36
  706. package/src/runtime/routes/memory-v3-routes.ts +273 -407
  707. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  708. package/src/runtime/routes/migration-routes.ts +29 -0
  709. package/src/runtime/routes/notification-routes.ts +17 -1
  710. package/src/runtime/routes/oauth-apps.ts +33 -11
  711. package/src/runtime/routes/oauth-commands-routes.ts +37 -14
  712. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  713. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  714. package/src/runtime/routes/oauth-providers.ts +35 -10
  715. package/src/runtime/routes/platform-routes.ts +21 -0
  716. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
  717. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  718. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  719. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  720. package/src/runtime/routes/playground/force-compact.ts +1 -1
  721. package/src/runtime/routes/playground/helpers.ts +0 -1
  722. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  723. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  724. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  725. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  726. package/src/runtime/routes/playground/state.ts +4 -3
  727. package/src/runtime/routes/plugins-routes.ts +22 -19
  728. package/src/runtime/routes/profiler-routes.ts +17 -4
  729. package/src/runtime/routes/ps-routes.ts +5 -0
  730. package/src/runtime/routes/publish-routes.ts +13 -3
  731. package/src/runtime/routes/question-routes.ts +5 -0
  732. package/src/runtime/routes/recording-routes.ts +25 -12
  733. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  734. package/src/runtime/routes/sanity-routes.ts +9 -2
  735. package/src/runtime/routes/schedule-routes.ts +137 -47
  736. package/src/runtime/routes/secret-routes.ts +17 -4
  737. package/src/runtime/routes/sequence-routes.ts +33 -0
  738. package/src/runtime/routes/settings-routes.ts +65 -19
  739. package/src/runtime/routes/skills-routes.ts +133 -69
  740. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  741. package/src/runtime/routes/stt-routes.ts +13 -6
  742. package/src/runtime/routes/subagents-routes.ts +24 -18
  743. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  744. package/src/runtime/routes/surface-action-routes.ts +9 -0
  745. package/src/runtime/routes/surface-content-routes.ts +10 -2
  746. package/src/runtime/routes/task-routes.ts +37 -0
  747. package/src/runtime/routes/telemetry-routes.ts +9 -0
  748. package/src/runtime/routes/trace-event-routes.ts +42 -1
  749. package/src/runtime/routes/trust-rules-routes.ts +5 -0
  750. package/src/runtime/routes/tts-routes.ts +13 -6
  751. package/src/runtime/routes/types.ts +17 -8
  752. package/src/runtime/routes/ui-request-routes.ts +5 -0
  753. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  754. package/src/runtime/routes/usage-routes.ts +71 -3
  755. package/src/runtime/routes/user-routes-cli.ts +9 -0
  756. package/src/runtime/routes/user-routes.ts +5 -1
  757. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  758. package/src/runtime/routes/watcher-routes.ts +21 -0
  759. package/src/runtime/routes/webhook-routes.ts +9 -0
  760. package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
  761. package/src/runtime/routes/work-items-routes.ts +47 -19
  762. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  763. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  764. package/src/runtime/routes/workspace-routes.ts +120 -9
  765. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
  766. package/src/runtime/services/analyze-conversation.ts +3 -6
  767. package/src/runtime/services/conversation-serializer.ts +24 -2
  768. package/src/runtime/sync/resource-sync-events.ts +16 -2
  769. package/src/runtime/sync/sync-publisher.ts +2 -2
  770. package/src/schedule/run-script.ts +28 -3
  771. package/src/schedule/schedule-store.ts +8 -0
  772. package/src/schedule/scheduler.ts +3 -1
  773. package/src/signals/user-message.ts +5 -8
  774. package/src/skills/catalog-files.ts +4 -1
  775. package/src/skills/clawhub-files.ts +2 -0
  776. package/src/skills/skillssh-files.ts +2 -0
  777. package/src/subagent/manager.ts +3 -6
  778. package/src/telemetry/types.ts +26 -0
  779. package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
  780. package/src/telemetry/usage-telemetry-reporter.ts +31 -0
  781. package/src/tools/acp/spawn.test.ts +88 -38
  782. package/src/tools/apps/definitions.ts +8 -4
  783. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  784. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  785. package/src/tools/computer-use/definitions.ts +28 -24
  786. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  787. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  788. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  789. package/src/tools/credentials/vault.ts +112 -111
  790. package/src/tools/execution-target.ts +1 -1
  791. package/src/tools/execution-timeout.ts +3 -4
  792. package/src/tools/filesystem/edit.ts +45 -42
  793. package/src/tools/filesystem/list.ts +33 -30
  794. package/src/tools/filesystem/read.ts +54 -35
  795. package/src/tools/filesystem/write.ts +34 -31
  796. package/src/tools/host-filesystem/edit.ts +44 -42
  797. package/src/tools/host-filesystem/read.ts +49 -35
  798. package/src/tools/host-filesystem/transfer.ts +121 -108
  799. package/src/tools/host-filesystem/write.ts +33 -31
  800. package/src/tools/host-terminal/host-shell.ts +50 -48
  801. package/src/tools/memory/register.ts +23 -24
  802. package/src/tools/network/web-fetch.ts +49 -46
  803. package/src/tools/network/web-search.ts +16 -13
  804. package/src/tools/registry.ts +39 -16
  805. package/src/tools/schedule/create.ts +11 -0
  806. package/src/tools/schedule/update.ts +16 -0
  807. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  808. package/src/tools/shared/filesystem/image-read.ts +1 -1
  809. package/src/tools/skills/execute.ts +34 -31
  810. package/src/tools/skills/load.ts +29 -23
  811. package/src/tools/subagent/notify-parent.ts +35 -32
  812. package/src/tools/system/avatar-generator.ts +13 -22
  813. package/src/tools/system/request-permission.ts +30 -27
  814. package/src/tools/terminal/shell.ts +190 -61
  815. package/src/tools/tool-defaults.ts +20 -9
  816. package/src/tools/tool-manifest.ts +4 -4
  817. package/src/tools/types.ts +74 -23
  818. package/src/tools/ui-surface/definitions.ts +69 -9
  819. package/src/usage/types.ts +10 -0
  820. package/src/util/errors.ts +2 -2
  821. package/src/util/map-limit.ts +27 -0
  822. package/src/util/platform.ts +15 -12
  823. package/src/work-items/work-item-runner.ts +7 -2
  824. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  825. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  826. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  827. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  828. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  829. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  830. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  831. package/src/workspace/migrations/registry.ts +6 -0
  832. package/src/workspace/provider-commit-message-generator.ts +15 -17
  833. package/tsconfig.json +4 -1
  834. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  835. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  836. package/src/cli/commands/memory-v3-render.ts +0 -491
  837. package/src/daemon/message-types/disk-pressure.ts +0 -9
  838. package/src/email/feature-gate.ts +0 -23
  839. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  840. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
  841. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
  842. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  843. package/src/memory/v3/__tests__/edges.test.ts +0 -706
  844. package/src/memory/v3/__tests__/filter.test.ts +0 -560
  845. package/src/memory/v3/__tests__/gate.test.ts +0 -637
  846. package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
  847. package/src/memory/v3/__tests__/loop.test.ts +0 -775
  848. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  849. package/src/memory/v3/__tests__/scouts.test.ts +0 -489
  850. package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
  851. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
  852. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  853. package/src/memory/v3/__tests__/traversal.test.ts +0 -508
  854. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  855. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  856. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
  857. package/src/memory/v3/__tests__/validate.test.ts +0 -277
  858. package/src/memory/v3/auto-edges.ts +0 -223
  859. package/src/memory/v3/coactivation-store.ts +0 -124
  860. package/src/memory/v3/consolidation-job.ts +0 -323
  861. package/src/memory/v3/coretrieval-seed.ts +0 -240
  862. package/src/memory/v3/edge-learning-job.ts +0 -160
  863. package/src/memory/v3/edges.ts +0 -286
  864. package/src/memory/v3/filter.ts +0 -286
  865. package/src/memory/v3/gate.ts +0 -349
  866. package/src/memory/v3/index-composition.ts +0 -126
  867. package/src/memory/v3/llm-capture.ts +0 -46
  868. package/src/memory/v3/loop.ts +0 -430
  869. package/src/memory/v3/maintenance.ts +0 -144
  870. package/src/memory/v3/prompt-context.ts +0 -33
  871. package/src/memory/v3/prompts/consolidation.ts +0 -458
  872. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  873. package/src/memory/v3/retriever.ts +0 -33
  874. package/src/memory/v3/scouts.ts +0 -431
  875. package/src/memory/v3/shadow-diff.ts +0 -287
  876. package/src/memory/v3/shadow-middleware.ts +0 -347
  877. package/src/memory/v3/traversal.ts +0 -211
  878. package/src/memory/v3/tree-index.ts +0 -237
  879. package/src/memory/v3/tree-store.ts +0 -394
  880. package/src/memory/v3/tree-walk.ts +0 -356
  881. package/src/memory/v3/validate.ts +0 -323
  882. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  883. package/src/plugins/defaults/compaction.ts +0 -141
  884. package/src/plugins/defaults/empty-response.ts +0 -124
  885. package/src/plugins/defaults/history-repair.ts +0 -83
  886. package/src/plugins/defaults/persistence.ts +0 -146
  887. package/src/plugins/defaults/title-generate.ts +0 -90
  888. package/src/plugins/defaults/token-estimate.ts +0 -101
  889. package/src/plugins/defaults/tool-error.ts +0 -119
  890. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
  891. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
@@ -25,16 +25,37 @@ const { initializeDb } = await import("../db-init.js");
25
25
  const { deleteMemoryCheckpoint, getMemoryCheckpoint } =
26
26
  await import("../checkpoints.js");
27
27
  const { maybeRunDbMaintenance } = await import("../db-maintenance.js");
28
+ const { getLastUserMessageTimestamp } = await import("../conversation-crud.js");
28
29
  const { getDbPath } = await import("../../util/platform.js");
29
30
 
30
31
  initializeDb();
31
32
 
32
33
  const MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
34
+ const QUIET_PERIOD_MS = 3 * 60 * 60 * 1000;
33
35
 
34
36
  beforeEach(() => {
35
37
  deleteMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY);
38
+ const sqlite = getSqlite();
39
+ sqlite.exec("DELETE FROM messages");
40
+ sqlite.exec("DELETE FROM conversations");
36
41
  });
37
42
 
43
+ /** Insert a message row directly, bypassing indexing/job side effects. */
44
+ function insertMessage(role: "user" | "assistant", createdAt: number): void {
45
+ const sqlite = getSqlite();
46
+ const convId = `conv-${createdAt}-${role}`;
47
+ sqlite
48
+ .prepare(
49
+ "INSERT OR IGNORE INTO conversations (id, created_at, updated_at) VALUES (?, ?, ?)",
50
+ )
51
+ .run(convId, createdAt, createdAt);
52
+ sqlite
53
+ .prepare(
54
+ "INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)",
55
+ )
56
+ .run(`msg-${createdAt}-${role}`, convId, role, "[]", createdAt);
57
+ }
58
+
38
59
  /** Inflate the test DB with bloat that VACUUM can reclaim. */
39
60
  function inflateAndDelete(byteTarget: number): void {
40
61
  const sqlite = getSqlite();
@@ -112,4 +133,111 @@ describe("maybeRunDbMaintenance", () => {
112
133
  const pagesAfter = readPageCount();
113
134
  expect(pagesAfter).toBeLessThan(pagesBefore);
114
135
  }, 60_000);
136
+
137
+ test("defers maintenance while the last user message is within the quiet period", async () => {
138
+ /** VACUUM must not fire while the user is active, so a recent user
139
+ * message keeps maintenance deferred. */
140
+ // GIVEN the user sent a message one minute ago (well within the quiet period)
141
+ const now = Date.now();
142
+ insertMessage("user", now - 60_000);
143
+
144
+ // WHEN maintenance is considered
145
+ await maybeRunDbMaintenance(now);
146
+
147
+ // THEN it is deferred — the checkpoint is never stamped, so a later idle
148
+ // tick will retry
149
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBeNull();
150
+ });
151
+
152
+ test("runs maintenance once the quiet period has elapsed since the last user message", async () => {
153
+ /** After the user has been quiet for longer than the quiet period,
154
+ * maintenance is allowed to run. */
155
+ // GIVEN the last user message is older than the quiet period
156
+ const now = Date.now();
157
+ insertMessage("user", now - (QUIET_PERIOD_MS + 60_000));
158
+
159
+ // WHEN maintenance is considered
160
+ await maybeRunDbMaintenance(now);
161
+
162
+ // THEN it runs and stamps the checkpoint
163
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
164
+ });
165
+
166
+ test("ignores quiet period when no user message exists", async () => {
167
+ /** A fresh install with no user messages must not be blocked from ever
168
+ * running maintenance. */
169
+ // GIVEN no user messages exist
170
+ const now = Date.now();
171
+
172
+ // WHEN maintenance is considered
173
+ await maybeRunDbMaintenance(now);
174
+
175
+ // THEN it runs and stamps the checkpoint
176
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
177
+ });
178
+
179
+ test("a recent assistant message does not keep maintenance deferred", async () => {
180
+ /** The gate keys off user activity only — background assistant writes
181
+ * must not postpone maintenance indefinitely. */
182
+ // GIVEN the user has been quiet past the quiet period
183
+ const now = Date.now();
184
+ insertMessage("user", now - (QUIET_PERIOD_MS + 60_000));
185
+ // AND the assistant wrote a message recently
186
+ insertMessage("assistant", now - 60_000);
187
+
188
+ // WHEN maintenance is considered
189
+ await maybeRunDbMaintenance(now);
190
+
191
+ // THEN it still runs, since only user activity gates it
192
+ expect(getMemoryCheckpoint(MAINTENANCE_CHECKPOINT_KEY)).toBe(String(now));
193
+ });
194
+ });
195
+
196
+ describe("getLastUserMessageTimestamp", () => {
197
+ test("returns 0 when no user message exists", () => {
198
+ /** With only non-user rows present, there is no user activity to report. */
199
+ // GIVEN only an assistant message exists
200
+ insertMessage("assistant", Date.now());
201
+
202
+ // WHEN the last user message timestamp is read
203
+ const result = getLastUserMessageTimestamp();
204
+
205
+ // THEN it reports 0 (no user activity)
206
+ expect(result).toBe(0);
207
+ });
208
+
209
+ test("returns the most recent user message by timestamp, ignoring assistant rows", () => {
210
+ /** The lookup reports the newest user-message timestamp and must skip
211
+ * non-user rows even when they are more recent. */
212
+ // GIVEN two user messages and a newer assistant message
213
+ const base = Date.now();
214
+ insertMessage("user", base - 10_000);
215
+ insertMessage("user", base - 5_000);
216
+ insertMessage("assistant", base);
217
+
218
+ // WHEN the last user message timestamp is read
219
+ const result = getLastUserMessageTimestamp();
220
+
221
+ // THEN it returns the most recent user message, not the assistant row
222
+ expect(result).toBe(base - 5_000);
223
+ });
224
+
225
+ test("reports the newest user timestamp even when an older turn was inserted later", () => {
226
+ /** `forkConversation` copies a parent's user turns into the fork with
227
+ * their original (older) `created_at` but fresh row ids, so insertion
228
+ * order can place an old turn last. The lookup must key off `created_at`
229
+ * so a fork can't make recent activity look stale and prematurely
230
+ * un-gate maintenance. */
231
+ // GIVEN a recent user message
232
+ const base = Date.now();
233
+ insertMessage("user", base - 5_000);
234
+ // AND a later insert of an older user turn (as a fork copy would produce)
235
+ insertMessage("user", base - 60 * 60 * 1000);
236
+
237
+ // WHEN the last user message timestamp is read
238
+ const result = getLastUserMessageTimestamp();
239
+
240
+ // THEN it reports the genuinely most recent turn, not the last-inserted one
241
+ expect(result).toBe(base - 5_000);
242
+ });
115
243
  });
@@ -40,11 +40,12 @@ describe("memory job classes", () => {
40
40
  expect(set.size).toBe(SLOW_LLM_JOB_TYPES.length);
41
41
  });
42
42
 
43
- test("memory_v3_consolidate is a slow LLM job (lane isolation)", () => {
44
- // It hands off to a background agent for up to 15 min, like
43
+ test("memory_v3_maintain is a slow LLM job (lane isolation)", () => {
44
+ // It runs an LLM classify-union pass over unassigned pages, like
45
45
  // memory_v2_consolidate; it must not sit in the fast lane and starve
46
- // short jobs. The mechanical v3 jobs intentionally stay fast.
47
- expect(SLOW_LLM_JOB_TYPES).toContain("memory_v3_consolidate");
46
+ // short jobs. The retired v3 job literals intentionally stay out.
47
+ expect(SLOW_LLM_JOB_TYPES).toContain("memory_v3_maintain");
48
+ expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_consolidate");
48
49
  expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_index_maintenance");
49
50
  expect(SLOW_LLM_JOB_TYPES).not.toContain("memory_v3_edge_learning");
50
51
  });
@@ -57,7 +57,7 @@ let addMessageCalls: Array<{
57
57
  conversationId: string;
58
58
  role: string;
59
59
  content: string;
60
- metadata: unknown;
60
+ options: unknown;
61
61
  }> = [];
62
62
 
63
63
  // Per-conversation overrides for getConversation. Lets fork-path tests stage
@@ -137,9 +137,9 @@ mock.module("../conversation-crud.js", () => ({
137
137
  conversationId: string,
138
138
  role: string,
139
139
  content: string,
140
- metadata: unknown,
140
+ options: unknown,
141
141
  ) => {
142
- addMessageCalls.push({ conversationId, role, content, metadata });
142
+ addMessageCalls.push({ conversationId, role, content, options });
143
143
  },
144
144
  deleteConversation: (id: string) => {
145
145
  deletedConversationIds.push(id);
@@ -316,10 +316,11 @@ describe("memoryRetrospectiveJob", () => {
316
316
  expect(bootstrapCalls[0]!.forkParentConversationId).toBe("src-conv-1");
317
317
  });
318
318
 
319
- test("legacy path: wake opts include suppressWakeSurface so the full retrospective prompt isn't rendered as a 'Conversation Woke' card body to clients", async () => {
319
+ test("legacy path: wake is scoped to memory saves and suppresses the internal wake surface", async () => {
320
320
  await memoryRetrospectiveJob(makeJob(), stubConfig);
321
321
 
322
322
  expect(wakeCalls).toHaveLength(1);
323
+ expect(wakeCalls[0]!.opts.allowedTools).toEqual(["remember"]);
323
324
  expect(wakeCalls[0]!.opts.suppressWakeSurface).toBe(true);
324
325
  });
325
326
 
@@ -519,7 +520,9 @@ describe("memoryRetrospectiveJob", () => {
519
520
  expect(addMessageCalls).toHaveLength(1);
520
521
  expect(addMessageCalls[0]!.conversationId).toBe("fork-conv-1");
521
522
  expect(addMessageCalls[0]!.role).toBe("user");
522
- expect(addMessageCalls[0]!.metadata).toEqual({
523
+ expect(
524
+ (addMessageCalls[0]!.options as Record<string, unknown>).metadata,
525
+ ).toEqual({
523
526
  kind: "memory_retrospective_instruction",
524
527
  hidden: true,
525
528
  });
@@ -535,7 +538,7 @@ describe("memoryRetrospectiveJob", () => {
535
538
  expect(forkCalls[0]!.groupId).toBe("system:background");
536
539
  });
537
540
 
538
- test("fork path: wake opts include suppressWakeSurface so clients don't render an empty wake card on top of the '(Retrospective)' fork", async () => {
541
+ test("fork path: wake is scoped to memory saves and suppresses the internal wake surface", async () => {
539
542
  forkFlagEnabled = true;
540
543
  await memoryRetrospectiveJob(makeJob(), stubConfig);
541
544
 
@@ -543,6 +546,7 @@ describe("memoryRetrospectiveJob", () => {
543
546
  expect(wakeCalls).toHaveLength(1);
544
547
  expect(wakeCalls[0]!.conversationId).toBe("fork-conv-1");
545
548
  const opts = wakeCalls[0]!.opts;
549
+ expect(opts.allowedTools).toEqual(["remember"]);
546
550
  expect(opts.suppressWakeSurface).toBe(true);
547
551
  // Sanity: the other fork-specific opts the handler relies on are still set.
548
552
  expect(opts.skipHintInjection).toBe(true);
@@ -0,0 +1,103 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import { getSqliteFrom } from "../db-connection.js";
7
+ import { migrateAddMemoryV3Selections } from "../migrations/268-add-memory-v3-selections.js";
8
+ import * as schema from "../schema.js";
9
+
10
+ interface ColumnRow {
11
+ name: string;
12
+ type: string;
13
+ notnull: number;
14
+ dflt_value: string | null;
15
+ pk: number;
16
+ }
17
+
18
+ interface IndexRow {
19
+ name: string;
20
+ }
21
+
22
+ function createTestDb() {
23
+ const sqlite = new Database(":memory:");
24
+ sqlite.exec("PRAGMA journal_mode=WAL");
25
+ sqlite.exec("PRAGMA foreign_keys = ON");
26
+ return drizzle(sqlite, { schema });
27
+ }
28
+
29
+ describe("memory_v3_selections migration", () => {
30
+ test("creates table with expected columns", () => {
31
+ const db = createTestDb();
32
+ const raw = getSqliteFrom(db);
33
+
34
+ migrateAddMemoryV3Selections(db);
35
+
36
+ const columns = raw
37
+ .query(`PRAGMA table_info(memory_v3_selections)`)
38
+ .all() as ColumnRow[];
39
+ const byName = new Map(columns.map((c) => [c.name, c]));
40
+
41
+ expect(byName.get("conversation_id")?.type).toBe("TEXT");
42
+ expect(byName.get("conversation_id")?.notnull).toBe(1);
43
+ expect(byName.get("conversation_id")?.pk).toBe(1);
44
+
45
+ expect(byName.get("turn")?.type).toBe("INTEGER");
46
+ expect(byName.get("turn")?.notnull).toBe(1);
47
+ expect(byName.get("turn")?.pk).toBe(2);
48
+
49
+ expect(byName.get("slug")?.type).toBe("TEXT");
50
+ expect(byName.get("slug")?.notnull).toBe(1);
51
+ expect(byName.get("slug")?.pk).toBe(3);
52
+
53
+ expect(byName.get("source")?.type).toBe("TEXT");
54
+ expect(byName.get("source")?.notnull).toBe(1);
55
+
56
+ expect(byName.get("pinned")?.type).toBe("INTEGER");
57
+ expect(byName.get("pinned")?.notnull).toBe(1);
58
+ expect(byName.get("pinned")?.dflt_value).toBe("0");
59
+
60
+ expect(byName.get("created_at")?.type).toBe("INTEGER");
61
+ expect(byName.get("created_at")?.notnull).toBe(1);
62
+ });
63
+
64
+ test("creates the conversation-turn index", () => {
65
+ const db = createTestDb();
66
+ const raw = getSqliteFrom(db);
67
+
68
+ migrateAddMemoryV3Selections(db);
69
+
70
+ const indexes = raw
71
+ .query(`PRAGMA index_list(memory_v3_selections)`)
72
+ .all() as IndexRow[];
73
+ const indexNames = new Set(indexes.map((i) => i.name));
74
+
75
+ expect(indexNames.has("idx_memory_v3_selections_conv")).toBe(true);
76
+ });
77
+
78
+ test("is idempotent — re-running does not throw and preserves rows", () => {
79
+ const db = createTestDb();
80
+ const raw = getSqliteFrom(db);
81
+
82
+ migrateAddMemoryV3Selections(db);
83
+
84
+ raw
85
+ .query(
86
+ /*sql*/ `
87
+ INSERT INTO memory_v3_selections (
88
+ conversation_id, turn, slug, source, pinned, created_at
89
+ ) VALUES (?, ?, ?, ?, ?, ?)
90
+ `,
91
+ )
92
+ .run("conv-abc", 3, "page-slug", "lane-a", 1, 1000);
93
+
94
+ expect(() => migrateAddMemoryV3Selections(db)).not.toThrow();
95
+
96
+ const row = raw
97
+ .query(
98
+ `SELECT slug FROM memory_v3_selections WHERE conversation_id = 'conv-abc' AND turn = 3`,
99
+ )
100
+ .get() as { slug: string } | null;
101
+ expect(row?.slug).toBe("page-slug");
102
+ });
103
+ });
@@ -290,9 +290,8 @@ export async function runAgenticRecall(
290
290
  try {
291
291
  response = await provider.sendMessage(
292
292
  [userTextMessage(promptBundle.prompt)],
293
- [...RECALL_AGENT_TOOL_DEFINITIONS],
294
- undefined,
295
293
  {
294
+ tools: [...RECALL_AGENT_TOOL_DEFINITIONS],
296
295
  // `thinking: disabled` is required because we set `temperature: 0`
297
296
  // explicitly. Anthropic 400s on `temperature` ≠ 1 whenever thinking
298
297
  // is enabled or in adaptive mode; without this, profiles that
@@ -591,9 +590,8 @@ async function tryFinalFinishRecall(options: {
591
590
  try {
592
591
  response = await options.provider.sendMessage(
593
592
  [userTextMessage(promptBundle.prompt)],
594
- [FINISH_RECALL_TOOL_DEFINITION],
595
- undefined,
596
593
  {
594
+ tools: [FINISH_RECALL_TOOL_DEFINITION],
597
595
  // `thinking: disabled` required for the same reason as the agent
598
596
  // round above — Anthropic 400s on `temperature` ≠ 1 whenever
599
597
  // thinking is enabled or in adaptive mode.
@@ -1195,23 +1195,29 @@ export function wipeConversation(id: string): WipeConversationResult {
1195
1195
  };
1196
1196
  }
1197
1197
 
1198
+ /** Options for {@link addMessage}. Only `skipIndexing` and `clientMessageId`
1199
+ * have defaults; `metadata` is genuinely optional. */
1200
+ export interface AddMessageOptions {
1201
+ metadata?: Record<string, unknown>;
1202
+ skipIndexing?: boolean;
1203
+ /** Client-generated nonce for idempotent inserts. When provided,
1204
+ * duplicate inserts for the same `(conversationId, clientMessageId)`
1205
+ * pair are silently skipped. */
1206
+ clientMessageId?: string;
1207
+ }
1208
+
1198
1209
  /**
1199
1210
  * Persist a message and run post-insert side effects (memory indexing,
1200
1211
  * attention projection). Delegates the core insert + retry logic to
1201
1212
  * {@link insertMessageCore}.
1202
- *
1203
- * @param clientMessageId Optional client-generated nonce. When
1204
- * provided, duplicate inserts for the same `(conversationId,
1205
- * clientMessageId)` pair are silently skipped (idempotent).
1206
1213
  */
1207
1214
  export async function addMessage(
1208
1215
  conversationId: string,
1209
1216
  role: MessageRole,
1210
1217
  content: string,
1211
- metadata?: Record<string, unknown>,
1212
- opts?: { skipIndexing?: boolean },
1213
- clientMessageId?: string,
1218
+ options?: AddMessageOptions,
1214
1219
  ) {
1220
+ const { metadata, skipIndexing, clientMessageId } = options ?? {};
1215
1221
  const inserted = await insertMessageCore({
1216
1222
  conversationId,
1217
1223
  role,
@@ -1226,7 +1232,7 @@ export async function addMessage(
1226
1232
 
1227
1233
  const message = inserted;
1228
1234
 
1229
- if (!opts?.skipIndexing) {
1235
+ if (!skipIndexing) {
1230
1236
  try {
1231
1237
  const config = getConfig();
1232
1238
  const parsed = metadata
@@ -1613,6 +1619,31 @@ export function getLastUserTimestampBefore(
1613
1619
  return row?.createdAt ?? 0;
1614
1620
  }
1615
1621
 
1622
+ /**
1623
+ * Most recent user-message timestamp (epoch ms) across all conversations, or
1624
+ * `0` when no user message exists.
1625
+ *
1626
+ * Ordered by `created_at` rather than insertion order, because `forkConversation`
1627
+ * copies a parent's messages into the fork while preserving their original
1628
+ * `created_at`. Those copies receive fresh row ids, so an insertion-order scan
1629
+ * could surface an old forked turn as if it were the latest activity and let
1630
+ * maintenance run while the user is in fact active. The `(role, created_at)`
1631
+ * index (`idx_messages_role_created_at`) makes this an indexed seek to the
1632
+ * newest `role = "user"` row rather than a scan of the whole (potentially
1633
+ * multi-GB) table.
1634
+ */
1635
+ export function getLastUserMessageTimestamp(): number {
1636
+ const db = getDb();
1637
+ const row = db
1638
+ .select({ createdAt: messages.createdAt })
1639
+ .from(messages)
1640
+ .where(eq(messages.role, "user"))
1641
+ .orderBy(desc(messages.createdAt))
1642
+ .limit(1)
1643
+ .get();
1644
+ return row?.createdAt ?? 0;
1645
+ }
1646
+
1616
1647
  /** Fetch a single message by ID, optionally scoped to a specific conversation. */
1617
1648
  export function getMessageById(
1618
1649
  messageId: string,
@@ -11,6 +11,7 @@ import type { ConversationRow } from "./conversation-crud.js";
11
11
  import { parseConversation } from "./conversation-crud.js";
12
12
  import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
13
13
  import { ensureGroupMigration } from "./conversation-group-migration.js";
14
+ import type { ConversationType } from "./conversation-types.js";
14
15
  import { getDb } from "./db-connection.js";
15
16
  import { rawAll } from "./raw-query.js";
16
17
  import { conversations, messages } from "./schema.js";
@@ -48,30 +49,78 @@ export function buildFtsMatchQuery(
48
49
  return unique.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
49
50
  }
50
51
 
52
+ /**
53
+ * How {@link listConversations} (and friends) treats archived rows.
54
+ *
55
+ * - `"active"` — exclude rows with a non-null `archivedAt`. The default
56
+ * for sidebar lists, restore, CLI pickers, and anything user-facing.
57
+ * - `"archived"` — return ONLY archived rows. Powers the Archive page
58
+ * so it does not have to pull the entire conversation history and
59
+ * filter client-side.
60
+ * - `"all"` — include both. Reserved for migrations and back-compat
61
+ * call sites that genuinely want everything in one query.
62
+ */
63
+ export type ArchiveStatusFilter = "active" | "archived" | "all";
64
+
65
+ function archiveStatusClause(status: ArchiveStatusFilter) {
66
+ switch (status) {
67
+ case "active":
68
+ return sql`${conversations.archivedAt} IS NULL`;
69
+ case "archived":
70
+ return sql`${conversations.archivedAt} IS NOT NULL`;
71
+ case "all":
72
+ return null;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * SQL predicate selecting which bucket {@link listConversations} and
78
+ * {@link countConversations} return, keyed by the canonical
79
+ * {@link ConversationType}:
80
+ *
81
+ * - `"standard"` — the primary sidebar list: standard conversations only,
82
+ * excluding background, scheduled, and private rows. `"private"` is excluded
83
+ * defensively because in-place snapshot restore swaps the SQLite file without
84
+ * running migrations in-process, so legacy private rows can briefly exist
85
+ * before migration cleanup deletes them.
86
+ * - `"background"` — the background **umbrella**: background *and* scheduled
87
+ * rows together. The back-compat bucket for the single
88
+ * `conversationType=background` fetch that older clients (e.g. the macOS app,
89
+ * which ships out of lockstep with the daemon) rely on to populate both the
90
+ * Background and Scheduled sidebar sections from one request.
91
+ * - `"scheduled"` — scheduled rows only, so the Scheduled section can load
92
+ * independently of the broader background backlog without over-fetching it.
93
+ *
94
+ * `group_id` is matched alongside `conversationType` so conversations routed to
95
+ * `system:background` / `system:scheduled` (heartbeat, reminders, schedule-job
96
+ * runs) but created with conversationType `"standard"` still land in the
97
+ * correct bucket. Subagent runs are excluded from the background/scheduled
98
+ * buckets so the sidebar never surfaces them.
99
+ */
100
+ function conversationTypeClause(type: ConversationType) {
101
+ const notSubagent = sql`(${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`;
102
+ switch (type) {
103
+ case "standard":
104
+ return sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private') AND COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`;
105
+ case "background":
106
+ return sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND ${notSubagent}`;
107
+ case "scheduled":
108
+ return sql`(${conversations.conversationType} = 'scheduled' OR group_id = 'system:scheduled') AND ${notSubagent}`;
109
+ }
110
+ }
111
+
51
112
  export function listConversations(
52
113
  limit?: number,
53
- backgroundOnly = false,
114
+ conversationType: ConversationType = "standard",
54
115
  offset = 0,
55
- includeArchived = true,
116
+ archiveStatus: ArchiveStatusFilter = "active",
56
117
  ): ConversationRow[] {
57
118
  ensureDisplayOrderMigration();
58
119
  ensureGroupMigration();
59
120
  const db = getDb();
60
- // 'private' is excluded defensively: in-place snapshot restore swaps the
61
- // SQLite file without running migrations in-process, so legacy private rows
62
- // can briefly exist before migration cleanup. Hide them from foreground
63
- // lists until the next migration pass deletes them.
64
- //
65
- // group_id is checked alongside conversationType so that conversations
66
- // routed to system:background (e.g. heartbeat) via conversationMetadata
67
- // but created with conversationType "standard" (vellum channel strategy)
68
- // appear in the correct bucket.
69
- const typeCond = backgroundOnly
70
- ? sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND (${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`
71
- : sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private') AND COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`;
72
- const where = includeArchived
73
- ? typeCond
74
- : sql`${typeCond} AND ${conversations.archivedAt} IS NULL`;
121
+ const typeCond = conversationTypeClause(conversationType);
122
+ const archiveCond = archiveStatusClause(archiveStatus);
123
+ const where = archiveCond ? sql`${typeCond} AND ${archiveCond}` : typeCond;
75
124
  const query = db
76
125
  .select()
77
126
  .from(conversations)
@@ -172,10 +221,13 @@ export function getMessageRoleStatsByConversation(
172
221
  );
173
222
  }
174
223
 
175
- export function listPinnedConversations(): ConversationRow[] {
224
+ export function listPinnedConversations(
225
+ archiveStatus: ArchiveStatusFilter = "active",
226
+ ): ConversationRow[] {
176
227
  ensureDisplayOrderMigration();
177
228
  ensureGroupMigration();
178
229
  const db = getDb();
230
+ const archiveCond = archiveStatusClause(archiveStatus);
179
231
  const query = db
180
232
  .select()
181
233
  .from(conversations)
@@ -183,6 +235,7 @@ export function listPinnedConversations(): ConversationRow[] {
183
235
  and(
184
236
  sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private')`,
185
237
  sql`is_pinned = 1`,
238
+ ...(archiveCond ? [archiveCond] : []),
186
239
  ),
187
240
  )
188
241
  .orderBy(
@@ -247,12 +300,15 @@ export function listConversationsByTitlePrefix(
247
300
  }));
248
301
  }
249
302
 
250
- export function countConversations(backgroundOnly = false): number {
303
+ export function countConversations(
304
+ conversationType: ConversationType = "standard",
305
+ archiveStatus: ArchiveStatusFilter = "active",
306
+ ): number {
251
307
  ensureGroupMigration();
252
308
  const db = getDb();
253
- const where = backgroundOnly
254
- ? sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND (${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`
255
- : sql`${conversations.conversationType} NOT IN ('background', 'scheduled', 'private') AND COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`;
309
+ const typeCond = conversationTypeClause(conversationType);
310
+ const archiveCond = archiveStatusClause(archiveStatus);
311
+ const where = archiveCond ? sql`${typeCond} AND ${archiveCond}` : typeCond;
256
312
  const [{ total }] = db
257
313
  .select({ total: count() })
258
314
  .from(conversations)
@@ -43,6 +43,7 @@ import {
43
43
  migrateActivationState,
44
44
  migrateActivationStateFkCascade,
45
45
  migrateAddConversationInferenceProfile,
46
+ migrateAddMemoryV3Selections,
46
47
  migrateAddSourceTypeColumns,
47
48
  migrateAssistantContactMetadata,
48
49
  migrateBackfillAudioAttachmentMimeTypes,
@@ -125,6 +126,7 @@ import {
125
126
  migrateLlmRequestLogsCreatedAtIndex,
126
127
  migrateLlmUsageAddRawUsage,
127
128
  migrateLlmUsageAttribution,
129
+ migrateLlmUsageEventsAddAssistantVersion,
128
130
  migrateMemoryGraphImageRefs,
129
131
  migrateMemoryItemSupersession,
130
132
  migrateMemoryRecallLogsQueryContext,
@@ -137,6 +139,7 @@ import {
137
139
  migrateMessagesClientMessageId,
138
140
  migrateMessagesConversationCreatedAtIndex,
139
141
  migrateMessagesFtsBackfill,
142
+ migrateMessagesRoleCreatedAtIndex,
140
143
  migrateNormalizePhoneIdentities,
141
144
  migrateNormalizeSlackExternalContent,
142
145
  migrateNormalizeUserFileByPrincipal,
@@ -184,6 +187,7 @@ import {
184
187
  migrateScheduleRetryPolicy,
185
188
  migrateScheduleReuseConversation,
186
189
  migrateScheduleScriptColumn,
190
+ migrateScheduleScriptTimeout,
187
191
  migrateScheduleWakeConversationId,
188
192
  migrateSchemaIndexesAndColumns,
189
193
  migrateScrubCorruptedImageAttachments,
@@ -466,6 +470,10 @@ export function initializeDb(): void {
466
470
  migrateLlmRequestLogCallSite,
467
471
  migrateDropProviderConnectionStatus,
468
472
  migrateMessagesClientMessageId,
473
+ migrateLlmUsageEventsAddAssistantVersion,
474
+ migrateAddMemoryV3Selections,
475
+ migrateScheduleScriptTimeout,
476
+ migrateMessagesRoleCreatedAtIndex,
469
477
  ];
470
478
 
471
479
  // Run each migration step, catching and logging individual failures so one
@@ -1,15 +1,16 @@
1
1
  import { statSync } from "node:fs";
2
2
 
3
+ import { getConfig } from "../config/loader.js";
3
4
  import { getLogger } from "../util/logger.js";
4
5
  import { getDbPath } from "../util/platform.js";
5
6
  import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
7
+ import { getLastUserMessageTimestamp } from "./conversation-crud.js";
6
8
  import { runAsyncSqlite } from "./db-async-query.js";
7
9
  import { getSqlite } from "./db-connection.js";
8
10
 
9
11
  const log = getLogger("db-maintenance");
10
12
 
11
13
  const DB_MAINTENANCE_CHECKPOINT_KEY = "db_maintenance:last_run";
12
- const DB_MAINTENANCE_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
13
14
 
14
15
  interface DbStats {
15
16
  pageSize: number;
@@ -101,11 +102,26 @@ async function runDbMaintenance(): Promise<void> {
101
102
  }
102
103
 
103
104
  export async function maybeRunDbMaintenance(nowMs = Date.now()): Promise<void> {
105
+ const { intervalMs, quietPeriodMs } = getConfig().memory.maintenance;
106
+
104
107
  const lastRun = parseInt(
105
108
  getMemoryCheckpoint(DB_MAINTENANCE_CHECKPOINT_KEY) ?? "0",
106
109
  10,
107
110
  );
108
- if (nowMs - lastRun < DB_MAINTENANCE_INTERVAL_MS) return;
111
+ if (nowMs - lastRun < intervalMs) return;
112
+
113
+ // VACUUM holds an exclusive lock on the database for its full duration —
114
+ // minutes on a multi-GB DB — during which every other write fails with
115
+ // SQLITE_BUSY ("database is locked"). Defer maintenance until the user has
116
+ // been quiet for `quietPeriodMs` so that lock never lands mid-conversation.
117
+ // The checkpoint below is only written once maintenance actually runs, so a
118
+ // deferred run is simply retried on a later (still-idle) worker tick.
119
+ if (quietPeriodMs > 0) {
120
+ const lastUserMessageAt = getLastUserMessageTimestamp();
121
+ if (lastUserMessageAt > 0 && nowMs - lastUserMessageAt < quietPeriodMs) {
122
+ return;
123
+ }
124
+ }
109
125
 
110
126
  try {
111
127
  await runDbMaintenance();