@vellumai/assistant 0.6.2 → 0.6.3

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 (396) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docs/architecture/memory.md +1 -1
  4. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  5. package/openapi.yaml +184 -69
  6. package/package.json +41 -41
  7. package/scripts/generate-openapi.ts +1 -2
  8. package/src/__tests__/acp-session.test.ts +43 -0
  9. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  10. package/src/__tests__/app-executors.test.ts +1 -0
  11. package/src/__tests__/app-source-watcher.test.ts +37 -11
  12. package/src/__tests__/approval-routes-http.test.ts +178 -1
  13. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  14. package/src/__tests__/browser-manager.test.ts +40 -27
  15. package/src/__tests__/catalog-files.test.ts +862 -0
  16. package/src/__tests__/channel-approvals.test.ts +53 -0
  17. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  18. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  19. package/src/__tests__/config-schema.test.ts +125 -48
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  21. package/src/__tests__/context-overflow-approval.test.ts +16 -1
  22. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  24. package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
  25. package/src/__tests__/conversation-attachments.test.ts +80 -4
  26. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  27. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  28. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  29. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  30. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  31. package/src/__tests__/conversation-queue.test.ts +45 -2
  32. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  33. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  34. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  35. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  36. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  37. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  38. package/src/__tests__/conversation-store.test.ts +195 -0
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  40. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
  41. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  42. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  43. package/src/__tests__/credential-vault.test.ts +152 -13
  44. package/src/__tests__/credentials-cli.test.ts +2 -2
  45. package/src/__tests__/date-context.test.ts +4 -4
  46. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  47. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  48. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  49. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  50. package/src/__tests__/gemini-provider.test.ts +2 -2
  51. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  52. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  53. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  54. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  55. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  56. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  57. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  58. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  59. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  60. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  61. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  62. package/src/__tests__/host-browser-routes.test.ts +198 -0
  63. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  64. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  65. package/src/__tests__/host-file-proxy.test.ts +185 -1
  66. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  67. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  68. package/src/__tests__/host-shell-tool.test.ts +1 -11
  69. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  70. package/src/__tests__/integration-status.test.ts +6 -7
  71. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  72. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  73. package/src/__tests__/mcp-health-check.test.ts +10 -3
  74. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  75. package/src/__tests__/migration-export-http.test.ts +61 -2
  76. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  77. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  78. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  79. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  80. package/src/__tests__/oauth-cli.test.ts +707 -60
  81. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  82. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  83. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  84. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  85. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  86. package/src/__tests__/oauth-store.test.ts +1386 -182
  87. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  88. package/src/__tests__/onboarding-template-contract.test.ts +75 -57
  89. package/src/__tests__/openai-provider.test.ts +2 -2
  90. package/src/__tests__/outlook-categories.test.ts +1 -1
  91. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  92. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  93. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  94. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  95. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  96. package/src/__tests__/outlook-trash.test.ts +1 -1
  97. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  98. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  99. package/src/__tests__/permission-mode.test.ts +28 -56
  100. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  101. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  102. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  103. package/src/__tests__/require-fresh-approval.test.ts +40 -1
  104. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  105. package/src/__tests__/schedule-routes.test.ts +162 -0
  106. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  107. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  108. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  109. package/src/__tests__/set-permission-mode.test.ts +13 -250
  110. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  111. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  112. package/src/__tests__/slack-channel-config.test.ts +12 -15
  113. package/src/__tests__/subagent-detail.test.ts +44 -2
  114. package/src/__tests__/subagent-disposal.test.ts +1 -0
  115. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  116. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  117. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  118. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  119. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  120. package/src/__tests__/subagent-tools.test.ts +1 -0
  121. package/src/__tests__/subagent-types.test.ts +1 -0
  122. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  123. package/src/__tests__/system-prompt.test.ts +72 -1
  124. package/src/__tests__/task-scheduler.test.ts +32 -6
  125. package/src/__tests__/telegram-config.test.ts +10 -13
  126. package/src/__tests__/terminal-tools.test.ts +9 -0
  127. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  128. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  129. package/src/__tests__/top-level-renderer.test.ts +73 -1
  130. package/src/__tests__/transport-hints-queue.test.ts +14 -29
  131. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  132. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  133. package/src/acp/client-handler.ts +30 -4
  134. package/src/agent/loop.ts +12 -6
  135. package/src/approvals/guardian-request-resolvers.ts +21 -15
  136. package/src/browser-session/__tests__/manager.test.ts +297 -0
  137. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  138. package/src/browser-session/backends/extension.ts +26 -0
  139. package/src/browser-session/backends/local.ts +24 -0
  140. package/src/browser-session/events.ts +164 -0
  141. package/src/browser-session/index.ts +27 -0
  142. package/src/browser-session/manager.ts +159 -0
  143. package/src/browser-session/types.ts +28 -0
  144. package/src/channels/__tests__/types.test.ts +134 -0
  145. package/src/channels/types.ts +53 -3
  146. package/src/cli/commands/browser-relay.ts +339 -409
  147. package/src/cli/commands/credentials.ts +3 -3
  148. package/src/cli/commands/email.ts +18 -13
  149. package/src/cli/commands/mcp.ts +16 -4
  150. package/src/cli/commands/oauth/__tests__/connect.test.ts +44 -44
  151. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  152. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  153. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  154. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  155. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  156. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  157. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  158. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  159. package/src/cli/commands/oauth/apps.ts +7 -4
  160. package/src/cli/commands/oauth/connect.ts +6 -3
  161. package/src/cli/commands/oauth/disconnect.ts +1 -1
  162. package/src/cli/commands/oauth/providers.ts +200 -36
  163. package/src/cli/commands/oauth/shared.ts +5 -5
  164. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  165. package/src/cli/commands/platform/index.ts +107 -10
  166. package/src/cli/commands/usage.ts +10 -9
  167. package/src/cli/lib/daemon-credential-client.ts +4 -0
  168. package/src/cli/program.ts +1 -1
  169. package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
  170. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  171. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  172. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  173. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  174. package/src/config/bundled-skills/document/SKILL.md +4 -0
  175. package/src/config/bundled-skills/gmail/SKILL.md +1 -1
  176. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  177. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  178. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  179. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  180. package/src/config/env-registry.ts +14 -0
  181. package/src/config/env.ts +21 -0
  182. package/src/config/feature-flag-registry.json +44 -5
  183. package/src/config/loader.ts +56 -1
  184. package/src/config/sanitize-for-transfer.ts +47 -0
  185. package/src/config/schema.ts +46 -5
  186. package/src/config/schemas/host-browser.ts +66 -0
  187. package/src/config/schemas/memory-lifecycle.ts +1 -1
  188. package/src/config/schemas/memory-retrieval.ts +103 -0
  189. package/src/config/schemas/security.ts +0 -6
  190. package/src/config/schemas/services.ts +8 -0
  191. package/src/config/types.ts +0 -1
  192. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  193. package/src/context/window-manager.ts +19 -1
  194. package/src/credential-execution/approval-bridge.ts +49 -15
  195. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  196. package/src/daemon/app-source-watcher.ts +35 -0
  197. package/src/daemon/context-overflow-approval.ts +5 -0
  198. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  199. package/src/daemon/conversation-agent-loop.ts +58 -24
  200. package/src/daemon/conversation-attachments.ts +40 -0
  201. package/src/daemon/conversation-process.ts +48 -1
  202. package/src/daemon/conversation-runtime-assembly.ts +118 -36
  203. package/src/daemon/conversation-surfaces.ts +37 -36
  204. package/src/daemon/conversation-tool-setup.ts +74 -8
  205. package/src/daemon/conversation-workspace.ts +12 -0
  206. package/src/daemon/conversation.ts +226 -8
  207. package/src/daemon/date-context.ts +10 -10
  208. package/src/daemon/first-greeting.ts +3 -2
  209. package/src/daemon/handlers/conversations.ts +9 -140
  210. package/src/daemon/handlers/shared.ts +58 -0
  211. package/src/daemon/handlers/skills.ts +232 -37
  212. package/src/daemon/host-bash-proxy.ts +48 -13
  213. package/src/daemon/host-browser-proxy.ts +191 -0
  214. package/src/daemon/host-cu-proxy.ts +36 -11
  215. package/src/daemon/host-file-proxy.ts +57 -9
  216. package/src/daemon/lifecycle.ts +65 -11
  217. package/src/daemon/message-protocol.ts +7 -0
  218. package/src/daemon/message-types/conversations.ts +55 -13
  219. package/src/daemon/message-types/host-browser.ts +100 -0
  220. package/src/daemon/message-types/messages.ts +5 -5
  221. package/src/daemon/message-types/skills.ts +10 -0
  222. package/src/daemon/message-types/subagents.ts +2 -0
  223. package/src/daemon/server.ts +92 -12
  224. package/src/daemon/tool-side-effects.ts +6 -0
  225. package/src/daemon/transport-hints.ts +5 -24
  226. package/src/inbound/platform-callback-registration.ts +18 -17
  227. package/src/mcp/client.ts +59 -24
  228. package/src/memory/app-store.ts +31 -1
  229. package/src/memory/conversation-crud.ts +23 -0
  230. package/src/memory/conversation-starters-cadence.ts +76 -0
  231. package/src/memory/conversation-title-service.ts +5 -2
  232. package/src/memory/db-init.ts +12 -0
  233. package/src/memory/embedding-backend.test.ts +75 -0
  234. package/src/memory/embedding-backend.ts +131 -5
  235. package/src/memory/embedding-gemini.test.ts +54 -0
  236. package/src/memory/embedding-gemini.ts +20 -9
  237. package/src/memory/embedding-local.ts +176 -17
  238. package/src/memory/graph/consolidation.ts +10 -23
  239. package/src/memory/graph/extraction-job.ts +15 -0
  240. package/src/memory/graph/retriever.ts +40 -22
  241. package/src/memory/graph/store.test.ts +7 -3
  242. package/src/memory/graph/store.ts +47 -12
  243. package/src/memory/llm-usage-store.ts +45 -4
  244. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  245. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  246. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  247. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  248. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  249. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  250. package/src/memory/migrations/index.ts +6 -0
  251. package/src/memory/migrations/registry.ts +8 -0
  252. package/src/memory/schema/conversations.ts +1 -0
  253. package/src/memory/schema/oauth.ts +18 -13
  254. package/src/oauth/AGENTS.md +76 -0
  255. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  256. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  257. package/src/oauth/byo-connection.test.ts +8 -8
  258. package/src/oauth/byo-connection.ts +7 -7
  259. package/src/oauth/connect-orchestrator.ts +23 -21
  260. package/src/oauth/connect-types.ts +3 -3
  261. package/src/oauth/connection-resolver.test.ts +17 -4
  262. package/src/oauth/connection-resolver.ts +16 -16
  263. package/src/oauth/connection.ts +1 -1
  264. package/src/oauth/manual-token-connection.ts +13 -13
  265. package/src/oauth/oauth-store.ts +214 -100
  266. package/src/oauth/platform-connection.test.ts +3 -3
  267. package/src/oauth/platform-connection.ts +4 -4
  268. package/src/oauth/provider-serializer.ts +31 -5
  269. package/src/oauth/revoke.ts +76 -0
  270. package/src/oauth/seed-providers.ts +126 -87
  271. package/src/oauth/token-persistence.ts +1 -1
  272. package/src/permissions/permission-mode.ts +4 -11
  273. package/src/permissions/prompter.ts +13 -1
  274. package/src/permissions/v2-consent-policy.ts +87 -0
  275. package/src/prompts/system-prompt.ts +18 -21
  276. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  277. package/src/prompts/templates/BOOTSTRAP.md +59 -105
  278. package/src/providers/anthropic/client.ts +1 -0
  279. package/src/providers/types.ts +1 -1
  280. package/src/runtime/AGENTS.md +23 -0
  281. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  282. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  283. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  284. package/src/runtime/assistant-event-hub.ts +2 -2
  285. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  286. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  287. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  288. package/src/runtime/auth/middleware.ts +98 -0
  289. package/src/runtime/auth/route-policy.ts +6 -7
  290. package/src/runtime/capability-tokens.ts +414 -0
  291. package/src/runtime/channel-approvals.ts +18 -5
  292. package/src/runtime/chrome-extension-registry.ts +332 -0
  293. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  294. package/src/runtime/guardian-decision-types.ts +7 -0
  295. package/src/runtime/http-server.ts +425 -70
  296. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  297. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  298. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  299. package/src/runtime/migrations/migration-transport.ts +6 -0
  300. package/src/runtime/migrations/migration-wizard.ts +22 -2
  301. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  302. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  303. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  304. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  305. package/src/runtime/pending-interactions.ts +29 -13
  306. package/src/runtime/routes/approval-routes.ts +90 -16
  307. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  308. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  309. package/src/runtime/routes/conversation-analysis-routes.ts +2 -1
  310. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  311. package/src/runtime/routes/conversation-routes.ts +301 -27
  312. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  313. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  314. package/src/runtime/routes/host-browser-routes.ts +279 -0
  315. package/src/runtime/routes/host-file-routes.ts +9 -1
  316. package/src/runtime/routes/identity-routes.ts +259 -16
  317. package/src/runtime/routes/log-export-routes.ts +42 -22
  318. package/src/runtime/routes/memory-item-routes.ts +1 -7
  319. package/src/runtime/routes/migration-routes.ts +87 -2
  320. package/src/runtime/routes/oauth-apps.ts +15 -17
  321. package/src/runtime/routes/oauth-providers.ts +4 -0
  322. package/src/runtime/routes/schedule-routes.ts +24 -11
  323. package/src/runtime/routes/settings-routes.ts +9 -97
  324. package/src/runtime/routes/skills-routes.ts +52 -2
  325. package/src/runtime/routes/subagents-routes.ts +14 -10
  326. package/src/runtime/routes/usage-routes.ts +8 -7
  327. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  328. package/src/runtime/routes/workspace-routes.ts +8 -1
  329. package/src/runtime/routes/workspace-utils.ts +2 -0
  330. package/src/schedule/scheduler.ts +7 -5
  331. package/src/security/ces-credential-client.ts +20 -0
  332. package/src/security/ces-rpc-credential-backend.ts +17 -0
  333. package/src/security/credential-backend.ts +5 -0
  334. package/src/security/oauth2.ts +42 -25
  335. package/src/security/secure-keys.ts +118 -25
  336. package/src/security/token-manager.ts +23 -10
  337. package/src/skills/catalog-files.ts +492 -0
  338. package/src/subagent/manager.ts +131 -26
  339. package/src/subagent/types.ts +19 -0
  340. package/src/tools/apps/executors.ts +11 -2
  341. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  342. package/src/tools/browser/auth-detector.ts +43 -12
  343. package/src/tools/browser/browser-execution.ts +645 -340
  344. package/src/tools/browser/browser-manager.ts +36 -12
  345. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  346. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  347. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  348. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  349. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  350. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  351. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  352. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  353. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  354. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  355. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  356. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  357. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  358. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  359. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  360. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  361. package/src/tools/browser/cdp-client/errors.ts +34 -0
  362. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  363. package/src/tools/browser/cdp-client/factory.ts +204 -0
  364. package/src/tools/browser/cdp-client/index.ts +14 -0
  365. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  366. package/src/tools/browser/cdp-client/types.ts +52 -0
  367. package/src/tools/filesystem/edit.ts +1 -1
  368. package/src/tools/filesystem/list.ts +1 -1
  369. package/src/tools/filesystem/read.ts +1 -1
  370. package/src/tools/filesystem/write.ts +2 -1
  371. package/src/tools/host-filesystem/edit.ts +1 -1
  372. package/src/tools/host-filesystem/read.ts +12 -15
  373. package/src/tools/host-filesystem/write.ts +1 -1
  374. package/src/tools/host-terminal/host-shell.ts +21 -16
  375. package/src/tools/permission-checker.ts +77 -82
  376. package/src/tools/registry.ts +0 -2
  377. package/src/tools/secret-detection-handler.ts +34 -0
  378. package/src/tools/shared/filesystem/image-read.ts +61 -40
  379. package/src/tools/subagent/spawn.ts +47 -3
  380. package/src/tools/subagent/status.ts +2 -0
  381. package/src/tools/system/register.ts +2 -16
  382. package/src/tools/terminal/safe-env.ts +7 -0
  383. package/src/tools/terminal/shell.ts +21 -16
  384. package/src/tools/tool-approval-handler.ts +48 -2
  385. package/src/tools/types.ts +2 -0
  386. package/src/util/platform.ts +14 -19
  387. package/src/workspace/top-level-renderer.ts +19 -1
  388. package/src/__tests__/chrome-cdp.test.ts +0 -419
  389. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  390. package/src/__tests__/permission-mode-store.test.ts +0 -277
  391. package/src/browser-extension-relay/protocol.ts +0 -63
  392. package/src/browser-extension-relay/server.ts +0 -203
  393. package/src/config/schemas/sandbox.ts +0 -14
  394. package/src/permissions/permission-mode-store.ts +0 -180
  395. package/src/tools/browser/chrome-cdp.ts +0 -239
  396. package/src/tools/system/set-permission-mode.ts +0 -103
