@vellumai/assistant 0.6.2 → 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 (895) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +41 -49
  4. package/bunfig.toml +3 -0
  5. package/docs/architecture/memory.md +1 -1
  6. package/docs/backup-troubleshooting.md +52 -0
  7. package/docs/browser-use-architecture-phase2.md +174 -0
  8. package/docs/stt-provider-onboarding.md +120 -0
  9. package/knip.json +12 -2
  10. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  11. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  12. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  13. package/openapi.yaml +1111 -86
  14. package/package.json +40 -42
  15. package/scripts/generate-openapi.ts +0 -2
  16. package/scripts/test.sh +73 -18
  17. package/src/__tests__/acp-session.test.ts +43 -0
  18. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  19. package/src/__tests__/agent-loop.test.ts +123 -0
  20. package/src/__tests__/anthropic-provider.test.ts +263 -10
  21. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  22. package/src/__tests__/app-executors.test.ts +1 -0
  23. package/src/__tests__/app-source-watcher.test.ts +37 -11
  24. package/src/__tests__/approval-routes-http.test.ts +178 -1
  25. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  26. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  27. package/src/__tests__/browser-fill-credential.test.ts +240 -94
  28. package/src/__tests__/browser-manager.test.ts +40 -27
  29. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  30. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  31. package/src/__tests__/btw-routes.test.ts +7 -0
  32. package/src/__tests__/call-controller.test.ts +581 -20
  33. package/src/__tests__/catalog-files.test.ts +1000 -0
  34. package/src/__tests__/channel-approvals.test.ts +53 -0
  35. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  36. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  37. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  38. package/src/__tests__/checker.test.ts +157 -10
  39. package/src/__tests__/clawhub-files.test.ts +347 -0
  40. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  41. package/src/__tests__/config-analysis.test.ts +100 -0
  42. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  43. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  44. package/src/__tests__/config-schema.test.ts +1248 -224
  45. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  46. package/src/__tests__/config-watcher.test.ts +43 -8
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  48. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  49. package/src/__tests__/contacts-write.test.ts +197 -0
  50. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  51. package/src/__tests__/context-window-manager.test.ts +88 -0
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  54. package/src/__tests__/conversation-agent-loop.test.ts +99 -3
  55. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  56. package/src/__tests__/conversation-attachments.test.ts +80 -4
  57. package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
  58. package/src/__tests__/conversation-error.test.ts +70 -0
  59. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  60. package/src/__tests__/conversation-history-web-search.test.ts +12 -4
  61. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  62. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  63. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  64. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  65. package/src/__tests__/conversation-list-source.test.ts +145 -0
  66. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  67. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  68. package/src/__tests__/conversation-queue.test.ts +946 -62
  69. package/src/__tests__/conversation-routes-disk-view.test.ts +275 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  72. package/src/__tests__/conversation-runtime-assembly.test.ts +324 -46
  73. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  74. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  76. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  77. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  78. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  79. package/src/__tests__/conversation-store.test.ts +195 -0
  80. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  81. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  82. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  83. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  84. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  85. package/src/__tests__/credential-health-service.test.ts +352 -0
  86. package/src/__tests__/credential-security-invariants.test.ts +6 -3
  87. package/src/__tests__/credential-vault-unit.test.ts +383 -7
  88. package/src/__tests__/credential-vault.test.ts +152 -13
  89. package/src/__tests__/credentials-cli.test.ts +42 -18
  90. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  91. package/src/__tests__/date-context.test.ts +4 -4
  92. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  93. package/src/__tests__/device-id.test.ts +112 -0
  94. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  95. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  96. package/src/__tests__/email-html-renderer.test.ts +71 -0
  97. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  98. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  99. package/src/__tests__/emit-event-signal.test.ts +71 -0
  100. package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
  101. package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
  102. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  103. package/src/__tests__/gateway-only-guard.test.ts +2 -0
  104. package/src/__tests__/gemini-provider.test.ts +66 -2
  105. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  106. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  107. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  108. package/src/__tests__/gmail-preferences.test.ts +117 -0
  109. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  110. package/src/__tests__/headless-browser-interactions.test.ts +738 -359
  111. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  112. package/src/__tests__/headless-browser-navigate.test.ts +528 -49
  113. package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
  114. package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
  115. package/src/__tests__/heartbeat-service.test.ts +70 -17
  116. package/src/__tests__/home-state-routes.test.ts +162 -0
  117. package/src/__tests__/host-bash-proxy.test.ts +145 -1
  118. package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -0
  119. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  120. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  121. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  122. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  123. package/src/__tests__/host-browser-routes.test.ts +198 -0
  124. package/src/__tests__/host-browser-ws-events-e2e.test.ts +423 -0
  125. package/src/__tests__/host-cu-proxy.test.ts +166 -1
  126. package/src/__tests__/host-file-proxy.test.ts +185 -1
  127. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  128. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  129. package/src/__tests__/host-shell-tool.test.ts +1 -11
  130. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  131. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  132. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  133. package/src/__tests__/integration-status.test.ts +6 -7
  134. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  135. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  136. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  137. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  138. package/src/__tests__/llm-usage-store.test.ts +363 -0
  139. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  140. package/src/__tests__/mcp-health-check.test.ts +10 -3
  141. package/src/__tests__/media-stream-output.test.ts +555 -0
  142. package/src/__tests__/media-stream-parser.test.ts +374 -0
  143. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  144. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  145. package/src/__tests__/media-turn-detector.test.ts +440 -0
  146. package/src/__tests__/message-queue.test.ts +125 -0
  147. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  148. package/src/__tests__/migration-export-http.test.ts +67 -8
  149. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  150. package/src/__tests__/migration-import-commit-http.test.ts +109 -7
  151. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  152. package/src/__tests__/migration-validate-http.test.ts +3 -3
  153. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  154. package/src/__tests__/model-intents.test.ts +2 -2
  155. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  156. package/src/__tests__/oauth-apps-routes.test.ts +18 -12
  157. package/src/__tests__/oauth-cli.test.ts +709 -60
  158. package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
  159. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  160. package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
  161. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  162. package/src/__tests__/oauth-providers-routes.test.ts +52 -14
  163. package/src/__tests__/oauth-store.test.ts +1465 -176
  164. package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
  165. package/src/__tests__/onboarding-template-contract.test.ts +81 -70
  166. package/src/__tests__/openai-provider.test.ts +178 -2
  167. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  168. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  169. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  170. package/src/__tests__/outlook-categories.test.ts +1 -1
  171. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  172. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  173. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  174. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  175. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  176. package/src/__tests__/outlook-trash.test.ts +1 -1
  177. package/src/__tests__/outlook-unsubscribe.test.ts +32 -3
  178. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  179. package/src/__tests__/permission-mode.test.ts +28 -56
  180. package/src/__tests__/persona-resolver.test.ts +251 -0
  181. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  182. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  183. package/src/__tests__/platform.test.ts +92 -1
  184. package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
  185. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  186. package/src/__tests__/pricing.test.ts +174 -0
  187. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  188. package/src/__tests__/qdrant-manager.test.ts +29 -8
  189. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  190. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  191. package/src/__tests__/relay-server.test.ts +423 -5
  192. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  193. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  194. package/src/__tests__/schedule-routes.test.ts +162 -0
  195. package/src/__tests__/search-skills-unified.test.ts +118 -0
  196. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  197. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  198. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  199. package/src/__tests__/secure-keys.test.ts +107 -0
  200. package/src/__tests__/send-endpoint-busy.test.ts +8 -1
  201. package/src/__tests__/sequence-store.test.ts +1 -1
  202. package/src/__tests__/server-history-render.test.ts +49 -0
  203. package/src/__tests__/set-permission-mode.test.ts +13 -250
  204. package/src/__tests__/settings-routes.test.ts +201 -0
  205. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  206. package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
  207. package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
  208. package/src/__tests__/skills.test.ts +5 -2
  209. package/src/__tests__/skillssh-files.test.ts +446 -0
  210. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  211. package/src/__tests__/slack-channel-config.test.ts +576 -16
  212. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  213. package/src/__tests__/stt-stream-session.test.ts +535 -0
  214. package/src/__tests__/subagent-detail.test.ts +44 -2
  215. package/src/__tests__/subagent-disposal.test.ts +1 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  217. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  218. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  219. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  220. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  221. package/src/__tests__/subagent-tools.test.ts +1 -0
  222. package/src/__tests__/subagent-types.test.ts +1 -0
  223. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  224. package/src/__tests__/system-prompt.test.ts +184 -27
  225. package/src/__tests__/task-scheduler.test.ts +32 -6
  226. package/src/__tests__/telegram-config.test.ts +10 -13
  227. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  228. package/src/__tests__/terminal-tools.test.ts +25 -5
  229. package/src/__tests__/test-preload.ts +18 -0
  230. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  231. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  232. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  233. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  234. package/src/__tests__/tool-executor.test.ts +33 -24
  235. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  236. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  237. package/src/__tests__/top-level-renderer.test.ts +73 -1
  238. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  239. package/src/__tests__/trust-store.test.ts +7 -1
  240. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  241. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  242. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  243. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  244. package/src/__tests__/twilio-routes.test.ts +376 -0
  245. package/src/__tests__/unicode.test.ts +293 -0
  246. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  247. package/src/__tests__/update-bulletin.test.ts +206 -5
  248. package/src/__tests__/usage-routes.test.ts +25 -4
  249. package/src/__tests__/user-reference.test.ts +46 -61
  250. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  251. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  252. package/src/__tests__/voice-config-update.test.ts +403 -0
  253. package/src/__tests__/voice-quality.test.ts +434 -19
  254. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  255. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  256. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  257. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  258. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  259. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  260. package/src/__tests__/workspace-policy.test.ts +2 -0
  261. package/src/acp/client-handler.ts +30 -4
  262. package/src/agent/image-optimize.ts +24 -12
  263. package/src/agent/loop.ts +55 -9
  264. package/src/approvals/guardian-request-resolvers.ts +21 -15
  265. package/src/backup/__tests__/backup-key.test.ts +152 -0
  266. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  267. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  268. package/src/backup/__tests__/local-writer.test.ts +218 -0
  269. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  270. package/src/backup/__tests__/paths.test.ts +300 -0
  271. package/src/backup/__tests__/restore.test.ts +498 -0
  272. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  273. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  274. package/src/backup/backup-key.ts +137 -0
  275. package/src/backup/backup-worker.ts +459 -0
  276. package/src/backup/list-snapshots.ts +147 -0
  277. package/src/backup/local-writer.ts +133 -0
  278. package/src/backup/offsite-writer.ts +222 -0
  279. package/src/backup/paths.ts +226 -0
  280. package/src/backup/restore.ts +322 -0
  281. package/src/backup/snapshot-lock.ts +431 -0
  282. package/src/backup/stream-crypt.ts +263 -0
  283. package/src/browser-session/__tests__/manager.test.ts +297 -0
  284. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  285. package/src/browser-session/backends/extension.ts +26 -0
  286. package/src/browser-session/backends/local.ts +24 -0
  287. package/src/browser-session/events.ts +164 -0
  288. package/src/browser-session/index.ts +27 -0
  289. package/src/browser-session/manager.ts +159 -0
  290. package/src/browser-session/types.ts +28 -0
  291. package/src/bundler/package-resolver.ts +4 -0
  292. package/src/calls/audio-store.ts +11 -5
  293. package/src/calls/call-controller.ts +226 -71
  294. package/src/calls/call-domain.ts +9 -0
  295. package/src/calls/call-speech-output.ts +190 -0
  296. package/src/calls/call-transport.ts +77 -0
  297. package/src/calls/media-stream-audio-transcode.ts +173 -0
  298. package/src/calls/media-stream-output.ts +660 -0
  299. package/src/calls/media-stream-parser.ts +300 -0
  300. package/src/calls/media-stream-protocol.ts +166 -0
  301. package/src/calls/media-stream-server.ts +592 -0
  302. package/src/calls/media-stream-stt-session.ts +460 -0
  303. package/src/calls/media-turn-detector.ts +230 -0
  304. package/src/calls/relay-server.ts +90 -75
  305. package/src/calls/resolve-call-tts-provider.ts +136 -0
  306. package/src/calls/telephony-stt-routing.ts +145 -0
  307. package/src/calls/tts-call-strategy.ts +161 -0
  308. package/src/calls/tts-text-sanitizer.ts +32 -16
  309. package/src/calls/twilio-routes.ts +281 -17
  310. package/src/calls/voice-quality.ts +78 -35
  311. package/src/calls/voice-session-bridge.ts +8 -1
  312. package/src/channels/__tests__/types.test.ts +134 -0
  313. package/src/channels/types.ts +69 -3
  314. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  315. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  316. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  317. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  318. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  319. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  320. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  321. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  322. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  323. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  324. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  325. package/src/cli/commands/backup.ts +993 -0
  326. package/src/cli/commands/conversations.ts +77 -0
  327. package/src/cli/commands/credentials.ts +3 -4
  328. package/src/cli/commands/domain.ts +210 -0
  329. package/src/cli/commands/email.ts +273 -16
  330. package/src/cli/commands/mcp.ts +16 -4
  331. package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
  332. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  333. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  334. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  335. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +32 -33
  336. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
  337. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -12
  338. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  339. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  340. package/src/cli/commands/oauth/apps.ts +7 -4
  341. package/src/cli/commands/oauth/connect.ts +6 -3
  342. package/src/cli/commands/oauth/disconnect.ts +1 -1
  343. package/src/cli/commands/oauth/mode.ts +12 -3
  344. package/src/cli/commands/oauth/providers.ts +215 -36
  345. package/src/cli/commands/oauth/shared.ts +7 -6
  346. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
  347. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  348. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  349. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  350. package/src/cli/commands/platform/index.ts +107 -10
  351. package/src/cli/commands/usage.ts +10 -9
  352. package/src/cli/lib/daemon-credential-client.ts +4 -0
  353. package/src/cli/program.ts +30 -4
  354. package/src/config/__tests__/backup-schema.test.ts +134 -0
  355. package/src/config/assistant-feature-flags.ts +61 -62
  356. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  357. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -0
  358. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  359. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  360. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  361. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  362. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  363. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  364. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  365. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  366. package/src/config/bundled-skills/contacts/SKILL.md +5 -2
  367. package/src/config/bundled-skills/document/SKILL.md +4 -0
  368. package/src/config/bundled-skills/gmail/SKILL.md +54 -8
  369. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  370. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  371. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  372. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  373. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  374. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  375. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  376. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  377. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  378. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  379. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  380. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  381. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  382. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  383. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  384. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  385. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  386. package/src/config/bundled-skills/outlook/SKILL.md +9 -2
  387. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  388. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  389. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  390. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  391. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  392. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  393. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  394. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  395. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  396. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  397. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  398. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  399. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  400. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  401. package/src/config/bundled-tool-registry.ts +8 -0
  402. package/src/config/env-registry.ts +38 -0
  403. package/src/config/env.ts +49 -4
  404. package/src/config/feature-flag-registry.json +85 -14
  405. package/src/config/loader.ts +82 -13
  406. package/src/config/sanitize-for-transfer.ts +47 -0
  407. package/src/config/schema.ts +81 -15
  408. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  409. package/src/config/schemas/analysis.ts +51 -0
  410. package/src/config/schemas/backup.ts +72 -0
  411. package/src/config/schemas/calls.ts +1 -26
  412. package/src/config/schemas/elevenlabs.ts +0 -59
  413. package/src/config/schemas/filing.ts +47 -7
  414. package/src/config/schemas/heartbeat.ts +27 -5
  415. package/src/config/schemas/host-browser.ts +112 -0
  416. package/src/config/schemas/inference.ts +1 -1
  417. package/src/config/schemas/memory-lifecycle.ts +14 -2
  418. package/src/config/schemas/memory-retrieval.ts +103 -0
  419. package/src/config/schemas/security.ts +0 -6
  420. package/src/config/schemas/services.ts +52 -0
  421. package/src/config/schemas/stt.ts +59 -0
  422. package/src/config/schemas/tts.ts +230 -0
  423. package/src/config/schemas/updates.ts +14 -0
  424. package/src/config/skills.ts +4 -0
  425. package/src/config/types.ts +4 -1
  426. package/src/contacts/contact-store.ts +56 -11
  427. package/src/contacts/contacts-write.ts +38 -1
  428. package/src/context/post-turn-tool-result-truncation.ts +177 -0
  429. package/src/context/tool-result-truncation.ts +2 -1
  430. package/src/context/window-manager.ts +61 -10
  431. package/src/credential-execution/approval-bridge.ts +49 -15
  432. package/src/credential-execution/executable-discovery.ts +12 -2
  433. package/src/credential-execution/process-manager.ts +33 -2
  434. package/src/credential-health/credential-health-service.ts +366 -0
  435. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  436. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  437. package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
  438. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  439. package/src/daemon/app-source-watcher.ts +35 -0
  440. package/src/daemon/config-watcher.ts +99 -5
  441. package/src/daemon/context-overflow-approval.ts +5 -0
  442. package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
  443. package/src/daemon/conversation-agent-loop.ts +153 -42
  444. package/src/daemon/conversation-attachments.ts +40 -0
  445. package/src/daemon/conversation-error.ts +11 -0
  446. package/src/daemon/conversation-history.ts +40 -6
  447. package/src/daemon/conversation-launch.ts +220 -0
  448. package/src/daemon/conversation-lifecycle.ts +59 -9
  449. package/src/daemon/conversation-messaging.ts +37 -3
  450. package/src/daemon/conversation-notifiers.ts +5 -0
  451. package/src/daemon/conversation-process.ts +622 -13
  452. package/src/daemon/conversation-queue-manager.ts +24 -0
  453. package/src/daemon/conversation-runtime-assembly.ts +128 -36
  454. package/src/daemon/conversation-slash.ts +36 -0
  455. package/src/daemon/conversation-surfaces.ts +131 -40
  456. package/src/daemon/conversation-tool-setup.ts +99 -8
  457. package/src/daemon/conversation-usage.ts +7 -4
  458. package/src/daemon/conversation-workspace.ts +12 -0
  459. package/src/daemon/conversation.ts +292 -16
  460. package/src/daemon/date-context.ts +10 -10
  461. package/src/daemon/first-greeting.ts +3 -2
  462. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  463. package/src/daemon/handlers/conversations.ts +13 -141
  464. package/src/daemon/handlers/shared.ts +80 -0
  465. package/src/daemon/handlers/skills.ts +483 -44
  466. package/src/daemon/host-bash-proxy.ts +48 -13
  467. package/src/daemon/host-browser-proxy.ts +192 -0
  468. package/src/daemon/host-cu-proxy.ts +36 -11
  469. package/src/daemon/host-file-proxy.ts +57 -9
  470. package/src/daemon/lifecycle.ts +179 -28
  471. package/src/daemon/message-protocol.ts +13 -0
  472. package/src/daemon/message-types/conversations.ts +89 -14
  473. package/src/daemon/message-types/home.ts +40 -0
  474. package/src/daemon/message-types/host-browser.ts +100 -0
  475. package/src/daemon/message-types/meet.ts +143 -0
  476. package/src/daemon/message-types/messages.ts +19 -5
  477. package/src/daemon/message-types/schedules.ts +34 -2
  478. package/src/daemon/message-types/skills.ts +26 -0
  479. package/src/daemon/message-types/subagents.ts +2 -0
  480. package/src/daemon/message-types/surfaces.ts +2 -0
  481. package/src/daemon/server.ts +439 -14
  482. package/src/daemon/shutdown-handlers.ts +32 -4
  483. package/src/daemon/shutdown-registry.ts +40 -0
  484. package/src/daemon/tool-side-effects.ts +15 -0
  485. package/src/daemon/transport-hints.ts +5 -24
  486. package/src/email/html-renderer.ts +76 -0
  487. package/src/heartbeat/heartbeat-service.ts +93 -7
  488. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  489. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  490. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  491. package/src/home/__tests__/feed-types.test.ts +275 -0
  492. package/src/home/__tests__/feed-writer.test.ts +688 -0
  493. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  494. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  495. package/src/home/__tests__/progress-formula.test.ts +213 -0
  496. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  497. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  498. package/src/home/assistant-feed-authoring.ts +124 -0
  499. package/src/home/emit-feed-event.ts +158 -0
  500. package/src/home/feed-scheduler.ts +247 -0
  501. package/src/home/feed-types.ts +181 -0
  502. package/src/home/feed-writer.ts +469 -0
  503. package/src/home/platform-gmail-digest.ts +163 -0
  504. package/src/home/progress-formula.ts +86 -0
  505. package/src/home/relationship-state-writer.ts +824 -0
  506. package/src/home/relationship-state.ts +143 -0
  507. package/src/home/rollup-producer.ts +384 -0
  508. package/src/hooks/runner.ts +7 -0
  509. package/src/inbound/platform-callback-registration.ts +30 -20
  510. package/src/inbound/public-ingress-urls.ts +12 -0
  511. package/src/instrument.ts +1 -1
  512. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  513. package/src/ipc/cli-client.ts +151 -0
  514. package/src/ipc/cli-server.ts +234 -0
  515. package/src/ipc/gateway-client.ts +180 -0
  516. package/src/ipc/routes/index.ts +5 -0
  517. package/src/ipc/routes/wake-conversation.ts +19 -0
  518. package/src/mcp/client.ts +59 -24
  519. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  520. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  521. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  522. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  523. package/src/memory/app-store.ts +31 -1
  524. package/src/memory/attachments-store.ts +70 -0
  525. package/src/memory/auto-analysis-enqueue.ts +127 -0
  526. package/src/memory/auto-analysis-guard.ts +27 -0
  527. package/src/memory/cleanup-schedule-state.ts +37 -0
  528. package/src/memory/conversation-analyze-job.ts +73 -0
  529. package/src/memory/conversation-crud.ts +122 -0
  530. package/src/memory/conversation-disk-view.ts +7 -0
  531. package/src/memory/conversation-group-migration.ts +34 -2
  532. package/src/memory/conversation-queries.ts +6 -5
  533. package/src/memory/conversation-starters-cadence.ts +76 -0
  534. package/src/memory/conversation-title-service.ts +5 -2
  535. package/src/memory/db-init.ts +18 -0
  536. package/src/memory/db-maintenance.ts +108 -0
  537. package/src/memory/db.ts +1 -0
  538. package/src/memory/embedding-backend.test.ts +75 -0
  539. package/src/memory/embedding-backend.ts +131 -5
  540. package/src/memory/embedding-gemini.test.ts +54 -0
  541. package/src/memory/embedding-gemini.ts +20 -9
  542. package/src/memory/embedding-local.ts +176 -17
  543. package/src/memory/graph/consolidation.ts +10 -23
  544. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  545. package/src/memory/graph/extraction-job.ts +15 -0
  546. package/src/memory/graph/extraction.test.ts +23 -0
  547. package/src/memory/graph/extraction.ts +8 -0
  548. package/src/memory/graph/retriever.ts +67 -40
  549. package/src/memory/graph/scoring.test.ts +186 -0
  550. package/src/memory/graph/scoring.ts +31 -1
  551. package/src/memory/graph/store.test.ts +7 -3
  552. package/src/memory/graph/store.ts +47 -12
  553. package/src/memory/graph/tools.ts +1 -1
  554. package/src/memory/group-crud.ts +6 -1
  555. package/src/memory/indexer.ts +95 -16
  556. package/src/memory/job-handlers/cleanup.ts +11 -8
  557. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  558. package/src/memory/jobs-store.ts +64 -4
  559. package/src/memory/jobs-worker.ts +22 -9
  560. package/src/memory/llm-usage-store.ts +137 -60
  561. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  562. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  563. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  564. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  565. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  566. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  567. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  568. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  569. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  570. package/src/memory/migrations/index.ts +12 -0
  571. package/src/memory/migrations/registry.ts +16 -0
  572. package/src/memory/qdrant-manager.ts +43 -16
  573. package/src/memory/schema/conversations.ts +3 -0
  574. package/src/memory/schema/oauth.ts +21 -13
  575. package/src/memory/usage-buckets.ts +396 -0
  576. package/src/messaging/providers/gmail/client.ts +57 -6
  577. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  578. package/src/messaging/providers/slack/adapter.ts +143 -38
  579. package/src/messaging/providers/slack/client.ts +16 -0
  580. package/src/messaging/providers/slack/types.ts +4 -0
  581. package/src/notifications/decision-engine.ts +3 -3
  582. package/src/notifications/signal.ts +5 -0
  583. package/src/oauth/AGENTS.md +76 -0
  584. package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
  585. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  586. package/src/oauth/byo-connection.test.ts +26 -9
  587. package/src/oauth/byo-connection.ts +10 -8
  588. package/src/oauth/connect-orchestrator.ts +25 -21
  589. package/src/oauth/connect-types.ts +3 -3
  590. package/src/oauth/connection-resolver.test.ts +17 -4
  591. package/src/oauth/connection-resolver.ts +22 -18
  592. package/src/oauth/connection.ts +3 -1
  593. package/src/oauth/manual-token-connection.ts +13 -13
  594. package/src/oauth/oauth-store.ts +223 -100
  595. package/src/oauth/platform-connection.test.ts +101 -3
  596. package/src/oauth/platform-connection.ts +56 -35
  597. package/src/oauth/provider-serializer.ts +31 -5
  598. package/src/oauth/revoke.ts +76 -0
  599. package/src/oauth/seed-providers.ts +133 -87
  600. package/src/oauth/token-persistence.ts +1 -1
  601. package/src/permissions/checker.ts +16 -6
  602. package/src/permissions/defaults.ts +49 -1
  603. package/src/permissions/permission-mode.ts +4 -11
  604. package/src/permissions/prompter.ts +13 -1
  605. package/src/permissions/trust-store.ts +3 -3
  606. package/src/permissions/v2-consent-policy.ts +87 -0
  607. package/src/permissions/workspace-policy.ts +3 -0
  608. package/src/platform/client.test.ts +10 -0
  609. package/src/platform/sync-identity.ts +129 -0
  610. package/src/prompts/persona-resolver.ts +126 -2
  611. package/src/prompts/system-prompt.ts +76 -38
  612. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  613. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  614. package/src/prompts/templates/SOUL.md +3 -1
  615. package/src/prompts/templates/UPDATES.md +12 -0
  616. package/src/prompts/templates/channels/slack.md +20 -0
  617. package/src/prompts/update-bulletin-format.ts +26 -9
  618. package/src/prompts/update-bulletin.ts +34 -23
  619. package/src/prompts/user-reference.ts +20 -17
  620. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  621. package/src/providers/anthropic/client.ts +157 -60
  622. package/src/providers/fireworks/client.ts +2 -2
  623. package/src/providers/gemini/client.ts +9 -1
  624. package/src/providers/model-catalog.ts +6 -0
  625. package/src/providers/model-intents.ts +4 -4
  626. package/src/providers/ollama/client.ts +2 -2
  627. package/src/providers/openai/chat-completions-provider.ts +474 -0
  628. package/src/providers/openai/client.ts +25 -440
  629. package/src/providers/openai/responses-provider.ts +502 -0
  630. package/src/providers/openrouter/client.ts +101 -4
  631. package/src/providers/provider-secret-catalog.ts +139 -0
  632. package/src/providers/registry.ts +2 -2
  633. package/src/providers/retry.ts +14 -3
  634. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  635. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  636. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  637. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  638. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  639. package/src/providers/speech-to-text/deepgram.ts +115 -0
  640. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  641. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  642. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  643. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  644. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  645. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  646. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  647. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  648. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  649. package/src/providers/speech-to-text/resolve.ts +386 -6
  650. package/src/providers/types.ts +10 -1
  651. package/src/runtime/AGENTS.md +65 -0
  652. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  653. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  654. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  655. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  656. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  657. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  658. package/src/runtime/agent-wake.ts +512 -0
  659. package/src/runtime/assistant-event-hub.ts +2 -2
  660. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  661. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  662. package/src/runtime/auth/__tests__/route-policy.test.ts +48 -0
  663. package/src/runtime/auth/middleware.ts +98 -0
  664. package/src/runtime/auth/route-policy.ts +33 -9
  665. package/src/runtime/auth/token-service.ts +56 -1
  666. package/src/runtime/btw-sidechain.ts +2 -0
  667. package/src/runtime/capability-tokens.ts +414 -0
  668. package/src/runtime/channel-approvals.ts +18 -5
  669. package/src/runtime/channel-invite-transport.ts +1 -1
  670. package/src/runtime/channel-invite-transports/email.ts +14 -6
  671. package/src/runtime/channel-readiness-service.ts +12 -22
  672. package/src/runtime/chrome-extension-registry.ts +368 -0
  673. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  674. package/src/runtime/guardian-decision-types.ts +7 -0
  675. package/src/runtime/http-server.ts +815 -75
  676. package/src/runtime/http-types.ts +6 -2
  677. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  678. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  679. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +198 -0
  680. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  681. package/src/runtime/migrations/migration-transport.ts +7 -0
  682. package/src/runtime/migrations/migration-wizard.ts +23 -2
  683. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  684. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  685. package/src/runtime/migrations/vbundle-import-analyzer.ts +96 -1
  686. package/src/runtime/migrations/vbundle-importer.ts +89 -5
  687. package/src/runtime/pending-interactions.ts +18 -13
  688. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  689. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  690. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  691. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  692. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  693. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  694. package/src/runtime/routes/app-management-routes.ts +12 -18
  695. package/src/runtime/routes/approval-routes.ts +90 -16
  696. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  697. package/src/runtime/routes/attachment-routes.ts +216 -17
  698. package/src/runtime/routes/backup-routes.ts +519 -0
  699. package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
  700. package/src/runtime/routes/btw-routes.ts +8 -6
  701. package/src/runtime/routes/contact-routes.test.ts +298 -0
  702. package/src/runtime/routes/contact-routes.ts +132 -5
  703. package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
  704. package/src/runtime/routes/conversation-management-routes.ts +223 -0
  705. package/src/runtime/routes/conversation-routes.ts +598 -103
  706. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  707. package/src/runtime/routes/filing-routes.ts +93 -0
  708. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  709. package/src/runtime/routes/home-feed-routes.ts +334 -0
  710. package/src/runtime/routes/home-state-routes.ts +138 -0
  711. package/src/runtime/routes/host-browser-routes.ts +268 -0
  712. package/src/runtime/routes/host-file-routes.ts +9 -1
  713. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  714. package/src/runtime/routes/identity-routes.ts +262 -33
  715. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  716. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  717. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  718. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  719. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  720. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  721. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  722. package/src/runtime/routes/log-export-routes.ts +42 -22
  723. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  724. package/src/runtime/routes/memory-item-routes.ts +1 -7
  725. package/src/runtime/routes/migration-routes.ts +122 -2
  726. package/src/runtime/routes/oauth-apps.ts +15 -17
  727. package/src/runtime/routes/oauth-providers.ts +4 -0
  728. package/src/runtime/routes/schedule-routes.ts +24 -11
  729. package/src/runtime/routes/settings-routes.ts +31 -102
  730. package/src/runtime/routes/skills-routes.ts +128 -9
  731. package/src/runtime/routes/stt-routes.ts +233 -0
  732. package/src/runtime/routes/subagents-routes.ts +14 -10
  733. package/src/runtime/routes/surface-action-routes.ts +41 -2
  734. package/src/runtime/routes/tts-routes.ts +108 -24
  735. package/src/runtime/routes/usage-routes.ts +38 -9
  736. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  737. package/src/runtime/routes/user-routes.ts +13 -1
  738. package/src/runtime/routes/work-items-routes.ts +8 -1
  739. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  740. package/src/runtime/routes/workspace-routes.ts +8 -1
  741. package/src/runtime/routes/workspace-utils.ts +2 -0
  742. package/src/runtime/runtime-mode.ts +33 -0
  743. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  744. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  745. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  746. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  747. package/src/runtime/services/analyze-conversation.ts +344 -0
  748. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  749. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  750. package/src/runtime/skill-route-registry.ts +49 -0
  751. package/src/runtime/slack-block-formatting.ts +437 -10
  752. package/src/schedule/scheduler.ts +57 -5
  753. package/src/security/ces-credential-client.ts +20 -0
  754. package/src/security/ces-rpc-credential-backend.ts +17 -0
  755. package/src/security/credential-backend.ts +5 -0
  756. package/src/security/oauth2.ts +68 -29
  757. package/src/security/secure-keys.ts +143 -27
  758. package/src/security/token-manager.ts +31 -10
  759. package/src/sequence/engine.ts +23 -0
  760. package/src/sequence/types.ts +1 -1
  761. package/src/skills/catalog-files.ts +554 -0
  762. package/src/skills/category-inference.ts +122 -0
  763. package/src/skills/clawhub-files.ts +213 -0
  764. package/src/skills/clawhub.ts +84 -23
  765. package/src/skills/skill-file-provider.ts +40 -0
  766. package/src/skills/skillssh-files.ts +395 -0
  767. package/src/skills/skillssh-registry.ts +4 -4
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  769. package/src/stt/__tests__/types.test.ts +89 -0
  770. package/src/stt/daemon-batch-transcriber.ts +195 -0
  771. package/src/stt/stt-stream-session.ts +499 -0
  772. package/src/stt/types.ts +330 -0
  773. package/src/stt/wav-encoder.test.ts +373 -0
  774. package/src/stt/wav-encoder.ts +175 -0
  775. package/src/subagent/manager.ts +169 -40
  776. package/src/subagent/types.ts +19 -0
  777. package/src/tools/apps/executors.ts +11 -2
  778. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  779. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  780. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  781. package/src/tools/browser/auth-detector.ts +43 -12
  782. package/src/tools/browser/browser-execution.ts +1787 -342
  783. package/src/tools/browser/browser-manager.ts +81 -12
  784. package/src/tools/browser/browser-mode-constants.ts +12 -0
  785. package/src/tools/browser/browser-mode.ts +92 -0
  786. package/src/tools/browser/browser-status-constants.ts +33 -0
  787. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  788. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  789. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +1263 -0
  790. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
  791. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -0
  792. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  793. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  794. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  795. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  796. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  797. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  798. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +1007 -0
  799. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  800. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +744 -0
  801. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  802. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
  803. package/src/tools/browser/cdp-client/errors.ts +49 -0
  804. package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
  805. package/src/tools/browser/cdp-client/factory.ts +914 -0
  806. package/src/tools/browser/cdp-client/index.ts +28 -0
  807. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  808. package/src/tools/browser/cdp-client/types.ts +120 -0
  809. package/src/tools/credentials/vault.ts +35 -6
  810. package/src/tools/filesystem/edit.ts +1 -1
  811. package/src/tools/filesystem/list.ts +1 -1
  812. package/src/tools/filesystem/read.ts +1 -1
  813. package/src/tools/filesystem/write.ts +2 -1
  814. package/src/tools/host-filesystem/edit.ts +1 -1
  815. package/src/tools/host-filesystem/read.ts +12 -15
  816. package/src/tools/host-filesystem/write.ts +1 -1
  817. package/src/tools/host-terminal/host-shell.ts +21 -16
  818. package/src/tools/network/web-fetch.ts +5 -2
  819. package/src/tools/network/web-search.ts +5 -2
  820. package/src/tools/permission-checker.ts +77 -82
  821. package/src/tools/registry.ts +0 -2
  822. package/src/tools/secret-detection-handler.ts +34 -0
  823. package/src/tools/shared/filesystem/image-read.ts +61 -40
  824. package/src/tools/shared/shell-output.ts +3 -1
  825. package/src/tools/side-effects.ts +2 -0
  826. package/src/tools/skills/sandbox-runner.ts +3 -2
  827. package/src/tools/subagent/spawn.ts +47 -3
  828. package/src/tools/subagent/status.ts +2 -0
  829. package/src/tools/system/register.ts +2 -16
  830. package/src/tools/terminal/safe-env.ts +15 -0
  831. package/src/tools/terminal/shell.ts +36 -20
  832. package/src/tools/tool-approval-handler.ts +48 -2
  833. package/src/tools/tool-manifest.ts +21 -0
  834. package/src/tools/types.ts +19 -0
  835. package/src/tools/ui-surface/definitions.ts +6 -1
  836. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  837. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  838. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  839. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  840. package/src/tts/provider-catalog.ts +201 -0
  841. package/src/tts/provider-registry.ts +73 -0
  842. package/src/tts/providers/deepgram-provider.ts +219 -0
  843. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  844. package/src/tts/providers/fish-audio-provider.ts +183 -0
  845. package/src/tts/providers/index.ts +42 -0
  846. package/src/tts/providers/register-builtins.ts +130 -0
  847. package/src/tts/synthesize-text.ts +110 -0
  848. package/src/tts/tts-config-resolver.ts +78 -0
  849. package/src/tts/types.ts +153 -0
  850. package/src/types/onboarding-context.ts +7 -0
  851. package/src/util/abort-reasons.ts +58 -0
  852. package/src/util/device-id.ts +32 -16
  853. package/src/util/errors.ts +9 -1
  854. package/src/util/platform.ts +63 -24
  855. package/src/util/pricing.ts +66 -3
  856. package/src/util/spawn.ts +1 -1
  857. package/src/util/truncate.ts +4 -2
  858. package/src/util/unicode.ts +201 -0
  859. package/src/version.ts +19 -24
  860. package/src/watcher/engine.ts +23 -0
  861. package/src/watcher/watcher-store.ts +31 -0
  862. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  863. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  864. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  865. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  866. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  867. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  868. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  869. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  870. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  871. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  872. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  873. package/src/workspace/migrations/registry.ts +16 -0
  874. package/src/workspace/top-level-renderer.ts +31 -1
  875. package/src/workspace/turn-commit.ts +31 -0
  876. package/src/__tests__/chrome-cdp.test.ts +0 -419
  877. package/src/__tests__/email-cli.test.ts +0 -297
  878. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  879. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  880. package/src/__tests__/permission-mode-store.test.ts +0 -277
  881. package/src/browser-extension-relay/protocol.ts +0 -63
  882. package/src/browser-extension-relay/server.ts +0 -203
  883. package/src/cli/commands/browser-relay.ts +0 -536
  884. package/src/config/schemas/sandbox.ts +0 -14
  885. package/src/email/guardrails.ts +0 -221
  886. package/src/email/provider.ts +0 -117
  887. package/src/email/providers/agentmail.ts +0 -361
  888. package/src/email/providers/index.ts +0 -65
  889. package/src/email/service.ts +0 -384
  890. package/src/email/types.ts +0 -126
  891. package/src/permissions/permission-mode-store.ts +0 -180
  892. package/src/prompts/templates/USER.md +0 -13
  893. package/src/providers/speech-to-text/types.ts +0 -17
  894. package/src/tools/browser/chrome-cdp.ts +0 -239
  895. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -9,11 +9,64 @@ mock.module("../util/logger.js", () => ({
9
9
  }),
10
10
  }));
