vellum 0.2.14 → 0.3.2

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 (1060) hide show
  1. package/bin/vellum.js +2 -0
  2. package/package.json +6 -65
  3. package/.dockerignore +0 -27
  4. package/.env.example +0 -22
  5. package/Dockerfile +0 -99
  6. package/Dockerfile.sandbox +0 -5
  7. package/README.md +0 -201
  8. package/bun.lock +0 -1743
  9. package/bunfig.toml +0 -2
  10. package/docs/skills.md +0 -158
  11. package/drizzle/0000_dizzy_maggott.sql +0 -301
  12. package/drizzle/meta/0000_snapshot.json +0 -1999
  13. package/drizzle/meta/_journal.json +0 -13
  14. package/drizzle.config.ts +0 -7
  15. package/eslint.config.mjs +0 -17
  16. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  17. package/hook-templates/debug-prompt-logger/run.sh +0 -68
  18. package/knip.json +0 -9
  19. package/scripts/capture-x-graphql.ts +0 -545
  20. package/scripts/ipc/check-contract-inventory.ts +0 -104
  21. package/scripts/ipc/check-swift-decoder-drift.ts +0 -164
  22. package/scripts/ipc/generate-swift.ts +0 -492
  23. package/scripts/test-filesystem-tools.sh +0 -48
  24. package/scripts/test.sh +0 -127
  25. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -2526
  26. package/src/__tests__/account-registry.test.ts +0 -245
  27. package/src/__tests__/active-skill-tools.test.ts +0 -378
  28. package/src/__tests__/agent-heartbeat-service.test.ts +0 -250
  29. package/src/__tests__/agent-loop-thinking.test.ts +0 -81
  30. package/src/__tests__/agent-loop.test.ts +0 -1135
  31. package/src/__tests__/anthropic-provider.test.ts +0 -778
  32. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -290
  33. package/src/__tests__/app-bundler.test.ts +0 -292
  34. package/src/__tests__/app-executors.test.ts +0 -613
  35. package/src/__tests__/app-git-history.test.ts +0 -176
  36. package/src/__tests__/app-git-service.test.ts +0 -169
  37. package/src/__tests__/app-open-proxy.test.ts +0 -62
  38. package/src/__tests__/asset-materialize-tool.test.ts +0 -452
  39. package/src/__tests__/asset-search-tool.test.ts +0 -477
  40. package/src/__tests__/assistant-attachment-directive.test.ts +0 -401
  41. package/src/__tests__/assistant-attachments.test.ts +0 -437
  42. package/src/__tests__/assistant-event-hub.test.ts +0 -226
  43. package/src/__tests__/assistant-event.test.ts +0 -123
  44. package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -315
  45. package/src/__tests__/attachments-store.test.ts +0 -476
  46. package/src/__tests__/attachments.test.ts +0 -134
  47. package/src/__tests__/audit-log-rotation.test.ts +0 -154
  48. package/src/__tests__/browser-fill-credential.test.ts +0 -309
  49. package/src/__tests__/browser-manager.test.ts +0 -203
  50. package/src/__tests__/browser-runtime-check.test.ts +0 -55
  51. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +0 -68
  52. package/src/__tests__/browser-skill-endstate.test.ts +0 -195
  53. package/src/__tests__/bundle-scanner.test.ts +0 -313
  54. package/src/__tests__/call-bridge.test.ts +0 -517
  55. package/src/__tests__/call-constants.test.ts +0 -40
  56. package/src/__tests__/call-domain.test.ts +0 -163
  57. package/src/__tests__/call-orchestrator.test.ts +0 -625
  58. package/src/__tests__/call-recovery.test.ts +0 -518
  59. package/src/__tests__/call-routes-http.test.ts +0 -699
  60. package/src/__tests__/call-state-machine.test.ts +0 -143
  61. package/src/__tests__/call-state.test.ts +0 -174
  62. package/src/__tests__/call-store.test.ts +0 -691
  63. package/src/__tests__/channel-approval-routes.test.ts +0 -438
  64. package/src/__tests__/channel-approval.test.ts +0 -266
  65. package/src/__tests__/channel-approvals.test.ts +0 -393
  66. package/src/__tests__/channel-delivery-store.test.ts +0 -447
  67. package/src/__tests__/checker.test.ts +0 -3519
  68. package/src/__tests__/clarification-resolver.test.ts +0 -159
  69. package/src/__tests__/classifier.test.ts +0 -67
  70. package/src/__tests__/claude-code-skill-regression.test.ts +0 -127
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +0 -88
  72. package/src/__tests__/cli-discover.test.ts +0 -85
  73. package/src/__tests__/cli.test.ts +0 -26
  74. package/src/__tests__/clipboard.test.ts +0 -80
  75. package/src/__tests__/commit-guarantee.test.ts +0 -335
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +0 -550
  77. package/src/__tests__/compaction.benchmark.test.ts +0 -176
  78. package/src/__tests__/computer-use-session-compaction.test.ts +0 -132
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +0 -293
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +0 -117
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +0 -74
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +0 -89
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +0 -217
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +0 -107
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +0 -54
  86. package/src/__tests__/computer-use-tools.test.ts +0 -250
  87. package/src/__tests__/config-schema.test.ts +0 -1462
  88. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -141
  89. package/src/__tests__/conflict-policy.test.ts +0 -121
  90. package/src/__tests__/conflict-store.test.ts +0 -332
  91. package/src/__tests__/connection-policy.test.ts +0 -102
  92. package/src/__tests__/contacts-tools.test.ts +0 -331
  93. package/src/__tests__/context-memory-e2e.test.ts +0 -434
  94. package/src/__tests__/context-token-estimator.test.ts +0 -135
  95. package/src/__tests__/context-window-manager.test.ts +0 -376
  96. package/src/__tests__/contradiction-checker.test.ts +0 -314
  97. package/src/__tests__/conversation-store.test.ts +0 -612
  98. package/src/__tests__/credential-broker-browser-fill.test.ts +0 -517
  99. package/src/__tests__/credential-broker-server-use.test.ts +0 -554
  100. package/src/__tests__/credential-broker.test.ts +0 -167
  101. package/src/__tests__/credential-host-pattern-match.test.ts +0 -104
  102. package/src/__tests__/credential-metadata-store.test.ts +0 -779
  103. package/src/__tests__/credential-policy-validate.test.ts +0 -121
  104. package/src/__tests__/credential-resolve.test.ts +0 -328
  105. package/src/__tests__/credential-security-e2e.test.ts +0 -352
  106. package/src/__tests__/credential-security-invariants.test.ts +0 -583
  107. package/src/__tests__/credential-selection.test.ts +0 -354
  108. package/src/__tests__/credential-vault-unit.test.ts +0 -780
  109. package/src/__tests__/credential-vault.test.ts +0 -852
  110. package/src/__tests__/daemon-assistant-events.test.ts +0 -164
  111. package/src/__tests__/daemon-server-session-init.test.ts +0 -522
  112. package/src/__tests__/date-context.test.ts +0 -373
  113. package/src/__tests__/db-schedule-syntax-migration.test.ts +0 -129
  114. package/src/__tests__/delete-managed-skill-tool.test.ts +0 -97
  115. package/src/__tests__/diff.test.ts +0 -121
  116. package/src/__tests__/domain-normalize.test.ts +0 -112
  117. package/src/__tests__/domain-policy.test.ts +0 -124
  118. package/src/__tests__/doordash-client.test.ts +0 -186
  119. package/src/__tests__/doordash-session.test.ts +0 -152
  120. package/src/__tests__/dynamic-page-surface.test.ts +0 -91
  121. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -132
  122. package/src/__tests__/edit-engine.test.ts +0 -180
  123. package/src/__tests__/elevenlabs-client.test.ts +0 -271
  124. package/src/__tests__/email-cli.test.ts +0 -283
  125. package/src/__tests__/encrypted-store.test.ts +0 -332
  126. package/src/__tests__/entity-extractor.test.ts +0 -190
  127. package/src/__tests__/ephemeral-permissions.test.ts +0 -362
  128. package/src/__tests__/evaluate-typescript-tool.test.ts +0 -286
  129. package/src/__tests__/event-bus.test.ts +0 -222
  130. package/src/__tests__/file-edit-tool.test.ts +0 -122
  131. package/src/__tests__/file-ops-service.test.ts +0 -330
  132. package/src/__tests__/file-read-tool.test.ts +0 -75
  133. package/src/__tests__/file-write-tool.test.ts +0 -113
  134. package/src/__tests__/filesystem-tools.test.ts +0 -579
  135. package/src/__tests__/fixtures/credential-security-fixtures.ts +0 -181
  136. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -126
  137. package/src/__tests__/fixtures/mock-signup-server.ts +0 -387
  138. package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
  139. package/src/__tests__/followup-tools.test.ts +0 -303
  140. package/src/__tests__/forbidden-legacy-symbols.test.ts +0 -71
  141. package/src/__tests__/fuzzy-match-property.test.ts +0 -216
  142. package/src/__tests__/fuzzy-match.test.ts +0 -138
  143. package/src/__tests__/gateway-only-enforcement.test.ts +0 -546
  144. package/src/__tests__/gemini-image-service.test.ts +0 -261
  145. package/src/__tests__/gemini-provider.test.ts +0 -651
  146. package/src/__tests__/get-weather.test.ts +0 -318
  147. package/src/__tests__/gmail-integration.test.ts +0 -73
  148. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +0 -202
  149. package/src/__tests__/handlers-cu-observation-blob.test.ts +0 -352
  150. package/src/__tests__/handlers-ipc-blob-probe.test.ts +0 -191
  151. package/src/__tests__/handlers-slack-config.test.ts +0 -200
  152. package/src/__tests__/handlers-task-submit-slash.test.ts +0 -38
  153. package/src/__tests__/handlers-telegram-config.test.ts +0 -855
  154. package/src/__tests__/handlers-twitter-config.test.ts +0 -858
  155. package/src/__tests__/headless-browser-interactions.test.ts +0 -536
  156. package/src/__tests__/headless-browser-navigate.test.ts +0 -211
  157. package/src/__tests__/headless-browser-read-tools.test.ts +0 -261
  158. package/src/__tests__/headless-browser-snapshot.test.ts +0 -185
  159. package/src/__tests__/history-repair-observability.test.ts +0 -56
  160. package/src/__tests__/history-repair.test.ts +0 -510
  161. package/src/__tests__/home-base-bootstrap.test.ts +0 -82
  162. package/src/__tests__/hooks-blocking.test.ts +0 -128
  163. package/src/__tests__/hooks-cli.test.ts +0 -144
  164. package/src/__tests__/hooks-config.test.ts +0 -93
  165. package/src/__tests__/hooks-discovery.test.ts +0 -199
  166. package/src/__tests__/hooks-integration.test.ts +0 -189
  167. package/src/__tests__/hooks-manager.test.ts +0 -187
  168. package/src/__tests__/hooks-runner.test.ts +0 -182
  169. package/src/__tests__/hooks-settings.test.ts +0 -154
  170. package/src/__tests__/hooks-templates.test.ts +0 -137
  171. package/src/__tests__/hooks-ts-runner.test.ts +0 -125
  172. package/src/__tests__/hooks-watch.test.ts +0 -100
  173. package/src/__tests__/host-file-edit-tool.test.ts +0 -228
  174. package/src/__tests__/host-file-read-tool.test.ts +0 -123
  175. package/src/__tests__/host-file-write-tool.test.ts +0 -136
  176. package/src/__tests__/host-shell-tool.test.ts +0 -562
  177. package/src/__tests__/ingress-reconcile.test.ts +0 -581
  178. package/src/__tests__/ingress-url-consistency.test.ts +0 -214
  179. package/src/__tests__/intent-routing.test.ts +0 -259
  180. package/src/__tests__/ipc-blob-store.test.ts +0 -315
  181. package/src/__tests__/ipc-contract-inventory.test.ts +0 -54
  182. package/src/__tests__/ipc-contract.test.ts +0 -74
  183. package/src/__tests__/ipc-protocol.test.ts +0 -113
  184. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +0 -237
  185. package/src/__tests__/ipc-snapshot.test.ts +0 -1757
  186. package/src/__tests__/ipc-validate.test.ts +0 -407
  187. package/src/__tests__/key-migration.test.ts +0 -206
  188. package/src/__tests__/keychain.test.ts +0 -258
  189. package/src/__tests__/llm-usage-store.test.ts +0 -221
  190. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -257
  191. package/src/__tests__/managed-store.test.ts +0 -608
  192. package/src/__tests__/media-generate-image.test.ts +0 -238
  193. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -676
  194. package/src/__tests__/media-visibility-policy.test.ts +0 -141
  195. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -235
  196. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -481
  197. package/src/__tests__/memory-query-builder.test.ts +0 -59
  198. package/src/__tests__/memory-recall-quality.test.ts +0 -846
  199. package/src/__tests__/memory-regressions.experimental.test.ts +0 -538
  200. package/src/__tests__/memory-regressions.test.ts +0 -4435
  201. package/src/__tests__/memory-retrieval-budget.test.ts +0 -49
  202. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -430
  203. package/src/__tests__/migration-cli-flows.test.ts +0 -169
  204. package/src/__tests__/migration-ordering.test.ts +0 -249
  205. package/src/__tests__/mock-signup-server.test.ts +0 -528
  206. package/src/__tests__/oauth-callback-registry.test.ts +0 -92
  207. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -285
  208. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -176
  209. package/src/__tests__/onboarding-template-contract.test.ts +0 -58
  210. package/src/__tests__/openai-provider.test.ts +0 -753
  211. package/src/__tests__/parallel-tool.benchmark.test.ts +0 -294
  212. package/src/__tests__/parser.test.ts +0 -472
  213. package/src/__tests__/path-classifier.test.ts +0 -73
  214. package/src/__tests__/path-policy.test.ts +0 -435
  215. package/src/__tests__/platform-move-helper.test.ts +0 -99
  216. package/src/__tests__/platform-socket-path.test.ts +0 -52
  217. package/src/__tests__/platform-workspace-migration.test.ts +0 -1000
  218. package/src/__tests__/platform.test.ts +0 -131
  219. package/src/__tests__/playbook-execution.test.ts +0 -502
  220. package/src/__tests__/playbook-tools.test.ts +0 -340
  221. package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -75
  222. package/src/__tests__/pricing.test.ts +0 -256
  223. package/src/__tests__/profile-compiler.test.ts +0 -374
  224. package/src/__tests__/provider-commit-message-generator.test.ts +0 -342
  225. package/src/__tests__/provider-registry-ollama.test.ts +0 -16
  226. package/src/__tests__/provider-streaming.benchmark.test.ts +0 -773
  227. package/src/__tests__/proxy-approval-callback.test.ts +0 -601
  228. package/src/__tests__/public-ingress-urls.test.ts +0 -256
  229. package/src/__tests__/qdrant-manager.test.ts +0 -267
  230. package/src/__tests__/ratelimit.test.ts +0 -297
  231. package/src/__tests__/recurrence-engine-rruleset.test.ts +0 -175
  232. package/src/__tests__/recurrence-engine.test.ts +0 -78
  233. package/src/__tests__/recurrence-types.test.ts +0 -79
  234. package/src/__tests__/registry.test.ts +0 -494
  235. package/src/__tests__/relay-server.test.ts +0 -688
  236. package/src/__tests__/reminder-store.test.ts +0 -223
  237. package/src/__tests__/reminder.test.ts +0 -229
  238. package/src/__tests__/request-file-tool.test.ts +0 -158
  239. package/src/__tests__/run-orchestrator-assistant-events.test.ts +0 -222
  240. package/src/__tests__/run-orchestrator.test.ts +0 -200
  241. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -189
  242. package/src/__tests__/runtime-events-sse-parity.test.ts +0 -343
  243. package/src/__tests__/runtime-events-sse.test.ts +0 -162
  244. package/src/__tests__/runtime-runs-http.test.ts +0 -433
  245. package/src/__tests__/runtime-runs.test.ts +0 -249
  246. package/src/__tests__/sandbox-diagnostics.test.ts +0 -408
  247. package/src/__tests__/sandbox-host-parity.test.ts +0 -950
  248. package/src/__tests__/scaffold-managed-skill-tool.test.ts +0 -253
  249. package/src/__tests__/schedule-store.test.ts +0 -484
  250. package/src/__tests__/schedule-tools.test.ts +0 -783
  251. package/src/__tests__/scheduler-recurrence.test.ts +0 -430
  252. package/src/__tests__/script-proxy-certs.test.ts +0 -90
  253. package/src/__tests__/script-proxy-connect-tunnel.test.ts +0 -177
  254. package/src/__tests__/script-proxy-decision-trace.test.ts +0 -156
  255. package/src/__tests__/script-proxy-http-forwarder.test.ts +0 -281
  256. package/src/__tests__/script-proxy-injection-runtime.test.ts +0 -401
  257. package/src/__tests__/script-proxy-mitm-handler.test.ts +0 -407
  258. package/src/__tests__/script-proxy-policy-runtime.test.ts +0 -287
  259. package/src/__tests__/script-proxy-policy.test.ts +0 -310
  260. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +0 -135
  261. package/src/__tests__/script-proxy-router.test.ts +0 -180
  262. package/src/__tests__/script-proxy-session-manager.test.ts +0 -382
  263. package/src/__tests__/script-proxy-session-runtime.test.ts +0 -113
  264. package/src/__tests__/secret-allowlist.test.ts +0 -230
  265. package/src/__tests__/secret-ingress-handler.test.ts +0 -110
  266. package/src/__tests__/secret-onetime-send.test.ts +0 -130
  267. package/src/__tests__/secret-prompt-log-hygiene.test.ts +0 -106
  268. package/src/__tests__/secret-response-routing.test.ts +0 -93
  269. package/src/__tests__/secret-scanner-executor.test.ts +0 -348
  270. package/src/__tests__/secret-scanner.test.ts +0 -900
  271. package/src/__tests__/secure-keys.test.ts +0 -323
  272. package/src/__tests__/server-history-render.test.ts +0 -431
  273. package/src/__tests__/session-abort-tool-results.test.ts +0 -240
  274. package/src/__tests__/session-conflict-gate.test.ts +0 -1136
  275. package/src/__tests__/session-error.test.ts +0 -369
  276. package/src/__tests__/session-evictor.test.ts +0 -188
  277. package/src/__tests__/session-init.benchmark.test.ts +0 -465
  278. package/src/__tests__/session-load-history-repair.test.ts +0 -222
  279. package/src/__tests__/session-pre-run-repair.test.ts +0 -213
  280. package/src/__tests__/session-process-bridge.test.ts +0 -242
  281. package/src/__tests__/session-profile-injection.test.ts +0 -444
  282. package/src/__tests__/session-provider-retry-repair.test.ts +0 -306
  283. package/src/__tests__/session-queue.test.ts +0 -1535
  284. package/src/__tests__/session-runtime-assembly.test.ts +0 -476
  285. package/src/__tests__/session-runtime-workspace.test.ts +0 -183
  286. package/src/__tests__/session-skill-tools.test.ts +0 -2431
  287. package/src/__tests__/session-slash-known.test.ts +0 -368
  288. package/src/__tests__/session-slash-queue.test.ts +0 -288
  289. package/src/__tests__/session-slash-unknown.test.ts +0 -271
  290. package/src/__tests__/session-surfaces-task-progress.test.ts +0 -104
  291. package/src/__tests__/session-tool-setup-app-refresh.test.ts +0 -473
  292. package/src/__tests__/session-tool-setup-memory-scope.test.ts +0 -140
  293. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +0 -140
  294. package/src/__tests__/session-undo.test.ts +0 -75
  295. package/src/__tests__/session-workspace-cache-state.test.ts +0 -246
  296. package/src/__tests__/session-workspace-injection.test.ts +0 -327
  297. package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -240
  298. package/src/__tests__/shared-filesystem-errors.test.ts +0 -78
  299. package/src/__tests__/shell-credential-ref.test.ts +0 -187
  300. package/src/__tests__/shell-identity.test.ts +0 -256
  301. package/src/__tests__/shell-parser-fuzz.test.ts +0 -544
  302. package/src/__tests__/shell-parser-property.test.ts +0 -433
  303. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -272
  304. package/src/__tests__/signup-e2e.test.ts +0 -353
  305. package/src/__tests__/size-guard.test.ts +0 -117
  306. package/src/__tests__/skill-include-graph.test.ts +0 -303
  307. package/src/__tests__/skill-load-tool.test.ts +0 -409
  308. package/src/__tests__/skill-projection.benchmark.test.ts +0 -338
  309. package/src/__tests__/skill-script-runner-host.test.ts +0 -489
  310. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -349
  311. package/src/__tests__/skill-script-runner.test.ts +0 -159
  312. package/src/__tests__/skill-tool-factory.test.ts +0 -252
  313. package/src/__tests__/skill-tool-manifest.test.ts +0 -658
  314. package/src/__tests__/skill-version-hash.test.ts +0 -182
  315. package/src/__tests__/skills.test.ts +0 -680
  316. package/src/__tests__/slash-commands-catalog.test.ts +0 -86
  317. package/src/__tests__/slash-commands-parser.test.ts +0 -119
  318. package/src/__tests__/slash-commands-resolver.test.ts +0 -193
  319. package/src/__tests__/slash-commands-rewrite.test.ts +0 -39
  320. package/src/__tests__/speaker-identification.test.ts +0 -52
  321. package/src/__tests__/starter-bundle.test.ts +0 -136
  322. package/src/__tests__/starter-task-flow.test.ts +0 -143
  323. package/src/__tests__/subagent-manager-notify.test.ts +0 -404
  324. package/src/__tests__/subagent-tools.test.ts +0 -801
  325. package/src/__tests__/subagent-types.test.ts +0 -78
  326. package/src/__tests__/swarm-orchestrator.test.ts +0 -428
  327. package/src/__tests__/swarm-plan-validator.test.ts +0 -330
  328. package/src/__tests__/swarm-recursion.test.ts +0 -165
  329. package/src/__tests__/swarm-router-planner.test.ts +0 -208
  330. package/src/__tests__/swarm-session-integration.test.ts +0 -274
  331. package/src/__tests__/swarm-tool.test.ts +0 -145
  332. package/src/__tests__/swarm-worker-backend.test.ts +0 -129
  333. package/src/__tests__/swarm-worker-runner.test.ts +0 -272
  334. package/src/__tests__/system-prompt.test.ts +0 -439
  335. package/src/__tests__/task-compiler.test.ts +0 -284
  336. package/src/__tests__/task-management-tools.test.ts +0 -936
  337. package/src/__tests__/task-runner.test.ts +0 -216
  338. package/src/__tests__/task-scheduler.test.ts +0 -217
  339. package/src/__tests__/task-tools.test.ts +0 -595
  340. package/src/__tests__/terminal-sandbox-docker.test.ts +0 -1064
  341. package/src/__tests__/terminal-sandbox.integration.test.ts +0 -178
  342. package/src/__tests__/terminal-sandbox.test.ts +0 -202
  343. package/src/__tests__/terminal-tools.test.ts +0 -840
  344. package/src/__tests__/test-support/browser-skill-harness.ts +0 -90
  345. package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -45
  346. package/src/__tests__/tool-audit-listener.test.ts +0 -113
  347. package/src/__tests__/tool-domain-event-publisher.test.ts +0 -253
  348. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -500
  349. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -516
  350. package/src/__tests__/tool-executor-redaction.test.ts +0 -289
  351. package/src/__tests__/tool-executor-shell-integration.test.ts +0 -301
  352. package/src/__tests__/tool-executor.test.ts +0 -1989
  353. package/src/__tests__/tool-metrics-listener.test.ts +0 -225
  354. package/src/__tests__/tool-notification-listener.test.ts +0 -49
  355. package/src/__tests__/tool-permission-simulate-handler.test.ts +0 -336
  356. package/src/__tests__/tool-policy.test.ts +0 -54
  357. package/src/__tests__/tool-profiling-listener.test.ts +0 -268
  358. package/src/__tests__/tool-result-truncation.test.ts +0 -217
  359. package/src/__tests__/tool-trace-listener.test.ts +0 -226
  360. package/src/__tests__/top-level-renderer.test.ts +0 -121
  361. package/src/__tests__/top-level-scanner.test.ts +0 -141
  362. package/src/__tests__/trace-emitter.test.ts +0 -173
  363. package/src/__tests__/trust-store.test.ts +0 -1605
  364. package/src/__tests__/turn-commit.test.ts +0 -554
  365. package/src/__tests__/twilio-provider.test.ts +0 -329
  366. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -375
  367. package/src/__tests__/twilio-routes-twiml.test.ts +0 -127
  368. package/src/__tests__/twilio-routes.test.ts +0 -577
  369. package/src/__tests__/twitter-auth-handler.test.ts +0 -667
  370. package/src/__tests__/twitter-cli-error-shaping.test.ts +0 -208
  371. package/src/__tests__/twitter-cli-routing.test.ts +0 -252
  372. package/src/__tests__/twitter-oauth-client.test.ts +0 -209
  373. package/src/__tests__/url-safety.test.ts +0 -418
  374. package/src/__tests__/view-image-tool.test.ts +0 -217
  375. package/src/__tests__/weather-skill-regression.test.ts +0 -225
  376. package/src/__tests__/web-fetch.test.ts +0 -869
  377. package/src/__tests__/web-search.test.ts +0 -584
  378. package/src/__tests__/workspace-git-service.test.ts +0 -1153
  379. package/src/__tests__/workspace-heartbeat-service.test.ts +0 -486
  380. package/src/__tests__/workspace-lifecycle.test.ts +0 -292
  381. package/src/__tests__/workspace-policy.test.ts +0 -213
  382. package/src/agent/attachments.ts +0 -35
  383. package/src/agent/loop.ts +0 -500
  384. package/src/agent/message-types.ts +0 -17
  385. package/src/agent-heartbeat/agent-heartbeat-service.ts +0 -155
  386. package/src/autonomy/autonomy-resolver.ts +0 -60
  387. package/src/autonomy/autonomy-store.ts +0 -122
  388. package/src/autonomy/disposition-mapper.ts +0 -31
  389. package/src/autonomy/index.ts +0 -11
  390. package/src/autonomy/types.ts +0 -39
  391. package/src/bundler/app-bundler.ts +0 -295
  392. package/src/bundler/bundle-scanner.ts +0 -535
  393. package/src/bundler/bundle-signer.ts +0 -124
  394. package/src/bundler/manifest.ts +0 -21
  395. package/src/bundler/signature-verifier.ts +0 -184
  396. package/src/calls/call-bridge.ts +0 -168
  397. package/src/calls/call-constants.ts +0 -48
  398. package/src/calls/call-domain.ts +0 -430
  399. package/src/calls/call-orchestrator.ts +0 -498
  400. package/src/calls/call-recovery.ts +0 -207
  401. package/src/calls/call-state-machine.ts +0 -68
  402. package/src/calls/call-state.ts +0 -87
  403. package/src/calls/call-store.ts +0 -422
  404. package/src/calls/elevenlabs-client.ts +0 -97
  405. package/src/calls/elevenlabs-config.ts +0 -31
  406. package/src/calls/relay-server.ts +0 -390
  407. package/src/calls/speaker-identification.ts +0 -213
  408. package/src/calls/twilio-config.ts +0 -45
  409. package/src/calls/twilio-provider.ts +0 -269
  410. package/src/calls/twilio-routes.ts +0 -311
  411. package/src/calls/types.ts +0 -39
  412. package/src/calls/voice-provider.ts +0 -14
  413. package/src/calls/voice-quality.ts +0 -114
  414. package/src/cli/autonomy.ts +0 -188
  415. package/src/cli/config-commands.ts +0 -334
  416. package/src/cli/contacts.ts +0 -149
  417. package/src/cli/core-commands.ts +0 -784
  418. package/src/cli/doordash.ts +0 -1055
  419. package/src/cli/email-guardrails.ts +0 -200
  420. package/src/cli/email.ts +0 -405
  421. package/src/cli/ipc-client.ts +0 -82
  422. package/src/cli/main-screen.tsx +0 -53
  423. package/src/cli/map.ts +0 -270
  424. package/src/cli/twitter.ts +0 -754
  425. package/src/cli.ts +0 -918
  426. package/src/commands/__tests__/cc-command-registry.test.ts +0 -319
  427. package/src/commands/cc-command-registry.ts +0 -209
  428. package/src/config/bundled-skills/.gitkeep +0 -0
  429. package/src/config/bundled-skills/agentmail/SKILL.md +0 -128
  430. package/src/config/bundled-skills/agentmail/icon.svg +0 -21
  431. package/src/config/bundled-skills/app-builder/SKILL.md +0 -1404
  432. package/src/config/bundled-skills/app-builder/TOOLS.json +0 -279
  433. package/src/config/bundled-skills/app-builder/icon.svg +0 -9
  434. package/src/config/bundled-skills/app-builder/tools/app-create.ts +0 -15
  435. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +0 -10
  436. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -11
  437. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -10
  438. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -18
  439. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -11
  440. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -10
  441. package/src/config/bundled-skills/app-builder/tools/app-query.ts +0 -10
  442. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -20
  443. package/src/config/bundled-skills/browser/SKILL.md +0 -28
  444. package/src/config/bundled-skills/browser/TOOLS.json +0 -234
  445. package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -9
  446. package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -9
  447. package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -9
  448. package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -9
  449. package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -9
  450. package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -9
  451. package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -9
  452. package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -9
  453. package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -9
  454. package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -9
  455. package/src/config/bundled-skills/claude-code/SKILL.md +0 -50
  456. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -40
  457. package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -9
  458. package/src/config/bundled-skills/computer-use/SKILL.md +0 -17
  459. package/src/config/bundled-skills/computer-use/TOOLS.json +0 -326
  460. package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +0 -9
  461. package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +0 -9
  462. package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +0 -9
  463. package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +0 -9
  464. package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +0 -9
  465. package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +0 -9
  466. package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +0 -9
  467. package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +0 -9
  468. package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +0 -9
  469. package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +0 -9
  470. package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +0 -9
  471. package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +0 -9
  472. package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +0 -9
  473. package/src/config/bundled-skills/contacts/SKILL.md +0 -39
  474. package/src/config/bundled-skills/contacts/TOOLS.json +0 -122
  475. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +0 -57
  476. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -60
  477. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -66
  478. package/src/config/bundled-skills/document/SKILL.md +0 -26
  479. package/src/config/bundled-skills/document/TOOLS.json +0 -53
  480. package/src/config/bundled-skills/document/tools/document-create.ts +0 -9
  481. package/src/config/bundled-skills/document/tools/document-update.ts +0 -9
  482. package/src/config/bundled-skills/doordash/SKILL.md +0 -163
  483. package/src/config/bundled-skills/followups/SKILL.md +0 -32
  484. package/src/config/bundled-skills/followups/TOOLS.json +0 -100
  485. package/src/config/bundled-skills/followups/icon.svg +0 -24
  486. package/src/config/bundled-skills/followups/tools/followup-create.ts +0 -9
  487. package/src/config/bundled-skills/followups/tools/followup-list.ts +0 -9
  488. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +0 -9
  489. package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
  490. package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -108
  491. package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -165
  492. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -21
  493. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -42
  494. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -13
  495. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -30
  496. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -41
  497. package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -18
  498. package/src/config/bundled-skills/google-calendar/types.ts +0 -97
  499. package/src/config/bundled-skills/image-studio/SKILL.md +0 -32
  500. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -42
  501. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +0 -115
  502. package/src/config/bundled-skills/macos-automation/SKILL.md +0 -66
  503. package/src/config/bundled-skills/messaging/SKILL.md +0 -145
  504. package/src/config/bundled-skills/messaging/TOOLS.json +0 -357
  505. package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +0 -23
  506. package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +0 -23
  507. package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +0 -25
  508. package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +0 -26
  509. package/src/config/bundled-skills/messaging/tools/gmail-label.ts +0 -25
  510. package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +0 -23
  511. package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +0 -84
  512. package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +0 -18
  513. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +0 -125
  514. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +0 -16
  515. package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +0 -49
  516. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +0 -21
  517. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +0 -25
  518. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +0 -28
  519. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +0 -32
  520. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +0 -22
  521. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -31
  522. package/src/config/bundled-skills/messaging/tools/shared.ts +0 -76
  523. package/src/config/bundled-skills/messaging/tools/slack-add-reaction.ts +0 -25
  524. package/src/config/bundled-skills/messaging/tools/slack-leave-channel.ts +0 -23
  525. package/src/config/bundled-skills/phone-calls/SKILL.md +0 -522
  526. package/src/config/bundled-skills/playbooks/SKILL.md +0 -31
  527. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -126
  528. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +0 -98
  529. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +0 -54
  530. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +0 -76
  531. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +0 -113
  532. package/src/config/bundled-skills/public-ingress/SKILL.md +0 -200
  533. package/src/config/bundled-skills/reminder/SKILL.md +0 -20
  534. package/src/config/bundled-skills/reminder/TOOLS.json +0 -67
  535. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +0 -9
  536. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +0 -9
  537. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +0 -9
  538. package/src/config/bundled-skills/schedule/SKILL.md +0 -74
  539. package/src/config/bundled-skills/schedule/TOOLS.json +0 -135
  540. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +0 -9
  541. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +0 -9
  542. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +0 -9
  543. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +0 -9
  544. package/src/config/bundled-skills/self-upgrade/SKILL.md +0 -68
  545. package/src/config/bundled-skills/start-the-day/SKILL.md +0 -70
  546. package/src/config/bundled-skills/start-the-day/icon.svg +0 -13
  547. package/src/config/bundled-skills/subagent/SKILL.md +0 -25
  548. package/src/config/bundled-skills/subagent/TOOLS.json +0 -107
  549. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +0 -9
  550. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +0 -9
  551. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +0 -9
  552. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +0 -9
  553. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +0 -9
  554. package/src/config/bundled-skills/tasks/SKILL.md +0 -28
  555. package/src/config/bundled-skills/tasks/TOOLS.json +0 -281
  556. package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -9
  557. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -9
  558. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -9
  559. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -9
  560. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -9
  561. package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -9
  562. package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -9
  563. package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -9
  564. package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -9
  565. package/src/config/bundled-skills/transcribe/SKILL.md +0 -25
  566. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -32
  567. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +0 -370
  568. package/src/config/bundled-skills/twitter/SKILL.md +0 -220
  569. package/src/config/bundled-skills/watcher/SKILL.md +0 -27
  570. package/src/config/bundled-skills/watcher/TOOLS.json +0 -147
  571. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -9
  572. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -9
  573. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -9
  574. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -9
  575. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -9
  576. package/src/config/bundled-skills/weather/SKILL.md +0 -37
  577. package/src/config/bundled-skills/weather/TOOLS.json +0 -32
  578. package/src/config/bundled-skills/weather/icon.svg +0 -24
  579. package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -9
  580. package/src/config/computer-use-prompt.ts +0 -97
  581. package/src/config/defaults.ts +0 -258
  582. package/src/config/loader.ts +0 -339
  583. package/src/config/schema.ts +0 -1415
  584. package/src/config/skill-state.ts +0 -95
  585. package/src/config/skills.ts +0 -972
  586. package/src/config/system-prompt.ts +0 -675
  587. package/src/config/templates/BOOTSTRAP.md +0 -70
  588. package/src/config/templates/IDENTITY.md +0 -25
  589. package/src/config/templates/LOOKS.md +0 -25
  590. package/src/config/templates/SOUL.md +0 -37
  591. package/src/config/templates/USER.md +0 -19
  592. package/src/config/types.ts +0 -41
  593. package/src/config/vellum-skills/deploy-fullstack-vercel/SKILL.md +0 -179
  594. package/src/config/vellum-skills/document-writer/SKILL.md +0 -195
  595. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
  596. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +0 -153
  597. package/src/config/vellum-skills/telegram-setup/SKILL.md +0 -97
  598. package/src/contacts/contact-store.ts +0 -410
  599. package/src/contacts/index.ts +0 -11
  600. package/src/contacts/types.ts +0 -28
  601. package/src/context/token-estimator.ts +0 -108
  602. package/src/context/tool-result-truncation.ts +0 -128
  603. package/src/context/window-manager.ts +0 -531
  604. package/src/daemon/assistant-attachments.ts +0 -691
  605. package/src/daemon/classifier.ts +0 -110
  606. package/src/daemon/computer-use-session.ts +0 -903
  607. package/src/daemon/connection-policy.ts +0 -41
  608. package/src/daemon/date-context.ts +0 -136
  609. package/src/daemon/handlers/apps.ts +0 -530
  610. package/src/daemon/handlers/browser.ts +0 -54
  611. package/src/daemon/handlers/computer-use.ts +0 -187
  612. package/src/daemon/handlers/config.ts +0 -1226
  613. package/src/daemon/handlers/diagnostics.ts +0 -338
  614. package/src/daemon/handlers/documents.ts +0 -173
  615. package/src/daemon/handlers/home-base.ts +0 -78
  616. package/src/daemon/handlers/identity.ts +0 -127
  617. package/src/daemon/handlers/index.ts +0 -129
  618. package/src/daemon/handlers/misc.ts +0 -331
  619. package/src/daemon/handlers/open-bundle-handler.ts +0 -80
  620. package/src/daemon/handlers/publish.ts +0 -187
  621. package/src/daemon/handlers/sessions.ts +0 -555
  622. package/src/daemon/handlers/shared.ts +0 -570
  623. package/src/daemon/handlers/signing.ts +0 -37
  624. package/src/daemon/handlers/skills.ts +0 -486
  625. package/src/daemon/handlers/subagents.ts +0 -210
  626. package/src/daemon/handlers/twitter-auth.ts +0 -198
  627. package/src/daemon/handlers/work-items.ts +0 -632
  628. package/src/daemon/handlers/workspace-files.ts +0 -75
  629. package/src/daemon/handlers.ts +0 -17
  630. package/src/daemon/history-repair.ts +0 -214
  631. package/src/daemon/ipc-blob-store.ts +0 -231
  632. package/src/daemon/ipc-contract-inventory.json +0 -491
  633. package/src/daemon/ipc-contract-inventory.ts +0 -126
  634. package/src/daemon/ipc-contract.ts +0 -2510
  635. package/src/daemon/ipc-protocol.ts +0 -75
  636. package/src/daemon/ipc-validate.ts +0 -188
  637. package/src/daemon/lifecycle.ts +0 -582
  638. package/src/daemon/main.ts +0 -21
  639. package/src/daemon/media-visibility-policy.ts +0 -57
  640. package/src/daemon/ride-shotgun-handler.ts +0 -309
  641. package/src/daemon/server.ts +0 -1213
  642. package/src/daemon/session-agent-loop.ts +0 -922
  643. package/src/daemon/session-attachments.ts +0 -196
  644. package/src/daemon/session-conflict-gate.ts +0 -184
  645. package/src/daemon/session-dynamic-profile.ts +0 -63
  646. package/src/daemon/session-error.ts +0 -290
  647. package/src/daemon/session-evictor.ts +0 -196
  648. package/src/daemon/session-history.ts +0 -437
  649. package/src/daemon/session-lifecycle.ts +0 -147
  650. package/src/daemon/session-media-retry.ts +0 -147
  651. package/src/daemon/session-memory.ts +0 -212
  652. package/src/daemon/session-messaging.ts +0 -145
  653. package/src/daemon/session-notifiers.ts +0 -193
  654. package/src/daemon/session-process.ts +0 -323
  655. package/src/daemon/session-queue-manager.ts +0 -82
  656. package/src/daemon/session-runtime-assembly.ts +0 -447
  657. package/src/daemon/session-skill-tools.ts +0 -356
  658. package/src/daemon/session-slash.ts +0 -305
  659. package/src/daemon/session-surfaces.ts +0 -702
  660. package/src/daemon/session-tool-setup.ts +0 -523
  661. package/src/daemon/session-usage.ts +0 -72
  662. package/src/daemon/session-workspace.ts +0 -19
  663. package/src/daemon/session.ts +0 -400
  664. package/src/daemon/tls-certs.ts +0 -189
  665. package/src/daemon/trace-emitter.ts +0 -82
  666. package/src/daemon/video-thumbnail.ts +0 -62
  667. package/src/daemon/watch-handler.ts +0 -274
  668. package/src/doordash/client.ts +0 -999
  669. package/src/doordash/queries.ts +0 -1311
  670. package/src/doordash/query-extractor.ts +0 -93
  671. package/src/doordash/session.ts +0 -82
  672. package/src/email/provider.ts +0 -117
  673. package/src/email/providers/agentmail.ts +0 -317
  674. package/src/email/providers/index.ts +0 -58
  675. package/src/email/service.ts +0 -303
  676. package/src/email/types.ts +0 -126
  677. package/src/events/bus.ts +0 -157
  678. package/src/events/domain-events.ts +0 -83
  679. package/src/events/index.ts +0 -18
  680. package/src/events/tool-audit-listener.ts +0 -80
  681. package/src/events/tool-domain-event-publisher.ts +0 -111
  682. package/src/events/tool-metrics-listener.ts +0 -159
  683. package/src/events/tool-notification-listener.ts +0 -17
  684. package/src/events/tool-profiling-listener.ts +0 -158
  685. package/src/events/tool-trace-listener.ts +0 -75
  686. package/src/export/formatter.ts +0 -98
  687. package/src/followups/followup-store.ts +0 -168
  688. package/src/followups/index.ts +0 -10
  689. package/src/followups/types.ts +0 -29
  690. package/src/gallery/default-gallery.ts +0 -795
  691. package/src/gallery/gallery-manifest.ts +0 -24
  692. package/src/home-base/app-link-store.ts +0 -82
  693. package/src/home-base/bootstrap.ts +0 -68
  694. package/src/home-base/prebuilt/index.html +0 -662
  695. package/src/home-base/prebuilt/seed-metadata.json +0 -21
  696. package/src/home-base/prebuilt/seed.ts +0 -112
  697. package/src/home-base/prebuilt-home-base-updater.ts +0 -30
  698. package/src/hooks/cli.ts +0 -163
  699. package/src/hooks/config.ts +0 -88
  700. package/src/hooks/discovery.ts +0 -110
  701. package/src/hooks/manager.ts +0 -124
  702. package/src/hooks/runner.ts +0 -123
  703. package/src/hooks/templates.ts +0 -52
  704. package/src/hooks/types.ts +0 -72
  705. package/src/inbound/public-ingress-urls.ts +0 -123
  706. package/src/index.ts +0 -75
  707. package/src/instrument.ts +0 -60
  708. package/src/logfire.ts +0 -99
  709. package/src/media/gemini-image-service.ts +0 -136
  710. package/src/memory/account-store.ts +0 -108
  711. package/src/memory/admin.ts +0 -211
  712. package/src/memory/app-git-service.ts +0 -295
  713. package/src/memory/app-store.ts +0 -577
  714. package/src/memory/attachments-store.ts +0 -397
  715. package/src/memory/channel-delivery-store.ts +0 -353
  716. package/src/memory/checkpoints.ts +0 -52
  717. package/src/memory/clarification-resolver.ts +0 -298
  718. package/src/memory/conflict-intent.ts +0 -157
  719. package/src/memory/conflict-policy.ts +0 -73
  720. package/src/memory/conflict-store.ts +0 -350
  721. package/src/memory/contradiction-checker.ts +0 -358
  722. package/src/memory/conversation-key-store.ts +0 -122
  723. package/src/memory/conversation-store.ts +0 -470
  724. package/src/memory/db.ts +0 -1906
  725. package/src/memory/embedding-backend.ts +0 -229
  726. package/src/memory/embedding-gemini.ts +0 -52
  727. package/src/memory/embedding-local.ts +0 -65
  728. package/src/memory/embedding-ollama.ts +0 -55
  729. package/src/memory/embedding-openai.ts +0 -25
  730. package/src/memory/entity-extractor.ts +0 -474
  731. package/src/memory/external-conversation-store.ts +0 -234
  732. package/src/memory/fingerprint.ts +0 -20
  733. package/src/memory/indexer.ts +0 -156
  734. package/src/memory/items-extractor.ts +0 -461
  735. package/src/memory/job-handlers/backfill.ts +0 -139
  736. package/src/memory/job-handlers/cleanup.ts +0 -58
  737. package/src/memory/job-handlers/conflict.ts +0 -141
  738. package/src/memory/job-handlers/embedding.ts +0 -61
  739. package/src/memory/job-handlers/extraction.ts +0 -123
  740. package/src/memory/job-handlers/index-maintenance.ts +0 -54
  741. package/src/memory/job-handlers/summarization.ts +0 -286
  742. package/src/memory/job-utils.ts +0 -170
  743. package/src/memory/jobs-store.ts +0 -401
  744. package/src/memory/jobs-worker.ts +0 -313
  745. package/src/memory/llm-request-log-store.ts +0 -45
  746. package/src/memory/llm-usage-store.ts +0 -60
  747. package/src/memory/message-content.ts +0 -54
  748. package/src/memory/profile-compiler.ts +0 -160
  749. package/src/memory/published-pages-store.ts +0 -137
  750. package/src/memory/qdrant-client.ts +0 -366
  751. package/src/memory/qdrant-manager.ts +0 -242
  752. package/src/memory/query-builder.ts +0 -45
  753. package/src/memory/retrieval-budget.ts +0 -30
  754. package/src/memory/retriever.ts +0 -653
  755. package/src/memory/runs-store.ts +0 -302
  756. package/src/memory/schema.ts +0 -608
  757. package/src/memory/search/entity.ts +0 -298
  758. package/src/memory/search/formatting.ts +0 -207
  759. package/src/memory/search/lexical.ts +0 -227
  760. package/src/memory/search/ranking.ts +0 -401
  761. package/src/memory/search/semantic.ts +0 -121
  762. package/src/memory/search/types.ts +0 -137
  763. package/src/memory/segmenter.ts +0 -68
  764. package/src/memory/shared-app-links-store.ts +0 -138
  765. package/src/memory/tool-usage-store.ts +0 -62
  766. package/src/messaging/activity-analyzer.ts +0 -76
  767. package/src/messaging/draft-store.ts +0 -88
  768. package/src/messaging/index.ts +0 -3
  769. package/src/messaging/provider-types.ts +0 -80
  770. package/src/messaging/provider.ts +0 -52
  771. package/src/messaging/providers/gmail/adapter.ts +0 -193
  772. package/src/messaging/providers/gmail/client.ts +0 -204
  773. package/src/messaging/providers/gmail/types.ts +0 -90
  774. package/src/messaging/providers/slack/adapter.ts +0 -202
  775. package/src/messaging/providers/slack/client.ts +0 -198
  776. package/src/messaging/providers/slack/types.ts +0 -119
  777. package/src/messaging/providers/telegram-bot/adapter.ts +0 -162
  778. package/src/messaging/providers/telegram-bot/client.ts +0 -104
  779. package/src/messaging/providers/telegram-bot/types.ts +0 -15
  780. package/src/messaging/registry.ts +0 -35
  781. package/src/messaging/style-analyzer.ts +0 -159
  782. package/src/messaging/thread-summarizer.ts +0 -306
  783. package/src/messaging/triage-engine.ts +0 -323
  784. package/src/messaging/types.ts +0 -55
  785. package/src/permissions/checker.ts +0 -640
  786. package/src/permissions/defaults.ts +0 -254
  787. package/src/permissions/prompter.ts +0 -98
  788. package/src/permissions/secret-prompter.ts +0 -114
  789. package/src/permissions/shell-identity.ts +0 -227
  790. package/src/permissions/trust-store.ts +0 -607
  791. package/src/permissions/types.ts +0 -43
  792. package/src/permissions/workspace-policy.ts +0 -114
  793. package/src/playbooks/index.ts +0 -2
  794. package/src/playbooks/playbook-compiler.ts +0 -90
  795. package/src/playbooks/types.ts +0 -55
  796. package/src/providers/anthropic/client.ts +0 -751
  797. package/src/providers/failover.ts +0 -129
  798. package/src/providers/fireworks/client.ts +0 -20
  799. package/src/providers/gemini/client.ts +0 -285
  800. package/src/providers/ollama/client.ts +0 -30
  801. package/src/providers/openai/client.ts +0 -337
  802. package/src/providers/openrouter/client.ts +0 -20
  803. package/src/providers/ratelimit.ts +0 -93
  804. package/src/providers/registry.ts +0 -146
  805. package/src/providers/retry.ts +0 -81
  806. package/src/providers/stream-timeout.ts +0 -38
  807. package/src/providers/types.ts +0 -109
  808. package/src/runtime/assistant-event-hub.ts +0 -157
  809. package/src/runtime/assistant-event.ts +0 -82
  810. package/src/runtime/channel-approval-parser.ts +0 -60
  811. package/src/runtime/channel-approval-types.ts +0 -71
  812. package/src/runtime/channel-approvals.ts +0 -145
  813. package/src/runtime/gateway-client.ts +0 -58
  814. package/src/runtime/http-server.ts +0 -1076
  815. package/src/runtime/http-types.ts +0 -66
  816. package/src/runtime/routes/app-routes.ts +0 -174
  817. package/src/runtime/routes/attachment-routes.ts +0 -133
  818. package/src/runtime/routes/call-routes.ts +0 -190
  819. package/src/runtime/routes/channel-routes.ts +0 -662
  820. package/src/runtime/routes/conversation-routes.ts +0 -352
  821. package/src/runtime/routes/events-routes.ts +0 -148
  822. package/src/runtime/routes/run-routes.ts +0 -257
  823. package/src/runtime/routes/secret-routes.ts +0 -76
  824. package/src/runtime/run-orchestrator.ts +0 -293
  825. package/src/schedule/recurrence-engine.ts +0 -162
  826. package/src/schedule/recurrence-types.ts +0 -67
  827. package/src/schedule/schedule-store.ts +0 -506
  828. package/src/schedule/scheduler.ts +0 -171
  829. package/src/security/encrypted-store.ts +0 -238
  830. package/src/security/keychain.ts +0 -252
  831. package/src/security/oauth-callback-registry.ts +0 -66
  832. package/src/security/oauth2.ts +0 -274
  833. package/src/security/redaction.ts +0 -89
  834. package/src/security/secret-allowlist.ts +0 -164
  835. package/src/security/secret-ingress.ts +0 -57
  836. package/src/security/secret-scanner.ts +0 -550
  837. package/src/security/secure-keys.ts +0 -180
  838. package/src/security/token-manager.ts +0 -141
  839. package/src/services/published-app-updater.ts +0 -69
  840. package/src/services/vercel-deploy.ts +0 -73
  841. package/src/skills/active-skill-tools.ts +0 -81
  842. package/src/skills/clawhub.ts +0 -414
  843. package/src/skills/include-graph.ts +0 -146
  844. package/src/skills/managed-store.ts +0 -233
  845. package/src/skills/path-classifier.ts +0 -128
  846. package/src/skills/slash-commands.ts +0 -174
  847. package/src/skills/tool-manifest.ts +0 -165
  848. package/src/skills/version-hash.ts +0 -110
  849. package/src/slack/slack-webhook.ts +0 -61
  850. package/src/subagent/index.ts +0 -19
  851. package/src/subagent/manager.ts +0 -511
  852. package/src/subagent/types.ts +0 -69
  853. package/src/swarm/backend-claude-code.ts +0 -145
  854. package/src/swarm/index.ts +0 -44
  855. package/src/swarm/limits.ts +0 -37
  856. package/src/swarm/orchestrator.ts +0 -279
  857. package/src/swarm/plan-validator.ts +0 -151
  858. package/src/swarm/router-planner.ts +0 -100
  859. package/src/swarm/router-prompts.ts +0 -36
  860. package/src/swarm/synthesizer.ts +0 -62
  861. package/src/swarm/types.ts +0 -62
  862. package/src/swarm/worker-backend.ts +0 -121
  863. package/src/swarm/worker-prompts.ts +0 -79
  864. package/src/swarm/worker-runner.ts +0 -164
  865. package/src/tasks/SPEC.md +0 -139
  866. package/src/tasks/candidate-store.ts +0 -86
  867. package/src/tasks/ephemeral-permissions.ts +0 -48
  868. package/src/tasks/task-compiler.ts +0 -199
  869. package/src/tasks/task-runner.ts +0 -90
  870. package/src/tasks/task-scheduler.ts +0 -21
  871. package/src/tasks/task-store.ts +0 -127
  872. package/src/tasks/tool-sanitizer.ts +0 -36
  873. package/src/tools/apps/definitions.ts +0 -59
  874. package/src/tools/apps/executors.ts +0 -313
  875. package/src/tools/apps/open-proxy.ts +0 -43
  876. package/src/tools/apps/registry.ts +0 -16
  877. package/src/tools/assets/materialize.ts +0 -218
  878. package/src/tools/assets/search.ts +0 -361
  879. package/src/tools/browser/__tests__/auth-cache.test.ts +0 -219
  880. package/src/tools/browser/__tests__/auth-detector.test.ts +0 -362
  881. package/src/tools/browser/__tests__/jit-auth.test.ts +0 -189
  882. package/src/tools/browser/api-map.ts +0 -293
  883. package/src/tools/browser/auth-cache.ts +0 -149
  884. package/src/tools/browser/auth-detector.ts +0 -347
  885. package/src/tools/browser/auto-navigate.ts +0 -270
  886. package/src/tools/browser/browser-execution.ts +0 -980
  887. package/src/tools/browser/browser-handoff.ts +0 -79
  888. package/src/tools/browser/browser-manager.ts +0 -715
  889. package/src/tools/browser/browser-screencast.ts +0 -217
  890. package/src/tools/browser/headless-browser.ts +0 -450
  891. package/src/tools/browser/jit-auth.ts +0 -51
  892. package/src/tools/browser/network-recorder.ts +0 -349
  893. package/src/tools/browser/network-recording-types.ts +0 -49
  894. package/src/tools/browser/recording-store.ts +0 -49
  895. package/src/tools/browser/runtime-check.ts +0 -43
  896. package/src/tools/browser/x-auto-navigate.ts +0 -207
  897. package/src/tools/calls/call-end.ts +0 -67
  898. package/src/tools/calls/call-start.ts +0 -81
  899. package/src/tools/calls/call-status.ts +0 -81
  900. package/src/tools/claude-code/claude-code.ts +0 -428
  901. package/src/tools/computer-use/definitions.ts +0 -443
  902. package/src/tools/computer-use/registry.ts +0 -22
  903. package/src/tools/computer-use/request-computer-control.ts +0 -53
  904. package/src/tools/computer-use/skill-proxy-bridge.ts +0 -28
  905. package/src/tools/credentials/account-registry.ts +0 -127
  906. package/src/tools/credentials/broker-types.ts +0 -107
  907. package/src/tools/credentials/broker.ts +0 -372
  908. package/src/tools/credentials/domain-policy.ts +0 -51
  909. package/src/tools/credentials/host-pattern-match.ts +0 -60
  910. package/src/tools/credentials/metadata-store.ts +0 -335
  911. package/src/tools/credentials/policy-types.ts +0 -52
  912. package/src/tools/credentials/policy-validate.ts +0 -80
  913. package/src/tools/credentials/resolve.ts +0 -122
  914. package/src/tools/credentials/selection.ts +0 -159
  915. package/src/tools/credentials/tool-policy.ts +0 -25
  916. package/src/tools/credentials/vault.ts +0 -657
  917. package/src/tools/document/document-tool.ts +0 -92
  918. package/src/tools/document/editor-template.ts +0 -237
  919. package/src/tools/execution-target.ts +0 -21
  920. package/src/tools/execution-timeout.ts +0 -49
  921. package/src/tools/executor.ts +0 -815
  922. package/src/tools/filesystem/edit.ts +0 -127
  923. package/src/tools/filesystem/fuzzy-match.ts +0 -202
  924. package/src/tools/filesystem/read.ts +0 -71
  925. package/src/tools/filesystem/view-image.ts +0 -199
  926. package/src/tools/filesystem/write.ts +0 -79
  927. package/src/tools/followups/followup_create.ts +0 -76
  928. package/src/tools/followups/followup_list.ts +0 -60
  929. package/src/tools/followups/followup_resolve.ts +0 -56
  930. package/src/tools/host-filesystem/edit.ts +0 -125
  931. package/src/tools/host-filesystem/read.ts +0 -80
  932. package/src/tools/host-filesystem/write.ts +0 -76
  933. package/src/tools/host-terminal/cli-discover.ts +0 -180
  934. package/src/tools/host-terminal/host-shell.ts +0 -191
  935. package/src/tools/memory/definitions.ts +0 -69
  936. package/src/tools/memory/handlers.ts +0 -246
  937. package/src/tools/memory/register.ts +0 -66
  938. package/src/tools/network/__tests__/web-search.test.ts +0 -427
  939. package/src/tools/network/domain-normalize.ts +0 -85
  940. package/src/tools/network/script-proxy/__tests__/logging.test.ts +0 -248
  941. package/src/tools/network/script-proxy/__tests__/policy.test.ts +0 -234
  942. package/src/tools/network/script-proxy/__tests__/router.test.ts +0 -76
  943. package/src/tools/network/script-proxy/certs.ts +0 -237
  944. package/src/tools/network/script-proxy/connect-tunnel.ts +0 -82
  945. package/src/tools/network/script-proxy/http-forwarder.ts +0 -151
  946. package/src/tools/network/script-proxy/index.ts +0 -28
  947. package/src/tools/network/script-proxy/logging.ts +0 -196
  948. package/src/tools/network/script-proxy/mitm-handler.ts +0 -269
  949. package/src/tools/network/script-proxy/policy.ts +0 -152
  950. package/src/tools/network/script-proxy/router.ts +0 -60
  951. package/src/tools/network/script-proxy/server.ts +0 -136
  952. package/src/tools/network/script-proxy/session-manager.ts +0 -534
  953. package/src/tools/network/script-proxy/types.ts +0 -125
  954. package/src/tools/network/url-safety.ts +0 -227
  955. package/src/tools/network/web-fetch.ts +0 -713
  956. package/src/tools/network/web-search.ts +0 -296
  957. package/src/tools/policy-context.ts +0 -29
  958. package/src/tools/registry.ts +0 -295
  959. package/src/tools/reminder/reminder-store.ts +0 -148
  960. package/src/tools/reminder/reminder.ts +0 -80
  961. package/src/tools/schedule/create.ts +0 -81
  962. package/src/tools/schedule/delete.ts +0 -28
  963. package/src/tools/schedule/list.ts +0 -69
  964. package/src/tools/schedule/update.ts +0 -97
  965. package/src/tools/shared/filesystem/edit-engine.ts +0 -56
  966. package/src/tools/shared/filesystem/errors.ts +0 -85
  967. package/src/tools/shared/filesystem/file-ops-service.ts +0 -215
  968. package/src/tools/shared/filesystem/format-diff.ts +0 -35
  969. package/src/tools/shared/filesystem/path-policy.ts +0 -125
  970. package/src/tools/shared/filesystem/size-guard.ts +0 -41
  971. package/src/tools/shared/filesystem/types.ts +0 -80
  972. package/src/tools/shared/shell-output.ts +0 -52
  973. package/src/tools/skills/delete-managed.ts +0 -60
  974. package/src/tools/skills/load.ts +0 -139
  975. package/src/tools/skills/sandbox-runner.ts +0 -279
  976. package/src/tools/skills/scaffold-managed.ts +0 -150
  977. package/src/tools/skills/script-contract.ts +0 -6
  978. package/src/tools/skills/skill-script-runner.ts +0 -86
  979. package/src/tools/skills/skill-tool-factory.ts +0 -64
  980. package/src/tools/skills/vellum-catalog.ts +0 -217
  981. package/src/tools/subagent/abort.ts +0 -33
  982. package/src/tools/subagent/message.ts +0 -39
  983. package/src/tools/subagent/read.ts +0 -67
  984. package/src/tools/subagent/spawn.ts +0 -46
  985. package/src/tools/subagent/status.ts +0 -45
  986. package/src/tools/swarm/delegate.ts +0 -183
  987. package/src/tools/system/request-permission.ts +0 -98
  988. package/src/tools/system/version.ts +0 -43
  989. package/src/tools/tasks/index.ts +0 -27
  990. package/src/tools/tasks/task-delete.ts +0 -82
  991. package/src/tools/tasks/task-list.ts +0 -44
  992. package/src/tools/tasks/task-run.ts +0 -97
  993. package/src/tools/tasks/task-save.ts +0 -47
  994. package/src/tools/tasks/work-item-enqueue.ts +0 -234
  995. package/src/tools/tasks/work-item-list.ts +0 -55
  996. package/src/tools/tasks/work-item-remove.ts +0 -60
  997. package/src/tools/tasks/work-item-run.ts +0 -78
  998. package/src/tools/tasks/work-item-update.ts +0 -114
  999. package/src/tools/terminal/backends/docker.ts +0 -372
  1000. package/src/tools/terminal/backends/native.ts +0 -190
  1001. package/src/tools/terminal/backends/types.ts +0 -26
  1002. package/src/tools/terminal/evaluate-typescript.ts +0 -275
  1003. package/src/tools/terminal/parser.ts +0 -413
  1004. package/src/tools/terminal/safe-env.ts +0 -37
  1005. package/src/tools/terminal/sandbox-diagnostics.ts +0 -149
  1006. package/src/tools/terminal/sandbox.ts +0 -44
  1007. package/src/tools/terminal/shell.ts +0 -257
  1008. package/src/tools/tool-manifest.ts +0 -198
  1009. package/src/tools/types.ts +0 -176
  1010. package/src/tools/ui-surface/definitions.ts +0 -244
  1011. package/src/tools/ui-surface/registry.ts +0 -14
  1012. package/src/tools/watch/screen-watch.ts +0 -130
  1013. package/src/tools/watch/watch-state.ts +0 -119
  1014. package/src/tools/watcher/create.ts +0 -64
  1015. package/src/tools/watcher/delete.ts +0 -27
  1016. package/src/tools/watcher/digest.ts +0 -50
  1017. package/src/tools/watcher/list.ts +0 -60
  1018. package/src/tools/watcher/update.ts +0 -56
  1019. package/src/tools/weather/service.ts +0 -551
  1020. package/src/twitter/client.ts +0 -690
  1021. package/src/twitter/oauth-client.ts +0 -102
  1022. package/src/twitter/router.ts +0 -101
  1023. package/src/twitter/session.ts +0 -91
  1024. package/src/usage/actors.ts +0 -24
  1025. package/src/usage/types.ts +0 -37
  1026. package/src/util/clipboard.ts +0 -33
  1027. package/src/util/content-id.ts +0 -16
  1028. package/src/util/debounce.ts +0 -88
  1029. package/src/util/diff.ts +0 -181
  1030. package/src/util/errors.ts +0 -129
  1031. package/src/util/logger.ts +0 -243
  1032. package/src/util/network-info.ts +0 -47
  1033. package/src/util/platform.ts +0 -632
  1034. package/src/util/pricing.ts +0 -150
  1035. package/src/util/promise-guard.ts +0 -37
  1036. package/src/util/retry.ts +0 -98
  1037. package/src/util/spinner.ts +0 -51
  1038. package/src/util/time.ts +0 -16
  1039. package/src/util/truncate.ts +0 -6
  1040. package/src/util/xml.ts +0 -4
  1041. package/src/version.ts +0 -3
  1042. package/src/watcher/constants.ts +0 -11
  1043. package/src/watcher/engine.ts +0 -199
  1044. package/src/watcher/provider-registry.ts +0 -15
  1045. package/src/watcher/provider-types.ts +0 -48
  1046. package/src/watcher/providers/gmail.ts +0 -198
  1047. package/src/watcher/providers/google-calendar.ts +0 -228
  1048. package/src/watcher/providers/slack.ts +0 -129
  1049. package/src/watcher/watcher-store.ts +0 -419
  1050. package/src/work-items/work-item-runner.ts +0 -171
  1051. package/src/work-items/work-item-store.ts +0 -325
  1052. package/src/workspace/commit-message-enrichment-service.ts +0 -284
  1053. package/src/workspace/commit-message-provider.ts +0 -95
  1054. package/src/workspace/git-service.ts +0 -857
  1055. package/src/workspace/heartbeat-service.ts +0 -345
  1056. package/src/workspace/provider-commit-message-generator.ts +0 -285
  1057. package/src/workspace/top-level-renderer.ts +0 -19
  1058. package/src/workspace/top-level-scanner.ts +0 -41
  1059. package/src/workspace/turn-commit.ts +0 -175
  1060. package/tsconfig.json +0 -21
