@vellumai/assistant 0.6.3 → 0.6.4

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 (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Route handlers for conversation messages and suggestions.
3
3
  */
4
- import { existsSync, readdirSync, statSync } from "node:fs";
4
+ import { existsSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
5
  import { join, relative } from "node:path";
6
6
 
7
7
  import { z } from "zod";
@@ -12,8 +12,10 @@ import {
12
12
  createUserMessage,
13
13
  } from "../../agent/message-types.js";
14
14
  import {
15
+ canServiceRegistryBrowser,
15
16
  CHANNEL_IDS,
16
17
  INTERFACE_IDS,
18
+ type InterfaceId,
17
19
  isInteractiveInterface,
18
20
  parseChannelId,
19
21
  parseInterfaceId,
@@ -21,6 +23,7 @@ import {
21
23
  } from "../../channels/types.js";
22
24
  import { isHttpAuthDisabled } from "../../config/env.js";
23
25
  import { getConfig } from "../../config/loader.js";
26
+ import type { Conversation } from "../../daemon/conversation.js";
24
27
  import {
25
28
  buildModelInfoEvent,
26
29
  formatCompactResult,
@@ -45,6 +48,10 @@ import type {
45
48
  NonHostProxyTransportMetadata,
46
49
  } from "../../daemon/message-types/conversations.js";
47
50
  import type { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
51
+ import {
52
+ writeOnboardingSidecar,
53
+ writeRelationshipState,
54
+ } from "../../home/relationship-state-writer.js";
48
55
  import * as attachmentsStore from "../../memory/attachments-store.js";
49
56
  import {
50
57
  createCanonicalGuardianRequest,
@@ -74,6 +81,7 @@ import { checkIngressForSecrets } from "../../security/secret-ingress.js";
74
81
  import { redactSecrets } from "../../security/secret-scanner.js";
75
82
  import { summarizeToolInput } from "../../tools/tool-input-summary.js";
76
83
  import { getLogger } from "../../util/logger.js";
84
+ import { getWorkspacePromptPath } from "../../util/platform.js";
77
85
  import { silentlyWithLog } from "../../util/silently.js";
78
86
  import { buildAssistantEvent } from "../assistant-event.js";
79
87
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
@@ -107,7 +115,7 @@ const SUGGESTION_CACHE_MAX = 100;
107
115
  function collectCanonicalGuardianRequestHintIds(
108
116
  conversationId: string,
109
117
  sourceChannel: string,
110
- conversation: import("../../daemon/conversation.js").Conversation,
118
+ conversation: Conversation,
111
119
  ): string[] {
112
120
  const requests = listPendingRequestsByConversationScope(
113
121
  conversationId,
@@ -172,7 +180,7 @@ async function tryConsumeCanonicalGuardianReply(params: {
172
180
  data: string;
173
181
  filePath?: string;
174
182
  }>;
175
- conversation: import("../../daemon/conversation.js").Conversation;
183
+ conversation: Conversation;
176
184
  onEvent: (msg: ServerMessage) => void;
177
185
  approvalConversationGenerator?: ApprovalConversationGenerator;
178
186
  /** Verified actor identity from actor-token middleware. */
@@ -967,7 +975,7 @@ function mergeConsecutiveAssistantMessages(messages: MessageRow[]): {
967
975
  function makeHubPublisher(
968
976
  deps: SendMessageDeps,
969
977
  conversationId: string,
970
- conversation: import("../../daemon/conversation.js").Conversation,
978
+ conversation: Conversation,
971
979
  ): (msg: ServerMessage) => void {
972
980
  let hubChain: Promise<void> = Promise.resolve();
973
981
  return (msg: ServerMessage) => {
@@ -1048,30 +1056,8 @@ function makeHubPublisher(
1048
1056
  conversationId,
1049
1057
  kind: "secret",
1050
1058
  });
1051
- } else if (msg.type === "host_bash_request") {
1052
- pendingInteractions.register(msg.requestId, {
1053
- conversation,
1054
- conversationId,
1055
- kind: "host_bash",
1056
- });
1057
- } else if (msg.type === "host_browser_request") {
1058
- pendingInteractions.register(msg.requestId, {
1059
- conversation,
1060
- conversationId,
1061
- kind: "host_browser",
1062
- });
1063
- } else if (msg.type === "host_file_request") {
1064
- pendingInteractions.register(msg.requestId, {
1065
- conversation,
1066
- conversationId,
1067
- kind: "host_file",
1068
- });
1069
- } else if (msg.type === "host_cu_request") {
1070
- pendingInteractions.register(msg.requestId, {
1071
- conversation,
1072
- conversationId,
1073
- kind: "host_cu",
1074
- });
1059
+ } else {
1060
+ registerHostProxyPendingInteraction(msg, conversation, conversationId);
1075
1061
  }
1076
1062
 
1077
1063
  // ServerMessage is a large union; conversationId exists on most but not all variants.
@@ -1100,6 +1086,202 @@ function makeHubPublisher(
1100
1086
  };
1101
1087
  }
1102
1088
 
1089
+ /**
1090
+ * Register pending interactions for host proxy request envelopes so
1091
+ * standalone result endpoints can resolve by requestId.
1092
+ *
1093
+ * Returns the registered requestId when a host proxy request was registered.
1094
+ * Callers that route through non-hub transports (e.g. registry-routed
1095
+ * host_browser sends) can use this to clean up the registration if send fails.
1096
+ */
1097
+ function registerHostProxyPendingInteraction(
1098
+ msg: ServerMessage,
1099
+ conversation: Conversation,
1100
+ conversationId: string,
1101
+ ): string | undefined {
1102
+ if (msg.type === "host_bash_request") {
1103
+ pendingInteractions.register(msg.requestId, {
1104
+ conversation,
1105
+ conversationId,
1106
+ kind: "host_bash",
1107
+ });
1108
+ return msg.requestId;
1109
+ }
1110
+ if (msg.type === "host_browser_request") {
1111
+ pendingInteractions.register(msg.requestId, {
1112
+ conversation,
1113
+ conversationId,
1114
+ kind: "host_browser",
1115
+ });
1116
+ return msg.requestId;
1117
+ }
1118
+ if (msg.type === "host_file_request") {
1119
+ pendingInteractions.register(msg.requestId, {
1120
+ conversation,
1121
+ conversationId,
1122
+ kind: "host_file",
1123
+ });
1124
+ return msg.requestId;
1125
+ }
1126
+ if (msg.type === "host_cu_request") {
1127
+ pendingInteractions.register(msg.requestId, {
1128
+ conversation,
1129
+ conversationId,
1130
+ kind: "host_cu",
1131
+ });
1132
+ return msg.requestId;
1133
+ }
1134
+ return undefined;
1135
+ }
1136
+
1137
+ /**
1138
+ * Resolve the host_browser sender function for a conversation turn.
1139
+ *
1140
+ * When the guardian has an active extension connection in the
1141
+ * ChromeExtensionRegistry, returns a registry-routed sender that forwards
1142
+ * `host_browser_request` / `host_browser_cancel` frames through the
1143
+ * WebSocket to the connected extension. Otherwise returns the SSE hub
1144
+ * emitter (`onEvent`).
1145
+ *
1146
+ * For `chrome-extension` turns the registry sender is **always** returned
1147
+ * regardless of the POST-time connection check. The chrome-extension
1148
+ * interface has no SSE consumer for `host_browser_request` frames, so
1149
+ * falling back to `onEvent` would cause CDP calls to stall until the proxy
1150
+ * timeout (30 s) instead of failing immediately at send time when the
1151
+ * registry throws on a missing connection.
1152
+ *
1153
+ * This helper is interface-agnostic: both chrome-extension and macOS turns
1154
+ * can obtain a registry-routed sender when extension connectivity exists.
1155
+ * The `isRegistryRouted` flag lets the caller decide whether to set
1156
+ * `hostBrowserSenderOverride` and whether to provision a `HostBrowserProxy`
1157
+ * for interfaces that don't statically support host_browser (e.g. macOS).
1158
+ */
1159
+ function resolveHostBrowserSender(
1160
+ conversation: Conversation,
1161
+ conversationId: string,
1162
+ authContext: AuthContext,
1163
+ onEvent: (msg: ServerMessage) => void,
1164
+ sourceInterface: InterfaceId,
1165
+ ): { sender: (msg: ServerMessage) => void; isRegistryRouted: boolean } {
1166
+ // Check whether the guardian has any active extension connection.
1167
+ const guardianId =
1168
+ conversation.trustContext?.guardianPrincipalId ??
1169
+ authContext.actorPrincipalId;
1170
+ const hasExtensionConnection =
1171
+ !!guardianId && !!getChromeExtensionRegistry().get(guardianId);
1172
+
1173
+ // For chrome-extension, always use the registry sender so that send-time
1174
+ // failures produce immediate errors rather than 30-second proxy timeouts.
1175
+ // The SSE hub has no extension consumer, so falling back to onEvent is
1176
+ // never correct for this interface.
1177
+ if (!hasExtensionConnection && sourceInterface !== "chrome-extension") {
1178
+ return { sender: onEvent, isRegistryRouted: false };
1179
+ }
1180
+
1181
+ // Build a registry-routed sender. The guardian principal ID is resolved
1182
+ // at send time rather than captured here so that queue-drain restores
1183
+ // (which re-fire this closure outside the original POST context) follow
1184
+ // the conversation's bound guardian identity rather than a stale
1185
+ // authContext.actorPrincipalId.
1186
+ const registrySender = (msg: ServerMessage): void => {
1187
+ const requestId = registerHostProxyPendingInteraction(
1188
+ msg,
1189
+ conversation,
1190
+ conversationId,
1191
+ );
1192
+ const gid =
1193
+ conversation.trustContext?.guardianPrincipalId ??
1194
+ authContext.actorPrincipalId;
1195
+ if (!gid) {
1196
+ if (requestId) pendingInteractions.resolve(requestId);
1197
+ throw new Error(
1198
+ "host_browser send skipped: no guardianId on AuthContext",
1199
+ );
1200
+ }
1201
+ const ok = getChromeExtensionRegistry().send(gid, msg);
1202
+ if (!ok) {
1203
+ if (requestId) pendingInteractions.resolve(requestId);
1204
+ throw new Error(
1205
+ `host_browser send failed: no active connection for guardian ${gid}`,
1206
+ );
1207
+ }
1208
+ };
1209
+
1210
+ return { sender: registrySender, isRegistryRouted: true };
1211
+ }
1212
+
1213
+ /**
1214
+ * Persist the pre-chat onboarding payload to disk.
1215
+ *
1216
+ * Runs only on the very first message of a fresh conversation. Three
1217
+ * artifacts are produced:
1218
+ *
1219
+ * 1. `data/onboarding-context.json` — sidecar read by the
1220
+ * relationship-state writer so onboarding-sourced facts survive
1221
+ * the pure-recomputation write cycle (every turn boundary rebuilds
1222
+ * facts from markdown; the sidecar is the durable source for the
1223
+ * tool/task/tone chips).
1224
+ * 2. `IDENTITY.md` / `USER.md` — persona seed files, only written
1225
+ * when missing so we never clobber existing content. These feed
1226
+ * the system prompt and the relationship-state writer's
1227
+ * `parseIdentity` / `parseUserName` helpers after a daemon
1228
+ * restart when the in-memory onboarding context is gone.
1229
+ * 3. `data/relationship-state.json` — kicked off fire-and-forget so
1230
+ * the Home page can populate immediately on first visit instead
1231
+ * of waiting for the first agent-turn boundary.
1232
+ *
1233
+ * Never throws: every write is guarded and logged as a warning on
1234
+ * failure. The route handler path must never reject because of a
1235
+ * best-effort persistence step.
1236
+ */
1237
+ function persistOnboardingArtifacts(onboarding: {
1238
+ tools: string[];
1239
+ tasks: string[];
1240
+ tone: string;
1241
+ userName?: string;
1242
+ assistantName?: string;
1243
+ }): void {
1244
+ writeOnboardingSidecar(onboarding);
1245
+
1246
+ const assistantName = onboarding.assistantName?.trim();
1247
+ if (assistantName) {
1248
+ const identityPath = getWorkspacePromptPath("IDENTITY.md");
1249
+ if (!existsSync(identityPath)) {
1250
+ try {
1251
+ writeFileSync(
1252
+ identityPath,
1253
+ `# Identity\n\n- Name: ${assistantName}\n`,
1254
+ "utf-8",
1255
+ );
1256
+ } catch (err) {
1257
+ log.warn(
1258
+ { err, identityPath },
1259
+ "Failed to seed IDENTITY.md from onboarding",
1260
+ );
1261
+ }
1262
+ }
1263
+ }
1264
+
1265
+ const userName = onboarding.userName?.trim();
1266
+ if (userName) {
1267
+ const userPath = getWorkspacePromptPath("USER.md");
1268
+ if (!existsSync(userPath)) {
1269
+ try {
1270
+ writeFileSync(userPath, `# User\n\n- Name: ${userName}\n`, "utf-8");
1271
+ } catch (err) {
1272
+ log.warn({ err, userPath }, "Failed to seed USER.md from onboarding");
1273
+ }
1274
+ }
1275
+ }
1276
+
1277
+ void writeRelationshipState().catch((err) => {
1278
+ log.warn(
1279
+ { err },
1280
+ "Failed to kick off relationship-state write after onboarding",
1281
+ );
1282
+ });
1283
+ }
1284
+
1103
1285
  export async function handleSendMessage(
1104
1286
  req: Request,
1105
1287
  deps: {
@@ -1120,6 +1302,13 @@ export async function handleSendMessage(
1120
1302
  bypassSecretCheck?: boolean;
1121
1303
  hostHomeDir?: string;
1122
1304
  hostUsername?: string;
1305
+ onboarding?: {
1306
+ tools: string[];
1307
+ tasks: string[];
1308
+ tone: string;
1309
+ userName?: string;
1310
+ assistantName?: string;
1311
+ };
1123
1312
  };
1124
1313
 
1125
1314
  const { conversationKey, content, attachmentIds } = body;
@@ -1222,8 +1411,24 @@ export async function handleSendMessage(
1222
1411
  const mapping = getOrCreateConversation(resolvedConversationKey, {
1223
1412
  conversationType,
1224
1413
  });
1414
+
1225
1415
  const smDeps = deps.sendMessageDeps;
1226
1416
 
1417
+ // Notify all connected clients that the conversation list changed when a
1418
+ // new standard conversation is created so sidebars can refresh.
1419
+ if (mapping.created && mapping.conversationType === "standard") {
1420
+ smDeps.assistantEventHub
1421
+ .publish(
1422
+ buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, {
1423
+ type: "conversation_list_invalidated",
1424
+ reason: "created",
1425
+ }),
1426
+ )
1427
+ .catch((err) => {
1428
+ log.warn({ err }, "Failed to publish conversation_list_invalidated");
1429
+ });
1430
+ }
1431
+
1227
1432
  // Build transport metadata from the request so the daemon can inject
1228
1433
  // host environment hints (home directory, username) into the LLM context.
1229
1434
  // The `supportsHostProxy` type predicate narrows `sourceInterface` to
@@ -1247,6 +1452,17 @@ export async function handleSendMessage(
1247
1452
  { transport },
1248
1453
  );
1249
1454
 
1455
+ // Store pre-chat onboarding context on the conversation when this is the
1456
+ // very first message (no prior messages loaded). Also persist the
1457
+ // onboarding selections so the Home page shows onboarding-sourced
1458
+ // chips immediately, and seed IDENTITY.md / USER.md so subsequent
1459
+ // turn-boundary recomputes of relationship-state have a stable
1460
+ // persona source beyond the in-memory conversation object.
1461
+ if (body.onboarding && conversation.messages.length === 0) {
1462
+ conversation.setOnboardingContext(body.onboarding);
1463
+ persistOnboardingArtifacts(body.onboarding);
1464
+ }
1465
+
1250
1466
  // Resolve guardian context from the AuthContext's actorPrincipalId.
1251
1467
  // The JWT-verified principal is used as the sender identity through
1252
1468
  // the same trust resolution pipeline that channel ingress uses.
@@ -1328,66 +1544,44 @@ export async function handleSendMessage(
1328
1544
  } else if (!conversation.isProcessing()) {
1329
1545
  conversation.setHostBashProxy(undefined);
1330
1546
  }
1331
- // For the chrome-extension interface we route host_browser_request /
1332
- // host_browser_cancel frames through the in-process ChromeExtensionRegistry
1333
- // to the WebSocket opened against /v1/browser-relay by the connected
1334
- // extension, instead of the SSE/onEvent hub used by macOS. The registry
1335
- // lookup is keyed by the JWT-derived actor principal id, which the
1336
- // runtime captured at WebSocket upgrade time.
1337
- //
1338
- // A single guardian may have multiple parallel extension installs
1339
- // connected at once (two Chrome profiles, two desktops). The registry
1340
- // tracks them under (guardianId, clientInstanceId) pairs and the
1341
- // default `send(guardianId, msg)` path routes to whichever instance
1342
- // has the most recent activity — typically the one the user is
1343
- // currently driving. Pinning to a specific instance can be done via
1344
- // `sendToInstance` if a caller ever needs it.
1345
- //
1346
- // macOS (and any other interface that supports host_browser in the
1347
- // future via the SSE hub) keeps using `onEvent` — see the else branch.
1348
- const browserProxySendToClient: (msg: ServerMessage) => void =
1349
- sourceInterface === "chrome-extension"
1350
- ? (msg) => {
1351
- // Resolve the guardian principal id at send time rather than
1352
- // capturing it from the POST-time authContext. This closure can be
1353
- // re-fired on queue drain — if a different actor's POST lands while
1354
- // the queue is still draining an earlier turn, a captured
1355
- // authContext.actorPrincipalId would mis-route the earlier turn's
1356
- // host_browser frames to the *new* actor. Preferring
1357
- // conversation.trustContext?.guardianPrincipalId makes the routing
1358
- // follow the conversation's bound guardian, which is stable across
1359
- // subsequent POSTs. Falls back to the per-POST authContext for
1360
- // turns that haven't been bound to a trust context yet.
1361
- const gid =
1362
- conversation.trustContext?.guardianPrincipalId ??
1363
- authContext.actorPrincipalId;
1364
- if (!gid) {
1365
- // No guardian identity on this turn — nothing to route to.
1366
- // The proxy will observe this via its try/catch and surface a
1367
- // transport error back to the caller.
1368
- throw new Error(
1369
- "chrome-extension host_browser send skipped: no guardianId on AuthContext",
1370
- );
1371
- }
1372
- const ok = getChromeExtensionRegistry().send(gid, msg);
1373
- if (!ok) {
1374
- throw new Error(
1375
- `chrome-extension host_browser send failed: no active connection for guardian ${gid}`,
1376
- );
1377
- }
1378
- }
1379
- : onEvent;
1547
+ // Resolve the host_browser sender — registry-routed when the guardian has
1548
+ // an active extension connection, SSE hub otherwise. This applies to both
1549
+ // chrome-extension and macOS interfaces so that macOS turns can route
1550
+ // browser automation through the user's real Chrome session when available.
1551
+ const { sender: browserProxySendToClient, isRegistryRouted } =
1552
+ resolveHostBrowserSender(
1553
+ conversation,
1554
+ mapping.conversationId,
1555
+ authContext,
1556
+ onEvent,
1557
+ sourceInterface,
1558
+ );
1559
+
1380
1560
  // Stash the registry-routed sender on the conversation so queue-drain
1381
1561
  // restores (which run outside of conversation-routes.ts and only have
1382
1562
  // access to `sendToClient`) can preserve it when calling
1383
- // `restoreBrowserProxyAvailability()`. For non-chrome-extension
1384
- // interfaces the override is cleared so the SSE hub sender is used.
1385
- if (sourceInterface === "chrome-extension") {
1563
+ // `restoreBrowserProxyAvailability()`. The override is set when the
1564
+ // sender is registry-routed (regardless of interface) and cleared when
1565
+ // the SSE hub sender is used, so the drain path always restores the
1566
+ // correct transport.
1567
+ if (isRegistryRouted) {
1386
1568
  conversation.hostBrowserSenderOverride = browserProxySendToClient;
1387
1569
  } else {
1388
1570
  conversation.hostBrowserSenderOverride = undefined;
1389
1571
  }
1390
- if (supportsHostProxy(sourceInterface, "host_browser")) {
1572
+
1573
+ // Provision the host browser proxy. For interfaces that natively support
1574
+ // host_browser (chrome-extension), always provision it. For macOS, the
1575
+ // static capability check returns false (supportsHostProxy("macos",
1576
+ // "host_browser") === false) because the extension isn't guaranteed to be
1577
+ // attached — but when the registry confirms an active extension
1578
+ // connection, we provision the proxy anyway so macOS turns can drive the
1579
+ // user's real Chrome session. When no extension is connected, macOS skips
1580
+ // provisioning and browser tools fall through to cdp-inspect/local.
1581
+ const shouldProvisionBrowserProxy =
1582
+ supportsHostProxy(sourceInterface, "host_browser") ||
1583
+ (canServiceRegistryBrowser(sourceInterface) && isRegistryRouted);
1584
+ if (shouldProvisionBrowserProxy) {
1391
1585
  if (!conversation.isProcessing() || !conversation.hostBrowserProxy) {
1392
1586
  const browserProxy = new HostBrowserProxy(
1393
1587
  browserProxySendToClient,
@@ -1445,18 +1639,17 @@ export async function handleSendMessage(
1445
1639
  conversation.updateClient(onEvent, !isInteractive, {
1446
1640
  skipProxySenderUpdate: preservingProxies,
1447
1641
  });
1448
- // For non-interactive interfaces that DO support host_browser
1449
- // (chrome-extension), explicitly re-enable just the browser proxy. The
1450
- // helper bypasses the `hasNoClient` gate so the single-capability
1451
- // chrome-extension turn can drive the browser via CDP without leaking
1452
- // host_bash/host_file tool availability into tool gating.
1642
+ // Re-enable the browser proxy for turns that provisioned one. This covers:
1643
+ // - chrome-extension: natively supports host_browser (non-interactive but
1644
+ // has a connected client for host_browser_request events)
1645
+ // - macOS with extension: provisioned above when isRegistryRouted is true
1453
1646
  //
1454
- // `restoreBrowserProxyAvailability()` reads `hostBrowserSenderOverride`
1455
- // (set above for chrome-extension) and applies the registry-routed
1456
- // sender, so the chrome-extension path gets the correct sender here
1457
- // including after queue-drain restores run from conversation-process.ts,
1458
- // which only have access to the conversation instance.
1459
- if (supportsHostProxy(sourceInterface, "host_browser")) {
1647
+ // The helper bypasses the `hasNoClient` gate so chrome-extension turns can
1648
+ // drive the browser via CDP without leaking host_bash/host_file tool
1649
+ // availability. It reads `hostBrowserSenderOverride` (set above when
1650
+ // registry-routed) and applies the correct sender including after
1651
+ // queue-drain restores run from conversation-process.ts.
1652
+ if (shouldProvisionBrowserProxy) {
1460
1653
  conversation.restoreBrowserProxyAvailability?.();
1461
1654
  }
1462
1655
 
@@ -1522,6 +1715,12 @@ export async function handleSendMessage(
1522
1715
  // fast path) so the HTTP response reaches the client before SSE
1523
1716
  // events arrive.
1524
1717
  setTimeout(() => {
1718
+ onEvent({
1719
+ type: "user_message_echo",
1720
+ text: rawContent,
1721
+ conversationId,
1722
+ messageId: persisted.id,
1723
+ });
1525
1724
  onEvent({ type: "assistant_text_delta", text: cannedGreeting });
1526
1725
  onEvent({ type: "message_complete", conversationId });
1527
1726
  conversation.processing = false;
@@ -1815,6 +2014,12 @@ export async function handleSendMessage(
1815
2014
  const conversationId = mapping.conversationId;
1816
2015
  const message = slashResult.message;
1817
2016
  setTimeout(() => {
2017
+ onEvent({
2018
+ type: "user_message_echo",
2019
+ text: rawContent,
2020
+ conversationId,
2021
+ messageId: persisted.id,
2022
+ });
1818
2023
  if (modelInfoEvent) {
1819
2024
  onEvent(modelInfoEvent);
1820
2025
  }
@@ -1841,80 +2046,88 @@ export async function handleSendMessage(
1841
2046
 
1842
2047
  if (slashResult.kind === "compact") {
1843
2048
  conversation.processing = true;
1844
- let cleanupDeferred = false;
1845
- try {
1846
- const provenance = provenanceFromTrustContext(conversation.trustContext);
1847
- const channelMeta = {
1848
- ...provenance,
1849
- userMessageChannel: sourceChannel,
1850
- assistantMessageChannel: sourceChannel,
1851
- userMessageInterface: sourceInterface,
1852
- assistantMessageInterface: sourceInterface,
1853
- };
1854
- const cleanMsg = createUserMessage(rawContent, attachments);
1855
- const persisted = await addMessage(
1856
- mapping.conversationId,
1857
- "user",
1858
- JSON.stringify(cleanMsg.content),
1859
- channelMeta,
1860
- );
1861
- conversation.getMessages().push(cleanMsg);
2049
+ const provenance = provenanceFromTrustContext(conversation.trustContext);
2050
+ const channelMeta = {
2051
+ ...provenance,
2052
+ userMessageChannel: sourceChannel,
2053
+ assistantMessageChannel: sourceChannel,
2054
+ userMessageInterface: sourceInterface,
2055
+ assistantMessageInterface: sourceInterface,
2056
+ };
2057
+ const cleanMsg = createUserMessage(rawContent, attachments);
2058
+ const persisted = await addMessage(
2059
+ mapping.conversationId,
2060
+ "user",
2061
+ JSON.stringify(cleanMsg.content),
2062
+ channelMeta,
2063
+ );
2064
+ conversation.getMessages().push(cleanMsg);
1862
2065
 
1863
- conversation.emitActivityState(
1864
- "thinking",
1865
- "context_compacting",
1866
- "assistant_turn",
1867
- );
1868
- const result = await conversation.forceCompact();
1869
- const responseText = formatCompactResult(result);
2066
+ const conversationId = mapping.conversationId;
1870
2067
 
1871
- const assistantMsg = createAssistantMessage(responseText);
1872
- await addMessage(
1873
- mapping.conversationId,
1874
- "assistant",
1875
- JSON.stringify(assistantMsg.content),
1876
- channelMeta,
1877
- );
1878
- conversation.getMessages().push(assistantMsg);
1879
-
1880
- const response = Response.json(
1881
- {
1882
- accepted: true,
2068
+ // Fire-and-forget: return 202 immediately, run compaction async.
2069
+ // forceCompact() makes an LLM call that can exceed the client's
2070
+ // HTTP timeout on large contexts, causing a false "Failed to send".
2071
+ (async () => {
2072
+ try {
2073
+ onEvent({
2074
+ type: "user_message_echo",
2075
+ text: rawContent,
2076
+ conversationId,
1883
2077
  messageId: persisted.id,
1884
- conversationId: mapping.conversationId,
1885
- },
1886
- { status: 202 },
1887
- );
2078
+ });
2079
+ conversation.emitActivityState(
2080
+ "thinking",
2081
+ "context_compacting",
2082
+ "assistant_turn",
2083
+ );
2084
+ const result = await conversation.forceCompact();
2085
+ const responseText = formatCompactResult(result);
2086
+
2087
+ const assistantMsg = createAssistantMessage(responseText);
2088
+ await addMessage(
2089
+ conversationId,
2090
+ "assistant",
2091
+ JSON.stringify(assistantMsg.content),
2092
+ channelMeta,
2093
+ );
2094
+ conversation.getMessages().push(assistantMsg);
1888
2095
 
1889
- const conversationId = mapping.conversationId;
1890
- setTimeout(() => {
1891
2096
  onEvent({ type: "assistant_text_delta", text: responseText });
2097
+ onEvent({ type: "message_complete", conversationId });
2098
+ } catch (err) {
2099
+ log.error({ err, conversationId }, "Compact command failed");
1892
2100
  onEvent({
1893
- type: "message_complete",
2101
+ type: "conversation_error",
1894
2102
  conversationId,
2103
+ code: "UNKNOWN",
2104
+ userMessage: `Compaction failed: ${err instanceof Error ? err.message : String(err)}`,
2105
+ retryable: true,
1895
2106
  });
2107
+ } finally {
1896
2108
  conversation.processing = false;
1897
2109
  silentlyWithLog(
1898
2110
  conversation.drainQueue(),
1899
2111
  "compact-command queue drain",
1900
2112
  );
1901
- }, 0);
1902
-
1903
- cleanupDeferred = true;
1904
- return response;
1905
- } finally {
1906
- if (!cleanupDeferred && conversation.processing) {
1907
- conversation.processing = false;
1908
- silentlyWithLog(conversation.drainQueue(), "error-path queue drain");
1909
2113
  }
1910
- }
2114
+ })();
2115
+
2116
+ return Response.json(
2117
+ {
2118
+ accepted: true,
2119
+ messageId: persisted.id,
2120
+ conversationId,
2121
+ },
2122
+ { status: 202 },
2123
+ );
1911
2124
  }
1912
2125
 
1913
2126
  const resolvedContent = slashResult.content;
1914
2127
 
2128
+ const requestId = crypto.randomUUID();
1915
2129
  let messageId: string;
1916
2130
  try {
1917
- const requestId = crypto.randomUUID();
1918
2131
  messageId = await conversation.persistUserMessage(
1919
2132
  resolvedContent,
1920
2133
  attachments,
@@ -1925,6 +2138,14 @@ export async function handleSendMessage(
1925
2138
  throw err;
1926
2139
  }
1927
2140
 
2141
+ onEvent({
2142
+ type: "user_message_echo",
2143
+ text: resolvedContent,
2144
+ conversationId: mapping.conversationId,
2145
+ messageId,
2146
+ requestId,
2147
+ });
2148
+
1928
2149
  // Fire-and-forget the agent loop; events flow to the hub via onEvent.
1929
2150
  conversation
1930
2151
  .runAgentLoop(resolvedContent, messageId, onEvent, {