@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
@@ -128,9 +128,17 @@ describe("scheduler run_task detection", () => {
128
128
  forceScheduleDue(schedule.id);
129
129
 
130
130
  // Track all processMessage calls
131
- const directCalls: { conversationId: string; message: string }[] = [];
132
- const processMessage = async (conversationId: string, message: string) => {
133
- directCalls.push({ conversationId, message });
131
+ const directCalls: Array<{
132
+ conversationId: string;
133
+ message: string;
134
+ options?: { trustClass?: string; taskRunId?: string };
135
+ }> = [];
136
+ const processMessage = async (
137
+ conversationId: string,
138
+ message: string,
139
+ options?: { trustClass?: string; taskRunId?: string },
140
+ ) => {
141
+ directCalls.push({ conversationId, message, options });
134
142
  };
135
143
 
136
144
  const scheduler = startScheduler(
@@ -154,6 +162,8 @@ describe("scheduler run_task detection", () => {
154
162
  expect(runTaskCalls.length).toBe(1);
155
163
  // The scheduler should NOT pass the raw run_task: message to processMessage
156
164
  expect(rawCalls.length).toBe(0);
165
+ expect(runTaskCalls[0].options?.trustClass).toBe("guardian");
166
+ expect(typeof runTaskCalls[0].options?.taskRunId).toBe("string");
157
167
  });
158
168
 
159
169
  test("regular messages still go through processMessage normally", async () => {
@@ -167,9 +177,17 @@ describe("scheduler run_task detection", () => {
167
177
 
168
178
  forceScheduleDue(schedule.id);
169
179
 
170
- const processedMessages: { conversationId: string; message: string }[] = [];
171
- const processMessage = async (conversationId: string, message: string) => {
172
- processedMessages.push({ conversationId, message });
180
+ const processedMessages: Array<{
181
+ conversationId: string;
182
+ message: string;
183
+ options?: { trustClass?: string; taskRunId?: string };
184
+ }> = [];
185
+ const processMessage = async (
186
+ conversationId: string,
187
+ message: string,
188
+ options?: { trustClass?: string; taskRunId?: string },
189
+ ) => {
190
+ processedMessages.push({ conversationId, message, options });
173
191
  };
174
192
 
175
193
  const scheduler = startScheduler(
@@ -185,6 +203,14 @@ describe("scheduler run_task detection", () => {
185
203
  expect(
186
204
  processedMessages.some((m) => m.message === "Do something normal"),
187
205
  ).toBe(true);
206
+ expect(
207
+ processedMessages.some(
208
+ (m) =>
209
+ m.message === "Do something normal" &&
210
+ m.options?.trustClass === "guardian" &&
211
+ m.options?.taskRunId === undefined,
212
+ ),
213
+ ).toBe(true);
188
214
  });
189
215
 
190
216
  test("handles task not found gracefully", async () => {
@@ -7,7 +7,7 @@ let oauthConnectionStore: Record<
7
7
  string,
8
8
  { id: string; status: string; accountInfo?: string | null }
9
9
  > = {};
10
- const syncCalls: Array<{ providerKey: string; accountInfo?: string }> = [];
10
+ const syncCalls: Array<{ provider: string; accountInfo?: string }> = [];
11
11
 
12
12
  mock.module("../config/loader.js", () => ({
13
13
  getConfig: () => ({ telegram: {}, ui: {} }),
@@ -49,32 +49,29 @@ mock.module("../security/secure-keys.js", () => ({
49
49
  }));
50
50
 
51
51
  mock.module("../oauth/oauth-store.js", () => ({
52
- getConnectionByProvider: (providerKey: string) =>
53
- oauthConnectionStore[providerKey] ?? undefined,
52
+ getConnectionByProvider: (provider: string) =>
53
+ oauthConnectionStore[provider] ?? undefined,
54
54
  }));
55
55
 
56
56
  mock.module("../oauth/manual-token-connection.js", () => ({
57
57
  ensureManualTokenConnection: async () => {},
58
58
  removeManualTokenConnection: () => {},
59
- syncManualTokenConnection: async (
60
- providerKey: string,
61
- accountInfo?: string,
62
- ) => {
63
- syncCalls.push({ providerKey, accountInfo });
64
- if (providerKey !== "telegram") return;
59
+ syncManualTokenConnection: async (provider: string, accountInfo?: string) => {
60
+ syncCalls.push({ provider, accountInfo });
61
+ if (provider !== "telegram") return;
65
62
  const hasBotToken =
66
63
  !!secureKeyStore[credentialKey("telegram", "bot_token")];
67
64
  const hasWebhookSecret =
68
65
  !!secureKeyStore[credentialKey("telegram", "webhook_secret")];
69
66
  if (hasBotToken && hasWebhookSecret) {
70
- oauthConnectionStore[providerKey] = {
71
- id: `conn-${providerKey}`,
67
+ oauthConnectionStore[provider] = {
68
+ id: `conn-${provider}`,
72
69
  status: "active",
73
70
  accountInfo: accountInfo ?? null,
74
71
  };
75
72
  return;
76
73
  }
77
- delete oauthConnectionStore[providerKey];
74
+ delete oauthConnectionStore[provider];
78
75
  },
79
76
  }));
80
77
 
@@ -114,7 +111,7 @@ describe("Telegram config handler", () => {
114
111
  expect(result.botUsername).toBe("testbot");
115
112
  expect(result.connected).toBe(true);
116
113
  expect(syncCalls).toEqual([
117
- { providerKey: "telegram", accountInfo: "@testbot" },
114
+ { provider: "telegram", accountInfo: "@testbot" },
118
115
  ]);
119
116
  expect(oauthConnectionStore["telegram"]?.accountInfo).toBe("@testbot");
120
117
  });
@@ -445,6 +445,15 @@ describe("buildSanitizedEnv", () => {
445
445
  expect(env.LC_CTYPE).toBe("UTF-8");
446
446
  });
447
447
 
448
+ test("defaults LANG and LC_ALL to UTF-8 when unset", () => {
449
+ delete process.env.LANG;
450
+ delete process.env.LC_ALL;
451
+
452
+ const env = buildSanitizedEnv();
453
+ expect(env.LANG).toBe("C.UTF-8");
454
+ expect(env.LC_ALL).toBe("C.UTF-8");
455
+ });
456
+
448
457
  test("injects INTERNAL_GATEWAY_BASE_URL from gateway config", () => {
449
458
  process.env.GATEWAY_PORT = "9000";
450
459
  const env = buildSanitizedEnv();
@@ -43,6 +43,11 @@ import {
43
43
  mintGrantFromDecision,
44
44
  type MintGrantParams,
45
45
  } from "../approvals/approval-primitive.js";
46
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
47
+ import {
48
+ createConversation,
49
+ updateConversationHostAccess,
50
+ } from "../memory/conversation-crud.js";
46
51
  import { getDb, initializeDb } from "../memory/db.js";
47
52
  import { scopedApprovalGrants } from "../memory/schema.js";
48
53
  import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
@@ -54,6 +59,8 @@ initializeDb();
54
59
  function clearTables(): void {
55
60
  const db = getDb();
56
61
  db.delete(scopedApprovalGrants).run();
62
+ db.run("DELETE FROM messages");
63
+ db.run("DELETE FROM conversations");
57
64
  }
58
65
 
59
66
  // ---------------------------------------------------------------------------
@@ -96,6 +103,7 @@ describe("ToolApprovalHandler / pre-exec gate grant check", () => {
96
103
  beforeEach(() => {
97
104
  clearTables();
98
105
  events.length = 0;
106
+ _setOverridesForTesting({});
99
107
  });
100
108
 
101
109
  test("untrusted actor + matching tool_signature grant -> allow", async () => {
@@ -508,4 +516,69 @@ describe("ToolApprovalHandler / pre-exec gate grant check", () => {
508
516
  expect(lastError.isExpected).toBe(true);
509
517
  }
510
518
  });
519
+
520
+ test("v2 trusted contact bypasses tool grants for sandboxed side-effect tools", async () => {
521
+ _setOverridesForTesting({ "permission-controls-v2": true });
522
+
523
+ const result = await handler.checkPreExecutionGates(
524
+ "bash",
525
+ { command: "echo hello" },
526
+ makeContext({ trustClass: "trusted_contact" }),
527
+ "sandbox",
528
+ "high",
529
+ Date.now(),
530
+ emitLifecycleEvent,
531
+ );
532
+
533
+ expect(result.allowed).toBe(true);
534
+ expect(events.filter((e) => e.type === "permission_denied")).toHaveLength(
535
+ 0,
536
+ );
537
+ });
538
+
539
+ test("v2 trusted contact host tools are denied until computer access is enabled for the conversation", async () => {
540
+ _setOverridesForTesting({ "permission-controls-v2": true });
541
+
542
+ const conv = createConversation("trusted contact host gate");
543
+ const result = await handler.checkPreExecutionGates(
544
+ "bash",
545
+ { command: "ls -la" },
546
+ makeContext({
547
+ trustClass: "trusted_contact",
548
+ conversationId: conv.id,
549
+ }),
550
+ "host",
551
+ "high",
552
+ Date.now(),
553
+ emitLifecycleEvent,
554
+ );
555
+
556
+ expect(result.allowed).toBe(false);
557
+ if (result.allowed) return;
558
+ expect(result.result.content).toContain(
559
+ "computer access is not enabled for this conversation",
560
+ );
561
+ });
562
+
563
+ test("v2 trusted contact host tools run once computer access is enabled for the conversation", async () => {
564
+ _setOverridesForTesting({ "permission-controls-v2": true });
565
+
566
+ const conv = createConversation("trusted contact host access enabled");
567
+ updateConversationHostAccess(conv.id, true);
568
+
569
+ const result = await handler.checkPreExecutionGates(
570
+ "bash",
571
+ { command: "ls -la" },
572
+ makeContext({
573
+ trustClass: "trusted_contact",
574
+ conversationId: conv.id,
575
+ }),
576
+ "host",
577
+ "high",
578
+ Date.now(),
579
+ emitLifecycleEvent,
580
+ );
581
+
582
+ expect(result.allowed).toBe(true);
583
+ });
511
584
  });
@@ -30,8 +30,30 @@ mock.module("../media/app-icon-generator.js", () => ({
30
30
  mock.module("../memory/app-store.js", () => ({
31
31
  getApp: mock(() => null),
32
32
  getAppDirPath: mock(() => ""),
33
+ getAppsDir: mock(() => ""),
33
34
  isMultifileApp: mock(() => false),
34
35
  resolveAppIdFromPath: mock(() => null),
36
+ resolveAppIdByDirName: mock(() => null),
37
+ resolveAppDir: mock(() => ({ dirName: "", dirPath: "" })),
38
+ slugify: mock((s: string) => s),
39
+ validateDirName: mock(() => {}),
40
+ generateAppDirName: mock(() => ""),
41
+ listApps: mock(() => []),
42
+ createApp: mock(() => ({})),
43
+ updateApp: mock(() => {}),
44
+ deleteApp: mock(() => {}),
45
+ getAppPreview: mock(() => null),
46
+ createAppRecord: mock(() => ({})),
47
+ getAppRecord: mock(() => null),
48
+ queryAppRecords: mock(() => []),
49
+ updateAppRecord: mock(() => {}),
50
+ deleteAppRecord: mock(() => {}),
51
+ listAppFiles: mock(() => []),
52
+ appFileExists: mock(() => false),
53
+ readAppFile: mock(() => ""),
54
+ writeAppFile: mock(() => {}),
55
+ editAppFile: mock(() => ({})),
56
+ inlineDistAssets: mock((_, html: string) => html),
35
57
  }));
36
58
  mock.module("../services/published-app-updater.js", () => ({
37
59
  updatePublishedAppDeployment: mock(() => Promise.resolve()),
@@ -1,10 +1,11 @@
1
+ import { homedir, userInfo } from "node:os";
1
2
  import { describe, expect, test } from "bun:test";
2
3
 
3
4
  import { renderWorkspaceTopLevelContext } from "../workspace/top-level-renderer.js";
4
5
  import type { TopLevelSnapshot } from "../workspace/top-level-scanner.js";
5
6
 
6
7
  describe("renderWorkspaceTopLevelContext", () => {
7
- test("renders basic snapshot with directories and files", () => {
8
+ test("renders basic snapshot with directories, files, and host env", () => {
8
9
  const snapshot: TopLevelSnapshot = {
9
10
  rootPath: "/sandbox",
10
11
  directories: ["lib", "src", "tests"],
@@ -19,6 +20,8 @@ describe("renderWorkspaceTopLevelContext", () => {
19
20
  "Root: /sandbox",
20
21
  "Directories: lib, src, tests",
21
22
  "Files: README.md, package.json",
23
+ `Host home directory: ${homedir()}`,
24
+ `Host username: ${userInfo().username}`,
22
25
  "</workspace>",
23
26
  ].join("\n"),
24
27
  );
@@ -65,6 +68,8 @@ describe("renderWorkspaceTopLevelContext", () => {
65
68
  "Root: /empty",
66
69
  "Directories: ",
67
70
  "Files: ",
71
+ `Host home directory: ${homedir()}`,
72
+ `Host username: ${userInfo().username}`,
68
73
  "</workspace>",
69
74
  ].join("\n"),
70
75
  );
@@ -142,4 +147,71 @@ describe("renderWorkspaceTopLevelContext", () => {
142
147
  );
143
148
  expect(result).not.toContain("Current conversation folder");
144
149
  });
150
+
151
+ test("prefers client-reported host env over daemon os.homedir()", () => {
152
+ const snapshot: TopLevelSnapshot = {
153
+ rootPath: "/sandbox",
154
+ directories: ["src"],
155
+ files: ["package.json"],
156
+ truncated: false,
157
+ };
158
+
159
+ const result = renderWorkspaceTopLevelContext(snapshot, {
160
+ hostHomeDir: "/Users/alice",
161
+ hostUsername: "alice",
162
+ });
163
+
164
+ expect(result).toContain("Host home directory: /Users/alice");
165
+ expect(result).toContain("Host username: alice");
166
+ // Fallback values must NOT appear when client values are provided.
167
+ expect(result).not.toContain(`Host home directory: ${homedir()}`);
168
+ expect(result).not.toContain(`Host username: ${userInfo().username}`);
169
+ });
170
+
171
+ test("falls back to daemon os info when host env options omitted", () => {
172
+ const snapshot: TopLevelSnapshot = {
173
+ rootPath: "/sandbox",
174
+ directories: ["src"],
175
+ files: ["package.json"],
176
+ truncated: false,
177
+ };
178
+
179
+ const result = renderWorkspaceTopLevelContext(snapshot, {});
180
+
181
+ expect(result).toContain(`Host home directory: ${homedir()}`);
182
+ expect(result).toContain(`Host username: ${userInfo().username}`);
183
+ });
184
+
185
+ test("falls back to daemon os info when host env options are undefined", () => {
186
+ const snapshot: TopLevelSnapshot = {
187
+ rootPath: "/sandbox",
188
+ directories: ["src"],
189
+ files: ["package.json"],
190
+ truncated: false,
191
+ };
192
+
193
+ const result = renderWorkspaceTopLevelContext(snapshot, {
194
+ hostHomeDir: undefined,
195
+ hostUsername: undefined,
196
+ });
197
+
198
+ expect(result).toContain(`Host home directory: ${homedir()}`);
199
+ expect(result).toContain(`Host username: ${userInfo().username}`);
200
+ });
201
+
202
+ test("uses client home dir but falls back to os username when only home is provided", () => {
203
+ const snapshot: TopLevelSnapshot = {
204
+ rootPath: "/sandbox",
205
+ directories: ["src"],
206
+ files: ["package.json"],
207
+ truncated: false,
208
+ };
209
+
210
+ const result = renderWorkspaceTopLevelContext(snapshot, {
211
+ hostHomeDir: "/Users/alice",
212
+ });
213
+
214
+ expect(result).toContain("Host home directory: /Users/alice");
215
+ expect(result).toContain(`Host username: ${userInfo().username}`);
216
+ });
145
217
  });
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import type {
4
- MacosTransportMetadata,
5
- NonMacosTransportMetadata,
4
+ HostProxyTransportMetadata,
5
+ NonHostProxyTransportMetadata,
6
6
  } from "../daemon/message-types/conversations.js";
7
7
  import { buildTransportHints } from "../daemon/transport-hints.js";
8
8
 
@@ -11,8 +11,8 @@ import { buildTransportHints } from "../daemon/transport-hints.js";
11
11
  // ---------------------------------------------------------------------------
12
12
 
13
13
  describe("buildTransportHints", () => {
14
- test("produces correct hints for macOS transport", () => {
15
- const transport: MacosTransportMetadata = {
14
+ test("returns empty array for host-proxy transport without client hints", () => {
15
+ const transport: HostProxyTransportMetadata = {
16
16
  channelId: "vellum",
17
17
  interfaceId: "macos",
18
18
  hostHomeDir: "/Users/alice",
@@ -21,29 +21,22 @@ describe("buildTransportHints", () => {
21
21
 
22
22
  const hints = buildTransportHints(transport);
23
23
 
24
- expect(hints).toContain("User is messaging from interface: macos");
25
- expect(hints).toContain("Host home directory: /Users/alice");
26
- expect(hints).toContain("Host username: alice");
27
- expect(hints).toHaveLength(3);
24
+ expect(hints).toHaveLength(0);
28
25
  });
29
26
 
30
- test("produces correct hints for non-macOS transport", () => {
31
- const transport: NonMacosTransportMetadata = {
27
+ test("returns empty array for non-host-proxy transport without client hints", () => {
28
+ const transport: NonHostProxyTransportMetadata = {
32
29
  channelId: "vellum",
33
30
  interfaceId: "ios",
34
31
  };
35
32
 
36
33
  const hints = buildTransportHints(transport);
37
34
 
38
- expect(hints).toContain("User is messaging from interface: ios");
39
- expect(hints).toHaveLength(1);
40
- // Should not include host environment hints
41
- expect(hints.some((h) => h.includes("Host home directory"))).toBe(false);
42
- expect(hints.some((h) => h.includes("Host username"))).toBe(false);
35
+ expect(hints).toHaveLength(0);
43
36
  });
44
37
 
45
- test("includes client-provided hints", () => {
46
- const transport: MacosTransportMetadata = {
38
+ test("forwards client-provided hints", () => {
39
+ const transport: HostProxyTransportMetadata = {
47
40
  channelId: "vellum",
48
41
  interfaceId: "macos",
49
42
  hostHomeDir: "/Users/bob",
@@ -53,25 +46,17 @@ describe("buildTransportHints", () => {
53
46
 
54
47
  const hints = buildTransportHints(transport);
55
48
 
56
- expect(hints).toContain("User is messaging from interface: macos");
57
- expect(hints).toContain("Host home directory: /Users/bob");
58
- expect(hints).toContain("Host username: bob");
59
- expect(hints).toContain("custom hint");
60
- expect(hints).toHaveLength(4);
49
+ expect(hints).toEqual(["custom hint"]);
61
50
  });
62
51
 
63
- test("handles missing optional fields on macOS transport", () => {
64
- const transport: MacosTransportMetadata = {
52
+ test("returns empty array when no hints field present", () => {
53
+ const transport: HostProxyTransportMetadata = {
65
54
  channelId: "vellum",
66
55
  interfaceId: "macos",
67
56
  };
68
57
 
69
58
  const hints = buildTransportHints(transport);
70
59
 
71
- expect(hints).toContain("User is messaging from interface: macos");
72
- // Without hostHomeDir and hostUsername, only the interface hint is present
73
- expect(hints).toHaveLength(1);
74
- expect(hints.some((h) => h.includes("Host home directory"))).toBe(false);
75
- expect(hints.some((h) => h.includes("Host username"))).toBe(false);
60
+ expect(hints).toHaveLength(0);
76
61
  });
77
62
  });
@@ -162,6 +162,7 @@ mock.module("../config/env.js", () => ({
162
162
  import { applyCanonicalGuardianDecision } from "../approvals/guardian-decision-primitive.js";
163
163
  import type { ActorContext } from "../approvals/guardian-request-resolvers.js";
164
164
  import { getResolver } from "../approvals/guardian-request-resolvers.js";
165
+ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
165
166
  import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
166
167
  import {
167
168
  createCanonicalGuardianRequest,
@@ -169,6 +170,10 @@ import {
169
170
  listCanonicalGuardianRequests,
170
171
  updateCanonicalGuardianRequest,
171
172
  } from "../memory/canonical-guardian-store.js";
173
+ import {
174
+ createConversation,
175
+ updateConversationHostAccess,
176
+ } from "../memory/conversation-crud.js";
172
177
  import { getDb, initializeDb } from "../memory/db.js";
173
178
  import { scopedApprovalGrants } from "../memory/schema.js";
174
179
  import { bridgeConfirmationRequestToGuardian } from "../runtime/confirmation-request-guardian-bridge.js";
@@ -184,6 +189,8 @@ initializeDb();
184
189
  function resetTables(): void {
185
190
  const db = getDb();
186
191
  db.delete(scopedApprovalGrants).run();
192
+ db.run("DELETE FROM messages");
193
+ db.run("DELETE FROM conversations");
187
194
  db.run("DELETE FROM canonical_guardian_deliveries");
188
195
  db.run("DELETE FROM canonical_guardian_requests");
189
196
  }
@@ -246,6 +253,7 @@ describe("(a) target flow: trusted-contact inline guardian approval end-to-end",
246
253
  events.length = 0;
247
254
  emittedSignals.length = 0;
248
255
  deliveredReplies.length = 0;
256
+ _setOverridesForTesting({});
249
257
  mockGuardianBinding = {
250
258
  id: "binding-1",
251
259
  assistantId: "self",
@@ -1160,3 +1168,104 @@ describe("cross-milestone integration checks", () => {
1160
1168
  expect(freshReq?.followupState).toBeNull();
1161
1169
  });
1162
1170
  });
1171
+
1172
+ describe("(g) v2 trusted-contact flow: model-mediated guardian consent", () => {
1173
+ const handler = new ToolApprovalHandler({
1174
+ inlineGrantWait: { maxWaitMs: 100, intervalMs: 20 },
1175
+ });
1176
+
1177
+ beforeEach(() => {
1178
+ resetTables();
1179
+ events.length = 0;
1180
+ emittedSignals.length = 0;
1181
+ deliveredReplies.length = 0;
1182
+ _setOverridesForTesting({ "permission-controls-v2": true });
1183
+ mockGuardianBinding = {
1184
+ id: "binding-1",
1185
+ assistantId: "self",
1186
+ channel: "telegram",
1187
+ guardianExternalUserId: "guardian-1",
1188
+ guardianDeliveryChatId: "guardian-chat-1",
1189
+ guardianPrincipalId: "test-principal-id",
1190
+ status: "active",
1191
+ };
1192
+ });
1193
+
1194
+ test("trusted-contact host tools do not create tool_grant_request rows under v2", async () => {
1195
+ const conv = createConversation("trusted contact v2 host gate");
1196
+ const result = await handler.checkPreExecutionGates(
1197
+ "bash",
1198
+ { command: "ls" },
1199
+ makeToolContext({
1200
+ trustClass: "trusted_contact",
1201
+ conversationId: conv.id,
1202
+ }),
1203
+ "host",
1204
+ "high",
1205
+ Date.now(),
1206
+ emitLifecycleEvent,
1207
+ );
1208
+
1209
+ expect(result.allowed).toBe(false);
1210
+ if (result.allowed) return;
1211
+ expect(result.result.content).toContain(
1212
+ "computer access is not enabled for this conversation",
1213
+ );
1214
+
1215
+ const requests = listCanonicalGuardianRequests({
1216
+ kind: "tool_grant_request",
1217
+ status: "pending",
1218
+ });
1219
+ expect(requests).toHaveLength(0);
1220
+ expect(emittedSignals).toHaveLength(0);
1221
+ });
1222
+
1223
+ test("trusted-contact host tools run inline once the guardian has already enabled computer access for the conversation", async () => {
1224
+ const conv = createConversation("trusted contact v2 host access enabled");
1225
+ updateConversationHostAccess(conv.id, true);
1226
+
1227
+ const result = await handler.checkPreExecutionGates(
1228
+ "bash",
1229
+ { command: "ls" },
1230
+ makeToolContext({
1231
+ trustClass: "trusted_contact",
1232
+ conversationId: conv.id,
1233
+ }),
1234
+ "host",
1235
+ "high",
1236
+ Date.now(),
1237
+ emitLifecycleEvent,
1238
+ );
1239
+
1240
+ expect(result.allowed).toBe(true);
1241
+ });
1242
+
1243
+ test("confirmation_request guardian bridge is disabled for trusted contacts under v2", () => {
1244
+ const canonicalRequest = createCanonicalGuardianRequest({
1245
+ id: `req-v2-skip-${Date.now()}`,
1246
+ kind: "tool_approval",
1247
+ sourceType: "channel",
1248
+ sourceChannel: "telegram",
1249
+ conversationId: "conv-v2-skip",
1250
+ requesterExternalUserId: "requester-1",
1251
+ guardianExternalUserId: "guardian-1",
1252
+ guardianPrincipalId: "test-principal-id",
1253
+ toolName: "bash",
1254
+ status: "pending",
1255
+ expiresAt: Date.now() + 5 * 60_000,
1256
+ });
1257
+
1258
+ const result = bridgeConfirmationRequestToGuardian({
1259
+ canonicalRequest,
1260
+ trustContext: makeTrustedContactTrustContext(),
1261
+ conversationId: "conv-v2-skip",
1262
+ toolName: "bash",
1263
+ });
1264
+
1265
+ expect("skipped" in result && result.skipped).toBe(true);
1266
+ if ("skipped" in result) {
1267
+ expect(result.reason).toBe("v2_model_mediated");
1268
+ }
1269
+ expect(emittedSignals).toHaveLength(0);
1270
+ });
1271
+ });