@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
@@ -16,7 +16,7 @@ import {
16
16
  getMessages,
17
17
  type MessageRow,
18
18
  } from "../memory/conversation-crud.js";
19
- import { enqueueMemoryJob } from "../memory/jobs-store.js";
19
+ import { enqueueMemoryJob, isMemoryEnabled } from "../memory/jobs-store.js";
20
20
  import { enqueueMemoryRetrospectiveIfEnabled } from "../memory/memory-retrospective-enqueue.js";
21
21
  import { shouldExposePersonalMemory } from "../memory/v2/static-context.js";
22
22
  import type { PermissionPrompter } from "../permissions/prompter.js";
@@ -189,17 +189,18 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
189
189
  ctx.contextCompactedAt = conv?.contextCompactedAt ?? null;
190
190
  }
191
191
 
192
- // `/clean` persists a timestamp; messages older than this should skip
193
- // metadata rehydration and have any injection prefixes still embedded in
194
- // their content stripped, so the cleaned state survives reload and forks.
195
- const cleanedAt = conv?.cleanedAt ?? null;
192
+ // Every injection-strip event (`/clean` or compaction) updates
193
+ // `historyStrippedAt`. Messages older than this should skip metadata
194
+ // rehydration and have any injection prefixes still embedded in their
195
+ // content stripped, so the post-strip view survives reload and forks.
196
+ const historyStrippedAt = conv?.historyStrippedAt ?? null;
196
197
  const slicedDbMessages = dbMessages.slice(ctx.contextCompactedMessageCount);
197
- let preCleanCount = 0;
198
- if (cleanedAt != null) {
198
+ let preStrippedCount = 0;
199
+ if (historyStrippedAt != null) {
199
200
  const boundary = slicedDbMessages.findIndex(
200
- (m) => m.createdAt >= cleanedAt,
201
+ (m) => m.createdAt >= historyStrippedAt,
201
202
  );
202
- preCleanCount = boundary === -1 ? slicedDbMessages.length : boundary;
203
+ preStrippedCount = boundary === -1 ? slicedDbMessages.length : boundary;
203
204
  }
204
205
 
205
206
  // Mirror the injection-time gate (`shouldExposePersonalMemory` in
@@ -213,7 +214,7 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
213
214
  isTrustedActor: resolveTrustClass(ctx.trustContext) === "guardian",
214
215
  });
