@vellumai/assistant 0.4.35 → 0.4.36

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 (239) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +44 -49
  3. package/README.md +32 -20
  4. package/docs/architecture/keychain-broker.md +186 -0
  5. package/docs/architecture/security.md +110 -116
  6. package/docs/runbook-trusted-contacts.md +2 -2
  7. package/docs/skills.md +25 -25
  8. package/package.json +4 -1
  9. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
  10. package/src/__tests__/actor-token-service.test.ts +1 -0
  11. package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
  13. package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
  14. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  15. package/src/__tests__/bundle-scanner.test.ts +1 -1
  16. package/src/__tests__/channel-guardian.test.ts +102 -102
  17. package/src/__tests__/channel-invite-transport.test.ts +155 -256
  18. package/src/__tests__/channel-readiness-routes.test.ts +336 -0
  19. package/src/__tests__/checker.test.ts +6 -6
  20. package/src/__tests__/chrome-cdp.test.ts +350 -0
  21. package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
  22. package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
  23. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
  24. package/src/__tests__/config-loader-migration.test.ts +85 -0
  25. package/src/__tests__/conversation-pairing.test.ts +370 -5
  26. package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
  27. package/src/__tests__/credential-broker-server-use.test.ts +1 -10
  28. package/src/__tests__/credential-security-e2e.test.ts +7 -1
  29. package/src/__tests__/credential-security-invariants.test.ts +14 -20
  30. package/src/__tests__/credential-vault-unit.test.ts +1 -11
  31. package/src/__tests__/credential-vault.test.ts +5 -19
  32. package/src/__tests__/credentials-cli.test.ts +806 -0
  33. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
  34. package/src/__tests__/email-invite-adapter.test.ts +78 -0
  35. package/src/__tests__/email-service-config-fallback.test.ts +102 -0
  36. package/src/__tests__/encrypted-store.test.ts +6 -6
  37. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  38. package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
  39. package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
  40. package/src/__tests__/guardian-outbound-http.test.ts +53 -47
  41. package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
  42. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
  43. package/src/__tests__/handlers-telegram-config.test.ts +8 -2
  44. package/src/__tests__/handlers-twitter-config.test.ts +2 -2
  45. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
  46. package/src/__tests__/ingress-reconcile.test.ts +6 -0
  47. package/src/__tests__/intent-routing.test.ts +23 -4
  48. package/src/__tests__/invite-routes-http.test.ts +12 -0
  49. package/src/__tests__/ipc-snapshot.test.ts +8 -2
  50. package/src/__tests__/keychain-broker-client.test.ts +543 -0
  51. package/src/__tests__/llm-usage-store.test.ts +344 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +2 -2
  53. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  54. package/src/__tests__/migration-transport.test.ts +49 -0
  55. package/src/__tests__/notification-broadcaster.test.ts +205 -5
  56. package/src/__tests__/notification-deep-link.test.ts +365 -1
  57. package/src/__tests__/oauth-connect-handler.test.ts +2 -2
  58. package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
  59. package/src/__tests__/proxy-approval-callback.test.ts +1 -1
  60. package/src/__tests__/recording-handler.test.ts +1 -1
  61. package/src/__tests__/recording-intent-handler.test.ts +6 -1
  62. package/src/__tests__/recording-state-machine.test.ts +1 -1
  63. package/src/__tests__/relay-server.test.ts +9 -1
  64. package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
  65. package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
  66. package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
  67. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
  68. package/src/__tests__/secret-onetime-send.test.ts +8 -2
  69. package/src/__tests__/secure-keys.test.ts +175 -216
  70. package/src/__tests__/session-confirmation-signals.test.ts +1 -1
  71. package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
  72. package/src/__tests__/session-queue.test.ts +2 -1
  73. package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
  74. package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
  75. package/src/__tests__/skill-feature-flags.test.ts +12 -9
  76. package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
  77. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  78. package/src/__tests__/skills.test.ts +34 -4
  79. package/src/__tests__/slack-channel-config.test.ts +2 -2
  80. package/src/__tests__/system-prompt.test.ts +26 -4
  81. package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
  82. package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
  83. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  84. package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
  85. package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
  86. package/src/__tests__/twitter-auth-handler.test.ts +2 -2
  87. package/src/__tests__/twitter-oauth-client.test.ts +1 -1
  88. package/src/__tests__/usage-routes.test.ts +339 -0
  89. package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
  90. package/src/agent/loop.ts +3 -0
  91. package/src/amazon/checkout.ts +0 -1
  92. package/src/approvals/guardian-request-resolvers.ts +9 -1
  93. package/src/bundler/app-bundler.ts +28 -12
  94. package/src/bundler/bundle-scanner.ts +1 -1
  95. package/src/bundler/bundle-signer.ts +3 -3
  96. package/src/bundler/manifest.ts +1 -1
  97. package/src/bundler/signature-verifier.ts +3 -3
  98. package/src/channels/config.ts +1 -1
  99. package/src/cli/AGENTS.md +63 -0
  100. package/src/cli/__tests__/notifications.test.ts +470 -0
  101. package/src/cli/amazon.ts +344 -167
  102. package/src/cli/audit.ts +85 -0
  103. package/src/cli/autonomy.ts +369 -0
  104. package/src/cli/channels.ts +51 -0
  105. package/src/cli/completions.ts +208 -0
  106. package/src/cli/config.ts +220 -0
  107. package/src/cli/contacts.ts +471 -0
  108. package/src/cli/credentials.ts +564 -0
  109. package/src/cli/default-action.ts +14 -0
  110. package/src/cli/dev.ts +131 -0
  111. package/src/cli/doctor.ts +398 -0
  112. package/src/cli/email.ts +491 -0
  113. package/src/cli/influencer.ts +72 -0
  114. package/src/cli/integrations.ts +248 -57
  115. package/src/cli/keys.ts +114 -0
  116. package/src/cli/map.ts +46 -54
  117. package/src/cli/mcp.ts +111 -3
  118. package/src/cli/{config-commands.ts → memory.ts} +133 -242
  119. package/src/cli/notifications.ts +407 -0
  120. package/src/cli/program.ts +65 -0
  121. package/src/cli/reference.ts +48 -0
  122. package/src/cli/sequence.ts +154 -0
  123. package/src/cli/sessions.ts +262 -0
  124. package/src/cli/trust.ts +177 -0
  125. package/src/cli/twitter.ts +323 -106
  126. package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
  127. package/src/config/bundled-skills/amazon/SKILL.md +2 -2
  128. package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
  129. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
  130. package/src/config/bundled-skills/contacts/SKILL.md +178 -10
  131. package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
  132. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
  133. package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
  134. package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
  135. package/src/config/bundled-tool-registry.ts +2 -0
  136. package/src/config/core-schema.ts +7 -0
  137. package/src/config/feature-flag-registry.json +16 -0
  138. package/src/config/loader.ts +26 -0
  139. package/src/config/schema.ts +4 -0
  140. package/src/config/skill-state.ts +0 -13
  141. package/src/config/system-prompt.ts +27 -0
  142. package/src/contacts/contact-store.ts +25 -0
  143. package/src/daemon/computer-use-session.ts +1 -1
  144. package/src/daemon/handlers/apps.ts +1 -0
  145. package/src/daemon/handlers/config-channels.ts +3 -3
  146. package/src/daemon/handlers/config-dispatch.ts +29 -0
  147. package/src/daemon/handlers/config-inbox.ts +4 -3
  148. package/src/daemon/handlers/config.ts +3 -43
  149. package/src/daemon/handlers/contacts.ts +34 -0
  150. package/src/daemon/handlers/index.ts +17 -3
  151. package/src/daemon/handlers/session-user-message.ts +7 -0
  152. package/src/daemon/handlers/sessions.ts +21 -2
  153. package/src/daemon/handlers/shared.ts +17 -0
  154. package/src/daemon/ipc-contract/apps.ts +2 -0
  155. package/src/daemon/ipc-contract/computer-use.ts +9 -0
  156. package/src/daemon/ipc-contract/contacts.ts +3 -3
  157. package/src/daemon/ipc-contract/inbox.ts +2 -0
  158. package/src/daemon/ipc-contract/messages.ts +4 -0
  159. package/src/daemon/ipc-contract/sessions.ts +8 -0
  160. package/src/daemon/ipc-contract-inventory.json +1 -0
  161. package/src/daemon/lifecycle.ts +0 -5
  162. package/src/daemon/ride-shotgun-handler.ts +139 -25
  163. package/src/daemon/session-agent-loop-handlers.ts +100 -0
  164. package/src/daemon/session-agent-loop.ts +72 -0
  165. package/src/daemon/session-tool-setup.ts +7 -0
  166. package/src/daemon/session.ts +23 -1
  167. package/src/daemon/tool-side-effects.ts +39 -1
  168. package/src/email/service.ts +59 -2
  169. package/src/index.ts +2 -60
  170. package/src/mcp/mcp-oauth-provider.ts +90 -8
  171. package/src/media/app-icon-generator.ts +86 -0
  172. package/src/memory/db-init.ts +11 -0
  173. package/src/memory/llm-usage-store.ts +186 -0
  174. package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
  175. package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
  176. package/src/memory/migrations/index.ts +2 -0
  177. package/src/memory/schema-migration.ts +1 -0
  178. package/src/memory/shared-app-links-store.ts +1 -1
  179. package/src/messaging/registry.ts +27 -0
  180. package/src/notifications/README.md +79 -70
  181. package/src/notifications/broadcaster.ts +2 -1
  182. package/src/notifications/conversation-pairing.ts +147 -13
  183. package/src/notifications/copy-composer.ts +7 -3
  184. package/src/notifications/destination-resolver.ts +14 -1
  185. package/src/notifications/emit-signal.ts +3 -2
  186. package/src/notifications/signal.ts +105 -1
  187. package/src/notifications/types.ts +16 -0
  188. package/src/permissions/checker.ts +29 -3
  189. package/src/permissions/prompter.ts +11 -3
  190. package/src/runtime/access-request-helper.ts +2 -1
  191. package/src/runtime/auth/route-policy.ts +7 -1
  192. package/src/runtime/channel-invite-transport.ts +40 -63
  193. package/src/runtime/channel-invite-transports/email.ts +13 -39
  194. package/src/runtime/channel-invite-transports/slack.ts +5 -34
  195. package/src/runtime/channel-invite-transports/sms.ts +8 -29
  196. package/src/runtime/channel-invite-transports/telegram.ts +69 -28
  197. package/src/runtime/channel-invite-transports/voice.ts +0 -7
  198. package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
  199. package/src/runtime/channel-readiness-service.ts +202 -45
  200. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
  201. package/src/runtime/guardian-outbound-actions.ts +8 -5
  202. package/src/runtime/http-server.ts +2 -0
  203. package/src/runtime/invite-instruction-generator.ts +178 -0
  204. package/src/runtime/invite-service.ts +22 -25
  205. package/src/runtime/migrations/migration-transport.ts +13 -0
  206. package/src/runtime/routes/app-routes.ts +1 -1
  207. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
  208. package/src/runtime/routes/channel-readiness-routes.ts +30 -11
  209. package/src/runtime/routes/contact-routes.ts +54 -26
  210. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
  211. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
  212. package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
  213. package/src/runtime/routes/integration-routes.ts +1 -1
  214. package/src/runtime/routes/invite-routes.ts +1 -1
  215. package/src/runtime/routes/secret-routes.ts +31 -7
  216. package/src/runtime/routes/twilio-routes.ts +32 -1
  217. package/src/runtime/routes/usage-routes.ts +114 -0
  218. package/src/runtime/tool-grant-request-helper.ts +2 -1
  219. package/src/security/encrypted-store.ts +9 -5
  220. package/src/security/keychain-broker-client.ts +393 -0
  221. package/src/security/secure-keys.ts +106 -321
  222. package/src/tools/apps/executors.ts +73 -0
  223. package/src/tools/browser/auto-navigate.ts +15 -6
  224. package/src/tools/browser/chrome-cdp.ts +211 -0
  225. package/src/tools/browser/network-recorder.test.ts +83 -0
  226. package/src/tools/browser/network-recorder.ts +8 -7
  227. package/src/tools/browser/x-auto-navigate.ts +12 -6
  228. package/src/tools/credentials/policy-types.ts +24 -0
  229. package/src/tools/credentials/vault.ts +22 -27
  230. package/src/tools/network/script-proxy/session-manager.ts +47 -3
  231. package/src/tools/permission-checker.ts +1 -0
  232. package/src/tools/types.ts +2 -0
  233. package/src/tools/ui-surface/definitions.ts +1 -2
  234. package/src/tools/watch/watch-state.ts +2 -0
  235. package/src/__tests__/key-migration.test.ts +0 -240
  236. package/src/__tests__/keychain.test.ts +0 -286
  237. package/src/cli/core-commands.ts +0 -899
  238. package/src/security/keychain-to-encrypted-migration.ts +0 -66
  239. package/src/security/keychain.ts +0 -490
