clawmini 0.0.3 → 0.0.5
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/README.md +19 -0
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +398 -193
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.d.mts +5 -0
- package/dist/adapter-google-chat/index.d.mts.map +1 -0
- package/dist/adapter-google-chat/index.mjs +1077 -0
- package/dist/adapter-google-chat/index.mjs.map +1 -0
- package/dist/cli/index.mjs +107 -14
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +175 -16
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/propose-policy.d.mts +1 -0
- package/dist/cli/propose-policy.mjs +7159 -0
- package/dist/cli/propose-policy.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +1427 -513
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{lite-oSYSvaOr.mjs → lite-CBxOT1y5.mjs} +101 -24
- package/dist/lite-CBxOT1y5.mjs.map +1 -0
- package/dist/routing-D8rTxtaV.mjs +245 -0
- package/dist/routing-D8rTxtaV.mjs.map +1 -0
- package/dist/web/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/dist/web/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/dist/web/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/8YNcRyEk.js → dist/web/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/dist/web/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/dist/web/_app/immutable/chunks/CME08kGM.js +1 -0
- package/dist/web/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/dist/web/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/dist/web/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/dist/web/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/entry/app.DO5eYwVz.js → dist/web/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/dist/web/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/dist/web/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/dist/web/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/dist/web/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/dist/web/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/{workspace-DjoNjhW0.mjs → workspace-BJmJBfKi.mjs} +103 -11
- package/dist/workspace-BJmJBfKi.mjs.map +1 -0
- package/docs/14_google_chat_adapter/development_log.md +40 -0
- package/docs/14_google_chat_adapter/notes.md +28 -0
- package/docs/14_google_chat_adapter/prd.md +35 -0
- package/docs/14_google_chat_adapter/questions.md +9 -0
- package/docs/14_google_chat_adapter/tickets.md +117 -0
- package/docs/15_sandbox_policies/tickets.md +33 -0
- package/docs/16_session_timeout/development_log.md +20 -0
- package/docs/16_session_timeout/notes.md +44 -0
- package/docs/16_session_timeout/prd.md +106 -0
- package/docs/16_session_timeout/questions.md +10 -0
- package/docs/16_session_timeout/tickets.md +64 -0
- package/docs/17_auto_approve_policy/development_log.md +29 -0
- package/docs/17_auto_approve_policy/notes.md +25 -0
- package/docs/17_auto_approve_policy/prd.md +34 -0
- package/docs/17_auto_approve_policy/questions.md +10 -0
- package/docs/17_auto_approve_policy/tickets.md +11 -0
- package/docs/18_clawmini_skills/development_log.md +36 -0
- package/docs/18_clawmini_skills/notes.md +8 -0
- package/docs/18_clawmini_skills/prd.md +45 -0
- package/docs/18_clawmini_skills/questions.md +10 -0
- package/docs/18_clawmini_skills/tickets.md +55 -0
- package/docs/19_subagents/development_log.md +69 -0
- package/docs/19_subagents/notes.md +18 -0
- package/docs/19_subagents/prd.md +156 -0
- package/docs/19_subagents/questions.md +13 -0
- package/docs/19_subagents/tickets.md +113 -0
- package/docs/20_chat_logs_cleanup/development_log.md +50 -0
- package/docs/20_chat_logs_cleanup/notes.md +43 -0
- package/docs/20_chat_logs_cleanup/prd.md +232 -0
- package/docs/20_chat_logs_cleanup/questions.md +2 -0
- package/docs/20_chat_logs_cleanup/tickets.md +98 -0
- package/docs/20_webui_markdown/development_log.md +36 -0
- package/docs/20_webui_markdown/notes.md +23 -0
- package/docs/20_webui_markdown/prd.md +49 -0
- package/docs/20_webui_markdown/questions.md +10 -0
- package/docs/20_webui_markdown/tickets.md +55 -0
- package/docs/21_adapter_filtering/development_log.md +29 -0
- package/docs/21_adapter_filtering/notes.md +25 -0
- package/docs/21_adapter_filtering/prd.md +44 -0
- package/docs/21_adapter_filtering/questions.md +12 -0
- package/docs/21_adapter_filtering/tickets.md +38 -0
- package/docs/21_built_in_routers/development_log.md +17 -0
- package/docs/21_built_in_routers/notes.md +27 -0
- package/docs/21_built_in_routers/prd.md +34 -0
- package/docs/21_built_in_routers/questions.md +4 -0
- package/docs/21_built_in_routers/tickets.md +25 -0
- package/docs/21_fancy_policies/development_log.md +38 -0
- package/docs/21_fancy_policies/notes.md +27 -0
- package/docs/21_fancy_policies/prd.md +58 -0
- package/docs/21_fancy_policies/questions.md +6 -0
- package/docs/21_fancy_policies/tickets.md +48 -0
- package/docs/22_adapter_multi_chat/development_log.md +76 -0
- package/docs/22_adapter_multi_chat/notes.md +42 -0
- package/docs/22_adapter_multi_chat/prd.md +76 -0
- package/docs/22_adapter_multi_chat/questions.md +16 -0
- package/docs/22_adapter_multi_chat/tickets.md +164 -0
- package/docs/23_custom_token_env/development_log.md +31 -0
- package/docs/23_custom_token_env/notes.md +16 -0
- package/docs/23_custom_token_env/prd.md +42 -0
- package/docs/23_custom_token_env/questions.md +8 -0
- package/docs/23_custom_token_env/tickets.md +54 -0
- package/docs/guides/discord_adapter_setup.md +15 -2
- package/docs/guides/google_chat_adapter_setup.md +145 -0
- package/napkin.md +5 -0
- package/package.json +7 -2
- package/src/adapter-discord/config.test.ts +27 -8
- package/src/adapter-discord/config.ts +6 -8
- package/src/adapter-discord/forwarder.test.ts +307 -114
- package/src/adapter-discord/forwarder.ts +260 -75
- package/src/adapter-discord/index.test.ts +278 -0
- package/src/adapter-discord/index.ts +160 -30
- package/src/adapter-discord/interactions.test.ts +96 -0
- package/src/adapter-discord/interactions.ts +156 -0
- package/src/adapter-discord/state.test.ts +9 -8
- package/src/adapter-discord/state.ts +51 -8
- package/src/adapter-google-chat/auth.test.ts +87 -0
- package/src/adapter-google-chat/auth.ts +132 -0
- package/src/adapter-google-chat/cards.ts +71 -0
- package/src/adapter-google-chat/client.test.ts +561 -0
- package/src/adapter-google-chat/client.ts +430 -0
- package/src/adapter-google-chat/config.test.ts +187 -0
- package/src/adapter-google-chat/config.ts +82 -0
- package/src/adapter-google-chat/cron.test.ts +143 -0
- package/src/adapter-google-chat/cron.ts +81 -0
- package/src/adapter-google-chat/forwarder.test.ts +537 -0
- package/src/adapter-google-chat/forwarder.ts +349 -0
- package/src/adapter-google-chat/index.test.ts +62 -0
- package/src/adapter-google-chat/index.ts +61 -0
- package/src/adapter-google-chat/state.test.ts +96 -0
- package/src/adapter-google-chat/state.ts +85 -0
- package/src/adapter-google-chat/subscriptions.ts +124 -0
- package/src/adapter-google-chat/upload.ts +88 -0
- package/src/adapter-google-chat/utils.test.ts +111 -0
- package/src/adapter-google-chat/utils.ts +133 -0
- package/src/cli/commands/init.ts +0 -7
- package/src/cli/commands/messages.ts +18 -3
- package/src/cli/commands/policies.ts +70 -0
- package/src/cli/commands/skills.ts +71 -0
- package/src/cli/commands/web-api/chats.ts +5 -1
- package/src/cli/e2e/basic.test.ts +1 -1
- package/src/cli/e2e/cron.test.ts +1 -1
- package/src/cli/e2e/daemon.test.ts +132 -4
- package/src/cli/e2e/export-lite-func.test.ts +54 -31
- package/src/cli/e2e/fallbacks.test.ts +8 -6
- package/src/cli/e2e/init.test.ts +7 -0
- package/src/cli/e2e/messages.test.ts +90 -55
- package/src/cli/e2e/propose-policy.test.ts +203 -0
- package/src/cli/e2e/requests.test.ts +15 -0
- package/src/cli/e2e/session-timeout.test.ts +192 -0
- package/src/cli/e2e/skills.test.ts +55 -0
- package/src/cli/e2e/slash-new.test.ts +93 -0
- package/src/cli/e2e/subagents.test.ts +106 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/lite.ts +51 -11
- package/src/cli/propose-policy.ts +91 -0
- package/src/cli/subagent-commands.ts +215 -0
- package/src/daemon/agent/agent-context.ts +89 -0
- package/src/daemon/agent/agent-extractors.ts +68 -0
- package/src/daemon/agent/agent-runner.ts +153 -0
- package/src/daemon/agent/agent-session.ts +261 -0
- package/src/daemon/agent/chat-logger.test.ts +158 -0
- package/src/daemon/agent/chat-logger.ts +188 -0
- package/src/daemon/agent/task-scheduler.test.ts +202 -0
- package/src/daemon/agent/task-scheduler.ts +276 -0
- package/src/daemon/agent/types.ts +84 -0
- package/src/daemon/agent/utils.ts +7 -0
- package/src/daemon/api/agent-router.ts +166 -18
- package/src/daemon/api/index.test.ts +50 -18
- package/src/daemon/api/policy-request.test.ts +39 -2
- package/src/daemon/api/subagent-router.test.ts +108 -0
- package/src/daemon/api/subagent-router.ts +296 -0
- package/src/daemon/api/subagent-utils.test.ts +56 -0
- package/src/daemon/api/subagent-utils.ts +130 -0
- package/src/daemon/api/user-router.ts +30 -13
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/chats.ts +6 -0
- package/src/daemon/cron.test.ts +66 -1
- package/src/daemon/cron.ts +35 -8
- package/src/daemon/index.ts +23 -0
- package/src/daemon/message-agent.test.ts +11 -25
- package/src/daemon/message-extraction.test.ts +10 -27
- package/src/daemon/message-fallbacks.test.ts +13 -35
- package/src/daemon/message-interruption.test.ts +70 -53
- package/src/daemon/message-jobs.test.ts +138 -0
- package/src/daemon/message-queue.test.ts +30 -43
- package/src/daemon/message-router.test.ts +12 -11
- package/src/daemon/message-session.test.ts +41 -28
- package/src/daemon/message-typing.test.ts +19 -6
- package/src/daemon/message.ts +103 -515
- package/src/daemon/policy-request-service.ts +8 -3
- package/src/daemon/policy-utils.ts +19 -1
- package/src/daemon/queue.ts +16 -0
- package/src/daemon/request-store.test.ts +4 -0
- package/src/daemon/routers/session-timeout.test.ts +122 -0
- package/src/daemon/routers/session-timeout.ts +71 -0
- package/src/daemon/routers/slash-new.ts +3 -1
- package/src/daemon/routers/slash-policies.test.ts +26 -13
- package/src/daemon/routers/slash-policies.ts +39 -29
- package/src/daemon/routers/types.ts +8 -0
- package/src/daemon/routers.ts +64 -2
- package/src/daemon/utils/spawn.ts +6 -8
- package/src/shared/adapters/commands.test.ts +155 -0
- package/src/shared/adapters/commands.ts +125 -0
- package/src/shared/adapters/filtering.test.ts +111 -0
- package/src/shared/adapters/filtering.ts +57 -0
- package/src/shared/adapters/routing.test.ts +144 -0
- package/src/shared/adapters/routing.ts +109 -0
- package/src/shared/agent-utils.ts +10 -0
- package/src/shared/chats.test.ts +145 -3
- package/src/shared/chats.ts +215 -18
- package/src/shared/config.ts +67 -15
- package/src/shared/lite.ts +22 -18
- package/src/shared/policies.ts +7 -0
- package/src/shared/workspace.test.ts +45 -1
- package/src/shared/workspace.ts +119 -6
- package/templates/debug/settings.json +5 -2
- package/templates/environments/cladding/env.json +2 -2
- package/templates/gemini/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini/.gemini/hooks/clawmini-logging.sh +17 -0
- package/templates/gemini/.gemini/hooks/insert-pending.sh +9 -0
- package/templates/gemini/.gemini/settings.json +50 -0
- package/templates/gemini/settings.json +22 -8
- package/templates/gemini-claw/.gemini/base-system.md +100 -0
- package/templates/gemini-claw/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +1 -1
- package/templates/gemini-claw/.gemini/settings.json +13 -0
- package/templates/gemini-claw/.gemini/subagent-system.md +7 -0
- package/templates/gemini-claw/.gemini/system.md +3 -99
- package/templates/gemini-claw/settings.json +27 -22
- package/templates/skills/clawmini-requests/SKILL.md +92 -0
- package/templates/skills/clawmini-subagents/SKILL.md +79 -0
- package/templates/skills/skill-creator/SKILL.md +60 -0
- package/tsdown.config.ts +10 -1
- package/web/.svelte-kit/generated/server/internal.js +2 -1
- package/web/.svelte-kit/non-ambient.d.ts +2 -0
- package/web/.svelte-kit/output/client/.vite/manifest.json +141 -138
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{dist/web/_app/immutable/chunks/8YNcRyEk.js → web/.svelte-kit/output/client/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{dist/web/_app/immutable/entry/app.DO5eYwVz.js → web/.svelte-kit/output/client/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -3
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/server/_app/immutable/assets/_page.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/server/chunks/app-state.svelte.js +5 -0
- package/web/.svelte-kit/output/server/chunks/bot.js +4 -4
- package/web/.svelte-kit/output/server/chunks/client.js +2 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +0 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +2 -1
- package/web/.svelte-kit/output/server/chunks/root.js +482 -392
- package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +57 -7
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +234 -9
- package/web/.svelte-kit/output/server/index.js +82 -10
- 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 +2 -2
- 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 +2 -2
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/types/src/routes/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +1 -2
- package/web/package.json +8 -0
- package/web/src/lib/app-state.svelte.ts +5 -1
- package/web/src/lib/components/app/markdown-renderer.svelte +56 -0
- package/web/src/lib/components/app/markdown-renderer.svelte.spec.ts +44 -0
- package/web/src/lib/components/app/message-content.svelte +16 -0
- package/web/src/lib/types.ts +67 -3
- package/web/src/routes/+layout.svelte +31 -1
- package/web/src/routes/chats/[id]/+page.svelte +167 -18
- package/web/src/routes/chats/[id]/page.svelte.spec.ts +58 -7
- package/dist/lite-oSYSvaOr.mjs.map +0 -1
- package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/dist/web/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/dist/web/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/dist/web/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/dist/web/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/dist/web/_app/immutable/chunks/DQoygso7.js +0 -1
- package/dist/web/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/dist/web/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/dist/web/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/dist/workspace-DjoNjhW0.mjs.map +0 -1
- package/src/daemon/message-verbosity.test.ts +0 -127
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DQoygso7.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +0 -1
- /package/templates/{gemini-claw/.gemini/skills → skills}/clawmini-jobs/SKILL.md +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChatMessage,
|
|
3
|
+
CommandLogMessage,
|
|
4
|
+
UserMessage,
|
|
5
|
+
SystemMessage,
|
|
6
|
+
AgentReplyMessage,
|
|
7
|
+
ToolMessage,
|
|
8
|
+
PolicyRequestMessage,
|
|
9
|
+
SubagentStatusMessage,
|
|
10
|
+
} from '../chats.js';
|
|
11
|
+
|
|
12
|
+
export interface Logger {
|
|
13
|
+
append<T extends ChatMessage>(msg: T): Promise<T>;
|
|
14
|
+
getMessages(limit?: number): Promise<ChatMessage[]>;
|
|
15
|
+
findLastMessage(predicate: (msg: ChatMessage) => boolean): Promise<ChatMessage | null>;
|
|
16
|
+
logUserMessage(msg: string): Promise<UserMessage>;
|
|
17
|
+
logSystemEvent(options: {
|
|
18
|
+
content: string;
|
|
19
|
+
level?: 'default' | 'debug' | 'verbose';
|
|
20
|
+
}): Promise<CommandLogMessage>;
|
|
21
|
+
logAutomaticReply(options: { messageId: string; content: string }): Promise<SystemMessage>;
|
|
22
|
+
logCommandRetry(options: {
|
|
23
|
+
messageId: string;
|
|
24
|
+
content: string;
|
|
25
|
+
cwd: string;
|
|
26
|
+
}): Promise<CommandLogMessage>;
|
|
27
|
+
logCommandResult(options: ExecutionResponse): Promise<CommandLogMessage>;
|
|
28
|
+
logSystemMessage(options: {
|
|
29
|
+
content: string;
|
|
30
|
+
event: SystemMessage['event'];
|
|
31
|
+
messageId?: string;
|
|
32
|
+
displayRole?: 'user' | 'agent';
|
|
33
|
+
}): Promise<SystemMessage>;
|
|
34
|
+
logSubagentStatus(options: {
|
|
35
|
+
subagentId: string;
|
|
36
|
+
status: 'completed' | 'failed';
|
|
37
|
+
}): Promise<SubagentStatusMessage>;
|
|
38
|
+
logAgentReply(options: { content: string; files?: string[] }): Promise<AgentReplyMessage>;
|
|
39
|
+
logToolMessage(options: {
|
|
40
|
+
content: string;
|
|
41
|
+
messageId: string;
|
|
42
|
+
name: string;
|
|
43
|
+
payload: unknown;
|
|
44
|
+
}): Promise<ToolMessage>;
|
|
45
|
+
logPolicyRequestMessage(options: {
|
|
46
|
+
content: string;
|
|
47
|
+
messageId: string;
|
|
48
|
+
requestId: string;
|
|
49
|
+
commandName: string;
|
|
50
|
+
args: string[];
|
|
51
|
+
status: 'pending' | 'approved' | 'rejected';
|
|
52
|
+
}): Promise<PolicyRequestMessage>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface Message {
|
|
56
|
+
id: string;
|
|
57
|
+
content: string;
|
|
58
|
+
env: Record<string, string>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ExecutionResponse {
|
|
62
|
+
messageId: string;
|
|
63
|
+
content: string;
|
|
64
|
+
command: string;
|
|
65
|
+
cwd: string;
|
|
66
|
+
result: RunCommandResult;
|
|
67
|
+
extractedSessionId?: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type RunCommandResult = {
|
|
71
|
+
stdout: string;
|
|
72
|
+
stderr: string;
|
|
73
|
+
exitCode: number;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type RunCommandFn = (args: {
|
|
77
|
+
command: string;
|
|
78
|
+
cwd: string;
|
|
79
|
+
env: Record<string, string>;
|
|
80
|
+
stdin?: string | undefined;
|
|
81
|
+
signal?: AbortSignal | undefined;
|
|
82
|
+
}) => Promise<RunCommandResult>;
|
|
83
|
+
|
|
84
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function formatPendingMessages(payloads: string[]): string {
|
|
2
|
+
return payloads.map((text) => `<message>\n${text}\n</message>`).join('\n\n');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isNewSession(env: Record<string, string>): boolean {
|
|
6
|
+
return env['SESSION_ID'] === undefined;
|
|
7
|
+
}
|
|
@@ -2,15 +2,21 @@ import { z } from 'zod';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { TRPCError } from '@trpc/server';
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
appendMessage,
|
|
7
|
+
type CommandLogMessage,
|
|
8
|
+
type AgentReplyMessage,
|
|
9
|
+
type ToolMessage,
|
|
10
|
+
type PolicyRequestMessage,
|
|
11
|
+
} from '../chats.js';
|
|
12
|
+
import { executeSafe, generateRequestPreview, executeRequest } from '../policy-utils.js';
|
|
7
13
|
import { getWorkspaceRoot, readPolicies, getClawminiDir } from '../../shared/workspace.js';
|
|
8
14
|
import { PolicyRequestService } from '../policy-request-service.js';
|
|
9
15
|
import { RequestStore } from '../request-store.js';
|
|
10
16
|
import { CronJobSchema } from '../../shared/config.js';
|
|
11
17
|
import { apiProcedure, router } from './trpc.js';
|
|
12
|
-
import {
|
|
13
|
-
import { formatPendingMessages } from '../
|
|
18
|
+
import { taskScheduler } from '../agent/task-scheduler.js';
|
|
19
|
+
import { formatPendingMessages } from '../agent/utils.js';
|
|
14
20
|
import {
|
|
15
21
|
resolveAgentDir,
|
|
16
22
|
validateLogFile,
|
|
@@ -48,14 +54,52 @@ export const logMessage = apiProcedure
|
|
|
48
54
|
const logMsg: CommandLogMessage = {
|
|
49
55
|
id,
|
|
50
56
|
messageId: id,
|
|
51
|
-
role: '
|
|
52
|
-
source: 'router',
|
|
57
|
+
role: 'command',
|
|
53
58
|
content: messageStr,
|
|
59
|
+
stdout: '',
|
|
54
60
|
stderr: '',
|
|
55
61
|
timestamp,
|
|
56
62
|
command: `clawmini-lite log${filesArgStr}`,
|
|
57
63
|
cwd: process.cwd(),
|
|
58
64
|
exitCode: 0,
|
|
65
|
+
...(ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}),
|
|
66
|
+
...(filePaths.length > 0 ? { files: filePaths } : {}),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
await appendMessage(chatId, logMsg);
|
|
70
|
+
return { success: true };
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const logReplyMessage = apiProcedure
|
|
74
|
+
.input(
|
|
75
|
+
z.object({
|
|
76
|
+
message: z.string(),
|
|
77
|
+
files: z.array(z.string()).optional(),
|
|
78
|
+
})
|
|
79
|
+
)
|
|
80
|
+
.mutation(async ({ input, ctx }) => {
|
|
81
|
+
if (!ctx.tokenPayload) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing token' });
|
|
82
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
83
|
+
const timestamp = new Date().toISOString();
|
|
84
|
+
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
85
|
+
|
|
86
|
+
const filePaths: string[] = [];
|
|
87
|
+
if (input.files && input.files.length > 0) {
|
|
88
|
+
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
89
|
+
const agentDir = await resolveAgentDir(ctx.tokenPayload?.agentId, workspaceRoot);
|
|
90
|
+
|
|
91
|
+
for (const file of input.files) {
|
|
92
|
+
const validPath = await validateLogFile(file, agentDir, workspaceRoot);
|
|
93
|
+
filePaths.push(validPath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const logMsg: AgentReplyMessage = {
|
|
98
|
+
id,
|
|
99
|
+
role: 'agent',
|
|
100
|
+
content: input.message,
|
|
101
|
+
timestamp,
|
|
102
|
+
...(ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}),
|
|
59
103
|
...(filePaths.length > 0 ? { files: filePaths } : {}),
|
|
60
104
|
};
|
|
61
105
|
|
|
@@ -63,6 +107,47 @@ export const logMessage = apiProcedure
|
|
|
63
107
|
return { success: true };
|
|
64
108
|
});
|
|
65
109
|
|
|
110
|
+
export const logToolMessage = apiProcedure
|
|
111
|
+
.input(
|
|
112
|
+
z.object({
|
|
113
|
+
name: z.string(),
|
|
114
|
+
payload: z.any().optional(),
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
.mutation(async ({ input, ctx }) => {
|
|
118
|
+
if (!ctx.tokenPayload) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing token' });
|
|
119
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
120
|
+
const timestamp = new Date().toISOString();
|
|
121
|
+
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
122
|
+
const messageId = randomUUID();
|
|
123
|
+
|
|
124
|
+
const payloadObj = input.payload !== undefined ? input.payload : {};
|
|
125
|
+
let contentStr: string;
|
|
126
|
+
if (typeof payloadObj === 'string') {
|
|
127
|
+
contentStr = payloadObj;
|
|
128
|
+
} else {
|
|
129
|
+
try {
|
|
130
|
+
contentStr = JSON.stringify(payloadObj, null, 2);
|
|
131
|
+
} catch {
|
|
132
|
+
contentStr = String(payloadObj);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const logMsg: ToolMessage = {
|
|
137
|
+
id,
|
|
138
|
+
messageId,
|
|
139
|
+
role: 'tool',
|
|
140
|
+
name: input.name,
|
|
141
|
+
payload: payloadObj,
|
|
142
|
+
content: contentStr,
|
|
143
|
+
timestamp,
|
|
144
|
+
...(ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
await appendMessage(chatId, logMsg);
|
|
148
|
+
return { success: true };
|
|
149
|
+
});
|
|
150
|
+
|
|
66
151
|
export const agentListCronJobs = apiProcedure.query(async ({ ctx }) => {
|
|
67
152
|
if (!ctx.tokenPayload) throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing token' });
|
|
68
153
|
const chatId = ctx.tokenPayload.chatId;
|
|
@@ -134,28 +219,70 @@ export const createPolicyRequest = apiProcedure
|
|
|
134
219
|
const chatId = ctx.tokenPayload.chatId;
|
|
135
220
|
const agentId = ctx.tokenPayload.agentId;
|
|
136
221
|
|
|
222
|
+
const config = await readPolicies();
|
|
223
|
+
const policy = config?.policies?.[input.commandName];
|
|
224
|
+
|
|
225
|
+
if (!policy) {
|
|
226
|
+
throw new TRPCError({
|
|
227
|
+
code: 'NOT_FOUND',
|
|
228
|
+
message: `Policy not found: ${input.commandName}`,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const isAutoApprove = !!policy.autoApprove;
|
|
233
|
+
|
|
137
234
|
const request = await service.createRequest(
|
|
138
235
|
input.commandName,
|
|
139
236
|
input.args,
|
|
140
237
|
input.fileMappings,
|
|
141
238
|
chatId,
|
|
142
|
-
agentId
|
|
239
|
+
agentId,
|
|
240
|
+
isAutoApprove,
|
|
241
|
+
ctx.tokenPayload.subagentId
|
|
143
242
|
);
|
|
144
243
|
|
|
244
|
+
if (isAutoApprove) {
|
|
245
|
+
const { stdout, stderr, exitCode, commandStr } = await executeRequest(
|
|
246
|
+
request,
|
|
247
|
+
policy,
|
|
248
|
+
getWorkspaceRoot()
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
request.executionResult = { stdout, stderr, exitCode };
|
|
252
|
+
await store.save(request);
|
|
253
|
+
|
|
254
|
+
const logMsg: PolicyRequestMessage = {
|
|
255
|
+
id: randomUUID(),
|
|
256
|
+
// TODO: we should store the message ID in the CLAW_API_TOKEN, and extract it here
|
|
257
|
+
messageId: randomUUID(),
|
|
258
|
+
role: 'policy',
|
|
259
|
+
requestId: request.id,
|
|
260
|
+
commandName: input.commandName,
|
|
261
|
+
args: input.args,
|
|
262
|
+
status: 'approved',
|
|
263
|
+
content: `[Auto-approved] Policy ${input.commandName} was executed.\n\nCommand: ${commandStr}\nExit Code: ${exitCode}\n\nStdout:\n${stdout}\n\nStderr:\n${stderr}`,
|
|
264
|
+
timestamp: new Date().toISOString(),
|
|
265
|
+
...(ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
await appendMessage(chatId, logMsg);
|
|
269
|
+
return request;
|
|
270
|
+
}
|
|
271
|
+
|
|
145
272
|
const previewContent = await generateRequestPreview(request);
|
|
146
273
|
|
|
147
|
-
const logMsg = {
|
|
274
|
+
const logMsg: PolicyRequestMessage = {
|
|
148
275
|
id: randomUUID(),
|
|
149
276
|
// TODO: we should store the message ID in the CLAW_API_TOKEN, and extract it here
|
|
150
277
|
messageId: randomUUID(),
|
|
151
|
-
role: '
|
|
152
|
-
|
|
278
|
+
role: 'policy',
|
|
279
|
+
requestId: request.id,
|
|
280
|
+
commandName: input.commandName,
|
|
281
|
+
args: input.args,
|
|
282
|
+
status: 'pending',
|
|
153
283
|
content: previewContent,
|
|
154
|
-
stderr: '',
|
|
155
284
|
timestamp: new Date().toISOString(),
|
|
156
|
-
|
|
157
|
-
cwd: process.cwd(),
|
|
158
|
-
exitCode: 0,
|
|
285
|
+
displayRole: 'agent',
|
|
159
286
|
};
|
|
160
287
|
|
|
161
288
|
await appendMessage(chatId, logMsg);
|
|
@@ -165,19 +292,33 @@ export const createPolicyRequest = apiProcedure
|
|
|
165
292
|
import { ping } from './user-router.js';
|
|
166
293
|
|
|
167
294
|
export const fetchPendingMessages = apiProcedure.mutation(async ({ ctx }) => {
|
|
168
|
-
|
|
169
|
-
|
|
295
|
+
if (!ctx.tokenPayload?.agentId) {
|
|
296
|
+
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Missing agent ID' });
|
|
297
|
+
}
|
|
170
298
|
const targetSessionId = ctx.tokenPayload?.sessionId || 'default';
|
|
171
299
|
|
|
172
|
-
const extracted =
|
|
300
|
+
const extracted = taskScheduler.extractPending(targetSessionId);
|
|
173
301
|
if (extracted.length === 0) {
|
|
174
302
|
return { messages: '' };
|
|
175
303
|
}
|
|
176
|
-
|
|
304
|
+
|
|
305
|
+
return { messages: formatPendingMessages(extracted) };
|
|
177
306
|
});
|
|
178
307
|
|
|
308
|
+
import {
|
|
309
|
+
subagentSpawn,
|
|
310
|
+
subagentSend,
|
|
311
|
+
subagentWait,
|
|
312
|
+
subagentStop,
|
|
313
|
+
subagentDelete,
|
|
314
|
+
subagentList,
|
|
315
|
+
subagentTail,
|
|
316
|
+
} from './subagent-router.js';
|
|
317
|
+
|
|
179
318
|
export const agentRouter = router({
|
|
180
319
|
logMessage,
|
|
320
|
+
logReplyMessage,
|
|
321
|
+
logToolMessage,
|
|
181
322
|
listCronJobs: agentListCronJobs,
|
|
182
323
|
addCronJob: agentAddCronJob,
|
|
183
324
|
deleteCronJob: agentDeleteCronJob,
|
|
@@ -186,6 +327,13 @@ export const agentRouter = router({
|
|
|
186
327
|
createPolicyRequest,
|
|
187
328
|
fetchPendingMessages,
|
|
188
329
|
ping,
|
|
330
|
+
subagentSpawn,
|
|
331
|
+
subagentSend,
|
|
332
|
+
subagentWait,
|
|
333
|
+
subagentStop,
|
|
334
|
+
subagentDelete,
|
|
335
|
+
subagentList,
|
|
336
|
+
subagentTail,
|
|
189
337
|
});
|
|
190
338
|
|
|
191
339
|
export type AgentRouter = typeof agentRouter;
|
|
@@ -6,7 +6,7 @@ import * as workspace from '../../shared/workspace.js';
|
|
|
6
6
|
import * as chats from '../../shared/chats.js';
|
|
7
7
|
import type { CronJob } from '../../shared/config.js';
|
|
8
8
|
import * as message from '../message.js';
|
|
9
|
-
import {
|
|
9
|
+
import { taskScheduler } from '../agent/task-scheduler.js';
|
|
10
10
|
import * as fs from 'node:fs/promises';
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
|
|
@@ -46,6 +46,7 @@ vi.mock('../../shared/workspace.js', async (importOriginal) => {
|
|
|
46
46
|
const actual = await importOriginal<typeof import('../../shared/workspace.js')>();
|
|
47
47
|
return {
|
|
48
48
|
...actual,
|
|
49
|
+
readSettings: vi.fn().mockResolvedValue(null),
|
|
49
50
|
readChatSettings: vi.fn(),
|
|
50
51
|
writeChatSettings: vi.fn(),
|
|
51
52
|
getSettingsPath: vi.fn().mockReturnValue('/mock/settings.json'),
|
|
@@ -169,7 +170,6 @@ describe('Daemon TRPC Router', () => {
|
|
|
169
170
|
{},
|
|
170
171
|
undefined,
|
|
171
172
|
false,
|
|
172
|
-
expect.any(Function),
|
|
173
173
|
undefined,
|
|
174
174
|
undefined
|
|
175
175
|
);
|
|
@@ -300,7 +300,7 @@ describe('Daemon TRPC Router', () => {
|
|
|
300
300
|
expect(chats.appendMessage).toHaveBeenCalledWith(
|
|
301
301
|
'default-chat',
|
|
302
302
|
expect.objectContaining({
|
|
303
|
-
role: '
|
|
303
|
+
role: 'command',
|
|
304
304
|
content: 'Test log',
|
|
305
305
|
}),
|
|
306
306
|
expect.any(String)
|
|
@@ -325,7 +325,7 @@ describe('Daemon TRPC Router', () => {
|
|
|
325
325
|
expect(chats.appendMessage).toHaveBeenCalledWith(
|
|
326
326
|
'default-chat',
|
|
327
327
|
expect.objectContaining({
|
|
328
|
-
role: '
|
|
328
|
+
role: 'command',
|
|
329
329
|
content: 'Test log with file',
|
|
330
330
|
files: [path.normalize('attachments/discord/image.png')],
|
|
331
331
|
}),
|
|
@@ -393,10 +393,9 @@ describe('Daemon TRPC Router', () => {
|
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
describe('fetchPendingMessages', () => {
|
|
396
|
-
let queue: ReturnType<typeof getMessageQueue>;
|
|
397
396
|
beforeEach(() => {
|
|
398
|
-
|
|
399
|
-
|
|
397
|
+
taskScheduler.abortTasks('s1');
|
|
398
|
+
taskScheduler.abortTasks('s2');
|
|
400
399
|
});
|
|
401
400
|
|
|
402
401
|
it('should extract pending messages from queue matching the session and format them', async () => {
|
|
@@ -406,21 +405,54 @@ describe('Daemon TRPC Router', () => {
|
|
|
406
405
|
});
|
|
407
406
|
|
|
408
407
|
// The first task will start and block, leaving the others in pending
|
|
409
|
-
|
|
410
|
-
|
|
408
|
+
taskScheduler.schedule({
|
|
409
|
+
id: 't1',
|
|
410
|
+
rootChatId: 'c1',
|
|
411
|
+
dirPath: 'd1',
|
|
412
|
+
sessionId: 's1',
|
|
413
|
+
execute: async () => {
|
|
411
414
|
await firstTaskPromise;
|
|
412
415
|
},
|
|
413
|
-
|
|
414
|
-
);
|
|
416
|
+
});
|
|
415
417
|
|
|
416
418
|
// These will stay in pending
|
|
417
|
-
const p2 =
|
|
418
|
-
|
|
419
|
-
|
|
419
|
+
const p2 = taskScheduler.schedule({
|
|
420
|
+
id: 't2',
|
|
421
|
+
rootChatId: 'c1', // force block
|
|
422
|
+
dirPath: 'd1',
|
|
423
|
+
sessionId: 's1',
|
|
424
|
+
text: 'Task 2',
|
|
425
|
+
execute: async () => {},
|
|
426
|
+
});
|
|
427
|
+
const p3 = taskScheduler.schedule({
|
|
428
|
+
id: 't3',
|
|
429
|
+
rootChatId: 'c1', // force block
|
|
430
|
+
dirPath: 'd1',
|
|
431
|
+
sessionId: 's1',
|
|
432
|
+
text: 'Task 3',
|
|
433
|
+
execute: async () => {},
|
|
434
|
+
});
|
|
435
|
+
const p3_5 = taskScheduler.schedule({
|
|
436
|
+
id: 't3_5',
|
|
437
|
+
rootChatId: 'c2', // force block
|
|
438
|
+
dirPath: 'd1',
|
|
439
|
+
sessionId: 's2',
|
|
440
|
+
text: 'Task 3.5',
|
|
441
|
+
execute: async () => {},
|
|
442
|
+
});
|
|
443
|
+
const p4 = taskScheduler.schedule({
|
|
444
|
+
id: 't4',
|
|
445
|
+
rootChatId: 'c2',
|
|
446
|
+
dirPath: 'd1',
|
|
447
|
+
sessionId: 's2',
|
|
448
|
+
text: 'Task 4',
|
|
449
|
+
execute: async () => {},
|
|
450
|
+
});
|
|
420
451
|
|
|
421
452
|
// We expect them to throw AbortError when extracted
|
|
422
453
|
p2.catch(() => {});
|
|
423
454
|
p3.catch(() => {});
|
|
455
|
+
p3_5.catch(() => {});
|
|
424
456
|
p4.catch(() => {});
|
|
425
457
|
|
|
426
458
|
const caller = agentRouter.createCaller({
|
|
@@ -431,15 +463,15 @@ describe('Daemon TRPC Router', () => {
|
|
|
431
463
|
expect(result.messages).toBe(
|
|
432
464
|
'<message>\nTask 2\n</message>\n\n<message>\nTask 3\n</message>'
|
|
433
465
|
);
|
|
434
|
-
expect(
|
|
435
|
-
{ text: 'Task 4', sessionId: 's2' },
|
|
436
|
-
]);
|
|
466
|
+
expect(taskScheduler.extractPending('s2')).toEqual(['Task 4']);
|
|
437
467
|
|
|
438
468
|
resolveFirstTask!(); // cleanup
|
|
439
469
|
});
|
|
440
470
|
|
|
441
471
|
it('should return empty string if no pending messages', async () => {
|
|
442
|
-
const caller = agentRouter.createCaller({
|
|
472
|
+
const caller = agentRouter.createCaller({
|
|
473
|
+
tokenPayload: { agentId: 'test', sessionId: 's1' } as any,
|
|
474
|
+
});
|
|
443
475
|
const result = await caller.fetchPendingMessages();
|
|
444
476
|
expect(result.messages).toBe('');
|
|
445
477
|
});
|
|
@@ -11,6 +11,18 @@ vi.mock('../../shared/chats.js', () => ({
|
|
|
11
11
|
vi.mock('../../shared/workspace.js', () => ({
|
|
12
12
|
getWorkspaceRoot: vi.fn().mockReturnValue('/mock/workspace'),
|
|
13
13
|
getClawminiDir: vi.fn().mockReturnValue('/mock/.clawmini'),
|
|
14
|
+
readPolicies: vi.fn().mockResolvedValue({
|
|
15
|
+
policies: {
|
|
16
|
+
'test-cmd': {
|
|
17
|
+
command: 'echo',
|
|
18
|
+
autoApprove: false,
|
|
19
|
+
},
|
|
20
|
+
'auto-cmd': {
|
|
21
|
+
command: 'echo',
|
|
22
|
+
autoApprove: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
14
26
|
}));
|
|
15
27
|
|
|
16
28
|
vi.mock('../policy-request-service.js', () => {
|
|
@@ -46,11 +58,13 @@ vi.mock('node:fs/promises', async (importOriginal) => {
|
|
|
46
58
|
default: {
|
|
47
59
|
...actual,
|
|
48
60
|
readFile: mockReadFile,
|
|
61
|
+
writeFile: vi.fn(),
|
|
49
62
|
mkdir: vi.fn(),
|
|
50
63
|
readdir: vi.fn().mockResolvedValue([]),
|
|
51
64
|
realpath: vi.fn().mockImplementation((p) => Promise.resolve(p)),
|
|
52
65
|
},
|
|
53
66
|
readFile: mockReadFile,
|
|
67
|
+
writeFile: vi.fn(),
|
|
54
68
|
mkdir: vi.fn(),
|
|
55
69
|
readdir: vi.fn().mockResolvedValue([]),
|
|
56
70
|
realpath: vi.fn().mockImplementation((p) => Promise.resolve(p)),
|
|
@@ -95,8 +109,7 @@ describe('createPolicyRequest preview message', () => {
|
|
|
95
109
|
const logMsg = callArgs[1] as any;
|
|
96
110
|
|
|
97
111
|
expect(chatId).toBe('default-chat');
|
|
98
|
-
expect(logMsg.role).toBe('
|
|
99
|
-
expect(logMsg.command).toBe('policy-request');
|
|
112
|
+
expect(logMsg.role).toBe('policy');
|
|
100
113
|
|
|
101
114
|
// Assert preview content format
|
|
102
115
|
const content = logMsg.content;
|
|
@@ -110,4 +123,28 @@ describe('createPolicyRequest preview message', () => {
|
|
|
110
123
|
expect(content).toContain('File [file2]:\n' + 'A'.repeat(500) + '\n... (truncated)');
|
|
111
124
|
expect(content).toContain('Use /approve req-123 or /reject req-123 [reason]');
|
|
112
125
|
});
|
|
126
|
+
|
|
127
|
+
it('should create an auto-approved request and execute it immediately', async () => {
|
|
128
|
+
const caller = appRouter.createCaller({
|
|
129
|
+
isApiServer: true,
|
|
130
|
+
tokenPayload: { agentId: 'default', chatId: 'default-chat' },
|
|
131
|
+
} as any);
|
|
132
|
+
|
|
133
|
+
const result = await caller.createPolicyRequest({
|
|
134
|
+
commandName: 'auto-cmd',
|
|
135
|
+
args: ['hello'],
|
|
136
|
+
fileMappings: {},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(result.id).toBe('REQ-123');
|
|
140
|
+
expect(result.executionResult).toBeDefined();
|
|
141
|
+
|
|
142
|
+
expect(chats.appendMessage).toHaveBeenCalledTimes(1);
|
|
143
|
+
const callArgs = vi.mocked(chats.appendMessage).mock.calls[0]!;
|
|
144
|
+
const logMsg = callArgs[1] as any;
|
|
145
|
+
|
|
146
|
+
expect(logMsg.content).toContain('[Auto-approved] Policy auto-cmd was executed.');
|
|
147
|
+
expect(logMsg.role).toBe('policy');
|
|
148
|
+
expect(logMsg.status).toBe('approved');
|
|
149
|
+
});
|
|
113
150
|
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { agentRouter } from './agent-router.js';
|
|
3
|
+
import { daemonEvents, DAEMON_EVENT_MESSAGE_APPENDED } from '../events.js';
|
|
4
|
+
import * as workspace from '../../shared/workspace.js';
|
|
5
|
+
|
|
6
|
+
vi.mock('../../shared/workspace.js', () => ({
|
|
7
|
+
readChatSettings: vi.fn(),
|
|
8
|
+
updateChatSettings: vi.fn(),
|
|
9
|
+
getWorkspaceRoot: vi.fn().mockReturnValue('/mock/root'),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('../agent/chat-logger.js', () => ({
|
|
13
|
+
createChatLogger: vi.fn(() => ({
|
|
14
|
+
findLastMessage: vi.fn().mockResolvedValue({ role: 'log', content: 'Mock output' }),
|
|
15
|
+
})),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('subagentWait', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
daemonEvents.removeAllListeners();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should not miss events if subagent completes immediately after checkSubagentStatus starts', async () => {
|
|
28
|
+
// We simulate checkSubagentStatus taking some time (e.g. reading from disk).
|
|
29
|
+
// While it is awaiting, an event is emitted indicating completion.
|
|
30
|
+
// The wait procedure should still catch the completion.
|
|
31
|
+
|
|
32
|
+
const subagentId = 'sub-1';
|
|
33
|
+
const chatId = 'chat-1';
|
|
34
|
+
|
|
35
|
+
let firstCall = true;
|
|
36
|
+
|
|
37
|
+
vi.mocked(workspace.readChatSettings).mockImplementation(async (_id: string) => {
|
|
38
|
+
if (firstCall) {
|
|
39
|
+
firstCall = false;
|
|
40
|
+
// Emit the event while the first check is "in flight"
|
|
41
|
+
// This simulates the race condition where the status is "active" at the exact moment
|
|
42
|
+
// of reading, but changes immediately after before the event listener would be bound in the buggy code.
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, {
|
|
45
|
+
chatId,
|
|
46
|
+
message: { role: 'subagent_status', status: 'completed', subagentId },
|
|
47
|
+
});
|
|
48
|
+
}, 10);
|
|
49
|
+
|
|
50
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
51
|
+
return {
|
|
52
|
+
subagents: {
|
|
53
|
+
[subagentId]: { status: 'active', id: subagentId, createdAt: '2023-01-01' },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
} else {
|
|
57
|
+
return {
|
|
58
|
+
subagents: {
|
|
59
|
+
[subagentId]: { status: 'completed', id: subagentId, createdAt: '2023-01-01' },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const ctx = {
|
|
66
|
+
tokenPayload: { chatId, agentId: 'agent', sessionId: 'session' },
|
|
67
|
+
};
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
+
const caller = agentRouter.createCaller(ctx as any);
|
|
70
|
+
|
|
71
|
+
const resultPromise = caller.subagentWait({ subagentId });
|
|
72
|
+
|
|
73
|
+
// We don't want the test to hang if the bug is present, so we use Promise.race
|
|
74
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
75
|
+
setTimeout(() => reject(new Error('Timeout - event missed')), 500)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const result = await Promise.race([resultPromise, timeoutPromise]);
|
|
79
|
+
expect(result).toEqual({ status: 'completed', output: 'Mock output' });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should clean up the event listener if it returns early due to initial status check', async () => {
|
|
83
|
+
const subagentId = 'sub-2';
|
|
84
|
+
const chatId = 'chat-2';
|
|
85
|
+
|
|
86
|
+
// Mock an immediate completed status
|
|
87
|
+
vi.mocked(workspace.readChatSettings).mockResolvedValue({
|
|
88
|
+
subagents: {
|
|
89
|
+
[subagentId]: { status: 'completed', id: subagentId, createdAt: '2023-01-01' },
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const ctx = {
|
|
94
|
+
tokenPayload: { chatId, agentId: 'agent', sessionId: 'session' },
|
|
95
|
+
};
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
const caller = agentRouter.createCaller(ctx as any);
|
|
98
|
+
|
|
99
|
+
const initialListeners = daemonEvents.listenerCount(DAEMON_EVENT_MESSAGE_APPENDED);
|
|
100
|
+
|
|
101
|
+
const result = await caller.subagentWait({ subagentId });
|
|
102
|
+
|
|
103
|
+
expect(result).toEqual({ status: 'completed', output: 'Mock output' });
|
|
104
|
+
|
|
105
|
+
const finalListeners = daemonEvents.listenerCount(DAEMON_EVENT_MESSAGE_APPENDED);
|
|
106
|
+
expect(finalListeners).toBe(initialListeners); // Should not leave any lingering listeners
|
|
107
|
+
});
|
|
108
|
+
});
|