215
216
  const parsedMessages: Message[] = slicedDbMessages.map((m, index, arr) => {
216
- const isPreClean = index < preCleanCount;
217
+ const isPreStripped = index < preStrippedCount;
217
218
  const role = m.role as "user" | "assistant";
218
219
  let content: ContentBlock[];
219
220
  try {
@@ -233,7 +234,7 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
233
234
 
234
235
  // Re-inject persisted injection blocks from metadata so it survives
235
236
  // conversation reloads (eviction, restart, fork).
236
- if (role === "user" && m.metadata && !isPreClean) {
237
+ if (role === "user" && m.metadata && !isPreStripped) {
237
238
  try {
238
239
  const meta = JSON.parse(m.metadata);
239
240
  const isTail = index === arr.length - 1;
@@ -244,9 +245,12 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
244
245
  // (pkb-context 30, pkb-reminder 35, memory-v2-static 38,
245
246
  // now-md 40 — the v2 static block lands inside the memory
246
247
  // prefix, so now-md splices *after* it):
247
- // [<workspace>, <turn_context>, <memory __injected>,
248
- // <memory>\n…</memory>, <NOW.md>, <system_reminder>,
248
+ // [<workspace>, <turn_context>, <memory>dynamic</memory>,
249
+ // <info>v2static</info>, <NOW.md>, <system_reminder>,
249
250
  // <knowledge_base>, ...original]
251
+ // The v2 static block is replayed verbatim from stored metadata,
252
+ // so rows may carry either `<info>…</info>` or `<memory>…</memory>`
253
+ // depending on when they were persisted.
250
254
  // Required so Anthropic's prefix cache keeps matching msg[0]
251
255
  // across daemon restart and conversation eviction. The tail
252
256
  // row only rehydrates `memoryInjectedBlock` — the next turn
@@ -273,11 +277,12 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
273
277
  }
274
278
 
275
279
  // The v2 static memory block (essentials/threads/recent/buffer
276
- // wrapped in `<memory>…</memory>`) carries personal user memory.
277
- // Trust-gated to mirror `shouldExposePersonalMemory` at injection
278
- // time — untrusted-actor views must not read persisted personal
279
- // memory back through metadata. Skipped on the tail row because
280
- // the next turn re-injects fresh content on full-mode turns.
280
+ // wrapped in either `<info>…</info>` or `<memory>…</memory>`)
281
+ // carries personal user memory. Trust-gated to mirror
282
+ // `shouldExposePersonalMemory` at injection time — untrusted-actor
283
+ // views must not read persisted personal memory back through
284
+ // metadata. Skipped on the tail row because the next turn
285
+ // re-injects fresh content on full-mode turns.
281
286
  if (
282
287
  !isTail &&
283
288
  personalMemoryAllowed &&
@@ -336,13 +341,13 @@ export async function loadFromDb(ctx: LoadFromDbContext): Promise<void> {
336
341
  // Strip pre-clean messages only; post-clean messages keep the fresh
337
342
  // injections they were generated with.
338
343
  const messagesBeforeRepair =
339
- preCleanCount === 0
344
+ preStrippedCount === 0
340
345
  ? parsedMessages
341
346
  : [
342
347
  ...stripInjectionsForCompaction(
343
- parsedMessages.slice(0, preCleanCount),
348
+ parsedMessages.slice(0, preStrippedCount),
344
349
  ),
345
- ...parsedMessages.slice(preCleanCount),
350
+ ...parsedMessages.slice(preStrippedCount),
346
351
  ];
347
352
 
348
353
  const { messages: repairedMessages, stats } =
@@ -448,7 +453,7 @@ export function disposeConversation(ctx: DisposeContext): void {
448
453
  } catch {
449
454
  // Best-effort — fall through to legacy v1 enqueue
450
455
  }
451
- if (!v2Enabled) {
456
+ if (!v2Enabled && isMemoryEnabled()) {
452
457
  try {
453
458
  enqueueMemoryJob("graph_extract", {
454
459
  conversationId: ctx.conversationId,
@@ -26,6 +26,7 @@ import {
26
26
  } from "../memory/attachments-store.js";
27
27
  import {
28
28
  addMessage,
29
+ extractImageSourcePaths,
29
30
  getConversation,
30
31
  provenanceFromTrustContext,
31
32
  setConversationOriginChannelIfUnset,
@@ -284,22 +285,44 @@ export function buildSlackMetaForPersistence(params: {
284
285
  return writeSlackMetadata(slackMeta);
285
286
  }
286
287
 
288
+ // ── EnqueueMessageOptions ────────────────────────────────────────────
289
+
290
+ /** Options for `enqueueMessage`. Only `content` is required; everything
291
+ * else has a sensible default or is genuinely optional. */
292
+ export interface EnqueueMessageOptions {
293
+ content: string;
294
+ attachments?: UserMessageAttachment[];
295
+ onEvent?: (msg: ServerMessage) => void;
296
+ requestId?: string;
297
+ activeSurfaceId?: string;
298
+ currentPage?: string;
299
+ metadata?: Record<string, unknown>;
300
+ isInteractive?: boolean;
301
+ displayContent?: string;
302
+ transport?: ConversationTransportMetadata;
303
+ clientMessageId?: string;
304
+ }
305
+
287
306
  // ── enqueueMessage ───────────────────────────────────────────────────
288
307
 
289
308
  export function enqueueMessage(
290
309
  ctx: MessagingConversationContext,
291
- content: string,
292
- attachments: UserMessageAttachment[],
293
- onEvent: (msg: ServerMessage) => void,
294
- requestId: string,
295
- activeSurfaceId?: string,
296
- currentPage?: string,
297
- metadata?: Record<string, unknown>,
298
- options?: { isInteractive?: boolean },
299
- displayContent?: string,
300
- transport?: ConversationTransportMetadata,
301
- clientMessageId?: string,
310
+ options: EnqueueMessageOptions,
302
311
  ): { queued: boolean; requestId: string; rejected?: boolean } {
312
+ const {
313
+ content,
314
+ attachments = [],
315
+ onEvent,
316
+ requestId = crypto.randomUUID(),
317
+ activeSurfaceId,
318
+ currentPage,
319
+ metadata,
320
+ isInteractive,
321
+ displayContent,
322
+ transport,
323
+ clientMessageId,
324
+ } = options;
325
+
303
326
  if (!ctx.processing) {
304
327
  return { queued: false, requestId };
305
328
  }
@@ -316,20 +339,20 @@ export function enqueueMessage(
316
339
  content,
317
340
  attachments,
318
341
  requestId,
319
- onEvent,
342
+ onEvent: onEvent ?? (() => {}),
320
343
  activeSurfaceId,
321
344
  currentPage,
322
345
  metadata,
323
346
  turnChannelContext,
324
347
  turnInterfaceContext,
325
- isInteractive: options?.isInteractive,
348
+ isInteractive,
326
349
  transport,
327
350
  displayContent,
328
351
  sentAt: Date.now(),
329
352
  clientMessageId,
330
353
  });
331
354
  if (!accepted) {
332
- onEvent({
355
+ onEvent?.({
333
356
  type: "error",
334
357
  conversationId: ctx.conversationId,
335
358
  message:
@@ -341,16 +364,26 @@ export function enqueueMessage(
341
364
  return { queued: true, requestId };
342
365
  }
343
366
 
367
+ // ── PersistMessageOptions ────────────────────────────────────────────
368
+
369
+ /** Shared options for `persistUserMessage` and `persistQueuedMessageBody`. */
370
+ export interface PersistMessageOptions {
371
+ content: string;
372
+ attachments?: UserMessageAttachment[];
373
+ requestId?: string;
374
+ metadata?: Record<string, unknown>;
375
+ displayContent?: string;
376
+ clientMessageId?: string;
377
+ }
378
+
344
379
  // ── persistUserMessage ───────────────────────────────────────────────
345
380
 
346
381
  export async function persistUserMessage(
347
382
  ctx: MessagingConversationContext,
348
- content: string,
349
- attachments: UserMessageAttachment[],
350
- requestId?: string,
351
- metadata?: Record<string, unknown>,
352
- displayContent?: string,
353
- ): Promise<string> {
383
+ options: PersistMessageOptions,
384
+ ): Promise<{ id: string; deduplicated: boolean }> {
385
+ const { content, attachments = [] } = options;
386
+
354
387
  if (ctx.processing) {
355
388
  throw new Error("Conversation is already processing a message");
356
389
  }
@@ -359,20 +392,23 @@ export async function persistUserMessage(
359
392
  throw new Error("Message content or attachments are required");
360
393
  }
361
394
 
362
- const reqId = requestId ?? uuid();
395
+ const reqId = options.requestId ?? uuid();
363
396
  ctx.currentRequestId = reqId;
364
397
  ctx.processing = true;
365
398
  ctx.abortController = new AbortController();
366
399
 
367
400
  try {
368
- return await persistQueuedMessageBody(
369
- ctx,
370
- content,
401
+ const result = await persistQueuedMessageBody(ctx, {
402
+ ...options,
371
403
  attachments,
372
- reqId,
373
- metadata,
374
- displayContent,
375
- );
404
+ requestId: reqId,
405
+ });
406
+ if (result.deduplicated) {
407
+ ctx.processing = false;
408
+ ctx.abortController = null;
409
+ ctx.currentRequestId = undefined;
410
+ }
411
+ return result;
376
412
  } catch (err) {
377
413
  ctx.processing = false;
378
414
  ctx.abortController = null;
@@ -394,12 +430,16 @@ export async function persistUserMessage(
394
430
  */
395
431
  export async function persistQueuedMessageBody(
396
432
  ctx: MessagingConversationContext,
397
- content: string,
398
- attachments: UserMessageAttachment[],
399
- requestId: string,
400
- metadata: Record<string, unknown> | undefined,
401
- displayContent: string | undefined,
402
- ): Promise<string> {
433
+ options: PersistMessageOptions,
434
+ ): Promise<{ id: string; deduplicated: boolean }> {
435
+ const {
436
+ content,
437
+ attachments = [],
438
+ requestId = uuid(),
439
+ metadata,
440
+ displayContent,
441
+ clientMessageId,
442
+ } = options;
403
443
  const attachmentInputs = attachments.map((attachment) => ({
404
444
  id: attachment.id,
405
445
  filename: attachment.filename,
@@ -431,13 +471,7 @@ export async function persistQueuedMessageBody(
431
471
  const turnIfCtx =
432
472
  extractTurnInterfaceContext(metadata) ?? ctx.getTurnInterfaceContext();
433
473
  const provenance = provenanceFromTrustContext(ctx.trustContext);
434
- const imageSourcePaths: Record<string, string> = {};
435
- for (let i = 0; i < attachments.length; i++) {
436
- const a = attachments[i];
437
- if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
438
- imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
439
- }
440
- }
474
+ const imageSourcePaths = extractImageSourcePaths(attachments);
441
475
 
442
476
  // Strip the transient `slackInbound` carrier key from the persisted
443
477
  // metadata — it's an in-memory plumbing field, not a stored column value.
@@ -468,7 +502,7 @@ export async function persistQueuedMessageBody(
468
502
  assistantMessageInterface: turnIfCtx.assistantMessageInterface,
469
503
  }
470
504
  : {}),
471
- ...(Object.keys(imageSourcePaths).length > 0 ? { imageSourcePaths } : {}),
505
+ ...(imageSourcePaths ? { imageSourcePaths } : {}),
472
506
  ...(slackMeta ? { slackMeta } : {}),
473
507
  };
474
508
 
@@ -486,8 +520,15 @@ export async function persistQueuedMessageBody(
486
520
  "user",
487
521
  contentToPersist,
488
522
  mergedMetadata,
523
+ undefined,
524
+ clientMessageId,
489
525
  );
490
526
 
527
+ if (persistedUserMessage.deduplicated) {
528
+ ctx.messages.pop();
529
+ return { id: persistedUserMessage.id, deduplicated: true };
530
+ }
531
+
491
532
  if (turnCtx) {
492
533
  setConversationOriginChannelIfUnset(
493
534
  ctx.conversationId,
@@ -569,7 +610,7 @@ export async function persistQueuedMessageBody(
569
610
  );
570
611
  }
571
612
 
572
- return persistedUserMessage.id;
613
+ return { id: persistedUserMessage.id, deduplicated: false };
573
614
  } catch (err) {
574
615
  ctx.messages.pop();
575
616
  throw err;
@@ -34,6 +34,7 @@ import { routeGuardianReply } from "../runtime/guardian-reply-router.js";
34
34
  import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
35
35
  import { getLogger } from "../util/logger.js";
36
36
  import type { CleanResult } from "./conversation.js";
37
+ import type { PersistMessageOptions } from "./conversation-messaging.js";
37
38
  import {
38
39
  persistQueuedMessageBody,
39
40
  serializePersistedUserMessageContent,
@@ -158,12 +159,8 @@ export interface ProcessConversationContext {
158
159
  currentTurnChannelCapabilities?: ChannelCapabilities;
159
160
  ensureActorScopedHistory(): Promise<void>;
160
161
  persistUserMessage(
161
- content: string,
162
- attachments: UserMessageAttachment[],
163
- requestId?: string,
164
- metadata?: Record<string, unknown>,
165
- displayContent?: string,
166
- ): Promise<string>;
162
+ options: PersistMessageOptions,
163
+ ): Promise<{ id: string; deduplicated: boolean }>;
167
164
  runAgentLoop(
168
165
  content: string,
169
166
  userMessageId: string,
@@ -921,15 +918,16 @@ async function drainSingleMessage(
921
918
  // succeeds, runAgentLoop is called and its finally block will drain
922
919
  // the next message. If persistUserMessage fails, processMessage
923
920
  // resolves early (no runAgentLoop call), so we must continue draining.
924
- let userMessageId: string;
921
+ let persistResult: { id: string; deduplicated: boolean };
925
922
  try {
926
- userMessageId = await conversation.persistUserMessage(
927
- resolvedContent,
928
- next.attachments,
929
- next.requestId,
930
- { ...next.metadata, sentAt: next.sentAt },
931
- next.displayContent,
932
- );
923
+ persistResult = await conversation.persistUserMessage({
924
+ content: resolvedContent,
925
+ attachments: next.attachments,
926
+ requestId: next.requestId,
927
+ metadata: { ...next.metadata, sentAt: next.sentAt },
928
+ displayContent: next.displayContent,
929
+ clientMessageId: next.clientMessageId,
930
+ });
933
931
  } catch (err) {
934
932
  const message = err instanceof Error ? err.message : String(err);
935
933
  log.error(
@@ -961,6 +959,18 @@ async function drainSingleMessage(
961
959
  return;
962
960
  }
963
961
 
962
+ const userMessageId = persistResult.id;
963
+
964
+ if (persistResult.deduplicated) {
965
+ log.info(
966
+ { conversationId: conversation.conversationId, userMessageId },
967
+ "Skipping agent loop for deduplicated queued message",
968
+ );
969
+ conversation.preactivatedSkillIds = undefined;
970
+ await drainQueue(conversation);
971
+ return;
972
+ }
973
+
964
974
  // Broadcast the user message to all hub subscribers so passive devices
965
975
  // see the user turn before the assistant reply starts streaming.
966
976
  next.onEvent({
@@ -1217,24 +1227,43 @@ async function drainBatch(
1217
1227
  const qmContent = qmSlash.content;
1218
1228
 
1219
1229
  try {
1230
+ let batchPersistResult: { id: string; deduplicated: boolean };
1231
+ const persistOptions = {
1232
+ content: qmContent,
1233
+ attachments: qm.attachments,
1234
+ requestId: qm.requestId,
1235
+ metadata: { ...qm.metadata, sentAt: qm.sentAt },
1236
+ displayContent: qm.displayContent,
1237
+ clientMessageId: qm.clientMessageId,
1238
+ };
1220
1239
  if (i === 0) {
1221
- lastUserMessageId = await conversation.persistUserMessage(
1222
- qmContent,
1223
- qm.attachments,
1224
- qm.requestId,
1225
- { ...qm.metadata, sentAt: qm.sentAt },
1226
- qm.displayContent,
1227
- );
1240
+ batchPersistResult =
1241
+ await conversation.persistUserMessage(persistOptions);
1228
1242
  } else {
1229
- lastUserMessageId = await persistQueuedMessageBody(
1243
+ batchPersistResult = await persistQueuedMessageBody(
1230
1244
  conversation,
1231
- qmContent,
1232
- qm.attachments,
1233
- qm.requestId,
1234
- { ...qm.metadata, sentAt: qm.sentAt },
1235
- qm.displayContent,
1245
+ persistOptions,
1236
1246
  );
1237
1247
  }
1248
+ if (batchPersistResult.deduplicated) {
1249
+ if (i === 0) {
1250
+ // Head was deduplicated — persistUserMessage cleared the
1251
+ // processing flag. Recursively drain remaining items so the
1252
+ // first non-duplicate becomes the new batch head and sets
1253
+ // processing via persistUserMessage.
1254
+ const remaining = batch.slice(1);
1255
+ if (remaining.length >= 2) {
1256
+ await drainBatch(conversation, remaining, reason);
1257
+ } else if (remaining.length === 1) {
1258
+ await drainSingleMessage(conversation, remaining[0], reason);
1259
+ } else {
1260
+ await drainQueue(conversation);
1261
+ }
1262
+ return;
1263
+ }
1264
+ continue;
1265
+ }
1266
+ lastUserMessageId = batchPersistResult.id;
1238
1267
  } catch (err) {
1239
1268
  const message = err instanceof Error ? err.message : String(err);
1240
1269
  log.error(
@@ -1377,12 +1406,19 @@ async function drainBatch(
1377
1406
  conversation.currentActiveSurfaceId = lastSuccessfulActiveSurfaceId;
1378
1407
  conversation.currentPage = lastSuccessfulCurrentPage;
1379
1408
 
1380
- // Broadcast agent-loop events only to members whose persist succeeded.
1381
- // Members whose persist failed already received an error event in the
1382
- // catch block above; sending them the assistant's streaming response
1383
- // would surface a reply for a user message that isn't in their DB.
1409
+ // Broadcast agent-loop events only to unique sinks whose persist succeeded.
1410
+ // Multiple web-queued messages share the same broadcastMessage callback; if
1411
+ // we call it once per queued message, every text delta is published N times
1412
+ // to the same SSE stream and the client renders duplicated text.
1413
+ //
1414
+ // Members whose persist failed already received an error event in the catch
1415
+ // block above; sending them the assistant's streaming response would surface
1416
+ // a reply for a user message that isn't in their DB.
1417
+ const successfulEventSinks = Array.from(
1418
+ new Set(successfulBatch.map((qm) => qm.onEvent)),
1419
+ );
1384
1420
  const fanOutOnEvent = (msg: ServerMessage) => {
1385
- for (const qm of successfulBatch) qm.onEvent(msg);
1421
+ for (const onEvent of successfulEventSinks) onEvent(msg);
1386
1422
  };
1387
1423
 
1388
1424
  const drainLoopOptions: {
@@ -1857,15 +1893,14 @@ export async function processMessage(
1857
1893
  }
1858
1894
  }
1859
1895
 
1860
- let userMessageId: string;
1896
+ let pmResult: { id: string; deduplicated: boolean };
1861
1897
  try {
1862
- userMessageId = await conversation.persistUserMessage(
1863
- resolvedContent,
1898
+ pmResult = await conversation.persistUserMessage({
1899
+ content: resolvedContent,
1864
1900
  attachments,
1865
1901
  requestId,
1866
- undefined,
1867
1902
  displayContent,
1868
- );
1903
+ });
1869
1904
  publishConversationMessagesChanged(conversation.conversationId);
1870
1905
  } catch (err) {
1871
1906
  const message = err instanceof Error ? err.message : String(err);
@@ -1879,6 +1914,8 @@ export async function processMessage(
1879
1914
  return "";
1880
1915
  }
1881
1916
 
1917
+ const userMessageId = pmResult.id;
1918
+
1882
1919
  // Fire-and-forget: detect notification preferences in the user message
1883
1920
  // and persist any that are found. Runs in the background so it doesn't
1884
1921
  // block the main conversation flow.
@@ -997,23 +997,38 @@ export function buildUnifiedTurnContextBlock(
997
997
  // ---------------------------------------------------------------------------
998
998
 
999
999
  /**
1000
- * Remove text blocks from user messages whose text starts with any of the
1001
- * given prefixes. If stripping removes all content blocks from a message,
1002
- * the message itself is dropped.
1000
+ * A matcher for an injected text block. A plain string matches by prefix
1001
+ * (`startsWith`). A `{ prefix, suffix }` wrapper requires BOTH the opening
1002
+ * prefix and the closing suffix, so user-authored content that merely begins
1003
+ * with an injection-like opening tag (e.g. a message discussing `<info>`
1004
+ * markup) is not mistaken for an injected block and dropped. This mirrors
1005
+ * `countMemoryPrefixBlocks`, which only treats `<memory>…</memory>` /
1006
+ * `<info>…</info>` blocks as injected when the full wrapper is present.
1007
+ */
1008
+ type InjectionMatcher = string | { prefix: string; suffix: string };
1009
+
1010
+ /**
1011
+ * Remove text blocks from user messages that match any of the given matchers.
1012
+ * If stripping removes all content blocks from a message, the message itself
1013
+ * is dropped.
1003
1014
  *
1004
1015
  * This is the shared primitive behind the individual strip* functions and
1005
1016
  * the `stripInjectionsForCompaction` pipeline.
1006
1017
  */
1007
1018
  function stripUserTextBlocksByPrefix(
1008
1019
  messages: Message[],
1009
- prefixes: string[],
1020
+ matchers: InjectionMatcher[],
1010
1021
  ): Message[] {
1011
1022
  return messages
1012
1023
  .map((message) => {
1013
1024
  if (message.role !== "user") return message;
1014
1025
  const nextContent = message.content.filter((block) => {
1015
1026
  if (block.type !== "text") return true;
1016
- return !prefixes.some((p) => block.text.startsWith(p));
1027
+ return !matchers.some((m) =>
1028
+ typeof m === "string"
1029
+ ? block.text.startsWith(m)
1030
+ : block.text.startsWith(m.prefix) && block.text.endsWith(m.suffix),
1031
+ );
1017
1032
  });
1018
1033
  if (nextContent.length === message.content.length) return message;
1019
1034
  if (nextContent.length === 0) return null;
@@ -1720,8 +1735,8 @@ export function loadSlackActiveThreadFocusBlock(
1720
1735
  return assembleSlackActiveThreadFocusBlock(rows, capabilities);
1721
1736
  }
1722
1737
 
1723
- /** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
1724
- const RUNTIME_INJECTION_PREFIXES = [
1738
+ /** Matchers stripped by the pipeline (order doesn't matter — single pass). */
1739
+ const RUNTIME_INJECTION_PREFIXES: InjectionMatcher[] = [
1725
1740
  "<channel_capabilities>",
1726
1741
  "<channel_command_context>",
1727
1742
  "<disk_pressure_warning>",
@@ -1734,15 +1749,21 @@ const RUNTIME_INJECTION_PREFIXES = [
1734
1749
  "<background_turn>",
1735
1750
  "<memory_context __injected>",
1736
1751
  "<memory_context>", // backward-compat: strip legacy blocks from pre-__injected history
1737
- // The static `memory-v2-static` block (opens `<memory>\n…`) IS stripped
1738
- // so each compaction re-injects the freshest essentials/threads/recent/
1739
- // buffer view, matching the `<knowledge_base>` cadence. The dynamic
1740
- // activation block (opens `<memory __injected>…`) is intentionally NOT
1741
- // stripped `startsWith("<memory>\n")` does not match it — so per-turn
1742
- // memory activations persist in history. The activation pipeline dedupes
1743
- // via `everInjected`, and compaction handles aggregate growth, so
1744
- // accumulation does not cause unbounded context growth.
1745
- "<memory>\n",
1752
+ // The static `memory-v2-static` block (`<info>\n…</info>`) and the
1753
+ // dynamic activation block (`<memory>\n…</memory>`, plus legacy
1754
+ // `<memory __injected>…`) are both stripped so each compaction
1755
+ // re-injects the freshest essentials/threads/recent/buffer view and
1756
+ // re-runs the activation pipeline, matching the `<knowledge_base>`
1757
+ // cadence. The activation pipeline dedupes via `everInjected`, and
1758
+ // compaction handles aggregate growth, so accumulation does not cause
1759
+ // unbounded context growth. Both wrappers may appear in persisted rows.
1760
+ //
1761
+ // These two use the full `{ prefix, suffix }` wrapper shape (not a bare
1762
+ // prefix) so that user-authored text merely starting with `<memory>\n` or
1763
+ // `<info>\n` is never silently dropped during compaction/`/clean`. This
1764
+ // matches the full-wrapper requirement in `countMemoryPrefixBlocks`.
1765
+ { prefix: "<memory>\n", suffix: "\n</memory>" },
1766
+ { prefix: "<info>\n", suffix: "\n</info>" },
1746
1767
  "<voice_call_control>",
1747
1768
  "<workspace_top_level>", // backward-compat: strip legacy workspace blocks
1748
1769
  // NOTE: <workspace> is intentionally NOT stripped — workspace context
@@ -2037,7 +2058,7 @@ export interface RuntimeInjectionOptions {
2037
2058
  /**
2038
2059
  * Pre-rendered v2 static memory content (essentials/threads/recent/buffer
2039
2060
  * concatenated, header-wrapped). When non-null on full-mode turns the
2040
- * `memory-v2-static` injector wraps it in `<memory>` and splices it onto
2061
+ * `memory-v2-static` injector wraps it in `<info>` and splices it onto
2041
2062
  * the user message; subsequent turns leave the prior block cached on its
2042
2063
  * original user message.
2043
2064
  */