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,80 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import {
|
|
6
|
+
TestEnvironment,
|
|
7
|
+
type ChatSubscription,
|
|
8
|
+
commandWith,
|
|
9
|
+
agentReplyWith,
|
|
10
|
+
} from '../_helpers/test-environment.js';
|
|
11
|
+
|
|
12
|
+
describe('E2E Agent Custom API Env Var Names', () => {
|
|
13
|
+
let env: TestEnvironment;
|
|
14
|
+
let chat: ChatSubscription | undefined;
|
|
15
|
+
|
|
16
|
+
beforeAll(async () => {
|
|
17
|
+
env = new TestEnvironment('e2e-custom-api-env');
|
|
18
|
+
await env.setup();
|
|
19
|
+
await env.setupSubagentEnv();
|
|
20
|
+
}, 30000);
|
|
21
|
+
|
|
22
|
+
afterAll(() => env.teardown(), 30000);
|
|
23
|
+
afterEach(() => env.disconnectAll());
|
|
24
|
+
|
|
25
|
+
it('injects custom-named URL/token envs and lite consumes them via pointer vars', async () => {
|
|
26
|
+
await env.runCli(['agents', 'add', 'custom-env-dumper']);
|
|
27
|
+
const agentDir = path.resolve(env.e2eDir, 'custom-env-dumper');
|
|
28
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
29
|
+
|
|
30
|
+
env.writeAgentSettings('custom-env-dumper', {
|
|
31
|
+
apiTokenEnvVar: 'MY_CUSTOM_TOKEN',
|
|
32
|
+
apiUrlEnvVar: 'MY_CUSTOM_URL',
|
|
33
|
+
commands: { new: 'env' },
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await env.addChat('custom-env-chat', 'custom-env-dumper');
|
|
37
|
+
chat = await env.connect('custom-env-chat');
|
|
38
|
+
|
|
39
|
+
await env.sendMessage('dump', { chat: 'custom-env-chat', agent: 'custom-env-dumper' });
|
|
40
|
+
|
|
41
|
+
const log = await chat.waitForMessage(commandWith('MY_CUSTOM_URL='));
|
|
42
|
+
|
|
43
|
+
expect(log.stdout).toContain('CLAW_LITE_API_VAR=MY_CUSTOM_TOKEN');
|
|
44
|
+
expect(log.stdout).toContain('CLAW_LITE_URL_VAR=MY_CUSTOM_URL');
|
|
45
|
+
expect(log.stdout).toMatch(/^MY_CUSTOM_URL=http:\/\/127\.0\.0\.1:\d+$/m);
|
|
46
|
+
expect(log.stdout).toMatch(/^MY_CUSTOM_TOKEN=.+$/m);
|
|
47
|
+
// The default-named vars should NOT be present when custom names are set.
|
|
48
|
+
expect(log.stdout).not.toMatch(/^CLAW_API_URL=/m);
|
|
49
|
+
expect(log.stdout).not.toMatch(/^CLAW_API_TOKEN=/m);
|
|
50
|
+
|
|
51
|
+
const urlMatch = log.stdout.match(/^MY_CUSTOM_URL=(.+)$/m);
|
|
52
|
+
const tokenMatch = log.stdout.match(/^MY_CUSTOM_TOKEN=(.+)$/m);
|
|
53
|
+
const customUrl = urlMatch![1]!.trim();
|
|
54
|
+
const customToken = tokenMatch![1]!.trim();
|
|
55
|
+
|
|
56
|
+
// Spawn lite directly (TestEnvironment.runLite forces CLAW_API_URL/TOKEN, which
|
|
57
|
+
// wouldn't exercise the dynamic-name path).
|
|
58
|
+
const litePath = path.resolve(env.e2eDir, 'clawmini-lite.js');
|
|
59
|
+
const reply = await new Promise<{ stdout: string; code: number | null }>((resolve) => {
|
|
60
|
+
const p = spawn('node', [litePath, 'reply', 'hello from custom env'], {
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
MY_CUSTOM_URL: customUrl,
|
|
64
|
+
MY_CUSTOM_TOKEN: customToken,
|
|
65
|
+
CLAW_LITE_URL_VAR: 'MY_CUSTOM_URL',
|
|
66
|
+
CLAW_LITE_API_VAR: 'MY_CUSTOM_TOKEN',
|
|
67
|
+
},
|
|
68
|
+
cwd: agentDir,
|
|
69
|
+
});
|
|
70
|
+
let stdout = '';
|
|
71
|
+
p.stdout.on('data', (d) => (stdout += d.toString()));
|
|
72
|
+
p.stderr.on('data', (d) => (stdout += d.toString()));
|
|
73
|
+
p.on('close', (code) => resolve({ stdout, code }));
|
|
74
|
+
});
|
|
75
|
+
expect(reply.stdout).toContain('Reply message appended');
|
|
76
|
+
|
|
77
|
+
const replyMsg = await chat.waitForMessage(agentReplyWith('hello from custom env'));
|
|
78
|
+
expect(replyMsg).toBeTruthy();
|
|
79
|
+
}, 20000);
|
|
80
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
TestEnvironment,
|
|
6
|
+
type ChatSubscription,
|
|
7
|
+
type ToolMessage,
|
|
8
|
+
agentReplyWith,
|
|
9
|
+
} from '../_helpers/test-environment.js';
|
|
10
|
+
|
|
11
|
+
// Covers lite-only commands that aren't reachable via the main CLI: reply,
|
|
12
|
+
// reply --file, tool, and fetch-pending. Lite's jobs CRUD is covered by
|
|
13
|
+
// e2e/jobs/agent-jobs.test.ts and e2e/jobs/cron.test.ts.
|
|
14
|
+
//
|
|
15
|
+
// Every `it` here shares `__creds__` — the chat TestEnvironment.runLite uses
|
|
16
|
+
// when resolving its API token. A token is scoped to a single chatId, so all
|
|
17
|
+
// lite mutations in this file target `__creds__`.
|
|
18
|
+
|
|
19
|
+
describe('E2E Export Lite Chat/Tool Commands', () => {
|
|
20
|
+
let env: TestEnvironment;
|
|
21
|
+
let chat: ChatSubscription | undefined;
|
|
22
|
+
|
|
23
|
+
beforeAll(async () => {
|
|
24
|
+
env = new TestEnvironment('e2e-exp-lite');
|
|
25
|
+
await env.setup();
|
|
26
|
+
await env.setupSubagentEnv();
|
|
27
|
+
// Warm up credentials (creates `__creds__` chat + sends one debug echo).
|
|
28
|
+
await env.getAgentCredentials();
|
|
29
|
+
}, 30000);
|
|
30
|
+
|
|
31
|
+
afterAll(() => env.teardown(), 30000);
|
|
32
|
+
afterEach(() => env.disconnectAll());
|
|
33
|
+
|
|
34
|
+
it('reply appends an agent reply to the chat', async () => {
|
|
35
|
+
chat = await env.connect('__creds__');
|
|
36
|
+
const { stdout, code } = await env.runLite(['reply', 'hello reply']);
|
|
37
|
+
expect(code).toBe(0);
|
|
38
|
+
expect(stdout).toContain('Reply message appended');
|
|
39
|
+
|
|
40
|
+
const msg = await chat.waitForMessage(agentReplyWith('hello reply'));
|
|
41
|
+
expect(msg).toBeTruthy();
|
|
42
|
+
}, 15000);
|
|
43
|
+
|
|
44
|
+
it('reply --file records the attachment path relative to the workspace', async () => {
|
|
45
|
+
chat = await env.connect('__creds__');
|
|
46
|
+
const agentDir = path.resolve(env.e2eDir, 'debug-agent');
|
|
47
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(path.join(agentDir, 'attach.txt'), 'attached');
|
|
49
|
+
|
|
50
|
+
const { stdout, code } = await env.runLite(
|
|
51
|
+
['reply', 'hello with file', '--file', 'attach.txt'],
|
|
52
|
+
{ cwd: agentDir }
|
|
53
|
+
);
|
|
54
|
+
expect(code).toBe(0);
|
|
55
|
+
expect(stdout).toContain('Reply message appended');
|
|
56
|
+
|
|
57
|
+
const msg = await chat.waitForMessage(agentReplyWith('hello with file'));
|
|
58
|
+
expect(msg.files).toEqual(['debug-agent/attach.txt']);
|
|
59
|
+
}, 15000);
|
|
60
|
+
|
|
61
|
+
it('tool appends a tool message preserving the JSON payload', async () => {
|
|
62
|
+
chat = await env.connect('__creds__');
|
|
63
|
+
const { stdout, code } = await env.runLite([
|
|
64
|
+
'tool',
|
|
65
|
+
'mytool',
|
|
66
|
+
JSON.stringify({ key: 'value' }),
|
|
67
|
+
]);
|
|
68
|
+
expect(code).toBe(0);
|
|
69
|
+
expect(stdout).toContain('Tool message appended');
|
|
70
|
+
|
|
71
|
+
const msg = await chat.waitForMessage(
|
|
72
|
+
(m): m is ToolMessage => m.role === 'tool' && m.name === 'mytool'
|
|
73
|
+
);
|
|
74
|
+
expect(msg.payload).toEqual({ key: 'value' });
|
|
75
|
+
}, 15000);
|
|
76
|
+
|
|
77
|
+
it('fetch-pending returns queued messages while the agent is busy', async () => {
|
|
78
|
+
chat = await env.connect('__creds__');
|
|
79
|
+
// Swap debug-agent's commands to something slow so the first send blocks
|
|
80
|
+
// the task queue and the second send stays pending until fetch-pending.
|
|
81
|
+
// Both `new` and `append` must be overridden — getAgentCredentials has
|
|
82
|
+
// already captured a SESSION_ID, so subsequent sends use `append`.
|
|
83
|
+
env.updateAgentSettings('debug-agent', {
|
|
84
|
+
commands: { new: 'sleep 5', append: 'sleep 5' },
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await env.sendMessage('block queue', {
|
|
88
|
+
chat: '__creds__',
|
|
89
|
+
agent: 'debug-agent',
|
|
90
|
+
noWait: true,
|
|
91
|
+
});
|
|
92
|
+
await env.sendMessage('my pending message', {
|
|
93
|
+
chat: '__creds__',
|
|
94
|
+
agent: 'debug-agent',
|
|
95
|
+
noWait: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const { stdout, code } = await env.runLite(['fetch-pending']);
|
|
99
|
+
expect(code).toBe(0);
|
|
100
|
+
expect(stdout).toContain('<message>');
|
|
101
|
+
expect(stdout).toContain('my pending message');
|
|
102
|
+
expect(stdout).toContain('</message>');
|
|
103
|
+
}, 20000);
|
|
104
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
agentReply,
|
|
9
|
+
} from '../_helpers/test-environment.js';
|
|
10
|
+
|
|
11
|
+
describe('E2E Fallbacks Tests', () => {
|
|
12
|
+
let env: TestEnvironment;
|
|
13
|
+
let chat: ChatSubscription | undefined;
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
env = new TestEnvironment('e2e-fallbacks');
|
|
17
|
+
await env.setup();
|
|
18
|
+
await env.init();
|
|
19
|
+
await env.up();
|
|
20
|
+
}, 30000);
|
|
21
|
+
|
|
22
|
+
afterAll(() => env.teardown(), 30000);
|
|
23
|
+
afterEach(() => env.disconnectAll());
|
|
24
|
+
|
|
25
|
+
it('should fallback when base agent fails with exit code', async () => {
|
|
26
|
+
env.setDefaultAgent({
|
|
27
|
+
commands: {
|
|
28
|
+
new: 'if [ "$SUCCESS" = "true" ]; then echo "Succeeded"; else echo "Failed" >&2; exit 1; fi',
|
|
29
|
+
},
|
|
30
|
+
fallbacks: [{ env: { SUCCESS: 'true' }, retries: 0, delayMs: 100 }],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
await env.addChat('fb-chat-1');
|
|
34
|
+
chat = await env.connect('fb-chat-1');
|
|
35
|
+
await env.sendMessage('test-1', { chat: 'fb-chat-1' });
|
|
36
|
+
|
|
37
|
+
const success = await chat.waitForMessage(
|
|
38
|
+
commandMatching((m) => m.stdout.trim() === 'Succeeded' && m.exitCode === 0)
|
|
39
|
+
);
|
|
40
|
+
expect(success).toBeTruthy();
|
|
41
|
+
expect(
|
|
42
|
+
chat.messageBuffer.some((m) => m.role === 'command' && m.content.includes('retrying'))
|
|
43
|
+
).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should fallback when base agent returns empty content', async () => {
|
|
47
|
+
env.setDefaultAgent({
|
|
48
|
+
commands: {
|
|
49
|
+
new: 'echo "Base output"',
|
|
50
|
+
getMessageContent: 'echo ""',
|
|
51
|
+
},
|
|
52
|
+
fallbacks: [
|
|
53
|
+
{
|
|
54
|
+
commands: { getMessageContent: 'echo "Fallback success"' },
|
|
55
|
+
retries: 0,
|
|
56
|
+
delayMs: 100,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await env.addChat('fb-chat-2');
|
|
62
|
+
chat = await env.connect('fb-chat-2');
|
|
63
|
+
await env.sendMessage('test-2', { chat: 'fb-chat-2' });
|
|
64
|
+
|
|
65
|
+
const reply = await chat.waitForMessage(agentReply());
|
|
66
|
+
expect(reply.content.trim()).toBe('Fallback success');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should support multiple retries with exponential backoff logs', async () => {
|
|
70
|
+
const attemptFile = path.resolve(env.e2eDir, 'attempts.txt');
|
|
71
|
+
fs.writeFileSync(attemptFile, '0');
|
|
72
|
+
|
|
73
|
+
env.setDefaultAgent({
|
|
74
|
+
commands: {
|
|
75
|
+
new: `
|
|
76
|
+
attempts=$(cat ${attemptFile})
|
|
77
|
+
attempts=$((attempts + 1))
|
|
78
|
+
echo $attempts > ${attemptFile}
|
|
79
|
+
if [ $attempts -lt 3 ]; then
|
|
80
|
+
exit 1
|
|
81
|
+
else
|
|
82
|
+
echo "Third time is a charm"
|
|
83
|
+
fi
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
fallbacks: [{ retries: 2, delayMs: 100 }],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await env.addChat('fb-chat-3');
|
|
90
|
+
chat = await env.connect('fb-chat-3');
|
|
91
|
+
await env.sendMessage('test-3', { chat: 'fb-chat-3' });
|
|
92
|
+
|
|
93
|
+
const success = await chat.waitForMessage(
|
|
94
|
+
commandMatching((m) => m.stdout.trim() === 'Third time is a charm')
|
|
95
|
+
);
|
|
96
|
+
expect(success).toBeTruthy();
|
|
97
|
+
expect(
|
|
98
|
+
chat.messageBuffer.filter((m) => m.role === 'command' && m.content.includes('retrying'))
|
|
99
|
+
.length
|
|
100
|
+
).toBeGreaterThanOrEqual(1);
|
|
101
|
+
}, 10000);
|
|
102
|
+
|
|
103
|
+
it('should report final failure when all fallbacks are exhausted', async () => {
|
|
104
|
+
env.setDefaultAgent({
|
|
105
|
+
commands: { new: 'exit 1' },
|
|
106
|
+
fallbacks: [
|
|
107
|
+
{
|
|
108
|
+
commands: { new: 'echo "Fallback 1 fail" && exit 1' },
|
|
109
|
+
retries: 0,
|
|
110
|
+
delayMs: 100,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await env.addChat('fb-chat-4');
|
|
116
|
+
chat = await env.connect('fb-chat-4');
|
|
117
|
+
await env.sendMessage('test-4', { chat: 'fb-chat-4' });
|
|
118
|
+
|
|
119
|
+
const failure = await chat.waitForMessage(
|
|
120
|
+
commandMatching((m) => m.stdout.trim() === 'Fallback 1 fail' && m.exitCode === 1)
|
|
121
|
+
);
|
|
122
|
+
expect(failure).toBeTruthy();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import { TestEnvironment, type ChatSubscription, commandMatching } from '../_helpers/test-environment.js';
|
|
3
|
+
|
|
4
|
+
describe('E2E Agent Interrupt + Pending Message Merge', () => {
|
|
5
|
+
let env: TestEnvironment;
|
|
6
|
+
let chat: ChatSubscription | undefined;
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
env = new TestEnvironment('e2e-agent-interrupt');
|
|
10
|
+
await env.setup();
|
|
11
|
+
await env.init();
|
|
12
|
+
await env.up();
|
|
13
|
+
}, 30000);
|
|
14
|
+
|
|
15
|
+
afterAll(() => env.teardown(), 30000);
|
|
16
|
+
afterEach(() => env.disconnectAll());
|
|
17
|
+
|
|
18
|
+
it('aborts the active task and prepends pending payloads to the new message', async () => {
|
|
19
|
+
await env.runCli(['agents', 'add', 'interrupt-agent']);
|
|
20
|
+
env.writeAgentSettings('interrupt-agent', {
|
|
21
|
+
// Echo the message we were invoked with so the final merged content lands
|
|
22
|
+
// in the command log's stdout, then sleep long enough that the first
|
|
23
|
+
// run is still active when /interrupt arrives.
|
|
24
|
+
commands: { new: 'echo "RAN: $CLAW_CLI_MESSAGE" && sleep 2' },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await env.addChat('interrupt-chat', 'interrupt-agent');
|
|
28
|
+
chat = await env.connect('interrupt-chat');
|
|
29
|
+
|
|
30
|
+
// first: becomes the active task
|
|
31
|
+
await env.sendMessage('first', { chat: 'interrupt-chat', noWait: true });
|
|
32
|
+
// Give the scheduler a moment to dispatch it into runCommand.
|
|
33
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
34
|
+
|
|
35
|
+
// second: queues behind first
|
|
36
|
+
await env.sendMessage('second', { chat: 'interrupt-chat', noWait: true });
|
|
37
|
+
// third: /interrupt aborts first, extracts second's payload, and prepends both
|
|
38
|
+
// as <message>...</message> blocks to "third".
|
|
39
|
+
await env.sendMessage('/interrupt third', { chat: 'interrupt-chat', noWait: true });
|
|
40
|
+
|
|
41
|
+
const mergedLog = await chat.waitForMessage(
|
|
42
|
+
commandMatching((m) => m.stdout.includes('RAN:') && m.stdout.includes('third')),
|
|
43
|
+
20000
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(mergedLog.stdout).toContain('<message>\nfirst\n</message>');
|
|
47
|
+
expect(mergedLog.stdout).toContain('<message>\nsecond\n</message>');
|
|
48
|
+
expect(mergedLog.stdout).toContain('<message>\nthird\n</message>');
|
|
49
|
+
}, 30000);
|
|
50
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
TestEnvironment,
|
|
4
|
+
type ChatSubscription,
|
|
5
|
+
commandWith,
|
|
6
|
+
agentReply,
|
|
7
|
+
} from '../_helpers/test-environment.js';
|
|
8
|
+
|
|
9
|
+
describe('E2E NO_REPLY_NECESSARY short-circuit', () => {
|
|
10
|
+
let env: TestEnvironment;
|
|
11
|
+
let chat: ChatSubscription | undefined;
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
env = new TestEnvironment('e2e-no-reply');
|
|
15
|
+
await env.setup();
|
|
16
|
+
await env.init();
|
|
17
|
+
await env.up();
|
|
18
|
+
}, 30000);
|
|
19
|
+
|
|
20
|
+
afterAll(() => env.teardown(), 30000);
|
|
21
|
+
afterEach(() => env.disconnectAll());
|
|
22
|
+
|
|
23
|
+
it('skips the agent reply when NO_REPLY_NECESSARY appears anywhere in the output', async () => {
|
|
24
|
+
await env.runCli(['agents', 'add', 'no-reply-agent']);
|
|
25
|
+
// Emit the sentinel mid-string to prove the match is a substring, not
|
|
26
|
+
// an exact token equality.
|
|
27
|
+
env.writeAgentSettings('no-reply-agent', {
|
|
28
|
+
commands: { new: 'echo "prefix NO_REPLY_NECESSARY suffix"' },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await env.addChat('no-reply-chat', 'no-reply-agent');
|
|
32
|
+
chat = await env.connect('no-reply-chat');
|
|
33
|
+
|
|
34
|
+
await env.sendMessage('ping', { chat: 'no-reply-chat', agent: 'no-reply-agent' });
|
|
35
|
+
|
|
36
|
+
// The command log is always written; only the agent reply should be suppressed.
|
|
37
|
+
await chat.waitForMessage(commandWith('NO_REPLY_NECESSARY'));
|
|
38
|
+
// Give the logger a beat in case an agent reply is (incorrectly) pending.
|
|
39
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
40
|
+
expect(chat.messageBuffer.some((m) => m.role === 'agent')).toBe(false);
|
|
41
|
+
}, 15000);
|
|
42
|
+
|
|
43
|
+
it('logs the agent reply when the sentinel is absent', async () => {
|
|
44
|
+
await env.runCli(['agents', 'add', 'reply-agent']);
|
|
45
|
+
env.writeAgentSettings('reply-agent', {
|
|
46
|
+
commands: { new: 'echo "normal reply"' },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await env.addChat('reply-chat', 'reply-agent');
|
|
50
|
+
chat = await env.connect('reply-chat');
|
|
51
|
+
|
|
52
|
+
await env.sendMessage('ping', { chat: 'reply-chat', agent: 'reply-agent' });
|
|
53
|
+
|
|
54
|
+
const reply = await chat.waitForMessage(agentReply());
|
|
55
|
+
expect(reply.content.trim()).toBe('normal reply');
|
|
56
|
+
}, 15000);
|
|
57
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import { TestEnvironment, type ChatSubscription, commandWith } from '../_helpers/test-environment.js';
|
|
3
|
+
|
|
4
|
+
describe('Session Timeout Subagents E2E', () => {
|
|
5
|
+
let env: TestEnvironment;
|
|
6
|
+
let chat: ChatSubscription | undefined;
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
env = new TestEnvironment('e2e-timeout-subagents');
|
|
10
|
+
await env.setup();
|
|
11
|
+
await env.setupSubagentEnv();
|
|
12
|
+
}, 30000);
|
|
13
|
+
|
|
14
|
+
afterAll(() => env.teardown(), 30000);
|
|
15
|
+
afterEach(() => env.disconnectAll());
|
|
16
|
+
|
|
17
|
+
it('should not schedule a session timeout when a subagent sends a message', async () => {
|
|
18
|
+
await env.addChat('chat-timeout');
|
|
19
|
+
chat = await env.connect('chat-timeout');
|
|
20
|
+
|
|
21
|
+
// First, send a normal message so we have a timeout job started from the user.
|
|
22
|
+
await env.sendMessage('hello', { chat: 'chat-timeout', agent: 'debug-agent' });
|
|
23
|
+
await chat.waitForMessage(commandWith('[DEBUG] hello'));
|
|
24
|
+
|
|
25
|
+
// Now let the subagent spawn a message
|
|
26
|
+
await env.sendMessage(
|
|
27
|
+
'clawmini-lite.js subagents spawn --async "echo subagent-hello"',
|
|
28
|
+
{ chat: 'chat-timeout', agent: 'debug-agent' }
|
|
29
|
+
);
|
|
30
|
+
const subLog = await chat.waitForMessage(commandWith('[DEBUG] echo subagent-hello:'));
|
|
31
|
+
expect(subLog).toBeTruthy();
|
|
32
|
+
|
|
33
|
+
// Check jobs list to verify no duplicate timeout was scheduled for the subagent interaction
|
|
34
|
+
const jobsList = (env.getChatSettings('chat-timeout').jobs || []) as Record<string, unknown>[];
|
|
35
|
+
const timeoutJobs = jobsList.filter(
|
|
36
|
+
(j: Record<string, unknown>) =>
|
|
37
|
+
typeof j.id === 'string' && j.id.startsWith('__session_timeout__')
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// It should just be the 1 job scheduled from the first message/second message
|
|
41
|
+
expect(timeoutJobs.length).toBe(1);
|
|
42
|
+
expect(timeoutJobs[0]!.subagentId).toBeUndefined();
|
|
43
|
+
expect(timeoutJobs[0]!).toMatchInlineSnapshot(
|
|
44
|
+
{
|
|
45
|
+
id: expect.stringMatching(/^__session_timeout__/),
|
|
46
|
+
nextSessionId: expect.any(String),
|
|
47
|
+
session: { id: expect.any(String) },
|
|
48
|
+
jobs: { remove: [expect.stringMatching(/^__session_timeout__/)] },
|
|
49
|
+
schedule: { at: expect.any(String) },
|
|
50
|
+
},
|
|
51
|
+
`
|
|
52
|
+
{
|
|
53
|
+
"env": {
|
|
54
|
+
"__SESSION_TIMEOUT__": "true",
|
|
55
|
+
},
|
|
56
|
+
"id": StringMatching /\\^__session_timeout__/,
|
|
57
|
+
"jobs": {
|
|
58
|
+
"remove": [
|
|
59
|
+
StringMatching /\\^__session_timeout__/,
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
"message": "This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY.",
|
|
63
|
+
"nextSessionId": Any<String>,
|
|
64
|
+
"reply": "[@clawmini/session-timeout] Starting a fresh session...",
|
|
65
|
+
"schedule": {
|
|
66
|
+
"at": Any<String>,
|
|
67
|
+
},
|
|
68
|
+
"session": {
|
|
69
|
+
"id": Any<String>,
|
|
70
|
+
"type": "existing",
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
`
|
|
74
|
+
);
|
|
75
|
+
}, 15000);
|
|
76
|
+
});
|