@vellumai/assistant 0.8.4 → 0.8.6

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 (802) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +3 -3
  3. package/bunfig.toml +6 -1
  4. package/docs/browser-use-architecture-phase2.md +1 -1
  5. package/docs/credential-execution-service.md +6 -6
  6. package/docs/plugins.md +4 -3
  7. package/knip.json +2 -1
  8. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
  9. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
  10. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  11. package/openapi.yaml +2748 -216
  12. package/package.json +1 -1
  13. package/src/__tests__/actor-token-service.test.ts +3 -2
  14. package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
  15. package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
  16. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  17. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  18. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  19. package/src/__tests__/annotate-risk-options.test.ts +1 -0
  20. package/src/__tests__/anthropic-provider.test.ts +34 -37
  21. package/src/__tests__/approval-cascade.test.ts +1 -0
  22. package/src/__tests__/approval-routes-http.test.ts +9 -13
  23. package/src/__tests__/assert-not-live-db.ts +79 -0
  24. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  25. package/src/__tests__/assistant-feature-flags-integration.test.ts +12 -28
  26. package/src/__tests__/audit-log-rotation.test.ts +72 -18
  27. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  28. package/src/__tests__/background-workers-disk-pressure.test.ts +8 -11
  29. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  30. package/src/__tests__/btw-routes.test.ts +5 -5
  31. package/src/__tests__/call-controller.test.ts +3 -3
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  33. package/src/__tests__/channel-approval-routes.test.ts +3 -2
  34. package/src/__tests__/channel-guardian.test.ts +6 -5
  35. package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
  36. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  37. package/src/__tests__/channel-retry-sweep.test.ts +320 -3
  38. package/src/__tests__/checker.test.ts +18 -27
  39. package/src/__tests__/compaction-events.test.ts +2 -0
  40. package/src/__tests__/compaction-trail-store.test.ts +264 -0
  41. package/src/__tests__/compactor-call-site-logging.test.ts +215 -0
  42. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  43. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -16
  44. package/src/__tests__/computer-use-tools.test.ts +14 -18
  45. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  46. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  48. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  49. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  50. package/src/__tests__/config-schema.test.ts +10 -10
  51. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  52. package/src/__tests__/connection-model-compat.test.ts +83 -0
  53. package/src/__tests__/contacts-tools.test.ts +3 -2
  54. package/src/__tests__/context-token-estimator.test.ts +22 -0
  55. package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
  56. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -1
  57. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  58. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -1
  59. package/src/__tests__/conversation-agent-loop-overflow.test.ts +231 -2
  60. package/src/__tests__/conversation-agent-loop.test.ts +581 -54
  61. package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
  62. package/src/__tests__/conversation-app-control-instantiation.test.ts +31 -24
  63. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
  64. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  65. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  66. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  67. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  68. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  69. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  70. package/src/__tests__/conversation-error.test.ts +61 -0
  71. package/src/__tests__/conversation-fork-crud.test.ts +239 -15
  72. package/src/__tests__/conversation-fork-route.test.ts +3 -2
  73. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  74. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  75. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  76. package/src/__tests__/conversation-lifecycle.test.ts +53 -11
  77. package/src/__tests__/conversation-list-source.test.ts +3 -2
  78. package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
  79. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +14 -13
  80. package/src/__tests__/conversation-pairing.test.ts +53 -0
  81. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
  82. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  83. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  84. package/src/__tests__/conversation-queue.test.ts +333 -291
  85. package/src/__tests__/conversation-routes-disk-view.test.ts +112 -18
  86. package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
  87. package/src/__tests__/conversation-routes-slash-commands.test.ts +68 -2
  88. package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
  89. package/src/__tests__/conversation-skill-tools.test.ts +40 -147
  90. package/src/__tests__/conversation-slash-queue.test.ts +84 -32
  91. package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
  92. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  93. package/src/__tests__/conversation-store.test.ts +1 -1
  94. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
  95. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  96. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  97. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  98. package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
  99. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
  100. package/src/__tests__/conversation-sync-tags.test.ts +218 -35
  101. package/src/__tests__/conversation-title-service.test.ts +1 -0
  102. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
  103. package/src/__tests__/conversation-usage.test.ts +1 -0
  104. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  105. package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
  106. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -1
  107. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  108. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  109. package/src/__tests__/credential-execution-client.test.ts +72 -1
  110. package/src/__tests__/credential-execution-feature-gates.test.ts +19 -19
  111. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  112. package/src/__tests__/credential-health-service.test.ts +252 -3
  113. package/src/__tests__/credential-security-invariants.test.ts +6 -5
  114. package/src/__tests__/credential-vault-unit.test.ts +21 -21
  115. package/src/__tests__/credential-vault.test.ts +5 -5
  116. package/src/__tests__/cross-provider-web-search.test.ts +56 -2
  117. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  118. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  119. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  120. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  121. package/src/__tests__/db-test-helpers.ts +58 -0
  122. package/src/__tests__/disk-pressure-guard.test.ts +58 -41
  123. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  124. package/src/__tests__/disk-pressure-routes.test.ts +0 -33
  125. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  126. package/src/__tests__/dm-persistence.test.ts +26 -40
  127. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  128. package/src/__tests__/document-find-replace.test.ts +3 -2
  129. package/src/__tests__/document-tool-security.test.ts +81 -2
  130. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  131. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  132. package/src/__tests__/email-html-renderer.test.ts +12 -0
  133. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  134. package/src/__tests__/encrypted-store.test.ts +11 -9
  135. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  136. package/src/__tests__/filing-service.test.ts +1 -0
  137. package/src/__tests__/first-greeting.test.ts +62 -12
  138. package/src/__tests__/gateway-flag-listener.test.ts +236 -0
  139. package/src/__tests__/gemini-provider.test.ts +104 -0
  140. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  141. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  142. package/src/__tests__/guardian-outbound-http.test.ts +10 -7
  143. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  144. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -1
  145. package/src/__tests__/heartbeat-disk-pressure.test.ts +5 -0
  146. package/src/__tests__/heartbeat-service.test.ts +5 -0
  147. package/src/__tests__/helpers/mock-logger.ts +26 -0
  148. package/src/__tests__/host-bash-routes.test.ts +1 -0
  149. package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
  150. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  151. package/src/__tests__/host-shell-tool.test.ts +6 -5
  152. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  153. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  154. package/src/__tests__/http-user-message-parity.test.ts +29 -7
  155. package/src/__tests__/identity-intro-cache.test.ts +133 -22
  156. package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
  157. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  158. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  159. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  160. package/src/__tests__/injector-disk-pressure.test.ts +3 -17
  161. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  162. package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
  163. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  164. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  165. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  166. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  167. package/src/__tests__/llm-resolver.test.ts +408 -9
  168. package/src/__tests__/llm-schema.test.ts +1 -1
  169. package/src/__tests__/llm-usage-store.test.ts +66 -0
  170. package/src/__tests__/logger.test.ts +89 -0
  171. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  172. package/src/__tests__/mcp-abort-signal.test.ts +16 -2
  173. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  174. package/src/__tests__/media-generate-image.test.ts +31 -0
  175. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  176. package/src/__tests__/messaging-send-tool.test.ts +1 -0
  177. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  178. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  179. package/src/__tests__/model-intents.test.ts +4 -6
  180. package/src/__tests__/native-web-search.test.ts +30 -2
  181. package/src/__tests__/notification-deep-link.test.ts +62 -0
  182. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  183. package/src/__tests__/oauth-commands-routes.test.ts +37 -0
  184. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  185. package/src/__tests__/oauth-store.test.ts +3 -2
  186. package/src/__tests__/onboarding-template-contract.test.ts +4 -3
  187. package/src/__tests__/openai-provider.test.ts +54 -9
  188. package/src/__tests__/openai-responses-provider.test.ts +176 -14
  189. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  190. package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
  191. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  192. package/src/__tests__/persistence-pipeline.test.ts +139 -1
  193. package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
  194. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  195. package/src/__tests__/platform.test.ts +2 -2
  196. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  197. package/src/__tests__/plugin-bootstrap.test.ts +11 -13
  198. package/src/__tests__/plugin-tool-contribution.test.ts +50 -40
  199. package/src/__tests__/plugin-types.test.ts +3 -2
  200. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  201. package/src/__tests__/pricing.test.ts +12 -0
  202. package/src/__tests__/process-message-background-slack.test.ts +21 -16
  203. package/src/__tests__/process-message-display-content.test.ts +19 -22
  204. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  205. package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
  206. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  207. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  208. package/src/__tests__/recording-handler.test.ts +1 -0
  209. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  210. package/src/__tests__/registry.test.ts +84 -84
  211. package/src/__tests__/relay-server.test.ts +10 -10
  212. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  213. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  214. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  215. package/src/__tests__/schedule-store.test.ts +16 -1
  216. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  217. package/src/__tests__/secret-ingress-http.test.ts +5 -1
  218. package/src/__tests__/secure-keys.test.ts +3 -3
  219. package/src/__tests__/send-endpoint-busy.test.ts +81 -42
  220. package/src/__tests__/server-history-render.test.ts +4 -1
  221. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  222. package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
  223. package/src/__tests__/skill-feature-flags.test.ts +16 -18
  224. package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
  225. package/src/__tests__/skill-projection-feature-flag.test.ts +48 -37
  226. package/src/__tests__/skill-projection.benchmark.test.ts +7 -13
  227. package/src/__tests__/skill-tool-factory.test.ts +97 -96
  228. package/src/__tests__/slack-channel-config.test.ts +3 -3
  229. package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
  230. package/src/__tests__/subagent-disposal.test.ts +27 -8
  231. package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
  232. package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
  233. package/src/__tests__/subagent-manager-notify.test.ts +20 -8
  234. package/src/__tests__/subagent-notify-parent.test.ts +6 -5
  235. package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
  236. package/src/__tests__/subagent-tools.test.ts +2 -1
  237. package/src/__tests__/suggestion-routes.test.ts +2 -0
  238. package/src/__tests__/sync-message-contract.test.ts +59 -0
  239. package/src/__tests__/system-prompt.test.ts +183 -131
  240. package/src/__tests__/terminal-tools.test.ts +1 -1
  241. package/src/__tests__/test-preload-verifier.ts +68 -0
  242. package/src/__tests__/test-preload.ts +32 -39
  243. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  244. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  245. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  246. package/src/__tests__/tool-executor-lifecycle-events.test.ts +35 -12
  247. package/src/__tests__/tool-executor.test.ts +64 -72
  248. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  249. package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
  250. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  251. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  252. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  253. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  254. package/src/__tests__/twilio-routes.test.ts +3 -2
  255. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  256. package/src/__tests__/usage-routes.test.ts +3 -0
  257. package/src/__tests__/validate-input.test.ts +381 -0
  258. package/src/__tests__/verification-control-plane-policy.test.ts +3 -2
  259. package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
  260. package/src/__tests__/voice-session-bridge.test.ts +37 -28
  261. package/src/__tests__/workspace-git-service.test.ts +6 -5
  262. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  263. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  264. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  265. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  266. package/src/acp/prepare-agent-env.ts +78 -0
  267. package/src/acp/session-manager.ts +6 -7
  268. package/src/agent/loop.ts +88 -0
  269. package/src/api/README.md +127 -0
  270. package/src/api/constants/call-sites.ts +27 -0
  271. package/src/api/events/assistant-outbound-attachment.ts +51 -0
  272. package/src/api/events/assistant-text-delta.ts +32 -0
  273. package/src/api/events/assistant-turn-start.ts +33 -0
  274. package/src/api/events/document-comment-created.ts +48 -0
  275. package/src/api/events/document-comment-deleted.ts +24 -0
  276. package/src/api/events/document-comment-reopened.ts +25 -0
  277. package/src/api/events/document-comment-resolved.ts +27 -0
  278. package/src/api/events/generation-cancelled.ts +24 -0
  279. package/src/api/events/generation-handoff.ts +41 -0
  280. package/src/api/events/message-complete.ts +42 -0
  281. package/src/api/events/open-url.ts +30 -0
  282. package/src/api/events/relationship-state-updated.ts +25 -0
  283. package/src/api/events/tool-use-start.ts +32 -0
  284. package/src/api/index.ts +129 -0
  285. package/src/api/package.json +10 -0
  286. package/src/api/responses/llm-context-response.ts +39 -0
  287. package/src/api/responses/llm-request-log-entry.ts +93 -0
  288. package/src/api/responses/memory-recall-log.ts +65 -0
  289. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  290. package/src/background-wake/background-wake-routes.test.ts +868 -0
  291. package/src/background-wake/platform-client.test.ts +308 -0
  292. package/src/background-wake/platform-client.ts +167 -0
  293. package/src/background-wake/publisher.ts +91 -0
  294. package/src/background-wake/runtime-registry.ts +24 -0
  295. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  296. package/src/calls/guardian-dispatch.ts +1 -0
  297. package/src/calls/voice-session-bridge.ts +4 -4
  298. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  299. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  300. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  301. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  302. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  303. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  304. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  305. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  306. package/src/cli/commands/__tests__/notifications.test.ts +184 -40
  307. package/src/cli/commands/browser.ts +247 -0
  308. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  309. package/src/cli/commands/channels/index.ts +229 -0
  310. package/src/cli/commands/domain.ts +91 -41
  311. package/src/cli/commands/inference.ts +93 -40
  312. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  313. package/src/cli/commands/memory-v2.ts +176 -1
  314. package/src/cli/commands/memory-v3-render.ts +491 -0
  315. package/src/cli/commands/memory-v3.ts +567 -0
  316. package/src/cli/commands/notifications.ts +365 -55
  317. package/src/cli/lib/open-browser.ts +7 -2
  318. package/src/cli/program.ts +4 -0
  319. package/src/config/assistant-feature-flags.ts +39 -46
  320. package/src/config/bundled-skills/document-editor/SKILL.md +16 -3
  321. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  322. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  323. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  324. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  325. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  326. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  327. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  328. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  329. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  330. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  331. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  332. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  333. package/src/config/bundled-tool-registry.ts +2 -0
  334. package/src/config/call-site-defaults.ts +8 -7
  335. package/src/config/feature-flag-cache.ts +86 -0
  336. package/src/config/feature-flag-registry.json +33 -17
  337. package/src/config/llm-context-resolution.ts +10 -1
  338. package/src/config/llm-resolver.ts +121 -15
  339. package/src/config/loader.ts +4 -5
  340. package/src/config/schemas/__tests__/memory-v2.test.ts +228 -1
  341. package/src/config/schemas/call-site-catalog.ts +21 -7
  342. package/src/config/schemas/heartbeat.ts +1 -1
  343. package/src/config/schemas/llm.ts +102 -2
  344. package/src/config/schemas/memory-v2.ts +272 -0
  345. package/src/config/schemas/memory.ts +2 -1
  346. package/src/config/schemas/services.ts +6 -2
  347. package/src/config/seed-inference-profiles.ts +36 -16
  348. package/src/context/compactor.ts +52 -0
  349. package/src/context/token-estimator.ts +10 -5
  350. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  351. package/src/conversations/message-consolidation.ts +404 -0
  352. package/src/credential-execution/executable-discovery.ts +40 -0
  353. package/src/credential-execution/process-manager.ts +6 -2
  354. package/src/credential-health/credential-health-service.ts +125 -40
  355. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
  356. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
  357. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +2 -3
  358. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  359. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  360. package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
  361. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  362. package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
  363. package/src/daemon/conversation-agent-loop-handlers.ts +390 -80
  364. package/src/daemon/conversation-agent-loop.ts +244 -90
  365. package/src/daemon/conversation-error.ts +64 -6
  366. package/src/daemon/conversation-lifecycle.ts +27 -22
  367. package/src/daemon/conversation-messaging.ts +84 -43
  368. package/src/daemon/conversation-process.ts +74 -37
  369. package/src/daemon/conversation-runtime-assembly.ts +38 -17
  370. package/src/daemon/conversation-skill-tools.ts +14 -30
  371. package/src/daemon/conversation-surfaces.ts +69 -34
  372. package/src/daemon/conversation-tool-setup.ts +77 -32
  373. package/src/daemon/conversation-usage.ts +2 -0
  374. package/src/daemon/conversation.ts +40 -75
  375. package/src/daemon/daemon-control.ts +1 -1
  376. package/src/daemon/daemon-skill-host.ts +9 -2
  377. package/src/daemon/disk-pressure-guard.ts +39 -29
  378. package/src/daemon/first-greeting.ts +31 -13
  379. package/src/daemon/handlers/config-model.test.ts +1 -0
  380. package/src/daemon/handlers/conversations.ts +11 -3
  381. package/src/daemon/handlers/shared.ts +6 -1
  382. package/src/daemon/host-browser-proxy.ts +5 -5
  383. package/src/daemon/host-cu-proxy.ts +4 -4
  384. package/src/daemon/host-file-proxy.ts +4 -4
  385. package/src/daemon/host-proxy-base.ts +4 -4
  386. package/src/daemon/host-transfer-proxy.ts +10 -10
  387. package/src/daemon/lifecycle.ts +29 -26
  388. package/src/daemon/mcp-reload-service.ts +1 -1
  389. package/src/daemon/meet-manifest-loader.ts +11 -24
  390. package/src/daemon/message-types/conversations.ts +22 -27
  391. package/src/daemon/message-types/document-comments.ts +8 -44
  392. package/src/daemon/message-types/home.ts +2 -14
  393. package/src/daemon/message-types/integrations.ts +2 -7
  394. package/src/daemon/message-types/messages.ts +25 -48
  395. package/src/daemon/message-types/subagents.ts +6 -0
  396. package/src/daemon/message-types/sync.ts +14 -0
  397. package/src/daemon/process-message.ts +9 -9
  398. package/src/daemon/providers-setup.ts +1 -1
  399. package/src/daemon/server.ts +16 -0
  400. package/src/daemon/shutdown-handlers.ts +24 -5
  401. package/src/daemon/switch-inference-profile-tool.ts +62 -0
  402. package/src/daemon/tool-setup-types.ts +7 -0
  403. package/src/daemon/wake-target-adapter.ts +10 -0
  404. package/src/documents/document-store.ts +38 -0
  405. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  406. package/src/heartbeat/__tests__/heartbeat-service.test.ts +30 -1
  407. package/src/heartbeat/heartbeat-service.ts +63 -0
  408. package/src/home/__tests__/feed-writer.test.ts +161 -0
  409. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  410. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  411. package/src/home/feed-writer.ts +146 -7
  412. package/src/home/home-greeting.ts +0 -9
  413. package/src/home/suggested-prompts.ts +27 -154
  414. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  415. package/src/ipc/gateway-client.test.ts +4 -1
  416. package/src/ipc/gateway-flag-listener.ts +123 -0
  417. package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
  418. package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
  419. package/src/ipc/skill-routes/memory.ts +4 -3
  420. package/src/ipc/skill-routes/registries.ts +35 -40
  421. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  422. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  423. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +242 -0
  424. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  425. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
  426. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  427. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  428. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  429. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  430. package/src/memory/auto-analysis-enqueue.ts +5 -1
  431. package/src/memory/conversation-attention-store.ts +17 -3
  432. package/src/memory/conversation-crud.ts +423 -182
  433. package/src/memory/conversation-starters-cadence.ts +3 -1
  434. package/src/memory/conversation-title-service.ts +19 -3
  435. package/src/memory/db-async-query.ts +214 -0
  436. package/src/memory/db-connection.ts +29 -19
  437. package/src/memory/db-init.ts +14 -0
  438. package/src/memory/db-maintenance.ts +30 -21
  439. package/src/memory/db-singleton.ts +77 -0
  440. package/src/memory/delivery-channels.ts +82 -0
  441. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  442. package/src/memory/graph/bootstrap.ts +8 -1
  443. package/src/memory/graph/capability-seed.ts +7 -3
  444. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  445. package/src/memory/graph/extraction.ts +1 -5
  446. package/src/memory/graph/graph-search.ts +7 -1
  447. package/src/memory/graph/retriever.test.ts +3 -3
  448. package/src/memory/indexer.ts +28 -18
  449. package/src/memory/job-handlers/cleanup.ts +76 -18
  450. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  451. package/src/memory/job-handlers/embedding.test.ts +3 -2
  452. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  453. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  454. package/src/memory/jobs-store.ts +14 -0
  455. package/src/memory/jobs-worker.ts +66 -22
  456. package/src/memory/llm-request-log-source-clickhouse.ts +122 -2
  457. package/src/memory/llm-request-log-source-local.ts +31 -0
  458. package/src/memory/llm-request-log-source.ts +40 -2
  459. package/src/memory/llm-request-log-store.ts +228 -1
  460. package/src/memory/llm-usage-store.ts +24 -0
  461. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  462. package/src/memory/memory-retrospective-job.ts +5 -0
  463. package/src/memory/memory-v2-activation-log-store.ts +110 -7
  464. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  465. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  466. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  467. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  468. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  469. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  470. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  471. package/src/memory/migrations/index.ts +19 -0
  472. package/src/memory/migrations/registry.ts +33 -0
  473. package/src/memory/schema/conversations.ts +10 -2
  474. package/src/memory/schema/inference.ts +0 -1
  475. package/src/memory/schema/infrastructure.ts +21 -0
  476. package/src/memory/tool-usage-store.ts +36 -8
  477. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  478. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  479. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  480. package/src/memory/v2/__tests__/harness-metrics.test.ts +83 -0
  481. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  482. package/src/memory/v2/__tests__/harness-replay-input.test.ts +230 -0
  483. package/src/memory/v2/__tests__/harness-runner.test.ts +135 -0
  484. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  485. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  486. package/src/memory/v2/__tests__/router.test.ts +171 -3
  487. package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
  488. package/src/memory/v2/harness/compare.ts +57 -0
  489. package/src/memory/v2/harness/metrics.ts +128 -0
  490. package/src/memory/v2/harness/oracle.ts +145 -0
  491. package/src/memory/v2/harness/replay-input.ts +240 -0
  492. package/src/memory/v2/harness/retriever.ts +74 -0
  493. package/src/memory/v2/harness/router-retriever.ts +43 -0
  494. package/src/memory/v2/harness/runner.ts +112 -0
  495. package/src/memory/v2/harness/trace.ts +64 -0
  496. package/src/memory/v2/injection.ts +21 -15
  497. package/src/memory/v2/prompts/router.ts +26 -1
  498. package/src/memory/v2/qdrant.ts +14 -2
  499. package/src/memory/v2/router.ts +171 -18
  500. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  501. package/src/memory/v3/__tests__/consolidation-job.test.ts +466 -0
  502. package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
  503. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  504. package/src/memory/v3/__tests__/edges.test.ts +706 -0
  505. package/src/memory/v3/__tests__/filter.test.ts +560 -0
  506. package/src/memory/v3/__tests__/gate.test.ts +637 -0
  507. package/src/memory/v3/__tests__/index-composition.test.ts +291 -0
  508. package/src/memory/v3/__tests__/loop.test.ts +775 -0
  509. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  510. package/src/memory/v3/__tests__/scouts.test.ts +489 -0
  511. package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
  512. package/src/memory/v3/__tests__/shadow-middleware.test.ts +398 -0
  513. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  514. package/src/memory/v3/__tests__/traversal.test.ts +508 -0
  515. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  516. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  517. package/src/memory/v3/__tests__/tree-walk.test.ts +784 -0
  518. package/src/memory/v3/__tests__/validate.test.ts +277 -0
  519. package/src/memory/v3/auto-edges.ts +223 -0
  520. package/src/memory/v3/coactivation-store.ts +124 -0
  521. package/src/memory/v3/consolidation-job.ts +323 -0
  522. package/src/memory/v3/coretrieval-seed.ts +240 -0
  523. package/src/memory/v3/edge-learning-job.ts +160 -0
  524. package/src/memory/v3/edges.ts +286 -0
  525. package/src/memory/v3/filter.ts +286 -0
  526. package/src/memory/v3/gate.ts +349 -0
  527. package/src/memory/v3/index-composition.ts +126 -0
  528. package/src/memory/v3/llm-capture.ts +46 -0
  529. package/src/memory/v3/loop.ts +430 -0
  530. package/src/memory/v3/maintenance.ts +144 -0
  531. package/src/memory/v3/prompt-context.ts +33 -0
  532. package/src/memory/v3/prompts/consolidation.ts +458 -0
  533. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  534. package/src/memory/v3/retriever.ts +33 -0
  535. package/src/memory/v3/scouts.ts +431 -0
  536. package/src/memory/v3/shadow-diff.ts +287 -0
  537. package/src/memory/v3/shadow-middleware.ts +347 -0
  538. package/src/memory/v3/traversal.ts +211 -0
  539. package/src/memory/v3/tree-index.ts +237 -0
  540. package/src/memory/v3/tree-store.ts +394 -0
  541. package/src/memory/v3/tree-walk.ts +356 -0
  542. package/src/memory/v3/types.ts +65 -0
  543. package/src/memory/v3/validate.ts +323 -0
  544. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  545. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  546. package/src/notifications/adapters/macos.ts +18 -1
  547. package/src/notifications/adapters/platform.ts +1 -1
  548. package/src/notifications/adapters/slack.ts +45 -11
  549. package/src/notifications/broadcaster.ts +114 -63
  550. package/src/notifications/conversation-pairing.ts +23 -3
  551. package/src/notifications/decision-engine.ts +1 -4
  552. package/src/notifications/decisions-store.ts +32 -1
  553. package/src/notifications/deliveries-store.ts +45 -0
  554. package/src/notifications/edit-notification.ts +201 -0
  555. package/src/notifications/emit-signal.ts +40 -50
  556. package/src/notifications/signal.ts +10 -0
  557. package/src/notifications/types.ts +37 -0
  558. package/src/oauth/byo-connection.test.ts +67 -3
  559. package/src/oauth/byo-connection.ts +32 -5
  560. package/src/oauth/connect-orchestrator.ts +9 -0
  561. package/src/oauth/connection-resolver.test.ts +76 -0
  562. package/src/oauth/connection-resolver.ts +49 -10
  563. package/src/oauth/manual-token-connection.ts +51 -3
  564. package/src/oauth/seed-providers.ts +3 -0
  565. package/src/permissions/approval-policy.test.ts +19 -5
  566. package/src/permissions/approval-policy.ts +14 -3
  567. package/src/permissions/checker.ts +21 -8
  568. package/src/permissions/prompter.ts +3 -3
  569. package/src/permissions/question-prompter.ts +5 -2
  570. package/src/permissions/secret-prompter.ts +2 -2
  571. package/src/platform/client.test.ts +24 -1
  572. package/src/platform/client.ts +8 -0
  573. package/src/platform/feature-gate.ts +15 -0
  574. package/src/plugin-api/index.ts +4 -0
  575. package/src/plugin-api/types.ts +7 -33
  576. package/src/plugins/defaults/index.ts +6 -0
  577. package/src/plugins/defaults/injectors.ts +20 -19
  578. package/src/plugins/defaults/persistence.ts +25 -6
  579. package/src/plugins/external-plugin-loader.ts +5 -68
  580. package/src/plugins/types.ts +68 -29
  581. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  582. package/src/proactive-artifact/job.test.ts +1 -0
  583. package/src/prompts/__tests__/system-prompt.test.ts +4 -4
  584. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  585. package/src/prompts/persona-resolver.ts +36 -21
  586. package/src/prompts/sections.ts +39 -7
  587. package/src/prompts/system-prompt.ts +84 -221
  588. package/src/prompts/template-detection.ts +10 -4
  589. package/src/prompts/templates/BOOTSTRAP.md +9 -13
  590. package/src/prompts/templates/IDENTITY.md +0 -2
  591. package/src/prompts/templates/system-sections.ts +230 -8
  592. package/src/providers/__tests__/connection-model-compat.test.ts +233 -0
  593. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  594. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  595. package/src/providers/anthropic/client.ts +32 -66
  596. package/src/providers/call-site-routing.ts +42 -6
  597. package/src/providers/connection-model-compat.ts +61 -0
  598. package/src/providers/connection-resolution.ts +47 -14
  599. package/src/providers/fireworks/client.ts +1 -0
  600. package/src/providers/gemini/client.ts +70 -6
  601. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  602. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  603. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  604. package/src/providers/inference/adapter-factory.ts +3 -0
  605. package/src/providers/inference/auth.ts +0 -8
  606. package/src/providers/inference/connections.ts +3 -66
  607. package/src/providers/inference/resolve-auth.ts +2 -3
  608. package/src/providers/minimax/client.ts +106 -0
  609. package/src/providers/model-catalog.ts +78 -1
  610. package/src/providers/model-intents.ts +4 -4
  611. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  612. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
  613. package/src/providers/openai/chat-completions-provider.ts +116 -15
  614. package/src/providers/openai/codex-models.ts +20 -0
  615. package/src/providers/openai/responses-provider.ts +87 -30
  616. package/src/providers/openrouter/client.ts +13 -8
  617. package/src/providers/provider-send-message.ts +20 -5
  618. package/src/providers/registry.ts +48 -8
  619. package/src/providers/retry.ts +50 -7
  620. package/src/providers/search-provider-catalog.ts +17 -9
  621. package/src/providers/thinking-config.ts +26 -1
  622. package/src/providers/types.ts +9 -0
  623. package/src/providers/usage-tracking.ts +2 -0
  624. package/src/runtime/AGENTS.md +2 -2
  625. package/src/runtime/__tests__/agent-wake.test.ts +1 -0
  626. package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
  627. package/src/runtime/access-request-helper.ts +1 -0
  628. package/src/runtime/agent-wake.ts +1 -0
  629. package/src/runtime/assistant-event-hub.ts +76 -6
  630. package/src/runtime/auth/route-policy.ts +46 -0
  631. package/src/runtime/btw-sidechain.ts +0 -6
  632. package/src/runtime/channel-readiness-service.ts +68 -0
  633. package/src/runtime/channel-reply-delivery.ts +23 -0
  634. package/src/runtime/channel-retry-sweep.ts +47 -14
  635. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  636. package/src/runtime/http-types.ts +0 -2
  637. package/src/runtime/migrations/vbundle-builder.ts +12 -4
  638. package/src/runtime/pending-interactions.ts +0 -1
  639. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  640. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
  641. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +204 -0
  642. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  643. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  644. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
  645. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +76 -9
  646. package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
  647. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  648. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  649. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
  650. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  651. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  652. package/src/runtime/routes/acp-routes.test.ts +255 -6
  653. package/src/runtime/routes/acp-routes.ts +8 -1
  654. package/src/runtime/routes/app-management-routes.ts +111 -4
  655. package/src/runtime/routes/avatar-routes.ts +10 -10
  656. package/src/runtime/routes/background-wake-routes.ts +356 -0
  657. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  658. package/src/runtime/routes/btw-routes.ts +4 -10
  659. package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
  660. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  661. package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
  662. package/src/runtime/routes/conversation-list-routes.ts +159 -4
  663. package/src/runtime/routes/conversation-management-routes.ts +108 -26
  664. package/src/runtime/routes/conversation-query-routes.ts +200 -44
  665. package/src/runtime/routes/conversation-routes.ts +409 -521
  666. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  667. package/src/runtime/routes/conversations-import-routes.ts +19 -6
  668. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  669. package/src/runtime/routes/documents-routes.ts +10 -1
  670. package/src/runtime/routes/domain-routes.ts +60 -10
  671. package/src/runtime/routes/email-routes.ts +5 -2
  672. package/src/runtime/routes/events-routes.ts +54 -10
  673. package/src/runtime/routes/group-routes.ts +35 -8
  674. package/src/runtime/routes/home-feed-routes.ts +129 -0
  675. package/src/runtime/routes/host-browser-routes.ts +10 -2
  676. package/src/runtime/routes/host-cu-routes.ts +2 -2
  677. package/src/runtime/routes/identity-intro-cache.ts +61 -16
  678. package/src/runtime/routes/identity-routes.ts +30 -9
  679. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  680. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
  681. package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
  682. package/src/runtime/routes/index.ts +10 -0
  683. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  684. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  685. package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
  686. package/src/runtime/routes/integrations/vercel.ts +15 -0
  687. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  688. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  689. package/src/runtime/routes/memory-item-routes.ts +8 -3
  690. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  691. package/src/runtime/routes/memory-v3-routes.ts +474 -0
  692. package/src/runtime/routes/migration-routes.ts +32 -28
  693. package/src/runtime/routes/notification-routes.ts +63 -1
  694. package/src/runtime/routes/oauth-commands-routes.ts +6 -1
  695. package/src/runtime/routes/plugins-routes.ts +337 -0
  696. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  697. package/src/runtime/routes/secret-routes.ts +25 -5
  698. package/src/runtime/routes/settings-routes.ts +12 -11
  699. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  700. package/src/runtime/routes/surface-action-routes.ts +1 -38
  701. package/src/runtime/routes/surface-content-routes.ts +12 -5
  702. package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
  703. package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
  704. package/src/runtime/routes/workspace-routes.ts +25 -10
  705. package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
  706. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  707. package/src/runtime/sync/resource-sync-events.ts +106 -38
  708. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  709. package/src/runtime/sync/sync-publisher.ts +2 -1
  710. package/src/runtime/tool-grant-request-helper.ts +1 -0
  711. package/src/runtime/verification-outbound-actions.ts +73 -1
  712. package/src/schedule/schedule-store.ts +8 -1
  713. package/src/schedule/scheduler.ts +111 -15
  714. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  715. package/src/security/encrypted-store.ts +7 -16
  716. package/src/security/store-path-override.ts +61 -0
  717. package/src/signals/user-message.ts +5 -8
  718. package/src/skills/validate-input.ts +177 -0
  719. package/src/subagent/manager.ts +13 -13
  720. package/src/subagent/types.ts +6 -0
  721. package/src/tasks/tool-sanitizer.ts +2 -2
  722. package/src/telemetry/types.ts +12 -0
  723. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  724. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  725. package/src/tools/acp/spawn.test.ts +119 -0
  726. package/src/tools/acp/spawn.ts +15 -2
  727. package/src/tools/apps/definitions.ts +36 -28
  728. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  729. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  730. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  731. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  732. package/src/tools/browser/browser-execution.ts +16 -3
  733. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  734. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  735. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  736. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  737. package/src/tools/browser/cdp-client/factory.ts +100 -17
  738. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  739. package/src/tools/browser/cdp-client/types.ts +65 -0
  740. package/src/tools/browser/pinned-tabs.ts +96 -40
  741. package/src/tools/computer-use/definitions.ts +282 -336
  742. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  743. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  744. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  745. package/src/tools/credentials/vault.ts +3 -9
  746. package/src/tools/document/document-tool.ts +189 -7
  747. package/src/tools/execution-target.ts +18 -23
  748. package/src/tools/executor.ts +24 -56
  749. package/src/tools/filesystem/edit.ts +3 -9
  750. package/src/tools/filesystem/list.ts +3 -9
  751. package/src/tools/filesystem/read.ts +3 -9
  752. package/src/tools/filesystem/write.ts +3 -9
  753. package/src/tools/host-filesystem/edit.test.ts +1 -0
  754. package/src/tools/host-filesystem/edit.ts +3 -9
  755. package/src/tools/host-filesystem/read.test.ts +1 -0
  756. package/src/tools/host-filesystem/read.ts +3 -9
  757. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  758. package/src/tools/host-filesystem/transfer.ts +3 -9
  759. package/src/tools/host-filesystem/write.test.ts +1 -0
  760. package/src/tools/host-filesystem/write.ts +3 -9
  761. package/src/tools/host-terminal/host-shell.ts +3 -9
  762. package/src/tools/mcp/mcp-tool-factory.ts +1 -10
  763. package/src/tools/memory/register.test.ts +1 -1
  764. package/src/tools/memory/register.ts +4 -9
  765. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  766. package/src/tools/network/__tests__/web-search.test.ts +211 -3
  767. package/src/tools/network/managed-search-proxy.ts +183 -0
  768. package/src/tools/network/web-fetch.ts +3 -9
  769. package/src/tools/network/web-search.ts +224 -76
  770. package/src/tools/policy-context.ts +3 -1
  771. package/src/tools/registry.ts +150 -123
  772. package/src/tools/schedule/create.ts +1 -1
  773. package/src/tools/schema-transforms.ts +1 -1
  774. package/src/tools/skills/execute.ts +3 -9
  775. package/src/tools/skills/load.ts +3 -9
  776. package/src/tools/skills/skill-tool-factory.ts +18 -44
  777. package/src/tools/subagent/notify-parent.ts +3 -9
  778. package/src/tools/subagent/spawn.ts +3 -0
  779. package/src/tools/system/request-permission.ts +3 -9
  780. package/src/tools/terminal/shell.ts +3 -9
  781. package/src/tools/tool-approval-handler.ts +10 -4
  782. package/src/tools/tool-defaults.ts +94 -0
  783. package/src/tools/tool-name-aliases.ts +72 -14
  784. package/src/tools/types.ts +32 -101
  785. package/src/tools/ui-surface/definitions.ts +104 -108
  786. package/src/types/onboarding-context.ts +6 -0
  787. package/src/usage/attribution.ts +32 -1
  788. package/src/usage/pricing.ts +23 -0
  789. package/src/usage/types.ts +12 -0
  790. package/src/util/browser.ts +7 -2
  791. package/src/util/logger.ts +16 -7
  792. package/src/util/platform.ts +7 -2
  793. package/src/util/sqlite3-runtime.ts +65 -0
  794. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  795. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  796. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  797. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  798. package/src/workspace/migrations/registry.ts +6 -0
  799. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  800. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  801. package/src/daemon/query-complexity-router.ts +0 -75
  802. package/src/prompts/cache-boundary.ts +0 -8
