@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
@@ -1052,4 +1052,92 @@ describe("ContextWindowManager", () => {
1052
1052
  normalResult.compactedMessages,
1053
1053
  );
1054
1054
  });
1055
+
1056
+ test("subtracts summaryOffset only when summary at index 0 was injected from parent", async () => {
1057
+ const provider = createProvider(() => ({
1058
+ content: [{ type: "text", text: "## Goals\n- new child summary" }],
1059
+ model: "mock-model",
1060
+ usage: { inputTokens: 75, outputTokens: 20 },
1061
+ stopReason: "end_turn",
1062
+ }));
1063
+ const manager = new ContextWindowManager({
1064
+ provider,
1065
+ systemPrompt: "system prompt",
1066
+ config: makeConfig({
1067
+ maxInputTokens: 320,
1068
+ targetBudgetRatio: 0.58,
1069
+ }),
1070
+ });
1071
+ const long = "k".repeat(220);
1072
+ // Parent-injected summary at index 0, plus 2 injected non-persisted
1073
+ // messages, plus 3 child-persisted messages. nonPersistedPrefixCount
1074
+ // includes the summary (set by injectInheritedContext).
1075
+ const history: Message[] = [
1076
+ createContextSummaryMessage("parent summary"),
1077
+ message("user", `injected-u ${long}`),
1078
+ message("assistant", `injected-a ${long}`),
1079
+ message("user", `persisted-u1 ${long}`),
1080
+ message("assistant", `persisted-a1 ${long}`),
1081
+ message("user", `persisted-u2 ${long}`),
1082
+ ];
1083
+ manager.nonPersistedPrefixCount = 3;
1084
+ manager.summaryIsInjected = true;
1085
+
1086
+ const result = await manager.maybeCompact(history, undefined, {
1087
+ force: true,
1088
+ });
1089
+ expect(result.compacted).toBe(true);
1090
+ // 4 messages compacted (2 injected + 2 child-persisted), but only the
1091
+ // 2 child-persisted ones count as DB-persisted.
1092
+ expect(result.compactedMessages).toBe(4);
1093
+ expect(result.compactedPersistedMessages).toBe(2);
1094
+ // Flag clears and prefix drains (both injected messages + summary slot).
1095
+ expect(manager.summaryIsInjected).toBe(false);
1096
+ expect(manager.nonPersistedPrefixCount).toBe(0);
1097
+ });
1098
+
1099
+ test("does not subtract summaryOffset when summary at index 0 is child-owned from prior compaction", async () => {
1100
+ const provider = createProvider(() => ({
1101
+ content: [{ type: "text", text: "## Goals\n- next child summary" }],
1102
+ model: "mock-model",
1103
+ usage: { inputTokens: 75, outputTokens: 20 },
1104
+ stopReason: "end_turn",
1105
+ }));
1106
+ const manager = new ContextWindowManager({
1107
+ provider,
1108
+ systemPrompt: "system prompt",
1109
+ config: makeConfig({
1110
+ maxInputTokens: 320,
1111
+ targetBudgetRatio: 0.58,
1112
+ }),
1113
+ });
1114
+ const long = "k".repeat(220);
1115
+ // Post-first-compaction state: child-owned summary at index 0, 2
1116
+ // still-injected messages that survived the first compaction's keep
1117
+ // region, 3 child-persisted messages. nonPersistedPrefixCount reflects
1118
+ // only the 2 remaining injected messages — the summary slot was already
1119
+ // consumed when the flag-gated decrement ran on the prior compaction.
1120
+ const history: Message[] = [
1121
+ createContextSummaryMessage("prior child summary"),
1122
+ message("user", `injected-u ${long}`),
1123
+ message("assistant", `injected-a ${long}`),
1124
+ message("user", `persisted-u1 ${long}`),
1125
+ message("assistant", `persisted-a1 ${long}`),
1126
+ message("user", `persisted-u2 ${long}`),
1127
+ ];
1128
+ manager.nonPersistedPrefixCount = 2;
1129
+ manager.summaryIsInjected = false;
1130
+
1131
+ const result = await manager.maybeCompact(history, undefined, {
1132
+ force: true,
1133
+ });
1134
+ expect(result.compacted).toBe(true);
1135
+ expect(result.compactedMessages).toBe(4);
1136
+ // Regression guard: without the flag gate, the subtraction from the
1137
+ // #24353 fix would double-apply here (nonPersistedPrefixCount - 1),
1138
+ // undercounting injectedInCompactable and inflating
1139
+ // compactedPersistedMessages by 1 (to 3).
1140
+ expect(result.compactedPersistedMessages).toBe(2);
1141
+ expect(manager.nonPersistedPrefixCount).toBe(0);
1142
+ });
1055
1143
  });
