@vellumai/assistant 0.6.1 → 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 (463) hide show
  1. package/bun.lock +40 -40
  2. package/bunfig.toml +3 -0
  3. package/docker-entrypoint.sh +12 -2
  4. package/docs/architecture/memory.md +1 -1
  5. package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
  6. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
  7. package/openapi.yaml +184 -69
  8. package/package.json +41 -41
  9. package/scripts/generate-openapi.ts +1 -2
  10. package/src/__tests__/acp-session.test.ts +43 -0
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
  12. package/src/__tests__/app-executors.test.ts +1 -0
  13. package/src/__tests__/app-source-watcher.test.ts +37 -11
  14. package/src/__tests__/approval-routes-http.test.ts +178 -1
  15. package/src/__tests__/assistant-event-hub.test.ts +30 -0
  16. package/src/__tests__/browser-fill-credential.test.ts +229 -94
  17. package/src/__tests__/browser-manager.test.ts +40 -27
  18. package/src/__tests__/catalog-files.test.ts +862 -0
  19. package/src/__tests__/channel-approvals.test.ts +53 -0
  20. package/src/__tests__/checker.test.ts +104 -170
  21. package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
  22. package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
  23. package/src/__tests__/config-schema-cmd.test.ts +2 -2
  24. package/src/__tests__/config-schema.test.ts +125 -48
  25. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
  26. package/src/__tests__/context-overflow-approval.test.ts +21 -6
  27. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
  28. package/src/__tests__/conversation-agent-loop.test.ts +1 -1
  29. package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
  30. package/src/__tests__/conversation-attachments.test.ts +80 -4
  31. package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
  32. package/src/__tests__/conversation-directories-parse.test.ts +105 -0
  33. package/src/__tests__/conversation-fork-crud.test.ts +17 -0
  34. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  35. package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
  36. package/src/__tests__/conversation-inject-context.test.ts +103 -0
  37. package/src/__tests__/conversation-queue.test.ts +45 -2
  38. package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
  39. package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
  40. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  41. package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
  42. package/src/__tests__/conversation-starter-routes.test.ts +126 -0
  43. package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
  44. package/src/__tests__/conversation-store.test.ts +195 -0
  45. package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
  46. package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
  47. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  48. package/src/__tests__/credential-vault-unit.test.ts +4 -4
  49. package/src/__tests__/credential-vault.test.ts +152 -13
  50. package/src/__tests__/credentials-cli.test.ts +2 -2
  51. package/src/__tests__/date-context.test.ts +4 -4
  52. package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
  53. package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
  54. package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
  55. package/src/__tests__/gateway-only-guard.test.ts +3 -0
  56. package/src/__tests__/gemini-provider.test.ts +2 -2
  57. package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
  58. package/src/__tests__/headless-browser-interactions.test.ts +707 -371
  59. package/src/__tests__/headless-browser-navigate.test.ts +389 -47
  60. package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
  61. package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
  62. package/src/__tests__/host-bash-proxy.test.ts +150 -1
  63. package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
  64. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
  65. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
  66. package/src/__tests__/host-browser-event-routes.test.ts +350 -0
  67. package/src/__tests__/host-browser-proxy.test.ts +444 -0
  68. package/src/__tests__/host-browser-routes.test.ts +198 -0
  69. package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
  70. package/src/__tests__/host-cu-proxy.test.ts +171 -1
  71. package/src/__tests__/host-file-proxy.test.ts +185 -1
  72. package/src/__tests__/host-file-read-tool.test.ts +52 -0
  73. package/src/__tests__/host-proxy-interface.test.ts +165 -0
  74. package/src/__tests__/host-shell-tool.test.ts +1 -11
  75. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  76. package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
  77. package/src/__tests__/inline-command-runner.test.ts +7 -5
  78. package/src/__tests__/integration-status.test.ts +6 -7
  79. package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
  80. package/src/__tests__/log-export-workspace.test.ts +190 -0
  81. package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
  82. package/src/__tests__/mcp-client-auth.test.ts +40 -4
  83. package/src/__tests__/mcp-health-check.test.ts +10 -3
  84. package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
  85. package/src/__tests__/migration-export-http.test.ts +61 -2
  86. package/src/__tests__/migration-export-streaming.test.ts +66 -0
  87. package/src/__tests__/migration-import-commit-http.test.ts +101 -1
  88. package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
  89. package/src/__tests__/navigate-settings-tab.test.ts +14 -1
  90. package/src/__tests__/notification-broadcaster.test.ts +65 -0
  91. package/src/__tests__/oauth-apps-routes.test.ts +17 -12
  92. package/src/__tests__/oauth-cli.test.ts +707 -60
  93. package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
  94. package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
  95. package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
  96. package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
  97. package/src/__tests__/oauth-providers-routes.test.ts +50 -14
  98. package/src/__tests__/oauth-store.test.ts +1386 -182
  99. package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
  100. package/src/__tests__/onboarding-template-contract.test.ts +74 -55
  101. package/src/__tests__/openai-provider.test.ts +2 -2
  102. package/src/__tests__/outlook-categories.test.ts +1 -1
  103. package/src/__tests__/outlook-client-automation.test.ts +1 -1
  104. package/src/__tests__/outlook-compose-tools.test.ts +1 -1
  105. package/src/__tests__/outlook-email-watcher.test.ts +1 -1
  106. package/src/__tests__/outlook-follow-up.test.ts +1 -1
  107. package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
  108. package/src/__tests__/outlook-trash.test.ts +1 -1
  109. package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
  110. package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
  111. package/src/__tests__/permission-mode.test.ts +28 -56
  112. package/src/__tests__/pkb-autoinject.test.ts +96 -0
  113. package/src/__tests__/platform-callback-registration.test.ts +19 -0
  114. package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
  115. package/src/__tests__/proxy-approval-callback.test.ts +18 -0
  116. package/src/__tests__/require-fresh-approval.test.ts +40 -3
  117. package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
  118. package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
  119. package/src/__tests__/schedule-routes.test.ts +162 -0
  120. package/src/__tests__/secret-detection-handler.test.ts +84 -0
  121. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  123. package/src/__tests__/set-permission-mode.test.ts +13 -250
  124. package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
  125. package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
  126. package/src/__tests__/slack-channel-config.test.ts +12 -15
  127. package/src/__tests__/subagent-detail.test.ts +44 -2
  128. package/src/__tests__/subagent-disposal.test.ts +1 -0
  129. package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
  130. package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
  131. package/src/__tests__/subagent-manager-notify.test.ts +1 -0
  132. package/src/__tests__/subagent-notify-parent.test.ts +1 -0
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
  134. package/src/__tests__/subagent-tools.test.ts +1 -0
  135. package/src/__tests__/subagent-types.test.ts +1 -0
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
  137. package/src/__tests__/system-prompt.test.ts +72 -1
  138. package/src/__tests__/task-scheduler.test.ts +32 -6
  139. package/src/__tests__/telegram-config.test.ts +10 -13
  140. package/src/__tests__/terminal-sandbox.test.ts +1 -1
  141. package/src/__tests__/terminal-tools.test.ts +11 -5
  142. package/src/__tests__/test-preload.ts +14 -0
  143. package/src/__tests__/tool-approval-handler.test.ts +73 -0
  144. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
  145. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
  146. package/src/__tests__/tool-executor.test.ts +0 -1
  147. package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
  148. package/src/__tests__/top-level-renderer.test.ts +73 -1
  149. package/src/__tests__/transport-hints-queue.test.ts +62 -0
  150. package/src/__tests__/trust-store.test.ts +4 -4
  151. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
  152. package/src/__tests__/v2-consent-policy.test.ts +103 -0
  153. package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
  154. package/src/__tests__/workspace-policy.test.ts +2 -7
  155. package/src/acp/client-handler.ts +30 -4
  156. package/src/agent/loop.ts +12 -35
  157. package/src/approvals/guardian-request-resolvers.ts +21 -15
  158. package/src/browser-session/__tests__/manager.test.ts +297 -0
  159. package/src/browser-session/backends/cdp-inspect.ts +30 -0
  160. package/src/browser-session/backends/extension.ts +26 -0
  161. package/src/browser-session/backends/local.ts +24 -0
  162. package/src/browser-session/events.ts +164 -0
  163. package/src/browser-session/index.ts +27 -0
  164. package/src/browser-session/manager.ts +159 -0
  165. package/src/browser-session/types.ts +28 -0
  166. package/src/channels/__tests__/types.test.ts +134 -0
  167. package/src/channels/types.ts +55 -0
  168. package/src/cli/__tests__/run-assistant-command.ts +34 -7
  169. package/src/cli/__tests__/unknown-command.test.ts +33 -0
  170. package/src/cli/commands/browser-relay.ts +339 -409
  171. package/src/cli/commands/credentials.ts +3 -3
  172. package/src/cli/commands/default-action.ts +68 -1
  173. package/src/cli/commands/email.ts +18 -13
  174. package/src/cli/commands/mcp.ts +16 -4
  175. package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
  176. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
  177. package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
  178. package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
  179. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
  180. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
  181. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
  182. package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
  183. package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
  184. package/src/cli/commands/oauth/apps.ts +7 -4
  185. package/src/cli/commands/oauth/connect.ts +16 -2
  186. package/src/cli/commands/oauth/disconnect.ts +1 -1
  187. package/src/cli/commands/oauth/providers.ts +200 -36
  188. package/src/cli/commands/oauth/shared.ts +5 -5
  189. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
  190. package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
  191. package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
  192. package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
  193. package/src/cli/commands/platform/index.ts +107 -10
  194. package/src/cli/commands/usage.ts +10 -9
  195. package/src/cli/lib/daemon-credential-client.ts +4 -0
  196. package/src/cli/program.ts +10 -3
  197. package/src/config/assistant-feature-flags.ts +59 -55
  198. package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
  199. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
  200. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
  201. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
  202. package/src/config/bundled-skills/contacts/SKILL.md +3 -0
  203. package/src/config/bundled-skills/document/SKILL.md +4 -0
  204. package/src/config/bundled-skills/gmail/SKILL.md +12 -7
  205. package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
  206. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
  207. package/src/config/bundled-skills/outlook/SKILL.md +7 -0
  208. package/src/config/bundled-skills/settings/TOOLS.json +1 -1
  209. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
  210. package/src/config/bundled-skills/subagent/SKILL.md +21 -0
  211. package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
  212. package/src/config/bundled-skills/tasks/SKILL.md +5 -0
  213. package/src/config/env-registry.ts +14 -0
  214. package/src/config/env.ts +21 -0
  215. package/src/config/feature-flag-registry.json +46 -7
  216. package/src/config/loader.ts +56 -1
  217. package/src/config/sanitize-for-transfer.ts +47 -0
  218. package/src/config/schema.ts +46 -5
  219. package/src/config/schemas/host-browser.ts +66 -0
  220. package/src/config/schemas/memory-lifecycle.ts +1 -1
  221. package/src/config/schemas/memory-retrieval.ts +103 -0
  222. package/src/config/schemas/security.ts +0 -6
  223. package/src/config/schemas/services.ts +16 -0
  224. package/src/config/types.ts +0 -1
  225. package/src/context/post-turn-tool-result-truncation.ts +176 -0
  226. package/src/context/window-manager.ts +19 -1
  227. package/src/credential-execution/approval-bridge.ts +49 -16
  228. package/src/credential-execution/managed-catalog.ts +3 -7
  229. package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
  230. package/src/daemon/app-source-watcher.ts +35 -0
  231. package/src/daemon/config-watcher.ts +6 -2
  232. package/src/daemon/context-overflow-approval.ts +5 -1
  233. package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
  234. package/src/daemon/conversation-agent-loop.ts +74 -19
  235. package/src/daemon/conversation-attachments.ts +40 -1
  236. package/src/daemon/conversation-messaging.ts +3 -0
  237. package/src/daemon/conversation-process.ts +66 -3
  238. package/src/daemon/conversation-queue-manager.ts +8 -0
  239. package/src/daemon/conversation-runtime-assembly.ts +159 -20
  240. package/src/daemon/conversation-surfaces.ts +78 -12
  241. package/src/daemon/conversation-tool-setup.ts +74 -11
  242. package/src/daemon/conversation-workspace.ts +12 -0
  243. package/src/daemon/conversation.ts +227 -11
  244. package/src/daemon/date-context.ts +10 -10
  245. package/src/daemon/first-greeting.ts +3 -2
  246. package/src/daemon/handlers/conversations.ts +9 -139
  247. package/src/daemon/handlers/shared.ts +65 -0
  248. package/src/daemon/handlers/skills.ts +232 -37
  249. package/src/daemon/host-bash-proxy.ts +48 -13
  250. package/src/daemon/host-browser-proxy.ts +191 -0
  251. package/src/daemon/host-cu-proxy.ts +36 -11
  252. package/src/daemon/host-file-proxy.ts +57 -9
  253. package/src/daemon/lifecycle.ts +86 -12
  254. package/src/daemon/message-protocol.ts +7 -0
  255. package/src/daemon/message-types/conversations.ts +59 -13
  256. package/src/daemon/message-types/host-browser.ts +100 -0
  257. package/src/daemon/message-types/messages.ts +5 -6
  258. package/src/daemon/message-types/notifications.ts +12 -0
  259. package/src/daemon/message-types/settings.ts +12 -0
  260. package/src/daemon/message-types/skills.ts +10 -0
  261. package/src/daemon/message-types/subagents.ts +2 -0
  262. package/src/daemon/server.ts +112 -35
  263. package/src/daemon/tool-side-effects.ts +6 -0
  264. package/src/daemon/transport-hints.ts +14 -0
  265. package/src/inbound/platform-callback-registration.ts +18 -17
  266. package/src/index.ts +1 -1
  267. package/src/mcp/client.ts +59 -24
  268. package/src/memory/app-store.ts +31 -1
  269. package/src/memory/conversation-crud.ts +38 -10
  270. package/src/memory/conversation-directories.ts +39 -0
  271. package/src/memory/conversation-group-migration.ts +65 -5
  272. package/src/memory/conversation-starters-cadence.ts +76 -0
  273. package/src/memory/conversation-title-service.ts +5 -2
  274. package/src/memory/db-init.ts +12 -0
  275. package/src/memory/embedding-backend.test.ts +75 -0
  276. package/src/memory/embedding-backend.ts +131 -5
  277. package/src/memory/embedding-gemini.test.ts +54 -0
  278. package/src/memory/embedding-gemini.ts +20 -9
  279. package/src/memory/embedding-local.ts +177 -18
  280. package/src/memory/graph/capability-seed.ts +3 -5
  281. package/src/memory/graph/consolidation.ts +10 -23
  282. package/src/memory/graph/extraction-job.ts +15 -0
  283. package/src/memory/graph/retriever.ts +40 -22
  284. package/src/memory/graph/store.test.ts +7 -3
  285. package/src/memory/graph/store.ts +47 -12
  286. package/src/memory/group-crud.ts +25 -9
  287. package/src/memory/llm-usage-store.ts +45 -4
  288. package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
  289. package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
  290. package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
  291. package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
  292. package/src/memory/migrations/217-conversation-host-access.ts +40 -0
  293. package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
  294. package/src/memory/migrations/index.ts +6 -0
  295. package/src/memory/migrations/registry.ts +8 -0
  296. package/src/memory/schema/conversations.ts +1 -0
  297. package/src/memory/schema/oauth.ts +18 -13
  298. package/src/messaging/provider.ts +1 -1
  299. package/src/notifications/broadcaster.ts +6 -0
  300. package/src/notifications/conversation-pairing.ts +12 -4
  301. package/src/notifications/emit-signal.ts +14 -0
  302. package/src/notifications/signal.ts +11 -0
  303. package/src/oauth/AGENTS.md +76 -0
  304. package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
  305. package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
  306. package/src/oauth/byo-connection.test.ts +8 -8
  307. package/src/oauth/byo-connection.ts +7 -7
  308. package/src/oauth/connect-orchestrator.ts +23 -21
  309. package/src/oauth/connect-types.ts +3 -3
  310. package/src/oauth/connection-resolver.test.ts +17 -4
  311. package/src/oauth/connection-resolver.ts +16 -16
  312. package/src/oauth/connection.ts +1 -1
  313. package/src/oauth/manual-token-connection.ts +13 -13
  314. package/src/oauth/oauth-store.ts +214 -100
  315. package/src/oauth/platform-connection.test.ts +5 -5
  316. package/src/oauth/platform-connection.ts +4 -4
  317. package/src/oauth/provider-serializer.ts +31 -5
  318. package/src/oauth/revoke.ts +76 -0
  319. package/src/oauth/seed-providers.ts +127 -87
  320. package/src/oauth/token-persistence.ts +1 -1
  321. package/src/permissions/checker.ts +3 -3
  322. package/src/permissions/defaults.ts +7 -8
  323. package/src/permissions/permission-mode.ts +4 -11
  324. package/src/permissions/prompter.ts +13 -3
  325. package/src/permissions/v2-consent-policy.ts +87 -0
  326. package/src/platform/client.ts +1 -1
  327. package/src/prompts/system-prompt.ts +18 -21
  328. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
  329. package/src/prompts/templates/BOOTSTRAP.md +59 -96
  330. package/src/prompts/templates/SOUL.md +11 -11
  331. package/src/providers/anthropic/client.ts +1 -0
  332. package/src/providers/types.ts +1 -1
  333. package/src/runtime/AGENTS.md +23 -0
  334. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
  335. package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
  336. package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
  337. package/src/runtime/assistant-event-hub.ts +24 -2
  338. package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
  339. package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
  340. package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
  341. package/src/runtime/auth/middleware.ts +98 -0
  342. package/src/runtime/auth/route-policy.ts +6 -7
  343. package/src/runtime/auth/token-service.ts +8 -0
  344. package/src/runtime/capability-tokens.ts +414 -0
  345. package/src/runtime/channel-approvals.ts +18 -5
  346. package/src/runtime/chrome-extension-registry.ts +332 -0
  347. package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
  348. package/src/runtime/guardian-decision-types.ts +7 -0
  349. package/src/runtime/http-server.ts +425 -70
  350. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
  351. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
  352. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
  353. package/src/runtime/migrations/migration-transport.ts +6 -0
  354. package/src/runtime/migrations/migration-wizard.ts +22 -2
  355. package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
  356. package/src/runtime/migrations/vbundle-builder.ts +145 -38
  357. package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
  358. package/src/runtime/migrations/vbundle-importer.ts +55 -5
  359. package/src/runtime/pending-interactions.ts +29 -13
  360. package/src/runtime/routes/approval-routes.ts +90 -16
  361. package/src/runtime/routes/browser-cdp-routes.ts +229 -0
  362. package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
  363. package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
  364. package/src/runtime/routes/conversation-management-routes.ts +108 -0
  365. package/src/runtime/routes/conversation-routes.ts +308 -28
  366. package/src/runtime/routes/conversation-starter-routes.ts +78 -16
  367. package/src/runtime/routes/group-routes.ts +22 -8
  368. package/src/runtime/routes/guardian-action-routes.ts +24 -13
  369. package/src/runtime/routes/host-browser-routes.ts +279 -0
  370. package/src/runtime/routes/host-file-routes.ts +9 -1
  371. package/src/runtime/routes/identity-routes.ts +259 -16
  372. package/src/runtime/routes/log-export/AGENTS.md +104 -0
  373. package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
  374. package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
  375. package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
  376. package/src/runtime/routes/log-export-routes.ts +60 -25
  377. package/src/runtime/routes/memory-item-routes.ts +1 -7
  378. package/src/runtime/routes/migration-routes.ts +87 -2
  379. package/src/runtime/routes/oauth-apps.ts +15 -17
  380. package/src/runtime/routes/oauth-providers.ts +4 -0
  381. package/src/runtime/routes/schedule-routes.ts +24 -11
  382. package/src/runtime/routes/settings-routes.ts +9 -97
  383. package/src/runtime/routes/skills-routes.ts +52 -2
  384. package/src/runtime/routes/subagents-routes.ts +14 -10
  385. package/src/runtime/routes/usage-routes.ts +8 -7
  386. package/src/runtime/routes/workspace-routes.test.ts +22 -0
  387. package/src/runtime/routes/workspace-routes.ts +8 -1
  388. package/src/runtime/routes/workspace-utils.ts +2 -0
  389. package/src/schedule/scheduler.ts +7 -5
  390. package/src/security/ces-credential-client.ts +20 -0
  391. package/src/security/ces-rpc-credential-backend.ts +17 -0
  392. package/src/security/credential-backend.ts +5 -0
  393. package/src/security/oauth2.ts +42 -25
  394. package/src/security/secure-keys.ts +118 -25
  395. package/src/security/token-manager.ts +23 -10
  396. package/src/skills/catalog-files.ts +492 -0
  397. package/src/skills/inline-command-runner.ts +12 -14
  398. package/src/subagent/manager.ts +131 -26
  399. package/src/subagent/types.ts +19 -0
  400. package/src/tools/apps/executors.ts +11 -2
  401. package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
  402. package/src/tools/browser/auth-detector.ts +43 -12
  403. package/src/tools/browser/browser-execution.ts +645 -340
  404. package/src/tools/browser/browser-manager.ts +36 -12
  405. package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
  406. package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
  407. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
  408. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
  409. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
  410. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
  411. package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
  412. package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
  413. package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
  414. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
  415. package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
  416. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
  417. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
  418. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
  419. package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
  420. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
  421. package/src/tools/browser/cdp-client/errors.ts +34 -0
  422. package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
  423. package/src/tools/browser/cdp-client/factory.ts +204 -0
  424. package/src/tools/browser/cdp-client/index.ts +14 -0
  425. package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
  426. package/src/tools/browser/cdp-client/types.ts +52 -0
  427. package/src/tools/filesystem/edit.ts +1 -1
  428. package/src/tools/filesystem/list.ts +1 -1
  429. package/src/tools/filesystem/read.ts +1 -1
  430. package/src/tools/filesystem/write.ts +2 -1
  431. package/src/tools/host-filesystem/edit.ts +1 -1
  432. package/src/tools/host-filesystem/read.ts +12 -15
  433. package/src/tools/host-filesystem/write.ts +1 -1
  434. package/src/tools/host-terminal/host-shell.ts +21 -16
  435. package/src/tools/permission-checker.ts +77 -100
  436. package/src/tools/registry.ts +0 -2
  437. package/src/tools/secret-detection-handler.ts +34 -1
  438. package/src/tools/shared/filesystem/image-read.ts +61 -40
  439. package/src/tools/skills/sandbox-runner.ts +3 -6
  440. package/src/tools/subagent/spawn.ts +47 -3
  441. package/src/tools/subagent/status.ts +2 -0
  442. package/src/tools/system/register.ts +2 -16
  443. package/src/tools/terminal/safe-env.ts +7 -0
  444. package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
  445. package/src/tools/terminal/sandbox.ts +4 -1
  446. package/src/tools/terminal/shell.ts +24 -21
  447. package/src/tools/tool-approval-handler.ts +48 -2
  448. package/src/tools/types.ts +2 -3
  449. package/src/util/platform.ts +14 -19
  450. package/src/watcher/provider-types.ts +1 -1
  451. package/src/workspace/migrations/029-seed-pkb.ts +1 -0
  452. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
  453. package/src/workspace/migrations/registry.ts +2 -0
  454. package/src/workspace/top-level-renderer.ts +19 -1
  455. package/src/__tests__/chrome-cdp.test.ts +0 -419
  456. package/src/__tests__/permission-mode-sse.test.ts +0 -418
  457. package/src/__tests__/permission-mode-store.test.ts +0 -277
  458. package/src/browser-extension-relay/protocol.ts +0 -63
  459. package/src/browser-extension-relay/server.ts +0 -203
  460. package/src/config/schemas/sandbox.ts +0 -14
  461. package/src/permissions/permission-mode-store.ts +0 -180
  462. package/src/tools/browser/chrome-cdp.ts +0 -239
  463. 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
  });
