@vellumai/assistant 0.6.5 → 0.7.0

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 (1520) hide show
  1. package/AGENTS.md +29 -1
  2. package/ARCHITECTURE.md +60 -53
  3. package/Dockerfile +25 -3
  4. package/README.md +8 -10
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +277 -0
  6. package/bun.lock +306 -119
  7. package/docs/architecture/integrations.md +32 -39
  8. package/docs/architecture/memory.md +26 -120
  9. package/docs/architecture/security.md +22 -36
  10. package/docs/browser-use-architecture-phase2.md +63 -20
  11. package/docs/credential-execution-service.md +7 -5
  12. package/docs/plugins.md +761 -0
  13. package/docs/skills.md +10 -10
  14. package/docs/stt-provider-onboarding.md +17 -45
  15. package/examples/plugins/echo/README.md +132 -0
  16. package/examples/plugins/echo/bun.lock +25 -0
  17. package/examples/plugins/echo/package.json +17 -0
  18. package/examples/plugins/echo/register.ts +187 -0
  19. package/knip.json +8 -22
  20. package/node_modules/@vellumai/ces-client/bun.lock +33 -0
  21. package/node_modules/@vellumai/ces-client/package.json +25 -0
  22. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +631 -0
  23. package/node_modules/@vellumai/ces-client/src/__tests__/package-boundary.test.ts +138 -0
  24. package/node_modules/@vellumai/ces-client/src/credential-rpc.ts +13 -0
  25. package/node_modules/@vellumai/ces-client/src/http-credentials.ts +296 -0
  26. package/node_modules/@vellumai/ces-client/src/http-log-export.ts +111 -0
  27. package/node_modules/@vellumai/ces-client/src/index.ts +43 -0
  28. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +445 -0
  29. package/node_modules/@vellumai/credential-storage/src/__tests__/package-boundary.test.ts +32 -6
  30. package/node_modules/@vellumai/egress-proxy/src/__tests__/package-boundary.test.ts +32 -1
  31. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  32. package/node_modules/@vellumai/gateway-client/bun.lock +39 -0
  33. package/node_modules/@vellumai/gateway-client/package.json +23 -0
  34. package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +343 -0
  35. package/node_modules/@vellumai/gateway-client/src/__tests__/package-boundary.test.ts +140 -0
  36. package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +422 -0
  37. package/node_modules/@vellumai/gateway-client/src/index.ts +35 -0
  38. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +331 -0
  39. package/node_modules/@vellumai/gateway-client/src/types.ts +131 -0
  40. package/node_modules/@vellumai/gateway-client/tsconfig.json +20 -0
  41. package/node_modules/@vellumai/{ces-contracts → service-contracts}/bun.lock +1 -1
  42. package/node_modules/@vellumai/{ces-contracts → service-contracts}/package.json +4 -2
  43. package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/contracts.test.ts +5 -1
  44. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +155 -0
  45. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +23 -0
  46. package/node_modules/@vellumai/service-contracts/src/index.ts +25 -0
  47. package/node_modules/@vellumai/{ces-contracts/src/index.ts → service-contracts/src/transport.ts} +6 -28
  48. package/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  49. package/node_modules/@vellumai/service-contracts/tsconfig.json +20 -0
  50. package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +891 -0
  51. package/node_modules/@vellumai/skill-host-contracts/bun.lock +24 -0
  52. package/node_modules/@vellumai/skill-host-contracts/package.json +18 -0
  53. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +91 -0
  54. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +1348 -0
  55. package/node_modules/@vellumai/skill-host-contracts/src/index.ts +6 -0
  56. package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +11 -0
  57. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +32 -0
  58. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +333 -0
  59. package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +444 -0
  60. package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +20 -0
  61. package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +12 -0
  62. package/openapi.yaml +3135 -692
  63. package/package.json +13 -7
  64. package/scripts/check-circular-deps.ts +80 -0
  65. package/scripts/generate-openapi.ts +24 -7
  66. package/{src/memory/graph/inspect.ts → scripts/memory-inspect.ts} +28 -28
  67. package/src/__tests__/access-request-decision.test.ts +2 -11
  68. package/src/__tests__/acp-session.test.ts +4 -150
  69. package/src/__tests__/actor-token-service.test.ts +17 -678
  70. package/src/__tests__/agent-loop-callsite-precedence.test.ts +2 -6
  71. package/src/__tests__/agent-loop-override-profile.test.ts +404 -0
  72. package/src/__tests__/agent-loop-thinking.test.ts +4 -4
  73. package/src/__tests__/agent-wake-override-profile.test.ts +261 -0
  74. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -1
  75. package/src/__tests__/anthropic-provider.test.ts +127 -15
  76. package/src/__tests__/app-compiler.test.ts +57 -0
  77. package/src/__tests__/app-routes-csp.test.ts +106 -55
  78. package/src/__tests__/approval-cascade.test.ts +10 -357
  79. package/src/__tests__/approval-conversation-turn.test.ts +3 -8
  80. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +1 -1
  81. package/src/__tests__/approval-primitive.test.ts +2 -1
  82. package/src/__tests__/approval-routes-http.test.ts +34 -451
  83. package/src/__tests__/assistant-events-sse-hardening.test.ts +73 -80
  84. package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -3
  85. package/src/__tests__/attachment-upload-trusted-source.test.ts +139 -0
  86. package/src/__tests__/attachments-store.test.ts +46 -1
  87. package/src/__tests__/audit-log-rotation.test.ts +2 -1
  88. package/src/__tests__/auto-analysis-end-to-end.test.ts +9 -20
  89. package/src/__tests__/avatar-generator.test.ts +4 -2
  90. package/src/__tests__/background-shell-bash.test.ts +227 -0
  91. package/src/__tests__/background-shell-host-bash.test.ts +474 -0
  92. package/src/__tests__/background-tool-registry.test.ts +145 -0
  93. package/src/__tests__/background-tool-routes.test.ts +175 -0
  94. package/src/__tests__/btw-routes.test.ts +147 -183
  95. package/src/__tests__/bundled-asset.test.ts +6 -6
  96. package/src/__tests__/call-controller.test.ts +15 -2
  97. package/src/__tests__/call-conversation-messages.test.ts +2 -1
  98. package/src/__tests__/call-domain.test.ts +2 -2
  99. package/src/__tests__/call-pointer-messages.test.ts +11 -13
  100. package/src/__tests__/call-recovery.test.ts +2 -1
  101. package/src/__tests__/call-routes-http.test.ts +3 -14
  102. package/src/__tests__/call-store.test.ts +2 -1
  103. package/src/__tests__/cancel-resolves-conversation-key.test.ts +31 -62
  104. package/src/__tests__/canonical-guardian-store.test.ts +2 -2
  105. package/src/__tests__/catalog-cache.test.ts +69 -0
  106. package/src/__tests__/catalog-files.test.ts +0 -26
  107. package/src/__tests__/ces-rpc-credential-backend.test.ts +1 -1
  108. package/src/__tests__/channel-approval-routes.test.ts +79 -49
  109. package/src/__tests__/channel-approval.test.ts +9 -7
  110. package/src/__tests__/channel-approvals.test.ts +9 -180
  111. package/src/__tests__/channel-delivery-store.test.ts +11 -10
  112. package/src/__tests__/channel-guardian.test.ts +14 -25
  113. package/src/__tests__/channel-readiness-service.test.ts +8 -6
  114. package/src/__tests__/channel-reply-delivery.test.ts +3 -19
  115. package/src/__tests__/channel-retry-sweep.test.ts +2 -5
  116. package/src/__tests__/checker.test.ts +447 -3806
  117. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  118. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +208 -0
  119. package/src/__tests__/cli.test.ts +1 -38
  120. package/src/__tests__/compaction-events.test.ts +500 -0
  121. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  122. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  123. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  124. package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -7
  125. package/src/__tests__/config-model-image-provider.test.ts +109 -0
  126. package/src/__tests__/config-schema-cmd.test.ts +1 -1
  127. package/src/__tests__/config-schema.test.ts +25 -203
  128. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  129. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -25
  130. package/src/__tests__/contact-store-user-file.test.ts +2 -1
  131. package/src/__tests__/contacts-tools.test.ts +71 -18
  132. package/src/__tests__/contacts-write.test.ts +6 -61
  133. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  134. package/src/__tests__/context-search-agent-protocol.test.ts +230 -0
  135. package/src/__tests__/context-search-agent-runner.test.ts +998 -0
  136. package/src/__tests__/context-search-conversations-source.test.ts +320 -0
  137. package/src/__tests__/context-search-fanout.test.ts +380 -0
  138. package/src/__tests__/context-search-memory-source.test.ts +311 -0
  139. package/src/__tests__/context-search-pkb-source.test.ts +444 -0
  140. package/src/__tests__/context-search-types.test.ts +95 -0
  141. package/src/__tests__/context-search-workspace-source.test.ts +545 -0
  142. package/src/__tests__/context-window-manager.test.ts +380 -4
  143. package/src/__tests__/conversation-abort-tool-results.test.ts +14 -2
  144. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +631 -0
  145. package/src/__tests__/conversation-agent-loop-overflow.test.ts +41 -32
  146. package/src/__tests__/conversation-agent-loop.test.ts +54 -143
  147. package/src/__tests__/conversation-analysis-routes.test.ts +60 -82
  148. package/src/__tests__/conversation-attachments.test.ts +9 -20
  149. package/src/__tests__/conversation-attention-store.test.ts +2 -1
  150. package/src/__tests__/conversation-attention-telegram.test.ts +4 -2
  151. package/src/__tests__/conversation-clear-safety.test.ts +53 -95
  152. package/src/__tests__/conversation-confirmation-signals.test.ts +7 -40
  153. package/src/__tests__/conversation-crud-inference-profile.test.ts +54 -0
  154. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +63 -157
  155. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  156. package/src/__tests__/conversation-disk-view.test.ts +5 -4
  157. package/src/__tests__/conversation-fork-crud.test.ts +26 -55
  158. package/src/__tests__/conversation-fork-route.test.ts +5 -74
  159. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  160. package/src/__tests__/conversation-inference-profile-list.test.ts +128 -0
  161. package/src/__tests__/conversation-inference-profile-route.test.ts +216 -0
  162. package/src/__tests__/conversation-init.benchmark.test.ts +4 -95
  163. package/src/__tests__/conversation-key-store-disk-view.test.ts +2 -1
  164. package/src/__tests__/conversation-lifecycle.test.ts +0 -1
  165. package/src/__tests__/conversation-list-source.test.ts +2 -2
  166. package/src/__tests__/conversation-load-history-repair.test.ts +0 -1
  167. package/src/__tests__/conversation-pairing.test.ts +174 -11
  168. package/src/__tests__/conversation-pre-run-repair.test.ts +137 -294
  169. package/src/__tests__/conversation-process-callsite.test.ts +3 -1
  170. package/src/__tests__/conversation-provider-retry-repair.test.ts +22 -8
  171. package/src/__tests__/conversation-queue.test.ts +30 -47
  172. package/src/__tests__/conversation-routes-disk-view.test.ts +131 -103
  173. package/src/__tests__/conversation-routes-guardian-reply.test.ts +80 -55
  174. package/src/__tests__/conversation-routes-slash-commands.test.ts +83 -12
  175. package/src/__tests__/conversation-runtime-assembly.test.ts +196 -194
  176. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  177. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  178. package/src/__tests__/conversation-slash-commands.test.ts +6 -43
  179. package/src/__tests__/conversation-slash-queue.test.ts +7 -3
  180. package/src/__tests__/conversation-slash-unknown.test.ts +25 -3
  181. package/src/__tests__/conversation-speed-override.test.ts +6 -2
  182. package/src/__tests__/conversation-starter-routes.test.ts +177 -55
  183. package/src/__tests__/conversation-starters-cadence.test.ts +2 -2
  184. package/src/__tests__/conversation-store.test.ts +2 -375
  185. package/src/__tests__/conversation-title-service.test.ts +116 -0
  186. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +42 -3
  187. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +6 -6
  188. package/src/__tests__/conversation-unread-route.test.ts +1 -1
  189. package/src/__tests__/conversation-usage.test.ts +3 -2
  190. package/src/__tests__/conversation-wipe.test.ts +2 -103
  191. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -2
  192. package/src/__tests__/conversation-workspace-injection.test.ts +3 -1
  193. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -2
  194. package/src/__tests__/conversations-defer-cli.test.ts +150 -0
  195. package/src/__tests__/credential-execution-admin-cli.test.ts +1 -1
  196. package/src/__tests__/credential-execution-api-key-propagation.test.ts +2 -2
  197. package/src/__tests__/credential-execution-approval-bridge.test.ts +22 -289
  198. package/src/__tests__/credential-execution-client.test.ts +1 -1
  199. package/src/__tests__/credential-execution-managed-contract.test.ts +1 -1
  200. package/src/__tests__/credential-health-service.test.ts +78 -9
  201. package/src/__tests__/credential-security-invariants.test.ts +16 -2
  202. package/src/__tests__/credentials-cli.test.ts +45 -21
  203. package/src/__tests__/daemon-credential-client.test.ts +23 -108
  204. package/src/__tests__/db-acp-history.test.ts +284 -0
  205. package/src/__tests__/db-activation-state.test.ts +240 -0
  206. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +2 -1
  207. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +248 -0
  208. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +2 -1
  209. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +116 -0
  210. package/src/__tests__/db-rename-inference-profile-snake-case-migration.test.ts +132 -0
  211. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  212. package/src/__tests__/delete-propagation.test.ts +3 -2
  213. package/src/__tests__/deterministic-verification-control-plane.test.ts +39 -32
  214. package/src/__tests__/dm-backfill.test.ts +3 -2
  215. package/src/__tests__/edit-propagation.test.ts +5 -7
  216. package/src/__tests__/embedding-managed-proxy-selection.test.ts +1 -1
  217. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  218. package/src/__tests__/events-client-registration.test.ts +297 -0
  219. package/src/__tests__/file-write-tool.test.ts +2 -4
  220. package/src/__tests__/filing-service.test.ts +144 -17
  221. package/src/__tests__/first-greeting.test.ts +247 -5
  222. package/src/__tests__/followup-tools.test.ts +2 -1
  223. package/src/__tests__/gateway-client-managed-outbound.test.ts +8 -12
  224. package/src/__tests__/gateway-only-enforcement.test.ts +2 -6
  225. package/src/__tests__/gateway-only-guard.test.ts +4 -3
  226. package/src/__tests__/gemini-provider.test.ts +276 -10
  227. package/src/__tests__/graph-extraction-event-date.test.ts +30 -0
  228. package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -1
  229. package/src/__tests__/guardian-action-followup-executor.test.ts +2 -2
  230. package/src/__tests__/guardian-action-followup-store.test.ts +2 -1
  231. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +9 -9
  232. package/src/__tests__/guardian-action-late-reply.test.ts +2 -1
  233. package/src/__tests__/guardian-action-store.test.ts +2 -1
  234. package/src/__tests__/guardian-action-sweep.test.ts +9 -8
  235. package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -1
  236. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +21 -118
  237. package/src/__tests__/guardian-dispatch.test.ts +14 -11
  238. package/src/__tests__/guardian-grant-minting.test.ts +9 -15
  239. package/src/__tests__/guardian-outbound-http.test.ts +71 -106
  240. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -2
  241. package/src/__tests__/guardian-routing-invariants.test.ts +34 -90
  242. package/src/__tests__/guardian-routing-state.test.ts +14 -22
  243. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -2
  244. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +253 -0
  245. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +8 -4
  246. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  247. package/src/__tests__/heartbeat-service.test.ts +39 -21
  248. package/src/__tests__/helpers/call-route-handler.ts +72 -0
  249. package/src/__tests__/helpers/channel-test-adapter.ts +161 -0
  250. package/src/__tests__/helpers/gateway-classify-mock.ts +67 -0
  251. package/src/__tests__/helpers/mock-logger.ts +36 -0
  252. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  253. package/src/__tests__/home-state-routes.test.ts +10 -31
  254. package/src/__tests__/host-browser-e2e-cloud.test.ts +309 -1
  255. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +12 -2
  256. package/src/__tests__/host-browser-routes.test.ts +36 -91
  257. package/src/__tests__/host-browser-ws-events-e2e.test.ts +10 -2
  258. package/src/__tests__/host-proxy-interface.test.ts +38 -4
  259. package/src/__tests__/host-shell-tool.test.ts +2 -4
  260. package/src/__tests__/host-transfer-pending-interactions.test.ts +160 -0
  261. package/src/__tests__/host-transfer-proxy.test.ts +733 -0
  262. package/src/__tests__/http-conversation-lineage.test.ts +3 -2
  263. package/src/__tests__/http-user-message-parity.test.ts +20 -11
  264. package/src/__tests__/image-credentials.test.ts +137 -0
  265. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  266. package/src/__tests__/inbound-invite-redemption.test.ts +3 -2
  267. package/src/__tests__/injector-chain.test.ts +525 -0
  268. package/src/__tests__/inline-skill-load-permissions.test.ts +41 -206
  269. package/src/__tests__/install-skill-routing.test.ts +1 -1
  270. package/src/__tests__/intent-routing.test.ts +0 -26
  271. package/src/__tests__/invite-redemption-service.test.ts +2 -1
  272. package/src/__tests__/invite-routes-http.test.ts +80 -12
  273. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -1
  274. package/src/__tests__/jobs-store-upsert-debounced.test.ts +2 -1
  275. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +157 -0
  276. package/src/__tests__/list-messages-attachments.test.ts +52 -55
  277. package/src/__tests__/list-messages-page-latest.test.ts +283 -0
  278. package/src/__tests__/list-messages-tool-merge.test.ts +16 -17
  279. package/src/__tests__/llm-call-pipeline.test.ts +284 -0
  280. package/src/__tests__/llm-context-normalization.test.ts +69 -4
  281. package/src/__tests__/llm-context-route-provider.test.ts +39 -113
  282. package/src/__tests__/llm-request-log-turn-query.test.ts +2 -1
  283. package/src/__tests__/llm-resolver.test.ts +211 -0
  284. package/src/__tests__/llm-schema.test.ts +56 -0
  285. package/src/__tests__/llm-usage-store.test.ts +2 -1
  286. package/src/__tests__/log-export-workspace.test.ts +28 -17
  287. package/src/__tests__/mcp-abort-signal.test.ts +2 -3
  288. package/src/__tests__/mcp-client-auth.test.ts +2 -3
  289. package/src/__tests__/media-generate-image.test.ts +119 -13
  290. package/src/__tests__/memory-admin-recall.test.ts +221 -0
  291. package/src/__tests__/memory-recall-log-store.test.ts +2 -1
  292. package/src/__tests__/memory-retrieval-pipeline.test.ts +399 -0
  293. package/src/__tests__/memory-upsert-concurrency.test.ts +3 -1
  294. package/src/__tests__/migration-cross-version-compatibility.test.ts +14 -13
  295. package/src/__tests__/migration-export-http.test.ts +17 -17
  296. package/src/__tests__/migration-export-to-gcs.test.ts +491 -0
  297. package/src/__tests__/migration-import-commit-http.test.ts +16 -16
  298. package/src/__tests__/migration-import-from-gcs.test.ts +533 -0
  299. package/src/__tests__/migration-import-from-url.test.ts +21 -91
  300. package/src/__tests__/migration-import-preflight-http.test.ts +13 -13
  301. package/src/__tests__/migration-jobs-status.test.ts +164 -0
  302. package/src/__tests__/migration-validate-http.test.ts +48 -83
  303. package/src/__tests__/mock-gateway-ipc.ts +32 -62
  304. package/src/__tests__/model-intents.test.ts +16 -1
  305. package/src/__tests__/nl-approval-parser.test.ts +13 -17
  306. package/src/__tests__/non-member-access-request.test.ts +13 -5
  307. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  308. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  309. package/src/__tests__/notification-guardian-path.test.ts +15 -8
  310. package/src/__tests__/notification-schedule-notify-dedup.test.ts +109 -0
  311. package/src/__tests__/notification-telegram-adapter.test.ts +57 -55
  312. package/src/__tests__/oauth-apps-routes.test.ts +77 -123
  313. package/src/__tests__/oauth-cli.test.ts +28 -13
  314. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  315. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  316. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  317. package/src/__tests__/oauth-provider-visibility.test.ts +6 -6
  318. package/src/__tests__/oauth-providers-routes.test.ts +81 -103
  319. package/src/__tests__/oauth-store.test.ts +44 -77
  320. package/src/__tests__/oauth2-gateway-transport.test.ts +6 -3
  321. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  322. package/src/__tests__/openai-image-service.test.ts +368 -0
  323. package/src/__tests__/openai-provider.test.ts +105 -6
  324. package/src/__tests__/openai-responses-provider.test.ts +146 -4
  325. package/src/__tests__/openrouter-provider-only.test.ts +22 -4
  326. package/src/__tests__/overflow-reduce-pipeline.test.ts +671 -0
  327. package/src/__tests__/permission-types.test.ts +3 -18
  328. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  329. package/src/__tests__/persistence-pipeline.test.ts +378 -0
  330. package/src/__tests__/pipeline-runner.test.ts +565 -0
  331. package/src/__tests__/platform-bash-auto-approve.test.ts +27 -20
  332. package/src/__tests__/platform.test.ts +10 -59
  333. package/src/__tests__/playbook-execution.test.ts +2 -1
  334. package/src/__tests__/playbook-tools.test.ts +2 -1
  335. package/src/__tests__/plugin-bootstrap.test.ts +529 -0
  336. package/src/__tests__/plugin-registry.test.ts +303 -0
  337. package/src/__tests__/plugin-route-contribution.test.ts +294 -0
  338. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  339. package/src/__tests__/plugin-tool-contribution.test.ts +292 -0
  340. package/src/__tests__/plugin-types.test.ts +320 -0
  341. package/src/__tests__/pricing.test.ts +195 -14
  342. package/src/__tests__/profiler-routes.test.ts +112 -177
  343. package/src/__tests__/provider-send-message-override-profile.test.ts +223 -0
  344. package/src/__tests__/proxy-approval-callback.test.ts +6 -493
  345. package/src/__tests__/qdrant-collection-migration.test.ts +7 -7
  346. package/src/__tests__/reaction-persistence.test.ts +4 -2
  347. package/src/__tests__/rebuild-index-graph-nodes.test.ts +1 -1
  348. package/src/__tests__/recording-handler.test.ts +0 -2
  349. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  350. package/src/__tests__/registry.test.ts +1 -2
  351. package/src/__tests__/relay-server.test.ts +19 -4
  352. package/src/__tests__/require-fresh-approval.test.ts +19 -168
  353. package/src/__tests__/resolve-trust-class.test.ts +2 -1
  354. package/src/__tests__/retry-thinking-tool-choice.test.ts +19 -7
  355. package/src/__tests__/retry-verbosity-normalization.test.ts +139 -0
  356. package/src/__tests__/runtime-attachment-metadata.test.ts +26 -6
  357. package/src/__tests__/runtime-events-sse-parity.test.ts +12 -13
  358. package/src/__tests__/runtime-events-sse.test.ts +13 -21
  359. package/src/__tests__/schedule-routes.test.ts +304 -77
  360. package/src/__tests__/schedule-store.test.ts +119 -1
  361. package/src/__tests__/schedule-tools.test.ts +2 -1
  362. package/src/__tests__/scheduler-recurrence.test.ts +16 -71
  363. package/src/__tests__/scheduler-reuse-conversation.test.ts +12 -51
  364. package/src/__tests__/scheduler-wake.test.ts +356 -0
  365. package/src/__tests__/scoped-approval-grants.test.ts +2 -1
  366. package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -1
  367. package/src/__tests__/secret-detection-handler.test.ts +2 -19
  368. package/src/__tests__/secret-ingress-http.test.ts +38 -21
  369. package/src/__tests__/secret-routes-managed-proxy.test.ts +46 -102
  370. package/src/__tests__/secret-scanner-executor.test.ts +1 -2
  371. package/src/__tests__/send-endpoint-busy.test.ts +38 -25
  372. package/src/__tests__/sequence-store.test.ts +2 -1
  373. package/src/__tests__/server-history-render.test.ts +2 -2
  374. package/src/__tests__/service-contracts-import-guard.test.ts +185 -0
  375. package/src/__tests__/set-permission-mode.test.ts +0 -10
  376. package/src/__tests__/settings-routes.test.ts +35 -68
  377. package/src/__tests__/skill-boundary-guard.test.ts +105 -0
  378. package/src/__tests__/skill-load-inline-command.test.ts +2 -2
  379. package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
  380. package/src/__tests__/skill-runtime-path.test.ts +64 -0
  381. package/src/__tests__/skills-file-content-endpoint.test.ts +0 -2
  382. package/src/__tests__/slack-inbound-verification.test.ts +11 -2
  383. package/src/__tests__/slack-messaging-token-resolution.test.ts +1 -3
  384. package/src/__tests__/slack-reaction-approvals.test.ts +4 -4
  385. package/src/__tests__/slack-share-routes.test.ts +37 -72
  386. package/src/__tests__/subagent-call-site-routing.test.ts +79 -0
  387. package/src/__tests__/subagent-fork-spawn.test.ts +20 -28
  388. package/src/__tests__/subagent-notify-parent.test.ts +6 -29
  389. package/src/__tests__/subagent-role-registry.test.ts +3 -3
  390. package/src/__tests__/subagent-spawn-tool-fork.test.ts +52 -104
  391. package/src/__tests__/subagent-tools.test.ts +0 -1
  392. package/src/__tests__/suggestion-routes.test.ts +149 -57
  393. package/src/__tests__/task-compiler.test.ts +2 -1
  394. package/src/__tests__/task-management-tools.test.ts +2 -1
  395. package/src/__tests__/task-memory-cleanup.test.ts +3 -1
  396. package/src/__tests__/task-scheduler.test.ts +5 -16
  397. package/src/__tests__/telegram-config.test.ts +0 -1
  398. package/src/__tests__/terminal-tools.test.ts +5 -314
  399. package/src/__tests__/thread-backfill.test.ts +3 -2
  400. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  401. package/src/__tests__/token-estimate-pipeline.test.ts +484 -0
  402. package/src/__tests__/tool-approval-handler.test.ts +21 -63
  403. package/src/__tests__/tool-audit-listener.test.ts +3 -3
  404. package/src/__tests__/tool-domain-event-publisher.test.ts +3 -3
  405. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  406. package/src/__tests__/tool-execute-pipeline.test.ts +429 -0
  407. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +61 -4
  408. package/src/__tests__/tool-executor-lifecycle-events.test.ts +28 -56
  409. package/src/__tests__/tool-executor.test.ts +434 -1604
  410. package/src/__tests__/tool-grant-request-escalation.test.ts +90 -311
  411. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  412. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  413. package/src/__tests__/trust-context-guards.test.ts +1 -1
  414. package/src/__tests__/trusted-contact-approval-notifier.test.ts +7 -15
  415. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +178 -354
  416. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  417. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  418. package/src/__tests__/trusted-contact-verification.test.ts +2 -1
  419. package/src/__tests__/turn-boundary-resolution.test.ts +2 -1
  420. package/src/__tests__/twilio-routes.test.ts +25 -66
  421. package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -7
  422. package/src/__tests__/usage-routes.test.ts +73 -90
  423. package/src/__tests__/user-plugin-loader.test.ts +233 -0
  424. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -2
  425. package/src/__tests__/verification-control-plane-policy.test.ts +95 -14
  426. package/src/__tests__/voice-ingress-preflight.test.ts +5 -5
  427. package/src/__tests__/voice-invite-redemption.test.ts +2 -1
  428. package/src/__tests__/voice-scoped-grant-consumer.test.ts +3 -3
  429. package/src/__tests__/voice-session-bridge.test.ts +285 -106
  430. package/src/__tests__/volume-security-guard.test.ts +0 -2
  431. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -1
  432. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +3 -1
  433. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +2 -1
  434. package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +1 -1
  435. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  436. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  437. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  438. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  439. package/src/__tests__/workspace-migration-052-seed-default-inference-profiles.test.ts +260 -0
  440. package/src/__tests__/workspace-migration-053-release-notes-acp-codex.test.ts +225 -0
  441. package/src/__tests__/workspace-migration-054-seed-recall-callsite.test.ts +235 -0
  442. package/src/__tests__/workspace-migration-055-release-notes-agentic-recall.test.ts +128 -0
  443. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +232 -0
  444. package/src/__tests__/workspace-migration-acp-sessions-ui.test.ts +144 -0
  445. package/src/__tests__/workspace-migration-drop-user-md.test.ts +1 -1
  446. package/src/__tests__/workspace-migration-memory-v2-init.test.ts +274 -0
  447. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  448. package/src/__tests__/workspace-policy.test.ts +21 -3
  449. package/src/acp/__tests__/client-handler.test.ts +64 -0
  450. package/src/acp/__tests__/helpers/acp-config-stub.ts +62 -0
  451. package/src/acp/__tests__/helpers/which-stub.ts +45 -0
  452. package/src/acp/__tests__/session-manager-persistence.test.ts +366 -0
  453. package/src/acp/__tests__/session-manager-startup.test.ts +159 -0
  454. package/src/acp/__tests__/session-manager.test.ts +83 -0
  455. package/src/acp/client-handler.ts +23 -139
  456. package/src/acp/resolve-agent.test.ts +291 -0
  457. package/src/acp/resolve-agent.ts +176 -0
  458. package/src/acp/session-manager.ts +166 -7
  459. package/src/acp/types.ts +2 -50
  460. package/src/agent/loop.ts +365 -104
  461. package/src/agent/message-types.ts +0 -2
  462. package/src/approvals/AGENTS.md +1 -1
  463. package/src/approvals/__tests__/guardian-feed-event.test.ts +296 -0
  464. package/src/approvals/approval-primitive.ts +3 -20
  465. package/src/approvals/guardian-decision-primitive.ts +37 -68
  466. package/src/approvals/guardian-request-resolvers.ts +109 -103
  467. package/src/avatar/character-components.ts +6 -6
  468. package/src/{config/bundled-skills/settings/tools → avatar}/identity-avatar.ts +1 -1
  469. package/src/backup/__tests__/backup-worker.test.ts +2 -15
  470. package/src/backup/__tests__/paths.test.ts +3 -2
  471. package/src/backup/backup-worker.ts +3 -24
  472. package/src/backup/paths.ts +2 -18
  473. package/src/backup/restore.ts +7 -11
  474. package/src/browser/__tests__/operations.test.ts +0 -35
  475. package/src/browser/operations.ts +1 -47
  476. package/src/bundler/app-compiler.ts +84 -1
  477. package/src/bundler/package-resolver.ts +2 -6
  478. package/src/calls/active-call-lease.ts +1 -1
  479. package/src/calls/call-constants.ts +1 -1
  480. package/src/calls/call-controller.ts +1 -5
  481. package/src/calls/call-domain.ts +14 -14
  482. package/src/calls/call-pointer-messages.ts +4 -9
  483. package/src/calls/call-state.ts +2 -2
  484. package/src/calls/call-store.ts +2 -1
  485. package/src/calls/guardian-action-sweep.ts +9 -25
  486. package/src/calls/guardian-dispatch.ts +1 -20
  487. package/src/calls/media-stream-audio-transcode.ts +2 -41
  488. package/src/calls/media-stream-server.ts +2 -3
  489. package/src/calls/media-stream-stt-session.ts +1 -3
  490. package/src/calls/relay-access-wait.ts +5 -8
  491. package/src/calls/relay-server.ts +15 -18
  492. package/src/calls/relay-setup-router.ts +2 -2
  493. package/src/calls/relay-verification.ts +4 -4
  494. package/src/calls/twilio-rest.ts +1 -1
  495. package/src/calls/twilio-routes.ts +160 -78
  496. package/src/calls/voice-control-protocol.ts +10 -10
  497. package/src/calls/voice-ingress-preflight.ts +2 -2
  498. package/src/calls/voice-session-bridge.ts +137 -42
  499. package/src/channels/__tests__/types.test.ts +28 -6
  500. package/src/channels/permission-profiles.ts +2 -72
  501. package/src/channels/types.ts +48 -30
  502. package/src/cli/AGENTS.md +1 -0
  503. package/src/cli/__tests__/notifications.test.ts +92 -214
  504. package/src/cli/commands/__tests__/attachment.test.ts +14 -8
  505. package/src/cli/commands/__tests__/backup.test.ts +4 -15
  506. package/src/cli/commands/__tests__/browser.test.ts +36 -31
  507. package/src/cli/commands/__tests__/cache.test.ts +23 -18
  508. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  509. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  510. package/src/cli/commands/__tests__/memory-v2.test.ts +396 -0
  511. package/src/cli/commands/__tests__/task.test.ts +36 -35
  512. package/src/cli/commands/__tests__/trust.test.ts +602 -0
  513. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  514. package/src/cli/commands/__tests__/ui-confirm.test.ts +14 -14
  515. package/src/cli/commands/__tests__/ui.test.ts +17 -17
  516. package/src/cli/commands/__tests__/watchers.test.ts +29 -29
  517. package/src/cli/commands/__tests__/webhooks.test.ts +544 -0
  518. package/src/cli/commands/attachment.ts +12 -8
  519. package/src/cli/commands/auth.ts +1 -1
  520. package/src/cli/commands/avatar.ts +192 -9
  521. package/src/cli/commands/backup.ts +16 -46
  522. package/src/cli/commands/browser.ts +52 -4
  523. package/src/cli/commands/cache.ts +7 -5
  524. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  525. package/src/cli/commands/clients.ts +137 -0
  526. package/src/cli/commands/completions.ts +3 -10
  527. package/src/cli/commands/contacts.ts +10 -10
  528. package/src/cli/commands/conversations-defer.ts +364 -0
  529. package/src/cli/commands/conversations-import.ts +2 -3
  530. package/src/cli/commands/conversations.ts +115 -57
  531. package/src/cli/commands/credential-execution.ts +1 -1
  532. package/src/cli/commands/credentials.ts +139 -5
  533. package/src/cli/commands/default-action.ts +1 -1
  534. package/src/cli/commands/domain.ts +2 -2
  535. package/src/cli/commands/email.ts +7 -7
  536. package/src/cli/commands/image-generation.ts +33 -34
  537. package/src/cli/commands/keys.ts +2 -2
  538. package/src/cli/commands/mcp.ts +1 -1
  539. package/src/cli/commands/memory-v2.ts +343 -0
  540. package/src/cli/commands/memory.ts +8 -8
  541. package/src/cli/commands/notifications.ts +87 -121
  542. package/src/cli/commands/oauth/__tests__/connect.test.ts +23 -5
  543. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +1 -1
  544. package/src/cli/commands/oauth/__tests__/mode.test.ts +1 -1
  545. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  546. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  547. package/src/cli/commands/oauth/__tests__/status.test.ts +1 -1
  548. package/src/cli/commands/oauth/__tests__/token.test.ts +1 -1
  549. package/src/cli/commands/oauth/connect.ts +4 -4
  550. package/src/cli/commands/oauth/providers.ts +176 -8
  551. package/src/cli/commands/oauth/shared.ts +29 -2
  552. package/src/cli/commands/oauth/status.ts +46 -36
  553. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -6
  554. package/src/cli/commands/platform/__tests__/connect.test.ts +23 -11
  555. package/src/cli/commands/platform/__tests__/disconnect.test.ts +22 -10
  556. package/src/cli/commands/platform/__tests__/status.test.ts +22 -10
  557. package/src/cli/commands/platform/connect.ts +3 -3
  558. package/src/cli/commands/platform/disconnect.ts +4 -6
  559. package/src/cli/commands/platform/index.ts +12 -10
  560. package/src/cli/commands/routes.ts +7 -1
  561. package/src/cli/commands/sequence.ts +7 -7
  562. package/src/cli/commands/skills.ts +189 -84
  563. package/src/cli/commands/task.ts +12 -10
  564. package/src/cli/commands/trust.ts +460 -162
  565. package/src/cli/commands/ui.ts +3 -3
  566. package/src/cli/commands/usage.ts +10 -5
  567. package/src/cli/commands/watchers.ts +8 -8
  568. package/src/cli/commands/webhooks.ts +270 -0
  569. package/src/cli/lib/daemon-avatar-client.ts +37 -0
  570. package/src/cli/lib/daemon-credential-client.ts +27 -189
  571. package/src/cli/lib/ipc-params.ts +22 -0
  572. package/src/cli/program.ts +29 -29
  573. package/src/cli.ts +1 -61
  574. package/src/config/__tests__/backup-schema.test.ts +7 -2
  575. package/src/config/acp-defaults.test.ts +57 -0
  576. package/src/config/acp-defaults.ts +40 -0
  577. package/src/config/acp-schema.ts +1 -1
  578. package/src/config/assistant-feature-flags.ts +18 -142
  579. package/src/config/bundled-skills/acp/SKILL.md +44 -16
  580. package/src/config/bundled-skills/acp/TOOLS.json +45 -1
  581. package/src/config/bundled-skills/{screen-watch/tools/start-screen-watch.ts → acp/tools/acp-list-agents.ts} +2 -2
  582. package/src/config/bundled-skills/acp/tools/acp-steer.ts +12 -0
  583. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  584. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  585. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  586. package/src/config/bundled-skills/contacts/tools/contact-search.ts +25 -51
  587. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +31 -44
  588. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  589. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  590. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  591. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +6 -6
  592. package/src/config/bundled-skills/media-processing/services/reduce.ts +0 -13
  593. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  594. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  595. package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +1 -1
  596. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
  597. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  598. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  599. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -1
  600. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +1 -1
  601. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -1
  602. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  603. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  604. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  605. package/src/config/bundled-skills/settings/SKILL.md +2 -17
  606. package/src/config/bundled-skills/settings/TOOLS.json +0 -56
  607. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  608. package/src/config/bundled-tool-registry.ts +4 -21
  609. package/src/config/env.ts +7 -8
  610. package/src/config/feature-flag-registry.json +25 -17
  611. package/src/config/llm-resolver.ts +51 -33
  612. package/src/config/loader.ts +12 -15
  613. package/src/config/schema.ts +22 -70
  614. package/src/config/schemas/__tests__/filing.test.ts +58 -0
  615. package/src/config/schemas/__tests__/memory-v2.test.ts +186 -0
  616. package/src/config/schemas/backup.ts +1 -1
  617. package/src/config/schemas/conversations.ts +16 -0
  618. package/src/config/schemas/filing.ts +12 -0
  619. package/src/config/schemas/host-browser.ts +2 -2
  620. package/src/config/schemas/inference.ts +0 -2
  621. package/src/config/schemas/ingress.ts +1 -1
  622. package/src/config/schemas/llm.ts +51 -10
  623. package/src/config/schemas/memory-storage.ts +1 -1
  624. package/src/config/schemas/memory-v2.ts +176 -0
  625. package/src/config/schemas/memory.ts +2 -0
  626. package/src/config/schemas/security.ts +0 -60
  627. package/src/config/schemas/services.ts +46 -7
  628. package/src/config/schemas/tts.ts +11 -0
  629. package/src/config/skill-state.ts +6 -2
  630. package/src/config/skills.ts +95 -6
  631. package/src/config/types.ts +0 -41
  632. package/src/contacts/contact-store.ts +2 -2
  633. package/src/contacts/contacts-write.ts +0 -38
  634. package/src/contacts/types.ts +8 -10
  635. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  636. package/src/context/prompts/compact.md +26 -12
  637. package/src/context/token-estimator.ts +1 -1
  638. package/src/context/tool-result-truncation.ts +4 -64
  639. package/src/context/window-manager.ts +191 -17
  640. package/src/credential-execution/approval-bridge.ts +7 -69
  641. package/src/credential-execution/client.ts +17 -422
  642. package/src/credential-execution/feature-gates.ts +1 -2
  643. package/src/credential-execution/managed-catalog.ts +1 -1
  644. package/src/credential-health/credential-health-service.ts +20 -7
  645. package/src/daemon/__tests__/conversation-feed-event.test.ts +304 -0
  646. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  647. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
  648. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  649. package/src/daemon/__tests__/daemon-skill-host.test.ts +272 -0
  650. package/src/daemon/__tests__/meet-host-supervisor.test.ts +587 -0
  651. package/src/daemon/__tests__/meet-manifest-loader.test.ts +463 -0
  652. package/src/daemon/approval-generators.ts +2 -14
  653. package/src/daemon/classifier.ts +0 -106
  654. package/src/daemon/config-watcher.ts +14 -56
  655. package/src/daemon/connection-policy.ts +0 -14
  656. package/src/daemon/context-overflow-policy.ts +4 -13
  657. package/src/daemon/conversation-agent-loop-handlers.ts +120 -28
  658. package/src/daemon/conversation-agent-loop.ts +1113 -701
  659. package/src/daemon/conversation-attachments.ts +5 -81
  660. package/src/daemon/conversation-error.ts +9 -5
  661. package/src/daemon/conversation-history.ts +11 -20
  662. package/src/daemon/conversation-launch.ts +1 -1
  663. package/src/daemon/conversation-lifecycle.ts +37 -19
  664. package/src/daemon/conversation-messaging.ts +1 -1
  665. package/src/daemon/conversation-notifiers.ts +3 -111
  666. package/src/daemon/conversation-process.ts +23 -20
  667. package/src/daemon/conversation-runtime-assembly.ts +530 -471
  668. package/src/daemon/conversation-slash.ts +4 -160
  669. package/src/daemon/conversation-store.ts +368 -0
  670. package/src/daemon/conversation-surfaces.ts +5 -4
  671. package/src/daemon/conversation-tool-setup.ts +49 -161
  672. package/src/daemon/conversation.ts +126 -217
  673. package/src/daemon/daemon-control.ts +3 -3
  674. package/src/daemon/daemon-skill-host.ts +262 -0
  675. package/src/daemon/external-plugins-bootstrap.ts +532 -0
  676. package/src/daemon/first-greeting.ts +191 -14
  677. package/src/daemon/handlers/config-channels.ts +2 -2
  678. package/src/daemon/handlers/config-embeddings.ts +1 -1
  679. package/src/daemon/handlers/config-ingress.ts +24 -2
  680. package/src/daemon/handlers/config-model.test.ts +17 -0
  681. package/src/daemon/handlers/config-model.ts +18 -52
  682. package/src/daemon/handlers/config-telegram.ts +6 -53
  683. package/src/daemon/handlers/config-voice.ts +1 -1
  684. package/src/daemon/handlers/conversations.ts +22 -156
  685. package/src/daemon/handlers/recording.ts +1 -1
  686. package/src/daemon/handlers/shared.ts +34 -35
  687. package/src/daemon/handlers/skills.ts +20 -24
  688. package/src/daemon/host-transfer-proxy.ts +500 -0
  689. package/src/daemon/lifecycle.ts +56 -326
  690. package/src/daemon/meet-host-startup.ts +51 -0
  691. package/src/daemon/meet-host-supervisor.ts +781 -0
  692. package/src/daemon/meet-manifest-loader.ts +410 -0
  693. package/src/daemon/memory-v2-startup.ts +35 -0
  694. package/src/daemon/message-protocol.ts +4 -7
  695. package/src/daemon/message-types/acp.ts +1 -0
  696. package/src/daemon/message-types/computer-use.ts +2 -34
  697. package/src/daemon/message-types/conversations.ts +65 -2
  698. package/src/daemon/message-types/host-transfer.ts +41 -0
  699. package/src/daemon/message-types/integrations.ts +6 -0
  700. package/src/daemon/message-types/messages.ts +26 -14
  701. package/src/daemon/message-types/schedules.ts +1 -0
  702. package/src/daemon/message-types/settings.ts +0 -6
  703. package/src/daemon/message-types/shared.ts +5 -2
  704. package/src/daemon/message-types/subagents.ts +2 -1
  705. package/src/daemon/message-types/workspace.ts +0 -2
  706. package/src/daemon/pkb-reminder-builder.test.ts +13 -12
  707. package/src/daemon/pkb-reminder-builder.ts +8 -16
  708. package/src/daemon/process-message.ts +616 -0
  709. package/src/daemon/providers-setup.ts +14 -6
  710. package/src/daemon/server.ts +79 -1272
  711. package/src/daemon/shutdown-handlers.ts +3 -13
  712. package/src/daemon/startup-error.ts +1 -1
  713. package/src/daemon/tool-side-effects.ts +14 -56
  714. package/src/daemon/trust-context.ts +32 -0
  715. package/src/daemon/wake-target-adapter.ts +223 -0
  716. package/src/email/feature-gate.ts +1 -1
  717. package/src/events/domain-events.ts +1 -8
  718. package/src/events/tool-audit-listener.ts +2 -8
  719. package/src/events/tool-metrics-listener.ts +1 -4
  720. package/src/filing/filing-service.ts +194 -54
  721. package/src/followups/followup-store.ts +3 -71
  722. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +228 -0
  723. package/src/heartbeat/heartbeat-service.ts +52 -8
  724. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  725. package/src/home/__tests__/phase5-exit-criteria.test.ts +18 -1
  726. package/src/home/__tests__/rollup-producer.test.ts +67 -2
  727. package/src/home/assistant-feed-authoring.ts +8 -1
  728. package/src/home/emit-feed-event.ts +7 -0
  729. package/src/home/feed-types.ts +42 -3
  730. package/src/home/relationship-state-writer.ts +1 -1
  731. package/src/home/rewrite-command-preview.ts +66 -0
  732. package/src/home/rewrite-feed-title.ts +58 -0
  733. package/src/home/rollup-producer.ts +16 -3
  734. package/src/inbound/platform-callback-registration.ts +1 -17
  735. package/src/ipc/__tests__/attachment-ipc.test.ts +128 -66
  736. package/src/ipc/__tests__/browser-ipc.test.ts +75 -51
  737. package/src/ipc/__tests__/cache-ipc.test.ts +52 -107
  738. package/src/ipc/__tests__/cli-ipc.test.ts +9 -6
  739. package/src/ipc/__tests__/skill-server-bidirectional.test.ts +254 -0
  740. package/src/ipc/__tests__/skill-server.test.ts +182 -0
  741. package/src/ipc/__tests__/socket-path.test.ts +44 -37
  742. package/src/ipc/__tests__/ui-request-route.test.ts +241 -216
  743. package/src/ipc/__tests__/watcher-ipc.test.ts +33 -33
  744. package/src/ipc/assistant-server.ts +450 -0
  745. package/src/ipc/cli-client.ts +3 -3
  746. package/src/ipc/gateway-client.test.ts +131 -0
  747. package/src/ipc/gateway-client.ts +98 -120
  748. package/src/ipc/ipc-framing.ts +281 -0
  749. package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +152 -0
  750. package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +219 -0
  751. package/src/ipc/routes/db-proxy.ts +73 -0
  752. package/src/ipc/routes/route-adapter.ts +32 -0
  753. package/src/ipc/routes/trust-rules.test.ts +218 -0
  754. package/src/ipc/skill-ipc-types.ts +13 -0
  755. package/src/ipc/skill-routes/__tests__/config.test.ts +146 -0
  756. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +402 -0
  757. package/src/ipc/skill-routes/__tests__/identity.test.ts +81 -0
  758. package/src/ipc/skill-routes/__tests__/log.test.ts +133 -0
  759. package/src/ipc/skill-routes/__tests__/memory.test.ts +178 -0
  760. package/src/ipc/skill-routes/__tests__/platform.test.ts +111 -0
  761. package/src/ipc/skill-routes/__tests__/providers.test.ts +265 -0
  762. package/src/ipc/skill-routes/__tests__/registries.test.ts +361 -0
  763. package/src/ipc/skill-routes/config.ts +47 -0
  764. package/src/ipc/skill-routes/events.ts +131 -0
  765. package/src/ipc/skill-routes/identity.ts +34 -0
  766. package/src/ipc/skill-routes/index.ts +37 -0
  767. package/src/ipc/skill-routes/log.ts +40 -0
  768. package/src/ipc/skill-routes/memory.ts +76 -0
  769. package/src/ipc/skill-routes/platform.ts +39 -0
  770. package/src/ipc/skill-routes/providers.ts +163 -0
  771. package/src/ipc/skill-routes/registries.ts +393 -0
  772. package/src/ipc/skill-server.ts +771 -0
  773. package/src/ipc/skill-socket-path.ts +20 -0
  774. package/src/ipc/socket-cleanup.ts +92 -0
  775. package/src/ipc/socket-path.ts +55 -48
  776. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +374 -0
  777. package/src/live-voice/__tests__/live-voice-archive.test.ts +525 -0
  778. package/src/live-voice/__tests__/live-voice-events.test.ts +473 -0
  779. package/src/live-voice/__tests__/live-voice-integration.test.ts +359 -0
  780. package/src/live-voice/__tests__/live-voice-metrics.test.ts +179 -0
  781. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +349 -0
  782. package/src/live-voice/__tests__/live-voice-stt.test.ts +244 -0
  783. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +337 -0
  784. package/src/live-voice/__tests__/live-voice-tts.test.ts +337 -0
  785. package/src/live-voice/__tests__/protocol.test.ts +295 -0
  786. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +421 -0
  787. package/src/live-voice/live-voice-archive.ts +758 -0
  788. package/src/live-voice/live-voice-metrics.ts +472 -0
  789. package/src/live-voice/live-voice-session-manager.ts +222 -0
  790. package/src/live-voice/live-voice-session.ts +1144 -0
  791. package/src/live-voice/live-voice-tts.ts +260 -0
  792. package/src/live-voice/protocol.ts +524 -0
  793. package/src/mcp/client.ts +2 -2
  794. package/src/media/app-icon-generator.ts +23 -46
  795. package/src/media/avatar-router.ts +26 -41
  796. package/src/media/gemini-image-service.ts +8 -41
  797. package/src/media/image-credentials.ts +73 -0
  798. package/src/media/image-service.ts +85 -0
  799. package/src/media/openai-image-service.ts +131 -0
  800. package/src/media/types.ts +46 -0
  801. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +4 -28
  802. package/src/memory/__tests__/auto-analysis-guard.test.ts +2 -2
  803. package/src/memory/__tests__/conversation-analyze-job.test.ts +7 -62
  804. package/src/memory/__tests__/conversation-group-migration.test.ts +2 -2
  805. package/src/memory/__tests__/find-analysis-conversation.test.ts +2 -1
  806. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +235 -0
  807. package/src/memory/admin.ts +65 -7
  808. package/src/memory/app-git-service.ts +0 -14
  809. package/src/memory/attachments-store.ts +14 -16
  810. package/src/memory/auto-analysis-enqueue.ts +2 -17
  811. package/src/memory/canonical-guardian-store.ts +2 -1
  812. package/src/memory/channel-verification-sessions.ts +1 -1
  813. package/src/memory/checkpoints.ts +1 -1
  814. package/src/memory/context-search/agent-protocol.ts +424 -0
  815. package/src/memory/context-search/agent-runner.ts +1295 -0
  816. package/src/memory/context-search/format.ts +160 -0
  817. package/src/memory/context-search/limits.ts +106 -0
  818. package/src/memory/context-search/search.ts +387 -0
  819. package/src/memory/context-search/sources/conversations.ts +278 -0
  820. package/src/memory/context-search/sources/memory.ts +90 -0
  821. package/src/memory/context-search/sources/pkb.ts +468 -0
  822. package/src/memory/context-search/sources/workspace.ts +1255 -0
  823. package/src/memory/context-search/types.ts +49 -0
  824. package/src/memory/conversation-analyze-job.ts +3 -24
  825. package/src/memory/conversation-attention-store.ts +1 -1
  826. package/src/memory/conversation-bootstrap.ts +1 -1
  827. package/src/memory/conversation-crud.ts +117 -145
  828. package/src/memory/conversation-directories.ts +1 -11
  829. package/src/memory/conversation-display-order-migration.ts +11 -2
  830. package/src/memory/conversation-group-migration.ts +20 -4
  831. package/src/memory/conversation-key-store.ts +3 -4
  832. package/src/memory/conversation-queries.ts +69 -29
  833. package/src/memory/conversation-starter-validation.ts +88 -0
  834. package/src/memory/conversation-starters-cadence.ts +1 -1
  835. package/src/memory/conversation-title-service.ts +27 -1
  836. package/src/memory/db-init.ts +22 -4
  837. package/src/memory/db-maintenance.ts +1 -1
  838. package/src/memory/delivery-channels.ts +1 -14
  839. package/src/memory/delivery-crud.ts +2 -32
  840. package/src/memory/delivery-status.ts +1 -1
  841. package/src/memory/embedding-gemini.test.ts +44 -5
  842. package/src/memory/embedding-gemini.ts +6 -1
  843. package/src/memory/external-conversation-store.ts +1 -1
  844. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +412 -0
  845. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +225 -0
  846. package/src/memory/graph/bootstrap.test.ts +277 -0
  847. package/src/memory/graph/bootstrap.ts +10 -6
  848. package/src/memory/graph/capability-seed.ts +3 -3
  849. package/src/memory/graph/compaction.ts +1 -1
  850. package/src/memory/graph/consolidation.ts +13 -10
  851. package/src/memory/graph/conversation-graph-memory.ts +151 -1
  852. package/src/memory/graph/decay.ts +1 -1
  853. package/src/memory/graph/extraction.ts +63 -23
  854. package/src/memory/graph/graph-memory-state-store.ts +1 -1
  855. package/src/memory/graph/graph-search.test.ts +95 -2
  856. package/src/memory/graph/graph-search.ts +22 -7
  857. package/src/memory/graph/image-ref-utils.ts +1 -1
  858. package/src/memory/graph/retriever.test.ts +158 -4
  859. package/src/memory/graph/retriever.ts +27 -8
  860. package/src/memory/graph/store.test.ts +2 -1
  861. package/src/memory/graph/store.ts +1 -1
  862. package/src/memory/graph/tool-handlers.ts +73 -247
  863. package/src/memory/graph/tools.ts +35 -53
  864. package/src/memory/group-crud.ts +1 -2
  865. package/src/memory/guardian-action-store.ts +2 -1
  866. package/src/memory/guardian-approvals.ts +1 -1
  867. package/src/memory/guardian-rate-limits.ts +1 -1
  868. package/src/memory/indexer.ts +43 -17
  869. package/src/memory/invite-store.ts +1 -1
  870. package/src/memory/job-handlers/backfill.ts +1 -1
  871. package/src/memory/job-handlers/cleanup.ts +2 -1
  872. package/src/memory/job-handlers/conversation-starters.ts +18 -10
  873. package/src/memory/job-handlers/embedding.test.ts +2 -1
  874. package/src/memory/job-handlers/embedding.ts +1 -1
  875. package/src/memory/job-handlers/index-maintenance.ts +1 -1
  876. package/src/memory/job-handlers/summarization.ts +3 -3
  877. package/src/memory/job-utils.ts +3 -3
  878. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +362 -0
  879. package/src/memory/jobs/embed-concept-page.ts +210 -0
  880. package/src/memory/jobs/embed-pkb-file.test.ts +2 -1
  881. package/src/memory/jobs-store.ts +10 -2
  882. package/src/memory/jobs-worker.ts +58 -5
  883. package/src/memory/lifecycle-events-store.ts +1 -1
  884. package/src/memory/llm-request-log-store.ts +1 -1
  885. package/src/memory/llm-usage-store.ts +1 -1
  886. package/src/memory/media-store.ts +1 -1
  887. package/src/memory/memory-recall-log-store.ts +1 -1
  888. package/src/memory/migrations/038-actor-token-records.ts +3 -0
  889. package/src/memory/migrations/039-actor-refresh-token-records.ts +3 -0
  890. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  891. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  892. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  893. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  894. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  895. package/src/memory/migrations/226-schedule-wake-conversation-id.ts +11 -0
  896. package/src/memory/migrations/227-add-conversation-inference-profile.ts +18 -0
  897. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +27 -0
  898. package/src/memory/migrations/229-delete-private-conversations.test.ts +1087 -0
  899. package/src/memory/migrations/229-delete-private-conversations.ts +210 -0
  900. package/src/memory/migrations/230-acp-session-history.ts +41 -0
  901. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +128 -0
  902. package/src/memory/migrations/232-activation-state.ts +38 -0
  903. package/src/memory/migrations/index.ts +14 -0
  904. package/src/memory/migrations/registry.ts +7 -0
  905. package/src/memory/pkb/pkb-index.test.ts +5 -5
  906. package/src/memory/pkb/pkb-reconcile.test.ts +5 -5
  907. package/src/memory/pkb/pkb-search.test.ts +148 -7
  908. package/src/memory/pkb/pkb-search.ts +65 -30
  909. package/src/memory/published-pages-store.ts +1 -1
  910. package/src/memory/qdrant-client.test.ts +60 -0
  911. package/src/memory/qdrant-client.ts +25 -0
  912. package/src/memory/schema/acp.ts +30 -0
  913. package/src/memory/schema/conversations.ts +1 -1
  914. package/src/memory/schema/index.ts +1 -0
  915. package/src/memory/schema/infrastructure.ts +2 -32
  916. package/src/memory/schema/memory-graph.ts +36 -14
  917. package/src/memory/schema/oauth.ts +4 -1
  918. package/src/memory/scoped-approval-grants.ts +2 -1
  919. package/src/memory/search/semantic.ts +2 -2
  920. package/src/memory/shared-app-links-store.ts +2 -1
  921. package/src/memory/tool-usage-store.ts +1 -1
  922. package/src/memory/trace-event-store.ts +2 -1
  923. package/src/memory/turn-events-store.ts +1 -1
  924. package/src/memory/v2/__tests__/activation-store.test.ts +202 -0
  925. package/src/memory/v2/__tests__/activation.test.ts +956 -0
  926. package/src/memory/v2/__tests__/backfill-jobs.test.ts +610 -0
  927. package/src/memory/v2/__tests__/consolidation-job.test.ts +395 -0
  928. package/src/memory/v2/__tests__/edges.test.ts +435 -0
  929. package/src/memory/v2/__tests__/injection.test.ts +792 -0
  930. package/src/memory/v2/__tests__/migration.test.ts +812 -0
  931. package/src/memory/v2/__tests__/page-store.test.ts +334 -0
  932. package/src/memory/v2/__tests__/qdrant.test.ts +438 -0
  933. package/src/memory/v2/__tests__/sim.test.ts +549 -0
  934. package/src/memory/v2/__tests__/skill-content.test.ts +85 -0
  935. package/src/memory/v2/__tests__/skill-qdrant.test.ts +657 -0
  936. package/src/memory/v2/__tests__/skill-store.test.ts +351 -0
  937. package/src/memory/v2/__tests__/sweep-job.test.ts +441 -0
  938. package/src/memory/v2/activation-store.ts +109 -0
  939. package/src/memory/v2/activation.ts +490 -0
  940. package/src/memory/v2/backfill-jobs.ts +442 -0
  941. package/src/memory/v2/consolidation-job.ts +304 -0
  942. package/src/memory/v2/edges.ts +217 -0
  943. package/src/memory/v2/injection.ts +307 -0
  944. package/src/memory/v2/migration.ts +654 -0
  945. package/src/memory/v2/now-text.ts +38 -0
  946. package/src/memory/v2/page-store.ts +245 -0
  947. package/src/memory/v2/prompts/consolidation.ts +185 -0
  948. package/src/memory/v2/prompts/sweep.ts +56 -0
  949. package/src/memory/v2/qdrant.ts +342 -0
  950. package/src/memory/v2/sim.ts +206 -0
  951. package/src/memory/v2/skill-content.ts +42 -0
  952. package/src/memory/v2/skill-qdrant.ts +395 -0
  953. package/src/memory/v2/skill-store.ts +128 -0
  954. package/src/memory/v2/sweep-job.ts +298 -0
  955. package/src/memory/v2/types.ts +116 -0
  956. package/src/memory/validation.ts +1 -1
  957. package/src/messaging/providers/index.ts +262 -0
  958. package/src/messaging/providers/slack/api.ts +242 -0
  959. package/src/messaging/providers/slack/message-metadata.ts +1 -1
  960. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  961. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  962. package/src/messaging/providers/slack/send.ts +383 -0
  963. package/src/messaging/providers/telegram-bot/adapter.ts +4 -42
  964. package/src/messaging/providers/telegram-bot/api.ts +253 -0
  965. package/src/messaging/providers/telegram-bot/client.ts +17 -58
  966. package/src/messaging/providers/telegram-bot/send.ts +232 -0
  967. package/src/messaging/providers/whatsapp/adapter.ts +4 -36
  968. package/src/messaging/providers/whatsapp/api.ts +319 -0
  969. package/src/messaging/providers/whatsapp/client.ts +4 -48
  970. package/src/messaging/providers/whatsapp/send.ts +209 -0
  971. package/src/notifications/adapters/slack.ts +5 -23
  972. package/src/notifications/adapters/telegram.ts +8 -29
  973. package/src/notifications/conversation-candidates.ts +1 -1
  974. package/src/notifications/conversation-pairing.ts +78 -19
  975. package/src/notifications/conversation-seed-composer.ts +12 -6
  976. package/src/notifications/copy-composer.ts +1 -6
  977. package/src/notifications/decision-engine.ts +1 -1
  978. package/src/notifications/decisions-store.ts +1 -1
  979. package/src/notifications/deliveries-store.ts +2 -1
  980. package/src/notifications/deterministic-checks.ts +1 -1
  981. package/src/notifications/emit-signal.ts +1 -1
  982. package/src/notifications/events-store.ts +1 -13
  983. package/src/notifications/preferences-store.ts +1 -1
  984. package/src/notifications/signal.ts +1 -11
  985. package/src/oauth/AGENTS.md +1 -1
  986. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  987. package/src/oauth/connect-orchestrator.ts +8 -34
  988. package/src/oauth/connect-types.ts +6 -10
  989. package/src/oauth/connection-resolver.ts +11 -2
  990. package/src/oauth/manual-token-connection.ts +23 -0
  991. package/src/oauth/oauth-store.ts +32 -15
  992. package/src/oauth/provider-serializer.ts +6 -1
  993. package/src/oauth/seed-providers.ts +56 -108
  994. package/src/outbound-proxy/http-forwarder.ts +9 -0
  995. package/src/outbound-proxy/index.ts +0 -1
  996. package/src/permissions/approval-policy.test.ts +398 -106
  997. package/src/permissions/approval-policy.ts +134 -108
  998. package/src/permissions/checker.test.ts +632 -0
  999. package/src/permissions/checker.ts +280 -345
  1000. package/src/permissions/gateway-threshold-reader.ts +177 -0
  1001. package/src/permissions/ipc-risk-types.ts +95 -0
  1002. package/src/permissions/prompter.ts +8 -9
  1003. package/src/permissions/risk-types.ts +24 -153
  1004. package/src/permissions/types.ts +19 -47
  1005. package/src/permissions/workspace-policy.ts +10 -7
  1006. package/src/playbooks/playbook-compiler.ts +1 -1
  1007. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  1008. package/src/plugins/defaults/compaction.ts +145 -0
  1009. package/src/plugins/defaults/empty-response.ts +126 -0
  1010. package/src/plugins/defaults/history-repair.ts +85 -0
  1011. package/src/plugins/defaults/index.ts +116 -0
  1012. package/src/plugins/defaults/injectors.ts +488 -0
  1013. package/src/plugins/defaults/llm-call.ts +79 -0
  1014. package/src/plugins/defaults/memory-retrieval.ts +221 -0
  1015. package/src/plugins/defaults/overflow-reduce.ts +185 -0
  1016. package/src/plugins/defaults/persistence.ts +129 -0
  1017. package/src/plugins/defaults/title-generate.ts +95 -0
  1018. package/src/plugins/defaults/token-estimate.ts +103 -0
  1019. package/src/plugins/defaults/tool-error.ts +126 -0
  1020. package/src/plugins/defaults/tool-execute.ts +89 -0
  1021. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  1022. package/src/plugins/pipeline.ts +316 -0
  1023. package/src/plugins/plugin-skill-contributions.ts +292 -0
  1024. package/src/plugins/registry.ts +301 -0
  1025. package/src/plugins/types.ts +1133 -0
  1026. package/src/plugins/user-loader.ts +203 -0
  1027. package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +197 -0
  1028. package/src/prompts/persona-resolver.ts +2 -4
  1029. package/src/prompts/system-prompt.ts +39 -0
  1030. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  1031. package/src/prompts/templates/SOUL.md +3 -1
  1032. package/src/providers/__tests__/provider-env-vars.test.ts +0 -21
  1033. package/src/providers/__tests__/retry-callsite.test.ts +3 -6
  1034. package/src/providers/anthropic/client.ts +71 -19
  1035. package/src/providers/call-site-routing.ts +7 -3
  1036. package/src/providers/fireworks/client.ts +3 -0
  1037. package/src/providers/gemini/client.ts +96 -22
  1038. package/src/providers/managed-proxy/context.ts +0 -12
  1039. package/src/providers/model-catalog.ts +123 -25
  1040. package/src/providers/model-intents.ts +6 -7
  1041. package/src/providers/openai/chat-completions-provider.ts +37 -7
  1042. package/src/providers/openai/responses-provider.ts +39 -4
  1043. package/src/providers/openrouter/client.ts +9 -6
  1044. package/src/providers/provider-env-vars.ts +4 -12
  1045. package/src/providers/provider-send-message.ts +16 -11
  1046. package/src/providers/registry.ts +1 -1
  1047. package/src/providers/retry.ts +52 -23
  1048. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  1049. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  1050. package/src/providers/speech-to-text/openai-whisper-stream.ts +1 -1
  1051. package/src/providers/speech-to-text/openai-whisper.ts +3 -6
  1052. package/src/providers/speech-to-text/provider-catalog.ts +75 -0
  1053. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  1054. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  1055. package/src/providers/speech-to-text/xai.ts +5 -5
  1056. package/src/providers/thinking-config.ts +34 -0
  1057. package/src/providers/types.ts +22 -10
  1058. package/src/runtime/AGENTS.md +27 -17
  1059. package/src/runtime/__tests__/agent-wake.test.ts +33 -9
  1060. package/src/runtime/__tests__/client-registry.test.ts +271 -0
  1061. package/src/runtime/__tests__/interactive-ui.test.ts +157 -246
  1062. package/src/runtime/access-request-helper.ts +9 -20
  1063. package/src/runtime/actor-trust-resolver.ts +2 -2
  1064. package/src/runtime/agent-wake.ts +174 -68
  1065. package/src/runtime/approval-conversation-turn.ts +2 -15
  1066. package/src/runtime/approval-message-composer.ts +11 -60
  1067. package/src/runtime/assistant-event.ts +18 -66
  1068. package/src/runtime/auth/__tests__/guard-tests.test.ts +6 -30
  1069. package/src/runtime/auth/__tests__/middleware.test.ts +10 -10
  1070. package/src/runtime/auth/__tests__/route-policy.test.ts +0 -8
  1071. package/src/runtime/auth/context.ts +9 -0
  1072. package/src/runtime/auth/middleware.ts +4 -4
  1073. package/src/runtime/auth/route-policy.ts +195 -4
  1074. package/src/runtime/auth/token-service.ts +1 -100
  1075. package/src/runtime/capability-tokens.ts +89 -313
  1076. package/src/runtime/channel-approval-types.ts +1 -6
  1077. package/src/runtime/channel-approvals.ts +7 -79
  1078. package/src/runtime/channel-readiness-service.ts +2 -2
  1079. package/src/runtime/channel-reply-delivery.ts +2 -8
  1080. package/src/runtime/channel-retry-sweep.ts +20 -17
  1081. package/src/runtime/client-registry.ts +254 -0
  1082. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -7
  1083. package/src/runtime/gateway-client.ts +37 -378
  1084. package/src/runtime/guardian-action-grant-minter.ts +2 -3
  1085. package/src/runtime/guardian-action-message-composer.ts +11 -52
  1086. package/src/runtime/guardian-action-service.ts +19 -7
  1087. package/src/runtime/guardian-decision-types.ts +4 -65
  1088. package/src/runtime/guardian-reply-router.ts +10 -19
  1089. package/src/runtime/guardian-vellum-migration.ts +5 -64
  1090. package/src/runtime/http-errors.ts +3 -0
  1091. package/src/runtime/http-router.ts +50 -7
  1092. package/src/runtime/http-server.ts +345 -1041
  1093. package/src/runtime/http-types.ts +15 -100
  1094. package/src/runtime/interactive-ui-types.ts +145 -0
  1095. package/src/runtime/interactive-ui.ts +38 -196
  1096. package/src/runtime/invite-redemption-service.ts +1 -1
  1097. package/src/runtime/invite-redemption-templates.ts +1 -1
  1098. package/src/runtime/local-actor-identity.ts +13 -43
  1099. package/src/runtime/message-composer-types.ts +134 -0
  1100. package/src/runtime/middleware/rate-limiter.ts +1 -1
  1101. package/src/runtime/middleware/request-logger.ts +5 -2
  1102. package/src/runtime/migrations/__tests__/job-registry.test.ts +346 -0
  1103. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +16 -0
  1104. package/src/runtime/migrations/job-registry.ts +281 -0
  1105. package/src/runtime/migrations/vbundle-builder.ts +4 -26
  1106. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  1107. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -13
  1108. package/src/runtime/migrations/vbundle-tar-stream.ts +11 -3
  1109. package/src/runtime/nl-approval-parser.ts +16 -21
  1110. package/src/runtime/pending-interactions.ts +29 -12
  1111. package/src/runtime/routes/__tests__/acp-routes.test.ts +395 -0
  1112. package/src/runtime/routes/__tests__/backup-routes.test.ts +204 -320
  1113. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +72 -4
  1114. package/src/runtime/routes/__tests__/stt-routes.test.ts +182 -223
  1115. package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +230 -0
  1116. package/src/{ipc/__tests__/task-ipc.test.ts → runtime/routes/__tests__/task-routes.test.ts} +116 -96
  1117. package/src/runtime/routes/__tests__/tts-routes.test.ts +185 -289
  1118. package/src/runtime/routes/access-request-decision.ts +25 -50
  1119. package/src/runtime/routes/acp-routes.test.ts +371 -0
  1120. package/src/runtime/routes/acp-routes.ts +392 -166
  1121. package/src/runtime/routes/app-management-routes.ts +464 -660
  1122. package/src/runtime/routes/app-routes.ts +192 -177
  1123. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  1124. package/src/runtime/routes/approval-routes.ts +133 -434
  1125. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +24 -84
  1126. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +3 -10
  1127. package/src/runtime/routes/attachment-routes.ts +409 -253
  1128. package/src/runtime/routes/audio-routes.ts +51 -18
  1129. package/src/runtime/routes/avatar-routes.ts +82 -75
  1130. package/src/runtime/routes/background-tool-routes.ts +94 -0
  1131. package/src/runtime/routes/backup-routes.ts +154 -336
  1132. package/src/runtime/routes/brain-graph-routes.ts +83 -110
  1133. package/src/runtime/routes/browser-routes.ts +141 -0
  1134. package/src/runtime/routes/btw-routes.ts +62 -106
  1135. package/src/runtime/routes/cache-routes.ts +96 -0
  1136. package/src/runtime/routes/call-routes.ts +208 -247
  1137. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +1 -1
  1138. package/src/runtime/routes/channel-delivery-routes.ts +25 -27
  1139. package/src/runtime/routes/channel-readiness-routes.ts +83 -120
  1140. package/src/runtime/routes/channel-route-definitions.ts +62 -0
  1141. package/src/runtime/routes/channel-route-shared.ts +14 -18
  1142. package/src/runtime/routes/channel-verification-routes.ts +207 -187
  1143. package/src/runtime/routes/client-routes.ts +48 -0
  1144. package/src/runtime/routes/contact-routes.ts +533 -407
  1145. package/src/runtime/routes/conversation-analysis-routes.ts +48 -49
  1146. package/src/runtime/routes/conversation-attention-routes.ts +55 -67
  1147. package/src/runtime/routes/conversation-list-routes.ts +265 -0
  1148. package/src/runtime/routes/conversation-management-routes.ts +626 -715
  1149. package/src/runtime/routes/conversation-query-routes.ts +510 -460
  1150. package/src/runtime/routes/conversation-routes.ts +652 -457
  1151. package/src/runtime/routes/conversation-starter-routes.ts +121 -71
  1152. package/src/runtime/routes/credential-prompt-routes.ts +124 -0
  1153. package/src/runtime/routes/debug-routes.ts +34 -39
  1154. package/src/runtime/routes/defer-routes.ts +230 -0
  1155. package/src/runtime/routes/diagnostics-routes.ts +79 -70
  1156. package/src/runtime/routes/documents-routes.ts +117 -106
  1157. package/src/runtime/routes/errors.ts +132 -0
  1158. package/src/runtime/routes/events-routes.ts +97 -58
  1159. package/src/runtime/routes/filing-routes.ts +65 -78
  1160. package/src/runtime/routes/global-search-routes.ts +51 -57
  1161. package/src/runtime/routes/group-routes.ts +199 -181
  1162. package/src/runtime/routes/guardian-action-routes.ts +103 -169
  1163. package/src/runtime/routes/guardian-approval-interception.ts +27 -58
  1164. package/src/runtime/routes/guardian-approval-prompt.ts +10 -21
  1165. package/src/runtime/routes/guardian-approval-reply-helpers.ts +2 -6
  1166. package/src/runtime/routes/guardian-expiry-sweep.ts +19 -36
  1167. package/src/runtime/routes/heartbeat-routes.ts +194 -209
  1168. package/src/runtime/routes/home-feed-routes.ts +85 -187
  1169. package/src/runtime/routes/home-state-routes.ts +27 -24
  1170. package/src/runtime/routes/host-bash-routes.ts +42 -52
  1171. package/src/runtime/routes/host-browser-routes.ts +38 -69
  1172. package/src/runtime/routes/host-cu-routes.ts +74 -70
  1173. package/src/runtime/routes/host-file-routes.ts +50 -60
  1174. package/src/runtime/routes/host-transfer-routes.ts +220 -0
  1175. package/src/runtime/routes/http-adapter.ts +172 -0
  1176. package/src/runtime/routes/identity-routes.ts +83 -79
  1177. package/src/runtime/routes/inbound-conversation.ts +11 -18
  1178. package/src/runtime/routes/inbound-message-handler.ts +162 -123
  1179. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +79 -138
  1180. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +2 -3
  1181. package/src/runtime/routes/inbound-stages/background-dispatch.ts +54 -90
  1182. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +25 -50
  1183. package/src/runtime/routes/inbound-stages/edit-intercept.ts +7 -7
  1184. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  1185. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +5 -6
  1186. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -24
  1187. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -10
  1188. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -4
  1189. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +3 -3
  1190. package/src/runtime/routes/inbound-stages/verification-intercept.ts +19 -26
  1191. package/src/runtime/routes/index.ts +197 -0
  1192. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +25 -32
  1193. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +22 -31
  1194. package/src/runtime/routes/integrations/slack/channel.ts +69 -66
  1195. package/src/runtime/routes/integrations/slack/share.ts +49 -58
  1196. package/src/runtime/routes/integrations/telegram.ts +91 -74
  1197. package/src/runtime/routes/integrations/twilio.ts +163 -240
  1198. package/src/runtime/routes/integrations/vercel.ts +57 -54
  1199. package/src/runtime/routes/interface-routes.ts +43 -0
  1200. package/src/runtime/routes/internal-oauth-routes.ts +56 -0
  1201. package/src/runtime/routes/internal-twilio-routes.ts +46 -0
  1202. package/src/runtime/routes/llm-context-normalization.ts +4 -2
  1203. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  1204. package/src/runtime/routes/log-export-routes.ts +90 -100
  1205. package/src/runtime/routes/memory-item-routes.test.ts +153 -175
  1206. package/src/runtime/routes/memory-item-routes.ts +243 -323
  1207. package/src/runtime/routes/memory-v2-routes.ts +193 -0
  1208. package/src/runtime/routes/migration-rollback-routes.ts +167 -212
  1209. package/src/runtime/routes/migration-routes.ts +877 -377
  1210. package/src/runtime/routes/notification-routes.ts +199 -70
  1211. package/src/runtime/routes/oauth-apps.ts +254 -251
  1212. package/src/runtime/routes/oauth-providers.ts +66 -57
  1213. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +224 -0
  1214. package/src/runtime/routes/playground/__tests__/guard.test.ts +60 -0
  1215. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +250 -0
  1216. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +195 -0
  1217. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +159 -0
  1218. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +207 -0
  1219. package/src/runtime/routes/playground/__tests__/state.test.ts +175 -0
  1220. package/src/runtime/routes/playground/conversation-not-found.ts +27 -0
  1221. package/src/runtime/routes/playground/force-compact.ts +60 -0
  1222. package/src/runtime/routes/playground/guard.ts +36 -0
  1223. package/src/runtime/routes/playground/helpers.ts +103 -0
  1224. package/src/runtime/routes/playground/index.ts +18 -0
  1225. package/src/runtime/routes/playground/inject-failures.ts +143 -0
  1226. package/src/runtime/routes/playground/reset-circuit.ts +89 -0
  1227. package/src/runtime/routes/playground/seed-conversation.ts +113 -0
  1228. package/src/runtime/routes/playground/seeded-conversations.ts +74 -0
  1229. package/src/runtime/routes/playground/state.ts +77 -0
  1230. package/src/runtime/routes/profiler-routes.ts +132 -167
  1231. package/src/runtime/routes/ps-routes.ts +120 -0
  1232. package/src/runtime/routes/recording-routes.ts +197 -258
  1233. package/src/runtime/routes/rename-conversation-routes.ts +89 -0
  1234. package/src/runtime/routes/schedule-routes.ts +284 -207
  1235. package/src/runtime/routes/secret-routes.ts +219 -265
  1236. package/src/runtime/routes/secrets-deps.ts +24 -0
  1237. package/src/runtime/routes/settings-routes.ts +361 -441
  1238. package/src/runtime/routes/skills-routes.ts +434 -469
  1239. package/src/runtime/routes/stt-routes.ts +196 -206
  1240. package/src/runtime/routes/subagents-routes.ts +125 -141
  1241. package/src/runtime/routes/suggest-trust-rule-routes.ts +244 -0
  1242. package/src/runtime/routes/surface-action-routes.ts +135 -190
  1243. package/src/runtime/routes/surface-content-routes.ts +84 -118
  1244. package/src/runtime/routes/task-routes.ts +354 -0
  1245. package/src/runtime/routes/telemetry-routes.ts +33 -49
  1246. package/src/runtime/routes/trace-event-routes.ts +55 -74
  1247. package/src/runtime/routes/trust-rules-routes.ts +147 -239
  1248. package/src/runtime/routes/tts-routes.ts +187 -169
  1249. package/src/runtime/routes/types.ts +139 -0
  1250. package/src/{ipc/routes/ui-request.ts → runtime/routes/ui-request-routes.ts} +23 -17
  1251. package/src/runtime/routes/upgrade-broadcast-routes.ts +156 -197
  1252. package/src/runtime/routes/usage-routes.ts +143 -169
  1253. package/src/runtime/routes/user-routes.ts +102 -18
  1254. package/src/runtime/routes/wake-conversation-routes.ts +49 -0
  1255. package/src/{ipc/routes/watcher.ts → runtime/routes/watcher-routes.ts} +84 -39
  1256. package/src/runtime/routes/wipe-conversation-routes.ts +89 -0
  1257. package/src/runtime/routes/work-items-routes.test.ts +10 -20
  1258. package/src/runtime/routes/work-items-routes.ts +418 -433
  1259. package/src/runtime/routes/workspace-commit-routes.ts +30 -61
  1260. package/src/runtime/routes/workspace-routes.test.ts +254 -381
  1261. package/src/runtime/routes/workspace-routes.ts +238 -246
  1262. package/src/runtime/runtime-mode.ts +8 -1
  1263. package/src/runtime/services/__tests__/analyze-conversation.test.ts +80 -118
  1264. package/src/runtime/services/analyze-conversation.ts +14 -41
  1265. package/src/runtime/services/conversation-serializer.ts +181 -0
  1266. package/src/runtime/skill-route-registry.ts +75 -15
  1267. package/src/runtime/trust-context-resolver.ts +3 -2
  1268. package/src/runtime/verification-outbound-actions.ts +13 -49
  1269. package/src/schedule/run-script.ts +68 -0
  1270. package/src/schedule/schedule-store.ts +70 -2
  1271. package/src/schedule/scheduler.ts +149 -8
  1272. package/src/security/ces-credential-client.ts +32 -169
  1273. package/src/security/ces-rpc-credential-backend.ts +1 -1
  1274. package/src/security/credential-backend.ts +6 -6
  1275. package/src/security/oauth-completion-page.ts +1 -1
  1276. package/src/security/oauth2.ts +3 -6
  1277. package/src/sequence/analytics.ts +1 -1
  1278. package/src/sequence/guardrails.ts +3 -3
  1279. package/src/sequence/store.ts +2 -1
  1280. package/src/signals/bash.ts +1 -1
  1281. package/src/signals/event-stream.ts +1 -1
  1282. package/src/skills/catalog-cache.ts +19 -5
  1283. package/src/skills/catalog-files.ts +0 -5
  1284. package/src/skills/catalog-install.ts +28 -18
  1285. package/src/skills/category-inference.ts +0 -11
  1286. package/src/skills/clawhub.ts +2 -2
  1287. package/src/skills/managed-store.ts +2 -2
  1288. package/src/skills/remote-skill-policy.ts +6 -7
  1289. package/src/subagent/index.ts +2 -6
  1290. package/src/subagent/manager.ts +27 -23
  1291. package/src/subagent/types.ts +9 -0
  1292. package/src/tasks/SPEC.md +2 -2
  1293. package/src/tasks/task-compiler.ts +1 -1
  1294. package/src/tasks/task-runner.ts +2 -22
  1295. package/src/tasks/task-store.ts +1 -1
  1296. package/src/tools/acp/list-agents.test.ts +115 -0
  1297. package/src/tools/acp/list-agents.ts +31 -0
  1298. package/src/tools/acp/spawn.test.ts +379 -0
  1299. package/src/tools/acp/spawn.ts +142 -62
  1300. package/src/tools/acp/steer.test.ts +101 -0
  1301. package/src/tools/acp/steer.ts +38 -0
  1302. package/src/tools/background-tool-registry.ts +98 -0
  1303. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  1304. package/src/tools/browser/browser-execution.ts +122 -26
  1305. package/src/tools/browser/browser-manager.ts +1 -8
  1306. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  1307. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  1308. package/src/tools/browser/cdp-client/accessibility-snapshot.ts +1 -1
  1309. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +3 -1
  1310. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  1311. package/src/tools/browser/cdp-client/factory.ts +15 -4
  1312. package/src/tools/browser/cdp-client/types.ts +4 -1
  1313. package/src/tools/computer-use/definitions.ts +1 -1
  1314. package/src/tools/credential-execution/make-authenticated-request.ts +2 -2
  1315. package/src/tools/credential-execution/manage-secure-command-tool.ts +1 -1
  1316. package/src/tools/credential-execution/run-authenticated-command.ts +2 -2
  1317. package/src/tools/credentials/broker-types.ts +2 -1
  1318. package/src/tools/document/editor-template.ts +1 -1
  1319. package/src/tools/execution-timeout.ts +1 -1
  1320. package/src/tools/executor.ts +123 -76
  1321. package/src/tools/host-filesystem/transfer.test.ts +268 -0
  1322. package/src/tools/host-filesystem/transfer.ts +234 -0
  1323. package/src/tools/host-terminal/host-shell.ts +189 -11
  1324. package/src/tools/mcp/mcp-tool-factory.ts +1 -1
  1325. package/src/tools/memory/register.test.ts +161 -1
  1326. package/src/tools/memory/register.ts +19 -34
  1327. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  1328. package/src/tools/permission-checker.ts +103 -255
  1329. package/src/tools/policy-context.ts +5 -8
  1330. package/src/tools/registry.ts +156 -4
  1331. package/src/tools/schedule/create.ts +23 -8
  1332. package/src/tools/schedule/update.ts +3 -1
  1333. package/src/tools/secret-detection-handler.ts +13 -154
  1334. package/src/tools/shared/shell-output.ts +4 -1
  1335. package/src/tools/side-effects.ts +2 -2
  1336. package/src/tools/skills/execute.ts +1 -1
  1337. package/src/tools/subagent/spawn.ts +35 -11
  1338. package/src/tools/system/avatar-generator.ts +6 -2
  1339. package/src/tools/terminal/safe-env.ts +9 -1
  1340. package/src/tools/terminal/shell.ts +161 -31
  1341. package/src/tools/tool-approval-handler.ts +4 -70
  1342. package/src/tools/tool-input-summary.ts +10 -0
  1343. package/src/tools/types.ts +157 -151
  1344. package/src/tools/ui-surface/definitions.ts +2 -2
  1345. package/src/util/debounce.ts +0 -21
  1346. package/src/util/errors.ts +0 -8
  1347. package/src/util/log-redact.ts +0 -1
  1348. package/src/util/platform.ts +85 -119
  1349. package/src/util/pricing.ts +135 -9
  1350. package/src/watcher/engine.ts +42 -20
  1351. package/src/watcher/watcher-store.ts +2 -1
  1352. package/src/work-items/work-item-store.ts +1 -1
  1353. package/src/workspace/git-service.ts +1 -6
  1354. package/src/workspace/migrations/006-services-config.ts +11 -4
  1355. package/src/workspace/migrations/017-seed-persona-dirs.ts +1 -1
  1356. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +1 -1
  1357. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  1358. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +1 -1
  1359. package/src/workspace/migrations/031-drop-user-md.ts +1 -1
  1360. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  1361. package/src/workspace/migrations/045-release-notes-meet-avatar.ts +3 -4
  1362. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  1363. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  1364. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  1365. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  1366. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  1367. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  1368. package/src/workspace/migrations/052-seed-default-inference-profiles.ts +150 -0
  1369. package/src/workspace/migrations/053-release-notes-acp-codex.ts +107 -0
  1370. package/src/workspace/migrations/054-seed-recall-callsite.ts +102 -0
  1371. package/src/workspace/migrations/055-release-notes-agentic-recall.ts +63 -0
  1372. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +65 -0
  1373. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +98 -0
  1374. package/src/workspace/migrations/058-release-notes-acp-sessions-ui.ts +71 -0
  1375. package/src/workspace/migrations/059-move-pid-to-workspace.ts +53 -0
  1376. package/src/workspace/migrations/060-memory-v2-init.ts +53 -0
  1377. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +1 -1
  1378. package/src/workspace/migrations/registry.ts +30 -0
  1379. package/src/workspace/migrations/runner.ts +2 -2
  1380. package/src/workspace/provider-commit-message-generator.ts +1 -1
  1381. package/tsconfig.json +1 -1
  1382. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  1383. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  1384. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +0 -471
  1385. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +0 -436
  1386. package/src/__tests__/cli-command-risk-guard.test.ts +0 -368
  1387. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  1388. package/src/__tests__/config-watcher-feature-flags.test.ts +0 -211
  1389. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  1390. package/src/__tests__/conversation-approval-overrides.test.ts +0 -207
  1391. package/src/__tests__/conversation-host-access-routes.test.ts +0 -229
  1392. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +0 -226
  1393. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +0 -167
  1394. package/src/__tests__/ephemeral-permissions.test.ts +0 -474
  1395. package/src/__tests__/extension-id-sync-guard.test.ts +0 -241
  1396. package/src/__tests__/hooks-blocking.test.ts +0 -178
  1397. package/src/__tests__/hooks-cli.test.ts +0 -182
  1398. package/src/__tests__/hooks-config.test.ts +0 -108
  1399. package/src/__tests__/hooks-discovery.test.ts +0 -211
  1400. package/src/__tests__/hooks-integration.test.ts +0 -196
  1401. package/src/__tests__/hooks-manager.test.ts +0 -226
  1402. package/src/__tests__/hooks-runner.test.ts +0 -175
  1403. package/src/__tests__/hooks-settings.test.ts +0 -160
  1404. package/src/__tests__/hooks-templates.test.ts +0 -169
  1405. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  1406. package/src/__tests__/hooks-watch.test.ts +0 -112
  1407. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +0 -374
  1408. package/src/__tests__/native-host-marker-sync-guard.test.ts +0 -157
  1409. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  1410. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  1411. package/src/__tests__/pairing-concurrent.test.ts +0 -84
  1412. package/src/__tests__/pairing-routes.test.ts +0 -181
  1413. package/src/__tests__/parser.test.ts +0 -595
  1414. package/src/__tests__/permission-checker-host-gate.test.ts +0 -512
  1415. package/src/__tests__/permission-controls-v2-flag.test.ts +0 -55
  1416. package/src/__tests__/permission-mode.test.ts +0 -89
  1417. package/src/__tests__/provider-env-vars-scope.test.ts +0 -52
  1418. package/src/__tests__/risk-classifier-parity.test.ts +0 -230
  1419. package/src/__tests__/send-notification-tool.test.ts +0 -83
  1420. package/src/__tests__/shell-identity.test.ts +0 -370
  1421. package/src/__tests__/shell-parser-fuzz.test.ts +0 -629
  1422. package/src/__tests__/shell-parser-property.test.ts +0 -936
  1423. package/src/__tests__/starter-bundle.test.ts +0 -173
  1424. package/src/__tests__/stt-catalog-parity.test.ts +0 -282
  1425. package/src/__tests__/task-runner.test.ts +0 -224
  1426. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -357
  1427. package/src/__tests__/trust-store-pattern-matches.test.ts +0 -29
  1428. package/src/__tests__/trust-store.test.ts +0 -2013
  1429. package/src/__tests__/v2-consent-policy.test.ts +0 -103
  1430. package/src/browser/identifiers.ts +0 -51
  1431. package/src/cli/commands/shotgun.ts +0 -266
  1432. package/src/cli/db.ts +0 -1
  1433. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  1434. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  1435. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  1436. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  1437. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  1438. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  1439. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  1440. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  1441. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  1442. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  1443. package/src/config/bundled-skills/settings/tools/avatar-get.ts +0 -40
  1444. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +0 -64
  1445. package/src/config/bundled-skills/settings/tools/avatar-update.ts +0 -88
  1446. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  1447. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +0 -127
  1448. package/src/daemon/approved-devices-store.ts +0 -110
  1449. package/src/daemon/context-overflow-approval.ts +0 -52
  1450. package/src/daemon/external-skills-bootstrap.ts +0 -41
  1451. package/src/daemon/message-types/trust.ts +0 -71
  1452. package/src/daemon/pairing-store.ts +0 -229
  1453. package/src/daemon/watch-handler.ts +0 -399
  1454. package/src/hooks/cli.ts +0 -253
  1455. package/src/hooks/config.ts +0 -100
  1456. package/src/hooks/discovery.ts +0 -135
  1457. package/src/hooks/manager.ts +0 -179
  1458. package/src/hooks/runner.ts +0 -117
  1459. package/src/hooks/templates.ts +0 -77
  1460. package/src/hooks/types.ts +0 -75
  1461. package/src/ipc/cli-server.ts +0 -252
  1462. package/src/ipc/routes/attachment.ts +0 -114
  1463. package/src/ipc/routes/browser-context.ts +0 -61
  1464. package/src/ipc/routes/browser.ts +0 -96
  1465. package/src/ipc/routes/cache.ts +0 -96
  1466. package/src/ipc/routes/index.ts +0 -21
  1467. package/src/ipc/routes/task-queue.ts +0 -226
  1468. package/src/ipc/routes/task.ts +0 -173
  1469. package/src/ipc/routes/wake-conversation.ts +0 -19
  1470. package/src/memory/db.ts +0 -23
  1471. package/src/oauth/scope-policy.ts +0 -89
  1472. package/src/permissions/bash-risk-classifier.test.ts +0 -1208
  1473. package/src/permissions/bash-risk-classifier.ts +0 -707
  1474. package/src/permissions/command-registry.test.ts +0 -535
  1475. package/src/permissions/command-registry.ts +0 -825
  1476. package/src/permissions/defaults.ts +0 -313
  1477. package/src/permissions/file-risk-classifier.test.ts +0 -535
  1478. package/src/permissions/file-risk-classifier.ts +0 -274
  1479. package/src/permissions/permission-mode.ts +0 -24
  1480. package/src/permissions/shell-identity.ts +0 -337
  1481. package/src/permissions/skill-risk-classifier.test.ts +0 -311
  1482. package/src/permissions/skill-risk-classifier.ts +0 -214
  1483. package/src/permissions/trust-client.ts +0 -359
  1484. package/src/permissions/trust-store-interface.ts +0 -100
  1485. package/src/permissions/trust-store.ts +0 -1330
  1486. package/src/permissions/v2-consent-policy.ts +0 -87
  1487. package/src/permissions/web-risk-classifier.test.ts +0 -170
  1488. package/src/permissions/web-risk-classifier.ts +0 -89
  1489. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +0 -715
  1490. package/src/runtime/__tests__/capability-tokens.test.ts +0 -258
  1491. package/src/runtime/actor-refresh-token-store.ts +0 -156
  1492. package/src/runtime/actor-token-store.ts +0 -207
  1493. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -264
  1494. package/src/runtime/auth/credential-service.ts +0 -352
  1495. package/src/runtime/conversation-approval-overrides.ts +0 -86
  1496. package/src/runtime/gateway-internal-client.ts +0 -94
  1497. package/src/runtime/routes/browser-extension-pair-routes.ts +0 -556
  1498. package/src/runtime/routes/channel-routes.ts +0 -112
  1499. package/src/runtime/routes/contact-routes.test.ts +0 -298
  1500. package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -175
  1501. package/src/runtime/routes/guardian-refresh-routes.ts +0 -79
  1502. package/src/runtime/routes/invite-routes.ts +0 -280
  1503. package/src/runtime/routes/pairing-routes.ts +0 -431
  1504. package/src/runtime/routes/watch-routes.ts +0 -156
  1505. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +0 -67
  1506. package/src/runtime/services/analyze-deps-singleton.ts +0 -32
  1507. package/src/signals/shotgun.ts +0 -203
  1508. package/src/tasks/ephemeral-permissions.ts +0 -55
  1509. package/src/tools/terminal/parser.ts +0 -623
  1510. package/src/tools/watch/screen-watch.ts +0 -144
  1511. package/src/tools/watch/watch-state.ts +0 -142
  1512. package/src/types/qrcode.d.ts +0 -13
  1513. package/src/util/network-info.ts +0 -55
  1514. /package/node_modules/@vellumai/{ces-contracts → ces-client}/tsconfig.json +0 -0
  1515. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/grants.test.ts +0 -0
  1516. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/error.ts +0 -0
  1517. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/grants.ts +0 -0
  1518. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/handles.ts +0 -0
  1519. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rendering.ts +0 -0
  1520. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rpc.ts +0 -0
