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,64 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { TestEnvironment } from '../_helpers/test-environment.js';
3
+
4
+ // Basic CRUD for jobs is covered via lite in agent-jobs.test.ts. This file
5
+ // covers behavior that only emerges through the scheduler/full CLI: firing a
6
+ // scheduled `--at` job, inheriting chat defaults, and CLI-level date parsing.
7
+
8
+ describe('E2E Cron Tests', () => {
9
+ let env: TestEnvironment;
10
+
11
+ beforeAll(async () => {
12
+ env = new TestEnvironment('e2e-tmp-cron');
13
+ await env.setup();
14
+ await env.init();
15
+ }, 30000);
16
+
17
+ afterAll(() => env.teardown(), 30000);
18
+
19
+ it('should execute a job and inherit chat default agent and session', async () => {
20
+ await env.runCli(['agents', 'add', 'cron-exec-agent']);
21
+ env.updateAgentSettings('cron-exec-agent', {
22
+ commands: { new: 'echo "executed with $SESSION_ID and msg: $CLAW_CLI_MESSAGE"' },
23
+ });
24
+
25
+ await env.addChat('cron-chat');
26
+ const { code: codeSetup, stderr: stderrSetup } = await env.runCli([
27
+ 'messages', 'send', 'setup session',
28
+ '-c', 'cron-chat',
29
+ '-a', 'cron-exec-agent',
30
+ ]);
31
+ if (codeSetup !== 0) console.error(stderrSetup);
32
+ expect(codeSetup).toBe(0);
33
+
34
+ const futureTime = new Date(Date.now() + 2000).toISOString();
35
+ const { stdout: stdoutAdd, code: codeAdd } = await env.runCli([
36
+ 'jobs', 'add', 'test-exec-job',
37
+ '-c', 'cron-chat',
38
+ '--at', futureTime,
39
+ '--message', 'hello from future',
40
+ ]);
41
+ expect(codeAdd).toBe(0);
42
+ expect(stdoutAdd).toContain("Job 'test-exec-job' created successfully.");
43
+
44
+ await new Promise((resolve) => setTimeout(resolve, 3000));
45
+
46
+ const { stdout: stdoutHistory } = await env.runCli(['messages', 'tail', '-c', 'cron-chat']);
47
+ expect(stdoutHistory).toContain('hello from future');
48
+ // Confirms cron-exec-agent (not default) ran the job.
49
+ expect(stdoutHistory).toContain('msg: hello from future');
50
+
51
+ // One-off --at jobs should be removed from settings after firing.
52
+ const { stdout: stdoutListAfter } = await env.runCli(['jobs', 'list', '-c', 'cron-chat']);
53
+ expect(stdoutListAfter).not.toContain('test-exec-job');
54
+ }, 10000);
55
+
56
+ it('should reject jobs with invalid --at date format', async () => {
57
+ const { stderr, code } = await env.runCli([
58
+ 'jobs', 'add', 'invalid-job',
59
+ '--at', 'invalid-date',
60
+ ]);
61
+ expect(code).not.toBe(0);
62
+ expect(stderr).toContain("Invalid date format for 'at' schedule: invalid-date");
63
+ });
64
+ });
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { TestEnvironment } from '../_helpers/test-environment.js';
3
+
4
+ // Covers what happens to scheduled jobs when the daemon stops and starts
5
+ // again — a case the other files do not exercise (they keep one daemon up
6
+ // for the whole describe block).
7
+ describe('E2E Job Restart Behavior', () => {
8
+ let env: TestEnvironment;
9
+
10
+ beforeAll(async () => {
11
+ env = new TestEnvironment('e2e-tmp-jobs-restart');
12
+ await env.setup();
13
+ await env.init();
14
+
15
+ await env.runCli(['agents', 'add', 'restart-agent']);
16
+ env.updateAgentSettings('restart-agent', {
17
+ commands: { new: 'echo "[restart-agent] msg: $CLAW_CLI_MESSAGE"' },
18
+ });
19
+ }, 30000);
20
+
21
+ afterAll(() => env.teardown(), 30000);
22
+
23
+ it('fires overdue --at jobs after the daemon restarts and removes them', async () => {
24
+ await env.addChat('restart-at-chat');
25
+ // Pin the chat to restart-agent so the cron firing routes through our
26
+ // echo command (the job below specifies no agentId and inherits).
27
+ const setup = await env.runCli([
28
+ 'messages', 'send', 'setup',
29
+ '-c', 'restart-at-chat',
30
+ '-a', 'restart-agent',
31
+ ]);
32
+ expect(setup.code).toBe(0);
33
+
34
+ // Schedule for ~5s out — enough margin to call down() before the
35
+ // running daemon's scheduler can fire the job.
36
+ const futureTime = new Date(Date.now() + 5000).toISOString();
37
+ const add = await env.runCli([
38
+ 'jobs', 'add', 'overdue-job',
39
+ '-c', 'restart-at-chat',
40
+ '--at', futureTime,
41
+ '--message', 'caught up after restart',
42
+ ]);
43
+ expect(add.code).toBe(0);
44
+
45
+ await env.down();
46
+
47
+ // Block past the target time so the job is overdue when the daemon
48
+ // comes back up.
49
+ await new Promise((r) => setTimeout(r, 6000));
50
+
51
+ await env.up();
52
+ // Give the cron manager time to detect the overdue job (~100ms scheduling
53
+ // delay in cron.ts) plus the agent's command execution.
54
+ await new Promise((r) => setTimeout(r, 3000));
55
+
56
+ const { stdout: history } = await env.runCli([
57
+ 'messages', 'tail', '-c', 'restart-at-chat',
58
+ ]);
59
+ expect(history).toContain('caught up after restart');
60
+ expect(history).toContain('msg: caught up after restart');
61
+
62
+ const { stdout: jobsList } = await env.runCli([
63
+ 'jobs', 'list', '-c', 'restart-at-chat',
64
+ ]);
65
+ expect(jobsList).not.toContain('overdue-job');
66
+ }, 30000);
67
+
68
+ // Documents current behavior: cron/every ticks that elapse while the
69
+ // daemon is down are silently dropped — the next tick is scheduled, but
70
+ // missed ones are not backfilled.
71
+ it('does not backfill cron ticks missed while the daemon was down', async () => {
72
+ await env.addChat('restart-cron-chat');
73
+ const setup = await env.runCli([
74
+ 'messages', 'send', 'setup',
75
+ '-c', 'restart-cron-chat',
76
+ '-a', 'restart-agent',
77
+ ]);
78
+ expect(setup.code).toBe(0);
79
+
80
+ // Daily-at-midnight: outside the rare midnight window, the previous
81
+ // tick is hours in the past and the next tick is hours in the future,
82
+ // so nothing should fire during the test regardless of restart timing.
83
+ const add = await env.runCli([
84
+ 'jobs', 'add', 'nightly-job',
85
+ '-c', 'restart-cron-chat',
86
+ '--cron', '0 0 * * *',
87
+ '--message', 'should-not-fire-on-restart',
88
+ ]);
89
+ expect(add.code).toBe(0);
90
+
91
+ await env.down();
92
+ await env.up();
93
+
94
+ // Give any (unwanted) immediate firing a chance to land before
95
+ // asserting it didn't happen.
96
+ await new Promise((r) => setTimeout(r, 3000));
97
+
98
+ const { stdout: history } = await env.runCli([
99
+ 'messages', 'tail', '-c', 'restart-cron-chat',
100
+ ]);
101
+ expect(history).not.toContain('should-not-fire-on-restart');
102
+
103
+ const { stdout: jobsList } = await env.runCli([
104
+ 'jobs', 'list', '-c', 'restart-cron-chat',
105
+ ]);
106
+ expect(jobsList).toContain('nightly-job');
107
+ }, 30000);
108
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import {
3
+ TestEnvironment,
4
+ type ChatSubscription,
5
+ type SystemMessage,
6
+ policyWith,
7
+ } from '../_helpers/test-environment.js';
8
+
9
+ // When an agent requests a policy and the user /approves it, the approval must
10
+ // be replayed into the agent's *current* session — not a hard-coded 'default'.
11
+ // The current session may have drifted from the one that created the request
12
+ // (e.g. session-timeout fired or the user ran /new).
13
+ describe('Policy approval runs on the agent\'s current session', () => {
14
+ let env: TestEnvironment;
15
+ let chat: ChatSubscription | undefined;
16
+
17
+ beforeAll(async () => {
18
+ env = new TestEnvironment('e2e-approval-session');
19
+ await env.setup();
20
+ await env.setupSubagentEnv({
21
+ policies: {
22
+ 'test-cmd': {
23
+ description: 'A test policy',
24
+ command: 'echo',
25
+ args: ['approved-output'],
26
+ },
27
+ },
28
+ });
29
+ }, 30000);
30
+
31
+ afterAll(() => env.teardown(), 30000);
32
+ afterEach(() => env.disconnectAll());
33
+
34
+ it('replays the approval into the session recorded in chatSettings.sessions, not "default"', async () => {
35
+ const chatId = 'chat-approval-session';
36
+ await env.addChat(chatId, 'debug-agent');
37
+
38
+ // Pin the chat's debug-agent session to a known non-'default' id so the
39
+ // bug (falling back to 'default') is observable. In production this drift
40
+ // is what happens after session-timeout or /new.
41
+ env.writeChatSettings(chatId, {
42
+ defaultAgent: 'debug-agent',
43
+ sessions: { 'debug-agent': 'pinned-session-xyz' },
44
+ });
45
+
46
+ chat = await env.connect(chatId);
47
+
48
+ // Agent requests the policy. This runs in sessionId='pinned-session-xyz'.
49
+ await env.sendMessage('clawmini-lite.js request test-cmd', {
50
+ chat: chatId,
51
+ agent: 'debug-agent',
52
+ });
53
+
54
+ const policy = await chat.waitForMessage(policyWith());
55
+ const reqId = policy.requestId;
56
+
57
+ await env.sendMessage(`/approve ${reqId}`, { chat: chatId });
58
+
59
+ // The second 'policy_approved' system message (displayRole='user') is the
60
+ // one emitted by the re-triggered agent message pipeline. Its sessionId
61
+ // reflects the session the approval was actually replayed on.
62
+ const actorNotif = await chat.waitForMessage(
63
+ (m): m is SystemMessage =>
64
+ m.role === 'system' && m.event === 'policy_approved' && m.displayRole === 'user'
65
+ );
66
+
67
+ expect(actorNotif.sessionId).toBe('pinned-session-xyz');
68
+ }, 30000);
69
+ });
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import { TestEnvironment } from '../_helpers/test-environment.js';
5
+
6
+ describe('clawmini up auto-creates policies.json', () => {
7
+ let env: TestEnvironment;
8
+
9
+ beforeAll(async () => {
10
+ env = new TestEnvironment('e2e-auto-create-policies');
11
+ await env.setup();
12
+ // Intentionally omit `policies` so no policies.json is pre-written.
13
+ await env.setupSubagentEnv();
14
+ }, 30000);
15
+
16
+ afterAll(() => env.teardown(), 30000);
17
+
18
+ it('creates an empty policies.json during up', () => {
19
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
20
+ expect(fs.existsSync(policiesPath)).toBe(true);
21
+ expect(JSON.parse(fs.readFileSync(policiesPath, 'utf8'))).toEqual({ policies: {} });
22
+ });
23
+
24
+ it('exposes the built-in run-host policy even though the user never wrote policies.json', async () => {
25
+ const { stdout, code } = await env.runLite(['requests', 'list']);
26
+ expect(code).toBe(0);
27
+ expect(stdout).toContain('- run-host');
28
+ }, 30000);
29
+
30
+ it('allows --help on run-host via lite without approval', async () => {
31
+ const { stdout, code } = await env.runLite(['request', 'run-host', '--help']);
32
+ expect(code).toBe(0);
33
+ expect(stdout).toContain('--command');
34
+ }, 30000);
35
+ });
@@ -0,0 +1,184 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import {
5
+ TestEnvironment,
6
+ type ChatSubscription,
7
+ type SystemMessage,
8
+ policyWith,
9
+ commandMatching
10
+ } from '../_helpers/test-environment.js';
11
+
12
+ const SCRIPT_REL = '.clawmini/policy-scripts/manage-policies.js';
13
+
14
+ describe('Built-in manage-policies installation', () => {
15
+ let env: TestEnvironment;
16
+
17
+ beforeAll(async () => {
18
+ env = new TestEnvironment('e2e-builtin-install');
19
+ await env.setup();
20
+ }, 30000);
21
+
22
+ afterAll(() => env.teardown(), 30000);
23
+
24
+ it('init writes the manage-policies script to disk', async () => {
25
+ const { code, stderr } = await env.init();
26
+ if (code !== 0) throw new Error(`Init failed: ${stderr}`);
27
+
28
+ const scriptPath = path.resolve(env.e2eDir, SCRIPT_REL);
29
+ expect(fs.existsSync(scriptPath)).toBe(true);
30
+ const content = fs.readFileSync(scriptPath, 'utf8');
31
+ expect(content.startsWith('#!')).toBe(true);
32
+ expect((fs.statSync(scriptPath).mode & 0o111) !== 0).toBe(true);
33
+ }, 30000);
34
+
35
+ it('up refreshes a stale manage-policies script', async () => {
36
+ const scriptPath = path.resolve(env.e2eDir, SCRIPT_REL);
37
+ const original = fs.readFileSync(scriptPath, 'utf8');
38
+
39
+ fs.writeFileSync(scriptPath, '#!/usr/bin/env node\n// stale\n');
40
+ expect(fs.readFileSync(scriptPath, 'utf8')).not.toBe(original);
41
+
42
+ const { code, stderr } = await env.up();
43
+ if (code !== 0) throw new Error(`Up failed: ${stderr}`);
44
+
45
+ expect(fs.readFileSync(scriptPath, 'utf8')).toBe(original);
46
+ }, 30000);
47
+ });
48
+
49
+ describe('Built-in manage-policies E2E', () => {
50
+ let env: TestEnvironment;
51
+ let chat: ChatSubscription | undefined;
52
+
53
+ beforeAll(async () => {
54
+ env = new TestEnvironment('e2e-builtin-manage-policies');
55
+ await env.setup();
56
+ await env.setupSubagentEnv({
57
+ policies: {}
58
+ });
59
+ }, 30000);
60
+
61
+ afterAll(() => env.teardown(), 30000);
62
+ afterEach(() => env.disconnectAll());
63
+
64
+ it('should allow the agent to add a new policy via manage-policies add', async () => {
65
+ await env.addChat('chat-add');
66
+ chat = await env.connect('chat-add');
67
+
68
+ await env.sendMessage(
69
+ 'clawmini-lite.js request manage-policies -- add --name npm-install --description "Run npm install" --command "npm install"',
70
+ {
71
+ chat: 'chat-add',
72
+ agent: 'debug-agent',
73
+ }
74
+ );
75
+
76
+ const policy = await chat.waitForMessage(policyWith());
77
+ const reqId = policy.requestId;
78
+
79
+ await env.sendMessage(`/approve ${reqId}`, { chat: 'chat-add' });
80
+
81
+ const actorNotif = await chat.waitForMessage(
82
+ (m): m is SystemMessage =>
83
+ m.role === 'system' &&
84
+ m.event === 'policy_approved' &&
85
+ m.displayRole === 'user'
86
+ );
87
+ expect(actorNotif.content).toContain("Successfully added policy 'npm-install'");
88
+
89
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
90
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
91
+
92
+ expect(policies.policies['npm-install']).toBeDefined();
93
+ expect(policies.policies['npm-install'].command).toBe('npm');
94
+ expect(policies.policies['npm-install'].args).toEqual(['install']);
95
+ }, 30000);
96
+
97
+ it('should disable manage-policies if set to false in policies.json', async () => {
98
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
99
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
100
+ policies.policies['manage-policies'] = false;
101
+ fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
102
+
103
+ await env.addChat('chat-disabled');
104
+ chat = await env.connect('chat-disabled');
105
+
106
+ await env.sendMessage(
107
+ 'clawmini-lite.js request manage-policies -- add --name foo --description "bar" --command "baz"',
108
+ {
109
+ chat: 'chat-disabled',
110
+ agent: 'debug-agent',
111
+ }
112
+ );
113
+
114
+ const reply = await chat.waitForMessage(
115
+ commandMatching((m) => m.stderr.includes('Policy not found: manage-policies'))
116
+ );
117
+
118
+ expect(reply.stderr).toContain('Policy not found: manage-policies');
119
+
120
+ // Disabling the policy must not delete the installed script — the user can
121
+ // re-enable it later just by removing the `false` entry.
122
+ const scriptPath = path.resolve(env.e2eDir, SCRIPT_REL);
123
+ expect(fs.existsSync(scriptPath)).toBe(true);
124
+ }, 30000);
125
+ });
126
+
127
+ describe('Built-in manage-policies override', () => {
128
+ let env: TestEnvironment;
129
+
130
+ beforeAll(async () => {
131
+ env = new TestEnvironment('e2e-builtin-override');
132
+ await env.setup();
133
+ await env.setupSubagentEnv({
134
+ policies: {
135
+ 'manage-policies': {
136
+ description: 'user-defined override',
137
+ command: 'echo',
138
+ args: ['overridden'],
139
+ allowHelp: true,
140
+ },
141
+ },
142
+ });
143
+ }, 30000);
144
+
145
+ afterAll(() => env.teardown(), 30000);
146
+
147
+ it('runs a user-defined manage-policies override instead of the built-in', async () => {
148
+ await env.addChat('chat-override');
149
+ const chat = await env.connect('chat-override');
150
+ try {
151
+ await env.sendMessage(
152
+ 'clawmini-lite.js request manage-policies -- add --name custom --description "x" --command "echo y"',
153
+ { chat: 'chat-override', agent: 'debug-agent' }
154
+ );
155
+
156
+ const policy = await chat.waitForMessage(policyWith());
157
+ const reqId = policy.requestId;
158
+
159
+ await env.sendMessage(`/approve ${reqId}`, { chat: 'chat-override' });
160
+
161
+ const actorNotif = await chat.waitForMessage(
162
+ (m): m is SystemMessage =>
163
+ m.role === 'system' && m.event === 'policy_approved' && m.displayRole === 'user'
164
+ );
165
+ // The override sends `echo overridden ...args` so stdout starts with "overridden".
166
+ expect(actorNotif.content).toContain('overridden');
167
+ // Built-in success message must NOT appear — the override took effect.
168
+ expect(actorNotif.content).not.toContain('Successfully added policy');
169
+
170
+ // policies.json should be unchanged: the override is `echo`, which never
171
+ // writes to it, so no `custom` entry should have been added.
172
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
173
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
174
+ expect(policies.policies['custom']).toBeUndefined();
175
+ // And the user override must still be intact.
176
+ expect(policies.policies['manage-policies']).toMatchObject({
177
+ command: 'echo',
178
+ args: ['overridden'],
179
+ });
180
+ } finally {
181
+ await chat.disconnect();
182
+ }
183
+ }, 30000);
184
+ });
@@ -0,0 +1,180 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import {
5
+ TestEnvironment,
6
+ type ChatSubscription,
7
+ type SystemMessage,
8
+ policyWith,
9
+ commandMatching,
10
+ } from '../_helpers/test-environment.js';
11
+
12
+ const RUN_HOST_SCRIPT_REL = '.clawmini/policy-scripts/run-host.js';
13
+
14
+ describe('Built-in run-host installation', () => {
15
+ let env: TestEnvironment;
16
+
17
+ beforeAll(async () => {
18
+ env = new TestEnvironment('e2e-builtin-run-host-install');
19
+ await env.setup();
20
+ }, 30000);
21
+
22
+ afterAll(() => env.teardown(), 30000);
23
+
24
+ it('init writes the run-host script to disk', async () => {
25
+ const { code, stderr } = await env.init();
26
+ if (code !== 0) throw new Error(`Init failed: ${stderr}`);
27
+
28
+ const scriptPath = path.resolve(env.e2eDir, RUN_HOST_SCRIPT_REL);
29
+ expect(fs.existsSync(scriptPath)).toBe(true);
30
+ const content = fs.readFileSync(scriptPath, 'utf8');
31
+ expect(content.startsWith('#!')).toBe(true);
32
+ expect((fs.statSync(scriptPath).mode & 0o111) !== 0).toBe(true);
33
+ }, 30000);
34
+ });
35
+
36
+ describe('Built-in run-host E2E', () => {
37
+ let env: TestEnvironment;
38
+ let chat: ChatSubscription | undefined;
39
+
40
+ beforeAll(async () => {
41
+ env = new TestEnvironment('e2e-builtin-run-host');
42
+ await env.setup();
43
+ await env.setupSubagentEnv({
44
+ policies: {},
45
+ });
46
+ }, 30000);
47
+
48
+ afterAll(() => env.teardown(), 30000);
49
+ afterEach(() => env.disconnectAll());
50
+
51
+ it('executes a shell command with pipes and && via run-host after approval', async () => {
52
+ await env.addChat('chat-run');
53
+ chat = await env.connect('chat-run');
54
+
55
+ await env.sendMessage(
56
+ 'clawmini-lite.js request run-host -- --command "echo hello && echo world | tr a-z A-Z"',
57
+ { chat: 'chat-run', agent: 'debug-agent' }
58
+ );
59
+
60
+ const policy = await chat.waitForMessage(policyWith());
61
+ const reqId = policy.requestId;
62
+
63
+ await env.sendMessage(`/approve ${reqId}`, { chat: 'chat-run' });
64
+
65
+ const actorNotif = await chat.waitForMessage(
66
+ (m): m is SystemMessage =>
67
+ m.role === 'system' &&
68
+ m.event === 'policy_approved' &&
69
+ m.displayRole === 'user'
70
+ );
71
+
72
+ expect(actorNotif.content).toContain('hello');
73
+ expect(actorNotif.content).toContain('WORLD');
74
+ expect(actorNotif.content).toContain('Exit Code: 0');
75
+ }, 30000);
76
+
77
+ it('propagates non-zero exit codes from the executed command', async () => {
78
+ await env.addChat('chat-exit');
79
+ chat = await env.connect('chat-exit');
80
+
81
+ await env.sendMessage(
82
+ 'clawmini-lite.js request run-host -- --command "false"',
83
+ { chat: 'chat-exit', agent: 'debug-agent' }
84
+ );
85
+
86
+ const policy = await chat.waitForMessage(policyWith());
87
+ const reqId = policy.requestId;
88
+
89
+ await env.sendMessage(`/approve ${reqId}`, { chat: 'chat-exit' });
90
+
91
+ const actorNotif = await chat.waitForMessage(
92
+ (m): m is SystemMessage =>
93
+ m.role === 'system' &&
94
+ m.event === 'policy_approved' &&
95
+ m.displayRole === 'user'
96
+ );
97
+
98
+ expect(actorNotif.content).toContain('Exit Code: 1');
99
+ }, 30000);
100
+
101
+ it('does not auto-approve — request stays pending until the user approves', async () => {
102
+ await env.addChat('chat-pending');
103
+ chat = await env.connect('chat-pending');
104
+
105
+ await env.sendMessage(
106
+ 'clawmini-lite.js request run-host -- --command "echo should-not-run-yet"',
107
+ { chat: 'chat-pending', agent: 'debug-agent' }
108
+ );
109
+
110
+ const policy = await chat.waitForMessage(policyWith());
111
+ expect(policy.status).toBe('pending');
112
+
113
+ const approvedEarly = chat.messageBuffer.find(
114
+ (m): m is SystemMessage =>
115
+ m.role === 'system' && m.event === 'policy_approved'
116
+ );
117
+ expect(approvedEarly).toBeUndefined();
118
+ }, 30000);
119
+
120
+ it('supports --help via lite', async () => {
121
+ const { stdout, code } = await env.runLite(['request', 'run-host', '--help']);
122
+ expect(code).toBe(0);
123
+ expect(stdout).toContain('--command');
124
+ }, 30000);
125
+
126
+ it('truncates large stdout to a saved-file summary', async () => {
127
+ await env.addChat('chat-long');
128
+ chat = await env.connect('chat-long');
129
+
130
+ await env.sendMessage(
131
+ 'clawmini-lite.js request run-host -- --command "yes a | head -c 600"',
132
+ { chat: 'chat-long', agent: 'debug-agent' }
133
+ );
134
+
135
+ const policy = await chat.waitForMessage(policyWith());
136
+ const reqId = policy.requestId;
137
+
138
+ await env.sendMessage(`/approve ${reqId}`, { chat: 'chat-long' });
139
+
140
+ const actorNotif = await chat.waitForMessage(
141
+ (m): m is SystemMessage =>
142
+ m.role === 'system' &&
143
+ m.event === 'policy_approved' &&
144
+ m.displayRole === 'user'
145
+ );
146
+ expect(actorNotif.content).toMatch(
147
+ /stdout is 60\d characters, saved to \.\/tmp\/stdout-[a-zA-Z0-9-]+\.txt/
148
+ );
149
+ expect(actorNotif.content).not.toContain('a\n'.repeat(300));
150
+ }, 30000);
151
+
152
+ it('rejects requests when run-host is disabled in policies.json', async () => {
153
+ const policiesPath = path.resolve(env.e2eDir, '.clawmini/policies.json');
154
+ const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
155
+ policies.policies['run-host'] = false;
156
+ fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
157
+
158
+ try {
159
+ await env.addChat('chat-disabled');
160
+ chat = await env.connect('chat-disabled');
161
+
162
+ await env.sendMessage(
163
+ 'clawmini-lite.js request run-host -- --command "echo nope"',
164
+ { chat: 'chat-disabled', agent: 'debug-agent' }
165
+ );
166
+
167
+ const reply = await chat.waitForMessage(
168
+ commandMatching((m) => m.stderr.includes('Policy not found: run-host'))
169
+ );
170
+ expect(reply.stderr).toContain('Policy not found: run-host');
171
+
172
+ // Disabling the policy must not delete the installed script.
173
+ const scriptPath = path.resolve(env.e2eDir, RUN_HOST_SCRIPT_REL);
174
+ expect(fs.existsSync(scriptPath)).toBe(true);
175
+ } finally {
176
+ delete policies.policies['run-host'];
177
+ fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));
178
+ }
179
+ }, 30000);
180
+ });