@@ -24,7 +24,11 @@ import type { ChannelId, InterfaceId } from "../channels/types.js";
24
24
  import { parseChannelId, parseInterfaceId } from "../channels/types.js";
25
25
  import { CHANNEL_IDS, isChannelId } from "../channels/types.js";
26
26
  import { getConfig } from "../config/loader.js";
27
+ import { findDisplayTurnEndIndex } from "../conversations/message-consolidation.js";
28
+ import { conversationMetadataSyncTag } from "../daemon/message-types/sync.js";
27
29
  import type { TrustContext } from "../daemon/trust-context.js";
30
+ import { clearAllConversationIds } from "../home/feed-writer.js";
31
+ import { publishSyncInvalidation } from "../runtime/sync/sync-publisher.js";
28
32
  import { UserError } from "../util/errors.js";
29
33
  import { safeParseRecord } from "../util/json.js";
30
34
  import { getLogger } from "../util/logger.js";
@@ -47,6 +51,7 @@ import {
47
51
  } from "./conversation-disk-view.js";
48
52
  import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
49
53
  import { ensureGroupMigration } from "./conversation-group-migration.js";
54
+ import { runAsyncSqlite } from "./db-async-query.js";
50
55
  import { getDb, getSqliteFrom } from "./db-connection.js";
