@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
@@ -31,9 +31,11 @@ const TMP_PATHS = String.raw`^(?:/tmp|/var/tmp|\./|\.\.\/)`;
31
31
 
32
32
  export const DEFAULT_COMMAND_REGISTRY = {
33
33
  // ── Read-only filesystem commands ──────────────────────────────────────────
34
- ls: { baseRisk: "low" },
34
+ ls: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
35
35
  cat: {
36
36
  baseRisk: "low",
37
+ sandboxAutoApprove: true,
38
+ argSchema: {},
37
39
  argRules: [
38
40
  {
39
41
  id: "cat:sensitive",
@@ -43,33 +45,78 @@ export const DEFAULT_COMMAND_REGISTRY = {
43
45
  },
44
46
  ],
45
47
  },
46
- head: { baseRisk: "low" },
47
- tail: { baseRisk: "low" },
48
- less: { baseRisk: "low" },
49
- more: { baseRisk: "low" },
50
- wc: { baseRisk: "low" },
51
- file: { baseRisk: "low" },
52
- stat: { baseRisk: "low" },
53
- du: { baseRisk: "low" },
54
- df: { baseRisk: "low" },
55
- diff: { baseRisk: "low" },
56
- tree: { baseRisk: "low" },
57
- pwd: { baseRisk: "low" },
58
- realpath: { baseRisk: "low" },
59
- basename: { baseRisk: "low" },
60
- dirname: { baseRisk: "low" },
48
+ head: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
49
+ tail: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
50
+ less: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
51
+ more: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
52
+ wc: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
53
+ file: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
54
+ stat: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
55
+ du: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
56
+ df: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
57
+ diff: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
58
+ tree: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
59
+ pwd: {
60
+ baseRisk: "low",
61
+ sandboxAutoApprove: true,
62
+ argSchema: { positionals: "none" },
63
+ },
64
+ realpath: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
65
+ basename: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
66
+ dirname: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
67
+ readlink: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
61
68
 
62
69
  // ── Search / filter / text processing ──────────────────────────────────────
63
- grep: { baseRisk: "low" },
64
- rg: { baseRisk: "low" },
65
- ag: { baseRisk: "low" },
66
- ack: { baseRisk: "low" },
67
- sort: { baseRisk: "low" },
68
- uniq: { baseRisk: "low" },
69
- cut: { baseRisk: "low" },
70
- tr: { baseRisk: "low" },
71
- sed: {
70
+ grep: {
72
71
  baseRisk: "low",
72
+ sandboxAutoApprove: true,
73
+ argSchema: {
74
+ positionals: [{ role: "pattern" }, { role: "path", rest: true }],
75
+ },
76
+ },
77
+ rg: {
78
+ baseRisk: "low",
79
+ sandboxAutoApprove: true,
80
+ argSchema: {
81
+ positionals: [{ role: "pattern" }, { role: "path", rest: true }],
82
+ },
83
+ },
84
+ ag: {
85
+ baseRisk: "low",
86
+ sandboxAutoApprove: true,
87
+ argSchema: {
88
+ positionals: [{ role: "pattern" }, { role: "path", rest: true }],
89
+ },
90
+ },
91
+ ack: {
92
+ baseRisk: "low",
93
+ sandboxAutoApprove: true,
94
+ argSchema: {
95
+ positionals: [{ role: "pattern" }, { role: "path", rest: true }],
96
+ },
97
+ },
98
+ sort: {
99
+ baseRisk: "low",
100
+ sandboxAutoApprove: true,
101
+ argSchema: {
102
+ valueFlags: ["-o", "--output"],
103
+ pathFlags: { "-o": true, "--output": true },
104
+ },
105
+ },
106
+ uniq: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
107
+ cut: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
108
+ tr: {
109
+ baseRisk: "low",
110
+ sandboxAutoApprove: true,
111
+ argSchema: { positionals: "none" },
112
+ },
113
+ sed: {
114
+ baseRisk: "medium",
115
+ reason: "Can write files or execute commands via sed scripts",
116
+ sandboxAutoApprove: true,
117
+ argSchema: {
118
+ positionals: [{ role: "script" }, { role: "path", rest: true }],
119
+ },
73
120
  argRules: [
74
121
  {
75
122
  id: "sed:inplace",
@@ -81,13 +128,24 @@ export const DEFAULT_COMMAND_REGISTRY = {
81
128
  },
82
129
  awk: {
83
130
  baseRisk: "medium",
131
+ argSchema: {
132
+ positionals: [{ role: "script" }, { role: "path", rest: true }],
133
+ },
84
134
  complexSyntax: true,
85
135
  reason: "Can execute shell commands via system()",
86
136
  },
87
137
 
88
138
  // ── System information (read-only) ─────────────────────────────────────────
89
- echo: { baseRisk: "low" },
90
- printf: { baseRisk: "low" },
139
+ echo: {
140
+ baseRisk: "low",
141
+ sandboxAutoApprove: true,
142
+ argSchema: { positionals: "none" },
143
+ },
144
+ printf: {
145
+ baseRisk: "low",
146
+ sandboxAutoApprove: true,
147
+ argSchema: { positionals: "none" },
148
+ },
91
149
  whoami: { baseRisk: "low" },
92
150
  uname: { baseRisk: "low" },
93
151
  uptime: { baseRisk: "low" },
@@ -113,14 +171,31 @@ export const DEFAULT_COMMAND_REGISTRY = {
113
171
  hexdump: { baseRisk: "low" },
114
172
 
115
173
  // ── Data processing ────────────────────────────────────────────────────────
116
- jq: { baseRisk: "low" },
117
- yq: { baseRisk: "low" },
174
+ jq: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
175
+ yq: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
118
176
 
119
177
  // ── Find ───────────────────────────────────────────────────────────────────
120
178
  // DIVERGENCE: checker.ts lists `find` as LOW_RISK unconditionally. Our
121
179
  // registry adds arg rules for -exec/-execdir/-delete which escalate to high.
122
180
  find: {
123
181
  baseRisk: "low",
182
+ argSchema: {
183
+ valueFlags: [
184
+ "-name",
185
+ "-iname",
186
+ "-path",
187
+ "-ipath",
188
+ "-regex",
189
+ "-iregex",
190
+ "-maxdepth",
191
+ "-mindepth",
192
+ "-newer",
193
+ "-user",
194
+ "-group",
195
+ "-printf",
196
+ "-fprintf",
197
+ ],
198
+ },
124
199
  complexSyntax: true,
125
200
  argRules: [
126
201
  {
@@ -137,11 +212,16 @@ export const DEFAULT_COMMAND_REGISTRY = {
137
212
  },
138
213
  ],
139
214
  },
140
- fd: { baseRisk: "low" },
215
+ fd: { baseRisk: "low", sandboxAutoApprove: true, argSchema: {} },
141
216
 
142
217
  // ── Write commands ─────────────────────────────────────────────────────────
143
218
  cp: {
144
219
  baseRisk: "medium",
220
+ sandboxAutoApprove: true,
221
+ argSchema: {
222
+ valueFlags: ["-t", "--target-directory"],
223
+ pathFlags: { "-t": true, "--target-directory": true },
224
+ },
145
225
  argRules: [
146
226
  {
147
227
  id: "cp:system",
@@ -153,6 +233,8 @@ export const DEFAULT_COMMAND_REGISTRY = {
153
233
  },
154
234
  mv: {
155
235
  baseRisk: "medium",
236
+ sandboxAutoApprove: true,
237
+ argSchema: {},
156
238
  argRules: [
157
239
  {
158
240
  id: "mv:system",
@@ -162,15 +244,25 @@ export const DEFAULT_COMMAND_REGISTRY = {
162
244
  },
163
245
  ],
164
246
  },
165
- mkdir: { baseRisk: "medium" },
166
- touch: { baseRisk: "medium" },
247
+ mkdir: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
248
+ touch: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
249
+ ln: {
250
+ baseRisk: "medium",
251
+ sandboxAutoApprove: true,
252
+ argSchema: {
253
+ valueFlags: ["-t", "--target-directory"],
254
+ pathFlags: { "-t": true, "--target-directory": true },
255
+ },
256
+ },
167
257
  // DIVERGENCE: checker.ts lists `tee` as LOW_RISK. Our registry classifies
168
258
  // it as medium because it writes to files.
169
- tee: { baseRisk: "medium" },
259
+ tee: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
170
260
 
171
261
  // ── Delete commands ────────────────────────────────────────────────────────
172
262
  rm: {
173
263
  baseRisk: "high",
264
+ sandboxAutoApprove: true,
265
+ argSchema: {},
174
266
  argRules: [
175
267
  {
176
268
  id: "rm:recursive-force",
@@ -204,11 +296,45 @@ export const DEFAULT_COMMAND_REGISTRY = {
204
296
  },
205
297
  ],
206
298
  },
207
- rmdir: { baseRisk: "high" },
299
+ rmdir: { baseRisk: "high", sandboxAutoApprove: true, argSchema: {} },
208
300
 
209
301
  // ── Network commands ───────────────────────────────────────────────────────
210
302
  curl: {
211
303
  baseRisk: "medium",
304
+ argSchema: {
305
+ valueFlags: [
306
+ "-d",
307
+ "--data",
308
+ "--data-binary",
309
+ "--data-raw",
310
+ "--data-urlencode",
311
+ "-T",
312
+ "--upload-file",
313
+ "-o",
314
+ "--output",
315
+ "-H",
316
+ "--header",
317
+ "-X",
318
+ "--request",
319
+ "-u",
320
+ "--user",
321
+ "-A",
322
+ "--user-agent",
323
+ "-e",
324
+ "--referer",
325
+ "-b",
326
+ "--cookie",
327
+ "-c",
328
+ "--cookie-jar",
329
+ "--connect-timeout",
330
+ "-m",
331
+ "--max-time",
332
+ "--retry",
333
+ "-w",
334
+ "--write-out",
335
+ ],
336
+ positionals: "none", // positionals are URLs, not paths
337
+ },
212
338
  argRules: [
213
339
  {
214
340
  id: "curl:upload-data",
@@ -254,15 +380,17 @@ export const DEFAULT_COMMAND_REGISTRY = {
254
380
  // Divergences are noted inline.
255
381
  git: {
256
382
  baseRisk: "medium",
257
- globalValueFlags: [
258
- "-C",
259
- "-c",
260
- "--git-dir",
261
- "--work-tree",
262
- "--namespace",
263
- "--super-prefix",
264
- "--config-env",
265
- ],
383
+ argSchema: {
384
+ valueFlags: [
385
+ "-C",
386
+ "-c",
387
+ "--git-dir",
388
+ "--work-tree",
389
+ "--namespace",
390
+ "--super-prefix",
391
+ "--config-env",
392
+ ],
393
+ },
266
394
  subcommands: {
267
395
  // LOW_RISK_GIT_SUBCOMMANDS from checker.ts:
268
396
  status: { baseRisk: "low" },
@@ -357,7 +485,9 @@ export const DEFAULT_COMMAND_REGISTRY = {
357
485
  // they download and execute code, with subcommand-level overrides.
358
486
  npm: {
359
487
  baseRisk: "medium",
360
- globalValueFlags: ["--prefix", "--userconfig", "--globalconfig", "--cache"],
488
+ argSchema: {
489
+ valueFlags: ["--prefix", "--userconfig", "--globalconfig", "--cache"],
490
+ },
361
491
  subcommands: {
362
492
  ls: { baseRisk: "low" },
363
493
  list: { baseRisk: "low" },
@@ -531,7 +661,9 @@ export const DEFAULT_COMMAND_REGISTRY = {
531
661
  // ── Docker ─────────────────────────────────────────────────────────────────
532
662
  docker: {
533
663
  baseRisk: "medium",
534
- globalValueFlags: ["--host", "-H", "--config", "--context", "--log-level"],
664
+ argSchema: {
665
+ valueFlags: ["--host", "-H", "--config", "--context", "--log-level"],
666
+ },
535
667
  subcommands: {
536
668
  ps: { baseRisk: "low" },
537
669
  images: { baseRisk: "low" },
@@ -544,6 +676,26 @@ export const DEFAULT_COMMAND_REGISTRY = {
544
676
  push: { baseRisk: "high", reason: "Pushes image to registry" },
545
677
  run: {
546
678
  baseRisk: "high",
679
+ argSchema: {
680
+ valueFlags: [
681
+ "-v",
682
+ "--volume",
683
+ "-p",
684
+ "--publish",
685
+ "-e",
686
+ "--env",
687
+ "--name",
688
+ "--network",
689
+ "-w",
690
+ "--workdir",
691
+ "--entrypoint",
692
+ "--mount",
693
+ "--cpus",
694
+ "--memory",
695
+ "--user",
696
+ "--platform",
697
+ ],
698
+ },
547
699
  reason: "Runs arbitrary container",
548
700
  argRules: [
549
701
  {
@@ -584,9 +736,24 @@ export const DEFAULT_COMMAND_REGISTRY = {
584
736
  isWrapper: true,
585
737
  reason: "Elevates privileges (OpenBSD sudo alternative)",
586
738
  },
587
- chmod: { baseRisk: "high", reason: "Changes file permissions" },
588
- chown: { baseRisk: "high", reason: "Changes file ownership" },
589
- chgrp: { baseRisk: "high", reason: "Changes file group" },
739
+ chmod: {
740
+ baseRisk: "high",
741
+ sandboxAutoApprove: true,
742
+ argSchema: {},
743
+ reason: "Changes file permissions",
744
+ },
745
+ chown: {
746
+ baseRisk: "high",
747
+ sandboxAutoApprove: true,
748
+ argSchema: {},
749
+ reason: "Changes file ownership",
750
+ },
751
+ chgrp: {
752
+ baseRisk: "high",
753
+ sandboxAutoApprove: true,
754
+ argSchema: {},
755
+ reason: "Changes file group",
756
+ },
590
757
  mount: { baseRisk: "high", reason: "Mounts filesystem" },
591
758
  umount: { baseRisk: "high", reason: "Unmounts filesystem" },
592
759
  systemctl: { baseRisk: "high", reason: "Controls system services" },
@@ -726,16 +893,29 @@ export const DEFAULT_COMMAND_REGISTRY = {
726
893
  complexSyntax: true,
727
894
  reason: "Executes command with piped arguments",
728
895
  },
729
- tar: { baseRisk: "medium", complexSyntax: true },
730
- zip: { baseRisk: "medium" },
731
- unzip: { baseRisk: "medium" },
732
- gzip: { baseRisk: "medium" },
733
- gunzip: { baseRisk: "medium" },
896
+ tar: {
897
+ baseRisk: "medium",
898
+ sandboxAutoApprove: true,
899
+ argSchema: {
900
+ valueFlags: ["-C", "--directory", "-f", "--file"],
901
+ pathFlags: {
902
+ "-C": true,
903
+ "--directory": true,
904
+ "-f": true,
905
+ "--file": true,
906
+ },
907
+ },
908
+ complexSyntax: true,
909
+ },
910
+ zip: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
911
+ unzip: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
912
+ gzip: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
913
+ gunzip: { baseRisk: "medium", sandboxAutoApprove: true, argSchema: {} },
734
914
 
735
915
  // ── Version control tools ──────────────────────────────────────────────────
736
916
  gh: {
737
917
  baseRisk: "low",
738
- globalValueFlags: ["--repo", "-R"],
918
+ argSchema: { valueFlags: ["--repo", "-R"] },
739
919
  subcommands: {
740
920
  pr: {
741
921
  baseRisk: "low",
@@ -85,10 +85,11 @@ export function getDefaultRuleTemplates(): DefaultRuleTemplate[] {
85
85
  };
86
86
 
87
87
  // When running inside a container (IS_CONTAINERIZED=true), bash commands
88
- // execute in an isolated environment — auto-allow all of them so the user
89
- // is never prompted. High-risk operations are handled by
90
- // shouldAutoAllowHighRisk() in checker.ts. Outside a container, bash
91
- // commands run on the host and go through normal permission checks.
88
+ // with sandboxAutoApprove tags auto-allow via the approval policy's
89
+ // sandbox auto-approve check. Non-allowlisted commands matched by the
90
+ // default allow rule below are still auto-allowed at low/medium risk;
91
+ // only high-risk non-allowlisted commands (runtimes, privilege escalation)
92
+ // fall through to be prompted.
92
93
  const bashShellRule: DefaultRuleTemplate | null = getIsContainerized()
93
94
  ? {
94
95
  id: "default:allow-bash-global",
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Gateway-backed auto-approve threshold reader.
3
+ *
4
+ * When the `permission-controls-v3` feature flag is enabled, reads
5
+ * thresholds from the gateway via IPC. Falls back to `undefined` (caller
6
+ * uses config-based `resolveThreshold`) when the flag is off or the gateway
7
+ * is unreachable.
8
+ */
9
+
10
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
11
+ import { getConfig } from "../config/loader.js";
12
+ import { ipcCall } from "../ipc/gateway-client.js";
13
+ import { getLogger } from "../util/logger.js";
14
+ import type { ExecutionContext } from "./approval-policy.js";
15
+
16
+ const log = getLogger("gateway-threshold-reader");
17
+
18
+ // ── Types ────────────────────────────────────────────────────────────────────
19
+
20
+ type Threshold = "none" | "low" | "medium" | "high";
21
+
22
+ interface GlobalThresholds {
23
+ interactive: string;
24
+ background: string;
25
+ headless: string;
26
+ }
27
+
28
+ interface ConversationThreshold {
29
+ threshold: string;
30
+ }
31
+
32
+ // ── Global threshold cache (30s TTL) ─────────────────────────────────────────
33
+
34
+ let cachedGlobalThresholds: GlobalThresholds | null = null;
35
+ let cachedGlobalTimestamp = 0;
36
+ const GLOBAL_CACHE_TTL_MS = 30_000;
37
+
38
+ // ── Conversation threshold cache (5s TTL) ────────────────────────────────────
39
+ // Shorter TTL than global because the user can change mid-conversation via the
40
+ // picker UI, but still avoids a network roundtrip on every single tool call
41
+ // within a burst.
42
+
43
+ const conversationThresholdCache = new Map<
44
+ string,
45
+ { threshold: string | null; timestamp: number }
46
+ >();
47
+ const CONVERSATION_CACHE_TTL_MS = 5_000;
48
+
49
+ /**
50
+ * Clear the global threshold cache. Exported for testing.
51
+ */
52
+ export function _clearGlobalCacheForTesting(): void {
53
+ cachedGlobalThresholds = null;
54
+ cachedGlobalTimestamp = 0;
55
+ conversationThresholdCache.clear();
56
+ }
57
+
58
+ // ── Hardcoded fallback defaults ──────────────────────────────────────────────
59
+
60
+ const HARDCODED_DEFAULTS: Record<ExecutionContext, Threshold> = {
61
+ conversation: "low",
62
+ background: "medium",
63
+ headless: "none",
64
+ };
65
+
66
+ // ── Helpers ──────────────────────────────────────────────────────────────────
67
+
68
+ function mapExecutionContextToField(
69
+ executionContext: ExecutionContext,
70
+ ): keyof GlobalThresholds {
71
+ if (executionContext === "conversation") return "interactive";
72
+ return executionContext;
73
+ }
74
+
75
+ function isValidThreshold(value: string): value is Threshold {
76
+ return (
77
+ value === "none" ||
78
+ value === "low" ||
79
+ value === "medium" ||
80
+ value === "high"
81
+ );
82
+ }
83
+
84
+ // ── Main export ──────────────────────────────────────────────────────────────
85
+
86
+ /**
87
+ * Read the auto-approve threshold from the gateway via IPC.
88
+ *
89
+ * Returns `undefined` when the feature flag is off (caller falls back to
90
+ * config-based `resolveThreshold`).
91
+ *
92
+ * For `"conversation"` context with a `conversationId`, checks for a
93
+ * per-conversation override first. Falls through to global defaults when
94
+ * the conversation override is absent.
95
+ *
96
+ * Caches global thresholds for 30 seconds to avoid hammering the gateway.
97
+ * On any IPC error, logs a warning and returns hardcoded defaults.
98
+ */
99
+ export async function getAutoApproveThreshold(
100
+ conversationId: string | undefined,
101
+ executionContext?: ExecutionContext,
102
+ ): Promise<Threshold | undefined> {
103
+ const config = getConfig();
104
+ if (!isAssistantFeatureFlagEnabled("permission-controls-v3", config)) {
105
+ return undefined;
106
+ }
107
+
108
+ const ctx: ExecutionContext = executionContext ?? "conversation";
109
+
110
+ // For conversation context with a conversationId, try per-conversation override first
111
+ if (ctx === "conversation" && conversationId) {
112
+ // Check cache first (5s TTL) — includes negative entries (no override)
113
+ const cached = conversationThresholdCache.get(conversationId);
114
+ if (cached && Date.now() - cached.timestamp < CONVERSATION_CACHE_TTL_MS) {
115
+ if (cached.threshold === null) {
116
+ // Negative cache hit — no override exists, fall through to global
117
+ } else if (isValidThreshold(cached.threshold)) {
118
+ return cached.threshold;
119
+ }
120
+ } else {
121
+ // ipcCall() returns undefined on transport failure (socket not found,
122
+ // timeout, etc.) and null when the gateway explicitly says "no override".
123
+ // We must distinguish the two: only cache the negative on `null`, and
124
+ // fall back to hardcoded defaults on `undefined` without poisoning the
125
+ // cache — otherwise a transient IPC failure would cause subsequent
126
+ // approval checks to skip a real override for up to 5 seconds.
127
+ const result = (await ipcCall("get_conversation_threshold", {
128
+ conversationId,
129
+ })) as ConversationThreshold | null | undefined;
130
+
131
+ if (result === undefined) {
132
+ log.warn(
133
+ { conversationId },
134
+ "IPC call failed for conversation threshold override, falling back to defaults",
135
+ );
136
+ return HARDCODED_DEFAULTS[ctx];
137
+ }
138
+
139
+ if (result && isValidThreshold(result.threshold)) {
140
+ conversationThresholdCache.set(conversationId, {
141
+ threshold: result.threshold,
142
+ timestamp: Date.now(),
143
+ });
144
+ return result.threshold;
145
+ }
146
+
147
+ // result === null (or an unexpected shape) — cache the negative result
148
+ // and fall through to global defaults.
149
+ conversationThresholdCache.set(conversationId, {
150
+ threshold: null,
151
+ timestamp: Date.now(),
152
+ });
153
+ }
154
+ }
155
+
156
+ // Fetch global thresholds (with 30s cache)
157
+ try {
158
+ const global = await fetchGlobalThresholds();
159
+ const field = mapExecutionContextToField(ctx);
160
+ const value = global[field];
161
+ if (isValidThreshold(value)) {
162
+ return value;
163
+ }
164
+ // Unexpected value from gateway — fall back to hardcoded
165
+ log.warn({ field, value }, "Gateway returned unexpected threshold value");
166
+ return HARDCODED_DEFAULTS[ctx];
167
+ } catch (err) {
168
+ log.warn(
169
+ { error: String(err) },
170
+ "Failed to fetch global thresholds, falling back to defaults",
171
+ );
172
+ return HARDCODED_DEFAULTS[ctx];
173
+ }
174
+ }
175
+
176
+ async function fetchGlobalThresholds(): Promise<GlobalThresholds> {
177
+ const now = Date.now();
178
+ if (
179
+ cachedGlobalThresholds &&
180
+ now - cachedGlobalTimestamp < GLOBAL_CACHE_TTL_MS
181
+ ) {
182
+ return cachedGlobalThresholds;
183
+ }
184
+
185
+ const result = (await ipcCall(
186
+ "get_global_thresholds",
187
+ )) as GlobalThresholds | null;
188
+
189
+ if (!result) {
190
+ throw new Error("Gateway IPC returned no result for global thresholds");
191
+ }
192
+
193
+ cachedGlobalThresholds = result;
194
+ cachedGlobalTimestamp = Date.now();
195
+ return result;
196
+ }
@@ -66,6 +66,8 @@ export class PermissionPrompter {
66
66
  temporaryOptionsAvailable?: Array<"allow_10m" | "allow_conversation">,
67
67
  toolUseId?: string,
68
68
  hostAccessEnablePrompt?: boolean,
69
+ riskReason?: string,
70
+ isContainerized?: boolean,
69
71
  ): Promise<{
70
72
  decision: UserDecision;
71
73
  selectedPattern?: string;
@@ -116,6 +118,8 @@ export class PermissionPrompter {
116
118
  toolName,
117
119
  input: redactSensitiveFields(input),
118
120
  riskLevel,
121
+ riskReason,
122
+ isContainerized,
119
123
  allowlistOptions: allowlistOptions.map((o) => ({
120
124
  label: o.label,
121
125
  description: o.description,