clawmini 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +398 -193
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.d.mts +5 -0
- package/dist/adapter-google-chat/index.d.mts.map +1 -0
- package/dist/adapter-google-chat/index.mjs +1077 -0
- package/dist/adapter-google-chat/index.mjs.map +1 -0
- package/dist/cli/index.mjs +107 -14
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +175 -16
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/propose-policy.d.mts +1 -0
- package/dist/cli/propose-policy.mjs +7159 -0
- package/dist/cli/propose-policy.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +1427 -513
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{lite-oSYSvaOr.mjs → lite-CBxOT1y5.mjs} +101 -24
- package/dist/lite-CBxOT1y5.mjs.map +1 -0
- package/dist/routing-D8rTxtaV.mjs +245 -0
- package/dist/routing-D8rTxtaV.mjs.map +1 -0
- package/dist/web/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/dist/web/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/dist/web/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/8YNcRyEk.js → dist/web/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/dist/web/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/dist/web/_app/immutable/chunks/CME08kGM.js +1 -0
- package/dist/web/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/dist/web/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/dist/web/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/dist/web/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/entry/app.DO5eYwVz.js → dist/web/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/dist/web/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/dist/web/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/dist/web/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/dist/web/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/dist/web/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/{workspace-DjoNjhW0.mjs → workspace-BJmJBfKi.mjs} +103 -11
- package/dist/workspace-BJmJBfKi.mjs.map +1 -0
- package/docs/14_google_chat_adapter/development_log.md +40 -0
- package/docs/14_google_chat_adapter/notes.md +28 -0
- package/docs/14_google_chat_adapter/prd.md +35 -0
- package/docs/14_google_chat_adapter/questions.md +9 -0
- package/docs/14_google_chat_adapter/tickets.md +117 -0
- package/docs/15_sandbox_policies/tickets.md +33 -0
- package/docs/16_session_timeout/development_log.md +20 -0
- package/docs/16_session_timeout/notes.md +44 -0
- package/docs/16_session_timeout/prd.md +106 -0
- package/docs/16_session_timeout/questions.md +10 -0
- package/docs/16_session_timeout/tickets.md +64 -0
- package/docs/17_auto_approve_policy/development_log.md +29 -0
- package/docs/17_auto_approve_policy/notes.md +25 -0
- package/docs/17_auto_approve_policy/prd.md +34 -0
- package/docs/17_auto_approve_policy/questions.md +10 -0
- package/docs/17_auto_approve_policy/tickets.md +11 -0
- package/docs/18_clawmini_skills/development_log.md +36 -0
- package/docs/18_clawmini_skills/notes.md +8 -0
- package/docs/18_clawmini_skills/prd.md +45 -0
- package/docs/18_clawmini_skills/questions.md +10 -0
- package/docs/18_clawmini_skills/tickets.md +55 -0
- package/docs/19_subagents/development_log.md +69 -0
- package/docs/19_subagents/notes.md +18 -0
- package/docs/19_subagents/prd.md +156 -0
- package/docs/19_subagents/questions.md +13 -0
- package/docs/19_subagents/tickets.md +113 -0
- package/docs/20_chat_logs_cleanup/development_log.md +50 -0
- package/docs/20_chat_logs_cleanup/notes.md +43 -0
- package/docs/20_chat_logs_cleanup/prd.md +232 -0
- package/docs/20_chat_logs_cleanup/questions.md +2 -0
- package/docs/20_chat_logs_cleanup/tickets.md +98 -0
- package/docs/20_webui_markdown/development_log.md +36 -0
- package/docs/20_webui_markdown/notes.md +23 -0
- package/docs/20_webui_markdown/prd.md +49 -0
- package/docs/20_webui_markdown/questions.md +10 -0
- package/docs/20_webui_markdown/tickets.md +55 -0
- package/docs/21_adapter_filtering/development_log.md +29 -0
- package/docs/21_adapter_filtering/notes.md +25 -0
- package/docs/21_adapter_filtering/prd.md +44 -0
- package/docs/21_adapter_filtering/questions.md +12 -0
- package/docs/21_adapter_filtering/tickets.md +38 -0
- package/docs/21_built_in_routers/development_log.md +17 -0
- package/docs/21_built_in_routers/notes.md +27 -0
- package/docs/21_built_in_routers/prd.md +34 -0
- package/docs/21_built_in_routers/questions.md +4 -0
- package/docs/21_built_in_routers/tickets.md +25 -0
- package/docs/21_fancy_policies/development_log.md +38 -0
- package/docs/21_fancy_policies/notes.md +27 -0
- package/docs/21_fancy_policies/prd.md +58 -0
- package/docs/21_fancy_policies/questions.md +6 -0
- package/docs/21_fancy_policies/tickets.md +48 -0
- package/docs/22_adapter_multi_chat/development_log.md +76 -0
- package/docs/22_adapter_multi_chat/notes.md +42 -0
- package/docs/22_adapter_multi_chat/prd.md +76 -0
- package/docs/22_adapter_multi_chat/questions.md +16 -0
- package/docs/22_adapter_multi_chat/tickets.md +164 -0
- package/docs/23_custom_token_env/development_log.md +31 -0
- package/docs/23_custom_token_env/notes.md +16 -0
- package/docs/23_custom_token_env/prd.md +42 -0
- package/docs/23_custom_token_env/questions.md +8 -0
- package/docs/23_custom_token_env/tickets.md +54 -0
- package/docs/guides/discord_adapter_setup.md +15 -2
- package/docs/guides/google_chat_adapter_setup.md +145 -0
- package/napkin.md +5 -0
- package/package.json +7 -2
- package/src/adapter-discord/config.test.ts +27 -8
- package/src/adapter-discord/config.ts +6 -8
- package/src/adapter-discord/forwarder.test.ts +307 -114
- package/src/adapter-discord/forwarder.ts +260 -75
- package/src/adapter-discord/index.test.ts +278 -0
- package/src/adapter-discord/index.ts +160 -30
- package/src/adapter-discord/interactions.test.ts +96 -0
- package/src/adapter-discord/interactions.ts +156 -0
- package/src/adapter-discord/state.test.ts +9 -8
- package/src/adapter-discord/state.ts +51 -8
- package/src/adapter-google-chat/auth.test.ts +87 -0
- package/src/adapter-google-chat/auth.ts +132 -0
- package/src/adapter-google-chat/cards.ts +71 -0
- package/src/adapter-google-chat/client.test.ts +561 -0
- package/src/adapter-google-chat/client.ts +430 -0
- package/src/adapter-google-chat/config.test.ts +187 -0
- package/src/adapter-google-chat/config.ts +82 -0
- package/src/adapter-google-chat/cron.test.ts +143 -0
- package/src/adapter-google-chat/cron.ts +81 -0
- package/src/adapter-google-chat/forwarder.test.ts +537 -0
- package/src/adapter-google-chat/forwarder.ts +349 -0
- package/src/adapter-google-chat/index.test.ts +62 -0
- package/src/adapter-google-chat/index.ts +61 -0
- package/src/adapter-google-chat/state.test.ts +96 -0
- package/src/adapter-google-chat/state.ts +85 -0
- package/src/adapter-google-chat/subscriptions.ts +124 -0
- package/src/adapter-google-chat/upload.ts +88 -0
- package/src/adapter-google-chat/utils.test.ts +111 -0
- package/src/adapter-google-chat/utils.ts +133 -0
- package/src/cli/commands/init.ts +0 -7
- package/src/cli/commands/messages.ts +18 -3
- package/src/cli/commands/policies.ts +70 -0
- package/src/cli/commands/skills.ts +71 -0
- package/src/cli/commands/web-api/chats.ts +5 -1
- package/src/cli/e2e/basic.test.ts +1 -1
- package/src/cli/e2e/cron.test.ts +1 -1
- package/src/cli/e2e/daemon.test.ts +132 -4
- package/src/cli/e2e/export-lite-func.test.ts +54 -31
- package/src/cli/e2e/fallbacks.test.ts +8 -6
- package/src/cli/e2e/init.test.ts +7 -0
- package/src/cli/e2e/messages.test.ts +90 -55
- package/src/cli/e2e/propose-policy.test.ts +203 -0
- package/src/cli/e2e/requests.test.ts +15 -0
- package/src/cli/e2e/session-timeout.test.ts +192 -0
- package/src/cli/e2e/skills.test.ts +55 -0
- package/src/cli/e2e/slash-new.test.ts +93 -0
- package/src/cli/e2e/subagents.test.ts +106 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/lite.ts +51 -11
- package/src/cli/propose-policy.ts +91 -0
- package/src/cli/subagent-commands.ts +215 -0
- package/src/daemon/agent/agent-context.ts +89 -0
- package/src/daemon/agent/agent-extractors.ts +68 -0
- package/src/daemon/agent/agent-runner.ts +153 -0
- package/src/daemon/agent/agent-session.ts +261 -0
- package/src/daemon/agent/chat-logger.test.ts +158 -0
- package/src/daemon/agent/chat-logger.ts +188 -0
- package/src/daemon/agent/task-scheduler.test.ts +202 -0
- package/src/daemon/agent/task-scheduler.ts +276 -0
- package/src/daemon/agent/types.ts +84 -0
- package/src/daemon/agent/utils.ts +7 -0
- package/src/daemon/api/agent-router.ts +166 -18
- package/src/daemon/api/index.test.ts +50 -18
- package/src/daemon/api/policy-request.test.ts +39 -2
- package/src/daemon/api/subagent-router.test.ts +108 -0
- package/src/daemon/api/subagent-router.ts +296 -0
- package/src/daemon/api/subagent-utils.test.ts +56 -0
- package/src/daemon/api/subagent-utils.ts +130 -0
- package/src/daemon/api/user-router.ts +30 -13
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/chats.ts +6 -0
- package/src/daemon/cron.test.ts +66 -1
- package/src/daemon/cron.ts +35 -8
- package/src/daemon/index.ts +23 -0
- package/src/daemon/message-agent.test.ts +11 -25
- package/src/daemon/message-extraction.test.ts +10 -27
- package/src/daemon/message-fallbacks.test.ts +13 -35
- package/src/daemon/message-interruption.test.ts +70 -53
- package/src/daemon/message-jobs.test.ts +138 -0
- package/src/daemon/message-queue.test.ts +30 -43
- package/src/daemon/message-router.test.ts +12 -11
- package/src/daemon/message-session.test.ts +41 -28
- package/src/daemon/message-typing.test.ts +19 -6
- package/src/daemon/message.ts +103 -515
- package/src/daemon/policy-request-service.ts +8 -3
- package/src/daemon/policy-utils.ts +19 -1
- package/src/daemon/queue.ts +16 -0
- package/src/daemon/request-store.test.ts +4 -0
- package/src/daemon/routers/session-timeout.test.ts +122 -0
- package/src/daemon/routers/session-timeout.ts +71 -0
- package/src/daemon/routers/slash-new.ts +3 -1
- package/src/daemon/routers/slash-policies.test.ts +26 -13
- package/src/daemon/routers/slash-policies.ts +39 -29
- package/src/daemon/routers/types.ts +8 -0
- package/src/daemon/routers.ts +64 -2
- package/src/daemon/utils/spawn.ts +6 -8
- package/src/shared/adapters/commands.test.ts +155 -0
- package/src/shared/adapters/commands.ts +125 -0
- package/src/shared/adapters/filtering.test.ts +111 -0
- package/src/shared/adapters/filtering.ts +57 -0
- package/src/shared/adapters/routing.test.ts +144 -0
- package/src/shared/adapters/routing.ts +109 -0
- package/src/shared/agent-utils.ts +10 -0
- package/src/shared/chats.test.ts +145 -3
- package/src/shared/chats.ts +215 -18
- package/src/shared/config.ts +67 -15
- package/src/shared/lite.ts +22 -18
- package/src/shared/policies.ts +7 -0
- package/src/shared/workspace.test.ts +45 -1
- package/src/shared/workspace.ts +119 -6
- package/templates/debug/settings.json +5 -2
- package/templates/environments/cladding/env.json +2 -2
- package/templates/gemini/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini/.gemini/hooks/clawmini-logging.sh +17 -0
- package/templates/gemini/.gemini/hooks/insert-pending.sh +9 -0
- package/templates/gemini/.gemini/settings.json +50 -0
- package/templates/gemini/settings.json +22 -8
- package/templates/gemini-claw/.gemini/base-system.md +100 -0
- package/templates/gemini-claw/.gemini/hooks/check-subagents.mjs +23 -0
- package/templates/gemini-claw/.gemini/hooks/clawmini-logging.sh +1 -1
- package/templates/gemini-claw/.gemini/settings.json +13 -0
- package/templates/gemini-claw/.gemini/subagent-system.md +7 -0
- package/templates/gemini-claw/.gemini/system.md +3 -99
- package/templates/gemini-claw/settings.json +27 -22
- package/templates/skills/clawmini-requests/SKILL.md +92 -0
- package/templates/skills/clawmini-subagents/SKILL.md +79 -0
- package/templates/skills/skill-creator/SKILL.md +60 -0
- package/tsdown.config.ts +10 -1
- package/web/.svelte-kit/generated/server/internal.js +2 -1
- package/web/.svelte-kit/non-ambient.d.ts +2 -0
- package/web/.svelte-kit/output/client/.vite/manifest.json +141 -138
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/assets/4.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Dc-UOHw9.js → BmRlVmv6.js} +1 -1
- package/{dist/web/_app/immutable/chunks/8YNcRyEk.js → web/.svelte-kit/output/client/_app/immutable/chunks/C20lZMGz.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C9lbZ-kT.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{BPy8HLo7.js → Ck-be5J2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{B8yYFADm.js → DhD271EB.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{DcrmIfTj.js → DpuLqk8d.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{ZkLyk0mE.js → Drm9vgeP.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{CyNaE55B.js → Zeh-C-mx.js} +1 -1
- package/{dist/web/_app/immutable/entry/app.DO5eYwVz.js → web/.svelte-kit/output/client/_app/immutable/entry/app.BgB5VkRU.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.DuxJo6av.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.C9oFZP9h.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.BON2Wk6k.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.CK3CLC0f.js → 2.BnwnD1Ki.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{3.ncP0xLO6.js → 3.CIs4tjjw.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.DLarELN4.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BpJUN6QH.js → 5.CE_QKy_3.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -3
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.C-4eziNy.css +1 -0
- package/web/.svelte-kit/output/server/_app/immutable/assets/_page.Cc_xwLNl.css +1 -0
- package/web/.svelte-kit/output/server/chunks/app-state.svelte.js +5 -0
- package/web/.svelte-kit/output/server/chunks/bot.js +4 -4
- package/web/.svelte-kit/output/server/chunks/client.js +2 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +0 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +2 -1
- package/web/.svelte-kit/output/server/chunks/root.js +482 -392
- package/web/.svelte-kit/output/server/entries/pages/_layout.svelte.js +57 -7
- package/web/.svelte-kit/output/server/entries/pages/chats/_id_/_page.svelte.js +234 -9
- package/web/.svelte-kit/output/server/index.js +82 -10
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +2 -2
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +2 -2
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/types/src/routes/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/agents/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/$types.d.ts +1 -2
- package/web/.svelte-kit/types/src/routes/chats/[id]/settings/$types.d.ts +1 -2
- package/web/package.json +8 -0
- package/web/src/lib/app-state.svelte.ts +5 -1
- package/web/src/lib/components/app/markdown-renderer.svelte +56 -0
- package/web/src/lib/components/app/markdown-renderer.svelte.spec.ts +44 -0
- package/web/src/lib/components/app/message-content.svelte +16 -0
- package/web/src/lib/types.ts +67 -3
- package/web/src/routes/+layout.svelte +31 -1
- package/web/src/routes/chats/[id]/+page.svelte +167 -18
- package/web/src/routes/chats/[id]/page.svelte.spec.ts +58 -7
- package/dist/lite-oSYSvaOr.mjs.map +0 -1
- package/dist/web/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/dist/web/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/dist/web/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/dist/web/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/dist/web/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/dist/web/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/dist/web/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/dist/web/_app/immutable/chunks/DQoygso7.js +0 -1
- package/dist/web/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/dist/web/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/dist/web/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/dist/workspace-DjoNjhW0.mjs.map +0 -1
- package/src/daemon/message-verbosity.test.ts +0 -127
- package/web/.svelte-kit/output/client/_app/immutable/assets/0.GI4C4dpV.css +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B5abRDXp.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bi0jeV7Q.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmUXQ3wy.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/C3k55nDF.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CpaGRn9L.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DG5RZBw-.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DQoygso7.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D48mVn1m.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/0.B-0CcADM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.FixKgvRO.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CQYJEgv8.js +0 -1
- package/web/.svelte-kit/output/server/_app/immutable/assets/_layout.GI4C4dpV.css +0 -1
- /package/templates/{gemini-claw/.gemini/skills → skills}/clawmini-jobs/SKILL.md +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { handleAdapterCommand, type CommandTrpcClient } from './commands.js';
|
|
3
|
+
|
|
4
|
+
describe('Adapter Commands', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.clearAllMocks();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const mockTrpcClient = {
|
|
10
|
+
getMessages: {
|
|
11
|
+
query: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it('should handle /show all', async () => {
|
|
16
|
+
const config = { filters: {} };
|
|
17
|
+
const result = await handleAdapterCommand(
|
|
18
|
+
'/show all',
|
|
19
|
+
config,
|
|
20
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
21
|
+
'chat-1'
|
|
22
|
+
);
|
|
23
|
+
expect(result).toEqual({
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: 'Configuration updated: Showing all messages.',
|
|
26
|
+
newConfig: expect.any(Object),
|
|
27
|
+
});
|
|
28
|
+
expect((result as Extract<typeof result, { type: 'text' }>).newConfig?.filters).toEqual({
|
|
29
|
+
subagent: true,
|
|
30
|
+
command: true,
|
|
31
|
+
system: true,
|
|
32
|
+
tool: true,
|
|
33
|
+
policy: true,
|
|
34
|
+
subagent_status: true,
|
|
35
|
+
legacy_log: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should handle /hide all', async () => {
|
|
40
|
+
const config = { filters: { tool: true } };
|
|
41
|
+
const result = await handleAdapterCommand(
|
|
42
|
+
'/hide all',
|
|
43
|
+
config,
|
|
44
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
45
|
+
'chat-1'
|
|
46
|
+
);
|
|
47
|
+
expect(result).toEqual({
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: 'Configuration updated: Hidden all overrides (using defaults).',
|
|
50
|
+
newConfig: expect.any(Object),
|
|
51
|
+
});
|
|
52
|
+
expect((result as Extract<typeof result, { type: 'text' }>).newConfig?.filters).toEqual({});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle /show with no arguments', async () => {
|
|
56
|
+
const config = { filters: {} };
|
|
57
|
+
const result = await handleAdapterCommand(
|
|
58
|
+
'/show',
|
|
59
|
+
config,
|
|
60
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
61
|
+
'chat-1'
|
|
62
|
+
);
|
|
63
|
+
expect(result?.type).toBe('text');
|
|
64
|
+
expect((result as { text: string }).text).toContain('Valid options for /show:');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should handle /hide with no arguments', async () => {
|
|
68
|
+
const config = { filters: {} };
|
|
69
|
+
const result = await handleAdapterCommand(
|
|
70
|
+
'/hide',
|
|
71
|
+
config,
|
|
72
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
73
|
+
'chat-1'
|
|
74
|
+
);
|
|
75
|
+
expect(result?.type).toBe('text');
|
|
76
|
+
expect((result as { text: string }).text).toContain('Valid options for /hide:');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle /show <role>', async () => {
|
|
80
|
+
const config = { filters: {} };
|
|
81
|
+
const result = await handleAdapterCommand(
|
|
82
|
+
'/show tool',
|
|
83
|
+
config,
|
|
84
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
85
|
+
'chat-1'
|
|
86
|
+
);
|
|
87
|
+
expect(result).toEqual({
|
|
88
|
+
type: 'text',
|
|
89
|
+
text: "Configuration updated: Showing messages for 'tool'.",
|
|
90
|
+
newConfig: expect.any(Object),
|
|
91
|
+
});
|
|
92
|
+
expect((result as Extract<typeof result, { type: 'text' }>).newConfig?.filters).toEqual({
|
|
93
|
+
tool: true,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should handle /hide <role>', async () => {
|
|
98
|
+
const config = { filters: { command: true } };
|
|
99
|
+
const result = await handleAdapterCommand(
|
|
100
|
+
'/hide subagent',
|
|
101
|
+
config,
|
|
102
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
103
|
+
'chat-1'
|
|
104
|
+
);
|
|
105
|
+
expect(result).toEqual({
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: "Configuration updated: Hiding messages for 'subagent'.",
|
|
108
|
+
newConfig: expect.any(Object),
|
|
109
|
+
});
|
|
110
|
+
expect((result as Extract<typeof result, { type: 'text' }>).newConfig?.filters).toEqual({
|
|
111
|
+
command: true,
|
|
112
|
+
subagent: false,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle /debug <N>', async () => {
|
|
117
|
+
const config = { filters: {} }; // defaults
|
|
118
|
+
const mockMessages = [
|
|
119
|
+
{ id: '1', role: 'user', content: 'hello' }, // user without subagentId (displayed by default, excluded)
|
|
120
|
+
{ id: '2', role: 'agent', content: 'hidden agent', subagentId: 'sub1' }, // agent with subagentId (ignored by default)
|
|
121
|
+
{ id: '3', role: 'agent', content: 'visible agent' }, // agent without subagentId (displayed by default)
|
|
122
|
+
{ id: '4', role: 'agent', content: 'hidden agent 2', subagentId: 'sub2' }, // agent with subagentId (ignored by default)
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
mockTrpcClient.getMessages.query.mockResolvedValueOnce(mockMessages);
|
|
126
|
+
|
|
127
|
+
const result = await handleAdapterCommand(
|
|
128
|
+
'/debug 2',
|
|
129
|
+
config,
|
|
130
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
131
|
+
'chat-1'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(mockTrpcClient.getMessages.query).toHaveBeenCalledWith({ chatId: 'chat-1', limit: 20 });
|
|
135
|
+
|
|
136
|
+
expect(result).toEqual({
|
|
137
|
+
type: 'debug',
|
|
138
|
+
messages: [
|
|
139
|
+
{ id: '2', role: 'agent', content: 'hidden agent', subagentId: 'sub1' },
|
|
140
|
+
{ id: '4', role: 'agent', content: 'hidden agent 2', subagentId: 'sub2' },
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should return null for non-commands', async () => {
|
|
146
|
+
const config = { filters: {} };
|
|
147
|
+
const result = await handleAdapterCommand(
|
|
148
|
+
'hello world',
|
|
149
|
+
config,
|
|
150
|
+
mockTrpcClient as unknown as CommandTrpcClient,
|
|
151
|
+
'chat-1'
|
|
152
|
+
);
|
|
153
|
+
expect(result).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { shouldDisplayMessage, type FilteringConfig } from './filtering.js';
|
|
2
|
+
import type { ChatMessage } from '../chats.js';
|
|
3
|
+
|
|
4
|
+
export interface CommandTrpcClient {
|
|
5
|
+
getMessages: {
|
|
6
|
+
query: (args: { chatId: string; limit: number }) => Promise<ChatMessage[]>;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const VALID_ROLES = new Set([
|
|
11
|
+
'subagent',
|
|
12
|
+
'command',
|
|
13
|
+
'system',
|
|
14
|
+
'tool',
|
|
15
|
+
'policy',
|
|
16
|
+
'subagent_status',
|
|
17
|
+
'legacy_log',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export type AdapterCommandResult =
|
|
21
|
+
| { type: 'text'; text: string; newConfig?: FilteringConfig }
|
|
22
|
+
| { type: 'debug'; messages: ChatMessage[] }
|
|
23
|
+
| null;
|
|
24
|
+
|
|
25
|
+
export async function handleAdapterCommand(
|
|
26
|
+
content: string,
|
|
27
|
+
config: FilteringConfig,
|
|
28
|
+
trpcClient: CommandTrpcClient,
|
|
29
|
+
chatId: string
|
|
30
|
+
): Promise<AdapterCommandResult> {
|
|
31
|
+
const trimmed = content.trim();
|
|
32
|
+
|
|
33
|
+
if (trimmed === '/show all') {
|
|
34
|
+
const newConfig: FilteringConfig = { filters: { ...config.filters } };
|
|
35
|
+
for (const role of VALID_ROLES) {
|
|
36
|
+
newConfig.filters![role] = true;
|
|
37
|
+
}
|
|
38
|
+
return { type: 'text', text: 'Configuration updated: Showing all messages.', newConfig };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (trimmed === '/hide all') {
|
|
42
|
+
return {
|
|
43
|
+
type: 'text',
|
|
44
|
+
text: 'Configuration updated: Hidden all overrides (using defaults).',
|
|
45
|
+
newConfig: { filters: {} },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (trimmed === '/show' || trimmed === '/hide') {
|
|
50
|
+
return {
|
|
51
|
+
type: 'text',
|
|
52
|
+
text: `Valid options for ${trimmed}: ${Array.from(VALID_ROLES).join(', ')}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (trimmed.startsWith('/show ')) {
|
|
57
|
+
const role = trimmed.slice(6).trim();
|
|
58
|
+
if (!VALID_ROLES.has(role)) {
|
|
59
|
+
return {
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: `Error: '${role}' is not a valid message role or special value. Valid options: ${Array.from(VALID_ROLES).join(', ')}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: `Configuration updated: Showing messages for '${role}'.`,
|
|
67
|
+
newConfig: { filters: { ...config.filters, [role]: true } },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (trimmed.startsWith('/hide ')) {
|
|
72
|
+
const role = trimmed.slice(6).trim();
|
|
73
|
+
if (!VALID_ROLES.has(role)) {
|
|
74
|
+
return {
|
|
75
|
+
type: 'text',
|
|
76
|
+
text: `Error: '${role}' is not a valid message role or special value. Valid options: ${Array.from(VALID_ROLES).join(', ')}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
type: 'text',
|
|
81
|
+
text: `Configuration updated: Hiding messages for '${role}'.`,
|
|
82
|
+
newConfig: { filters: { ...config.filters, [role]: false } },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (trimmed === '/debug' || trimmed.startsWith('/debug ')) {
|
|
87
|
+
const match = trimmed.match(/^\/debug\s+(\d+)$/);
|
|
88
|
+
const limit = match ? parseInt(match[1] as string, 10) : 5;
|
|
89
|
+
|
|
90
|
+
// Fetch recent messages
|
|
91
|
+
// Fetch a larger batch since we need to filter them down
|
|
92
|
+
const messages: ChatMessage[] = await trpcClient.getMessages.query({
|
|
93
|
+
chatId,
|
|
94
|
+
limit: limit * 10,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const ignoredMessages: ChatMessage[] = [];
|
|
98
|
+
|
|
99
|
+
// Iterating backwards (newest to oldest)
|
|
100
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
101
|
+
const msg = messages[i];
|
|
102
|
+
if (!msg) continue;
|
|
103
|
+
|
|
104
|
+
// Exclude user messages without subagentIds
|
|
105
|
+
const isUserWithoutSubagent =
|
|
106
|
+
(msg.role === 'user' || msg.displayRole === 'user') && !msg.subagentId;
|
|
107
|
+
if (isUserWithoutSubagent) continue;
|
|
108
|
+
|
|
109
|
+
const isDisplayed = shouldDisplayMessage(msg, config);
|
|
110
|
+
if (!isDisplayed) {
|
|
111
|
+
ignoredMessages.push(msg);
|
|
112
|
+
if (ignoredMessages.length >= limit) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Reverse back to chronological order
|
|
119
|
+
ignoredMessages.reverse();
|
|
120
|
+
|
|
121
|
+
return { type: 'debug', messages: ignoredMessages };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { shouldDisplayMessage, formatMessage } from './filtering.js';
|
|
3
|
+
import type { ChatMessage } from '../chats.js';
|
|
4
|
+
|
|
5
|
+
describe('shouldDisplayMessage', () => {
|
|
6
|
+
const defaultConfig = {};
|
|
7
|
+
|
|
8
|
+
it('hides messages with subagentId if subagent is not explicitly true', () => {
|
|
9
|
+
const msg: ChatMessage = {
|
|
10
|
+
id: '1',
|
|
11
|
+
role: 'agent',
|
|
12
|
+
content: 'hello',
|
|
13
|
+
subagentId: 'sub1',
|
|
14
|
+
timestamp: '',
|
|
15
|
+
};
|
|
16
|
+
expect(shouldDisplayMessage(msg, defaultConfig)).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('hides standard user messages without subagentId', () => {
|
|
20
|
+
const msg: ChatMessage = { id: '1', role: 'user', content: 'hello', timestamp: '' };
|
|
21
|
+
expect(shouldDisplayMessage(msg, defaultConfig)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('displays standard agent messages without subagentId', () => {
|
|
25
|
+
const msg: ChatMessage = { id: '1', role: 'agent', content: 'hello', timestamp: '' };
|
|
26
|
+
expect(shouldDisplayMessage(msg, defaultConfig)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('hides non-standard messages by default', () => {
|
|
30
|
+
const msg: ChatMessage = {
|
|
31
|
+
id: '1',
|
|
32
|
+
role: 'command',
|
|
33
|
+
content: 'ls',
|
|
34
|
+
messageId: '123',
|
|
35
|
+
command: 'ls',
|
|
36
|
+
cwd: '.',
|
|
37
|
+
stdout: '',
|
|
38
|
+
stderr: '',
|
|
39
|
+
exitCode: 0,
|
|
40
|
+
timestamp: '',
|
|
41
|
+
};
|
|
42
|
+
expect(shouldDisplayMessage(msg, defaultConfig)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('displays subagent messages if subagent: true', () => {
|
|
46
|
+
const msg: ChatMessage = {
|
|
47
|
+
id: '1',
|
|
48
|
+
role: 'agent',
|
|
49
|
+
content: 'hello',
|
|
50
|
+
subagentId: 'sub1',
|
|
51
|
+
timestamp: '',
|
|
52
|
+
};
|
|
53
|
+
expect(shouldDisplayMessage(msg, { filters: { subagent: true } })).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('displays user messages with subagentId if subagent: true', () => {
|
|
57
|
+
const msg: ChatMessage = {
|
|
58
|
+
id: '2',
|
|
59
|
+
role: 'user',
|
|
60
|
+
content: 'hello subagent',
|
|
61
|
+
subagentId: 'sub1',
|
|
62
|
+
timestamp: '',
|
|
63
|
+
};
|
|
64
|
+
expect(shouldDisplayMessage(msg, { filters: { subagent: true } })).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('displays specific role if explicitly allowed', () => {
|
|
68
|
+
const msg: ChatMessage = {
|
|
69
|
+
id: '1',
|
|
70
|
+
role: 'command',
|
|
71
|
+
content: 'ls',
|
|
72
|
+
messageId: '123',
|
|
73
|
+
command: 'ls',
|
|
74
|
+
cwd: '.',
|
|
75
|
+
stdout: '',
|
|
76
|
+
stderr: '',
|
|
77
|
+
exitCode: 0,
|
|
78
|
+
timestamp: '',
|
|
79
|
+
};
|
|
80
|
+
expect(shouldDisplayMessage(msg, { filters: { command: true } })).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('formatMessage', () => {
|
|
85
|
+
it('returns content as-is for messages without subagentId', () => {
|
|
86
|
+
const msg: ChatMessage = { id: '1', role: 'agent', content: 'hello world', timestamp: '' };
|
|
87
|
+
expect(formatMessage(msg)).toBe('hello world');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('prepends [To:<id>] for user messages to subagents', () => {
|
|
91
|
+
const msg: ChatMessage = {
|
|
92
|
+
id: '1',
|
|
93
|
+
role: 'user',
|
|
94
|
+
content: 'do task',
|
|
95
|
+
subagentId: 'sub1',
|
|
96
|
+
timestamp: '',
|
|
97
|
+
};
|
|
98
|
+
expect(formatMessage(msg)).toBe('[To:sub1]\ndo task');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('prepends [From:<id>] for agent messages from subagents', () => {
|
|
102
|
+
const msg: ChatMessage = {
|
|
103
|
+
id: '1',
|
|
104
|
+
role: 'agent',
|
|
105
|
+
content: 'done',
|
|
106
|
+
subagentId: 'sub1',
|
|
107
|
+
timestamp: '',
|
|
108
|
+
};
|
|
109
|
+
expect(formatMessage(msg)).toBe('[From:sub1]\ndone');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ChatMessage } from '../chats.js';
|
|
2
|
+
|
|
3
|
+
export interface FilteringConfig {
|
|
4
|
+
filters?: Record<string, boolean> | undefined;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function shouldDisplayMessage(message: ChatMessage, config: FilteringConfig): boolean {
|
|
8
|
+
const overrides = config.filters || {};
|
|
9
|
+
|
|
10
|
+
// If the message has a subagentId, return false immediately unless subagent messages are allowed.
|
|
11
|
+
if (message.subagentId && overrides['subagent'] !== true) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Then check if it's a standard agent message (via role/displayRole) and always return true if so.
|
|
16
|
+
const isStandardAgent =
|
|
17
|
+
message.displayRole === 'agent' ||
|
|
18
|
+
message.role === 'agent' ||
|
|
19
|
+
message.role === 'legacy_log' ||
|
|
20
|
+
(message.role === 'policy' && message.status === 'pending');
|
|
21
|
+
|
|
22
|
+
if (isStandardAgent) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Then check if it's a user message directed to a subagent, if subagent messages are allowed
|
|
27
|
+
if (
|
|
28
|
+
message.subagentId &&
|
|
29
|
+
overrides['subagent'] === true &&
|
|
30
|
+
(message.role === 'user' || message.displayRole === 'user')
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Finally, check if the role is allowed and forward it if so.
|
|
36
|
+
if (
|
|
37
|
+
overrides[message.role] === true ||
|
|
38
|
+
(message.displayRole && overrides[message.displayRole] === true)
|
|
39
|
+
) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function formatMessage(message: ChatMessage): string {
|
|
47
|
+
if (!message.subagentId) {
|
|
48
|
+
return message.content;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isToSubagent = message.role === 'user' || message.displayRole === 'user';
|
|
52
|
+
if (isToSubagent) {
|
|
53
|
+
return `[To:${message.subagentId}]\n${message.content}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `[From:${message.subagentId}]\n${message.content}`;
|
|
57
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { handleRoutingCommand, type RoutingTrpcClient } from './routing.js';
|
|
3
|
+
|
|
4
|
+
describe('handleRoutingCommand', () => {
|
|
5
|
+
let mockTrpcClient: RoutingTrpcClient;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockTrpcClient = {
|
|
9
|
+
getChats: { query: vi.fn().mockResolvedValue(['chat-1', 'chat-2']) },
|
|
10
|
+
getAgents: { query: vi.fn().mockResolvedValue(['agent-1', 'agent-2']) },
|
|
11
|
+
createChat: { mutate: vi.fn().mockResolvedValue({ success: true, chatId: 'new-chat' }) },
|
|
12
|
+
sendMessage: { mutate: vi.fn().mockResolvedValue({ success: true }) },
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('/chat', () => {
|
|
17
|
+
it('should list available chats if no id provided', async () => {
|
|
18
|
+
const result = await handleRoutingCommand('/chat', 'ext-1', {}, 'discord', mockTrpcClient);
|
|
19
|
+
expect(result).toEqual({
|
|
20
|
+
type: 'reply',
|
|
21
|
+
text: 'Available chats:\n- chat-1\n- chat-2\n\nPlease specify a valid chat ID: `/chat [chat-id]`',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should list available chats if invalid id provided', async () => {
|
|
26
|
+
const result = await handleRoutingCommand(
|
|
27
|
+
'/chat chat-3',
|
|
28
|
+
'ext-1',
|
|
29
|
+
{},
|
|
30
|
+
'discord',
|
|
31
|
+
mockTrpcClient
|
|
32
|
+
);
|
|
33
|
+
expect(result).toEqual({
|
|
34
|
+
type: 'reply',
|
|
35
|
+
text: 'Available chats:\n- chat-1\n- chat-2\n\nPlease specify a valid chat ID: `/chat [chat-id]`',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return error if chat is already mapped to another channel', async () => {
|
|
40
|
+
const result = await handleRoutingCommand(
|
|
41
|
+
'/chat chat-1',
|
|
42
|
+
'ext-1',
|
|
43
|
+
{ 'ext-2': 'chat-1' },
|
|
44
|
+
'discord',
|
|
45
|
+
mockTrpcClient
|
|
46
|
+
);
|
|
47
|
+
expect(result).toEqual({
|
|
48
|
+
type: 'reply',
|
|
49
|
+
text: 'Error: Chat `chat-1` is already mapped to another channel/space. Strict 1:1 mapping is required.',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should map successfully if chat is valid and unmapped', async () => {
|
|
54
|
+
const result = await handleRoutingCommand(
|
|
55
|
+
'/chat chat-1',
|
|
56
|
+
'ext-1',
|
|
57
|
+
{},
|
|
58
|
+
'discord',
|
|
59
|
+
mockTrpcClient
|
|
60
|
+
);
|
|
61
|
+
expect(result).toEqual({
|
|
62
|
+
type: 'mapped',
|
|
63
|
+
text: 'Successfully mapped this channel/space to chat `chat-1`.',
|
|
64
|
+
newChatId: 'chat-1',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should map successfully if chat is already mapped to the same channel', async () => {
|
|
69
|
+
const result = await handleRoutingCommand(
|
|
70
|
+
'/chat chat-1',
|
|
71
|
+
'ext-1',
|
|
72
|
+
{ 'ext-1': 'chat-1' },
|
|
73
|
+
'discord',
|
|
74
|
+
mockTrpcClient
|
|
75
|
+
);
|
|
76
|
+
expect(result).toEqual({
|
|
77
|
+
type: 'mapped',
|
|
78
|
+
text: 'Successfully mapped this channel/space to chat `chat-1`.',
|
|
79
|
+
newChatId: 'chat-1',
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('/agent', () => {
|
|
85
|
+
it('should list available agents if no id provided', async () => {
|
|
86
|
+
const result = await handleRoutingCommand('/agent', 'ext-1', {}, 'discord', mockTrpcClient);
|
|
87
|
+
expect(result).toEqual({
|
|
88
|
+
type: 'reply',
|
|
89
|
+
text: 'Available agents:\n- agent-1\n- agent-2\n\nPlease specify a valid agent ID: `/agent [agent-id]`',
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should create new chat and map it if agent is valid', async () => {
|
|
94
|
+
const result = await handleRoutingCommand(
|
|
95
|
+
'/agent agent-1',
|
|
96
|
+
'ext-1',
|
|
97
|
+
{},
|
|
98
|
+
'discord',
|
|
99
|
+
mockTrpcClient
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(mockTrpcClient.createChat.mutate).toHaveBeenCalledWith({
|
|
103
|
+
chatId: 'agent-1-discord',
|
|
104
|
+
agent: 'agent-1',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
type: 'mapped',
|
|
109
|
+
text: 'Successfully created new chat `agent-1-discord` with agent `agent-1` and mapped it to this channel/space.',
|
|
110
|
+
newChatId: 'agent-1-discord',
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should append counter if base chat id exists', async () => {
|
|
115
|
+
mockTrpcClient.getChats.query = vi
|
|
116
|
+
.fn()
|
|
117
|
+
.mockResolvedValue(['agent-1-discord', 'agent-1-discord-1']);
|
|
118
|
+
|
|
119
|
+
const result = await handleRoutingCommand(
|
|
120
|
+
'/agent agent-1',
|
|
121
|
+
'ext-1',
|
|
122
|
+
{},
|
|
123
|
+
'discord',
|
|
124
|
+
mockTrpcClient
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
expect(mockTrpcClient.createChat.mutate).toHaveBeenCalledWith({
|
|
128
|
+
chatId: 'agent-1-discord-2',
|
|
129
|
+
agent: 'agent-1',
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(result).toEqual({
|
|
133
|
+
type: 'mapped',
|
|
134
|
+
text: 'Successfully created new chat `agent-1-discord-2` with agent `agent-1` and mapped it to this channel/space.',
|
|
135
|
+
newChatId: 'agent-1-discord-2',
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should return null for non-routing commands', async () => {
|
|
141
|
+
const result = await handleRoutingCommand('/filter', 'ext-1', {}, 'discord', mockTrpcClient);
|
|
142
|
+
expect(result).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export type RoutingTrpcClient = {
|
|
2
|
+
getChats: { query: () => Promise<string[]> };
|
|
3
|
+
getAgents: { query: () => Promise<string[]> };
|
|
4
|
+
createChat: {
|
|
5
|
+
mutate: (args: {
|
|
6
|
+
chatId: string;
|
|
7
|
+
agent?: string;
|
|
8
|
+
}) => Promise<{ success: boolean; chatId: string }>;
|
|
9
|
+
};
|
|
10
|
+
sendMessage: {
|
|
11
|
+
mutate: (args: {
|
|
12
|
+
type: 'send-message';
|
|
13
|
+
client: 'cli';
|
|
14
|
+
data: {
|
|
15
|
+
message: string;
|
|
16
|
+
chatId?: string;
|
|
17
|
+
agentId?: string;
|
|
18
|
+
adapter?: string;
|
|
19
|
+
noWait?: boolean;
|
|
20
|
+
};
|
|
21
|
+
}) => Promise<{ success: boolean }>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type RoutingCommandResult =
|
|
26
|
+
| { type: 'reply'; text: string }
|
|
27
|
+
| { type: 'mapped'; text: string; newChatId: string }
|
|
28
|
+
| null;
|
|
29
|
+
|
|
30
|
+
export async function handleRoutingCommand(
|
|
31
|
+
content: string,
|
|
32
|
+
externalContextId: string,
|
|
33
|
+
currentChannelChatMap: Record<string, string>,
|
|
34
|
+
adapterName: string,
|
|
35
|
+
trpcClient: RoutingTrpcClient
|
|
36
|
+
): Promise<RoutingCommandResult> {
|
|
37
|
+
const trimmed = content.trim();
|
|
38
|
+
|
|
39
|
+
if (trimmed.startsWith('/chat')) {
|
|
40
|
+
const args = trimmed.split(/\s+/).slice(1);
|
|
41
|
+
const chatId = args[0];
|
|
42
|
+
|
|
43
|
+
const availableChats = await trpcClient.getChats.query();
|
|
44
|
+
|
|
45
|
+
if (!chatId || !availableChats.includes(chatId)) {
|
|
46
|
+
const formattedList =
|
|
47
|
+
availableChats.length > 0
|
|
48
|
+
? availableChats.map((c) => `- ${c}`).join('\n')
|
|
49
|
+
: 'No chats available.';
|
|
50
|
+
return {
|
|
51
|
+
type: 'reply',
|
|
52
|
+
text: `Available chats:\n${formattedList}\n\nPlease specify a valid chat ID: \`/chat [chat-id]\``,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Strict 1:1 Mapping Constraint
|
|
57
|
+
for (const [channelId, mappedId] of Object.entries(currentChannelChatMap)) {
|
|
58
|
+
if (mappedId === chatId && channelId !== externalContextId) {
|
|
59
|
+
return {
|
|
60
|
+
type: 'reply',
|
|
61
|
+
text: `Error: Chat \`${chatId}\` is already mapped to another channel/space. Strict 1:1 mapping is required.`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
type: 'mapped',
|
|
68
|
+
text: `Successfully mapped this channel/space to chat \`${chatId}\`.`,
|
|
69
|
+
newChatId: chatId,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (trimmed.startsWith('/agent')) {
|
|
74
|
+
const args = trimmed.split(/\s+/).slice(1);
|
|
75
|
+
const agentId = args[0];
|
|
76
|
+
|
|
77
|
+
const availableAgents = await trpcClient.getAgents.query();
|
|
78
|
+
|
|
79
|
+
if (!agentId || !availableAgents.includes(agentId)) {
|
|
80
|
+
const formattedList =
|
|
81
|
+
availableAgents.length > 0
|
|
82
|
+
? availableAgents.map((a) => `- ${a}`).join('\n')
|
|
83
|
+
: 'No agents available.';
|
|
84
|
+
return {
|
|
85
|
+
type: 'reply',
|
|
86
|
+
text: `Available agents:\n${formattedList}\n\nPlease specify a valid agent ID: \`/agent [agent-id]\``,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const availableChats = await trpcClient.getChats.query();
|
|
91
|
+
let newChatId = `${agentId}-${adapterName}`;
|
|
92
|
+
let counter = 1;
|
|
93
|
+
|
|
94
|
+
while (availableChats.includes(newChatId)) {
|
|
95
|
+
newChatId = `${agentId}-${adapterName}-${counter}`;
|
|
96
|
+
counter++;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await trpcClient.createChat.mutate({ chatId: newChatId, agent: agentId });
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
type: 'mapped',
|
|
103
|
+
text: `Successfully created new chat \`${newChatId}\` with agent \`${agentId}\` and mapped it to this channel/space.`,
|
|
104
|
+
newChatId,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
}
|