@@ -1,1605 +0,0 @@
1
- import { describe, test, expect, beforeEach, mock, spyOn } from 'bun:test';
2
- import * as fs from 'node:fs';
3
- import { mkdtempSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join, dirname } from 'node:path';
6
-
7
- // Create a temp directory for the trust file
8
- const testDir = mkdtempSync(join(tmpdir(), 'trust-store-test-'));
9
-
10
- // Mock platform module so trust-store writes to temp dir instead of ~/.vellum
11
- mock.module('../util/platform.js', () => ({
12
- getRootDir: () => testDir,
13
- getDataDir: () => testDir,
14
- isMacOS: () => process.platform === 'darwin',
15
- isLinux: () => process.platform === 'linux',
16
- isWindows: () => process.platform === 'win32',
17
- getSocketPath: () => join(testDir, 'test.sock'),
18
- getPidPath: () => join(testDir, 'test.pid'),
19
- getDbPath: () => join(testDir, 'test.db'),
20
- getLogPath: () => join(testDir, 'test.log'),
21
- ensureDataDir: () => {},
22
- }));
23
-
24
- // Mock logger to suppress output during tests
25
- mock.module('../util/logger.js', () => ({
26
- getLogger: () => ({
27
- info: () => {},
28
- warn: () => {},
29
- error: () => {},
30
- debug: () => {},
31
- trace: () => {},
32
- fatal: () => {},
33
- child: () => ({
34
- info: () => {},
35
- warn: () => {},
36
- error: () => {},
37
- debug: () => {},
38
- }),
39
- }),
40
- }));
41
-
42
- import { addRule, removeRule, updateRule, findMatchingRule, findDenyRule, findHighestPriorityRule, getAllRules, clearAllRules, clearCache } from '../permissions/trust-store.js';
43
- import { getDefaultRuleTemplates } from '../permissions/defaults.js';
44
-
45
- const trustPath = join(testDir, 'protected', 'trust.json');
46
- const DEFAULT_TEMPLATES = getDefaultRuleTemplates();
47
- const NUM_DEFAULTS = DEFAULT_TEMPLATES.length;
48
- const DEFAULT_PRIORITY_BY_ID = new Map(DEFAULT_TEMPLATES.map((t) => [t.id, t.priority]));
49
-
50
- describe('Trust Store', () => {
51
- beforeEach(() => {
52
- // Clear cached rules and remove the trust file between tests
53
- clearCache();
54
- try { rmSync(trustPath); } catch { /* may not exist */ }
55
- });
56
-
57
- // Intentionally do not remove `testDir` in afterAll.
58
- // A late async log flush can still attempt to open `test.log` under this dir,
59
- // which intermittently causes an unhandled ENOENT in CI if the dir is removed.
60
- // ── addRule ─────────────────────────────────────────────────────
61
-
62
- describe('addRule', () => {
63
- test('adds a rule and returns it', () => {
64
- const rule = addRule('bash', 'git *', '/home/user/project');
65
- expect(rule.id).toBeDefined();
66
- expect(rule.tool).toBe('bash');
67
- expect(rule.pattern).toBe('git *');
68
- expect(rule.scope).toBe('/home/user/project');
69
- expect(rule.decision).toBe('allow');
70
- expect(rule.priority).toBe(100);
71
- expect(rule.createdAt).toBeGreaterThan(0);
72
- });
73
-
74
- test('assigns unique IDs to each rule', () => {
75
- const rule1 = addRule('bash', 'npm *', '/tmp');
76
- const rule2 = addRule('bash', 'bun *', '/tmp');
77
- expect(rule1.id).not.toBe(rule2.id);
78
- });
79
-
80
- test('persists rule to disk', () => {
81
- addRule('bash', 'git push', '/home/user');
82
- const raw = readFileSync(trustPath, 'utf-8');
83
- const data = JSON.parse(raw);
84
- expect(data.version).toBe(3);
85
- expect(data.rules).toHaveLength(1 + NUM_DEFAULTS);
86
- const userRule = data.rules.find((r: { pattern: string }) => r.pattern === 'git push');
87
- expect(userRule).toBeDefined();
88
- expect(userRule.priority).toBe(100);
89
- });
90
-
91
- test('multiple rules accumulate', () => {
92
- addRule('bash', 'git *', '/tmp');
93
- addRule('file_write', '/tmp/*', '/tmp');
94
- addRule('bash', 'npm *', '/tmp');
95
- expect(getAllRules()).toHaveLength(3 + NUM_DEFAULTS);
96
- });
97
-
98
- test('default priority is 100', () => {
99
- const rule = addRule('bash', 'git *', '/tmp');
100
- expect(rule.priority).toBe(100);
101
- });
102
-
103
- test('custom priority is respected', () => {
104
- const rule = addRule('bash', 'git *', '/tmp', 'allow', 5);
105
- expect(rule.priority).toBe(5);
106
- });
107
-
108
- test('rules are sorted by priority descending in getAllRules', () => {
109
- addRule('bash', 'low *', '/tmp', 'allow', 0);
110
- addRule('bash', 'high *', '/tmp', 'allow', 2);
111
- addRule('bash', 'med *', '/tmp', 'allow', 1);
112
- const rules = getAllRules();
113
- // Default ask rules have higher priority than user rules
114
- const maxDefaultPriority = Math.max(...DEFAULT_TEMPLATES.map((t) => t.priority));
115
- expect(rules[0].priority).toBe(maxDefaultPriority);
116
- const userRules = rules.filter((r) => !r.id.startsWith('default:'));
117
- expect(userRules[0].priority).toBe(2);
118
- expect(userRules[1].priority).toBe(1);
119
- expect(userRules[2].priority).toBe(0);
120
- });
121
-
122
- test('accepts allowHighRisk option and persists it', () => {
123
- const rule = addRule('bash', 'sudo *', 'everywhere', 'allow', 100, { allowHighRisk: true });
124
- expect(rule.allowHighRisk).toBe(true);
125
- // Verify it persists to disk
126
- clearCache();
127
- const rules = getAllRules();
128
- const found = rules.find((r) => r.id === rule.id);
129
- expect(found).toBeDefined();
130
- expect(found!.allowHighRisk).toBe(true);
131
- });
132
-
133
- test('addRule without allowHighRisk option does not set the field', () => {
134
- const rule = addRule('bash', 'git *', '/tmp');
135
- expect(rule.allowHighRisk).toBeUndefined();
136
- // Verify on disk
137
- const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
138
- const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
139
- expect(diskRule).toBeDefined();
140
- expect(diskRule).not.toHaveProperty('allowHighRisk');
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((r) => !r.id.startsWith('default:'));
147
- expect(userRules[0].decision).toBe('deny');
148
- expect(userRules[1].decision).toBe('allow');
149
- });
150
-
151
- test('accepts executionTarget option and persists it', () => {
152
- const rule = addRule('skill_tool', 'skill_tool:*', '/tmp', 'allow', 100, {
153
- executionTarget: 'sandbox',
154
- });
155
- expect(rule.executionTarget).toBe('sandbox');
156
-
157
- // Verify persistence to disk
158
- clearCache();
159
- const rules = getAllRules();
160
- const found = rules.find((r) => r.id === rule.id);
161
- expect(found).toBeDefined();
162
- expect(found!.executionTarget).toBe('sandbox');
163
- });
164
-
165
- test('accepts all contextual options together (target, allowHighRisk)', () => {
166
- const rule = addRule('risky_tool', 'risky_tool:*', 'everywhere', 'allow', 100, {
167
- allowHighRisk: true,
168
- executionTarget: 'host',
169
- });
170
- expect(rule.allowHighRisk).toBe(true);
171
- expect(rule.executionTarget).toBe('host');
172
-
173
- // Verify on disk
174
- const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
175
- const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
176
- expect(diskRule).toBeDefined();
177
- expect(diskRule.allowHighRisk).toBe(true);
178
- expect(diskRule.executionTarget).toBe('host');
179
- });
180
-
181
- test('addRule without options does not set optional fields', () => {
182
- const rule = addRule('bash', 'echo *', '/tmp');
183
- expect(rule.executionTarget).toBeUndefined();
184
-
185
- // Verify on disk
186
- const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
187
- const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
188
- expect(diskRule).toBeDefined();
189
- expect(diskRule).not.toHaveProperty('executionTarget');
190
- });
191
- });
192
-
193
- // ── removeRule ──────────────────────────────────────────────────
194
-
195
- describe('removeRule', () => {
196
- test('removes an existing rule', () => {
197
- const rule = addRule('bash', 'git *', '/tmp');
198
- expect(removeRule(rule.id)).toBe(true);
199
- expect(getAllRules()).toHaveLength(NUM_DEFAULTS);
200
- });
201
-
202
- test('returns false for non-existent ID', () => {
203
- expect(removeRule('non-existent-id')).toBe(false);
204
- });
205
-
206
- test('persists removal to disk', () => {
207
- const rule = addRule('bash', 'npm *', '/tmp');
208
- removeRule(rule.id);
209
- // Reload from disk to verify
210
- clearCache();
211
- expect(getAllRules()).toHaveLength(NUM_DEFAULTS);
212
- });
213
-
214
- test('only removes the targeted rule', () => {
215
- const rule1 = addRule('bash', 'git *', '/tmp');
216
- const rule2 = addRule('bash', 'npm *', '/tmp');
217
- removeRule(rule1.id);
218
- const remaining = getAllRules();
219
- expect(remaining).toHaveLength(1 + NUM_DEFAULTS);
220
- expect(remaining.find((r) => r.id === rule2.id)).toBeDefined();
221
- });
222
- });
223
-
224
- // ── updateRule ─────────────────────────────────────────────────
225
-
226
- describe('updateRule', () => {
227
- test('updates pattern on an existing rule', () => {
228
- const rule = addRule('bash', 'git *', '/tmp');
229
- const updated = updateRule(rule.id, { pattern: 'git push *' });
230
- expect(updated.pattern).toBe('git push *');
231
- expect(updated.id).toBe(rule.id);
232
- expect(updated.tool).toBe('bash');
233
- });
234
-
235
- test('updates multiple fields at once', () => {
236
- const rule = addRule('bash', 'npm *', '/tmp');
237
- const updated = updateRule(rule.id, { tool: 'file_write', scope: '/home', decision: 'deny', priority: 50 });
238
- expect(updated.tool).toBe('file_write');
239
- expect(updated.scope).toBe('/home');
240
- expect(updated.decision).toBe('deny');
241
- expect(updated.priority).toBe(50);
242
- });
243
-
244
- test('throws for non-existent rule ID', () => {
245
- expect(() => updateRule('non-existent-id', { pattern: 'test' })).toThrow('Trust rule not found: non-existent-id');
246
- });
247
-
248
- test('persists update to disk', () => {
249
- const rule = addRule('bash', 'git *', '/tmp');
250
- updateRule(rule.id, { pattern: 'git status' });
251
- clearCache();
252
- const rules = getAllRules();
253
- const found = rules.find((r) => r.id === rule.id);
254
- expect(found).toBeDefined();
255
- expect(found!.pattern).toBe('git status');
256
- });
257
-
258
- test('re-sorts rules after priority change', () => {
259
- const rule1 = addRule('bash', 'low *', '/tmp', 'allow', 10);
260
- const rule2 = addRule('bash', 'high *', '/tmp', 'allow', 200);
261
- // rule2 should be first (higher priority)
262
- let userRules = getAllRules().filter((r) => !r.id.startsWith('default:'));
263
- expect(userRules[0].id).toBe(rule2.id);
264
- // Update rule1 to have higher priority
265
- updateRule(rule1.id, { priority: 300 });
266
- userRules = getAllRules().filter((r) => !r.id.startsWith('default:'));
267
- expect(userRules[0].id).toBe(rule1.id);
268
- });
269
-
270
- test('leaves unchanged fields intact', () => {
271
- const rule = addRule('bash', 'git *', '/home/user', 'allow', 100);
272
- updateRule(rule.id, { pattern: 'git push *' });
273
- const updated = getAllRules().find((r) => r.id === rule.id)!;
274
- expect(updated.tool).toBe('bash');
275
- expect(updated.scope).toBe('/home/user');
276
- expect(updated.decision).toBe('allow');
277
- expect(updated.priority).toBe(100);
278
- expect(updated.createdAt).toBe(rule.createdAt);
279
- });
280
- });
281
-
282
- // ── findMatchingRule ────────────────────────────────────────────
283
-
284
- describe('findMatchingRule', () => {
285
- test('finds exact match', () => {
286
- addRule('bash', 'git push', '/tmp');
287
- const match = findMatchingRule('bash', 'git push', '/tmp');
288
- expect(match).not.toBeNull();
289
- expect(match!.pattern).toBe('git push');
290
- });
291
-
292
- test('finds glob wildcard match', () => {
293
- addRule('bash', 'git *', '/tmp');
294
- const match = findMatchingRule('bash', 'git push origin main', '/tmp');
295
- expect(match).not.toBeNull();
296
- });
297
-
298
- test('returns null when tool does not match', () => {
299
- addRule('file_write', 'git *', '/tmp');
300
- // host_bash default is 'ask' so findMatchingRule (allow-only) won't find it
301
- const match = findMatchingRule('host_bash', 'git push', '/tmp');
302
- expect(match).toBeNull();
303
- });
304
-
305
- test('returns null when pattern does not match', () => {
306
- addRule('host_bash', 'git *', '/tmp');
307
- const match = findMatchingRule('host_bash', 'npm install', '/tmp');
308
- expect(match).toBeNull();
309
- });
310
-
311
- // Scope matching
312
- describe('scope matching', () => {
313
- test('matches when scope equals rule scope', () => {
314
- addRule('bash', 'npm *', '/home/user/project');
315
- const match = findMatchingRule('bash', 'npm install', '/home/user/project');
316
- expect(match).not.toBeNull();
317
- });
318
-
319
- test('matches when scope is under rule scope (prefix)', () => {
320
- addRule('bash', 'npm *', '/home/user');
321
- const match = findMatchingRule('bash', 'npm install', '/home/user/project/sub');
322
- expect(match).not.toBeNull();
323
- });
324
-
325
- test('does not match when scope is outside rule scope', () => {
326
- addRule('host_bash', 'npm *', '/home/user/project');
327
- const match = findMatchingRule('host_bash', 'npm install', '/home/other');
328
- expect(match).toBeNull();
329
- });
330
-
331
- test('everywhere scope matches any directory', () => {
332
- addRule('bash', 'git *', 'everywhere');
333
- const match = findMatchingRule('bash', 'git status', '/any/random/path');
334
- expect(match).not.toBeNull();
335
- });
336
-
337
- test('everywhere scope matches root', () => {
338
- addRule('bash', 'ls', 'everywhere');
339
- const match = findMatchingRule('bash', 'ls', '/');
340
- expect(match).not.toBeNull();
341
- });
342
-
343
- test('does not match sibling path with shared prefix', () => {
344
- addRule('host_bash', 'npm *', '/home/user/project');
345
- const match = findMatchingRule('host_bash', 'npm install', '/home/user/project-evil');
346
- expect(match).toBeNull();
347
- });
348
-
349
- test('matches exact scope with trailing slash on working dir', () => {
350
- addRule('bash', 'npm *', '/home/user/project');
351
- const match = findMatchingRule('bash', 'npm install', '/home/user/project/');
352
- expect(match).not.toBeNull();
353
- });
354
-
355
- test('matches when rule scope has trailing slash', () => {
356
- addRule('bash', 'npm *', '/home/user/project/');
357
- const match = findMatchingRule('bash', 'npm install', '/home/user/project');
358
- expect(match).not.toBeNull();
359
- });
360
-
361
- test('does not match sibling with glob-suffixed scope', () => {
362
- addRule('host_bash', 'npm *', '/home/user/project*');
363
- const match = findMatchingRule('host_bash', 'npm install', '/home/user/project-evil');
364
- expect(match).toBeNull();
365
- });
366
- });
367
-
368
- // Pattern matching with minimatch
369
- describe('pattern matching', () => {
370
- test('matches * wildcard', () => {
371
- addRule('bash', 'npm *', '/tmp');
372
- expect(findMatchingRule('bash', 'npm install', '/tmp')).not.toBeNull();
373
- expect(findMatchingRule('bash', 'npm test', '/tmp')).not.toBeNull();
374
- });
375
-
376
- test('matches exact string', () => {
377
- addRule('host_bash', 'git status', '/tmp');
378
- expect(findMatchingRule('host_bash', 'git status', '/tmp')).not.toBeNull();
379
- expect(findMatchingRule('host_bash', 'git push', '/tmp')).toBeNull();
380
- });
381
-
382
- test('matches file path pattern', () => {
383
- addRule('file_write', '/tmp/*', '/tmp');
384
- expect(findMatchingRule('file_write', '/tmp/file.txt', '/tmp')).not.toBeNull();
385
- });
386
-
387
- test('star pattern matches single-segment strings', () => {
388
- addRule('file_write', '*', '/tmp');
389
- // minimatch '*' matches strings without path separators
390
- expect(findMatchingRule('file_write', 'file.txt', '/tmp')).not.toBeNull();
391
- });
392
-
393
- test('star pattern does not match paths with slashes', () => {
394
- addRule('file_write', '*', '/tmp');
395
- // minimatch '*' does not cross '/' boundaries
396
- expect(findMatchingRule('file_write', '/any/path/file.txt', '/tmp')).toBeNull();
397
- });
398
- });
399
- });
400
-
401
- // ── findHighestPriorityRule ──────────────────────────────────────
402
-
403
- describe('findHighestPriorityRule', () => {
404
- test('returns highest priority matching rule', () => {
405
- addRule('bash', 'rm *', '/tmp', 'allow', 0);
406
- addRule('bash', 'rm *', '/tmp', 'deny', 100);
407
- const match = findHighestPriorityRule('bash', ['rm file.txt'], '/tmp');
408
- expect(match).not.toBeNull();
409
- expect(match!.decision).toBe('deny');
410
- expect(match!.priority).toBe(100);
411
- });
412
-
413
- test('higher priority allow beats lower priority deny', () => {
414
- addRule('bash', 'rm *', '/tmp', 'deny', 0);
415
- addRule('bash', 'rm *', '/tmp', 'allow', 100);
416
- const match = findHighestPriorityRule('bash', ['rm file.txt'], '/tmp');
417
- expect(match).not.toBeNull();
418
- expect(match!.decision).toBe('allow');
419
- });
420
-
421
- test('same priority: deny beats allow', () => {
422
- addRule('bash', 'rm *', '/tmp', 'allow', 100);
423
- addRule('bash', 'rm *', '/tmp', 'deny', 100);
424
- const match = findHighestPriorityRule('bash', ['rm file.txt'], '/tmp');
425
- expect(match).not.toBeNull();
426
- expect(match!.decision).toBe('deny');
427
- });
428
-
429
- test('checks multiple command candidates', () => {
430
- addRule('web_fetch', 'web_fetch:https://example.com/*', '/tmp', 'allow');
431
- const match = findHighestPriorityRule(
432
- 'web_fetch',
433
- ['web_fetch:https://example.com/page', 'web_fetch:https://example.com/*'],
434
- '/tmp',
435
- );
436
- expect(match).not.toBeNull();
437
- });
438
-
439
- test('returns null when no rule matches', () => {
440
- // Use file_read with a non-workspace path — file_read defaults only
441
- // cover specific workspace files, so /tmp paths won't match any default.
442
- addRule('file_read', 'file_read:/specific/*', '/tmp', 'allow');
443
- const match = findHighestPriorityRule('file_read', ['file_read:/other/path'], '/tmp');
444
- expect(match).toBeNull();
445
- });
446
-
447
- test('respects scope matching', () => {
448
- // Use file_read — bash has a global default allow rule that matches everywhere.
449
- addRule('file_read', 'file_read:/home/user/project/*', '/home/user/project', 'deny');
450
- expect(findHighestPriorityRule('file_read', ['file_read:/home/user/project/file.txt'], '/home/user/project/sub')).not.toBeNull();
451
- expect(findHighestPriorityRule('file_read', ['file_read:/home/user/project/file.txt'], '/home/other')).toBeNull();
452
- });
453
-
454
- test('everywhere scope matches any directory', () => {
455
- addRule('bash', 'git *', 'everywhere', 'allow');
456
- const match = findHighestPriorityRule('bash', ['git status'], '/any/random/path');
457
- expect(match).not.toBeNull();
458
- });
459
- });
460
-
461
- // ── getAllRules ─────────────────────────────────────────────────
462
-
463
- describe('getAllRules', () => {
464
- test('returns default rules when no user rules exist', () => {
465
- const rules = getAllRules();
466
- expect(rules).toHaveLength(NUM_DEFAULTS);
467
- expect(rules.every((r) => r.id.startsWith('default:'))).toBe(true);
468
- });
469
-
470
- test('returns a copy (not the internal array)', () => {
471
- addRule('bash', 'git *', '/tmp');
472
- const rules1 = getAllRules();
473
- const rules2 = getAllRules();
474
- expect(rules1).toEqual(rules2);
475
- expect(rules1).not.toBe(rules2); // different references
476
- });
477
- });
478
-
479
- // ── clearCache ─────────────────────────────────────────────────
480
-
481
- describe('clearCache', () => {
482
- test('forces reload from disk on next access', () => {
483
- addRule('bash', 'git *', '/tmp');
484
- expect(getAllRules()).toHaveLength(1 + NUM_DEFAULTS);
485
- clearCache();
486
- // After clearing cache, rules are reloaded from disk
487
- expect(getAllRules()).toHaveLength(1 + NUM_DEFAULTS);
488
- });
489
- });
490
-
491
- // ── persistence ─────────────────────────────────────────────────
492
-
493
- describe('persistence', () => {
494
- test('rules survive cache clear (loaded from disk)', () => {
495
- const rule = addRule('bash', 'npm *', '/tmp');
496
- clearCache();
497
- const rules = getAllRules();
498
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
499
- expect(rules.find((r) => r.id === rule.id)).toBeDefined();
500
- });
501
-
502
- test('trust file has correct structure', () => {
503
- addRule('bash', 'git *', '/tmp');
504
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
505
- expect(data).toHaveProperty('version', 3);
506
- expect(data).toHaveProperty('rules');
507
- expect(Array.isArray(data.rules)).toBe(true);
508
- const userRule = data.rules.find((r: { pattern: string }) => r.pattern === 'git *');
509
- expect(userRule).toHaveProperty('priority', 100);
510
- });
511
- });
512
-
513
- // ── deny rules ─────────────────────────────────────────────────
514
-
515
- describe('deny rules', () => {
516
- test('addRule with deny decision creates a deny rule', () => {
517
- const rule = addRule('bash', 'rm -rf *', '/tmp', 'deny');
518
- expect(rule.decision).toBe('deny');
519
- expect(rule.tool).toBe('bash');
520
- expect(rule.pattern).toBe('rm -rf *');
521
- });
522
-
523
- test('deny rule persists to disk', () => {
524
- addRule('bash', 'rm *', '/tmp', 'deny');
525
- clearCache();
526
- const rules = getAllRules();
527
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
528
- const userRule = rules.find((r) => r.pattern === 'rm *');
529
- expect(userRule).toBeDefined();
530
- expect(userRule!.decision).toBe('deny');
531
- });
532
-
533
- test('findDenyRule finds deny rules', () => {
534
- addRule('bash', 'rm *', '/tmp', 'deny');
535
- const match = findDenyRule('bash', 'rm file.txt', '/tmp');
536
- expect(match).not.toBeNull();
537
- expect(match!.decision).toBe('deny');
538
- });
539
-
540
- test('findDenyRule ignores allow rules', () => {
541
- addRule('bash', 'rm *', '/tmp', 'allow');
542
- const match = findDenyRule('bash', 'rm file.txt', '/tmp');
543
- expect(match).toBeNull();
544
- });
545
-
546
- test('findMatchingRule ignores deny rules', () => {
547
- // Use host_bash — bash has a default allow rule that would match.
548
- addRule('host_bash', 'rm *', '/tmp', 'deny');
549
- const match = findMatchingRule('host_bash', 'rm file.txt', '/tmp');
550
- expect(match).toBeNull();
551
- });
552
-
553
- test('deny and allow rules coexist', () => {
554
- addRule('bash', 'git *', '/tmp', 'allow');
555
- addRule('bash', 'git push --force *', '/tmp', 'deny');
556
- expect(findMatchingRule('bash', 'git status', '/tmp')).not.toBeNull();
557
- expect(findDenyRule('bash', 'git push --force origin', '/tmp')).not.toBeNull();
558
- });
559
-
560
- test('deny rule with scope matching', () => {
561
- addRule('bash', 'rm *', '/home/user/project', 'deny');
562
- expect(findDenyRule('bash', 'rm file.txt', '/home/user/project/sub')).not.toBeNull();
563
- expect(findDenyRule('bash', 'rm file.txt', '/home/other')).toBeNull();
564
- });
565
-
566
- test('deny rule with everywhere scope', () => {
567
- addRule('bash', 'rm -rf *', 'everywhere', 'deny');
568
- expect(findDenyRule('bash', 'rm -rf /', '/any/path')).not.toBeNull();
569
- });
570
-
571
- test('removeRule works for deny rules', () => {
572
- const rule = addRule('bash', 'rm *', '/tmp', 'deny');
573
- expect(removeRule(rule.id)).toBe(true);
574
- expect(findDenyRule('bash', 'rm file.txt', '/tmp')).toBeNull();
575
- });
576
- });
577
-
578
- // ── v1 migration ───────────────────────────────────────────────
579
-
580
- describe('v1 migration', () => {
581
- test('v1 rules get priority 100 on load', () => {
582
- mkdirSync(dirname(trustPath), { recursive: true });
583
- writeFileSync(trustPath, JSON.stringify({
584
- version: 1,
585
- rules: [{
586
- id: 'test-v1-id',
587
- tool: 'bash',
588
- pattern: 'git *',
589
- scope: '/tmp',
590
- decision: 'allow',
591
- createdAt: 1000,
592
- }],
593
- }));
594
- clearCache();
595
- const rules = getAllRules();
596
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
597
- const migratedRule = rules.find((r) => r.id === 'test-v1-id');
598
- expect(migratedRule).toBeDefined();
599
- expect(migratedRule!.priority).toBe(100);
600
- });
601
-
602
- test('v1 file is upgraded to v3 on disk', () => {
603
- mkdirSync(dirname(trustPath), { recursive: true });
604
- writeFileSync(trustPath, JSON.stringify({
605
- version: 1,
606
- rules: [{
607
- id: 'migrate-me',
608
- tool: 'bash',
609
- pattern: 'npm *',
610
- scope: 'everywhere',
611
- decision: 'allow',
612
- createdAt: 2000,
613
- }],
614
- }));
615
- clearCache();
616
- getAllRules(); // triggers load + migration
617
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
618
- expect(data.version).toBe(3);
619
- const migratedRule = data.rules.find((r: { id: string }) => r.id === 'migrate-me');
620
- expect(migratedRule.priority).toBe(100);
621
- });
622
- });
623
-
624
- // ── loadFromDisk resilience ─────────────────────────────────────
625
-
626
- describe('loadFromDisk resilience', () => {
627
- test('returns in-memory rules when saveToDisk fails during migration', () => {
628
- // Write a v1 trust file that triggers needsSave on load
629
- mkdirSync(dirname(trustPath), { recursive: true });
630
- writeFileSync(trustPath, JSON.stringify({
631
- version: 1,
632
- rules: [{
633
- id: 'v1-readonly',
634
- tool: 'bash',
635
- pattern: 'git *',
636
- scope: '/tmp',
637
- decision: 'allow' as const,
638
- createdAt: 1000,
639
- }],
640
- }));
641
-
642
- // Spy on writeFileSync to throw when saveToDisk is called during migration.
643
- // This is deterministic regardless of user privileges (unlike chmod 0o555).
644
- const spy = spyOn(fs, 'writeFileSync').mockImplementation(() => {
645
- throw new Error('Simulated write failure');
646
- });
647
-
648
- try {
649
- clearCache();
650
- const rules = getAllRules();
651
- // Should still return the migrated rules + defaults in-memory
652
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
653
- const migratedRule = rules.find((r) => r.id === 'v1-readonly');
654
- expect(migratedRule).toBeDefined();
655
- expect(migratedRule!.priority).toBe(100);
656
- // Verify that saveToDisk was attempted (writeFileSync was called)
657
- expect(spy).toHaveBeenCalled();
658
- } finally {
659
- spy.mockRestore();
660
- }
661
- });
662
- });
663
-
664
- // ── default rules ─────────────────────────────────────────────
665
-
666
- describe('default rules', () => {
667
- test('backfills default rules on first load', () => {
668
- const rules = getAllRules();
669
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
670
- expect(defaults).toHaveLength(NUM_DEFAULTS);
671
- for (const rule of defaults) {
672
- expect(rule.priority).toBe(DEFAULT_PRIORITY_BY_ID.get(rule.id)!);
673
- if (rule.id === 'default:allow-bash-rm-bootstrap') {
674
- expect(rule.scope).toBe(join(testDir, 'workspace'));
675
- } else {
676
- expect(rule.scope).toBe('everywhere');
677
- }
678
- }
679
-
680
- });
681
-
682
- test('default rules cover file, host file, host shell, and workspace prompt tools', () => {
683
- const rules = getAllRules();
684
- const defaultTools = [...new Set(
685
- rules
686
- .filter((r) => r.id.startsWith('default:'))
687
- .map((r) => r.tool),
688
- )].sort();
689
- expect(defaultTools).toEqual([
690
- 'bash',
691
- 'browser_click',
692
- 'browser_close',
693
- 'browser_extract',
694
- 'browser_fill_credential',
695
- 'browser_navigate',
696
- 'browser_press_key',
697
- 'browser_screenshot',
698
- 'browser_snapshot',
699
- 'browser_type',
700
- 'browser_wait_for',
701
- 'computer_use_click',
702
- 'computer_use_double_click',
703
- 'computer_use_drag',
704
- 'computer_use_key',
705
- 'computer_use_open_app',
706
- 'computer_use_request_control',
707
- 'computer_use_right_click',
708
- 'computer_use_run_applescript',
709
- 'computer_use_scroll',
710
- 'computer_use_type_text',
711
- 'computer_use_wait',
712
- 'delete_managed_skill',
713
- 'file_edit',
714
- 'file_read',
715
- 'file_write',
716
- 'host_bash',
717
- 'host_file_edit',
718
- 'host_file_read',
719
- 'host_file_write',
720
- 'memory_search',
721
- 'scaffold_managed_skill',
722
- 'skill_load',
723
- 'ui_dismiss',
724
- 'ui_update',
725
- 'view_image',
726
- ]);
727
- });
728
-
729
- test('default rules are not duplicated on reload', () => {
730
- getAllRules(); // first load
731
- clearCache();
732
- const rules = getAllRules(); // second load
733
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
734
- expect(defaults).toHaveLength(NUM_DEFAULTS);
735
- });
736
-
737
- test('default rules persist to disk', () => {
738
- getAllRules(); // triggers backfill + save
739
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
740
- const defaults = data.rules.filter((r: { id: string }) => r.id.startsWith('default:'));
741
- expect(defaults).toHaveLength(NUM_DEFAULTS);
742
- });
743
-
744
- test('default rules are backfilled alongside v1 migration', () => {
745
- mkdirSync(dirname(trustPath), { recursive: true });
746
- writeFileSync(trustPath, JSON.stringify({
747
- version: 1,
748
- rules: [{
749
- id: 'v1-user-rule',
750
- tool: 'bash',
751
- pattern: 'git *',
752
- scope: '/tmp',
753
- decision: 'allow',
754
- createdAt: 1000,
755
- }],
756
- }));
757
- clearCache();
758
- const rules = getAllRules();
759
- expect(rules).toHaveLength(1 + NUM_DEFAULTS);
760
- expect(rules.find((r) => r.id === 'v1-user-rule')!.priority).toBe(100);
761
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
762
- expect(defaults).toHaveLength(NUM_DEFAULTS);
763
- expect(defaults.every((r) => r.priority === DEFAULT_PRIORITY_BY_ID.get(r.id))).toBe(true);
764
- });
765
-
766
- test('removed default rule is re-backfilled on next load', () => {
767
- // First load backfills defaults
768
- getAllRules();
769
- // Remove one default rule by editing trust.json directly on disk
770
- // (removeRule() throws for default rules, so we simulate external editing)
771
- const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
772
- raw.rules = raw.rules.filter((r: { id: string }) => r.id !== 'default:ask-host_file_read-global');
773
- writeFileSync(trustPath, JSON.stringify(raw, null, 2));
774
- // After reload, the rule is re-backfilled (defaults are always present)
775
- clearCache();
776
- const rules = getAllRules();
777
- expect(rules.find((r) => r.id === 'default:ask-host_file_read-global')).toBeDefined();
778
- });
779
-
780
- test('findHighestPriorityRule matches default ask for host_file_read', () => {
781
- const match = findHighestPriorityRule('host_file_read', ['host_file_read:/etc/hosts'], '/tmp');
782
- expect(match).not.toBeNull();
783
- expect(match!.id).toBe('default:ask-host_file_read-global');
784
- expect(match!.decision).toBe('ask');
785
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-host_file_read-global')!);
786
- });
787
-
788
- test('findHighestPriorityRule matches default ask for host_file_write', () => {
789
- const match = findHighestPriorityRule('host_file_write', ['host_file_write:/etc/hosts'], '/tmp');
790
- expect(match).not.toBeNull();
791
- expect(match!.id).toBe('default:ask-host_file_write-global');
792
- expect(match!.decision).toBe('ask');
793
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-host_file_write-global')!);
794
- });
795
-
796
- test('findHighestPriorityRule matches default ask for host_file_edit', () => {
797
- const match = findHighestPriorityRule('host_file_edit', ['host_file_edit:/etc/hosts'], '/tmp');
798
- expect(match).not.toBeNull();
799
- expect(match!.id).toBe('default:ask-host_file_edit-global');
800
- expect(match!.decision).toBe('ask');
801
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-host_file_edit-global')!);
802
- });
803
-
804
- test('findHighestPriorityRule matches default ask for host_bash', () => {
805
- const match = findHighestPriorityRule('host_bash', ['ls'], '/tmp');
806
- expect(match).not.toBeNull();
807
- expect(match!.id).toBe('default:ask-host_bash-global');
808
- expect(match!.decision).toBe('ask');
809
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-host_bash-global')!);
810
- });
811
-
812
- test('findHighestPriorityRule matches default ask for computer_use_click', () => {
813
- const match = findHighestPriorityRule('computer_use_click', ['computer_use_click:'], '/tmp');
814
- expect(match).not.toBeNull();
815
- expect(match!.id).toBe('default:ask-computer_use_click-global');
816
- expect(match!.decision).toBe('ask');
817
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-computer_use_click-global')!);
818
- });
819
-
820
- test('findHighestPriorityRule matches default ask for computer_use_request_control', () => {
821
- const match = findHighestPriorityRule('computer_use_request_control', ['computer_use_request_control:'], '/tmp');
822
- expect(match).not.toBeNull();
823
- expect(match!.id).toBe('default:ask-computer_use_request_control-global');
824
- expect(match!.decision).toBe('ask');
825
- expect(match!.priority).toBe(DEFAULT_PRIORITY_BY_ID.get('default:ask-computer_use_request_control-global')!);
826
- });
827
-
828
- test('bootstrap delete rule matches only when workingDir is the workspace dir', () => {
829
- const workspaceDir = join(testDir, 'workspace');
830
- // Should match when workingDir is the workspace directory — the bootstrap
831
- // rule (priority 100) outranks the global default allow (priority 50).
832
- const match = findHighestPriorityRule('bash', ['rm BOOTSTRAP.md'], workspaceDir);
833
- expect(match).not.toBeNull();
834
- expect(match!.id).toBe('default:allow-bash-rm-bootstrap');
835
- expect(match!.decision).toBe('allow');
836
- // Outside workspace, the bootstrap rule doesn't match — the global
837
- // default:allow-bash-global rule matches instead (not the bootstrap rule).
838
- const other = findHighestPriorityRule('bash', ['rm BOOTSTRAP.md'], '/tmp/other-project');
839
- expect(other).not.toBeNull();
840
- expect(other!.id).not.toBe('default:allow-bash-rm-bootstrap');
841
- expect(other!.id).toBe('default:allow-bash-global');
842
- });
843
-
844
- test('default ask does not affect files outside protected directory', () => {
845
- const safePath = join(testDir, 'data', 'assistant.db');
846
- const match = findHighestPriorityRule('file_read', [`file_read:${safePath}`], '/tmp');
847
- // Should not match a default deny rule
848
- expect(match === null || !match.id.startsWith('default:')).toBe(true);
849
- });
850
-
851
- test('default rules are backfilled after malformed JSON in trust file', () => {
852
- mkdirSync(dirname(trustPath), { recursive: true });
853
- writeFileSync(trustPath, 'NOT VALID JSON {{{');
854
- clearCache();
855
- const rules = getAllRules();
856
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
857
- expect(defaults).toHaveLength(NUM_DEFAULTS);
858
- });
859
-
860
- test('default rules are backfilled in-memory after unknown file version without overwriting disk', () => {
861
- mkdirSync(dirname(trustPath), { recursive: true });
862
- const originalContent = JSON.stringify({ version: 9999, rules: [{ id: 'future-rule', tool: 'bash', pattern: 'future *', scope: 'everywhere', decision: 'allow', priority: 50, createdAt: 1000 }] });
863
- writeFileSync(trustPath, originalContent);
864
- clearCache();
865
- const rules = getAllRules();
866
- // Defaults should be present in-memory
867
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
868
- expect(defaults).toHaveLength(NUM_DEFAULTS);
869
- // The on-disk file must NOT be overwritten — it preserves the unknown format
870
- const diskContent = readFileSync(trustPath, 'utf-8');
871
- expect(diskContent).toBe(originalContent);
872
- });
873
-
874
- test('clearAllRules preserves default rules', () => {
875
- addRule('bash', 'git *', '/tmp');
876
- clearAllRules();
877
- const rules = getAllRules();
878
- // User rules should be gone, but defaults should remain
879
- expect(rules.filter((r) => !r.id.startsWith('default:'))).toHaveLength(0);
880
- const defaults = rules.filter((r) => r.id.startsWith('default:'));
881
- expect(defaults).toHaveLength(NUM_DEFAULTS);
882
- });
883
-
884
- // ── skill source mutation rules ────────────────────────────────
885
-
886
- test('default rules include ask rules for file_write on skill source paths', () => {
887
- const rules = getAllRules();
888
- const managed = rules.find((r) => r.id === 'default:ask-file_write-managed-skills');
889
- expect(managed).toBeDefined();
890
- expect(managed!.tool).toBe('file_write');
891
- expect(managed!.decision).toBe('ask');
892
- expect(managed!.priority).toBe(50);
893
- expect(managed!.pattern).toContain('workspace/skills/**');
894
-
895
- const bundled = rules.find((r) => r.id === 'default:ask-file_write-bundled-skills');
896
- expect(bundled).toBeDefined();
897
- expect(bundled!.tool).toBe('file_write');
898
- expect(bundled!.decision).toBe('ask');
899
- expect(bundled!.priority).toBe(50);
900
- });
901
-
902
- test('default rules include ask rules for file_edit on skill source paths', () => {
903
- const rules = getAllRules();
904
- const managed = rules.find((r) => r.id === 'default:ask-file_edit-managed-skills');
905
- expect(managed).toBeDefined();
906
- expect(managed!.tool).toBe('file_edit');
907
- expect(managed!.decision).toBe('ask');
908
- expect(managed!.priority).toBe(50);
909
- expect(managed!.pattern).toContain('workspace/skills/**');
910
-
911
- const bundled = rules.find((r) => r.id === 'default:ask-file_edit-bundled-skills');
912
- expect(bundled).toBeDefined();
913
- expect(bundled!.tool).toBe('file_edit');
914
- expect(bundled!.decision).toBe('ask');
915
- expect(bundled!.priority).toBe(50);
916
- });
917
-
918
- // ── default allow: skill_load ────────────────────────────────
919
-
920
- test('skill_load default allow rule exists in templates', () => {
921
- const templates = getDefaultRuleTemplates();
922
- const skillLoadRule = templates.find(t => t.id === 'default:allow-skill_load-global');
923
- expect(skillLoadRule).toBeDefined();
924
- expect(skillLoadRule!.tool).toBe('skill_load');
925
- expect(skillLoadRule!.pattern).toBe('skill_load:*');
926
- expect(skillLoadRule!.decision).toBe('allow');
927
- expect(skillLoadRule!.scope).toBe('everywhere');
928
- });
929
-
930
- test('findHighestPriorityRule matches default allow for skill_load', () => {
931
- const match = findHighestPriorityRule('skill_load', ['skill_load:browser'], '/tmp');
932
- expect(match).not.toBeNull();
933
- expect(match!.id).toBe('default:allow-skill_load-global');
934
- expect(match!.decision).toBe('allow');
935
- expect(match!.priority).toBe(100);
936
- });
937
-
938
- test('findHighestPriorityRule matches default allow for skill_load with any skill name', () => {
939
- const match = findHighestPriorityRule('skill_load', ['skill_load:some-random-skill'], '/tmp');
940
- expect(match).not.toBeNull();
941
- expect(match!.id).toBe('default:allow-skill_load-global');
942
- expect(match!.decision).toBe('allow');
943
- });
944
-
945
- // ── default allow: browser tools ────────────────────────────
946
-
947
- test('all 10 browser tools have default allow rules', () => {
948
- const templates = getDefaultRuleTemplates();
949
- const browserTools = [
950
- 'browser_navigate', 'browser_snapshot', 'browser_screenshot', 'browser_close',
951
- 'browser_click', 'browser_type', 'browser_press_key', 'browser_wait_for',
952
- 'browser_extract', 'browser_fill_credential',
953
- ];
954
-
955
- for (const tool of browserTools) {
956
- const rule = templates.find(t => t.id === `default:allow-${tool}-global`);
957
- expect(rule).toBeDefined();
958
- expect(rule!.tool).toBe(tool);
959
- // browser_navigate uses standalone "**" because its candidates
960
- // contain URLs with "/" that single "*" cannot match.
961
- const expectedPattern = tool === 'browser_navigate' ? '**' : `${tool}:*`;
962
- expect(rule!.pattern).toBe(expectedPattern);
963
- expect(rule!.decision).toBe('allow');
964
- expect(rule!.scope).toBe('everywhere');
965
- }
966
- });
967
-
968
- test('browser tool default rules match via findHighestPriorityRule', () => {
969
- // Use a candidate without slashes so the `browser_snapshot:*` pattern
970
- // matches (minimatch `*` does not cross `/` boundaries).
971
- const result = findHighestPriorityRule('browser_snapshot', ['browser_snapshot:'], '/tmp');
972
- expect(result).toBeDefined();
973
- expect(result!.decision).toBe('allow');
974
- });
975
-
976
- test('no default ask rules exist for file_read on skill source paths', () => {
977
- const rules = getAllRules();
978
- // There should be no default rules with IDs matching file_read for skill sources
979
- const readManagedSkill = rules.find((r) => r.id === 'default:ask-file_read-managed-skills');
980
- const readBundledSkill = rules.find((r) => r.id === 'default:ask-file_read-bundled-skills');
981
- expect(readManagedSkill).toBeUndefined();
982
- expect(readBundledSkill).toBeUndefined();
983
- });
984
-
985
- test('findHighestPriorityRule matches default ask for file_write on managed skill path', () => {
986
- const skillFile = join(testDir, 'workspace', 'skills', 'my-skill', 'SKILL.md');
987
- const match = findHighestPriorityRule('file_write', [`file_write:${skillFile}`], '/tmp');
988
- expect(match).not.toBeNull();
989
- expect(match!.id).toBe('default:ask-file_write-managed-skills');
990
- expect(match!.decision).toBe('ask');
991
- });
992
-
993
- test('findHighestPriorityRule matches default ask for file_edit on managed skill path', () => {
994
- const skillFile = join(testDir, 'workspace', 'skills', 'my-skill', 'tools.ts');
995
- const match = findHighestPriorityRule('file_edit', [`file_edit:${skillFile}`], '/tmp');
996
- expect(match).not.toBeNull();
997
- expect(match!.id).toBe('default:ask-file_edit-managed-skills');
998
- expect(match!.decision).toBe('ask');
999
- });
1000
- });
1001
-
1002
- // ── trust rule schema v3 (PR 14) ──────────────────────────────
1003
-
1004
- describe('trust rule schema v3 (PR 14)', () => {
1005
- test('new rules can include v3 optional fields', () => {
1006
- const rule = addRule('bash', 'git *', '/tmp');
1007
- // Manually set v3 optional fields on the rule and persist
1008
- rule.executionTarget = '/usr/local/bin/node';
1009
- rule.allowHighRisk = true;
1010
- // Re-persist the updated rules
1011
- const rules = getAllRules().map((r) =>
1012
- r.id === rule.id ? rule : r,
1013
- );
1014
- // Write directly to verify round-trip
1015
- const trustData = { version: 3, rules };
1016
- writeFileSync(trustPath, JSON.stringify(trustData, null, 2));
1017
- clearCache();
1018
- const reloaded = getAllRules();
1019
- const found = reloaded.find((r) => r.id === rule.id);
1020
- expect(found).toBeDefined();
1021
- expect(found!.executionTarget).toBe('/usr/local/bin/node');
1022
- expect(found!.allowHighRisk).toBe(true);
1023
- });
1024
-
1025
- test('v2 file is upgraded to v3 on disk', () => {
1026
- mkdirSync(dirname(trustPath), { recursive: true });
1027
- writeFileSync(trustPath, JSON.stringify({
1028
- version: 2,
1029
- rules: [{
1030
- id: 'v2-rule',
1031
- tool: 'bash',
1032
- pattern: 'npm *',
1033
- scope: 'everywhere',
1034
- decision: 'allow',
1035
- priority: 100,
1036
- createdAt: 3000,
1037
- }],
1038
- }));
1039
- clearCache();
1040
- getAllRules(); // triggers load + migration
1041
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1042
- expect(data.version).toBe(3);
1043
- });
1044
-
1045
- test('v2 rules survive v3 migration with no v3-only fields', () => {
1046
- mkdirSync(dirname(trustPath), { recursive: true });
1047
- writeFileSync(trustPath, JSON.stringify({
1048
- version: 2,
1049
- rules: [
1050
- {
1051
- id: 'user-v2-a',
1052
- tool: 'bash',
1053
- pattern: 'git *',
1054
- scope: '/tmp',
1055
- decision: 'allow',
1056
- priority: 100,
1057
- createdAt: 4000,
1058
- },
1059
- {
1060
- id: 'user-v2-b',
1061
- tool: 'file_write',
1062
- pattern: '/tmp/*',
1063
- scope: '/tmp',
1064
- decision: 'deny',
1065
- priority: 50,
1066
- createdAt: 4001,
1067
- },
1068
- ],
1069
- }));
1070
- clearCache();
1071
- const rules = getAllRules();
1072
- const ruleA = rules.find((r) => r.id === 'user-v2-a');
1073
- const ruleB = rules.find((r) => r.id === 'user-v2-b');
1074
- expect(ruleA).toBeDefined();
1075
- expect(ruleB).toBeDefined();
1076
- expect(ruleA!.pattern).toBe('git *');
1077
- expect(ruleB!.decision).toBe('deny');
1078
- // No v3-only fields should be present
1079
- expect(ruleA).not.toHaveProperty('executionTarget');
1080
- expect(ruleA).not.toHaveProperty('allowHighRisk');
1081
- });
1082
-
1083
- test('trust file persists with version 3', () => {
1084
- addRule('bash', 'echo *', '/tmp');
1085
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1086
- expect(data.version).toBe(3);
1087
- });
1088
- });
1089
-
1090
- // ── v2 → v3 migration hardening (PR 15) ────────────────────────
1091
-
1092
- describe('v2 → v3 migration hardening (PR 15)', () => {
1093
- test('v2 rules with extra unknown fields survive migration cleanly', () => {
1094
- mkdirSync(dirname(trustPath), { recursive: true });
1095
- writeFileSync(trustPath, JSON.stringify({
1096
- version: 2,
1097
- rules: [{
1098
- id: 'v2-extra-fields',
1099
- tool: 'bash',
1100
- pattern: 'git *',
1101
- scope: '/tmp',
1102
- decision: 'allow',
1103
- priority: 100,
1104
- createdAt: 5000,
1105
- customField: 'should-survive',
1106
- nested: { deep: true },
1107
- }],
1108
- }));
1109
- clearCache();
1110
- const rules = getAllRules();
1111
- const rule = rules.find((r) => r.id === 'v2-extra-fields');
1112
- expect(rule).toBeDefined();
1113
- expect(rule!.tool).toBe('bash');
1114
- expect(rule!.pattern).toBe('git *');
1115
- // Extra fields pass through because the migration does not strip them
1116
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- asserting extra fields pass through migration
1117
- expect((rule as any).customField).toBe('should-survive');
1118
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- asserting extra fields pass through migration
1119
- expect((rule as any).nested).toEqual({ deep: true });
1120
- });
1121
-
1122
- test('v2 file with empty rules array migrates correctly', () => {
1123
- mkdirSync(dirname(trustPath), { recursive: true });
1124
- writeFileSync(trustPath, JSON.stringify({
1125
- version: 2,
1126
- rules: [],
1127
- }));
1128
- clearCache();
1129
- const rules = getAllRules();
1130
- // Should only have default rules, no user rules
1131
- expect(rules).toHaveLength(NUM_DEFAULTS);
1132
- expect(rules.every((r) => r.id.startsWith('default:'))).toBe(true);
1133
- // File should be upgraded to v3 on disk
1134
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1135
- expect(data.version).toBe(3);
1136
- });
1137
-
1138
- test('v2 file with no rules field at all migrates correctly', () => {
1139
- mkdirSync(dirname(trustPath), { recursive: true });
1140
- writeFileSync(trustPath, JSON.stringify({
1141
- version: 2,
1142
- }));
1143
- clearCache();
1144
- const rules = getAllRules();
1145
- // rules defaults to [] so only defaults should appear
1146
- expect(rules).toHaveLength(NUM_DEFAULTS);
1147
- expect(rules.every((r) => r.id.startsWith('default:'))).toBe(true);
1148
- // File should be upgraded to v3 on disk
1149
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1150
- expect(data.version).toBe(3);
1151
- });
1152
-
1153
- test('malformed v2 file (rules is a string instead of array) is handled gracefully', () => {
1154
- mkdirSync(dirname(trustPath), { recursive: true });
1155
- writeFileSync(trustPath, JSON.stringify({
1156
- version: 2,
1157
- rules: 'not-an-array',
1158
- }));
1159
- clearCache();
1160
- const rules = getAllRules();
1161
- // Should fall back to empty rules and backfill defaults
1162
- expect(rules).toHaveLength(NUM_DEFAULTS);
1163
- expect(rules.every((r) => r.id.startsWith('default:'))).toBe(true);
1164
- });
1165
-
1166
- test('malformed v2 file (rules is an object instead of array) is handled gracefully', () => {
1167
- mkdirSync(dirname(trustPath), { recursive: true });
1168
- writeFileSync(trustPath, JSON.stringify({
1169
- version: 2,
1170
- rules: { notAnArray: true },
1171
- }));
1172
- clearCache();
1173
- const rules = getAllRules();
1174
- expect(rules).toHaveLength(NUM_DEFAULTS);
1175
- expect(rules.every((r) => r.id.startsWith('default:'))).toBe(true);
1176
- });
1177
-
1178
- test('malformed file (valid JSON but null) is handled gracefully', () => {
1179
- mkdirSync(dirname(trustPath), { recursive: true });
1180
- writeFileSync(trustPath, 'null');
1181
- clearCache();
1182
- const rules = getAllRules();
1183
- // Accessing null.version throws TypeError, caught by try/catch,
1184
- // falls through to backfill defaults
1185
- expect(rules).toHaveLength(NUM_DEFAULTS);
1186
- });
1187
-
1188
- test('concurrent v2 → v3 migration (loading twice in sequence) is idempotent', () => {
1189
- mkdirSync(dirname(trustPath), { recursive: true });
1190
- writeFileSync(trustPath, JSON.stringify({
1191
- version: 2,
1192
- rules: [{
1193
- id: 'idempotent-rule',
1194
- tool: 'bash',
1195
- pattern: 'npm *',
1196
- scope: 'everywhere',
1197
- decision: 'allow',
1198
- priority: 100,
1199
- createdAt: 6000,
1200
- }],
1201
- }));
1202
- // First load — triggers v2 → v3 migration
1203
- clearCache();
1204
- const rules1 = getAllRules();
1205
- const rule1 = rules1.find((r) => r.id === 'idempotent-rule');
1206
- expect(rule1).toBeDefined();
1207
- expect(rule1!.pattern).toBe('npm *');
1208
-
1209
- // Second load — should load the already-migrated v3 file without re-migrating
1210
- clearCache();
1211
- const rules2 = getAllRules();
1212
- const rule2 = rules2.find((r) => r.id === 'idempotent-rule');
1213
- expect(rule2).toBeDefined();
1214
- expect(rule2!.pattern).toBe('npm *');
1215
- expect(rule2!.priority).toBe(100);
1216
-
1217
- // Verify file is still v3 and rule count is stable
1218
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1219
- expect(data.version).toBe(3);
1220
- const userRules = data.rules.filter((r: { id: string }) => !r.id.startsWith('default:'));
1221
- expect(userRules).toHaveLength(1);
1222
- });
1223
-
1224
- test('v3 file with optional fields is loaded correctly without re-migration', () => {
1225
- mkdirSync(dirname(trustPath), { recursive: true });
1226
- const v3Rules = [
1227
- {
1228
- id: 'v3-with-options',
1229
- tool: 'bash',
1230
- pattern: 'skill-cmd *',
1231
- scope: '/tmp',
1232
- decision: 'allow',
1233
- priority: 100,
1234
- createdAt: 7000,
1235
- executionTarget: '/usr/bin/node',
1236
- allowHighRisk: false,
1237
- },
1238
- {
1239
- id: 'v3-without-options',
1240
- tool: 'bash',
1241
- pattern: 'git *',
1242
- scope: '/tmp',
1243
- decision: 'allow',
1244
- priority: 100,
1245
- createdAt: 7001,
1246
- },
1247
- ];
1248
- writeFileSync(trustPath, JSON.stringify({ version: 3, rules: v3Rules }));
1249
- clearCache();
1250
- const rules = getAllRules();
1251
-
1252
- // Rule with optional fields should have them preserved
1253
- const withOptions = rules.find((r) => r.id === 'v3-with-options');
1254
- expect(withOptions).toBeDefined();
1255
- expect(withOptions!.executionTarget).toBe('/usr/bin/node');
1256
- expect(withOptions!.allowHighRisk).toBe(false);
1257
-
1258
- // Rule without optional fields should remain without them
1259
- const withoutOptions = rules.find((r) => r.id === 'v3-without-options');
1260
- expect(withoutOptions).toBeDefined();
1261
- expect(withoutOptions).not.toHaveProperty('executionTarget');
1262
- });
1263
-
1264
- test('v2 migration preserves rule meaning exactly — no extra fields added', () => {
1265
- mkdirSync(dirname(trustPath), { recursive: true });
1266
- const originalRules = [
1267
- {
1268
- id: 'preserve-a',
1269
- tool: 'bash',
1270
- pattern: 'git *',
1271
- scope: '/home/user',
1272
- decision: 'allow' as const,
1273
- priority: 100,
1274
- createdAt: 8000,
1275
- },
1276
- {
1277
- id: 'preserve-b',
1278
- tool: 'file_write',
1279
- pattern: '/tmp/**',
1280
- scope: 'everywhere',
1281
- decision: 'deny' as const,
1282
- priority: 50,
1283
- createdAt: 8001,
1284
- },
1285
- ];
1286
- writeFileSync(trustPath, JSON.stringify({ version: 2, rules: originalRules }));
1287
- clearCache();
1288
- const rules = getAllRules();
1289
-
1290
- for (const original of originalRules) {
1291
- const migrated = rules.find((r) => r.id === original.id);
1292
- expect(migrated).toBeDefined();
1293
- // Every original field is preserved exactly
1294
- expect(migrated!.tool).toBe(original.tool);
1295
- expect(migrated!.pattern).toBe(original.pattern);
1296
- expect(migrated!.scope).toBe(original.scope);
1297
- expect(migrated!.decision).toBe(original.decision);
1298
- expect(migrated!.priority).toBe(original.priority);
1299
- expect(migrated!.createdAt).toBe(original.createdAt);
1300
- // No extra fields were injected by migration
1301
- expect(migrated).not.toHaveProperty('executionTarget');
1302
- expect(migrated).not.toHaveProperty('allowHighRisk');
1303
- }
1304
- });
1305
-
1306
- test('v1 → v3 full migration preserves rules and adds priority', () => {
1307
- mkdirSync(dirname(trustPath), { recursive: true });
1308
- writeFileSync(trustPath, JSON.stringify({
1309
- version: 1,
1310
- rules: [{
1311
- id: 'v1-full-migration',
1312
- tool: 'bash',
1313
- pattern: 'docker *',
1314
- scope: '/srv',
1315
- decision: 'allow',
1316
- createdAt: 9000,
1317
- }],
1318
- }));
1319
- clearCache();
1320
- const rules = getAllRules();
1321
- const rule = rules.find((r) => r.id === 'v1-full-migration');
1322
- expect(rule).toBeDefined();
1323
- // v1 → v2 adds priority 100
1324
- expect(rule!.priority).toBe(100);
1325
- // File should be v3 on disk
1326
- const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
1327
- expect(data.version).toBe(3);
1328
- });
1329
- });
1330
-
1331
- // ── executionTarget-aware rule matching ──────────────────────
1332
-
1333
- describe('executionTarget-aware rule matching', () => {
1334
- /**
1335
- * Helper: write a v3 trust file with the given rules directly to disk,
1336
- * then clear the cache so the next getRules() call picks them up.
1337
- */
1338
- function seedRules(rules: Array<Record<string, unknown>>): void {
1339
- mkdirSync(dirname(trustPath), { recursive: true });
1340
- writeFileSync(trustPath, JSON.stringify({ version: 3, rules }));
1341
- clearCache();
1342
- }
1343
-
1344
- // ── wildcard semantics (no executionTarget on rule) ──────────
1345
-
1346
- describe('wildcard semantics — rules without executionTarget', () => {
1347
- test('rule with no executionTarget matches when no context is provided', () => {
1348
- addRule('bash', 'git *', '/tmp', 'allow', 200);
1349
- const match = findHighestPriorityRule('bash', ['git status'], '/tmp');
1350
- expect(match).not.toBeNull();
1351
- expect(match!.decision).toBe('allow');
1352
- });
1353
-
1354
- test('rule with no executionTarget matches any execution target', () => {
1355
- addRule('bash', 'git *', '/tmp', 'allow', 200);
1356
- const match = findHighestPriorityRule('bash', ['git status'], '/tmp', {
1357
- executionTarget: '/usr/bin/node',
1358
- });
1359
- expect(match).not.toBeNull();
1360
- expect(match!.decision).toBe('allow');
1361
- });
1362
- });
1363
-
1364
- // ── executionTarget matching ──────────────────────────────────
1365
-
1366
- describe('executionTarget matching', () => {
1367
- test('rule with executionTarget matches exact target', () => {
1368
- seedRules([{
1369
- id: 'et-exact',
1370
- tool: 'bash',
1371
- pattern: 'run *',
1372
- scope: 'everywhere',
1373
- decision: 'allow',
1374
- priority: 200,
1375
- createdAt: Date.now(),
1376
- executionTarget: '/usr/local/bin/node',
1377
- }]);
1378
- const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {
1379
- executionTarget: '/usr/local/bin/node',
1380
- });
1381
- expect(match).not.toBeNull();
1382
- expect(match!.id).toBe('et-exact');
1383
- });
1384
-
1385
- test('rule with executionTarget does NOT match different target', () => {
1386
- seedRules([{
1387
- id: 'et-diff',
1388
- tool: 'bash',
1389
- pattern: 'run *',
1390
- scope: 'everywhere',
1391
- decision: 'allow',
1392
- priority: 200,
1393
- createdAt: Date.now(),
1394
- executionTarget: '/usr/local/bin/node',
1395
- }]);
1396
- const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {
1397
- executionTarget: '/usr/local/bin/bun',
1398
- });
1399
- expect(match === null || match.id !== 'et-diff').toBe(true);
1400
- });
1401
-
1402
- test('rule with executionTarget does NOT match when no target in context', () => {
1403
- seedRules([{
1404
- id: 'et-no-ctx',
1405
- tool: 'bash',
1406
- pattern: 'run *',
1407
- scope: 'everywhere',
1408
- decision: 'allow',
1409
- priority: 200,
1410
- createdAt: Date.now(),
1411
- executionTarget: '/usr/local/bin/node',
1412
- }]);
1413
- const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {});
1414
- expect(match === null || match.id !== 'et-no-ctx').toBe(true);
1415
- });
1416
-
1417
- test('rule WITHOUT executionTarget matches any target (wildcard)', () => {
1418
- addRule('bash', 'run *', '/tmp', 'allow', 200);
1419
- const match = findHighestPriorityRule('bash', ['run script.js'], '/tmp', {
1420
- executionTarget: '/any/path/to/runtime',
1421
- });
1422
- expect(match).not.toBeNull();
1423
- expect(match!.pattern).toBe('run *');
1424
- });
1425
- });
1426
-
1427
- // ── backward compatibility ────────────────────────────────────
1428
-
1429
- describe('backward compatibility', () => {
1430
- test('existing callers without ctx parameter still work', () => {
1431
- addRule('bash', 'git *', '/tmp', 'allow', 200);
1432
- // Calling without the 4th argument — must still match
1433
- const match = findHighestPriorityRule('bash', ['git status'], '/tmp');
1434
- expect(match).not.toBeNull();
1435
- expect(match!.pattern).toBe('git *');
1436
- });
1437
-
1438
- test('empty PolicyContext object behaves the same as no context', () => {
1439
- addRule('bash', 'ls *', '/tmp', 'allow', 200);
1440
- const matchNoCtx = findHighestPriorityRule('bash', ['ls -la'], '/tmp');
1441
- const matchEmptyCtx = findHighestPriorityRule('bash', ['ls -la'], '/tmp', {});
1442
- expect(matchNoCtx).not.toBeNull();
1443
- expect(matchEmptyCtx).not.toBeNull();
1444
- expect(matchNoCtx!.id).toBe(matchEmptyCtx!.id);
1445
- });
1446
- });
1447
- });
1448
-
1449
- // ── network_request trust rule matching ────────────────────────
1450
-
1451
- describe('network_request trust rules', () => {
1452
- test('exact origin rule matches network_request candidates', () => {
1453
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere');
1454
- const rule = findHighestPriorityRule(
1455
- 'network_request',
1456
- ['network_request:https://api.example.com/v1/data', 'network_request:https://api.example.com/*'],
1457
- '/tmp',
1458
- );
1459
- expect(rule).not.toBeNull();
1460
- expect(rule!.decision).toBe('allow');
1461
- });
1462
-
1463
- test('exact url rule matches only that url candidate', () => {
1464
- addRule('network_request', 'network_request:https://api.example.com/v1/data', 'everywhere');
1465
- const match = findHighestPriorityRule(
1466
- 'network_request',
1467
- ['network_request:https://api.example.com/v1/data', 'network_request:https://api.example.com/*'],
1468
- '/tmp',
1469
- );
1470
- expect(match).not.toBeNull();
1471
-
1472
- const noMatch = findHighestPriorityRule(
1473
- 'network_request',
1474
- ['network_request:https://api.example.com/v2/other'],
1475
- '/tmp',
1476
- );
1477
- expect(noMatch).toBeNull();
1478
- });
1479
-
1480
- test('globstar rule matches any network_request candidate', () => {
1481
- // minimatch treats standalone "**" as globstar (matching "/"), but
1482
- // "network_request:*" uses single "*" which doesn't cross slashes.
1483
- // The tool field is already filtered by findHighestPriorityRule, so
1484
- // "**" is the correct catch-all pattern.
1485
- addRule('network_request', '**', 'everywhere');
1486
- const rule = findHighestPriorityRule(
1487
- 'network_request',
1488
- ['network_request:https://any-host.example.org/path'],
1489
- '/tmp',
1490
- );
1491
- expect(rule).not.toBeNull();
1492
- });
1493
-
1494
- test('single-star wildcard matches flat candidates only', () => {
1495
- // "network_request:*" won't match URLs with slashes — consistent
1496
- // with the behavior of web_fetch:* and browser_navigate:* patterns.
1497
- addRule('network_request', 'network_request:*', 'everywhere');
1498
- const noSlashMatch = findHighestPriorityRule(
1499
- 'network_request',
1500
- ['network_request:flat-target'],
1501
- '/tmp',
1502
- );
1503
- expect(noSlashMatch).not.toBeNull();
1504
-
1505
- const slashNoMatch = findHighestPriorityRule(
1506
- 'network_request',
1507
- ['network_request:https://example.com/path'],
1508
- '/tmp',
1509
- );
1510
- // Single "*" does not match "/" so this URL candidate won't match.
1511
- expect(slashNoMatch).toBeNull();
1512
- });
1513
-
1514
- test('network_request rule does not match web_fetch tool', () => {
1515
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere');
1516
- const rule = findHighestPriorityRule(
1517
- 'web_fetch',
1518
- ['web_fetch:https://api.example.com/v1/data', 'web_fetch:https://api.example.com/*'],
1519
- '/tmp',
1520
- );
1521
- expect(rule).toBeNull();
1522
- });
1523
-
1524
- test('web_fetch rule does not match network_request tool', () => {
1525
- addRule('web_fetch', 'web_fetch:https://api.example.com/*', 'everywhere');
1526
- const rule = findHighestPriorityRule(
1527
- 'network_request',
1528
- ['network_request:https://api.example.com/v1/data', 'network_request:https://api.example.com/*'],
1529
- '/tmp',
1530
- );
1531
- expect(rule).toBeNull();
1532
- });
1533
-
1534
- test('deny rule takes precedence over allow at same priority', () => {
1535
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere', 'allow', 100);
1536
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere', 'deny', 100);
1537
- const rule = findHighestPriorityRule(
1538
- 'network_request',
1539
- ['network_request:https://api.example.com/v1/data', 'network_request:https://api.example.com/*'],
1540
- '/tmp',
1541
- );
1542
- expect(rule).not.toBeNull();
1543
- expect(rule!.decision).toBe('deny');
1544
- });
1545
-
1546
- test('higher-priority allow overrides lower-priority deny', () => {
1547
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere', 'deny', 50);
1548
- addRule('network_request', 'network_request:https://api.example.com/*', 'everywhere', 'allow', 100);
1549
- const rule = findHighestPriorityRule(
1550
- 'network_request',
1551
- ['network_request:https://api.example.com/v1/data', 'network_request:https://api.example.com/*'],
1552
- '/tmp',
1553
- );
1554
- expect(rule).not.toBeNull();
1555
- expect(rule!.decision).toBe('allow');
1556
- });
1557
-
1558
- test('scope restricts network_request rule matching', () => {
1559
- addRule('network_request', 'network_request:https://api.example.com/*', '/home/user/project');
1560
- const inScope = findHighestPriorityRule(
1561
- 'network_request',
1562
- ['network_request:https://api.example.com/*'],
1563
- '/home/user/project',
1564
- );
1565
- expect(inScope).not.toBeNull();
1566
-
1567
- const outOfScope = findHighestPriorityRule(
1568
- 'network_request',
1569
- ['network_request:https://api.example.com/*'],
1570
- '/tmp/other',
1571
- );
1572
- expect(outOfScope).toBeNull();
1573
- });
1574
- });
1575
- });
1576
-
1577
- describe('computer-use tool trust rule matching', () => {
1578
- test('actionable CU tools have default ask trust rules', () => {
1579
- // Actionable CU tools (those that perform screen interactions) should
1580
- // have default "ask" rules so strict mode prompts before use.
1581
- const actionableCuTools = [
1582
- 'computer_use_click',
1583
- 'computer_use_type_text',
1584
- 'computer_use_request_control',
1585
- ];
1586
-
1587
- for (const name of actionableCuTools) {
1588
- const rule = findHighestPriorityRule(name, [name], '/tmp/test');
1589
- expect(rule).not.toBeNull();
1590
- expect(rule!.decision).toBe('ask');
1591
- }
1592
- });
1593
-
1594
- test('terminal CU tools (done/respond) have no default trust rules', () => {
1595
- // computer_use_done and computer_use_respond are terminal signal tools
1596
- // with RiskLevel.Low — they should not have ask rules since they don't
1597
- // perform any screen action.
1598
- const terminalCuTools = ['computer_use_done', 'computer_use_respond'];
1599
-
1600
- for (const name of terminalCuTools) {
1601
- const defaultRule = DEFAULT_TEMPLATES.find((t) => t.tool === name);
1602
- expect(defaultRule).toBeUndefined();
1603
- }
1604
- });
1605
- });