@vellumai/assistant 0.8.5 → 0.8.7-dev.202606052118.34cd356

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 (1323) hide show
  1. package/AGENTS.md +33 -1
  2. package/ARCHITECTURE.md +1 -1
  3. package/Dockerfile +21 -4
  4. package/bun.lock +13 -4
  5. package/bunfig.toml +6 -1
  6. package/docker-entrypoint.sh +12 -8
  7. package/docker-init-apt-root.sh +3 -1
  8. package/docker-kata-apt-env.sh +3 -1
  9. package/docker-kata-runtime-family.sh +12 -0
  10. package/docs/architecture/memory.md +1 -1
  11. package/docs/credential-execution-service.md +6 -6
  12. package/docs/plugins.md +114 -86
  13. package/examples/plugins/echo/README.md +13 -12
  14. package/examples/plugins/echo/register.ts +0 -54
  15. package/knip.json +1 -0
  16. package/node_modules/@vellumai/environments/bun.lock +24 -0
  17. package/node_modules/@vellumai/environments/package.json +18 -0
  18. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
  19. package/node_modules/@vellumai/environments/src/index.ts +11 -0
  20. package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
  21. package/node_modules/@vellumai/environments/src/types.ts +70 -0
  22. package/node_modules/@vellumai/environments/tsconfig.json +20 -0
  23. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
  24. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +15 -17
  25. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  26. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +15 -7
  27. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
  28. package/openapi.yaml +8748 -589
  29. package/package.json +8 -4
  30. package/scripts/generate-openapi.ts +88 -54
  31. package/src/__tests__/actor-token-service.test.ts +3 -2
  32. package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
  33. package/src/__tests__/agent-loop-exit-reason.test.ts +284 -48
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
  35. package/src/__tests__/agent-loop-override-profile.test.ts +21 -33
  36. package/src/__tests__/agent-loop-provider-error-recording.test.ts +7 -5
  37. package/src/__tests__/agent-loop-thinking.test.ts +17 -12
  38. package/src/__tests__/agent-loop.test.ts +238 -422
  39. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +7 -2
  40. package/src/__tests__/agent-wake-override-profile.test.ts +23 -40
  41. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
  42. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  43. package/src/__tests__/annotate-risk-options.test.ts +3 -3
  44. package/src/__tests__/anthropic-provider.test.ts +296 -57
  45. package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
  46. package/src/__tests__/app-control-flow.test.ts +6 -1
  47. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  48. package/src/__tests__/approval-cascade.test.ts +5 -11
  49. package/src/__tests__/approval-routes-http.test.ts +17 -16
  50. package/src/__tests__/assert-not-live-db.ts +79 -0
  51. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  52. package/src/__tests__/assistant-event.test.ts +15 -0
  53. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  54. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -27
  55. package/src/__tests__/assistant-stream-state.test.ts +645 -0
  56. package/src/__tests__/audit-log-rotation.test.ts +2 -2
  57. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  58. package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
  59. package/src/__tests__/avatar-e2e.test.ts +7 -37
  60. package/src/__tests__/avatar-generator.test.ts +12 -42
  61. package/src/__tests__/avatar-identity-sync.test.ts +28 -3
  62. package/src/__tests__/background-shell-bash.test.ts +3 -7
  63. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -8
  64. package/src/__tests__/browser-skill-endstate.test.ts +3 -3
  65. package/src/__tests__/btw-routes.test.ts +72 -17
  66. package/src/__tests__/build-persisted-content.test.ts +184 -0
  67. package/src/__tests__/call-controller.test.ts +3 -2
  68. package/src/__tests__/call-pointer-messages.test.ts +5 -3
  69. package/src/__tests__/call-site-routing-provider.test.ts +22 -40
  70. package/src/__tests__/catalog-files.test.ts +1 -0
  71. package/src/__tests__/channel-approval-routes.test.ts +52 -23
  72. package/src/__tests__/channel-approvals.test.ts +4 -2
  73. package/src/__tests__/channel-guardian.test.ts +3 -2
  74. package/src/__tests__/channel-invite-transport.test.ts +1 -5
  75. package/src/__tests__/channel-readiness-routes.test.ts +0 -4
  76. package/src/__tests__/channel-readiness-slack-remote.test.ts +170 -0
  77. package/src/__tests__/channel-reply-delivery.test.ts +35 -0
  78. package/src/__tests__/channel-retry-sweep.test.ts +388 -79
  79. package/src/__tests__/checker.test.ts +12 -12
  80. package/src/__tests__/clawhub-files.test.ts +1 -0
  81. package/src/__tests__/compaction-circuit.test.ts +258 -0
  82. package/src/__tests__/compaction-direct.test.ts +132 -0
  83. package/src/__tests__/compaction-events.test.ts +6 -17
  84. package/src/__tests__/compaction-trail-store.test.ts +186 -0
  85. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  86. package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
  87. package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
  88. package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
  89. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
  90. package/src/__tests__/computer-use-tools.test.ts +14 -16
  91. package/src/__tests__/config-loader-backfill.test.ts +13 -28
  92. package/src/__tests__/config-loader-corrupt.test.ts +5 -5
  93. package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
  94. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
  95. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
  96. package/src/__tests__/config-schema.test.ts +10 -10
  97. package/src/__tests__/config-watcher.test.ts +28 -0
  98. package/src/__tests__/connection-model-compat.test.ts +83 -0
  99. package/src/__tests__/contacts-tools.test.ts +3 -2
  100. package/src/__tests__/context-search-agent-runner.test.ts +6 -3
  101. package/src/__tests__/context-token-estimator.test.ts +56 -0
  102. package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
  103. package/src/__tests__/conversation-abort-tool-results.test.ts +73 -23
  104. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +10 -7
  105. package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
  106. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +23 -34
  107. package/src/__tests__/conversation-agent-loop-overflow.test.ts +477 -930
  108. package/src/__tests__/conversation-agent-loop.test.ts +1056 -1068
  109. package/src/__tests__/conversation-analysis-routes.test.ts +8 -3
  110. package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
  111. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -1
  112. package/src/__tests__/conversation-attention-store.test.ts +101 -0
  113. package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
  114. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  115. package/src/__tests__/conversation-clear-safety.test.ts +20 -10
  116. package/src/__tests__/conversation-confirmation-signals.test.ts +16 -45
  117. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  118. package/src/__tests__/conversation-disk-view.test.ts +10 -17
  119. package/src/__tests__/conversation-error.test.ts +30 -0
  120. package/src/__tests__/conversation-fork-crud.test.ts +132 -157
  121. package/src/__tests__/conversation-fork-route.test.ts +19 -16
  122. package/src/__tests__/conversation-history-web-search.test.ts +12 -1
  123. package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
  124. package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
  125. package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
  126. package/src/__tests__/conversation-lifecycle.test.ts +4 -2
  127. package/src/__tests__/conversation-list-source.test.ts +3 -2
  128. package/src/__tests__/conversation-load-history-repair.test.ts +5 -3
  129. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -1
  130. package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
  131. package/src/__tests__/conversation-pairing.test.ts +60 -4
  132. package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
  133. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +36 -7
  134. package/src/__tests__/conversation-process-callsite.test.ts +28 -30
  135. package/src/__tests__/conversation-provider-retry-repair.test.ts +85 -51
  136. package/src/__tests__/conversation-queue.test.ts +605 -455
  137. package/src/__tests__/conversation-routes-disk-view.test.ts +9 -20
  138. package/src/__tests__/conversation-routes-guardian-reply.test.ts +35 -10
  139. package/src/__tests__/conversation-routes-slash-commands.test.ts +40 -8
  140. package/src/__tests__/conversation-runtime-assembly.test.ts +395 -313
  141. package/src/__tests__/conversation-runtime-workspace.test.ts +114 -36
  142. package/src/__tests__/conversation-skill-tools.test.ts +38 -142
  143. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  144. package/src/__tests__/conversation-slash-queue.test.ts +125 -62
  145. package/src/__tests__/conversation-slash-unknown.test.ts +18 -15
  146. package/src/__tests__/conversation-speed-override.test.ts +9 -22
  147. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  148. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +136 -15
  149. package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
  150. package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
  151. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
  152. package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
  153. package/src/__tests__/conversation-surfaces-state-update.test.ts +8 -5
  154. package/src/__tests__/conversation-surfaces-table-action.test.ts +13 -32
  155. package/src/__tests__/conversation-sync-tags.test.ts +140 -12
  156. package/src/__tests__/conversation-title-service.test.ts +136 -2
  157. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +53 -11
  158. package/src/__tests__/conversation-unread-route.test.ts +14 -2
  159. package/src/__tests__/conversation-usage.test.ts +1 -2
  160. package/src/__tests__/conversation-wipe.test.ts +1 -1
  161. package/src/__tests__/conversation-workspace-cache-state.test.ts +21 -17
  162. package/src/__tests__/conversation-workspace-injection.test.ts +119 -23
  163. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +39 -13
  164. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  165. package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
  166. package/src/__tests__/credential-broker-server-use.test.ts +5 -5
  167. package/src/__tests__/credential-execution-client.test.ts +72 -1
  168. package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
  169. package/src/__tests__/credential-execution-tools.test.ts +1 -2
  170. package/src/__tests__/credential-health-service.test.ts +252 -3
  171. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  172. package/src/__tests__/credential-vault-unit.test.ts +19 -19
  173. package/src/__tests__/credential-vault.test.ts +5 -5
  174. package/src/__tests__/cross-provider-web-search.test.ts +275 -4
  175. package/src/__tests__/cu-unified-flow.test.ts +26 -1
  176. package/src/__tests__/db-acp-history.test.ts +101 -0
  177. package/src/__tests__/db-connection-isolation.test.ts +7 -6
  178. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
  179. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
  180. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
  181. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -0
  182. package/src/__tests__/db-test-helpers.ts +58 -0
  183. package/src/__tests__/disk-pressure-guard.test.ts +119 -36
  184. package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
  185. package/src/__tests__/disk-pressure-routes.test.ts +9 -35
  186. package/src/__tests__/disk-pressure-tools.test.ts +0 -4
  187. package/src/__tests__/dm-persistence.test.ts +38 -43
  188. package/src/__tests__/document-create-dedupe.test.ts +189 -0
  189. package/src/__tests__/document-find-replace.test.ts +3 -2
  190. package/src/__tests__/document-tool-security.test.ts +81 -2
  191. package/src/__tests__/dynamic-page-surface.test.ts +99 -0
  192. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
  193. package/src/__tests__/edit-propagation.test.ts +1 -2
  194. package/src/__tests__/empty-response-hook.test.ts +304 -0
  195. package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
  196. package/src/__tests__/encrypted-store.test.ts +11 -9
  197. package/src/__tests__/feature-flag-test-helpers.ts +53 -0
  198. package/src/__tests__/file-write-tool.test.ts +63 -0
  199. package/src/__tests__/filing-service.test.ts +3 -2
  200. package/src/__tests__/first-greeting.test.ts +103 -12
  201. package/src/__tests__/gateway-flag-listener.test.ts +0 -1
  202. package/src/__tests__/gemini-image-service.test.ts +13 -0
  203. package/src/__tests__/gemini-inline-media.test.ts +78 -0
  204. package/src/__tests__/gemini-provider.test.ts +375 -26
  205. package/src/__tests__/guardian-action-sweep.test.ts +3 -2
  206. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  207. package/src/__tests__/guardian-outbound-http.test.ts +3 -2
  208. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  209. package/src/__tests__/guardian-routing-state.test.ts +60 -71
  210. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
  211. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +11 -8
  212. package/src/__tests__/heartbeat-disk-pressure.test.ts +3 -0
  213. package/src/__tests__/heartbeat-service.test.ts +4 -1
  214. package/src/__tests__/helpers/mock-logger.ts +26 -0
  215. package/src/__tests__/helpers/mock-provider.ts +110 -0
  216. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  217. package/src/__tests__/history-repair-hook.test.ts +162 -0
  218. package/src/__tests__/history-repair-observability.test.ts +1 -1
  219. package/src/__tests__/history-repair.test.ts +2 -1
  220. package/src/__tests__/host-app-control-proxy.test.ts +2 -0
  221. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  222. package/src/__tests__/host-bash-routes.test.ts +1 -0
  223. package/src/__tests__/host-cu-proxy.test.ts +2 -0
  224. package/src/__tests__/host-cu-routes-targeted.test.ts +4 -3
  225. package/src/__tests__/host-file-edit-tool.test.ts +4 -2
  226. package/src/__tests__/host-file-proxy.test.ts +31 -0
  227. package/src/__tests__/host-file-read-tool.test.ts +4 -2
  228. package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
  229. package/src/__tests__/host-file-write-tool.test.ts +9 -3
  230. package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
  231. package/src/__tests__/host-shell-tool.test.ts +11 -5
  232. package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
  233. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  234. package/src/__tests__/http-user-message-parity.test.ts +31 -9
  235. package/src/__tests__/identity-intro-cache.test.ts +140 -96
  236. package/src/__tests__/identity-routes.test.ts +248 -7
  237. package/src/__tests__/inbound-slack-persistence.test.ts +56 -75
  238. package/src/__tests__/inference-profile-reaper.test.ts +3 -2
  239. package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
  240. package/src/__tests__/injector-background-turn.test.ts +3 -9
  241. package/src/__tests__/injector-chain.test.ts +139 -275
  242. package/src/__tests__/injector-disk-pressure.test.ts +78 -58
  243. package/src/__tests__/injector-document-comments.test.ts +3 -3
  244. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  245. package/src/__tests__/injector-v3-suppression.test.ts +214 -0
  246. package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
  247. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  248. package/src/__tests__/list-messages-attachments.test.ts +7 -8
  249. package/src/__tests__/list-messages-hidden-metadata.test.ts +131 -11
  250. package/src/__tests__/list-messages-page-latest.test.ts +60 -1
  251. package/src/__tests__/list-messages-tool-merge.test.ts +56 -6
  252. package/src/__tests__/llm-context-normalization.test.ts +42 -0
  253. package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
  254. package/src/__tests__/llm-resolver.test.ts +346 -39
  255. package/src/__tests__/llm-schema.test.ts +1 -1
  256. package/src/__tests__/llm-usage-store.test.ts +268 -1
  257. package/src/__tests__/log-export-routes.test.ts +59 -0
  258. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
  259. package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
  260. package/src/__tests__/mcp-abort-signal.test.ts +14 -0
  261. package/src/__tests__/mcp-auth-routes.test.ts +15 -10
  262. package/src/__tests__/mcp-client-auth.test.ts +14 -0
  263. package/src/__tests__/mcp-health-check.test.ts +18 -13
  264. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  265. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  266. package/src/__tests__/messaging-send-tool.test.ts +9 -4
  267. package/src/__tests__/migration-export-http.test.ts +12 -12
  268. package/src/__tests__/migration-import-commit-http.test.ts +8 -8
  269. package/src/__tests__/migration-import-from-url.test.ts +3 -3
  270. package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
  271. package/src/__tests__/migration-validate-http.test.ts +3 -3
  272. package/src/__tests__/mock-gateway-ipc.ts +18 -2
  273. package/src/__tests__/model-intents.test.ts +3 -3
  274. package/src/__tests__/native-web-search.test.ts +235 -22
  275. package/src/__tests__/notification-decision-identity.test.ts +9 -18
  276. package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
  277. package/src/__tests__/notification-deep-link.test.ts +62 -0
  278. package/src/__tests__/oauth-commands-routes.test.ts +38 -1
  279. package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
  280. package/src/__tests__/oauth-store.test.ts +3 -2
  281. package/src/__tests__/onboarding-template-contract.test.ts +15 -2
  282. package/src/__tests__/openai-image-service.test.ts +17 -0
  283. package/src/__tests__/openai-provider.test.ts +105 -80
  284. package/src/__tests__/openai-responses-provider.test.ts +90 -86
  285. package/src/__tests__/openrouter-provider-only.test.ts +27 -5
  286. package/src/__tests__/outbound-slack-persistence.test.ts +48 -2
  287. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -286
  288. package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
  289. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  290. package/src/__tests__/persistence-secret-redaction.test.ts +86 -13
  291. package/src/__tests__/pipeline-runner.test.ts +31 -43
  292. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  293. package/src/__tests__/plugin-bootstrap.test.ts +72 -63
  294. package/src/__tests__/plugin-registry.test.ts +0 -27
  295. package/src/__tests__/plugin-route-contribution.test.ts +6 -16
  296. package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
  297. package/src/__tests__/plugin-tool-contribution.test.ts +51 -64
  298. package/src/__tests__/plugin-types.test.ts +8 -173
  299. package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
  300. package/src/__tests__/process-message-background-slack.test.ts +38 -32
  301. package/src/__tests__/process-message-display-content.test.ts +55 -66
  302. package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
  303. package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
  304. package/src/__tests__/provider-error-scenarios.test.ts +7 -6
  305. package/src/__tests__/provider-platform-proxy-integration.test.ts +215 -8
  306. package/src/__tests__/provider-registry-ollama.test.ts +45 -22
  307. package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
  308. package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
  309. package/src/__tests__/provider-usage-tracking.test.ts +0 -6
  310. package/src/__tests__/ratelimit.test.ts +9 -4
  311. package/src/__tests__/reaction-persistence.test.ts +1 -1
  312. package/src/__tests__/recording-handler.test.ts +1 -0
  313. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +6 -1
  314. package/src/__tests__/registry.test.ts +82 -76
  315. package/src/__tests__/relay-server.test.ts +30 -23
  316. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  317. package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
  318. package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
  319. package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
  320. package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
  321. package/src/__tests__/runtime-events-sse-reconnect.test.ts +390 -0
  322. package/src/__tests__/schedule-routes.test.ts +683 -12
  323. package/src/__tests__/schedule-store.test.ts +124 -1
  324. package/src/__tests__/schedule-tools.test.ts +160 -0
  325. package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
  326. package/src/__tests__/secret-ingress-http.test.ts +7 -3
  327. package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
  328. package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
  329. package/src/__tests__/secret-response-routing.test.ts +13 -11
  330. package/src/__tests__/secure-keys.test.ts +3 -3
  331. package/src/__tests__/send-endpoint-busy.test.ts +87 -44
  332. package/src/__tests__/server-history-render.test.ts +318 -2
  333. package/src/__tests__/shell-observability.test.ts +249 -0
  334. package/src/__tests__/skill-feature-flags-integration.test.ts +52 -21
  335. package/src/__tests__/skill-feature-flags.test.ts +20 -22
  336. package/src/__tests__/skill-load-feature-flag.test.ts +15 -15
  337. package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
  338. package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
  339. package/src/__tests__/skill-tool-factory.test.ts +96 -95
  340. package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
  341. package/src/__tests__/skillssh-files.test.ts +1 -0
  342. package/src/__tests__/slack-channel-config.test.ts +3 -3
  343. package/src/__tests__/starter-task-flow.test.ts +6 -6
  344. package/src/__tests__/strip-memory-injections.test.ts +102 -14
  345. package/src/__tests__/subagent-call-site-routing.test.ts +14 -6
  346. package/src/__tests__/subagent-disposal.test.ts +27 -8
  347. package/src/__tests__/subagent-fork-notifications.test.ts +25 -12
  348. package/src/__tests__/subagent-fork-spawn.test.ts +14 -5
  349. package/src/__tests__/subagent-manager-notify.test.ts +21 -11
  350. package/src/__tests__/subagent-notify-parent.test.ts +6 -7
  351. package/src/__tests__/subagent-spawn-tool-fork.test.ts +59 -1
  352. package/src/__tests__/subagent-tools.test.ts +2 -1
  353. package/src/__tests__/suggestion-routes.test.ts +4 -3
  354. package/src/__tests__/sync-message-contract.test.ts +19 -16
  355. package/src/__tests__/system-prompt.test.ts +112 -0
  356. package/src/__tests__/task-scheduler.test.ts +162 -1
  357. package/src/__tests__/terminal-tools.test.ts +9 -25
  358. package/src/__tests__/test-preload-verifier.ts +68 -0
  359. package/src/__tests__/test-preload.ts +32 -39
  360. package/src/__tests__/thread-backfill.test.ts +4 -9
  361. package/src/__tests__/title-generate-hook.test.ts +319 -0
  362. package/src/__tests__/tool-error-hook.test.ts +278 -0
  363. package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
  364. package/src/__tests__/tool-executor.test.ts +55 -10
  365. package/src/__tests__/tool-preview-lifecycle.test.ts +481 -15
  366. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  367. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  368. package/src/__tests__/tool-result-truncation.test.ts +1 -1
  369. package/src/__tests__/tools-audio-read.test.ts +113 -0
  370. package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
  371. package/src/__tests__/turn-events-store.test.ts +11 -7
  372. package/src/__tests__/twilio-routes.test.ts +3 -2
  373. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  374. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  375. package/src/__tests__/usage-routes.test.ts +285 -1
  376. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  377. package/src/__tests__/validate-input.test.ts +381 -0
  378. package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
  379. package/src/__tests__/voice-scoped-grant-consumer.test.ts +10 -7
  380. package/src/__tests__/voice-session-bridge.test.ts +56 -38
  381. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  382. package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
  383. package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
  384. package/src/acp/__tests__/agent-process.test.ts +161 -0
  385. package/src/acp/__tests__/client-handler.test.ts +40 -0
  386. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  387. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  388. package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
  389. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  390. package/src/acp/__tests__/session-manager-resume.test.ts +695 -0
  391. package/src/acp/agent-process.ts +61 -1
  392. package/src/acp/auto-install.test.ts +125 -0
  393. package/src/acp/auto-install.ts +174 -0
  394. package/src/acp/client-handler.ts +31 -0
  395. package/src/acp/feature-gate.test.ts +48 -0
  396. package/src/acp/feature-gate.ts +34 -0
  397. package/src/acp/prepare-agent-env.ts +52 -11
  398. package/src/acp/resolve-agent.test.ts +147 -6
  399. package/src/acp/resolve-agent.ts +81 -7
  400. package/src/acp/resume-hint.ts +22 -0
  401. package/src/acp/session-manager.ts +492 -77
  402. package/src/agent/compaction-circuit.ts +98 -0
  403. package/src/agent/loop.ts +730 -449
  404. package/src/api/README.md +126 -2
  405. package/src/api/constants/call-sites.ts +27 -0
  406. package/src/api/constants/tool-execution.ts +21 -0
  407. package/src/api/events/assistant-activity-state.ts +75 -0
  408. package/src/api/events/assistant-outbound-attachment.ts +49 -0
  409. package/src/api/events/assistant-text-delta.ts +30 -0
  410. package/src/api/events/assistant-thinking-delta.ts +33 -0
  411. package/src/api/events/assistant-turn-start.ts +31 -0
  412. package/src/api/events/avatar-updated.ts +24 -0
  413. package/src/api/events/compaction-circuit-closed.ts +26 -0
  414. package/src/api/events/compaction-circuit-open.ts +28 -0
  415. package/src/api/events/confirmation-request.ts +114 -0
  416. package/src/api/events/contact-request.ts +33 -0
  417. package/src/api/events/conversation-error.ts +77 -0
  418. package/src/api/events/conversation-list-invalidated.ts +38 -0
  419. package/src/api/events/conversation-title-updated.ts +24 -0
  420. package/src/api/events/disk-pressure-status-changed.ts +61 -0
  421. package/src/api/events/document-comment-created.ts +44 -0
  422. package/src/api/events/document-comment-deleted.ts +22 -0
  423. package/src/api/events/document-comment-reopened.ts +23 -0
  424. package/src/api/events/document-comment-resolved.ts +25 -0
  425. package/src/api/events/document-editor-update.ts +27 -0
  426. package/src/api/events/error.ts +32 -0
  427. package/src/api/events/generation-cancelled.ts +22 -0
  428. package/src/api/events/generation-handoff.ts +39 -0
  429. package/src/api/events/home-feed-updated.ts +26 -0
  430. package/src/api/events/identity-changed.ts +32 -0
  431. package/src/api/events/interaction-resolved.ts +50 -0
  432. package/src/api/events/message-complete.ts +40 -0
  433. package/src/api/events/message-dequeued.ts +21 -0
  434. package/src/api/events/message-queued-deleted.ts +23 -0
  435. package/src/api/events/message-queued.ts +22 -0
  436. package/src/api/events/message-request-complete.ts +29 -0
  437. package/src/api/events/navigate-settings.ts +20 -0
  438. package/src/api/events/notification-intent.ts +33 -0
  439. package/src/api/events/open-url.ts +28 -0
  440. package/src/api/events/question-request.ts +67 -0
  441. package/src/{events → api/events}/relationship-state-updated.ts +6 -8
  442. package/src/api/events/secret-request.ts +42 -0
  443. package/src/api/events/subagent-event.ts +79 -0
  444. package/src/api/events/subagent-spawned.ts +40 -0
  445. package/src/api/events/subagent-status-changed.ts +65 -0
  446. package/src/api/events/sync-changed.ts +29 -0
  447. package/src/api/events/tool-output-chunk.ts +45 -0
  448. package/src/api/events/tool-result.ts +129 -0
  449. package/src/api/events/tool-use-preview-start.ts +32 -0
  450. package/src/api/events/tool-use-start.ts +30 -0
  451. package/src/api/events/trace-event.ts +69 -0
  452. package/src/api/events/turn-profile-auto-routed.ts +28 -0
  453. package/src/api/events/ui-surface-complete.ts +30 -0
  454. package/src/api/events/ui-surface-dismiss.ts +22 -0
  455. package/src/api/events/ui-surface-show.ts +67 -0
  456. package/src/api/events/ui-surface-update.ts +26 -0
  457. package/src/api/events/usage-update.ts +34 -0
  458. package/src/api/events/user-message-echo.ts +35 -0
  459. package/src/api/index.ts +517 -3
  460. package/src/api/requests/dictation.ts +45 -0
  461. package/src/api/responses/conversation-message.ts +374 -0
  462. package/src/api/responses/disk-pressure-status.ts +26 -0
  463. package/src/api/responses/home.ts +217 -0
  464. package/src/api/responses/llm-context-response.ts +41 -0
  465. package/src/api/responses/llm-request-log-entry.ts +93 -0
  466. package/src/api/responses/memory-recall-log.ts +65 -0
  467. package/src/api/responses/memory-v2-activation-log.ts +78 -0
  468. package/src/api/responses/memory-v3-selection-log.ts +50 -0
  469. package/src/api/responses/subagent-detail.ts +48 -0
  470. package/src/approvals/guardian-decision-primitive.ts +7 -15
  471. package/src/approvals/guardian-request-resolvers.ts +7 -10
  472. package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
  473. package/src/avatar/__tests__/avatar-store.test.ts +198 -0
  474. package/src/avatar/avatar-manifest.ts +195 -0
  475. package/src/avatar/avatar-store.ts +113 -0
  476. package/src/avatar/traits-png-sync.ts +8 -2
  477. package/src/background-wake/background-wake-routes.test.ts +687 -52
  478. package/src/background-wake/next-wake.test.ts +31 -1
  479. package/src/background-wake/next-wake.ts +5 -1
  480. package/src/background-wake/platform-client.test.ts +308 -0
  481. package/src/background-wake/platform-client.ts +167 -0
  482. package/src/background-wake/publisher.ts +91 -0
  483. package/src/background-wake/runtime-registry.ts +2 -2
  484. package/src/background-wake/wake-intent-hooks.test.ts +282 -0
  485. package/src/calls/call-conversation-messages.ts +6 -4
  486. package/src/calls/guardian-action-sweep.ts +6 -4
  487. package/src/calls/guardian-dispatch.ts +1 -0
  488. package/src/calls/relay-server.ts +12 -8
  489. package/src/calls/voice-session-bridge.ts +17 -31
  490. package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
  491. package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
  492. package/src/cli/commands/__tests__/notifications.test.ts +242 -54
  493. package/src/cli/commands/avatar.ts +17 -11
  494. package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
  495. package/src/cli/commands/channels/index.ts +229 -0
  496. package/src/cli/commands/conversations.ts +15 -1
  497. package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
  498. package/src/cli/commands/db/__tests__/status.test.ts +253 -0
  499. package/src/cli/commands/db/format.ts +48 -0
  500. package/src/cli/commands/db/index.ts +29 -0
  501. package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
  502. package/src/cli/commands/db/repair-step-integrity.ts +146 -0
  503. package/src/cli/commands/db/repair-steps.ts +164 -0
  504. package/src/cli/commands/db/repair.ts +141 -0
  505. package/src/cli/commands/db/status.ts +366 -0
  506. package/src/cli/commands/memory-v3.ts +168 -203
  507. package/src/cli/commands/notifications.ts +427 -65
  508. package/src/cli/lib/cli-colors.ts +24 -6
  509. package/src/cli/lib/open-browser.ts +7 -2
  510. package/src/cli/program.ts +6 -5
  511. package/src/config/__tests__/feature-flag-registry-guard.test.ts +4 -4
  512. package/src/config/acp-defaults.test.ts +10 -0
  513. package/src/config/acp-defaults.ts +6 -0
  514. package/src/config/assistant-feature-flags.ts +47 -55
  515. package/src/config/bundled-skills/acp/SKILL.md +64 -30
  516. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  517. package/src/config/bundled-skills/app-builder/SKILL.md +224 -387
  518. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  519. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  520. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  521. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  522. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  523. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  524. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  525. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  526. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  527. package/src/config/bundled-skills/document-editor/SKILL.md +33 -24
  528. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  529. package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
  530. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  531. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
  532. package/src/config/bundled-skills/schedule/SKILL.md +2 -2
  533. package/src/config/bundled-skills/schedule/TOOLS.json +10 -2
  534. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
  535. package/src/config/bundled-tool-registry.ts +2 -0
  536. package/src/config/call-site-defaults.ts +3 -8
  537. package/src/config/feature-flag-cache.ts +86 -0
  538. package/src/config/feature-flag-registry.json +81 -25
  539. package/src/config/llm-context-resolution.ts +10 -1
  540. package/src/config/llm-resolver.ts +121 -15
  541. package/src/config/loader.ts +4 -5
  542. package/src/config/schemas/__tests__/memory-v2.test.ts +2 -211
  543. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  544. package/src/config/schemas/call-site-catalog.ts +8 -15
  545. package/src/config/schemas/heartbeat.ts +10 -1
  546. package/src/config/schemas/llm.ts +93 -4
  547. package/src/config/schemas/memory-lifecycle.ts +24 -0
  548. package/src/config/schemas/memory-v2.ts +8 -227
  549. package/src/config/schemas/memory-v3.ts +47 -0
  550. package/src/config/schemas/memory.ts +6 -1
  551. package/src/config/schemas/platform.ts +8 -0
  552. package/src/config/schemas/services.ts +6 -2
  553. package/src/config/schemas/timeouts.ts +3 -1
  554. package/src/config/seed-inference-profiles.ts +38 -18
  555. package/src/config/skills.ts +13 -0
  556. package/src/context/compactor.ts +55 -32
  557. package/src/context/strip-injections.ts +128 -0
  558. package/src/context/token-estimator.ts +52 -5
  559. package/src/context/tool-result-truncation.ts +1 -66
  560. package/src/context/window-manager.ts +141 -26
  561. package/src/credential-execution/executable-discovery.ts +56 -0
  562. package/src/credential-execution/process-manager.ts +6 -2
  563. package/src/credential-health/credential-health-service.ts +125 -40
  564. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +9 -6
  565. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +15 -17
  566. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
  567. package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
  568. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  569. package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
  570. package/src/daemon/__tests__/native-web-search-metadata.test.ts +11 -8
  571. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
  572. package/src/daemon/__tests__/web-search-status-text.test.ts +11 -6
  573. package/src/daemon/approval-generators.ts +4 -4
  574. package/src/daemon/assistant-attachments.ts +1 -1
  575. package/src/daemon/config-watcher.ts +7 -1
  576. package/src/daemon/context-overflow-reducer.ts +0 -1
  577. package/src/daemon/conversation-agent-loop-handlers.ts +1146 -247
  578. package/src/daemon/conversation-agent-loop.ts +570 -1457
  579. package/src/daemon/conversation-error.ts +40 -12
  580. package/src/daemon/conversation-history.ts +27 -10
  581. package/src/daemon/conversation-launch.ts +4 -8
  582. package/src/daemon/conversation-lifecycle.ts +13 -42
  583. package/src/daemon/conversation-messaging.ts +89 -49
  584. package/src/daemon/conversation-notifiers.ts +7 -5
  585. package/src/daemon/conversation-process.ts +183 -130
  586. package/src/daemon/conversation-registry.ts +159 -0
  587. package/src/daemon/conversation-runtime-assembly.ts +209 -362
  588. package/src/daemon/conversation-skill-tools.ts +14 -30
  589. package/src/daemon/conversation-slash.ts +6 -25
  590. package/src/daemon/conversation-store.ts +15 -95
  591. package/src/daemon/conversation-surfaces.ts +346 -107
  592. package/src/daemon/conversation-tool-setup.ts +38 -77
  593. package/src/daemon/conversation-workspace.ts +17 -0
  594. package/src/daemon/conversation.ts +143 -186
  595. package/src/daemon/daemon-control.ts +1 -1
  596. package/src/daemon/daemon-skill-host.ts +7 -4
  597. package/src/daemon/disk-pressure-guard.ts +54 -50
  598. package/src/daemon/external-plugins-bootstrap.ts +53 -32
  599. package/src/daemon/first-greeting.ts +53 -13
  600. package/src/daemon/guardian-action-generators.ts +2 -2
  601. package/src/daemon/handlers/config-a2a.ts +51 -36
  602. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  603. package/src/daemon/handlers/config-telegram.ts +16 -2
  604. package/src/daemon/handlers/conversations.ts +9 -23
  605. package/src/daemon/handlers/shared.ts +158 -77
  606. package/src/daemon/handlers/skills.ts +53 -20
  607. package/src/daemon/host-app-control-proxy.ts +54 -1
  608. package/src/daemon/host-cu-proxy.ts +46 -22
  609. package/src/daemon/host-file-proxy.ts +25 -1
  610. package/src/daemon/host-proxy-preactivation.ts +25 -6
  611. package/src/daemon/lifecycle.ts +65 -67
  612. package/src/daemon/mcp-reload-service.ts +1 -1
  613. package/src/daemon/meet-manifest-loader.ts +10 -17
  614. package/src/daemon/message-protocol.ts +2 -3
  615. package/src/daemon/message-provenance.ts +49 -0
  616. package/src/daemon/message-types/apps.ts +1 -29
  617. package/src/daemon/message-types/contacts.ts +3 -20
  618. package/src/daemon/message-types/conversations.ts +25 -125
  619. package/src/daemon/message-types/document-comments.ts +8 -44
  620. package/src/daemon/message-types/documents.ts +3 -9
  621. package/src/daemon/message-types/home.ts +5 -18
  622. package/src/daemon/message-types/integrations.ts +4 -13
  623. package/src/daemon/message-types/messages.ts +50 -428
  624. package/src/daemon/message-types/notifications.ts +2 -32
  625. package/src/daemon/message-types/settings.ts +3 -8
  626. package/src/daemon/message-types/skills.ts +4 -0
  627. package/src/daemon/message-types/subagents.ts +6 -0
  628. package/src/daemon/message-types/surfaces.ts +138 -3
  629. package/src/daemon/message-types/sync.ts +12 -25
  630. package/src/daemon/message-types/workspace.ts +3 -11
  631. package/src/daemon/now-scratchpad.ts +21 -0
  632. package/src/daemon/orphan-reaper.test.ts +210 -0
  633. package/src/daemon/orphan-reaper.ts +240 -0
  634. package/src/daemon/overflow-reduction-loop.ts +230 -0
  635. package/src/daemon/persist-unsendable-image.ts +117 -0
  636. package/src/daemon/process-message.ts +59 -58
  637. package/src/daemon/providers-setup.ts +1 -1
  638. package/src/daemon/server.ts +30 -0
  639. package/src/daemon/switch-inference-profile-tool.ts +13 -3
  640. package/src/daemon/tool-setup-types.ts +0 -6
  641. package/src/daemon/tool-side-effects.ts +10 -7
  642. package/src/daemon/trace-emitter.ts +6 -4
  643. package/src/daemon/trust-context.ts +32 -0
  644. package/src/daemon/wake-target-adapter.ts +24 -2
  645. package/src/documents/document-store.ts +38 -0
  646. package/src/export/__tests__/transcript-formatter.test.ts +1 -0
  647. package/src/heartbeat/__tests__/heartbeat-service.test.ts +34 -0
  648. package/src/heartbeat/heartbeat-run-store.ts +54 -1
  649. package/src/heartbeat/heartbeat-service.ts +105 -0
  650. package/src/home/__tests__/feed-writer.test.ts +161 -0
  651. package/src/home/__tests__/post-connect-feed.test.ts +1 -0
  652. package/src/home/__tests__/suggested-prompts.test.ts +55 -59
  653. package/src/home/feed-types.ts +36 -221
  654. package/src/home/feed-writer.ts +146 -7
  655. package/src/home/home-greeting-cache.ts +24 -1
  656. package/src/home/suggested-prompts.ts +27 -145
  657. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  658. package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
  659. package/src/ipc/__tests__/email-ipc.test.ts +0 -9
  660. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  661. package/src/ipc/gateway-client.test.ts +6 -3
  662. package/src/ipc/gateway-client.ts +3 -3
  663. package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
  664. package/src/ipc/routes/route-adapter.ts +45 -6
  665. package/src/ipc/skill-routes/__tests__/memory.test.ts +34 -9
  666. package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
  667. package/src/ipc/skill-routes/__tests__/registries.test.ts +59 -20
  668. package/src/ipc/skill-routes/memory.ts +29 -13
  669. package/src/ipc/skill-routes/providers.ts +5 -6
  670. package/src/ipc/skill-routes/registries.ts +39 -88
  671. package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
  672. package/src/media/gemini-image-service.ts +15 -0
  673. package/src/media/openai-image-service.ts +14 -0
  674. package/src/media/types.ts +34 -0
  675. package/src/memory/__tests__/conversation-queries.test.ts +192 -8
  676. package/src/memory/__tests__/db-maintenance.test.ts +128 -0
  677. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
  678. package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
  679. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +82 -5
  680. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
  681. package/src/memory/__tests__/memory-retrospective-job.test.ts +11 -6
  682. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
  683. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
  684. package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
  685. package/src/memory/auth-fallback-events-store.ts +94 -0
  686. package/src/memory/context-search/agent-runner.ts +2 -4
  687. package/src/memory/conversation-attention-store.ts +17 -3
  688. package/src/memory/conversation-crud.ts +386 -115
  689. package/src/memory/conversation-queries.ts +78 -22
  690. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  691. package/src/memory/conversation-title-service.ts +65 -41
  692. package/src/memory/db-connection.ts +29 -19
  693. package/src/memory/db-init.ts +18 -0
  694. package/src/memory/db-maintenance.ts +18 -2
  695. package/src/memory/db-singleton.ts +77 -0
  696. package/src/memory/delivery-channels.ts +82 -0
  697. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  698. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
  699. package/src/memory/graph/consolidation.ts +8 -11
  700. package/src/memory/graph/conversation-graph-memory.ts +106 -8
  701. package/src/memory/graph/extraction.ts +6 -9
  702. package/src/memory/graph/narrative.ts +2 -2
  703. package/src/memory/graph/pattern-scan.ts +2 -2
  704. package/src/memory/graph/retriever.test.ts +3 -3
  705. package/src/memory/graph/retriever.ts +20 -26
  706. package/src/memory/graph/tools.ts +4 -4
  707. package/src/memory/job-handlers/conversation-starters.ts +45 -34
  708. package/src/memory/job-handlers/embedding.test.ts +3 -2
  709. package/src/memory/job-handlers/summarization.ts +1 -2
  710. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
  711. package/src/memory/jobs-store.ts +36 -1
  712. package/src/memory/jobs-worker.ts +93 -43
  713. package/src/memory/llm-request-log-source-clickhouse.ts +55 -1
  714. package/src/memory/llm-request-log-source-local.ts +13 -0
  715. package/src/memory/llm-request-log-source.ts +21 -6
  716. package/src/memory/llm-request-log-store.ts +147 -3
  717. package/src/memory/llm-usage-store.ts +234 -50
  718. package/src/memory/memory-marker.ts +17 -0
  719. package/src/memory/memory-retrospective-job.ts +6 -2
  720. package/src/memory/memory-v2-activation-log-store.ts +13 -1
  721. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  722. package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
  723. package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
  724. package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
  725. package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
  726. package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
  727. package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
  728. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  729. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  730. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  731. package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
  732. package/src/memory/migrations/index.ts +9 -0
  733. package/src/memory/pkb/autoinject.ts +61 -0
  734. package/src/memory/pkb/context.ts +50 -0
  735. package/src/memory/pkb/types.ts +14 -0
  736. package/src/memory/schedule-attribution-sql.ts +104 -0
  737. package/src/memory/schema/acp.ts +4 -0
  738. package/src/memory/schema/conversations.ts +9 -1
  739. package/src/memory/schema/inference.ts +0 -1
  740. package/src/memory/schema/infrastructure.ts +27 -0
  741. package/src/memory/usage-grouped-buckets.ts +6 -1
  742. package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
  743. package/src/memory/v2/__tests__/consolidation-job.test.ts +125 -1
  744. package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
  745. package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
  746. package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
  747. package/src/memory/v2/__tests__/migration.test.ts +11 -3
  748. package/src/memory/v2/__tests__/page-index.test.ts +37 -1
  749. package/src/memory/v2/__tests__/router.test.ts +14 -4
  750. package/src/memory/v2/__tests__/sweep-job.test.ts +9 -5
  751. package/src/memory/v2/backfill-jobs.ts +6 -0
  752. package/src/memory/v2/consolidation-job.ts +99 -10
  753. package/src/memory/v2/harness/metrics.ts +5 -1
  754. package/src/memory/v2/harness/replay-input.ts +19 -3
  755. package/src/memory/v2/harness/runner.ts +6 -0
  756. package/src/memory/v2/harness/trace.ts +6 -0
  757. package/src/memory/v2/migration.ts +5 -3
  758. package/src/memory/v2/page-index.ts +11 -0
  759. package/src/memory/v2/router.ts +8 -11
  760. package/src/memory/v2/sweep-job.ts +8 -11
  761. package/src/memory/v2/types.ts +1 -0
  762. package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
  763. package/src/messaging/providers/slack/render-transcript.ts +2 -2
  764. package/src/messaging/style-analyzer.ts +8 -11
  765. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  766. package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
  767. package/src/notifications/adapters/slack.ts +45 -11
  768. package/src/notifications/broadcaster.ts +114 -63
  769. package/src/notifications/conversation-pairing.ts +23 -8
  770. package/src/notifications/decision-engine.ts +16 -16
  771. package/src/notifications/decisions-store.ts +32 -1
  772. package/src/notifications/deliveries-store.ts +45 -0
  773. package/src/notifications/edit-notification.ts +201 -0
  774. package/src/notifications/emit-signal.ts +11 -1
  775. package/src/notifications/home-feed-side-effect.ts +12 -1
  776. package/src/notifications/preference-extractor.ts +11 -14
  777. package/src/notifications/signal.ts +10 -0
  778. package/src/notifications/types.ts +37 -0
  779. package/src/oauth/byo-connection.test.ts +67 -3
  780. package/src/oauth/byo-connection.ts +32 -5
  781. package/src/oauth/connect-orchestrator.ts +9 -0
  782. package/src/oauth/connection-resolver.test.ts +76 -0
  783. package/src/oauth/connection-resolver.ts +49 -10
  784. package/src/oauth/manual-token-connection.ts +51 -3
  785. package/src/oauth/seed-providers.ts +3 -0
  786. package/src/permissions/approval-policy.test.ts +19 -5
  787. package/src/permissions/approval-policy.ts +14 -3
  788. package/src/permissions/checker.ts +21 -8
  789. package/src/permissions/prompter.ts +46 -36
  790. package/src/permissions/question-prompter.test.ts +35 -26
  791. package/src/permissions/question-prompter.ts +6 -10
  792. package/src/platform/client.test.ts +24 -1
  793. package/src/platform/client.ts +8 -0
  794. package/src/platform/feature-gate.ts +15 -0
  795. package/src/plugin-api/constants.ts +4 -0
  796. package/src/plugin-api/index.ts +10 -1
  797. package/src/plugin-api/types.ts +176 -4
  798. package/src/plugins/defaults/compaction/compact.ts +59 -0
  799. package/src/plugins/defaults/compaction/package.json +15 -0
  800. package/src/plugins/defaults/compaction/register.ts +24 -0
  801. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  802. package/src/plugins/defaults/empty-response/package.json +15 -0
  803. package/src/plugins/defaults/empty-response/register.ts +23 -0
  804. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
  805. package/src/plugins/defaults/history-repair/package.json +15 -0
  806. package/src/plugins/defaults/history-repair/register.ts +24 -0
  807. package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
  808. package/src/plugins/defaults/index.ts +22 -49
  809. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  810. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  811. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  812. package/src/plugins/defaults/{injectors.ts → memory-retrieval/injectors.ts} +297 -120
  813. package/src/plugins/defaults/memory-v3-shadow/__tests__/assign.test.ts +242 -0
  814. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +118 -0
  815. package/src/plugins/defaults/memory-v3-shadow/__tests__/core.test.ts +39 -0
  816. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +36 -0
  817. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +37 -0
  818. package/src/plugins/defaults/memory-v3-shadow/__tests__/health.test.ts +219 -0
  819. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +330 -0
  820. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +288 -0
  821. package/src/plugins/defaults/memory-v3-shadow/__tests__/needle.test.ts +107 -0
  822. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +436 -0
  823. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  824. package/src/plugins/defaults/memory-v3-shadow/__tests__/reconcile.test.ts +274 -0
  825. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +61 -0
  826. package/src/plugins/defaults/memory-v3-shadow/__tests__/router.test.ts +332 -0
  827. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +179 -0
  828. package/src/plugins/defaults/memory-v3-shadow/__tests__/selector.test.ts +470 -0
  829. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +432 -0
  830. package/src/plugins/defaults/memory-v3-shadow/__tests__/snapshot.test.ts +168 -0
  831. package/src/plugins/defaults/memory-v3-shadow/__tests__/tree.test.ts +192 -0
  832. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +54 -0
  833. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +106 -0
  834. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +44 -0
  835. package/src/plugins/defaults/memory-v3-shadow/assign.ts +268 -0
  836. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +124 -0
  837. package/src/plugins/defaults/memory-v3-shadow/core.ts +26 -0
  838. package/src/plugins/defaults/memory-v3-shadow/data/README.md +84 -0
  839. package/src/plugins/defaults/memory-v3-shadow/data/assignments.json +5 -0
  840. package/src/plugins/defaults/memory-v3-shadow/data/core.json +1 -0
  841. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-x.md +9 -0
  842. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-a/topic-y.md +9 -0
  843. package/src/plugins/defaults/memory-v3-shadow/data/leaves/domain-b/topic-z.md +9 -0
  844. package/src/plugins/defaults/memory-v3-shadow/health.ts +0 -0
  845. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  846. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  847. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  848. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  849. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +314 -0
  850. package/src/plugins/defaults/memory-v3-shadow/needle.ts +115 -0
  851. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +126 -0
  852. package/src/plugins/defaults/memory-v3-shadow/package.json +15 -0
  853. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +34 -0
  854. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  855. package/src/plugins/defaults/memory-v3-shadow/reconcile.ts +523 -0
  856. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  857. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +32 -0
  858. package/src/plugins/defaults/memory-v3-shadow/router.ts +190 -0
  859. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +84 -0
  860. package/src/plugins/defaults/memory-v3-shadow/selector.ts +226 -0
  861. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +349 -0
  862. package/src/plugins/defaults/memory-v3-shadow/snapshot.ts +209 -0
  863. package/src/plugins/defaults/memory-v3-shadow/tree.ts +174 -0
  864. package/src/plugins/defaults/memory-v3-shadow/types.ts +59 -0
  865. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +88 -0
  866. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  867. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  868. package/src/plugins/defaults/title-generate/package.json +15 -0
  869. package/src/plugins/defaults/title-generate/register.ts +35 -0
  870. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  871. package/src/plugins/defaults/tool-error/package.json +15 -0
  872. package/src/plugins/defaults/tool-error/register.ts +23 -0
  873. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  874. package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
  875. package/src/plugins/defaults/tool-result-truncate/register.ts +24 -0
  876. package/src/plugins/defaults/tool-result-truncate/terminal.ts +132 -0
  877. package/src/plugins/external-plugin-loader.ts +2 -2
  878. package/src/plugins/pipeline.ts +8 -35
  879. package/src/plugins/registry.ts +8 -25
  880. package/src/plugins/types.ts +62 -677
  881. package/src/plugins/user-loader.ts +4 -3
  882. package/src/proactive-artifact/aux-message-injector.ts +4 -5
  883. package/src/proactive-artifact/job.test.ts +29 -21
  884. package/src/proactive-artifact/job.ts +3 -1
  885. package/src/prompts/__tests__/system-prompt.test.ts +46 -4
  886. package/src/prompts/sections.ts +20 -7
  887. package/src/prompts/system-prompt.ts +38 -40
  888. package/src/prompts/template-detection.ts +10 -4
  889. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  890. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
  891. package/src/prompts/templates/BOOTSTRAP.md +12 -12
  892. package/src/prompts/templates/IDENTITY.md +0 -2
  893. package/src/prompts/templates/system-sections.ts +21 -0
  894. package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
  895. package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
  896. package/src/providers/__tests__/retry-callsite.test.ts +25 -25
  897. package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
  898. package/src/providers/anthropic/client.ts +61 -34
  899. package/src/providers/call-site-routing.ts +34 -18
  900. package/src/providers/connection-model-compat.ts +23 -0
  901. package/src/providers/connection-resolution.ts +39 -20
  902. package/src/providers/fireworks/client.ts +1 -0
  903. package/src/providers/gemini/client.ts +176 -37
  904. package/src/providers/gemini/inline-media.ts +74 -0
  905. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
  906. package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
  907. package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
  908. package/src/providers/inference/auth.ts +0 -8
  909. package/src/providers/inference/connections.ts +3 -66
  910. package/src/providers/inference/resolve-auth.ts +2 -3
  911. package/src/providers/model-catalog.ts +35 -1
  912. package/src/providers/model-intents.ts +3 -3
  913. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  914. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +269 -7
  915. package/src/providers/openai/chat-completions-provider.ts +155 -16
  916. package/src/providers/openai/codex-models.ts +2 -0
  917. package/src/providers/openai/responses-provider.ts +54 -57
  918. package/src/providers/openrouter/client.ts +15 -14
  919. package/src/providers/placeholder-sentinels.ts +35 -0
  920. package/src/providers/provider-send-message.ts +23 -14
  921. package/src/providers/ratelimit.ts +1 -9
  922. package/src/providers/registry.ts +48 -8
  923. package/src/providers/retry.ts +16 -9
  924. package/src/providers/search-provider-catalog.ts +17 -9
  925. package/src/providers/types.ts +20 -2
  926. package/src/providers/usage-tracking.ts +1 -9
  927. package/src/runtime/__tests__/agent-wake.test.ts +142 -32
  928. package/src/runtime/__tests__/background-job-runner.test.ts +2 -3
  929. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  930. package/src/runtime/access-request-helper.ts +1 -0
  931. package/src/runtime/agent-wake.ts +95 -23
  932. package/src/runtime/assistant-event-hub.ts +38 -8
  933. package/src/runtime/assistant-stream-state.ts +368 -0
  934. package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
  935. package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
  936. package/src/runtime/auth/route-policy.ts +42 -1069
  937. package/src/runtime/background-job-runner.ts +1 -4
  938. package/src/runtime/btw-sidechain.ts +3 -1
  939. package/src/runtime/channel-approvals.ts +4 -15
  940. package/src/runtime/channel-invite-transport.ts +5 -6
  941. package/src/runtime/channel-readiness-service.ts +70 -5
  942. package/src/runtime/channel-reply-delivery.ts +23 -0
  943. package/src/runtime/channel-retry-sweep.ts +59 -30
  944. package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
  945. package/src/runtime/http-router.ts +35 -43
  946. package/src/runtime/http-types.ts +23 -71
  947. package/src/runtime/interactive-ui.ts +1 -1
  948. package/src/runtime/invite-instruction-generator.ts +3 -3
  949. package/src/runtime/migrations/vbundle-builder.ts +3 -2
  950. package/src/runtime/pending-interactions.ts +3 -2
  951. package/src/runtime/routes/__tests__/acp-routes.test.ts +253 -55
  952. package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
  953. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
  954. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  955. package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
  956. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +436 -0
  957. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
  958. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +129 -1
  959. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  960. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
  961. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +26 -72
  962. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  963. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +58 -5
  964. package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
  965. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
  966. package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
  967. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
  968. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  969. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +297 -0
  970. package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
  971. package/src/runtime/routes/__tests__/tts-routes.test.ts +9 -5
  972. package/src/runtime/routes/acp-routes-list.test.ts +3 -0
  973. package/src/runtime/routes/acp-routes.test.ts +186 -100
  974. package/src/runtime/routes/acp-routes.ts +110 -35
  975. package/src/runtime/routes/app-management-routes.ts +175 -106
  976. package/src/runtime/routes/app-routes.ts +38 -20
  977. package/src/runtime/routes/approval-routes.ts +17 -5
  978. package/src/runtime/routes/attachment-routes.ts +51 -16
  979. package/src/runtime/routes/audio-routes.ts +1 -0
  980. package/src/runtime/routes/audit-routes.ts +5 -0
  981. package/src/runtime/routes/auth-routes.ts +5 -0
  982. package/src/runtime/routes/avatar-routes.ts +264 -59
  983. package/src/runtime/routes/background-tool-routes.ts +9 -0
  984. package/src/runtime/routes/background-wake-routes.ts +201 -23
  985. package/src/runtime/routes/backup-routes.ts +45 -0
  986. package/src/runtime/routes/bookmark-routes.ts +13 -0
  987. package/src/runtime/routes/brain-graph-routes.ts +9 -0
  988. package/src/runtime/routes/browser-routes.ts +6 -1
  989. package/src/runtime/routes/browser-tabs-routes.ts +11 -10
  990. package/src/runtime/routes/btw-routes.ts +37 -27
  991. package/src/runtime/routes/cache-routes.ts +13 -0
  992. package/src/runtime/routes/call-routes.ts +21 -10
  993. package/src/runtime/routes/channel-availability-routes.ts +5 -1
  994. package/src/runtime/routes/channel-readiness-routes.ts +37 -4
  995. package/src/runtime/routes/channel-route-definitions.ts +21 -0
  996. package/src/runtime/routes/channel-verification-routes.ts +21 -0
  997. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
  998. package/src/runtime/routes/client-routes.ts +9 -0
  999. package/src/runtime/routes/consolidation-routes.ts +133 -25
  1000. package/src/runtime/routes/contact-prompt-routes.ts +9 -0
  1001. package/src/runtime/routes/contact-routes.ts +90 -23
  1002. package/src/runtime/routes/content-source-routes.ts +5 -1
  1003. package/src/runtime/routes/conversation-analysis-routes.ts +11 -1
  1004. package/src/runtime/routes/conversation-attention-routes.ts +5 -0
  1005. package/src/runtime/routes/conversation-cli-routes.ts +54 -7
  1006. package/src/runtime/routes/conversation-compaction-routes.ts +292 -0
  1007. package/src/runtime/routes/conversation-list-routes.ts +225 -9
  1008. package/src/runtime/routes/conversation-management-routes.ts +96 -28
  1009. package/src/runtime/routes/conversation-query-routes.ts +150 -51
  1010. package/src/runtime/routes/conversation-routes.ts +615 -327
  1011. package/src/runtime/routes/conversation-starter-routes.ts +35 -20
  1012. package/src/runtime/routes/conversations-import-routes.ts +48 -13
  1013. package/src/runtime/routes/credential-prompt-routes.ts +5 -0
  1014. package/src/runtime/routes/credential-routes.ts +25 -6
  1015. package/src/runtime/routes/debug-bash-routes.ts +5 -0
  1016. package/src/runtime/routes/debug-routes.ts +11 -2
  1017. package/src/runtime/routes/defer-routes.ts +13 -0
  1018. package/src/runtime/routes/diagnostics-routes.ts +37 -46
  1019. package/src/runtime/routes/disk-pressure-routes.ts +17 -31
  1020. package/src/runtime/routes/document-comments-routes.ts +46 -27
  1021. package/src/runtime/routes/documents-routes.ts +35 -11
  1022. package/src/runtime/routes/domain-routes.ts +98 -51
  1023. package/src/runtime/routes/email-routes.ts +33 -0
  1024. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  1025. package/src/runtime/routes/events-routes.ts +107 -8
  1026. package/src/runtime/routes/filing-routes.ts +9 -4
  1027. package/src/runtime/routes/gateway-log-routes.ts +31 -4
  1028. package/src/runtime/routes/global-search-routes.ts +53 -50
  1029. package/src/runtime/routes/group-routes.ts +32 -5
  1030. package/src/runtime/routes/guardian-action-routes.ts +9 -0
  1031. package/src/runtime/routes/guardian-approval-interception.ts +0 -31
  1032. package/src/runtime/routes/heartbeat-routes.ts +57 -21
  1033. package/src/runtime/routes/home-feed-routes.ts +149 -16
  1034. package/src/runtime/routes/home-state-routes.ts +8 -40
  1035. package/src/runtime/routes/host-app-control-routes.ts +6 -1
  1036. package/src/runtime/routes/host-bash-routes.ts +5 -0
  1037. package/src/runtime/routes/host-browser-routes.ts +13 -0
  1038. package/src/runtime/routes/host-cu-routes.ts +6 -1
  1039. package/src/runtime/routes/host-file-routes.ts +26 -6
  1040. package/src/runtime/routes/host-transfer-routes.ts +13 -2
  1041. package/src/runtime/routes/http-adapter.ts +1 -2
  1042. package/src/runtime/routes/identity-intro-cache.ts +72 -39
  1043. package/src/runtime/routes/identity-routes.ts +257 -20
  1044. package/src/runtime/routes/image-generation-routes.ts +45 -2
  1045. package/src/runtime/routes/inbound-message-handler.ts +16 -12
  1046. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +524 -12
  1047. package/src/runtime/routes/inbound-stages/background-dispatch.ts +72 -27
  1048. package/src/runtime/routes/index.ts +4 -0
  1049. package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
  1050. package/src/runtime/routes/inference-provider-connection-routes.ts +26 -31
  1051. package/src/runtime/routes/inference-send-routes.ts +11 -11
  1052. package/src/runtime/routes/integrations/a2a.ts +32 -7
  1053. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  1054. package/src/runtime/routes/integrations/slack/channel.ts +23 -3
  1055. package/src/runtime/routes/integrations/slack/share.ts +36 -8
  1056. package/src/runtime/routes/integrations/telegram.ts +34 -9
  1057. package/src/runtime/routes/integrations/twilio.ts +77 -7
  1058. package/src/runtime/routes/integrations/vercel.ts +18 -3
  1059. package/src/runtime/routes/internal-oauth-routes.ts +5 -0
  1060. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  1061. package/src/runtime/routes/internal-twilio-routes.ts +13 -0
  1062. package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
  1063. package/src/runtime/routes/llm-context-normalization.ts +7 -2
  1064. package/src/runtime/routes/log-export-routes.ts +36 -10
  1065. package/src/runtime/routes/mcp-auth-routes.ts +25 -0
  1066. package/src/runtime/routes/memory-item-routes.ts +21 -10
  1067. package/src/runtime/routes/memory-v2-routes.ts +105 -44
  1068. package/src/runtime/routes/memory-v3-routes.ts +313 -257
  1069. package/src/runtime/routes/migration-rollback-routes.ts +5 -1
  1070. package/src/runtime/routes/migration-routes.ts +49 -13
  1071. package/src/runtime/routes/notification-routes.ts +80 -2
  1072. package/src/runtime/routes/oauth-apps.ts +99 -23
  1073. package/src/runtime/routes/oauth-commands-routes.ts +43 -15
  1074. package/src/runtime/routes/oauth-connect-routes.ts +9 -0
  1075. package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
  1076. package/src/runtime/routes/oauth-providers.ts +79 -15
  1077. package/src/runtime/routes/platform-routes.ts +102 -5
  1078. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +9 -6
  1079. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
  1080. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
  1081. package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
  1082. package/src/runtime/routes/playground/force-compact.ts +2 -2
  1083. package/src/runtime/routes/playground/helpers.ts +1 -2
  1084. package/src/runtime/routes/playground/inject-failures.ts +13 -8
  1085. package/src/runtime/routes/playground/reset-circuit.ts +14 -9
  1086. package/src/runtime/routes/playground/seed-conversation.ts +1 -1
  1087. package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
  1088. package/src/runtime/routes/playground/state.ts +4 -3
  1089. package/src/runtime/routes/plugins-routes.ts +22 -19
  1090. package/src/runtime/routes/profiler-routes.ts +17 -4
  1091. package/src/runtime/routes/ps-routes.ts +5 -0
  1092. package/src/runtime/routes/publish-routes.ts +13 -3
  1093. package/src/runtime/routes/question-routes.ts +5 -0
  1094. package/src/runtime/routes/recording-routes.ts +25 -12
  1095. package/src/runtime/routes/rename-conversation-routes.ts +10 -0
  1096. package/src/runtime/routes/sanity-routes.ts +9 -2
  1097. package/src/runtime/routes/schedule-routes.ts +288 -88
  1098. package/src/runtime/routes/secret-routes.ts +31 -6
  1099. package/src/runtime/routes/sequence-routes.ts +33 -0
  1100. package/src/runtime/routes/settings-routes.ts +65 -19
  1101. package/src/runtime/routes/skills-routes.ts +166 -73
  1102. package/src/runtime/routes/slack-channel-routes.ts +5 -0
  1103. package/src/runtime/routes/stt-routes.ts +13 -6
  1104. package/src/runtime/routes/subagents-routes.ts +24 -18
  1105. package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
  1106. package/src/runtime/routes/surface-action-routes.ts +10 -38
  1107. package/src/runtime/routes/surface-content-routes.ts +21 -6
  1108. package/src/runtime/routes/surface-conversation-resolver.ts +66 -0
  1109. package/src/runtime/routes/task-routes.ts +37 -0
  1110. package/src/runtime/routes/telemetry-routes.ts +9 -0
  1111. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  1112. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  1113. package/src/runtime/routes/trace-event-routes.ts +42 -1
  1114. package/src/runtime/routes/trust-rules-routes.ts +31 -2
  1115. package/src/runtime/routes/tts-routes.ts +48 -6
  1116. package/src/runtime/routes/types.ts +83 -16
  1117. package/src/runtime/routes/ui-request-routes.ts +5 -0
  1118. package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
  1119. package/src/runtime/routes/usage-routes.ts +118 -42
  1120. package/src/runtime/routes/user-routes-cli.ts +9 -0
  1121. package/src/runtime/routes/user-routes.ts +5 -1
  1122. package/src/runtime/routes/wake-conversation-routes.ts +5 -0
  1123. package/src/runtime/routes/watcher-routes.ts +21 -0
  1124. package/src/runtime/routes/webhook-routes.ts +50 -2
  1125. package/src/runtime/routes/wipe-conversation-routes.ts +8 -0
  1126. package/src/runtime/routes/work-items-routes.ts +49 -23
  1127. package/src/runtime/routes/workspace-commit-routes.ts +5 -0
  1128. package/src/runtime/routes/workspace-routes.test.ts +42 -0
  1129. package/src/runtime/routes/workspace-routes.ts +124 -9
  1130. package/src/runtime/services/__tests__/analyze-conversation.test.ts +10 -4
  1131. package/src/runtime/services/analyze-conversation.ts +5 -8
  1132. package/src/runtime/services/conversation-serializer.ts +24 -2
  1133. package/src/runtime/slack-dm-text-delivery.ts +177 -0
  1134. package/src/runtime/sync/resource-sync-events.ts +17 -3
  1135. package/src/runtime/sync/sync-publisher.ts +2 -2
  1136. package/src/runtime/tool-grant-request-helper.ts +1 -0
  1137. package/src/schedule/run-script.ts +28 -3
  1138. package/src/schedule/schedule-store.ts +36 -2
  1139. package/src/schedule/schedule-usage-store.ts +83 -0
  1140. package/src/schedule/scheduler.ts +126 -21
  1141. package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
  1142. package/src/security/encrypted-store.ts +7 -16
  1143. package/src/security/store-path-override.ts +61 -0
  1144. package/src/signals/cancel.ts +2 -4
  1145. package/src/signals/user-message.ts +10 -16
  1146. package/src/skills/catalog-files.ts +4 -1
  1147. package/src/skills/catalog-install.ts +3 -0
  1148. package/src/skills/categories-cache.ts +118 -0
  1149. package/src/skills/clawhub-files.ts +1 -0
  1150. package/src/skills/skillssh-files.ts +1 -0
  1151. package/src/skills/validate-input.ts +177 -0
  1152. package/src/subagent/manager.ts +33 -24
  1153. package/src/subagent/types.ts +6 -0
  1154. package/src/tasks/tool-sanitizer.ts +2 -2
  1155. package/src/telemetry/types.ts +55 -1
  1156. package/src/telemetry/usage-telemetry-reporter.test.ts +250 -4
  1157. package/src/telemetry/usage-telemetry-reporter.ts +88 -2
  1158. package/src/tools/acp/context.ts +20 -0
  1159. package/src/tools/acp/list-agents.test.ts +7 -1
  1160. package/src/tools/acp/spawn.test.ts +198 -93
  1161. package/src/tools/acp/spawn.ts +32 -70
  1162. package/src/tools/acp/steer.test.ts +105 -8
  1163. package/src/tools/acp/steer.ts +48 -17
  1164. package/src/tools/apps/definitions.ts +42 -24
  1165. package/src/tools/apps/executors.ts +13 -8
  1166. package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
  1167. package/src/tools/ask-question/ask-question-tool.ts +85 -90
  1168. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
  1169. package/src/tools/computer-use/definitions.ts +295 -289
  1170. package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
  1171. package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
  1172. package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
  1173. package/src/tools/credentials/vault.ts +112 -111
  1174. package/src/tools/document/document-tool.ts +131 -8
  1175. package/src/tools/execution-target.ts +3 -6
  1176. package/src/tools/execution-timeout.ts +3 -4
  1177. package/src/tools/executor.ts +18 -107
  1178. package/src/tools/filesystem/edit.ts +45 -42
  1179. package/src/tools/filesystem/list.ts +33 -30
  1180. package/src/tools/filesystem/read.ts +54 -35
  1181. package/src/tools/filesystem/write.ts +69 -32
  1182. package/src/tools/host-filesystem/edit.test.ts +1 -0
  1183. package/src/tools/host-filesystem/edit.ts +44 -42
  1184. package/src/tools/host-filesystem/read.test.ts +1 -0
  1185. package/src/tools/host-filesystem/read.ts +49 -35
  1186. package/src/tools/host-filesystem/transfer.test.ts +31 -6
  1187. package/src/tools/host-filesystem/transfer.ts +121 -108
  1188. package/src/tools/host-filesystem/write.test.ts +1 -0
  1189. package/src/tools/host-filesystem/write.ts +33 -31
  1190. package/src/tools/host-terminal/host-shell.ts +50 -48
  1191. package/src/tools/mcp/mcp-tool-factory.ts +0 -2
  1192. package/src/tools/memory/register.ts +23 -24
  1193. package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
  1194. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  1195. package/src/tools/network/__tests__/web-search.test.ts +222 -6
  1196. package/src/tools/network/managed-search-proxy.ts +183 -0
  1197. package/src/tools/network/web-fetch.ts +49 -46
  1198. package/src/tools/network/web-search-error.test.ts +248 -0
  1199. package/src/tools/network/web-search-error.ts +267 -0
  1200. package/src/tools/network/web-search.ts +414 -97
  1201. package/src/tools/policy-context.ts +3 -1
  1202. package/src/tools/registry.ts +184 -118
  1203. package/src/tools/schedule/create.ts +14 -1
  1204. package/src/tools/schedule/update.ts +16 -0
  1205. package/src/tools/shared/filesystem/audio-read.ts +122 -0
  1206. package/src/tools/shared/filesystem/image-read.ts +1 -1
  1207. package/src/tools/skills/execute.ts +34 -31
  1208. package/src/tools/skills/load.ts +29 -23
  1209. package/src/tools/skills/skill-tool-factory.ts +17 -36
  1210. package/src/tools/subagent/notify-parent.ts +35 -32
  1211. package/src/tools/subagent/spawn.ts +2 -1
  1212. package/src/tools/system/avatar-generator.ts +13 -22
  1213. package/src/tools/system/request-permission.ts +30 -27
  1214. package/src/tools/terminal/safe-env.ts +10 -1
  1215. package/src/tools/terminal/shell.ts +190 -61
  1216. package/src/tools/tool-approval-handler.ts +10 -4
  1217. package/src/tools/tool-defaults.ts +20 -9
  1218. package/src/tools/tool-manifest.ts +4 -4
  1219. package/src/tools/tool-name-aliases.ts +72 -14
  1220. package/src/tools/types.ts +86 -33
  1221. package/src/tools/ui-surface/definitions.ts +196 -95
  1222. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  1223. package/src/tts/provider-catalog.ts +76 -1
  1224. package/src/types/onboarding-context.ts +6 -0
  1225. package/src/usage/attribution.ts +32 -1
  1226. package/src/usage/types.ts +10 -0
  1227. package/src/util/browser.ts +7 -2
  1228. package/src/util/errors.ts +2 -2
  1229. package/src/util/map-limit.ts +27 -0
  1230. package/src/util/mutex.ts +47 -0
  1231. package/src/util/platform.ts +15 -12
  1232. package/src/work-items/work-item-runner.ts +7 -2
  1233. package/src/workspace/git-service.ts +1 -42
  1234. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
  1235. package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
  1236. package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
  1237. package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
  1238. package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
  1239. package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
  1240. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  1241. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  1242. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  1243. package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
  1244. package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
  1245. package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
  1246. package/src/workspace/migrations/registry.ts +16 -0
  1247. package/src/workspace/provider-commit-message-generator.ts +15 -17
  1248. package/tsconfig.json +4 -1
  1249. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  1250. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  1251. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  1252. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -262
  1253. package/src/__tests__/empty-response-pipeline.test.ts +0 -301
  1254. package/src/__tests__/history-repair-pipeline.test.ts +0 -396
  1255. package/src/__tests__/llm-call-pipeline.test.ts +0 -281
  1256. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  1257. package/src/__tests__/persistence-pipeline.test.ts +0 -376
  1258. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  1259. package/src/__tests__/token-estimate-pipeline.test.ts +0 -481
  1260. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  1261. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  1262. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -344
  1263. package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
  1264. package/src/cli/commands/memory-v3-render.ts +0 -344
  1265. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  1266. package/src/daemon/message-types/disk-pressure.ts +0 -9
  1267. package/src/email/feature-gate.ts +0 -23
  1268. package/src/gallery/default-gallery.ts +0 -1359
  1269. package/src/gallery/gallery-manifest.ts +0 -28
  1270. package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
  1271. package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -468
  1272. package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
  1273. package/src/memory/v3/__tests__/edges.test.ts +0 -563
  1274. package/src/memory/v3/__tests__/filter.test.ts +0 -512
  1275. package/src/memory/v3/__tests__/gate.test.ts +0 -574
  1276. package/src/memory/v3/__tests__/index-composition.test.ts +0 -233
  1277. package/src/memory/v3/__tests__/loop.test.ts +0 -530
  1278. package/src/memory/v3/__tests__/retriever.test.ts +0 -226
  1279. package/src/memory/v3/__tests__/scouts.test.ts +0 -440
  1280. package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -312
  1281. package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
  1282. package/src/memory/v3/__tests__/traversal.test.ts +0 -469
  1283. package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
  1284. package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
  1285. package/src/memory/v3/__tests__/tree-walk.test.ts +0 -707
  1286. package/src/memory/v3/__tests__/validate.test.ts +0 -245
  1287. package/src/memory/v3/auto-edges.ts +0 -223
  1288. package/src/memory/v3/coactivation-store.ts +0 -124
  1289. package/src/memory/v3/consolidation-job.ts +0 -323
  1290. package/src/memory/v3/edge-learning-job.ts +0 -160
  1291. package/src/memory/v3/edges.ts +0 -249
  1292. package/src/memory/v3/filter.ts +0 -281
  1293. package/src/memory/v3/gate.ts +0 -334
  1294. package/src/memory/v3/index-composition.ts +0 -113
  1295. package/src/memory/v3/llm-capture.ts +0 -46
  1296. package/src/memory/v3/loop.ts +0 -382
  1297. package/src/memory/v3/maintenance.ts +0 -144
  1298. package/src/memory/v3/prompt-context.ts +0 -33
  1299. package/src/memory/v3/prompts/consolidation.ts +0 -458
  1300. package/src/memory/v3/prompts/system-prompts.ts +0 -196
  1301. package/src/memory/v3/retriever.ts +0 -33
  1302. package/src/memory/v3/scouts.ts +0 -420
  1303. package/src/memory/v3/shadow-middleware.ts +0 -305
  1304. package/src/memory/v3/traversal.ts +0 -206
  1305. package/src/memory/v3/tree-index.ts +0 -237
  1306. package/src/memory/v3/tree-store.ts +0 -394
  1307. package/src/memory/v3/tree-walk.ts +0 -351
  1308. package/src/memory/v3/types.ts +0 -65
  1309. package/src/memory/v3/validate.ts +0 -300
  1310. package/src/plugins/defaults/circuit-breaker.ts +0 -141
  1311. package/src/plugins/defaults/compaction.ts +0 -141
  1312. package/src/plugins/defaults/empty-response.ts +0 -124
  1313. package/src/plugins/defaults/history-repair.ts +0 -83
  1314. package/src/plugins/defaults/llm-call.ts +0 -77
  1315. package/src/plugins/defaults/memory-retrieval.ts +0 -219
  1316. package/src/plugins/defaults/overflow-reduce.ts +0 -185
  1317. package/src/plugins/defaults/persistence.ts +0 -127
  1318. package/src/plugins/defaults/title-generate.ts +0 -90
  1319. package/src/plugins/defaults/token-estimate.ts +0 -101
  1320. package/src/plugins/defaults/tool-error.ts +0 -119
  1321. package/src/plugins/defaults/tool-execute.ts +0 -87
  1322. package/src/plugins/defaults/tool-result-truncate.ts +0 -84
  1323. package/src/skills/category-inference.ts +0 -111
