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.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.github/workflows/release.yml +49 -0
- package/CHANGELOG.md +36 -0
- package/README.md +5 -4
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +465 -282
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.mjs +367 -243
- package/dist/adapter-google-chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +684 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +43 -13
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
- package/dist/cli/manage-policies.mjs.map +1 -0
- package/dist/cli/run-host.d.mts +1 -0
- package/dist/cli/run-host.mjs +3090 -0
- package/dist/cli/run-host.mjs.map +1 -0
- package/dist/config-CPFQIGdG.mjs +57 -0
- package/dist/config-CPFQIGdG.mjs.map +1 -0
- package/dist/config-Dvl-Pov4.mjs +76 -0
- package/dist/config-Dvl-Pov4.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +970 -332
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
- package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/dist/web/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.Dr0ot9sV.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/workspace-oWmVh5mi.mjs +1001 -0
- package/dist/workspace-oWmVh5mi.mjs.map +1 -0
- package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
- package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
- package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
- package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
- package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
- package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
- package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
- package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
- package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
- package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
- package/docs/25_e2e_test_improvements/development_log.md +30 -0
- package/docs/25_e2e_test_improvements/notes.md +29 -0
- package/docs/25_e2e_test_improvements/prd.md +43 -0
- package/docs/25_e2e_test_improvements/questions.md +12 -0
- package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
- package/docs/25_e2e_test_improvements/tickets.md +22 -0
- package/docs/25_policy_cwd/development_log.md +30 -0
- package/docs/25_policy_cwd/notes.md +28 -0
- package/docs/25_policy_cwd/prd.md +77 -0
- package/docs/25_policy_cwd/questions.md +6 -0
- package/docs/25_policy_cwd/tickets.md +77 -0
- package/docs/CLI_REFERENCE.md +3 -1
- package/docs/PHILOSOPHY.md +35 -0
- package/docs/adapter-visibility/SPEC.md +461 -0
- package/docs/adapter-visibility/SPEC_v2.md +202 -0
- package/docs/auto-update/SPEC.md +344 -0
- package/docs/backups/SPEC.md +296 -0
- package/docs/backups/clawmini.gitignore +69 -0
- package/docs/guides/assets/clawmini-avatar.png +0 -0
- package/docs/guides/backups.md +332 -0
- package/docs/guides/discord_adapter_setup.md +1 -1
- package/docs/guides/google_chat_adapter_setup.md +81 -0
- package/docs/unified-startup/SPEC.md +203 -0
- package/e2e/_helpers/test-environment.test.ts +49 -0
- package/e2e/_helpers/test-environment.ts +548 -0
- package/e2e/adapters/_google-chat-fixtures.ts +340 -0
- package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
- package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
- package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
- package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
- package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
- package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
- package/e2e/agents/custom-api-env.test.ts +80 -0
- package/e2e/agents/export-lite-func.test.ts +104 -0
- package/e2e/agents/fallbacks.test.ts +124 -0
- package/e2e/agents/interrupt.test.ts +50 -0
- package/e2e/agents/no-reply-necessary.test.ts +57 -0
- package/e2e/agents/session-timeout-subagents.test.ts +76 -0
- package/e2e/agents/subagent-authorization.test.ts +246 -0
- package/e2e/agents/subagent-env.test.ts +49 -0
- package/e2e/agents/subagent-lifecycle.test.ts +782 -0
- package/e2e/agents/subagents-depth.test.ts +47 -0
- package/e2e/cli/agents.test.ts +176 -0
- package/e2e/cli/auto-update.test.ts +741 -0
- package/e2e/cli/basic.test.ts +44 -0
- package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
- package/e2e/cli/init-gitignore.test.ts +86 -0
- package/e2e/cli/init.test.ts +76 -0
- package/e2e/cli/messages.test.ts +363 -0
- package/e2e/cli/serve.test.ts +76 -0
- package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
- package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
- package/e2e/jobs/agent-jobs.test.ts +216 -0
- package/e2e/jobs/cron.test.ts +64 -0
- package/e2e/jobs/restart.test.ts +108 -0
- package/e2e/policies/approval-session.test.ts +69 -0
- package/e2e/policies/auto-create-policies-file.test.ts +35 -0
- package/e2e/policies/builtin-manage-policies.test.ts +184 -0
- package/e2e/policies/builtin-run-host.test.ts +180 -0
- package/e2e/policies/environment-policies.test.ts +177 -0
- package/e2e/policies/manage-policies.test.ts +566 -0
- package/e2e/policies/output-size.test.ts +98 -0
- package/e2e/policies/policies-context-cwd.test.ts +160 -0
- package/e2e/policies/relative-script-path.test.ts +60 -0
- package/e2e/policies/requests-show.test.ts +135 -0
- package/e2e/policies/requests.test.ts +208 -0
- package/e2e/policies/slash-policies.test.ts +308 -0
- package/e2e/policies/startup-cleanup.test.ts +48 -0
- package/e2e/routers/session-timeout.test.ts +106 -0
- package/e2e/routers/slash-model.test.ts +152 -0
- package/e2e/routers/slash-new.test.ts +50 -0
- package/e2e/routers/slash-restart-adapter.test.ts +96 -0
- package/e2e/routers/slash-restart.test.ts +114 -0
- package/e2e/routers/slash-shutdown.test.ts +55 -0
- package/e2e/routers/slash-stop.test.ts +232 -0
- package/e2e/routers/slash-upgrade.test.ts +88 -0
- package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
- package/eslint.config.js +6 -0
- package/napkin.md +1 -1
- package/package.json +8 -3
- package/src/adapter-discord/commands.test.ts +42 -0
- package/src/adapter-discord/commands.ts +33 -0
- package/src/adapter-discord/config.ts +12 -0
- package/src/adapter-discord/forwarder.test.ts +499 -21
- package/src/adapter-discord/forwarder.ts +343 -124
- package/src/adapter-discord/inbound-cache.test.ts +47 -0
- package/src/adapter-discord/inbound-cache.ts +37 -0
- package/src/adapter-discord/index.test.ts +67 -2
- package/src/adapter-discord/index.ts +84 -216
- package/src/adapter-discord/interactions.test.ts +54 -3
- package/src/adapter-discord/interactions.ts +97 -53
- package/src/adapter-discord/processMessage.ts +239 -0
- package/src/adapter-discord/state.ts +1 -0
- package/src/adapter-google-chat/auth.test.ts +9 -5
- package/src/adapter-google-chat/auth.ts +29 -23
- package/src/adapter-google-chat/cards.ts +7 -2
- package/src/adapter-google-chat/client.test.ts +37 -2
- package/src/adapter-google-chat/client.ts +138 -38
- package/src/adapter-google-chat/config.ts +19 -0
- package/src/adapter-google-chat/forwarder.test.ts +81 -56
- package/src/adapter-google-chat/forwarder.ts +394 -185
- package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
- package/src/adapter-google-chat/inbound-cache.ts +36 -0
- package/src/adapter-google-chat/state.test.ts +1 -0
- package/src/adapter-google-chat/state.ts +9 -1
- package/src/adapter-google-chat/subscriptions.ts +8 -6
- package/src/cli/builtin-policies.ts +44 -0
- package/src/cli/commands/agents.ts +59 -5
- package/src/cli/commands/down.ts +54 -2
- package/src/cli/commands/environments.ts +8 -2
- package/src/cli/commands/init.ts +31 -0
- package/src/cli/commands/logs.ts +116 -0
- package/src/cli/commands/policies.ts +6 -4
- package/src/cli/commands/serve.test.ts +67 -0
- package/src/cli/commands/serve.ts +284 -0
- package/src/cli/commands/up.ts +122 -2
- package/src/cli/commands/web-api/agents.ts +3 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/install-detection.test.ts +72 -0
- package/src/cli/install-detection.ts +48 -0
- package/src/cli/lite.ts +54 -22
- package/src/cli/manage-policies-utils.ts +104 -0
- package/src/cli/manage-policies.ts +291 -0
- package/src/cli/run-host.ts +45 -0
- package/src/cli/supervisor-actions.ts +267 -0
- package/src/cli/supervisor-control.test.ts +129 -0
- package/src/cli/supervisor-control.ts +155 -0
- package/src/cli/supervisor-pid.ts +68 -0
- package/src/cli/supervisor.ts +277 -0
- package/src/daemon/agent/agent-context.ts +11 -11
- package/src/daemon/agent/agent-session.ts +8 -1
- package/src/daemon/agent/chat-logger.test.ts +78 -9
- package/src/daemon/agent/chat-logger.ts +25 -5
- package/src/daemon/agent/turn-registry.test.ts +89 -0
- package/src/daemon/agent/turn-registry.ts +94 -0
- package/src/daemon/agent/types.ts +2 -0
- package/src/daemon/api/agent-policy-endpoints.ts +263 -0
- package/src/daemon/api/agent-router.ts +47 -126
- package/src/daemon/api/index.test.ts +1 -0
- package/src/daemon/api/policy-request.test.ts +7 -5
- package/src/daemon/api/router-utils.ts +6 -5
- package/src/daemon/api/subagent-router.ts +110 -74
- package/src/daemon/api/subagent-utils.test.ts +60 -0
- package/src/daemon/api/subagent-utils.ts +113 -87
- package/src/daemon/api/user-router.ts +34 -8
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/cron.test.ts +62 -4
- package/src/daemon/cron.ts +42 -16
- package/src/daemon/events.ts +65 -0
- package/src/daemon/index.ts +24 -1
- package/src/daemon/message-interruption.test.ts +1 -0
- package/src/daemon/message-jobs.test.ts +1 -0
- package/src/daemon/message.ts +78 -14
- package/src/daemon/observation.test.ts +26 -18
- package/src/daemon/pending-replies.test.ts +112 -0
- package/src/daemon/pending-replies.ts +162 -0
- package/src/daemon/policy-request-service.ts +3 -1
- package/src/daemon/policy-utils.test.ts +66 -1
- package/src/daemon/policy-utils.ts +126 -1
- package/src/daemon/request-store.ts +31 -0
- package/src/daemon/routers/session-timeout.ts +4 -0
- package/src/daemon/routers/slash-model.test.ts +344 -0
- package/src/daemon/routers/slash-model.ts +207 -0
- package/src/daemon/routers/slash-policies.test.ts +38 -32
- package/src/daemon/routers/slash-policies.ts +84 -33
- package/src/daemon/routers/slash-restart.test.ts +69 -0
- package/src/daemon/routers/slash-restart.ts +36 -0
- package/src/daemon/routers/slash-shutdown.test.ts +50 -0
- package/src/daemon/routers/slash-shutdown.ts +28 -0
- package/src/daemon/routers/slash-upgrade.test.ts +116 -0
- package/src/daemon/routers/slash-upgrade.ts +76 -0
- package/src/daemon/routers/types.ts +7 -0
- package/src/daemon/routers.ts +16 -0
- package/src/shared/adapters/blockquote.test.ts +28 -0
- package/src/shared/adapters/blockquote.ts +20 -0
- package/src/shared/adapters/filtering.test.ts +224 -10
- package/src/shared/adapters/filtering.ts +95 -7
- package/src/shared/adapters/inbound-cache.test.ts +48 -0
- package/src/shared/adapters/inbound-cache.ts +54 -0
- package/src/shared/adapters/turn-log-buffer.ts +266 -0
- package/src/shared/adapters/turn-log.test.ts +389 -0
- package/src/shared/adapters/turn-log.ts +357 -0
- package/src/shared/agent-utils.ts +12 -5
- package/src/shared/chats.test.ts +4 -0
- package/src/shared/chats.ts +9 -0
- package/src/shared/config.ts +16 -1
- package/src/shared/lite.ts +76 -2
- package/src/shared/policies.ts +26 -0
- package/src/shared/template-manifest.ts +267 -0
- package/src/shared/utils/shell.ts +61 -0
- package/src/shared/version.ts +34 -0
- package/src/shared/workspace.test.ts +217 -0
- package/src/shared/workspace.ts +626 -48
- package/templates/environments/cladding/allowlist-domain.mjs +125 -0
- package/templates/environments/cladding/env.json +21 -1
- package/templates/environments/cladding/run-with-network.mjs +54 -0
- package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
- package/templates/environments/macos-proxy/env.json +8 -1
- package/templates/environments/macos-proxy/proxy.mjs +42 -13
- package/templates/gemini/template.json +5 -0
- package/templates/gemini-claw/template.json +13 -0
- package/templates/skills/clawmini-requests/SKILL.md +69 -10
- package/templates/skills/run-host/SKILL.md +51 -0
- package/templates/skills/skill-creator/SKILL.md +4 -3
- package/templates/skills/skill-creator/scripts/validate.sh +52 -0
- package/tsdown.config.ts +10 -1
- package/vitest.config.ts +2 -2
- package/web/.svelte-kit/ambient.d.ts +292 -176
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.Dr0ot9sV.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
- package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
- package/web/.svelte-kit/output/server/chunks/client.js +1 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
- package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
- package/web/.svelte-kit/output/server/chunks/root.js +739 -788
- package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
- package/web/.svelte-kit/output/server/index.js +126 -90
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/output/server/remote-entry.js +245 -81
- package/web/.svelte-kit/tsconfig.json +4 -1
- package/dist/cli/propose-policy.mjs.map +0 -1
- package/dist/lite-CBxOT1y5.mjs +0 -241
- package/dist/lite-CBxOT1y5.mjs.map +0 -1
- package/dist/routing-D8rTxtaV.mjs +0 -245
- package/dist/routing-D8rTxtaV.mjs.map +0 -1
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
- package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/dist/web/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/dist/workspace-BJmJBfKi.mjs +0 -456
- package/dist/workspace-BJmJBfKi.mjs.map +0 -1
- package/src/cli/e2e/agents.test.ts +0 -140
- package/src/cli/e2e/basic.test.ts +0 -43
- package/src/cli/e2e/cron.test.ts +0 -132
- package/src/cli/e2e/export-lite-func.test.ts +0 -206
- package/src/cli/e2e/fallbacks.test.ts +0 -175
- package/src/cli/e2e/init.test.ts +0 -77
- package/src/cli/e2e/messages.test.ts +0 -332
- package/src/cli/e2e/propose-policy.test.ts +0 -203
- package/src/cli/e2e/requests.test.ts +0 -180
- package/src/cli/e2e/session-timeout.test.ts +0 -192
- package/src/cli/e2e/slash-new.test.ts +0 -93
- package/src/cli/e2e/subagents.test.ts +0 -106
- package/src/cli/e2e/utils.ts +0 -66
- package/src/cli/propose-policy.ts +0 -91
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/web/.svelte-kit/output/server/chunks/false.js +0 -4
- /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
- /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Adapter Visibility v2: Simplifications
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
v1 ([SPEC.md](./SPEC.md)) shipped a threaded activity log for Google Chat. Four things in that design are more complex than they need to be. v2 is a refactor — same user-visible behavior, smaller surface area.
|
|
6
|
+
|
|
7
|
+
The four changes, roughly ordered from least to most disruptive:
|
|
8
|
+
|
|
9
|
+
1. Merge `turnStarted` / `turnEnded` into the existing chat-message stream.
|
|
10
|
+
2. Keep `subagent_update` on the parent's original `turnId` instead of minting a new one.
|
|
11
|
+
3. Fire `turnEnded` only after all async subagents for that turn have settled.
|
|
12
|
+
4. Drop the on-disk inbound ring buffer in favor of an in-memory map shared within the adapter process.
|
|
13
|
+
|
|
14
|
+
None of these change the `visibility.*` config surface or the `Destination` routing from v1.
|
|
15
|
+
|
|
16
|
+
## 1. Single event stream
|
|
17
|
+
|
|
18
|
+
### Today
|
|
19
|
+
|
|
20
|
+
`src/daemon/api/turns-router.ts` exposes `waitForTurns`, a separate tRPC subscription alongside `waitForMessages`. The forwarder runs both subscriptions per chat and serializes their outputs through a shared `messageQueue` to avoid `turnStarted`-vs-first-message ordering races.
|
|
21
|
+
|
|
22
|
+
### v2
|
|
23
|
+
|
|
24
|
+
Collapse turn lifecycle events into `waitForMessages`. The stream yields a discriminated union:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
type ChatStreamItem =
|
|
28
|
+
| { kind: 'message'; message: ChatMessage }
|
|
29
|
+
| { kind: 'turn'; event: TurnLifecycleEvent };
|
|
30
|
+
|
|
31
|
+
type TurnLifecycleEvent =
|
|
32
|
+
| { type: 'started'; turnId: string; rootMessageId: string; externalRef?: string }
|
|
33
|
+
| { type: 'ended'; turnId: string; outcome: 'ok' | 'error' };
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The daemon emits turn events onto the same `daemonEvents` channel as messages (or a merged one), and `waitForMessages`'s subscription generator interleaves them in emission order.
|
|
37
|
+
|
|
38
|
+
### Why
|
|
39
|
+
|
|
40
|
+
- The ordering race the `messageQueue` exists to solve disappears — events arrive in emission order on a single subscription.
|
|
41
|
+
- One subscription per chat instead of two; fewer reconnect cursors, fewer error paths.
|
|
42
|
+
- `turns-router.ts` and its merge-two-async-iterators machinery delete entirely.
|
|
43
|
+
|
|
44
|
+
### Cost
|
|
45
|
+
|
|
46
|
+
- `waitForMessages` is no longer "only chat messages" — consumers must handle the envelope. For CLI-style consumers that don't care about turns, this is one `if (item.kind !== 'message') continue` line.
|
|
47
|
+
- Backpressure/ordering across the two event types now lives in the daemon's emitter, not the adapter. Straightforward since `EventEmitter` is already synchronous FIFO.
|
|
48
|
+
|
|
49
|
+
### Migration
|
|
50
|
+
|
|
51
|
+
`waitForTurns` remains for one release as a thin shim that filters the merged stream, then is removed. Forwarder migrates to the merged stream first; other adapters are unaffected.
|
|
52
|
+
|
|
53
|
+
## 2. Subagent completion stays on the parent's turn
|
|
54
|
+
|
|
55
|
+
### Today
|
|
56
|
+
|
|
57
|
+
`src/daemon/api/subagent-utils.ts` inherits `parentTurnId` for the synchronous execution phase, but when an async subagent finishes and calls back into the parent agent via the `subagent_update` system event, `executeDirectMessage` is invoked *without* a `parentTurnId`, minting a fresh `turnId`. That fragments the activity log: the delegated work and the parent's response-to-completion land on different turns.
|
|
58
|
+
|
|
59
|
+
The code comment at `subagent-utils.ts:~125` explicitly calls this out (`// no parentTurnId — this starts a fresh turn for the parent`). It's a workaround for the fact that, under v1's semantics, the original turn already ended when the parent's agent loop exited.
|
|
60
|
+
|
|
61
|
+
### v2
|
|
62
|
+
|
|
63
|
+
`subagent_update` inherits the original parent `turnId`. The turn represents the *logical conversation turn*, not a single agent-loop invocation. Delegated work, async completion, and the parent's follow-up response are all the same turn.
|
|
64
|
+
|
|
65
|
+
Concretely: `executeSubagent` captures the parent's `turnId` at spawn time (it already does, for the sync path) and passes it through to the `subagent_update` `executeDirectMessage` call.
|
|
66
|
+
|
|
67
|
+
### Why
|
|
68
|
+
|
|
69
|
+
- Matches the reader's mental model: one user message → one logical turn → one activity log.
|
|
70
|
+
- Eliminates a class of "orphan turn" bugs in the forwarder where a proactive-anchor workaround compensates for a turn that shouldn't have existed at all.
|
|
71
|
+
- `proactiveAnchors` in the forwarder shrinks in scope — it's only needed for *actually* proactive turns (cron), not for subagent completions.
|
|
72
|
+
|
|
73
|
+
### Cost
|
|
74
|
+
|
|
75
|
+
- Couples to change 3. If a subagent completion extends the original turn, `turnEnded` can't fire until completions settle — otherwise the forwarder sees `turnEnded` while new activity is still arriving.
|
|
76
|
+
|
|
77
|
+
## 3. `turnEnded` waits for subagents
|
|
78
|
+
|
|
79
|
+
### Today
|
|
80
|
+
|
|
81
|
+
`turnEnded` fires when `agentSession.handleMessage`'s promise settles — i.e., when the parent agent's loop exits. Async subagents spawned during that loop may still be running. The forwarder compensates by keeping `TurnContext` alive past `turnEnded` via LRU retention, so late subagent activity still lands on the right log.
|
|
82
|
+
|
|
83
|
+
### v2
|
|
84
|
+
|
|
85
|
+
The daemon tracks per-turn outstanding subagents and fires `turnEnded` only when the count reaches zero.
|
|
86
|
+
|
|
87
|
+
Implementation sketch:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// src/daemon/agent/turn-registry.ts
|
|
91
|
+
const outstanding = new Map<string, number>(); // turnId → active subagent count
|
|
92
|
+
|
|
93
|
+
export function incrementSubagent(turnId: string) { ... }
|
|
94
|
+
export function decrementSubagent(turnId: string) {
|
|
95
|
+
// when count hits 0 AND the parent loop has exited, emit turnEnded
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- `executeSubagent` increments on spawn, decrements on completion.
|
|
100
|
+
- `executeDirectMessage` marks "parent loop exited" on promise settle; emits `turnEnded` if outstanding count is already 0, otherwise defers to the final subagent's decrement.
|
|
101
|
+
- Add a per-turn timeout (config: `turnMaxDurationMs`, default ~30min) that force-fires `turnEnded` with `outcome: 'error'` if the count never drains — protects against stuck subagents pinning the log open forever.
|
|
102
|
+
|
|
103
|
+
### Why
|
|
104
|
+
|
|
105
|
+
- Removes the forwarder's need to retain `TurnContext` past `turnEnded`. Cleanup becomes immediate and obvious.
|
|
106
|
+
- `turnEnded` becomes a meaningful signal consumers can trust for "activity is done."
|
|
107
|
+
- Combined with change 2, the LRU cap on `turnContexts` in the forwarder can go away — contexts live for the duration of their turn and are deleted on `turnEnded`.
|
|
108
|
+
|
|
109
|
+
### Cost
|
|
110
|
+
|
|
111
|
+
- One new piece of daemon state (outstanding-count map).
|
|
112
|
+
- A stuck subagent now pins the turn until the timeout fires. Acceptable with a reasonable default; operators can tune.
|
|
113
|
+
|
|
114
|
+
### Optional follow-up
|
|
115
|
+
|
|
116
|
+
If any consumer needs the "agent stopped typing" signal distinct from "turn fully settled," emit a separate `turnReplyComplete` event when the parent loop exits. **Don't add this preemptively** — wait for a consumer to ask.
|
|
117
|
+
|
|
118
|
+
## 4. Drop the disk-persisted inbound ring buffer
|
|
119
|
+
|
|
120
|
+
### Today
|
|
121
|
+
|
|
122
|
+
`src/adapter-google-chat/state.ts` stores `recentMessages` (ring buffer, 50 entries per space) on disk at `.clawmini/adapters/google-chat/state.json`. On every inbound, `recordInboundMessage` appends; on every `turnStarted`, `resolveInboundByGchatMessageName` reads + Zod-parses the file to find the thread anchor.
|
|
123
|
+
|
|
124
|
+
The only reason it's on disk is historical — the adapter once ran ingestion and forwarding in separate processes. They don't anymore.
|
|
125
|
+
|
|
126
|
+
### v2
|
|
127
|
+
|
|
128
|
+
The adapter's ingestion and forwarder run in the same Node process (`startGoogleChatIngestion` and `startDaemonToGoogleChatForwarder` start side-by-side in `index.ts`). Share an in-memory map:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// src/adapter-google-chat/inbound-cache.ts
|
|
132
|
+
interface InboundRecord {
|
|
133
|
+
gchatMessageName: string;
|
|
134
|
+
gchatThreadName: string;
|
|
135
|
+
receivedAt: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cache = new Map<string, InboundRecord>(); // keyed by gchatMessageName
|
|
139
|
+
|
|
140
|
+
export function recordInbound(r: InboundRecord) { ... }
|
|
141
|
+
export function resolveInbound(gchatMessageName: string): InboundRecord | null { ... }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- Sweep entries older than `INBOUND_TTL_MS` (default 10min) on every insert — bounded memory without an LRU.
|
|
145
|
+
- `externalRef` on the tRPC wire stays unchanged — still the correlation key between a daemon turn and a GChat message.
|
|
146
|
+
- `state.json` schema loses `channelChatMap[space].recentMessages` and its `RecentInboundEntrySchema`.
|
|
147
|
+
|
|
148
|
+
### Companion change: `TurnContext` retention
|
|
149
|
+
|
|
150
|
+
With change 3, `TurnContext` is deleted on `turnEnded`. Drop `MAX_TURN_CONTEXTS` (the LRU cap) and `evictOldestTurnContextIfFull`. Add a belt-and-suspenders TTL sweeper (default ~30min) for the pathological case where `turnEnded` never fires *and* the timeout somehow also didn't fire.
|
|
151
|
+
|
|
152
|
+
### Why
|
|
153
|
+
|
|
154
|
+
- Deletes `recordInboundMessage`, `resolveInboundByGchatMessageName`, `RecentInboundEntrySchema`, the ring-buffer migration path, and the disk I/O on every `turnStarted`.
|
|
155
|
+
- Same outcome for the common case (daemon runs indefinitely; in-memory state is authoritative).
|
|
156
|
+
- `proactiveAnchors` is already in-memory and stays as-is — it's the other half of anchor resolution and already small.
|
|
157
|
+
|
|
158
|
+
### Cost
|
|
159
|
+
|
|
160
|
+
- **Adapter restart mid-turn** now loses the inbound cache for any turn that started but hadn't yet seen its `turnStarted` event delivered. Same failure mode as daemon restart mid-turn, which v1 already tolerates. Explicitly a "not the common case" tradeoff.
|
|
161
|
+
- **Daemon restart with long-lived adapter:** today the ring buffer survives, but without a `turnStarted` to match it's dead weight anyway. No regression.
|
|
162
|
+
|
|
163
|
+
## Net code delta (estimate)
|
|
164
|
+
|
|
165
|
+
Deletions:
|
|
166
|
+
|
|
167
|
+
- `src/daemon/api/turns-router.ts` — merged into `waitForMessages` (change 1).
|
|
168
|
+
- `recordInboundMessage` / `resolveInboundByGchatMessageName` / `RecentInboundEntrySchema` from `state.ts` (change 4).
|
|
169
|
+
- `MAX_TURN_CONTEXTS` / `evictOldestTurnContextIfFull` / LRU machinery in `forwarder.ts` (changes 3+4).
|
|
170
|
+
- Proactive-anchor fallback paths specific to subagent completions (change 2).
|
|
171
|
+
|
|
172
|
+
Additions:
|
|
173
|
+
|
|
174
|
+
- Per-turn outstanding-subagent registry in the daemon (change 3).
|
|
175
|
+
- In-memory inbound cache module in the adapter (change 4).
|
|
176
|
+
- Stream-envelope type on `waitForMessages` (change 1).
|
|
177
|
+
|
|
178
|
+
Net: meaningful reduction. `forwarder.ts` in particular sheds the bulk of its turn-lifecycle state management.
|
|
179
|
+
|
|
180
|
+
## Migration order
|
|
181
|
+
|
|
182
|
+
Changes are mostly independent; recommended sequence:
|
|
183
|
+
|
|
184
|
+
1. **Change 2** first — smallest patch, no wire protocol change. Removes a known source of fragmentation in v1. Gate on existing tests.
|
|
185
|
+
2. **Change 3** — depends on 2's semantics. Adds the outstanding-count registry; validate with a unit test that an async subagent extends `turnEnded`.
|
|
186
|
+
3. **Change 4** — pure adapter-internal refactor; can land independently of 1–3. Removes the ring buffer and its schema.
|
|
187
|
+
4. **Change 1** — touches the wire protocol. Land last so 2/3 are stable first. Ship with `waitForTurns` as a shim for one release before removing.
|
|
188
|
+
|
|
189
|
+
Each step merges independently. `npm run validate` must pass at every step.
|
|
190
|
+
|
|
191
|
+
## Out of scope
|
|
192
|
+
|
|
193
|
+
- Discord adapter migration (unchanged from v1 deferred list).
|
|
194
|
+
- Emoji reactions, merged turns, expand-on-demand — all still deferred per v1.
|
|
195
|
+
- Changing the `Destination` routing or the condenser strategies — these are working as intended.
|
|
196
|
+
|
|
197
|
+
## Open questions
|
|
198
|
+
|
|
199
|
+
1. **Turn-timeout default.** 30min feels right for "stuck subagent" protection but is a guess. Instrument first, tune after observing real distributions.
|
|
200
|
+
2. **Inbound cache TTL.** 10min covers the realistic inbound→turnStarted delay. Too short risks losing anchors on a laggy daemon; too long wastes memory. Make it configurable, default to 10min.
|
|
201
|
+
3. **`turnReplyComplete` event.** Speculative — do we ship it preemptively for future consumers (typing indicator, etc.) or wait? Leaning wait.
|
|
202
|
+
4. **Single-stream envelope naming.** `ChatStreamItem` / `{ kind: 'message' | 'turn' }` is a first pass — alternatives like flattening (`{ type: 'message' | 'turn-started' | 'turn-ended' }`) are worth weighing during implementation.
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Auto-update strategy for clawmini
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
`clawmini up` currently only refreshes one thing — the built-in policy scripts in `.clawmini/policy-scripts/` (via `installBuiltinPolicies` at `src/cli/builtin-policies.ts:14`). Everything else clawmini ships — `clawmini-lite.js`, environments, agent templates, skills — is copied into the workspace once at create-time and then never touched again. Upgrading clawmini does not upgrade what's on disk.
|
|
6
|
+
|
|
7
|
+
Concrete symptoms today:
|
|
8
|
+
|
|
9
|
+
- **clawmini-lite.js** is exported on demand via `clawmini export-lite` and on daemon startup for environments that declare `exportLiteTo` (`src/daemon/index.ts:63`). After a `npm install -g clawmini` upgrade, environments re-export at the next daemon startup but a manual `clawmini export-lite --out ...` location is silently stale until the user re-runs the export. We don't even refresh the env-based exports in the `up` command itself, only in the daemon's startup hook.
|
|
10
|
+
- **Environments** require a full template copy into `.clawmini/environments/<name>/` before they can be enabled (`src/shared/workspace.ts:678`). The vast majority of users never touch the copy, so they're carrying a frozen fork of `templates/environments/macos/sandbox.sb` with no upgrade path.
|
|
11
|
+
- **Agent templates** are even worse: the template is destructured at create-time across two locations (`settings.json` lands in `.clawmini/agents/<id>/settings.json` after merging in `directory`/`env`; everything else lands in the agent's working directory, e.g. `./bob/`). There is no record of which template the agent came from once the copy completes, so `BOOTSTRAP.md`, `GEMINI.md`, `TOOLS.md` etc. ossify on the version that shipped when the agent was created.
|
|
12
|
+
- **Skills** are template-copied into the agent's `skillsDir` at create-time and never refreshed (`copyAgentSkills` in `src/shared/workspace.ts:439`). Same staleness problem — but with the wrinkle that agents are _expected_ to edit their own skills as they learn, so we can't blindly overwrite.
|
|
13
|
+
|
|
14
|
+
The user's mental model is "I upgraded clawmini, my workspace should pick up the new defaults." The current model is "I upgraded clawmini, my workspace is a fossil."
|
|
15
|
+
|
|
16
|
+
## Constraints from the security model
|
|
17
|
+
|
|
18
|
+
The clawmini security model (see `docs/PHILOSOPHY.md`) shapes what's even possible to auto-update:
|
|
19
|
+
|
|
20
|
+
- **Agents cannot read `.clawmini/`.** The macOS sandbox profile explicitly denies it (`templates/environments/macos/sandbox.sb:23`). Anything an agent needs at runtime — like the `clawmini-lite.js` shim — must live _outside_ `.clawmini/`, in a path the agent's environment makes accessible (e.g., `.local/bin` for `macos`, `.cladding/tools/bin` for `cladding`).
|
|
21
|
+
- **The daemon's config (commands, allowlists) is sensitive.** Auto-update logic runs only on data clawmini ships in its own package; it never reaches into agent-owned files to make decisions about command execution.
|
|
22
|
+
- **Agent working directories are agent-writable.** That means we can't assume our last-installed template content is still on disk byte-for-byte; the agent may have edited any file. The auto-update logic must detect divergence and refuse to overwrite.
|
|
23
|
+
|
|
24
|
+
These constraints are why "put `clawmini-lite.js` on the agent's PATH from a well-known location" is _not_ a viable workspace-default — there's no path inside the workspace that agents can both read _and_ that we can guarantee exists across environments. Each environment (or each agent template) is responsible for declaring where its lite shim lives.
|
|
25
|
+
|
|
26
|
+
## Goals
|
|
27
|
+
|
|
28
|
+
1. **`clawmini up` brings the workspace forward.** All upgradeable surfaces refresh on `up` to whatever the installed clawmini ships with, by default.
|
|
29
|
+
2. **Local edits win.** Users (and agents) can edit any file, and the auto-updater leaves edited files alone. We never silently overwrite a file that has diverged from what we last wrote.
|
|
30
|
+
3. **Per-file upgrade decisions.** For agent templates, the template author decides on a per-file basis which files auto-update on `up` vs. which are seeded once and then owned by the agent (e.g., `MEMORY.md` is the agent's memory — never overwrite; `GEMINI.md` is core instructions — always refresh).
|
|
31
|
+
4. **One mechanism, applied consistently.** The same per-file refresh policy and the same SHA-based divergence check apply to agent working-directory files and skills. The only thing that varies between surfaces is the default mode for files not listed in a manifest (seed-once for agents, track for skills).
|
|
32
|
+
5. **Overlay > fork.** Where users want to customize a template, they declare overrides in a small overlay file rather than copy-pasting the whole template. This keeps them on the upgrade path.
|
|
33
|
+
|
|
34
|
+
## Non-goals
|
|
35
|
+
|
|
36
|
+
- Versioning or migration of user data (chat histories, agent memory files, request stores). Those are owned by the user/agent and never touched by the upgrader.
|
|
37
|
+
- Mutating `.clawmini/settings.json` itself on upgrade. That's the user's source of truth.
|
|
38
|
+
- Network-fetched updates. The "source of truth" for built-ins is always the installed clawmini package on disk.
|
|
39
|
+
- Cross-version backwards-compat for old workspaces. Existing `.clawmini/environments/<name>/` copies and existing agents (without an `extends` field) keep working — they just don't get the upgrade benefit until they opt in. No automatic migration.
|
|
40
|
+
- Workspace-default `clawmini-lite.js` placement. Each environment/agent template declares its own export location.
|
|
41
|
+
|
|
42
|
+
## Core mechanism: `template.json` + SHA tracking
|
|
43
|
+
|
|
44
|
+
The unifying primitive is a `template.json` file that lives next to a template's content and tells the auto-updater how to handle each file:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"files": {
|
|
49
|
+
"GEMINI.md": "track",
|
|
50
|
+
".gemini/": "track",
|
|
51
|
+
"SOUL.md": "seed-once",
|
|
52
|
+
"USER.md": "seed-once",
|
|
53
|
+
"MEMORY.md": "seed-once",
|
|
54
|
+
"memory/": "seed-once",
|
|
55
|
+
"BOOTSTRAP.md": "seed-once"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Two modes per file (or directory entry):
|
|
61
|
+
|
|
62
|
+
- **`track`** — install/refresh on every `clawmini up`, _unless_ the on-disk content has diverged from what we last wrote. Divergence is detected by storing the SHA of the last-installed content in `.clawmini/agents/<id>/installed-files.json` and comparing it to the current on-disk SHA before refreshing. If they differ (the user or agent edited the file), skip and log a one-line warning.
|
|
63
|
+
- **`seed-once`** — install at create time; never touch again. The agent/user owns it.
|
|
64
|
+
|
|
65
|
+
Files not listed in the manifest default to `seed-once`. This is the safe default — we never auto-modify a file the template author didn't explicitly mark as auto-managed.
|
|
66
|
+
|
|
67
|
+
A directory entry (`"path/": "track"` or `"seed-once"`) applies recursively to every file under that directory.
|
|
68
|
+
|
|
69
|
+
**Two universal rules for `template.json`:**
|
|
70
|
+
|
|
71
|
+
1. The `template.json` file itself is metadata; the auto-updater reads it to know what to do but **never copies it to the destination**. Users don't see template.json in their agent dirs or environment dirs.
|
|
72
|
+
2. The first-install time also records SHAs into `installed-files.json`. If a user upgrades clawmini and the manifest didn't exist on the previous version (so no SHA was recorded for a file), `track` files default to "treat as user-owned" — we don't touch them until the user opts in via `clawmini agents refresh <id> --accept`. Strict by default; users explicitly accept the risk.
|
|
73
|
+
|
|
74
|
+
## Per-surface design
|
|
75
|
+
|
|
76
|
+
### 1. Built-in policies
|
|
77
|
+
|
|
78
|
+
**Today:** `installBuiltinPolicies` writes built-in policy scripts to `.clawmini/policy-scripts/` on every `up`, content-hashed to skip no-op writes (`src/cli/builtin-policies.ts:14`).
|
|
79
|
+
|
|
80
|
+
**Proposal:** no behavior change. The existing approach is the reference pattern: hash-compare, skip on match, atomic write on diff. Documented here as the canonical approach for any "always refresh" surface.
|
|
81
|
+
|
|
82
|
+
### 2. clawmini-lite.js
|
|
83
|
+
|
|
84
|
+
**Today:** The daemon re-exports `clawmini-lite.js` to every active environment's `exportLiteTo` on startup (`src/daemon/index.ts:63`). The `clawmini export-lite` CLI command also exists for manual exports to arbitrary paths.
|
|
85
|
+
|
|
86
|
+
**Proposal:**
|
|
87
|
+
|
|
88
|
+
- Move the env-based re-export loop to also run inside `clawmini up` itself, before the daemon starts (so an `up` command on a freshly-upgraded clawmini refreshes lite scripts even if the daemon was already running and gets restarted as part of the version bump — see Implementation order).
|
|
89
|
+
- Reuse the same content-hash skip pattern: if the on-disk `clawmini-lite.js` matches the bundled one, skip the write. Avoids touching mtimes for tools watching files.
|
|
90
|
+
- Refusal: if the existing file is not a known clawmini-lite (no recognizable hashbang/header), log a warning and skip rather than clobbering an arbitrary user file at that path.
|
|
91
|
+
- **No workspace-default location.** Agents can't read `.clawmini/`, so there's no universally-readable path we can place the shim in. Each environment declares `exportLiteTo` for sandbox-accessible placement; each agent template can also declare its own export location if it doesn't run inside an environment. We don't auto-place a shim anywhere else.
|
|
92
|
+
- `clawmini export-lite` (manual) keeps working as today for one-off cases.
|
|
93
|
+
|
|
94
|
+
### 3. Environments
|
|
95
|
+
|
|
96
|
+
**Today:** `enable` copies `templates/environments/<name>/` into `.clawmini/environments/<name>/`, then registers a path mapping in `settings.json` (`enableEnvironment` at `src/shared/workspace.ts:671`). All resolution thereafter reads from the local copy.
|
|
97
|
+
|
|
98
|
+
Some environments need a real on-disk directory: `init`/`up`/`down` hooks may write state into the env dir, allowlists are runtime-mutable, etc. We're not going to remove the local copy.
|
|
99
|
+
|
|
100
|
+
**Proposal:** keep today's copy-on-enable behavior, but add an overlay form for users who want small customizations without forking the whole config.
|
|
101
|
+
|
|
102
|
+
**Default mode (today's behavior):** `clawmini environments enable macos` copies the full template into `.clawmini/environments/macos/`. The user owns the directory; this content does not auto-update. Same as today.
|
|
103
|
+
|
|
104
|
+
**Overlay mode (new):** the user can hand-write a smaller `env.json` that inherits from a built-in:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"extends": "macos",
|
|
109
|
+
"env": { "MY_VAR": "value" },
|
|
110
|
+
"policies": {
|
|
111
|
+
"my-extra-policy": { "command": "./my-extra.mjs" }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Resolution: when `readEnvironment(name)` finds an `extends` field, it loads the built-in's `env.json` first and shallow-merges the local fields on top (with `env` and `policies` deep-merged at one level so adding a single env var doesn't drop the rest). The local directory still exists on disk for sandbox profile files, init scripts, runtime state, etc.
|
|
117
|
+
|
|
118
|
+
For files referenced by the env config (sandbox profiles, allowlist scripts), `{ENV_DIR}` resolution is layered: a relative path resolves first against the local overlay dir, then against the built-in template dir. This means an overlay can reference the built-in's `sandbox.sb` without copying it, but can also override it by placing a local `sandbox.sb`.
|
|
119
|
+
|
|
120
|
+
`EnvironmentSchema` gets `extends: z.string().optional()`. No CLI flag needed initially — overlays are hand-edited; we can add a `--customize` scaffolder later if it's a common ask.
|
|
121
|
+
|
|
122
|
+
**No environment auto-refresh of files yet.** Environment files (sandbox profiles, scripts) don't use the `template.json` track mechanism in the MVP. If we want sandbox profile improvements to flow to existing workspaces, that's a future extension — open question below.
|
|
123
|
+
|
|
124
|
+
### 4. Agents
|
|
125
|
+
|
|
126
|
+
This is the hardest surface because agent templates split across two locations:
|
|
127
|
+
|
|
128
|
+
- `settings.json` is _transformed_ (template's `settings.json` is read, `directory`/`env` overrides applied, then written to `.clawmini/agents/<id>/settings.json` — see `applyTemplateToAgent` at `src/shared/workspace.ts:475`).
|
|
129
|
+
- All other template files (e.g., `BOOTSTRAP.md`, `GEMINI.md`, `MEMORY.md`, `TOOLS.md` for `gemini-claw`) are copied verbatim into the agent's working directory (e.g., `./bob/`). There is no record of which template they came from.
|
|
130
|
+
|
|
131
|
+
**Proposal:** track the template link via a single `extends` field on the agent's settings, and use `template.json` to govern working-directory file refresh.
|
|
132
|
+
|
|
133
|
+
#### 4a. Settings overlay
|
|
134
|
+
|
|
135
|
+
Add an optional template reference:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"extends": "gemini-claw",
|
|
140
|
+
"directory": "./bob",
|
|
141
|
+
"env": { "GEMINI_API_KEY": true }
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Resolution: `getAgent(id)` first reads the local `settings.json`, then resolves `extends` to the template's `settings.json`, then shallow-merges in this order:
|
|
146
|
+
|
|
147
|
+
1. Template `settings.json` (the base).
|
|
148
|
+
2. Local file's top-level fields (`directory`, `env`, `commands`, `fallbacks`, etc.) — these override the template field-by-field.
|
|
149
|
+
|
|
150
|
+
`env` and `subagentEnv` are deep-merged at one level (template entries + local entries, local wins on conflict) so the user can add a single env var without dropping the template's defaults. Other fields (`commands`, `fallbacks`, `apiTokenEnvVar`) shallowly override — if the user sets one, they replace the template's value entirely. This matches today's behavior in `applyTemplateToAgent` and is simpler than a deep-merge `patch` block: users either inherit or override at the field level, no nested patch grammar to learn.
|
|
151
|
+
|
|
152
|
+
`AgentSchema` adds `extends: z.string().optional()`.
|
|
153
|
+
|
|
154
|
+
**Backwards compatibility:** an agent without `extends` is treated exactly as today (the local file is the full source of truth). `clawmini agents add bar --template gemini-claw` defaults to writing the new overlay shape (with `extends: "gemini-claw"`), so newly created agents auto-update. Existing agents stay frozen unless the user manually adds `extends`.
|
|
155
|
+
|
|
156
|
+
`applyTemplateToAgent` no longer copies `settings.json` into the agent working dir, merges, and deletes — it just writes the overlay file directly to `.clawmini/agents/<id>/settings.json`. Simpler code path.
|
|
157
|
+
|
|
158
|
+
#### 4b. Working-directory file manifest
|
|
159
|
+
|
|
160
|
+
The non-settings template files need per-file upgrade policy. The template ships a `template.json` alongside `settings.json` (copied into the template dir but **not** copied to the destination — see Core mechanism).
|
|
161
|
+
|
|
162
|
+
For `gemini-claw` specifically, the manifest is:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"files": {
|
|
167
|
+
"GEMINI.md": "track",
|
|
168
|
+
".gemini/": "track",
|
|
169
|
+
"SOUL.md": "seed-once",
|
|
170
|
+
"USER.md": "seed-once",
|
|
171
|
+
"MEMORY.md": "seed-once",
|
|
172
|
+
"memory/": "seed-once",
|
|
173
|
+
"BOOTSTRAP.md": "seed-once",
|
|
174
|
+
"TOOLS.md": "seed-once",
|
|
175
|
+
"HEARTBEAT.md": "seed-once"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`GEMINI.md` (the core instructions read at the start of every session) and `.gemini/` (framework config consumed by the Gemini CLI) auto-refresh on `up`. The agent's identity (`SOUL.md`), user profile (`USER.md`), and memory files (`MEMORY.md`, `memory/`) are seeded once and never overwritten. `BOOTSTRAP.md` is one-shot self-deleting; `TOOLS.md` and `HEARTBEAT.md` are reference docs the agent may amend.
|
|
181
|
+
|
|
182
|
+
The `up` command iterates every agent with `extends` set, reads the template's manifest, and for each `track` entry:
|
|
183
|
+
|
|
184
|
+
1. Read the current on-disk file's SHA.
|
|
185
|
+
2. Compare against the SHA we recorded in `.clawmini/agents/<id>/installed-files.json` for that path.
|
|
186
|
+
3. If they match (user/agent hasn't edited it), write the new content + record the new SHA.
|
|
187
|
+
4. If they differ, skip and log: `./bob/GEMINI.md differs from template; skipping refresh. Run 'clawmini agents refresh bob --accept' to overwrite.`
|
|
188
|
+
5. If no SHA was recorded (the file pre-exists from before this feature shipped), treat as diverged → skip + warn. Strict default; the user opts in once via `--accept`.
|
|
189
|
+
|
|
190
|
+
Directory entries walk recursively, applying the same per-file rule to every file under the directory. Files added to the directory by the agent (not in the template) are untouched.
|
|
191
|
+
|
|
192
|
+
**Failure modes called out:**
|
|
193
|
+
|
|
194
|
+
- File present in template, missing on disk: re-create it (benign re-seeding; SHA recorded).
|
|
195
|
+
- File absent from template but present on disk: untouched (it's the agent's own file).
|
|
196
|
+
- File present in template, on disk, no SHA: skip + one-time hint (see above).
|
|
197
|
+
- Directory `"track"` containing a `template.json`: skipped (manifest files are never copied).
|
|
198
|
+
|
|
199
|
+
#### 4c. Full-fork escape hatch
|
|
200
|
+
|
|
201
|
+
`clawmini agents add bob --template gemini-claw --fork` keeps the legacy behavior: copy everything (including `settings.json` merged into the local file with no `extends`), no manifest, no auto-update. The agent is fully owned by the user.
|
|
202
|
+
|
|
203
|
+
### 5. Skills
|
|
204
|
+
|
|
205
|
+
Skills are tricky because the agent is _expected_ to modify its skills as it learns — adding examples, refining steps, even creating new skills via the `skill-creator` skill. But clawmini also ships skill updates (bug fixes, new helpers) that should flow to existing agents.
|
|
206
|
+
|
|
207
|
+
**Proposal:** reuse the same `track` / `seed-once` mechanism as agent files, but with the **opposite default**: unlisted files default to `track` (clawmini ships skill content; the agent gets latest unless it edited). This keeps "one mechanism" while reflecting the different ownership model.
|
|
208
|
+
|
|
209
|
+
- On `clawmini up`, for each agent with a resolved skills directory: walk `templates/skills/` and for each skill subfolder, refresh files according to its `template.json`.
|
|
210
|
+
- A skill's `template.json` is read for per-file declarations and **never copied** to the destination (universal rule).
|
|
211
|
+
- **No `template.json` in the skill source:** every file is treated as `track` — refresh unless the on-disk SHA diverges from the recorded SHA. This is the "always refresh if not present" default.
|
|
212
|
+
- **`template.json` present:** declared files use their explicit mode (`track` or `seed-once`); files not listed default to `track` (skills-side default — opposite of agents).
|
|
213
|
+
- SHA store is the same per-agent `installed-files.json`, with skill paths recorded under their full relative path (e.g., `.gemini/skills/skill-creator/SKILL.md`).
|
|
214
|
+
- Same first-install strict rule applies: a file with no recorded SHA is treated as diverged → skip + hint, requires `--accept` to opt in.
|
|
215
|
+
- `clawmini skills add <skill-name> -a <agent>` (existing) keeps today's `--force` semantics: overwrites the skill directory unconditionally and re-records SHAs.
|
|
216
|
+
- New: `clawmini agents refresh <id>` (see CLI surface) covers skills as part of the same refresh; `--accept` overwrites diverged files for both agent files and skills.
|
|
217
|
+
|
|
218
|
+
This unifies agents and skills under one code path. The only difference is the default for unlisted files (seed-once for agents, track for skills) — chosen per-surface based on who typically authors content there.
|
|
219
|
+
|
|
220
|
+
### 6. Surfaces that should _not_ auto-update (called out explicitly)
|
|
221
|
+
|
|
222
|
+
- **`.clawmini/settings.json`** — user's source of truth. We never write to it during `up`.
|
|
223
|
+
- **`.clawmini/policies.json`** — user-curated allowlist. Built-in policies are merged in at read time (`resolvePolicies`), so the file itself doesn't need updating.
|
|
224
|
+
- **`.clawmini/chats/*/settings.json`** — chat-level configuration (default agent, routers, jobs, subagent tracking). Owned by the user and the daemon at runtime. No template, nothing to update from. The daemon already migrates this file as needed at runtime.
|
|
225
|
+
- **`.clawmini/agents/<id>/sessions/*/settings.json`** — per-session env overrides. Ephemeral, owned by the daemon.
|
|
226
|
+
- **Adapter config (`adapter-discord` / `adapter-google-chat`)** — these are separate processes the user runs, with their own config files (e.g., `channelChatMap` state in `src/adapter-google-chat/state.ts`). They aren't part of `.clawmini/`, aren't shipped as templates, and have no upgrade story to manage. If we ever start shipping default adapter config templates (we currently don't), they'd fall under the same overlay-or-fork model. Out of scope here.
|
|
227
|
+
- **Environment files (sandbox profiles, etc.)** — see Section 3. No `template.json` mechanism for environments yet; users who want updated sandbox profiles delete and re-enable.
|
|
228
|
+
- **Agent working-directory files outside the template manifest** — anything the user or agent has dropped into `./bob/` that isn't in the template's `template.json`. Untouched.
|
|
229
|
+
|
|
230
|
+
## CLI surface
|
|
231
|
+
|
|
232
|
+
Minimal additions:
|
|
233
|
+
|
|
234
|
+
- `clawmini up` (existing) gains:
|
|
235
|
+
- Refresh `clawmini-lite.js` for every active environment that declares `exportLiteTo` (content-hashed; was previously only run by the daemon's startup hook).
|
|
236
|
+
- Refresh `track`'d files for every agent that has an `extends` field, skipping diverged files and updating the SHA store.
|
|
237
|
+
- Refresh skills for every agent the same way (using the skills-side default of `track` for unlisted files).
|
|
238
|
+
- Existing built-in policy install runs unchanged.
|
|
239
|
+
- On clawmini-version mismatch with a running daemon: stop + restart, so the daemon re-runs its environment hooks.
|
|
240
|
+
- New flag `--dry-run`: print the per-file plan (refresh / skip-diverged / skip-unchanged) and exit without writing. Lets cautious users preview the blast radius.
|
|
241
|
+
|
|
242
|
+
- `clawmini agents add <id> --template <name>` (existing): default writes the overlay shape (`extends`, `directory`, `env`). Add `--fork` for the legacy full-copy behavior.
|
|
243
|
+
|
|
244
|
+
- `clawmini agents refresh <id>` (new): manually re-run the agent's track-file refresh (both working-dir files and skills) outside of `up`.
|
|
245
|
+
- `--accept`: overwrite files that have diverged from their recorded SHA. Acknowledges the user is okay losing edits to those specific files. Same flag covers agent files and skills — there's no separate `--force-skills` toggle.
|
|
246
|
+
- `--dry-run`: same semantics as `up --dry-run` but scoped to one agent.
|
|
247
|
+
|
|
248
|
+
- `clawmini skills add <skill-name> -a <agent>` (existing): keeps today's `--force` overwrite semantics for one-shot installs.
|
|
249
|
+
|
|
250
|
+
- `clawmini agents diff <id>` (nice-to-have, not blocking): show which `track` files have diverged from the template. Helps the user understand what `up` will skip.
|
|
251
|
+
|
|
252
|
+
- `clawmini environments enable <name>` (existing): no flag changes. Overlay mode is hand-edited; full copy is the default. We can add a `--customize` scaffolder later.
|
|
253
|
+
|
|
254
|
+
## Implementation order
|
|
255
|
+
|
|
256
|
+
1. **Lite refresh on `up`.** Move the daemon's `exportLiteToEnvironment` loop into `clawmini up` (still also runs in the daemon, for the case where the daemon starts via something other than `up`). Adds the refusal-on-unknown-content guard. No schema changes; ships independently.
|
|
257
|
+
2. **Add `extends` to `EnvironmentSchema` and overlay merge in `readEnvironment`.** Layered `{ENV_DIR}` resolution in `src/daemon/agent/agent-context.ts`. No CLI changes; verify by hand-creating an overlay file.
|
|
258
|
+
3. **Add `extends` to `AgentSchema` and rewrite `applyTemplateToAgent`.** New agents created with `--template` write the overlay shape. `getAgent` resolves `extends` and shallow-merges (with one-level deep-merge for `env`/`subagentEnv`). Existing agents (no `extends`) unchanged. Pure read-side change — no `up`-time refresh yet.
|
|
259
|
+
4. **Add the `template.json` manifest, the file-tracking SHA store, and the universal "never copy `template.json`" rule.** Ship `template.json` for the templates that have one (`gemini-claw` first; others can opt in incrementally). Create `.clawmini/agents/<id>/installed-files.json` per agent. New agents created with `--template` get their tracked files refreshed at create time + SHAs recorded.
|
|
260
|
+
5. **Wire agent refresh into `clawmini up`.** Walk every agent with `extends`, refresh tracked files (skip diverged with warning), update `installed-files.json`. Same logic powers `clawmini agents refresh`.
|
|
261
|
+
6. **Skills refresh on `up`.** Reuse the step-4/5 manifest+SHA mechanism for skills, with the skills-side default of `track` for unlisted files. Skill `template.json` files are skipped on copy (universal rule). SHAs share `installed-files.json` keyed by full relative path.
|
|
262
|
+
7. **`--dry-run` plumbing for `up` and `agents refresh`.** Print the per-file plan (refresh / skip-diverged / skip-unchanged) and exit without writing.
|
|
263
|
+
8. **(Optional polish) `clawmini agents diff` and `clawmini environments enable --customize`.**
|
|
264
|
+
|
|
265
|
+
Each step is independently shippable. After step 1 the lite-staleness problem is fixed for environment-based workflows. After step 6 the whole upgrade story is uniform; step 7 is the safety-net flag for cautious users.
|
|
266
|
+
|
|
267
|
+
## E2E test plan
|
|
268
|
+
|
|
269
|
+
All tests use the existing `TestEnvironment` harness in `e2e/_helpers/test-environment.ts` and the `env.runCli([...])` pattern. New file: **`e2e/cli/auto-update.test.ts`** for the cross-cutting flows; surface-specific assertions can extend the existing `e2e/cli/{init,agents,skills,export-lite}.test.ts` suites.
|
|
270
|
+
|
|
271
|
+
Each test below names what it asserts; the harness gives us isolated `.clawmini/` dirs and a real subprocess CLI invocation per test.
|
|
272
|
+
|
|
273
|
+
### Lite refresh on `up`
|
|
274
|
+
|
|
275
|
+
1. **`up refreshes clawmini-lite.js for active environments`** — enable `macos`, run `up`, verify `${env.dir}/.local/bin/clawmini-lite.js` exists and matches the bundled shim's content (compare SHA-256). Then mutate the bundled shim source on disk in the test fixture (write a sentinel string into the staged copy used by `resolveCompiledScript`), run `up` again, verify the file is overwritten.
|
|
276
|
+
2. **`up skips lite write when content matches (no mtime touch)`** — record `mtime` after first `up`, run `up` again immediately, assert mtime unchanged.
|
|
277
|
+
3. **`up refuses to overwrite a non-clawmini file at the export path`** — pre-write arbitrary content (no hashbang, no recognizable header) at `.local/bin/clawmini-lite.js`, run `up`, assert the file content is unchanged and stderr contains a warning.
|
|
278
|
+
4. **`up does not place a default lite shim when no environment is active`** — run `init` + `up` with no `environments enable`, assert no `clawmini-lite.js` is created anywhere in the workspace outside `.clawmini/`.
|
|
279
|
+
|
|
280
|
+
### Environment overlay
|
|
281
|
+
|
|
282
|
+
5. **`environment with extends inherits built-in env.json fields`** — write an overlay `env.json` containing `{"extends": "macos", "env": {"MY_VAR": "v"}}`, register it via settings, run a daemon command that reads the resolved environment, assert the resulting config has the macOS `prefix`/`exportLiteTo` (from the built-in) plus `MY_VAR=v` (from the overlay), and that `env` from the built-in is preserved (deep-merge at one level).
|
|
283
|
+
6. **`{ENV_DIR} resolution falls back to built-in for missing local files`** — overlay env has no `sandbox.sb`; assert that `prefix` references the built-in package's `sandbox.sb` path. Then place a local `sandbox.sb` next to the overlay; assert the local one wins.
|
|
284
|
+
7. **`environments enable still copies by default (no extends added)`** — `environments enable cladding`, assert `.clawmini/environments/cladding/env.json` is a full copy with no `extends` field — backwards-compatible behavior.
|
|
285
|
+
|
|
286
|
+
### Agent settings overlay
|
|
287
|
+
|
|
288
|
+
8. **`agents add --template writes overlay shape with extends`** — `agents add bob --template gemini-claw`, assert `.clawmini/agents/bob/settings.json` contains `extends: "gemini-claw"` and only the create-time fields (`directory`, possibly `env`), not the full template merged in.
|
|
289
|
+
9. **`getAgent merges template fields into local fields`** — read the agent via the daemon API and assert the resolved settings include the template's `commands`, `apiTokenEnvVar`, `fallbacks`, etc.
|
|
290
|
+
10. **`local field overrides template field shallowly`** — write a local `commands.new` that differs from the template; assert the resolved value is the local one.
|
|
291
|
+
11. **`env deep-merges one level`** — template defines `env: {MODEL: "x", API_KEY: true}`, local defines `env: {MODEL: "y"}`; assert resolved `env` is `{MODEL: "y", API_KEY: true}`.
|
|
292
|
+
12. **`agents add --fork copies everything and writes no extends`** — `agents add bob --template gemini-claw --fork`, assert `.clawmini/agents/bob/settings.json` has no `extends` and contains the fully-merged template fields inline (legacy shape).
|
|
293
|
+
13. **`existing agent without extends is unchanged after up`** — pre-create an agent with the legacy shape (no `extends`); run `up`; assert `.clawmini/agents/<id>/settings.json` and the agent dir are untouched.
|
|
294
|
+
|
|
295
|
+
### Agent working-dir refresh
|
|
296
|
+
|
|
297
|
+
14. **`new agent with --template records SHAs and creates installed-files.json`** — `agents add bob --template gemini-claw`, assert `.clawmini/agents/bob/installed-files.json` exists and contains entries for every file in `gemini-claw`'s manifest with their SHAs.
|
|
298
|
+
15. **`up refreshes a track'd file when on-disk SHA matches recorded SHA`** — modify the bundled `gemini-claw/GEMINI.md` template content (sentinel), run `up`, assert `./bob/GEMINI.md` now has the new content and the SHA in `installed-files.json` was updated.
|
|
299
|
+
16. **`up skips a track'd file when on-disk SHA differs (user edited)`** — edit `./bob/GEMINI.md` to add a sentinel line, run `up`, assert the file is unchanged and stderr contains a warning naming the file and suggesting `--accept`.
|
|
300
|
+
17. **`up never touches seed-once files`** — modify the bundled `MEMORY.md` template, run `up`, assert `./bob/MEMORY.md` is unchanged (it's `seed-once`).
|
|
301
|
+
18. **`up never touches files outside the manifest`** — drop `./bob/some-agent-file.md` (not in template); run `up`; assert untouched.
|
|
302
|
+
19. **`up re-creates a track'd file that was deleted`** — `rm ./bob/GEMINI.md`, run `up`, assert the file is recreated from the template and the SHA recorded.
|
|
303
|
+
20. **`up does not copy template.json to the destination`** — assert `./bob/template.json` does not exist after agent creation or after `up`.
|
|
304
|
+
21. **`pre-existing agent without recorded SHAs gets strict skip + hint`** — pre-create an agent with `extends` but no `installed-files.json`, edit `./bob/GEMINI.md` matching the template byte-for-byte (so it would be safe to refresh), run `up`, assert the file is still skipped (no recorded SHA → diverged) and stderr suggests `agents refresh --accept`.
|
|
305
|
+
22. **`directory entry walks recursively`** — template manifest has `".gemini/": "track"`, multiple files under `.gemini/`; `up` refreshes all of them, recording SHAs for each path.
|
|
306
|
+
|
|
307
|
+
### Skills refresh
|
|
308
|
+
|
|
309
|
+
23. **`up refreshes all skill files when no template.json is present`** — modify a bundled skill's `SKILL.md`, run `up`, assert the on-disk skill content is updated and SHA recorded under the full path in `installed-files.json`.
|
|
310
|
+
24. **`up skips diverged skill files`** — agent edits `<skillsDir>/skill-creator/SKILL.md`, run `up`, assert the file is skipped + warning logged.
|
|
311
|
+
25. **`skill template.json marks file seed-once`** — ship a test skill with `{"files": {"examples/": "seed-once"}}`, agent edits an example, run `up`, assert the example is untouched even after a bundled update.
|
|
312
|
+
26. **`skill files unlisted in template.json default to track`** — same test skill has unlisted `SKILL.md`; modify the bundle's `SKILL.md`; run `up`; assert it's refreshed (skills-side default = track).
|
|
313
|
+
27. **`skill template.json itself is never copied`** — assert `<skillsDir>/<skill>/template.json` does not exist after `up`.
|
|
314
|
+
28. **`skills add --force overwrites diverged files unconditionally`** — agent edits `<skillsDir>/skill-creator/SKILL.md`, run `clawmini skills add skill-creator -a bob` (no `--force`) — assert today's behavior (which is force-overwrite per current code at `src/cli/commands/skills.ts:62`).
|
|
315
|
+
|
|
316
|
+
### `agents refresh` and `--accept`
|
|
317
|
+
|
|
318
|
+
29. **`agents refresh <id> runs the same pass as up but scoped`** — set up two agents, edit one's `GEMINI.md`, run `agents refresh other-agent`, assert only the other agent's SHAs are touched (no side effect on the edited agent).
|
|
319
|
+
30. **`agents refresh --accept overwrites diverged files`** — agent edits `GEMINI.md`, run `agents refresh bob --accept`, assert the file is overwritten with template content and the SHA re-recorded. Same flag works for skill files.
|
|
320
|
+
|
|
321
|
+
### `--dry-run`
|
|
322
|
+
|
|
323
|
+
31. **`up --dry-run prints a plan and writes nothing`** — set up a workspace where some files would refresh, some would skip-diverged, some are unchanged; run `up --dry-run`; assert stdout names each category, no on-disk file changes (stat all template-tracked files before/after; SHAs in `installed-files.json` unchanged).
|
|
324
|
+
32. **`agents refresh --dry-run prints scoped plan`** — same idea, scoped to one agent.
|
|
325
|
+
|
|
326
|
+
### Built-in policies (regression)
|
|
327
|
+
|
|
328
|
+
33. **`up still installs built-in policy scripts unchanged`** — existing behavior; assert `.clawmini/policy-scripts/run-host.js` and `propose-policy.js` exist after `up` and content matches the bundled scripts. This guards against the new logic accidentally breaking the existing install path.
|
|
329
|
+
|
|
330
|
+
### Cross-surface
|
|
331
|
+
|
|
332
|
+
34. **`up runs all four refreshes in one invocation`** — fresh workspace with an agent + active environment + skills + policies; run `up` once, assert lite is exported, agent files refreshed, skills refreshed, policy scripts present, all in one command.
|
|
333
|
+
35. **`up exits non-zero if any refresh fails`** — make one of the refreshes fail (e.g., template path unreadable); assert `up` exits non-zero and the error message names the failing surface. Other surfaces still attempt their work (failures don't short-circuit) — verify by checking that policy scripts are still installed even if the agent refresh fails.
|
|
334
|
+
|
|
335
|
+
All tests must run with `npm run validate` green before merge. Tests that mutate the bundled template content (#15, #23, #25, #26) do so against a copy staged in the test environment, not the real package — the harness needs a small extension to point template resolution at a test-controlled directory.
|
|
336
|
+
|
|
337
|
+
## Decided (was open questions)
|
|
338
|
+
|
|
339
|
+
1. **Per-file SHA store location:** per-agent at `.clawmini/agents/<id>/installed-files.json`. Orphaned by `deleteAgent` already. Skill SHAs go into the same file under the skill's full relative path.
|
|
340
|
+
2. **Environment files using `track`/`seed-once`:** out of scope for the MVP. Environments stay copy-once; users delete + re-enable to get sandbox profile updates. Revisit if asked.
|
|
341
|
+
3. **First-refresh strictness (no recorded SHA):** strict — skip + hint, require `--accept`. Agents actively edit files; silent overwrite is the worse failure mode.
|
|
342
|
+
4. **Versioned templates:** not in scope. Templates ship as one version per clawmini package version.
|
|
343
|
+
5. **`--dry-run` flag:** added on `up` and `agents refresh`. Prints the per-file plan without writing.
|
|
344
|
+
6. **Skills `template.json`:** supported, with skills-side default = `track` for unlisted files (opposite of agents). One mechanism, two defaults.
|