@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
@@ -0,0 +1,1223 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type { ApprovalContext, ApprovalDecision } from "./approval-policy.js";
4
+ import { DefaultApprovalPolicy, resolveThreshold } from "./approval-policy.js";
5
+ import type { TrustRule } from "./types.js";
6
+ import { RiskLevel } from "./types.js";
7
+
8
+ // ── Helpers ──────────────────────────────────────────────────────────────────
9
+
10
+ const policy = new DefaultApprovalPolicy();
11
+
12
+ function makeRule(
13
+ overrides: Partial<TrustRule> & { decision: TrustRule["decision"] },
14
+ ): TrustRule {
15
+ return {
16
+ id: "test-rule",
17
+ tool: "bash",
18
+ pattern: "test-pattern",
19
+ priority: 100,
20
+ createdAt: Date.now(),
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ function makeContext(overrides: Partial<ApprovalContext>): ApprovalContext {
26
+ return {
27
+ riskLevel: RiskLevel.Low,
28
+ toolName: "bash",
29
+ permissionsMode: "workspace",
30
+ isContainerized: false,
31
+ isWorkspaceScoped: false,
32
+ ...overrides,
33
+ };
34
+ }
35
+
36
+ function evaluate(overrides: Partial<ApprovalContext>): ApprovalDecision {
37
+ return policy.evaluate(makeContext(overrides));
38
+ }
39
+
40
+ // ── Deny rule at each risk level ─────────────────────────────────────────────
41
+
42
+ describe("deny rule", () => {
43
+ const denyRule = makeRule({ decision: "deny" });
44
+
45
+ test("deny at Low risk", () => {
46
+ const result = evaluate({
47
+ riskLevel: RiskLevel.Low,
48
+ matchedRule: denyRule,
49
+ });
50
+ expect(result.decision).toBe("deny");
51
+ expect(result.reason).toContain("deny rule");
52
+ expect(result.matchedRule).toBe(denyRule);
53
+ });
54
+
55
+ test("deny at Medium risk", () => {
56
+ const result = evaluate({
57
+ riskLevel: RiskLevel.Medium,
58
+ matchedRule: denyRule,
59
+ });
60
+ expect(result.decision).toBe("deny");
61
+ });
62
+
63
+ test("deny at High risk", () => {
64
+ const result = evaluate({
65
+ riskLevel: RiskLevel.High,
66
+ matchedRule: denyRule,
67
+ });
68
+ expect(result.decision).toBe("deny");
69
+ });
70
+ });
71
+
72
+ // ── Ask rule at each risk level ──────────────────────────────────────────────
73
+
74
+ describe("ask rule", () => {
75
+ const askRule = makeRule({ decision: "ask" });
76
+
77
+ test("ask at Low risk", () => {
78
+ const result = evaluate({ riskLevel: RiskLevel.Low, matchedRule: askRule });
79
+ expect(result.decision).toBe("prompt");
80
+ expect(result.reason).toContain("ask rule");
81
+ expect(result.matchedRule).toBe(askRule);
82
+ });
83
+
84
+ test("ask at Medium risk", () => {
85
+ const result = evaluate({
86
+ riskLevel: RiskLevel.Medium,
87
+ matchedRule: askRule,
88
+ });
89
+ expect(result.decision).toBe("prompt");
90
+ });
91
+
92
+ test("ask at High risk", () => {
93
+ const result = evaluate({
94
+ riskLevel: RiskLevel.High,
95
+ matchedRule: askRule,
96
+ });
97
+ expect(result.decision).toBe("prompt");
98
+ });
99
+ });
100
+
101
+ // ── Allow rule at each risk level ────────────────────────────────────────────
102
+
103
+ describe("allow rule", () => {
104
+ const allowRule = makeRule({ decision: "allow" });
105
+
106
+ test("allow at Low risk", () => {
107
+ const result = evaluate({
108
+ riskLevel: RiskLevel.Low,
109
+ matchedRule: allowRule,
110
+ });
111
+ expect(result.decision).toBe("allow");
112
+ expect(result.reason).toContain("Matched trust rule");
113
+ expect(result.matchedRule).toBe(allowRule);
114
+ });
115
+
116
+ test("allow at Medium risk", () => {
117
+ const result = evaluate({
118
+ riskLevel: RiskLevel.Medium,
119
+ matchedRule: allowRule,
120
+ });
121
+ expect(result.decision).toBe("allow");
122
+ expect(result.reason).toContain("Matched trust rule");
123
+ expect(result.matchedRule).toBe(allowRule);
124
+ });
125
+
126
+ test("allow at High risk — non-containerized bash → prompt, no matchedRule in decision", () => {
127
+ const result = evaluate({
128
+ riskLevel: RiskLevel.High,
129
+ toolName: "bash",
130
+ matchedRule: allowRule,
131
+ isContainerized: false,
132
+ });
133
+ expect(result.decision).toBe("prompt");
134
+ expect(result.reason).toContain("high risk");
135
+ // Decision is driven by risk-based fallback, not the rule
136
+ expect(result.matchedRule).toBeUndefined();
137
+ });
138
+
139
+ test("allow at High risk — containerized bash without sandboxAutoApprove flag → prompt", () => {
140
+ const result = evaluate({
141
+ riskLevel: RiskLevel.High,
142
+ toolName: "bash",
143
+ matchedRule: allowRule,
144
+ isContainerized: true,
145
+ });
146
+ expect(result.decision).toBe("prompt");
147
+ expect(result.reason).toContain("high risk");
148
+ expect(result.matchedRule).toBeUndefined();
149
+ });
150
+
151
+ test("allow at High risk — non-bash tool, containerized → prompt, no matchedRule in decision", () => {
152
+ const result = evaluate({
153
+ riskLevel: RiskLevel.High,
154
+ toolName: "file_write",
155
+ matchedRule: allowRule,
156
+ isContainerized: true,
157
+ });
158
+ expect(result.decision).toBe("prompt");
159
+ expect(result.reason).toContain("high risk");
160
+ // Decision is driven by risk-based fallback, not the rule
161
+ expect(result.matchedRule).toBeUndefined();
162
+ });
163
+
164
+ test("allow at High risk — non-bash tool, non-containerized → prompt, no matchedRule in decision", () => {
165
+ const result = evaluate({
166
+ riskLevel: RiskLevel.High,
167
+ toolName: "file_write",
168
+ matchedRule: allowRule,
169
+ isContainerized: false,
170
+ });
171
+ expect(result.decision).toBe("prompt");
172
+ expect(result.reason).toContain("high risk");
173
+ // Decision is driven by risk-based fallback, not the rule
174
+ expect(result.matchedRule).toBeUndefined();
175
+ });
176
+ });
177
+
178
+ // ── Sandbox auto-approve ─────────────────────────────────────────────────────
179
+
180
+ describe("sandbox auto-approve", () => {
181
+ test("bash + hasSandboxAutoApprove + containerized → allow", () => {
182
+ const result = evaluate({
183
+ riskLevel: RiskLevel.High,
184
+ toolName: "bash",
185
+ hasSandboxAutoApprove: true,
186
+ isContainerized: true,
187
+ });
188
+ expect(result.decision).toBe("allow");
189
+ expect(result.reason).toContain("sandbox auto-approve");
190
+ });
191
+
192
+ test("bash + hasSandboxAutoApprove + not containerized → allow (path resolution is baked in)", () => {
193
+ const result = evaluate({
194
+ riskLevel: RiskLevel.Low,
195
+ toolName: "bash",
196
+ hasSandboxAutoApprove: true,
197
+ isContainerized: false,
198
+ });
199
+ // hasSandboxAutoApprove === true means path resolution already passed upstream.
200
+ // The isContainerized gate was removed — sandbox auto-approve fires regardless.
201
+ expect(result.decision).toBe("allow");
202
+ expect(result.reason).toContain("sandbox auto-approve");
203
+ });
204
+
205
+ test("bash + hasSandboxAutoApprove + not containerized + High risk → allow (path resolution validated upstream)", () => {
206
+ const result = evaluate({
207
+ riskLevel: RiskLevel.High,
208
+ toolName: "bash",
209
+ hasSandboxAutoApprove: true,
210
+ isContainerized: false,
211
+ });
212
+ // Even at High risk, hasSandboxAutoApprove === true means the checker already
213
+ // validated that all path arguments are within the workspace root.
214
+ expect(result.decision).toBe("allow");
215
+ expect(result.reason).toContain("sandbox auto-approve");
216
+ });
217
+
218
+ test("host_bash + hasSandboxAutoApprove + containerized → falls through", () => {
219
+ const result = evaluate({
220
+ riskLevel: RiskLevel.Low,
221
+ toolName: "host_bash",
222
+ hasSandboxAutoApprove: true,
223
+ isContainerized: true,
224
+ });
225
+ // host_bash is not "bash", so sandbox auto-approve doesn't fire.
226
+ // Falls through to risk-based: Low → allow (within default "low" threshold)
227
+ expect(result.decision).toBe("allow");
228
+ expect(result.reason).toContain("within auto-approve threshold");
229
+ });
230
+
231
+ test("bash + no hasSandboxAutoApprove + containerized → falls through", () => {
232
+ const result = evaluate({
233
+ riskLevel: RiskLevel.High,
234
+ toolName: "bash",
235
+ hasSandboxAutoApprove: false,
236
+ isContainerized: true,
237
+ });
238
+ // hasSandboxAutoApprove is false, so sandbox auto-approve doesn't fire.
239
+ // Falls through to risk-based: High → prompt
240
+ expect(result.decision).toBe("prompt");
241
+ expect(result.reason).toContain("high risk");
242
+ });
243
+
244
+ test("sandbox auto-approve fires even for High risk commands", () => {
245
+ // e.g. rm -rf in a container — should be auto-approved
246
+ const result = evaluate({
247
+ riskLevel: RiskLevel.High,
248
+ toolName: "bash",
249
+ hasSandboxAutoApprove: true,
250
+ isContainerized: true,
251
+ });
252
+ expect(result.decision).toBe("allow");
253
+ expect(result.reason).toContain("sandbox auto-approve");
254
+ });
255
+
256
+ test("deny rule still blocks sandbox auto-approve commands", () => {
257
+ const denyRule = makeRule({ decision: "deny" });
258
+ const result = evaluate({
259
+ riskLevel: RiskLevel.High,
260
+ toolName: "bash",
261
+ hasSandboxAutoApprove: true,
262
+ isContainerized: true,
263
+ matchedRule: denyRule,
264
+ });
265
+ // Deny at step 1 prevents step 3 (sandbox auto-approve)
266
+ expect(result.decision).toBe("deny");
267
+ expect(result.reason).toContain("deny rule");
268
+ });
269
+
270
+ test("strict mode blocks sandbox auto-approve", () => {
271
+ const result = evaluate({
272
+ riskLevel: RiskLevel.Low,
273
+ toolName: "bash",
274
+ hasSandboxAutoApprove: true,
275
+ isContainerized: true,
276
+ permissionsMode: "strict",
277
+ });
278
+ // Strict mode requires explicit rules — sandbox auto-approve only
279
+ // fires in workspace mode.
280
+ expect(result.decision).toBe("prompt");
281
+ expect(result.reason).toContain("Strict mode");
282
+ });
283
+ });
284
+
285
+ // ── No rule: third-party skill tool ──────────────────────────────────────────
286
+
287
+ describe("no rule — third-party skill tool", () => {
288
+ test("skill origin, not bundled → prompt", () => {
289
+ const result = evaluate({
290
+ riskLevel: RiskLevel.Low,
291
+ toolName: "custom_tool",
292
+ toolOrigin: "skill",
293
+ isSkillBundled: false,
294
+ });
295
+ expect(result.decision).toBe("prompt");
296
+ expect(result.reason).toContain("Skill tool");
297
+ });
298
+
299
+ test("skill origin, not bundled, Medium risk → prompt", () => {
300
+ const result = evaluate({
301
+ riskLevel: RiskLevel.Medium,
302
+ toolName: "custom_tool",
303
+ toolOrigin: "skill",
304
+ isSkillBundled: false,
305
+ });
306
+ expect(result.decision).toBe("prompt");
307
+ expect(result.reason).toContain("Skill tool");
308
+ });
309
+
310
+ test("no tool origin but hasManifestOverride → prompt (unregistered skill tool)", () => {
311
+ const result = evaluate({
312
+ riskLevel: RiskLevel.Low,
313
+ toolName: "unknown_tool",
314
+ hasManifestOverride: true,
315
+ });
316
+ expect(result.decision).toBe("prompt");
317
+ expect(result.reason).toContain("Skill tool");
318
+ });
319
+
320
+ test("skill origin, bundled → falls through (not third-party)", () => {
321
+ const result = evaluate({
322
+ riskLevel: RiskLevel.Low,
323
+ toolName: "bundled_tool",
324
+ toolOrigin: "skill",
325
+ isSkillBundled: true,
326
+ });
327
+ // Bundled skill + Low risk + no rule → handled by step 9 or 11
328
+ expect(result.decision).toBe("allow");
329
+ });
330
+
331
+ test("skill origin, not bundled, gateway threshold covers risk → allow", () => {
332
+ const result = evaluate({
333
+ riskLevel: RiskLevel.Low,
334
+ toolName: "custom_tool",
335
+ toolOrigin: "skill",
336
+ isSkillBundled: false,
337
+ autoApproveUpTo: "medium",
338
+ isGatewayThreshold: true,
339
+ });
340
+ expect(result.decision).toBe("allow");
341
+ expect(result.reason).toContain("within auto-approve threshold");
342
+ });
343
+
344
+ test("skill origin, not bundled, gateway threshold does not cover risk → prompt", () => {
345
+ const result = evaluate({
346
+ riskLevel: RiskLevel.High,
347
+ toolName: "custom_tool",
348
+ toolOrigin: "skill",
349
+ isSkillBundled: false,
350
+ autoApproveUpTo: "medium",
351
+ isGatewayThreshold: true,
352
+ });
353
+ expect(result.decision).toBe("prompt");
354
+ expect(result.reason).toContain("Skill tool");
355
+ });
356
+
357
+ test("hasManifestOverride, gateway threshold covers risk → allow", () => {
358
+ const result = evaluate({
359
+ riskLevel: RiskLevel.Low,
360
+ toolName: "unknown_tool",
361
+ hasManifestOverride: true,
362
+ autoApproveUpTo: "low",
363
+ isGatewayThreshold: true,
364
+ });
365
+ expect(result.decision).toBe("allow");
366
+ expect(result.reason).toContain("within auto-approve threshold");
367
+ });
368
+ });
369
+
370
+ // ── No rule: strict mode ─────────────────────────────────────────────────────
371
+
372
+ describe("no rule — strict mode", () => {
373
+ test("strict mode, Low risk → prompt", () => {
374
+ const result = evaluate({
375
+ riskLevel: RiskLevel.Low,
376
+ toolName: "file_read",
377
+ permissionsMode: "strict",
378
+ });
379
+ expect(result.decision).toBe("prompt");
380
+ expect(result.reason).toContain("Strict mode");
381
+ });
382
+
383
+ test("strict mode, Medium risk → prompt", () => {
384
+ const result = evaluate({
385
+ riskLevel: RiskLevel.Medium,
386
+ toolName: "file_read",
387
+ permissionsMode: "strict",
388
+ });
389
+ expect(result.decision).toBe("prompt");
390
+ expect(result.reason).toContain("Strict mode");
391
+ });
392
+
393
+ test("strict mode, High risk → prompt", () => {
394
+ const result = evaluate({
395
+ riskLevel: RiskLevel.High,
396
+ toolName: "file_read",
397
+ permissionsMode: "strict",
398
+ });
399
+ expect(result.decision).toBe("prompt");
400
+ expect(result.reason).toContain("Strict mode");
401
+ });
402
+
403
+ test("strict mode blocks bundled skill tools without explicit rule", () => {
404
+ const result = evaluate({
405
+ riskLevel: RiskLevel.Low,
406
+ toolName: "bundled_tool",
407
+ permissionsMode: "strict",
408
+ toolOrigin: "skill",
409
+ isSkillBundled: true,
410
+ });
411
+ expect(result.decision).toBe("prompt");
412
+ expect(result.reason).toContain("Strict mode");
413
+ });
414
+
415
+ test("strict mode, gateway threshold covers risk → allow", () => {
416
+ const result = evaluate({
417
+ riskLevel: RiskLevel.Low,
418
+ toolName: "file_read",
419
+ permissionsMode: "strict",
420
+ autoApproveUpTo: "low",
421
+ isGatewayThreshold: true,
422
+ });
423
+ expect(result.decision).toBe("allow");
424
+ expect(result.reason).toContain("within auto-approve threshold");
425
+ });
426
+
427
+ test("strict mode, gateway threshold does not cover risk → prompt", () => {
428
+ const result = evaluate({
429
+ riskLevel: RiskLevel.Medium,
430
+ toolName: "file_read",
431
+ permissionsMode: "strict",
432
+ autoApproveUpTo: "low",
433
+ isGatewayThreshold: true,
434
+ });
435
+ expect(result.decision).toBe("prompt");
436
+ expect(result.reason).toContain("Strict mode");
437
+ });
438
+ });
439
+
440
+ // ── No rule: workspace mode ──────────────────────────────────────────────────
441
+
442
+ describe("no rule — workspace mode", () => {
443
+ test("workspace mode, Low risk, workspace-scoped → allow", () => {
444
+ const result = evaluate({
445
+ riskLevel: RiskLevel.Low,
446
+ toolName: "file_read",
447
+ permissionsMode: "workspace",
448
+ isWorkspaceScoped: true,
449
+ });
450
+ expect(result.decision).toBe("allow");
451
+ expect(result.reason).toContain("Workspace mode");
452
+ });
453
+
454
+ test("workspace mode, Low risk, NOT workspace-scoped → falls through", () => {
455
+ const result = evaluate({
456
+ riskLevel: RiskLevel.Low,
457
+ toolName: "file_read",
458
+ permissionsMode: "workspace",
459
+ isWorkspaceScoped: false,
460
+ });
461
+ // Falls through to risk-based: Low → allow (within default "low" threshold)
462
+ expect(result.decision).toBe("allow");
463
+ expect(result.reason).toContain("low risk");
464
+ });
465
+
466
+ test("workspace mode, Medium risk → falls through to risk-based prompt", () => {
467
+ const result = evaluate({
468
+ riskLevel: RiskLevel.Medium,
469
+ toolName: "file_write",
470
+ permissionsMode: "workspace",
471
+ isWorkspaceScoped: true,
472
+ });
473
+ expect(result.decision).toBe("prompt");
474
+ expect(result.reason).toContain("medium risk");
475
+ });
476
+
477
+ test("workspace mode, bash, NOT containerized, Low risk, workspace-scoped → allow via workspace mode", () => {
478
+ const result = evaluate({
479
+ riskLevel: RiskLevel.Low,
480
+ toolName: "bash",
481
+ permissionsMode: "workspace",
482
+ isContainerized: false,
483
+ isWorkspaceScoped: true,
484
+ });
485
+ expect(result.decision).toBe("allow");
486
+ expect(result.reason).toContain("Workspace mode");
487
+ });
488
+
489
+ test("workspace mode, bash, containerized, Low risk, workspace-scoped → allow via workspace mode", () => {
490
+ const result = evaluate({
491
+ riskLevel: RiskLevel.Low,
492
+ toolName: "bash",
493
+ permissionsMode: "workspace",
494
+ isContainerized: true,
495
+ isWorkspaceScoped: true,
496
+ });
497
+ expect(result.decision).toBe("allow");
498
+ expect(result.reason).toContain("Workspace mode");
499
+ });
500
+ });
501
+
502
+ // ── No rule: bundled skill tool ──────────────────────────────────────────────
503
+
504
+ describe("no rule — bundled skill tool", () => {
505
+ test("bundled skill, Low risk → allow", () => {
506
+ const result = evaluate({
507
+ riskLevel: RiskLevel.Low,
508
+ toolName: "bundled_tool",
509
+ permissionsMode: "workspace",
510
+ toolOrigin: "skill",
511
+ isSkillBundled: true,
512
+ });
513
+ expect(result.decision).toBe("allow");
514
+ expect(result.reason).toContain("Bundled skill");
515
+ });
516
+
517
+ test("bundled skill, Medium risk → prompt (only Low auto-allows)", () => {
518
+ const result = evaluate({
519
+ riskLevel: RiskLevel.Medium,
520
+ toolName: "bundled_tool",
521
+ permissionsMode: "workspace",
522
+ toolOrigin: "skill",
523
+ isSkillBundled: true,
524
+ });
525
+ expect(result.decision).toBe("prompt");
526
+ expect(result.reason).toContain("medium risk");
527
+ });
528
+
529
+ test("bundled skill, High risk → prompt", () => {
530
+ const result = evaluate({
531
+ riskLevel: RiskLevel.High,
532
+ toolName: "bundled_tool",
533
+ permissionsMode: "workspace",
534
+ toolOrigin: "skill",
535
+ isSkillBundled: true,
536
+ });
537
+ expect(result.decision).toBe("prompt");
538
+ expect(result.reason).toContain("high risk");
539
+ });
540
+ });
541
+
542
+ // ── Risk-based fallback ──────────────────────────────────────────────────────
543
+
544
+ describe("risk-based fallback (no rule, no special case)", () => {
545
+ test("High risk → prompt", () => {
546
+ const result = evaluate({
547
+ riskLevel: RiskLevel.High,
548
+ toolName: "some_tool",
549
+ });
550
+ expect(result.decision).toBe("prompt");
551
+ expect(result.reason).toContain("high risk");
552
+ });
553
+
554
+ test("Low risk → allow", () => {
555
+ const result = evaluate({
556
+ riskLevel: RiskLevel.Low,
557
+ toolName: "some_tool",
558
+ });
559
+ expect(result.decision).toBe("allow");
560
+ expect(result.reason).toContain("low risk");
561
+ });
562
+
563
+ test("Medium risk → prompt", () => {
564
+ const result = evaluate({
565
+ riskLevel: RiskLevel.Medium,
566
+ toolName: "some_tool",
567
+ });
568
+ expect(result.decision).toBe("prompt");
569
+ expect(result.reason).toContain("medium risk");
570
+ });
571
+ });
572
+
573
+ // ── Edge cases and combined scenarios ────────────────────────────────────────
574
+
575
+ describe("edge cases", () => {
576
+ test("deny rule takes precedence over allow-everything else", () => {
577
+ const denyRule = makeRule({ decision: "deny" });
578
+ const result = evaluate({
579
+ riskLevel: RiskLevel.Low,
580
+ toolName: "bash",
581
+ matchedRule: denyRule,
582
+ isContainerized: true,
583
+ isWorkspaceScoped: true,
584
+ permissionsMode: "workspace",
585
+ });
586
+ expect(result.decision).toBe("deny");
587
+ });
588
+
589
+ test("ask rule takes precedence over allow-for-low", () => {
590
+ const askRule = makeRule({ decision: "ask" });
591
+ const result = evaluate({
592
+ riskLevel: RiskLevel.Low,
593
+ toolName: "bash",
594
+ matchedRule: askRule,
595
+ isContainerized: true,
596
+ });
597
+ expect(result.decision).toBe("prompt");
598
+ });
599
+
600
+ test("allow rule High risk falls through to prompt even with workspace mode", () => {
601
+ const allowRule = makeRule({ decision: "allow" });
602
+ const result = evaluate({
603
+ riskLevel: RiskLevel.High,
604
+ toolName: "file_write",
605
+ matchedRule: allowRule,
606
+ permissionsMode: "workspace",
607
+ isContainerized: false,
608
+ isWorkspaceScoped: true,
609
+ });
610
+ // Allow rule + High risk + non-bash → falls through to risk-based: High → prompt
611
+ expect(result.decision).toBe("prompt");
612
+ expect(result.reason).toContain("high risk");
613
+ });
614
+
615
+ test("reason includes the matched rule pattern", () => {
616
+ const rule = makeRule({ decision: "allow", pattern: "git status" });
617
+ const result = evaluate({
618
+ riskLevel: RiskLevel.Low,
619
+ matchedRule: rule,
620
+ });
621
+ expect(result.reason).toContain("git status");
622
+ });
623
+
624
+ test("deny reason includes the matched rule pattern", () => {
625
+ const rule = makeRule({ decision: "deny", pattern: "rm -rf /" });
626
+ const result = evaluate({
627
+ riskLevel: RiskLevel.Low,
628
+ matchedRule: rule,
629
+ });
630
+ expect(result.reason).toContain("rm -rf /");
631
+ });
632
+
633
+ test("strict mode with matched allow rule at Low risk → allow (rule takes precedence)", () => {
634
+ const allowRule = makeRule({ decision: "allow" });
635
+ const result = evaluate({
636
+ riskLevel: RiskLevel.Low,
637
+ toolName: "file_read",
638
+ matchedRule: allowRule,
639
+ permissionsMode: "strict",
640
+ });
641
+ expect(result.decision).toBe("allow");
642
+ });
643
+
644
+ test("workspace mode non-containerized bash, Low risk, workspace-scoped → workspace allow", () => {
645
+ // Non-containerized bash auto-allows via workspace mode like any other
646
+ // workspace-scoped tool when risk is Low.
647
+ const result = evaluate({
648
+ riskLevel: RiskLevel.Low,
649
+ toolName: "bash",
650
+ permissionsMode: "workspace",
651
+ isContainerized: false,
652
+ isWorkspaceScoped: true,
653
+ });
654
+ expect(result.decision).toBe("allow");
655
+ expect(result.reason).toContain("Workspace mode");
656
+ });
657
+
658
+ test("hasManifestOverride with toolOrigin set to skill — third-party check triggers on origin", () => {
659
+ const result = evaluate({
660
+ riskLevel: RiskLevel.Low,
661
+ toolName: "manifest_tool",
662
+ toolOrigin: "skill",
663
+ isSkillBundled: false,
664
+ hasManifestOverride: true,
665
+ });
666
+ expect(result.decision).toBe("prompt");
667
+ expect(result.reason).toContain("Skill tool");
668
+ });
669
+
670
+ test("hasManifestOverride with toolOrigin set to builtin — falls through (not a skill)", () => {
671
+ const result = evaluate({
672
+ riskLevel: RiskLevel.Low,
673
+ toolName: "manifest_tool",
674
+ toolOrigin: "builtin",
675
+ hasManifestOverride: true,
676
+ });
677
+ // toolOrigin is "builtin", so the third-party skill check doesn't trigger.
678
+ // The hasManifestOverride check requires !toolOrigin, but toolOrigin is set.
679
+ // Falls through to risk-based: Low → allow (within default "low" threshold).
680
+ expect(result.decision).toBe("allow");
681
+ expect(result.reason).toContain("low risk");
682
+ });
683
+ });
684
+
685
+ // ── autoApproveUpTo threshold ─────────────────────────────────────────────────
686
+
687
+ describe("autoApproveUpTo threshold", () => {
688
+ describe('autoApproveUpTo: "none" — everything prompts', () => {
689
+ test("Low risk → prompt", () => {
690
+ const result = evaluate({
691
+ riskLevel: RiskLevel.Low,
692
+ toolName: "some_tool",
693
+ autoApproveUpTo: "none",
694
+ });
695
+ expect(result.decision).toBe("prompt");
696
+ expect(result.reason).toContain("above auto-approve threshold");
697
+ });
698
+
699
+ test("Medium risk → prompt", () => {
700
+ const result = evaluate({
701
+ riskLevel: RiskLevel.Medium,
702
+ toolName: "some_tool",
703
+ autoApproveUpTo: "none",
704
+ });
705
+ expect(result.decision).toBe("prompt");
706
+ expect(result.reason).toContain("above auto-approve threshold");
707
+ });
708
+
709
+ test("High risk → prompt", () => {
710
+ const result = evaluate({
711
+ riskLevel: RiskLevel.High,
712
+ toolName: "some_tool",
713
+ autoApproveUpTo: "none",
714
+ });
715
+ expect(result.decision).toBe("prompt");
716
+ expect(result.reason).toContain("above auto-approve threshold");
717
+ });
718
+ });
719
+
720
+ describe('autoApproveUpTo: "low" — default, matches existing behavior', () => {
721
+ test("Low risk → allow", () => {
722
+ const result = evaluate({
723
+ riskLevel: RiskLevel.Low,
724
+ toolName: "some_tool",
725
+ autoApproveUpTo: "low",
726
+ });
727
+ expect(result.decision).toBe("allow");
728
+ expect(result.reason).toContain("within auto-approve threshold");
729
+ });
730
+
731
+ test("Medium risk → prompt", () => {
732
+ const result = evaluate({
733
+ riskLevel: RiskLevel.Medium,
734
+ toolName: "some_tool",
735
+ autoApproveUpTo: "low",
736
+ });
737
+ expect(result.decision).toBe("prompt");
738
+ expect(result.reason).toContain("above auto-approve threshold");
739
+ });
740
+
741
+ test("High risk → prompt", () => {
742
+ const result = evaluate({
743
+ riskLevel: RiskLevel.High,
744
+ toolName: "some_tool",
745
+ autoApproveUpTo: "low",
746
+ });
747
+ expect(result.decision).toBe("prompt");
748
+ expect(result.reason).toContain("above auto-approve threshold");
749
+ });
750
+ });
751
+
752
+ describe('autoApproveUpTo: "medium" — Low and Medium auto-allow', () => {
753
+ test("Low risk → allow", () => {
754
+ const result = evaluate({
755
+ riskLevel: RiskLevel.Low,
756
+ toolName: "some_tool",
757
+ autoApproveUpTo: "medium",
758
+ });
759
+ expect(result.decision).toBe("allow");
760
+ expect(result.reason).toContain("within auto-approve threshold");
761
+ });
762
+
763
+ test("Medium risk → allow", () => {
764
+ const result = evaluate({
765
+ riskLevel: RiskLevel.Medium,
766
+ toolName: "some_tool",
767
+ autoApproveUpTo: "medium",
768
+ });
769
+ expect(result.decision).toBe("allow");
770
+ expect(result.reason).toContain("within auto-approve threshold");
771
+ });
772
+
773
+ test("High risk → prompt", () => {
774
+ const result = evaluate({
775
+ riskLevel: RiskLevel.High,
776
+ toolName: "some_tool",
777
+ autoApproveUpTo: "medium",
778
+ });
779
+ expect(result.decision).toBe("prompt");
780
+ expect(result.reason).toContain("above auto-approve threshold");
781
+ });
782
+ });
783
+
784
+ describe('autoApproveUpTo: "high" — everything auto-allows', () => {
785
+ test("Low risk → allow", () => {
786
+ const result = evaluate({
787
+ riskLevel: RiskLevel.Low,
788
+ toolName: "some_tool",
789
+ autoApproveUpTo: "high",
790
+ });
791
+ expect(result.decision).toBe("allow");
792
+ expect(result.reason).toContain("within auto-approve threshold");
793
+ });
794
+
795
+ test("Medium risk → allow", () => {
796
+ const result = evaluate({
797
+ riskLevel: RiskLevel.Medium,
798
+ toolName: "some_tool",
799
+ autoApproveUpTo: "high",
800
+ });
801
+ expect(result.decision).toBe("allow");
802
+ expect(result.reason).toContain("within auto-approve threshold");
803
+ });
804
+
805
+ test("High risk → allow", () => {
806
+ const result = evaluate({
807
+ riskLevel: RiskLevel.High,
808
+ toolName: "some_tool",
809
+ autoApproveUpTo: "high",
810
+ });
811
+ expect(result.decision).toBe("allow");
812
+ expect(result.reason).toContain("within auto-approve threshold");
813
+ });
814
+ });
815
+
816
+ describe("threshold interacts correctly with rule-based decisions", () => {
817
+ test("deny rule still denies regardless of threshold", () => {
818
+ const denyRule = makeRule({ decision: "deny" });
819
+ const result = evaluate({
820
+ riskLevel: RiskLevel.Low,
821
+ toolName: "bash",
822
+ matchedRule: denyRule,
823
+ autoApproveUpTo: "medium",
824
+ });
825
+ expect(result.decision).toBe("deny");
826
+ expect(result.matchedRule).toBe(denyRule);
827
+ });
828
+
829
+ test("ask rule still prompts without gateway threshold flag", () => {
830
+ const askRule = makeRule({ decision: "ask" });
831
+ const result = evaluate({
832
+ riskLevel: RiskLevel.Low,
833
+ toolName: "bash",
834
+ matchedRule: askRule,
835
+ autoApproveUpTo: "medium",
836
+ });
837
+ expect(result.decision).toBe("prompt");
838
+ expect(result.matchedRule).toBe(askRule);
839
+ });
840
+
841
+ test("ask rule auto-approves when gateway threshold covers the risk", () => {
842
+ const askRule = makeRule({ decision: "ask" });
843
+ const result = evaluate({
844
+ riskLevel: RiskLevel.Low,
845
+ toolName: "bash",
846
+ matchedRule: askRule,
847
+ autoApproveUpTo: "medium",
848
+ isGatewayThreshold: true,
849
+ });
850
+ expect(result.decision).toBe("allow");
851
+ expect(result.reason).toContain("within auto-approve threshold");
852
+ });
853
+
854
+ test("ask rule still prompts when gateway threshold does not cover the risk", () => {
855
+ const askRule = makeRule({ decision: "ask" });
856
+ const result = evaluate({
857
+ riskLevel: RiskLevel.High,
858
+ toolName: "bash",
859
+ matchedRule: askRule,
860
+ autoApproveUpTo: "medium",
861
+ isGatewayThreshold: true,
862
+ });
863
+ expect(result.decision).toBe("prompt");
864
+ expect(result.matchedRule).toBe(askRule);
865
+ });
866
+
867
+ test("skill_load_dynamic ask rule always prompts even with gateway threshold", () => {
868
+ const dynamicSkillAskRule = makeRule({
869
+ decision: "ask",
870
+ pattern: "skill_load_dynamic:my-skill",
871
+ });
872
+ const result = evaluate({
873
+ riskLevel: RiskLevel.Low,
874
+ toolName: "skill_load",
875
+ matchedRule: dynamicSkillAskRule,
876
+ autoApproveUpTo: "high",
877
+ isGatewayThreshold: true,
878
+ });
879
+ expect(result.decision).toBe("prompt");
880
+ expect(result.matchedRule).toBe(dynamicSkillAskRule);
881
+ });
882
+
883
+ test("allow rule still allows non-High regardless of threshold", () => {
884
+ const allowRule = makeRule({ decision: "allow" });
885
+ const result = evaluate({
886
+ riskLevel: RiskLevel.Medium,
887
+ toolName: "file_write",
888
+ matchedRule: allowRule,
889
+ autoApproveUpTo: "none",
890
+ });
891
+ expect(result.decision).toBe("allow");
892
+ expect(result.matchedRule).toBe(allowRule);
893
+ });
894
+ });
895
+
896
+ describe("threshold interacts correctly with strict mode", () => {
897
+ test("strict mode still prompts even with medium threshold", () => {
898
+ const result = evaluate({
899
+ riskLevel: RiskLevel.Low,
900
+ toolName: "file_read",
901
+ permissionsMode: "strict",
902
+ autoApproveUpTo: "medium",
903
+ });
904
+ expect(result.decision).toBe("prompt");
905
+ expect(result.reason).toContain("Strict mode");
906
+ });
907
+ });
908
+
909
+ describe("threshold interacts correctly with workspace mode", () => {
910
+ test("workspace mode workspace-scoped Low still allows via workspace path (before threshold)", () => {
911
+ const result = evaluate({
912
+ riskLevel: RiskLevel.Low,
913
+ toolName: "file_read",
914
+ permissionsMode: "workspace",
915
+ isWorkspaceScoped: true,
916
+ autoApproveUpTo: "none",
917
+ });
918
+ // Workspace mode auto-allow fires before the threshold fallback
919
+ expect(result.decision).toBe("allow");
920
+ expect(result.reason).toContain("Workspace mode");
921
+ });
922
+
923
+ test("workspace mode non-workspace-scoped Low with none threshold → prompt", () => {
924
+ const result = evaluate({
925
+ riskLevel: RiskLevel.Low,
926
+ toolName: "file_read",
927
+ permissionsMode: "workspace",
928
+ isWorkspaceScoped: false,
929
+ autoApproveUpTo: "none",
930
+ });
931
+ // Falls through workspace check (not workspace-scoped), then threshold
932
+ // "none" means Low risk is above threshold → prompt
933
+ expect(result.decision).toBe("prompt");
934
+ expect(result.reason).toContain("above auto-approve threshold");
935
+ });
936
+ });
937
+
938
+ describe("threshold defaults to low when omitted", () => {
939
+ test("omitted autoApproveUpTo behaves as low", () => {
940
+ const result = evaluate({
941
+ riskLevel: RiskLevel.Low,
942
+ toolName: "some_tool",
943
+ // autoApproveUpTo not set
944
+ });
945
+ expect(result.decision).toBe("allow");
946
+ expect(result.reason).toContain("within auto-approve threshold");
947
+ });
948
+ });
949
+ });
950
+
951
+ // ── resolveThreshold ─────────────────────────────────────────────────────────
952
+
953
+ describe("resolveThreshold", () => {
954
+ describe("scalar form", () => {
955
+ test("returns scalar for conversation context", () => {
956
+ expect(resolveThreshold("low", "conversation")).toBe("low");
957
+ });
958
+
959
+ test("returns scalar for background context", () => {
960
+ expect(resolveThreshold("medium", "background")).toBe("medium");
961
+ });
962
+
963
+ test("returns scalar for headless context", () => {
964
+ expect(resolveThreshold("none", "headless")).toBe("none");
965
+ });
966
+
967
+ test("returns high scalar for any context", () => {
968
+ expect(resolveThreshold("high", "conversation")).toBe("high");
969
+ expect(resolveThreshold("high", "background")).toBe("high");
970
+ expect(resolveThreshold("high", "headless")).toBe("high");
971
+ });
972
+
973
+ test("returns scalar when executionContext is omitted", () => {
974
+ expect(resolveThreshold("low")).toBe("low");
975
+ });
976
+ });
977
+
978
+ describe("object form", () => {
979
+ const perContext = {
980
+ conversation: "low" as const,
981
+ background: "medium" as const,
982
+ headless: "none" as const,
983
+ };
984
+
985
+ test("returns conversation threshold for conversation context", () => {
986
+ expect(resolveThreshold(perContext, "conversation")).toBe("low");
987
+ });
988
+
989
+ test("returns background threshold for background context", () => {
990
+ expect(resolveThreshold(perContext, "background")).toBe("medium");
991
+ });
992
+
993
+ test("returns headless threshold for headless context", () => {
994
+ expect(resolveThreshold(perContext, "headless")).toBe("none");
995
+ });
996
+
997
+ test("defaults to conversation when executionContext is omitted", () => {
998
+ expect(resolveThreshold(perContext)).toBe("low");
999
+ });
1000
+ });
1001
+
1002
+ describe("per-context thresholds in policy evaluation", () => {
1003
+ test("conversation context with low threshold prompts medium risk", () => {
1004
+ const result = evaluate({
1005
+ riskLevel: RiskLevel.Medium,
1006
+ toolName: "some_tool",
1007
+ autoApproveUpTo: "low",
1008
+ });
1009
+ expect(result.decision).toBe("prompt");
1010
+ });
1011
+
1012
+ test("background context with medium threshold allows medium risk", () => {
1013
+ const result = evaluate({
1014
+ riskLevel: RiskLevel.Medium,
1015
+ toolName: "some_tool",
1016
+ autoApproveUpTo: "medium",
1017
+ });
1018
+ expect(result.decision).toBe("allow");
1019
+ expect(result.reason).toContain("within auto-approve threshold");
1020
+ });
1021
+
1022
+ test("headless context with none threshold prompts low risk", () => {
1023
+ const result = evaluate({
1024
+ riskLevel: RiskLevel.Low,
1025
+ toolName: "some_tool",
1026
+ autoApproveUpTo: "none",
1027
+ });
1028
+ expect(result.decision).toBe("prompt");
1029
+ expect(result.reason).toContain("above auto-approve threshold");
1030
+ });
1031
+
1032
+ test("background context with medium threshold prompts high risk", () => {
1033
+ const result = evaluate({
1034
+ riskLevel: RiskLevel.High,
1035
+ toolName: "some_tool",
1036
+ autoApproveUpTo: "medium",
1037
+ });
1038
+ expect(result.decision).toBe("prompt");
1039
+ expect(result.reason).toContain("above auto-approve threshold");
1040
+ });
1041
+ });
1042
+ });
1043
+
1044
+ // ── Guardian threshold-based auto-approve ────────────────────────────────────
1045
+ // These tests verify the ordinal comparison used in permission-checker.ts
1046
+ // to decide whether a guardian non-interactive session should auto-approve.
1047
+ // The comparison logic: riskOrdinal <= thresholdOrdinal.
1048
+
1049
+ describe("guardian threshold-based auto-approve (ordinal comparison)", () => {
1050
+ // Helper that mirrors the ordinal comparison from permission-checker.ts.
1051
+ // This is the logic that replaces the old `riskLevel !== RiskLevel.High` check.
1052
+ function isWithinThreshold(
1053
+ riskLevel: RiskLevel,
1054
+ bgThreshold: "none" | "low" | "medium" | "high",
1055
+ ): boolean {
1056
+ const thresholdOrdinal: Record<string, number> = {
1057
+ none: -1,
1058
+ low: 0,
1059
+ medium: 1,
1060
+ high: 2,
1061
+ };
1062
+ const riskOrdinal: Record<string, number> = {
1063
+ [RiskLevel.Low]: 0,
1064
+ [RiskLevel.Medium]: 1,
1065
+ [RiskLevel.High]: 2,
1066
+ };
1067
+ return (
1068
+ (riskOrdinal[riskLevel] ?? 2) <= (thresholdOrdinal[bgThreshold] ?? 0)
1069
+ );
1070
+ }
1071
+
1072
+ describe('default config (background: "medium") — behavioral parity with old riskLevel !== High', () => {
1073
+ test("Low risk → within threshold (auto-approve)", () => {
1074
+ const bgThreshold = resolveThreshold(
1075
+ { conversation: "low", background: "medium", headless: "none" },
1076
+ "background",
1077
+ );
1078
+ expect(bgThreshold).toBe("medium");
1079
+ expect(isWithinThreshold(RiskLevel.Low, bgThreshold)).toBe(true);
1080
+ });
1081
+
1082
+ test("Medium risk → within threshold (auto-approve)", () => {
1083
+ const bgThreshold = resolveThreshold(
1084
+ { conversation: "low", background: "medium", headless: "none" },
1085
+ "background",
1086
+ );
1087
+ expect(isWithinThreshold(RiskLevel.Medium, bgThreshold)).toBe(true);
1088
+ });
1089
+
1090
+ test("High risk → above threshold (prompt)", () => {
1091
+ const bgThreshold = resolveThreshold(
1092
+ { conversation: "low", background: "medium", headless: "none" },
1093
+ "background",
1094
+ );
1095
+ expect(isWithinThreshold(RiskLevel.High, bgThreshold)).toBe(false);
1096
+ });
1097
+ });
1098
+
1099
+ describe('tighter config (background: "low") — only Low auto-approves', () => {
1100
+ test("Low risk → within threshold", () => {
1101
+ const bgThreshold = resolveThreshold(
1102
+ { conversation: "low", background: "low", headless: "none" },
1103
+ "background",
1104
+ );
1105
+ expect(bgThreshold).toBe("low");
1106
+ expect(isWithinThreshold(RiskLevel.Low, bgThreshold)).toBe(true);
1107
+ });
1108
+
1109
+ test("Medium risk → above threshold (prompt)", () => {
1110
+ const bgThreshold = resolveThreshold(
1111
+ { conversation: "low", background: "low", headless: "none" },
1112
+ "background",
1113
+ );
1114
+ expect(isWithinThreshold(RiskLevel.Medium, bgThreshold)).toBe(false);
1115
+ });
1116
+
1117
+ test("High risk → above threshold (prompt)", () => {
1118
+ const bgThreshold = resolveThreshold(
1119
+ { conversation: "low", background: "low", headless: "none" },
1120
+ "background",
1121
+ );
1122
+ expect(isWithinThreshold(RiskLevel.High, bgThreshold)).toBe(false);
1123
+ });
1124
+ });
1125
+
1126
+ describe('strictest config (background: "none") — nothing auto-approves', () => {
1127
+ test("Low risk → above threshold (prompt)", () => {
1128
+ const bgThreshold = resolveThreshold(
1129
+ { conversation: "low", background: "none", headless: "none" },
1130
+ "background",
1131
+ );
1132
+ expect(bgThreshold).toBe("none");
1133
+ expect(isWithinThreshold(RiskLevel.Low, bgThreshold)).toBe(false);
1134
+ });
1135
+
1136
+ test("Medium risk → above threshold (prompt)", () => {
1137
+ const bgThreshold = resolveThreshold(
1138
+ { conversation: "low", background: "none", headless: "none" },
1139
+ "background",
1140
+ );
1141
+ expect(isWithinThreshold(RiskLevel.Medium, bgThreshold)).toBe(false);
1142
+ });
1143
+
1144
+ test("High risk → above threshold (prompt)", () => {
1145
+ const bgThreshold = resolveThreshold(
1146
+ { conversation: "low", background: "none", headless: "none" },
1147
+ "background",
1148
+ );
1149
+ expect(isWithinThreshold(RiskLevel.High, bgThreshold)).toBe(false);
1150
+ });
1151
+ });
1152
+
1153
+ describe('loosest config (background: "high") — everything auto-approves', () => {
1154
+ test("Low risk → within threshold (auto-approve)", () => {
1155
+ const bgThreshold = resolveThreshold(
1156
+ { conversation: "low", background: "high", headless: "none" },
1157
+ "background",
1158
+ );
1159
+ expect(bgThreshold).toBe("high");
1160
+ expect(isWithinThreshold(RiskLevel.Low, bgThreshold)).toBe(true);
1161
+ });
1162
+
1163
+ test("Medium risk → within threshold (auto-approve)", () => {
1164
+ const bgThreshold = resolveThreshold(
1165
+ { conversation: "low", background: "high", headless: "none" },
1166
+ "background",
1167
+ );
1168
+ expect(isWithinThreshold(RiskLevel.Medium, bgThreshold)).toBe(true);
1169
+ });
1170
+
1171
+ test("High risk → within threshold (auto-approve)", () => {
1172
+ const bgThreshold = resolveThreshold(
1173
+ { conversation: "low", background: "high", headless: "none" },
1174
+ "background",
1175
+ );
1176
+ expect(isWithinThreshold(RiskLevel.High, bgThreshold)).toBe(true);
1177
+ });
1178
+ });
1179
+
1180
+ describe("scalar config form resolves correctly for background context", () => {
1181
+ test('scalar "high" → background resolves to high', () => {
1182
+ expect(resolveThreshold("high", "background")).toBe("high");
1183
+ });
1184
+
1185
+ test('scalar "medium" → background resolves to medium', () => {
1186
+ expect(resolveThreshold("medium", "background")).toBe("medium");
1187
+ });
1188
+
1189
+ test('scalar "low" → background resolves to low', () => {
1190
+ expect(resolveThreshold("low", "background")).toBe("low");
1191
+ });
1192
+
1193
+ test('scalar "none" → background resolves to none', () => {
1194
+ expect(resolveThreshold("none", "background")).toBe("none");
1195
+ });
1196
+ });
1197
+
1198
+ describe("default (undefined) resolves per-context defaults", () => {
1199
+ test("undefined config → medium threshold for background", () => {
1200
+ const bgThreshold = resolveThreshold(undefined, "background");
1201
+ expect(bgThreshold).toBe("medium");
1202
+ // Low and Medium risk are within threshold, High is not
1203
+ expect(isWithinThreshold(RiskLevel.Low, bgThreshold)).toBe(true);
1204
+ expect(isWithinThreshold(RiskLevel.Medium, bgThreshold)).toBe(true);
1205
+ expect(isWithinThreshold(RiskLevel.High, bgThreshold)).toBe(false);
1206
+ });
1207
+
1208
+ test("undefined config → low threshold for conversation", () => {
1209
+ const convThreshold = resolveThreshold(undefined, "conversation");
1210
+ expect(convThreshold).toBe("low");
1211
+ });
1212
+
1213
+ test("undefined config → none threshold for headless", () => {
1214
+ const hlThreshold = resolveThreshold(undefined, "headless");
1215
+ expect(hlThreshold).toBe("none");
1216
+ });
1217
+
1218
+ test("undefined config + no context → low (conversation default)", () => {
1219
+ const threshold = resolveThreshold(undefined);
1220
+ expect(threshold).toBe("low");
1221
+ });
1222
+ });
1223
+ });