@@ -4,7 +4,7 @@ Permission, trust, and credential-security architecture details.
4
4
 
5
5
  ## Permission and Trust Security Model
6
6
 
7
- The permission system controls which tool actions the agent can execute without explicit user approval. It supports three operating modes (`workspace`, `strict`, and `legacy`), execution-target-scoped trust rules, and risk-based escalation to provide defense-in-depth against unintended or malicious tool execution.
7
+ The permission system controls which tool actions the agent can execute without explicit user approval. It supports two operating modes (`workspace` and `strict`), execution-target-scoped trust rules, and risk-based escalation to provide defense-in-depth against unintended or malicious tool execution.
8
8
 
9
9
  ### Permission Evaluation Flow
10
10
 
@@ -32,50 +32,46 @@ graph TB
32
32
  RISK_FALLBACK_WS -->|"Low"| AUTO_WS_LOW["decision: allow<br/>Low risk auto-allow"]
33
33
  RISK_FALLBACK_WS -->|"Medium"| PROMPT_WS_MED["decision: prompt"]
34
34
  RISK_FALLBACK_WS -->|"High"| PROMPT_WS_HIGH["decision: prompt"]
35
- NO_MATCH -->|"legacy mode"| RISK_FALLBACK{"Risk level?"}
36
- RISK_FALLBACK -->|"Low"| AUTO_LOW["decision: allow<br/>Low risk auto-allow"]
37
- RISK_FALLBACK -->|"Medium"| PROMPT_MED["decision: prompt"]
38
- RISK_FALLBACK -->|"High"| PROMPT_HIGH2["decision: prompt"]
39
35
  ```
40
36
 
41
- ### Permission Modes: Workspace, Strict, and Legacy
37
+ ### Permission Modes: Workspace and Strict
42
38
 
43
- The `permissions.mode` config option (`workspace`, `strict`, or `legacy`) controls the default behavior when no trust rule matches a tool invocation. The default is `workspace`.
39
+ The `permissions.mode` config option (`workspace` or `strict`) controls the default behavior when no trust rule matches a tool invocation. The default is `workspace`.
44
40
 
45
- | Behavior | Workspace mode (default) | Strict mode | Legacy mode (deprecated) |
46
- |---|---|---|---|
47
- | Workspace-scoped ops with no matching rule | Auto-allowed | Prompted | Auto-allowed (low risk) |
48
- | Non-workspace low-risk tools with no matching rule | Auto-allowed | Prompted | Auto-allowed |
49
- | Medium-risk tools with no matching rule | Prompted | Prompted | Prompted |
50
- | High-risk tools with no matching rule | Prompted | Prompted | Prompted |
51
- | `skill_load` with no matching rule | Prompted | Prompted | Auto-allowed (low risk) |
52
- | `skill_load` with system default rule | Auto-allowed (`skill_load:*` at priority 100) | Auto-allowed (`skill_load:*` at priority 100) | Auto-allowed (`skill_load:*` at priority 100) |
53
- | `browser_*` skill tools with system default rules | Auto-allowed (priority 100 allow rules) | Auto-allowed (priority 100 allow rules) | Auto-allowed (priority 100 allow rules) |
54
- | Skill-origin tools with no matching rule | Prompted | Prompted | Prompted |
55
- | Allow rules for non-high-risk tools | Auto-allowed | Auto-allowed | Auto-allowed |
56
- | Allow rules with `allowHighRisk: true` | Auto-allowed (even high risk) | Auto-allowed (even high risk) | Auto-allowed (even high risk) |
57
- | Deny rules | Blocked | Blocked | Blocked |
41
+ | Behavior | Workspace mode (default) | Strict mode |
42
+ | -------------------------------------------------- | --------------------------------------------- | --------------------------------------------- |
43
+ | Workspace-scoped ops with no matching rule | Auto-allowed | Prompted |
44
+ | Non-workspace low-risk tools with no matching rule | Auto-allowed | Prompted |
45
+ | Medium-risk tools with no matching rule | Prompted | Prompted |
46
+ | High-risk tools with no matching rule | Prompted | Prompted |
47
+ | `skill_load` with no matching rule | Prompted | Prompted |
48
+ | `skill_load` with system default rule | Auto-allowed (`skill_load:*` at priority 100) | Auto-allowed (`skill_load:*` at priority 100) |
49
+ | `browser_*` skill tools with system default rules | Auto-allowed (priority 100 allow rules) | Auto-allowed (priority 100 allow rules) |
50
+ | Skill-origin tools with no matching rule | Prompted | Prompted |
51
+ | Allow rules for non-high-risk tools | Auto-allowed | Auto-allowed |
52
+ | Allow rules with `allowHighRisk: true` | Auto-allowed (even high risk) | Auto-allowed (even high risk) |
53
+ | Deny rules | Blocked | Blocked |
58
54
 
59
55
  **Workspace mode** (default) auto-allows operations scoped to the workspace (file reads/writes/edits within the workspace directory, sandboxed bash) without prompting. Host operations, network requests, and operations outside the workspace still follow the normal approval flow. Explicit deny and ask rules override auto-allow.
60
56
 
61
57
  **Strict mode** is designed for security-conscious deployments where every tool action must have an explicit matching rule in the trust store. It eliminates implicit auto-allow for any risk level, ensuring the user has consciously approved each class of tool usage.
62
58
 
63
- **Legacy mode** (deprecated) auto-allows all low-risk tools regardless of scope. It is deprecated and will be removed in a future release. A one-time runtime warning is emitted when legacy mode is active. Users should migrate to `workspace` (default) or `strict`.
59
+ > **Migration note:** Existing config files with `permissions.mode = "legacy"` are automatically migrated to `workspace` during config loading. The `legacy` value is not a supported steady-state mode.
64
60
 
65
61
  ### Trust Rules (v3 Schema)
66
62
 
67
63
  Rules are stored in `~/.vellum/protected/trust.json` with version `3`. Each rule can include the following fields:
68
64
 
69
- | Field | Type | Purpose |
70
- |---|---|---|
71
- | `id` | `string` | Unique identifier (UUID for user rules, `default:*` for system defaults) |
72
- | `tool` | `string` | Tool name to match (e.g., `bash`, `file_write`, `skill_load`) |
73
- | `pattern` | `string` | Minimatch glob pattern for the command/target string |
74
- | `scope` | `string` | Path prefix or `everywhere` — restricts where the rule applies |
75
- | `decision` | `allow \| deny \| ask` | What to do when the rule matches |
76
- | `priority` | `number` | Higher priority wins; deny wins ties at equal priority |
77
- | `executionTarget` | `string?` | `sandbox` or `host` — restricts by execution context |
78
- | `allowHighRisk` | `boolean?` | When true, auto-allows even high-risk invocations |
65
+ | Field | Type | Purpose |
66
+ | ----------------- | ---------------------- | ------------------------------------------------------------------------ |
67
+ | `id` | `string` | Unique identifier (UUID for user rules, `default:*` for system defaults) |
68
+ | `tool` | `string` | Tool name to match (e.g., `bash`, `file_write`, `skill_load`) |
69
+ | `pattern` | `string` | Minimatch glob pattern for the command/target string |
70
+ | `scope` | `string` | Path prefix or `everywhere` — restricts where the rule applies |
71
+ | `decision` | `allow \| deny \| ask` | What to do when the rule matches |
72
+ | `priority` | `number` | Higher priority wins; deny wins ties at equal priority |
73
+ | `executionTarget` | `string?` | `sandbox` or `host` — restricts by execution context |
74
+ | `allowHighRisk` | `boolean?` | When true, auto-allows even high-risk invocations |
79
75
 
80
76
  Missing optional fields act as wildcards. A rule with no `executionTarget` matches any target.
81
77
 
@@ -83,16 +79,16 @@ Missing optional fields act as wildcards. A rule with no `executionTarget` match
83
79
 
84
80
  The `classifyRisk()` function determines the risk level for each tool invocation:
85
81
 
86
- | Tool | Risk level | Notes |
87
- |---|---|---|
88
- | `file_read`, `web_search`, `skill_load` | Low | Read-only or informational |
89
- | `file_write`, `file_edit` | Medium (default) | Filesystem mutations |
90
- | `file_write`, `file_edit` targeting skill source paths | **High** | `isSkillSourcePath()` detects managed/bundled/workspace/extra skill roots |
91
- | `host_file_write`, `host_file_edit` targeting skill source paths | **High** | Same path classification, host variant |
92
- | `bash`, `host_bash` | Varies | Parsed via tree-sitter: low-risk programs = Low, high-risk programs = High, unknown = Medium |
93
- | `scaffold_managed_skill`, `delete_managed_skill` | High | Skill lifecycle mutations always high-risk |
94
- | `evaluate_typescript_code` | High | Arbitrary code execution |
95
- | Skill-origin tools with no matching rule | Prompted regardless of risk | Even Low-risk skill tools default to `ask` |
82
+ | Tool | Risk level | Notes |
83
+ | ---------------------------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------- |
84
+ | `file_read`, `web_search`, `skill_load` | Low | Read-only or informational |
85
+ | `file_write`, `file_edit` | Medium (default) | Filesystem mutations |
86
+ | `file_write`, `file_edit` targeting skill source paths | **High** | `isSkillSourcePath()` detects managed/bundled/workspace/extra skill roots |
87
+ | `host_file_write`, `host_file_edit` targeting skill source paths | **High** | Same path classification, host variant |
88
+ | `bash`, `host_bash` | Varies | Parsed via tree-sitter: low-risk programs = Low, high-risk programs = High, unknown = Medium |
89
+ | `scaffold_managed_skill`, `delete_managed_skill` | High | Skill lifecycle mutations always high-risk |
90
+ | `evaluate_typescript_code` | High | Arbitrary code execution |
91
+ | Skill-origin tools with no matching rule | Prompted regardless of risk | Even Low-risk skill tools default to `ask` |
96
92
 
97
93
  The escalation of skill source file mutations to High risk is a privilege-escalation defense: modifying skill source code could grant the agent new capabilities, so such operations always require explicit approval.
98
94
 
@@ -104,20 +100,20 @@ The `skill_load` tool generates version-aware command candidates for rule matchi
104
100
  2. `skill_load:<skill-id>` — matches any-version rules
105
101
  3. `skill_load:<raw-selector>` — matches the raw user-provided selector
106
102
 
107
- In strict mode, `skill_load` without a matching rule is always prompted. In legacy mode, it is auto-allowed as a Low-risk tool. The allowlist options presented to the user include both version-specific and any-version patterns. Note: the system default allow rule `skill_load:*` (priority 100) now globally allows all skill loads in both modes (see "System Default Allow Rules" below).
103
+ In strict mode, `skill_load` without a matching rule is always prompted. The allowlist options presented to the user include both version-specific and any-version patterns. Note: the system default allow rule `skill_load:*` (priority 100) now globally allows all skill loads in both modes (see "System Default Allow Rules" below).
108
104
 
109
105
  ### Starter Approval Bundle
110
106
 
111
107
  The starter bundle is an opt-in set of low-risk allow rules that reduces prompt noise, particularly in strict mode. It covers read-only tools that never mutate the filesystem or execute arbitrary code:
112
108
 
113
- | Rule | Tool | Pattern |
114
- |---|---|---|
115
- | `file_read` | `file_read` | `file_read:**` |
116
- | `glob` | `glob` | `glob:**` |
117
- | `grep` | `grep` | `grep:**` |
109
+ | Rule | Tool | Pattern |
110
+ | ---------------- | ---------------- | ------------------- |
111
+ | `file_read` | `file_read` | `file_read:**` |
112
+ | `glob` | `glob` | `glob:**` |
113
+ | `grep` | `grep` | `grep:**` |
118
114
  | `list_directory` | `list_directory` | `list_directory:**` |
119
- | `web_search` | `web_search` | `web_search:**` |
120
- | `web_fetch` | `web_fetch` | `web_fetch:**` |
115
+ | `web_search` | `web_search` | `web_search:**` |
116
+ | `web_fetch` | `web_fetch` | `web_fetch:**` |
121
117
 
122
118
  Acceptance is idempotent and persisted as `starterBundleAccepted: true` in `trust.json`. Rules are seeded at priority 90 (below user rules at 100, above system defaults at 50).
123
119
 
@@ -125,21 +121,21 @@ Acceptance is idempotent and persisted as `starterBundleAccepted: true` in `trus
125
121
 