@@ -9,6 +9,7 @@ mock.module("../util/logger.js", () => ({
9
9
  }),
10
10
  }));
11
11
 
12
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
12
13
  import type { Conversation } from "../daemon/conversation.js";
13
14
  import * as trustStore from "../permissions/trust-store.js";
14
15
  import type {
@@ -81,6 +82,7 @@ function registerPendingConfirmation(
81
82
 
82
83
  describe("getChannelApprovalPrompt", () => {
83
84
  beforeEach(() => {
85
+ _setOverridesForTesting({});
84
86
  pendingInteractions.clear();
85
87
  });
86
88
 
@@ -169,6 +171,20 @@ describe("getChannelApprovalPrompt", () => {
169
171
  const result = getChannelApprovalPrompt("conv-2");
170
172
  expect(result).toBeNull();
171
173
  });
174
+
175
+ test("returns approve_once + reject only under v2", () => {
176
+ _setOverridesForTesting({ "permission-controls-v2": true });
177
+ registerPendingConfirmation("req-v2", "conv-1", "shell");
178
+
179
+ const result = getChannelApprovalPrompt("conv-1");
180
+ expect(result).not.toBeNull();
181
+ expect(result!.actions.map((a) => a.id)).toEqual([
182
+ "approve_once",
183
+ "reject",
184
+ ]);
185
+ expect(result!.plainTextFallback).not.toContain("10 minutes");
186
+ expect(result!.plainTextFallback).not.toContain("always");
187
+ });
172
188
  });
173
189
 
174
190
  // ═══════════════════════════════════════════════════════════════════════════
@@ -241,6 +257,7 @@ describe("buildApprovalUIMetadata", () => {
241
257
 
242
258
  describe("handleChannelDecision", () => {
243
259
  beforeEach(() => {
260
+ _setOverridesForTesting({});
244
261
  pendingInteractions.clear();
245
262
  });
246
263
 
@@ -414,6 +431,24 @@ describe("handleChannelDecision", () => {
414
431
 
415
432
  addRuleSpy.mockRestore();
416
433
  });
434
+
435
+ test("v2 collapses legacy approval actions to one-time allow without persisting rules", () => {
436
+ _setOverridesForTesting({ "permission-controls-v2": true });
437
+ registerPendingConfirmation("req-v2", "conv-1", "shell");
438
+
439
+ const addRuleSpy = spyOn(trustStore, "addRule");
440
+ const result = handleChannelDecision("conv-1", {
441
+ action: "approve_always",
442
+ requestId: "req-v2",
443
+ source: "plain_text",
444
+ });
445
+
446
+ expect(result).toEqual({
447
+ applied: true,
448
+ requestId: "req-v2",
449
+ });
450
+ expect(addRuleSpy).not.toHaveBeenCalled();
451
+ });
417
452
  });
418
453
 
419
454
  // ═══════════════════════════════════════════════════════════════════════════
@@ -421,6 +456,10 @@ describe("handleChannelDecision", () => {
421
456
  // ═══════════════════════════════════════════════════════════════════════════
422
457
 
423
458
  describe("buildGuardianApprovalPrompt", () => {
459
+ beforeEach(() => {
460
+ _setOverridesForTesting({});
461
+ });
462
+
424
463
  test("prompt includes requester identifier and tool name", () => {
425
464
  const approvalInfo: PendingApprovalInfo = {
426
465
  requestId: "req-g1",
@@ -457,6 +496,20 @@ describe("buildGuardianApprovalPrompt", () => {
457
496
  expect(prompt.plainTextFallback).toContain("yes");
458
497
  expect(prompt.plainTextFallback).toContain("no");
459
498
  });
499
+
500
+ test("uses approve_once + reject only under v2", () => {
501
+ _setOverridesForTesting({ "permission-controls-v2": true });
502
+ const approvalInfo: PendingApprovalInfo = {
503
+ requestId: "req-g4",
504
+ toolName: "shell",
505
+ input: {},
506
+ riskLevel: "medium",
507
+ persistentDecisionsAllowed: true,
508
+ };
509
+
510
+ const prompt = buildGuardianApprovalPrompt(approvalInfo, "dana");
511
+ expect(prompt.actions.map((a) => a.id)).toEqual(["approve_once", "reject"]);
512
+ });
460
513
  });
461
514
 
462
515
  // ═══════════════════════════════════════════════════════════════════════════
@@ -0,0 +1,326 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { join } from "node:path";
9
+ import {
10
+ afterAll,
11
+ afterEach,
12
+ beforeEach,
13
+ describe,
14
+ expect,
15
+ mock,
16
+ test,
17
+ } from "bun:test";
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Mocks — declared before imports that depend on platform/logger
21
+ // ---------------------------------------------------------------------------
22
+
23
+ const WORKSPACE_DIR = process.env.VELLUM_WORKSPACE_DIR!;
24
+ const CONFIG_PATH = join(WORKSPACE_DIR, "config.json");
25
+
26
+ function ensureTestDir(): void {
27
+ const dirs = [
28
+ WORKSPACE_DIR,
29
+ join(WORKSPACE_DIR, "data"),
30
+ join(WORKSPACE_DIR, "data", "memory"),
31
+ join(WORKSPACE_DIR, "data", "memory", "knowledge"),
32
+ join(WORKSPACE_DIR, "data", "logs"),
33
+ ];
34
+ for (const dir of dirs) {
35
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
36
+ }
37
+ }
38
+
39
+ function makeLoggerStub(): Record<string, unknown> {
40
+ const stub: Record<string, unknown> = {};
41
+ for (const m of [
42
+ "info",
43
+ "warn",
44
+ "error",
45
+ "debug",
46
+ "trace",
47
+ "fatal",
48
+ "silent",
49
+ "child",
50
+ ]) {
51
+ stub[m] = m === "child" ? () => makeLoggerStub() : () => {};
52
+ }
53
+ return stub;
54
+ }
55
+
56
+ mock.module("../util/logger.js", () => ({
57
+ getLogger: () => makeLoggerStub(),
58
+ }));
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Feature flag mock — controls whether managed-gemini-embeddings-enabled is on
62
+ // ---------------------------------------------------------------------------
63
+
64
+ let featureFlagEnabled = false;
65
+
66
+ mock.module("../config/assistant-feature-flags.js", () => ({
67
+ isAssistantFeatureFlagEnabled: (key: string) => {
68
+ if (key === "managed-gemini-embeddings-enabled") return featureFlagEnabled;
69
+ return true;
70
+ },
71
+ _setOverridesForTesting: () => {},
72
+ clearFeatureFlagOverridesCache: () => {},
73
+ initFeatureFlagOverrides: async () => {},
74
+ getAssistantFeatureFlagDefaults: () => ({}),
75
+ }));
76
+
77
+ // Restore all mocked modules after this file's tests complete to prevent
78
+ // cross-test contamination when running grouped with other test files.
79
+ afterAll(() => {
80
+ mock.restore();
81
+ });
82
+
83
+ import { invalidateConfigCache, loadConfig } from "../config/loader.js";
84
+ import { _setStorePath } from "../security/encrypted-store.js";
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Helpers
88
+ // ---------------------------------------------------------------------------
89
+
90
+ function writeConfig(obj: unknown): void {
91
+ writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
92
+ }
93
+
94
+ function readConfig(): Record<string, unknown> {
95
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
96
+ }
97
+
98
+ /** Stash and restore IS_PLATFORM across each test. */
99
+ let originalIsPlatform: string | undefined;
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Tests
103
+ // ---------------------------------------------------------------------------
104
+
105
+ describe("managed Gemini embedding defaults (via loadConfig)", () => {
106
+ beforeEach(() => {
107
+ ensureTestDir();
108
+ const resetPaths = [
109
+ CONFIG_PATH,
110
+ join(WORKSPACE_DIR, "keys.enc"),
111
+ join(WORKSPACE_DIR, "data"),
112
+ join(WORKSPACE_DIR, "data", "memory"),
113
+ ];
114
+ for (const path of resetPaths) {
115
+ if (existsSync(path)) {
116
+ rmSync(path, { recursive: true, force: true });
117
+ }
118
+ }
119
+ ensureTestDir();
120
+ _setStorePath(join(WORKSPACE_DIR, "keys.enc"));
121
+ invalidateConfigCache();
122
+
123
+ // Reset mock state
124
+ featureFlagEnabled = false;
125
+ originalIsPlatform = process.env.IS_PLATFORM;
126
+ delete process.env.IS_PLATFORM;
127
+ });
128
+
129
+ afterEach(() => {
130
+ _setStorePath(null);
131
+ invalidateConfigCache();
132
+
133
+ // Restore IS_PLATFORM
134
+ if (originalIsPlatform !== undefined) {
135
+ process.env.IS_PLATFORM = originalIsPlatform;
136
+ } else {
137
+ delete process.env.IS_PLATFORM;
138
+ }
139
+ });
140
+
141
+ test("applies managed Gemini defaults when FF on + IS_PLATFORM + provider auto", () => {
142
+ writeConfig({});
143
+
144
+ featureFlagEnabled = true;
145
+ process.env.IS_PLATFORM = "true";
146
+
147
+ const config = loadConfig();
148
+
149
+ // In-memory config should have managed Gemini defaults
150
+ expect(config.memory.embeddings.provider).toBe("gemini");
151
+ expect(config.memory.embeddings.geminiModel).toBe(
152
+ "gemini-embedding-2-preview",
153
+ );
154
+ expect(config.memory.embeddings.geminiDimensions).toBe(3072);
155
+ expect(config.memory.qdrant.vectorSize).toBe(3072);
156
+
157
+ // Config file on disk should also be updated
158
+ const raw = readConfig();
159
+ const memoryRaw = raw.memory as Record<string, unknown>;
160
+ const embeddingsRaw = memoryRaw.embeddings as Record<string, unknown>;
161
+ const qdrantRaw = memoryRaw.qdrant as Record<string, unknown>;
162
+ expect(embeddingsRaw.provider).toBe("gemini");
163
+ expect(embeddingsRaw.geminiModel).toBe("gemini-embedding-2-preview");
164
+ expect(embeddingsRaw.geminiDimensions).toBe(3072);
165
+ expect(qdrantRaw.vectorSize).toBe(3072);
166
+ });
167
+
168
+ test("does NOT apply when feature flag is OFF", () => {
169
+ writeConfig({});
170
+
171
+ featureFlagEnabled = false;
172
+ process.env.IS_PLATFORM = "true";
173
+
174
+ const config = loadConfig();
175
+
176
+ expect(config.memory.embeddings.provider).toBe("auto");
177
+ expect(config.memory.qdrant.vectorSize).toBe(384);
178
+ });
179
+
180
+ test("does NOT apply when IS_PLATFORM is not set", () => {
181
+ writeConfig({});
182
+
183
+ featureFlagEnabled = true;
184
+ delete process.env.IS_PLATFORM;
185
+
186
+ const config = loadConfig();
187
+
188
+ expect(config.memory.embeddings.provider).toBe("auto");
189
+ expect(config.memory.qdrant.vectorSize).toBe(384);
190
+ });
191
+
192
+ test("does NOT apply when provider is explicitly set to local", () => {
193
+ writeConfig({
194
+ memory: { embeddings: { provider: "local" } },
195
+ });
196
+
197
+ featureFlagEnabled = true;
198
+ process.env.IS_PLATFORM = "true";
199
+
200
+ const config = loadConfig();
201
+ expect(config.memory.embeddings.provider).toBe("local");
202
+ expect(config.memory.qdrant.vectorSize).toBe(384);
203
+ });
204
+
205
+ test("does NOT apply when provider is explicitly set to openai", () => {
206
+ writeConfig({
207
+ memory: { embeddings: { provider: "openai" } },
208
+ });
209
+
210
+ featureFlagEnabled = true;
211
+ process.env.IS_PLATFORM = "true";
212
+
213
+ const config = loadConfig();
214
+ expect(config.memory.embeddings.provider).toBe("openai");
215
+ });
216
+
217
+ test("does NOT apply when provider is explicitly set to gemini", () => {
218
+ writeConfig({
219
+ memory: {
220
+ embeddings: { provider: "gemini", geminiDimensions: 768 },
221
+ qdrant: { vectorSize: 768 },
222
+ },
223
+ });
224
+
225
+ featureFlagEnabled = true;
226
+ process.env.IS_PLATFORM = "true";
227
+
228
+ const config = loadConfig();
229
+
230
+ // Already gemini — should not overwrite user's custom dimensions
231
+ expect(config.memory.embeddings.provider).toBe("gemini");
232
+ expect(config.memory.embeddings.geminiDimensions).toBe(768);
233
+ expect(config.memory.qdrant.vectorSize).toBe(768);
234
+ });
235
+
236
+ test("does NOT apply when provider is explicitly set to ollama", () => {
237
+ writeConfig({
238
+ memory: { embeddings: { provider: "ollama" } },
239
+ });
240
+
241
+ featureFlagEnabled = true;
242
+ process.env.IS_PLATFORM = "true";
243
+
244
+ const config = loadConfig();
245
+ expect(config.memory.embeddings.provider).toBe("ollama");
246
+ });
247
+
248
+ test("is idempotent — second loadConfig is a no-op after migration", () => {
249
+ writeConfig({});
250
+
251
+ featureFlagEnabled = true;
252
+ process.env.IS_PLATFORM = "true";
253
+
254
+ const config = loadConfig();
255
+ expect(config.memory.embeddings.provider).toBe("gemini");
256
+
257
+ // Read file content after first migration
258
+ const contentAfterFirst = readFileSync(CONFIG_PATH, "utf-8");
259
+
260
+ // Second call — provider is now "gemini", not "auto", so migration skipped
261
+ invalidateConfigCache();
262
+ const config2 = loadConfig();
263
+ expect(config2.memory.embeddings.provider).toBe("gemini");
264
+
265
+ // File on disk should not have changed
266
+ const contentAfterSecond = readFileSync(CONFIG_PATH, "utf-8");
267
+ expect(contentAfterSecond).toBe(contentAfterFirst);
268
+ });
269
+
270
+ test("preserves existing config values while setting managed defaults", () => {
271
+ writeConfig({
272
+ provider: "anthropic",
273
+ model: "claude-opus-4-6",
274
+ memory: {
275
+ enabled: true,
276
+ qdrant: { collection: "my-collection", onDisk: false },
277
+ },
278
+ });
279
+
280
+ featureFlagEnabled = true;
281
+ process.env.IS_PLATFORM = "true";
282
+
283
+ const config = loadConfig();
284
+
285
+ // Managed defaults applied
286
+ expect(config.memory.embeddings.provider).toBe("gemini");
287
+ expect(config.memory.embeddings.geminiModel).toBe(
288
+ "gemini-embedding-2-preview",
289
+ );
290
+ expect(config.memory.qdrant.vectorSize).toBe(3072);
291
+
292
+ // Existing values preserved
293
+ const raw = readConfig();
294
+ expect(raw.provider).toBe("anthropic");
295
+ expect(raw.model).toBe("claude-opus-4-6");
296
+ const memoryRaw = raw.memory as Record<string, unknown>;
297
+ expect(memoryRaw.enabled).toBe(true);
298
+ const qdrantRaw = memoryRaw.qdrant as Record<string, unknown>;
299
+ expect(qdrantRaw.collection).toBe("my-collection");
300
+ expect(qdrantRaw.onDisk).toBe(false);
301
+ });
302
+
303
+ test("does NOT apply when both FF off and IS_PLATFORM not set", () => {
304
+ writeConfig({});
305
+
306
+ featureFlagEnabled = false;
307
+ delete process.env.IS_PLATFORM;
308
+
309
+ const config = loadConfig();
310
+
311
+ expect(config.memory.embeddings.provider).toBe("auto");
312
+ expect(config.memory.qdrant.vectorSize).toBe(384);
313
+ });
314
+
315
+ test("applies when IS_PLATFORM is '1'", () => {
316
+ writeConfig({});
317
+
318
+ featureFlagEnabled = true;
319
+ process.env.IS_PLATFORM = "1";
320
+
321
+ const config = loadConfig();
322
+
323
+ expect(config.memory.embeddings.provider).toBe("gemini");
324
+ expect(config.memory.embeddings.geminiDimensions).toBe(3072);
325
+ });
326
+ });
@@ -87,7 +87,7 @@ describe("getSchemaAtPath", () => {
87
87
  expect(result).not.toBeNull();
88
88
  // maxTokens has a default, so it should be parseable
89
89
  const parsed = (result as z.ZodType).parse(undefined);
90
- expect(parsed).toBe(16000);
90
+ expect(parsed).toBe(64000);
91
91
  });
92
92
 
93
93
  test("navigates nested paths (memory.segmentation → object schema)", () => {
@@ -188,7 +188,7 @@ describe("z.toJSONSchema integration", () => {
188
188
  expect(properties.calls).toBeDefined();
189
189
  expect(properties.memory).toBeDefined();
190
190
  expect(properties.timeouts).toBeDefined();
191
- expect(properties.sandbox).toBeDefined();
191
+ expect(properties.permissions).toBeDefined();
192
192
  });
193
193
 
194
194
  test("full schema emits real properties for transformed fields (ingress)", () => {
@@ -81,7 +81,7 @@ describe("AssistantConfigSchema", () => {
81
81
  "inference-provider-native",
82
82
  );
83
83
  expect(result.services["web-search"].mode).toBe("your-own");
84
- expect(result.maxTokens).toBe(16000);
84
+ expect(result.maxTokens).toBe(64000);
85
85
  expect(result.thinking).toEqual({
86
86
  enabled: true,
87
87
  streamThinking: true,
@@ -107,9 +107,6 @@ describe("AssistantConfigSchema", () => {
107
107
  toolExecutionTimeoutSec: 120,
108
108
  providerStreamTimeoutSec: 1800,
109
109
  });
110
- expect(result.sandbox).toEqual({
111
- enabled: false,
112
- });
113
110
  expect(result.rateLimit).toEqual({
114
111
  maxRequestsPerMinute: 0,
115
112
  });
@@ -169,7 +166,7 @@ describe("AssistantConfigSchema", () => {
169
166
  enqueueIntervalMs: 6 * 60 * 60 * 1000,
170
167
  supersededItemRetentionMs: 30 * 24 * 60 * 60 * 1000,
171
168
  conversationRetentionDays: 0,
172
- llmRequestLogRetentionMs: 7 * 24 * 60 * 60 * 1000,
169
+ llmRequestLogRetentionMs: 1 * 24 * 60 * 60 * 1000,
173
170
  });
174
171
  });
175
172
 
@@ -400,29 +397,10 @@ describe("AssistantConfigSchema", () => {
400
397
  }
401
398
  });
402
399
 
403
- test("sandbox with only enabled still parses", () => {
404
- const result = AssistantConfigSchema.parse({ sandbox: { enabled: false } });
405
- expect(result.sandbox.enabled).toBe(false);
406
- });
407
-
408
- test("rejects unknown sandbox fields", () => {
409
- const result = AssistantConfigSchema.safeParse({
410
- sandbox: { backend: "docker" },
411
- });
412
- // Unknown keys are stripped by Zod passthrough/strip, so parse should still succeed
413
- // but the unknown field should not appear in the output
414
- if (result.success) {
415
- expect(
416
- (result.data.sandbox as Record<string, unknown>)["backend"],
417
- ).toBeUndefined();
418
- }
419
- });
420
-
421
400
  test("defaults permissions.mode to workspace", () => {
422
401
  const result = AssistantConfigSchema.parse({});
423
402
  expect(result.permissions).toEqual({
424
403
  mode: "workspace",
425
- askBeforeActing: true,
426
404
  hostAccess: false,
427
405
  });
428
406
  });
@@ -861,6 +839,125 @@ describe("AssistantConfigSchema", () => {
861
839
  });
862
840
  expect(result.calls.callerIdentity.allowPerCallOverride).toBe(true);
863
841
  });
842
+
843
+ // ── hostBrowser.cdpInspect config ─────────────────────────────────
844
+
845
+ test("applies hostBrowser.cdpInspect defaults", () => {
846
+ const result = AssistantConfigSchema.parse({});
847
+ expect(result.hostBrowser).toEqual({
848
+ cdpInspect: {
849
+ enabled: false,
850
+ host: "localhost",
851
+ port: 9222,
852
+ probeTimeoutMs: 500,
853
+ },
854
+ });
855
+ });
856
+
857
+ test("accepts hostBrowser.cdpInspect enabled with custom host/port", () => {
858
+ const result = AssistantConfigSchema.parse({
859
+ hostBrowser: {
860
+ cdpInspect: {
861
+ enabled: true,
862
+ host: "127.0.0.1",
863
+ port: 9333,
864
+ },
865
+ },
866
+ });
867
+ expect(result.hostBrowser.cdpInspect.enabled).toBe(true);
868
+ expect(result.hostBrowser.cdpInspect.host).toBe("127.0.0.1");
869
+ expect(result.hostBrowser.cdpInspect.port).toBe(9333);
870
+ // Unset field should still receive its default.
871
+ expect(result.hostBrowser.cdpInspect.probeTimeoutMs).toBe(500);
872
+ });
873
+
874
+ test("accepts hostBrowser.cdpInspect custom probeTimeoutMs", () => {
875
+ const result = AssistantConfigSchema.parse({
876
+ hostBrowser: { cdpInspect: { probeTimeoutMs: 1000 } },
877
+ });
878
+ expect(result.hostBrowser.cdpInspect.probeTimeoutMs).toBe(1000);
879
+ });
880
+
881
+ test("rejects hostBrowser.cdpInspect.port below 1", () => {
882
+ const result = AssistantConfigSchema.safeParse({
883
+ hostBrowser: { cdpInspect: { port: 0 } },
884
+ });
885
+ expect(result.success).toBe(false);
886
+ if (!result.success) {
887
+ expect(
888
+ result.error.issues.some((issue) =>
889
+ issue.path.join(".").includes("hostBrowser.cdpInspect.port"),
890
+ ),
891
+ ).toBe(true);
892
+ }
893
+ });
894
+
895
+ test("rejects hostBrowser.cdpInspect.port above 65535", () => {
896
+ const result = AssistantConfigSchema.safeParse({
897
+ hostBrowser: { cdpInspect: { port: 70000 } },
898
+ });
899
+ expect(result.success).toBe(false);
900
+ if (!result.success) {
901
+ expect(
902
+ result.error.issues.some((issue) =>
903
+ issue.path.join(".").includes("hostBrowser.cdpInspect.port"),
904
+ ),
905
+ ).toBe(true);
906
+ }
907
+ });
908
+
909
+ test("rejects non-integer hostBrowser.cdpInspect.port", () => {
910
+ const result = AssistantConfigSchema.safeParse({
911
+ hostBrowser: { cdpInspect: { port: 9222.5 } },
912
+ });
913
+ expect(result.success).toBe(false);
914
+ });
915
+
916
+ test("rejects hostBrowser.cdpInspect.probeTimeoutMs below 50", () => {
917
+ const result = AssistantConfigSchema.safeParse({
918
+ hostBrowser: { cdpInspect: { probeTimeoutMs: 10 } },
919
+ });
920
+ expect(result.success).toBe(false);
921
+ if (!result.success) {
922
+ expect(
923
+ result.error.issues.some((issue) =>
924
+ issue.path
925
+ .join(".")
926
+ .includes("hostBrowser.cdpInspect.probeTimeoutMs"),
927
+ ),
928
+ ).toBe(true);
929
+ }
930
+ });
931
+
932
+ test("rejects hostBrowser.cdpInspect.probeTimeoutMs above 5000", () => {
933
+ const result = AssistantConfigSchema.safeParse({
934
+ hostBrowser: { cdpInspect: { probeTimeoutMs: 10000 } },
935
+ });
936
+ expect(result.success).toBe(false);
937
+ if (!result.success) {
938
+ expect(
939
+ result.error.issues.some((issue) =>
940
+ issue.path
941
+ .join(".")
942
+ .includes("hostBrowser.cdpInspect.probeTimeoutMs"),
943
+ ),
944
+ ).toBe(true);
945
+ }
946
+ });
947
+
948
+ test("rejects non-integer hostBrowser.cdpInspect.probeTimeoutMs", () => {
949
+ const result = AssistantConfigSchema.safeParse({
950
+ hostBrowser: { cdpInspect: { probeTimeoutMs: 500.5 } },
951
+ });
952
+ expect(result.success).toBe(false);
953
+ });
954
+
955
+ test("rejects non-boolean hostBrowser.cdpInspect.enabled", () => {
956
+ const result = AssistantConfigSchema.safeParse({
957
+ hostBrowser: { cdpInspect: { enabled: "yes" } },
958
+ });
959
+ expect(result.success).toBe(false);
960
+ });
864
961
  });
865
962
 
866
963
  // ---------------------------------------------------------------------------
@@ -1002,7 +1099,7 @@ describe("loadConfig with schema validation", () => {
1002
1099
  const config = loadConfig();
1003
1100
  expect(config.services.inference.provider).toBe("anthropic");
1004
1101
  expect(config.services.inference.model).toBe("claude-opus-4-6");
1005
- expect(config.maxTokens).toBe(16000);
1102
+ expect(config.maxTokens).toBe(64000);
1006
1103
  expect(config.thinking).toEqual({
1007
1104
  enabled: true,
1008
1105
  streamThinking: true,
@@ -1034,7 +1131,7 @@ describe("loadConfig with schema validation", () => {
1034
1131
  test("falls back to default for invalid maxTokens", () => {
1035
1132
  writeConfig({ maxTokens: -100 });
1036
1133
  const config = loadConfig();
1037
- expect(config.maxTokens).toBe(16000);
1134
+ expect(config.maxTokens).toBe(64000);
1038
1135
  });
1039
1136
 
1040
1137
  test("falls back to defaults for invalid nested values", () => {
@@ -1059,13 +1156,13 @@ describe("loadConfig with schema validation", () => {
1059
1156
  expect(config.services.inference.provider).toBe("openai");
1060
1157
  expect(config.services.inference.model).toBe("gpt-4");
1061
1158
  expect(config.thinking.enabled).toBe(true);
1062
- expect(config.maxTokens).toBe(16000);
1159
+ expect(config.maxTokens).toBe(64000);
1063
1160
  });
1064
1161
 
1065
1162
  test("handles no config file", () => {
1066
1163
  const config = loadConfig();
1067
1164
  expect(config.services.inference.provider).toBe("anthropic");
1068
- expect(config.maxTokens).toBe(16000);
1165
+ expect(config.maxTokens).toBe(64000);
1069
1166
  });
1070
1167
 
1071
1168
  test("partial nested objects get defaults for missing fields", () => {
@@ -1084,25 +1181,6 @@ describe("loadConfig with schema validation", () => {
1084
1181
  expect(config.secretDetection.action).toBe("redact");
1085
1182
  });
1086
1183
 
1087
- test("falls back for invalid sandbox.enabled", () => {
1088
- writeConfig({ sandbox: { enabled: "yes" } });
1089
- const config = loadConfig();
1090
- expect(config.sandbox.enabled).toBe(false);
1091
- });
1092
-
1093
- test("loads sandbox with only enabled field", () => {
1094
- writeConfig({ sandbox: { enabled: false } });
1095
- const config = loadConfig();
1096
- expect(config.sandbox.enabled).toBe(false);
1097
- });
1098
-
1099
- test("strips unknown sandbox fields", () => {
1100
- writeConfig({ sandbox: { enabled: true, backend: "docker" } });
1101
- const config = loadConfig();
1102
- expect(config.sandbox.enabled).toBe(true);
1103
- expect("backend" in config.sandbox).toBe(false);
1104
- });
1105
-
1106
1184
  test("falls back for invalid contextWindow relationship", () => {
1107
1185
  writeConfig({
1108
1186
  contextWindow: { targetBudgetRatio: 0.8, compactThreshold: 0.8 },
@@ -1131,7 +1209,6 @@ describe("loadConfig with schema validation", () => {
1131
1209
  const config = loadConfig();
1132
1210
  expect(config.permissions).toEqual({
1133
1211
  mode: "workspace",
1134
- askBeforeActing: true,
1135
1212
  hostAccess: false,
1136
1213
  });
1137
1214
  });