@@ -112,6 +112,8 @@ mock.module("../memory/conversation-crud.js", () => ({
112
112
  },
113
113
  updateConversationUsage: () => {},
114
114
  updateConversationTitle: () => {},
115
+ getMessageById: () => null,
116
+ getLastUserTimestampBefore: () => 0,
115
117
  }));
116
118
 
117
119
  mock.module("../memory/conversation-queries.js", () => ({
@@ -462,6 +462,7 @@ function makeCtx(
462
462
  runMessages: messages,
463
463
  injectedTokens: 0,
464
464
  }),
465
+ retrackCachedNodes: () => {},
465
466
  } as unknown as AgentLoopConversationContext["graphMemory"],
466
467
 
467
468
  ...overrides,
@@ -137,6 +137,8 @@ mock.module("../memory/conversation-crud.js", () => ({
137
137
  updateConversationContextWindow: () => {},
138
138
  updateConversationTitle: () => {},
139
139
  getConversationOriginChannel: () => null,
140
+ getMessageById: () => null,
141
+ getLastUserTimestampBefore: () => 0,
140
142
  }));
141
143
 
142
144
  const syncMessageToDiskMock = mock(() => {});
@@ -444,11 +446,17 @@ function makeCtx(
444
446
 
445
447
  graphMemory: {
446
448
  onCompacted: () => {},
447
- prepareMemory: async () => ({ runMessages: [], injectedTokens: 0, latencyMs: 0, mode: "none" as const }),
449
+ prepareMemory: async () => ({
450
+ runMessages: [],
451
+ injectedTokens: 0,
452
+ latencyMs: 0,
453
+ mode: "none" as const,
454
+ }),
448
455
  reinjectCachedMemory: (messages: Message[]) => ({
449
456
  runMessages: messages,
450
457
  injectedTokens: 0,
451
458
  }),
459
+ retrackCachedNodes: () => {},
452
460
  } as unknown as AgentLoopConversationContext["graphMemory"],
453
461
 
454
462
  ...overrides,
@@ -763,6 +771,95 @@ describe("session-agent-loop", () => {
763
771
  ];
764
772
  expect(call[4]).toBe("openrouter");
765
773
  });
774
+
775
+ test("record request log handles Responses API shaped payloads", async () => {
776
+ const events: ServerMessage[] = [];
777
+ const rawRequest = {
778
+ model: "gpt-5.4",
779
+ instructions: "Be helpful.",
780
+ input: [
781
+ {
782
+ role: "user",
783
+ content: [{ type: "input_text", text: "Hello" }],
784
+ type: "message",
785
+ },
786
+ ],
787
+ };
788
+ const rawResponse = {
789
+ id: "resp_test",
790
+ model: "gpt-5.4",
791
+ output: [
792
+ {
793
+ type: "message",
794
+ role: "assistant",
795
+ content: [{ type: "output_text", text: "Hi there." }],
796
+ },
797
+ ],
798
+ usage: {
799
+ input_tokens: 12,
800
+ output_tokens: 3,
801
+ },
802
+ status: "completed",
803
+ };
804
+
805
+ const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
806
+ onEvent({
807
+ type: "message_complete",
808
+ message: {
809
+ role: "assistant",
810
+ content: [{ type: "text", text: "Hi there." }],
811
+ },
812
+ });
813
+ onEvent({
814
+ type: "usage",
815
+ inputTokens: 12,
816
+ outputTokens: 3,
817
+ model: "gpt-5.4",
818
+ actualProvider: "openai",
819
+ providerDurationMs: 45,
820
+ rawRequest,
821
+ rawResponse,
822
+ });
823
+ return [
824
+ ...messages,
825
+ {
826
+ role: "assistant" as const,
827
+ content: [{ type: "text", text: "Hi there." }] as ContentBlock[],
828
+ },
829
+ ];
830
+ };
831
+
832
+ const ctx = makeCtx({
833
+ agentLoopRun,
834
+ provider: {
835
+ name: "openai",
836
+ sendMessage: async () => ({
837
+ content: [{ type: "text", text: "title" }],
838
+ model: "mock",
839
+ usage: { inputTokens: 0, outputTokens: 0 },
840
+ stopReason: "end_turn",
841
+ }),
842
+ } as unknown as AgentLoopConversationContext["provider"],
843
+ });
844
+
845
+ await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
846
+
847
+ expect(recordRequestLogMock).toHaveBeenCalledTimes(1);
848
+ const call = recordRequestLogMock.mock.calls[0] as unknown as [
849
+ string,
850
+ string,
851
+ string,
852
+ undefined,
853
+ string,
854
+ ];
855
+ expect(call).toEqual([
856
+ "test-conv",
857
+ JSON.stringify(rawRequest),
858
+ JSON.stringify(rawResponse),
859
+ undefined,
860
+ "openai",
861
+ ]);
862
+ });
766
863
  });