126
122
  In addition to the opt-in starter bundle, the permission system seeds unconditional default allow rules at priority 100 for two categories:
127
123
 
128
- | Rule ID | Tool | Pattern | Rationale |
129
- |---|---|---|---|
130
- | `default:allow-skill_load-global` | `skill_load` | `skill_load:*` | Loading any skill is globally allowed — no prompt for activating bundled, managed, or workspace skills |
131
- | `default:allow-browser_navigate-global` | `browser_navigate` | `browser_navigate:*` | Browser tools migrated from core to the bundled `browser` skill; default allow preserves frictionless UX |
132
- | `default:allow-browser_snapshot-global` | `browser_snapshot` | `browser_snapshot:*` | (same) |
133
- | `default:allow-browser_screenshot-global` | `browser_screenshot` | `browser_screenshot:*` | (same) |
134
- | `default:allow-browser_close-global` | `browser_close` | `browser_close:*` | (same) |
135
- | `default:allow-browser_click-global` | `browser_click` | `browser_click:*` | (same) |
136
- | `default:allow-browser_type-global` | `browser_type` | `browser_type:*` | (same) |
137
- | `default:allow-browser_press_key-global` | `browser_press_key` | `browser_press_key:*` | (same) |
138
- | `default:allow-browser_wait_for-global` | `browser_wait_for` | `browser_wait_for:*` | (same) |
139
- | `default:allow-browser_extract-global` | `browser_extract` | `browser_extract:*` | (same) |
140
- | `default:allow-browser_fill_credential-global` | `browser_fill_credential` | `browser_fill_credential:*` | (same) |
141
-
142
- These rules are emitted by `getDefaultRuleTemplates()` in `assistant/src/permissions/defaults.ts`. Because they use priority 100 (equal to user rules), they take effect in both strict and legacy modes. The `skill_load` rule means skill activation never prompts; the `browser_*` rules mean the browser skill's tools behave identically to the old core `headless-browser` tool from a permission standpoint.
124
+ | Rule ID | Tool | Pattern | Rationale |
125
+ | ---------------------------------------------- | ------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------- |
126
+ | `default:allow-skill_load-global` | `skill_load` | `skill_load:*` | Loading any skill is globally allowed — no prompt for activating bundled, managed, or workspace skills |
127
+ | `default:allow-browser_navigate-global` | `browser_navigate` | `browser_navigate:*` | Browser tools migrated from core to the bundled `browser` skill; default allow preserves frictionless UX |
128
+ | `default:allow-browser_snapshot-global` | `browser_snapshot` | `browser_snapshot:*` | (same) |
129
+ | `default:allow-browser_screenshot-global` | `browser_screenshot` | `browser_screenshot:*` | (same) |
130
+ | `default:allow-browser_close-global` | `browser_close` | `browser_close:*` | (same) |
131
+ | `default:allow-browser_click-global` | `browser_click` | `browser_click:*` | (same) |
132
+ | `default:allow-browser_type-global` | `browser_type` | `browser_type:*` | (same) |
133
+ | `default:allow-browser_press_key-global` | `browser_press_key` | `browser_press_key:*` | (same) |
134
+ | `default:allow-browser_wait_for-global` | `browser_wait_for` | `browser_wait_for:*` | (same) |
135
+ | `default:allow-browser_extract-global` | `browser_extract` | `browser_extract:*` | (same) |
136
+ | `default:allow-browser_fill_credential-global` | `browser_fill_credential` | `browser_fill_credential:*` | (same) |
137
+
138
+ These rules are emitted by `getDefaultRuleTemplates()` in `assistant/src/permissions/defaults.ts`. Because they use priority 100 (equal to user rules), they take effect in both workspace and strict modes. The `skill_load` rule means skill activation never prompts; the `browser_*` rules mean the browser skill's tools behave identically to the old core `headless-browser` tool from a permission standpoint.
143
139
 
