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.
- 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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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 +0 -1
- 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 -118
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
- 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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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/D5iV40bG.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/entry/app.BCSV3nrG.js +0 -2
- package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/dist/web/_app/immutable/nodes/4.ClM1bXLE.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/D5iV40bG.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/entry/app.BCSV3nrG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.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
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { createE2EContext } from './utils.js';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
describe('propose-policy CLI', () => {
|
|
12
|
-
const { runCli, e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-propose-policy');
|
|
13
|
-
const binPath = path.resolve(__dirname, '../../../dist/cli/propose-policy.mjs');
|
|
14
|
-
|
|
15
|
-
beforeAll(async () => {
|
|
16
|
-
await setupE2E();
|
|
17
|
-
const { code, stderr } = await runCli(['init']);
|
|
18
|
-
if (code !== 0) throw new Error(`Init failed: ${stderr}`);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterAll(async () => {
|
|
22
|
-
await teardownE2E();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
function runProposePolicy(
|
|
26
|
-
args: string[]
|
|
27
|
-
): Promise<{ stdout: string; stderr: string; code: number | null }> {
|
|
28
|
-
return new Promise((resolve) => {
|
|
29
|
-
const child = spawn('node', [binPath, ...args], {
|
|
30
|
-
cwd: e2eDir,
|
|
31
|
-
env: { ...process.env },
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
let stdout = '';
|
|
35
|
-
let stderr = '';
|
|
36
|
-
|
|
37
|
-
child.stdout.on('data', (data) => (stdout += data.toString()));
|
|
38
|
-
child.stderr.on('data', (data) => (stderr += data.toString()));
|
|
39
|
-
|
|
40
|
-
child.on('close', (code) => {
|
|
41
|
-
resolve({ stdout, stderr, code });
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
it('should fail if missing required arguments', async () => {
|
|
47
|
-
const { stderr, code } = await runProposePolicy([]);
|
|
48
|
-
expect(code).toBe(1);
|
|
49
|
-
expect(stderr).toContain("error: required option '--name <policy_name>' not specified");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should fail if policy name is invalid', async () => {
|
|
53
|
-
const { stderr, code } = await runProposePolicy([
|
|
54
|
-
'--name',
|
|
55
|
-
'Invalid_Name!',
|
|
56
|
-
'--description',
|
|
57
|
-
'Test description',
|
|
58
|
-
'--command',
|
|
59
|
-
'echo test',
|
|
60
|
-
]);
|
|
61
|
-
expect(code).toBe(1);
|
|
62
|
-
expect(stderr).toContain(
|
|
63
|
-
'Error: Policy name must only contain lowercase letters, numbers, and hyphens.'
|
|
64
|
-
);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should fail if neither command nor script-file is provided', async () => {
|
|
68
|
-
const { stderr, code } = await runProposePolicy([
|
|
69
|
-
'--name',
|
|
70
|
-
'test-policy',
|
|
71
|
-
'--description',
|
|
72
|
-
'Test description',
|
|
73
|
-
]);
|
|
74
|
-
expect(code).toBe(1);
|
|
75
|
-
expect(stderr).toContain('Error: Must provide either --command or --script-file.');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should create a policy with a command', async () => {
|
|
79
|
-
const { stdout, stderr, code } = await runProposePolicy([
|
|
80
|
-
'--name',
|
|
81
|
-
'echo-test',
|
|
82
|
-
'--description',
|
|
83
|
-
'A simple echo command',
|
|
84
|
-
'--command',
|
|
85
|
-
'echo "Hello World"',
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
if (code !== 0) console.error(stderr);
|
|
89
|
-
expect(code).toBe(0);
|
|
90
|
-
expect(stdout).toContain("Successfully proposed and registered policy 'echo-test'");
|
|
91
|
-
|
|
92
|
-
const policiesPath = path.resolve(e2eDir, '.clawmini/policies.json');
|
|
93
|
-
const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
|
|
94
|
-
|
|
95
|
-
expect(policies.policies['echo-test']).toBeDefined();
|
|
96
|
-
expect(policies.policies['echo-test'].description).toBe('A simple echo command');
|
|
97
|
-
expect(policies.policies['echo-test'].command).toBe('echo');
|
|
98
|
-
expect(policies.policies['echo-test'].args).toEqual(['"Hello', 'World"']);
|
|
99
|
-
expect(policies.policies['echo-test'].allowHelp).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should create a policy with a script file', async () => {
|
|
103
|
-
const scriptPath = path.resolve(e2eDir, 'test-script.sh');
|
|
104
|
-
fs.writeFileSync(scriptPath, '#!/bin/bash\necho "From script"', { mode: 0o755 });
|
|
105
|
-
|
|
106
|
-
const { stdout, code } = await runProposePolicy([
|
|
107
|
-
'--name',
|
|
108
|
-
'script-test',
|
|
109
|
-
'--description',
|
|
110
|
-
'A test script policy',
|
|
111
|
-
'--script-file',
|
|
112
|
-
scriptPath,
|
|
113
|
-
]);
|
|
114
|
-
|
|
115
|
-
expect(code).toBe(0);
|
|
116
|
-
expect(stdout).toContain("Successfully proposed and registered policy 'script-test'");
|
|
117
|
-
|
|
118
|
-
const destScriptPath = path.resolve(e2eDir, '.clawmini/policy-scripts/script-test.sh');
|
|
119
|
-
expect(fs.existsSync(destScriptPath)).toBe(true);
|
|
120
|
-
|
|
121
|
-
const policiesPath = path.resolve(e2eDir, '.clawmini/policies.json');
|
|
122
|
-
const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
|
|
123
|
-
|
|
124
|
-
expect(policies.policies['script-test']).toBeDefined();
|
|
125
|
-
expect(policies.policies['script-test'].command).toBe(
|
|
126
|
-
'./.clawmini/policy-scripts/script-test.sh'
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should overwrite an existing policy with the same name', async () => {
|
|
131
|
-
// Overwrite the 'echo-test' policy from previous test
|
|
132
|
-
const { stdout, code } = await runProposePolicy([
|
|
133
|
-
'--name',
|
|
134
|
-
'echo-test',
|
|
135
|
-
'--description',
|
|
136
|
-
'An updated echo command',
|
|
137
|
-
'--command',
|
|
138
|
-
'echo "Updated"',
|
|
139
|
-
]);
|
|
140
|
-
|
|
141
|
-
expect(code).toBe(0);
|
|
142
|
-
expect(stdout).toContain("Successfully proposed and registered policy 'echo-test'");
|
|
143
|
-
|
|
144
|
-
const policiesPath = path.resolve(e2eDir, '.clawmini/policies.json');
|
|
145
|
-
const policies = JSON.parse(fs.readFileSync(policiesPath, 'utf8'));
|
|
146
|
-
|
|
147
|
-
expect(policies.policies['echo-test']).toBeDefined();
|
|
148
|
-
expect(policies.policies['echo-test'].description).toBe('An updated echo command');
|
|
149
|
-
expect(policies.policies['echo-test'].command).toBe('echo');
|
|
150
|
-
expect(policies.policies['echo-test'].args).toEqual(['"Updated"']);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('propose-policy CLI (uninitialized)', () => {
|
|
155
|
-
const { e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-propose-policy-uninit');
|
|
156
|
-
const binPath = path.resolve(__dirname, '../../../dist/cli/propose-policy.mjs');
|
|
157
|
-
|
|
158
|
-
beforeAll(async () => {
|
|
159
|
-
await setupE2E();
|
|
160
|
-
// Intentionally not running init
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
afterAll(async () => {
|
|
164
|
-
await teardownE2E();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
function runProposePolicy(
|
|
168
|
-
args: string[]
|
|
169
|
-
): Promise<{ stdout: string; stderr: string; code: number | null }> {
|
|
170
|
-
return new Promise((resolve) => {
|
|
171
|
-
const child = spawn('node', [binPath, ...args], {
|
|
172
|
-
cwd: e2eDir,
|
|
173
|
-
env: { ...process.env },
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
let stdout = '';
|
|
177
|
-
let stderr = '';
|
|
178
|
-
|
|
179
|
-
child.stdout.on('data', (data) => (stdout += data.toString()));
|
|
180
|
-
child.stderr.on('data', (data) => (stderr += data.toString()));
|
|
181
|
-
|
|
182
|
-
child.on('close', (code) => {
|
|
183
|
-
resolve({ stdout, stderr, code });
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
it('should fail if .clawmini directory does not exist', async () => {
|
|
189
|
-
const { stderr, code } = await runProposePolicy([
|
|
190
|
-
'--name',
|
|
191
|
-
'echo-test',
|
|
192
|
-
'--description',
|
|
193
|
-
'A simple echo command',
|
|
194
|
-
'--command',
|
|
195
|
-
'echo "Hello World"',
|
|
196
|
-
]);
|
|
197
|
-
|
|
198
|
-
expect(code).toBe(1);
|
|
199
|
-
expect(stderr).toContain(
|
|
200
|
-
'Error: .clawmini directory not found. Please run "clawmini init" first.'
|
|
201
|
-
);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import fsPromises from 'node:fs/promises';
|
|
5
|
-
import { spawn } from 'node:child_process';
|
|
6
|
-
import { createE2EContext } from './utils.js';
|
|
7
|
-
|
|
8
|
-
const { runCli, e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-tmp-requests');
|
|
9
|
-
|
|
10
|
-
describe('E2E Requests Tests (Lite)', () => {
|
|
11
|
-
let litePath = '';
|
|
12
|
-
let envUrl = '';
|
|
13
|
-
let envToken = '';
|
|
14
|
-
|
|
15
|
-
beforeAll(async () => {
|
|
16
|
-
await setupE2E();
|
|
17
|
-
await runCli(['init']);
|
|
18
|
-
|
|
19
|
-
// Setup policies.json
|
|
20
|
-
const policiesPath = path.join(e2eDir, '.clawmini', 'policies.json');
|
|
21
|
-
await fsPromises.writeFile(
|
|
22
|
-
policiesPath,
|
|
23
|
-
JSON.stringify({
|
|
24
|
-
policies: {
|
|
25
|
-
'test-cmd': {
|
|
26
|
-
description: 'A test policy',
|
|
27
|
-
command: 'echo',
|
|
28
|
-
args: ['hello'],
|
|
29
|
-
allowHelp: true,
|
|
30
|
-
},
|
|
31
|
-
'no-help-cmd': {
|
|
32
|
-
description: 'A no help policy',
|
|
33
|
-
command: 'echo',
|
|
34
|
-
args: ['nohelp'],
|
|
35
|
-
},
|
|
36
|
-
'auto-cmd': {
|
|
37
|
-
description: 'An auto approve policy',
|
|
38
|
-
command: 'echo',
|
|
39
|
-
args: ['autoresult'],
|
|
40
|
-
autoApprove: true,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
// Setup daemon with API enabled
|
|
47
|
-
const settingsPath = path.resolve(e2eDir, '.clawmini/settings.json');
|
|
48
|
-
let originalSettings = '{}';
|
|
49
|
-
if (fs.existsSync(settingsPath)) {
|
|
50
|
-
originalSettings = fs.readFileSync(settingsPath, 'utf8');
|
|
51
|
-
}
|
|
52
|
-
fs.writeFileSync(
|
|
53
|
-
settingsPath,
|
|
54
|
-
JSON.stringify({
|
|
55
|
-
...JSON.parse(originalSettings),
|
|
56
|
-
api: { host: '127.0.0.1', port: 3008 },
|
|
57
|
-
})
|
|
58
|
-
);
|
|
59
|
-
await runCli(['up']);
|
|
60
|
-
|
|
61
|
-
// Export lite script
|
|
62
|
-
litePath = path.resolve(e2eDir, 'clawmini-lite.js');
|
|
63
|
-
await runCli(['export-lite', '--out', litePath]);
|
|
64
|
-
|
|
65
|
-
// Setup env-dumper agent
|
|
66
|
-
const envDumperAgentDir = path.resolve(e2eDir, 'lite-env-dumper');
|
|
67
|
-
fs.mkdirSync(envDumperAgentDir, { recursive: true });
|
|
68
|
-
await runCli(['agents', 'add', 'lite-env-dumper', '--dir', 'lite-env-dumper']);
|
|
69
|
-
|
|
70
|
-
const dumperSettings = path.resolve(e2eDir, '.clawmini/agents/lite-env-dumper/settings.json');
|
|
71
|
-
fs.mkdirSync(path.dirname(dumperSettings), { recursive: true });
|
|
72
|
-
const dumperScript = process.platform === 'win32' ? 'set > env.txt' : 'env > env.txt';
|
|
73
|
-
fs.writeFileSync(dumperSettings, JSON.stringify({ commands: { new: dumperScript } }));
|
|
74
|
-
|
|
75
|
-
await runCli(['chats', 'add', 'lite-chat']);
|
|
76
|
-
await runCli(['messages', 'send', 'dump', '--chat', 'lite-chat', '--agent', 'lite-env-dumper']);
|
|
77
|
-
|
|
78
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
79
|
-
|
|
80
|
-
const envTxtPath = path.resolve(envDumperAgentDir, 'env.txt');
|
|
81
|
-
if (!fs.existsSync(envTxtPath)) {
|
|
82
|
-
throw new Error('env.txt not generated by agent');
|
|
83
|
-
}
|
|
84
|
-
const envContent = fs.readFileSync(envTxtPath, 'utf8');
|
|
85
|
-
const urlMatch = envContent.match(/CLAW_API_URL=(.+)/);
|
|
86
|
-
const tokenMatch = envContent.match(/CLAW_API_TOKEN=(.+)/);
|
|
87
|
-
|
|
88
|
-
if (!urlMatch || !tokenMatch) {
|
|
89
|
-
throw new Error('Could not find API credentials');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
envUrl = urlMatch[1]!.trim();
|
|
93
|
-
envToken = tokenMatch[1]!.trim();
|
|
94
|
-
}, 30000);
|
|
95
|
-
|
|
96
|
-
afterAll(teardownE2E, 30000);
|
|
97
|
-
|
|
98
|
-
function runLite(
|
|
99
|
-
args: string[]
|
|
100
|
-
): Promise<{ stdout: string; stderr: string; code: number | null }> {
|
|
101
|
-
return new Promise((resolve) => {
|
|
102
|
-
const p = spawn('node', [litePath, ...args], {
|
|
103
|
-
env: { ...process.env, CLAW_API_URL: envUrl, CLAW_API_TOKEN: envToken },
|
|
104
|
-
cwd: e2eDir,
|
|
105
|
-
});
|
|
106
|
-
let stdout = '';
|
|
107
|
-
let stderr = '';
|
|
108
|
-
p.stdout.on('data', (d) => (stdout += d.toString()));
|
|
109
|
-
p.stderr.on('data', (d) => (stderr += d.toString()));
|
|
110
|
-
p.on('close', (code) => resolve({ stdout, stderr, code }));
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
it('should list policies', async () => {
|
|
115
|
-
const { stdout, code } = await runLite(['requests', 'list']);
|
|
116
|
-
expect(code).toBe(0);
|
|
117
|
-
expect(stdout).toContain('Available Policies:');
|
|
118
|
-
expect(stdout).toContain('- test-cmd');
|
|
119
|
-
expect(stdout).toContain('Description: A test policy');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should run --help on underlying command', async () => {
|
|
123
|
-
const { stdout, code } = await runLite(['request', 'test-cmd', '--help']);
|
|
124
|
-
expect(code).toBe(0);
|
|
125
|
-
expect(stdout).toBeTruthy();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should block --help if allowHelp is not true', async () => {
|
|
129
|
-
const { stderr, code } = await runLite(['request', 'no-help-cmd', '--help']);
|
|
130
|
-
expect(code).toBe(1);
|
|
131
|
-
expect(stderr).toContain('This command does not support --help');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should create a request and return an ID', async () => {
|
|
135
|
-
const dummyFilePath = path.join(e2eDir, 'lite-env-dumper', 'dummy.txt');
|
|
136
|
-
await fsPromises.writeFile(dummyFilePath, 'dummy content');
|
|
137
|
-
|
|
138
|
-
const { stdout, stderr, code } = await runLite([
|
|
139
|
-
'request',
|
|
140
|
-
'test-cmd',
|
|
141
|
-
'--file',
|
|
142
|
-
`target=${dummyFilePath}`,
|
|
143
|
-
'--',
|
|
144
|
-
'extra1',
|
|
145
|
-
'extra2',
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
expect(stderr).toBe('');
|
|
149
|
-
expect(code).toBe(0);
|
|
150
|
-
expect(stdout).toContain('Request created successfully.');
|
|
151
|
-
expect(stdout).toContain('Request ID:');
|
|
152
|
-
|
|
153
|
-
// Verify the request was saved
|
|
154
|
-
const requestsDir = path.join(e2eDir, '.clawmini', 'tmp', 'requests');
|
|
155
|
-
const files = await fsPromises.readdir(requestsDir);
|
|
156
|
-
const jsonFiles = files.filter((f) => f.endsWith('.json'));
|
|
157
|
-
expect(jsonFiles.length).toBe(1);
|
|
158
|
-
|
|
159
|
-
const reqData = await fsPromises.readFile(path.join(requestsDir, jsonFiles[0]!), 'utf8');
|
|
160
|
-
const req = JSON.parse(reqData);
|
|
161
|
-
|
|
162
|
-
expect(req.commandName).toBe('test-cmd');
|
|
163
|
-
expect(req.args).toEqual(['extra1', 'extra2']);
|
|
164
|
-
expect(req.fileMappings).toHaveProperty('target');
|
|
165
|
-
|
|
166
|
-
// Check snapshot exists
|
|
167
|
-
const snapshotPath = req.fileMappings.target;
|
|
168
|
-
const snapshotContent = await fsPromises.readFile(snapshotPath, 'utf8');
|
|
169
|
-
expect(snapshotContent).toBe('dummy content');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should synchronously output execution result for auto-approved policy', async () => {
|
|
173
|
-
const { stdout, stderr, code } = await runLite(['request', 'auto-cmd', '--', 'extra-auto']);
|
|
174
|
-
|
|
175
|
-
expect(stderr).toBe('');
|
|
176
|
-
expect(code).toBe(0);
|
|
177
|
-
// Should output the result of `echo autoresult extra-auto`
|
|
178
|
-
expect(stdout.trim()).toBe('autoresult extra-auto');
|
|
179
|
-
});
|
|
180
|
-
});
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import { createE2EContext } from './utils.js';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
const { runCli, e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-timeout');
|
|
7
|
-
|
|
8
|
-
describe('Session Timeout E2E', () => {
|
|
9
|
-
beforeAll(async () => {
|
|
10
|
-
await setupE2E();
|
|
11
|
-
await runCli(['init', '--agent', 'test-agent', '--agent-template', 'debug']);
|
|
12
|
-
|
|
13
|
-
// Override settings to configure the router with a 3-second timeout
|
|
14
|
-
const settingsPath = path.join(e2eDir, '.clawmini', 'settings.json');
|
|
15
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
16
|
-
|
|
17
|
-
// Explicitly add the custom configured router
|
|
18
|
-
settings.routers = [
|
|
19
|
-
{
|
|
20
|
-
use: '@clawmini/session-timeout',
|
|
21
|
-
with: { timeout: '5s' },
|
|
22
|
-
},
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
26
|
-
|
|
27
|
-
// Force the test agent to succeed so that sessions actually get created!
|
|
28
|
-
const agentSettingsPath = path.join(
|
|
29
|
-
e2eDir,
|
|
30
|
-
'.clawmini',
|
|
31
|
-
'agents',
|
|
32
|
-
'test-agent',
|
|
33
|
-
'settings.json'
|
|
34
|
-
);
|
|
35
|
-
const agentSettings = JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
|
|
36
|
-
agentSettings.commands.new = 'echo "[DEBUG NEW $SESSION_ID] $CLAW_CLI_MESSAGE"';
|
|
37
|
-
agentSettings.commands.append = 'echo "[DEBUG APPEND $SESSION_ID] $CLAW_CLI_MESSAGE"';
|
|
38
|
-
fs.writeFileSync(agentSettingsPath, JSON.stringify(agentSettings, null, 2));
|
|
39
|
-
}, 30000);
|
|
40
|
-
|
|
41
|
-
afterAll(async () => {
|
|
42
|
-
await teardownE2E();
|
|
43
|
-
}, 30000);
|
|
44
|
-
|
|
45
|
-
function getMessages(stdout: string) {
|
|
46
|
-
return stdout
|
|
47
|
-
.trim()
|
|
48
|
-
.split('\n')
|
|
49
|
-
.filter((l) => l.trim().startsWith('{') && l.trim().endsWith('}'))
|
|
50
|
-
.map((l) => {
|
|
51
|
-
try {
|
|
52
|
-
return JSON.parse(l);
|
|
53
|
-
} catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
.filter(Boolean);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getAgentResponse(messages: any[], userMessage: string) {
|
|
61
|
-
return messages.find(
|
|
62
|
-
(m) => m.content && m.content.includes(userMessage) && m.content.includes('[DEBUG')
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
it('(1) sending a message and waiting for timeout, ensuring that the old session has the timeout message sent to it and new messages go to a different session. The user should be sent a system message saying that a new session has started.', async () => {
|
|
67
|
-
// Send an initial message to trigger the router pipeline
|
|
68
|
-
const { code } = await runCli(['messages', 'send', 'first message']);
|
|
69
|
-
expect(code).toBe(0);
|
|
70
|
-
|
|
71
|
-
// Verify the timeout job was scheduled
|
|
72
|
-
const { stdout: jobsList } = await runCli(['jobs', 'list']);
|
|
73
|
-
expect(jobsList).toContain('__session_timeout__');
|
|
74
|
-
|
|
75
|
-
// Wait for the 5-second timeout job to execute
|
|
76
|
-
await new Promise((resolve) => setTimeout(resolve, 5500));
|
|
77
|
-
|
|
78
|
-
// Tail the chat history to verify the timeout message was automatically appended
|
|
79
|
-
const { stdout: history1 } = await runCli(['messages', 'tail', '-n', '30', '--json']);
|
|
80
|
-
|
|
81
|
-
// Validate the automated reply was sent
|
|
82
|
-
expect(history1).toContain('[@clawmini/session-timeout] Starting a fresh session...');
|
|
83
|
-
|
|
84
|
-
// Send another message
|
|
85
|
-
await runCli(['messages', 'send', 'second message']);
|
|
86
|
-
|
|
87
|
-
const { stdout: history2, stderr: err2 } = await runCli([
|
|
88
|
-
'messages',
|
|
89
|
-
'tail',
|
|
90
|
-
'-n',
|
|
91
|
-
'30',
|
|
92
|
-
'--json',
|
|
93
|
-
]);
|
|
94
|
-
const messages = getMessages(history2);
|
|
95
|
-
if (messages.length === 0) console.error('RAW STDOUT:', history2, 'STDERR:', err2);
|
|
96
|
-
|
|
97
|
-
// Check that we see the first message, the fresh session reply, and the second message as a NEW session
|
|
98
|
-
const firstMsgLog = getAgentResponse(messages, 'first message');
|
|
99
|
-
if (!firstMsgLog) console.error('FAILED TO FIND FIRST MSG LOG in:', messages);
|
|
100
|
-
expect(firstMsgLog).toBeDefined();
|
|
101
|
-
|
|
102
|
-
const timeoutOutput = messages.find(
|
|
103
|
-
(m: any) =>
|
|
104
|
-
m.content && m.content.includes('[@clawmini/session-timeout] Starting a fresh session...')
|
|
105
|
-
);
|
|
106
|
-
expect(timeoutOutput).toBeDefined();
|
|
107
|
-
|
|
108
|
-
// The second message should use `commands.new`, which prints `[DEBUG NEW ] second message` because $SESSION_ID is empty
|
|
109
|
-
const secondMsgLog = getAgentResponse(messages, 'second message');
|
|
110
|
-
expect(secondMsgLog).toBeDefined();
|
|
111
|
-
expect(secondMsgLog.content).toContain('[DEBUG NEW ]');
|
|
112
|
-
|
|
113
|
-
// It should NOT be appended.
|
|
114
|
-
const secondMsgAppended = messages.find(
|
|
115
|
-
(m: any) =>
|
|
116
|
-
m.content && m.content.includes('second message') && m.content.includes('[DEBUG APPEND ')
|
|
117
|
-
);
|
|
118
|
-
expect(secondMsgAppended).toBeUndefined();
|
|
119
|
-
}, 20000);
|
|
120
|
-
|
|
121
|
-
it('(2) sending a message, then sending /new, then sending another message. after the timeout for the first message, that session should be told to save; but the current session should still be saved. the user should NOT be sent a system message saying that a new session has started.', async () => {
|
|
122
|
-
// Send an initial message
|
|
123
|
-
await runCli(['messages', 'send', '--chat', 'test2', '--agent', 'test-agent', 'msg A']);
|
|
124
|
-
|
|
125
|
-
// Send /new
|
|
126
|
-
await runCli(['messages', 'send', '--chat', 'test2', '/new']);
|
|
127
|
-
|
|
128
|
-
// Send second message immediately
|
|
129
|
-
await runCli(['messages', 'send', '--chat', 'test2', 'msg B']);
|
|
130
|
-
|
|
131
|
-
// Wait 2 seconds (so we are at 2.5s since msg A, but 2s since msg B)
|
|
132
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
133
|
-
|
|
134
|
-
// Send a keep-alive message so the new session's timeout is reset to 5s from now!
|
|
135
|
-
await runCli(['messages', 'send', '--chat', 'test2', 'msg KEEP ALIVE']);
|
|
136
|
-
|
|
137
|
-
// Wait another 3.5 seconds.
|
|
138
|
-
// Now we are at ~6s since msg A (timeout FIRED).
|
|
139
|
-
// We are at ~3.5s since msg KEEP ALIVE (timeout NOT FIRED).
|
|
140
|
-
await new Promise((resolve) => setTimeout(resolve, 3500));
|
|
141
|
-
const { stdout: history, stderr: err } = await runCli([
|
|
142
|
-
'messages',
|
|
143
|
-
'tail',
|
|
144
|
-
'--chat',
|
|
145
|
-
'test2',
|
|
146
|
-
'-n',
|
|
147
|
-
'30',
|
|
148
|
-
'--json',
|
|
149
|
-
]);
|
|
150
|
-
if (history.trim() === '') console.error('TEST 2 RAW STDOUT EMPTY', err);
|
|
151
|
-
|
|
152
|
-
// The user should NOT be sent a system message saying that a new session has started.
|
|
153
|
-
expect(history).not.toContain('[@clawmini/session-timeout] Starting a fresh session...');
|
|
154
|
-
|
|
155
|
-
// But the background timeout prompt SHOULD have been sent for msg A!
|
|
156
|
-
const messages = getMessages(history);
|
|
157
|
-
if (messages.length === 0) console.error('TEST 2 NO PARSED MESSAGES. RAW:', history);
|
|
158
|
-
|
|
159
|
-
// Find msg KEEP ALIVE and get its session ID
|
|
160
|
-
const keepAliveLog = getAgentResponse(messages, 'msg KEEP ALIVE');
|
|
161
|
-
expect(keepAliveLog).toBeDefined();
|
|
162
|
-
const keepAliveSessionId = keepAliveLog.content.match(/\[DEBUG APPEND (.*?)\]/)?.[1];
|
|
163
|
-
expect(keepAliveSessionId).toBeTruthy();
|
|
164
|
-
|
|
165
|
-
const backgroundPrompts = messages.filter(
|
|
166
|
-
(m: any) => m.content && m.content.includes('This chat session has ended.')
|
|
167
|
-
);
|
|
168
|
-
// We expect exactly ONE background prompt (for the old session)
|
|
169
|
-
if (backgroundPrompts.length === 0)
|
|
170
|
-
console.error('FAILED TO FIND BACKGROUND PROMPT in:', messages);
|
|
171
|
-
expect(backgroundPrompts.length).toBeGreaterThanOrEqual(1);
|
|
172
|
-
|
|
173
|
-
// Send a third message to check that the current session wasn't blown away by the timeout from msg A.
|
|
174
|
-
await runCli(['messages', 'send', '--chat', 'test2', 'msg C']);
|
|
175
|
-
const { stdout: history2 } = await runCli([
|
|
176
|
-
'messages',
|
|
177
|
-
'tail',
|
|
178
|
-
'--chat',
|
|
179
|
-
'test2',
|
|
180
|
-
'-n',
|
|
181
|
-
'20',
|
|
182
|
-
'--json',
|
|
183
|
-
]);
|
|
184
|
-
|
|
185
|
-
const messages2 = getMessages(history2);
|
|
186
|
-
|
|
187
|
-
const appendedMsgLog = getAgentResponse(messages2, 'msg C');
|
|
188
|
-
if (!appendedMsgLog) console.error('FAILED TO FIND APPENDED MSG LOG in:', messages2);
|
|
189
|
-
expect(appendedMsgLog).toBeDefined();
|
|
190
|
-
expect(appendedMsgLog.content).toContain(`[DEBUG APPEND ${keepAliveSessionId}]`);
|
|
191
|
-
}, 20000);
|
|
192
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { createE2EContext } from './utils.js';
|
|
5
|
-
|
|
6
|
-
const { runCli, e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-slash-new');
|
|
7
|
-
|
|
8
|
-
describe('/new Command E2E', () => {
|
|
9
|
-
beforeAll(async () => {
|
|
10
|
-
await setupE2E();
|
|
11
|
-
await runCli(['init', '--agent', 'test-agent', '--agent-template', 'debug']);
|
|
12
|
-
|
|
13
|
-
const agentSettingsPath = path.join(
|
|
14
|
-
e2eDir,
|
|
15
|
-
'.clawmini',
|
|
16
|
-
'agents',
|
|
17
|
-
'test-agent',
|
|
18
|
-
'settings.json'
|
|
19
|
-
);
|
|
20
|
-
const agentSettings = JSON.parse(fs.readFileSync(agentSettingsPath, 'utf8'));
|
|
21
|
-
agentSettings.commands.new = 'echo "[DEBUG NEW $SESSION_ID] $CLAW_CLI_MESSAGE"';
|
|
22
|
-
agentSettings.commands.append = 'echo "[DEBUG APPEND $SESSION_ID] $CLAW_CLI_MESSAGE"';
|
|
23
|
-
fs.writeFileSync(agentSettingsPath, JSON.stringify(agentSettings, null, 2));
|
|
24
|
-
}, 30000);
|
|
25
|
-
|
|
26
|
-
afterAll(async () => {
|
|
27
|
-
await teardownE2E();
|
|
28
|
-
}, 30000);
|
|
29
|
-
|
|
30
|
-
function getMessages(stdout: string) {
|
|
31
|
-
return stdout
|
|
32
|
-
.trim()
|
|
33
|
-
.split('\n')
|
|
34
|
-
.filter((l) => l.trim().startsWith('{') && l.trim().endsWith('}'))
|
|
35
|
-
.map((l) => {
|
|
36
|
-
try {
|
|
37
|
-
return JSON.parse(l);
|
|
38
|
-
} catch {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
.filter(Boolean);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getAgentResponse(messages: any[], userMessage: string) {
|
|
46
|
-
return messages.find(
|
|
47
|
-
(m) => m.content && m.content.includes(userMessage) && m.content.includes('[DEBUG')
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
it('should reset the session ID when /new is sent', async () => {
|
|
52
|
-
// 1. Send an initial message
|
|
53
|
-
const { code } = await runCli(['messages', 'send', 'message 1']);
|
|
54
|
-
expect(code).toBe(0);
|
|
55
|
-
|
|
56
|
-
// 2. Send another message to get an append
|
|
57
|
-
await runCli(['messages', 'send', 'message 2']);
|
|
58
|
-
|
|
59
|
-
// Get the first session ID
|
|
60
|
-
const { stdout: history } = await runCli(['messages', 'tail', '-n', '30', '--json']);
|
|
61
|
-
let messages = getMessages(history);
|
|
62
|
-
|
|
63
|
-
const msg1Log = getAgentResponse(messages, 'message 1');
|
|
64
|
-
expect(msg1Log).toBeDefined();
|
|
65
|
-
const msg2Log = getAgentResponse(messages, 'message 2');
|
|
66
|
-
expect(msg2Log).toBeDefined();
|
|
67
|
-
|
|
68
|
-
// message 2 should be appended to the first session
|
|
69
|
-
const firstSessionMatch = msg2Log.content.match(/\[DEBUG APPEND (.*?)\]/);
|
|
70
|
-
expect(firstSessionMatch).toBeTruthy();
|
|
71
|
-
const firstSessionId = firstSessionMatch[1];
|
|
72
|
-
expect(firstSessionId).toBeTruthy();
|
|
73
|
-
|
|
74
|
-
// 3. Send /new
|
|
75
|
-
await runCli(['messages', 'send', '/new']);
|
|
76
|
-
|
|
77
|
-
// 4. Send a third message
|
|
78
|
-
await runCli(['messages', 'send', 'message 3']);
|
|
79
|
-
|
|
80
|
-
// 5. Check the session ID of the third message
|
|
81
|
-
const { stdout: history2 } = await runCli(['messages', 'tail', '-n', '30', '--json']);
|
|
82
|
-
messages = getMessages(history2);
|
|
83
|
-
|
|
84
|
-
const msg3Log = getAgentResponse(messages, 'message 3');
|
|
85
|
-
expect(msg3Log).toBeDefined();
|
|
86
|
-
|
|
87
|
-
// Because it's a new session, it should use the 'new' command
|
|
88
|
-
expect(msg3Log.content).toContain('[DEBUG NEW ]');
|
|
89
|
-
|
|
90
|
-
// It should NOT be appended
|
|
91
|
-
expect(msg3Log.content).not.toContain('[DEBUG APPEND');
|
|
92
|
-
}, 30000);
|
|
93
|
-
});
|