@@ -2,7 +2,7 @@ import * as realChildProcess from "node:child_process";
2
2
  import * as realFs from "node:fs";
3
3
  import { beforeEach, describe, expect, mock, test } from "bun:test";
4
4
 
5
- import type { SandboxConfig } from "../config/schema.js";
5
+ import type { SandboxConfig } from "../tools/terminal/sandbox.js";
6
6
 
7
7
  let platform = "linux";
8
8
 
@@ -65,13 +65,13 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
65
65
 
66
66
  // ── Imports (after mocks) ───────────────────────────────────────────────────
67
67
 
68
- import type { SandboxConfig } from "../config/schema.js";
69
68
  import { parse } from "../tools/terminal/parser.js";
70
69
  import {
71
70
  ALWAYS_INJECTED_ENV_VARS,
72
71
  buildSanitizedEnv,
73
72
  SAFE_ENV_VARS,
74
73
  } from "../tools/terminal/safe-env.js";
74
+ import type { SandboxConfig } from "../tools/terminal/sandbox.js";
75
75
  import { wrapCommand } from "../tools/terminal/sandbox.js";
76
76
  import { ToolError } from "../util/errors.js";
77
77
 
@@ -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();
@@ -455,10 +464,7 @@ describe("buildSanitizedEnv", () => {
455
464
  test("result is a plain object with no prototype-inherited secrets", () => {
456
465
  const env = buildSanitizedEnv();
457
466
  const keys = Object.keys(env);
458
- const safeKeys: string[] = [
459
- ...SAFE_ENV_VARS,
460
- ...ALWAYS_INJECTED_ENV_VARS,
461
- ];
467
+ const safeKeys: string[] = [...SAFE_ENV_VARS, ...ALWAYS_INJECTED_ENV_VARS];
462
468
  for (const key of keys) {
463
469
  expect(safeKeys).toContain(key);
464
470
  }
@@ -25,11 +25,25 @@ process.env.VELLUM_WORKSPACE_DIR = testDir;
25
25
  process.env.VELLUM_PLATFORM_URL = "https://test-platform.vellum.ai";
26
26
  process.exitCode = 0;
27
27
 
28
+ // Prevent tests from routing credential writes through the real CES
29
+ // (Credential Execution Service). Without this, setSecureKeyAsync() in
30
+ // containerized environments writes to the live credential store.
31
+ const savedIsContainerized = process.env.IS_CONTAINERIZED;
32
+ const savedCesCredentialUrl = process.env.CES_CREDENTIAL_URL;
33
+ delete process.env.IS_CONTAINERIZED;
34
+ delete process.env.CES_CREDENTIAL_URL;
35
+
28
36
  afterAll(() => {
29
37
  resetDb();
30
38
  process.exitCode = 0;
31
39
  delete process.env.VELLUM_WORKSPACE_DIR;
32
40
  delete process.env.VELLUM_PLATFORM_URL;
41
+ if (savedIsContainerized !== undefined) {
42
+ process.env.IS_CONTAINERIZED = savedIsContainerized;
43
+ }
44
+ if (savedCesCredentialUrl !== undefined) {
45
+ process.env.CES_CREDENTIAL_URL = savedCesCredentialUrl;
46
+ }
33
47
  try {
34
48
  rmSync(testDir, { recursive: true, force: true });
35
49
  } catch {
@@ -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
  });
@@ -37,7 +37,6 @@ describe("createToolDomainEventPublisher", () => {
37
37
  reason: "needs approval",
38
38
  allowlistOptions: [],
39
39
  scopeOptions: [],
40
- sandboxed: true,
41
40
  });
42
41
 
43
42
  await publish({
@@ -41,7 +41,6 @@ let checkerDecision: "allow" | "prompt" | "deny" = "allow";
41
41
  let checkerReason = "allowed";
42
42
  let checkerRisk = "low";
43
43
  let promptDecision: "allow" | "always_allow" | "deny" | "always_deny" = "allow";
44
- let sandboxed = false;
45
44
  let fakeToolResult: ToolExecutionResult = { content: "ok", isError: false };
46
45
  let toolThrow: Error | null = null;
47
46
 
@@ -151,7 +150,7 @@ mock.module("../tools/shared/filesystem/path-policy.js", () => ({
151
150
  }));
152
151
 
153
152
  mock.module("../tools/terminal/sandbox.js", () => ({
154
- wrapCommand: () => ({ command: "", sandboxed }),
153
+ wrapCommand: () => ({ command: "", sandboxed: false }),
155
154
  }));
156
155
 
157
156
  import { PermissionPrompter } from "../permissions/prompter.js";
@@ -193,7 +192,6 @@ describe("ToolExecutor lifecycle events", () => {
193
192
  checkerReason = "allowed";
194
193
  checkerRisk = "low";
195
194
  promptDecision = "allow";
196
- sandboxed = false;
197
195
  fakeToolResult = { content: "ok", isError: false };
198
196
  toolThrow = null;
199
197
  });
@@ -231,7 +229,6 @@ describe("ToolExecutor lifecycle events", () => {
231
229
  checkerReason = "medium risk: requires approval";
232
230
  checkerRisk = "medium";
233
231
  promptDecision = "deny";
234
- sandboxed = true;
235
232
 
236
233
  const events: ToolLifecycleEvent[] = [];
237
234
  const executor = new ToolExecutor(makePrompter());
@@ -256,7 +253,6 @@ describe("ToolExecutor lifecycle events", () => {
256
253
  expect(promptEvent.executionTarget).toBe("sandbox");
257
254
  expect(promptEvent.riskLevel).toBe("medium");
258
255
  expect(promptEvent.reason).toBe("medium risk: requires approval");
259
- expect(promptEvent.sandboxed).toBe(true);
260
256
  expect(promptEvent.allowlistOptions).toEqual([
261
257
  { label: "exact", description: "exact", pattern: "exact" },
262
258
  ]);
@@ -276,7 +272,6 @@ describe("ToolExecutor lifecycle events", () => {
276
272
  checkerDecision = "prompt";
277
273
  checkerReason = "guardrail prompt";
278
274
  checkerRisk = "high";
279
- sandboxed = true;
280
275
 
281
276
  const events: ToolLifecycleEvent[] = [];
282
277
  const executor = new ToolExecutor(
@@ -580,7 +575,6 @@ describe("ToolExecutor lifecycle events", () => {
580
575
  checkerReason = "Matched trust rule";
581
576
  checkerRisk = "low";
582
577
  promptDecision = "allow";
583
- sandboxed = true;
584
578
 
585
579
  const events: ToolLifecycleEvent[] = [];
586
580
  const executor = new ToolExecutor(makePrompter());
@@ -602,7 +596,6 @@ describe("ToolExecutor lifecycle events", () => {
602
596
  expect(promptEvent.reason).toBe(
603
597
  "Private conversation: side-effect tools require explicit approval",
604
598
  );
605
- expect(promptEvent.sandboxed).toBe(true);
606
599
  });
607
600
 
608
601
  test("no permission_prompt event for read-only tool even with forcePromptSideEffects", async () => {
@@ -1949,7 +1949,6 @@ describe("ToolExecutor persistentDecisionsAllowed contract", () => {
1949
1949
  _allowlistOptions: AllowlistOption[],
1950
1950
  _scopeOptions: ScopeOption[],
1951
1951
  _diff: unknown,
1952
- _sandboxed: unknown,
1953
1952
  _conversationId: unknown,
1954
1953
  _executionTarget: unknown,
1955
1954
  persistentDecisionsAllowed: boolean | undefined,
@@ -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
  });
@@ -0,0 +1,62 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type {
4
+ HostProxyTransportMetadata,
5
+ NonHostProxyTransportMetadata,
6
+ } from "../daemon/message-types/conversations.js";
7
+ import { buildTransportHints } from "../daemon/transport-hints.js";
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // buildTransportHints
11
+ // ---------------------------------------------------------------------------
12
+
13
+ describe("buildTransportHints", () => {
14
+ test("returns empty array for host-proxy transport without client hints", () => {
15
+ const transport: HostProxyTransportMetadata = {
16
+ channelId: "vellum",
17
+ interfaceId: "macos",
18
+ hostHomeDir: "/Users/alice",
19
+ hostUsername: "alice",
20
+ };
21
+
22
+ const hints = buildTransportHints(transport);
23
+
24
+ expect(hints).toHaveLength(0);
25
+ });
26
+
27
+ test("returns empty array for non-host-proxy transport without client hints", () => {
28
+ const transport: NonHostProxyTransportMetadata = {
29
+ channelId: "vellum",
30
+ interfaceId: "ios",
31
+ };
32
+
33
+ const hints = buildTransportHints(transport);
34
+
35
+ expect(hints).toHaveLength(0);
36
+ });
37
+
38
+ test("forwards client-provided hints", () => {
39
+ const transport: HostProxyTransportMetadata = {
40
+ channelId: "vellum",
41
+ interfaceId: "macos",
42
+ hostHomeDir: "/Users/bob",
43
+ hostUsername: "bob",
44
+ hints: ["custom hint"],
45
+ };
46
+
47
+ const hints = buildTransportHints(transport);
48
+
49
+ expect(hints).toEqual(["custom hint"]);
50
+ });
51
+
52
+ test("returns empty array when no hints field present", () => {
53
+ const transport: HostProxyTransportMetadata = {
54
+ channelId: "vellum",
55
+ interfaceId: "macos",
56
+ };
57
+
58
+ const hints = buildTransportHints(transport);
59
+
60
+ expect(hints).toHaveLength(0);
61
+ });
62
+ });
@@ -909,8 +909,8 @@ describe("Trust Store", () => {
909
909
  expect(match!.id).toBe("default:allow-bash-rm-bootstrap");
910
910
  expect(match!.decision).toBe("allow");
911
911
  expect(match!.allowHighRisk).toBe(true);
912
- // Outside workspace, the bootstrap rule doesn't match — with sandbox
913
- // disabled (the default), there is no catch-all bash allow rule either.
912
+ // Outside workspace, the bootstrap rule doesn't match — without
913
+ // IS_CONTAINERIZED there is no catch-all bash allow rule either.
914
914
  const other = findHighestPriorityRule(
915
915
  "bash",
916
916
  ["rm BOOTSTRAP.md"],
@@ -930,8 +930,8 @@ describe("Trust Store", () => {
930
930
  expect(match!.id).toBe("default:allow-bash-rm-updates");
931
931
  expect(match!.decision).toBe("allow");
932
932
  expect(match!.allowHighRisk).toBe(true);
933
- // Outside workspace, should NOT match the updates rule — with sandbox
934
- // disabled (the default), there is no catch-all bash allow rule either.
933
+ // Outside workspace, should NOT match the updates rule — without
934
+ // IS_CONTAINERIZED there is no catch-all bash allow rule either.
935
935
  const other = findHighestPriorityRule(
936
936
  "bash",
937
937
  ["rm UPDATES.md"],