144
140
  ### Shell Command Identity and Allowlist Options
145
141
 
@@ -160,14 +156,14 @@ For `bash` and `host_bash` tool invocations, the permission system uses parser-d
160
156
 
161
157
  When a permission prompt is sent to the client (via `confirmation_request` IPC message), it includes:
162
158
 
163
- | Field | Content |
164
- |---|---|
165
- | `toolName` | The tool being invoked |
166
- | `input` | Redacted tool input (sensitive fields removed) |
167
- | `riskLevel` | `low`, `medium`, or `high` |
168
- | `executionTarget` | `sandbox` or `host` — where the action will execute |
169
- | `allowlistOptions` | Suggested patterns for "always allow" rules |
170
- | `scopeOptions` | Suggested scopes for rule persistence |
159
+ | Field | Content |
160
+ | ------------------ | --------------------------------------------------- |
161
+ | `toolName` | The tool being invoked |
162
+ | `input` | Redacted tool input (sensitive fields removed) |
163
+ | `riskLevel` | `low`, `medium`, or `high` |
164
+ | `executionTarget` | `sandbox` or `host` — where the action will execute |
165
+ | `allowlistOptions` | Suggested patterns for "always allow" rules |
166
+ | `scopeOptions` | Suggested scopes for rule persistence |
171
167
 