51
56
  import { forkGraphMemoryState } from "./graph/graph-memory-state-store.js";
52
57
  import { indexMessageNow } from "./indexer.js";
@@ -179,6 +184,24 @@ export function provenanceFromTrustContext(
179
184
  };
180
185
  }
181
186
 
187
+ /** Extract image file paths from resolved attachments for message metadata. */
188
+ export function extractImageSourcePaths(
189
+ attachments: ReadonlyArray<{
190
+ filename: string;
191
+ mimeType: string;
192
+ filePath?: string;
193
+ }>,
194
+ ): Record<string, string> | undefined {
195
+ const paths: Record<string, string> = {};
196
+ for (let i = 0; i < attachments.length; i++) {
197
+ const a = attachments[i];
198
+ if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
199
+ paths[`${i}:${a.filename}`] = a.filePath;
200
+ }
201
+ }
202
+ return Object.keys(paths).length > 0 ? paths : undefined;
203
+ }
204
+
182
205
  export interface ConversationRow {
183
206
  id: string;
184
207
  title: string | null;
@@ -190,7 +213,7 @@ export interface ConversationRow {
190
213
  contextSummary: string | null;
191
214
  contextCompactedMessageCount: number;
192
215
  contextCompactedAt: number | null;
193
- cleanedAt: number | null;
216
+ historyStrippedAt: number | null;
194
217
  slackContextCompactionWatermarkTs: string | null;
195
218
  slackContextCompactionWatermarkAt: number | null;
196
219
  conversationType: string;
@@ -224,7 +247,7 @@ export const parseConversation = createRowMapper<
224
247
  contextSummary: "contextSummary",
225
248
  contextCompactedMessageCount: "contextCompactedMessageCount",
226
249
  contextCompactedAt: "contextCompactedAt",
227
- cleanedAt: "cleanedAt",
250
+ historyStrippedAt: "historyStrippedAt",
228
251
  slackContextCompactionWatermarkTs: "slackContextCompactionWatermarkTs",
229
252
  slackContextCompactionWatermarkAt: "slackContextCompactionWatermarkAt",
230
253
  conversationType: "conversationType",
@@ -244,6 +267,9 @@ export const parseConversation = createRowMapper<
244
267
  lastNotifiedInferenceProfile: "lastNotifiedInferenceProfile",
245
268
  });