767
864
 
768
865
  describe("usage accounting", () => {
@@ -1938,7 +2035,6 @@ describe("session-agent-loop", () => {
1938
2035
 
1939
2036
  expect(drainReason).toBe("loop_complete");
1940
2037
  });
1941
-
1942
2038
  });
1943
2039
 
1944
2040
  describe("stale pending surface cleanup", () => {
@@ -713,3 +713,138 @@ describe("restoreBrowserProxyAvailability", () => {
713
713
  expect(registry).not.toContain(probe);
714
714
  });
715
715
  });
716
+
717
+ describe("hostBrowserSenderOverride is sender-mode based, not interface-string based", () => {
718
+ test("macOS turn with registry override routes browser frames through the registry sender", () => {
719
+ // When a macOS turn sets hostBrowserSenderOverride (because the
720
+ // guardian has an active extension connection), the browser proxy
721
+ // must route through the registry sender, not the SSE hub — the
722
+ // same behavior chrome-extension turns have always used.
723
+ const sseHub: ServerMessage[] = [];
724
+ const registry: ServerMessage[] = [];
725
+ const conversation = makeConversation((msg) => sseHub.push(msg));
726
+ const browserProxy = new HostBrowserProxy(() => {});
727
+ conversation.setHostBrowserProxy(browserProxy);
728
+
729
+ // macOS is interactive — hasNoClient is false.
730
+ conversation.updateClient((msg) => sseHub.push(msg), false);
731
+
732
+ // The POST /messages handler detected an active extension connection
733
+ // and set the registry-routed sender override.
734
+ const registrySender = (msg: ServerMessage) => registry.push(msg);
735
+ conversation.hostBrowserSenderOverride = registrySender;
736
+
737
+ // restoreBrowserProxyAvailability (called after updateClient in the
738
+ // POST handler) must prefer the override.
739
+ conversation.restoreBrowserProxyAvailability();
740
+ expect(browserProxy.isAvailable()).toBe(true);
741
+
742
+ // Verify frames flow through the registry, not the SSE hub.
743
+ const internalSend = (
744
+ browserProxy as unknown as {
745
+ sendToClient: (msg: ServerMessage) => void;
746
+ }
747
+ ).sendToClient;
748
+ const probe: ServerMessage = {
749
+ type: "host_browser_cancel",
750
+ requestId: "probe-macos-registry",
751
+ } as ServerMessage;
752
+ internalSend(probe);
753
+ expect(registry).toHaveLength(1);
754
+ expect(sseHub.some((m) => m === probe)).toBe(false);
755
+ });
756
+
757
+ test("macOS turn without registry override clears the browser proxy on restore", () => {
758
+ // When a macOS turn has no active extension connection, the override
759
+ // is cleared and restoreBrowserProxyAvailability falls back to the
760
+ // SSE hub sender. The proxy should not be stuck on an unavailable
761
+ // registry-routed sender from a prior chrome-extension turn.
762
+ const sseHub: ServerMessage[] = [];
763
+ const conversation = makeConversation((msg) => sseHub.push(msg));
764
+ const browserProxy = new HostBrowserProxy(() => {});
765
+ conversation.setHostBrowserProxy(browserProxy);
766
+
767
+ // Interactive macOS turn without extension connectivity.
768
+ conversation.updateClient((msg) => sseHub.push(msg), false);
769
+ conversation.hostBrowserSenderOverride = undefined;
770
+ conversation.restoreBrowserProxyAvailability();
771
+
772
+ // The proxy should be available and routed through SSE.
773
+ expect(browserProxy.isAvailable()).toBe(true);
774
+ const internalSend = (
775
+ browserProxy as unknown as {
776
+ sendToClient: (msg: ServerMessage) => void;
777
+ }
778
+ ).sendToClient;
779
+ const probe: ServerMessage = {
780
+ type: "host_browser_cancel",
781
+ requestId: "probe-macos-sse",
782
+ } as ServerMessage;
783
+ internalSend(probe);
784
+ expect(sseHub).toContain(probe);
785
+ });
786
+
787
+ test("override semantics are symmetric: chrome-extension and macOS both set override when registry-routed", () => {
788
+ // The override field must be set for ANY interface that uses a
789
+ // registry-routed sender, and cleared for any that does not.
790
+ // This test verifies the field is not gated on interface strings.
791
+ const conversation = makeConversation();
792
+ const registrySender = () => {};
793
+
794
+ // Simulate chrome-extension setting the override.
795
+ conversation.hostBrowserSenderOverride = registrySender;
796
+ expect(conversation.hostBrowserSenderOverride).toBe(registrySender);
797
+
798
+ // Simulate macOS-with-extension setting the same override.
799
+ // The field value is the same registry sender, not gated by interface.
800
+ const macosRegistrySender = () => {};
801
+ conversation.hostBrowserSenderOverride = macosRegistrySender;
802
+ expect(conversation.hostBrowserSenderOverride).toBe(macosRegistrySender);
803
+
804
+ // Simulate macOS-without-extension clearing the override.
805
+ conversation.hostBrowserSenderOverride = undefined;
806
+ expect(conversation.hostBrowserSenderOverride).toBeUndefined();
807
+ });
808
+
809
+ test("queue-drain restore path preserves registry-routed sender for macOS turns", () => {
810
+ // When a macOS turn with an active extension connection has its
811
+ // messages queued and later drained, the drain-queue path calls
812
+ // restoreBrowserProxyAvailability(). With the override set, the
813
+ // proxy must be restored with the registry sender, not the SSE hub.
814
+ const sseHub: ServerMessage[] = [];
815
+ const registry: ServerMessage[] = [];
816
+ const conversation = makeConversation((msg) => sseHub.push(msg));
817
+ const browserProxy = new HostBrowserProxy(() => {});
818
+ conversation.setHostBrowserProxy(browserProxy);
819
+
820
+ // macOS interactive turn with extension connectivity.
821
+ conversation.updateClient((msg) => sseHub.push(msg), false);
822
+ const registrySender = (msg: ServerMessage) => registry.push(msg);
823
+ conversation.hostBrowserSenderOverride = registrySender;
824
+ conversation.restoreBrowserProxyAvailability();
825
+ expect(browserProxy.isAvailable()).toBe(true);
826
+
827
+ // Simulate drain-queue clearing all proxies for a non-interactive
828
+ // queued message, then restoring for the next macOS turn.
829
+ conversation.clearProxyAvailability();
830
+ expect(browserProxy.isAvailable()).toBe(false);
831
+
832
+ // Drain restore — override is still set from the POST handler.
833
+ conversation.restoreBrowserProxyAvailability();
834
+ expect(browserProxy.isAvailable()).toBe(true);
835
+
836
+ // Verify the registry sender was preserved, not the SSE hub.
837
+ const internalSend = (
838
+ browserProxy as unknown as {
839
+ sendToClient: (msg: ServerMessage) => void;
840
+ }
841
+ ).sendToClient;
842
+ const probe: ServerMessage = {
843
+ type: "host_browser_cancel",
844
+ requestId: "probe-drain-macos",
845
+ } as ServerMessage;
846
+ internalSend(probe);
847
+ expect(registry).toHaveLength(1);
848
+ expect(sseHub.some((m) => m === probe)).toBe(false);
849
+ });
850
+ });
@@ -6,6 +6,10 @@ import {
6
6
  classifyConversationError,
7
7
  isUserCancellation,
8
8
  } from "../daemon/conversation-error.js";