172
168
  The user can respond with: `allow` (one-time), `always_allow` (create allow rule), `always_allow_high_risk` (create allow rule with `allowHighRisk: true`), `deny` (one-time), or `always_deny` (create deny rule).
173
169
 
@@ -177,19 +173,19 @@ File tool candidates include canonical (symlink-resolved) absolute paths via `no
177
173
 
178
174
  ### Key Source Files
179
175
 
180
- | File | Role |
181
- |---|---|
182
- | `assistant/src/permissions/types.ts` | `TrustRule`, `PolicyContext`, `RiskLevel`, `UserDecision` types |
183
- | `assistant/src/permissions/checker.ts` | `classifyRisk()`, `check()`, `buildCommandCandidates()`, allowlist/scope generation |
176
+ | File | Role |
177
+ | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
178
+ | `assistant/src/permissions/types.ts` | `TrustRule`, `PolicyContext`, `RiskLevel`, `UserDecision` types |
179
+ | `assistant/src/permissions/checker.ts` | `classifyRisk()`, `check()`, `buildCommandCandidates()`, allowlist/scope generation |
184
180
  | `assistant/src/permissions/shell-identity.ts` | `analyzeShellCommand()`, `deriveShellActionKeys()`, `buildShellCommandCandidates()`, `buildShellAllowlistOptions()` — parser-based shell command identity and action key derivation |
185
- | `assistant/src/permissions/trust-store.ts` | Rule persistence, `findHighestPriorityRule()`, execution-target matching, starter bundle |
186
- | `assistant/src/permissions/prompter.ts` | IPC prompt flow: `confirmation_request` → `confirmation_response` |
187
- | `assistant/src/permissions/defaults.ts` | Default rule templates (system ask rules for host tools, CU, etc.) |
188
- | `assistant/src/skills/version-hash.ts` | `computeSkillVersionHash()` — deterministic SHA-256 of skill source files |
189
- | `assistant/src/skills/path-classifier.ts` | `isSkillSourcePath()`, `normalizeFilePath()`, skill root detection |
190
- | `assistant/src/config/schema.ts` | `PermissionsConfigSchema` — `permissions.mode` (`workspace` / `strict` / `legacy`) |
191
- | `assistant/src/tools/executor.ts` | `ToolExecutor` — orchestrates risk classification, permission check, and execution |
192
- | `assistant/src/daemon/handlers/config.ts` | `handleToolPermissionSimulate()` — dry-run simulation handler |
181
+ | `assistant/src/permissions/trust-store.ts` | Rule persistence, `findHighestPriorityRule()`, execution-target matching, starter bundle |
182
+ | `assistant/src/permissions/prompter.ts` | IPC prompt flow: `confirmation_request` → `confirmation_response` |
183
+ | `assistant/src/permissions/defaults.ts` | Default rule templates (system ask rules for host tools, CU, etc.) |
184
+ | `assistant/src/skills/version-hash.ts` | `computeSkillVersionHash()` — deterministic SHA-256 of skill source files |
185
+ | `assistant/src/skills/path-classifier.ts` | `isSkillSourcePath()`, `normalizeFilePath()`, skill root detection |
186
+ | `assistant/src/config/schema.ts` | `PermissionsConfigSchema` — `permissions.mode` (`workspace` / `strict`) |
187
+ | `assistant/src/tools/executor.ts` | `ToolExecutor` — orchestrates risk classification, permission check, and execution |
188
+ | `assistant/src/daemon/handlers/config.ts` | `handleToolPermissionSimulate()` — dry-run simulation handler |
193
189
 
194
190
  ### Permission Simulation (Tool Permission Tester)
195
191
 
@@ -206,7 +202,6 @@ The `tool_permission_simulate` IPC message lets clients dry-run a tool invocatio
206
202
 
207
203
  ---
208
204
 
209
-
210
205
  ---
211
206
 
212
207
  ## Credential Storage and Secret Security
@@ -293,25 +288,25 @@ The `allowOneTimeSend` config gate (default: `false`) enables a secondary "Send
293
288
 
294
289
  ### Storage Layout
295
290
 
296
- | Component | Location | What it stores |
297
- |-----------|----------|----------------|
298
- | Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential:{service}:{field}`. Falls back to encrypted file backend on Linux/headless or when Keychain is unavailable. |
299
- | Credential metadata | `~/.vellum/workspace/data/credentials/metadata.json` | Service, field, label, policy (allowedTools, allowedDomains), timestamps |
300
- | Config | `~/.vellum/workspace/config.*` | `secretDetection` settings: enabled, action, entropyThreshold, allowOneTimeSend |
291
+ | Component | Location | What it stores |
292
+ | ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
293
+ | Secret values | macOS Keychain (primary) or encrypted file fallback | Encrypted credential values keyed as `credential:{service}:{field}`. Falls back to encrypted file backend on Linux/headless or when Keychain is unavailable. |
294
+ | Credential metadata | `~/.vellum/workspace/data/credentials/metadata.json` | Service, field, label, policy (allowedTools, allowedDomains), timestamps |
295
+ | Config | `~/.vellum/workspace/config.*` | `secretDetection` settings: enabled, action, entropyThreshold, allowOneTimeSend |
301
296
 