11
11
 
12
- // Track calls to browserManager and url-safety helpers
12
+ // ── Fake CdpClient ───────────────────────────────────────────────────
13
+ //
14
+ // Programmable send handler + call log shared across tests. Each test
15
+ // resets these in `beforeEach` via `resetCdp()`. The mocked
16
+ // `getCdpClient` mirrors the real factory's routing decision (local
17
+ // vs extension is driven by `context.hostBrowserProxy`) so individual
18
+ // tests can exercise either transport without process-wide coupling.
19
+ //
20
+ // Note: bun's `mock.module` is process-global, but `scripts/test.sh`
21
+ // runs each test file in its own bun process so this mock only
22
+ // affects this file's tests.
23
+
24
+ let cdpSendCalls: Array<{ method: string; params?: unknown }> = [];
25
+ let cdpSendHandler: (
26
+ method: string,
27
+ params?: Record<string, unknown>,
28
+ ) => unknown = () => ({});
29
+ let cdpDisposed = false;
30
+
31
+ function makeFakeCdp(kind: "local" | "extension", conversationId: string) {
32
+ return {
33
+ kind,
34
+ conversationId,
35
+ async send<T>(
36
+ method: string,
37
+ params?: Record<string, unknown>,
38
+ ): Promise<T> {
39
+ cdpSendCalls.push({ method, params });
40
+ const value = cdpSendHandler(method, params);
41
+ return (await value) as T;
42
+ },
43
+ dispose() {
44
+ cdpDisposed = true;
45
+ },
46
+ };
47
+ }
48
+
49
+ mock.module("../tools/browser/cdp-client/factory.js", () => ({
50
+ getCdpClient: (context: {
51
+ hostBrowserProxy?: unknown;
52
+ conversationId: string;
53
+ }) =>
54
+ makeFakeCdp(
55
+ context.hostBrowserProxy ? "extension" : "local",
56
+ context.conversationId,
57
+ ),
58
+ }));
59
+
60
+ // ── Minimal browserManager stub ──────────────────────────────────────
61
+ //
62
+ // The local path still installs a Playwright route handler via
63
+ // browserManager.getOrCreateSessionPage() → page.route(...). We keep
64
+ // a tiny stub so the happy path doesn't blow up when the route handler
65
+ // is installed/uninstalled; the route logic itself is only exercised
66
+ // by the SSRF redirect test below.
67
+
13
68
  let mockPage: {
14
- goto: ReturnType<typeof mock>;
15
- title: ReturnType<typeof mock>;
16
- url: ReturnType<typeof mock>;
69
+ url: () => string;
17
70
  route: ReturnType<typeof mock>;
18
71
  unroute: ReturnType<typeof mock>;
19
72
  close: () => Promise<void>;
@@ -21,28 +74,52 @@ let mockPage: {
21
74
  };
22
75
 
23
76
  let getOrCreateSessionPageMock: ReturnType<typeof mock>;
77
+ let clearSnapshotBackendNodeMapMock: ReturnType<typeof mock>;
78
+ let positionWindowSidebarMock: ReturnType<typeof mock>;
79
+
80
+ const preferredBackendKinds = new Map<string, string>();
24
81
 
25
82
  mock.module("../tools/browser/browser-manager.js", () => {
26
83
  getOrCreateSessionPageMock = mock(async () => mockPage);
84
+ clearSnapshotBackendNodeMapMock = mock(() => {});
85
+ positionWindowSidebarMock = mock(async () => {});
86
+ preferredBackendKinds.clear();
27
87
  return {
28
88
  browserManager: {
29
89
  getOrCreateSessionPage: getOrCreateSessionPageMock,
30
- clearSnapshotMap: mock(() => {}),
90
+ clearSnapshotBackendNodeMap: clearSnapshotBackendNodeMapMock,
31
91
  supportsRouteInterception: true,
32
92
  isInteractive: () => false,
33
- positionWindowSidebar: () => {},
93
+ positionWindowSidebar: positionWindowSidebarMock,
94
+ getPreferredBackendKind: (conversationId: string) =>
95
+ preferredBackendKinds.get(conversationId) ?? null,
96
+ setPreferredBackendKind: (conversationId: string, kind: string) => {
97
+ preferredBackendKinds.set(conversationId, kind);
98
+ },
99
+ clearPreferredBackendKind: (conversationId: string) => {
100
+ preferredBackendKinds.delete(conversationId);
101
+ },
34
102
  },
35
103
  };
36
104
  });
37
105
 
106
+ mock.module("../tools/browser/browser-screencast.js", () => ({
107
+ ensureScreencast: async () => {},
108
+ getSender: () => null,
109
+ stopAllScreencasts: async () => {},
110
+ stopBrowserScreencast: async () => {},
111
+ }));
112
+
38
113
  // Default url-safety: allow everything
39
114
  let parseUrlResult: URL | null = null;
115
+ let parseUrlMock: (input: unknown) => URL | null = () => parseUrlResult;
40
116
  let isPrivateResult = false;
117
+ let isPrivateHostMock: (hostname: string) => boolean = () => isPrivateResult;
41
118
  let resolveResult: { blockedAddress?: string } = {};
42
119
 
43
120
  mock.module("../tools/network/url-safety.js", () => ({
44
- parseUrl: (_input: unknown) => parseUrlResult,
45
- isPrivateOrLocalHost: () => isPrivateResult,
121
+ parseUrl: (input: unknown) => parseUrlMock(input),
122
+ isPrivateOrLocalHost: (hostname: string) => isPrivateHostMock(hostname),
46
123
  resolveHostAddresses: async () => [],
47
124
  resolveRequestAddress: async () => resolveResult,
48
125
  sanitizeUrlForOutput: (url: URL) => url.href,
@@ -59,12 +136,7 @@ const ctx: ToolContext = {
59
136
 
60
137
  function resetMockPage() {
61
138
  mockPage = {
62
- goto: mock(async () => ({
63
- status: () => 200,
64
- url: () => "https://example.com/",
65
- })),
66
- title: mock(async () => "Example"),
67
- url: mock(() => "https://example.com/"),
139
+ url: () => "https://example.com/",
68
140
  route: mock(async () => {}),
69
141
  unroute: mock(async () => {}),
70
142
  close: async () => {},
@@ -72,20 +144,89 @@ function resetMockPage() {
72
144
  };
73
145
  }
74
146
 
147
+ /**
148
+ * Default CDP handler. Returns values in the CDP response shape
149
+ * (`{ result: { value } }`) for `Runtime.evaluate` calls and resolves
150
+ * with `{}` for other methods.
151
+ *
152
+ * navigateAndWait now reads the pre-nav URL, then polls readyState +
153
+ * href in a single combined evaluate. The default flow:
154
+ *
155
+ * 1. Pre-nav `document.location.href` → "about:blank" (the baseline
156
+ * used by navigateAndWait's commit detection).
157
+ * 2. `Page.navigate`
158
+ * 3. Combined poll `({ readyState, href })` → `{ readyState:
159
+ * "complete", href: "https://example.com/page" }`. Because the
160
+ * href changed from the pre-nav value, commit detection fires
161
+ * on the first poll.
162
+ * 4. `document.title` → "Example".
163
+ */
164
+ function defaultCdpHandler(
165
+ method: string,
166
+ params?: Record<string, unknown>,
167
+ ): unknown {
168
+ if (method === "Page.navigate") return { frameId: "f1" };
169
+ if (method === "Runtime.evaluate") {
170
+ const expression = String(params?.["expression"] ?? "");
171
+ if (expression === "document.location.href") {
172
+ return { result: { value: "about:blank" } };
173
+ }
174
+ if (expression === "document.title") {
175
+ return { result: { value: "Example" } };
176
+ }
177
+ // Combined readyState + href polling expression from
178
+ // navigateAndWait. The commit-detection logic requires a
179
+ // different href from the pre-nav baseline so we return the
180
+ // requested page URL here.
181
+ if (
182
+ expression.includes("readyState") &&
183
+ expression.includes("document.location.href")
184
+ ) {
185
+ return {
186
+ result: {
187
+ value: {
188
+ readyState: "complete",
189
+ href: "https://example.com/page",
190
+ },
191
+ },
192
+ };
193
+ }
194
+ // DOM_DETECT / CAPTCHA_DETECT / DISMISS_MODALS IIFEs fall through
195
+ // to a generic "no challenge" result. The auth-detector IIFE
196
+ // expects `{result: {value: null | {...}}}` shape.
197
+ return { result: { value: null } };
198
+ }
199
+ return {};
200
+ }
201
+
202
+ function resetCdp() {
203
+ cdpSendCalls = [];
204
+ cdpDisposed = false;
205
+ cdpSendHandler = defaultCdpHandler;
206
+ }
207
+
75
208
  describe("executeBrowserNavigate", () => {
76
209
  beforeEach(() => {
77
210
  parseUrlResult = null;
211
+ parseUrlMock = () => parseUrlResult;
78
212
  isPrivateResult = false;
213
+ isPrivateHostMock = () => isPrivateResult;
79
214
  resolveResult = {};
80
215
  resetMockPage();
216
+ resetCdp();
81
217
  });
82
218
 
83
219
  // ── Input validation ───────────────────────────────────────────
220
+ //
221
+ // These run entirely within the upfront validation block and do
222
+ // not touch CDP. The tests intentionally do not assert anything
223
+ // about the CdpClient — the factory should never be called.
84
224
 
85
225
  test("rejects missing or invalid url", async () => {
86
226
  const result = await executeBrowserNavigate({}, ctx);
87
227
  expect(result.isError).toBe(true);
88
228
  expect(result.content).toContain("url is required");
229
+ expect(cdpSendCalls).toEqual([]);
89
230
  });
90
231
 
91
232
  test("rejects non-http(s) protocols", async () => {
@@ -96,6 +237,7 @@ describe("executeBrowserNavigate", () => {
96
237
  );
97
238
  expect(result.isError).toBe(true);
98
239
  expect(result.content).toContain("http or https");
240
+ expect(cdpSendCalls).toEqual([]);
99
241
  });
100
242
 
101
243
  // ── Private network blocking ───────────────────────────────────
@@ -110,6 +252,7 @@ describe("executeBrowserNavigate", () => {
110
252
  expect(result.isError).toBe(true);
111
253
  expect(result.content).toContain("Refusing to navigate");
112
254
  expect(result.content).toContain("localhost");
255
+ expect(cdpSendCalls).toEqual([]);
113
256
  });
114
257
 
115
258
  test("allows private hosts with allow_private_network=true", async () => {
@@ -120,7 +263,7 @@ describe("executeBrowserNavigate", () => {
120
263
  ctx,
121
264
  );
122
265
  expect(result.isError).toBe(false);
123
- expect(result.content).toContain("Status: 200");
266
+ expect(result.content).toContain("Status: unknown");
124
267
  });
125
268
 
126
269
  test("blocks DNS-resolved private addresses by default", async () => {
@@ -133,6 +276,7 @@ describe("executeBrowserNavigate", () => {
133
276
  );
134
277
  expect(result.isError).toBe(true);
135
278
  expect(result.content).toContain("10.0.0.1");
279
+ expect(cdpSendCalls).toEqual([]);
136
280
  });
137
281
 
138
282
  test("skips DNS check with allow_private_network=true", async () => {
@@ -144,12 +288,12 @@ describe("executeBrowserNavigate", () => {
144
288
  ctx,
145
289
  );
146
290
  expect(result.isError).toBe(false);
147
- expect(result.content).toContain("Status: 200");
291
+ expect(result.content).toContain("Status: unknown");
148
292
  });
149
293
 
150
- // ── Successful navigation ──────────────────────────────────────
294
+ // ── Happy path (CDP navigate) ──────────────────────────────────
151
295
 
152
- test("returns structured result on success", async () => {
296
+ test("calls Page.navigate with the requested URL and returns URL+title", async () => {
153
297
  parseUrlResult = new URL("https://example.com/page");
154
298
  const result = await executeBrowserNavigate(
155
299
  { url: "https://example.com/page" },
@@ -158,13 +302,64 @@ describe("executeBrowserNavigate", () => {
158
302
  expect(result.isError).toBe(false);
159
303
  expect(result.content).toContain("Requested URL:");
160
304
  expect(result.content).toContain("Final URL:");
161
- expect(result.content).toContain("Status: 200");
305
+ expect(result.content).toContain("Status: unknown");
162
306
  expect(result.content).toContain("Title: Example");
307
+
308
+ // Page.navigate was called with the expected URL
309
+ const navigateCall = cdpSendCalls.find((c) => c.method === "Page.navigate");
310
+ expect(navigateCall).toBeDefined();
311
+ expect(navigateCall!.params).toEqual({ url: "https://example.com/page" });
312
+
313
+ // navigateAndWait polls readyState+href in a single combined
314
+ // evaluate and also reads `document.location.href` pre-nav; the
315
+ // caller separately reads `document.title` after the nav.
316
+ const evaluateCalls = cdpSendCalls.filter(
317
+ (c) => c.method === "Runtime.evaluate",
318
+ );
319
+ const expressions = evaluateCalls.map(
320
+ (c) => (c.params as Record<string, unknown>)["expression"] as string,
321
+ );
322
+ expect(expressions.some((e) => e.includes("readyState"))).toBe(true);
323
+ expect(expressions).toContain("document.location.href");
324
+ expect(expressions).toContain("document.title");
325
+
326
+ // The CdpClient was disposed in the finally block.
327
+ expect(cdpDisposed).toBe(true);
163
328
  });
164
329
 
165
330
  test("notes redirect when final URL differs", async () => {
166
331
  parseUrlResult = new URL("https://example.com/old");
167
- mockPage.url = mock(() => "https://example.com/new");
332
+ // Pre-nav URL is about:blank so commit detection fires on the
333
+ // first poll. The combined poll returns a different href than
334
+ // the requested URL — that's what triggers the "redirected" note.
335
+ cdpSendHandler = (method, params) => {
336
+ if (method === "Page.navigate") return { frameId: "f1" };
337
+ if (method === "Runtime.evaluate") {
338
+ const expression = String(params?.["expression"] ?? "");
339
+ if (expression === "document.location.href") {
340
+ return { result: { value: "about:blank" } };
341
+ }
342
+ if (expression === "document.title") {
343
+ return { result: { value: "New" } };
344
+ }
345
+ if (
346
+ expression.includes("readyState") &&
347
+ expression.includes("document.location.href")
348
+ ) {
349
+ return {
350
+ result: {
351
+ value: {
352
+ readyState: "complete",
353
+ href: "https://example.com/new",
354
+ },
355
+ },
356
+ };
357
+ }
358
+ return { result: { value: null } };
359
+ }
360
+ return {};
361
+ };
362
+
168
363
  const result = await executeBrowserNavigate(
169
364
  { url: "https://example.com/old" },
170
365
  ctx,
@@ -173,24 +368,95 @@ describe("executeBrowserNavigate", () => {
173
368
  expect(result.content).toContain("redirected");
174
369
  });
175
370
 
176
- test("handles null response status", async () => {
177
- parseUrlResult = new URL("https://example.com");
178
- mockPage.goto = mock(async () => null);
371
+ // ── Timeout / readyState stays "loading" ───────────────────────
372
+
373
+ test("reports a timeout note when document.readyState never completes", async () => {
374
+ parseUrlResult = new URL("https://example.com/slow");
375
+ cdpSendHandler = (method, params) => {
376
+ if (method === "Page.navigate") return { frameId: "f1" };
377
+ if (method === "Runtime.evaluate") {
378
+ const expression = String(params?.["expression"] ?? "");
379
+ if (expression === "document.location.href") {
380
+ // Pre-nav URL read. Returning the same URL as the target
381
+ // would trigger the same-URL reload fallback; using
382
+ // about:blank keeps the cross-URL commit-detection path
383
+ // active so the polling loop actually exercises readyState.
384
+ return { result: { value: "about:blank" } };
385
+ }
386
+ if (expression === "document.title") {
387
+ return { result: { value: "Loading" } };
388
+ }
389
+ if (
390
+ expression.includes("readyState") &&
391
+ expression.includes("document.location.href")
392
+ ) {
393
+ // Stuck in "loading" — forces navigateAndWait to exhaust
394
+ // its timeout budget (or get aborted by the test's signal).
395
+ return {
396
+ result: {
397
+ value: {
398
+ readyState: "loading",
399
+ href: "https://example.com/slow",
400
+ },
401
+ },
402
+ };
403
+ }
404
+ return { result: { value: null } };
405
+ }
406
+ return {};
407
+ };
408
+
409
+ // Use a short deadline for the test — the NAVIGATE_TIMEOUT_MS
410
+ // const is 15s which is too slow for a unit test. We bound this
411
+ // by aborting after ~200ms so the helper surfaces a CdpError
412
+ // with code "aborted" rather than waiting the full 15s.
413
+ //
414
+ // The in-function `navigationTimedOut` branch is NOT the path
415
+ // exercised here (aborts throw instead of returning timedOut).
416
+ // The happy-path timeout is simulated by the other timeout
417
+ // behavior test below.
418
+ const ctrl = new AbortController();
419
+ const timer = setTimeout(() => ctrl.abort(), 200);
179
420
  const result = await executeBrowserNavigate(
180
- { url: "https://example.com" },
181
- ctx,
421
+ { url: "https://example.com/slow" },
422
+ { ...ctx, signal: ctrl.signal },
182
423
  );
183
- expect(result.isError).toBe(false);
184
- expect(result.content).toContain("Status: unknown");
424
+ clearTimeout(timer);
425
+ expect(result.isError).toBe(true);
426
+ expect(result.content).toContain("Navigation failed");
427
+ expect(cdpDisposed).toBe(true);
185
428
  });
186
429
 
187
- // ── Error handling ─────────────────────────────────────────────
430
+ // ── Pre-aborted signal ─────────────────────────────────────────
188
431
 
189
- test("catches navigation errors", async () => {
432
+ test("returns early-abort error when signal is already aborted", async () => {
433
+ parseUrlResult = new URL("https://example.com/page");
434
+ const ctrl = new AbortController();
435
+ ctrl.abort();
436
+ const result = await executeBrowserNavigate(
437
+ { url: "https://example.com/page" },
438
+ { ...ctx, signal: ctrl.signal },
439
+ );
440
+ expect(result.isError).toBe(true);
441
+ expect(result.content).toContain("operation was cancelled");
442
+ // Pre-abort short-circuits before any CDP call is made.
443
+ expect(cdpSendCalls).toEqual([]);
444
+ });
445
+
446
+ // ── Navigation errors ──────────────────────────────────────────
447
+
448
+ test("catches navigation errors from Page.navigate", async () => {
190
449
  parseUrlResult = new URL("https://example.com");
191
- mockPage.goto = mock(async () => {
192
- throw new Error("net::ERR_CONNECTION_REFUSED");
193
- });
450
+ cdpSendHandler = (method) => {
451
+ if (method === "Page.navigate") {
452
+ throw new Error("net::ERR_CONNECTION_REFUSED");
453
+ }
454
+ if (method === "Runtime.evaluate") {
455
+ return { result: { value: "https://example.com/" } };
456
+ }
457
+ return {};
458
+ };
459
+
194
460
  const result = await executeBrowserNavigate(
195
461
  { url: "https://example.com" },
196
462
  ctx,
@@ -198,13 +464,62 @@ describe("executeBrowserNavigate", () => {
198
464
  expect(result.isError).toBe(true);
199
465
  expect(result.content).toContain("Navigation failed");
200
466
  expect(result.content).toContain("ERR_CONNECTION_REFUSED");
467
+ expect(cdpDisposed).toBe(true);
201
468
  });
202
469
 
203
- test("returns security message when route handler blocks a redirect and goto throws", async () => {
470
+ test("surfaces Page.navigate errorText as a navigation failure", async () => {
471
+ // CDP signals DNS / connection errors via the response's
472
+ // `errorText` field rather than throwing. Without this, the
473
+ // navigate helper would poll readyState on the OLD page (which is
474
+ // "complete") and report success with the stale URL — leaking
475
+ // potentially sensitive content the agent never asked for.
476
+ parseUrlResult = new URL("https://nope.invalid");
477
+ cdpSendHandler = (method, params) => {
478
+ if (method === "Page.navigate") {
479
+ return { frameId: "f1", errorText: "net::ERR_NAME_NOT_RESOLVED" };
480
+ }
481
+ if (method === "Runtime.evaluate") {
482
+ const expression = String(params?.["expression"] ?? "");
483
+ if (expression === "document.location.href") {
484
+ return { result: { value: "https://example.com/old" } };
485
+ }
486
+ return { result: { value: null } };
487
+ }
488
+ return {};
489
+ };
490
+
491
+ const result = await executeBrowserNavigate(
492
+ { url: "https://nope.invalid" },
493
+ ctx,
494
+ );
495
+ expect(result.isError).toBe(true);
496
+ expect(result.content).toContain("Navigation failed");
497
+ expect(result.content).toContain("ERR_NAME_NOT_RESOLVED");
498
+ expect(cdpDisposed).toBe(true);
499
+
500
+ // Should NOT have polled readyState — navigate failed before the
501
+ // wait loop ran. navigateAndWait combines readyState + href into a
502
+ // single evaluate, so we look for any expression containing both.
503
+ const readyStateCalls = cdpSendCalls.filter((c) => {
504
+ if (c.method !== "Runtime.evaluate") return false;
505
+ const expr = (c.params as { expression?: string } | undefined)
506
+ ?.expression;
507
+ return (
508
+ typeof expr === "string" &&
509
+ expr.includes("readyState") &&
510
+ expr.includes("document.location.href")
511
+ );
512
+ });
513
+ expect(readyStateCalls).toHaveLength(0);
514
+ });
515
+
516
+ // ── SSRF route interception (local path only) ─────────────────
517
+
518
+ test("returns security message when route handler blocks a redirect", async () => {
204
519
  parseUrlResult = new URL("https://public.example.com");
205
520
  isPrivateResult = false;
206
521
 
207
- // Capture the route handler when page.route is called
522
+ // Capture the installed route handler.
208
523
  let capturedHandler:
209
524
  | ((route: unknown, request: unknown) => Promise<void>)
210
525
  | null = null;
@@ -217,21 +532,33 @@ describe("executeBrowserNavigate", () => {
217
532
  },
218
533
  );
219
534
 
220
- // Make goto invoke the captured handler with a private redirect target, then throw
221
- mockPage.goto = mock(async () => {
222
- if (capturedHandler) {
223
- // Temporarily make isPrivateOrLocalHost return true for the redirect target
224
- isPrivateResult = true;
225
- const mockRoute = {
226
- abort: mock(async () => {}),
227
- continue: mock(async () => {}),
228
- };
229
- const mockRequest = { url: () => "http://169.254.169.254/metadata" };
230
- await capturedHandler(mockRoute, mockRequest);
231
- isPrivateResult = false;
535
+ // When Page.navigate is called, simulate a private redirect by
536
+ // invoking the captured route handler, then throw to mirror how
537
+ // the Playwright route interceptor signals blockage to the caller.
538
+ cdpSendHandler = (method) => {
539
+ if (method === "Page.navigate") {
540
+ if (capturedHandler) {
541
+ const origPrivate = isPrivateResult;
542
+ isPrivateResult = true;
543
+ const mockRoute = {
544
+ abort: mock(async () => {}),
545
+ continue: mock(async () => {}),
546
+ };
547
+ const mockRequest = { url: () => "http://169.254.169.254/metadata" };
548
+ // Invoke the captured handler. Intentionally fire-and-forget
549
+ // because Page.navigate is synchronous from the test's
550
+ // perspective — the handler only mutates `blockedUrl` in the
551
+ // closed-over scope.
552
+ void capturedHandler(mockRoute, mockRequest);
553
+ isPrivateResult = origPrivate;
554
+ }
555
+ throw new Error("net::ERR_BLOCKED_BY_CLIENT");
232
556
  }
233
- throw new Error("net::ERR_BLOCKED_BY_CLIENT");
234
- });
557
+ if (method === "Runtime.evaluate") {
558
+ return { result: { value: "https://public.example.com/" } };
559
+ }
560
+ return {};
561
+ };
235
562
 
236
563
  const result = await executeBrowserNavigate(
237
564
  { url: "https://public.example.com" },
@@ -240,7 +567,159 @@ describe("executeBrowserNavigate", () => {
240
567
  expect(result.isError).toBe(true);
241
568
  expect(result.content).toContain("Navigation blocked");
242
569
  expect(result.content).toContain("allow_private_network=true");
243
- // Should NOT contain the raw Playwright error
570
+ // Should NOT contain the raw underlying error
244
571
  expect(result.content).not.toContain("ERR_BLOCKED_BY_CLIENT");
572
+ expect(cdpDisposed).toBe(true);
573
+ });
574
+
575
+ // ── Extension path (no Playwright route interception) ──────────
576
+
577
+ test("extension path skips Playwright route interception", async () => {
578
+ parseUrlResult = new URL("https://example.com/page");
579
+ // Supplying a non-null hostBrowserProxy on the context routes the
580
+ // mocked getCdpClient to the extension path (it mirrors the real
581
+ // factory's routing logic).
582
+ const extensionCtx: ToolContext = {
583
+ ...ctx,
584
+ hostBrowserProxy: {} as unknown as ToolContext["hostBrowserProxy"],
585
+ };
586
+ // Reset page call trackers to verify they are not touched.
587
+ const routeCallsBefore = mockPage.route.mock.calls.length;
588
+ const unrouteCallsBefore = mockPage.unroute.mock.calls.length;
589
+
590
+ const result = await executeBrowserNavigate(
591
+ { url: "https://example.com/page" },
592
+ extensionCtx,
593
+ );
594
+
595
+ expect(result.isError).toBe(false);
596
+ // Extension path never installs or removes a Playwright route
597
+ // (route interception only works on the local Playwright path).
598
+ expect(mockPage.route.mock.calls.length).toBe(routeCallsBefore);
599
+ expect(mockPage.unroute.mock.calls.length).toBe(unrouteCallsBefore);
600
+ // Page.navigate still goes through the CdpClient.
601
+ expect(cdpSendCalls.some((c) => c.method === "Page.navigate")).toBe(true);
602
+ expect(cdpDisposed).toBe(true);
603
+ });
604
+
605
+ test("extension path blocks redirects via post-navigation final URL check", async () => {
606
+ // The initial URL is public and passes pre-flight checks. The
607
+ // extension path has no Playwright route interception, but the
608
+ // post-navigation defense-in-depth check catches the private final URL.
609
+ parseUrlResult = new URL("https://public.example.com/start");
610
+ isPrivateResult = false;
611
+
612
+ // Configure mocks to return different results for the initial URL
613
+ // vs. the final URL returned by navigateAndWait.
614
+ parseUrlMock = (input: unknown) => {
615
+ if (typeof input === "string" && input.includes("127.0.0.1")) {
616
+ return new URL(input);
617
+ }
618
+ return parseUrlResult;
619
+ };
620
+ isPrivateHostMock = (hostname: string) => {
621
+ return hostname === "127.0.0.1";
622
+ };
623
+
624
+ // navigateAndWait returns a private final URL (simulating a
625
+ // server-side redirect).
626
+ cdpSendHandler = (method, params) => {
627
+ if (method === "Page.navigate") return { frameId: "f1" };
628
+ if (method === "Runtime.evaluate") {
629
+ const expression = String(params?.["expression"] ?? "");
630
+ if (expression === "document.location.href") {
631
+ return { result: { value: "about:blank" } };
632
+ }
633
+ if (
634
+ expression.includes("readyState") &&
635
+ expression.includes("document.location.href")
636
+ ) {
637
+ return {
638
+ result: {
639
+ value: {
640
+ readyState: "complete",
641
+ href: "http://127.0.0.1/admin",
642
+ },
643
+ },
644
+ };
645
+ }
646
+ return { result: { value: null } };
647
+ }
648
+ return {};
649
+ };
650
+
651
+ const extensionCtx: ToolContext = {
652
+ ...ctx,
653
+ hostBrowserProxy: {} as unknown as ToolContext["hostBrowserProxy"],
654
+ };
655
+ const result = await executeBrowserNavigate(
656
+ { url: "https://public.example.com/start" },
657
+ extensionCtx,
658
+ );
659
+ expect(result.isError).toBe(true);
660
+ expect(result.content).toContain("Navigation blocked");
661
+ expect(result.content).toContain("Final URL resolved to a local/private");
662
+ expect(result.content).toContain("allow_private_network=true");
663
+ expect(cdpDisposed).toBe(true);
664
+ });
665
+
666
+ // ── Defense-in-depth: post-navigation final URL check ─────────
667
+
668
+ test("post-nav check blocks when final URL resolves to private target", async () => {
669
+ // The initial URL is public and passes pre-flight checks, but the
670
+ // final URL (after redirect) resolves to a private address. The
671
+ // route handler is NOT triggered (navigation succeeds), so only the
672
+ // post-navigation defense-in-depth check catches it.
673
+ parseUrlResult = new URL("https://public.example.com/redirect");
674
+ isPrivateResult = false;
675
+
676
+ // Configure parseUrlMock to return different results for the initial
677
+ // URL vs. the final URL returned by navigateAndWait.
678
+ parseUrlMock = (input: unknown) => {
679
+ if (typeof input === "string" && input.includes("192.168")) {
680
+ return new URL(input);
681
+ }
682
+ return parseUrlResult;
683
+ };
684
+ isPrivateHostMock = (hostname: string) => {
685
+ return hostname === "192.168.1.1";
686
+ };
687
+
688
+ // navigateAndWait returns a private final URL (simulating a
689
+ // server-side redirect that the route handler didn't catch).
690
+ cdpSendHandler = (method, params) => {
691
+ if (method === "Page.navigate") return { frameId: "f1" };
692
+ if (method === "Runtime.evaluate") {
693
+ const expression = String(params?.["expression"] ?? "");
694
+ if (expression === "document.location.href") {
695
+ return { result: { value: "about:blank" } };
696
+ }
697
+ if (
698
+ expression.includes("readyState") &&
699
+ expression.includes("document.location.href")
700
+ ) {
701
+ return {
702
+ result: {
703
+ value: {
704
+ readyState: "complete",
705
+ href: "http://192.168.1.1/admin",
706
+ },
707
+ },
708
+ };
709
+ }
710
+ return { result: { value: null } };
711
+ }
712
+ return {};
713
+ };
714
+
715
+ const result = await executeBrowserNavigate(
716
+ { url: "https://public.example.com/redirect" },
717
+ ctx,
718
+ );
719
+ expect(result.isError).toBe(true);
720
+ expect(result.content).toContain("Navigation blocked");
721
+ expect(result.content).toContain("Final URL resolved to a local/private");
722
+ expect(result.content).toContain("allow_private_network=true");
723
+ expect(cdpDisposed).toBe(true);
245
724
  });
246
725
  });