@vellumai/assistant 0.6.5 → 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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -136,16 +136,16 @@ describe("allow rule", () => {
136
136
  expect(result.matchedRule).toBeUndefined();
137
137
  });
138
138
 
139
- test("allow at High risk — containerized bash allow (auto-allow), matchedRule present", () => {
139
+ test("allow at High risk — containerized bash without sandboxAutoApprove flag prompt", () => {
140
140
  const result = evaluate({
141
141
  riskLevel: RiskLevel.High,
142
142
  toolName: "bash",
143
143
  matchedRule: allowRule,
144
144
  isContainerized: true,
145
145
  });
146
- expect(result.decision).toBe("allow");
147
- expect(result.reason).toContain("auto-allow-high-risk");
148
- expect(result.matchedRule).toBe(allowRule);
146
+ expect(result.decision).toBe("prompt");
147
+ expect(result.reason).toContain("high risk");
148
+ expect(result.matchedRule).toBeUndefined();
149
149
  });
150
150
 
151
151
  test("allow at High risk — non-bash tool, containerized → prompt, no matchedRule in decision", () => {
@@ -175,6 +175,113 @@ describe("allow rule", () => {
175
175
  });
176
176
  });
177
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
+
178
285
  // ── No rule: third-party skill tool ──────────────────────────────────────────
179
286
 
180
287
  describe("no rule — third-party skill tool", () => {
@@ -220,6 +327,44 @@ describe("no rule — third-party skill tool", () => {
220
327
  // Bundled skill + Low risk + no rule → handled by step 9 or 11
221
328
  expect(result.decision).toBe("allow");
222
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
+ });
223
368
  });
224
369
 
225
370
  // ── No rule: strict mode ─────────────────────────────────────────────────────
@@ -266,6 +411,30 @@ describe("no rule — strict mode", () => {
266
411
  expect(result.decision).toBe("prompt");
267
412
  expect(result.reason).toContain("Strict mode");
268
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
+ });
269
438
  });
270
439
 
271
440
  // ── No rule: workspace mode ──────────────────────────────────────────────────
@@ -305,7 +474,7 @@ describe("no rule — workspace mode", () => {
305
474
  expect(result.reason).toContain("medium risk");
306
475
  });
307
476
 
308
- test("workspace mode, bash, NOT containerized, Low risk, workspace-scoped → falls through (no auto-allow for host bash)", () => {
477
+ test("workspace mode, bash, NOT containerized, Low risk, workspace-scoped → allow via workspace mode", () => {
309
478
  const result = evaluate({
310
479
  riskLevel: RiskLevel.Low,
311
480
  toolName: "bash",
@@ -313,10 +482,8 @@ describe("no rule — workspace mode", () => {
313
482
  isContainerized: false,
314
483
  isWorkspaceScoped: true,
315
484
  });
316
- // Non-containerized bash falls through the workspace check.
317
- // Then hits risk-based: Low → allow (within default "low" threshold)
318
485
  expect(result.decision).toBe("allow");
319
- expect(result.reason).toContain("low risk");
486
+ expect(result.reason).toContain("Workspace mode");
320
487
  });
321
488
 
322
489
  test("workspace mode, bash, containerized, Low risk, workspace-scoped → allow via workspace mode", () => {
@@ -474,10 +641,9 @@ describe("edge cases", () => {
474
641
  expect(result.decision).toBe("allow");
475
642
  });
476
643
 
477
- test("workspace mode non-containerized bash, Low risk, workspace-scoped → low risk allow (not workspace allow)", () => {
478
- // This is the subtle bash host exception. The workspace mode check
479
- // specifically skips bash when not containerized, so it falls through
480
- // to the risk-based path where Low risk still auto-allows.
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.
481
647
  const result = evaluate({
482
648
  riskLevel: RiskLevel.Low,
483
649
  toolName: "bash",
@@ -486,10 +652,7 @@ describe("edge cases", () => {
486
652
  isWorkspaceScoped: true,
487
653
  });
488
654
  expect(result.decision).toBe("allow");
489
- // The reason should be "low risk" not "Workspace mode" — the workspace
490
- // auto-allow was bypassed because bash is on the host.
491
- expect(result.reason).not.toContain("Workspace mode");
492
- expect(result.reason).toContain("low risk");
655
+ expect(result.reason).toContain("Workspace mode");
493
656
  });
494
657
 
495
658
  test("hasManifestOverride with toolOrigin set to skill — third-party check triggers on origin", () => {
@@ -618,6 +781,38 @@ describe("autoApproveUpTo threshold", () => {
618
781
  });
619
782
  });
620
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
+
621
816
  describe("threshold interacts correctly with rule-based decisions", () => {
622
817
  test("deny rule still denies regardless of threshold", () => {
623
818
  const denyRule = makeRule({ decision: "deny" });
@@ -631,18 +826,60 @@ describe("autoApproveUpTo threshold", () => {
631
826
  expect(result.matchedRule).toBe(denyRule);
632
827
  });
633
828
 
634
- test("ask rule still prompts regardless of threshold", () => {
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", () => {
635
842
  const askRule = makeRule({ decision: "ask" });
636
843
  const result = evaluate({
637
844
  riskLevel: RiskLevel.Low,
638
845
  toolName: "bash",
639
846
  matchedRule: askRule,
640
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,
641
862
  });
642
863
  expect(result.decision).toBe("prompt");
643
864
  expect(result.matchedRule).toBe(askRule);
644
865
  });
645
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
+
646
883
  test("allow rule still allows non-High regardless of threshold", () => {
647
884
  const allowRule = makeRule({ decision: "allow" });
648
885
  const result = evaluate({
@@ -727,6 +964,12 @@ describe("resolveThreshold", () => {
727
964
  expect(resolveThreshold("none", "headless")).toBe("none");
728
965
  });
729
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
+
730
973
  test("returns scalar when executionContext is omitted", () => {
731
974
  expect(resolveThreshold("low")).toBe("low");
732
975
  });
@@ -808,12 +1051,13 @@ describe("guardian threshold-based auto-approve (ordinal comparison)", () => {
808
1051
  // This is the logic that replaces the old `riskLevel !== RiskLevel.High` check.
809
1052
  function isWithinThreshold(
810
1053
  riskLevel: RiskLevel,
811
- bgThreshold: "none" | "low" | "medium",
1054
+ bgThreshold: "none" | "low" | "medium" | "high",
812
1055
  ): boolean {
813
1056
  const thresholdOrdinal: Record<string, number> = {
814
1057
  none: -1,
815
1058
  low: 0,
816
1059
  medium: 1,
1060
+ high: 2,
817
1061
  };
818
1062
  const riskOrdinal: Record<string, number> = {
819
1063
  [RiskLevel.Low]: 0,
@@ -906,7 +1150,38 @@ describe("guardian threshold-based auto-approve (ordinal comparison)", () => {
906
1150
  });
907
1151
  });
908
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
+
909
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
+
910
1185
  test('scalar "medium" → background resolves to medium', () => {
911
1186
  expect(resolveThreshold("medium", "background")).toBe("medium");
912
1187
  });