302
297
  ### Key Files
303
298
 
304
- | File | Role |
305
- |------|------|
306
- | `assistant/src/tools/credentials/vault.ts` | `credential_store` tool — store, list, delete, prompt actions |
307
- | `assistant/src/security/secure-keys.ts` | Keychain read/write via `/usr/bin/security` CLI |
308
- | `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
309
- | `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
310
- | `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
311
- | `assistant/src/permissions/secret-prompter.ts` | IPC secret_request/secret_response flow |
312
- | `assistant/src/security/secret-scanner.ts` | Regex + entropy-based secret detection |
313
- | `assistant/src/security/secret-ingress.ts` | Inbound message secret blocking |
314
- | `clients/macos/.../SecretPromptManager.swift` | Floating panel UI for secure credential entry |
299
+ | File | Role |
300
+ | ---------------------------------------------------- | --------------------------------------------------------------------- |
301
+ | `assistant/src/tools/credentials/vault.ts` | `credential_store` tool — store, list, delete, prompt actions |
302
+ | `assistant/src/security/secure-keys.ts` | Keychain read/write via `/usr/bin/security` CLI |
303
+ | `assistant/src/tools/credentials/metadata-store.ts` | JSON file metadata CRUD for credential records |
304
+ | `assistant/src/tools/credentials/broker.ts` | Brokered credential access with policy enforcement and transient send |
305
+ | `assistant/src/tools/credentials/policy-validate.ts` | Policy input validation (allowedTools, allowedDomains) |
306
+ | `assistant/src/permissions/secret-prompter.ts` | IPC secret_request/secret_response flow |
307
+ | `assistant/src/security/secret-scanner.ts` | Regex + entropy-based secret detection |
308
+ | `assistant/src/security/secret-ingress.ts` | Inbound message secret blocking |
309
+ | `clients/macos/.../SecretPromptManager.swift` | Floating panel UI for secure credential entry |
315
310
 
316
311
  ---
317
312
 
@@ -323,9 +318,9 @@ Scoped approval grants are a channel-agnostic primitive that allows a guardian's
323
318
 
324
319
  Two scope modes exist:
325
320
 
326
- | Mode | Key fields | Use case |
327
- |------|-----------|----------|
328
- | `request_id` | `requestId` | Grant is bound to a specific pending confirmation request. Consumed by matching the request ID. |
321
+ | Mode | Key fields | Use case |
322
+ | ---------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
323
+ | `request_id` | `requestId` | Grant is bound to a specific pending confirmation request. Consumed by matching the request ID. |
329
324
  | `tool_signature` | `toolName` + `inputDigest` | Grant is bound to a specific tool invocation identified by tool name and a canonical SHA-256 digest of the input. Consumed by matching both fields plus optional context constraints. |
330
325
 
331
326
  ### Lifecycle Flow
@@ -376,22 +371,21 @@ sequenceDiagram
376
371
 
377
372
  ### Key Source Files
378
373
 
379
- | File | Role |
380
- |------|------|
381
- | `assistant/src/memory/scoped-approval-grants.ts` | CRUD, atomic CAS consume, expiry sweep, context-based revocation |
382
- | `assistant/src/memory/migrations/033-scoped-approval-grants.ts` | SQLite schema migration for the `scoped_approval_grants` table |
383
- | `assistant/src/security/tool-approval-digest.ts` | Canonical JSON serialization + SHA-256 digest for tool signatures |
374
+ | File | Role |
375
+ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------- |
376
+ | `assistant/src/memory/scoped-approval-grants.ts` | CRUD, atomic CAS consume, expiry sweep, context-based revocation |
377
+ | `assistant/src/memory/migrations/033-scoped-approval-grants.ts` | SQLite schema migration for the `scoped_approval_grants` table |
378
+ | `assistant/src/security/tool-approval-digest.ts` | Canonical JSON serialization + SHA-256 digest for tool signatures |
384
379
  | `assistant/src/runtime/routes/guardian-approval-interception.ts` | Grant minting on guardian approve_once decisions (`tryMintToolApprovalGrant`) |
385
- | `assistant/src/calls/voice-session-bridge.ts` | Voice consumer: checks and consumes grants before auto-denying |
380
+ | `assistant/src/calls/voice-session-bridge.ts` | Voice consumer: checks and consumes grants before auto-denying |
386
381
 
387
382
  ### Test Coverage
388
383
 
389
- | Test file | Scenarios covered |
390
- |-----------|-------------------|
391
- | `assistant/src/__tests__/scoped-approval-grants.test.ts` | Store CRUD, request_id consume, tool_signature consume, expiry, revocation, digest stability |
392
- | `assistant/src/__tests__/voice-scoped-grant-consumer.test.ts` | Voice bridge integration: grant-allowed, no-grant-denied, tool-mismatch, guardian-bypass, one-time-use, revocation on call end |
393
- | `assistant/src/__tests__/guardian-grant-minting.test.ts` | Grant minting: callback/engine/legacy paths, informational-skip, reject-skip, identity-mismatch, stale-skip, TTL verification |
384
+ | Test file | Scenarios covered |
385
+ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
386
+ | `assistant/src/__tests__/scoped-approval-grants.test.ts` | Store CRUD, request_id consume, tool_signature consume, expiry, revocation, digest stability |
387
+ | `assistant/src/__tests__/voice-scoped-grant-consumer.test.ts` | Voice bridge integration: grant-allowed, no-grant-denied, tool-mismatch, guardian-bypass, one-time-use, revocation on call end |
388
+ | `assistant/src/__tests__/guardian-grant-minting.test.ts` | Grant minting: callback/engine/legacy paths, informational-skip, reject-skip, identity-mismatch, stale-skip, TTL verification |
394
389
  | `assistant/src/__tests__/scoped-grant-security-matrix.test.ts` | Security matrix: requester identity mismatch, concurrent CAS, persistence across restart, fail-closed default, cross-scope invariants |
395
390
 
396
391
  ---
397
-
@@ -142,7 +142,7 @@ CHANNEL_ID=$(curl -s "$BASE/v1/contacts?channelType=telegram" \
142
142
  -H "Authorization: Bearer $TOKEN" | jq -r '.contacts[] | select(.channels[] | select(.externalUserId == "TARGET_USER_ID")) | .channels[] | select(.externalUserId == "TARGET_USER_ID") | .id')
143
143
 
144
144
  # Revoke with reason
145
- curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
145
+ curl -s -X PATCH "$BASE/v1/contact-channels/$CHANNEL_ID" \
146
146
  -H "Authorization: Bearer $TOKEN" \
147
147
  -H "Content-Type: application/json" \
148
148
  -d '{"status": "revoked", "reason": "Revoked by operator"}' | jq
@@ -153,7 +153,7 @@ curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
153
153
  Blocking prevents the contact from re-entering the flow without explicit unblocking.
154
154
 
155
155
  ```bash