246
269
 
270
+ /** Allowed values for the `role` column on `messages`. */
271
+ export type MessageRole = "user" | "assistant" | "system";
272
+
247
273
  export interface MessageRow {
248
274
  id: string;
249
275
  conversationId: string;
@@ -278,6 +304,193 @@ function monotonicNow(): number {
278
304
  return lastTimestamp;
279
305
  }
280
306
 
307
+ // ── insertMessageCore ─────────────────────────────────────────────────
308
+
309
+ /** Shape returned by {@link insertMessageCore} and its public wrappers. */
310
+ interface InsertedMessage {
311
+ id: string;
312
+ conversationId: string;
313
+ role: MessageRole;
314
+ content: string;
315
+ createdAt: number;
316
+ metadata?: string;
317
+ clientMessageId?: string;
318
+ deduplicated: boolean;
319
+ }
320
+
321
+ interface InsertMessageCoreParams {
322
+ conversationId: string;
323
+ role: MessageRole;
324
+ content: string;
325
+ metadata?: Record<string, unknown>;
326
+ clientMessageId?: string;
327
+ }
328
+
329
+ /**
330
+ * Core message persistence primitive shared by {@link addMessage} and
331
+ * {@link reserveMessage}.
332
+ *
333
+ * Inserts a message row inside a transaction that atomically bumps the
334
+ * parent conversation's `updatedAt` / `lastMessageAt` timestamps and
335
+ * conditionally sets the conversation's `originChannel` when the first
336
+ * channel-originated message arrives.
337
+ *
338
+ * When a `clientMessageId` is provided the insert runs inside a
339
+ * SAVEPOINT. If the partial unique index on
340
+ * `(conversation_id, client_message_id)` raises
341
+ * `SQLITE_CONSTRAINT_UNIQUE`, the SAVEPOINT is rolled back, the
342
+ * existing row is fetched, and returned with `deduplicated: true`.
343
+ * This makes the operation idempotent for client-generated
344
+ * correlation nonces.
345
+ *
346
+ * Retries up to 3 times on `SQLITE_BUSY*` / `SQLITE_IOERR*` to handle
347
+ * WAL contention. The timestamp is recomputed each attempt so a late
348
+ * retry doesn't persist a stale `updatedAt`.
349
+ */
350
+ async function insertMessageCore(
351
+ params: InsertMessageCoreParams,
352
+ ): Promise<InsertedMessage> {
353
+ const { conversationId, role, content, metadata, clientMessageId } = params;
354
+ const db = getDb();
355
+ const messageId = uuid();
356
+
357
+ if (metadata) {
358
+ const result = messageMetadataSchema.safeParse(metadata);
359
+ if (!result.success) {
360
+ log.warn(
361
+ { conversationId, messageId, issues: result.error.issues },
362
+ "Invalid message metadata, storing as-is",
363
+ );
364
+ }
365
+ }
366
+
367
+ const metadataStr = metadata ? JSON.stringify(metadata) : undefined;
368
+ const originChannelCandidate =
369
+ metadata && isChannelId(metadata.userMessageChannel)
370
+ ? metadata.userMessageChannel
371
+ : null;
372
+
373
+ const MAX_RETRIES = 3;
374
+ let now!: number;
375
+ for (let attempt = 0; ; attempt++) {
376
+ now = monotonicNow();
377
+ try {
378
+ const values = {
379
+ id: messageId,
380
+ conversationId,
381
+ role,
382
+ content,
383
+ createdAt: now,
384
+ ...(metadataStr ? { metadata: metadataStr } : {}),
385
+ ...(clientMessageId ? { clientMessageId } : {}),
386
+ };
387
+
388
+ if (clientMessageId) {
389
+ // Idempotent insert: skip silently if this clientMessageId was
390
+ // already persisted for the conversation.
391
+ const raw = getSqliteFrom(db);
392
+ raw.exec("SAVEPOINT insert_msg");
393
+ try {
394
+ db.insert(messages).values(values).run();
395
+ if (originChannelCandidate) {
396
+ db.update(conversations)
397
+ .set({ originChannel: originChannelCandidate })
398
+ .where(
399
+ and(
400
+ eq(conversations.id, conversationId),
401
+ isNull(conversations.originChannel),
402
+ ),
403
+ )
404
+ .run();
405
+ }
406
+ db.update(conversations)
407
+ .set({ updatedAt: now, lastMessageAt: now })
408
+ .where(eq(conversations.id, conversationId))
409
+ .run();
410
+ raw.exec("RELEASE insert_msg");
411
+ } catch (insertErr) {
412
+ raw.exec("ROLLBACK TO insert_msg");
413
+ raw.exec("RELEASE insert_msg");
414
+ const code = (insertErr as { code?: string }).code ?? "";
415
+ if (code === "SQLITE_CONSTRAINT_UNIQUE") {
416
+ // Duplicate clientMessageId — return the existing row.
417
+ const existing = db
418
+ .select()
419
+ .from(messages)
420
+ .where(
421
+ and(
422
+ eq(messages.conversationId, conversationId),
423
+ eq(messages.clientMessageId, clientMessageId),
424
+ ),
425
+ )
426
+ .get();
427
+ if (existing) {
428
+ return {
429
+ id: existing.id,
430
+ conversationId: existing.conversationId,
431
+ role: existing.role as MessageRole,
432
+ content: existing.content,
433
+ createdAt: existing.createdAt,
434
+ ...(existing.metadata ? { metadata: existing.metadata } : {}),
435
+ clientMessageId: existing.clientMessageId ?? undefined,
436
+ deduplicated: true,
437
+ };
438
+ }
439
+ }
440
+ throw insertErr;
441
+ }
442
+ } else {
443
+ // No clientMessageId — standard insert inside a transaction.
444
+ db.transaction((tx) => {
445
+ tx.insert(messages).values(values).run();
446
+ if (originChannelCandidate) {
447
+ tx.update(conversations)
448
+ .set({ originChannel: originChannelCandidate })
449
+ .where(
450
+ and(
451
+ eq(conversations.id, conversationId),
452
+ isNull(conversations.originChannel),
453
+ ),
454
+ )
455
+ .run();
456
+ }
457
+ tx.update(conversations)
458
+ .set({ updatedAt: now, lastMessageAt: now })
459
+ .where(eq(conversations.id, conversationId))
460
+ .run();
461
+ });
462
+ }
463
+ break;
464
+ } catch (err) {
465
+ const errCode = (err as { code?: string }).code ?? "";
466
+ if (
467
+ attempt < MAX_RETRIES &&
468
+ (errCode.startsWith("SQLITE_BUSY") ||
469
+ errCode.startsWith("SQLITE_IOERR"))
470
+ ) {
471
+ log.warn(
472
+ { attempt, conversationId, code: errCode },
473
+ "insertMessageCore: transient SQLite error, retrying",
474
+ );
475
+ await Bun.sleep(50 * (attempt + 1));
476
+ continue;
477
+ }
478
+ throw err;
479
+ }
480
+ }
481
+
482
+ return {
483
+ id: messageId,
484
+ conversationId,
485
+ role,
486
+ content,
487
+ createdAt: now,
488
+ ...(metadataStr ? { metadata: metadataStr } : {}),
489
+ ...(clientMessageId ? { clientMessageId } : {}),
490
+ deduplicated: false,
491
+ };
492
+ }
493
+
281
494
  export function createConversation(
282
495
  titleOrOpts?:
283
496
  | string
@@ -574,6 +787,20 @@ export function forkConversation(params: {
574
787
  throw new UserError(`Conversation ${conversationId} not found`);
575
788
  }
576
789
  const sourceMessages = getMessages(conversationId);
790
+ if (throughMessageId != null) {
791
+ // `getMessages` orders by `createdAt` only; when rows share an identical
792
+ // millisecond timestamp the tie order is unspecified. Callers that pin the
793
+ // fork to a cutoff choose it from a `(createdAt, id)` cursor (e.g. the
794
+ // memory-retrospective job, via `getMessagesAfter`), so slicing through
795
+ // `throughMessageId` under the unstable order could include same-timestamp
796
+ // siblings the cursor considers *after* the cutoff (reprocessed next run)
797
+ // or exclude ones it considers *before* it (skipped forever). Re-sort on
798
+ // `(createdAt, id)` so the slice agrees with the cutoff. The unpinned full
799
+ // fork copies every row regardless of order, so it keeps source order.
800
+ sourceMessages.sort(
801
+ (a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id),
802
+ );
803
+ }
577
804
 
578
805
  if (sourceMessages.length === 0) {
579
806
  throw new UserError(
@@ -581,17 +808,30 @@ export function forkConversation(params: {
581
808
  );
582
809
  }
583
810
 
584
- const copyBoundaryIndex =
811
+ const initialBoundaryIndex =
585
812
  throughMessageId == null
586
813
  ? sourceMessages.length - 1
587
814
  : sourceMessages.findIndex((message) => message.id === throughMessageId);
588
815
 
589
- if (throughMessageId != null && copyBoundaryIndex === -1) {
816
+ if (throughMessageId != null && initialBoundaryIndex === -1) {
590
817
  throw new UserError(
591
818
  `Message ${throughMessageId} does not belong to conversation ${conversationId}`,
592
819
  );
593
820
  }
594
821
 
822
+ // Extend the boundary to cover the full display turn the client
823
+ // addressed. The read-path collapses each assistant turn across
824
+ // multiple DB rows — consecutive assistant rows AND tool-result-only
825
+ // user rows between them — so "fork through message X" semantically
826
+ // means "fork through the entire display turn containing X" no matter
827
+ // which DB row in the cluster the client supplied. Single source of
828
+ // truth is `findDisplayTurnEndIndex`, shared with the read path so
829
+ // both stay in sync.
830
+ const copyBoundaryIndex = findDisplayTurnEndIndex(
831
+ sourceMessages,
832
+ initialBoundaryIndex,
833
+ );
834
+
595
835
  const visibleWindowStartIndex = Math.max(
596
836
  0,
597
837
  Math.min(
@@ -607,15 +847,16 @@ export function forkConversation(params: {
607
847
  ? sourceMessages.slice(0, copyBoundaryIndex + 1)
608
848
  : ([] as MessageRow[]);
609
849
 
610
- // Inherit /clean state only when the fork boundary is at-or-after the
611
- // clean event. Pre-clean forks branch from history that pre-dates the
612
- // clean, so the marker would be a no-op and is misleading to copy.
613
- const sourceCleanedAt = sourceConversation.cleanedAt ?? null;
850
+ // Inherit the history-strip marker only when the fork boundary is at-or-
851
+ // after the strip event. Pre-strip forks branch from history that pre-
852
+ // dates the strip, so the marker would be a no-op and is misleading to
853
+ // copy.
854
+ const sourceHistoryStrippedAt = sourceConversation.historyStrippedAt ?? null;
614
855
  const boundaryMessageCreatedAt = messagesToCopy.at(-1)?.createdAt ?? null;
615
- const inheritsCleanedAt =
616
- sourceCleanedAt != null &&
856
+ const inheritsHistoryStrippedAt =
857
+ sourceHistoryStrippedAt != null &&
617
858
  boundaryMessageCreatedAt != null &&
618
- boundaryMessageCreatedAt >= sourceCleanedAt;
859
+ boundaryMessageCreatedAt >= sourceHistoryStrippedAt;
619
860
  const forkParentMessageId = messagesToCopy.at(-1)?.id ?? null;
620
861
  const forkTitle =
621
862
  params.title ?? `${sourceConversation.title ?? "Untitled"} (Fork)`;
@@ -662,7 +903,9 @@ export function forkConversation(params: {
662
903
  slackContextCompactionWatermarkAt: preserveSourceCompactionState
663
904
  ? sourceConversation.slackContextCompactionWatermarkAt
664
905
  : null,
665
- cleanedAt: inheritsCleanedAt ? sourceCleanedAt : null,
906
+ historyStrippedAt: inheritsHistoryStrippedAt
907
+ ? sourceHistoryStrippedAt
908
+ : null,
666
909
  inferenceProfile: sourceConversation.inferenceProfile,
667
910
  })
668
911
  .where(eq(conversations.id, fc.id))
@@ -952,92 +1195,36 @@ export function wipeConversation(id: string): WipeConversationResult {
952
1195
  };
953
1196
  }
954
1197
 
1198
+ /**
1199
+ * Persist a message and run post-insert side effects (memory indexing,
1200
+ * attention projection). Delegates the core insert + retry logic to
1201
+ * {@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
+ */
955
1207
  export async function addMessage(
956
1208
  conversationId: string,
957
- role: string,
1209
+ role: MessageRole,
958
1210
  content: string,
959
1211
  metadata?: Record<string, unknown>,
960
1212
  opts?: { skipIndexing?: boolean },
1213
+ clientMessageId?: string,
961
1214
  ) {
962
- const db = getDb();
963
- const messageId = uuid();
964
-
965
- if (metadata) {
966
- const result = messageMetadataSchema.safeParse(metadata);
967
- if (!result.success) {
968
- log.warn(
969
- { conversationId, messageId, issues: result.error.issues },
970
- "Invalid message metadata, storing as-is",
971
- );
972
- }
973
- }
974
-
975
- const metadataStr = metadata ? JSON.stringify(metadata) : undefined;
976
- const originChannelCandidate =
977
- metadata && isChannelId(metadata.userMessageChannel)
978
- ? metadata.userMessageChannel
979
- : null;
980
- // Wrap insert + updatedAt bump in a transaction so they're atomic.
981
- // Retry on SQLITE_BUSY* and SQLITE_IOERR* — covers WAL contention variants
982
- // (SQLITE_BUSY_SNAPSHOT, SQLITE_BUSY_RECOVERY) and transient disk I/O errors.
983
- // Timestamp is recomputed each attempt so a late retry doesn't persist a stale updatedAt.
984
- const MAX_RETRIES = 3;
985
- let now!: number;
986
- for (let attempt = 0; ; attempt++) {
987
- now = monotonicNow();
988
- try {
989
- const values = {
990
- id: messageId,
991
- conversationId,
992
- role,
993
- content,
994
- createdAt: now,
995
- ...(metadataStr ? { metadata: metadataStr } : {}),
996
- };
997
- db.transaction((tx) => {
998
- tx.insert(messages).values(values).run();
999
- if (originChannelCandidate) {
1000
- tx.update(conversations)
1001
- .set({ originChannel: originChannelCandidate })
1002
- .where(
1003
- and(
1004
- eq(conversations.id, conversationId),
1005
- isNull(conversations.originChannel),
1006
- ),
1007
- )
1008
- .run();
1009
- }
1010
- tx.update(conversations)
1011
- .set({ updatedAt: now, lastMessageAt: now })
1012
- .where(eq(conversations.id, conversationId))
1013
- .run();
1014
- });
1015
- break;
1016
- } catch (err) {
1017
- const errCode = (err as { code?: string }).code ?? "";
1018
- if (
1019
- attempt < MAX_RETRIES &&
1020
- (errCode.startsWith("SQLITE_BUSY") ||
1021
- errCode.startsWith("SQLITE_IOERR"))
1022
- ) {
1023
- log.warn(
1024
- { attempt, conversationId, code: errCode },
1025
- "addMessage: transient SQLite error, retrying",
1026
- );
1027
- await Bun.sleep(50 * (attempt + 1));
1028
- continue;
1029
- }
1030
- throw err;
1031
- }
1032
- }
1033
- const message = {
1034
- id: messageId,
1215
+ const inserted = await insertMessageCore({
1035
1216
  conversationId,
1036
1217
  role,
1037
1218
  content,
1038
- createdAt: now,
1039
- ...(metadataStr ? { metadata: metadataStr } : {}),
1040
- };
1219
+ metadata,
1220
+ clientMessageId,
1221
+ });
1222
+
1223
+ if (inserted.deduplicated) {
1224
+ return inserted;
1225
+ }
1226
+
1227
+ const message = inserted;
1041
1228
 
1042
1229
  if (!opts?.skipIndexing) {
1043
1230
  try {
@@ -1072,11 +1259,16 @@ export async function addMessage(
1072
1259
 
1073
1260
  if (role === "assistant") {
1074
1261
  try {
1075
- projectAssistantMessage({
1262
+ const attentionStateChanged = projectAssistantMessage({
1076
1263
  conversationId,
1077
1264
  messageId: message.id,
1078
1265
  messageAt: message.createdAt,
1079
1266
  });
1267
+ if (attentionStateChanged) {
1268
+ void publishSyncInvalidation([
1269
+ conversationMetadataSyncTag(conversationId),
1270
+ ]);
1271
+ }
1080
1272
  } catch (err) {
1081
1273
  log.warn(
1082
1274
  { err, conversationId, messageId: message.id },
@@ -1270,11 +1462,30 @@ export function hasMessages(conversationId: string): boolean {
1270
1462
  interface PaginatedMessagesResult {
1271
1463
  messages: MessageRow[];
1272
1464
  hasMore: boolean;
1465
+ /**
1466
+ * Position of the last row scanned when the loop stops on
1467
+ * `PAGINATION_SCAN_CAP` rather than DB exhaustion. Callers derive their
1468
+ * client cursor from the visible page's oldest row, but a cap-truncated
1469
+ * page can be empty (a contiguous block of filtered-out rows longer than
1470
+ * the cap), leaving nothing to resume from. Surfacing the last scanned
1471
+ * `(createdAt, id)` lets the caller hand the client a cursor so it can
1472
+ * request the next window and keep draining instead of stalling.
1473
+ */
1474
+ nextCursor?: { createdAt: number; id: string };
1273
1475
  }
1274
1476
 
1275
1477
  const PAGINATION_CHUNK_MIN = 50;
1276
1478
  const PAGINATION_SCAN_CAP = 10_000;
1277
1479
 
1480
+ // Test-only override for PAGINATION_SCAN_CAP so tests can exercise the
1481
+ // cap-truncation branch with a small cap instead of seeding >10k rows (which
1482
+ // makes the suite slow and the post-test DELETE flaky under parallel CI load).
1483
+ // `undefined` restores the production cap.
1484
+ let paginationScanCapOverride: number | undefined;
1485
+ export function _setPaginationScanCapForTesting(cap: number | undefined): void {
1486
+ paginationScanCapOverride = cap;
1487
+ }
1488
+
1278
1489
  export function getMessagesPaginated(
1279
1490
  conversationId: string,
1280
1491
  limit: number | undefined,
@@ -1315,8 +1526,19 @@ export function getMessagesPaginated(
1315
1526
  // row — otherwise a pathological filter against a huge conversation would
1316
1527
  // tie up a connection for thousands of roundtrips.
1317
1528
  let rowsScanned = 0;
1318
-
1319
- while (visible.length < limit + 1 && rowsScanned < PAGINATION_SCAN_CAP) {
1529
+ // Distinguish "stopped because we hit the scan cap" from "stopped because the
1530
+ // DB ran out of rows". On a cap-truncated stop there may be more visible rows
1531
+ // past the scanned window, so `hasMore` must stay true and we record the last
1532
+ // scanned position as a resume cursor (the visible page may be empty).
1533
+ let scanCapTruncated = false;
1534
+ let lastScanned: { createdAt: number; id: string } | undefined;
1535
+ const scanCap = paginationScanCapOverride ?? PAGINATION_SCAN_CAP;
1536
+
1537
+ while (visible.length < limit + 1) {
1538
+ if (rowsScanned >= scanCap) {
1539
+ scanCapTruncated = true;
1540
+ break;
1541
+ }
1320
1542
  const cursorPredicate =
1321
1543
  cursorCreatedAt === undefined
1322
1544
  ? undefined
@@ -1349,15 +1571,25 @@ export function getMessagesPaginated(
1349
1571
 
1350
1572
  if (chunk.length < chunkSize) break;
1351
1573
  const lastRow = chunk[chunk.length - 1];
1574
+ lastScanned = { createdAt: lastRow.createdAt, id: lastRow.id };
1352
1575
  cursorCreatedAt = lastRow.createdAt;
1353
1576
  cursorMessageId = lastRow.id;
1354
1577
  }
1355
1578
 
1356
- const hasMore = visible.length > limit;
1357
- if (hasMore) visible.splice(limit);
1579
+ const filledPage = visible.length > limit;
1580
+ // A cap-truncated stop means the DB may still hold older visible rows past
1581
+ // the scanned window, so report `hasMore: true` to keep the client draining
1582
+ // — returning `false` here is the stall this loop exists to prevent.
1583
+ const hasMore = filledPage || scanCapTruncated;
1584
+ if (filledPage) visible.splice(limit);
1358
1585
  visible.reverse();
1359
1586
 
1360
- return { messages: visible, hasMore };
1587
+ // Only hand back a resume cursor when the cap (not DB exhaustion) cut the
1588
+ // search short; callers fall back to it when the visible page came back
1589
+ // empty and has no oldest row to anchor the next request.
1590
+ const nextCursor = scanCapTruncated ? lastScanned : undefined;
1591
+
1592
+ return { messages: visible, hasMore, nextCursor };
1361
1593
  }
1362
1594
 
1363
1595
  export function getLastUserTimestampBefore(
@@ -1451,14 +1683,14 @@ export function updateConversationContextWindow(
1451
1683
  .run();
1452
1684
  }
1453
1685
 
1454
- export function setConversationCleanedAt(
1686
+ export function setConversationHistoryStrippedAt(
1455
1687
  id: string,
1456
- cleanedAt: number | null,
1688
+ historyStrippedAt: number | null,
1457
1689
  ): void {
1458
1690
  const db = getDb();
1459
1691
  db.update(conversations)
1460
1692
  .set({
1461
- cleanedAt,
1693
+ historyStrippedAt,
1462
1694
  updatedAt: Date.now(),
1463
1695
  })
1464
1696
  .where(eq(conversations.id, id))
@@ -1703,13 +1935,35 @@ export function setLastNotifiedInferenceProfile(
1703
1935
  * Delete all conversations, messages, and related data (tool invocations,
1704
1936
  * memory segments, etc.) from the daemon database.
1705
1937
  * Returns { conversations, messages } counts.
1938
+ *
1939
+ * Each bulk DELETE is dispatched through {@link runAsyncSqlite}: when
1940
+ * the host has a `sqlite3` CLI it executes in a subprocess and the
1941
+ * daemon's main event loop stays responsive while large tables
1942
+ * (`llm_request_logs`, `tool_invocations`, etc.) are wiped. On hosts
1943
+ * without the CLI the abstraction falls back to in-process blocking
1944
+ * execution — the same behaviour the daemon had before.
1706
1945
  */
1707
- export function clearAll(): { conversations: number; messages: number } {
1946
+ export async function clearAll(): Promise<{
1947
+ conversations: number;
1948
+ messages: number;
1949
+ }> {
1708
1950
  const msgCount =
1709
1951
  rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM messages")?.c ?? 0;
1710
1952
  const convCount =
1711
1953
  rawGet<{ c: number }>("SELECT COUNT(*) AS c FROM conversations")?.c ?? 0;
1712
1954
 
1955
+ // Each DELETE goes through `runAsyncSqlite`. The original code threw
1956
+ // on rawExec failure; mirror that here by throwing when the async
1957
+ // result reports `ok: false`, so the route handler still returns 500.
1958
+ const runOrThrow = async (sql: string): Promise<void> => {
1959
+ const result = await runAsyncSqlite(sql);
1960
+ if (!result.ok) {
1961
+ throw new Error(
1962
+ `clearAll: \`${sql}\` failed (${result.backend}): ${result.error ?? "unknown"}`,
1963
+ );
1964
+ }
1965
+ };
1966
+
1713
1967
  // Delete in dependency order. Cascades handle memory_segments and
1714
1968
  // tool_invocations, but we explicitly clear non-cascading memory
1715
1969
  // tables too.
@@ -1719,22 +1973,21 @@ export function clearAll(): { conversations: number; messages: number } {
1719
1973
  // triggers so that the subsequent base-table DELETEs don't also fail
1720
1974
  // (SQLite triggers are atomic with the triggering statement, so a
1721
1975
  // corrupted FTS table would roll back every base-table DELETE).
1722
- rawExec("DELETE FROM memory_segments");
1723
- rawExec("DELETE FROM memory_summaries");
1724
- rawExec("DELETE FROM memory_embeddings");
1725
- rawExec("DELETE FROM memory_jobs");
1726
- rawExec("DELETE FROM memory_checkpoints");
1727
- rawExec("DELETE FROM llm_request_logs");
1728
- rawExec("DELETE FROM llm_usage_events");
1729
- rawExec("DELETE FROM message_attachments");
1730
- rawExec("DELETE FROM attachments");
1731
- rawExec("DELETE FROM tool_invocations");
1976
+ await runOrThrow("DELETE FROM memory_segments");
1977
+ await runOrThrow("DELETE FROM memory_summaries");
1978
+ await runOrThrow("DELETE FROM memory_embeddings");
1979
+ await runOrThrow("DELETE FROM memory_jobs");
1980
+ await runOrThrow("DELETE FROM memory_checkpoints");
1981
+ await runOrThrow("DELETE FROM llm_request_logs");
1982
+ await runOrThrow("DELETE FROM llm_usage_events");
1983
+ await runOrThrow("DELETE FROM message_attachments");
1984
+ await runOrThrow("DELETE FROM attachments");
1985
+ await runOrThrow("DELETE FROM tool_invocations");
1732
1986
  let messagesFtsCorrupted = false;
1733
- try {
1734
- rawExec("DELETE FROM messages_fts");
1735
- } catch (err) {
1987
+ const ftsResult = await runAsyncSqlite("DELETE FROM messages_fts");
1988
+ if (!ftsResult.ok) {
1736
1989
  log.warn(
1737
- { err },
1990
+ { error: ftsResult.error, backend: ftsResult.backend },
1738
1991
  "clearAll: failed to clear messages_fts — dropping triggers so base-table cleanup can proceed",
1739
1992
  );
1740
1993
  rawExec("DROP TRIGGER IF EXISTS messages_fts_ai");
@@ -1742,8 +1995,8 @@ export function clearAll(): { conversations: number; messages: number } {
1742
1995
  rawExec("DROP TRIGGER IF EXISTS messages_fts_au");
1743
1996
  messagesFtsCorrupted = true;
1744
1997
  }
1745
- rawExec("DELETE FROM messages");
1746
- rawExec("DELETE FROM conversations");
1998
+ await runOrThrow("DELETE FROM messages");
1999
+ await runOrThrow("DELETE FROM conversations");
1747
2000
 
1748
2001
  // Record audit event — lifecycle_events is NOT deleted by clearAll(),
1749
2002
  // so this survives the wipe and provides a permanent trail.
@@ -1782,6 +2035,8 @@ export function clearAll(): { conversations: number; messages: number } {
1782
2035
  log.warn({ err }, "clearAll: failed to reset conversations directory");
1783
2036
  }
1784
2037
 
2038
+ void clearAllConversationIds();
2039
+
1785
2040
  return { conversations: convCount, messages: msgCount };
1786
2041
  }
1787
2042
 
@@ -1874,6 +2129,29 @@ interface WipeConversationResult extends DeletedMemoryIds {
1874
2129
  cancelledJobCount: number;
1875
2130
  }
1876
2131
 
2132
+ /**
2133
+ * Reserve an empty message row so the agent loop can stamp outbound
2134
+ * streaming events with a stable identity before content is produced.
2135
+ *
2136
+ * Intentionally skips Qdrant indexing and attention projection — an empty
2137
+ * placeholder is meaningless for either. The caller writes final content
2138
+ * via {@link updateMessageContent} and handles indexing/projection itself.
2139
+ *
2140
+ * Delegates the core insert + retry logic to {@link insertMessageCore}.
2141
+ */
2142
+ export async function reserveMessage(
2143
+ conversationId: string,
2144
+ role: MessageRole,
2145
+ metadata?: Record<string, unknown>,
2146
+ ) {
2147
+ return insertMessageCore({
2148
+ conversationId,
2149
+ role,
2150
+ content: "[]",
2151
+ metadata,
2152
+ });
2153
+ }
2154
+
1877
2155
  /**
1878
2156
  * Update the content of an existing message. Used when consolidating
1879
2157
  * multiple assistant messages into one.
@@ -1910,44 +2188,6 @@ export function updateMessageMetadata(
1910
2188
  .run();
1911
2189
  }
1912
2190
 
1913
- /**
1914
- * Bulk-remove the metadata fields that back the blocks stripped by
1915
- * `stripInjectionsForCompaction` — currently `pkbSystemReminderBlock`
1916
- * (`<system_reminder>`), `nowScratchpadBlock` (`<NOW.md …>`),
1917
- * `pkbContextBlock` (`<knowledge_base>`), and `memoryV2StaticBlock`
1918
- * (the static `<memory>\n…</memory>` block matched by the `<memory>\n`
1919
- * prefix in `RUNTIME_INJECTION_PREFIXES`). Called from compaction-strip
1920
- * sites so post-restart rehydration stays consistent with the in-memory
1921
- * state produced by `stripInjectionsForCompaction` (which removes those
1922
- * tags from live messages but cannot touch the DB). Fields backing
1923
- * blocks that are intentionally NOT stripped (`turnContextBlock`,
1924
- * `workspaceBlock`, `memoryInjectedBlock`) are preserved.
1925
- */
1926
- export function clearStrippedInjectionMetadataForConversation(
1927
- conversationId: string,
1928
- ): void {
1929
- rawRun(
1930
- `UPDATE messages
1931
- SET metadata = json_remove(
1932
- metadata,
1933
- '$.pkbSystemReminderBlock',
1934
- '$.nowScratchpadBlock',
1935
- '$.pkbContextBlock',
1936
- '$.memoryV2StaticBlock'
1937
- )
1938
- WHERE conversation_id = ?
1939
- AND role = 'user'
1940
- AND metadata IS NOT NULL
1941
- AND (
1942
- json_extract(metadata, '$.pkbSystemReminderBlock') IS NOT NULL
1943
- OR json_extract(metadata, '$.nowScratchpadBlock') IS NOT NULL
1944
- OR json_extract(metadata, '$.pkbContextBlock') IS NOT NULL
1945
- OR json_extract(metadata, '$.memoryV2StaticBlock') IS NOT NULL
1946
- )`,
1947
- conversationId,
1948
- );
1949
- }
1950
-
1951
2191
  /**
1952
2192
  * Atomically update both `content` and (shallow-merged) `metadata` for a
1953
2193
  * message. Used by edit-propagation paths that need to update the message
@@ -2185,7 +2425,7 @@ export function batchSetDisplayOrders(
2185
2425
  updates: Array<{
2186
2426
  id: string;
2187
2427
  displayOrder: number | null;
2188
- isPinned: boolean;
2428
+ isPinned?: boolean;
2189
2429
  groupId?: string | null;
2190
2430
  }>,
2191
2431
  ): void {
@@ -2218,35 +2458,36 @@ export function batchSetDisplayOrders(
2218
2458
  safeGroupId,
2219
2459
  update.id,
2220
2460
  );
2461
+ } else if (update.isPinned === undefined) {
2462
+ // Only displayOrder provided — preserve existing pin state and group.
2463
+ rawRun(
2464
+ "UPDATE conversations SET display_order = ? WHERE id = ?",
2465
+ update.displayOrder,
2466
+ update.id,
2467
+ );
2468
+ } else if (update.isPinned) {
2469
+ rawRun(
2470
+ "UPDATE conversations SET display_order = ?, is_pinned = 1, group_id = 'system:pinned' WHERE id = ?",
2471
+ update.displayOrder,
2472
+ update.id,
2473
+ );
2221
2474
  } else {
2222
- // Old client: no groupId in payload
2223
- // isPinned true -> set group_id = system:pinned
2224
- // isPinned false -> clear group_id ONLY IF currently system:pinned
2225
- // otherwise preserve existing group_id
2226
- if (update.isPinned) {
2227
- rawRun(
2228
- "UPDATE conversations SET display_order = ?, is_pinned = 1, group_id = 'system:pinned' WHERE id = ?",
2229
- update.displayOrder,
2230
- update.id,
2231
- );
2232
- } else {
2233
- // Restore system group from source/conversationType when old clients
2234
- // unpin, instead of clearing to NULL (which would lose provenance).
2235
- rawRun(
2236
- `UPDATE conversations SET display_order = ?, is_pinned = 0,
2237
- group_id = CASE WHEN group_id = 'system:pinned' THEN
2238
- CASE
2239
- WHEN source IN ('schedule', 'reminder') THEN 'system:scheduled'
2240
- WHEN source IN ('heartbeat', 'task') THEN 'system:background'
2241
- WHEN conversation_type = 'background' AND COALESCE(source, '') != 'notification' THEN 'system:background'
2242
- ELSE 'system:all'
2243
- END
2244
- ELSE group_id END
2245
- WHERE id = ?`,
2246
- update.displayOrder,
2247
- update.id,
2248
- );
2249
- }
2475
+ // Restore system group from source/conversationType when unpinning,
2476
+ // instead of clearing to NULL (which would lose provenance).
2477
+ rawRun(
2478
+ `UPDATE conversations SET display_order = ?, is_pinned = 0,
2479
+ group_id = CASE WHEN group_id = 'system:pinned' THEN
2480
+ CASE
2481
+ WHEN source IN ('schedule', 'reminder') THEN 'system:scheduled'
2482
+ WHEN source IN ('heartbeat', 'task') THEN 'system:background'
2483
+ WHEN conversation_type = 'background' AND COALESCE(source, '') != 'notification' THEN 'system:background'
2484
+ ELSE 'system:all'
2485
+ END
2486
+ ELSE group_id END
2487
+ WHERE id = ?`,
2488
+ update.displayOrder,
2489
+ update.id,
2490
+ );
2250
2491
  }
2251
2492
  }
2252
2493
  rawExec("COMMIT");