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
package/dist/daemon/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CronJobSchema, S as
|
|
2
|
-
import {
|
|
1
|
+
import { C as writeAgentSessionSettings, D as pathIsInsideDir, O as CronJobSchema, S as updateChatSettings, T as writeChatSettings, _ as readEnvironment, b as resolveAgentWorkDir, c as getClawminiDir, d as getSocketPath, f as getWorkspaceRoot, g as readChatSettings, h as readAgentSessionSettings, k as SettingsSchema, l as getEnvironmentPath, m as listAgents, o as getActiveEnvironmentInfo, s as getAgent, u as getSettingsPath, v as readPolicies, y as readSettings } from "../workspace-BJmJBfKi.mjs";
|
|
2
|
+
import { c as createChat, f as getDefaultChatId, h as listChats, n as exportLiteToEnvironment, p as getMessages$1, s as appendMessage$1, u as findLastMessage } from "../lite-CBxOT1y5.mjs";
|
|
3
3
|
import fs, { constants } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { execSync, spawn } from "node:child_process";
|
|
@@ -10,11 +10,11 @@ import net from "node:net";
|
|
|
10
10
|
import { createHTTPHandler } from "@trpc/server/adapters/standalone";
|
|
11
11
|
import { TRPCError, initTRPC } from "@trpc/server";
|
|
12
12
|
import schedule from "node-schedule";
|
|
13
|
-
import { EventEmitter, on } from "node:events";
|
|
14
13
|
import crypto$1, { randomBytes, randomUUID } from "node:crypto";
|
|
15
14
|
import fs$2 from "fs/promises";
|
|
16
15
|
import path$1 from "path";
|
|
17
16
|
import { randomInt } from "crypto";
|
|
17
|
+
import { EventEmitter, on } from "node:events";
|
|
18
18
|
|
|
19
19
|
//#region src/daemon/api/trpc.ts
|
|
20
20
|
const t = initTRPC.context().create();
|
|
@@ -34,107 +34,17 @@ const apiAuthMiddleware = t.middleware(({ ctx, next }) => {
|
|
|
34
34
|
});
|
|
35
35
|
const apiProcedure = t.procedure.use(apiAuthMiddleware);
|
|
36
36
|
|
|
37
|
-
//#endregion
|
|
38
|
-
//#region src/daemon/events.ts
|
|
39
|
-
const daemonEvents = new EventEmitter();
|
|
40
|
-
const DAEMON_EVENT_MESSAGE_APPENDED = "message-appended";
|
|
41
|
-
const DAEMON_EVENT_TYPING = "typing";
|
|
42
|
-
function emitMessageAppended(chatId, message) {
|
|
43
|
-
daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, {
|
|
44
|
-
chatId,
|
|
45
|
-
message
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
function emitTyping(chatId) {
|
|
49
|
-
daemonEvents.emit(DAEMON_EVENT_TYPING, { chatId });
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
//#endregion
|
|
53
|
-
//#region src/daemon/chats.ts
|
|
54
|
-
async function appendMessage(id, message, startDir = process.cwd()) {
|
|
55
|
-
await appendMessage$1(id, message, startDir);
|
|
56
|
-
emitMessageAppended(id, message);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/daemon/queue.ts
|
|
61
|
-
var Queue = class {
|
|
62
|
-
pending = [];
|
|
63
|
-
isRunning = false;
|
|
64
|
-
currentController = null;
|
|
65
|
-
currentPayload;
|
|
66
|
-
enqueue(task, payload) {
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
this.pending.push({
|
|
69
|
-
task,
|
|
70
|
-
payload,
|
|
71
|
-
resolve,
|
|
72
|
-
reject
|
|
73
|
-
});
|
|
74
|
-
this.processNext().catch(() => {});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
async processNext() {
|
|
78
|
-
if (this.isRunning || this.pending.length === 0) return;
|
|
79
|
-
this.isRunning = true;
|
|
80
|
-
const entry = this.pending.shift();
|
|
81
|
-
this.currentController = new AbortController();
|
|
82
|
-
this.currentPayload = entry.payload;
|
|
83
|
-
try {
|
|
84
|
-
await entry.task(this.currentController.signal);
|
|
85
|
-
entry.resolve();
|
|
86
|
-
} catch (error) {
|
|
87
|
-
entry.reject(error);
|
|
88
|
-
} finally {
|
|
89
|
-
this.isRunning = false;
|
|
90
|
-
this.currentController = null;
|
|
91
|
-
this.currentPayload = void 0;
|
|
92
|
-
this.processNext().catch(() => {});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
abortCurrent(predicate) {
|
|
96
|
-
if (this.currentController) {
|
|
97
|
-
if (!predicate || this.currentPayload !== void 0 && predicate(this.currentPayload)) {
|
|
98
|
-
const error = /* @__PURE__ */ new Error("Task aborted");
|
|
99
|
-
error.name = "AbortError";
|
|
100
|
-
this.currentController.abort(error);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
getCurrentPayload() {
|
|
105
|
-
return this.currentPayload;
|
|
106
|
-
}
|
|
107
|
-
clear(reason = "Task cleared", predicate) {
|
|
108
|
-
const tasksToClear = predicate ? this.pending.filter((p) => p.payload !== void 0 && predicate(p.payload)) : [...this.pending];
|
|
109
|
-
if (predicate) this.pending = this.pending.filter((p) => !(p.payload !== void 0 && predicate(p.payload)));
|
|
110
|
-
else this.pending = [];
|
|
111
|
-
for (const { reject } of tasksToClear) {
|
|
112
|
-
const error = new Error(reason);
|
|
113
|
-
error.name = "AbortError";
|
|
114
|
-
reject(error);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
extractPending(predicate) {
|
|
118
|
-
const extracted = this.pending.map((p) => p.payload).filter((p) => p !== void 0 && (!predicate || predicate(p)));
|
|
119
|
-
this.clear("Task extracted for batching", predicate);
|
|
120
|
-
return extracted;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
const messageQueues = /* @__PURE__ */ new Map();
|
|
124
|
-
function getMessageQueue(dir) {
|
|
125
|
-
if (!messageQueues.has(dir)) messageQueues.set(dir, new Queue());
|
|
126
|
-
return messageQueues.get(dir);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
37
|
//#endregion
|
|
130
38
|
//#region src/daemon/routers/slash-new.ts
|
|
131
39
|
function slashNew(state) {
|
|
132
40
|
if (/^\/new(\s|$)/.test(state.message)) {
|
|
133
41
|
const newMessage = state.message.replace(/^\/new(\s+|$)/, "").trim();
|
|
42
|
+
const id = crypto.randomUUID();
|
|
134
43
|
return {
|
|
135
44
|
...state,
|
|
136
45
|
message: newMessage,
|
|
137
|
-
sessionId:
|
|
46
|
+
sessionId: id,
|
|
47
|
+
nextSessionId: id,
|
|
138
48
|
reply: "[@clawmini/slash-new] Starting a new session..."
|
|
139
49
|
};
|
|
140
50
|
}
|
|
@@ -356,6 +266,16 @@ function executeSafe(command, args, options) {
|
|
|
356
266
|
});
|
|
357
267
|
});
|
|
358
268
|
}
|
|
269
|
+
async function executeRequest(request, policy, cwd) {
|
|
270
|
+
const interpolatedArgs = interpolateArgs([...policy.args || [], ...request.args], request.fileMappings);
|
|
271
|
+
const { stdout, stderr, exitCode } = await executeSafe(policy.command, interpolatedArgs, cwd ? { cwd } : void 0);
|
|
272
|
+
return {
|
|
273
|
+
stdout,
|
|
274
|
+
stderr,
|
|
275
|
+
exitCode,
|
|
276
|
+
commandStr: `${policy.command} ${interpolatedArgs.join(" ")}`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
359
279
|
async function generateRequestPreview(request) {
|
|
360
280
|
let previewContent = `Sandbox Policy Request: ${request.commandName}\n`;
|
|
361
281
|
previewContent += `ID: ${request.id}\n`;
|
|
@@ -374,6 +294,28 @@ async function generateRequestPreview(request) {
|
|
|
374
294
|
return previewContent;
|
|
375
295
|
}
|
|
376
296
|
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/daemon/events.ts
|
|
299
|
+
const daemonEvents = new EventEmitter();
|
|
300
|
+
const DAEMON_EVENT_MESSAGE_APPENDED = "message-appended";
|
|
301
|
+
const DAEMON_EVENT_TYPING = "typing";
|
|
302
|
+
function emitMessageAppended(chatId, message) {
|
|
303
|
+
daemonEvents.emit(DAEMON_EVENT_MESSAGE_APPENDED, {
|
|
304
|
+
chatId,
|
|
305
|
+
message
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function emitTyping(chatId) {
|
|
309
|
+
daemonEvents.emit(DAEMON_EVENT_TYPING, { chatId });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/daemon/chats.ts
|
|
314
|
+
async function appendMessage(id, message, startDir = process.cwd()) {
|
|
315
|
+
await appendMessage$1(id, message, startDir);
|
|
316
|
+
emitMessageAppended(id, message);
|
|
317
|
+
}
|
|
318
|
+
|
|
377
319
|
//#endregion
|
|
378
320
|
//#region src/daemon/routers/slash-policies.ts
|
|
379
321
|
async function loadAndValidateRequest(id, state) {
|
|
@@ -425,29 +367,30 @@ async function slashPolicies(state) {
|
|
|
425
367
|
reply: `Policy not found: ${req.commandName}`
|
|
426
368
|
};
|
|
427
369
|
req.state = "Approved";
|
|
370
|
+
const { stdout, stderr, exitCode } = await executeRequest(req, policy, getWorkspaceRoot());
|
|
371
|
+
req.executionResult = {
|
|
372
|
+
stdout,
|
|
373
|
+
stderr,
|
|
374
|
+
exitCode
|
|
375
|
+
};
|
|
428
376
|
await store.save(req);
|
|
429
|
-
const
|
|
430
|
-
const { stdout, stderr, exitCode } = await executeSafe(policy.command, interpolatedArgs, { cwd: getWorkspaceRoot() });
|
|
431
|
-
const commandStr = `${policy.command} ${interpolatedArgs.join(" ")}`;
|
|
377
|
+
const agentMessage = `Request ${id} approved.\n\n${wrapInHtml("stdout", stdout)}\n\n${wrapInHtml("stderr", stderr)}\n\nExit Code: ${exitCode}`;
|
|
432
378
|
const logMsg = {
|
|
433
379
|
id: randomUUID(),
|
|
434
380
|
messageId: state.messageId,
|
|
435
|
-
role: "
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
stdout,
|
|
381
|
+
role: "system",
|
|
382
|
+
event: "policy_approved",
|
|
383
|
+
displayRole: "user",
|
|
384
|
+
content: agentMessage,
|
|
440
385
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
441
|
-
|
|
442
|
-
cwd: getWorkspaceRoot(),
|
|
443
|
-
exitCode
|
|
386
|
+
...req.subagentId ? { subagentId: req.subagentId } : {}
|
|
444
387
|
};
|
|
445
388
|
await appendMessage(state.chatId, logMsg);
|
|
446
|
-
const agentMessage = `Request ${id} approved.\n\n${wrapInHtml("stdout", stdout)}\n\n${wrapInHtml("stderr", stderr)}\n\nExit Code: ${exitCode}`;
|
|
447
389
|
return {
|
|
448
390
|
...state,
|
|
449
391
|
message: agentMessage,
|
|
450
|
-
reply: `Approved request, running ${req.commandName}
|
|
392
|
+
reply: `Approved request, running ${req.commandName}`,
|
|
393
|
+
...req.subagentId ? { subagentId: req.subagentId } : {}
|
|
451
394
|
};
|
|
452
395
|
}
|
|
453
396
|
const rejectMatch = message.match(/^\/reject\s+([^\s]+)(?:\s+(.*))?/);
|
|
@@ -461,23 +404,33 @@ async function slashPolicies(state) {
|
|
|
461
404
|
req.state = "Rejected";
|
|
462
405
|
req.rejectionReason = reason;
|
|
463
406
|
await store.save(req);
|
|
407
|
+
const agentMessage = `Request ${id} rejected. Reason: ${reason}`;
|
|
464
408
|
const logMsg = {
|
|
465
409
|
id: randomUUID(),
|
|
466
410
|
messageId: state.messageId,
|
|
467
|
-
role: "
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
411
|
+
role: "system",
|
|
412
|
+
event: "policy_rejected",
|
|
413
|
+
displayRole: "user",
|
|
414
|
+
content: agentMessage,
|
|
471
415
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
416
|
+
...req.subagentId ? { subagentId: req.subagentId } : {}
|
|
417
|
+
};
|
|
418
|
+
const userNotificationMsg = {
|
|
419
|
+
id: randomUUID(),
|
|
420
|
+
messageId: state.messageId,
|
|
421
|
+
role: "system",
|
|
422
|
+
event: "policy_rejected",
|
|
423
|
+
displayRole: "agent",
|
|
424
|
+
content: agentMessage,
|
|
425
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
426
|
+
...req.subagentId ? { subagentId: req.subagentId } : {}
|
|
475
427
|
};
|
|
476
428
|
await appendMessage(state.chatId, logMsg);
|
|
477
|
-
|
|
429
|
+
await appendMessage(state.chatId, userNotificationMsg);
|
|
478
430
|
return {
|
|
479
431
|
...state,
|
|
480
|
-
message: agentMessage
|
|
432
|
+
message: agentMessage,
|
|
433
|
+
...req.subagentId ? { subagentId: req.subagentId } : {}
|
|
481
434
|
};
|
|
482
435
|
}
|
|
483
436
|
return state;
|
|
@@ -487,17 +440,130 @@ function wrapInHtml(tag, text) {
|
|
|
487
440
|
return `<${tag}>\n${text.trim()}\n</${tag}>`;
|
|
488
441
|
}
|
|
489
442
|
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/daemon/routers/session-timeout.ts
|
|
445
|
+
/**
|
|
446
|
+
* Router that automatically starts a new session after a period of inactivity.
|
|
447
|
+
*
|
|
448
|
+
* To register this router, add it to your `~/.gemini/settings.json`:
|
|
449
|
+
* ```json
|
|
450
|
+
* {
|
|
451
|
+
* "routers": [
|
|
452
|
+
* {
|
|
453
|
+
* "use": "session-timeout",
|
|
454
|
+
* "with": {
|
|
455
|
+
* "timeout": "60m",
|
|
456
|
+
* "prompt": "This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY."
|
|
457
|
+
* }
|
|
458
|
+
* }
|
|
459
|
+
* ]
|
|
460
|
+
* }
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
function createSessionTimeoutRouter(config = {}) {
|
|
464
|
+
const timeStr = config.timeout ?? "60m";
|
|
465
|
+
const prompt = config.prompt ?? "This chat session has ended. Save any important details from it to your memory. When finished, reply with NO_REPLY_NECESSARY.";
|
|
466
|
+
return function(state) {
|
|
467
|
+
if (state.env?.__SESSION_TIMEOUT__ === "true") return state;
|
|
468
|
+
const sessionId = state.sessionId || crypto.randomUUID();
|
|
469
|
+
const jobId = `__session_timeout__${sessionId}`;
|
|
470
|
+
const jobs = {
|
|
471
|
+
...state.jobs,
|
|
472
|
+
remove: [
|
|
473
|
+
...state.jobs?.remove || [],
|
|
474
|
+
jobId,
|
|
475
|
+
"__session_timeout__"
|
|
476
|
+
]
|
|
477
|
+
};
|
|
478
|
+
return {
|
|
479
|
+
...state,
|
|
480
|
+
sessionId,
|
|
481
|
+
jobs: {
|
|
482
|
+
...jobs,
|
|
483
|
+
add: [...jobs.add || [], {
|
|
484
|
+
id: jobId,
|
|
485
|
+
schedule: { at: timeStr },
|
|
486
|
+
message: prompt,
|
|
487
|
+
reply: "[@clawmini/session-timeout] Starting a fresh session...",
|
|
488
|
+
nextSessionId: randomUUID(),
|
|
489
|
+
session: {
|
|
490
|
+
type: "existing",
|
|
491
|
+
id: sessionId
|
|
492
|
+
},
|
|
493
|
+
env: { __SESSION_TIMEOUT__: "true" },
|
|
494
|
+
jobs: { remove: [jobId] }
|
|
495
|
+
}]
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
490
501
|
//#endregion
|
|
491
502
|
//#region src/daemon/routers.ts
|
|
503
|
+
const GLOBAL_ROUTERS = ["@clawmini/session-timeout"];
|
|
504
|
+
const USER_ROUTERS = [
|
|
505
|
+
"@clawmini/slash-new",
|
|
506
|
+
"@clawmini/slash-command",
|
|
507
|
+
"@clawmini/slash-stop",
|
|
508
|
+
"@clawmini/slash-interrupt",
|
|
509
|
+
"@clawmini/slash-policies"
|
|
510
|
+
];
|
|
511
|
+
function resolveRouters(userRouters, isUserMessage) {
|
|
512
|
+
const resolvedGlobals = [];
|
|
513
|
+
const resolvedUsers = [];
|
|
514
|
+
const userConfigMap = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const r of userRouters) {
|
|
516
|
+
const name = typeof r === "string" ? r : r.use;
|
|
517
|
+
const config = typeof r === "string" ? {} : r.with || {};
|
|
518
|
+
if (name.startsWith("@clawmini/")) userConfigMap.set(name, config);
|
|
519
|
+
else resolvedUsers.push(r);
|
|
520
|
+
}
|
|
521
|
+
for (const globalRouter of GLOBAL_ROUTERS) {
|
|
522
|
+
const name = typeof globalRouter === "string" ? globalRouter : globalRouter.use;
|
|
523
|
+
const baseConfig = typeof globalRouter === "string" ? {} : globalRouter.with || {};
|
|
524
|
+
const userConfig = userConfigMap.get(name) || {};
|
|
525
|
+
const mergedConfig = {
|
|
526
|
+
...baseConfig,
|
|
527
|
+
...userConfig
|
|
528
|
+
};
|
|
529
|
+
resolvedGlobals.push({
|
|
530
|
+
use: name,
|
|
531
|
+
with: mergedConfig
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const defaultUserRouters = [];
|
|
535
|
+
for (const defaultUserRouter of USER_ROUTERS) {
|
|
536
|
+
const name = typeof defaultUserRouter === "string" ? defaultUserRouter : defaultUserRouter.use;
|
|
537
|
+
const baseConfig = typeof defaultUserRouter === "string" ? {} : defaultUserRouter.with || {};
|
|
538
|
+
const userConfig = userConfigMap.get(name) || {};
|
|
539
|
+
const mergedConfig = {
|
|
540
|
+
...baseConfig,
|
|
541
|
+
...userConfig
|
|
542
|
+
};
|
|
543
|
+
defaultUserRouters.push({
|
|
544
|
+
use: name,
|
|
545
|
+
with: mergedConfig
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
if (isUserMessage) return [
|
|
549
|
+
...resolvedGlobals,
|
|
550
|
+
...defaultUserRouters,
|
|
551
|
+
...resolvedUsers
|
|
552
|
+
];
|
|
553
|
+
else return resolvedGlobals;
|
|
554
|
+
}
|
|
492
555
|
async function executeRouterPipeline(initialState, routers) {
|
|
493
556
|
let state = { ...initialState };
|
|
494
|
-
for (const
|
|
557
|
+
for (const routerDef of routers) {
|
|
495
558
|
if (state.action === "stop") break;
|
|
559
|
+
const router = typeof routerDef === "string" ? routerDef : routerDef.use;
|
|
560
|
+
const config = typeof routerDef === "string" ? {} : routerDef.with || {};
|
|
496
561
|
if (router === "@clawmini/slash-new") state = slashNew(state);
|
|
497
562
|
else if (router === "@clawmini/slash-command") state = await slashCommand(state);
|
|
498
563
|
else if (router === "@clawmini/slash-stop") state = slashStop(state);
|
|
499
564
|
else if (router === "@clawmini/slash-interrupt") state = slashInterrupt(state);
|
|
500
565
|
else if (router === "@clawmini/slash-policies") state = await slashPolicies(state);
|
|
566
|
+
else if (router === "@clawmini/session-timeout") state = createSessionTimeoutRouter(config)(state);
|
|
501
567
|
else try {
|
|
502
568
|
state = await executeCustomRouter(router, state);
|
|
503
569
|
} catch (err) {
|
|
@@ -563,6 +629,387 @@ async function executeCustomRouter(command, state) {
|
|
|
563
629
|
});
|
|
564
630
|
}
|
|
565
631
|
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/daemon/agent/agent-context.ts
|
|
634
|
+
function formatEnvironmentPrefix(prefix, replacements) {
|
|
635
|
+
const map = {
|
|
636
|
+
"{WORKSPACE_DIR}": replacements.targetPath,
|
|
637
|
+
"{AGENT_DIR}": replacements.executionCwd,
|
|
638
|
+
"{ENV_DIR}": replacements.envDir,
|
|
639
|
+
"{HOME_DIR}": process.env.HOME || "",
|
|
640
|
+
"{ENV_ARGS}": replacements.envArgs
|
|
641
|
+
};
|
|
642
|
+
return prefix.replace(/{(WORKSPACE_DIR|AGENT_DIR|ENV_DIR|HOME_DIR|ENV_ARGS)}/g, (match) => map[match] || match);
|
|
643
|
+
}
|
|
644
|
+
async function sandboxExecutionContext(initialCommand, env, agentSpecificEnvKeys, executionCwd, cwd) {
|
|
645
|
+
let command = initialCommand;
|
|
646
|
+
const activeEnvInfo = await getActiveEnvironmentInfo(executionCwd, cwd);
|
|
647
|
+
if (!activeEnvInfo) return command;
|
|
648
|
+
const activeEnvName = activeEnvInfo.name;
|
|
649
|
+
const activeEnv = await readEnvironment(activeEnvName, cwd);
|
|
650
|
+
if (activeEnv?.env) for (const [key, value] of Object.entries(activeEnv.env)) if (value === false) {
|
|
651
|
+
delete env[key];
|
|
652
|
+
agentSpecificEnvKeys.delete(key);
|
|
653
|
+
} else {
|
|
654
|
+
let interpolatedValue = String(value);
|
|
655
|
+
interpolatedValue = interpolatedValue.replace(/\{PATH\}/g, process.env.PATH || "");
|
|
656
|
+
interpolatedValue = interpolatedValue.replace(/\{ENV_DIR\}/g, getEnvironmentPath(activeEnvName, cwd));
|
|
657
|
+
interpolatedValue = interpolatedValue.replace(/\{WORKSPACE_DIR\}/g, activeEnvInfo.targetPath);
|
|
658
|
+
env[key] = interpolatedValue;
|
|
659
|
+
agentSpecificEnvKeys.add(key);
|
|
660
|
+
}
|
|
661
|
+
if (activeEnv?.prefix) {
|
|
662
|
+
const envArgs = Array.from(agentSpecificEnvKeys).map((key) => {
|
|
663
|
+
if (activeEnv.envFormat) return activeEnv.envFormat.replace("{key}", key);
|
|
664
|
+
return key;
|
|
665
|
+
}).join(" ");
|
|
666
|
+
const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, {
|
|
667
|
+
targetPath: activeEnvInfo.targetPath,
|
|
668
|
+
executionCwd,
|
|
669
|
+
envDir: getEnvironmentPath(activeEnvName, cwd),
|
|
670
|
+
envArgs
|
|
671
|
+
});
|
|
672
|
+
if (prefixReplaced.includes("{COMMAND}")) command = prefixReplaced.replace("{COMMAND}", command);
|
|
673
|
+
else command = `${prefixReplaced} ${command}`;
|
|
674
|
+
}
|
|
675
|
+
return command;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/daemon/agent/agent-extractors.ts
|
|
680
|
+
async function runExtractionCommand(name, command, runCommand, cwd, env, mainResult, signal) {
|
|
681
|
+
try {
|
|
682
|
+
console.log(`Executing extraction command (${name}): ${command}`);
|
|
683
|
+
const res = await runCommand({
|
|
684
|
+
command,
|
|
685
|
+
cwd,
|
|
686
|
+
env,
|
|
687
|
+
stdin: mainResult.stdout,
|
|
688
|
+
signal
|
|
689
|
+
});
|
|
690
|
+
if (res.exitCode === 0) return { result: res.stdout.trim() };
|
|
691
|
+
else return { error: `${name} failed: ${res.stderr}` };
|
|
692
|
+
} catch (e) {
|
|
693
|
+
return { error: `${name} error: ${e.message}` };
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async function extractMessageContent(context, mainResult, runCommand, executionCwd, signal) {
|
|
697
|
+
if (!context.currentAgent.commands?.getMessageContent) return {};
|
|
698
|
+
return runExtractionCommand("getMessageContent", context.currentAgent.commands.getMessageContent, runCommand, executionCwd, context.env, mainResult, signal);
|
|
699
|
+
}
|
|
700
|
+
async function extractSessionId(context, mainResult, runCommand, executionCwd, signal) {
|
|
701
|
+
if (!context.currentAgent.commands?.getSessionId) return {};
|
|
702
|
+
return runExtractionCommand("getSessionId", context.currentAgent.commands.getSessionId, runCommand, executionCwd, context.env, mainResult, signal);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/daemon/agent/utils.ts
|
|
707
|
+
function formatPendingMessages(payloads) {
|
|
708
|
+
return payloads.map((text) => `<message>\n${text}\n</message>`).join("\n\n");
|
|
709
|
+
}
|
|
710
|
+
function isNewSession(env) {
|
|
711
|
+
return env["SESSION_ID"] === void 0;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
//#endregion
|
|
715
|
+
//#region src/daemon/agent/agent-runner.ts
|
|
716
|
+
function calculateDelay(attempt, baseDelayMs, isFallback = false) {
|
|
717
|
+
const effectiveAttempt = isFallback ? attempt + 1 : attempt;
|
|
718
|
+
if (effectiveAttempt <= 0) return 0;
|
|
719
|
+
const delay = baseDelayMs * Math.pow(2, effectiveAttempt - 1);
|
|
720
|
+
return Math.min(delay, 15e3);
|
|
721
|
+
}
|
|
722
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
723
|
+
var AgentRunner = class {
|
|
724
|
+
constructor(session, runCommand) {
|
|
725
|
+
this.session = session;
|
|
726
|
+
this.runCommand = runCommand;
|
|
727
|
+
}
|
|
728
|
+
async withTypingIndicator(fn) {
|
|
729
|
+
const interval = setInterval(() => emitTyping(this.session.chatId), 5e3);
|
|
730
|
+
try {
|
|
731
|
+
return await fn();
|
|
732
|
+
} finally {
|
|
733
|
+
clearInterval(interval);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
*getExecutionAttempts() {
|
|
737
|
+
const executionConfigs = [{
|
|
738
|
+
fallback: void 0,
|
|
739
|
+
retries: 0,
|
|
740
|
+
delayMs: 1e3
|
|
741
|
+
}, ...(this.session.settings.fallbacks || []).map((f) => ({
|
|
742
|
+
fallback: f,
|
|
743
|
+
retries: f.retries,
|
|
744
|
+
delayMs: f.delayMs
|
|
745
|
+
}))];
|
|
746
|
+
for (let configIdx = 0; configIdx < executionConfigs.length; configIdx++) {
|
|
747
|
+
const config = executionConfigs[configIdx];
|
|
748
|
+
const isFallbackConfig = configIdx > 0;
|
|
749
|
+
for (let attempt = 0; attempt <= config.retries; attempt++) yield {
|
|
750
|
+
fallback: config.fallback,
|
|
751
|
+
delay: calculateDelay(attempt, config.delayMs, isFallbackConfig)
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
async executeSingleAttempt(message, fallback, signal) {
|
|
756
|
+
const context = await this.session.buildExecutionContext(message.content, message.env, fallback);
|
|
757
|
+
if (!context) return { success: false };
|
|
758
|
+
const mainResult = await this.withTypingIndicator(() => this.runCommand({
|
|
759
|
+
command: context.command,
|
|
760
|
+
cwd: this.session.workDirectory,
|
|
761
|
+
env: context.env,
|
|
762
|
+
signal
|
|
763
|
+
}));
|
|
764
|
+
let success = mainResult.exitCode === 0;
|
|
765
|
+
let finalContent = mainResult.stdout.trim();
|
|
766
|
+
const additonalErrors = [];
|
|
767
|
+
if (success && context.currentAgent.commands?.getMessageContent) {
|
|
768
|
+
const extraction = await extractMessageContent(context, mainResult, this.runCommand, this.session.workDirectory, signal);
|
|
769
|
+
if (extraction.error) additonalErrors.push(extraction.error);
|
|
770
|
+
if (extraction.result !== void 0) finalContent = extraction.result.trim();
|
|
771
|
+
if (!finalContent) success = false;
|
|
772
|
+
}
|
|
773
|
+
let extractedSessionId;
|
|
774
|
+
if (success && isNewSession(message.env) && context.currentAgent.commands?.getSessionId) {
|
|
775
|
+
const extraction = await extractSessionId(context, mainResult, this.runCommand, this.session.workDirectory, signal);
|
|
776
|
+
if (extraction.error) additonalErrors.push(extraction.error);
|
|
777
|
+
if (extraction.result) extractedSessionId = extraction.result;
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
success,
|
|
781
|
+
response: {
|
|
782
|
+
messageId: message.id,
|
|
783
|
+
content: finalContent,
|
|
784
|
+
command: context.command,
|
|
785
|
+
cwd: this.session.workDirectory,
|
|
786
|
+
extractedSessionId,
|
|
787
|
+
result: {
|
|
788
|
+
...mainResult,
|
|
789
|
+
stderr: [mainResult.stderr, ...additonalErrors].join("\n\n")
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async executeWithFallbacks(message, signal) {
|
|
795
|
+
let lastResponse;
|
|
796
|
+
for (const attempt of this.getExecutionAttempts()) {
|
|
797
|
+
if (attempt.delay > 0) {
|
|
798
|
+
await this.session.logger.logCommandRetry({
|
|
799
|
+
messageId: message.id,
|
|
800
|
+
content: `Error running agent, retrying in ${Math.round(attempt.delay / 1e3)} seconds...`,
|
|
801
|
+
cwd: this.session.workDirectory
|
|
802
|
+
});
|
|
803
|
+
await sleep(attempt.delay);
|
|
804
|
+
}
|
|
805
|
+
const attemptResult = await this.executeSingleAttempt(message, attempt.fallback, signal);
|
|
806
|
+
lastResponse = attemptResult.response || lastResponse;
|
|
807
|
+
if (attemptResult.success) return lastResponse;
|
|
808
|
+
}
|
|
809
|
+
return lastResponse;
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/daemon/utils/spawn.ts
|
|
815
|
+
const runCommand = async ({ command, cwd, env, stdin, signal }) => {
|
|
816
|
+
return new Promise((resolve, reject) => {
|
|
817
|
+
const p = spawn(command, {
|
|
818
|
+
shell: true,
|
|
819
|
+
cwd,
|
|
820
|
+
env,
|
|
821
|
+
signal
|
|
822
|
+
});
|
|
823
|
+
if (stdin && p.stdin) {
|
|
824
|
+
p.stdin.on("error", (err) => {
|
|
825
|
+
if (err.code !== "EPIPE") console.error("stdin error:", err);
|
|
826
|
+
});
|
|
827
|
+
p.stdin.write(stdin);
|
|
828
|
+
p.stdin.end();
|
|
829
|
+
}
|
|
830
|
+
let stdout = "";
|
|
831
|
+
let stderr = "";
|
|
832
|
+
if (p.stdout) p.stdout.on("data", (data) => {
|
|
833
|
+
stdout += data.toString();
|
|
834
|
+
if (!stdin) process.stdout.write(data);
|
|
835
|
+
});
|
|
836
|
+
if (p.stderr) p.stderr.on("data", (data) => {
|
|
837
|
+
stderr += data.toString();
|
|
838
|
+
if (!stdin) process.stderr.write(data);
|
|
839
|
+
});
|
|
840
|
+
p.on("close", (code) => {
|
|
841
|
+
resolve({
|
|
842
|
+
stdout,
|
|
843
|
+
stderr,
|
|
844
|
+
exitCode: code ?? 1
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
p.on("error", (err) => {
|
|
848
|
+
if (err.name === "AbortError") {
|
|
849
|
+
reject(err);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
resolve({
|
|
853
|
+
stdout: "",
|
|
854
|
+
stderr: err.toString(),
|
|
855
|
+
exitCode: 1
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
//#endregion
|
|
862
|
+
//#region src/daemon/agent/chat-logger.ts
|
|
863
|
+
function createChatLogger(chatId, subagentId) {
|
|
864
|
+
async function append(msg) {
|
|
865
|
+
const finalMsg = subagentId ? {
|
|
866
|
+
...msg,
|
|
867
|
+
subagentId
|
|
868
|
+
} : msg;
|
|
869
|
+
await appendMessage(chatId, finalMsg);
|
|
870
|
+
return finalMsg;
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
append,
|
|
874
|
+
getMessages: async (limit) => {
|
|
875
|
+
let filtered = (await getMessages$1(chatId)).filter((m) => m.subagentId === subagentId);
|
|
876
|
+
if (limit !== void 0 && limit > 0) filtered = filtered.slice(-limit);
|
|
877
|
+
return filtered;
|
|
878
|
+
},
|
|
879
|
+
findLastMessage: async (predicate) => {
|
|
880
|
+
return findLastMessage(chatId, (msg) => {
|
|
881
|
+
if (msg.subagentId !== subagentId) return false;
|
|
882
|
+
return predicate(msg);
|
|
883
|
+
});
|
|
884
|
+
},
|
|
885
|
+
logUserMessage: async (msg) => append({
|
|
886
|
+
id: crypto.randomUUID(),
|
|
887
|
+
role: "user",
|
|
888
|
+
content: msg,
|
|
889
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
890
|
+
}),
|
|
891
|
+
logCommandResult: async ({ messageId, content, command, cwd, result }) => append({
|
|
892
|
+
id: crypto.randomUUID(),
|
|
893
|
+
role: "command",
|
|
894
|
+
content,
|
|
895
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
896
|
+
messageId,
|
|
897
|
+
command,
|
|
898
|
+
cwd,
|
|
899
|
+
stdout: result.stdout,
|
|
900
|
+
stderr: result.stderr,
|
|
901
|
+
exitCode: result.exitCode
|
|
902
|
+
}),
|
|
903
|
+
logSystemEvent: async ({ content }) => append({
|
|
904
|
+
id: crypto.randomUUID(),
|
|
905
|
+
role: "command",
|
|
906
|
+
content,
|
|
907
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
908
|
+
messageId: crypto.randomUUID(),
|
|
909
|
+
stderr: "",
|
|
910
|
+
command: "",
|
|
911
|
+
cwd: "",
|
|
912
|
+
stdout: "",
|
|
913
|
+
exitCode: 0
|
|
914
|
+
}),
|
|
915
|
+
logAutomaticReply: async ({ messageId, content }) => append({
|
|
916
|
+
id: crypto.randomUUID(),
|
|
917
|
+
role: "system",
|
|
918
|
+
content,
|
|
919
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
920
|
+
messageId,
|
|
921
|
+
event: "router",
|
|
922
|
+
displayRole: "agent"
|
|
923
|
+
}),
|
|
924
|
+
logCommandRetry: async ({ messageId, content, cwd }) => append({
|
|
925
|
+
id: crypto.randomUUID(),
|
|
926
|
+
role: "command",
|
|
927
|
+
content,
|
|
928
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
929
|
+
messageId,
|
|
930
|
+
command: "retry-delay",
|
|
931
|
+
stderr: "",
|
|
932
|
+
stdout: "",
|
|
933
|
+
cwd,
|
|
934
|
+
exitCode: 0
|
|
935
|
+
}),
|
|
936
|
+
logSystemMessage: async ({ content, event, messageId, displayRole }) => {
|
|
937
|
+
const msg = {
|
|
938
|
+
id: crypto.randomUUID(),
|
|
939
|
+
role: "system",
|
|
940
|
+
content,
|
|
941
|
+
event,
|
|
942
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
943
|
+
};
|
|
944
|
+
if (messageId !== void 0) msg.messageId = messageId;
|
|
945
|
+
if (displayRole !== void 0) msg.displayRole = displayRole;
|
|
946
|
+
return append(msg);
|
|
947
|
+
},
|
|
948
|
+
logSubagentStatus: async ({ subagentId: targetSubagentId, status }) => {
|
|
949
|
+
return append({
|
|
950
|
+
id: crypto.randomUUID(),
|
|
951
|
+
role: "subagent_status",
|
|
952
|
+
content: `Subagent ${status}`,
|
|
953
|
+
subagentId: targetSubagentId,
|
|
954
|
+
status,
|
|
955
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
956
|
+
});
|
|
957
|
+
},
|
|
958
|
+
logAgentReply: async ({ content, files }) => {
|
|
959
|
+
const msg = {
|
|
960
|
+
id: crypto.randomUUID(),
|
|
961
|
+
role: "agent",
|
|
962
|
+
content,
|
|
963
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
964
|
+
};
|
|
965
|
+
if (files !== void 0) msg.files = files;
|
|
966
|
+
return append(msg);
|
|
967
|
+
},
|
|
968
|
+
logToolMessage: async ({ content, messageId, name, payload }) => {
|
|
969
|
+
return append({
|
|
970
|
+
id: crypto.randomUUID(),
|
|
971
|
+
role: "tool",
|
|
972
|
+
content,
|
|
973
|
+
messageId,
|
|
974
|
+
name,
|
|
975
|
+
payload,
|
|
976
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
977
|
+
});
|
|
978
|
+
},
|
|
979
|
+
logPolicyRequestMessage: async ({ content, messageId, requestId, commandName, args, status }) => {
|
|
980
|
+
return append({
|
|
981
|
+
id: crypto.randomUUID(),
|
|
982
|
+
role: "policy",
|
|
983
|
+
content,
|
|
984
|
+
messageId,
|
|
985
|
+
requestId,
|
|
986
|
+
commandName,
|
|
987
|
+
args,
|
|
988
|
+
status,
|
|
989
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
//#endregion
|
|
996
|
+
//#region src/shared/utils/env.ts
|
|
997
|
+
function applyEnvOverrides(targetEnv, overrides) {
|
|
998
|
+
if (!overrides) return;
|
|
999
|
+
for (const [key, val] of Object.entries(overrides)) if (val === true && process.env[key] !== void 0) targetEnv[key] = process.env[key];
|
|
1000
|
+
else if (typeof val === "string") targetEnv[key] = val;
|
|
1001
|
+
}
|
|
1002
|
+
function getActiveEnvKeys(...envs) {
|
|
1003
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1004
|
+
for (const env of envs) {
|
|
1005
|
+
if (!env) continue;
|
|
1006
|
+
Object.entries(env).forEach(([key, val]) => {
|
|
1007
|
+
if (val === true || typeof val === "string") keys.add(key);
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
return keys;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
566
1013
|
//#endregion
|
|
567
1014
|
//#region src/daemon/auth.ts
|
|
568
1015
|
const DAEMON_SECRET = crypto$1.randomBytes(32);
|
|
@@ -608,315 +1055,387 @@ function getApiContext(settings) {
|
|
|
608
1055
|
}
|
|
609
1056
|
|
|
610
1057
|
//#endregion
|
|
611
|
-
//#region src/
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
1058
|
+
//#region src/daemon/agent/task-scheduler.ts
|
|
1059
|
+
var ResourceLock = class {
|
|
1060
|
+
resources = /* @__PURE__ */ new Map();
|
|
1061
|
+
async acquire(resourceId, workspaceId, signal) {
|
|
1062
|
+
if (signal?.aborted) {
|
|
1063
|
+
const error = /* @__PURE__ */ new Error("Task aborted");
|
|
1064
|
+
error.name = "AbortError";
|
|
1065
|
+
throw error;
|
|
1066
|
+
}
|
|
1067
|
+
const res = this.resources.get(resourceId);
|
|
1068
|
+
if (!res) {
|
|
1069
|
+
this.resources.set(resourceId, {
|
|
1070
|
+
activeWorkspace: workspaceId,
|
|
1071
|
+
count: 1,
|
|
1072
|
+
waiters: []
|
|
1073
|
+
});
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (res.activeWorkspace === workspaceId) {
|
|
1077
|
+
res.count++;
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
return new Promise((resolve, reject) => {
|
|
1081
|
+
const waiter = {
|
|
1082
|
+
workspaceId,
|
|
1083
|
+
resolve,
|
|
1084
|
+
reject
|
|
1085
|
+
};
|
|
1086
|
+
res.waiters.push(waiter);
|
|
1087
|
+
if (signal) {
|
|
1088
|
+
const onAbort = () => {
|
|
1089
|
+
const idx = res.waiters.indexOf(waiter);
|
|
1090
|
+
if (idx !== -1) {
|
|
1091
|
+
res.waiters.splice(idx, 1);
|
|
1092
|
+
const error = /* @__PURE__ */ new Error("Task aborted");
|
|
1093
|
+
error.name = "AbortError";
|
|
1094
|
+
reject(error);
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
signal.addEventListener("abort", onAbort);
|
|
1098
|
+
waiter.resolve = () => {
|
|
1099
|
+
signal.removeEventListener("abort", onAbort);
|
|
1100
|
+
resolve();
|
|
1101
|
+
};
|
|
1102
|
+
waiter.reject = (err) => {
|
|
1103
|
+
signal.removeEventListener("abort", onAbort);
|
|
1104
|
+
reject(err);
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
623
1107
|
});
|
|
624
1108
|
}
|
|
625
|
-
|
|
626
|
-
|
|
1109
|
+
release(resourceId, _workspaceId) {
|
|
1110
|
+
const res = this.resources.get(resourceId);
|
|
1111
|
+
if (!res) return;
|
|
1112
|
+
res.count--;
|
|
1113
|
+
if (res.count === 0) if (res.waiters.length > 0) {
|
|
1114
|
+
const nextWorkspace = res.waiters[0].workspaceId;
|
|
1115
|
+
res.activeWorkspace = nextWorkspace;
|
|
1116
|
+
const remainingWaiters = [];
|
|
1117
|
+
for (const waiter of res.waiters) if (waiter.workspaceId === nextWorkspace) {
|
|
1118
|
+
res.count++;
|
|
1119
|
+
waiter.resolve();
|
|
1120
|
+
} else remainingWaiters.push(waiter);
|
|
1121
|
+
res.waiters = remainingWaiters;
|
|
1122
|
+
} else this.resources.delete(resourceId);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
var TaskQueue = class {
|
|
1126
|
+
queue = [];
|
|
1127
|
+
activeTask = null;
|
|
1128
|
+
isProcessing = false;
|
|
1129
|
+
constructor(sessionId, resourceLock, onEmpty) {
|
|
1130
|
+
this.sessionId = sessionId;
|
|
1131
|
+
this.resourceLock = resourceLock;
|
|
1132
|
+
this.onEmpty = onEmpty;
|
|
1133
|
+
}
|
|
1134
|
+
enqueue(task) {
|
|
1135
|
+
return new Promise((resolve, reject) => {
|
|
1136
|
+
this.queue.push({
|
|
1137
|
+
task,
|
|
1138
|
+
resolve,
|
|
1139
|
+
reject
|
|
1140
|
+
});
|
|
1141
|
+
this.process();
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
async process() {
|
|
1145
|
+
if (this.isProcessing || this.activeTask || this.queue.length === 0) return;
|
|
1146
|
+
this.isProcessing = true;
|
|
1147
|
+
while (this.queue.length > 0) {
|
|
1148
|
+
const next = this.queue.shift();
|
|
1149
|
+
const controller = new AbortController();
|
|
1150
|
+
this.activeTask = {
|
|
1151
|
+
task: next.task,
|
|
1152
|
+
controller
|
|
1153
|
+
};
|
|
1154
|
+
let acquired = false;
|
|
1155
|
+
try {
|
|
1156
|
+
await this.resourceLock.acquire(next.task.dirPath, next.task.rootChatId, controller.signal);
|
|
1157
|
+
acquired = true;
|
|
1158
|
+
if (!controller.signal.aborted) await next.task.execute(controller.signal);
|
|
1159
|
+
next.resolve();
|
|
1160
|
+
} catch (err) {
|
|
1161
|
+
next.reject(err);
|
|
1162
|
+
} finally {
|
|
1163
|
+
if (acquired) this.resourceLock.release(next.task.dirPath, next.task.rootChatId);
|
|
1164
|
+
this.activeTask = null;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
this.isProcessing = false;
|
|
1168
|
+
this.onEmpty(this.sessionId);
|
|
1169
|
+
}
|
|
1170
|
+
abortAll() {
|
|
1171
|
+
const error = /* @__PURE__ */ new Error("Task aborted");
|
|
1172
|
+
error.name = "AbortError";
|
|
1173
|
+
if (this.activeTask) this.activeTask.controller.abort(error);
|
|
1174
|
+
for (const qTask of this.queue) qTask.reject(error);
|
|
1175
|
+
this.queue = [];
|
|
1176
|
+
}
|
|
1177
|
+
interruptAndExtract() {
|
|
1178
|
+
const payloads = [];
|
|
1179
|
+
const error = /* @__PURE__ */ new Error("Task aborted");
|
|
1180
|
+
error.name = "AbortError";
|
|
1181
|
+
if (this.activeTask) {
|
|
1182
|
+
if (this.activeTask.task.text !== void 0) payloads.push(this.activeTask.task.text);
|
|
1183
|
+
this.activeTask.controller.abort(error);
|
|
1184
|
+
}
|
|
1185
|
+
for (const qTask of this.queue) {
|
|
1186
|
+
if (qTask.task.text !== void 0) payloads.push(qTask.task.text);
|
|
1187
|
+
qTask.reject(error);
|
|
1188
|
+
}
|
|
1189
|
+
this.queue = [];
|
|
1190
|
+
return payloads;
|
|
1191
|
+
}
|
|
1192
|
+
extractPending() {
|
|
1193
|
+
const payloads = [];
|
|
1194
|
+
const error = /* @__PURE__ */ new Error("Task extracted for batching");
|
|
1195
|
+
error.name = "AbortError";
|
|
1196
|
+
for (const qTask of this.queue) {
|
|
1197
|
+
if (qTask.task.text !== void 0) payloads.push(qTask.task.text);
|
|
1198
|
+
qTask.reject(error);
|
|
1199
|
+
}
|
|
1200
|
+
this.queue = [];
|
|
1201
|
+
return payloads;
|
|
1202
|
+
}
|
|
1203
|
+
hasTasks() {
|
|
1204
|
+
return this.activeTask !== null || this.queue.length > 0;
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
var TaskScheduler = class {
|
|
1208
|
+
queues = /* @__PURE__ */ new Map();
|
|
1209
|
+
resourceLock = new ResourceLock();
|
|
1210
|
+
getQueueKey(sessionId, rootChatId) {
|
|
1211
|
+
return `${rootChatId}:${sessionId}`;
|
|
1212
|
+
}
|
|
1213
|
+
schedule(task) {
|
|
1214
|
+
const key = this.getQueueKey(task.sessionId, task.rootChatId);
|
|
1215
|
+
let queue = this.queues.get(key);
|
|
1216
|
+
if (!queue) {
|
|
1217
|
+
queue = new TaskQueue(task.sessionId, this.resourceLock, () => {
|
|
1218
|
+
this.queues.delete(key);
|
|
1219
|
+
});
|
|
1220
|
+
this.queues.set(key, queue);
|
|
1221
|
+
}
|
|
1222
|
+
return queue.enqueue(task);
|
|
1223
|
+
}
|
|
1224
|
+
hasTasks(sessionId) {
|
|
1225
|
+
for (const queue of this.queues.values()) if (queue.sessionId === sessionId && queue.hasTasks()) return true;
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
extractPending(sessionId) {
|
|
1229
|
+
const payloads = [];
|
|
1230
|
+
for (const queue of this.queues.values()) if (queue.sessionId === sessionId) payloads.push(...queue.extractPending());
|
|
1231
|
+
return payloads;
|
|
1232
|
+
}
|
|
1233
|
+
abortTasks(sessionId) {
|
|
1234
|
+
for (const queue of this.queues.values()) if (queue.sessionId === sessionId) queue.abortAll();
|
|
1235
|
+
}
|
|
1236
|
+
interruptTasks(sessionId) {
|
|
1237
|
+
const payloads = [];
|
|
1238
|
+
for (const queue of this.queues.values()) if (queue.sessionId === sessionId) payloads.push(...queue.interruptAndExtract());
|
|
1239
|
+
return payloads;
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
const taskScheduler = new TaskScheduler();
|
|
627
1243
|
|
|
628
1244
|
//#endregion
|
|
629
|
-
//#region src/daemon/
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
agentId,
|
|
649
|
-
targetSessionId,
|
|
650
|
-
agentSessionSettings,
|
|
651
|
-
isNewSession: !agentSessionSettings
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
function prepareCommandAndEnv(agent, message, isNewSession, agentSessionSettings, fallback) {
|
|
655
|
-
const currentAgent = {
|
|
656
|
-
...agent,
|
|
657
|
-
commands: {
|
|
658
|
-
...agent.commands,
|
|
659
|
-
...fallback?.commands || {}
|
|
660
|
-
},
|
|
661
|
-
env: {
|
|
662
|
-
...agent.env,
|
|
663
|
-
...fallback?.env || {}
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
let command = currentAgent.commands?.new ?? "";
|
|
667
|
-
const env = {
|
|
668
|
-
...process.env,
|
|
669
|
-
CLAW_CLI_MESSAGE: message
|
|
670
|
-
};
|
|
671
|
-
applyEnvOverrides(env, currentAgent.env);
|
|
672
|
-
if (!isNewSession && currentAgent.commands?.append) {
|
|
673
|
-
command = currentAgent.commands.append;
|
|
674
|
-
applyEnvOverrides(env, agentSessionSettings?.env);
|
|
1245
|
+
//#region src/daemon/agent/agent-session.ts
|
|
1246
|
+
var AgentSession = class {
|
|
1247
|
+
agentId;
|
|
1248
|
+
sessionId;
|
|
1249
|
+
chatId;
|
|
1250
|
+
subagentId;
|
|
1251
|
+
settings;
|
|
1252
|
+
workspaceRoot;
|
|
1253
|
+
globalSettings;
|
|
1254
|
+
logger;
|
|
1255
|
+
constructor(config) {
|
|
1256
|
+
this.agentId = config.agentId;
|
|
1257
|
+
this.sessionId = config.sessionId;
|
|
1258
|
+
this.chatId = config.chatId;
|
|
1259
|
+
this.subagentId = config.subagentId;
|
|
1260
|
+
this.settings = config.settings;
|
|
1261
|
+
this.workspaceRoot = config.workspaceRoot;
|
|
1262
|
+
this.globalSettings = config.globalSettings;
|
|
1263
|
+
this.logger = config.logger ?? createChatLogger(this.chatId, this.subagentId);
|
|
675
1264
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1265
|
+
async buildExecutionContext(messageContent, routerEnv, fallback) {
|
|
1266
|
+
const currentAgent = {
|
|
1267
|
+
...this.settings,
|
|
1268
|
+
commands: {
|
|
1269
|
+
...this.settings.commands,
|
|
1270
|
+
...fallback?.commands || {}
|
|
1271
|
+
},
|
|
1272
|
+
env: {
|
|
1273
|
+
...this.settings.env,
|
|
1274
|
+
...this.subagentId && this.settings.subagentEnv ? this.settings.subagentEnv : {},
|
|
1275
|
+
...fallback?.env || {}
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
let initialCommand = currentAgent.commands?.new ?? "";
|
|
1279
|
+
const env = {
|
|
1280
|
+
...process.env,
|
|
1281
|
+
CLAW_CLI_MESSAGE: messageContent
|
|
1282
|
+
};
|
|
1283
|
+
applyEnvOverrides(env, currentAgent.env);
|
|
1284
|
+
if (!isNewSession(routerEnv) && currentAgent.commands?.append) initialCommand = currentAgent.commands.append;
|
|
1285
|
+
if (!initialCommand) return null;
|
|
1286
|
+
const agentSpecificEnvKeys = getActiveEnvKeys(currentAgent.env);
|
|
1287
|
+
agentSpecificEnvKeys.add("CLAW_CLI_MESSAGE");
|
|
1288
|
+
Object.assign(env, routerEnv);
|
|
1289
|
+
Object.keys(routerEnv).forEach((k) => agentSpecificEnvKeys.add(k));
|
|
1290
|
+
const apiCtx = getApiContext(this.globalSettings);
|
|
1291
|
+
if (apiCtx) {
|
|
1292
|
+
const proxyUrl = apiCtx.proxy_host ? `${apiCtx.proxy_host}:${apiCtx.port}` : `http://${apiCtx.host}:${apiCtx.port}`;
|
|
1293
|
+
if (currentAgent.apiUrlEnvVar) {
|
|
1294
|
+
env[currentAgent.apiUrlEnvVar] = proxyUrl;
|
|
1295
|
+
agentSpecificEnvKeys.add(currentAgent.apiUrlEnvVar);
|
|
1296
|
+
env["CLAW_LITE_URL_VAR"] = currentAgent.apiUrlEnvVar;
|
|
1297
|
+
agentSpecificEnvKeys.add("CLAW_LITE_URL_VAR");
|
|
1298
|
+
} else {
|
|
1299
|
+
env["CLAW_API_URL"] = proxyUrl;
|
|
1300
|
+
agentSpecificEnvKeys.add("CLAW_API_URL");
|
|
1301
|
+
}
|
|
1302
|
+
const token = generateToken({
|
|
1303
|
+
chatId: this.chatId,
|
|
1304
|
+
agentId: this.agentId,
|
|
1305
|
+
sessionId: this.sessionId,
|
|
1306
|
+
...this.subagentId ? { subagentId: this.subagentId } : {},
|
|
1307
|
+
timestamp: Date.now()
|
|
1308
|
+
});
|
|
1309
|
+
if (currentAgent.apiTokenEnvVar) {
|
|
1310
|
+
env[currentAgent.apiTokenEnvVar] = token;
|
|
1311
|
+
agentSpecificEnvKeys.add(currentAgent.apiTokenEnvVar);
|
|
1312
|
+
env["CLAW_LITE_API_VAR"] = currentAgent.apiTokenEnvVar;
|
|
1313
|
+
agentSpecificEnvKeys.add("CLAW_LITE_API_VAR");
|
|
1314
|
+
} else {
|
|
1315
|
+
env["CLAW_API_TOKEN"] = token;
|
|
1316
|
+
agentSpecificEnvKeys.add("CLAW_API_TOKEN");
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
let command = initialCommand;
|
|
1320
|
+
command = await sandboxExecutionContext(command, env, agentSpecificEnvKeys, this.workDirectory, this.workspaceRoot);
|
|
1321
|
+
return {
|
|
686
1322
|
command,
|
|
687
|
-
cwd,
|
|
688
1323
|
env,
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
});
|
|
692
|
-
if (res.exitCode === 0) return { result: res.stdout.trim() };
|
|
693
|
-
else return { error: `${name} failed: ${res.stderr}` };
|
|
694
|
-
} catch (e) {
|
|
695
|
-
return { error: `${name} error: ${e.message}` };
|
|
1324
|
+
currentAgent
|
|
1325
|
+
};
|
|
696
1326
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
* Formats the environment prefix string by replacing placeholders with actual values.
|
|
700
|
-
* Available placeholders:
|
|
701
|
-
* - {WORKSPACE_DIR}: The root directory of the workspace.
|
|
702
|
-
* - {AGENT_DIR}: The directory where the agent is executing.
|
|
703
|
-
* - {ENV_DIR}: The directory of the active environment.
|
|
704
|
-
* - {HOME_DIR}: The home directory of the current user.
|
|
705
|
-
* - {ENV_ARGS}: The formatted environment arguments based on envFormat.
|
|
706
|
-
*/
|
|
707
|
-
function formatEnvironmentPrefix(prefix, replacements) {
|
|
708
|
-
const map = {
|
|
709
|
-
"{WORKSPACE_DIR}": replacements.targetPath,
|
|
710
|
-
"{AGENT_DIR}": replacements.executionCwd,
|
|
711
|
-
"{ENV_DIR}": replacements.envDir,
|
|
712
|
-
"{HOME_DIR}": process.env.HOME || "",
|
|
713
|
-
"{ENV_ARGS}": replacements.envArgs
|
|
714
|
-
};
|
|
715
|
-
return prefix.replace(/{(WORKSPACE_DIR|AGENT_DIR|ENV_DIR|HOME_DIR|ENV_ARGS)}/g, (match) => map[match] || match);
|
|
716
|
-
}
|
|
717
|
-
async function executeDirectMessage(chatId, state, settings, cwd, runCommand, noWait = false, userMessageContent) {
|
|
718
|
-
const userMsg = {
|
|
719
|
-
id: state.messageId ?? crypto.randomUUID(),
|
|
720
|
-
role: "user",
|
|
721
|
-
content: userMessageContent ?? state.message,
|
|
722
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
723
|
-
};
|
|
724
|
-
await appendMessage(chatId, userMsg);
|
|
725
|
-
if (state.reply) await appendMessage(chatId, {
|
|
726
|
-
id: crypto.randomUUID(),
|
|
727
|
-
messageId: userMsg.id,
|
|
728
|
-
role: "log",
|
|
729
|
-
source: "router",
|
|
730
|
-
content: state.reply,
|
|
731
|
-
stderr: "",
|
|
732
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
733
|
-
command: "router",
|
|
734
|
-
cwd,
|
|
735
|
-
exitCode: 0,
|
|
736
|
-
...state.reply.includes("NO_REPLY_NECESSARY") ? { level: "verbose" } : {}
|
|
737
|
-
});
|
|
738
|
-
if (!state.message.trim() && state.action !== "stop" && state.action !== "interrupt") return;
|
|
739
|
-
const queue = getMessageQueue(cwd);
|
|
740
|
-
if (state.action === "stop") {
|
|
741
|
-
queue.abortCurrent();
|
|
742
|
-
queue.clear();
|
|
743
|
-
return;
|
|
1327
|
+
createRunner() {
|
|
1328
|
+
return new AgentRunner(this, runCommand);
|
|
744
1329
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const payloads =
|
|
753
|
-
if (payloads.length > 0)
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const { agentId, agentSessionSettings, isNewSession, targetSessionId: finalSessionId } = await resolveSessionState(chatId, cwd, state.sessionId, state.agentId);
|
|
759
|
-
let mergedAgent = settings?.defaultAgent || {};
|
|
760
|
-
if (agentId !== "default") try {
|
|
761
|
-
const customAgent = await getAgent(agentId, cwd);
|
|
762
|
-
if (customAgent) mergedAgent = {
|
|
763
|
-
...mergedAgent,
|
|
764
|
-
...customAgent,
|
|
765
|
-
commands: {
|
|
766
|
-
...mergedAgent.commands,
|
|
767
|
-
...customAgent.commands
|
|
768
|
-
},
|
|
769
|
-
env: {
|
|
770
|
-
...mergedAgent.env,
|
|
771
|
-
...customAgent.env
|
|
772
|
-
}
|
|
1330
|
+
get workDirectory() {
|
|
1331
|
+
return resolveAgentWorkDir(this.agentId, this.settings.directory, this.workspaceRoot);
|
|
1332
|
+
}
|
|
1333
|
+
stop() {
|
|
1334
|
+
taskScheduler.abortTasks(this.sessionId);
|
|
1335
|
+
}
|
|
1336
|
+
interrupt(message) {
|
|
1337
|
+
const payloads = taskScheduler.interruptTasks(this.sessionId);
|
|
1338
|
+
if (payloads.length > 0) {
|
|
1339
|
+
const pendingText = formatPendingMessages(payloads);
|
|
1340
|
+
return {
|
|
1341
|
+
...message,
|
|
1342
|
+
content: `${pendingText}\n\n<message>\n${message.content}\n</message>`.trim()
|
|
773
1343
|
};
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
const activeEnvInfo = await getActiveEnvironmentInfo(executionCwd, cwd);
|
|
828
|
-
if (activeEnvInfo) {
|
|
829
|
-
const activeEnvName = activeEnvInfo.name;
|
|
830
|
-
const activeEnv = await readEnvironment(activeEnvName, cwd);
|
|
831
|
-
if (activeEnv?.env) for (const [key, value] of Object.entries(activeEnv.env)) if (value === false) {
|
|
832
|
-
delete env[key];
|
|
833
|
-
agentSpecificEnv.delete(key);
|
|
834
|
-
} else {
|
|
835
|
-
let interpolatedValue = String(value);
|
|
836
|
-
interpolatedValue = interpolatedValue.replace(/\{PATH\}/g, process.env.PATH || "");
|
|
837
|
-
interpolatedValue = interpolatedValue.replace(/\{ENV_DIR\}/g, getEnvironmentPath(activeEnvName, cwd));
|
|
838
|
-
interpolatedValue = interpolatedValue.replace(/\{WORKSPACE_DIR\}/g, activeEnvInfo.targetPath);
|
|
839
|
-
env[key] = interpolatedValue;
|
|
840
|
-
agentSpecificEnv.add(key);
|
|
841
|
-
}
|
|
842
|
-
if (activeEnv?.prefix) {
|
|
843
|
-
const envArgs = Array.from(agentSpecificEnv).map((key) => {
|
|
844
|
-
if (activeEnv.envFormat) return activeEnv.envFormat.replace("{key}", key);
|
|
845
|
-
return key;
|
|
846
|
-
}).join(" ");
|
|
847
|
-
const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, {
|
|
848
|
-
targetPath: activeEnvInfo.targetPath,
|
|
849
|
-
executionCwd,
|
|
850
|
-
envDir: getEnvironmentPath(activeEnvName, cwd),
|
|
851
|
-
envArgs
|
|
852
|
-
});
|
|
853
|
-
if (prefixReplaced.includes("{COMMAND}")) command = prefixReplaced.replace("{COMMAND}", command);
|
|
854
|
-
else command = `${prefixReplaced} ${command}`;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
console.log(`Executing command: ${command}`);
|
|
858
|
-
let mainResult;
|
|
859
|
-
const typingInterval = setInterval(() => {
|
|
860
|
-
emitTyping(chatId);
|
|
861
|
-
}, 5e3);
|
|
862
|
-
try {
|
|
863
|
-
mainResult = await runCommand({
|
|
864
|
-
command,
|
|
865
|
-
cwd: executionCwd,
|
|
866
|
-
env,
|
|
867
|
-
signal
|
|
868
|
-
});
|
|
869
|
-
} finally {
|
|
870
|
-
clearInterval(typingInterval);
|
|
871
|
-
}
|
|
872
|
-
const logMsg = {
|
|
873
|
-
id: crypto.randomUUID(),
|
|
874
|
-
messageId: userMsg.id,
|
|
875
|
-
role: "log",
|
|
876
|
-
content: mainResult.stdout,
|
|
877
|
-
stdout: mainResult.stdout,
|
|
878
|
-
stderr: "",
|
|
879
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
880
|
-
command,
|
|
881
|
-
cwd: executionCwd,
|
|
882
|
-
exitCode: mainResult.exitCode,
|
|
883
|
-
...mainResult.stdout.includes("NO_REPLY_NECESSARY") ? { level: "verbose" } : {}
|
|
884
|
-
};
|
|
885
|
-
const errors = [];
|
|
886
|
-
if (mainResult.stderr) errors.push(mainResult.stderr);
|
|
887
|
-
let currentSuccess = mainResult.exitCode === 0;
|
|
888
|
-
if (currentSuccess) {
|
|
889
|
-
if (currentAgent.commands?.getMessageContent) {
|
|
890
|
-
const { result, error } = await runExtractionCommand("getMessageContent", currentAgent.commands.getMessageContent, runCommand, executionCwd, env, mainResult, signal);
|
|
891
|
-
if (result !== void 0) {
|
|
892
|
-
logMsg.content = result;
|
|
893
|
-
logMsg.stdout = mainResult.stdout;
|
|
894
|
-
if (result.includes("NO_REPLY_NECESSARY")) logMsg.level = "verbose";
|
|
895
|
-
else delete logMsg.level;
|
|
896
|
-
if (result.trim() === "") currentSuccess = false;
|
|
897
|
-
}
|
|
898
|
-
if (error) errors.push(error);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
logMsg.stderr = errors.join("\n\n");
|
|
902
|
-
lastLogMsg = logMsg;
|
|
903
|
-
if (currentSuccess) {
|
|
904
|
-
success = true;
|
|
905
|
-
if (isNewSession && currentAgent.commands?.getSessionId) {
|
|
906
|
-
const { result, error } = await runExtractionCommand("getSessionId", currentAgent.commands.getSessionId, runCommand, executionCwd, env, mainResult, signal);
|
|
907
|
-
if (result) await writeAgentSessionSettings(agentId, finalSessionId, { env: { SESSION_ID: result } }, cwd);
|
|
908
|
-
if (error) logMsg.stderr = [logMsg.stderr, error].filter(Boolean).join("\n\n");
|
|
909
|
-
}
|
|
910
|
-
break;
|
|
911
|
-
}
|
|
1344
|
+
}
|
|
1345
|
+
return message;
|
|
1346
|
+
}
|
|
1347
|
+
async handleMessage(message) {
|
|
1348
|
+
if (!message.content.trim()) return;
|
|
1349
|
+
await taskScheduler.schedule({
|
|
1350
|
+
id: `task-${this.agentId}-${randomUUID()}`,
|
|
1351
|
+
rootChatId: this.chatId,
|
|
1352
|
+
dirPath: this.workDirectory,
|
|
1353
|
+
sessionId: this.sessionId,
|
|
1354
|
+
text: message.content,
|
|
1355
|
+
execute: async (signal) => {
|
|
1356
|
+
const sessionSettings = await readAgentSessionSettings(this.agentId, this.sessionId, this.workspaceRoot);
|
|
1357
|
+
applyEnvOverrides(message.env, sessionSettings?.env);
|
|
1358
|
+
const result = await this.createRunner().executeWithFallbacks(message, signal);
|
|
1359
|
+
if (!result) return;
|
|
1360
|
+
if (result.extractedSessionId) await writeAgentSessionSettings(this.agentId, this.sessionId, { env: { SESSION_ID: result.extractedSessionId } }, this.workspaceRoot);
|
|
1361
|
+
await this.logger.logCommandResult(result);
|
|
1362
|
+
if (!result.content.includes("NO_REPLY_NECESSARY")) await this.logger.logAgentReply({ content: result.content });
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
async function createAgentSession(options) {
|
|
1368
|
+
const settings = options.settings ?? await readSettings(options.cwd) ?? void 0;
|
|
1369
|
+
const mergedAgent = await resolveMergedAgent(options.agentId, settings, options.cwd);
|
|
1370
|
+
const workspaceRoot = getWorkspaceRoot(options.cwd);
|
|
1371
|
+
return new AgentSession({
|
|
1372
|
+
agentId: options.agentId,
|
|
1373
|
+
sessionId: options.sessionId,
|
|
1374
|
+
chatId: options.chatId,
|
|
1375
|
+
...options.subagentId ? { subagentId: options.subagentId } : {},
|
|
1376
|
+
settings: mergedAgent,
|
|
1377
|
+
workspaceRoot,
|
|
1378
|
+
globalSettings: settings,
|
|
1379
|
+
...options.logger ? { logger: options.logger } : {}
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
async function resolveMergedAgent(agentId, settings, cwd) {
|
|
1383
|
+
let mergedAgent = settings?.defaultAgent || {};
|
|
1384
|
+
if (agentId !== "default") try {
|
|
1385
|
+
const customAgent = await getAgent(agentId, cwd);
|
|
1386
|
+
if (customAgent) mergedAgent = {
|
|
1387
|
+
...mergedAgent,
|
|
1388
|
+
...customAgent,
|
|
1389
|
+
commands: {
|
|
1390
|
+
...mergedAgent.commands,
|
|
1391
|
+
...customAgent.commands
|
|
1392
|
+
},
|
|
1393
|
+
env: {
|
|
1394
|
+
...mergedAgent.env,
|
|
1395
|
+
...customAgent.env
|
|
912
1396
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1397
|
+
};
|
|
1398
|
+
} catch {}
|
|
1399
|
+
return mergedAgent;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
//#endregion
|
|
1403
|
+
//#region src/daemon/message.ts
|
|
1404
|
+
async function executeDirectMessage(chatId, state, settings, cwd, noWait = false, userMessageContent, subagentId, systemEvent, displayRole) {
|
|
1405
|
+
const logger = createChatLogger(chatId, subagentId);
|
|
1406
|
+
let msgId;
|
|
1407
|
+
if (systemEvent) msgId = (await logger.logSystemMessage({
|
|
1408
|
+
content: userMessageContent ?? state.message,
|
|
1409
|
+
event: systemEvent,
|
|
1410
|
+
messageId: state.messageId,
|
|
1411
|
+
...displayRole ? { displayRole } : {}
|
|
1412
|
+
})).id;
|
|
1413
|
+
else msgId = (await logger.logUserMessage(userMessageContent ?? state.message)).id;
|
|
1414
|
+
if (state.reply) await logger.logAutomaticReply({
|
|
1415
|
+
messageId: msgId,
|
|
1416
|
+
content: state.reply
|
|
1417
|
+
});
|
|
1418
|
+
if (!state.message.trim() && state.action !== "stop" && state.action !== "interrupt") return;
|
|
1419
|
+
const agentSession = await createAgentSession({
|
|
1420
|
+
chatId,
|
|
1421
|
+
agentId: state.agentId || "default",
|
|
1422
|
+
sessionId: state.sessionId || "default",
|
|
1423
|
+
...subagentId ? { subagentId } : {},
|
|
1424
|
+
cwd,
|
|
1425
|
+
settings,
|
|
1426
|
+
logger
|
|
919
1427
|
});
|
|
1428
|
+
let finalMessage = {
|
|
1429
|
+
id: state.messageId,
|
|
1430
|
+
content: state.message,
|
|
1431
|
+
env: state.env ?? {}
|
|
1432
|
+
};
|
|
1433
|
+
if (state.action === "stop") {
|
|
1434
|
+
agentSession.stop();
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
if (state.action === "interrupt") finalMessage = agentSession.interrupt(finalMessage);
|
|
1438
|
+
const taskPromise = agentSession.handleMessage(finalMessage);
|
|
920
1439
|
if (!noWait) try {
|
|
921
1440
|
await taskPromise;
|
|
922
1441
|
} catch (err) {
|
|
@@ -926,12 +1445,11 @@ async function executeDirectMessage(chatId, state, settings, cwd, runCommand, no
|
|
|
926
1445
|
if (err.name !== "AbortError") console.error("Task execution error:", err);
|
|
927
1446
|
});
|
|
928
1447
|
}
|
|
929
|
-
async function getInitialRouterState(chatId, message,
|
|
930
|
-
const chatSettings = await readChatSettings(chatId, cwd) ?? {};
|
|
1448
|
+
async function getInitialRouterState(chatId, message, chatSettings, overrideAgentId, overrideSessionId) {
|
|
931
1449
|
const agentId = overrideAgentId ?? chatSettings.defaultAgent ?? "default";
|
|
932
1450
|
const sessionId = overrideSessionId ?? chatSettings.sessions?.[agentId] ?? "default";
|
|
933
1451
|
return {
|
|
934
|
-
messageId:
|
|
1452
|
+
messageId: crypto.randomUUID(),
|
|
935
1453
|
message,
|
|
936
1454
|
chatId,
|
|
937
1455
|
agentId,
|
|
@@ -939,92 +1457,52 @@ async function getInitialRouterState(chatId, message, cwd = process.cwd(), overr
|
|
|
939
1457
|
env: {}
|
|
940
1458
|
};
|
|
941
1459
|
}
|
|
942
|
-
async function handleUserMessage(chatId, message, settings, cwd = process.cwd(), noWait = false,
|
|
1460
|
+
async function handleUserMessage(chatId, message, settings, cwd = process.cwd(), noWait = false, sessionId, overrideAgentId) {
|
|
943
1461
|
const chatSettings = await readChatSettings(chatId, cwd) ?? {};
|
|
944
1462
|
if (overrideAgentId && chatSettings.defaultAgent !== overrideAgentId) {
|
|
945
1463
|
chatSettings.defaultAgent = overrideAgentId;
|
|
946
1464
|
await writeChatSettings(chatId, chatSettings, cwd);
|
|
947
1465
|
}
|
|
948
|
-
const initialState = await getInitialRouterState(chatId, message,
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
1466
|
+
const initialState = await getInitialRouterState(chatId, message, chatSettings, overrideAgentId, sessionId);
|
|
1467
|
+
const finalState = await executeRouterPipeline(initialState, resolveRouters(chatSettings.routers ?? settings?.routers ?? [], true));
|
|
1468
|
+
await applyRouterStateUpdates(chatId, cwd, finalState, chatSettings, initialState.agentId);
|
|
1469
|
+
await executeDirectMessage(chatId, finalState, settings, cwd, noWait, message);
|
|
1470
|
+
}
|
|
1471
|
+
async function applyRouterStateUpdates(chatId, cwd, finalState, chatSettings, initialAgent) {
|
|
952
1472
|
const finalAgentId = finalState.agentId;
|
|
953
1473
|
const finalSessionId = finalState.sessionId ?? crypto.randomUUID();
|
|
954
|
-
const routerEnv = finalState.env ?? {};
|
|
955
1474
|
const currentAgentId = finalAgentId ?? chatSettings.defaultAgent ?? "default";
|
|
956
1475
|
let settingsChanged = false;
|
|
957
1476
|
if (finalAgentId && finalAgentId !== initialAgent) {
|
|
958
1477
|
chatSettings.defaultAgent = finalAgentId;
|
|
959
1478
|
settingsChanged = true;
|
|
960
1479
|
}
|
|
961
|
-
if (
|
|
1480
|
+
if (finalState.nextSessionId) {
|
|
962
1481
|
chatSettings.sessions = chatSettings.sessions || {};
|
|
963
|
-
chatSettings.sessions[currentAgentId] =
|
|
1482
|
+
chatSettings.sessions[currentAgentId] = finalState.nextSessionId;
|
|
964
1483
|
settingsChanged = true;
|
|
965
1484
|
}
|
|
1485
|
+
if (finalState.jobs) {
|
|
1486
|
+
chatSettings.jobs = chatSettings.jobs || [];
|
|
1487
|
+
if (finalState.jobs.remove?.length) {
|
|
1488
|
+
const removeSet = new Set(finalState.jobs.remove);
|
|
1489
|
+
for (const jobId of finalState.jobs.remove) cronManager.unscheduleJob(chatId, jobId);
|
|
1490
|
+
chatSettings.jobs = chatSettings.jobs.filter((job) => !removeSet.has(job.id));
|
|
1491
|
+
settingsChanged = true;
|
|
1492
|
+
}
|
|
1493
|
+
if (finalState.jobs.add?.length) {
|
|
1494
|
+
const addMap = new Map(finalState.jobs.add.map((job) => [job.id, job]));
|
|
1495
|
+
for (const job of finalState.jobs.add) cronManager.scheduleJob(chatId, job);
|
|
1496
|
+
chatSettings.jobs = chatSettings.jobs.filter((job) => !addMap.has(job.id));
|
|
1497
|
+
chatSettings.jobs.push(...finalState.jobs.add);
|
|
1498
|
+
settingsChanged = true;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
966
1501
|
if (settingsChanged) await writeChatSettings(chatId, chatSettings, cwd);
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
message: finalMessage,
|
|
970
|
-
chatId,
|
|
971
|
-
env: routerEnv
|
|
972
|
-
};
|
|
973
|
-
if (finalAgentId !== void 0) directState.agentId = finalAgentId;
|
|
974
|
-
if (finalSessionId !== void 0) directState.sessionId = finalSessionId;
|
|
975
|
-
if (finalState.reply !== void 0) directState.reply = finalState.reply;
|
|
976
|
-
if (finalState.action !== void 0) directState.action = finalState.action;
|
|
977
|
-
await executeDirectMessage(chatId, directState, settings, cwd, runCommand, noWait, message);
|
|
1502
|
+
if (finalState.sessionId === void 0) finalState.sessionId = finalSessionId;
|
|
1503
|
+
if (finalState.agentId === void 0) finalState.agentId = currentAgentId;
|
|
978
1504
|
}
|
|
979
1505
|
|
|
980
|
-
//#endregion
|
|
981
|
-
//#region src/daemon/utils/spawn.ts
|
|
982
|
-
const runCommand = async ({ command, cwd, env, stdin, signal, logToTerminal }) => {
|
|
983
|
-
return new Promise((resolve, reject) => {
|
|
984
|
-
const p = spawn(command, {
|
|
985
|
-
shell: true,
|
|
986
|
-
cwd,
|
|
987
|
-
env,
|
|
988
|
-
signal
|
|
989
|
-
});
|
|
990
|
-
if (stdin && p.stdin) {
|
|
991
|
-
p.stdin.on("error", (err) => {
|
|
992
|
-
if (err.code !== "EPIPE") console.error("stdin error:", err);
|
|
993
|
-
});
|
|
994
|
-
p.stdin.write(stdin);
|
|
995
|
-
p.stdin.end();
|
|
996
|
-
}
|
|
997
|
-
let stdout = "";
|
|
998
|
-
let stderr = "";
|
|
999
|
-
if (p.stdout) p.stdout.on("data", (data) => {
|
|
1000
|
-
stdout += data.toString();
|
|
1001
|
-
if (logToTerminal && !stdin) process.stdout.write(data);
|
|
1002
|
-
});
|
|
1003
|
-
if (p.stderr) p.stderr.on("data", (data) => {
|
|
1004
|
-
stderr += data.toString();
|
|
1005
|
-
if (logToTerminal && !stdin) process.stderr.write(data);
|
|
1006
|
-
});
|
|
1007
|
-
p.on("close", (code) => {
|
|
1008
|
-
resolve({
|
|
1009
|
-
stdout,
|
|
1010
|
-
stderr,
|
|
1011
|
-
exitCode: code ?? 1
|
|
1012
|
-
});
|
|
1013
|
-
});
|
|
1014
|
-
p.on("error", (err) => {
|
|
1015
|
-
if (err.name === "AbortError") {
|
|
1016
|
-
reject(err);
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
resolve({
|
|
1020
|
-
stdout: "",
|
|
1021
|
-
stderr: err.toString(),
|
|
1022
|
-
exitCode: 1
|
|
1023
|
-
});
|
|
1024
|
-
});
|
|
1025
|
-
});
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
1506
|
//#endregion
|
|
1029
1507
|
//#region src/daemon/cron.ts
|
|
1030
1508
|
var CronManager = class {
|
|
@@ -1107,14 +1585,25 @@ var CronManager = class {
|
|
|
1107
1585
|
} catch {
|
|
1108
1586
|
globalSettings = void 0;
|
|
1109
1587
|
}
|
|
1110
|
-
const overrideSessionId = job.session?.type === "new" ? crypto.randomUUID() :
|
|
1111
|
-
const
|
|
1588
|
+
const overrideSessionId = job.session?.type === "new" ? crypto.randomUUID() : job.session?.id;
|
|
1589
|
+
const chatSettings = await readChatSettings(chatId, process.cwd()) ?? {};
|
|
1590
|
+
let routerState = await getInitialRouterState(chatId, job.message, chatSettings, job.agentId, overrideSessionId);
|
|
1112
1591
|
if (job.env !== void 0) {
|
|
1113
1592
|
routerState.env = routerState.env || {};
|
|
1114
1593
|
applyEnvOverrides(routerState.env, job.env);
|
|
1115
1594
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1595
|
+
const currentAgentId = job.agentId ?? chatSettings.defaultAgent ?? "default";
|
|
1596
|
+
const currentActiveSession = chatSettings.sessions?.[currentAgentId];
|
|
1597
|
+
const isOutdatedSession = job.session?.type === "existing" && currentActiveSession !== void 0 && currentActiveSession !== job.session.id;
|
|
1598
|
+
if (job.reply !== void 0 && !isOutdatedSession) routerState.reply = job.reply;
|
|
1599
|
+
if (job.nextSessionId !== void 0 && !isOutdatedSession) routerState.nextSessionId = job.nextSessionId;
|
|
1600
|
+
if (job.action !== void 0) routerState.action = job.action;
|
|
1601
|
+
if (job.jobs !== void 0) routerState.jobs = job.jobs;
|
|
1602
|
+
const resolvedRouters = resolveRouters(chatSettings.routers ?? globalSettings?.routers ?? [], false);
|
|
1603
|
+
const initialState = { ...routerState };
|
|
1604
|
+
routerState = await executeRouterPipeline(routerState, resolvedRouters);
|
|
1605
|
+
await applyRouterStateUpdates(chatId, process.cwd(), routerState, chatSettings, initialState.agentId);
|
|
1606
|
+
await executeDirectMessage(chatId, routerState, globalSettings, process.cwd(), false, job.message, void 0, "cron");
|
|
1118
1607
|
if (isOneOff) {
|
|
1119
1608
|
const chatSettings = await readChatSettings(chatId);
|
|
1120
1609
|
if (chatSettings && chatSettings.jobs) {
|
|
@@ -1297,10 +1786,7 @@ const sendMessage = apiProcedure.input(z.object({
|
|
|
1297
1786
|
const fileList = `Attached files:\n${finalPaths.map((p) => `- ${p}`).join("\n")}`;
|
|
1298
1787
|
message = message ? `${message}\n\n${fileList}` : fileList;
|
|
1299
1788
|
}
|
|
1300
|
-
await handleUserMessage(chatId, message, settings, void 0, noWait,
|
|
1301
|
-
...args,
|
|
1302
|
-
logToTerminal: true
|
|
1303
|
-
}), sessionId, agentId);
|
|
1789
|
+
await handleUserMessage(chatId, message, settings, void 0, noWait, sessionId, agentId);
|
|
1304
1790
|
return { success: true };
|
|
1305
1791
|
});
|
|
1306
1792
|
const getMessages = apiProcedure.input(z.object({
|
|
@@ -1348,6 +1834,23 @@ const shutdown = publicProcedure.mutation(() => {
|
|
|
1348
1834
|
const userListCronJobs = apiProcedure.input(z.object({ chatId: z.string().optional() })).query(async ({ input }) => {
|
|
1349
1835
|
return listCronJobsShared(input.chatId ?? await getDefaultChatId());
|
|
1350
1836
|
});
|
|
1837
|
+
const getChats = apiProcedure.query(async () => {
|
|
1838
|
+
return listChats();
|
|
1839
|
+
});
|
|
1840
|
+
const getAgents = apiProcedure.query(async () => {
|
|
1841
|
+
return listAgents();
|
|
1842
|
+
});
|
|
1843
|
+
const userCreateChat = apiProcedure.input(z.object({
|
|
1844
|
+
chatId: z.string(),
|
|
1845
|
+
agent: z.string().optional()
|
|
1846
|
+
})).mutation(async ({ input }) => {
|
|
1847
|
+
await createChat(input.chatId);
|
|
1848
|
+
if (input.agent) await writeChatSettings(input.chatId, { defaultAgent: input.agent });
|
|
1849
|
+
return {
|
|
1850
|
+
success: true,
|
|
1851
|
+
chatId: input.chatId
|
|
1852
|
+
};
|
|
1853
|
+
});
|
|
1351
1854
|
const userAddCronJob = apiProcedure.input(z.object({
|
|
1352
1855
|
chatId: z.string().optional(),
|
|
1353
1856
|
job: CronJobSchema
|
|
@@ -1369,7 +1872,10 @@ const userRouter = router({
|
|
|
1369
1872
|
shutdown,
|
|
1370
1873
|
listCronJobs: userListCronJobs,
|
|
1371
1874
|
addCronJob: userAddCronJob,
|
|
1372
|
-
deleteCronJob: userDeleteCronJob
|
|
1875
|
+
deleteCronJob: userDeleteCronJob,
|
|
1876
|
+
getChats,
|
|
1877
|
+
getAgents,
|
|
1878
|
+
createChat: userCreateChat
|
|
1373
1879
|
});
|
|
1374
1880
|
|
|
1375
1881
|
//#endregion
|
|
@@ -1385,7 +1891,7 @@ var PolicyRequestService = class {
|
|
|
1385
1891
|
this.snapshotDir = snapshotDir;
|
|
1386
1892
|
this.maxPending = maxPending;
|
|
1387
1893
|
}
|
|
1388
|
-
async createRequest(commandName, args, fileMappings, chatId, agentId) {
|
|
1894
|
+
async createRequest(commandName, args, fileMappings, chatId, agentId, skipSave = false, subagentId) {
|
|
1389
1895
|
const allRequests = await this.store.list();
|
|
1390
1896
|
if (allRequests.filter((r) => r.state === "Pending").length >= this.maxPending) throw new Error(`Maximum number of pending requests (${this.maxPending}) reached.`);
|
|
1391
1897
|
const snapshotMappings = {};
|
|
@@ -1399,12 +1905,13 @@ var PolicyRequestService = class {
|
|
|
1399
1905
|
commandName,
|
|
1400
1906
|
args,
|
|
1401
1907
|
fileMappings: snapshotMappings,
|
|
1402
|
-
state: "Pending",
|
|
1908
|
+
state: skipSave ? "Approved" : "Pending",
|
|
1403
1909
|
createdAt: Date.now(),
|
|
1404
1910
|
chatId,
|
|
1405
|
-
agentId
|
|
1911
|
+
agentId,
|
|
1912
|
+
...subagentId ? { subagentId } : {}
|
|
1406
1913
|
};
|
|
1407
|
-
await this.store.save(request);
|
|
1914
|
+
if (!skipSave) await this.store.save(request);
|
|
1408
1915
|
return request;
|
|
1409
1916
|
}
|
|
1410
1917
|
getInterpolatedArgs(request) {
|
|
@@ -1412,6 +1919,295 @@ var PolicyRequestService = class {
|
|
|
1412
1919
|
}
|
|
1413
1920
|
};
|
|
1414
1921
|
|
|
1922
|
+
//#endregion
|
|
1923
|
+
//#region src/daemon/api/subagent-utils.ts
|
|
1924
|
+
function getSubagentDepth(settings, parentId) {
|
|
1925
|
+
let depth = 0;
|
|
1926
|
+
let currentParentId = parentId;
|
|
1927
|
+
while (currentParentId && settings.subagents?.[currentParentId]) {
|
|
1928
|
+
depth++;
|
|
1929
|
+
currentParentId = settings.subagents[currentParentId]?.parentId;
|
|
1930
|
+
}
|
|
1931
|
+
return depth;
|
|
1932
|
+
}
|
|
1933
|
+
async function executeSubagent(chatId, subagentId, agentId, sessionId, prompt, isAsync, parentTokenPayload, workspaceRoot) {
|
|
1934
|
+
try {
|
|
1935
|
+
const settings = await readChatSettings(chatId) || {};
|
|
1936
|
+
const resolvedRouters = resolveRouters(settings.routers ?? [], false);
|
|
1937
|
+
let routerState = {
|
|
1938
|
+
messageId: randomUUID(),
|
|
1939
|
+
message: prompt,
|
|
1940
|
+
chatId,
|
|
1941
|
+
agentId,
|
|
1942
|
+
sessionId,
|
|
1943
|
+
env: {}
|
|
1944
|
+
};
|
|
1945
|
+
const initialState = { ...routerState };
|
|
1946
|
+
routerState = await executeRouterPipeline(routerState, resolvedRouters);
|
|
1947
|
+
await applyRouterStateUpdates(chatId, workspaceRoot, routerState, settings, initialState.agentId);
|
|
1948
|
+
await executeDirectMessage(chatId, routerState, void 0, workspaceRoot, false, void 0, subagentId);
|
|
1949
|
+
if (taskScheduler.hasTasks(sessionId)) return;
|
|
1950
|
+
await updateChatSettings(chatId, (finalSettings) => {
|
|
1951
|
+
if (finalSettings.subagents?.[subagentId]) finalSettings.subagents[subagentId].status = "completed";
|
|
1952
|
+
return finalSettings;
|
|
1953
|
+
});
|
|
1954
|
+
const logger = createChatLogger(chatId, subagentId);
|
|
1955
|
+
await logger.logSubagentStatus({
|
|
1956
|
+
subagentId,
|
|
1957
|
+
status: "completed"
|
|
1958
|
+
});
|
|
1959
|
+
if (isAsync) {
|
|
1960
|
+
const lastLogMessage = await logger.findLastMessage((m) => m.role === "agent" || m.displayRole === "agent");
|
|
1961
|
+
let outputContent = "";
|
|
1962
|
+
if (lastLogMessage && "content" in lastLogMessage) outputContent = `\n\n<subagent_output>\n${lastLogMessage.content}\n</subagent_output>`;
|
|
1963
|
+
console.log("Notifying parent", chatId, parentTokenPayload?.agentId, parentTokenPayload?.subagentId);
|
|
1964
|
+
await executeDirectMessage(chatId, {
|
|
1965
|
+
messageId: randomUUID(),
|
|
1966
|
+
message: `<notification>Subagent ${subagentId} completed.</notification>${outputContent}`,
|
|
1967
|
+
chatId,
|
|
1968
|
+
agentId: parentTokenPayload?.agentId || "default",
|
|
1969
|
+
...parentTokenPayload?.subagentId ? { subagentId: parentTokenPayload.subagentId } : {},
|
|
1970
|
+
sessionId: parentTokenPayload?.sessionId || "default",
|
|
1971
|
+
env: {}
|
|
1972
|
+
}, void 0, workspaceRoot, true, void 0, parentTokenPayload?.subagentId, "subagent_update");
|
|
1973
|
+
}
|
|
1974
|
+
} catch {
|
|
1975
|
+
await updateChatSettings(chatId, (errSettings) => {
|
|
1976
|
+
if (errSettings.subagents?.[subagentId]) errSettings.subagents[subagentId].status = "failed";
|
|
1977
|
+
return errSettings;
|
|
1978
|
+
});
|
|
1979
|
+
await createChatLogger(chatId, subagentId).logSubagentStatus({
|
|
1980
|
+
subagentId,
|
|
1981
|
+
status: "failed"
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
//#endregion
|
|
1987
|
+
//#region src/daemon/api/subagent-router.ts
|
|
1988
|
+
const MAX_SUBAGENT_DEPTH = 2;
|
|
1989
|
+
const subagentSpawn = apiProcedure.input(z.object({
|
|
1990
|
+
subagentId: z.string().optional(),
|
|
1991
|
+
targetAgentId: z.string().optional(),
|
|
1992
|
+
prompt: z.string(),
|
|
1993
|
+
async: z.boolean().optional()
|
|
1994
|
+
})).mutation(async ({ input, ctx }) => {
|
|
1995
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
1996
|
+
code: "UNAUTHORIZED",
|
|
1997
|
+
message: "Missing token"
|
|
1998
|
+
});
|
|
1999
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2000
|
+
const parentAgentId = ctx.tokenPayload.agentId;
|
|
2001
|
+
const parentId = ctx.tokenPayload.subagentId;
|
|
2002
|
+
const id = input.subagentId || randomUUID();
|
|
2003
|
+
const sessionId = randomUUID();
|
|
2004
|
+
const agentId = input.targetAgentId || parentAgentId;
|
|
2005
|
+
let depth = 0;
|
|
2006
|
+
await updateChatSettings(chatId, (settings) => {
|
|
2007
|
+
settings.subagents = settings.subagents || {};
|
|
2008
|
+
depth = getSubagentDepth(settings, parentId);
|
|
2009
|
+
if (depth >= MAX_SUBAGENT_DEPTH) throw new TRPCError({
|
|
2010
|
+
code: "BAD_REQUEST",
|
|
2011
|
+
message: "Max subagent depth reached"
|
|
2012
|
+
});
|
|
2013
|
+
if (settings.subagents[id]) throw new TRPCError({
|
|
2014
|
+
code: "BAD_REQUEST",
|
|
2015
|
+
message: "Subagent ID already exists"
|
|
2016
|
+
});
|
|
2017
|
+
settings.subagents[id] = {
|
|
2018
|
+
id,
|
|
2019
|
+
agentId,
|
|
2020
|
+
sessionId,
|
|
2021
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2022
|
+
status: "active",
|
|
2023
|
+
parentId
|
|
2024
|
+
};
|
|
2025
|
+
return settings;
|
|
2026
|
+
});
|
|
2027
|
+
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
2028
|
+
const isAsync = input.async ?? depth === 0;
|
|
2029
|
+
executeSubagent(chatId, id, agentId, sessionId, input.prompt, isAsync, ctx.tokenPayload, workspaceRoot);
|
|
2030
|
+
return {
|
|
2031
|
+
id,
|
|
2032
|
+
depth,
|
|
2033
|
+
isAsync
|
|
2034
|
+
};
|
|
2035
|
+
});
|
|
2036
|
+
const subagentSend = apiProcedure.input(z.object({
|
|
2037
|
+
subagentId: z.string(),
|
|
2038
|
+
prompt: z.string(),
|
|
2039
|
+
async: z.boolean().optional()
|
|
2040
|
+
})).mutation(async ({ input, ctx }) => {
|
|
2041
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2042
|
+
code: "UNAUTHORIZED",
|
|
2043
|
+
message: "Missing token"
|
|
2044
|
+
});
|
|
2045
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2046
|
+
let sub;
|
|
2047
|
+
await updateChatSettings(chatId, (settings) => {
|
|
2048
|
+
if (!settings.subagents?.[input.subagentId]) throw new TRPCError({
|
|
2049
|
+
code: "NOT_FOUND",
|
|
2050
|
+
message: "Subagent not found"
|
|
2051
|
+
});
|
|
2052
|
+
sub = settings.subagents[input.subagentId];
|
|
2053
|
+
sub.status = "active";
|
|
2054
|
+
return settings;
|
|
2055
|
+
});
|
|
2056
|
+
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
2057
|
+
executeSubagent(chatId, sub.id, sub.agentId || "default", sub.sessionId || "default", input.prompt, input.async, ctx.tokenPayload, workspaceRoot);
|
|
2058
|
+
return { success: true };
|
|
2059
|
+
});
|
|
2060
|
+
async function checkSubagentStatus(chatId, subagentId) {
|
|
2061
|
+
const sub = (await readChatSettings(chatId))?.subagents?.[subagentId];
|
|
2062
|
+
if (!sub) throw new TRPCError({
|
|
2063
|
+
code: "NOT_FOUND",
|
|
2064
|
+
message: "Subagent not found"
|
|
2065
|
+
});
|
|
2066
|
+
if (sub.status === "completed" || sub.status === "failed") {
|
|
2067
|
+
let outputContent;
|
|
2068
|
+
if (sub.status === "completed") {
|
|
2069
|
+
const lastLogMessage = await createChatLogger(chatId, subagentId).findLastMessage((m) => m.role === "agent");
|
|
2070
|
+
if (lastLogMessage && "content" in lastLogMessage) outputContent = lastLogMessage.content;
|
|
2071
|
+
}
|
|
2072
|
+
return {
|
|
2073
|
+
status: sub.status,
|
|
2074
|
+
output: outputContent
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2079
|
+
const subagentWait = apiProcedure.input(z.object({ subagentId: z.string() })).mutation(async ({ input, ctx, signal }) => {
|
|
2080
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2081
|
+
code: "UNAUTHORIZED",
|
|
2082
|
+
message: "Missing token"
|
|
2083
|
+
});
|
|
2084
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2085
|
+
const ac = new AbortController();
|
|
2086
|
+
const timeout = setTimeout(() => ac.abort(), 6e4);
|
|
2087
|
+
const onAbort = () => {
|
|
2088
|
+
clearTimeout(timeout);
|
|
2089
|
+
ac.abort();
|
|
2090
|
+
};
|
|
2091
|
+
if (signal) signal.addEventListener("abort", onAbort);
|
|
2092
|
+
const eventIterator = on(daemonEvents, DAEMON_EVENT_MESSAGE_APPENDED, { signal: ac.signal });
|
|
2093
|
+
try {
|
|
2094
|
+
const initialStatus = await checkSubagentStatus(chatId, input.subagentId);
|
|
2095
|
+
if (initialStatus) {
|
|
2096
|
+
clearTimeout(timeout);
|
|
2097
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
2098
|
+
return initialStatus;
|
|
2099
|
+
}
|
|
2100
|
+
for await (const [event] of eventIterator) if (event.chatId === chatId && event.message?.subagentId === input.subagentId) {
|
|
2101
|
+
if (event.message.role === "subagent_status") {
|
|
2102
|
+
const status = await checkSubagentStatus(chatId, input.subagentId);
|
|
2103
|
+
if (status) {
|
|
2104
|
+
clearTimeout(timeout);
|
|
2105
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
2106
|
+
return status;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
} catch (err) {
|
|
2111
|
+
if (err && typeof err === "object" && "name" in err && err.name === "AbortError") return {
|
|
2112
|
+
status: "active",
|
|
2113
|
+
output: void 0
|
|
2114
|
+
};
|
|
2115
|
+
throw err;
|
|
2116
|
+
} finally {
|
|
2117
|
+
clearTimeout(timeout);
|
|
2118
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
2119
|
+
ac.abort();
|
|
2120
|
+
}
|
|
2121
|
+
return {
|
|
2122
|
+
status: "active",
|
|
2123
|
+
output: void 0
|
|
2124
|
+
};
|
|
2125
|
+
});
|
|
2126
|
+
const subagentStop = apiProcedure.input(z.object({ subagentId: z.string() })).mutation(async ({ input, ctx }) => {
|
|
2127
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2128
|
+
code: "UNAUTHORIZED",
|
|
2129
|
+
message: "Missing token"
|
|
2130
|
+
});
|
|
2131
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2132
|
+
let subToStop;
|
|
2133
|
+
await updateChatSettings(chatId, (settings) => {
|
|
2134
|
+
if (settings.subagents) {
|
|
2135
|
+
const sub = settings.subagents[input.subagentId];
|
|
2136
|
+
if (sub) {
|
|
2137
|
+
sub.status = "failed";
|
|
2138
|
+
subToStop = sub;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
return settings;
|
|
2142
|
+
});
|
|
2143
|
+
if (subToStop) (await createAgentSession({
|
|
2144
|
+
chatId,
|
|
2145
|
+
agentId: subToStop.agentId || "default",
|
|
2146
|
+
sessionId: subToStop.sessionId || "default",
|
|
2147
|
+
subagentId: input.subagentId,
|
|
2148
|
+
cwd: process.cwd()
|
|
2149
|
+
})).stop();
|
|
2150
|
+
return { success: true };
|
|
2151
|
+
});
|
|
2152
|
+
const subagentDelete = apiProcedure.input(z.object({ subagentId: z.string() })).mutation(async ({ input, ctx }) => {
|
|
2153
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2154
|
+
code: "UNAUTHORIZED",
|
|
2155
|
+
message: "Missing token"
|
|
2156
|
+
});
|
|
2157
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2158
|
+
let subToDelete;
|
|
2159
|
+
await updateChatSettings(chatId, (settings) => {
|
|
2160
|
+
if (settings.subagents && settings.subagents[input.subagentId]) {
|
|
2161
|
+
subToDelete = settings.subagents[input.subagentId];
|
|
2162
|
+
delete settings.subagents[input.subagentId];
|
|
2163
|
+
}
|
|
2164
|
+
return settings;
|
|
2165
|
+
});
|
|
2166
|
+
if (subToDelete) {
|
|
2167
|
+
(await createAgentSession({
|
|
2168
|
+
chatId,
|
|
2169
|
+
agentId: subToDelete.agentId || "default",
|
|
2170
|
+
sessionId: subToDelete.sessionId || "default",
|
|
2171
|
+
subagentId: input.subagentId,
|
|
2172
|
+
cwd: process.cwd()
|
|
2173
|
+
})).stop();
|
|
2174
|
+
return {
|
|
2175
|
+
success: true,
|
|
2176
|
+
deleted: true
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
return {
|
|
2180
|
+
success: true,
|
|
2181
|
+
deleted: false
|
|
2182
|
+
};
|
|
2183
|
+
});
|
|
2184
|
+
const subagentList = apiProcedure.input(z.object({ blocking: z.boolean().optional() }).optional()).query(async ({ input, ctx }) => {
|
|
2185
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2186
|
+
code: "UNAUTHORIZED",
|
|
2187
|
+
message: "Missing token"
|
|
2188
|
+
});
|
|
2189
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2190
|
+
const settings = await readChatSettings(chatId);
|
|
2191
|
+
let subagents = Object.values(settings?.subagents || {});
|
|
2192
|
+
const isSubagent = !!ctx.tokenPayload.subagentId;
|
|
2193
|
+
const myId = ctx.tokenPayload.subagentId;
|
|
2194
|
+
subagents = subagents.filter((s) => s.parentId === myId);
|
|
2195
|
+
if (input?.blocking) if (!isSubagent) subagents = [];
|
|
2196
|
+
else subagents = subagents.filter((s) => s.status === "active");
|
|
2197
|
+
return { subagents };
|
|
2198
|
+
});
|
|
2199
|
+
const subagentTail = apiProcedure.input(z.object({
|
|
2200
|
+
subagentId: z.string(),
|
|
2201
|
+
limit: z.number().optional()
|
|
2202
|
+
})).query(async ({ input, ctx }) => {
|
|
2203
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2204
|
+
code: "UNAUTHORIZED",
|
|
2205
|
+
message: "Missing token"
|
|
2206
|
+
});
|
|
2207
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2208
|
+
return { messages: await createChatLogger(chatId, input.subagentId).getMessages(input.limit) };
|
|
2209
|
+
});
|
|
2210
|
+
|
|
1415
2211
|
//#endregion
|
|
1416
2212
|
//#region src/daemon/api/agent-router.ts
|
|
1417
2213
|
const logMessage = apiProcedure.input(z.object({
|
|
@@ -1438,18 +2234,81 @@ const logMessage = apiProcedure.input(z.object({
|
|
|
1438
2234
|
await appendMessage(chatId, {
|
|
1439
2235
|
id,
|
|
1440
2236
|
messageId: id,
|
|
1441
|
-
role: "
|
|
1442
|
-
source: "router",
|
|
2237
|
+
role: "command",
|
|
1443
2238
|
content: input.message || "",
|
|
2239
|
+
stdout: "",
|
|
1444
2240
|
stderr: "",
|
|
1445
2241
|
timestamp,
|
|
1446
2242
|
command: `clawmini-lite log${filesArgStr}`,
|
|
1447
2243
|
cwd: process.cwd(),
|
|
1448
2244
|
exitCode: 0,
|
|
2245
|
+
...ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {},
|
|
2246
|
+
...filePaths.length > 0 ? { files: filePaths } : {}
|
|
2247
|
+
});
|
|
2248
|
+
return { success: true };
|
|
2249
|
+
});
|
|
2250
|
+
const logReplyMessage = apiProcedure.input(z.object({
|
|
2251
|
+
message: z.string(),
|
|
2252
|
+
files: z.array(z.string()).optional()
|
|
2253
|
+
})).mutation(async ({ input, ctx }) => {
|
|
2254
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2255
|
+
code: "UNAUTHORIZED",
|
|
2256
|
+
message: "Missing token"
|
|
2257
|
+
});
|
|
2258
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2259
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2260
|
+
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
2261
|
+
const filePaths = [];
|
|
2262
|
+
if (input.files && input.files.length > 0) {
|
|
2263
|
+
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
2264
|
+
const agentDir = await resolveAgentDir(ctx.tokenPayload?.agentId, workspaceRoot);
|
|
2265
|
+
for (const file of input.files) {
|
|
2266
|
+
const validPath = await validateLogFile(file, agentDir, workspaceRoot);
|
|
2267
|
+
filePaths.push(validPath);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
await appendMessage(chatId, {
|
|
2271
|
+
id,
|
|
2272
|
+
role: "agent",
|
|
2273
|
+
content: input.message,
|
|
2274
|
+
timestamp,
|
|
2275
|
+
...ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {},
|
|
1449
2276
|
...filePaths.length > 0 ? { files: filePaths } : {}
|
|
1450
2277
|
});
|
|
1451
2278
|
return { success: true };
|
|
1452
2279
|
});
|
|
2280
|
+
const logToolMessage = apiProcedure.input(z.object({
|
|
2281
|
+
name: z.string(),
|
|
2282
|
+
payload: z.any().optional()
|
|
2283
|
+
})).mutation(async ({ input, ctx }) => {
|
|
2284
|
+
if (!ctx.tokenPayload) throw new TRPCError({
|
|
2285
|
+
code: "UNAUTHORIZED",
|
|
2286
|
+
message: "Missing token"
|
|
2287
|
+
});
|
|
2288
|
+
const chatId = ctx.tokenPayload.chatId;
|
|
2289
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2290
|
+
const id = Date.now().toString() + Math.random().toString(36).substring(2, 7);
|
|
2291
|
+
const messageId = randomUUID();
|
|
2292
|
+
const payloadObj = input.payload !== void 0 ? input.payload : {};
|
|
2293
|
+
let contentStr;
|
|
2294
|
+
if (typeof payloadObj === "string") contentStr = payloadObj;
|
|
2295
|
+
else try {
|
|
2296
|
+
contentStr = JSON.stringify(payloadObj, null, 2);
|
|
2297
|
+
} catch {
|
|
2298
|
+
contentStr = String(payloadObj);
|
|
2299
|
+
}
|
|
2300
|
+
await appendMessage(chatId, {
|
|
2301
|
+
id,
|
|
2302
|
+
messageId,
|
|
2303
|
+
role: "tool",
|
|
2304
|
+
name: input.name,
|
|
2305
|
+
payload: payloadObj,
|
|
2306
|
+
content: contentStr,
|
|
2307
|
+
timestamp,
|
|
2308
|
+
...ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}
|
|
2309
|
+
});
|
|
2310
|
+
return { success: true };
|
|
2311
|
+
});
|
|
1453
2312
|
const agentListCronJobs = apiProcedure.query(async ({ ctx }) => {
|
|
1454
2313
|
if (!ctx.tokenPayload) throw new TRPCError({
|
|
1455
2314
|
code: "UNAUTHORIZED",
|
|
@@ -1510,34 +2369,68 @@ const createPolicyRequest = apiProcedure.input(z.object({
|
|
|
1510
2369
|
});
|
|
1511
2370
|
const workspaceRoot = getWorkspaceRoot(process.cwd());
|
|
1512
2371
|
const snapshotDir = path.join(getClawminiDir(process.cwd()), "tmp", "snapshots");
|
|
1513
|
-
const
|
|
2372
|
+
const store = new RequestStore(process.cwd());
|
|
2373
|
+
const service = new PolicyRequestService(store, await resolveAgentDir(ctx.tokenPayload?.agentId, workspaceRoot), snapshotDir);
|
|
1514
2374
|
const chatId = ctx.tokenPayload.chatId;
|
|
1515
2375
|
const agentId = ctx.tokenPayload.agentId;
|
|
1516
|
-
const
|
|
2376
|
+
const policy = (await readPolicies())?.policies?.[input.commandName];
|
|
2377
|
+
if (!policy) throw new TRPCError({
|
|
2378
|
+
code: "NOT_FOUND",
|
|
2379
|
+
message: `Policy not found: ${input.commandName}`
|
|
2380
|
+
});
|
|
2381
|
+
const isAutoApprove = !!policy.autoApprove;
|
|
2382
|
+
const request = await service.createRequest(input.commandName, input.args, input.fileMappings, chatId, agentId, isAutoApprove, ctx.tokenPayload.subagentId);
|
|
2383
|
+
if (isAutoApprove) {
|
|
2384
|
+
const { stdout, stderr, exitCode, commandStr } = await executeRequest(request, policy, getWorkspaceRoot());
|
|
2385
|
+
request.executionResult = {
|
|
2386
|
+
stdout,
|
|
2387
|
+
stderr,
|
|
2388
|
+
exitCode
|
|
2389
|
+
};
|
|
2390
|
+
await store.save(request);
|
|
2391
|
+
await appendMessage(chatId, {
|
|
2392
|
+
id: randomUUID(),
|
|
2393
|
+
messageId: randomUUID(),
|
|
2394
|
+
role: "policy",
|
|
2395
|
+
requestId: request.id,
|
|
2396
|
+
commandName: input.commandName,
|
|
2397
|
+
args: input.args,
|
|
2398
|
+
status: "approved",
|
|
2399
|
+
content: `[Auto-approved] Policy ${input.commandName} was executed.\n\nCommand: ${commandStr}\nExit Code: ${exitCode}\n\nStdout:\n${stdout}\n\nStderr:\n${stderr}`,
|
|
2400
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2401
|
+
...ctx.tokenPayload.subagentId ? { subagentId: ctx.tokenPayload.subagentId } : {}
|
|
2402
|
+
});
|
|
2403
|
+
return request;
|
|
2404
|
+
}
|
|
1517
2405
|
const previewContent = await generateRequestPreview(request);
|
|
1518
2406
|
await appendMessage(chatId, {
|
|
1519
2407
|
id: randomUUID(),
|
|
1520
2408
|
messageId: randomUUID(),
|
|
1521
|
-
role: "
|
|
1522
|
-
|
|
2409
|
+
role: "policy",
|
|
2410
|
+
requestId: request.id,
|
|
2411
|
+
commandName: input.commandName,
|
|
2412
|
+
args: input.args,
|
|
2413
|
+
status: "pending",
|
|
1523
2414
|
content: previewContent,
|
|
1524
|
-
stderr: "",
|
|
1525
2415
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1526
|
-
|
|
1527
|
-
cwd: process.cwd(),
|
|
1528
|
-
exitCode: 0
|
|
2416
|
+
displayRole: "agent"
|
|
1529
2417
|
});
|
|
1530
2418
|
return request;
|
|
1531
2419
|
});
|
|
1532
2420
|
const fetchPendingMessages = apiProcedure.mutation(async ({ ctx }) => {
|
|
1533
|
-
|
|
2421
|
+
if (!ctx.tokenPayload?.agentId) throw new TRPCError({
|
|
2422
|
+
code: "UNAUTHORIZED",
|
|
2423
|
+
message: "Missing agent ID"
|
|
2424
|
+
});
|
|
1534
2425
|
const targetSessionId = ctx.tokenPayload?.sessionId || "default";
|
|
1535
|
-
const extracted =
|
|
2426
|
+
const extracted = taskScheduler.extractPending(targetSessionId);
|
|
1536
2427
|
if (extracted.length === 0) return { messages: "" };
|
|
1537
|
-
return { messages: formatPendingMessages(extracted
|
|
2428
|
+
return { messages: formatPendingMessages(extracted) };
|
|
1538
2429
|
});
|
|
1539
2430
|
const agentRouter = router({
|
|
1540
2431
|
logMessage,
|
|
2432
|
+
logReplyMessage,
|
|
2433
|
+
logToolMessage,
|
|
1541
2434
|
listCronJobs: agentListCronJobs,
|
|
1542
2435
|
addCronJob: agentAddCronJob,
|
|
1543
2436
|
deleteCronJob: agentDeleteCronJob,
|
|
@@ -1545,7 +2438,14 @@ const agentRouter = router({
|
|
|
1545
2438
|
executePolicyHelp,
|
|
1546
2439
|
createPolicyRequest,
|
|
1547
2440
|
fetchPendingMessages,
|
|
1548
|
-
ping
|
|
2441
|
+
ping,
|
|
2442
|
+
subagentSpawn,
|
|
2443
|
+
subagentSend,
|
|
2444
|
+
subagentWait,
|
|
2445
|
+
subagentStop,
|
|
2446
|
+
subagentDelete,
|
|
2447
|
+
subagentList,
|
|
2448
|
+
subagentTail
|
|
1549
2449
|
});
|
|
1550
2450
|
|
|
1551
2451
|
//#endregion
|
|
@@ -1644,6 +2544,20 @@ async function initDaemon() {
|
|
|
1644
2544
|
resolve();
|
|
1645
2545
|
});
|
|
1646
2546
|
});
|
|
2547
|
+
const cleanOrphanedSubagents = async () => {
|
|
2548
|
+
try {
|
|
2549
|
+
const chats = await listChats();
|
|
2550
|
+
for (const chatId of chats) await updateChatSettings(chatId, (settings) => {
|
|
2551
|
+
if (settings.subagents) {
|
|
2552
|
+
for (const subagent of Object.values(settings.subagents)) if (subagent.status === "active") subagent.status = "failed";
|
|
2553
|
+
}
|
|
2554
|
+
return settings;
|
|
2555
|
+
});
|
|
2556
|
+
} catch (err) {
|
|
2557
|
+
console.warn("Failed to clean orphaned subagents:", err);
|
|
2558
|
+
}
|
|
2559
|
+
};
|
|
2560
|
+
await cleanOrphanedSubagents();
|
|
1647
2561
|
await runHooks("up");
|
|
1648
2562
|
isReady = true;
|
|
1649
2563
|
readyPromiseResolve();
|