156
- curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
156
+ curl -s -X PATCH "$BASE/v1/contact-channels/$CHANNEL_ID" \
157
157
  -H "Authorization: Bearer $TOKEN" \
158
158
  -H "Content-Type: application/json" \
159
159
  -d '{"status": "blocked", "reason": "Blocked by operator"}' | jq
package/docs/skills.md CHANGED
@@ -12,20 +12,20 @@ Because skills can introduce arbitrary tool behavior, they are subject to strict
12
12
 
13
13
  Skill-origin tools follow a stricter default permission policy than core tools:
14
14
 
15
- | Scenario | Core tool behavior | Skill tool behavior |
16
- |---|---|---|
17
- | Low risk, no matching rule | Auto-allowed (legacy mode) | **Prompted** |
18
- | Medium risk, no matching rule | Prompted | Prompted |
19
- | High risk, no matching rule | Prompted | Prompted |
20
- | Allow rule matches, non-high risk | Auto-allowed | Auto-allowed |
21
- | Allow rule matches, high risk, `allowHighRisk: true` | Auto-allowed | Auto-allowed |
22
- | Allow rule matches, high risk, `allowHighRisk` absent | Prompted | Prompted |
15
+ | Scenario | Core tool behavior | Skill tool behavior |
16
+ | ----------------------------------------------------- | ----------------------------- | ------------------- |
17
+ | Low risk, no matching rule | Auto-allowed (workspace mode) | **Prompted** |
18
+ | Medium risk, no matching rule | Prompted | Prompted |
19
+ | High risk, no matching rule | Prompted | Prompted |
20
+ | Allow rule matches, non-high risk | Auto-allowed | Auto-allowed |
21
+ | Allow rule matches, high risk, `allowHighRisk: true` | Auto-allowed | Auto-allowed |
22
+ | Allow rule matches, high risk, `allowHighRisk` absent | Prompted | Prompted |
23
23
 
24
24
  Even if a skill's `TOOLS.json` declares `"risk": "low"` for one of its tools, the permission checker will prompt the user unless an explicit trust rule in `~/.vellum/protected/trust.json` allows it. This prevents third-party skill tools from silently auto-executing.
25
25
 
26
26
  ## Skill Load Approval
27
27
 
28
- The `skill_load` tool activates a skill within the current session. In **strict mode** (`permissions.mode = 'strict'`), loading a skill requires an explicit matching trust rule. In **legacy mode**, `skill_load` is classified as low risk and auto-allowed.
28
+ The `skill_load` tool activates a skill within the current session. In **strict mode** (`permissions.mode = 'strict'`), loading a skill requires an explicit matching trust rule. In **workspace mode** (default), `skill_load` is prompted unless a matching trust rule exists.
29
29
 
30
30
  When `skill_load` is invoked, the permission checker generates multiple command candidates for rule matching:
31
31
 
@@ -57,10 +57,10 @@ When a skill's source files change (any file added, removed, or modified), the h
57
57
 
58
58
  ### Choosing between version-specific and any-version rules
59
59
 
60
- | Approval type | Rule pattern | Behavior |
61
- |---|---|---|
60
+ | Approval type | Rule pattern | Behavior |
61
+ | ---------------- | ---------------------------------- | ---------------------------------------------------------------------------- |
62
62
  | Version-specific | `skill_load:my-skill@v1:abc123...` | Only this exact version is auto-allowed. Any code change triggers re-prompt. |
63
- | Any-version | `skill_load:my-skill` | All versions of this skill are auto-allowed. No re-prompt on code changes. |
63
+ | Any-version | `skill_load:my-skill` | All versions of this skill are auto-allowed. No re-prompt on code changes. |
64
64
 
65
65
  Version-specific rules are more secure but require re-approval after every skill update. Any-version rules are more convenient but grant persistent access regardless of code changes.
66
66
 
@@ -91,7 +91,7 @@ The `normalizeFilePath()` function walks up the directory tree to find the neare
91
91
 
92
92
  When `permissions.mode` is set to `strict` in the assistant configuration, **all** tool actions require a matching trust rule. There is no implicit auto-allow for any risk level. This means:
93
93
 
94
- - Low-risk tools that would normally auto-execute in legacy mode (e.g., `file_read`, `web_search`) will prompt unless a trust rule allows them.
94
+ - Low-risk tools that would normally auto-execute in workspace mode (e.g., `file_read`, `web_search`) will prompt unless a trust rule allows them.
95
95
  - `skill_load` requires an explicit rule match, even though it is classified as low risk.
96
96
  - The **starter bundle** can be accepted to seed common safe rules and reduce prompt noise.
97
97
 
@@ -99,14 +99,14 @@ When `permissions.mode` is set to `strict` in the assistant configuration, **all
99
99
 
100
100
  The starter bundle is an opt-in set of allow rules for read-only tools that most users would approve individually. Accepting the bundle seeds these rules at once:
101
101
 
102
- | Tool | Pattern |
103
- |---|---|
104
- | `file_read` | `file_read:**` |
105
- | `glob` | `glob:**` |
106
- | `grep` | `grep:**` |
102
+ | Tool | Pattern |
103
+ | ---------------- | ------------------- |
104
+ | `file_read` | `file_read:**` |
105
+ | `glob` | `glob:**` |
106
+ | `grep` | `grep:**` |
107
107
  | `list_directory` | `list_directory:**` |
108
- | `web_search` | `web_search:**` |
109
- | `web_fetch` | `web_fetch:**` |
108
+ | `web_search` | `web_search:**` |
109
+ | `web_fetch` | `web_fetch:**` |
110
110
 
111
111
  Acceptance is idempotent and recorded in `trust.json`. The bundle does not include any tools that mutate the filesystem or execute arbitrary code.
112
112
 
@@ -114,10 +114,10 @@ Acceptance is idempotent and recorded in `trust.json`. The bundle does not inclu
114
114
 
115
115
  Tools can execute in two contexts:
116
116
 
117
- | Target | Description |
118
- |---|---|
117
+ | Target | Description |
118
+ | --------- | -------------------------------------------------------------------------------------- |
119
119
  | `sandbox` | Isolated execution within `~/.vellum/workspace` (Docker container or OS-level sandbox) |
120
- | `host` | Direct execution on the host machine |
120
+ | `host` | Direct execution on the host machine |
121
121
 
