@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
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Capability token minting and verification for scoped, short-lived tokens
3
+ * issued to the chrome extension (and other thin clients) so they can submit
4
+ * results back to the runtime without a full guardian-bound JWT.
5
+ *
6
+ * Design:
7
+ * - Tokens are HMAC-SHA256 signed over a JSON claims payload.
8
+ * - Claims include a bound capability, guardian id, nonce, and expiry.
9
+ * - Signing uses a long-lived random secret persisted to
10
+ * `~/.vellum/protected/` with 0600 permissions. The protected
11
+ * directory sits outside the workspace per AGENTS.md: workspace
12
+ * directories must not hold security-sensitive material.
13
+ * - The secret is generated once on first launch and reused across
14
+ * subsequent daemon restarts so previously-minted tokens still verify.
15
+ * - Tests inject their own secret via `setCapabilityTokenSecretForTests`.
16
+ *
17
+ * The encoded token format is `<base64url(payload)>.<base64url(sig)>`.
18
+ */
19
+
20
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
21
+ import {
22
+ chmodSync,
23
+ existsSync,
24
+ mkdirSync,
25
+ readFileSync,
26
+ renameSync,
27
+ unlinkSync,
28
+ writeFileSync,
29
+ } from "node:fs";
30
+ import { homedir } from "node:os";
31
+ import { dirname, join } from "node:path";
32
+
33
+ import { getLogger } from "../util/logger.js";
34
+ import { getDataDir, getProtectedDir } from "../util/platform.js";
35
+
36
+ const log = getLogger("capability-tokens");
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Types
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /** Capability identifiers that can be bound to a capability token. */
43
+ export type Capability = "host_browser_command";
44
+
45
+ /** Claims encoded in the signed payload. */
46
+ export interface CapabilityClaims {
47
+ capability: Capability;
48
+ guardianId: string;
49
+ /** 16-byte random nonce, hex-encoded. Prevents replay across fresh mints. */
50
+ nonce: string;
51
+ /** ms-since-epoch expiry. */
52
+ expiresAt: number;
53
+ }
54
+
55
+ /** A freshly-minted capability token and its absolute expiry. */
56
+ export interface CapabilityToken {
57
+ token: string;
58
+ expiresAt: number;
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Secret lifecycle
63
+ // ---------------------------------------------------------------------------
64
+
65
+ let _secret: Buffer | undefined;
66
+
67
+ /**
68
+ * Returns the canonical path where the capability-token secret is
69
+ * persisted: `~/.vellum/protected/capability-token-secret`. The protected
70
+ * directory is the canonical location for security-sensitive material
71
+ * and sits outside the workspace (which AGENTS.md forbids for secrets).
72
+ */
73
+ function getSecretPath(): string {
74
+ return join(getProtectedDir(), "capability-token-secret");
75
+ }
76
+
77
+ /**
78
+ * Legacy path under `workspace/data/` where earlier builds persisted the
79
+ * capability-token secret. We keep this as a read-only migration source
80
+ * so existing deployments don't regenerate their secret (and invalidate
81
+ * every outstanding token) on upgrade — the first launch after the
82
+ * upgrade copies the legacy file into `getProtectedDir()` and removes it
83
+ * from the workspace.
84
+ */
85
+ function getLegacySecretPath(): string {
86
+ return join(getDataDir(), "capability-token-secret");
87
+ }
88
+
89
+ /**
90
+ * Path overrides used by unit tests to drive the secret lifecycle
91
+ * without touching the real `~/.vellum/` tree. Production callers must
92
+ * omit this argument so the canonical paths (`getProtectedDir()` +
93
+ * `getDataDir()`) are used.
94
+ */
95
+ export interface CapabilityTokenSecretPaths {
96
+ /** Protected-directory secret path (authoritative). */
97
+ secretPath: string;
98
+ /** Legacy workspace-directory secret path (migration source). */
99
+ legacySecretPath: string;
100
+ }
101
+
102
+ /**
103
+ * Load the capability-token secret from disk or generate and persist a new
104
+ * one. Atomically writes with mode 0o600 so the secret is not readable by
105
+ * other users on the same host.
106
+ *
107
+ * Migration: if the secret exists only at the legacy workspace path, copy
108
+ * it into the protected directory and delete the workspace copy so we do
109
+ * not leave security-sensitive material inside `workspace/`.
110
+ *
111
+ * The optional `paths` argument is for unit tests only — production
112
+ * callers must omit it and use the canonical `~/.vellum/protected/` /
113
+ * `~/.vellum/workspace/data/` paths.
114
+ */
115
+ export function loadOrCreateCapabilityTokenSecret(
116
+ paths?: CapabilityTokenSecretPaths,
117
+ ): Buffer {
118
+ const keyPath = paths?.secretPath ?? getSecretPath();
119
+ const legacyPath = paths?.legacySecretPath ?? getLegacySecretPath();
120
+ if (existsSync(keyPath)) {
121
+ try {
122
+ const raw = readFileSync(keyPath);
123
+ if (raw.length === 32) {
124
+ return raw;
125
+ }
126
+ log.warn(
127
+ { keyPath, length: raw.length },
128
+ "capability token secret has unexpected length — regenerating",
129
+ );
130
+ } catch (err) {
131
+ log.warn(
132
+ { err, keyPath },
133
+ "Failed to read capability token secret — regenerating",
134
+ );
135
+ }
136
+ }
137
+
138
+ // Attempt to migrate a legacy workspace-directory secret before we
139
+ // generate a fresh one. If this succeeds we end up with the legacy
140
+ // secret persisted at the protected path and the workspace copy
141
+ // removed, preserving every outstanding token across the upgrade.
142
+ const migrated = migrateLegacyCapabilityTokenSecret(keyPath, legacyPath);
143
+ if (migrated) {
144
+ return migrated;
145
+ }
146
+
147
+ const fresh = randomBytes(32);
148
+ writeSecretAtomic(keyPath, fresh);
149
+ log.info("Capability token secret generated and persisted");
150
+ return fresh;
151
+ }
152
+
153
+ /**
154
+ * Write `secret` to `keyPath` atomically with mode 0o600. Ensures the
155
+ * parent directory exists.
156
+ */
157
+ function writeSecretAtomic(keyPath: string, secret: Buffer): void {
158
+ const dir = dirname(keyPath);
159
+ if (!existsSync(dir)) {
160
+ mkdirSync(dir, { recursive: true });
161
+ }
162
+ const tmpPath = `${keyPath}.tmp.${process.pid}`;
163
+ writeFileSync(tmpPath, secret, { mode: 0o600 });
164
+ renameSync(tmpPath, keyPath);
165
+ try {
166
+ chmodSync(keyPath, 0o600);
167
+ } catch (err) {
168
+ log.warn(
169
+ { err, keyPath },
170
+ "Failed to chmod capability token secret after write",
171
+ );
172
+ }
173
+ }
174
+
175
+ /**
176
+ * If a pre-migration capability token secret exists under the workspace
177
+ * data directory, copy it into the protected directory and remove the
178
+ * workspace copy. Returns the migrated secret if migration ran
179
+ * successfully, or `undefined` if there was nothing to migrate or the
180
+ * migration failed.
181
+ */
182
+ function migrateLegacyCapabilityTokenSecret(
183
+ secretPath: string,
184
+ legacyPath: string,
185
+ ): Buffer | undefined {
186
+ if (!existsSync(legacyPath)) {
187
+ return undefined;
188
+ }
189
+ try {
190
+ const raw = readFileSync(legacyPath);
191
+ if (raw.length !== 32) {
192
+ log.warn(
193
+ { legacyPath, length: raw.length },
194
+ "legacy capability token secret has unexpected length — ignoring",
195
+ );
196
+ return undefined;
197
+ }
198
+ writeSecretAtomic(secretPath, raw);
199
+ try {
200
+ unlinkSync(legacyPath);
201
+ } catch (err) {
202
+ log.warn(
203
+ { err, legacyPath },
204
+ "Failed to remove legacy workspace capability token secret after migration",
205
+ );
206
+ }
207
+ log.info(
208
+ { from: legacyPath, to: secretPath },
209
+ "Migrated capability token secret out of workspace into protected directory",
210
+ );
211
+ return raw;
212
+ } catch (err) {
213
+ log.warn(
214
+ { err, legacyPath },
215
+ "Failed to migrate legacy capability token secret — regenerating",
216
+ );
217
+ return undefined;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Initialize the module-level secret. Called once at daemon startup. Safe
223
+ * to call multiple times — subsequent calls overwrite the cached value
224
+ * (useful in tests that reset state).
225
+ */
226
+ export function initCapabilityTokenSecret(secret: Buffer): void {
227
+ if (secret.length !== 32) {
228
+ throw new Error(
229
+ `capability token secret must be 32 bytes, got ${secret.length}`,
230
+ );
231
+ }
232
+ _secret = secret;
233
+ }
234
+
235
+ /**
236
+ * Test-only helper to inject a deterministic secret.
237
+ */
238
+ export function setCapabilityTokenSecretForTests(secret: Buffer): void {
239
+ _secret = secret;
240
+ }
241
+
242
+ /**
243
+ * Reset the cached secret. Test-only — exposed so test isolation can
244
+ * force a reload from disk.
245
+ */
246
+ export function resetCapabilityTokenSecretForTests(): void {
247
+ _secret = undefined;
248
+ }
249
+
250
+ function getSecret(): Buffer {
251
+ if (_secret) return _secret;
252
+ if (process.env.NODE_ENV === "test") {
253
+ _secret = randomBytes(32);
254
+ return _secret;
255
+ }
256
+ // Lazy load — daemon startup is expected to call
257
+ // `initCapabilityTokenSecret(loadOrCreateCapabilityTokenSecret())` but
258
+ // we fall back to a disk load here so unit tests and early call sites
259
+ // don't have to depend on startup ordering.
260
+ _secret = loadOrCreateCapabilityTokenSecret();
261
+ return _secret;
262
+ }
263
+
264
+ // ---------------------------------------------------------------------------
265
+ // Mint / verify
266
+ // ---------------------------------------------------------------------------
267
+
268
+ const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30 minutes
269
+
270
+ function base64urlEncode(buf: Buffer): string {
271
+ return buf
272
+ .toString("base64")
273
+ .replace(/\+/g, "-")
274
+ .replace(/\//g, "_")
275
+ .replace(/=+$/, "");
276
+ }
277
+
278
+ function base64urlDecode(s: string): Buffer {
279
+ const pad = s.length % 4 === 0 ? 0 : 4 - (s.length % 4);
280
+ const b64 = s.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat(pad);
281
+ return Buffer.from(b64, "base64");
282
+ }
283
+
284
+ function sign(payload: string, secret: Buffer): string {
285
+ return base64urlEncode(createHmac("sha256", secret).update(payload).digest());
286
+ }
287
+
288
+ /**
289
+ * Mint a capability token bound to the `host_browser_command` capability
290
+ * for the given guardian id. Default TTL is 30 minutes.
291
+ */
292
+ export function mintHostBrowserCapability(
293
+ guardianId: string,
294
+ ttlMs: number = DEFAULT_TTL_MS,
295
+ ): CapabilityToken {
296
+ const expiresAt = Date.now() + ttlMs;
297
+ const nonce = randomBytes(16).toString("hex");
298
+ const claims: CapabilityClaims = {
299
+ capability: "host_browser_command",
300
+ guardianId,
301
+ nonce,
302
+ expiresAt,
303
+ };
304
+ const payload = base64urlEncode(Buffer.from(JSON.stringify(claims), "utf8"));
305
+ const sig = sign(payload, getSecret());
306
+ return { token: `${payload}.${sig}`, expiresAt };
307
+ }
308
+
309
+ /**
310
+ * Verify a capability token minted by `mintHostBrowserCapability`.
311
+ *
312
+ * Returns the decoded claims on success or null if the signature is
313
+ * invalid, the payload is malformed, the token has expired, or the bound
314
+ * capability is not `host_browser_command`.
315
+ *
316
+ * Signature comparison uses `timingSafeEqual` to avoid leaking the secret
317
+ * through timing side channels.
318
+ *
319
+ * The `/v1/browser-relay` WebSocket upgrade handler in `http-server.ts`
320
+ * (`handleBrowserRelayUpgrade`) calls this to authenticate self-hosted
321
+ * chrome extensions on the capability-token branch before falling
322
+ * through to the JWT compatibility path. The `/v1/host-browser-result`
323
+ * POST route may also call it (see that route's auth handling) when a
324
+ * result is posted back with a capability-token bearer instead of a
325
+ * guardian-bound JWT.
326
+ */
327
+ export function verifyHostBrowserCapability(
328
+ token: string,
329
+ ): CapabilityClaims | null {
330
+ if (typeof token !== "string") return null;
331
+ const dot = token.indexOf(".");
332
+ if (dot < 0) return null;
333
+ const payload = token.slice(0, dot);
334
+ const sig = token.slice(dot + 1);
335
+ if (!payload || !sig) return null;
336
+
337
+ const expected = sign(payload, getSecret());
338
+ const a = Buffer.from(sig, "utf8");
339
+ const b = Buffer.from(expected, "utf8");
340
+ if (a.length !== b.length) return null;
341
+ if (!timingSafeEqual(a, b)) return null;
342
+
343
+ let claims: CapabilityClaims;
344
+ try {
345
+ claims = JSON.parse(
346
+ base64urlDecode(payload).toString("utf8"),
347
+ ) as CapabilityClaims;
348
+ } catch {
349
+ return null;
350
+ }
351
+
352
+ if (!claims || typeof claims !== "object") return null;
353
+ if (claims.capability !== "host_browser_command") return null;
354
+ if (typeof claims.guardianId !== "string" || claims.guardianId.length === 0) {
355
+ return null;
356
+ }
357
+ if (typeof claims.nonce !== "string" || claims.nonce.length === 0) {
358
+ return null;
359
+ }
360
+ if (typeof claims.expiresAt !== "number" || claims.expiresAt <= Date.now()) {
361
+ return null;
362
+ }
363
+ return claims;
364
+ }
365
+
366
+ // ---------------------------------------------------------------------------
367
+ // Dev-only fallback token file
368
+ // ---------------------------------------------------------------------------
369
+
370
+ /**
371
+ * Path to the dev-pairing fallback token file. The runtime writes a freshly
372
+ * minted capability token to this location on daemon startup so developers
373
+ * can manually pair the chrome extension without wiring the native
374
+ * messaging helper. Production users should pair via the native helper
375
+ * (PRs 7/12/13).
376
+ */
377
+ export function getDaemonTokenFilePath(): string {
378
+ // Always under `~/.vellum/` (not the configurable workspace dir) so the
379
+ // native messaging helper can find it at a fixed path regardless of
380
+ // workspace overrides. This is a dev-only convenience path — production
381
+ // pairing goes through the native messaging flow.
382
+ return join(homedir(), ".vellum", "daemon-token");
383
+ }
384
+
385
+ /**
386
+ * Write a freshly-minted capability token to `~/.vellum/daemon-token` with
387
+ * 0600 permissions. Swallows errors so a failure here never blocks daemon
388
+ * startup — this is a dev-convenience path, not a production auth
389
+ * requirement.
390
+ */
391
+ export function writeDaemonTokenFallback(guardianId: string): void {
392
+ try {
393
+ const { token } = mintHostBrowserCapability(guardianId);
394
+ const filePath = getDaemonTokenFilePath();
395
+ const dir = dirname(filePath);
396
+ if (!existsSync(dir)) {
397
+ mkdirSync(dir, { recursive: true });
398
+ }
399
+ const tmpPath = `${filePath}.tmp.${process.pid}`;
400
+ writeFileSync(tmpPath, token, { mode: 0o600 });
401
+ renameSync(tmpPath, filePath);
402
+ try {
403
+ chmodSync(filePath, 0o600);
404
+ } catch {
405
+ // best-effort
406
+ }
407
+ log.info({ filePath }, "Dev capability token written to daemon-token file");
408
+ } catch (err) {
409
+ log.warn(
410
+ { err },
411
+ "Failed to write dev capability token file; manual pairing still available via /v1/browser-extension-pair",
412
+ );
413
+ }
414
+ }
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { addRule } from "../permissions/trust-store.js";
13
13
  import type { UserDecision } from "../permissions/types.js";
14
+ import { isPermissionControlsV2Enabled } from "../permissions/v2-consent-policy.js";
14
15
  import { getTool } from "../tools/registry.js";
15
16
  import { composeApprovalMessage } from "./approval-message-composer.js";
16
17
  import type {
@@ -22,6 +23,7 @@ import type {
22
23
  import { toApprovalActionOptions } from "./channel-approval-types.js";
23
24
  import {
24
25
  buildDecisionActions,
26
+ buildOneTimeDecisionActions,
25
27
  buildPlainTextFallback,
26
28
  } from "./guardian-decision-types.js";
27
29
  import * as pendingInteractions from "./pending-interactions.js";
@@ -92,9 +94,11 @@ function buildPromptFromApprovalInfo(
92
94
  toolName: info.toolName,
93
95
  });
94
96
 
95
- const decisionActions = buildDecisionActions({
96
- persistentDecisionsAllowed: info.persistentDecisionsAllowed,
97
- });
97
+ const decisionActions = isPermissionControlsV2Enabled()
98
+ ? buildOneTimeDecisionActions()
99
+ : buildDecisionActions({
100
+ persistentDecisionsAllowed: info.persistentDecisionsAllowed,
101
+ });
98
102
  const actions = toApprovalActionOptions(decisionActions);
99
103
  const plainTextFallback = buildPlainTextFallback(promptText, decisionActions);
100
104
 
@@ -138,6 +142,10 @@ export function buildApprovalUIMetadata(
138
142
  * the permission pipeline can activate the appropriate override.
139
143
  */
140
144
  function mapApprovalActionToUserDecision(action: ApprovalAction): UserDecision {
145
+ if (isPermissionControlsV2Enabled()) {
146
+ return action === "reject" ? "deny" : "allow";
147
+ }
148
+
141
149
  switch (action) {
142
150
  case "reject":
143
151
  return "deny";
@@ -182,7 +190,10 @@ export function handleChannelDecision(
182
190
  : pending[0];
183
191
  if (!info) return { applied: false };
184
192
 
185
- if (decision.action === "approve_always") {
193
+ if (
194
+ !isPermissionControlsV2Enabled() &&
195
+ decision.action === "approve_always"
196
+ ) {
186
197
  // Only persist a trust rule when the confirmation explicitly allows persistence
187
198
  // AND provides explicit allowlist/scope options. Without explicit options we
188
199
  // would create a blanket "**"/"everywhere" rule, which is a security risk.
@@ -264,7 +275,9 @@ export function buildGuardianApprovalPrompt(
264
275
  requesterIdentifier,
265
276
  });
266
277
 
267
- const decisionActions = buildDecisionActions({ forGuardianOnBehalf: true });
278
+ const decisionActions = isPermissionControlsV2Enabled()
279
+ ? buildOneTimeDecisionActions()
280
+ : buildDecisionActions({ forGuardianOnBehalf: true });
268
281
  const actions = toApprovalActionOptions(decisionActions);
269
282
  const plainTextFallback = buildPlainTextFallback(promptText, decisionActions);
270
283