9
+ import {
10
+ type AbortReasonKind,
11
+ createAbortReason,
12
+ } from "../util/abort-reasons.js";
9
13
  import { ProviderError, ProviderNotConfiguredError } from "../util/errors.js";
10
14
 
11
15
  describe("isUserCancellation", () => {
@@ -563,6 +567,72 @@ describe("classifyConversationError", () => {
563
567
  expect(isUserCancellation(err, aborted)).toBe(true);
564
568
  });
565
569
  });
570
+
571
+ describe("wrapped ProviderError carrying tagged abort reason", () => {
572
+ const abortedCtx: ErrorContext = { phase: "agent_loop", aborted: true };
573
+ const taggedKinds: AbortReasonKind[] = [
574
+ "user_cancel",
575
+ "preempted_by_new_message",
576
+ "conversation_disposed",
577
+ "subagent_aborted",
578
+ "signal_cancel",
579
+ "voice_session_aborted",
580
+ "work_item_aborted",
581
+ ];
582
+
583
+ for (const kind of taggedKinds) {
584
+ it(`treats ProviderError with abortReason kind="${kind}" as user cancellation`, () => {
585
+ const wrapped = new ProviderError(
586
+ "Anthropic API error (undefined): Request was aborted.",
587
+ "anthropic",
588
+ undefined,
589
+ { abortReason: createAbortReason(kind, `test:${kind}`) },
590
+ );
591
+ expect(isUserCancellation(wrapped, abortedCtx)).toBe(true);
592
+ });
593
+ }
594
+
595
+ it("does NOT treat tagged ProviderError as cancellation when ctx.aborted is false", () => {
596
+ const wrapped = new ProviderError(
597
+ "Anthropic API error (undefined): Request was aborted.",
598
+ "anthropic",
599
+ undefined,
600
+ { abortReason: createAbortReason("user_cancel", "test") },
601
+ );
602
+ const notAborted: ErrorContext = { phase: "agent_loop", aborted: false };
603
+ expect(isUserCancellation(wrapped, notAborted)).toBe(false);
604
+ });
605
+
606
+ it("does NOT treat ProviderError without abortReason as cancellation", () => {
607
+ const wrapped = new ProviderError(
608
+ "Anthropic API error (undefined): Request was aborted.",
609
+ "anthropic",
610
+ undefined,
611
+ );
612
+ expect(isUserCancellation(wrapped, abortedCtx)).toBe(false);
613
+ });
614
+
615
+ it("does NOT treat ProviderError with foreign reason as cancellation", () => {
616
+ const wrapped = new ProviderError(
617
+ "Anthropic API error (undefined): Request was aborted.",
618
+ "anthropic",
619
+ undefined,
620
+ { abortReason: { kind: "user_cancel", source: "spoofed" } },
621
+ );
622
+ expect(isUserCancellation(wrapped, abortedCtx)).toBe(false);
623
+ });
624
+
625
+ it("falls through to CONVERSATION_ABORTED when wrapped ProviderError has no tagged reason", () => {
626
+ const wrapped = new ProviderError(
627
+ "Anthropic API error (undefined): Request was aborted.",
628
+ "anthropic",
629
+ undefined,
630
+ );
631
+ const result = classifyConversationError(wrapped, abortedCtx);
632
+ expect(result.code).toBe("CONVERSATION_ABORTED");
633
+ expect(result.errorCategory).toBe("session_aborted");
634
+ });
635
+ });
566
636
  });
