clawmini 0.0.8 → 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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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 +0 -1
  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 -118
  274. package/web/.svelte-kit/generated/server/internal.js +1 -1
  275. package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
  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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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/D5iV40bG.js +0 -1
  328. package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
  329. package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
  330. package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
  331. package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
  332. package/dist/web/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  333. package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  334. package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  335. package/dist/web/_app/immutable/nodes/4.ClM1bXLE.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/D5iV40bG.js +0 -1
  357. package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
  358. package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
  359. package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
  360. package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
  361. package/web/.svelte-kit/output/client/_app/immutable/entry/app.BCSV3nrG.js +0 -2
  362. package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
  363. package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
  364. package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.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,61 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import {
3
+ recordInbound,
4
+ resolveInbound,
5
+ INBOUND_TTL_MS,
6
+ _resetInboundCacheForTests,
7
+ } from './inbound-cache.js';
8
+
9
+ describe('inbound-cache', () => {
10
+ beforeEach(() => {
11
+ _resetInboundCacheForTests();
12
+ });
13
+
14
+ afterEach(() => {
15
+ vi.useRealTimers();
16
+ });
17
+
18
+ it('records and resolves an inbound by gchatMessageName', () => {
19
+ recordInbound({
20
+ gchatMessageName: 'spaces/x/messages/m1',
21
+ gchatThreadName: 'spaces/x/threads/t1',
22
+ });
23
+ const record = resolveInbound('spaces/x/messages/m1');
24
+ expect(record).toMatchObject({
25
+ gchatMessageName: 'spaces/x/messages/m1',
26
+ gchatThreadName: 'spaces/x/threads/t1',
27
+ });
28
+ });
29
+
30
+ it('returns null for unknown keys', () => {
31
+ expect(resolveInbound('spaces/x/messages/unknown')).toBeNull();
32
+ });
33
+
34
+ it('expires entries older than INBOUND_TTL_MS on resolve', () => {
35
+ vi.useFakeTimers();
36
+ recordInbound({
37
+ gchatMessageName: 'spaces/x/messages/m1',
38
+ gchatThreadName: 'spaces/x/threads/t1',
39
+ });
40
+ expect(resolveInbound('spaces/x/messages/m1')).not.toBeNull();
41
+
42
+ vi.advanceTimersByTime(INBOUND_TTL_MS + 1000);
43
+ expect(resolveInbound('spaces/x/messages/m1')).toBeNull();
44
+ });
45
+
46
+ it('sweeps expired entries on every insert', () => {
47
+ vi.useFakeTimers();
48
+ recordInbound({
49
+ gchatMessageName: 'spaces/x/messages/m1',
50
+ gchatThreadName: 'spaces/x/threads/t1',
51
+ });
52
+ vi.advanceTimersByTime(INBOUND_TTL_MS + 1000);
53
+ recordInbound({
54
+ gchatMessageName: 'spaces/x/messages/m2',
55
+ gchatThreadName: 'spaces/x/threads/t2',
56
+ });
57
+ // m1 should have been swept when m2 was inserted.
58
+ expect(resolveInbound('spaces/x/messages/m1')).toBeNull();
59
+ expect(resolveInbound('spaces/x/messages/m2')).not.toBeNull();
60
+ });
61
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Google Chat-side wrapper around the shared inbound-message TTL cache.
3
+ *
4
+ * Ingestion records each inbound by its `gchatMessageName` (also sent to the
5
+ * daemon as `externalRef`). When the forwarder later sees `turnStarted` with
6
+ * that `externalRef`, it resolves the thread anchor by looking up the same
7
+ * key here.
8
+ */
9
+ import { createInboundCache } from '../shared/adapters/inbound-cache.js';
10
+
11
+ export const INBOUND_TTL_MS = 10 * 60 * 1000; // 10 minutes
12
+
13
+ interface GChatInboundValue {
14
+ gchatThreadName: string;
15
+ }
16
+
17
+ const cache = createInboundCache<GChatInboundValue>(INBOUND_TTL_MS);
18
+
19
+ export interface GChatInboundRecord {
20
+ gchatMessageName: string;
21
+ gchatThreadName: string;
22
+ }
23
+
24
+ export function recordInbound(entry: GChatInboundRecord): void {
25
+ cache.record(entry.gchatMessageName, { gchatThreadName: entry.gchatThreadName });
26
+ }
27
+
28
+ export function resolveInbound(gchatMessageName: string): GChatInboundRecord | null {
29
+ const value = cache.resolve(gchatMessageName);
30
+ return value ? { gchatMessageName, gchatThreadName: value.gchatThreadName } : null;
31
+ }
32
+
33
+ /** Test hook: drop all cached records. */
34
+ export function _resetInboundCacheForTests(): void {
35
+ cache.reset();
36
+ }
@@ -7,6 +7,7 @@ vi.mock('node:fs/promises', () => ({
7
7
  readFile: vi.fn(),
8
8
  writeFile: vi.fn(),
9
9
  mkdir: vi.fn(),
10
+ rename: vi.fn(),
10
11
  },
11
12
  }));
12
13
 
@@ -1,3 +1,4 @@
1
+ import crypto from 'node:crypto';
1
2
  import fsPromises from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { z } from 'zod';
@@ -13,6 +14,7 @@ export const GoogleChatStateSchema = z.object({
13
14
  subscriptionId: z.string().optional(),
14
15
  expirationDate: z.string().optional(),
15
16
  requireMention: z.boolean().optional(),
17
+ threadsDisabled: z.boolean().optional(),
16
18
  })
17
19
  )
