clawmini 0.0.7 → 0.0.9

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 (367) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/CHANGELOG.md +36 -0
  5. package/README.md +5 -4
  6. package/dist/adapter-discord/index.d.mts.map +1 -1
  7. package/dist/adapter-discord/index.mjs +465 -282
  8. package/dist/adapter-discord/index.mjs.map +1 -1
  9. package/dist/adapter-google-chat/index.mjs +367 -243
  10. package/dist/adapter-google-chat/index.mjs.map +1 -1
  11. package/dist/cli/index.mjs +684 -24
  12. package/dist/cli/index.mjs.map +1 -1
  13. package/dist/cli/lite.mjs +43 -13
  14. package/dist/cli/lite.mjs.map +1 -1
  15. package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
  16. package/dist/cli/manage-policies.mjs.map +1 -0
  17. package/dist/cli/run-host.d.mts +1 -0
  18. package/dist/cli/run-host.mjs +3090 -0
  19. package/dist/cli/run-host.mjs.map +1 -0
  20. package/dist/config-CPFQIGdG.mjs +57 -0
  21. package/dist/config-CPFQIGdG.mjs.map +1 -0
  22. package/dist/config-Dvl-Pov4.mjs +76 -0
  23. package/dist/config-Dvl-Pov4.mjs.map +1 -0
  24. package/dist/daemon/index.d.mts.map +1 -1
  25. package/dist/daemon/index.mjs +970 -332
  26. package/dist/daemon/index.mjs.map +1 -1
  27. package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
  28. package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
  29. package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
  30. package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
  31. package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  32. package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
  33. package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
  34. package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  35. package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
  36. package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
  37. package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
  38. package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
  39. package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  40. package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
  41. package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
  42. package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  43. package/dist/web/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
  44. package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  45. package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  46. package/dist/web/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
  47. package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  48. package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  49. package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.Dr0ot9sV.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  50. package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  51. package/dist/web/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
  52. package/dist/web/_app/version.json +1 -1
  53. package/dist/web/index.html +12 -12
  54. package/dist/workspace-oWmVh5mi.mjs +1001 -0
  55. package/dist/workspace-oWmVh5mi.mjs.map +1 -0
  56. package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
  57. package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
  58. package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
  59. package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
  60. package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
  61. package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
  62. package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
  63. package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
  64. package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
  65. package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
  66. package/docs/25_e2e_test_improvements/development_log.md +30 -0
  67. package/docs/25_e2e_test_improvements/notes.md +29 -0
  68. package/docs/25_e2e_test_improvements/prd.md +43 -0
  69. package/docs/25_e2e_test_improvements/questions.md +12 -0
  70. package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
  71. package/docs/25_e2e_test_improvements/tickets.md +22 -0
  72. package/docs/25_policy_cwd/development_log.md +30 -0
  73. package/docs/25_policy_cwd/notes.md +28 -0
  74. package/docs/25_policy_cwd/prd.md +77 -0
  75. package/docs/25_policy_cwd/questions.md +6 -0
  76. package/docs/25_policy_cwd/tickets.md +77 -0
  77. package/docs/CLI_REFERENCE.md +3 -1
  78. package/docs/PHILOSOPHY.md +35 -0
  79. package/docs/adapter-visibility/SPEC.md +461 -0
  80. package/docs/adapter-visibility/SPEC_v2.md +202 -0
  81. package/docs/auto-update/SPEC.md +344 -0
  82. package/docs/backups/SPEC.md +296 -0
  83. package/docs/backups/clawmini.gitignore +69 -0
  84. package/docs/guides/assets/clawmini-avatar.png +0 -0
  85. package/docs/guides/backups.md +332 -0
  86. package/docs/guides/discord_adapter_setup.md +1 -1
  87. package/docs/guides/google_chat_adapter_setup.md +81 -0
  88. package/docs/unified-startup/SPEC.md +203 -0
  89. package/e2e/_helpers/test-environment.test.ts +49 -0
  90. package/e2e/_helpers/test-environment.ts +548 -0
  91. package/e2e/adapters/_google-chat-fixtures.ts +340 -0
  92. package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
  93. package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
  94. package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
  95. package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
  96. package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
  97. package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
  98. package/e2e/agents/custom-api-env.test.ts +80 -0
  99. package/e2e/agents/export-lite-func.test.ts +104 -0
  100. package/e2e/agents/fallbacks.test.ts +124 -0
  101. package/e2e/agents/interrupt.test.ts +50 -0
  102. package/e2e/agents/no-reply-necessary.test.ts +57 -0
  103. package/e2e/agents/session-timeout-subagents.test.ts +76 -0
  104. package/e2e/agents/subagent-authorization.test.ts +246 -0
  105. package/e2e/agents/subagent-env.test.ts +49 -0
  106. package/e2e/agents/subagent-lifecycle.test.ts +782 -0
  107. package/e2e/agents/subagents-depth.test.ts +47 -0
  108. package/e2e/cli/agents.test.ts +176 -0
  109. package/e2e/cli/auto-update.test.ts +741 -0
  110. package/e2e/cli/basic.test.ts +44 -0
  111. package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
  112. package/e2e/cli/init-gitignore.test.ts +86 -0
  113. package/e2e/cli/init.test.ts +76 -0
  114. package/e2e/cli/messages.test.ts +363 -0
  115. package/e2e/cli/serve.test.ts +76 -0
  116. package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
  117. package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
  118. package/e2e/jobs/agent-jobs.test.ts +216 -0
  119. package/e2e/jobs/cron.test.ts +64 -0
  120. package/e2e/jobs/restart.test.ts +108 -0
  121. package/e2e/policies/approval-session.test.ts +69 -0
  122. package/e2e/policies/auto-create-policies-file.test.ts +35 -0
  123. package/e2e/policies/builtin-manage-policies.test.ts +184 -0
  124. package/e2e/policies/builtin-run-host.test.ts +180 -0
  125. package/e2e/policies/environment-policies.test.ts +177 -0
  126. package/e2e/policies/manage-policies.test.ts +566 -0
  127. package/e2e/policies/output-size.test.ts +98 -0
  128. package/e2e/policies/policies-context-cwd.test.ts +160 -0
  129. package/e2e/policies/relative-script-path.test.ts +60 -0
  130. package/e2e/policies/requests-show.test.ts +135 -0
  131. package/e2e/policies/requests.test.ts +208 -0
  132. package/e2e/policies/slash-policies.test.ts +308 -0
  133. package/e2e/policies/startup-cleanup.test.ts +48 -0
  134. package/e2e/routers/session-timeout.test.ts +106 -0
  135. package/e2e/routers/slash-model.test.ts +152 -0
  136. package/e2e/routers/slash-new.test.ts +50 -0
  137. package/e2e/routers/slash-restart-adapter.test.ts +96 -0
  138. package/e2e/routers/slash-restart.test.ts +114 -0
  139. package/e2e/routers/slash-shutdown.test.ts +55 -0
  140. package/e2e/routers/slash-stop.test.ts +232 -0
  141. package/e2e/routers/slash-upgrade.test.ts +88 -0
  142. package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
  143. package/eslint.config.js +6 -0
  144. package/napkin.md +1 -1
  145. package/package.json +8 -3
  146. package/src/adapter-discord/commands.test.ts +42 -0
  147. package/src/adapter-discord/commands.ts +33 -0
  148. package/src/adapter-discord/config.ts +12 -0
  149. package/src/adapter-discord/forwarder.test.ts +499 -21
  150. package/src/adapter-discord/forwarder.ts +343 -124
  151. package/src/adapter-discord/inbound-cache.test.ts +47 -0
  152. package/src/adapter-discord/inbound-cache.ts +37 -0
  153. package/src/adapter-discord/index.test.ts +67 -2
  154. package/src/adapter-discord/index.ts +84 -216
  155. package/src/adapter-discord/interactions.test.ts +54 -3
  156. package/src/adapter-discord/interactions.ts +97 -53
  157. package/src/adapter-discord/processMessage.ts +239 -0
  158. package/src/adapter-discord/state.ts +1 -0
  159. package/src/adapter-google-chat/auth.test.ts +9 -5
  160. package/src/adapter-google-chat/auth.ts +29 -23
  161. package/src/adapter-google-chat/cards.ts +7 -2
  162. package/src/adapter-google-chat/client.test.ts +37 -2
  163. package/src/adapter-google-chat/client.ts +138 -38
  164. package/src/adapter-google-chat/config.ts +19 -0
  165. package/src/adapter-google-chat/forwarder.test.ts +81 -56
  166. package/src/adapter-google-chat/forwarder.ts +394 -185
  167. package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
  168. package/src/adapter-google-chat/inbound-cache.ts +36 -0
  169. package/src/adapter-google-chat/state.test.ts +1 -0
  170. package/src/adapter-google-chat/state.ts +9 -1
  171. package/src/adapter-google-chat/subscriptions.ts +8 -6
  172. package/src/cli/builtin-policies.ts +44 -0
  173. package/src/cli/commands/agents.ts +59 -5
  174. package/src/cli/commands/down.ts +54 -2
  175. package/src/cli/commands/environments.ts +8 -2
  176. package/src/cli/commands/init.ts +31 -0
  177. package/src/cli/commands/logs.ts +116 -0
  178. package/src/cli/commands/policies.ts +6 -4
  179. package/src/cli/commands/serve.test.ts +67 -0
  180. package/src/cli/commands/serve.ts +284 -0
  181. package/src/cli/commands/up.ts +122 -2
  182. package/src/cli/commands/web-api/agents.ts +3 -2
  183. package/src/cli/index.ts +4 -0
  184. package/src/cli/install-detection.test.ts +72 -0
  185. package/src/cli/install-detection.ts +48 -0
  186. package/src/cli/lite.ts +54 -22
  187. package/src/cli/manage-policies-utils.ts +104 -0
  188. package/src/cli/manage-policies.ts +291 -0
  189. package/src/cli/run-host.ts +45 -0
  190. package/src/cli/supervisor-actions.ts +267 -0
  191. package/src/cli/supervisor-control.test.ts +129 -0
  192. package/src/cli/supervisor-control.ts +155 -0
  193. package/src/cli/supervisor-pid.ts +68 -0
  194. package/src/cli/supervisor.ts +277 -0
  195. package/src/daemon/agent/agent-context.ts +11 -11
  196. package/src/daemon/agent/agent-session.ts +8 -1
  197. package/src/daemon/agent/chat-logger.test.ts +78 -9
  198. package/src/daemon/agent/chat-logger.ts +25 -5
  199. package/src/daemon/agent/turn-registry.test.ts +89 -0
  200. package/src/daemon/agent/turn-registry.ts +94 -0
  201. package/src/daemon/agent/types.ts +2 -0
  202. package/src/daemon/api/agent-policy-endpoints.ts +263 -0
  203. package/src/daemon/api/agent-router.ts +47 -126
  204. package/src/daemon/api/index.test.ts +1 -0
  205. package/src/daemon/api/policy-request.test.ts +7 -5
  206. package/src/daemon/api/router-utils.ts +6 -5
  207. package/src/daemon/api/subagent-router.ts +110 -74
  208. package/src/daemon/api/subagent-utils.test.ts +60 -0
  209. package/src/daemon/api/subagent-utils.ts +113 -87
  210. package/src/daemon/api/user-router.ts +34 -8
  211. package/src/daemon/auth.ts +1 -0
  212. package/src/daemon/cron.test.ts +62 -4
  213. package/src/daemon/cron.ts +42 -16
  214. package/src/daemon/events.ts +65 -0
  215. package/src/daemon/index.ts +24 -1
  216. package/src/daemon/message-interruption.test.ts +1 -0
  217. package/src/daemon/message-jobs.test.ts +1 -0
  218. package/src/daemon/message.ts +78 -14
  219. package/src/daemon/observation.test.ts +26 -18
  220. package/src/daemon/pending-replies.test.ts +112 -0
  221. package/src/daemon/pending-replies.ts +162 -0
  222. package/src/daemon/policy-request-service.ts +3 -1
  223. package/src/daemon/policy-utils.test.ts +66 -1
  224. package/src/daemon/policy-utils.ts +126 -1
  225. package/src/daemon/request-store.ts +31 -0
  226. package/src/daemon/routers/session-timeout.ts +4 -0
  227. package/src/daemon/routers/slash-model.test.ts +344 -0
  228. package/src/daemon/routers/slash-model.ts +207 -0
  229. package/src/daemon/routers/slash-policies.test.ts +38 -32
  230. package/src/daemon/routers/slash-policies.ts +84 -33
  231. package/src/daemon/routers/slash-restart.test.ts +69 -0
  232. package/src/daemon/routers/slash-restart.ts +36 -0
  233. package/src/daemon/routers/slash-shutdown.test.ts +50 -0
  234. package/src/daemon/routers/slash-shutdown.ts +28 -0
  235. package/src/daemon/routers/slash-upgrade.test.ts +116 -0
  236. package/src/daemon/routers/slash-upgrade.ts +76 -0
  237. package/src/daemon/routers/types.ts +7 -0
  238. package/src/daemon/routers.ts +16 -0
  239. package/src/shared/adapters/blockquote.test.ts +28 -0
  240. package/src/shared/adapters/blockquote.ts +20 -0
  241. package/src/shared/adapters/filtering.test.ts +224 -10
  242. package/src/shared/adapters/filtering.ts +95 -7
  243. package/src/shared/adapters/inbound-cache.test.ts +48 -0
  244. package/src/shared/adapters/inbound-cache.ts +54 -0
  245. package/src/shared/adapters/turn-log-buffer.ts +266 -0
  246. package/src/shared/adapters/turn-log.test.ts +389 -0
  247. package/src/shared/adapters/turn-log.ts +357 -0
  248. package/src/shared/agent-utils.ts +12 -5
  249. package/src/shared/chats.test.ts +4 -0
  250. package/src/shared/chats.ts +9 -0
  251. package/src/shared/config.ts +16 -1
  252. package/src/shared/lite.ts +76 -2
  253. package/src/shared/policies.ts +26 -0
  254. package/src/shared/template-manifest.ts +267 -0
  255. package/src/shared/utils/shell.ts +61 -0
  256. package/src/shared/version.ts +34 -0
  257. package/src/shared/workspace.test.ts +217 -0
  258. package/src/shared/workspace.ts +626 -48
  259. package/templates/environments/cladding/allowlist-domain.mjs +125 -0
  260. package/templates/environments/cladding/env.json +21 -1
  261. package/templates/environments/cladding/run-with-network.mjs +54 -0
  262. package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
  263. package/templates/environments/macos-proxy/env.json +8 -1
  264. package/templates/environments/macos-proxy/proxy.mjs +42 -13
  265. package/templates/gemini/template.json +5 -0
  266. package/templates/gemini-claw/template.json +13 -0
  267. package/templates/skills/clawmini-requests/SKILL.md +69 -10
  268. package/templates/skills/run-host/SKILL.md +51 -0
  269. package/templates/skills/skill-creator/SKILL.md +4 -3
  270. package/templates/skills/skill-creator/scripts/validate.sh +52 -0
  271. package/tsdown.config.ts +10 -1
  272. package/vitest.config.ts +2 -2
  273. package/web/.svelte-kit/ambient.d.ts +292 -176
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
  276. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
  277. package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
  278. package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
  279. package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
  280. package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
  281. package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
  282. package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
  283. package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
  284. package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
  285. package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
  286. package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
  287. package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
  288. package/web/.svelte-kit/output/client/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
  289. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
  290. package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
  291. package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
  292. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
  293. package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
  294. package/{dist/web/_app/immutable/nodes/3.Dr0ot9sV.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
  295. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
  296. package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
  297. package/web/.svelte-kit/output/client/_app/version.json +1 -1
  298. package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
  299. package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
  300. package/web/.svelte-kit/output/server/chunks/client.js +1 -1
  301. package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
  302. package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
  303. package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
  304. package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
  305. package/web/.svelte-kit/output/server/chunks/root.js +739 -788
  306. package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
  307. package/web/.svelte-kit/output/server/index.js +126 -90
  308. package/web/.svelte-kit/output/server/manifest-full.js +1 -1
  309. package/web/.svelte-kit/output/server/manifest.js +1 -1
  310. package/web/.svelte-kit/output/server/nodes/0.js +1 -1
  311. package/web/.svelte-kit/output/server/nodes/1.js +1 -1
  312. package/web/.svelte-kit/output/server/nodes/2.js +1 -1
  313. package/web/.svelte-kit/output/server/nodes/3.js +1 -1
  314. package/web/.svelte-kit/output/server/nodes/4.js +1 -1
  315. package/web/.svelte-kit/output/server/nodes/5.js +1 -1
  316. package/web/.svelte-kit/output/server/remote-entry.js +245 -81
  317. package/web/.svelte-kit/tsconfig.json +4 -1
  318. package/dist/cli/propose-policy.mjs.map +0 -1
  319. package/dist/lite-CBxOT1y5.mjs +0 -241
  320. package/dist/lite-CBxOT1y5.mjs.map +0 -1
  321. package/dist/routing-D8rTxtaV.mjs +0 -245
  322. package/dist/routing-D8rTxtaV.mjs.map +0 -1
  323. package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  324. package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
  325. package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
  326. package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
  327. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
  328. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  329. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  331. package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
  336. package/dist/workspace-BJmJBfKi.mjs +0 -456
  337. package/dist/workspace-BJmJBfKi.mjs.map +0 -1
  338. package/src/cli/e2e/agents.test.ts +0 -140
  339. package/src/cli/e2e/basic.test.ts +0 -43
  340. package/src/cli/e2e/cron.test.ts +0 -132
  341. package/src/cli/e2e/export-lite-func.test.ts +0 -206
  342. package/src/cli/e2e/fallbacks.test.ts +0 -175
  343. package/src/cli/e2e/init.test.ts +0 -77
  344. package/src/cli/e2e/messages.test.ts +0 -332
  345. package/src/cli/e2e/propose-policy.test.ts +0 -203
  346. package/src/cli/e2e/requests.test.ts +0 -180
  347. package/src/cli/e2e/session-timeout.test.ts +0 -192
  348. package/src/cli/e2e/slash-new.test.ts +0 -93
  349. package/src/cli/e2e/subagents.test.ts +0 -106
  350. package/src/cli/e2e/utils.ts +0 -66
  351. package/src/cli/propose-policy.ts +0 -91
  352. package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
  353. package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
  354. package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
  355. package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
  356. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
  365. package/web/.svelte-kit/output/server/chunks/false.js +0 -4
  366. /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
  367. /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
@@ -0,0 +1,566 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { TestEnvironment } from '../_helpers/test-environment.js';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const binPath = path.resolve(__dirname, '../../dist/cli/manage-policies.mjs');
10
+
11
+ describe('manage-policies add', () => {
12
+ let env: TestEnvironment;
13
+
14
+ beforeAll(async () => {
15
+ env = new TestEnvironment('e2e-manage-policies-add');
16
+ await env.setup();
17
+ const { code, stderr } = await env.init();
18
+ if (code !== 0) throw new Error(`Init failed: ${stderr}`);
19
+ });
20
+
21
+ afterAll(() => env.teardown(), 30000);
22
+
23
+ it('should fail if missing required arguments', async () => {
24
+ const { stderr, code } = await env.runBin(binPath, ['add']);
25
+ expect(code).toBe(1);
26
+ expect(stderr).toContain("error: required option '--name <policy_name>' not specified");
27
+ });
28
+
29
+ it('should fail if policy name is invalid', async () => {
30
+ const { stderr, code } = await env.runBin(binPath, [
31
+ 'add',
32
+ '--name',
33
+ 'Invalid_Name!',
34
+ '--description',
35
+ 'Test description',
36
+ '--command',
37
+ 'echo test',
38
+ ]);
39
+ expect(code).toBe(1);
40
+ expect(stderr).toContain(
41
+ 'Error: Policy name must only contain lowercase letters, numbers, and hyphens.'
42
+ );
43
+ });
44
+
45
+ it('should fail if neither command nor script-file is provided', async () => {
46
+ const { stderr, code } = await env.runBin(binPath, [
47
+ 'add',
48
+ '--name',
49
+ 'test-policy',
50
+ '--description',
51
+ 'Test description',
52
+ ]);
53
+ expect(code).toBe(1);
54
+ expect(stderr).toContain('Error: Must provide either --command or --script-file.');
55
+ });
56
+
57
+ it('should create a policy with a command', async () => {
58
+ const { stdout, stderr, code } = await env.runBin(binPath, [
59
+ 'add',
60
+ '--name',
61
+ 'echo-test',
62
+ '--description',
63
+ 'A simple echo command',
64
+ '--command',
65
+ 'echo "Hello World"',
66
+ ]);
67
+
68
+ if (code !== 0) console.error(stderr);
69
+ expect(code).toBe(0);
70
+ expect(stdout).toContain("Successfully added policy 'echo-test'");
71
+
72
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
73
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
74
+
75
+ expect(policies.policies['echo-test']).toBeDefined();
76
+ expect(policies.policies['echo-test'].description).toBe('A simple echo command');
77
+ expect(policies.policies['echo-test'].command).toBe('echo');
78
+ expect(policies.policies['echo-test'].args).toEqual(['Hello World']);
79
+ expect(policies.policies['echo-test'].allowHelp).toBe(false);
80
+ expect(policies.policies['echo-test'].autoApprove).toBe(false);
81
+ });
82
+
83
+ it('should set autoApprove and allowHelp when dangerous flags are passed', async () => {
84
+ const { stdout, stderr, code } = await env.runBin(binPath, [
85
+ 'add',
86
+ '--name',
87
+ 'dangerous-echo',
88
+ '--description',
89
+ 'Echo with both dangerous opt-ins',
90
+ '--command',
91
+ 'echo dangerous',
92
+ '--dangerously-auto-approve',
93
+ '--dangerously-allow-help',
94
+ ]);
95
+
96
+ if (code !== 0) console.error(stderr);
97
+ expect(code).toBe(0);
98
+ expect(stdout).toContain("Successfully added policy 'dangerous-echo'");
99
+
100
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
101
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
102
+
103
+ expect(policies.policies['dangerous-echo'].allowHelp).toBe(true);
104
+ expect(policies.policies['dangerous-echo'].autoApprove).toBe(true);
105
+ });
106
+
107
+ it('should set only autoApprove when only --dangerously-auto-approve is passed', async () => {
108
+ const { code } = await env.runBin(binPath, [
109
+ 'add',
110
+ '--name',
111
+ 'auto-only',
112
+ '--description',
113
+ 'Auto-approve only',
114
+ '--command',
115
+ 'echo auto',
116
+ '--dangerously-auto-approve',
117
+ ]);
118
+ expect(code).toBe(0);
119
+
120
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
121
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
122
+
123
+ expect(policies.policies['auto-only'].autoApprove).toBe(true);
124
+ expect(policies.policies['auto-only'].allowHelp).toBe(false);
125
+ });
126
+
127
+ it('should set only allowHelp when only --dangerously-allow-help is passed', async () => {
128
+ const { code } = await env.runBin(binPath, [
129
+ 'add',
130
+ '--name',
131
+ 'help-only',
132
+ '--description',
133
+ 'Allow help only',
134
+ '--command',
135
+ 'echo help',
136
+ '--dangerously-allow-help',
137
+ ]);
138
+ expect(code).toBe(0);
139
+
140
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
141
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
142
+
143
+ expect(policies.policies['help-only'].allowHelp).toBe(true);
144
+ expect(policies.policies['help-only'].autoApprove).toBe(false);
145
+ });
146
+
147
+ it('should create a policy with a script file', async () => {
148
+ const scriptPath = path.resolve(env.e2eDir, 'test-script.sh');
149
+ fs.writeFileSync(scriptPath, '#!/bin/bash\necho "From script"', { mode: 0o755 });
150
+
151
+ const { stdout, code } = await env.runBin(binPath, [
152
+ 'add',
153
+ '--name',
154
+ 'script-test',
155
+ '--description',
156
+ 'A test script policy',
157
+ '--script-file',
158
+ scriptPath,
159
+ ]);
160
+
161
+ expect(code).toBe(0);
162
+ expect(stdout).toContain("Successfully added policy 'script-test'");
163
+
164
+ const destScriptPath = path.resolve(env.e2eDir, '.clawmini/policy-scripts/script-test.sh');
165
+ expect(fs.existsSync(destScriptPath)).toBe(true);
166
+
167
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
168
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
169
+
170
+ expect(policies.policies['script-test']).toBeDefined();
171
+ expect(policies.policies['script-test'].command).toBe(
172
+ './.clawmini/policy-scripts/script-test.sh'
173
+ );
174
+ });
175
+
176
+ it('should refuse to overwrite an existing policy with the same name', async () => {
177
+ const { stderr, code } = await env.runBin(binPath, [
178
+ 'add',
179
+ '--name',
180
+ 'echo-test',
181
+ '--description',
182
+ 'An updated echo command',
183
+ '--command',
184
+ 'echo "Updated"',
185
+ ]);
186
+
187
+ expect(code).toBe(1);
188
+ expect(stderr).toContain(
189
+ "Policy 'echo-test' is already registered. Use 'manage-policies update' to modify it"
190
+ );
191
+
192
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
193
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
194
+ expect(policies.policies['echo-test'].description).toBe('A simple echo command');
195
+ });
196
+ });
197
+
198
+ describe('manage-policies update', () => {
199
+ let env: TestEnvironment;
200
+
201
+ beforeAll(async () => {
202
+ env = new TestEnvironment('e2e-manage-policies-update');
203
+ await env.setup();
204
+ const { code, stderr } = await env.init();
205
+ if (code !== 0) throw new Error(`Init failed: ${stderr}`);
206
+
207
+ const { code: addCode, stderr: addStderr } = await env.runBin(binPath, [
208
+ 'add',
209
+ '--name',
210
+ 'echo-test',
211
+ '--description',
212
+ 'A simple echo command',
213
+ '--command',
214
+ 'echo "Hello"',
215
+ ]);
216
+ if (addCode !== 0) throw new Error(`Seed failed: ${addStderr}`);
217
+ });
218
+
219
+ afterAll(() => env.teardown(), 30000);
220
+
221
+ it('should fail if policy does not exist', async () => {
222
+ const { stderr, code } = await env.runBin(binPath, [
223
+ 'update',
224
+ '--name',
225
+ 'nonexistent',
226
+ '--description',
227
+ 'whatever',
228
+ ]);
229
+ expect(code).toBe(1);
230
+ expect(stderr).toContain(
231
+ "No user-registered policy 'nonexistent' to update. Use 'manage-policies add'"
232
+ );
233
+ });
234
+
235
+ it('should explain the recovery path when updating a disabled policy', async () => {
236
+ // Manually mark a policy as disabled (`false`) so we exercise that branch
237
+ // without depending on remove/add ordering inside this suite.
238
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
239
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
240
+ policies.policies['disabled-thing'] = false;
241
+ fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
242
+
243
+ const { stderr, code } = await env.runBin(binPath, [
244
+ 'update',
245
+ '--name',
246
+ 'disabled-thing',
247
+ '--description',
248
+ 'whatever',
249
+ ]);
250
+ expect(code).toBe(1);
251
+ expect(stderr).toContain(
252
+ "Policy 'disabled-thing' is currently disabled. Run 'manage-policies remove --name disabled-thing'"
253
+ );
254
+
255
+ // Cleanup so later tests in this suite see a clean slate.
256
+ delete policies.policies['disabled-thing'];
257
+ fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
258
+ });
259
+
260
+ it('should fail if no fields are specified to update', async () => {
261
+ const { stderr, code } = await env.runBin(binPath, [
262
+ 'update',
263
+ '--name',
264
+ 'echo-test',
265
+ ]);
266
+ expect(code).toBe(1);
267
+ expect(stderr).toContain('No fields specified to update.');
268
+ });
269
+
270
+ it('should update only the description by default', async () => {
271
+ const { stdout, code } = await env.runBin(binPath, [
272
+ 'update',
273
+ '--name',
274
+ 'echo-test',
275
+ '--description',
276
+ 'Updated description',
277
+ ]);
278
+ expect(code).toBe(0);
279
+ expect(stdout).toContain("Successfully updated policy 'echo-test'");
280
+
281
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
282
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
283
+ expect(policies.policies['echo-test'].description).toBe('Updated description');
284
+ expect(policies.policies['echo-test'].command).toBe('echo');
285
+ expect(policies.policies['echo-test'].args).toEqual(['Hello']);
286
+ });
287
+
288
+ it('should replace the command and clear stale args', async () => {
289
+ const { code } = await env.runBin(binPath, [
290
+ 'update',
291
+ '--name',
292
+ 'echo-test',
293
+ '--command',
294
+ 'date',
295
+ ]);
296
+ expect(code).toBe(0);
297
+
298
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
299
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
300
+ expect(policies.policies['echo-test'].command).toBe('date');
301
+ expect(policies.policies['echo-test'].args).toBeUndefined();
302
+ });
303
+
304
+ it('should enable the dangerous flags via bare flags', async () => {
305
+ const { code } = await env.runBin(binPath, [
306
+ 'update',
307
+ '--name',
308
+ 'echo-test',
309
+ '--dangerously-auto-approve',
310
+ '--dangerously-allow-help',
311
+ ]);
312
+ expect(code).toBe(0);
313
+
314
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
315
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
316
+ expect(policies.policies['echo-test'].autoApprove).toBe(true);
317
+ expect(policies.policies['echo-test'].allowHelp).toBe(true);
318
+ });
319
+
320
+ it('should disable a dangerous flag via the --no- negation', async () => {
321
+ const { code } = await env.runBin(binPath, [
322
+ 'update',
323
+ '--name',
324
+ 'echo-test',
325
+ '--no-dangerously-auto-approve',
326
+ ]);
327
+ expect(code).toBe(0);
328
+
329
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
330
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
331
+ expect(policies.policies['echo-test'].autoApprove).toBe(false);
332
+ // The other flag stays as it was — we only touched auto-approve.
333
+ expect(policies.policies['echo-test'].allowHelp).toBe(true);
334
+ });
335
+
336
+ it('should unlink the prior script when --script-file changes the extension', async () => {
337
+ // Seed a policy backed by a .sh script.
338
+ const shSrc = path.resolve(env.e2eDir, 'extension-test.sh');
339
+ fs.writeFileSync(shSrc, '#!/bin/bash\necho sh\n', { mode: 0o755 });
340
+ const { code: addCode } = await env.runBin(binPath, [
341
+ 'add',
342
+ '--name',
343
+ 'ext-policy',
344
+ '--description',
345
+ 'extension test',
346
+ '--script-file',
347
+ shSrc,
348
+ ]);
349
+ expect(addCode).toBe(0);
350
+
351
+ const oldScript = path.resolve(
352
+ env.e2eDir,
353
+ '.clawmini/policy-scripts/ext-policy.sh'
354
+ );
355
+ expect(fs.existsSync(oldScript)).toBe(true);
356
+
357
+ // Replace with a .py script of the same policy name; the extension
358
+ // changes, so the old .sh would otherwise be orphaned.
359
+ const pySrc = path.resolve(env.e2eDir, 'extension-test.py');
360
+ fs.writeFileSync(pySrc, '#!/usr/bin/env python3\nprint("py")\n', { mode: 0o755 });
361
+ const { code: updateCode } = await env.runBin(binPath, [
362
+ 'update',
363
+ '--name',
364
+ 'ext-policy',
365
+ '--script-file',
366
+ pySrc,
367
+ ]);
368
+ expect(updateCode).toBe(0);
369
+
370
+ const newScript = path.resolve(
371
+ env.e2eDir,
372
+ '.clawmini/policy-scripts/ext-policy.py'
373
+ );
374
+ expect(fs.existsSync(newScript)).toBe(true);
375
+ expect(fs.existsSync(oldScript)).toBe(false);
376
+
377
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
378
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
379
+ expect(policies.policies['ext-policy'].command).toBe(
380
+ './.clawmini/policy-scripts/ext-policy.py'
381
+ );
382
+ });
383
+
384
+ it('should unlink the prior script when --command replaces a script policy', async () => {
385
+ // The previous test left ext-policy backed by ext-policy.py.
386
+ const pyScript = path.resolve(
387
+ env.e2eDir,
388
+ '.clawmini/policy-scripts/ext-policy.py'
389
+ );
390
+ expect(fs.existsSync(pyScript)).toBe(true);
391
+
392
+ const { code } = await env.runBin(binPath, [
393
+ 'update',
394
+ '--name',
395
+ 'ext-policy',
396
+ '--command',
397
+ 'echo plain',
398
+ ]);
399
+ expect(code).toBe(0);
400
+
401
+ expect(fs.existsSync(pyScript)).toBe(false);
402
+
403
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
404
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
405
+ expect(policies.policies['ext-policy'].command).toBe('echo');
406
+ expect(policies.policies['ext-policy'].args).toEqual(['plain']);
407
+ });
408
+ });
409
+
410
+ describe('manage-policies remove', () => {
411
+ let env: TestEnvironment;
412
+
413
+ beforeAll(async () => {
414
+ env = new TestEnvironment('e2e-manage-policies-remove');
415
+ await env.setup();
416
+ const { code, stderr } = await env.init();
417
+ if (code !== 0) throw new Error(`Init failed: ${stderr}`);
418
+
419
+ const { code: addCode } = await env.runBin(binPath, [
420
+ 'add',
421
+ '--name',
422
+ 'doomed',
423
+ '--description',
424
+ 'will be removed',
425
+ '--command',
426
+ 'echo gone',
427
+ ]);
428
+ if (addCode !== 0) throw new Error('Seed failed');
429
+ });
430
+
431
+ afterAll(() => env.teardown(), 30000);
432
+
433
+ it('should remove an existing user policy', async () => {
434
+ const { stdout, code } = await env.runBin(binPath, ['remove', '--name', 'doomed']);
435
+ expect(code).toBe(0);
436
+ expect(stdout).toContain("Successfully removed policy 'doomed'");
437
+
438
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
439
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
440
+ expect(policies.policies['doomed']).toBeUndefined();
441
+ });
442
+
443
+ it('should fail when removing a missing policy', async () => {
444
+ const { stderr, code } = await env.runBin(binPath, ['remove', '--name', 'doomed']);
445
+ expect(code).toBe(1);
446
+ expect(stderr).toContain("No policy entry 'doomed' to remove.");
447
+ });
448
+
449
+ it('should hint at --disable-builtin when removing an unregistered built-in', async () => {
450
+ const { stderr, code } = await env.runBin(binPath, [
451
+ 'remove',
452
+ '--name',
453
+ 'manage-policies',
454
+ ]);
455
+ expect(code).toBe(1);
456
+ expect(stderr).toContain(
457
+ "'manage-policies' is a built-in. To opt out, re-run with --disable-builtin."
458
+ );
459
+ });
460
+
461
+ it('should disable a built-in policy with --disable-builtin', async () => {
462
+ const { stdout, code } = await env.runBin(binPath, [
463
+ 'remove',
464
+ '--name',
465
+ 'manage-policies',
466
+ '--disable-builtin',
467
+ ]);
468
+ expect(code).toBe(0);
469
+ expect(stdout).toContain("Successfully disabled built-in policy 'manage-policies'");
470
+
471
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
472
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
473
+ expect(policies.policies['manage-policies']).toBe(false);
474
+ });
475
+
476
+ it('should refuse --disable-builtin for non-builtin names', async () => {
477
+ const { stderr, code } = await env.runBin(binPath, [
478
+ 'remove',
479
+ '--name',
480
+ 'not-a-builtin',
481
+ '--disable-builtin',
482
+ ]);
483
+ expect(code).toBe(1);
484
+ expect(stderr).toContain(
485
+ "--disable-builtin can only be used for built-in policies; 'not-a-builtin' is not a built-in."
486
+ );
487
+ });
488
+
489
+ it('should clear a previous false entry by removing without --disable-builtin', async () => {
490
+ const { stdout, code } = await env.runBin(binPath, [
491
+ 'remove',
492
+ '--name',
493
+ 'manage-policies',
494
+ ]);
495
+ expect(code).toBe(0);
496
+ expect(stdout).toContain(
497
+ "Successfully cleared the disable entry for 'manage-policies'"
498
+ );
499
+
500
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
501
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
502
+ expect(policies.policies['manage-policies']).toBeUndefined();
503
+ });
504
+
505
+ it('should refuse --disable-builtin when a user override exists', async () => {
506
+ // Register a user override of the built-in `manage-policies` first.
507
+ const { code: addCode } = await env.runBin(binPath, [
508
+ 'add',
509
+ '--name',
510
+ 'manage-policies',
511
+ '--description',
512
+ 'override',
513
+ '--command',
514
+ 'echo override',
515
+ ]);
516
+ expect(addCode).toBe(0);
517
+
518
+ const { stderr, code } = await env.runBin(binPath, [
519
+ 'remove',
520
+ '--name',
521
+ 'manage-policies',
522
+ '--disable-builtin',
523
+ ]);
524
+ expect(code).toBe(1);
525
+ expect(stderr).toContain(
526
+ "Policy 'manage-policies' has a user override that would be lost. Run 'manage-policies remove --name manage-policies' first"
527
+ );
528
+
529
+ // The override must be intact — the failed --disable-builtin must not
530
+ // have replaced it with `false`.
531
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
532
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
533
+ expect(policies.policies['manage-policies']).toMatchObject({
534
+ command: 'echo',
535
+ args: ['override'],
536
+ });
537
+ });
538
+ });
539
+
540
+ describe('manage-policies CLI (uninitialized)', () => {
541
+ let env: TestEnvironment;
542
+
543
+ beforeAll(async () => {
544
+ env = new TestEnvironment('e2e-manage-policies-uninit');
545
+ await env.setup();
546
+ });
547
+
548
+ afterAll(() => env.teardown(), 30000);
549
+
550
+ it('should fail if .clawmini directory does not exist', async () => {
551
+ const { stderr, code } = await env.runBin(binPath, [
552
+ 'add',
553
+ '--name',
554
+ 'echo-test',
555
+ '--description',
556
+ 'A simple echo command',
557
+ '--command',
558
+ 'echo "Hello World"',
559
+ ]);
560
+
561
+ expect(code).toBe(1);
562
+ expect(stderr).toContain(
563
+ 'Error: .clawmini directory not found. Please run "clawmini init" first.'
564
+ );
565
+ });
566
+ });
@@ -0,0 +1,98 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import {
3
+ TestEnvironment,
4
+ type ChatSubscription,
5
+ agentReply,
6
+ policyWith,
7
+ commandMatching,
8
+ } from '../_helpers/test-environment.js';
9
+
10
+ describe('Output Size E2E', () => {
11
+ let env: TestEnvironment;
12
+ let chat: ChatSubscription | undefined;
13
+
14
+ beforeAll(async () => {
15
+ env = new TestEnvironment('e2e-output-size');
16
+ await env.setup();
17
+ await env.setupSubagentEnv({
18
+ policies: {
19
+ 'short-cmd': {
20
+ description: 'A short output policy',
21
+ command: 'node',
22
+ args: ['-e', 'console.log("short output")'],
23
+ autoApprove: true,
24
+ },
25
+ 'long-cmd': {
26
+ description: 'A long output policy',
27
+ command: 'node',
28
+ args: ['-e', 'console.log("a".repeat(600))'],
29
+ autoApprove: true,
30
+ },
31
+ 'long-err': {
32
+ description: 'A long stderr policy',
33
+ command: 'node',
34
+ args: ['-e', 'console.error("e".repeat(600))'],
35
+ autoApprove: true,
36
+ },
37
+ },
38
+ });
39
+ }, 30000);
40
+
41
+ afterAll(() => env.teardown(), 30000);
42
+ afterEach(() => env.disconnectAll());
43
+
44
+ it('should return inline output for < 500 characters', async () => {
45
+ await env.addChat('chat-short');
46
+ chat = await env.connect('chat-short');
47
+
48
+ await env.sendMessage('clawmini-lite.js request short-cmd', {
49
+ chat: 'chat-short',
50
+ agent: 'debug-agent',
51
+ });
52
+
53
+ const reply = await chat.waitForMessage(agentReply());
54
+ expect(reply.content).toContain('[DEBUG] clawmini-lite.js request short-cmd');
55
+ expect(reply.content).toContain('short output');
56
+ }, 30000);
57
+
58
+ it('should intercept large stdout and return a summary string', async () => {
59
+ await env.addChat('chat-long-out');
60
+ chat = await env.connect('chat-long-out');
61
+
62
+ await env.sendMessage('clawmini-lite.js request long-cmd', {
63
+ chat: 'chat-long-out',
64
+ agent: 'debug-agent',
65
+ });
66
+
67
+ const policy = await chat.waitForMessage(policyWith('approved'));
68
+ expect(policy.content).toMatch(
69
+ /stdout is 60\d characters, saved to \.\/tmp\/stdout-[a-zA-Z0-9-]+\.txt/
70
+ );
71
+
72
+ // Read the file via the debug-agent
73
+ await env.sendMessage(`cat ./tmp/stdout-${policy.requestId}.txt`, {
74
+ chat: 'chat-long-out',
75
+ agent: 'debug-agent',
76
+ });
77
+
78
+ const reply = await chat.waitForMessage(
79
+ commandMatching((m) => m.content.includes('cat ./tmp/stdout-'))
80
+ );
81
+ expect(reply.content).toContain('a'.repeat(600));
82
+ }, 30000);
83
+
84
+ it('should intercept large stderr and return a summary string', async () => {
85
+ await env.addChat('chat-long-err');
86
+ chat = await env.connect('chat-long-err');
87
+
88
+ await env.sendMessage('clawmini-lite.js request long-err', {
89
+ chat: 'chat-long-err',
90
+ agent: 'debug-agent',
91
+ });
92
+
93
+ const policy = await chat.waitForMessage(policyWith('approved'));
94
+ expect(policy.content).toMatch(
95
+ /stderr is 60\d characters, saved to \.\/tmp\/stderr-[a-zA-Z0-9-]+\.txt/
96
+ );
97
+ }, 30000);
98
+ });