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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { TestEnvironment } from '../_helpers/test-environment.js';
|
|
4
|
+
|
|
5
|
+
describe('E2E Basic Tests', () => {
|
|
6
|
+
let env: TestEnvironment;
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
env = new TestEnvironment('e2e-tmp-basic');
|
|
10
|
+
await env.setup();
|
|
11
|
+
}, 30000);
|
|
12
|
+
|
|
13
|
+
afterAll(() => env.teardown(), 30000);
|
|
14
|
+
|
|
15
|
+
it('should run init and initialize settings', async () => {
|
|
16
|
+
const { stdout, code } = await env.init();
|
|
17
|
+
|
|
18
|
+
expect(code).toBe(0);
|
|
19
|
+
expect(stdout).toContain('Initialized .clawmini/settings.json');
|
|
20
|
+
|
|
21
|
+
expect(fs.existsSync(env.getClawminiPath('settings.json'))).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should create, list, set-default and delete chats', async () => {
|
|
25
|
+
const { stdout: stdoutAdd, code: codeAdd } = await env.runCli(['chats', 'add', 'test-chat']);
|
|
26
|
+
expect(codeAdd).toBe(0);
|
|
27
|
+
expect(stdoutAdd).toContain('Chat test-chat created successfully.');
|
|
28
|
+
|
|
29
|
+
expect(fs.existsSync(env.getChatPath('test-chat', 'chat.jsonl'))).toBe(true);
|
|
30
|
+
|
|
31
|
+
const { stdout: stdoutList1 } = await env.runCli(['chats', 'list']);
|
|
32
|
+
expect(stdoutList1).toContain('- test-chat');
|
|
33
|
+
|
|
34
|
+
const { stdout: stdoutSetDefault } = await env.runCli(['chats', 'set-default', 'test-chat']);
|
|
35
|
+
expect(stdoutSetDefault).toContain('Default chat set to test-chat.');
|
|
36
|
+
|
|
37
|
+
const { stdout: stdoutList2 } = await env.runCli(['chats', 'list']);
|
|
38
|
+
expect(stdoutList2).toContain('- test-chat *');
|
|
39
|
+
|
|
40
|
+
const { stdout: stdoutDelete } = await env.runCli(['chats', 'delete', 'test-chat']);
|
|
41
|
+
expect(stdoutDelete).toContain('Chat test-chat deleted successfully.');
|
|
42
|
+
expect(fs.existsSync(env.getChatPath('test-chat'))).toBe(false);
|
|
43
|
+
}, 15000);
|
|
44
|
+
});
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const { runCli, e2eDir, setupE2E, teardownE2E } = createE2EContext('e2e-export-lite');
|
|
4
|
+
import { TestEnvironment } from '../_helpers/test-environment.js';
|
|
7
5
|
|
|
8
6
|
describe('E2E Export Lite Tests', () => {
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
let env: TestEnvironment;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
env = new TestEnvironment('e2e-export-lite');
|
|
11
|
+
await env.setup();
|
|
12
|
+
}, 30000);
|
|
13
|
+
|
|
14
|
+
afterAll(() => env.teardown(), 30000);
|
|
11
15
|
|
|
12
16
|
it('should export clawmini-lite script to current directory', async () => {
|
|
13
17
|
// Current directory context in runCli is e2eDir
|
|
14
|
-
const { stdout, code } = await runCli(['export-lite']);
|
|
18
|
+
const { stdout, code } = await env.runCli(['export-lite']);
|
|
15
19
|
expect(code).toBe(0);
|
|
16
20
|
expect(stdout).toContain('Successfully exported clawmini-lite to');
|
|
17
21
|
|
|
18
|
-
const expectedPath = path.join(e2eDir, 'clawmini-lite.js');
|
|
22
|
+
const expectedPath = path.join(env.e2eDir, 'clawmini-lite.js');
|
|
19
23
|
expect(fs.existsSync(expectedPath)).toBe(true);
|
|
20
24
|
|
|
21
25
|
const content = fs.readFileSync(expectedPath, 'utf8');
|
|
@@ -23,25 +27,25 @@ describe('E2E Export Lite Tests', () => {
|
|
|
23
27
|
});
|
|
24
28
|
|
|
25
29
|
it('should export clawmini-lite script to stdout', async () => {
|
|
26
|
-
const { stdout, code } = await runCli(['export-lite', '--stdout']);
|
|
30
|
+
const { stdout, code } = await env.runCli(['export-lite', '--stdout']);
|
|
27
31
|
expect(code).toBe(0);
|
|
28
32
|
expect(stdout).toContain('clawmini-lite - A standalone client');
|
|
29
33
|
expect(stdout).not.toContain('Successfully exported clawmini-lite to');
|
|
30
34
|
});
|
|
31
35
|
|
|
32
36
|
it('should export clawmini-lite script to specified file path', async () => {
|
|
33
|
-
const customPath = path.join(e2eDir, 'custom-lite.js');
|
|
34
|
-
const { stdout, code } = await runCli(['export-lite', '--out', customPath]);
|
|
37
|
+
const customPath = path.join(env.e2eDir, 'custom-lite.js');
|
|
38
|
+
const { stdout, code } = await env.runCli(['export-lite', '--out', customPath]);
|
|
35
39
|
expect(code).toBe(0);
|
|
36
40
|
expect(stdout).toContain(`Successfully exported clawmini-lite to ${customPath}`);
|
|
37
41
|
expect(fs.existsSync(customPath)).toBe(true);
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
it('should export clawmini-lite script to specified directory path', async () => {
|
|
41
|
-
const customDir = path.join(e2eDir, 'custom-dir');
|
|
45
|
+
const customDir = path.join(env.e2eDir, 'custom-dir');
|
|
42
46
|
fs.mkdirSync(customDir);
|
|
43
47
|
|
|
44
|
-
const { stdout, code } = await runCli(['export-lite', '--out', customDir]);
|
|
48
|
+
const { stdout, code } = await env.runCli(['export-lite', '--out', customDir]);
|
|
45
49
|
expect(code).toBe(0);
|
|
46
50
|
|
|
47
51
|
const expectedPath = path.join(customDir, 'clawmini-lite.js');
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { TestEnvironment } from '../_helpers/test-environment.js';
|
|
6
|
+
|
|
7
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
8
|
+
const TEMPLATE_PATH = path.join(REPO_ROOT, 'docs', 'backups', 'clawmini.gitignore');
|
|
9
|
+
|
|
10
|
+
// (path relative to .clawmini/, expected to be ignored by the allow-list)
|
|
11
|
+
const cases: Array<[string, boolean]> = [
|
|
12
|
+
// Safe — must NOT be ignored
|
|
13
|
+
['settings.json', false],
|
|
14
|
+
['policies.json', false],
|
|
15
|
+
['policy-scripts/run-host.js', false],
|
|
16
|
+
['commands/foo.md', false],
|
|
17
|
+
['templates/my-agent/settings.json', false],
|
|
18
|
+
['chats/default/settings.json', false],
|
|
19
|
+
['agents/bob/settings.json', false],
|
|
20
|
+
['agents/bob/installed-files.json', false],
|
|
21
|
+
['environments/macos/env.json', false],
|
|
22
|
+
['adapters/discord/state.json', false],
|
|
23
|
+
|
|
24
|
+
// Sensitive — MUST be ignored
|
|
25
|
+
['chats/default/chat.jsonl', true],
|
|
26
|
+
['adapters/discord/config.json', true],
|
|
27
|
+
['adapters/google-chat/config.json', true],
|
|
28
|
+
['adapters/google-chat/state.json', true],
|
|
29
|
+
['agents/bob/sessions/abc/settings.json', true],
|
|
30
|
+
['agents/bob/tmp/stdout-x.txt', true],
|
|
31
|
+
['daemon.log', true],
|
|
32
|
+
['daemon.sock', true],
|
|
33
|
+
['supervisor.pid', true],
|
|
34
|
+
['logs/web.log', true],
|
|
35
|
+
['tmp/requests/r.json', true],
|
|
36
|
+
['tmp/snapshots/x.txt', true],
|
|
37
|
+
['settings.json.1234.deadbeef.tmp', true],
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
describe('clawmini init installs backup .gitignore', () => {
|
|
41
|
+
let env: TestEnvironment;
|
|
42
|
+
let clawminiDir: string;
|
|
43
|
+
|
|
44
|
+
beforeAll(async () => {
|
|
45
|
+
env = new TestEnvironment('e2e-tmp-init-gitignore');
|
|
46
|
+
await env.setup();
|
|
47
|
+
|
|
48
|
+
const { code, stdout } = await env.init();
|
|
49
|
+
expect(code).toBe(0);
|
|
50
|
+
expect(stdout).toContain('Initialized .clawmini/.gitignore');
|
|
51
|
+
|
|
52
|
+
clawminiDir = env.getClawminiPath();
|
|
53
|
+
execFileSync('git', ['init', '--quiet'], { cwd: clawminiDir });
|
|
54
|
+
}, 30000);
|
|
55
|
+
|
|
56
|
+
afterAll(() => env.teardown(), 30000);
|
|
57
|
+
|
|
58
|
+
it('writes .clawmini/.gitignore matching the docs template', () => {
|
|
59
|
+
const installed = fs.readFileSync(env.getClawminiPath('.gitignore'), 'utf8');
|
|
60
|
+
const template = fs.readFileSync(TEMPLATE_PATH, 'utf8');
|
|
61
|
+
expect(installed).toBe(template);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
for (const [relPath, expectIgnored] of cases) {
|
|
65
|
+
it(`${expectIgnored ? 'ignores' : 'tracks'} ${relPath}`, () => {
|
|
66
|
+
// Materialize the file so git's directory-traversal rules see it the
|
|
67
|
+
// same way they would in a real workspace. (Allow-list `.gitignore`
|
|
68
|
+
// patterns can otherwise behave differently for non-existent paths.)
|
|
69
|
+
const fullPath = path.join(clawminiDir, relPath);
|
|
70
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
71
|
+
if (!fs.existsSync(fullPath)) fs.writeFileSync(fullPath, '');
|
|
72
|
+
|
|
73
|
+
const result = spawnSync('git', ['check-ignore', '-q', '--', relPath], {
|
|
74
|
+
cwd: clawminiDir,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// git check-ignore exit codes:
|
|
78
|
+
// 0 = path is ignored
|
|
79
|
+
// 1 = path is NOT ignored
|
|
80
|
+
// Anything else is an error.
|
|
81
|
+
expect([0, 1]).toContain(result.status);
|
|
82
|
+
const actuallyIgnored = result.status === 0;
|
|
83
|
+
expect(actuallyIgnored).toBe(expectIgnored);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { TestEnvironment } from '../_helpers/test-environment.js';
|
|
5
|
+
|
|
6
|
+
describe('initCmd with flags', () => {
|
|
7
|
+
let env: TestEnvironment;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
env = new TestEnvironment('e2e-tmp-init');
|
|
11
|
+
await env.setup();
|
|
12
|
+
}, 30000);
|
|
13
|
+
|
|
14
|
+
afterAll(() => env.teardown(), 30000);
|
|
15
|
+
|
|
16
|
+
it('should fail if --agent-template is provided without --agent', async () => {
|
|
17
|
+
const { stderr, code } = await env.runCli(['init', '--agent-template', 'bob']);
|
|
18
|
+
expect(code).toBe(1);
|
|
19
|
+
expect(stderr).toContain('--agent-template cannot be used without --agent');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should fail with invalid agent id', async () => {
|
|
23
|
+
const { stderr, code } = await env.runCli(['init', '--agent', 'invalid/id']);
|
|
24
|
+
expect(code).toBe(1);
|
|
25
|
+
expect(stderr).toContain('Invalid agent ID');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should run init, create agent, and set default chat', async () => {
|
|
29
|
+
const { stdout, stderr, code } = await env.runCli(['init', '--agent', 'test-agent']);
|
|
30
|
+
|
|
31
|
+
expect(stderr).toBe('');
|
|
32
|
+
expect(code).toBe(0);
|
|
33
|
+
expect(stdout).toContain('Initialized .clawmini/settings.json');
|
|
34
|
+
expect(stdout).toContain('Agent test-agent created successfully');
|
|
35
|
+
expect(stdout).toContain('Default chat set to test-agent');
|
|
36
|
+
|
|
37
|
+
expect(fs.existsSync(env.getClawminiPath('settings.json'))).toBe(true);
|
|
38
|
+
|
|
39
|
+
const settings = env.getSettings();
|
|
40
|
+
expect((settings.chats as Record<string, unknown> | undefined)?.defaultId).toBe('test-agent');
|
|
41
|
+
|
|
42
|
+
expect(fs.existsSync(env.getAgentPath('test-agent', 'settings.json'))).toBe(true);
|
|
43
|
+
expect(fs.existsSync(env.getChatPath('test-agent', 'settings.json'))).toBe(true);
|
|
44
|
+
|
|
45
|
+
// Verify skills were copied to the agent's default skills directory (.agents/skills)
|
|
46
|
+
const skillsDir = path.join(env.e2eDir, 'test-agent', '.agents', 'skills');
|
|
47
|
+
expect(fs.existsSync(skillsDir)).toBe(true);
|
|
48
|
+
// Check for at least one skill inside
|
|
49
|
+
const skillsList = fs.readdirSync(skillsDir);
|
|
50
|
+
expect(skillsList.length).toBeGreaterThan(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it.skip('should run init and enable an environment', async () => {
|
|
54
|
+
const clawminiDir = env.getClawminiPath();
|
|
55
|
+
if (fs.existsSync(clawminiDir)) {
|
|
56
|
+
fs.rmSync(clawminiDir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { stdout, stderr, code } = await env.runCli(['init', '--environment', 'macos']);
|
|
60
|
+
|
|
61
|
+
expect(stderr).toBe('');
|
|
62
|
+
expect(code).toBe(0);
|
|
63
|
+
expect(stdout).toContain('Initialized .clawmini/settings.json');
|
|
64
|
+
expect(stdout).toContain("Copied environment template 'macos'");
|
|
65
|
+
expect(stdout).toContain("Enabled environment 'macos' for path './'");
|
|
66
|
+
|
|
67
|
+
expect(fs.existsSync(env.getClawminiPath('settings.json'))).toBe(true);
|
|
68
|
+
|
|
69
|
+
const settings = env.getSettings();
|
|
70
|
+
expect(
|
|
71
|
+
(settings.environments as Record<string, unknown> | undefined)?.['./']
|
|
72
|
+
).toBe('macos');
|
|
73
|
+
|
|
74
|
+
expect(fs.existsSync(env.getClawminiPath('environments', 'macos'))).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
TestEnvironment,
|
|
6
|
+
type ChatSubscription,
|
|
7
|
+
commandMatching,
|
|
8
|
+
type SystemMessage,
|
|
9
|
+
type ToolMessage,
|
|
10
|
+
} from '../_helpers/test-environment.js';
|
|
11
|
+
|
|
12
|
+
describe('E2E Messages Tests', () => {
|
|
13
|
+
let env: TestEnvironment;
|
|
14
|
+
let chat: ChatSubscription | undefined;
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
env = new TestEnvironment('e2e-tmp-messages');
|
|
18
|
+
await env.setup();
|
|
19
|
+
await env.init();
|
|
20
|
+
await env.up();
|
|
21
|
+
}, 30000);
|
|
22
|
+
|
|
23
|
+
afterAll(async () => {
|
|
24
|
+
await env.teardown();
|
|
25
|
+
}, 30000);
|
|
26
|
+
|
|
27
|
+
afterEach(() => env.disconnectAll());
|
|
28
|
+
|
|
29
|
+
it('should send a message via the daemon', async () => {
|
|
30
|
+
chat = await env.connect('default');
|
|
31
|
+
|
|
32
|
+
const { stdout, code } = await env.runCli(['messages', 'send', 'e2e test message']);
|
|
33
|
+
|
|
34
|
+
expect(code).toBe(0);
|
|
35
|
+
expect(stdout).toContain('Message sent successfully.');
|
|
36
|
+
|
|
37
|
+
await chat.waitForMessage((m) => m.content === 'e2e test message');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should send a message to a specific chat', async () => {
|
|
41
|
+
await env.addChat('specific-chat', 'default');
|
|
42
|
+
chat = await env.connect('specific-chat');
|
|
43
|
+
|
|
44
|
+
const { stdout, code } = await env.runCli([
|
|
45
|
+
'messages',
|
|
46
|
+
'send',
|
|
47
|
+
'specific chat message',
|
|
48
|
+
'--chat',
|
|
49
|
+
'specific-chat',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
expect(code).toBe(0);
|
|
53
|
+
expect(stdout).toContain('Message sent successfully.');
|
|
54
|
+
|
|
55
|
+
await chat.waitForMessage((m) => m.content === 'specific chat message');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should send a message with a specific agent and persist it', async () => {
|
|
59
|
+
await env.runCli(['agents', 'add', 'custom-agent', '--env', 'CUSTOM_VAR=HELLO']);
|
|
60
|
+
await env.addChat('agent-chat', 'default'); // default agent initially
|
|
61
|
+
chat = await env.connect('agent-chat');
|
|
62
|
+
|
|
63
|
+
const { stdout, code } = await env.runCli([
|
|
64
|
+
'messages',
|
|
65
|
+
'send',
|
|
66
|
+
'hello custom agent',
|
|
67
|
+
'--chat',
|
|
68
|
+
'agent-chat',
|
|
69
|
+
'--agent',
|
|
70
|
+
'custom-agent',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
expect(code).toBe(0);
|
|
74
|
+
expect(stdout).toContain('Message sent successfully.');
|
|
75
|
+
|
|
76
|
+
const chatSettings = env.getChatSettings('agent-chat');
|
|
77
|
+
expect(chatSettings.defaultAgent).toBe('custom-agent');
|
|
78
|
+
|
|
79
|
+
const { stderr: stderrFail, code: codeFail } = await env.runCli([
|
|
80
|
+
'messages',
|
|
81
|
+
'send',
|
|
82
|
+
'fail msg',
|
|
83
|
+
'--chat',
|
|
84
|
+
'agent-chat',
|
|
85
|
+
'--agent',
|
|
86
|
+
'non-existent-agent',
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
expect(codeFail).toBe(1);
|
|
90
|
+
expect(stderrFail).toContain("Error: Agent 'non-existent-agent' not found.");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should send a message with a file attachment', async () => {
|
|
94
|
+
await env.addChat('file-chat', 'default');
|
|
95
|
+
chat = await env.connect('file-chat');
|
|
96
|
+
|
|
97
|
+
const testFilePath = path.resolve(env.e2eDir, 'test-attach.txt');
|
|
98
|
+
fs.writeFileSync(testFilePath, 'file content');
|
|
99
|
+
|
|
100
|
+
const { stdout, code } = await env.runCli([
|
|
101
|
+
'messages',
|
|
102
|
+
'send',
|
|
103
|
+
'here is a file',
|
|
104
|
+
'--chat',
|
|
105
|
+
'file-chat',
|
|
106
|
+
'--file',
|
|
107
|
+
testFilePath,
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
expect(code).toBe(0);
|
|
111
|
+
expect(stdout).toContain('Message sent successfully.');
|
|
112
|
+
|
|
113
|
+
await chat.waitForMessage(
|
|
114
|
+
(m) =>
|
|
115
|
+
!!(m.content && m.content.includes('here is a file') && m.content.includes('test-attach'))
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should view history with tail and --json flag', async () => {
|
|
120
|
+
await env.addChat('tail-chat', 'default');
|
|
121
|
+
chat = await env.connect('tail-chat');
|
|
122
|
+
|
|
123
|
+
const { code: sendCode } = await env.runCli([
|
|
124
|
+
'messages',
|
|
125
|
+
'send',
|
|
126
|
+
'tail chat message',
|
|
127
|
+
'--chat',
|
|
128
|
+
'tail-chat',
|
|
129
|
+
]);
|
|
130
|
+
expect(sendCode).toBe(0);
|
|
131
|
+
|
|
132
|
+
await chat.waitForMessage((m) => m.content === 'tail chat message');
|
|
133
|
+
|
|
134
|
+
const { stdout, code } = await env.runCli(['messages', 'tail', '--chat', 'tail-chat']);
|
|
135
|
+
expect(code).toBe(0);
|
|
136
|
+
expect(stdout).toContain('[USER]');
|
|
137
|
+
expect(stdout).toContain('tail chat message');
|
|
138
|
+
|
|
139
|
+
const { stdout: jsonStdout, code: jsonCode } = await env.runCli([
|
|
140
|
+
'messages',
|
|
141
|
+
'tail',
|
|
142
|
+
'--json',
|
|
143
|
+
'--chat',
|
|
144
|
+
'tail-chat',
|
|
145
|
+
]);
|
|
146
|
+
expect(jsonCode).toBe(0);
|
|
147
|
+
expect(jsonStdout).toContain('"role":"user"');
|
|
148
|
+
expect(jsonStdout).toContain('"content":"tail chat message"');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should maintain atomic ordering of user and log messages with --no-wait', async () => {
|
|
152
|
+
await env.addAgent('order-agent');
|
|
153
|
+
env.writeAgentSettings('order-agent', {
|
|
154
|
+
commands: { new: 'sleep 1 && echo $CLAW_CLI_MESSAGE' },
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await env.addChat('order-chat', 'order-agent');
|
|
158
|
+
chat = await env.connect('order-chat');
|
|
159
|
+
|
|
160
|
+
await env.runCli(['messages', 'send', 'first', '--chat', 'order-chat', '--no-wait']);
|
|
161
|
+
await env.runCli(['messages', 'send', 'second', '--chat', 'order-chat', '--no-wait']);
|
|
162
|
+
|
|
163
|
+
// We wait for the second command log to arrive.
|
|
164
|
+
// The messages buffer will collect all messages as they arrive via SSE.
|
|
165
|
+
await chat.waitForMessage(
|
|
166
|
+
(m) => !!(m.role === 'command' && m.content && m.content.trim() === 'second')
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Retrieve the stored messages via trpcClient to ensure final ordering on disk
|
|
170
|
+
const storedMessages = await env.trpcClient!.getMessages.query({ chatId: 'order-chat' });
|
|
171
|
+
|
|
172
|
+
const commandLogs = storedMessages.filter((m) => m.role === 'command');
|
|
173
|
+
expect(commandLogs).toHaveLength(2);
|
|
174
|
+
expect(storedMessages[0]!.role).toBe('user');
|
|
175
|
+
expect(storedMessages[0]!.content).toBe('first');
|
|
176
|
+
expect(storedMessages[1]!.role).toBe('user');
|
|
177
|
+
expect(storedMessages[1]!.content).toBe('second');
|
|
178
|
+
expect(commandLogs[0]!.content.trim()).toBe('first');
|
|
179
|
+
expect(commandLogs[1]!.content.trim()).toBe('second');
|
|
180
|
+
}, 10000);
|
|
181
|
+
|
|
182
|
+
it('should no-op (no command log, no agent reply) when the message is whitespace-only', async () => {
|
|
183
|
+
await env.addAgent('empty-msg-agent');
|
|
184
|
+
env.writeAgentSettings('empty-msg-agent', {
|
|
185
|
+
commands: { new: 'echo should-not-run' },
|
|
186
|
+
});
|
|
187
|
+
await env.addChat('empty-msg-chat', 'empty-msg-agent');
|
|
188
|
+
chat = await env.connect('empty-msg-chat');
|
|
189
|
+
|
|
190
|
+
const { code } = await env.runCli([
|
|
191
|
+
'messages',
|
|
192
|
+
'send',
|
|
193
|
+
' ',
|
|
194
|
+
'--chat',
|
|
195
|
+
'empty-msg-chat',
|
|
196
|
+
]);
|
|
197
|
+
expect(code).toBe(0);
|
|
198
|
+
|
|
199
|
+
// The user message is still persisted before the short-circuit,
|
|
200
|
+
await chat.waitForMessage((m) => m.role === 'user' && m.content.trim() === '');
|
|
201
|
+
// but handleMessage bails before the agent runs: no command log, no reply.
|
|
202
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
203
|
+
expect(chat.messageBuffer.some((m) => m.role === 'command' || m.role === 'agent')).toBe(
|
|
204
|
+
false
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should handle full multi-message session workflow (extraction & append)', async () => {
|
|
209
|
+
await env.addAgent('workflow-agent');
|
|
210
|
+
env.writeAgentSettings('workflow-agent', {
|
|
211
|
+
commands: {
|
|
212
|
+
new: 'echo "NEW $CLAW_CLI_MESSAGE" && echo "ERR NEW" >&2',
|
|
213
|
+
append: 'echo "APPEND $CLAW_CLI_MESSAGE" && echo "ERR APPEND" >&2',
|
|
214
|
+
getSessionId: 'echo "session-123"',
|
|
215
|
+
getMessageContent: 'sed "s/^/EXTRACTED-/"',
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
await env.addChat('workflow-chat', 'workflow-agent');
|
|
220
|
+
chat = await env.connect('workflow-chat');
|
|
221
|
+
|
|
222
|
+
await env.runCli(['messages', 'send', 'msg-1', '--chat', 'workflow-chat']);
|
|
223
|
+
|
|
224
|
+
const log1 = await chat.waitForMessage(
|
|
225
|
+
commandMatching((m) => m.command.includes('ERR NEW'))
|
|
226
|
+
);
|
|
227
|
+
expect(log1.command).toBe('echo "NEW $CLAW_CLI_MESSAGE" && echo "ERR NEW" >&2');
|
|
228
|
+
expect(log1.content).toContain('EXTRACTED-NEW msg-1');
|
|
229
|
+
expect(log1.stderr).toContain('ERR NEW');
|
|
230
|
+
expect(log1.stdout).toContain('NEW msg-1');
|
|
231
|
+
|
|
232
|
+
const sessionSettings = env.getSessionSettings('workflow-agent', 'default');
|
|
233
|
+
expect(
|
|
234
|
+
(sessionSettings.env as Record<string, unknown> | undefined)?.SESSION_ID
|
|
235
|
+
).toBe('session-123');
|
|
236
|
+
|
|
237
|
+
await env.runCli(['messages', 'send', 'msg-2', '--chat', 'workflow-chat']);
|
|
238
|
+
|
|
239
|
+
const log2 = await chat.waitForMessage(
|
|
240
|
+
commandMatching((m) => m.command.includes('ERR APPEND'))
|
|
241
|
+
);
|
|
242
|
+
expect(log2.command).toBe('echo "APPEND $CLAW_CLI_MESSAGE" && echo "ERR APPEND" >&2');
|
|
243
|
+
expect(log2.content).toContain('EXTRACTED-APPEND msg-2');
|
|
244
|
+
expect(log2.stderr).toContain('ERR APPEND');
|
|
245
|
+
expect(log2.stdout).toContain('APPEND msg-2');
|
|
246
|
+
|
|
247
|
+
// Break the extraction command and verify the failure is reported
|
|
248
|
+
env.updateAgentSettings('workflow-agent', {
|
|
249
|
+
commands: { getMessageContent: 'echo "EXTRACTION_FAIL" >&2 && exit 1' },
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
await env.runCli(['messages', 'send', 'msg-3', '--chat', 'workflow-chat']);
|
|
253
|
+
|
|
254
|
+
const log3 = await chat.waitForMessage(
|
|
255
|
+
commandMatching((m) => m.stderr.includes('EXTRACTION_FAIL'))
|
|
256
|
+
);
|
|
257
|
+
expect(log3.stdout).toContain('APPEND msg-3');
|
|
258
|
+
expect(log3.stderr).toContain('ERR APPEND');
|
|
259
|
+
expect(log3.stderr).toContain('getMessageContent failed: EXTRACTION_FAIL');
|
|
260
|
+
}, 15000);
|
|
261
|
+
|
|
262
|
+
it('should tail legacy chat entries that predate the sessionId field', async () => {
|
|
263
|
+
await env.addChat('legacy-chat', 'default');
|
|
264
|
+
|
|
265
|
+
const ts = new Date().toISOString();
|
|
266
|
+
const legacyLines = [
|
|
267
|
+
{ id: 'u1', role: 'user', content: 'legacy user message', timestamp: ts },
|
|
268
|
+
{
|
|
269
|
+
id: 'c1',
|
|
270
|
+
role: 'command',
|
|
271
|
+
content: 'legacy cmd output',
|
|
272
|
+
messageId: 'u1',
|
|
273
|
+
command: 'echo legacy',
|
|
274
|
+
cwd: '/tmp',
|
|
275
|
+
stdout: 'legacy',
|
|
276
|
+
stderr: '',
|
|
277
|
+
exitCode: 0,
|
|
278
|
+
timestamp: ts,
|
|
279
|
+
},
|
|
280
|
+
{ id: 'a1', role: 'agent', content: 'legacy agent reply', timestamp: ts },
|
|
281
|
+
];
|
|
282
|
+
const chatFile = env.getChatPath('legacy-chat', 'chat.jsonl');
|
|
283
|
+
fs.writeFileSync(chatFile, legacyLines.map((l) => JSON.stringify(l)).join('\n') + '\n');
|
|
284
|
+
|
|
285
|
+
const { stdout, code } = await env.runCli(['messages', 'tail', '--chat', 'legacy-chat']);
|
|
286
|
+
expect(code).toBe(0);
|
|
287
|
+
expect(stdout).toContain('legacy user message');
|
|
288
|
+
expect(stdout).toContain('legacy agent reply');
|
|
289
|
+
|
|
290
|
+
const { stdout: jsonStdout, code: jsonCode } = await env.runCli([
|
|
291
|
+
'messages',
|
|
292
|
+
'tail',
|
|
293
|
+
'--json',
|
|
294
|
+
'--chat',
|
|
295
|
+
'legacy-chat',
|
|
296
|
+
]);
|
|
297
|
+
expect(jsonCode).toBe(0);
|
|
298
|
+
expect(jsonStdout).toContain('"content":"legacy user message"');
|
|
299
|
+
expect(jsonStdout).toContain('"content":"legacy cmd output"');
|
|
300
|
+
expect(jsonStdout).toContain('"content":"legacy agent reply"');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('E2E Messages sessionId Persistence', () => {
|
|
305
|
+
let env: TestEnvironment;
|
|
306
|
+
let chat: ChatSubscription | undefined;
|
|
307
|
+
|
|
308
|
+
beforeAll(async () => {
|
|
309
|
+
env = new TestEnvironment('e2e-tmp-msg-sid');
|
|
310
|
+
await env.setup();
|
|
311
|
+
await env.setupSubagentEnv();
|
|
312
|
+
await env.getAgentCredentials();
|
|
313
|
+
}, 30000);
|
|
314
|
+
|
|
315
|
+
afterAll(() => env.teardown(), 30000);
|
|
316
|
+
afterEach(() => env.disconnectAll());
|
|
317
|
+
|
|
318
|
+
it('stamps sessionId on user, command, agent, tool, and cron system messages', async () => {
|
|
319
|
+
chat = await env.connect('__creds__');
|
|
320
|
+
|
|
321
|
+
await env.sendMessage('echo hello-session', {
|
|
322
|
+
chat: '__creds__',
|
|
323
|
+
agent: 'debug-agent',
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const userMsg = await chat.waitForMessage(
|
|
327
|
+
(m) => m.role === 'user' && m.content === 'echo hello-session'
|
|
328
|
+
);
|
|
329
|
+
const cmdMsg = await chat.waitForMessage(
|
|
330
|
+
commandMatching((m) => m.stdout.includes('echo hello-session'))
|
|
331
|
+
);
|
|
332
|
+
const replyMsg = await chat.waitForMessage((m) => m.role === 'agent');
|
|
333
|
+
|
|
334
|
+
expect(userMsg.sessionId).toBeTruthy();
|
|
335
|
+
expect(cmdMsg.sessionId).toBe(userMsg.sessionId);
|
|
336
|
+
expect(replyMsg.sessionId).toBe(userMsg.sessionId);
|
|
337
|
+
|
|
338
|
+
await env.runLite(['tool', 'session-tool', JSON.stringify({ probe: 1 })]);
|
|
339
|
+
const toolMsg = await chat.waitForMessage(
|
|
340
|
+
(m): m is ToolMessage => m.role === 'tool' && m.name === 'session-tool'
|
|
341
|
+
);
|
|
342
|
+
expect(toolMsg.sessionId).toBe(userMsg.sessionId);
|
|
343
|
+
|
|
344
|
+
await env.runLite([
|
|
345
|
+
'jobs', 'add', 'session-id-cron',
|
|
346
|
+
'--at', '1s',
|
|
347
|
+
'--message', 'echo session-cron-fired',
|
|
348
|
+
'--session', 'new',
|
|
349
|
+
]);
|
|
350
|
+
const sysMsg = await chat.waitForMessage(
|
|
351
|
+
(m): m is SystemMessage =>
|
|
352
|
+
m.role === 'system' && (m as SystemMessage).event === 'cron',
|
|
353
|
+
10000
|
|
354
|
+
);
|
|
355
|
+
expect(sysMsg.sessionId).toBeTruthy();
|
|
356
|
+
|
|
357
|
+
const cronCmd = await chat.waitForMessage(
|
|
358
|
+
commandMatching((m) => m.stdout.includes('echo session-cron-fired')),
|
|
359
|
+
10000
|
|
360
|
+
);
|
|
361
|
+
expect(cronCmd.sessionId).toBe(sysMsg.sessionId);
|
|
362
|
+
}, 45000);
|
|
363
|
+
});
|