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,782 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
TestEnvironment,
|
|
4
|
+
type ChatSubscription,
|
|
5
|
+
commandMatching,
|
|
6
|
+
commandWith,
|
|
7
|
+
} from '../_helpers/test-environment.js';
|
|
8
|
+
|
|
9
|
+
// Exercises the subagent lifecycle commands (send, wait, stop, delete, list,
|
|
10
|
+
// tail) from a parent agent's perspective, plus the completion-notification
|
|
11
|
+
// paths (sync, wait-before/after completion, parent busy/idle on completion).
|
|
12
|
+
// Spawn + async happy-path is already covered by session-timeout-subagents.test.ts.
|
|
13
|
+
|
|
14
|
+
describe('E2E Subagent Lifecycle', () => {
|
|
15
|
+
let env: TestEnvironment;
|
|
16
|
+
let chat: ChatSubscription | undefined;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
env = new TestEnvironment('e2e-subagent-lifecycle');
|
|
20
|
+
await env.setup();
|
|
21
|
+
await env.setupSubagentEnv();
|
|
22
|
+
}, 30000);
|
|
23
|
+
|
|
24
|
+
afterAll(() => env.teardown(), 30000);
|
|
25
|
+
afterEach(() => env.disconnectAll());
|
|
26
|
+
|
|
27
|
+
it('send delivers a follow-up message to an existing subagent session', async () => {
|
|
28
|
+
await env.addChat('send-chat', 'debug-agent');
|
|
29
|
+
chat = await env.connect('send-chat');
|
|
30
|
+
|
|
31
|
+
await env.sendMessage(
|
|
32
|
+
'clawmini-lite.js subagents spawn --id send-sub --async "echo first-msg"',
|
|
33
|
+
{ chat: 'send-chat', agent: 'debug-agent' }
|
|
34
|
+
);
|
|
35
|
+
// First message through the subagent runs via `new` (no SESSION_ID yet).
|
|
36
|
+
await chat.waitForMessage(
|
|
37
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('[DEBUG] echo first-msg:'))
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
await env.sendMessage('clawmini-lite.js subagents send send-sub --async -p "echo second-msg"', {
|
|
41
|
+
chat: 'send-chat',
|
|
42
|
+
agent: 'debug-agent',
|
|
43
|
+
});
|
|
44
|
+
// Follow-up runs via `append`, so the prefix is `[DEBUG <sessionId>]`.
|
|
45
|
+
const followUp = await chat.waitForMessage(
|
|
46
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('echo second-msg'))
|
|
47
|
+
);
|
|
48
|
+
expect(followUp.stdout).toMatch(/\[DEBUG [^\]]+\] echo second-msg:/);
|
|
49
|
+
}, 30000);
|
|
50
|
+
|
|
51
|
+
it('wait returns the completed subagent output to the caller', async () => {
|
|
52
|
+
await env.addChat('wait-chat', 'debug-agent');
|
|
53
|
+
chat = await env.connect('wait-chat');
|
|
54
|
+
|
|
55
|
+
await env.sendMessage(
|
|
56
|
+
'clawmini-lite.js subagents spawn --id wait-sub --async "echo wait-complete"',
|
|
57
|
+
{ chat: 'wait-chat', agent: 'debug-agent' }
|
|
58
|
+
);
|
|
59
|
+
await chat.waitForMessage(
|
|
60
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('wait-complete'))
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
await env.sendMessage('clawmini-lite.js subagents wait wait-sub', {
|
|
64
|
+
chat: 'wait-chat',
|
|
65
|
+
agent: 'debug-agent',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// `subagents wait` prints the subagent's last agent reply (the debug
|
|
69
|
+
// template's full stdout) back to the parent. That output is wrapped by
|
|
70
|
+
// the parent's own debug invocation in this chat's command log.
|
|
71
|
+
const waitLog = await chat.waitForMessage(
|
|
72
|
+
commandMatching(
|
|
73
|
+
(m) =>
|
|
74
|
+
!m.subagentId &&
|
|
75
|
+
m.stdout.includes('subagents wait wait-sub:') &&
|
|
76
|
+
m.stdout.includes('wait-complete')
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
expect(waitLog.exitCode).toBe(0);
|
|
80
|
+
}, 30000);
|
|
81
|
+
|
|
82
|
+
it('stop aborts an active subagent task before it finishes', async () => {
|
|
83
|
+
await env.addChat('stop-chat', 'debug-agent');
|
|
84
|
+
chat = await env.connect('stop-chat');
|
|
85
|
+
|
|
86
|
+
// Long-running subagent command so we can stop it mid-flight.
|
|
87
|
+
await env.sendMessage(
|
|
88
|
+
'clawmini-lite.js subagents spawn --id stop-sub --async "sleep 30 && echo should-not-print"',
|
|
89
|
+
{ chat: 'stop-chat', agent: 'debug-agent' }
|
|
90
|
+
);
|
|
91
|
+
for (let i = 0; i < 50; i++) {
|
|
92
|
+
const settings = env.getChatSettings('stop-chat') as {
|
|
93
|
+
subagents?: Record<string, { status: string }>;
|
|
94
|
+
};
|
|
95
|
+
if (settings.subagents?.['stop-sub']?.status === 'active') break;
|
|
96
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await env.sendMessage('clawmini-lite.js subagents stop stop-sub', {
|
|
100
|
+
chat: 'stop-chat',
|
|
101
|
+
agent: 'debug-agent',
|
|
102
|
+
});
|
|
103
|
+
await chat.waitForMessage(commandWith('Subagent stop-sub stopped'));
|
|
104
|
+
|
|
105
|
+
// Stop transiently flips the tracker to 'failed' (router writes it
|
|
106
|
+
// before calling session.stop()), but executeDirectMessage swallows
|
|
107
|
+
// the AbortError and executeSubagent's success path then writes
|
|
108
|
+
// 'completed'. The tracker therefore settles at 'completed' — not
|
|
109
|
+
// 'active' (the sleep would still be running) and not 'failed' (the
|
|
110
|
+
// post-abort update wins the race). Assert the settled value directly
|
|
111
|
+
// so a regression to either other state surfaces.
|
|
112
|
+
let finalStatus: string | undefined;
|
|
113
|
+
for (let i = 0; i < 50; i++) {
|
|
114
|
+
const settings = env.getChatSettings('stop-chat') as {
|
|
115
|
+
subagents?: Record<string, { status: string }>;
|
|
116
|
+
};
|
|
117
|
+
finalStatus = settings.subagents?.['stop-sub']?.status;
|
|
118
|
+
if (finalStatus === 'completed') break;
|
|
119
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
120
|
+
}
|
|
121
|
+
expect(finalStatus).toBe('completed');
|
|
122
|
+
|
|
123
|
+
// The aborted command must never have reached its echo. The literal text
|
|
124
|
+
// "should-not-print" appears in the debug template's prefix echo of the
|
|
125
|
+
// spawn command, so we only count exact standalone-line matches — which
|
|
126
|
+
// only come from the eval output itself.
|
|
127
|
+
expect(
|
|
128
|
+
chat.messageBuffer.some(
|
|
129
|
+
(m) =>
|
|
130
|
+
typeof (m as { stdout?: string }).stdout === 'string' &&
|
|
131
|
+
/^should-not-print$/m.test((m as { stdout?: string }).stdout!)
|
|
132
|
+
)
|
|
133
|
+
).toBe(false);
|
|
134
|
+
}, 30000);
|
|
135
|
+
|
|
136
|
+
it('delete removes the subagent tracker from chat settings', async () => {
|
|
137
|
+
await env.addChat('delete-chat', 'debug-agent');
|
|
138
|
+
chat = await env.connect('delete-chat');
|
|
139
|
+
|
|
140
|
+
await env.sendMessage(
|
|
141
|
+
'clawmini-lite.js subagents spawn --id del-sub --async "echo delete-me"',
|
|
142
|
+
{ chat: 'delete-chat', agent: 'debug-agent' }
|
|
143
|
+
);
|
|
144
|
+
await chat.waitForMessage(
|
|
145
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('delete-me'))
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
let settings = env.getChatSettings('delete-chat') as {
|
|
149
|
+
subagents?: Record<string, unknown>;
|
|
150
|
+
};
|
|
151
|
+
expect(settings.subagents?.['del-sub']).toBeTruthy();
|
|
152
|
+
|
|
153
|
+
await env.sendMessage('clawmini-lite.js subagents delete del-sub', {
|
|
154
|
+
chat: 'delete-chat',
|
|
155
|
+
agent: 'debug-agent',
|
|
156
|
+
});
|
|
157
|
+
await chat.waitForMessage(commandWith('Subagent del-sub deleted'));
|
|
158
|
+
|
|
159
|
+
for (let i = 0; i < 50; i++) {
|
|
160
|
+
settings = env.getChatSettings('delete-chat') as {
|
|
161
|
+
subagents?: Record<string, unknown>;
|
|
162
|
+
};
|
|
163
|
+
if (!settings.subagents?.['del-sub']) break;
|
|
164
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
165
|
+
}
|
|
166
|
+
expect(settings.subagents?.['del-sub']).toBeUndefined();
|
|
167
|
+
}, 30000);
|
|
168
|
+
|
|
169
|
+
it('list returns all subagents spawned by the current agent', async () => {
|
|
170
|
+
await env.addChat('list-chat', 'debug-agent');
|
|
171
|
+
chat = await env.connect('list-chat');
|
|
172
|
+
|
|
173
|
+
await env.sendMessage('clawmini-lite.js subagents spawn --id list-a --async "echo a"', {
|
|
174
|
+
chat: 'list-chat',
|
|
175
|
+
agent: 'debug-agent',
|
|
176
|
+
});
|
|
177
|
+
await chat.waitForMessage(
|
|
178
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('[DEBUG] echo a:'))
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
await env.sendMessage('clawmini-lite.js subagents spawn --id list-b --async "echo b"', {
|
|
182
|
+
chat: 'list-chat',
|
|
183
|
+
agent: 'debug-agent',
|
|
184
|
+
});
|
|
185
|
+
await chat.waitForMessage(
|
|
186
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('[DEBUG] echo b:'))
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
await env.sendMessage('clawmini-lite.js subagents list --json', {
|
|
190
|
+
chat: 'list-chat',
|
|
191
|
+
agent: 'debug-agent',
|
|
192
|
+
});
|
|
193
|
+
const listLog = await chat.waitForMessage(
|
|
194
|
+
commandMatching(
|
|
195
|
+
(m) =>
|
|
196
|
+
!m.subagentId &&
|
|
197
|
+
m.stdout.includes('subagents list --json:') &&
|
|
198
|
+
m.stdout.includes('"id": "list-a"') &&
|
|
199
|
+
m.stdout.includes('"id": "list-b"')
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
expect(listLog.stdout).toContain('"status": "completed"');
|
|
203
|
+
}, 30000);
|
|
204
|
+
|
|
205
|
+
it('tail returns the subagent chat log to the caller', async () => {
|
|
206
|
+
await env.addChat('tail-chat', 'debug-agent');
|
|
207
|
+
chat = await env.connect('tail-chat');
|
|
208
|
+
|
|
209
|
+
await env.sendMessage(
|
|
210
|
+
'clawmini-lite.js subagents spawn --id tail-sub --async "echo tail-content"',
|
|
211
|
+
{ chat: 'tail-chat', agent: 'debug-agent' }
|
|
212
|
+
);
|
|
213
|
+
await chat.waitForMessage(
|
|
214
|
+
commandMatching((m) => !!m.subagentId && m.stdout.includes('tail-content'))
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
await env.sendMessage('clawmini-lite.js subagents tail tail-sub --json', {
|
|
218
|
+
chat: 'tail-chat',
|
|
219
|
+
agent: 'debug-agent',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// `subagents tail --json` prints the subagent's chat log as JSONL. The
|
|
223
|
+
// parent's debug wrapper echoes `[DEBUG] <cmd>:` then the command stdout,
|
|
224
|
+
// so the subagent's log messages appear in this chat's command log.
|
|
225
|
+
const tailLog = await chat.waitForMessage(
|
|
226
|
+
commandMatching(
|
|
227
|
+
(m) =>
|
|
228
|
+
!m.subagentId &&
|
|
229
|
+
m.stdout.includes('subagents tail tail-sub --json:') &&
|
|
230
|
+
m.stdout.includes('tail-content')
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
expect(tailLog.exitCode).toBe(0);
|
|
234
|
+
// Must include both the user prompt and an agent reply from the subagent's
|
|
235
|
+
// own log, not just an echo of the tail command itself.
|
|
236
|
+
expect(tailLog.stdout).toMatch(/"role":\s*"user"/);
|
|
237
|
+
expect(tailLog.stdout).toMatch(/"role":\s*"agent"/);
|
|
238
|
+
}, 30000);
|
|
239
|
+
|
|
240
|
+
it('list from a subagent returns only its own children, not peers or parents', async () => {
|
|
241
|
+
await env.addChat('nested-list-chat', 'debug-agent');
|
|
242
|
+
chat = await env.connect('nested-list-chat');
|
|
243
|
+
|
|
244
|
+
// Parent spawns outer-sub. outer-sub itself spawns inner-sub (its child)
|
|
245
|
+
// and then calls `subagents list --json`. From outer-sub's perspective,
|
|
246
|
+
// only inner-sub should appear (parentId === outer-sub).
|
|
247
|
+
await env.sendMessage(
|
|
248
|
+
'clawmini-lite.js subagents spawn --id outer-sub --async "clawmini-lite.js subagents spawn --id inner-sub --async \\"echo inner-done\\" && sleep 1 && clawmini-lite.js subagents list --json"',
|
|
249
|
+
{ chat: 'nested-list-chat', agent: 'debug-agent' }
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// outer-sub's own command log (subagentId=outer-sub) must contain the
|
|
253
|
+
// JSON output of its own `list` call, showing inner-sub.
|
|
254
|
+
const outerLog = await chat.waitForMessage(
|
|
255
|
+
commandMatching(
|
|
256
|
+
(m) => !!m.subagentId && m.stdout.includes('"id": "inner-sub"')
|
|
257
|
+
)
|
|
258
|
+
);
|
|
259
|
+
expect(outerLog.stdout).toContain('"id": "inner-sub"');
|
|
260
|
+
// outer-sub is its own parent, not its own child — must not appear in
|
|
261
|
+
// its own list output.
|
|
262
|
+
expect(outerLog.stdout).not.toMatch(/"id":\s*"outer-sub"/);
|
|
263
|
+
|
|
264
|
+
// Parent's view: list must include outer-sub (direct child) but not
|
|
265
|
+
// inner-sub (grandchild — parentId=outer-sub, not undefined).
|
|
266
|
+
await env.sendMessage('clawmini-lite.js subagents list --json', {
|
|
267
|
+
chat: 'nested-list-chat',
|
|
268
|
+
agent: 'debug-agent',
|
|
269
|
+
});
|
|
270
|
+
const parentLog = await chat.waitForMessage(
|
|
271
|
+
commandMatching(
|
|
272
|
+
(m) =>
|
|
273
|
+
!m.subagentId &&
|
|
274
|
+
m.stdout.includes('subagents list --json:') &&
|
|
275
|
+
m.stdout.includes('"id": "outer-sub"')
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
expect(parentLog.stdout).toContain('"id": "outer-sub"');
|
|
279
|
+
expect(parentLog.stdout).not.toMatch(/"id":\s*"inner-sub"/);
|
|
280
|
+
}, 30000);
|
|
281
|
+
|
|
282
|
+
it('sync spawn blocks the caller and returns <subagent_output> inline', async () => {
|
|
283
|
+
await env.addChat('sync-chat', 'debug-agent');
|
|
284
|
+
chat = await env.connect('sync-chat');
|
|
285
|
+
|
|
286
|
+
// The router's default is `isAsync = input.async ?? depth === 0`, so a
|
|
287
|
+
// spawn WITHOUT --async runs sync only when depth > 0. We trigger this
|
|
288
|
+
// by having an outer subagent spawn sync-inner without --async. The
|
|
289
|
+
// inner call's CLI then polls subagentWait and prints the completed
|
|
290
|
+
// output wrapped in <subagent_output>...</subagent_output> tags.
|
|
291
|
+
await env.sendMessage(
|
|
292
|
+
'clawmini-lite.js subagents spawn --id sync-outer --async "clawmini-lite.js subagents spawn --id sync-inner \\"echo sync-value\\""',
|
|
293
|
+
{ chat: 'sync-chat', agent: 'debug-agent' }
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// sync-outer's stdout (subagentId=sync-outer) should contain both the
|
|
297
|
+
// <subagent_output> wrapper and the inner's echo value.
|
|
298
|
+
const outerLog = await chat.waitForMessage(
|
|
299
|
+
commandMatching(
|
|
300
|
+
(m) =>
|
|
301
|
+
!!m.subagentId &&
|
|
302
|
+
m.stdout.includes('<subagent_output>') &&
|
|
303
|
+
m.stdout.includes('sync-value')
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
expect(outerLog.stdout).toContain('</subagent_output>');
|
|
307
|
+
}, 30000);
|
|
308
|
+
|
|
309
|
+
it('wait called before subagent finishes blocks until completion', async () => {
|
|
310
|
+
await env.addChat('wait-before-chat', 'debug-agent');
|
|
311
|
+
chat = await env.connect('wait-before-chat');
|
|
312
|
+
|
|
313
|
+
// Chain spawn && wait in the same shell so `wait` is called while the
|
|
314
|
+
// subagent is still sleeping. This exercises the event-iterator path
|
|
315
|
+
// of subagentWait (not the synchronous early-return hit by the
|
|
316
|
+
// `wait returns the completed subagent output` test above).
|
|
317
|
+
await env.sendMessage(
|
|
318
|
+
'clawmini-lite.js subagents spawn --id wait-before-sub --async "sleep 3 && echo slow-done" && clawmini-lite.js subagents wait wait-before-sub',
|
|
319
|
+
{ chat: 'wait-before-chat', agent: 'debug-agent' }
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// The parent's wrapped stdout contains the wait-returned subagent
|
|
323
|
+
// output (the debug template's own echo of `sleep 3 && echo slow-done`).
|
|
324
|
+
// That specific prefix only appears when wait actually returned with
|
|
325
|
+
// the subagent's agent-role content — it does not appear in the
|
|
326
|
+
// parent's own debug-echo of the outer command line.
|
|
327
|
+
const waitLog = await chat.waitForMessage(
|
|
328
|
+
commandMatching(
|
|
329
|
+
(m) =>
|
|
330
|
+
!m.subagentId &&
|
|
331
|
+
m.stdout.includes('[DEBUG] sleep 3 && echo slow-done:')
|
|
332
|
+
),
|
|
333
|
+
20000
|
|
334
|
+
);
|
|
335
|
+
expect(waitLog.exitCode).toBe(0);
|
|
336
|
+
}, 30000);
|
|
337
|
+
|
|
338
|
+
it('notifies the parent session when an async subagent completes during parent work', async () => {
|
|
339
|
+
await env.addChat('notify-busy-chat', 'debug-agent');
|
|
340
|
+
chat = await env.connect('notify-busy-chat');
|
|
341
|
+
|
|
342
|
+
// Parent spawns a fast subagent then sleeps longer than the subagent
|
|
343
|
+
// takes. The subagent finishes while the parent's shell command is
|
|
344
|
+
// still running and the parent never calls `wait`. Current behavior:
|
|
345
|
+
// executeSubagent injects a <notification> message into the parent's
|
|
346
|
+
// session via executeDirectMessage (subagent-utils.ts:101).
|
|
347
|
+
await env.sendMessage(
|
|
348
|
+
'clawmini-lite.js subagents spawn --id notify-busy-sub --async "echo notify-done-early" && sleep 2 && echo parent-still-working',
|
|
349
|
+
{ chat: 'notify-busy-chat', agent: 'debug-agent' }
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// The notification text must land in the parent chat regardless of
|
|
353
|
+
// which message role carries it (debug-agent may shell-eval the
|
|
354
|
+
// notification body — see the TODO at subagent-utils.ts:98-100).
|
|
355
|
+
await chat.waitForMessage(
|
|
356
|
+
(m) => JSON.stringify(m).includes('Subagent notify-busy-sub completed'),
|
|
357
|
+
15000
|
|
358
|
+
);
|
|
359
|
+
}, 30000);
|
|
360
|
+
|
|
361
|
+
it('notifies the parent session when an async subagent completes after parent is idle', async () => {
|
|
362
|
+
await env.addChat('notify-idle-chat', 'debug-agent');
|
|
363
|
+
chat = await env.connect('notify-idle-chat');
|
|
364
|
+
|
|
365
|
+
// Parent spawns a slow subagent and the parent's shell command
|
|
366
|
+
// returns immediately — the parent is idle by the time the subagent
|
|
367
|
+
// finishes ~2s later. The async completion path still injects the
|
|
368
|
+
// notification into the parent session.
|
|
369
|
+
await env.sendMessage(
|
|
370
|
+
'clawmini-lite.js subagents spawn --id notify-idle-sub --async "sleep 2 && echo late-done"',
|
|
371
|
+
{ chat: 'notify-idle-chat', agent: 'debug-agent' }
|
|
372
|
+
);
|
|
373
|
+
// Confirm parent has returned (spawn CLI prints this line immediately
|
|
374
|
+
// after registering the subagent; it does NOT wait for completion).
|
|
375
|
+
await chat.waitForMessage(
|
|
376
|
+
commandMatching(
|
|
377
|
+
(m) =>
|
|
378
|
+
!m.subagentId &&
|
|
379
|
+
m.stdout.includes('Subagent spawned successfully with ID: notify-idle-sub')
|
|
380
|
+
)
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Then, once the subagent finishes, the notification arrives.
|
|
384
|
+
await chat.waitForMessage(
|
|
385
|
+
(m) => JSON.stringify(m).includes('Subagent notify-idle-sub completed'),
|
|
386
|
+
15000
|
|
387
|
+
);
|
|
388
|
+
}, 30000);
|
|
389
|
+
|
|
390
|
+
it('waiting on two subagents sequentially returns each one\'s output inline', async () => {
|
|
391
|
+
await env.addChat('two-wait-chat', 'debug-agent');
|
|
392
|
+
chat = await env.connect('two-wait-chat');
|
|
393
|
+
|
|
394
|
+
// Spawn two async subagents, then wait on each in turn. Each wait
|
|
395
|
+
// call prints its OWN subagent's last agent-role message via the
|
|
396
|
+
// CLI's stdout — so the parent's wrapped stdout must contain both
|
|
397
|
+
// subagents' debug-template echoes, in order.
|
|
398
|
+
await env.sendMessage(
|
|
399
|
+
'clawmini-lite.js subagents spawn --id two-a --async "echo both-output-a" && clawmini-lite.js subagents spawn --id two-b --async "echo both-output-b" && clawmini-lite.js subagents wait two-a && clawmini-lite.js subagents wait two-b',
|
|
400
|
+
{ chat: 'two-wait-chat', agent: 'debug-agent' }
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// `[DEBUG] echo both-output-a:` only appears inside the subagent's
|
|
404
|
+
// own log (printed back by wait), not in the parent's outer echo of
|
|
405
|
+
// the chained command — same logic as the `wait-before` test above.
|
|
406
|
+
const log = await chat.waitForMessage(
|
|
407
|
+
commandMatching(
|
|
408
|
+
(m) =>
|
|
409
|
+
!m.subagentId &&
|
|
410
|
+
m.stdout.includes('[DEBUG] echo both-output-a:') &&
|
|
411
|
+
m.stdout.includes('[DEBUG] echo both-output-b:')
|
|
412
|
+
),
|
|
413
|
+
20000
|
|
414
|
+
);
|
|
415
|
+
const aIdx = log.stdout.indexOf('[DEBUG] echo both-output-a:');
|
|
416
|
+
const bIdx = log.stdout.indexOf('[DEBUG] echo both-output-b:');
|
|
417
|
+
expect(aIdx).toBeGreaterThanOrEqual(0);
|
|
418
|
+
// wait(a) runs before wait(b), so A's wait output appears first.
|
|
419
|
+
expect(bIdx).toBeGreaterThan(aIdx);
|
|
420
|
+
}, 30000);
|
|
421
|
+
|
|
422
|
+
it('list --blocking returns empty for the main agent', async () => {
|
|
423
|
+
await env.addChat('blocking-root-chat', 'debug-agent');
|
|
424
|
+
chat = await env.connect('blocking-root-chat');
|
|
425
|
+
|
|
426
|
+
// Main agent spawns an active subagent, then lists with --blocking.
|
|
427
|
+
// Per the router, main-agent `--blocking` is always empty — blocking
|
|
428
|
+
// is meaningful only for subagents deciding whether to wait for their
|
|
429
|
+
// own children.
|
|
430
|
+
await env.sendMessage(
|
|
431
|
+
'clawmini-lite.js subagents spawn --id block-root-sub --async "sleep 2 && echo done"',
|
|
432
|
+
{ chat: 'blocking-root-chat', agent: 'debug-agent' }
|
|
433
|
+
);
|
|
434
|
+
for (let i = 0; i < 50; i++) {
|
|
435
|
+
const settings = env.getChatSettings('blocking-root-chat') as {
|
|
436
|
+
subagents?: Record<string, { status: string }>;
|
|
437
|
+
};
|
|
438
|
+
if (settings.subagents?.['block-root-sub']?.status === 'active') break;
|
|
439
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
await env.sendMessage('clawmini-lite.js subagents list --blocking --json', {
|
|
443
|
+
chat: 'blocking-root-chat',
|
|
444
|
+
agent: 'debug-agent',
|
|
445
|
+
});
|
|
446
|
+
const log = await chat.waitForMessage(
|
|
447
|
+
commandMatching(
|
|
448
|
+
(m) => !m.subagentId && m.stdout.includes('subagents list --blocking --json:')
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
// Extract the JSON body between the first `[` and last `]` — the CLI
|
|
452
|
+
// prints "[\n ...\n]" for an empty or populated array.
|
|
453
|
+
const jsonStart = log.stdout.indexOf('[', log.stdout.indexOf(':'));
|
|
454
|
+
const jsonEnd = log.stdout.lastIndexOf(']');
|
|
455
|
+
const body = log.stdout.slice(jsonStart, jsonEnd + 1).trim();
|
|
456
|
+
expect(body).toBe('[]');
|
|
457
|
+
}, 30000);
|
|
458
|
+
|
|
459
|
+
it('list --blocking returns only active children when called from a subagent', async () => {
|
|
460
|
+
await env.addChat('blocking-sub-chat', 'debug-agent');
|
|
461
|
+
chat = await env.connect('blocking-sub-chat');
|
|
462
|
+
|
|
463
|
+
// Outer subagent spawns two children: one instant (will be completed),
|
|
464
|
+
// one sleeping (will still be active). `list --blocking --json` from
|
|
465
|
+
// outer must return ONLY block-active, not block-done.
|
|
466
|
+
await env.sendMessage(
|
|
467
|
+
[
|
|
468
|
+
'clawmini-lite.js subagents spawn --id block-outer --async',
|
|
469
|
+
'"clawmini-lite.js subagents spawn --id block-done --async \\"echo done-fast\\"',
|
|
470
|
+
'&& clawmini-lite.js subagents spawn --id block-active --async \\"sleep 3 && echo late\\"',
|
|
471
|
+
'&& sleep 1',
|
|
472
|
+
'&& clawmini-lite.js subagents list --blocking --json"',
|
|
473
|
+
].join(' '),
|
|
474
|
+
{ chat: 'blocking-sub-chat', agent: 'debug-agent' }
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
const outerLog = await chat.waitForMessage(
|
|
478
|
+
commandMatching(
|
|
479
|
+
(m) =>
|
|
480
|
+
m.subagentId === 'block-outer' && m.stdout.includes('"id": "block-active"')
|
|
481
|
+
),
|
|
482
|
+
20000
|
|
483
|
+
);
|
|
484
|
+
expect(outerLog.stdout).toContain('"id": "block-active"');
|
|
485
|
+
expect(outerLog.stdout).not.toMatch(/"id":\s*"block-done"/);
|
|
486
|
+
}, 30000);
|
|
487
|
+
|
|
488
|
+
it('spawn without --id auto-generates a UUID that the CLI reports back', async () => {
|
|
489
|
+
await env.addChat('auto-id-chat', 'debug-agent');
|
|
490
|
+
chat = await env.connect('auto-id-chat');
|
|
491
|
+
|
|
492
|
+
await env.sendMessage(
|
|
493
|
+
'clawmini-lite.js subagents spawn --async "echo auto-id-output"',
|
|
494
|
+
{ chat: 'auto-id-chat', agent: 'debug-agent' }
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
// CLI prints "Subagent spawned successfully with ID: <uuid>" on the
|
|
498
|
+
// parent's stdout. Capture the UUID and verify a subagent tracker
|
|
499
|
+
// exists for it in chat settings.
|
|
500
|
+
const announce = await chat.waitForMessage(
|
|
501
|
+
commandMatching(
|
|
502
|
+
(m) =>
|
|
503
|
+
!m.subagentId && /Subagent spawned successfully with ID: [0-9a-f-]{36}/.test(m.stdout)
|
|
504
|
+
)
|
|
505
|
+
);
|
|
506
|
+
const match = announce.stdout.match(/Subagent spawned successfully with ID: ([0-9a-f-]{36})/);
|
|
507
|
+
expect(match).not.toBeNull();
|
|
508
|
+
const generatedId = match![1]!;
|
|
509
|
+
|
|
510
|
+
// Wait for the subagent's own output to land, then verify its tracker.
|
|
511
|
+
await chat.waitForMessage(
|
|
512
|
+
commandMatching((m) => m.subagentId === generatedId && m.stdout.includes('auto-id-output'))
|
|
513
|
+
);
|
|
514
|
+
const settings = env.getChatSettings('auto-id-chat') as {
|
|
515
|
+
subagents?: Record<string, { id?: string }>;
|
|
516
|
+
};
|
|
517
|
+
expect(settings.subagents?.[generatedId]?.id).toBe(generatedId);
|
|
518
|
+
}, 30000);
|
|
519
|
+
|
|
520
|
+
it('spawn --agent <other> routes the subagent through a different agent', async () => {
|
|
521
|
+
await env.addChat('alt-agent-chat', 'debug-agent');
|
|
522
|
+
chat = await env.connect('alt-agent-chat');
|
|
523
|
+
|
|
524
|
+
// Define a second agent whose template prefixes output with [ALT] so
|
|
525
|
+
// we can verify the spawn actually routed through it rather than
|
|
526
|
+
// inheriting the parent's debug-agent.
|
|
527
|
+
await env.addAgent('alt-agent');
|
|
528
|
+
env.writeAgentSettings('alt-agent', {
|
|
529
|
+
commands: {
|
|
530
|
+
new: 'echo "[ALT] $CLAW_CLI_MESSAGE:" && eval "$CLAW_CLI_MESSAGE"',
|
|
531
|
+
append: 'echo "[ALT] $CLAW_CLI_MESSAGE:" && eval "$CLAW_CLI_MESSAGE"',
|
|
532
|
+
getSessionId: 'node -e "console.log(Math.random().toString(36).slice(2, 10))"',
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
await env.sendMessage(
|
|
537
|
+
'clawmini-lite.js subagents spawn --id alt-sub --agent alt-agent --async "echo alt-output"',
|
|
538
|
+
{ chat: 'alt-agent-chat', agent: 'debug-agent' }
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
// The subagent's command_log should show the [ALT] prefix, proving
|
|
542
|
+
// alt-agent's template ran rather than debug-agent's [DEBUG] one.
|
|
543
|
+
const subLog = await chat.waitForMessage(
|
|
544
|
+
commandMatching(
|
|
545
|
+
(m) => m.subagentId === 'alt-sub' && m.stdout.includes('[ALT] echo alt-output:')
|
|
546
|
+
)
|
|
547
|
+
);
|
|
548
|
+
expect(subLog.stdout).toContain('alt-output');
|
|
549
|
+
expect(subLog.stdout).not.toContain('[DEBUG]');
|
|
550
|
+
|
|
551
|
+
const settings = env.getChatSettings('alt-agent-chat') as {
|
|
552
|
+
subagents?: Record<string, { agentId?: string }>;
|
|
553
|
+
};
|
|
554
|
+
expect(settings.subagents?.['alt-sub']?.agentId).toBe('alt-agent');
|
|
555
|
+
}, 30000);
|
|
556
|
+
|
|
557
|
+
it('spawn with a duplicate --id is rejected', async () => {
|
|
558
|
+
await env.addChat('dup-id-chat', 'debug-agent');
|
|
559
|
+
chat = await env.connect('dup-id-chat');
|
|
560
|
+
|
|
561
|
+
await env.sendMessage(
|
|
562
|
+
'clawmini-lite.js subagents spawn --id dup-sub --async "echo first"',
|
|
563
|
+
{ chat: 'dup-id-chat', agent: 'debug-agent' }
|
|
564
|
+
);
|
|
565
|
+
await chat.waitForMessage(
|
|
566
|
+
commandMatching((m) => m.subagentId === 'dup-sub' && m.stdout.includes('first'))
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
// Second spawn with the same id must hit the router's duplicate-id
|
|
570
|
+
// guard (`Subagent ID already exists`). The CLI surfaces the TRPC
|
|
571
|
+
// error via stderr + exit 1, so we match on the serialized message.
|
|
572
|
+
await env.sendMessage(
|
|
573
|
+
'clawmini-lite.js subagents spawn --id dup-sub --async "echo second"',
|
|
574
|
+
{ chat: 'dup-id-chat', agent: 'debug-agent' }
|
|
575
|
+
);
|
|
576
|
+
await chat.waitForMessage(
|
|
577
|
+
(m) => JSON.stringify(m).includes('Subagent ID already exists'),
|
|
578
|
+
15000
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
// The duplicate spawn must not have overwritten the first: there's
|
|
582
|
+
// still exactly one 'first'-producing subagent and no 'second' output.
|
|
583
|
+
expect(
|
|
584
|
+
chat.messageBuffer.some(
|
|
585
|
+
(m) =>
|
|
586
|
+
(m as { subagentId?: string }).subagentId === 'dup-sub' &&
|
|
587
|
+
typeof (m as { stdout?: string }).stdout === 'string' &&
|
|
588
|
+
(m as { stdout?: string }).stdout!.includes('second')
|
|
589
|
+
)
|
|
590
|
+
).toBe(false);
|
|
591
|
+
}, 30000);
|
|
592
|
+
|
|
593
|
+
it('send without --async blocks the caller and returns <subagent_output>', async () => {
|
|
594
|
+
await env.addChat('send-sync-chat', 'debug-agent');
|
|
595
|
+
chat = await env.connect('send-sync-chat');
|
|
596
|
+
|
|
597
|
+
// Spawn a child and let it complete first so the second `send` is
|
|
598
|
+
// exercising the wake-a-completed-child path, not the initial spawn.
|
|
599
|
+
await env.sendMessage(
|
|
600
|
+
'clawmini-lite.js subagents spawn --id send-sync-sub --async "echo sync-initial"',
|
|
601
|
+
{ chat: 'send-sync-chat', agent: 'debug-agent' }
|
|
602
|
+
);
|
|
603
|
+
await chat.waitForMessage(
|
|
604
|
+
commandMatching(
|
|
605
|
+
(m) => m.subagentId === 'send-sync-sub' && m.stdout.includes('sync-initial')
|
|
606
|
+
)
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
// `send` without --async: the CLI polls subagentWait and prints the
|
|
610
|
+
// subagent's agent-role output wrapped in <subagent_output> tags.
|
|
611
|
+
await env.sendMessage(
|
|
612
|
+
"clawmini-lite.js subagents send send-sync-sub -p 'echo sync-send-output'",
|
|
613
|
+
{ chat: 'send-sync-chat', agent: 'debug-agent' }
|
|
614
|
+
);
|
|
615
|
+
const log = await chat.waitForMessage(
|
|
616
|
+
commandMatching(
|
|
617
|
+
(m) =>
|
|
618
|
+
!m.subagentId &&
|
|
619
|
+
m.stdout.includes('<subagent_output>') &&
|
|
620
|
+
m.stdout.includes('sync-send-output')
|
|
621
|
+
),
|
|
622
|
+
20000
|
|
623
|
+
);
|
|
624
|
+
expect(log.stdout).toContain('</subagent_output>');
|
|
625
|
+
}, 30000);
|
|
626
|
+
|
|
627
|
+
it('send wakes a completed child, flipping status active → completed', async () => {
|
|
628
|
+
await env.addChat('send-wake-chat', 'debug-agent');
|
|
629
|
+
chat = await env.connect('send-wake-chat');
|
|
630
|
+
|
|
631
|
+
await env.sendMessage(
|
|
632
|
+
'clawmini-lite.js subagents spawn --id wake-sub --async "echo initial"',
|
|
633
|
+
{ chat: 'send-wake-chat', agent: 'debug-agent' }
|
|
634
|
+
);
|
|
635
|
+
await chat.waitForMessage(
|
|
636
|
+
commandMatching((m) => m.subagentId === 'wake-sub' && m.stdout.includes('initial'))
|
|
637
|
+
);
|
|
638
|
+
// Child is now completed — confirm before sending.
|
|
639
|
+
for (let i = 0; i < 50; i++) {
|
|
640
|
+
const s = env.getChatSettings('send-wake-chat') as {
|
|
641
|
+
subagents?: Record<string, { status: string }>;
|
|
642
|
+
};
|
|
643
|
+
if (s.subagents?.['wake-sub']?.status === 'completed') break;
|
|
644
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
await env.sendMessage(
|
|
648
|
+
"clawmini-lite.js subagents send wake-sub --async -p 'echo after-wake'",
|
|
649
|
+
{ chat: 'send-wake-chat', agent: 'debug-agent' }
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
// The child's command_log should contain the wake-up output (runs via
|
|
653
|
+
// `append`, so it has the `[DEBUG <sessionId>]` prefix).
|
|
654
|
+
const followUp = await chat.waitForMessage(
|
|
655
|
+
commandMatching((m) => m.subagentId === 'wake-sub' && m.stdout.includes('after-wake'))
|
|
656
|
+
);
|
|
657
|
+
expect(followUp.stdout).toMatch(/\[DEBUG [^\]]+\] echo after-wake:/);
|
|
658
|
+
|
|
659
|
+
// And the tracker eventually settles back on completed.
|
|
660
|
+
let status: string | undefined;
|
|
661
|
+
for (let i = 0; i < 50; i++) {
|
|
662
|
+
const s = env.getChatSettings('send-wake-chat') as {
|
|
663
|
+
subagents?: Record<string, { status: string }>;
|
|
664
|
+
};
|
|
665
|
+
status = s.subagents?.['wake-sub']?.status;
|
|
666
|
+
if (status === 'completed') break;
|
|
667
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
668
|
+
}
|
|
669
|
+
expect(status).toBe('completed');
|
|
670
|
+
}, 30000);
|
|
671
|
+
|
|
672
|
+
it('send queues a second message while the child is still running', async () => {
|
|
673
|
+
await env.addChat('send-queue-chat', 'debug-agent');
|
|
674
|
+
chat = await env.connect('send-queue-chat');
|
|
675
|
+
|
|
676
|
+
// Start a slow initial command so the follow-up `send` lands while
|
|
677
|
+
// the first turn is still in flight. The task scheduler keys queues
|
|
678
|
+
// by `rootChatId:sessionId`, so the second handleMessage call must
|
|
679
|
+
// wait for the first before running.
|
|
680
|
+
await env.sendMessage(
|
|
681
|
+
'clawmini-lite.js subagents spawn --id queue-sub --async "sleep 2 && echo queued-first"',
|
|
682
|
+
{ chat: 'send-queue-chat', agent: 'debug-agent' }
|
|
683
|
+
);
|
|
684
|
+
// Wait for active, then immediately send the follow-up.
|
|
685
|
+
for (let i = 0; i < 50; i++) {
|
|
686
|
+
const s = env.getChatSettings('send-queue-chat') as {
|
|
687
|
+
subagents?: Record<string, { status: string }>;
|
|
688
|
+
};
|
|
689
|
+
if (s.subagents?.['queue-sub']?.status === 'active') break;
|
|
690
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
await env.sendMessage(
|
|
694
|
+
"clawmini-lite.js subagents send queue-sub --async -p 'echo queued-second'",
|
|
695
|
+
{ chat: 'send-queue-chat', agent: 'debug-agent' }
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
// Both outputs must land, and the first must come before the second.
|
|
699
|
+
await chat.waitForMessage(
|
|
700
|
+
commandMatching((m) => m.subagentId === 'queue-sub' && m.stdout.includes('queued-first')),
|
|
701
|
+
20000
|
|
702
|
+
);
|
|
703
|
+
await chat.waitForMessage(
|
|
704
|
+
commandMatching((m) => m.subagentId === 'queue-sub' && m.stdout.includes('queued-second')),
|
|
705
|
+
20000
|
|
706
|
+
);
|
|
707
|
+
const firstIdx = chat.messageBuffer.findIndex(
|
|
708
|
+
(m) =>
|
|
709
|
+
(m as { subagentId?: string; stdout?: string }).subagentId === 'queue-sub' &&
|
|
710
|
+
typeof (m as { stdout?: string }).stdout === 'string' &&
|
|
711
|
+
(m as { stdout?: string }).stdout!.includes('queued-first')
|
|
712
|
+
);
|
|
713
|
+
const secondIdx = chat.messageBuffer.findIndex(
|
|
714
|
+
(m) =>
|
|
715
|
+
(m as { subagentId?: string; stdout?: string }).subagentId === 'queue-sub' &&
|
|
716
|
+
typeof (m as { stdout?: string }).stdout === 'string' &&
|
|
717
|
+
(m as { stdout?: string }).stdout!.includes('queued-second')
|
|
718
|
+
);
|
|
719
|
+
expect(firstIdx).toBeGreaterThanOrEqual(0);
|
|
720
|
+
expect(secondIdx).toBeGreaterThan(firstIdx);
|
|
721
|
+
}, 40000);
|
|
722
|
+
|
|
723
|
+
it('async completion notification wakes the parent agent to run a new turn', async () => {
|
|
724
|
+
await env.addChat('wake-parent-chat', 'debug-agent');
|
|
725
|
+
chat = await env.connect('wake-parent-chat');
|
|
726
|
+
|
|
727
|
+
// The parent's shell command is just the spawn — it returns immediately
|
|
728
|
+
// after the subagent is registered. When the subagent finishes later,
|
|
729
|
+
// executeSubagent injects `<notification>Subagent … completed.</notification>`
|
|
730
|
+
// as a new message into the parent's session, which causes the parent's
|
|
731
|
+
// debug-agent command to RUN against that notification content. The
|
|
732
|
+
// result is a fresh command_log (no subagentId) with the [DEBUG …]
|
|
733
|
+
// prefix wrapping the notification text.
|
|
734
|
+
await env.sendMessage(
|
|
735
|
+
'clawmini-lite.js subagents spawn --id wake-parent-sub --async "echo wake-content"',
|
|
736
|
+
{ chat: 'wake-parent-chat', agent: 'debug-agent' }
|
|
737
|
+
);
|
|
738
|
+
await chat.waitForMessage(
|
|
739
|
+
commandMatching(
|
|
740
|
+
(m) => m.subagentId === 'wake-parent-sub' && m.stdout.includes('wake-content')
|
|
741
|
+
)
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
// A command_log with no subagentId and the [DEBUG …] prefix wrapping
|
|
745
|
+
// `<notification>` text is the proof the parent actually ran — the raw
|
|
746
|
+
// system message alone would not carry that prefix.
|
|
747
|
+
const wakeLog = await chat.waitForMessage(
|
|
748
|
+
commandMatching(
|
|
749
|
+
(m) =>
|
|
750
|
+
!m.subagentId &&
|
|
751
|
+
/\[DEBUG[^\]]*\] <notification>Subagent wake-parent-sub completed/.test(m.stdout)
|
|
752
|
+
),
|
|
753
|
+
15000
|
|
754
|
+
);
|
|
755
|
+
expect(wakeLog.stdout).toContain('wake-parent-sub completed');
|
|
756
|
+
}, 30000);
|
|
757
|
+
|
|
758
|
+
it('operations on a nonexistent subagent id return NOT_FOUND', async () => {
|
|
759
|
+
await env.addChat('missing-id-chat', 'debug-agent');
|
|
760
|
+
chat = await env.connect('missing-id-chat');
|
|
761
|
+
|
|
762
|
+
// Each of these CLI calls should hit the router and fail with a
|
|
763
|
+
// NOT_FOUND error surfaced back to the caller's stderr. We rely on
|
|
764
|
+
// the serialized message — both 'Subagent not found' and the CLI's
|
|
765
|
+
// 'Error:' prefix land in the command log.
|
|
766
|
+
const ops = [
|
|
767
|
+
'clawmini-lite.js subagents wait ghost',
|
|
768
|
+
'clawmini-lite.js subagents stop ghost',
|
|
769
|
+
'clawmini-lite.js subagents delete ghost',
|
|
770
|
+
'clawmini-lite.js subagents tail ghost --json',
|
|
771
|
+
"clawmini-lite.js subagents send ghost --async -p 'echo x'",
|
|
772
|
+
];
|
|
773
|
+
for (const op of ops) {
|
|
774
|
+
await env.sendMessage(op, { chat: 'missing-id-chat', agent: 'debug-agent' });
|
|
775
|
+
const log = await chat.waitForMessage(
|
|
776
|
+
commandMatching((m) => !m.subagentId && m.stdout.includes(`${op}:`)),
|
|
777
|
+
15000
|
|
778
|
+
);
|
|
779
|
+
expect(JSON.stringify(log)).toMatch(/Subagent not found/);
|
|
780
|
+
}
|
|
781
|
+
}, 60000);
|
|
782
|
+
});
|