@@ -10,6 +10,7 @@ import {
10
10
  createAssistantMessage,
11
11
  createUserMessage,
12
12
  } from "../../agent/message-types.js";
13
+ import { ConversationMessageSchema } from "../../api/responses/conversation-message.js";
13
14
  import {
14
15
  CHANNEL_IDS,
15
16
  INTERFACE_IDS,
@@ -18,6 +19,7 @@ import {
18
19
  parseInterfaceId,
19
20
  supportsHostProxy,
20
21
  } from "../../channels/types.js";
22
+ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
21
23
  import { isHttpAuthDisabled } from "../../config/env.js";
22
24
  import { getConfig } from "../../config/loader.js";
23
25
  import {
@@ -26,6 +28,7 @@ import {
26
28
  } from "../../conversations/message-consolidation.js";
27
29
  import { createApprovalConversationGenerator } from "../../daemon/approval-generators.js";
28
30
  import type { Conversation } from "../../daemon/conversation.js";
31
+ import { persistQueuedMessageBody } from "../../daemon/conversation-messaging.js";
29
32
  import {
30
33
  buildModelInfoEvent,
31
34
  formatCleanResult,
@@ -40,10 +43,15 @@ import { getOrCreateConversation as getOrCreateConversationInstance } from "../.
40
43
  import { canonicalizeTimeZone } from "../../daemon/date-context.js";
41
44
  import {
42
45
  buildScanFirstMessage,
46
+ buildSelfIntroMessage,
43
47
  getCannedFirstGreeting,
44
48
  isWakeUpGreeting,
45
49
  } from "../../daemon/first-greeting.js";
46
- import { renderHistoryContent } from "../../daemon/handlers/shared.js";
50
+ import {
51
+ collectAttachmentRefs,
52
+ type HistoryAttachmentRef,
53
+ renderHistoryContent,
54
+ } from "../../daemon/handlers/shared.js";
47
55
  import { HostAppControlProxy } from "../../daemon/host-app-control-proxy.js";
48
56
  import { HostCuProxy } from "../../daemon/host-cu-proxy.js";
49
57
  import {
@@ -75,6 +83,7 @@ import {
75
83
  } from "../../memory/canonical-guardian-store.js";
76
84
  import {
77
85
  addMessage,
86
+ extractImageSourcePaths,
78
87
  getConversation,
79
88
  getMessages,
80
89
  getMessagesPaginated,
@@ -82,8 +91,6 @@ import {
82
91
  type MessageRow,
83
92
  provenanceFromTrustContext,
84
93
  setConversationInferenceProfile,
85
- setConversationOriginChannelIfUnset,
86
- setConversationOriginInterfaceIfUnset,
87
94
  } from "../../memory/conversation-crud.js";
88
95
  import {
89
96
  getConversationByKey,
@@ -103,10 +110,15 @@ import type { Provider } from "../../providers/types.js";
103
110
  import { checkIngressForSecrets } from "../../security/secret-ingress.js";
104
111
  import { getSubagentManager } from "../../subagent/index.js";
105
112
  import { getLogger } from "../../util/logger.js";
106
- import { getWorkspacePromptPath } from "../../util/platform.js";
113
+ import {
114
+ getWorkspaceDir,
115
+ getWorkspacePromptPath,
116
+ } from "../../util/platform.js";
107
117
  import { silentlyWithLog } from "../../util/silently.js";
108
118
  import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
109
119
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
120
+ import { getPersistedSeq } from "../assistant-stream-state.js";
121
+ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
110
122
  import { routeGuardianReply } from "../guardian-reply-router.js";
111
123
  import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
112
124
  import type {
@@ -131,6 +143,10 @@ import {
131
143
  NotFoundError,
132
144
  RouteError,
133
145
  } from "./errors.js";
146
+ import {
147
+ collectPendingConfirmations,
148
+ enrichToolCallsWithConfirmation,
149
+ } from "./tool-call-confirmation-enrichment.js";
134
150
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
135
151
  import { RouteResponse } from "./types.js";
136
152
 
@@ -140,6 +156,128 @@ const log = getLogger("conversation-routes");
140
156
  const NO_RESPONSE_INLINE_RE = /<no_response\s*\/?>/g;
141
157
  const ATTACHMENT_ENTRY_RE = /^attachment:(\d+)$/;
142
158
 
159
+ /** Rewrites a rendered `contentOrder` to reflect attachment alignment. */
160
+ type ContentOrderRewrite = (contentOrder: string[]) => string[];
161
+
162
+ interface AlignedAttachments {
163
+ /** Hydrated rows, reordered to match the inline file-block order. */
164
+ attachments: RuntimeAttachmentMetadata[];
165
+ /**
166
+ * Resolves a content-walk attachment ref index to its hydrated DB row,
167
+ * mirroring the inline placement `rewriteContentOrder` encodes. Refs with no
168
+ * inline placement (unmatched ids, count mismatch, no DB rows) are absent, so
169
+ * `renderHistoryContent` emits no `attachment` block for them — the row still
170
+ * ships via the flat `attachments` array.
171
+ */
172
+ refIndexToAttachment: Map<number, RuntimeAttachmentMetadata>;
173
+ rewriteContentOrder: ContentOrderRewrite;
174
+ }
175
+
176
+ /**
177
+ * Align DB-hydrated attachment rows with the file-block refs `renderHistoryContent`
178
+ * captured. When a file block was persisted with `_attachmentId` (user-message
179
+ * uploads) we join on that id to position the chip inline; DB rows without a
180
+ * matching ref go to the tail as orphan chips, and unmatched refs drop their
181
+ * `attachment:N` entry. Assistant-authored file blocks carry no `_attachmentId`,
182
+ * so when no ids match we fall back to positional alignment if the ref and row
183
+ * counts agree; otherwise we strip the markers and let chips fall to the tail.
184
+ */
185
+ function alignAttachments(
186
+ attachmentRefs: HistoryAttachmentRef[],
187
+ attachments: RuntimeAttachmentMetadata[],
188
+ ): AlignedAttachments {
189
+ const refIndexToAttachment = new Map<number, RuntimeAttachmentMetadata>();
190
+ const identity: ContentOrderRewrite = (contentOrder) => contentOrder;
191
+ const stripAttachmentEntries: ContentOrderRewrite = (contentOrder) =>
192
+ contentOrder.filter((entry) => !ATTACHMENT_ENTRY_RE.test(entry));
193
+
194
+ if (attachmentRefs.length === 0) {
195
+ return { attachments, refIndexToAttachment, rewriteContentOrder: identity };
196
+ }
197
+ if (attachments.length === 0) {
198
+ // Refs were captured but no DB rows came back — drop the contentOrder
199
+ // entries to avoid out-of-bounds renders.
200
+ return {
201
+ attachments,
202
+ refIndexToAttachment,
203
+ rewriteContentOrder: stripAttachmentEntries,
204
+ };
205
+ }
206
+
207
+ const byId = new Map<string, number>();
208
+ attachments.forEach((att, idx) => {
209
+ if (att.id) byId.set(att.id, idx);
210
+ });
211
+ const consumed = new Set<number>();
212
+ const orderedRowIdx: Array<number | null> = attachmentRefs.map((ref) => {
213
+ if (!ref.attachmentId) return null;
214
+ const idx = byId.get(ref.attachmentId);
215
+ if (idx === undefined || consumed.has(idx)) return null;
216
+ consumed.add(idx);
217
+ return idx;
218
+ });
219
+ const matchedRows = orderedRowIdx.filter(
220
+ (idx): idx is number => idx !== null,
221
+ );
222
+
223
+ if (matchedRows.length > 0) {
224
+ const orphanRows: number[] = [];
225
+ for (let i = 0; i < attachments.length; i++) {
226
+ if (!consumed.has(i)) orphanRows.push(i);
227
+ }
228
+ const reordered = [
229
+ ...matchedRows.map((i) => attachments[i]),
230
+ ...orphanRows.map((i) => attachments[i]),
231
+ ];
232
+ const refToNewIdx = new Map<number, number>();
233
+ let nextIdx = 0;
234
+ orderedRowIdx.forEach((rowIdx, refIdx) => {
235
+ if (rowIdx !== null) {
236
+ refToNewIdx.set(refIdx, nextIdx);
237
+ refIndexToAttachment.set(refIdx, reordered[nextIdx]);
238
+ nextIdx++;
239
+ }
240
+ });
241
+ const rewriteContentOrder: ContentOrderRewrite = (contentOrder) =>
242
+ contentOrder
243
+ .map((entry) => {
244
+ const match = entry.match(ATTACHMENT_ENTRY_RE);
245
+ if (!match) return entry;
246
+ const remapped = refToNewIdx.get(Number(match[1]));
247
+ return remapped !== undefined ? `attachment:${remapped}` : undefined;
248
+ })
249
+ .filter((e): e is string => e !== undefined);
250
+ return {
251
+ attachments: reordered,
252
+ refIndexToAttachment,
253
+ rewriteContentOrder,
254
+ };
255
+ }
256
+
257
+ if (attachmentRefs.length !== attachments.length) {
258
+ // No ref carried an attachmentId we could match and the counts disagree, so
259
+ // positional mapping can't be trusted — strip any attachment:N entries so
260
+ // the client doesn't position attachments inline against a misaligned array
261
+ // (they fall to the tail instead).
262
+ return {
263
+ attachments,
264
+ refIndexToAttachment,
265
+ rewriteContentOrder: stripAttachmentEntries,
266
+ };
267
+ }
268
+
269
+ // No ref matched an id but the counts agree (the assistant-authored case):
270
+ // the Nth marker maps to the Nth row positionally, so the original
271
+ // contentOrder is left untouched.
272
+ attachmentRefs.forEach((_ref, refIdx) => {
273
+ refIndexToAttachment.set(refIdx, attachments[refIdx]);
274
+ });
275
+ return { attachments, refIndexToAttachment, rewriteContentOrder: identity };
276
+ }
277
+
278
+ /** Feature flag gating the self-intro first message (see first-greeting.ts). */
279
+ const SELF_INTRO_GREETING_FLAG = "self-intro-greeting" as const;
280
+
143
281
  const SUGGESTION_CACHE_MAX = 100;
144
282
  const VALID_RISK_THRESHOLDS = ["none", "low", "medium", "high"] as const;
145
283
  type RiskThreshold = (typeof VALID_RISK_THRESHOLDS)[number];
@@ -395,23 +533,10 @@ async function tryConsumeCanonicalGuardianReply(params: {
395
533
  // is not re-processed as a new user turn.
396
534
  let messageId: string | undefined;
397
535
  try {
398
- const guardianImageSourcePaths: Record<string, string> = {};
399
- for (let i = 0; i < attachments.length; i++) {
400
- const a = attachments[i];
401
- if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
402
- guardianImageSourcePaths[`${i}:${a.filename}`] = a.filePath;
403
- }
404
- }
405
- const channelMeta = {
406
- userMessageChannel: sourceChannel,
407
- assistantMessageChannel: sourceChannel,
408
- userMessageInterface: sourceInterface,
409
- assistantMessageInterface: sourceInterface,
410
- provenanceTrustClass: "guardian" as const,
411
- ...(Object.keys(guardianImageSourcePaths).length > 0
412
- ? { imageSourcePaths: guardianImageSourcePaths }
413
- : {}),
414
- };
536
+ const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
537
+ provenanceOverride: { provenanceTrustClass: "guardian" },
538
+ attachments,
539
+ });
415
540
 
416
541
  const cleanUserMessage = createUserMessage(content, attachments);
417
542
  const llmUserMessage = enrichMessageWithSourcePaths(
@@ -422,7 +547,7 @@ async function tryConsumeCanonicalGuardianReply(params: {
422
547
  conversationId,
423
548
  "user",
424
549
  JSON.stringify(cleanUserMessage.content),
425
- channelMeta,
550
+ { metadata: channelMeta },
426
551
  );
427
552
  messageId = persistedUser.id;
428
553
 
@@ -436,7 +561,7 @@ async function tryConsumeCanonicalGuardianReply(params: {
436
561
  conversationId,
437
562
  "assistant",
438
563
  JSON.stringify(assistantMessage.content),
439
- channelMeta,
564
+ { metadata: channelMeta },
440
565
  );
441
566
 
442
567
  // Avoid mutating in-memory history / emitting stream deltas while a run is active.
@@ -516,6 +641,7 @@ export function handleListMessages({
516
641
  hasMore: false,
517
642
  oldestTimestamp: null,
518
643
  oldestMessageId: null,
644
+ seq: null,
519
645
  };
520
646
  }
521
647
  return { messages: [] };
@@ -538,6 +664,10 @@ export function handleListMessages({
538
664
 
539
665
  let rawMessages: MessageRow[];
540
666
  let hasMore = false;
667
+ // Resume cursor surfaced when the paginated scan stops on its row cap with a
668
+ // (possibly empty) page — lets us still emit an oldest cursor so the client
669
+ // can request the next window instead of stalling.
670
+ let scanResumeCursor: { createdAt: number; id: string } | undefined;
541
671
 
542
672
  // Drop messages flagged as hidden in metadata (e.g. internal scaffolding
543
673
  // like retrospective instructions). The LLM-side history loader
@@ -550,7 +680,14 @@ export function handleListMessages({
550
680
  // assistant message has tool_use blocks but its matching user tool_result
551
681
  // is left visible, the result will render as a standalone orphan because
552
682
  // `mergeToolResultsIntoAssistantMessages` has nothing to merge it into.
553
- const visibleFilter = (m: MessageRow) => !isHiddenMessage(m.metadata);
683
+ //
684
+ // Only renderable roles reach this UI-facing transcript. `system` rows (a
685
+ // permitted `MessageRole`, e.g. skill-authored context) are agent-context
686
+ // scaffolding, never a displayed turn, so they are dropped here at the
687
+ // source rather than narrowed away per-client.
688
+ const visibleFilter = (m: MessageRow) =>
689
+ !isHiddenMessage(m.metadata) &&
690
+ (m.role === "user" || m.role === "assistant");
554
691
 
555
692
  if (isPaginated) {
556
693
  const result = getMessagesPaginated(
@@ -561,6 +698,7 @@ export function handleListMessages({
561
698
  );
562
699
  rawMessages = result.messages;
563
700
  hasMore = result.hasMore;
701
+ scanResumeCursor = result.nextCursor;
564
702
  } else {
565
703
  rawMessages = getMessages(resolvedConversationId).filter(visibleFilter);
566
704
  }
@@ -583,7 +721,10 @@ export function handleListMessages({
583
721
  mergeConsecutiveAssistantMessages(mergedMessages);
584
722
  const assistantSlackDisplayName = getAssistantName()?.trim() || undefined;
585
723
 
586
- // Parse content blocks and extract text + tool calls
724
+ // Parse each row's stored content and per-message metadata. Rendering is
725
+ // deferred to the serializer pass below so it runs after attachment
726
+ // alignment, letting renderHistoryContent inline `attachment` blocks during
727
+ // its single content walk.
587
728
  const parsed = consolidatedMessages.map((msg) => {
588
729
  let content: unknown;
589
730
  try {
@@ -591,7 +732,6 @@ export function handleListMessages({
591
732
  } catch {
592
733
  content = msg.content;
593
734
  }
594
- const rendered = renderHistoryContent(content);
595
735
 
596
736
  // Extract sentAt from metadata for display timestamps. When a message
597
737
  // was queued or its persistence was delayed (long assistant generation),
@@ -639,94 +779,51 @@ export function handleListMessages({
639
779
  },
640
780
  );
641
781
 
642
- // Strip <no_response/> markers from assistant messages so web/API
643
- // clients never see the raw sentinel. Only assistant messages produce
644
- // this marker; user messages are left untouched.
645
- if (msg.role === "assistant") {
646
- const originalSegments = rendered.textSegments;
647
- const keepIndices: number[] = [];
648
- const filteredSegments: string[] = [];
649
- for (let i = 0; i < originalSegments.length; i++) {
650
- const cleaned = originalSegments[i]
651
- .replace(NO_RESPONSE_INLINE_RE, "")
652
- .trim();
653
- if (cleaned.length > 0) {
654
- keepIndices.push(i);
655
- filteredSegments.push(cleaned);
656
- }
657
- }
658
- // Remap contentOrder text:N indices to account for removed segments
659
- const indexMap = new Map<number, number>();
660
- keepIndices.forEach((oldIdx, newIdx) => indexMap.set(oldIdx, newIdx));
661
- const filteredContentOrder = rendered.contentOrder
662
- .map((entry) => {
663
- const m = entry.match(/^text:(\d+)$/);
664
- if (!m) return entry;
665
- const newIdx = indexMap.get(Number(m[1]));
666
- return newIdx !== undefined ? `text:${newIdx}` : undefined;
667
- })
668
- .filter((e): e is string => e !== undefined);
669
-
670
- return {
671
- role: msg.role,
672
- text: rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
673
- timestamp: msg.createdAt,
674
- sentAt,
675
- toolCalls: rendered.toolCalls,
676
- toolCallsBeforeText: rendered.toolCallsBeforeText,
677
- textSegments: filteredSegments,
678
- contentOrder: filteredContentOrder,
679
- surfaces: rendered.surfaces,
680
- attachmentRefs: rendered.attachments,
681
- slackMessage,
682
- ...(rendered.thinkingSegments.length > 0
683
- ? { thinkingSegments: rendered.thinkingSegments }
684
- : {}),
685
- id: msg.id,
686
- subagentNotification,
687
- };
688
- }
782
+ // `visibleFilter` has already dropped every non-renderable role, so the
783
+ // only values reaching here are `user` and `assistant`; narrow the raw DB
784
+ // `string` to the wire union.
785
+ const role: "user" | "assistant" =
786
+ msg.role === "assistant" ? "assistant" : "user";
689
787
 
690
788
  return {
691
- role: msg.role,
692
- text: rendered.text,
693
- timestamp: msg.createdAt,
694
- sentAt,
695
- toolCalls: rendered.toolCalls,
696
- toolCallsBeforeText: rendered.toolCallsBeforeText,
697
- textSegments: rendered.textSegments,
698
- contentOrder: rendered.contentOrder,
699
- surfaces: rendered.surfaces,
700
- attachmentRefs: rendered.attachments,
701
- slackMessage,
702
- ...(rendered.thinkingSegments.length > 0
703
- ? { thinkingSegments: rendered.thinkingSegments }
704
- : {}),
705
789
  id: msg.id,
790
+ role,
791
+ content,
792
+ createdAt: msg.createdAt,
793
+ sentAt,
706
794
  subagentNotification,
795
+ slackMessage,
707
796
  };
708
797
  });
709
798
 
799
+ // Confirmation context layered onto rendered tool calls at render time: the
800
+ // derived scope ladder for scope-aware tools, and any in-flight prompt read
801
+ // from the pending-interactions registry. Both are computed once per request
802
+ // and applied per message below.
803
+ const workspaceDir = getWorkspaceDir();
804
+ const pendingConfirmations = collectPendingConfirmations(
805
+ resolvedConversationId,
806
+ );
807
+
710
808
  const messages: RuntimeMessagePayload[] = parsed.map((m) => {
809
+ const mergedMessageIds = m.id ? (mergedIdMap.get(m.id) ?? []) : [];
810
+
811
+ // Hydrate the row's attachments from the DB. A metadata-only query avoids
812
+ // loading large base64 blobs for non-image attachments (documents, audio);
813
+ // full data is fetched only for images so the client can generate
814
+ // thumbnails for inline display on history restore. Merged messages
815
+ // (consecutive assistant merge) are queried too so their attachments
816
+ // aren't lost before DB compaction relinks them.
711
817
  let msgAttachments: RuntimeAttachmentMetadata[] = [];
712
818
  if (m.id) {
713
- // Use metadata-only query first to avoid loading large base64
714
- // blobs for non-image attachments (documents, audio). Then
715
- // selectively fetch full data only for images so the client can
716
- // generate thumbnails for inline display on history restore.
717
- // Also query attachments for any messages that were merged into
718
- // this one (consecutive assistant merge), so their attachments
719
- // aren't lost before DB compaction relinks them.
720
- const idsToQuery = [m.id, ...(mergedIdMap.get(m.id) ?? [])];
819
+ const idsToQuery = [m.id, ...mergedMessageIds];
721
820
  const linked = idsToQuery.flatMap((id) =>
722
821
  getAttachmentMetadataForMessage(id),
723
822
  );
724
823
  if (linked.length > 0) {
725
824
  msgAttachments = linked.map((a) => {
726
825
  if (a.mimeType.startsWith("image/")) {
727
- const full = getAttachmentById(a.id, {
728
- hydrateFileData: true,
729
- });
826
+ const full = getAttachmentById(a.id, { hydrateFileData: true });
730
827
  return {
731
828
  id: a.id,
732
829
  filename: a.originalFilename,
@@ -753,100 +850,101 @@ export function handleListMessages({
753
850
  }
754
851
  }
755
852
 
756
- // Align msgAttachments order with the file-block order captured by
757
- // renderHistoryContent. When a file block was persisted with
758
- // `_attachmentId`, we can join on that id to position the chip inline
759
- // (the `attachment:N` entries in contentOrder index into msgAttachments).
760
- // DB rows without a matching ref go to the tail as orphan chips;
761
- // unmatched refs drop their contentOrder entry and trigger a remap.
762
- let alignedContentOrder = m.contentOrder;
763
- if (
764
- m.attachmentRefs.length > 0 &&
765
- msgAttachments.length > 0 &&
766
- m.contentOrder.length > 0
767
- ) {
768
- const byId = new Map<string, number>();
769
- msgAttachments.forEach((att, idx) => {
770
- if (att.id) byId.set(att.id, idx);
771
- });
772
- const consumed = new Set<number>();
773
- const orderedRowIdx: Array<number | null> = m.attachmentRefs.map(
774
- (ref) => {
775
- if (!ref.attachmentId) return null;
776
- const idx = byId.get(ref.attachmentId);
777
- if (idx === undefined || consumed.has(idx)) return null;
778
- consumed.add(idx);
779
- return idx;
780
- },
781
- );
782
- const matchedRows = orderedRowIdx.filter(
783
- (idx): idx is number => idx !== null,
784
- );
785
- if (matchedRows.length > 0) {
786
- const orphanRows: number[] = [];
787
- for (let i = 0; i < msgAttachments.length; i++) {
788
- if (!consumed.has(i)) orphanRows.push(i);
853
+ // Align the hydrated rows with the file-block refs, then render. Rendering
854
+ // after alignment lets renderHistoryContent inline each `attachment` block
855
+ // during its single content walk, so `contentBlocks` comes back ready to
856
+ // ship with no post-processing. The aligned reorder/rewrite keeps the
857
+ // legacy `attachments` array and `contentOrder` positions consistent.
858
+ const attachmentRefs = collectAttachmentRefs(m.content);
859
+ const aligned = alignAttachments(attachmentRefs, msgAttachments);
860
+ msgAttachments = aligned.attachments;
861
+ const attachmentBlocks = attachmentRefs.map(
862
+ (_ref, refIdx) => aligned.refIndexToAttachment.get(refIdx) ?? null,
863
+ );
864
+ const rendered = renderHistoryContent(
865
+ m.content,
866
+ attachmentBlocks,
867
+ m.id ?? undefined,
868
+ );
869
+
870
+ const toolCalls = enrichToolCallsWithConfirmation(rendered.toolCalls, {
871
+ workspaceDir,
872
+ pendingConfirmations,
873
+ });
874
+
875
+ // Strip <no_response/> markers from assistant messages so web/API clients
876
+ // never see the raw sentinel. Only assistant messages produce it; user
877
+ // messages are untouched. The filter is applied consistently to the flat
878
+ // text, the segments, the contentOrder text refs, and the text blocks of
879
+ // contentBlocks.
880
+ let text = rendered.text;
881
+ let textSegments = rendered.textSegments;
882
+ let contentOrder = rendered.contentOrder;
883
+ let contentBlocks = rendered.contentBlocks;
884
+ if (m.role === "assistant") {
885
+ const keepIndices: number[] = [];
886
+ const filteredSegments: string[] = [];
887
+ for (let i = 0; i < rendered.textSegments.length; i++) {
888
+ const cleaned = rendered.textSegments[i]
889
+ .replace(NO_RESPONSE_INLINE_RE, "")
890
+ .trim();
891
+ if (cleaned.length > 0) {
892
+ keepIndices.push(i);
893
+ filteredSegments.push(cleaned);
789
894
  }
790
- msgAttachments = [
791
- ...matchedRows.map((i) => msgAttachments[i]),
792
- ...orphanRows.map((i) => msgAttachments[i]),
793
- ];
794
- const refToNewIdx = new Map<number, number>();
795
- let nextIdx = 0;
796
- orderedRowIdx.forEach((rowIdx, refIdx) => {
797
- if (rowIdx !== null) {
798
- refToNewIdx.set(refIdx, nextIdx);
799
- nextIdx++;
800
- }
801
- });
802
- alignedContentOrder = m.contentOrder
803
- .map((entry) => {
804
- const match = entry.match(ATTACHMENT_ENTRY_RE);
805
- if (!match) return entry;
806
- const remapped = refToNewIdx.get(Number(match[1]));
807
- return remapped !== undefined
808
- ? `attachment:${remapped}`
809
- : undefined;
810
- })
811
- .filter((e): e is string => e !== undefined);
812
- } else {
813
- // No refs carried an attachmentId we could match — strip any
814
- // attachment:N entries so the client doesn't try to position
815
- // attachments inline against a misaligned array.
816
- alignedContentOrder = m.contentOrder.filter(
817
- (entry) => !ATTACHMENT_ENTRY_RE.test(entry),
818
- );
819
895
  }
820
- } else if (m.attachmentRefs.length > 0 && msgAttachments.length === 0) {
821
- // Refs were captured but no DB rows came back — drop the
822
- // contentOrder entries to avoid out-of-bounds renders.
823
- alignedContentOrder = m.contentOrder.filter(
824
- (entry) => !ATTACHMENT_ENTRY_RE.test(entry),
825
- );
896
+ const indexMap = new Map<number, number>();
897
+ keepIndices.forEach((oldIdx, newIdx) => indexMap.set(oldIdx, newIdx));
898
+ contentOrder = rendered.contentOrder
899
+ .map((entry) => {
900
+ const tm = entry.match(/^text:(\d+)$/);
901
+ if (!tm) return entry;
902
+ const newIdx = indexMap.get(Number(tm[1]));
903
+ return newIdx !== undefined ? `text:${newIdx}` : undefined;
904
+ })
905
+ .filter((e): e is string => e !== undefined);
906
+ textSegments = filteredSegments;
907
+ text = rendered.text.replace(NO_RESPONSE_INLINE_RE, "").trim();
908
+ contentBlocks = rendered.contentBlocks
909
+ .map((block) =>
910
+ block.type === "text"
911
+ ? {
912
+ type: "text" as const,
913
+ text: block.text.replace(NO_RESPONSE_INLINE_RE, "").trim(),
914
+ }
915
+ : block,
916
+ )
917
+ .filter((block) => block.type !== "text" || block.text.length > 0);
826
918
  }
827
919
 
828
- // Use sentAt (actual event time) for the display timestamp when
829
- // available, falling back to createdAt (persistence time).
830
- // Note: clients use this display timestamp as their pagination cursor
831
- // after memory-pressure trimming, while server-side pagination filters
832
- // on createdAt. The mismatch is benign — it may return slightly extra
833
- // data on a page boundary but never loses messages.
834
- const displayTimestamp = m.sentAt ?? m.timestamp;
920
+ const alignedContentOrder = aligned.rewriteContentOrder(contentOrder);
921
+
922
+ // Use sentAt (actual event time) for the display timestamp when available,
923
+ // falling back to createdAt (persistence time). Clients use this display
924
+ // timestamp as their pagination cursor after memory-pressure trimming,
925
+ // while server-side pagination filters on createdAt. The mismatch is
926
+ // benign — it may return slightly extra data on a page boundary but never
927
+ // loses messages.
928
+ const displayTimestamp = m.sentAt ?? m.createdAt;
835
929
  return {
836
930
  id: m.id ?? "",
931
+ ...(mergedMessageIds.length > 0 ? { mergedMessageIds } : {}),
837
932
  role: m.role,
838
- content: m.text,
933
+ // Flat plain-text body the legacy Swift client reads directly; see the
934
+ // `content` field on ConversationMessageSchema for why this must stay.
935
+ content: text,
839
936
  timestamp: new Date(displayTimestamp).toISOString(),
840
937
  attachments: msgAttachments,
841
- ...(m.toolCalls.length > 0 ? { toolCalls: m.toolCalls } : {}),
842
- ...(m.surfaces.length > 0 ? { surfaces: m.surfaces } : {}),
843
- ...(m.textSegments.length > 0 ? { textSegments: m.textSegments } : {}),
844
- ...(m.thinkingSegments?.length
845
- ? { thinkingSegments: m.thinkingSegments }
938
+ ...(toolCalls.length > 0 ? { toolCalls } : {}),
939
+ ...(rendered.surfaces.length > 0 ? { surfaces: rendered.surfaces } : {}),
940
+ ...(textSegments.length > 0 ? { textSegments } : {}),
941
+ ...(rendered.thinkingSegments.length > 0
942
+ ? { thinkingSegments: rendered.thinkingSegments }
846
943
  : {}),
847
944
  ...(alignedContentOrder.length > 0
848
945
  ? { contentOrder: alignedContentOrder }
849
946
  : {}),
947
+ ...(contentBlocks.length > 0 ? { contentBlocks } : {}),
850
948
  ...(m.subagentNotification
851
949
  ? { subagentNotification: m.subagentNotification }
852
950
  : {}),
@@ -854,11 +952,24 @@ export function handleListMessages({
854
952
  };
855
953
  });
856
954
 
955
+ // Snapshot↔stream alignment token: the `seq` of the last event whose
956
+ // content is durably persisted for this conversation in the current
957
+ // daemon process. Returned on every resolved-conversation response so a
958
+ // client can apply only stream events with a higher `seq`. Null when
959
+ // nothing has been persisted in-process (cold/aged-out/post-restart).
960
+ const persistedSeq = getPersistedSeq(resolvedConversationId);
961
+
857
962
  if (isPaginated) {
963
+ // Prefer the page's oldest visible row (the documented cursor semantic).
964
+ // When a scan-cap-truncated page comes back empty there's no visible row
965
+ // to anchor on, so fall back to the resume cursor so the client still gets
966
+ // a `(timestamp, id)` to continue paginating from instead of stalling.
858
967
  const oldestTimestamp =
859
- rawMessages.length > 0 ? rawMessages[0].createdAt : undefined;
968
+ rawMessages.length > 0
969
+ ? rawMessages[0].createdAt
970
+ : scanResumeCursor?.createdAt;
860
971
  const oldestMessageId =
861
- rawMessages.length > 0 ? rawMessages[0].id : undefined;
972
+ rawMessages.length > 0 ? rawMessages[0].id : scanResumeCursor?.id;
862
973
  // `page=latest` always emits both metadata fields so the web client has
863
974
  // a stable contract; emit `null` when the conversation is empty.
864
975
  // The existing `beforeTimestamp` branch keeps its conditional shape to
@@ -869,6 +980,7 @@ export function handleListMessages({
869
980
  hasMore,
870
981
  oldestTimestamp: oldestTimestamp ?? null,
871
982
  oldestMessageId: oldestMessageId ?? null,
983
+ seq: persistedSeq,
872
984
  };
873
985
  }
874
986
 
@@ -877,10 +989,11 @@ export function handleListMessages({
877
989
  hasMore,
878
990
  ...(oldestTimestamp != null ? { oldestTimestamp } : {}),
879
991
  ...(oldestMessageId != null ? { oldestMessageId } : {}),
992
+ seq: persistedSeq,
880
993
  };
881
994
  }
882
995
 
883
- return { messages };
996
+ return { messages, seq: persistedSeq };
884
997
  }
885
998
 
886
999
  /**
@@ -1002,13 +1115,15 @@ export async function handleSendMessage(
1002
1115
  cohort?: string;
1003
1116
  websiteUrl?: string;
1004
1117
  contentSourceUrl?: string;
1118
+ bootstrapTemplate?: string;
1119
+ initialMessage?: string;
1120
+ skills?: string[];
1005
1121
  };
1006
1122
  };
1007
1123
 
1008
1124
  const actorPrincipalId = headers?.["x-vellum-actor-principal-id"];
1009
1125
  const principalType = headers?.["x-vellum-principal-type"];
1010
- const originClientId =
1011
- headers?.["x-vellum-client-id"]?.trim() || undefined;
1126
+ const originClientId = headers?.["x-vellum-client-id"]?.trim() || undefined;
1012
1127
 
1013
1128
  const { conversationKey, content, attachmentIds } = body;
1014
1129
  const inboundConversationId =
@@ -1149,10 +1264,13 @@ export async function handleSendMessage(
1149
1264
  // assistant-minted internal id) when the client supplies it — clients
1150
1265
  // must obtain this id from a prior daemon response, so a missing row
1151
1266
  // is a 404. Otherwise fall through to the external-key path: the
1152
- // client-supplied `conversationKey` (used by non-vellum channels and
1153
- // the web idempotency flow) or, when neither is provided, a stable
1154
- // default keyed on sourceChannel + sourceInterface so repeated calls
1155
- // from the same channel/interface share a single thread.
1267
+ // client-supplied `conversationKey` (external-key lookup; materializes
1268
+ // on first use) or, when neither is provided, a channel-dependent
1269
+ // default. The vellum channel mints a fresh conversation on every
1270
+ // empty-handed send so first-message-of-a-new-chat surfaces with a
1271
+ // server-minted id; other channels (phone, slack, …) share a stable
1272
+ // `default:<channel>:<interface>` thread so repeated calls from the
1273
+ // same channel/interface stay co-located.
1156
1274
  let mapping: {
1157
1275
  conversationId: string;
1158
1276
  conversationType: string;
@@ -1174,7 +1292,9 @@ export async function handleSendMessage(
1174
1292
  const resolvedConversationKey =
1175
1293
  conversationKey && conversationKey.length > 0
1176
1294
  ? conversationKey
1177
- : `default:${sourceChannel}:${sourceInterface}`;
1295
+ : sourceChannel === "vellum"
1296
+ ? crypto.randomUUID()
1297
+ : `default:${sourceChannel}:${sourceInterface}`;
1178
1298
  mapping = getOrCreateConversation(resolvedConversationKey, {
1179
1299
  conversationType: "standard",
1180
1300
  });
@@ -1318,10 +1438,20 @@ export async function handleSendMessage(
1318
1438
  }
1319
1439
 
1320
1440
  const isInteractive = isInteractiveInterface(sourceInterface);
1441
+ // Use the JWT-verified requester principal — not guardianPrincipalId,
1442
+ // which is the workspace owner and would let a trusted contact's web
1443
+ // turn match against the guardian's macOS client.
1444
+ const sourceActorPrincipalId = actorPrincipalId ?? undefined;
1321
1445
  // Bash/File/Transfer singletons are globally available via isAvailable() —
1322
1446
  // no per-conversation gating needed. CU is per-conversation (owns step
1323
1447
  // count, AX tree history, loop detection).
1324
- if (shouldAttachHostProxyForCapability("host_cu", sourceInterface)) {
1448
+ if (
1449
+ shouldAttachHostProxyForCapability(
1450
+ "host_cu",
1451
+ sourceInterface,
1452
+ sourceActorPrincipalId,
1453
+ )
1454
+ ) {
1325
1455
  if (!conversation.isProcessing() || !conversation.hostCuProxy) {
1326
1456
  conversation.setHostCuProxy(new HostCuProxy());
1327
1457
  }
@@ -1334,7 +1464,13 @@ export async function handleSendMessage(
1334
1464
  // lives in the skill-projection layer (which reads the `feature-flag:
1335
1465
  // app-control` declaration in SKILL.md frontmatter), so an attached proxy
1336
1466
  // is harmless when the flag resolves to off.
1337
- if (shouldAttachHostProxyForCapability("host_app_control", sourceInterface)) {
1467
+ if (
1468
+ shouldAttachHostProxyForCapability(
1469
+ "host_app_control",
1470
+ sourceInterface,
1471
+ sourceActorPrincipalId,
1472
+ )
1473
+ ) {
1338
1474
  if (!conversation.isProcessing() || !conversation.hostAppControlProxy) {
1339
1475
  conversation.setHostAppControlProxy(
1340
1476
  new HostAppControlProxy(mapping.conversationId),
@@ -1347,7 +1483,11 @@ export async function handleSendMessage(
1347
1483
  // this message will be queued and preactivation is deferred to dequeue
1348
1484
  // time in drainQueueImpl to avoid mutating in-flight turn state.
1349
1485
  if (!conversation.isProcessing()) {
1350
- preactivateHostProxySkills(conversation, sourceInterface);
1486
+ preactivateHostProxySkills(
1487
+ conversation,
1488
+ sourceInterface,
1489
+ sourceActorPrincipalId,
1490
+ );
1351
1491
  }
1352
1492
  // Wire sendToClient to the SSE hub so all subsystems can reach the HTTP client.
1353
1493
  // hasNoClient must remain `!isInteractive` so downstream tool gating
@@ -1380,6 +1520,16 @@ export async function handleSendMessage(
1380
1520
  conversation.getMessages().length,
1381
1521
  );
1382
1522
  const isScanPath = !!scanUrl && isWakeUp;
1523
+ // Self-intro path: when we know a name, send a natural introduction on the
1524
+ // user's behalf instead of the canned greeting, so the assistant generates a
1525
+ // real first response. Gated behind the `self-intro-greeting` flag (default
1526
+ // off); `undefined` (flag off or no names) falls back to the canned path.
1527
+ const selfIntroGreetingEnabled =
1528
+ isWakeUp &&
1529
+ isAssistantFeatureFlagEnabled(SELF_INTRO_GREETING_FLAG, getConfig());
1530
+ const selfIntro = selfIntroGreetingEnabled
1531
+ ? buildSelfIntroMessage(body.onboarding ?? undefined)
1532
+ : undefined;
1383
1533
 
1384
1534
  let effectiveContent: string | undefined;
1385
1535
  if (isScanPath) {
@@ -1388,55 +1538,46 @@ export async function handleSendMessage(
1388
1538
  : ("content-source" as const);
1389
1539
  effectiveContent = buildScanFirstMessage(scanUrl, scanVariant);
1390
1540
  // Fall through to normal inference path below
1391
- } else if (isWakeUp && body.onboarding?.cohort === "content-automation") {
1392
- effectiveContent = "I want to write articles that rank better in GEO";
1393
- // Fall through to normal inference path — the bootstrap template
1394
- // and geo-writing skill handle this message.
1541
+ } else if (selfIntroGreetingEnabled && body.onboarding?.initialMessage) {
1542
+ effectiveContent = body.onboarding.initialMessage;
1543
+ } else if (isWakeUp && selfIntro) {
1544
+ // Rewrite to the self-introduction and fall through to real inference
1545
+ // (mirrors the scan path above).
1546
+ effectiveContent = selfIntro;
1395
1547
  } else if (isWakeUp) {
1396
1548
  const cannedGreeting = getCannedFirstGreeting(body.onboarding ?? undefined);
1397
1549
 
1398
- conversation.processing = true;
1550
+ conversation.setProcessing(true);
1399
1551
  let cleanupDeferred = false;
1400
1552
  try {
1401
- const provenance = provenanceFromTrustContext(conversation.trustContext);
1402
- const channelMeta = {
1403
- ...provenance,
1553
+ const rawContent = content ?? "";
1554
+ const attachments = hasAttachments
1555
+ ? smDeps.resolveAttachments(attachmentIds)
1556
+ : [];
1557
+ const greetingMeta = {
1404
1558
  userMessageChannel: sourceChannel,
1405
1559
  assistantMessageChannel: sourceChannel,
1406
1560
  userMessageInterface: sourceInterface,
1407
1561
  assistantMessageInterface: sourceInterface,
1408
1562
  };
1409
-
1410
- const rawContent = content ?? "";
1411
- const attachments = hasAttachments
1412
- ? smDeps.resolveAttachments(attachmentIds)
1413
- : [];
1414
- const userMsg = createUserMessage(rawContent, attachments);
1415
- const persisted = await addMessage(
1416
- mapping.conversationId,
1417
- "user",
1418
- JSON.stringify(userMsg.content),
1419
- channelMeta,
1420
- );
1421
- conversation.getMessages().push(userMsg);
1422
-
1423
- setConversationOriginChannelIfUnset(
1424
- mapping.conversationId,
1425
- sourceChannel,
1426
- );
1427
- setConversationOriginInterfaceIfUnset(
1428
- mapping.conversationId,
1429
- sourceInterface,
1430
- );
1563
+ const persisted = await persistQueuedMessageBody(conversation, {
1564
+ content: rawContent,
1565
+ attachments,
1566
+ requestId: crypto.randomUUID(),
1567
+ metadata: greetingMeta,
1568
+ });
1431
1569
 
1432
1570
  const conversationId = mapping.conversationId;
1571
+ const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
1572
+ trustContext: conversation.trustContext,
1573
+ });
1433
1574
 
1434
1575
  const assistantMsg = createAssistantMessage(cannedGreeting);
1435
1576
  const persistedAssistant = await addMessage(
1436
1577
  mapping.conversationId,
1437
1578
  "assistant",
1438
1579
  JSON.stringify(assistantMsg.content),
1439
- channelMeta,
1580
+ { metadata: channelMeta },
1440
1581
  );
1441
1582
  conversation.getMessages().push(assistantMsg);
1442
1583
 
@@ -1482,7 +1623,7 @@ export async function handleSendMessage(
1482
1623
  persistedAssistant.id,
1483
1624
  );
1484
1625
  publishConversationMessagesChanged(conversationId, originClientId);
1485
- conversation.processing = false;
1626
+ conversation.setProcessing(false);
1486
1627
  silentlyWithLog(
1487
1628
  conversation.drainQueue(),
1488
1629
  "canned-greeting queue drain",
@@ -1498,8 +1639,8 @@ export async function handleSendMessage(
1498
1639
  cleanupDeferred = true;
1499
1640
  return response;
1500
1641
  } finally {
1501
- if (!cleanupDeferred && conversation.processing) {
1502
- conversation.processing = false;
1642
+ if (!cleanupDeferred && conversation.isProcessing()) {
1643
+ conversation.setProcessing(false);
1503
1644
  silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1504
1645
  }
1505
1646
  }
@@ -1580,25 +1721,22 @@ export async function handleSendMessage(
1580
1721
  if (conversation.isProcessing()) {
1581
1722
  // Queue the message so it's processed when the current turn completes
1582
1723
  const requestId = crypto.randomUUID();
1583
- const enqueueResult = conversation.enqueueMessage(
1584
- contentAfterScan,
1724
+ const enqueueResult = conversation.enqueueMessage({
1725
+ content: contentAfterScan,
1585
1726
  attachments,
1586
- broadcastMessage,
1727
+ onEvent: broadcastMessage,
1587
1728
  requestId,
1588
- undefined, // activeSurfaceId
1589
- undefined, // currentPage
1590
- {
1729
+ metadata: {
1591
1730
  userMessageChannel: sourceChannel,
1592
1731
  assistantMessageChannel: sourceChannel,
1593
1732
  userMessageInterface: sourceInterface,
1594
1733
  assistantMessageInterface: sourceInterface,
1595
1734
  ...(body.automated === true ? { automated: true } : {}),
1596
1735
  },
1597
- { isInteractive },
1598
- undefined, // displayContent
1736
+ isInteractive,
1599
1737
  transport,
1600
1738
  clientMessageId,
1601
- );
1739
+ });
1602
1740
  if (enqueueResult.rejected) {
1603
1741
  return new RouteResponse(
1604
1742
  JSON.stringify({ accepted: false, error: "queue_full" }),
@@ -1715,56 +1853,43 @@ export async function handleSendMessage(
1715
1853
  const slashResult = await resolveSlash(rawContent, slashContext);
1716
1854
 
1717
1855
  if (slashResult.kind === "unknown") {
1718
- conversation.processing = true;
1856
+ conversation.setProcessing(true);
1719
1857
  let cleanupDeferred = false;
1720
1858
  try {
1721
- const provenance = provenanceFromTrustContext(conversation.trustContext);
1722
- const imageSourcePaths: Record<string, string> = {};
1723
- for (let i = 0; i < attachments.length; i++) {
1724
- const a = attachments[i];
1725
- if (a.filePath && a.mimeType.toLowerCase().startsWith("image/")) {
1726
- imageSourcePaths[`${i}:${a.filename}`] = a.filePath;
1727
- }
1728
- }
1729
- const channelMeta = {
1730
- ...provenance,
1859
+ const slashMeta = {
1731
1860
  userMessageChannel: sourceChannel,
1732
1861
  assistantMessageChannel: sourceChannel,
1733
1862
  userMessageInterface: sourceInterface,
1734
1863
  assistantMessageInterface: sourceInterface,
1735
1864
  ...(body.automated === true ? { automated: true } : {}),
1736
- ...(Object.keys(imageSourcePaths).length > 0
1737
- ? { imageSourcePaths }
1738
- : {}),
1739
1865
  };
1740
- const cleanMsg = createUserMessage(rawContent, attachments);
1741
- const llmMsg = enrichMessageWithSourcePaths(cleanMsg, attachments);
1742
- const persisted = await addMessage(
1743
- mapping.conversationId,
1744
- "user",
1745
- JSON.stringify(cleanMsg.content),
1746
- channelMeta,
1747
- );
1748
- conversation.getMessages().push(llmMsg);
1866
+ const persisted = await persistQueuedMessageBody(conversation, {
1867
+ content: rawContent,
1868
+ attachments,
1869
+ requestId: crypto.randomUUID(),
1870
+ metadata: slashMeta,
1871
+ clientMessageId,
1872
+ });
1873
+ if (persisted.deduplicated) {
1874
+ return {
1875
+ accepted: true,
1876
+ messageId: persisted.id,
1877
+ conversationId: mapping.conversationId,
1878
+ };
1879
+ }
1749
1880
 
1881
+ const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
1882
+ trustContext: conversation.trustContext,
1883
+ });
1750
1884
  const assistantMsg = createAssistantMessage(slashResult.message);
1751
1885
  const persistedAssistant = await addMessage(
1752
1886
  mapping.conversationId,
1753
1887
  "assistant",
1754
1888
  JSON.stringify(assistantMsg.content),
1755
- channelMeta,
1889
+ { metadata: channelMeta },
1756
1890
  );
1757
1891
  conversation.getMessages().push(assistantMsg);
1758
1892
 
1759
- setConversationOriginChannelIfUnset(
1760
- mapping.conversationId,
1761
- sourceChannel,
1762
- );
1763
- setConversationOriginInterfaceIfUnset(
1764
- mapping.conversationId,
1765
- sourceInterface,
1766
- );
1767
-
1768
1893
  // Snapshot model info now so the deferred callback cannot observe
1769
1894
  // a config change from a concurrent request.
1770
1895
  const modelInfoEvent = isModelSlashCommand(rawContent)
@@ -1809,7 +1934,7 @@ export async function handleSendMessage(
1809
1934
  persistedAssistant.id,
1810
1935
  );
1811
1936
  publishConversationMessagesChanged(conversationId, originClientId);
1812
- conversation.processing = false;
1937
+ conversation.setProcessing(false);
1813
1938
  silentlyWithLog(conversation.drainQueue(), "slash-command queue drain");
1814
1939
  }, 0);
1815
1940
 
@@ -1818,43 +1943,52 @@ export async function handleSendMessage(
1818
1943
  } finally {
1819
1944
  // No-op for the slash-command early-return path (handled inside
1820
1945
  // setTimeout above), but still needed for error paths.
1821
- if (!cleanupDeferred && conversation.processing) {
1822
- conversation.processing = false;
1946
+ if (!cleanupDeferred && conversation.isProcessing()) {
1947
+ conversation.setProcessing(false);
1823
1948
  silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1824
1949
  }
1825
1950
  }
1826
1951
  }
1827
1952
 
1828
1953
  if (slashResult.kind === "compact") {
1829
- conversation.processing = true;
1830
- const provenance = provenanceFromTrustContext(conversation.trustContext);
1831
- const channelMeta = {
1832
- ...provenance,
1954
+ conversation.setProcessing(true);
1955
+ const slashMeta = {
1833
1956
  userMessageChannel: sourceChannel,
1834
1957
  assistantMessageChannel: sourceChannel,
1835
1958
  userMessageInterface: sourceInterface,
1836
1959
  assistantMessageInterface: sourceInterface,
1837
1960
  };
1838
- const cleanMsg = createUserMessage(rawContent, attachments);
1839
- let persisted: Awaited<ReturnType<typeof addMessage>>;
1961
+ let persisted: Awaited<ReturnType<typeof persistQueuedMessageBody>>;
1840
1962
  try {
1841
- persisted = await addMessage(
1842
- mapping.conversationId,
1843
- "user",
1844
- JSON.stringify(cleanMsg.content),
1845
- channelMeta,
1846
- );
1963
+ persisted = await persistQueuedMessageBody(conversation, {
1964
+ content: rawContent,
1965
+ attachments,
1966
+ requestId: crypto.randomUUID(),
1967
+ metadata: slashMeta,
1968
+ clientMessageId,
1969
+ });
1847
1970
  } catch (err) {
1848
1971
  // The fire-and-forget compaction below owns clearing `processing`, but a
1849
1972
  // throw from this initial persist never reaches it — reset here so the
1850
1973
  // conversation isn't stranded in queued mode.
1851
- conversation.processing = false;
1974
+ conversation.setProcessing(false);
1852
1975
  silentlyWithLog(conversation.drainQueue(), "compact-command queue drain");
1853
1976
  throw err;
1854
1977
  }
1855
- conversation.getMessages().push(cleanMsg);
1978
+ if (persisted.deduplicated) {
1979
+ conversation.setProcessing(false);
1980
+ silentlyWithLog(conversation.drainQueue(), "compact-dedup queue drain");
1981
+ return {
1982
+ accepted: true,
1983
+ messageId: persisted.id,
1984
+ conversationId: mapping.conversationId,
1985
+ };
1986
+ }
1856
1987
 
1857
1988
  const conversationId = mapping.conversationId;
1989
+ const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
1990
+ trustContext: conversation.trustContext,
1991
+ });
1858
1992
 
1859
1993
  // Fire-and-forget: return 202 immediately, run compaction async.
1860
1994
  // forceCompact() makes an LLM call that can exceed the client's
@@ -1870,14 +2004,8 @@ export async function handleSendMessage(
1870
2004
  clientMessageId,
1871
2005
  });
1872
2006
  publishConversationMessagesChanged(conversationId, originClientId);
1873
- conversation.emitActivityState(
1874
- "thinking",
1875
- "context_compacting",
1876
- "assistant_turn",
1877
- );
1878
- const result = await conversation.forceCompact({
1879
- targetInputTokensOverride: slashResult.targetInputTokensOverride,
1880
- });
2007
+ conversation.emitActivityState("thinking", "context_compacting");
2008
+ const result = await conversation.forceCompact();
1881
2009
  const responseText = formatCompactResult(result);
1882
2010
 
1883
2011
  const assistantMsg = createAssistantMessage(responseText);
@@ -1885,7 +2013,7 @@ export async function handleSendMessage(
1885
2013
  conversationId,
1886
2014
  "assistant",
1887
2015
  JSON.stringify(assistantMsg.content),
1888
- channelMeta,
2016
+ { metadata: channelMeta },
1889
2017
  );
1890
2018
  assistantMessagePersisted = true;
1891
2019
  conversation.getMessages().push(assistantMsg);
@@ -1914,7 +2042,7 @@ export async function handleSendMessage(
1914
2042
  retryable: true,
1915
2043
  });
1916
2044
  } finally {
1917
- conversation.processing = false;
2045
+ conversation.setProcessing(false);
1918
2046
  silentlyWithLog(
1919
2047
  conversation.drainQueue(),
1920
2048
  "compact-command queue drain",
@@ -1930,30 +2058,37 @@ export async function handleSendMessage(
1930
2058
  }
1931
2059
 
1932
2060
  if (slashResult.kind === "clean") {
1933
- conversation.processing = true;
2061
+ conversation.setProcessing(true);
1934
2062
  const conversationId = mapping.conversationId;
1935
2063
  // Outer try/finally guarantees the processing flag is cleared (and the
1936
2064
  // queue drained) on every failure path — including a throw from the
1937
2065
  // initial user-message persist below, which would otherwise leave the
1938
2066
  // conversation stuck in queued mode indefinitely.
1939
2067
  try {
1940
- const provenance = provenanceFromTrustContext(conversation.trustContext);
1941
- const channelMeta = {
1942
- ...provenance,
2068
+ const slashMeta = {
1943
2069
  userMessageChannel: sourceChannel,
1944
2070
  assistantMessageChannel: sourceChannel,
1945
2071
  userMessageInterface: sourceInterface,
1946
2072
  assistantMessageInterface: sourceInterface,
1947
2073
  };
1948
- const cleanMsg = createUserMessage(rawContent, attachments);
1949
- const persisted = await addMessage(
1950
- mapping.conversationId,
1951
- "user",
1952
- JSON.stringify(cleanMsg.content),
1953
- channelMeta,
1954
- );
1955
- conversation.getMessages().push(cleanMsg);
2074
+ const persisted = await persistQueuedMessageBody(conversation, {
2075
+ content: rawContent,
2076
+ attachments,
2077
+ requestId: crypto.randomUUID(),
2078
+ metadata: slashMeta,
2079
+ clientMessageId,
2080
+ });
2081
+ if (persisted.deduplicated) {
2082
+ return {
2083
+ accepted: true,
2084
+ messageId: persisted.id,
2085
+ conversationId,
2086
+ };
2087
+ }
1956
2088
 
2089
+ const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
2090
+ trustContext: conversation.trustContext,
2091
+ });
1957
2092
  let assistantMessagePersisted = false;
1958
2093
  try {
1959
2094
  broadcastMessage({
@@ -1973,7 +2108,7 @@ export async function handleSendMessage(
1973
2108
  conversationId,
1974
2109
  "assistant",
1975
2110
  JSON.stringify(assistantMsg.content),
1976
- channelMeta,
2111
+ { metadata: channelMeta },
1977
2112
  );
1978
2113
  assistantMessagePersisted = true;
1979
2114
  conversation.getMessages().push(assistantMsg);
@@ -2009,7 +2144,7 @@ export async function handleSendMessage(
2009
2144
  conversationId,
2010
2145
  };
2011
2146
  } finally {
2012
- conversation.processing = false;
2147
+ conversation.setProcessing(false);
2013
2148
  silentlyWithLog(conversation.drainQueue(), "clean-command queue drain");
2014
2149
  }
2015
2150
  }
@@ -2017,16 +2152,22 @@ export async function handleSendMessage(
2017
2152
  const resolvedContent = slashResult.content;
2018
2153
 
2019
2154
  const requestId = crypto.randomUUID();
2020
- let messageId: string;
2021
- try {
2022
- messageId = await conversation.persistUserMessage(
2023
- resolvedContent,
2024
- attachments,
2025
- requestId,
2026
- body.automated === true ? { automated: true } : undefined,
2027
- );
2028
- } catch (err) {
2029
- throw err;
2155
+ const persistResult = await conversation.persistUserMessage({
2156
+ content: resolvedContent,
2157
+ attachments,
2158
+ requestId,
2159
+ metadata: body.automated === true ? { automated: true } : undefined,
2160
+ clientMessageId,
2161
+ });
2162
+
2163
+ const messageId = persistResult.id;
2164
+
2165
+ if (persistResult.deduplicated) {
2166
+ return {
2167
+ accepted: true,
2168
+ messageId,
2169
+ conversationId: mapping.conversationId,
2170
+ };
2030
2171
  }
2031
2172
 
2032
2173
  broadcastMessage({
@@ -2041,7 +2182,8 @@ export async function handleSendMessage(
2041
2182
 
2042
2183
  // Fire-and-forget the agent loop; events flow to the hub via broadcastMessage.
2043
2184
  conversation
2044
- .runAgentLoop(resolvedContent, messageId, broadcastMessage, {
2185
+ .runAgentLoop(resolvedContent, messageId, {
2186
+ onEvent: broadcastMessage,
2045
2187
  isInteractive,
2046
2188
  isUserMessage: true,
2047
2189
  })
@@ -2088,8 +2230,8 @@ async function generateLlmSuggestion(
2088
2230
  "",
2089
2231
  "CRITICAL — write from the USER'S perspective only, NEVER from the assistant's:",
2090
2232
  "- The suggestion is what the USER will type into the chat input",
2091
- "- Use first-person \"I\" only if the user has used it in their prior messages",
2092
- "- NEVER start with phrases like \"I can help\", \"Here's what\", \"Let me\", \"I'd suggest\" — those are assistant-voice",
2233
+ '- Use first-person "I" only if the user has used it in their prior messages',
2234
+ '- NEVER start with phrases like "I can help", "Here\'s what", "Let me", "I\'d suggest" — those are assistant-voice',
2093
2235
  "- Think: if you were the user reading the assistant's reply, what question or follow-up would you ask next?",
2094
2236
  "",
2095
2237
  "Output only the reply text inside the requested tags — no preamble, no commentary.",
@@ -2119,9 +2261,10 @@ async function generateLlmSuggestion(
2119
2261
  // tokens here would be wasteful.
2120
2262
  const response = await provider.sendMessage(
2121
2263
  [{ role: "user", content: [{ type: "text", text: userPrompt }] }],
2122
- [], // no tools
2123
- systemPrompt,
2124
2264
  {
2265
+ tools: [],
2266
+ // no tools
2267
+ systemPrompt,
2125
2268
  config: {
2126
2269
  callSite: "replySuggestion",
2127
2270
  max_tokens: 60,
@@ -2342,6 +2485,47 @@ function handleSearchConversations({
2342
2485
  return { query, results };
2343
2486
  }
2344
2487
 
2488
+ // ---------------------------------------------------------------------------
2489
+ // Metadata helpers
2490
+ // ---------------------------------------------------------------------------
2491
+
2492
+ /**
2493
+ * Assemble the standard channel metadata object for message persistence.
2494
+ *
2495
+ * Combines provenance (trust context), channel/interface routing, and
2496
+ * optional per-message fields (automated flag, image source paths) into the
2497
+ * Record that `addMessage` stores in the `metadata` column.
2498
+ */
2499
+ function buildChannelMetadata(
2500
+ sourceChannel: string,
2501
+ sourceInterface: string,
2502
+ opts?: {
2503
+ trustContext?: Parameters<typeof provenanceFromTrustContext>[0];
2504
+ provenanceOverride?: Record<string, unknown>;
2505
+ automated?: boolean;
2506
+ attachments?: ReadonlyArray<{
2507
+ filename: string;
2508
+ mimeType: string;
2509
+ filePath?: string;
2510
+ }>;
2511
+ },
2512
+ ): Record<string, unknown> {
2513
+ const provenance =
2514
+ opts?.provenanceOverride ?? provenanceFromTrustContext(opts?.trustContext);
2515
+ const imageSourcePaths = opts?.attachments
2516
+ ? extractImageSourcePaths(opts.attachments)
2517
+ : undefined;
2518
+ return {
2519
+ ...provenance,
2520
+ userMessageChannel: sourceChannel,
2521
+ assistantMessageChannel: sourceChannel,
2522
+ userMessageInterface: sourceInterface,
2523
+ assistantMessageInterface: sourceInterface,
2524
+ ...(opts?.automated ? { automated: true } : {}),
2525
+ ...(imageSourcePaths ? { imageSourcePaths } : {}),
2526
+ };
2527
+ }
2528
+
2345
2529
  // ---------------------------------------------------------------------------
2346
2530
  // Module-level state
2347
2531
  // ---------------------------------------------------------------------------
@@ -2372,12 +2556,54 @@ export const ROUTES: RouteDefinition[] = [
2372
2556
  operationId: "messages_get",
2373
2557
  endpoint: "messages",
2374
2558
  method: "GET",
2559
+ policy: {
2560
+ requiredScopes: ["chat.read"],
2561
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
2562
+ },
2375
2563
  summary: "List messages",
2376
2564
  description:
2377
2565
  "Return messages for a conversation, including attachments and interface file metadata.",
2378
2566
  tags: ["messages"],
2567
+ queryParams: [
2568
+ {
2569
+ name: "conversationId",
2570
+ type: "string",
2571
+ required: false,
2572
+ description:
2573
+ "Conversation UUID. One of conversationId or conversationKey is required.",
2574
+ },
2575
+ {
2576
+ name: "conversationKey",
2577
+ type: "string",
2578
+ required: false,
2579
+ description:
2580
+ "Channel/external conversation key. One of conversationId or conversationKey is required.",
2581
+ },
2582
+ {
2583
+ name: "page",
2584
+ type: "string",
2585
+ required: false,
2586
+ description:
2587
+ "When set to 'latest', returns the most recent page of messages with pagination metadata.",
2588
+ },
2589
+ {
2590
+ name: "beforeTimestamp",
2591
+ type: "integer",
2592
+ required: false,
2593
+ description:
2594
+ "Return messages older than this timestamp (ms since epoch). Used for paging older history.",
2595
+ },
2596
+ {
2597
+ name: "limit",
2598
+ type: "integer",
2599
+ required: false,
2600
+ description: "Maximum number of messages to return.",
2601
+ },
2602
+ ],
2379
2603
  responseBody: z.object({
2380
- messages: z.array(z.unknown()).describe("Array of message objects"),
2604
+ messages: z
2605
+ .array(ConversationMessageSchema)
2606
+ .describe("Array of message objects"),
2381
2607
  hasMore: z
2382
2608
  .boolean()
2383
2609
  .optional()
@@ -2394,6 +2620,13 @@ export const ROUTES: RouteDefinition[] = [
2394
2620
  .nullable()
2395
2621
  .optional()
2396
2622
  .describe("ID of the oldest message in this page"),
2623
+ seq: z
2624
+ .number()
2625
+ .nullable()
2626
+ .optional()
2627
+ .describe(
2628
+ "Global SSE `seq` of the last event whose content is durably persisted for this conversation in the current daemon process. A client can align this snapshot with the `/events` stream by applying only events with `seq` greater than this value. Null when no events have been persisted in this process (cold conversation, after a daemon restart, or when the conversation has aged out of the in-memory map) — clients should cold-start in that case. Absent on older daemons that predate this field.",
2629
+ ),
2397
2630
  }),
2398
2631
  handler: (args) => handleListMessages(args),
2399
2632
  },
@@ -2401,23 +2634,70 @@ export const ROUTES: RouteDefinition[] = [
2401
2634
  operationId: "messages_post",
2402
2635
  endpoint: "messages",
2403
2636
  method: "POST",
2637
+ policy: {
2638
+ requiredScopes: ["chat.write"],
2639
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
2640
+ },
2404
2641
  summary: "Send a message",
2405
2642
  description:
2406
2643
  "Send a user message to a conversation and trigger the assistant response.",
2407
2644
  tags: ["messages"],
2408
2645
  responseStatus: "202",
2409
2646
  requestBody: z.object({
2410
- conversationKey: z.string().optional(),
2647
+ conversationId: z
2648
+ .string()
2649
+ .nullable()
2650
+ .optional()
2651
+ .describe(
2652
+ "Internal conversation id (0.8.6+ strict lookup). Omit both id and key to mint a new conversation server-side.",
2653
+ ),
2654
+ conversationKey: z.string().nullable().optional(),
2411
2655
  content: z.string().describe("Message text content"),
2412
2656
  attachments: z
2413
2657
  .array(z.unknown())
2414
- .describe("Optional file attachments")
2658
+ .describe("Optional inline file attachments")
2659
+ .optional(),
2660
+ attachmentIds: z
2661
+ .array(z.string())
2662
+ .describe("Ids of previously uploaded attachments to attach")
2415
2663
  .optional(),
2664
+ sourceChannel: z
2665
+ .string()
2666
+ .describe('Originating channel id (e.g. "vellum")'),
2667
+ interface: z
2668
+ .string()
2669
+ .describe('Originating interface id (e.g. "vellum")'),
2416
2670
  conversationType: z.string().optional(),
2417
2671
  slashCommand: z.string().optional(),
2418
2672
  clientTimezone: z.string().optional(),
2419
2673
  inferenceProfile: z.string().nullable().optional(),
2420
2674
  riskThreshold: z.enum(VALID_RISK_THRESHOLDS).optional(),
2675
+ onboarding: z
2676
+ .object({
2677
+ tools: z.array(z.string()),
2678
+ tasks: z.array(z.string()),
2679
+ tone: z.string(),
2680
+ userName: z.string().optional(),
2681
+ assistantName: z.string().optional(),
2682
+ googleConnected: z.boolean().optional(),
2683
+ googleScopes: z.array(z.string()).optional(),
2684
+ priorAssistants: z.array(z.string()).optional(),
2685
+ cohort: z.string().optional(),
2686
+ websiteUrl: z.string().optional(),
2687
+ contentSourceUrl: z.string().optional(),
2688
+ bootstrapTemplate: z.string().optional(),
2689
+ initialMessage: z.string().optional(),
2690
+ skills: z.array(z.string()).optional(),
2691
+ })
2692
+ .describe("PreChat onboarding context, sent on the first message only")
2693
+ .optional(),
2694
+ }),
2695
+ responseBody: z.object({
2696
+ accepted: z.boolean(),
2697
+ conversationId: z.string().optional(),
2698
+ messageId: z.string().optional(),
2699
+ queued: z.boolean().optional(),
2700
+ requestId: z.string().optional(),
2421
2701
  }),
2422
2702
  handler: async (args) =>
2423
2703
  handleSendMessage(args, {
@@ -2433,6 +2713,10 @@ export const ROUTES: RouteDefinition[] = [
2433
2713
  operationId: "search_get",
2434
2714
  endpoint: "search",
2435
2715
  method: "GET",
2716
+ policy: {
2717
+ requiredScopes: ["chat.read"],
2718
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
2719
+ },
2436
2720
  summary: "Search conversations",
2437
2721
  description: "Full-text search across all conversations.",
2438
2722
  tags: ["conversations"],
@@ -2446,6 +2730,10 @@ export const ROUTES: RouteDefinition[] = [
2446
2730
  operationId: "suggestion_get",
2447
2731
  endpoint: "suggestion",
2448
2732
  method: "GET",
2733
+ policy: {
2734
+ requiredScopes: ["chat.read"],
2735
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
2736
+ },
2449
2737
  summary: "Get reply suggestion",
2450
2738
  description:
2451
2739
  "Return an LLM-generated follow-up suggestion for the most recent assistant message.",