18
20
  .optional(),
@@ -74,7 +76,13 @@ export function updateGoogleChatState(
74
76
  const statePath = getGoogleChatStatePath(startDir);
75
77
  const dir = path.dirname(statePath);
76
78
  await fsPromises.mkdir(dir, { recursive: true });
77
- await fsPromises.writeFile(statePath, JSON.stringify(newState, null, 2), 'utf-8');
79
+ // Atomic write: a plain writeFile truncates then writes, so a
80
+ // concurrent reader (resolveSpaceForChat runs on every inbound) can
81
+ // observe an empty file and throw `JSON.parse("")`. rename(2) on the
82
+ // same filesystem is atomic, so readers always see old or new.
83
+ const tmpPath = `${statePath}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;
84
+ await fsPromises.writeFile(tmpPath, JSON.stringify(newState, null, 2), 'utf-8');
85
+ await fsPromises.rename(tmpPath, statePath);
78
86
  resolve(newState);
79
87
  } catch (err) {
80
88
  console.error(`Failed to write Google Chat state:`, err);
@@ -9,11 +9,12 @@ export async function handleAddedToSpace(
9
9
  spaceType: string | undefined,
10
10
  targetChatId: string | null | undefined,
11
11
  mappedChatId: string | null | undefined,
12
- config: GoogleChatConfig
12
+ config: GoogleChatConfig,
13
+ startDir: string = process.cwd()
13
14
  ) {
14
15
  if (spaceType !== 'DIRECT_MESSAGE') {
15
16
  try {
16
- const userAuthClient = await getUserAuthClient(config);
17
+ const userAuthClient = await getUserAuthClient(config, startDir);
17
18
  const tokenResponse = await userAuthClient.getAccessToken();
18
19
  const token = tokenResponse.token;
19
20
 
@@ -48,7 +49,7 @@ export async function handleAddedToSpace(
48
49
  },
49
50
  },
50
51
  };
51
- });
52
+ }, startDir);
52
53
  console.log(`Created subscription ${subData.name} for space ${externalContextId}`);
53
54
  } else {
54
55
  const errText = await res.text();
@@ -79,12 +80,13 @@ export async function handleAddedToSpace(
79
80
  export async function handleRemovedFromSpace(
80
81
  externalContextId: string,
81
82
  currentState: GoogleChatState,
82
- config: GoogleChatConfig
83
+ config: GoogleChatConfig,
84
+ startDir: string = process.cwd()
83
85
  ) {
84
86
  const subId = currentState.channelChatMap?.[externalContextId]?.subscriptionId;
85
87
  if (subId) {
86
88
  try {
87
- const userAuthClient = await getUserAuthClient(config);
89
+ const userAuthClient = await getUserAuthClient(config, startDir);
88
90
  const tokenResponse = await userAuthClient.getAccessToken();
89
91
  const token = tokenResponse.token;
90
92
 
@@ -120,5 +122,5 @@ export async function handleRemovedFromSpace(
120
122
  }
121
123
  }
122
124
  return { channelChatMap: map };
123
- });
125
+ }, startDir);
124
126
  }
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import { resolveCompiledScript } from '../shared/lite.js';
5
+ import { getClawminiDir } from '../shared/workspace.js';
6
+ import { BUILTIN_POLICIES } from '../shared/policies.js';
7
+
8
+ const HASHBANG = '#!/usr/bin/env node\n';
9
+
10
+ function sha256(content: string): string {
11
+ return createHash('sha256').update(content).digest('hex');
12
+ }
13
+
14
+ export async function installBuiltinPolicies(dirPath = getClawminiDir()): Promise<void> {
15
+ const policyScriptsDir = path.join(dirPath, 'policy-scripts');
16
+ await fs.mkdir(policyScriptsDir, { recursive: true });
17
+
18
+ for (const name of Object.keys(BUILTIN_POLICIES)) {
19
+ try {
20
+ const sourcePath = await resolveCompiledScript(name, import.meta.url);
21
+ let scriptContent = await fs.readFile(sourcePath, 'utf8');
22
+ if (!scriptContent.startsWith('#!')) {
23
+ scriptContent = HASHBANG + scriptContent;
24
+ }
25
+
26
+ const destPath = path.join(policyScriptsDir, `${name}.js`);
27
+ let existing: string | null = null;
28
+ try {
29
+ existing = await fs.readFile(destPath, 'utf8');
30
+ } catch {
31
+ // missing — write below
32
+ }
33
+ if (existing !== null && sha256(existing) === sha256(scriptContent)) {
34
+ continue;
35
+ }
36
+ await fs.writeFile(destPath, scriptContent, { mode: 0o755 });
37
+ } catch (err) {
38
+ console.warn(
39
+ `Warning: Could not install built-in policy ${name}:`,
40
+ err instanceof Error ? err.message : String(err)
41
+ );
42
+ }
43
+ }
44
+ }
@@ -2,9 +2,13 @@ import { Command } from 'commander';
2
2
  import {
3
3
  listAgents,
4
4
  getAgent,
5
+ getAgentOverlay,
5
6
  writeAgentSettings,
6
7
  deleteAgent,
7
8
  isValidAgentId,
9
+ refreshAgentTemplate,
10
+ refreshAgentSkills,
11
+ formatPlanActions,
8
12
  } from '../../shared/workspace.js';
9
13
  import { type Agent } from '../../shared/config.js';
10
14
  import { createAgentWithChat } from '../../shared/agent-utils.js';
@@ -57,11 +61,22 @@ agentsCmd
57
61
  '-e, --env <env...>',
58
62
  'Environment variables in KEY=VALUE format (can be specified multiple times)'
59
63
  )
64
+ .option('--fork', 'Copy the template settings into the agent fully (legacy, no auto-update)')
65
+ .option('--force', 'Overwrite existing files in the target directory on first install')
60
66
  .action(
61
- async (id: string, options: { directory?: string; template?: string; env?: string[] }) => {
67
+ async (
68
+ id: string,
69
+ options: {
70
+ directory?: string;
71
+ template?: string;
72
+ env?: string[];
73
+ fork?: boolean;
74
+ force?: boolean;
75
+ }
76
+ ) => {
62
77
  try {
63
78
  assertValidAgentId(id);
64
- const existing = await getAgent(id);
79
+ const existing = await getAgentOverlay(id);
65
80
  if (existing) {
66
81
  throw new Error(`Agent ${id} already exists.`);
67
82
  }
@@ -76,7 +91,11 @@ agentsCmd
76
91
  agentData.env = { ...(agentData.env || {}), ...env };
77
92
  }
78
93
 
79
- await createAgentWithChat(id, agentData, options.template);
94
+ const applyOpts = {
95
+ ...(options.fork ? { fork: true } : {}),
96
+ ...(options.force ? { force: true } : {}),
97
+ };
98
+ await createAgentWithChat(id, agentData, options.template, process.cwd(), applyOpts);
80
99
 
81
100
  console.log(`Agent ${id} created successfully.`);
82
101
  } catch (err) {
@@ -96,7 +115,7 @@ agentsCmd
96
115
  .action(async (id: string, options: { directory?: string; env?: string[] }) => {
97
116
  try {
98
117
  assertValidAgentId(id);
99
- const existing = await getAgent(id);
118
+ const existing = await getAgentOverlay(id);
100
119
  if (!existing) {
101
120
  throw new Error(`Agent ${id} does not exist.`);
102
121
  }
@@ -125,7 +144,7 @@ agentsCmd
125
144
  .action(async (id: string) => {
126
145
  try {
127
146
  assertValidAgentId(id);
128
- const existing = await getAgent(id);
147
+ const existing = await getAgentOverlay(id);
129
148
  if (!existing) {
130
149
  throw new Error(`Agent ${id} does not exist.`);
131
150
  }
@@ -136,3 +155,38 @@ agentsCmd
136
155
  handleError('delete agent', err);
137
156
  }
138
157
  });
158
+
159
+ agentsCmd
160
+ .command('refresh <id>')
161
+ .description("Refresh the agent's tracked template files against the installed clawmini")
162
+ .option('--accept', 'Overwrite files that have diverged from the recorded SHA')
163
+ .option('--dry-run', 'Print the per-file plan without writing anything')
164
+ .action(async (id: string, options: { accept?: boolean; dryRun?: boolean }) => {
165
+ try {
166
+ assertValidAgentId(id);
167
+ const overlay = await getAgentOverlay(id);
168
+ if (!overlay) {
169
+ throw new Error(`Agent ${id} does not exist.`);
170
+ }
171
+ const refreshOpts = {
172
+ ...(options.accept === undefined ? {} : { accept: options.accept }),
173
+ ...(options.dryRun === undefined ? {} : { dryRun: options.dryRun }),
174
+ };
175
+ if (overlay.extends) {
176
+ const plan = await refreshAgentTemplate(id, overlay, process.cwd(), refreshOpts);
177
+ if (plan) {
178
+ for (const line of formatPlanActions(plan)) console.log(line);
179
+ }
180
+ }
181
+
182
+ const resolved = await getAgent(id);
183
+ if (resolved) {
184
+ const skillsPlan = await refreshAgentSkills(id, resolved, process.cwd(), refreshOpts);
185
+ if (skillsPlan) {
186
+ for (const line of formatPlanActions(skillsPlan)) console.log(line);
187
+ }
188
+ }
189
+ } catch (err) {
190
+ handleError('refresh agent', err);
191
+ }
192
+ });
@@ -1,18 +1,70 @@
1
1
  import { Command } from 'commander';
2
2
  import { getDaemonClient } from '../client.js';
3
3
  import { getSocketPath } from '../../shared/workspace.js';
4
+ import { readSupervisorPid, removeSupervisorPid } from '../supervisor-pid.js';
4
5
  import fs from 'node:fs';
5
6
 
7
+ function isErrnoCode(err: unknown, code: string): boolean {
8
+ return err instanceof Error && (err as NodeJS.ErrnoException).code === code;
9
+ }
10
+
11
+ async function stopSupervisor(pid: number): Promise<void> {
12
+ process.stdout.write(`Stopping clawmini supervisor (pid ${pid})`);
13
+ try {
14
+ process.kill(pid, 'SIGTERM');
15
+ } catch (err) {
16
+ if (isErrnoCode(err, 'ESRCH')) {
17
+ process.stdout.write('\nSupervisor already exited.\n');
18
+ return;
19
+ }
20
+ throw new Error(
21
+ `Failed to signal supervisor: ${err instanceof Error ? err.message : String(err)}`,
22
+ { cause: err }
23
+ );
24
+ }
25
+
26
+ // Poll until the process is gone or we time out. Must exceed the
27
+ // supervisor's internal phase-2 timeout (60s for the daemon) so we don't
28
+ // bail while it's still draining `down` hooks.
29
+ const TIMEOUT_MS = 90_000;
30
+ const deadline = Date.now() + TIMEOUT_MS;
31
+ while (Date.now() < deadline) {
32
+ await new Promise((r) => setTimeout(r, 200));
33
+ process.stdout.write('.');
34
+ try {
35
+ process.kill(pid, 0);
36
+ } catch {
37
+ process.stdout.write('\nSuccessfully shut down clawmini supervisor.\n');
38
+ return;
39
+ }
40
+ }
41
+ throw new Error(`Supervisor did not exit within ${TIMEOUT_MS / 1000} seconds.`);
42
+ }
43
+
6
44
  export const downCmd = new Command('down')
7
- .description('Stop the local clawmini daemon server')
45
+ .description('Stop the local clawmini supervisor or daemon')
8
46
  .action(async () => {
47
+ const supPid = readSupervisorPid();
48
+ if (supPid) {
49
+ try {
50
+ await stopSupervisor(supPid);
51
+ return;
52
+ } catch (err) {
53
+ console.error('\n', err instanceof Error ? err.message : String(err));
54
+ process.exit(1);
55
+ }
56
+ }
57
+
58
+ // No live supervisor — drop any stale pid file so future commands
59
+ // aren't confused by it.
60
+ removeSupervisorPid();
61
+
9
62
  try {
10
63
  const client = await getDaemonClient({ autoStart: false });
11
64
  process.stdout.write('Shutting down clawmini daemon...');
12
65
  await client.shutdown.mutate();
13
66
 
14
67
  const socketPath = getSocketPath();
15
- // Wait for the socket file to be removed by the daemon's exit handler
16
68
  while (fs.existsSync(socketPath)) {
17
69
  await new Promise((resolve) => setTimeout(resolve, 200));
18
70
  process.stdout.write('.');
@@ -8,9 +8,15 @@ environmentsCmd
8
8
  .command('enable <name>')
9
9
  .description('Enable an environment for a path in the workspace')
10
10
  .option('-p, --path <subpath>', 'Path to apply the environment to', './')
11
- .action(async (name: string, options: { path: string }) => {
11
+ .option('--fork', 'Clone the built-in template directory instead of writing a thin overlay')
12
+ .action(async (name: string, options: { path: string; fork?: boolean }) => {
12
13
  try {
13
- await enableEnvironment(name, options.path);
14
+ await enableEnvironment(
15
+ name,
16
+ options.path,
17
+ process.cwd(),
18
+ options.fork ? { fork: true } : {}
19
+ );
14
20
  } catch (err) {
15
21
  handleError('enable environment', err);
16
22
  }
@@ -1,11 +1,29 @@
1
1
  import { Command } from 'commander';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
4
5
  import { isValidAgentId, enableEnvironment } from '../../shared/workspace.js';
5
6
  import { setDefaultChatId } from '../../shared/chats.js';
6
7
  import { type Agent } from '../../shared/config.js';
7
8
  import { createAgentWithChat } from '../../shared/agent-utils.js';
8
9
  import { handleError } from '../utils.js';
10
+ import { installBuiltinPolicies } from '../builtin-policies.js';
11
+
12
+ function readBackupGitignoreTemplate(): string | null {
13
+ let currentDir = path.dirname(fileURLToPath(import.meta.url));
14
+ while (
15
+ currentDir !== path.parse(currentDir).root &&
16
+ !fs.existsSync(path.join(currentDir, 'package.json'))
17
+ ) {
18
+ currentDir = path.dirname(currentDir);
19
+ }
20
+ const templatePath = path.join(currentDir, 'docs', 'backups', 'clawmini.gitignore');
21
+ try {
22
+ return fs.readFileSync(templatePath, 'utf8');
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
9
27
 
10
28
  export const initCmd = new Command('init')
11
29
  .description('Initialize a new .clawmini settings folder')
@@ -47,6 +65,19 @@ export const initCmd = new Command('init')
47
65
  fs.writeFileSync(settingsPath, JSON.stringify(defaultSettings, null, 2));
48
66
  console.log('Initialized .clawmini/settings.json');
49
67
 
68
+ const gitignoreTemplate = readBackupGitignoreTemplate();
69
+ if (gitignoreTemplate) {
70
+ const gitignorePath = path.join(dirPath, '.gitignore');
71
+ if (!fs.existsSync(gitignorePath)) {
72
+ fs.writeFileSync(gitignorePath, gitignoreTemplate);
73
+ console.log('Initialized .clawmini/.gitignore');
74
+ }
75
+ } else {
76
+ console.warn('Warning: backup .gitignore template not found; skipping .clawmini/.gitignore');
77
+ }
78
+
79
+ await installBuiltinPolicies(dirPath);
80
+
50
81
  if (options.agent) {
51
82
  try {
52
83
  const agentId = options.agent;
@@ -0,0 +1,116 @@
1
+ import { Command } from 'commander';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ import { getClawminiDir } from '../../shared/workspace.js';
6
+
7
+ const SERVICE_ALIASES: Record<string, string> = {
8
+ daemon: 'daemon',
9
+ web: 'web',
10
+ discord: 'adapter-discord',
11
+ 'adapter-discord': 'adapter-discord',
12
+ 'google-chat': 'adapter-google-chat',
13
+ 'adapter-google-chat': 'adapter-google-chat',
14
+ supervisor: 'supervisor',
15
+ };
16
+
17
+ function displayNameFor(logBase: string): string {
18
+ if (logBase.startsWith('adapter-')) return logBase.slice('adapter-'.length);
19
+ return logBase;
20
+ }
21
+
22
+ function tailString(content: string, n: number): string[] {
23
+ const lines = content.split('\n');
24
+ if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
25
+ return lines.slice(-n);
26
+ }
27
+
28
+ interface LogsOptions {
29
+ follow?: boolean;
30
+ service?: string;
31
+ lines?: string;
32
+ }
33
+
34
+ export const logsCmd = new Command('logs')
35
+ .description('View logs from clawmini services (daemon, web, adapters)')
36
+ .option('-f, --follow', 'Follow the logs as new lines are written')
37
+ .option(
38
+ '-s, --service <name>',
39
+ 'Restrict to one service: daemon, web, discord, google-chat, supervisor'
40
+ )
41
+ .option('-n, --lines <count>', 'Number of lines to show from the tail of each file', '50')
42
+ .action(async (options: LogsOptions) => {
43
+ const logDir = path.join(getClawminiDir(), 'logs');
44
+ if (!fs.existsSync(logDir)) {
45
+ console.error(`No log directory at ${logDir}. Start the supervisor with 'clawmini serve'.`);
46
+ process.exit(1);
47
+ }
48
+
49
+ let targets: string[];
50
+ if (options.service) {
51
+ const resolved = SERVICE_ALIASES[options.service];
52
+ if (!resolved) {
53
+ console.error(
54
+ `Unknown service '${options.service}'. Valid: ${Object.keys(SERVICE_ALIASES).join(', ')}`
55
+ );
56
+ process.exit(1);
57
+ }
58
+ targets = [`${resolved}.log`];
59
+ } else {
60
+ targets = fs.readdirSync(logDir).filter((f) => f.endsWith('.log'));
61
+ }
62
+
63
+ const linesCount = Math.max(0, parseInt(options.lines ?? '50', 10) || 50);
64
+
65
+ for (const file of targets) {
66
+ const full = path.join(logDir, file);
67
+ if (!fs.existsSync(full)) continue;
68
+ const prefix = `[${displayNameFor(file.replace(/\.log$/, ''))}] `;
69
+ const content = fs.readFileSync(full, 'utf-8');
70
+ for (const line of tailString(content, linesCount)) {
71
+ process.stdout.write(prefix + line + '\n');
72
+ }
73
+ }
74
+
75
+ if (!options.follow) return;
76
+
77
+ const watched: Array<{ file: string; full: string; prefix: string; position: number }> = [];
78
+ for (const file of targets) {
79
+ const full = path.join(logDir, file);
80
+ const prefix = `[${displayNameFor(file.replace(/\.log$/, ''))}] `;
81
+ const position = fs.existsSync(full) ? fs.statSync(full).size : 0;
82
+ watched.push({ file, full, prefix, position });
83
+ fs.watchFile(full, { interval: 500 }, (curr) => {
84
+ const entry = watched.find((w) => w.full === full);
85
+ if (!entry) return;
86
+ if (curr.size < entry.position) {
87
+ entry.position = 0;
88
+ }
89
+ if (curr.size > entry.position) {
90
+ const fd = fs.openSync(full, 'r');
91
+ const buf = Buffer.alloc(curr.size - entry.position);
92
+ fs.readSync(fd, buf, 0, buf.length, entry.position);
93
+ fs.closeSync(fd);
94
+ entry.position = curr.size;
95
+
96
+ const text = buf.toString();
97
+ const lines = text.split('\n');
98
+ if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
99
+ for (const line of lines) {
100
+ process.stdout.write(entry.prefix + line + '\n');
101
+ }
102
+ }
103
+ });
104
+ }
105
+
106
+ const stop = () => {
107
+ for (const entry of watched) fs.unwatchFile(entry.full);
108
+ process.exit(0);
109
+ };
110
+ process.on('SIGINT', stop);
111
+ process.on('SIGTERM', stop);
112
+
113
+ await new Promise<void>(() => {
114
+ /* intentionally unresolved; signals drive termination */
115
+ });
116
+ });
@@ -3,10 +3,10 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { handleError } from '../utils.js';
5
5
  import { resolveCompiledScript } from '../../shared/lite.js';
6
- import type { PolicyConfig } from '../../shared/policies.js';
6
+ import { BUILTIN_POLICIES, type PolicyConfig } from '../../shared/policies.js';
7
7
  import { getClawminiDir } from '../../shared/workspace.js';
8
8
 
9
- const SUPPORTED_POLICIES = ['propose-policy'];
9
+ const SUPPORTED_POLICIES = ['manage-policies'];
10
10
 
11
11
  export const policiesCmd = new Command('policies').description('Manage sandbox policies');
12
12
 
@@ -44,10 +44,12 @@ policiesCmd
44
44
  policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
45
45
  }
46
46
 
47
+ const builtin = BUILTIN_POLICIES[name];
47
48
  policies.policies[name] = {
48
- description: 'Propose a new policy to create',
49
+ description: builtin?.description ?? `Built-in policy ${name}`,
49
50
  command: `./.clawmini/policy-scripts/${name}.js`,
50
- allowHelp: true,
51
+ allowHelp: builtin?.allowHelp ?? true,
52
+ ...(builtin?.autoApprove !== undefined ? { autoApprove: builtin.autoApprove } : {}),
51
53
  };
52
54
 
53
55
  fs.writeFileSync(policiesPath, JSON.stringify(policies, null, 2));