122
122
  Trust rules can include an `executionTarget` field to bind the rule to a specific context. A rule without `executionTarget` matches both sandbox and host invocations.
123
123
 
@@ -143,9 +143,9 @@ Strict mode requires an explicit trust rule for every tool action. There is no i
143
143
 
144
144
  **Fix**: Accept the starter approval bundle to seed common safe rules. Then approve additional tools as needed — each approval creates a persistent trust rule.
145
145
 
146
- ### "Why does skill_load prompt in strict mode but not in legacy mode?"
146
+ ### "Why does skill_load prompt in strict mode but not in workspace mode?"
147
147
 
148
- In legacy mode, `skill_load` is classified as low risk and auto-allowed. In strict mode, low-risk auto-allow is disabled, so an explicit rule is needed.
148
+ In workspace mode, `skill_load` may be auto-allowed by system default rules (e.g., `skill_load:*` at priority 100). In strict mode, low-risk auto-allow is disabled, so an explicit rule is needed.
149
149
 
150
150
  **Fix**: Create a rule for the specific skill (version-specific or any-version) or accept the starter bundle if it covers your needs.
151
151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.4.35",
3
+ "version": "0.4.36",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -56,6 +56,9 @@
56
56
  "web-tree-sitter": "0.26.5",
57
57
  "zod": "^4.3.6"
58
58
  },
59
+ "engines": {
60
+ "node": ">=20.12.0"
61
+ },
59
62
  "devDependencies": {
60
63
  "@pydantic/logfire-node": "^0.13.0",
61
64
  "@types/archiver": "^7.0.0",
@@ -495,7 +495,7 @@ exports[`IPC message snapshots ClientMessage types fork_shared_app serializes to
495
495
 
496
496
  exports[`IPC message snapshots ClientMessage types open_bundle serializes to expected JSON 1`] = `
497
497
  {
498
- "filePath": "/tmp/My_App.vellumapp",
498
+ "filePath": "/tmp/My_App.vellum",
499
499
  "type": "open_bundle",
500
500
  }
501
501
  `;
@@ -1641,6 +1641,15 @@ exports[`IPC message snapshots ServerMessage types task_routed serializes to exp
1641
1641
  }
1642
1642
  `;
1643
1643
 
1644
+ exports[`IPC message snapshots ServerMessage types ride_shotgun_error serializes to expected JSON 1`] = `
1645
+ {
1646
+ "message": "Failed to start browser — Chrome CDP could not be launched.",
1647
+ "sessionId": "sess-shotgun-001",
1648
+ "type": "ride_shotgun_error",
1649
+ "watchId": "watch-shotgun-001",
1650
+ }
1651
+ `;
1652
+
1644
1653
  exports[`IPC message snapshots ServerMessage types ride_shotgun_progress serializes to expected JSON 1`] = `
1645
1654
  {
1646
1655
  "message": "Observing user activity...",
@@ -1988,7 +1997,7 @@ exports[`IPC message snapshots ServerMessage types reminders_list_response seria
1988
1997
 
1989
1998
  exports[`IPC message snapshots ServerMessage types bundle_app_response serializes to expected JSON 1`] = `
1990
1999
  {
1991
- "bundlePath": "/tmp/My_App-abc12345.vellumapp",
2000
+ "bundlePath": "/tmp/My_App-abc12345.vellum",
1992
2001
  "manifest": {
1993
2002
  "capabilities": [],
1994
2003
  "content_id": "a1b2c3d4e5f6a7b8",
@@ -19,6 +19,7 @@ mock.module("../util/platform.js", () => ({
19
19
  getDbPath: () => join(testDir, "test.db"),
20
20
  normalizeAssistantId: (id: string) => (id === "self" ? "self" : id),
21
21
  readLockfile: () => null,
22
+ writeLockfile: () => {},
22
23
  isMacOS: () => process.platform === "darwin",
23
24
  isLinux: () => process.platform === "linux",
24
25
  isWindows: () => process.platform === "win32",
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Verify that the Amazon CLI delegates CDP management to the shared
3
+ * chrome-cdp helper instead of owning its own launch/window logic.
4
+ */
5
+
6
+ import { readFileSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { describe, expect, test } from "bun:test";
9
+
10
+ const AMAZON_CLI_PATH = join(
11
+ import.meta.dirname ?? __dirname,
12
+ "..",
13
+ "cli",
14
+ "amazon.ts",
15
+ );
16
+ const amazonSource = readFileSync(AMAZON_CLI_PATH, "utf-8");
17
+
18
+ describe("Amazon CLI CDP integration", () => {
19
+ test("imports shared CDP helpers instead of defining its own", () => {
20
+ // Should import from the shared module
21
+ expect(amazonSource).toContain('from "../tools/browser/chrome-cdp.js"');
22
+
23
+ // Should import the key entrypoints
24
+ expect(amazonSource).toContain("ensureChromeWithCdp");
25
+ expect(amazonSource).toContain("minimizeChromeWindow");
26
+ expect(amazonSource).toContain("restoreChromeWindow");
27
+ });
28
+
29
+ test("does not define its own CDP_BASE constant", () => {
30
+ // The old inline constant was: const CDP_BASE = "http://localhost:9222";
31
+ expect(amazonSource).not.toMatch(/const\s+CDP_BASE\s*=/);
32
+ });
33
+
34
+ test("does not define its own Chrome data dir constant", () => {
35
+ expect(amazonSource).not.toMatch(/const\s+CHROME_DATA_DIR\s*=/);
36
+ });
37
+
38
+ test("does not define a local isCdpReady function", () => {
39
+ expect(amazonSource).not.toMatch(/async\s+function\s+isCdpReady\s*\(/);
40
+ });
41
+
42
+ test("does not define a local ensureChromeWithCDP function", () => {
43
+ expect(amazonSource).not.toMatch(
44
+ /async\s+function\s+ensureChromeWithCDP\s*\(/,
45
+ );
46
+ });
47
+
48
+ test("does not define local minimize/restore window functions", () => {
49
+ expect(amazonSource).not.toMatch(
50
+ /async\s+function\s+minimizeChromeWindow\s*\(/,
51
+ );
52
+ expect(amazonSource).not.toMatch(
53
+ /async\s+function\s+restoreChromeWindow\s*\(/,
54
+ );
55
+ });
56
+
57
+ test("does not spawn Chrome directly", () => {
58
+ // The old code imported spawn and called it with the Chrome app path.
59
+ // After migration, spawn should not be imported or used.
60
+ expect(amazonSource).not.toMatch(/spawn\s+as\s+spawnChild/);
61
+ expect(amazonSource).not.toContain(
62
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
63
+ );
64
+ });
65
+
66
+ test("still calls ensureChromeWithCdp in the learn session flow", () => {
67
+ // The learn session helper should delegate to the shared entrypoint
68
+ expect(amazonSource).toMatch(/await\s+ensureChromeWithCdp\s*\(/);
69
+ });
70
+
71
+ test("passes amazon.com as the start URL", () => {
72
+ expect(amazonSource).toContain("https://www.amazon.com/");
73
+ });
74
+ });