@@ -1,2013 +0,0 @@
1
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { beforeEach, describe, expect, mock, test } from "bun:test";
4
-
5
- import { ruleScope } from "@vellumai/ces-contracts";
6
-
7
- // Create a temp directory for the trust file
8
- const testDir = process.env.VELLUM_WORKSPACE_DIR!;
9
-
10
- // Point the file-based trust backend at the test temp dir so
11
- // getGatewaySecurityDir() (which checks this env var first) writes
12
- // trust.json under the test directory instead of ~/.vellum/protected.
13
- process.env.GATEWAY_SECURITY_DIR = join(testDir, "protected");
14
-
15
- // Mock platform module so trust-store writes to temp dir instead of ~/.vellum
16
- // Mock logger to suppress output during tests
17
- mock.module("../util/logger.js", () => ({
18
- getLogger: () => ({
19
- info: () => {},
20
- warn: () => {},
21
- error: () => {},
22
- debug: () => {},
23
- trace: () => {},
24
- fatal: () => {},
25
- child: () => ({
26
- info: () => {},
27
- warn: () => {},
28
- error: () => {},
29
- debug: () => {},
30
- }),
31
- }),
32
- }));
33
-
34
- import { getDefaultRuleTemplates } from "../permissions/defaults.js";
35
- import {
36
- addRule,
37
- clearAllRules,
38
- clearCache,
39
- findDenyRule,
40
- findHighestPriorityRule,
41
- findMatchingRule,
42
- getAllRules,
43
- removeRule,
44
- updateRule,
45
- } from "../permissions/trust-store.js";
46
-
47
- const trustPath = join(testDir, "protected", "trust.json");
48
- const DEFAULT_TEMPLATES = getDefaultRuleTemplates();
49
- const NUM_DEFAULTS = DEFAULT_TEMPLATES.length;
50
- const DEFAULT_PRIORITY_BY_ID = new Map(
51
- DEFAULT_TEMPLATES.map((t) => [t.id, t.priority]),
52
- );
53
-
54
- describe("Trust Store", () => {
55
- beforeEach(() => {
56
- // Clear cached rules and remove the trust file between tests
57
- clearCache();
58
- try {
59
- rmSync(trustPath);
60
- } catch {
61
- /* may not exist */
62
- }
63
- });
64
-
65
- // Intentionally do not remove `testDir` in afterAll.
66
- // A late async log flush can still attempt to open `test.log` under this dir,
67
- // which intermittently causes an unhandled ENOENT in CI if the dir is removed.
68
- // ── addRule ─────────────────────────────────────────────────────
69
-
70
- describe("addRule", () => {
71
- test("adds a rule and returns it", () => {
72
- const rule = addRule("bash", "git *", "/home/user/project");
73
- expect(rule.id).toBeDefined();
74
- expect(rule.tool).toBe("bash");
75
- expect(rule.pattern).toBe("git *");
76
- expect(rule.scope).toBe("/home/user/project");
77
- expect(rule.decision).toBe("allow");
78
- expect(rule.priority).toBe(100);
79
- expect(rule.createdAt).toBeGreaterThan(0);
80
- });
81
-
82
- test("assigns unique IDs to each rule", () => {
83
- const rule1 = addRule("bash", "npm *", "/tmp");
84
- const rule2 = addRule("bash", "bun *", "/tmp");
85
- expect(rule1.id).not.toBe(rule2.id);
86
- });
87
-
88
- test("persists rule to disk", () => {
89
- addRule("bash", "git push", "/home/user");
90
- const raw = readFileSync(trustPath, "utf-8");
91
- const data = JSON.parse(raw);
92
- expect(data.version).toBe(3);
93
- expect(data.rules).toHaveLength(1 + NUM_DEFAULTS);
94
- const userRule = data.rules.find(
95
- (r: { pattern: string }) => r.pattern === "git push",
96
- );
97
- expect(userRule).toBeDefined();
98
- expect(userRule.priority).toBe(100);
99
- });
100
-
101
- test("multiple rules accumulate", () => {
102
- addRule("bash", "git *", "/tmp");
103
- addRule("file_write", "/tmp/*", "/tmp");
104
- addRule("bash", "npm *", "/tmp");
105
- expect(getAllRules()).toHaveLength(3 + NUM_DEFAULTS);
106
- });
107
-
108
- test("default priority is 100", () => {
109
- const rule = addRule("bash", "git *", "/tmp");
110
- expect(rule.priority).toBe(100);
111
- });
112
-
113
- test("custom priority is respected", () => {
114
- const rule = addRule("bash", "git *", "/tmp", "allow", 5);
115
- expect(rule.priority).toBe(5);
116
- });
117
-
118
- test("rules are sorted by priority descending in getAllRules", () => {
119
- addRule("bash", "low *", "/tmp", "allow", 0);
120
- addRule("bash", "high *", "/tmp", "allow", 2);
121
- addRule("bash", "med *", "/tmp", "allow", 1);
122
- const rules = getAllRules();
123
- // Default ask rules have higher priority than user rules
124
- const maxDefaultPriority = Math.max(
125
- ...DEFAULT_TEMPLATES.map((t) => t.priority),
126
- );
127
- expect(rules[0].priority).toBe(maxDefaultPriority);
128
- const userRules = rules.filter((r) => !r.id.startsWith("default:"));
129
- expect(userRules[0].priority).toBe(2);
130
- expect(userRules[1].priority).toBe(1);
131
- expect(userRules[2].priority).toBe(0);
132
- });
133
-
134
- test("allowHighRisk option is stripped during normalization", () => {
135
- // allowHighRisk is no longer persisted — the parser strips it.
136
- const rule = addRule("bash", "sudo *", "everywhere", "allow", 100);
137
- // Verify the field is not on the rule
138
- expect(
139
- (rule as unknown as Record<string, unknown>).allowHighRisk,
140
- ).toBeUndefined();
141
- });
142
-
143
- test("at same priority deny rules sort before allow rules", () => {
144
- addRule("bash", "allow *", "/tmp", "allow", 100);
145
- addRule("bash", "deny *", "/tmp", "deny", 100);
146
- const userRules = getAllRules().filter(
147
- (r) => !r.id.startsWith("default:"),
148
- );
149
- expect(userRules[0].decision).toBe("deny");
150
- expect(userRules[1].decision).toBe("allow");
151
- });
152
-
153
- test("accepts executionTarget option and persists it", () => {
154
- const rule = addRule("skill_tool", "skill_tool:*", "/tmp", "allow", 100, {
155
- executionTarget: "sandbox",
156
- });
157
- expect(rule.executionTarget).toBe("sandbox");
158
-
159
- // Verify persistence to disk
160
- clearCache();
161
- const rules = getAllRules();
162
- const found = rules.find((r) => r.id === rule.id);
163
- expect(found).toBeDefined();
164
- expect(found!.executionTarget).toBe("sandbox");
165
- });
166
-
167
- test("accepts executionTarget option (allowHighRisk no longer supported)", () => {
168
- const rule = addRule(
169
- "risky_tool",
170
- "risky_tool:*",
171
- "everywhere",
172
- "allow",
173
- 100,
174
- {
175
- executionTarget: "host",
176
- },
177
- );
178
- expect(rule.executionTarget).toBe("host");
179
-
180
- // Verify on disk — allowHighRisk should not appear
181
- const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
182
- const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
183
- expect(diskRule).toBeDefined();
184
- expect(diskRule).not.toHaveProperty("allowHighRisk");
185
- expect(diskRule.executionTarget).toBe("host");
186
- });
187
-
188
- test("addRule without options does not set optional fields", () => {
189
- const rule = addRule("bash", "echo *", "/tmp");
190
- expect(rule.executionTarget).toBeUndefined();
191
-
192
- // Verify on disk
193
- const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
194
- const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
195
- expect(diskRule).toBeDefined();
196
- expect(diskRule).not.toHaveProperty("executionTarget");
197
- });
198
- });
199
-
200
- // ── removeRule ──────────────────────────────────────────────────
201
-
202
- describe("removeRule", () => {
203
- test("removes an existing rule", () => {
204
- const rule = addRule("bash", "git *", "/tmp");
205
- expect(removeRule(rule.id)).toBe(true);
206
- expect(getAllRules()).toHaveLength(NUM_DEFAULTS);
207
- });
208
-
209
- test("returns false for non-existent ID", () => {
210
- expect(removeRule("non-existent-id")).toBe(false);
211
- });
212
-
213
- test("persists removal to disk", () => {
214
- const rule = addRule("bash", "npm *", "/tmp");
215
- removeRule(rule.id);
216
- // Reload from disk to verify
217
- clearCache();
218
- expect(getAllRules()).toHaveLength(NUM_DEFAULTS);
219
- });
220
-
221
- test("only removes the targeted rule", () => {
222
- const rule1 = addRule("bash", "git *", "/tmp");
223
- const rule2 = addRule("bash", "npm *", "/tmp");
224
- removeRule(rule1.id);
225
- const remaining = getAllRules();
226
- expect(remaining).toHaveLength(1 + NUM_DEFAULTS);
227
- expect(remaining.find((r) => r.id === rule2.id)).toBeDefined();
228
- });
229
- });
230
-
231
- // ── updateRule ─────────────────────────────────────────────────
232
-
233
- describe("updateRule", () => {
234
- test("updates pattern on an existing rule", () => {
235
- const rule = addRule("bash", "git *", "/tmp");
236
- const updated = updateRule(rule.id, { pattern: "git push *" });
237
- expect(updated.pattern).toBe("git push *");
238
- expect(updated.id).toBe(rule.id);
239
- expect(updated.tool).toBe("bash");
240
- });
241
-
242
- test("updates multiple fields at once", () => {
243
- const rule = addRule("bash", "npm *", "/tmp");
244
- const updated = updateRule(rule.id, {
245
- tool: "file_write",
246
- scope: "/home",
247
- decision: "deny",
248
- priority: 50,
249
- });
250
- expect(updated.tool).toBe("file_write");
251
- expect(updated.scope).toBe("/home");
252
- expect(updated.decision).toBe("deny");
253
- expect(updated.priority).toBe(50);
254
- });
255
-
256
- test("throws for non-existent rule ID", () => {
257
- expect(() => updateRule("non-existent-id", { pattern: "test" })).toThrow(
258
- "Trust rule not found: non-existent-id",
259
- );
260
- });
261
-
262
- test("persists update to disk", () => {
263
- const rule = addRule("bash", "git *", "/tmp");
264
- updateRule(rule.id, { pattern: "git status" });
265
- clearCache();
266
- const rules = getAllRules();
267
- const found = rules.find((r) => r.id === rule.id);
268
- expect(found).toBeDefined();
269
- expect(found!.pattern).toBe("git status");
270
- });
271
-
272
- test("re-sorts rules after priority change", () => {
273
- const rule1 = addRule("bash", "low *", "/tmp", "allow", 10);
274
- const rule2 = addRule("bash", "high *", "/tmp", "allow", 200);
275
- // rule2 should be first (higher priority)
276
- let userRules = getAllRules().filter((r) => !r.id.startsWith("default:"));
277
- expect(userRules[0].id).toBe(rule2.id);
278
- // Update rule1 to have higher priority
279
- updateRule(rule1.id, { priority: 300 });
280
- userRules = getAllRules().filter((r) => !r.id.startsWith("default:"));
281
- expect(userRules[0].id).toBe(rule1.id);
282
- });
283
-
284
- test("leaves unchanged fields intact", () => {
285
- const rule = addRule("bash", "git *", "/home/user", "allow", 100);
286
- updateRule(rule.id, { pattern: "git push *" });
287
- const updated = getAllRules().find((r) => r.id === rule.id)!;
288
- expect(updated.tool).toBe("bash");
289
- expect(updated.scope).toBe("/home/user");
290
- expect(updated.decision).toBe("allow");
291
- expect(updated.priority).toBe(100);
292
- expect(updated.createdAt).toBe(rule.createdAt);
293
- });
294
-
295
- test("does not set userModifiedAt on non-default rules", () => {
296
- const rule = addRule("bash", "git *", "/tmp");
297
- const updated = updateRule(rule.id, { decision: "deny" });
298
- expect(updated.userModifiedAt).toBeUndefined();
299
- });
300
-
301
- test("sets userModifiedAt when updating a default rule", () => {
302
- const before = Date.now();
303
- const updated = updateRule("default:ask-host_bash-global", {
304
- decision: "allow",
305
- });
306
- expect(updated.userModifiedAt).toBeGreaterThanOrEqual(before);
307
- expect(updated.userModifiedAt).toBeLessThanOrEqual(Date.now());
308
- });
309
-
310
- test("persists userModifiedAt to disk for default rules", () => {
311
- updateRule("default:ask-host_bash-global", { decision: "allow" });
312
- clearCache();
313
- const rules = getAllRules();
314
- const found = rules.find((r) => r.id === "default:ask-host_bash-global");
315
- expect(found).toBeDefined();
316
- expect(found!.userModifiedAt).toBeGreaterThan(0);
317
- });
318
-
319
- test("does not set userModifiedAt on no-op default rule update", () => {
320
- // Updating a default rule with values identical to the template
321
- // should NOT set userModifiedAt — the rule hasn't actually diverged.
322
- const updated = updateRule("default:ask-host_bash-global", {
323
- decision: "ask",
324
- });
325
- expect(updated.userModifiedAt).toBeUndefined();
326
- });
327
-
328
- test("clears userModifiedAt when default rule is reset to template values", () => {
329
- // First, modify the rule to diverge from the template
330
- updateRule("default:ask-host_bash-global", { decision: "allow" });
331
- let found = getAllRules().find(
332
- (r) => r.id === "default:ask-host_bash-global",
333
- )!;
334
- expect(found.userModifiedAt).toBeGreaterThan(0);
335
-
336
- // Now reset it back to the template value
337
- updateRule("default:ask-host_bash-global", { decision: "ask" });
338
- found = getAllRules().find(
339
- (r) => r.id === "default:ask-host_bash-global",
340
- )!;
341
- // userModifiedAt should be cleared since rule matches template again
342
- expect(found.userModifiedAt).toBeUndefined();
343
- });
344
- });
345
-
346
- // ── findMatchingRule ────────────────────────────────────────────
347
-
348
- describe("findMatchingRule", () => {
349
- test("finds exact match", () => {
350
- addRule("bash", "git push", "/tmp");
351
- const match = findMatchingRule("bash", "git push", "/tmp");
352
- expect(match).not.toBeNull();
353
- expect(match!.pattern).toBe("git push");
354
- });
355
-
356
- test("finds glob wildcard match", () => {
357
- addRule("bash", "git *", "/tmp");
358
- const match = findMatchingRule("bash", "git push origin main", "/tmp");
359
- expect(match).not.toBeNull();
360
- });
361
-
362
- test("returns null when tool does not match", () => {
363
- addRule("file_write", "file_write:/tmp/*", "/tmp");
364
- // host_file_read default is 'ask' so findMatchingRule (allow-only) won't find it
365
- const match = findMatchingRule(
366
- "host_file_read",
367
- "host_file_read:/etc/hosts",
368
- "/tmp",
369
- );
370
- expect(match).toBeNull();
371
- });
372
-
373
- test("returns null when pattern does not match", () => {
374
- addRule("host_file_read", "host_file_read:/etc/hosts", "/tmp");
375
- const match = findMatchingRule(
376
- "host_file_read",
377
- "host_file_read:/var/log/syslog",
378
- "/tmp",
379
- );
380
- expect(match).toBeNull();
381
- });
382
-
383
- // Scope matching
384
- describe("scope matching", () => {
385
- test("matches when scope equals rule scope", () => {
386
- addRule("bash", "npm *", "/home/user/project");
387
- const match = findMatchingRule(
388
- "bash",
389
- "npm install",
390
- "/home/user/project",
391
- );
392
- expect(match).not.toBeNull();
393
- });
394
-
395
- test("matches when scope is under rule scope (prefix)", () => {
396
- addRule("bash", "npm *", "/home/user");
397
- const match = findMatchingRule(
398
- "bash",
399
- "npm install",
400
- "/home/user/project/sub",
401
- );
402
- expect(match).not.toBeNull();
403
- });
404
-
405
- test("does not match when scope is outside rule scope", () => {
406
- addRule(
407
- "host_file_read",
408
- "host_file_read:/home/user/project/*",
409
- "/home/user/project",
410
- );
411
- const match = findMatchingRule(
412
- "host_file_read",
413
- "host_file_read:/home/user/project/file.txt",
414
- "/home/other",
415
- );
416
- expect(match).toBeNull();
417
- });
418
-
419
- test("everywhere scope matches any directory", () => {
420
- addRule("bash", "git *", "everywhere");
421
- const match = findMatchingRule(
422
- "bash",
423
- "git status",
424
- "/any/random/path",
425
- );
426
- expect(match).not.toBeNull();
427
- });
428
-
429
- test("everywhere scope matches root", () => {
430
- addRule("bash", "ls", "everywhere");
431
- const match = findMatchingRule("bash", "ls", "/");
432
- expect(match).not.toBeNull();
433
- });
434
-
435
- test("does not match sibling path with shared prefix", () => {
436
- addRule(
437
- "host_file_read",
438
- "host_file_read:/home/user/project/*",
439
- "/home/user/project",
440
- );
441
- const match = findMatchingRule(
442
- "host_file_read",
443
- "host_file_read:/home/user/project/file.txt",
444
- "/home/user/project-evil",
445
- );
446
- expect(match).toBeNull();
447
- });
448
-
449
- test("matches exact scope with trailing slash on working dir", () => {
450
- addRule("bash", "npm *", "/home/user/project");
451
- const match = findMatchingRule(
452
- "bash",
453
- "npm install",
454
- "/home/user/project/",
455
- );
456
- expect(match).not.toBeNull();
457
- });
458
-
459
- test("matches when rule scope has trailing slash", () => {
460
- addRule("bash", "npm *", "/home/user/project/");
461
- const match = findMatchingRule(
462
- "bash",
463
- "npm install",
464
- "/home/user/project",
465
- );
466
- expect(match).not.toBeNull();
467
- });
468
-
469
- test("does not match sibling with glob-suffixed scope", () => {
470
- addRule(
471
- "host_file_read",
472
- "host_file_read:/home/user/project/*",
473
- "/home/user/project*",
474
- );
475
- const match = findMatchingRule(
476
- "host_file_read",
477
- "host_file_read:/home/user/project/file.txt",
478
- "/home/user/project-evil",
479
- );
480
- expect(match).toBeNull();
481
- });
482
- });
483
-
484
- // Pattern matching with minimatch
485
- describe("pattern matching", () => {
486
- test("matches * wildcard", () => {
487
- addRule("bash", "npm *", "/tmp");
488
- expect(findMatchingRule("bash", "npm install", "/tmp")).not.toBeNull();
489
- expect(findMatchingRule("bash", "npm test", "/tmp")).not.toBeNull();
490
- });
491
-
492
- test("matches exact string", () => {
493
- addRule("host_file_read", "host_file_read:/etc/hosts", "/tmp");
494
- expect(
495
- findMatchingRule(
496
- "host_file_read",
497
- "host_file_read:/etc/hosts",
498
- "/tmp",
499
- ),
500
- ).not.toBeNull();
501
- expect(
502
- findMatchingRule(
503
- "host_file_read",
504
- "host_file_read:/etc/passwd",
505
- "/tmp",
506
- ),
507
- ).toBeNull();
508
- });
509
-
510
- test("matches file path pattern", () => {
511
- addRule("file_write", "/tmp/*", "/tmp");
512
- expect(
513
- findMatchingRule("file_write", "/tmp/file.txt", "/tmp"),
514
- ).not.toBeNull();
515
- });
516
-
517
- test("star pattern matches single-segment strings", () => {
518
- addRule("file_write", "*", "/tmp");
519
- // minimatch '*' matches strings without path separators
520
- expect(
521
- findMatchingRule("file_write", "file.txt", "/tmp"),
522
- ).not.toBeNull();
523
- });
524
-
525
- test("star pattern does not match paths with slashes", () => {
526
- addRule("file_write", "*", "/tmp");
527
- // minimatch '*' does not cross '/' boundaries
528
- expect(
529
- findMatchingRule("file_write", "/any/path/file.txt", "/tmp"),
530
- ).toBeNull();
531
- });
532
- });
533
- });
534
-
535
- // ── findHighestPriorityRule ──────────────────────────────────────
536
-
537
- describe("findHighestPriorityRule", () => {
538
- test("returns highest priority matching rule", () => {
539
- addRule("bash", "rm *", "/tmp", "allow", 0);
540
- addRule("bash", "rm *", "/tmp", "deny", 100);
541
- const match = findHighestPriorityRule("bash", ["rm file.txt"], "/tmp");
542
- expect(match).not.toBeNull();
543
- expect(match!.decision).toBe("deny");
544
- expect(match!.priority).toBe(100);
545
- });
546
-
547
- test("higher priority allow beats lower priority deny", () => {
548
- addRule("bash", "rm *", "/tmp", "deny", 0);
549
- addRule("bash", "rm *", "/tmp", "allow", 100);
550
- const match = findHighestPriorityRule("bash", ["rm file.txt"], "/tmp");
551
- expect(match).not.toBeNull();
552
- expect(match!.decision).toBe("allow");
553
- });
554
-
555
- test("same priority: deny beats allow", () => {
556
- addRule("bash", "rm *", "/tmp", "allow", 100);
557
- addRule("bash", "rm *", "/tmp", "deny", 100);
558
- const match = findHighestPriorityRule("bash", ["rm file.txt"], "/tmp");
559
- expect(match).not.toBeNull();
560
- expect(match!.decision).toBe("deny");
561
- });
562
-
563
- test("checks multiple command candidates", () => {
564
- addRule("web_fetch", "web_fetch:https://example.com/*", "/tmp", "allow");
565
- const match = findHighestPriorityRule(
566
- "web_fetch",
567
- [
568
- "web_fetch:https://example.com/page",
569
- "web_fetch:https://example.com/*",
570
- ],
571
- "/tmp",
572
- );
573
- expect(match).not.toBeNull();
574
- });
575
-
576
- test("returns null when no rule matches", () => {
577
- // Use file_read with a non-workspace path — file_read defaults only
578
- // cover specific workspace files, so /tmp paths won't match any default.
579
- addRule("file_read", "file_read:/specific/*", "/tmp", "allow");
580
- const match = findHighestPriorityRule(
581
- "file_read",
582
- ["file_read:/other/path"],
583
- "/tmp",
584
- );
585
- expect(match).toBeNull();
586
- });
587
-
588
- test("respects scope matching", () => {
589
- // Use file_read — bash has a global default allow rule that matches everywhere.
590
- addRule(
591
- "file_read",
592
- "file_read:/home/user/project/*",
593
- "/home/user/project",
594
- "deny",
595
- );
596
- expect(
597
- findHighestPriorityRule(
598
- "file_read",
599
- ["file_read:/home/user/project/file.txt"],
600
- "/home/user/project/sub",
601
- ),
602
- ).not.toBeNull();
603
- expect(
604
- findHighestPriorityRule(
605
- "file_read",
606
- ["file_read:/home/user/project/file.txt"],
607
- "/home/other",
608
- ),
609
- ).toBeNull();
610
- });
611
-
612
- test("everywhere scope matches any directory", () => {
613
- addRule("bash", "git *", "everywhere", "allow");
614
- const match = findHighestPriorityRule(
615
- "bash",
616
- ["git status"],
617
- "/any/random/path",
618
- );
619
- expect(match).not.toBeNull();
620
- });
621
- });
622
-
623
- // ── getAllRules ─────────────────────────────────────────────────
624
-
625
- describe("getAllRules", () => {
626
- test("returns default rules when no user rules exist", () => {
627
- const rules = getAllRules();
628
- expect(rules).toHaveLength(NUM_DEFAULTS);
629
- expect(rules.every((r) => r.id.startsWith("default:"))).toBe(true);
630
- });
631
-
632
- test("returns a copy (not the internal array)", () => {
633
- addRule("bash", "git *", "/tmp");
634
- const rules1 = getAllRules();
635
- const rules2 = getAllRules();
636
- expect(rules1).toEqual(rules2);
637
- expect(rules1).not.toBe(rules2); // different references
638
- });
639
- });
640
-
641
- // ── clearCache ─────────────────────────────────────────────────
642
-
643
- describe("clearCache", () => {
644
- test("forces reload from disk on next access", () => {
645
- addRule("bash", "git *", "/tmp");
646
- expect(getAllRules()).toHaveLength(1 + NUM_DEFAULTS);
647
- clearCache();
648
- // After clearing cache, rules are reloaded from disk
649
- expect(getAllRules()).toHaveLength(1 + NUM_DEFAULTS);
650
- });
651
- });
652
-
653
- // ── persistence ─────────────────────────────────────────────────
654
-
655
- describe("persistence", () => {
656
- test("rules survive cache clear (loaded from disk)", () => {
657
- const rule = addRule("bash", "npm *", "/tmp");
658
- clearCache();
659
- const rules = getAllRules();
660
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
661
- expect(rules.find((r) => r.id === rule.id)).toBeDefined();
662
- });
663
-
664
- test("trust file has correct structure", () => {
665
- addRule("bash", "git *", "/tmp");
666
- const data = JSON.parse(readFileSync(trustPath, "utf-8"));
667
- expect(data).toHaveProperty("version", 3);
668
- expect(data).toHaveProperty("rules");
669
- expect(Array.isArray(data.rules)).toBe(true);
670
- const userRule = data.rules.find(
671
- (r: { pattern: string }) => r.pattern === "git *",
672
- );
673
- expect(userRule).toHaveProperty("priority", 100);
674
- });
675
- });
676
-
677
- // ── deny rules ─────────────────────────────────────────────────
678
-
679
- describe("deny rules", () => {
680
- test("addRule with deny decision creates a deny rule", () => {
681
- const rule = addRule("bash", "rm -rf *", "/tmp", "deny");
682
- expect(rule.decision).toBe("deny");
683
- expect(rule.tool).toBe("bash");
684
- expect(rule.pattern).toBe("rm -rf *");
685
- });
686
-
687
- test("deny rule persists to disk", () => {
688
- addRule("bash", "rm *", "/tmp", "deny");
689
- clearCache();
690
- const rules = getAllRules();
691
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
692
- const userRule = rules.find((r) => r.pattern === "rm *");
693
- expect(userRule).toBeDefined();
694
- expect(userRule!.decision).toBe("deny");
695
- });
696
-
697
- test("findDenyRule finds deny rules", () => {
698
- addRule("bash", "rm *", "/tmp", "deny");
699
- const match = findDenyRule("bash", "rm file.txt", "/tmp");
700
- expect(match).not.toBeNull();
701
- expect(match!.decision).toBe("deny");
702
- });
703
-
704
- test("findDenyRule ignores allow rules", () => {
705
- addRule("bash", "rm *", "/tmp", "allow");
706
- const match = findDenyRule("bash", "rm file.txt", "/tmp");
707
- expect(match).toBeNull();
708
- });
709
-
710
- test("findMatchingRule ignores deny rules", () => {
711
- // Use host_file_read — it has an 'ask' default so findMatchingRule (allow-only) won't find it.
712
- addRule("host_file_read", "host_file_read:/etc/*", "/tmp", "deny");
713
- const match = findMatchingRule(
714
- "host_file_read",
715
- "host_file_read:/etc/hosts",
716
- "/tmp",
717
- );
718
- expect(match).toBeNull();
719
- });
720
-
721
- test("deny and allow rules coexist", () => {
722
- addRule("bash", "git *", "/tmp", "allow");
723
- addRule("bash", "git push --force *", "/tmp", "deny");
724
- expect(findMatchingRule("bash", "git status", "/tmp")).not.toBeNull();
725
- expect(
726
- findDenyRule("bash", "git push --force origin", "/tmp"),
727
- ).not.toBeNull();
728
- });
729
-
730
- test("deny rule with scope matching", () => {
731
- addRule("bash", "rm *", "/home/user/project", "deny");
732
- expect(
733
- findDenyRule("bash", "rm file.txt", "/home/user/project/sub"),
734
- ).not.toBeNull();
735
- expect(findDenyRule("bash", "rm file.txt", "/home/other")).toBeNull();
736
- });
737
-
738
- test("deny rule with everywhere scope", () => {
739
- addRule("bash", "rm -rf *", "everywhere", "deny");
740
- expect(findDenyRule("bash", "rm -rf /", "/any/path")).not.toBeNull();
741
- });
742
-
743
- test("removeRule works for deny rules", () => {
744
- const rule = addRule("bash", "rm *", "/tmp", "deny");
745
- expect(removeRule(rule.id)).toBe(true);
746
- expect(findDenyRule("bash", "rm file.txt", "/tmp")).toBeNull();
747
- });
748
- });
749
-
750
- // ── default rules ─────────────────────────────────────────────
751
-
752
- describe("default rules", () => {
753
- test("backfills default rules on first load", () => {
754
- const rules = getAllRules();
755
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
756
- expect(defaults).toHaveLength(NUM_DEFAULTS);
757
- for (const rule of defaults) {
758
- expect(rule.priority).toBe(DEFAULT_PRIORITY_BY_ID.get(rule.id)!);
759
- if (
760
- rule.id === "default:allow-bash-rm-bootstrap" ||
761
- rule.id === "default:allow-bash-rm-updates"
762
- ) {
763
- expect(ruleScope(rule)).toBe(testDir);
764
- } else {
765
- // Non-scoped tool families (managed skill tools, skill_load) no
766
- // longer carry an explicit scope field after normalization, but
767
- // ruleScope() returns "everywhere" for rules without scope.
768
- expect(ruleScope(rule)).toBe("everywhere");
769
- }
770
- }
771
- });
772
-
773
- test("default rules cover file, host file, host shell, and workspace prompt tools", () => {
774
- const rules = getAllRules();
775
- const defaultTools = [
776
- ...new Set(
777
- rules.filter((r) => r.id.startsWith("default:")).map((r) => r.tool),
778
- ),
779
- ].sort();
780
- expect(defaultTools).toEqual([
781
- "bash",
782
- "computer_use_click",
783
- "computer_use_drag",
784
- "computer_use_key",
785
- "computer_use_observe",
786
- "computer_use_open_app",
787
- "computer_use_run_applescript",
788
- "computer_use_scroll",
789
- "computer_use_type_text",
790
- "computer_use_wait",
791
- "delete_managed_skill",
792
- "file_edit",
793
- "file_read",
794
- "file_write",
795
- "host_bash",
796
- "host_file_edit",
797
- "host_file_read",
798
- "host_file_write",
799
- "recall",
800
- "scaffold_managed_skill",
801
- "skill_execute",
802
- "skill_load",
803
- "ui_dismiss",
804
- "ui_show",
805
- "ui_update",
806
- ]);
807
- });
808
-
809
- test("default rules are not duplicated on reload", () => {
810
- getAllRules(); // first load
811
- clearCache();
812
- const rules = getAllRules(); // second load
813
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
814
- expect(defaults).toHaveLength(NUM_DEFAULTS);
815
- });
816
-
817
- test("default rules persist to disk", () => {
818
- getAllRules(); // triggers backfill + save
819
- const data = JSON.parse(readFileSync(trustPath, "utf-8"));
820
- const defaults = data.rules.filter((r: { id: string }) =>
821
- r.id.startsWith("default:"),
822
- );
823
- expect(defaults).toHaveLength(NUM_DEFAULTS);
824
- });
825
-
826
- test("removed default rule is re-backfilled on next load", () => {
827
- // First load backfills defaults
828
- getAllRules();
829
- // Remove one default rule by editing trust.json directly on disk
830
- // (removeRule() throws for default rules, so we simulate external editing)
831
- const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
832
- raw.rules = raw.rules.filter(
833
- (r: { id: string }) => r.id !== "default:ask-host_file_read-global",
834
- );
835
- writeFileSync(trustPath, JSON.stringify(raw, null, 2));
836
- // After reload, the rule is re-backfilled (defaults are always present)
837
- clearCache();
838
- const rules = getAllRules();
839
- expect(
840
- rules.find((r) => r.id === "default:ask-host_file_read-global"),
841
- ).toBeDefined();
842
- });
843
-
844
- test("findHighestPriorityRule matches default ask for host_file_read", () => {
845
- const match = findHighestPriorityRule(
846
- "host_file_read",
847
- ["host_file_read:/etc/hosts"],
848
- "/tmp",
849
- );
850
- expect(match).not.toBeNull();
851
- expect(match!.id).toBe("default:ask-host_file_read-global");
852
- expect(match!.decision).toBe("ask");
853
- expect(match!.priority).toBe(
854
- DEFAULT_PRIORITY_BY_ID.get("default:ask-host_file_read-global")!,
855
- );
856
- });
857
-
858
- test("findHighestPriorityRule matches default ask for host_file_write", () => {
859
- const match = findHighestPriorityRule(
860
- "host_file_write",
861
- ["host_file_write:/etc/hosts"],
862
- "/tmp",
863
- );
864
- expect(match).not.toBeNull();
865
- expect(match!.id).toBe("default:ask-host_file_write-global");
866
- expect(match!.decision).toBe("ask");
867
- expect(match!.priority).toBe(
868
- DEFAULT_PRIORITY_BY_ID.get("default:ask-host_file_write-global")!,
869
- );
870
- });
871
-
872
- test("findHighestPriorityRule matches default ask for host_file_edit", () => {
873
- const match = findHighestPriorityRule(
874
- "host_file_edit",
875
- ["host_file_edit:/etc/hosts"],
876
- "/tmp",
877
- );
878
- expect(match).not.toBeNull();
879
- expect(match!.id).toBe("default:ask-host_file_edit-global");
880
- expect(match!.decision).toBe("ask");
881
- expect(match!.priority).toBe(
882
- DEFAULT_PRIORITY_BY_ID.get("default:ask-host_file_edit-global")!,
883
- );
884
- });
885
-
886
- test("findHighestPriorityRule matches default ask for host_bash", () => {
887
- const match = findHighestPriorityRule("host_bash", ["ls"], "/tmp");
888
- expect(match).not.toBeNull();
889
- expect(match!.id).toBe("default:ask-host_bash-global");
890
- expect(match!.decision).toBe("ask");
891
- expect(match!.priority).toBe(
892
- DEFAULT_PRIORITY_BY_ID.get("default:ask-host_bash-global")!,
893
- );
894
- });
895
-
896
- test("findHighestPriorityRule matches default ask for computer_use_click", () => {
897
- const match = findHighestPriorityRule(
898
- "computer_use_click",
899
- ["computer_use_click:"],
900
- "/tmp",
901
- );
902
- expect(match).not.toBeNull();
903
- expect(match!.id).toBe("default:ask-computer_use_click-global");
904
- expect(match!.decision).toBe("ask");
905
- expect(match!.priority).toBe(
906
- DEFAULT_PRIORITY_BY_ID.get("default:ask-computer_use_click-global")!,
907
- );
908
- });
909
-
910
- test("findHighestPriorityRule matches default ask for computer_use_observe", () => {
911
- const match = findHighestPriorityRule(
912
- "computer_use_observe",
913
- ["computer_use_observe:"],
914
- "/tmp",
915
- );
916
- expect(match).not.toBeNull();
917
- expect(match!.id).toBe("default:ask-computer_use_observe-global");
918
- expect(match!.decision).toBe("ask");
919
- expect(match!.priority).toBe(
920
- DEFAULT_PRIORITY_BY_ID.get("default:ask-computer_use_observe-global")!,
921
- );
922
- });
923
-
924
- test("bootstrap delete rule matches only when workingDir is the workspace dir", () => {
925
- const workspaceDir = testDir;
926
- // Should match when workingDir is the workspace directory — the bootstrap
927
- // rule (priority 100) outranks the global default allow (priority 50).
928
- const match = findHighestPriorityRule(
929
- "bash",
930
- ["rm BOOTSTRAP.md"],
931
- workspaceDir,
932
- );
933
- expect(match).not.toBeNull();
934
- expect(match!.id).toBe("default:allow-bash-rm-bootstrap");
935
- expect(match!.decision).toBe("allow");
936
- // Outside workspace, the bootstrap rule doesn't match — without
937
- // IS_CONTAINERIZED there is no catch-all bash allow rule either.
938
- const other = findHighestPriorityRule(
939
- "bash",
940
- ["rm BOOTSTRAP.md"],
941
- "/tmp/other-project",
942
- );
943
- expect(other).toBeNull();
944
- });
945
-
946
- test("updates delete rule matches only when workingDir is the workspace dir", () => {
947
- const workspaceDir = testDir;
948
- const match = findHighestPriorityRule(
949
- "bash",
950
- ["rm UPDATES.md"],
951
- workspaceDir,
952
- );
953
- expect(match).not.toBeNull();
954
- expect(match!.id).toBe("default:allow-bash-rm-updates");
955
- expect(match!.decision).toBe("allow");
956
- // Outside workspace, should NOT match the updates rule — without
957
- // IS_CONTAINERIZED there is no catch-all bash allow rule either.
958
- const other = findHighestPriorityRule(
959
- "bash",
960
- ["rm UPDATES.md"],
961
- "/tmp/other-project",
962
- );
963
- expect(other).toBeNull();
964
- });
965
-
966
- test("default ask does not affect files outside protected directory", () => {
967
- const safePath = join(testDir, "data", "assistant.db");
968
- const match = findHighestPriorityRule(
969
- "file_read",
970
- [`file_read:${safePath}`],
971
- "/tmp",
972
- );
973
- // Should not match a default deny rule
974
- expect(match == null || !match.id.startsWith("default:")).toBe(true);
975
- });
976
-
977
- test("default rules are backfilled after malformed JSON in trust file", () => {
978
- mkdirSync(dirname(trustPath), { recursive: true });
979
- writeFileSync(trustPath, "NOT VALID JSON {{{");
980
- clearCache();
981
- const rules = getAllRules();
982
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
983
- expect(defaults).toHaveLength(NUM_DEFAULTS);
984
- });
985
-
986
- test("default rules are backfilled in-memory after unknown file version without overwriting disk", () => {
987
- mkdirSync(dirname(trustPath), { recursive: true });
988
- const originalContent = JSON.stringify({
989
- version: 9999,
990
- rules: [
991
- {
992
- id: "future-rule",
993
- tool: "bash",
994
- pattern: "future *",
995
- scope: "everywhere",
996
- decision: "allow",
997
- priority: 50,
998
- createdAt: 1000,
999
- },
1000
- ],
1001
- });
1002
- writeFileSync(trustPath, originalContent);
1003
- clearCache();
1004
- const rules = getAllRules();
1005
- // Defaults should be present in-memory
1006
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
1007
- expect(defaults).toHaveLength(NUM_DEFAULTS);
1008
- // The on-disk file must NOT be overwritten — it preserves the unknown format
1009
- const diskContent = readFileSync(trustPath, "utf-8");
1010
- expect(diskContent).toBe(originalContent);
1011
- });
1012
-
1013
- test("clearAllRules preserves default rules", () => {
1014
- addRule("bash", "git *", "/tmp");
1015
- clearAllRules();
1016
- const rules = getAllRules();
1017
- // User rules should be gone, but defaults should remain
1018
- expect(rules.filter((r) => !r.id.startsWith("default:"))).toHaveLength(0);
1019
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
1020
- expect(defaults).toHaveLength(NUM_DEFAULTS);
1021
- });
1022
-
1023
- // ── skill source mutation rules ────────────────────────────────
1024
-
1025
- test("default rules include ask rules for file_write on skill source paths", () => {
1026
- const rules = getAllRules();
1027
- const managed = rules.find(
1028
- (r) => r.id === "default:ask-file_write-managed-skills",
1029
- );
1030
- expect(managed).toBeDefined();
1031
- expect(managed!.tool).toBe("file_write");
1032
- expect(managed!.decision).toBe("ask");
1033
- expect(managed!.priority).toBe(50);
1034
- expect(managed!.pattern).toContain("skills/**");
1035
-
1036
- const bundled = rules.find(
1037
- (r) => r.id === "default:ask-file_write-bundled-skills",
1038
- );
1039
- expect(bundled).toBeDefined();
1040
- expect(bundled!.tool).toBe("file_write");
1041
- expect(bundled!.decision).toBe("ask");
1042
- expect(bundled!.priority).toBe(50);
1043
- });
1044
-
1045
- test("default rules include ask rules for file_edit on skill source paths", () => {
1046
- const rules = getAllRules();
1047
- const managed = rules.find(
1048
- (r) => r.id === "default:ask-file_edit-managed-skills",
1049
- );
1050
- expect(managed).toBeDefined();
1051
- expect(managed!.tool).toBe("file_edit");
1052
- expect(managed!.decision).toBe("ask");
1053
- expect(managed!.priority).toBe(50);
1054
- expect(managed!.pattern).toContain("skills/**");
1055
-
1056
- const bundled = rules.find(
1057
- (r) => r.id === "default:ask-file_edit-bundled-skills",
1058
- );
1059
- expect(bundled).toBeDefined();
1060
- expect(bundled!.tool).toBe("file_edit");
1061
- expect(bundled!.decision).toBe("ask");
1062
- expect(bundled!.priority).toBe(50);
1063
- });
1064
-
1065
- // ── default allow: skill_load ────────────────────────────────
1066
-
1067
- test("skill_load default allow rule exists in templates", () => {
1068
- const templates = getDefaultRuleTemplates();
1069
- const skillLoadRule = templates.find(
1070
- (t) => t.id === "default:allow-skill_load-global",
1071
- );
1072
- expect(skillLoadRule).toBeDefined();
1073
- expect(skillLoadRule!.tool).toBe("skill_load");
1074
- expect(skillLoadRule!.pattern).toBe("skill_load:*");
1075
- expect(skillLoadRule!.decision).toBe("allow");
1076
- // skill_load is a non-scoped tool — template omits scope
1077
- expect(skillLoadRule!.scope).toBeUndefined();
1078
- });
1079
-
1080
- test("findHighestPriorityRule matches default allow for skill_load", () => {
1081
- const match = findHighestPriorityRule(
1082
- "skill_load",
1083
- ["skill_load:browser"],
1084
- "/tmp",
1085
- );
1086
- expect(match).not.toBeNull();
1087
- expect(match!.id).toBe("default:allow-skill_load-global");
1088
- expect(match!.decision).toBe("allow");
1089
- expect(match!.priority).toBe(100);
1090
- });
1091
-
1092
- test("findHighestPriorityRule matches default allow for skill_load with any skill name", () => {
1093
- const match = findHighestPriorityRule(
1094
- "skill_load",
1095
- ["skill_load:some-random-skill"],
1096
- "/tmp",
1097
- );
1098
- expect(match).not.toBeNull();
1099
- expect(match!.id).toBe("default:allow-skill_load-global");
1100
- expect(match!.decision).toBe("allow");
1101
- });
1102
-
1103
- test("no default ask rules exist for file_read on skill source paths", () => {
1104
- const rules = getAllRules();
1105
- // There should be no default rules with IDs matching file_read for skill sources
1106
- const readManagedSkill = rules.find(
1107
- (r) => r.id === "default:ask-file_read-managed-skills",
1108
- );
1109
- const readBundledSkill = rules.find(
1110
- (r) => r.id === "default:ask-file_read-bundled-skills",
1111
- );
1112
- expect(readManagedSkill).toBeUndefined();
1113
- expect(readBundledSkill).toBeUndefined();
1114
- });
1115
-
1116
- test("findHighestPriorityRule matches default ask for file_write on managed skill path", () => {
1117
- const skillFile = join(testDir, "skills", "my-skill", "SKILL.md");
1118
- const match = findHighestPriorityRule(
1119
- "file_write",
1120
- [`file_write:${skillFile}`],
1121
- "/tmp",
1122
- );
1123
- expect(match).not.toBeNull();
1124
- expect(match!.id).toBe("default:ask-file_write-managed-skills");
1125
- expect(match!.decision).toBe("ask");
1126
- });
1127
-
1128
- test("findHighestPriorityRule matches default ask for file_edit on managed skill path", () => {
1129
- const skillFile = join(testDir, "skills", "my-skill", "tools.ts");
1130
- const match = findHighestPriorityRule(
1131
- "file_edit",
1132
- [`file_edit:${skillFile}`],
1133
- "/tmp",
1134
- );
1135
- expect(match).not.toBeNull();
1136
- expect(match!.id).toBe("default:ask-file_edit-managed-skills");
1137
- expect(match!.decision).toBe("ask");
1138
- });
1139
-
1140
- // ── userModifiedAt and backfill migration ──────────────────────
1141
-
1142
- test("default rules without userModifiedAt are migrated when template changes", () => {
1143
- // First load backfills defaults
1144
- getAllRules();
1145
- // Manually alter a default rule on disk to simulate a template change
1146
- // (the rule on disk has old values, template has new ones)
1147
- const raw = JSON.parse(readFileSync(trustPath, "utf-8"));
1148
- const idx = raw.rules.findIndex(
1149
- (r: { id: string }) => r.id === "default:ask-host_bash-global",
1150
- );
1151
- expect(idx).toBeGreaterThanOrEqual(0);
1152
- // Manually set an old priority to simulate the rule diverging from template
1153
- raw.rules[idx].priority = 9999;
1154
- writeFileSync(trustPath, JSON.stringify(raw, null, 2));
1155
- clearCache();
1156
- const rules = getAllRules();
1157
- const found = rules.find((r) => r.id === "default:ask-host_bash-global");
1158
- expect(found).toBeDefined();
1159
- // Should be migrated back to the template priority (50)
1160
- expect(found!.priority).toBe(50);
1161
- });
1162
-
1163
- test("default rules with userModifiedAt are preserved during backfill migration", () => {
1164
- // First load backfills defaults
1165
- getAllRules();
1166
- // Modify the rule via updateRule to set userModifiedAt
1167
- updateRule("default:ask-host_bash-global", { decision: "allow" });
1168
- // Verify userModifiedAt is set
1169
- let rules = getAllRules();
1170
- let found = rules.find((r) => r.id === "default:ask-host_bash-global");
1171
- expect(found).toBeDefined();
1172
- expect(found!.userModifiedAt).toBeGreaterThan(0);
1173
- expect(found!.decision).toBe("allow");
1174
-
1175
- // Now simulate a template change by altering what the template expects:
1176
- // on disk the rule has decision=allow + userModifiedAt, but the template
1177
- // would try to migrate it back to decision=ask. Since userModifiedAt is
1178
- // set, backfillDefaults should skip it.
1179
- clearCache();
1180
- rules = getAllRules();
1181
- found = rules.find((r) => r.id === "default:ask-host_bash-global");
1182
- expect(found).toBeDefined();
1183
- // The user's override should be preserved
1184
- expect(found!.decision).toBe("allow");
1185
- expect(found!.userModifiedAt).toBeGreaterThan(0);
1186
- });
1187
-
1188
- test("userModifiedAt survives round-trip through disk", () => {
1189
- getAllRules(); // backfill
1190
- updateRule("default:ask-host_bash-global", { priority: 999 });
1191
- const before = getAllRules().find(
1192
- (r) => r.id === "default:ask-host_bash-global",
1193
- )!;
1194
- expect(before.userModifiedAt).toBeGreaterThan(0);
1195
-
1196
- // Round-trip through disk
1197
- clearCache();
1198
- const after = getAllRules().find(
1199
- (r) => r.id === "default:ask-host_bash-global",
1200
- )!;
1201
- expect(after.userModifiedAt).toBe(before.userModifiedAt);
1202
- expect(after.priority).toBe(999);
1203
- });
1204
- });
1205
-
1206
- // ── trust rule schema v3 (PR 14) ──────────────────────────────
1207
-
1208
- describe("trust rule schema v3 (PR 14)", () => {
1209
- test("new rules can include v3 optional fields (executionTarget)", () => {
1210
- const rule = addRule("bash", "git *", "/tmp");
1211
- // Manually set v3 optional field on the rule and persist
1212
- rule.executionTarget = "/usr/local/bin/node";
1213
- // Re-persist the updated rules
1214
- const rules = getAllRules().map((r) => (r.id === rule.id ? rule : r));
1215
- // Write directly to verify round-trip
1216
- const trustData = { version: 3, rules };
1217
- writeFileSync(trustPath, JSON.stringify(trustData, null, 2));
1218
- clearCache();
1219
- const reloaded = getAllRules();
1220
- const found = reloaded.find((r) => r.id === rule.id);
1221
- expect(found).toBeDefined();
1222
- expect(found!.executionTarget).toBe("/usr/local/bin/node");
1223
- });
1224
-
1225
- test("trust file persists with version 3", () => {
1226
- addRule("bash", "echo *", "/tmp");
1227
- const data = JSON.parse(readFileSync(trustPath, "utf-8"));
1228
- expect(data.version).toBe(3);
1229
- });
1230
- });
1231
-
1232
- // ── loadFromDisk resilience (misc) ──────────────────────────────
1233
-
1234
- describe("loadFromDisk resilience (misc)", () => {
1235
- test("malformed file (valid JSON but null) is handled gracefully", () => {
1236
- mkdirSync(dirname(trustPath), { recursive: true });
1237
- writeFileSync(trustPath, "null");
1238
- clearCache();
1239
- const rules = getAllRules();
1240
- // Accessing null.version throws TypeError, caught by try/catch,
1241
- // falls through to backfill defaults
1242
- expect(rules).toHaveLength(NUM_DEFAULTS);
1243
- });
1244
-
1245
- test("v3 file with optional fields is loaded correctly without re-migration", () => {
1246
- mkdirSync(dirname(trustPath), { recursive: true });
1247
- const v3Rules = [
1248
- {
1249
- id: "v3-with-options",
1250
- tool: "bash",
1251
- pattern: "skill-cmd *",
1252
- scope: "/tmp",
1253
- decision: "allow",
1254
- priority: 100,
1255
- createdAt: 7000,
1256
- executionTarget: "/usr/bin/node",
1257
- },
1258
- {
1259
- id: "v3-without-options",
1260
- tool: "bash",
1261
- pattern: "git *",
1262
- scope: "/tmp",
1263
- decision: "allow",
1264
- priority: 100,
1265
- createdAt: 7001,
1266
- },
1267
- ];
1268
- writeFileSync(trustPath, JSON.stringify({ version: 3, rules: v3Rules }));
1269
- clearCache();
1270
- const rules = getAllRules();
1271
-
1272
- // Rule with optional fields should have them preserved
1273
- const withOptions = rules.find((r) => r.id === "v3-with-options");
1274
- expect(withOptions).toBeDefined();
1275
- expect(withOptions!.executionTarget).toBe("/usr/bin/node");
1276
-
1277
- // Rule without optional fields should remain without them
1278
- const withoutOptions = rules.find((r) => r.id === "v3-without-options");
1279
- expect(withoutOptions).toBeDefined();
1280
- expect(withoutOptions).not.toHaveProperty("executionTarget");
1281
- });
1282
-
1283
- test("legacy v2 version migrates rules and persists as v3", () => {
1284
- mkdirSync(dirname(trustPath), { recursive: true });
1285
- writeFileSync(
1286
- trustPath,
1287
- JSON.stringify({
1288
- version: 2,
1289
- rules: [
1290
- {
1291
- id: "old-version-rule",
1292
- tool: "bash",
1293
- pattern: "git *",
1294
- scope: "/tmp",
1295
- decision: "allow",
1296
- priority: 100,
1297
- createdAt: 5000,
1298
- },
1299
- ],
1300
- }),
1301
- );
1302
- clearCache();
1303
- const rules = getAllRules();
1304
- const migratedRule = rules.find((r) => r.id === "old-version-rule");
1305
- expect(migratedRule).toBeDefined();
1306
- expect(migratedRule!.decision).toBe("allow");
1307
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
1308
-
1309
- // File should be persisted to the current schema version.
1310
- const data = JSON.parse(readFileSync(trustPath, "utf-8"));
1311
- expect(data.version).toBe(3);
1312
- expect(
1313
- data.rules.some((r: { id: string }) => r.id === "old-version-rule"),
1314
- ).toBe(true);
1315
- });
1316
-
1317
- test("legacy v1 version migrates rules and persists as v3", () => {
1318
- mkdirSync(dirname(trustPath), { recursive: true });
1319
- writeFileSync(
1320
- trustPath,
1321
- JSON.stringify({
1322
- version: 1,
1323
- rules: [
1324
- {
1325
- id: "v1-rule",
1326
- tool: "bash",
1327
- pattern: "rm *",
1328
- scope: "everywhere",
1329
- decision: "deny",
1330
- priority: 200,
1331
- createdAt: 4000,
1332
- },
1333
- ],
1334
- }),
1335
- );
1336
-
1337
- clearCache();
1338
- const rules = getAllRules();
1339
- const migratedRule = rules.find((r) => r.id === "v1-rule");
1340
- expect(migratedRule).toBeDefined();
1341
- expect(migratedRule!.decision).toBe("deny");
1342
-
1343
- const data = JSON.parse(readFileSync(trustPath, "utf-8"));
1344
- expect(data.version).toBe(3);
1345
- expect(data.rules.some((r: { id: string }) => r.id === "v1-rule")).toBe(
1346
- true,
1347
- );
1348
- });
1349
- });
1350
-
1351
- // ── executionTarget-aware rule matching ──────────────────────
1352
-
1353
- describe("executionTarget-aware rule matching", () => {
1354
- /**
1355
- * Helper: write a v3 trust file with the given rules directly to disk,
1356
- * then clear the cache so the next getRules() call picks them up.
1357
- */
1358
- function seedRules(rules: Array<Record<string, unknown>>): void {
1359
- mkdirSync(dirname(trustPath), { recursive: true });
1360
- writeFileSync(trustPath, JSON.stringify({ version: 3, rules }));
1361
- clearCache();
1362
- }
1363
-
1364
- // ── wildcard semantics (no executionTarget on rule) ──────────
1365
-
1366
- describe("wildcard semantics — rules without executionTarget", () => {
1367
- test("rule with no executionTarget matches when no context is provided", () => {
1368
- addRule("bash", "git *", "/tmp", "allow", 200);
1369
- const match = findHighestPriorityRule("bash", ["git status"], "/tmp");
1370
- expect(match).not.toBeNull();
1371
- expect(match!.decision).toBe("allow");
1372
- });
1373
-
1374
- test("rule with no executionTarget matches any execution target", () => {
1375
- addRule("bash", "git *", "/tmp", "allow", 200);
1376
- const match = findHighestPriorityRule("bash", ["git status"], "/tmp", {
1377
- executionTarget: "/usr/bin/node",
1378
- });
1379
- expect(match).not.toBeNull();
1380
- expect(match!.decision).toBe("allow");
1381
- });
1382
- });
1383
-
1384
- // ── executionTarget matching ──────────────────────────────────
1385
-
1386
- describe("executionTarget matching", () => {
1387
- test("rule with executionTarget matches exact target", () => {
1388
- seedRules([
1389
- {
1390
- id: "et-exact",
1391
- tool: "bash",
1392
- pattern: "run *",
1393
- scope: "everywhere",
1394
- decision: "allow",
1395
- priority: 200,
1396
- createdAt: Date.now(),
1397
- executionTarget: "/usr/local/bin/node",
1398
- },
1399
- ]);
1400
- const match = findHighestPriorityRule(
1401
- "bash",
1402
- ["run script.js"],
1403
- "/tmp",
1404
- {
1405
- executionTarget: "/usr/local/bin/node",
1406
- },
1407
- );
1408
- expect(match).not.toBeNull();
1409
- expect(match!.id).toBe("et-exact");
1410
- });
1411
-
1412
- test("rule with executionTarget does NOT match different target", () => {
1413
- seedRules([
1414
- {
1415
- id: "et-diff",
1416
- tool: "bash",
1417
- pattern: "run *",
1418
- scope: "everywhere",
1419
- decision: "allow",
1420
- priority: 200,
1421
- createdAt: Date.now(),
1422
- executionTarget: "/usr/local/bin/node",
1423
- },
1424
- ]);
1425
- const match = findHighestPriorityRule(
1426
- "bash",
1427
- ["run script.js"],
1428
- "/tmp",
1429
- {
1430
- executionTarget: "/usr/local/bin/bun",
1431
- },
1432
- );
1433
- expect(match == null || match.id !== "et-diff").toBe(true);
1434
- });
1435
-
1436
- test("rule with executionTarget does NOT match when no target in context", () => {
1437
- seedRules([
1438
- {
1439
- id: "et-no-ctx",
1440
- tool: "bash",
1441
- pattern: "run *",
1442
- scope: "everywhere",
1443
- decision: "allow",
1444
- priority: 200,
1445
- createdAt: Date.now(),
1446
- executionTarget: "/usr/local/bin/node",
1447
- },
1448
- ]);
1449
- const match = findHighestPriorityRule(
1450
- "bash",
1451
- ["run script.js"],
1452
- "/tmp",
1453
- {},
1454
- );
1455
- expect(match == null || match.id !== "et-no-ctx").toBe(true);
1456
- });
1457
-
1458
- test("rule WITHOUT executionTarget matches any target (wildcard)", () => {
1459
- addRule("bash", "run *", "/tmp", "allow", 200);
1460
- const match = findHighestPriorityRule(
1461
- "bash",
1462
- ["run script.js"],
1463
- "/tmp",
1464
- {
1465
- executionTarget: "/any/path/to/runtime",
1466
- },
1467
- );
1468
- expect(match).not.toBeNull();
1469
- expect(match!.pattern).toBe("run *");
1470
- });
1471
- });
1472
-
1473
- // ── optional ctx parameter ────────────────────────────────────
1474
-
1475
- describe("optional ctx parameter", () => {
1476
- test("callers without ctx parameter still work", () => {
1477
- addRule("bash", "git *", "/tmp", "allow", 200);
1478
- // Calling without the 4th argument — must still match
1479
- const match = findHighestPriorityRule("bash", ["git status"], "/tmp");
1480
- expect(match).not.toBeNull();
1481
- expect(match!.pattern).toBe("git *");
1482
- });
1483
-
1484
- test("empty PolicyContext object behaves the same as no context", () => {
1485
- addRule("bash", "ls *", "/tmp", "allow", 200);
1486
- const matchNoCtx = findHighestPriorityRule("bash", ["ls -la"], "/tmp");
1487
- const matchEmptyCtx = findHighestPriorityRule(
1488
- "bash",
1489
- ["ls -la"],
1490
- "/tmp",
1491
- {},
1492
- );
1493
- expect(matchNoCtx).not.toBeNull();
1494
- expect(matchEmptyCtx).not.toBeNull();
1495
- expect(matchNoCtx!.id).toBe(matchEmptyCtx!.id);
1496
- });
1497
- });
1498
- });
1499
-
1500
- // ── network_request trust rule matching ────────────────────────
1501
-
1502
- describe("network_request trust rules", () => {
1503
- test("exact origin rule matches network_request candidates", () => {
1504
- addRule(
1505
- "network_request",
1506
- "network_request:https://api.example.com/*",
1507
- "everywhere",
1508
- );
1509
- const rule = findHighestPriorityRule(
1510
- "network_request",
1511
- [
1512
- "network_request:https://api.example.com/v1/data",
1513
- "network_request:https://api.example.com/*",
1514
- ],
1515
- "/tmp",
1516
- );
1517
- expect(rule).not.toBeNull();
1518
- expect(rule!.decision).toBe("allow");
1519
- });
1520
-
1521
- test("exact url rule matches only that url candidate", () => {
1522
- addRule(
1523
- "network_request",
1524
- "network_request:https://api.example.com/v1/data",
1525
- "everywhere",
1526
- );
1527
- const match = findHighestPriorityRule(
1528
- "network_request",
1529
- [
1530
- "network_request:https://api.example.com/v1/data",
1531
- "network_request:https://api.example.com/*",
1532
- ],
1533
- "/tmp",
1534
- );
1535
- expect(match).not.toBeNull();
1536
-
1537
- const noMatch = findHighestPriorityRule(
1538
- "network_request",
1539
- ["network_request:https://api.example.com/v2/other"],
1540
- "/tmp",
1541
- );
1542
- expect(noMatch).toBeNull();
1543
- });
1544
-
1545
- test("globstar rule matches any network_request candidate", () => {
1546
- // minimatch treats standalone "**" as globstar (matching "/"), but
1547
- // "network_request:*" uses single "*" which doesn't cross slashes.
1548
- // The tool field is already filtered by findHighestPriorityRule, so
1549
- // "**" is the correct catch-all pattern.
1550
- addRule("network_request", "**", "everywhere");
1551
- const rule = findHighestPriorityRule(
1552
- "network_request",
1553
- ["network_request:https://any-host.example.org/path"],
1554
- "/tmp",
1555
- );
1556
- expect(rule).not.toBeNull();
1557
- });
1558
-
1559
- test("single-star wildcard matches flat candidates only", () => {
1560
- // "network_request:*" won't match URLs with slashes — consistent
1561
- // with the behavior of web_fetch:* patterns.
1562
- addRule("network_request", "network_request:*", "everywhere");
1563
- const noSlashMatch = findHighestPriorityRule(
1564
- "network_request",
1565
- ["network_request:flat-target"],
1566
- "/tmp",
1567
- );
1568
- expect(noSlashMatch).not.toBeNull();
1569
-
1570
- const slashNoMatch = findHighestPriorityRule(
1571
- "network_request",
1572
- ["network_request:https://example.com/path"],
1573
- "/tmp",
1574
- );
1575
- // Single "*" does not match "/" so this URL candidate won't match.
1576
- expect(slashNoMatch).toBeNull();
1577
- });
1578
-
1579
- test("network_request rule does not match web_fetch tool", () => {
1580
- addRule(
1581
- "network_request",
1582
- "network_request:https://api.example.com/*",
1583
- "everywhere",
1584
- );
1585
- const rule = findHighestPriorityRule(
1586
- "web_fetch",
1587
- [
1588
- "web_fetch:https://api.example.com/v1/data",
1589
- "web_fetch:https://api.example.com/*",
1590
- ],
1591
- "/tmp",
1592
- );
1593
- expect(rule).toBeNull();
1594
- });
1595
-
1596
- test("web_fetch rule does not match network_request tool", () => {
1597
- addRule("web_fetch", "web_fetch:https://api.example.com/*", "everywhere");
1598
- const rule = findHighestPriorityRule(
1599
- "network_request",
1600
- [
1601
- "network_request:https://api.example.com/v1/data",
1602
- "network_request:https://api.example.com/*",
1603
- ],
1604
- "/tmp",
1605
- );
1606
- expect(rule).toBeNull();
1607
- });
1608
-
1609
- test("deny rule takes precedence over allow at same priority", () => {
1610
- addRule(
1611
- "network_request",
1612
- "network_request:https://api.example.com/*",
1613
- "everywhere",
1614
- "allow",
1615
- 100,
1616
- );
1617
- addRule(
1618
- "network_request",
1619
- "network_request:https://api.example.com/*",
1620
- "everywhere",
1621
- "deny",
1622
- 100,
1623
- );
1624
- const rule = findHighestPriorityRule(
1625
- "network_request",
1626
- [
1627
- "network_request:https://api.example.com/v1/data",
1628
- "network_request:https://api.example.com/*",
1629
- ],
1630
- "/tmp",
1631
- );
1632
- expect(rule).not.toBeNull();
1633
- expect(rule!.decision).toBe("deny");
1634
- });
1635
-
1636
- test("higher-priority allow overrides lower-priority deny", () => {
1637
- addRule(
1638
- "network_request",
1639
- "network_request:https://api.example.com/*",
1640
- "everywhere",
1641
- "deny",
1642
- 50,
1643
- );
1644
- addRule(
1645
- "network_request",
1646
- "network_request:https://api.example.com/*",
1647
- "everywhere",
1648
- "allow",
1649
- 100,
1650
- );
1651
- const rule = findHighestPriorityRule(
1652
- "network_request",
1653
- [
1654
- "network_request:https://api.example.com/v1/data",
1655
- "network_request:https://api.example.com/*",
1656
- ],
1657
- "/tmp",
1658
- );
1659
- expect(rule).not.toBeNull();
1660
- expect(rule!.decision).toBe("allow");
1661
- });
1662
-
1663
- test("network_request rules match regardless of working directory (URL tools ignore scope)", () => {
1664
- addRule(
1665
- "network_request",
1666
- "network_request:https://api.example.com/*",
1667
- "/home/user/project",
1668
- );
1669
- const inScope = findHighestPriorityRule(
1670
- "network_request",
1671
- ["network_request:https://api.example.com/*"],
1672
- "/home/user/project",
1673
- );
1674
- expect(inScope).not.toBeNull();
1675
-
1676
- // URL tools (network_request) do not support scope — the rule matches
1677
- // regardless of working directory because scope is stripped during
1678
- // normalization.
1679
- const outOfScope = findHighestPriorityRule(
1680
- "network_request",
1681
- ["network_request:https://api.example.com/*"],
1682
- "/tmp/other",
1683
- );
1684
- expect(outOfScope).not.toBeNull();
1685
- });
1686
- });
1687
- });
1688
-
1689
- describe("computer-use tool trust rule matching", () => {
1690
- test("actionable CU tools have default ask trust rules", () => {
1691
- // Actionable CU tools (those that perform screen interactions) should
1692
- // have default "ask" rules so strict mode prompts before use.
1693
- const actionableCuTools = ["computer_use_click", "computer_use_type_text"];
1694
-
1695
- for (const name of actionableCuTools) {
1696
- const rule = findHighestPriorityRule(name, [name], "/tmp/test");
1697
- expect(rule).not.toBeNull();
1698
- expect(rule!.decision).toBe("ask");
1699
- }
1700
- });
1701
-
1702
- test("terminal CU tools (done/respond) have no default trust rules", () => {
1703
- // computer_use_done and computer_use_respond are terminal signal tools
1704
- // with RiskLevel.Low — they should not have ask rules since they don't
1705
- // perform any screen action.
1706
- const terminalCuTools = ["computer_use_done", "computer_use_respond"];
1707
-
1708
- for (const name of terminalCuTools) {
1709
- const defaultRule = DEFAULT_TEMPLATES.find((t) => t.tool === name);
1710
- expect(defaultRule).toBeUndefined();
1711
- }
1712
- });
1713
- });
1714
-
1715
- // ── canonical parser normalization-on-load ─────────────────────────────────
1716
-
1717
- describe("canonical parser normalization-on-load", () => {
1718
- beforeEach(() => {
1719
- clearCache();
1720
- try {
1721
- rmSync(trustPath);
1722
- } catch {
1723
- /* may not exist */
1724
- }
1725
- });
1726
-
1727
- test("URL rule with executionTarget is stripped on load and re-saved", () => {
1728
- // A URL rule (web_fetch) should not carry executionTarget — the canonical
1729
- // parser strips it and marks the file for re-save.
1730
- mkdirSync(dirname(trustPath), { recursive: true });
1731
- writeFileSync(
1732
- trustPath,
1733
- JSON.stringify({
1734
- version: 3,
1735
- rules: [
1736
- {
1737
- id: "url-rule-with-et",
1738
- tool: "web_fetch",
1739
- pattern: "web_fetch:https://example.com/*",
1740
- scope: "everywhere",
1741
- decision: "allow",
1742
- priority: 100,
1743
- createdAt: 1000,
1744
- executionTarget: "/usr/bin/node",
1745
- },
1746
- ],
1747
- }),
1748
- );
1749
- clearCache();
1750
- const rules = getAllRules();
1751
- const found = rules.find((r) => r.id === "url-rule-with-et");
1752
- expect(found).toBeDefined();
1753
- // executionTarget should have been stripped by the canonical parser
1754
- expect(found).not.toHaveProperty("executionTarget");
1755
-
1756
- // Verify the re-save persisted the normalized rule
1757
- const disk = JSON.parse(readFileSync(trustPath, "utf-8"));
1758
- const diskRule = disk.rules.find(
1759
- (r: { id: string }) => r.id === "url-rule-with-et",
1760
- );
1761
- expect(diskRule).toBeDefined();
1762
- expect(diskRule).not.toHaveProperty("executionTarget");
1763
- });
1764
-
1765
- test("URL rule with allowHighRisk is stripped on load (normalized)", () => {
1766
- mkdirSync(dirname(trustPath), { recursive: true });
1767
- writeFileSync(
1768
- trustPath,
1769
- JSON.stringify({
1770
- version: 3,
1771
- rules: [
1772
- {
1773
- id: "url-rule-with-ahr",
1774
- tool: "web_fetch",
1775
- pattern: "**",
1776
- scope: "everywhere",
1777
- decision: "allow",
1778
- priority: 100,
1779
- createdAt: 2000,
1780
- allowHighRisk: true,
1781
- },
1782
- ],
1783
- }),
1784
- );
1785
- clearCache();
1786
- const rules = getAllRules();
1787
- const found = rules.find((r) => r.id === "url-rule-with-ahr");
1788
- expect(found).toBeDefined();
1789
- // allowHighRisk is stripped during normalization
1790
- expect(
1791
- (found as unknown as Record<string, unknown>).allowHighRisk,
1792
- ).toBeUndefined();
1793
- });
1794
-
1795
- test("scoped rule preserves executionTarget but strips allowHighRisk on load", () => {
1796
- mkdirSync(dirname(trustPath), { recursive: true });
1797
- writeFileSync(
1798
- trustPath,
1799
- JSON.stringify({
1800
- version: 3,
1801
- rules: [
1802
- {
1803
- id: "scoped-rule-with-opts",
1804
- tool: "bash",
1805
- pattern: "npm *",
1806
- scope: "/tmp",
1807
- decision: "allow",
1808
- priority: 100,
1809
- createdAt: 3000,
1810
- executionTarget: "/usr/local/bin/node",
1811
- allowHighRisk: true,
1812
- },
1813
- ],
1814
- }),
1815
- );
1816
- clearCache();
1817
- const rules = getAllRules();
1818
- const found = rules.find((r) => r.id === "scoped-rule-with-opts");
1819
- expect(found).toBeDefined();
1820
- expect((found as { executionTarget?: string }).executionTarget).toBe(
1821
- "/usr/local/bin/node",
1822
- );
1823
- // allowHighRisk is stripped during normalization
1824
- expect(
1825
- (found as unknown as Record<string, unknown>).allowHighRisk,
1826
- ).toBeUndefined();
1827
- });
1828
-
1829
- test("normalization on v2 file triggers re-save as v3", () => {
1830
- mkdirSync(dirname(trustPath), { recursive: true });
1831
- writeFileSync(
1832
- trustPath,
1833
- JSON.stringify({
1834
- version: 2,
1835
- rules: [
1836
- {
1837
- id: "v2-url-rule",
1838
- tool: "network_request",
1839
- pattern: "network_request:https://api.test.com/*",
1840
- scope: "everywhere",
1841
- decision: "allow",
1842
- priority: 100,
1843
- createdAt: 4000,
1844
- executionTarget: "stale-value",
1845
- },
1846
- ],
1847
- }),
1848
- );
1849
- clearCache();
1850
- getAllRules();
1851
-
1852
- // File should be re-saved as v3 with normalized rules
1853
- const disk = JSON.parse(readFileSync(trustPath, "utf-8"));
1854
- expect(disk.version).toBe(3);
1855
- const diskRule = disk.rules.find(
1856
- (r: { id: string }) => r.id === "v2-url-rule",
1857
- );
1858
- expect(diskRule).toBeDefined();
1859
- expect(diskRule).not.toHaveProperty("executionTarget");
1860
- });
1861
- });
1862
-
1863
- // ── optional-scope matching fallback ──────────────────────────────────────
1864
-
1865
- describe("optional-scope matching fallback", () => {
1866
- beforeEach(() => {
1867
- clearCache();
1868
- try {
1869
- rmSync(trustPath);
1870
- } catch {
1871
- /* may not exist */
1872
- }
1873
- });
1874
-
1875
- test("rule with missing scope is normalized to everywhere and matches any dir", () => {
1876
- // Simulate a persisted rule that somehow lacks a scope field —
1877
- // the canonical parser normalizes it to "everywhere".
1878
- mkdirSync(dirname(trustPath), { recursive: true });
1879
- writeFileSync(
1880
- trustPath,
1881
- JSON.stringify({
1882
- version: 3,
1883
- rules: [
1884
- {
1885
- id: "no-scope-rule",
1886
- tool: "bash",
1887
- pattern: "echo *",
1888
- decision: "allow",
1889
- priority: 200,
1890
- createdAt: 5000,
1891
- },
1892
- ],
1893
- }),
1894
- );
1895
- clearCache();
1896
- const rules = getAllRules();
1897
- const found = rules.find((r) => r.id === "no-scope-rule");
1898
- expect(found).toBeDefined();
1899
- expect(found!.scope).toBe("everywhere");
1900
-
1901
- // Should match any working directory since scope defaults to everywhere
1902
- const match = findHighestPriorityRule(
1903
- "bash",
1904
- ["echo hello"],
1905
- "/any/random/dir",
1906
- );
1907
- expect(match).not.toBeNull();
1908
- expect(match!.id).toBe("no-scope-rule");
1909
- });
1910
-
1911
- test("rule with empty-string scope is normalized to everywhere", () => {
1912
- mkdirSync(dirname(trustPath), { recursive: true });
1913
- writeFileSync(
1914
- trustPath,
1915
- JSON.stringify({
1916
- version: 3,
1917
- rules: [
1918
- {
1919
- id: "empty-scope-rule",
1920
- tool: "bash",
1921
- pattern: "ls *",
1922
- scope: "",
1923
- decision: "allow",
1924
- priority: 200,
1925
- createdAt: 6000,
1926
- },
1927
- ],
1928
- }),
1929
- );
1930
- clearCache();
1931
- const rules = getAllRules();
1932
- const found = rules.find((r) => r.id === "empty-scope-rule");
1933
- expect(found).toBeDefined();
1934
- // The ruleScope helper treats "" as "everywhere"
1935
- const match = findHighestPriorityRule(
1936
- "bash",
1937
- ["ls -la"],
1938
- "/some/other/dir",
1939
- );
1940
- expect(match).not.toBeNull();
1941
- expect(match!.id).toBe("empty-scope-rule");
1942
- });
1943
- });
1944
-
1945
- // ── unknown-version no-overwrite semantics ────────────────────────────────
1946
-
1947
- describe("unknown-version no-overwrite semantics", () => {
1948
- beforeEach(() => {
1949
- clearCache();
1950
- try {
1951
- rmSync(trustPath);
1952
- } catch {
1953
- /* may not exist */
1954
- }
1955
- });
1956
-
1957
- test("unknown version file is never overwritten even if normalization would apply", () => {
1958
- mkdirSync(dirname(trustPath), { recursive: true });
1959
- // A future version file with rules that would normally be normalized —
1960
- // the unknown-version guard must prevent any disk writes.
1961
- const originalContent = JSON.stringify({
1962
- version: 9999,
1963
- rules: [
1964
- {
1965
- id: "future-url-rule",
1966
- tool: "web_fetch",
1967
- pattern: "web_fetch:https://future.io/*",
1968
- scope: "everywhere",
1969
- decision: "allow",
1970
- priority: 50,
1971
- createdAt: 7000,
1972
- executionTarget: "should-be-stripped-but-file-not-overwritten",
1973
- },
1974
- ],
1975
- });
1976
- writeFileSync(trustPath, originalContent);
1977
- clearCache();
1978
- const rules = getAllRules();
1979
- // Defaults should be present in-memory
1980
- const defaults = rules.filter((r) => r.id.startsWith("default:"));
1981
- expect(defaults).toHaveLength(NUM_DEFAULTS);
1982
- // The on-disk file must NOT be overwritten
1983
- const diskContent = readFileSync(trustPath, "utf-8");
1984
- expect(diskContent).toBe(originalContent);
1985
- });
1986
-
1987
- test("unknown version returns only in-memory defaults, not file rules", () => {
1988
- mkdirSync(dirname(trustPath), { recursive: true });
1989
- writeFileSync(
1990
- trustPath,
1991
- JSON.stringify({
1992
- version: 42,
1993
- rules: [
1994
- {
1995
- id: "v42-rule",
1996
- tool: "bash",
1997
- pattern: "dangerous *",
1998
- scope: "everywhere",
1999
- decision: "allow",
2000
- priority: 500,
2001
- createdAt: 8000,
2002
- },
2003
- ],
2004
- }),
2005
- );
2006
- clearCache();
2007
- const rules = getAllRules();
2008
- // The file's rules should NOT appear in the loaded set
2009
- expect(rules.find((r) => r.id === "v42-rule")).toBeUndefined();
2010
- // Only defaults should be present
2011
- expect(rules.every((r) => r.id.startsWith("default:"))).toBe(true);
2012
- });
2013
- });