567
637
 
568
638
  describe("buildConversationErrorMessage", () => {
@@ -753,10 +753,17 @@ describe("web_search_tool_result structural guard", () => {
753
753
  // It has a separate isWebSearchToolResultBlock for the other type.
754
754
  "providers/anthropic/client.ts",
755
755
 
756
- // OpenAI provider converts Anthropic-style messages to OpenAI format.
757
- // OpenAI API does not support web_search_tool_result natively; those
758
- // blocks are handled upstream before reaching the OpenAI client.
759
- "providers/openai/client.ts",
756
+ // Chat-completions transport used by OpenAI-compatible providers
757
+ // (OpenRouter, Fireworks, Ollama). These APIs do not support
758
+ // web_search_tool_result natively; those blocks are handled upstream
759
+ // before reaching the chat-completions provider.
760
+ "providers/openai/chat-completions-provider.ts",
761
+
762
+ // OpenAI Responses API transport (used for direct OpenAI inference).
763
+ // Converts Anthropic-style messages to Responses API input format.
764
+ // web_search_tool_result blocks are handled upstream before reaching
765
+ // the responses provider.
766
+ "providers/openai/responses-provider.ts",
760
767
 
761
768
  // Renders tool_result blocks for client display. web_search_tool_result
762
769
  // blocks are rendered by the client via their own display path.
@@ -44,7 +44,6 @@ writeFileSync(
44
44
  "# Test Identity\nYou are a test assistant.",
45
45
  );
46
46
  writeFileSync(join(testDir, "SOUL.md"), "# Test Soul\nBe helpful.");
47
- writeFileSync(join(testDir, "USER.md"), "# Test User\nName: Benchmark Runner");
48
47
 
49
48
  // Create real skill directories so projectSkillTools can load them from the catalog
50
49
  const testSkillIds = ["bench-skill-a", "bench-skill-b", "bench-skill-c"];
@@ -150,8 +149,13 @@ mock.module("../memory/conversation-crud.js", () => ({
150
149
  getConversationOriginInterface: () => null,
151
150
  getConversationType: () => "standard",
152
151
  getConversationMemoryScopeId: () => "default",
152
+ getConversationHostAccess: () => false,
153
+ getConversationGroupId: () => null,
153
154
  getConversationRecentProvenanceTrustClass: () => null,
154
155
  provenanceFromTrustContext: () => ({}),
156
+ updateConversationHostAccess: () => {},
157
+ updateMessageMetadata: () => {},
158
+ getLastUserTimestampBefore: () => null,
155
159
  relinkAttachments: () => 0,
156
160
  setConversationOriginChannelIfUnset: () => {},
157
161
  setConversationOriginInterfaceIfUnset: () => {},
@@ -251,6 +255,7 @@ mock.module("../calls/call-store.js", () => ({
251
255
  recordProcessedCallback: () => {},
252
256
  claimCallback: () => true,
253
257
  releaseCallbackClaim: () => {},
258
+ finalizeCallbackClaim: () => true,
254
259
  }));
255
260
 
256
261
  mock.module("../daemon/watch-handler.js", () => ({
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { describe, expect, test } from "bun:test";
4
+
5
+ const REPO_ROOT = resolve(import.meta.dirname ?? __dirname, "..", "..", "..");
6
+ const SKILL_PATH = resolve(
7
+ REPO_ROOT,
8
+ "skills",
9
+ "conversation-launcher",
10
+ "SKILL.md",
11
+ );
12
+ const skillContent = readFileSync(SKILL_PATH, "utf-8");
13
+
14
+ describe("conversation-launcher skill regression", () => {
15
+ test("describes the direct surface-action contract the daemon dispatches on", () => {
16
+ // The skill must render one `ui_show` card whose actions carry the wire
17
+ // contract that `handleSurfaceAction`'s `launch_conversation` branch reads.
18
+ // These tokens are the minimum the model needs to produce a valid card.
19
+ const requiredTokens = [
20
+ "ui_show",
21
+ "persistent: true",
22
+ '_action: "launch_conversation"',
23
+ "title",
24
+ "seedPrompt",
25
+ '"await_action": false',
26
+ ];
27
+ for (const token of requiredTokens) {
28
+ expect(skillContent).toContain(token);
29
+ }
30
+ });
31
+
32
+ test("does not resurrect the deprecated bash + signal-file launch flow", () => {
33
+ // The skill must not reference signal files, HOME-based workspace paths,
34
+ // or shell-based plumbing — it dispatches surface actions directly.
35
+ const forbiddenTokens = [
36
+ "bash",
37
+ "curl",
38
+ "signals/",
39
+ "jq ",
40
+ // Assembled from parts so this literal does not appear in repo grep
41
+ // results — the forbidden-tokens check would otherwise match this file.
42
+ ["launch", "conversation."].join("-"),
43
+ "VELLUM_WORKSPACE_DIR",
44
+ "INTERNAL_GATEWAY_BASE_URL",
45
+ "emit-event",
46
+ ];
47
+ for (const token of forbiddenTokens) {
48
+ expect(skillContent).not.toContain(token);
49
+ }
50
+ });
51
+ });