crewswarm 0.8.1-beta
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/.env.example +155 -0
- package/LICENSE +21 -0
- package/README.md +316 -0
- package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js +1 -0
- package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +1 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/components-CSUb80ze.js +1 -0
- package/apps/dashboard/dist/assets/components-CSUb80ze.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +1 -0
- package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css +1 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/index-Px49zu76.js +2 -0
- package/apps/dashboard/dist/assets/index-Px49zu76.js.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js +1 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js +1 -0
- package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js +1 -0
- package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-DfCuAClu.js +1 -0
- package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js +1 -0
- package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js +1 -0
- package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js +1 -0
- package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js +1 -0
- package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js +1 -0
- package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js +1 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js +1 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js +1 -0
- package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js +1 -0
- package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js +1 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js +1 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js +1 -0
- package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js +1 -0
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js +1 -0
- package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js +1 -0
- package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js +1 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js +1 -0
- package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js.br +0 -0
- package/apps/dashboard/dist/favicon.png +0 -0
- package/apps/dashboard/dist/index.html +6466 -0
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/dashboard/dist/signup.html +446 -0
- package/apps/dashboard/index.html +6442 -0
- package/apps/dashboard/package.json +15 -0
- package/apps/dashboard/src/app.js +2823 -0
- package/apps/dashboard/src/app.js.br +0 -0
- package/apps/dashboard/src/app.js.gz +0 -0
- package/apps/dashboard/src/chat/chat-actions.js +1847 -0
- package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
- package/apps/dashboard/src/chat/unified-messages.js +327 -0
- package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
- package/apps/dashboard/src/cli-process.js +208 -0
- package/apps/dashboard/src/cli-process.js.br +0 -0
- package/apps/dashboard/src/cli-process.js.gz +0 -0
- package/apps/dashboard/src/components/active-tasks-panel.js +175 -0
- package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
- package/apps/dashboard/src/core/api.js +18 -0
- package/apps/dashboard/src/core/api.js.br +0 -0
- package/apps/dashboard/src/core/dom.js +220 -0
- package/apps/dashboard/src/core/dom.js.br +0 -0
- package/apps/dashboard/src/core/state.js +91 -0
- package/apps/dashboard/src/core/state.js.br +0 -0
- package/apps/dashboard/src/core/task-manager.js +134 -0
- package/apps/dashboard/src/core/task-manager.js.br +0 -0
- package/apps/dashboard/src/orchestration-status.js +127 -0
- package/apps/dashboard/src/orchestration-status.js.br +0 -0
- package/apps/dashboard/src/setup-wizard.js +555 -0
- package/apps/dashboard/src/setup-wizard.js.br +0 -0
- package/apps/dashboard/src/styles.css +2085 -0
- package/apps/dashboard/src/styles.css.br +0 -0
- package/apps/dashboard/src/styles.css.gz +0 -0
- package/apps/dashboard/src/tabs/agents-tab.js +2237 -0
- package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/benchmarks-tab.js +229 -0
- package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/comms-tab.js +955 -0
- package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/contacts-tab.js +654 -0
- package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/engines-tab.js +175 -0
- package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/memory-tab.js +182 -0
- package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/models-tab.js +441 -0
- package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/pm-loop-tab.js +185 -0
- package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js +663 -0
- package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
- package/apps/dashboard/src/tabs/prompts-tab.js +160 -0
- package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/services-tab.js +202 -0
- package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/settings-tab.js +803 -0
- package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/skills-tab.js +284 -0
- package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/spending-tab.js +173 -0
- package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-chat-tab.js +660 -0
- package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-tab.js +538 -0
- package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/usage-tab.js +390 -0
- package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/waves-tab.js +238 -0
- package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/workflows-tab.js +747 -0
- package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +249 -0
- package/apps/vibe/.crew/cost.json +17 -0
- package/apps/vibe/.crew/json-parse-metrics.jsonl +22 -0
- package/apps/vibe/.crew/pipeline-metrics.jsonl +22 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +1 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +2 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +5 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +5 -0
- package/apps/vibe/.crew/sandbox.json +7 -0
- package/apps/vibe/.crew/session.json +285 -0
- package/apps/vibe/.crew/training-data.jsonl +0 -0
- package/apps/vibe/.github/workflows/studio-quality.yml +37 -0
- package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +12 -0
- package/apps/vibe/.studio-data/project-messages/general.jsonl +54 -0
- package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +10 -0
- package/apps/vibe/ARCHITECTURE.md +3393 -0
- package/apps/vibe/QUICK-REFERENCE.md +211 -0
- package/apps/vibe/README.md +76 -0
- package/apps/vibe/ROADMAP.md +41 -0
- package/apps/vibe/STUDIO-SETUP-COMPLETE.md +35 -0
- package/apps/vibe/VISUAL-GUIDE.md +378 -0
- package/apps/vibe/capture-demo.mjs +160 -0
- package/apps/vibe/capture-vibe-assets.mjs +71 -0
- package/apps/vibe/capture-vibe-video.mjs +260 -0
- package/apps/vibe/check-buttons.js +41 -0
- package/apps/vibe/diagnose.html +106 -0
- package/apps/vibe/fix-buttons.js +103 -0
- package/apps/vibe/index.html +3401 -0
- package/apps/vibe/package-lock.json +920 -0
- package/apps/vibe/package.json +31 -0
- package/apps/vibe/public/favicon.png +0 -0
- package/apps/vibe/scripts/studio-pty-host.py +117 -0
- package/apps/vibe/server.mjs +1835 -0
- package/apps/vibe/src/main.js +2846 -0
- package/apps/vibe/src/register-all-languages.js +98 -0
- package/apps/vibe/start-studio.sh +11 -0
- package/apps/vibe/test/accessibility-tests.js +77 -0
- package/apps/vibe/test/browser-performance-audit.mjs +205 -0
- package/apps/vibe/test/performance-tests.js +120 -0
- package/apps/vibe/test/security-tests.js +213 -0
- package/apps/vibe/tests/e2e.local.mjs +54 -0
- package/apps/vibe/tests/server.smoke.mjs +106 -0
- package/apps/vibe/update_website.mjs +74 -0
- package/apps/vibe/vite.config.js +19 -0
- package/apps/vibe/watch-server.mjs +108 -0
- package/contrib/openclaw-plugin/README.md +199 -0
- package/contrib/openclaw-plugin/index.ts +306 -0
- package/contrib/openclaw-plugin/openclaw.plugin.json +41 -0
- package/contrib/openclaw-plugin/package.json +27 -0
- package/contrib/openclaw-plugin/skills/crewswarm/SKILL.md +88 -0
- package/crew-lead.mjs +649 -0
- package/engines/claude-code.json +36 -0
- package/engines/codex.json +37 -0
- package/engines/crew-cli.json +42 -0
- package/engines/cursor.json +40 -0
- package/engines/docker-sandbox.json +38 -0
- package/engines/gemini-cli.json +75 -0
- package/engines/opencode.json +31 -0
- package/gateway-bridge.mjs +1575 -0
- package/install.sh +738 -0
- package/lib/agent-registry.mjs +232 -0
- package/lib/agents/daemon.mjs +121 -0
- package/lib/agents/dispatch.mjs +225 -0
- package/lib/agents/permissions.mjs +90 -0
- package/lib/agents/platform-formatting.mjs +102 -0
- package/lib/agents/registry.mjs +81 -0
- package/lib/agents/tool-instructions.mjs +257 -0
- package/lib/agents/validation.mjs +75 -0
- package/lib/approval/policy-manager.mjs +221 -0
- package/lib/autoharness/index.mjs +391 -0
- package/lib/bridges/cli-executor.mjs +332 -0
- package/lib/bridges/gateway-ws.mjs +345 -0
- package/lib/bridges/integration.mjs +229 -0
- package/lib/bridges/rag-helper.mjs +90 -0
- package/lib/browser/opencode-passthrough-filter.js +44 -0
- package/lib/browser/passthrough-stderr.js +109 -0
- package/lib/chat/autonomous-mentions.mjs +373 -0
- package/lib/chat/history.mjs +82 -0
- package/lib/chat/mention-routing-intent.mjs +136 -0
- package/lib/chat/participants.mjs +95 -0
- package/lib/chat/project-messages-rag.mjs +265 -0
- package/lib/chat/project-messages.mjs +479 -0
- package/lib/chat/shared-chat-prompt-overlay.mjs +52 -0
- package/lib/chat/thread-binding.mjs +34 -0
- package/lib/chat/unified-history.mjs +223 -0
- package/lib/chat/unified-wrapper.mjs +41 -0
- package/lib/cli-process-tracker.mjs +228 -0
- package/lib/collections/index.mjs +433 -0
- package/lib/contacts/identity-linker.mjs +248 -0
- package/lib/contacts/index.mjs +341 -0
- package/lib/crew-judge/PROMPT.md +93 -0
- package/lib/crew-judge/judge.mjs +260 -0
- package/lib/crew-lead/agent-manager.mjs +125 -0
- package/lib/crew-lead/background.mjs +270 -0
- package/lib/crew-lead/brain.mjs +110 -0
- package/lib/crew-lead/chat-handler.mjs +2603 -0
- package/lib/crew-lead/chat-handler.mjs.bak +1274 -0
- package/lib/crew-lead/classifier.mjs +83 -0
- package/lib/crew-lead/http-server.mjs +4824 -0
- package/lib/crew-lead/intent.mjs +102 -0
- package/lib/crew-lead/interval-manager.mjs +41 -0
- package/lib/crew-lead/llm-caller.mjs +544 -0
- package/lib/crew-lead/prompts.mjs +392 -0
- package/lib/crew-lead/retry-manager.mjs +118 -0
- package/lib/crew-lead/tools.mjs +318 -0
- package/lib/crew-lead/wave-dispatcher.mjs +798 -0
- package/lib/crew-lead/waves-config.json +73 -0
- package/lib/crew-lead/waves-loader.mjs +110 -0
- package/lib/crew-lead/ws-router.mjs +428 -0
- package/lib/dispatch/parsers.mjs +299 -0
- package/lib/domain-planning/detector.mjs +196 -0
- package/lib/domain-planning/prompts/crew-pm-cli.md +96 -0
- package/lib/domain-planning/prompts/crew-pm-core.md +122 -0
- package/lib/domain-planning/prompts/crew-pm-frontend.md +111 -0
- package/lib/engines/crew-cli-sandbox.mjs +422 -0
- package/lib/engines/crew-cli.mjs +155 -0
- package/lib/engines/cursor-launcher.mjs +110 -0
- package/lib/engines/engine-registry.mjs +253 -0
- package/lib/engines/llm-direct.mjs +184 -0
- package/lib/engines/opencode.mjs +256 -0
- package/lib/engines/ouroboros.mjs +114 -0
- package/lib/engines/rt-envelope.mjs +1643 -0
- package/lib/engines/rt-envelope.mjs.backup-current +870 -0
- package/lib/engines/runners.mjs +1367 -0
- package/lib/gemini-cli-passthrough-noise.mjs +37 -0
- package/lib/integrations/code-search.mjs +259 -0
- package/lib/integrations/greptile.mjs +148 -0
- package/lib/integrations/multimodal.mjs +313 -0
- package/lib/integrations/telegram-streaming.mjs +153 -0
- package/lib/integrations/tts.mjs +312 -0
- package/lib/integrations/twitter-links.mjs +294 -0
- package/lib/memory/shared-adapter.mjs +296 -0
- package/lib/pipeline/manager.mjs +539 -0
- package/lib/preferences/extractor.mjs +347 -0
- package/lib/project-dir.mjs +20 -0
- package/lib/runtime/config.mjs +388 -0
- package/lib/runtime/dlq.mjs +170 -0
- package/lib/runtime/log-rotation.mjs +82 -0
- package/lib/runtime/logger.mjs +58 -0
- package/lib/runtime/memory.mjs +421 -0
- package/lib/runtime/paths.mjs +76 -0
- package/lib/runtime/project-dir.mjs +127 -0
- package/lib/runtime/spending.mjs +204 -0
- package/lib/runtime/startup-guard.mjs +291 -0
- package/lib/runtime/task-lease.mjs +234 -0
- package/lib/runtime/telemetry-schema.mjs +208 -0
- package/lib/runtime/telemetry.mjs +101 -0
- package/lib/runtime/utils.mjs +64 -0
- package/lib/skills/index.mjs +265 -0
- package/lib/tools/browser.mjs +135 -0
- package/lib/tools/executor.mjs +913 -0
- package/lib/types.d.ts +57 -0
- package/package.json +106 -0
- package/pm-loop.mjs +1626 -0
- package/prompts/coder-back.md +27 -0
- package/prompts/coder-front.md +27 -0
- package/prompts/coder.md +28 -0
- package/prompts/copywriter.md +17 -0
- package/prompts/fixer.md +39 -0
- package/prompts/frontend.md +23 -0
- package/prompts/github.md +24 -0
- package/prompts/main.md +39 -0
- package/prompts/pm-cli.md +95 -0
- package/prompts/pm-core.md +121 -0
- package/prompts/pm-frontend.md +110 -0
- package/prompts/pm.md +234 -0
- package/prompts/qa.md +44 -0
- package/prompts/security.md +19 -0
- package/scripts/build-crew-chat.sh +28 -0
- package/scripts/build-llms-full.mjs +52 -0
- package/scripts/chatmock-login.sh +16 -0
- package/scripts/chatmock-serve.sh +16 -0
- package/scripts/check-dashboard.mjs +88 -0
- package/scripts/crew-scribe.mjs +326 -0
- package/scripts/dashboard-helpers.mjs +391 -0
- package/scripts/dashboard-validation.mjs +198 -0
- package/scripts/dashboard.mjs +9717 -0
- package/scripts/dlq-replay.mjs +61 -0
- package/scripts/doctor.mjs +196 -0
- package/scripts/file-lock.mjs +186 -0
- package/scripts/fresh-machine-smoke.sh +323 -0
- package/scripts/generate-changelog.mjs +227 -0
- package/scripts/generate-openapi.mjs +334 -0
- package/scripts/health-check.mjs +229 -0
- package/scripts/install-docker.sh +213 -0
- package/scripts/mcp-server.mjs +1625 -0
- package/scripts/opencrew-rt-daemon.mjs +568 -0
- package/scripts/openswitchctl +646 -0
- package/scripts/refactor-configs.mjs +39 -0
- package/scripts/release-check.sh +46 -0
- package/scripts/resolve-node-bin.sh +25 -0
- package/scripts/restart-all-from-repo.sh +329 -0
- package/scripts/restart-crew-lead.sh +98 -0
- package/scripts/restart-dashboard.sh +104 -0
- package/scripts/restart-service.sh +274 -0
- package/scripts/run-accessibility-audit.mjs +356 -0
- package/scripts/run-integration-bounded.mjs +188 -0
- package/scripts/run-scheduled-pipeline.mjs +230 -0
- package/scripts/run.mjs +41 -0
- package/scripts/scan-skills.mjs +79 -0
- package/scripts/setup-firewall.sh +128 -0
- package/scripts/smoke-dispatch.mjs +149 -0
- package/scripts/smoke.sh +163 -0
- package/scripts/start-crew.mjs +328 -0
- package/scripts/start.mjs +146 -0
- package/scripts/swiftbar-restart-service.sh +19 -0
- package/scripts/sync-agents.mjs +152 -0
- package/scripts/sync-prompts.mjs +79 -0
- package/scripts/validate-config.mjs +337 -0
- package/scripts/wow.mjs +89 -0
- package/telegram-bridge.mjs +2421 -0
- package/unified-orchestrator.mjs +519 -0
- package/whatsapp-bridge.mjs +1481 -0
|
@@ -0,0 +1,2237 @@
|
|
|
1
|
+
import { getJSON, postJSON } from "../core/api.js";
|
|
2
|
+
import { showNotification, renderStatusBadge } from "../core/dom.js";
|
|
3
|
+
import {
|
|
4
|
+
sortAgents,
|
|
5
|
+
state,
|
|
6
|
+
persistState,
|
|
7
|
+
restoreScrollPosition,
|
|
8
|
+
} from "../core/state.js";
|
|
9
|
+
|
|
10
|
+
let hideAllViews = () => {};
|
|
11
|
+
let setNavActive = () => {};
|
|
12
|
+
let refreshAgents = () => {};
|
|
13
|
+
|
|
14
|
+
export function initAgentsTab(deps = {}) {
|
|
15
|
+
hideAllViews = deps.hideAllViews || hideAllViews;
|
|
16
|
+
setNavActive = deps.setNavActive || setNavActive;
|
|
17
|
+
refreshAgents = deps.refreshAgents || refreshAgents;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function showAgents() {
|
|
21
|
+
hideAllViews();
|
|
22
|
+
document.getElementById("agentsView").classList.add("active");
|
|
23
|
+
setNavActive("navAgents");
|
|
24
|
+
state.activeTab = "agents";
|
|
25
|
+
persistState();
|
|
26
|
+
|
|
27
|
+
// Skip re-fetch if agents are already rendered
|
|
28
|
+
const agentsBox =
|
|
29
|
+
document.getElementById("agentsList") ||
|
|
30
|
+
document.getElementById("agentsView");
|
|
31
|
+
const alreadyLoaded = agentsBox && agentsBox.querySelector(".agent-card");
|
|
32
|
+
if (!alreadyLoaded) {
|
|
33
|
+
loadAgents_cfg();
|
|
34
|
+
} else {
|
|
35
|
+
restoreScrollPosition("agents");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ββ Agents UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
40
|
+
let _allModels = [];
|
|
41
|
+
let _modelsByProvider = {}; // { "cerebras": ["llama3.1-8b", ...], ... }
|
|
42
|
+
let _ocModels = [];
|
|
43
|
+
let _engines = [];
|
|
44
|
+
|
|
45
|
+
const ENGINE_ICONS = {
|
|
46
|
+
opencode: "β‘",
|
|
47
|
+
cursor: "π±",
|
|
48
|
+
claudecode: "π€",
|
|
49
|
+
"claude-code": "π€",
|
|
50
|
+
codex: "π£",
|
|
51
|
+
gemini: "π΅",
|
|
52
|
+
"gemini-cli": "π΅",
|
|
53
|
+
"crew-cli": "π§",
|
|
54
|
+
"docker-sandbox": "π³",
|
|
55
|
+
direct: "π¬",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const ENGINE_COLORS = {
|
|
59
|
+
opencode: "#22c55e",
|
|
60
|
+
cursor: "#38bdf8",
|
|
61
|
+
claudecode: "#f59e0b",
|
|
62
|
+
"claude-code": "#f59e0b",
|
|
63
|
+
codex: "#a855f7",
|
|
64
|
+
gemini: "#4285f4",
|
|
65
|
+
"gemini-cli": "#4285f4",
|
|
66
|
+
"crew-cli": "#10b981",
|
|
67
|
+
"docker-sandbox": "#0ea5e9",
|
|
68
|
+
direct: "#6366f1",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const ELEVENLABS_VOICES = [
|
|
72
|
+
{ id: "21m00Tcm4TlvDq8ikWAM", name: "Rachel" },
|
|
73
|
+
{ id: "pNInz6obpgDQGcFmaJgB", name: "Adam" },
|
|
74
|
+
{ id: "EXAVITQu4vr4xnSDxMaL", name: "Bella" },
|
|
75
|
+
{ id: "XB0fDUnXU5powFXDhCwa", name: "Charlotte" },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const GOOGLE_VOICES = [
|
|
79
|
+
{ id: "en-US-Neural2-C", name: "US Female C" },
|
|
80
|
+
{ id: "en-US-Neural2-A", name: "US Male A" },
|
|
81
|
+
{ id: "en-GB-Neural2-A", name: "UK Female A" },
|
|
82
|
+
{ id: "en-AU-Neural2-A", name: "AU Female A" },
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const KNOWN_VOICES = [...ELEVENLABS_VOICES, ...GOOGLE_VOICES];
|
|
86
|
+
|
|
87
|
+
function getVoicePresetValue(voiceId = "") {
|
|
88
|
+
if (!voiceId) return "";
|
|
89
|
+
return KNOWN_VOICES.some((voice) => voice.id === voiceId) ? voiceId : "custom";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getVoiceDisplayName(voiceId = "") {
|
|
93
|
+
return KNOWN_VOICES.find((voice) => voice.id === voiceId)?.name || voiceId;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function buildVoiceOptions(selectedVoiceId = "") {
|
|
97
|
+
const elevenlabs = ELEVENLABS_VOICES.map(
|
|
98
|
+
(voice) =>
|
|
99
|
+
`<option value="${voice.id}" ${selectedVoiceId === voice.id ? "selected" : ""}>${voice.name}</option>`,
|
|
100
|
+
).join("");
|
|
101
|
+
const google = GOOGLE_VOICES.map(
|
|
102
|
+
(voice) =>
|
|
103
|
+
`<option value="${voice.id}" ${selectedVoiceId === voice.id ? "selected" : ""}>${voice.name}</option>`,
|
|
104
|
+
).join("");
|
|
105
|
+
|
|
106
|
+
return `
|
|
107
|
+
<option value="" ${!selectedVoiceId ? "selected" : ""}>Use default voice</option>
|
|
108
|
+
<optgroup label="ElevenLabs">${elevenlabs}</optgroup>
|
|
109
|
+
<optgroup label="Google TTS">${google}</optgroup>
|
|
110
|
+
<option value="custom" ${getVoicePresetValue(selectedVoiceId) === "custom" ? "selected" : ""}>Custom voice IDβ¦</option>
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// crewswarm gateway-bridge tool definitions
|
|
115
|
+
const CREWSWARM_TOOLS = [
|
|
116
|
+
{ id: "write_file", desc: "Write files to disk (@@WRITE_FILE)" },
|
|
117
|
+
{ id: "read_file", desc: "Read files from disk (@@READ_FILE)" },
|
|
118
|
+
{ id: "mkdir", desc: "Create directories (@@MKDIR)" },
|
|
119
|
+
{ id: "run_cmd", desc: "Run whitelisted shell commands (@@RUN_CMD)" },
|
|
120
|
+
{ id: "git", desc: "Git & GitHub CLI operations" },
|
|
121
|
+
{ id: "web_search", desc: "Web search (Brave Search β @@WEB_SEARCH)" },
|
|
122
|
+
{ id: "web_fetch", desc: "Fetch URLs (@@WEB_FETCH)" },
|
|
123
|
+
{ id: "dispatch", desc: "Dispatch tasks to other agents" },
|
|
124
|
+
{ id: "telegram", desc: "Send Telegram messages (@@TELEGRAM)" },
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
// Role-based tool defaults β applied when "Apply role defaults" is clicked
|
|
128
|
+
const AGENT_TOOL_DEFAULTS = {
|
|
129
|
+
"crew-qa": ["read_file"],
|
|
130
|
+
"crew-coder": ["write_file", "read_file", "mkdir", "run_cmd"],
|
|
131
|
+
"crew-coder-front": ["write_file", "read_file", "mkdir", "run_cmd"],
|
|
132
|
+
"crew-coder-back": ["write_file", "read_file", "mkdir", "run_cmd"],
|
|
133
|
+
"crew-frontend": ["write_file", "read_file", "mkdir", "run_cmd"],
|
|
134
|
+
"crew-fixer": ["write_file", "read_file", "mkdir", "run_cmd"],
|
|
135
|
+
"crew-github": ["read_file", "run_cmd", "git"],
|
|
136
|
+
"crew-pm": ["read_file", "dispatch"],
|
|
137
|
+
"crew-main": ["read_file", "write_file", "run_cmd", "dispatch"],
|
|
138
|
+
"crew-security": ["read_file", "run_cmd"],
|
|
139
|
+
"crew-copywriter": ["write_file", "read_file"],
|
|
140
|
+
"crew-telegram": ["telegram", "read_file"],
|
|
141
|
+
"crew-lead": ["dispatch"],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
function getToolDefaults(agentId) {
|
|
145
|
+
if (AGENT_TOOL_DEFAULTS[agentId]) return AGENT_TOOL_DEFAULTS[agentId];
|
|
146
|
+
// Fuzzy match β e.g. crew-coder-3 β coder defaults
|
|
147
|
+
for (const [key, val] of Object.entries(AGENT_TOOL_DEFAULTS)) {
|
|
148
|
+
if (agentId.startsWith(key) || agentId.includes(key.replace("crew-", "")))
|
|
149
|
+
return val;
|
|
150
|
+
}
|
|
151
|
+
return ["read_file", "write_file", "mkdir", "run_cmd"]; // sensible default for unknown roles
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function applyToolPreset(agentId) {
|
|
155
|
+
const defaults = getToolDefaults(agentId);
|
|
156
|
+
const container = document.getElementById("tools-" + agentId);
|
|
157
|
+
if (!container) return;
|
|
158
|
+
container.querySelectorAll("input[type=checkbox]").forEach((cb) => {
|
|
159
|
+
cb.checked = defaults.includes(cb.dataset.tool);
|
|
160
|
+
});
|
|
161
|
+
await saveAgentTools(agentId);
|
|
162
|
+
showNotification("Role defaults applied for " + agentId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function loadAgents_cfg() {
|
|
166
|
+
const list = document.getElementById("agentsList");
|
|
167
|
+
list.innerHTML =
|
|
168
|
+
'<div class="meta" style="padding:20px;">Loading agentsβ¦</div>';
|
|
169
|
+
try {
|
|
170
|
+
const data = await getJSON("/api/agents-config");
|
|
171
|
+
_allModels = data.allModels || [];
|
|
172
|
+
_modelsByProvider = data.modelsByProvider || {};
|
|
173
|
+
const agents = sortAgents(data.agents || []);
|
|
174
|
+
if (!agents.length) {
|
|
175
|
+
list.innerHTML =
|
|
176
|
+
'<div class="meta" style="padding:20px;">No agents found in config. Check ~/.crewswarm/crewswarm.json</div>';
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
list.innerHTML = "";
|
|
180
|
+
agents.forEach((a) => {
|
|
181
|
+
const card = document.createElement("div");
|
|
182
|
+
card.className = "agent-card";
|
|
183
|
+
card.id = "agent-card-" + a.id;
|
|
184
|
+
const modelOpts = _allModels
|
|
185
|
+
.map(
|
|
186
|
+
(m) =>
|
|
187
|
+
`<option value="${m}" ${m === a.model ? "selected" : ""}>${m}</option>`,
|
|
188
|
+
)
|
|
189
|
+
.join("");
|
|
190
|
+
const customOpt =
|
|
191
|
+
!a.model || _allModels.includes(a.model)
|
|
192
|
+
? ""
|
|
193
|
+
: `<option value="${a.model}" selected>${a.model} (custom)</option>`;
|
|
194
|
+
const liveDot = renderStatusBadge(a.liveness, a.ageSec);
|
|
195
|
+
const voiceConfig = a.voice || {};
|
|
196
|
+
const currentVoiceId = voiceConfig.voiceId || voiceConfig.voice || "";
|
|
197
|
+
const customVoiceValue =
|
|
198
|
+
getVoicePresetValue(currentVoiceId) === "custom" ? currentVoiceId : "";
|
|
199
|
+
card.innerHTML = `
|
|
200
|
+
<div class="agent-card-header">
|
|
201
|
+
<div class="agent-avatar" id="avatar-${a.id}" style="position:relative;">${a.emoji}</div>
|
|
202
|
+
<div class="agent-meta">
|
|
203
|
+
<div class="agent-id" style="display:flex;align-items:center;">${liveDot}${a.id} <span class="meta" style="font-weight:400;margin-left:4px;">Β· ${a.name}</span>
|
|
204
|
+
${MODEL_ROLE[a.id] ? '<span style="font-size:9px;font-weight:700;letter-spacing:0.04em;padding:1px 6px;border-radius:4px;margin-left:8px;' + (ROLE_STYLE[MODEL_ROLE[a.id]] || "") + '">' + MODEL_ROLE[a.id] + "</span>" : ""}
|
|
205
|
+
<span id="coding-dot-${a.id}" style="display:none;margin-left:8px;align-items:center;gap:4px;font-size:11px;color:var(--accent);">
|
|
206
|
+
<span style="display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);animation:pulse 1s ease-in-out infinite;"></span>coding
|
|
207
|
+
</span>
|
|
208
|
+
</div>
|
|
209
|
+
<div id="cur-model-${a.id}" style="margin-top:3px;display:flex;flex-wrap:wrap;align-items:center;gap:6px;">
|
|
210
|
+
<span style="font-size:11px;font-family:'SF Mono',monospace;color:${BROKEN_MODELS.has(a.model) ? "var(--red-hi)" : "var(--text-2)"};" title="Conversation model β used for direct replies and chat">
|
|
211
|
+
${BROKEN_MODELS.has(a.model) ? "β " : "π¬ "}${a.model || "(none)"}
|
|
212
|
+
</span>
|
|
213
|
+
${a.useCursorCli ? '<span style="font-size:11px;font-family:monospace;color:var(--purple);" title="Cursor CLI β routing tasks through Cursor agent subagents">β‘ cursor</span>' : ""}
|
|
214
|
+
${a.useClaudeCode ? '<span style="font-size:11px;font-family:monospace;color:var(--green-hi);" title="Claude Code CLI β routing tasks through claude -p">π€ claude</span>' : ""}
|
|
215
|
+
${a.useCodex ? '<span style="font-size:11px;font-family:monospace;color:var(--purple);" title="Codex CLI β routing tasks through codex exec">π£ ' + (a.codexModel || "codex") + "</span>" : ""}
|
|
216
|
+
${a.useGeminiCli ? '<span style="font-size:11px;font-family:monospace;color:#4285f4;" title="Gemini CLI β routing tasks through gemini -p">π΅ gemini</span>' : ""}
|
|
217
|
+
${a.useCrewCLI ? '<span style="font-size:11px;font-family:monospace;color:#10b981;" title="Crew CLI β routing tasks through crew-cli native agents">π§ ' + (a.crewCliModel || "crew-cli") + "</span>" : ""}
|
|
218
|
+
${a.opencodeModel && !a.useCursorCli && !a.useClaudeCode && !a.useCodex && !a.useGeminiCli && !a.useCrewCLI ? '<span style="font-size:11px;font-family:monospace;color:' + (BROKEN_MODELS.has(a.opencodeModel) ? "var(--red-hi)" : "var(--green-hi)") + ';" title="OpenCode model β used when routing tasks through OpenCode CLI">β‘ ' + a.opencodeModel + "</span>" : ""}
|
|
219
|
+
${BROKEN_MODELS.has(a.model) ? '<span style="font-size:10px;font-weight:600;color:var(--red-hi);background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);padding:1px 6px;border-radius:4px;">BROKEN β REASSIGN</span>' : ""}
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<button class="btn-ghost" style="font-size:11px; padding:4px 10px;" data-action="toggleAgentBody" data-arg="${a.id}">Edit βΎ</button>
|
|
223
|
+
<button class="btn-ghost" style="font-size:11px; padding:4px 10px; color:var(--red); border-color:rgba(248,113,113,0.3);" data-action="deleteAgent" data-arg="${a.id}">β</button>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="agent-body" id="body-${a.id}" style="display:none;">
|
|
226
|
+
<div>
|
|
227
|
+
<div class="field-label" style="display:flex;align-items:center;gap:8px;">
|
|
228
|
+
<span>π¬ Conversation Model</span>
|
|
229
|
+
<span style="font-size:10px;font-weight:400;color:var(--text-3);">Used for direct replies, planning, and chat. <strong style="color:var(--text-2);">Not used when OpenCode is enabled.</strong></span>
|
|
230
|
+
</div>
|
|
231
|
+
${BROKEN_MODELS.has(a.model) ? '<div style="font-size:11px;color:var(--red-hi);background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.25);border-radius:5px;padding:6px 10px;margin-bottom:8px;">β Current model <code>' + a.model + "</code> is broken (returns empty responses). Please reassign.</div>" : ""}
|
|
232
|
+
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
|
233
|
+
<select id="model-${a.id}" style="flex:1; min-width:200px;" onchange="syncModelText('${a.id}')">${customOpt}${modelOpts}</select>
|
|
234
|
+
<input id="modeltext-${a.id}" type="text" placeholder="or type provider/modelβ¦" value="${a.model || ""}" style="flex:1; min-width:160px; font-size:12px;" oninput="syncModelSelect('${a.id}')" />
|
|
235
|
+
<button data-action="saveAgentModel" data-arg="${a.id}" class="btn-green" style="white-space:nowrap;">Save</button>
|
|
236
|
+
</div>
|
|
237
|
+
<div style="margin-top:8px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
|
238
|
+
<span style="font-size:11px;color:var(--text-3);white-space:nowrap;">β© Fallback:</span>
|
|
239
|
+
${(() => {
|
|
240
|
+
const fbCustomOpt =
|
|
241
|
+
a.fallbackModel && !_allModels.includes(a.fallbackModel)
|
|
242
|
+
? `<option value="${a.fallbackModel}" selected>${a.fallbackModel} (custom)</option>`
|
|
243
|
+
: "";
|
|
244
|
+
const fbOpts = _allModels
|
|
245
|
+
.map(
|
|
246
|
+
(m) =>
|
|
247
|
+
`<option value="${m}" ${m === a.fallbackModel ? "selected" : ""}>${m}</option>`,
|
|
248
|
+
)
|
|
249
|
+
.join("");
|
|
250
|
+
return `<select id="fmodel-${a.id}" style="flex:1;min-width:180px;font-size:11px;" onchange="syncFallbackText('${a.id}')"><option value="">β none β</option>${fbCustomOpt}${fbOpts}</select>`;
|
|
251
|
+
})()}
|
|
252
|
+
<input id="fallback-${a.id}" type="text" placeholder="or type any modelβ¦"
|
|
253
|
+
value="${a.fallbackModel || ""}"
|
|
254
|
+
style="flex:1; min-width:140px; font-size:11px; color:var(--text-2);"
|
|
255
|
+
oninput="syncFallbackSelect('${a.id}')" />
|
|
256
|
+
<button data-action="saveAgentFallback" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:11px;">Save</button>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
<div>
|
|
260
|
+
<div class="field-label">Display name & emoji</div>
|
|
261
|
+
<div style="display:flex; gap:8px;">
|
|
262
|
+
<input id="aname-${a.id}" type="text" value="${a.name}" placeholder="Display name" style="flex:1;" />
|
|
263
|
+
<div class="emoji-picker-wrap">
|
|
264
|
+
<button type="button" class="emoji-btn" id="aemoji-btn-${a.id}" data-action="toggleEmojiPicker" data-arg="${a.id}" title="Pick emoji">${a.emoji || "π€"}</button>
|
|
265
|
+
<input type="hidden" id="aemoji-${a.id}" value="${a.emoji || "π€"}" />
|
|
266
|
+
<div class="emoji-picker-panel" id="aemoji-panel-${a.id}">
|
|
267
|
+
<div class="emoji-grid" id="aemoji-grid-${a.id}"></div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
<button data-action="saveAgentIdentity" data-arg="${a.id}" class="btn-ghost">Save</button>
|
|
271
|
+
</div>
|
|
272
|
+
<div style="margin-top:8px;">
|
|
273
|
+
<div class="field-label" style="margin-bottom:4px;">Role / Theme <span style="font-weight:400; color:var(--text-3); font-size:11px;">β used by PM router to assign tasks (e.g. "iOS/Swift developer (SwiftUI, UIKit)")</span></div>
|
|
274
|
+
<input id="atheme-${a.id}" type="text" value="${a.theme || ""}" placeholder="Describe what this agent specialises in..." style="width:100%;" />
|
|
275
|
+
</div>
|
|
276
|
+
<div style="margin-top:10px; padding:10px; background:var(--surface-2); border:1px solid var(--border); border-radius:8px;">
|
|
277
|
+
<div class="field-label" style="margin-bottom:6px;">Voice / TTS</div>
|
|
278
|
+
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
|
|
279
|
+
<select id="voice-provider-${a.id}" style="min-width:140px; flex:0 0 160px;">
|
|
280
|
+
<option value="auto" ${(voiceConfig.provider || "auto") === "auto" ? "selected" : ""}>Auto provider</option>
|
|
281
|
+
<option value="elevenlabs" ${voiceConfig.provider === "elevenlabs" ? "selected" : ""}>ElevenLabs</option>
|
|
282
|
+
<option value="google" ${voiceConfig.provider === "google" ? "selected" : ""}>Google TTS</option>
|
|
283
|
+
</select>
|
|
284
|
+
<select id="voice-preset-${a.id}" style="flex:1; min-width:220px;" onchange="toggleAgentVoiceCustom('${a.id}')">
|
|
285
|
+
${buildVoiceOptions(currentVoiceId)}
|
|
286
|
+
</select>
|
|
287
|
+
<input id="voice-custom-${a.id}" type="text" placeholder="Custom voice ID or Google voice name"
|
|
288
|
+
value="${customVoiceValue}"
|
|
289
|
+
style="flex:1; min-width:200px; display:${getVoicePresetValue(currentVoiceId) === "custom" ? "block" : "none"};" />
|
|
290
|
+
<button data-action="saveAgentVoice" data-arg="${a.id}" class="btn-ghost">Save voice</button>
|
|
291
|
+
</div>
|
|
292
|
+
<div class="meta" style="margin-top:6px;">Used by Telegram and WhatsApp TTS when voice replies are enabled for the recipient.</div>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
<div>
|
|
296
|
+
<div style="display:flex; align-items:center; gap:10px; margin-bottom:6px;">
|
|
297
|
+
<div class="field-label" style="margin:0;">System Prompt</div>
|
|
298
|
+
${!a.systemPrompt ? '<span style="font-size:11px; color:var(--yellow);">β No prompt set β agent has no role context</span>' : ""}
|
|
299
|
+
<select style="font-size:11px; padding:3px 8px; margin-left:auto;" onchange="applyAgentPromptPreset('${a.id}', this.value); this.value=''">
|
|
300
|
+
${buildPresetOptions()}
|
|
301
|
+
</select>
|
|
302
|
+
</div>
|
|
303
|
+
<textarea id="prompt-${a.id}" rows="5" placeholder="Describe this agent's role. It's injected at the top of every task.">${a.systemPrompt || ""}</textarea>
|
|
304
|
+
<div style="margin-top:8px; display:flex; gap:8px;">
|
|
305
|
+
<button data-action="saveAgentPrompt" data-arg="${a.id}" class="btn-ghost">Save prompt</button>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
<div style="border-top:1px solid var(--border); padding-top:10px;">
|
|
309
|
+
<div class="field-label" style="margin-bottom:8px;">Session</div>
|
|
310
|
+
<div style="display:flex; gap:8px; align-items:center; margin-bottom:12px;">
|
|
311
|
+
<button data-action="resetAgentSession" data-arg="${a.id}" class="btn-ghost" style="font-size:12px;">βΊ Reset context window</button>
|
|
312
|
+
<span style="font-size:11px; color:var(--text-3);">Clears accumulated token context. Shared memory is re-injected on next task.</span>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
<div style="border-top:1px solid var(--border); padding-top:10px;">
|
|
316
|
+
<div class="field-label" style="display:flex; align-items:center; gap:8px; margin-bottom:4px;">
|
|
317
|
+
<span>crewswarm β Agent Tools</span>
|
|
318
|
+
<span style="font-size:10px; font-weight:600; color:var(--accent); padding:2px 6px; border-radius:4px; background:rgba(56,189,248,0.08); border:1px solid rgba(56,189,248,0.25);">gateway-bridge</span>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="meta" style="margin-bottom:10px; font-size:11px;">Controls which tools this agent can execute on disk and network. Enforced by gateway-bridge on every task β only checked tools are active.</div>
|
|
321
|
+
<div id="tools-${a.id}" style="display:grid; grid-template-columns:repeat(auto-fill,minmax(210px,1fr)); gap:6px; margin-bottom:12px;">
|
|
322
|
+
${CREWSWARM_TOOLS.map(
|
|
323
|
+
(t) => `
|
|
324
|
+
<label style="display:flex; align-items:flex-start; gap:7px; font-size:12px; color:var(--text-2); cursor:pointer; padding:6px 8px; border-radius:5px; border:1px solid var(--border); background:var(--bg-card2);">
|
|
325
|
+
<input type="checkbox" data-tool="${t.id}" ${(a.alsoAllow || []).includes(t.id) ? "checked" : ""} style="accent-color:var(--accent); margin-top:2px; flex-shrink:0;" />
|
|
326
|
+
<div>
|
|
327
|
+
<code style="font-size:11px; color:var(--text-1);">${t.id}</code>
|
|
328
|
+
<div style="font-size:10px; color:var(--text-3); margin-top:2px; line-height:1.3;">${t.desc}</div>
|
|
329
|
+
</div>
|
|
330
|
+
</label>
|
|
331
|
+
`,
|
|
332
|
+
).join("")}
|
|
333
|
+
</div>
|
|
334
|
+
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
335
|
+
<button data-action="saveAgentTools" data-arg="${a.id}" class="btn-ghost" style="font-size:12px;">Save tools</button>
|
|
336
|
+
<button data-action="applyToolPreset" data-arg="${a.id}" class="btn-ghost" style="font-size:12px; color:var(--text-3);">β© Role defaults</button>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="meta">Workspace: <code style="font-size:11px;">${a.workspace}</code></div>
|
|
339
|
+
</div>
|
|
340
|
+
<div style="border-top:1px solid var(--border); padding-top:10px;">
|
|
341
|
+
<div class="field-label" style="display:flex; align-items:center; gap:8px; margin-bottom:8px;">
|
|
342
|
+
<span>β‘ Execution Route</span>
|
|
343
|
+
<span style="font-size:10px; font-weight:600; color:var(--text-3); padding:2px 6px; border-radius:4px; background:var(--surface-2);">pick one β mutually exclusive</span>
|
|
344
|
+
</div>
|
|
345
|
+
<div id="engine-buttons-${a.id}" style="display:flex; gap:6px; margin-bottom:10px; flex-wrap:wrap;">
|
|
346
|
+
<!-- Dynamically populated from /api/engines -->
|
|
347
|
+
</div>
|
|
348
|
+
<div id="loop-row-${a.id}" style="display:${a.useOpenCode || a.useCursorCli || a.useClaudeCode || a.useCodex || a.useGeminiCli || a.useCrewCLI ? "flex" : "none"}; align-items:center; gap:10px; margin-bottom:10px; padding:8px 10px; background:var(--surface-2); border-radius:8px; border:1px solid var(--border);">
|
|
349
|
+
<label style="display:flex; align-items:center; gap:8px; cursor:pointer; flex:1;">
|
|
350
|
+
<input type="checkbox" id="loop-toggle-${a.id}" ${a.opencodeLoop ? "checked" : ""} onchange="saveAgentLoop('${a.id}')" style="width:14px; height:14px; cursor:pointer;" />
|
|
351
|
+
<span style="font-size:12px; font-weight:600; color:var(--text-1);">π Ouroboros Loop</span>
|
|
352
|
+
<span style="font-size:11px; color:var(--text-3);">LLM decomposes task β engine runs each step β feeds result back until DONE</span>
|
|
353
|
+
</label>
|
|
354
|
+
<div style="display:flex; align-items:center; gap:6px;">
|
|
355
|
+
<span style="font-size:11px; color:var(--text-3); white-space:nowrap;">Max rounds:</span>
|
|
356
|
+
<input type="number" id="loop-rounds-${a.id}" min="1" max="20" value="${a.opencodeLoopMaxRounds || 10}" class="inp-xs" style="width:52px;text-align:center;" onchange="saveAgentLoop('${a.id}')" />
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
<div id="oc-model-row-${a.id}" style="display:${a.useOpenCode && !a.useCursorCli ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:6px;">
|
|
360
|
+
<select id="oc-model-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncOcModelText('${a.id}')"></select>
|
|
361
|
+
<input id="oc-modeltext-${a.id}" type="text" placeholder="opencode/modelβ¦" value="${a.opencodeModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
362
|
+
<button data-action="saveOpenCodeConfig" data-arg="${a.id}" class="btn-green" style="white-space:nowrap; font-size:12px;">Save</button>
|
|
363
|
+
</div>
|
|
364
|
+
<div id="oc-fallback-row-${a.id}" style="display:${a.useOpenCode && !a.useCursorCli ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
365
|
+
<span style="font-size:11px; color:var(--text-3); white-space:nowrap;">β© Fallback:</span>
|
|
366
|
+
<select id="oc-fallback-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncOcFallbackText('${a.id}')"></select>
|
|
367
|
+
<input id="oc-fallback-${a.id}" type="text" placeholder="opencode/model or leave blank" value="${a.opencodeFallbackModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
368
|
+
<button data-action="saveOpenCodeFallback" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:12px;">Save</button>
|
|
369
|
+
</div>
|
|
370
|
+
<div id="cursor-model-row-${a.id}" style="display:${a.useCursorCli ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
371
|
+
<select id="cursor-model-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncCursorModelText('${a.id}')"></select>
|
|
372
|
+
<input id="cursor-model-txt-${a.id}" type="text" placeholder="sonnet-4.6 or leave blank for auto" value="${a.cursorCliModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
373
|
+
<button data-action="saveCursorCliConfig" data-arg="${a.id}" class="btn-sky" style="white-space:nowrap; font-size:12px;">Save</button>
|
|
374
|
+
</div>
|
|
375
|
+
<div id="claudecode-model-row-${a.id}" style="display:${a.useClaudeCode ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
376
|
+
<select id="claudecode-model-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncClaudeCodeModelText('${a.id}')">
|
|
377
|
+
<option value="">β auto (claude-sonnet-4-5) β</option>
|
|
378
|
+
<option value="claude-opus-4-5" ${(a.claudeCodeModel || "") === "claude-opus-4-5" ? "selected" : ""}>claude-opus-4-5 β best reasoning</option>
|
|
379
|
+
<option value="claude-sonnet-4-5" ${(a.claudeCodeModel || "") === "claude-sonnet-4-5" ? "selected" : ""}>claude-sonnet-4-5 β best coding</option>
|
|
380
|
+
<option value="claude-haiku-4-5" ${(a.claudeCodeModel || "") === "claude-haiku-4-5" ? "selected" : ""}>claude-haiku-4-5 β fast & cheap</option>
|
|
381
|
+
</select>
|
|
382
|
+
<input id="claudecode-model-txt-${a.id}" type="text" placeholder="claude-sonnet-4-5 or leave blank" value="${a.claudeCodeModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
383
|
+
<button data-action="saveClaudeCodeConfig" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:12px; color:#f59e0b; border-color:rgba(245,158,11,0.3);">Save</button>
|
|
384
|
+
</div>
|
|
385
|
+
<div id="codex-model-row-${a.id}" style="display:${a.useCodex ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
386
|
+
<select id="codex-model-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncCodexModelText('${a.id}')">
|
|
387
|
+
<option value="">β auto (Codex default) β</option>
|
|
388
|
+
<option value="gpt-5.3-codex" ${(a.codexModel || "") === "gpt-5.3-codex" ? "selected" : ""}>gpt-5.3-codex</option>
|
|
389
|
+
<option value="gpt-5.2-codex" ${(a.codexModel || "") === "gpt-5.2-codex" ? "selected" : ""}>gpt-5.2-codex</option>
|
|
390
|
+
<option value="gpt-5.1-codex" ${(a.codexModel || "") === "gpt-5.1-codex" ? "selected" : ""}>gpt-5.1-codex</option>
|
|
391
|
+
<option value="gpt-5.1-codex-mini" ${(a.codexModel || "") === "gpt-5.1-codex-mini" ? "selected" : ""}>gpt-5.1-codex-mini</option>
|
|
392
|
+
<option value="codex-mini" ${(a.codexModel || "") === "codex-mini" ? "selected" : ""}>codex-mini</option>
|
|
393
|
+
</select>
|
|
394
|
+
<input id="codex-model-txt-${a.id}" type="text" placeholder="gpt-5.3-codex or leave blank for auto" value="${a.codexModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
395
|
+
<button data-action="saveCodexConfig" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:12px; color:#a855f7; border-color:rgba(168,85,247,0.3);">Save</button>
|
|
396
|
+
</div>
|
|
397
|
+
<div id="gemini-model-row-${a.id}" style="display:${a.useGeminiCli ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:10px;">
|
|
398
|
+
<select id="gemini-model-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncGeminiModelText('${a.id}')">
|
|
399
|
+
<option value="">β auto (gemini-2.5-flash) β</option>
|
|
400
|
+
<option value="gemini-2.5-flash" ${(a.geminiCliModel || "") === "gemini-2.5-flash" ? "selected" : ""}>gemini-2.5-flash β fast & cheap</option>
|
|
401
|
+
<option value="gemini-2.5-pro" ${(a.geminiCliModel || "") === "gemini-2.5-pro" ? "selected" : ""}>gemini-2.5-pro β best reasoning</option>
|
|
402
|
+
<option value="gemini-2.0-flash" ${(a.geminiCliModel || "") === "gemini-2.0-flash" ? "selected" : ""}>gemini-2.0-flash β ultra fast</option>
|
|
403
|
+
</select>
|
|
404
|
+
<input id="gemini-model-txt-${a.id}" type="text" placeholder="gemini-2.5-flash or leave blank for auto" value="${a.geminiCliModel || ""}" style="flex:1; min-width:160px; font-size:12px;" />
|
|
405
|
+
<button data-action="saveGeminiCliConfig" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:12px; color:#4285f4; border-color:rgba(66,133,244,0.3);">Save</button>
|
|
406
|
+
</div>
|
|
407
|
+
<div id="crew-cli-config-row-${a.id}" style="display:${a.useCrewCLI ? "flex" : "none"}; gap:8px; align-items:center; flex-wrap:wrap; padding:10px; background:var(--surface-2); border-radius:8px; border:1px solid var(--border); margin-bottom:10px;">
|
|
408
|
+
<span style="font-size:12px; font-weight:600; color:var(--text-1);">π§ Crew CLI Mode Active</span>
|
|
409
|
+
<select id="crew-cli-model-sel-${a.id}" style="flex:1; min-width:200px; font-size:12px;" onchange="syncCrewCliModelText('${a.id}')"></select>
|
|
410
|
+
<input id="crew-cli-model-txt-${a.id}" type="text" placeholder="provider/model or leave blank for default" value="${a.crewCliModel || ""}" style="flex:1; min-width:180px; font-size:12px;" />
|
|
411
|
+
<button data-action="saveCrewCLIConfig" data-arg="${a.id}" class="btn-ghost" style="white-space:nowrap; font-size:12px; color:#10b981; border-color:rgba(16,185,129,0.3);">Save</button>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
<div style="border-top:1px solid var(--border); padding:10px 16px; display:flex; align-items:center; justify-content:space-between; gap:8px;">
|
|
415
|
+
<div style="font-size:11px; color:var(--text-3);">
|
|
416
|
+
Session context accumulates over time. Reset clears the conversation history and re-injects shared memory.
|
|
417
|
+
</div>
|
|
418
|
+
<button data-action="resetAgentSession" data-arg="${a.id}" class="btn-ghost" style="font-size:12px; white-space:nowrap; color:var(--amber); border-color:rgba(245,158,11,0.3);">βΊ Reset session</button>
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
`;
|
|
422
|
+
list.appendChild(card);
|
|
423
|
+
});
|
|
424
|
+
// Re-populate model selects with grouped optgroups + role-specific recommendations
|
|
425
|
+
agents.forEach((a) => {
|
|
426
|
+
const sel = document.getElementById("model-" + a.id);
|
|
427
|
+
const agentRole = MODEL_ROLE[a.id] || null;
|
|
428
|
+
if (sel) populateModelDropdown("model-" + a.id, a.model, agentRole);
|
|
429
|
+
});
|
|
430
|
+
// Load engines and populate route buttons
|
|
431
|
+
await loadEnginesAndPopulateButtons(agents);
|
|
432
|
+
// Load OpenCode models and populate dropdowns
|
|
433
|
+
loadOcModels().then(() => {
|
|
434
|
+
agents.forEach((a) => {
|
|
435
|
+
populateOcModelDropdown("oc-model-" + a.id, a.opencodeModel || "");
|
|
436
|
+
populateOcModelDropdown(
|
|
437
|
+
"oc-fallback-sel-" + a.id,
|
|
438
|
+
a.opencodeFallbackModel || "",
|
|
439
|
+
);
|
|
440
|
+
populateCursorModelDropdown(
|
|
441
|
+
"cursor-model-sel-" + a.id,
|
|
442
|
+
a.cursorCliModel || "",
|
|
443
|
+
);
|
|
444
|
+
populateGenericModelDropdown(
|
|
445
|
+
"crew-cli-model-sel-" + a.id,
|
|
446
|
+
a.crewCliModel || "",
|
|
447
|
+
);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
} catch (e) {
|
|
451
|
+
list.innerHTML =
|
|
452
|
+
'<div class="meta" style="padding:20px; color:var(--red);">Error: ' +
|
|
453
|
+
e.message +
|
|
454
|
+
"</div>";
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function loadEnginesAndPopulateButtons(agents) {
|
|
459
|
+
try {
|
|
460
|
+
const data = await getJSON("/api/engines");
|
|
461
|
+
_engines = data.engines || [];
|
|
462
|
+
|
|
463
|
+
agents.forEach((a) => {
|
|
464
|
+
const container = document.getElementById(`engine-buttons-${a.id}`);
|
|
465
|
+
if (!container) return;
|
|
466
|
+
|
|
467
|
+
// Clear existing buttons before re-populating
|
|
468
|
+
container.innerHTML = "";
|
|
469
|
+
|
|
470
|
+
// Check which engine is active for this agent
|
|
471
|
+
const activeEngine = a.useOpenCode
|
|
472
|
+
? "opencode"
|
|
473
|
+
: a.useCursorCli
|
|
474
|
+
? "cursor"
|
|
475
|
+
: a.useClaudeCode
|
|
476
|
+
? "claude-code"
|
|
477
|
+
: a.useCodex
|
|
478
|
+
? "codex"
|
|
479
|
+
: a.useGeminiCli
|
|
480
|
+
? "gemini-cli"
|
|
481
|
+
: a.useCrewCLI
|
|
482
|
+
? "crew-cli"
|
|
483
|
+
: "direct";
|
|
484
|
+
|
|
485
|
+
// Direct API button (always available)
|
|
486
|
+
const directBtn = createEngineButton(
|
|
487
|
+
a.id,
|
|
488
|
+
"direct",
|
|
489
|
+
"π¬ Direct API",
|
|
490
|
+
activeEngine === "direct",
|
|
491
|
+
"#6366f1",
|
|
492
|
+
);
|
|
493
|
+
container.appendChild(directBtn);
|
|
494
|
+
|
|
495
|
+
// Dynamic engine buttons from JSON
|
|
496
|
+
_engines
|
|
497
|
+
.filter((e) => e.ready) // Only show installed engines
|
|
498
|
+
.sort((a, b) => String(a.label || a.id).localeCompare(String(b.label || b.id)))
|
|
499
|
+
.forEach((eng) => {
|
|
500
|
+
const icon = ENGINE_ICONS[eng.id] || "π§";
|
|
501
|
+
const color = ENGINE_COLORS[eng.id] || eng.color || "#6b7280";
|
|
502
|
+
const label = `${icon} ${eng.label}`;
|
|
503
|
+
const hint = eng.installUrl ? "(ready)" : "";
|
|
504
|
+
const btn = createEngineButton(
|
|
505
|
+
a.id,
|
|
506
|
+
eng.id,
|
|
507
|
+
label,
|
|
508
|
+
activeEngine === eng.id,
|
|
509
|
+
color,
|
|
510
|
+
hint,
|
|
511
|
+
);
|
|
512
|
+
container.appendChild(btn);
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
} catch (err) {
|
|
516
|
+
console.error("Failed to load engines:", err);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function createEngineButton(
|
|
521
|
+
agentId,
|
|
522
|
+
engineId,
|
|
523
|
+
label,
|
|
524
|
+
isActive,
|
|
525
|
+
color,
|
|
526
|
+
hint = "",
|
|
527
|
+
) {
|
|
528
|
+
const btn = document.createElement("button");
|
|
529
|
+
btn.id = `route-${engineId}-${agentId}`;
|
|
530
|
+
btn.dataset.action = "setRoute";
|
|
531
|
+
btn.dataset.arg = agentId;
|
|
532
|
+
btn.dataset.arg2 = engineId;
|
|
533
|
+
|
|
534
|
+
const borderColor = isActive ? color : "var(--border)";
|
|
535
|
+
const bgColor = isActive ? `${color}20` : "var(--surface-2)";
|
|
536
|
+
const textColor = isActive ? color : "var(--text-2)";
|
|
537
|
+
|
|
538
|
+
btn.style.cssText = `
|
|
539
|
+
font-size:11px; font-weight:600; padding:5px 12px; border-radius:6px; cursor:pointer;
|
|
540
|
+
border:1px solid ${borderColor}; background:${bgColor}; color:${textColor};
|
|
541
|
+
`;
|
|
542
|
+
|
|
543
|
+
btn.innerHTML =
|
|
544
|
+
label +
|
|
545
|
+
(hint
|
|
546
|
+
? ` <span style="font-size:10px; font-weight:400; opacity:0.7;">${hint}</span>`
|
|
547
|
+
: "");
|
|
548
|
+
|
|
549
|
+
return btn;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function toggleAgentBody(id) {
|
|
553
|
+
const body = document.getElementById("body-" + id);
|
|
554
|
+
body.style.display = body.style.display === "none" ? "grid" : "none";
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async function resetAgentSessionRT(agentId) {
|
|
558
|
+
if (
|
|
559
|
+
!confirm(
|
|
560
|
+
"Reset session for " +
|
|
561
|
+
agentId +
|
|
562
|
+
"?\\n\\nThis clears accumulated conversation context. Shared memory (memory/*.md) is preserved and re-injected on next task.",
|
|
563
|
+
)
|
|
564
|
+
)
|
|
565
|
+
return;
|
|
566
|
+
try {
|
|
567
|
+
const r = await postJSON("/api/agents/reset-session", { agentId });
|
|
568
|
+
if (r.ok) {
|
|
569
|
+
showNotification("Session reset for " + agentId);
|
|
570
|
+
} else {
|
|
571
|
+
showNotification("Reset failed: " + (r.error || "unknown"), true);
|
|
572
|
+
}
|
|
573
|
+
} catch (e) {
|
|
574
|
+
showNotification("Reset error: " + e.message, true);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async function deleteAgent(agentId) {
|
|
579
|
+
if (!confirm('Delete agent "' + agentId + '"? This cannot be undone.'))
|
|
580
|
+
return;
|
|
581
|
+
// Remove card from DOM instantly so it feels immediate
|
|
582
|
+
const card = document.getElementById("agent-card-" + agentId);
|
|
583
|
+
if (card) card.style.opacity = "0.3";
|
|
584
|
+
try {
|
|
585
|
+
await postJSON("/api/agents-config/delete", { agentId });
|
|
586
|
+
if (card) card.remove();
|
|
587
|
+
showNotification("Agent " + agentId + " deleted");
|
|
588
|
+
await loadAgents_cfg();
|
|
589
|
+
} catch (e) {
|
|
590
|
+
if (card) card.style.opacity = "1";
|
|
591
|
+
showNotification("Delete failed: " + e.message, true);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function syncModelText(agentId) {
|
|
596
|
+
const sel = document.getElementById("model-" + agentId);
|
|
597
|
+
const txt = document.getElementById("modeltext-" + agentId);
|
|
598
|
+
if (txt) txt.value = sel.value;
|
|
599
|
+
}
|
|
600
|
+
function syncModelSelect(agentId) {
|
|
601
|
+
const txt = document.getElementById("modeltext-" + agentId);
|
|
602
|
+
const sel = document.getElementById("model-" + agentId);
|
|
603
|
+
if (!sel) return;
|
|
604
|
+
const typed = txt.value.trim();
|
|
605
|
+
const match = [...sel.options].find((o) => o.value === typed);
|
|
606
|
+
sel.value = match ? typed : "";
|
|
607
|
+
}
|
|
608
|
+
function syncFallbackText(agentId) {
|
|
609
|
+
const sel = document.getElementById("fmodel-" + agentId);
|
|
610
|
+
const txt = document.getElementById("fallback-" + agentId);
|
|
611
|
+
if (txt) txt.value = sel.value;
|
|
612
|
+
}
|
|
613
|
+
function syncFallbackSelect(agentId) {
|
|
614
|
+
const txt = document.getElementById("fallback-" + agentId);
|
|
615
|
+
const sel = document.getElementById("fmodel-" + agentId);
|
|
616
|
+
if (!sel) return;
|
|
617
|
+
const typed = txt.value.trim();
|
|
618
|
+
const match = [...sel.options].find((o) => o.value === typed);
|
|
619
|
+
sel.value = match ? typed : "";
|
|
620
|
+
}
|
|
621
|
+
// Expose sync helpers globally β onchange="" attributes in dynamic HTML need window scope
|
|
622
|
+
window.syncModelText = syncModelText;
|
|
623
|
+
window.syncModelSelect = syncModelSelect;
|
|
624
|
+
window.syncFallbackText = syncFallbackText;
|
|
625
|
+
window.syncFallbackSelect = syncFallbackSelect;
|
|
626
|
+
window.toggleAgentVoiceCustom = function (agentId) {
|
|
627
|
+
const preset = document.getElementById("voice-preset-" + agentId);
|
|
628
|
+
const custom = document.getElementById("voice-custom-" + agentId);
|
|
629
|
+
if (!preset || !custom) return;
|
|
630
|
+
custom.style.display = preset.value === "custom" ? "block" : "none";
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
async function saveAgentVoice(agentId) {
|
|
634
|
+
const provider = document.getElementById("voice-provider-" + agentId)?.value || "auto";
|
|
635
|
+
const preset = document.getElementById("voice-preset-" + agentId)?.value || "";
|
|
636
|
+
const custom = document.getElementById("voice-custom-" + agentId)?.value.trim() || "";
|
|
637
|
+
const selectedVoiceId = preset === "custom" ? custom : preset;
|
|
638
|
+
|
|
639
|
+
const voice = selectedVoiceId
|
|
640
|
+
? {
|
|
641
|
+
provider,
|
|
642
|
+
voiceId: selectedVoiceId,
|
|
643
|
+
voice: selectedVoiceId,
|
|
644
|
+
name: getVoiceDisplayName(selectedVoiceId),
|
|
645
|
+
}
|
|
646
|
+
: null;
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
await postJSON("/api/agents-config/update", { agentId, voice });
|
|
650
|
+
showNotification(
|
|
651
|
+
voice
|
|
652
|
+
? `Voice saved for ${agentId}`
|
|
653
|
+
: `Voice reset to default for ${agentId}`,
|
|
654
|
+
);
|
|
655
|
+
} catch (e) {
|
|
656
|
+
showNotification("Failed: " + e.message, true);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async function resetAgentSession(agentId) {
|
|
661
|
+
if (
|
|
662
|
+
!confirm(
|
|
663
|
+
"Reset context window for " +
|
|
664
|
+
agentId +
|
|
665
|
+
"?\\n\\nThis clears the agent's accumulated conversation history. Shared memory files will be re-injected on the next task.",
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
return;
|
|
669
|
+
showNotification("Resetting " + agentId + " session...");
|
|
670
|
+
try {
|
|
671
|
+
await postJSON("/api/agents-config/reset-session", { agentId });
|
|
672
|
+
showNotification(agentId + " session reset");
|
|
673
|
+
} catch (e) {
|
|
674
|
+
showNotification("Reset failed: " + e.message, true);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function refreshModelHeader(agentId, model, opencodeModel) {
|
|
679
|
+
const el = document.getElementById("cur-model-" + agentId);
|
|
680
|
+
if (!el) return;
|
|
681
|
+
const chatBroken = BROKEN_MODELS.has(model);
|
|
682
|
+
const ocBroken = opencodeModel && BROKEN_MODELS.has(opencodeModel);
|
|
683
|
+
el.innerHTML =
|
|
684
|
+
`<span style="font-size:11px;font-family:'SF Mono',monospace;color:${chatBroken ? "var(--red-hi)" : "var(--text-2)"};" title="Conversation model">${chatBroken ? "β " : "π¬ "}${model || "(none)"}</span>` +
|
|
685
|
+
(opencodeModel
|
|
686
|
+
? `<span style="font-size:11px;font-family:'SF Mono',monospace;color:${ocBroken ? "var(--red-hi)" : "var(--green-hi)"};" title="OpenCode model">β‘ ${opencodeModel}</span>`
|
|
687
|
+
: "") +
|
|
688
|
+
(chatBroken
|
|
689
|
+
? `<span style="font-size:10px;font-weight:600;color:var(--red-hi);background:rgba(239,68,68,0.1);border:1px solid rgba(239,68,68,0.3);padding:1px 6px;border-radius:4px;">BROKEN β REASSIGN</span>`
|
|
690
|
+
: "");
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async function saveAgentModel(agentId) {
|
|
694
|
+
const txt = document.getElementById("modeltext-" + agentId);
|
|
695
|
+
const sel = document.getElementById("model-" + agentId);
|
|
696
|
+
const model = (txt && txt.value.trim()) || (sel && sel.value) || "";
|
|
697
|
+
if (!model) {
|
|
698
|
+
showNotification("Select or type a model", true);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
if (BROKEN_MODELS.has(model)) {
|
|
702
|
+
showNotification(
|
|
703
|
+
"β That model returns empty responses β choose another",
|
|
704
|
+
true,
|
|
705
|
+
);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
await postJSON("/api/agents-config/update", { agentId, model });
|
|
710
|
+
const ocModel =
|
|
711
|
+
document.getElementById("oc-modeltext-" + agentId)?.value.trim() || "";
|
|
712
|
+
refreshModelHeader(agentId, model, ocModel);
|
|
713
|
+
showNotification(`${agentId} β ${model}`);
|
|
714
|
+
} catch (e) {
|
|
715
|
+
showNotification("Failed: " + e.message, true);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
async function saveAgentFallback(agentId) {
|
|
720
|
+
const inp = document.getElementById("fallback-" + agentId);
|
|
721
|
+
const fallbackModel = inp?.value.trim() || "";
|
|
722
|
+
try {
|
|
723
|
+
await postJSON("/api/agents-config/update", { agentId, fallbackModel });
|
|
724
|
+
showNotification(
|
|
725
|
+
fallbackModel
|
|
726
|
+
? `Fallback set: ${fallbackModel}`
|
|
727
|
+
: `Fallback cleared for ${agentId}`,
|
|
728
|
+
);
|
|
729
|
+
} catch (e) {
|
|
730
|
+
showNotification("Failed: " + e.message, true);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// ββ OpenCode per-agent config βββββββββββββββββββββββββββββββββββββββββββββββ
|
|
735
|
+
let _ocModelsCache = null;
|
|
736
|
+
|
|
737
|
+
async function loadOcModels() {
|
|
738
|
+
if (_ocModelsCache) return _ocModelsCache;
|
|
739
|
+
try {
|
|
740
|
+
const r = await fetch("/api/opencode-models");
|
|
741
|
+
const d = await r.json();
|
|
742
|
+
_ocModelsCache = Array.isArray(d.models) ? d.models : [];
|
|
743
|
+
} catch {
|
|
744
|
+
_ocModelsCache = [];
|
|
745
|
+
}
|
|
746
|
+
return _ocModelsCache;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const OC_MODEL_LABELS = {
|
|
750
|
+
"opencode/big-pickle": "Big Pickle (Stealth)",
|
|
751
|
+
"opencode/trinity-large-preview-free": "Trinity Large Preview (Stealth)",
|
|
752
|
+
"opencode/gpt-5": "GPT 5",
|
|
753
|
+
"opencode/gpt-5-codex": "GPT 5 Codex",
|
|
754
|
+
"opencode/gpt-5-nano": "GPT 5 Nano",
|
|
755
|
+
"opencode/gpt-5.1": "GPT 5.1",
|
|
756
|
+
"opencode/gpt-5.1-codex": "GPT 5.1 Codex",
|
|
757
|
+
"opencode/gpt-5.1-codex-max": "GPT 5.1 Codex Max",
|
|
758
|
+
"opencode/gpt-5.1-codex-mini": "GPT 5.1 Codex Mini",
|
|
759
|
+
"opencode/gpt-5.2": "GPT 5.2",
|
|
760
|
+
"opencode/gpt-5.2-codex": "GPT 5.2 Codex",
|
|
761
|
+
"opencode/alpha-gpt-5.3-codex": "GPT 5.3 Codex (alpha)",
|
|
762
|
+
"opencode/alpha-gpt-5.4": "GPT 5.4 (alpha)",
|
|
763
|
+
"opencode/claude-sonnet-4": "Claude Sonnet 4",
|
|
764
|
+
"opencode/claude-sonnet-4-5": "Claude Sonnet 4.5",
|
|
765
|
+
"opencode/claude-sonnet-4-6": "Claude Sonnet 4.6",
|
|
766
|
+
"opencode/claude-opus-4-1": "Claude Opus 4.1",
|
|
767
|
+
"opencode/claude-opus-4-5": "Claude Opus 4.5",
|
|
768
|
+
"opencode/claude-opus-4-6": "Claude Opus 4.6",
|
|
769
|
+
"opencode/claude-haiku-4-5": "Claude Haiku 4.5",
|
|
770
|
+
"opencode/claude-3-5-haiku": "Claude 3.5 Haiku",
|
|
771
|
+
"opencode/gemini-3-flash": "Gemini 3 Flash",
|
|
772
|
+
"opencode/gemini-3-pro": "Gemini 3 Pro",
|
|
773
|
+
"opencode/gemini-3.1-pro": "Gemini 3.1 Pro",
|
|
774
|
+
"opencode/kimi-k2": "Kimi K2",
|
|
775
|
+
"opencode/kimi-k2-thinking": "Kimi K2 Thinking",
|
|
776
|
+
"opencode/kimi-k2.5": "Kimi K2.5",
|
|
777
|
+
"opencode/kimi-k2.5-free": "Kimi K2.5 Free",
|
|
778
|
+
"opencode/glm-4.6": "GLM 4.6 (Z.ai)",
|
|
779
|
+
"opencode/glm-4.7": "GLM 4.7 (Z.ai)",
|
|
780
|
+
"opencode/glm-5": "GLM 5 (Z.ai)",
|
|
781
|
+
"opencode/glm-5-free": "GLM 5 Free (Z.ai)",
|
|
782
|
+
"opencode/minimax-m2.1": "MiniMax M2.1",
|
|
783
|
+
"opencode/minimax-m2.1-free": "MiniMax M2.1 Free",
|
|
784
|
+
"opencode/minimax-m2.5": "MiniMax M2.5",
|
|
785
|
+
"opencode/minimax-m2.5-free": "MiniMax M2.5 Free",
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
function populateOcModelDropdown(selectId, currentVal) {
|
|
789
|
+
const sel = document.getElementById(selectId);
|
|
790
|
+
if (!sel) return;
|
|
791
|
+
sel.innerHTML = '<option value="">β select model β</option>';
|
|
792
|
+
|
|
793
|
+
// Merge OpenCode server models + all provider models so Groq/xAI/etc all appear
|
|
794
|
+
const ocModels = (_ocModelsCache || []).map((m) =>
|
|
795
|
+
typeof m === "string"
|
|
796
|
+
? m
|
|
797
|
+
: m.provider
|
|
798
|
+
? m.provider + "/" + m.id
|
|
799
|
+
: m.id || m.name || String(m),
|
|
800
|
+
);
|
|
801
|
+
const allCombined = [...new Set([...ocModels, ...(_allModels || [])])].filter(
|
|
802
|
+
Boolean,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
const grouped = {};
|
|
806
|
+
allCombined.forEach((full) => {
|
|
807
|
+
const provider = full.includes("/") ? full.split("/")[0] : "other";
|
|
808
|
+
if (!grouped[provider]) grouped[provider] = [];
|
|
809
|
+
grouped[provider].push(full);
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
for (const [provider, ids] of Object.entries(grouped)) {
|
|
813
|
+
const grp = document.createElement("optgroup");
|
|
814
|
+
grp.label = provider.toUpperCase();
|
|
815
|
+
ids.forEach((full) => {
|
|
816
|
+
const opt = document.createElement("option");
|
|
817
|
+
opt.value = full;
|
|
818
|
+
opt.textContent = OC_MODEL_LABELS[full] || full;
|
|
819
|
+
if (full === currentVal) opt.selected = true;
|
|
820
|
+
grp.appendChild(opt);
|
|
821
|
+
});
|
|
822
|
+
sel.appendChild(grp);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (currentVal && !sel.value) {
|
|
826
|
+
const opt = document.createElement("option");
|
|
827
|
+
opt.value = currentVal;
|
|
828
|
+
opt.textContent = (OC_MODEL_LABELS[currentVal] || currentVal) + " (custom)";
|
|
829
|
+
opt.selected = true;
|
|
830
|
+
sel.prepend(opt);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Cursor CLI subscription models (populated from agent models command)
|
|
835
|
+
const CURSOR_CLI_MODELS = [
|
|
836
|
+
{ id: "", label: "β auto (subscription default) β" },
|
|
837
|
+
{
|
|
838
|
+
id: "sonnet-4.5-thinking",
|
|
839
|
+
label: "Claude 4.5 Sonnet (Thinking) β default",
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
id: "opus-4.6-thinking",
|
|
843
|
+
label: "Claude 4.6 Opus (Thinking) β best reasoning",
|
|
844
|
+
},
|
|
845
|
+
{ id: "opus-4.6", label: "Claude 4.6 Opus" },
|
|
846
|
+
{ id: "sonnet-4.6-thinking", label: "Claude 4.6 Sonnet (Thinking)" },
|
|
847
|
+
{ id: "sonnet-4.6", label: "Claude 4.6 Sonnet β best coding" },
|
|
848
|
+
{ id: "sonnet-4.5", label: "Claude 4.5 Sonnet" },
|
|
849
|
+
{ id: "gpt-5.3-codex-xhigh", label: "GPT-5.3 Codex XHigh" },
|
|
850
|
+
{ id: "gpt-5.3-codex-high", label: "GPT-5.3 Codex High" },
|
|
851
|
+
{ id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
|
|
852
|
+
{ id: "gpt-5.3-codex-fast", label: "GPT-5.3 Codex Fast" },
|
|
853
|
+
{ id: "gpt-5.2", label: "GPT-5.2" },
|
|
854
|
+
{ id: "gemini-3.1-pro", label: "Gemini 3.1 Pro" },
|
|
855
|
+
{ id: "gemini-3-flash", label: "Gemini 3 Flash" },
|
|
856
|
+
{ id: "grok", label: "Grok" },
|
|
857
|
+
{ id: "kimi-k2.5", label: "Kimi K2.5" },
|
|
858
|
+
];
|
|
859
|
+
|
|
860
|
+
function populateCursorModelDropdown(selId, currentVal) {
|
|
861
|
+
const sel = document.getElementById(selId);
|
|
862
|
+
if (!sel) return;
|
|
863
|
+
sel.innerHTML = CURSOR_CLI_MODELS.map(
|
|
864
|
+
(m) =>
|
|
865
|
+
'<option value="' +
|
|
866
|
+
m.id +
|
|
867
|
+
'"' +
|
|
868
|
+
(m.id === (currentVal || "") ? " selected" : "") +
|
|
869
|
+
">" +
|
|
870
|
+
m.label +
|
|
871
|
+
"</option>",
|
|
872
|
+
).join("");
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function syncCursorModelText(agentId) {
|
|
876
|
+
const sel = document.getElementById("cursor-model-sel-" + agentId);
|
|
877
|
+
const txt = document.getElementById("cursor-model-txt-" + agentId);
|
|
878
|
+
if (sel && txt) txt.value = sel.value;
|
|
879
|
+
}
|
|
880
|
+
window.syncCursorModelText = syncCursorModelText;
|
|
881
|
+
|
|
882
|
+
function populateGenericModelDropdown(selectId, currentVal) {
|
|
883
|
+
const sel = document.getElementById(selectId);
|
|
884
|
+
if (!sel) return;
|
|
885
|
+
sel.innerHTML = '<option value="">β auto / default β</option>';
|
|
886
|
+
|
|
887
|
+
const grouped = {};
|
|
888
|
+
(_allModels || []).forEach((full) => {
|
|
889
|
+
const provider = full.includes("/") ? full.split("/")[0] : "other";
|
|
890
|
+
if (!grouped[provider]) grouped[provider] = [];
|
|
891
|
+
grouped[provider].push(full);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
for (const [provider, ids] of Object.entries(grouped)) {
|
|
895
|
+
const grp = document.createElement("optgroup");
|
|
896
|
+
grp.label = provider.toUpperCase();
|
|
897
|
+
ids.forEach((full) => {
|
|
898
|
+
const opt = document.createElement("option");
|
|
899
|
+
opt.value = full;
|
|
900
|
+
opt.textContent = full;
|
|
901
|
+
if (full === currentVal) opt.selected = true;
|
|
902
|
+
grp.appendChild(opt);
|
|
903
|
+
});
|
|
904
|
+
sel.appendChild(grp);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (currentVal && !sel.value) {
|
|
908
|
+
const opt = document.createElement("option");
|
|
909
|
+
opt.value = currentVal;
|
|
910
|
+
opt.textContent = currentVal + " (custom)";
|
|
911
|
+
opt.selected = true;
|
|
912
|
+
sel.prepend(opt);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// route toggle β mutually exclusive
|
|
917
|
+
async function setRoute(agentId, route) {
|
|
918
|
+
// Map engine IDs to agent config keys
|
|
919
|
+
const ENGINE_CONFIG_MAP = {
|
|
920
|
+
direct: {},
|
|
921
|
+
opencode: { useOpenCode: true },
|
|
922
|
+
cursor: { useCursorCli: true },
|
|
923
|
+
"claude-code": { useClaudeCode: true },
|
|
924
|
+
claudecode: { useClaudeCode: true },
|
|
925
|
+
codex: { useCodex: true },
|
|
926
|
+
"gemini-cli": { useGeminiCli: true },
|
|
927
|
+
gemini: { useGeminiCli: true },
|
|
928
|
+
"crew-cli": { useCrewCLI: true },
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
// Get the config update for this engine
|
|
932
|
+
const configUpdate = ENGINE_CONFIG_MAP[route] || {};
|
|
933
|
+
|
|
934
|
+
// Reset all engine flags
|
|
935
|
+
const payload = {
|
|
936
|
+
agentId,
|
|
937
|
+
useOpenCode: false,
|
|
938
|
+
useCursorCli: false,
|
|
939
|
+
useClaudeCode: false,
|
|
940
|
+
useCodex: false,
|
|
941
|
+
useGeminiCli: false,
|
|
942
|
+
useCrewCLI: false,
|
|
943
|
+
...configUpdate,
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
// Update all button styles dynamically
|
|
947
|
+
const container = document.getElementById(`engine-buttons-${agentId}`);
|
|
948
|
+
if (container) {
|
|
949
|
+
const allButtons = container.querySelectorAll("button");
|
|
950
|
+
allButtons.forEach((btn) => {
|
|
951
|
+
const btnRoute = btn.dataset.arg2;
|
|
952
|
+
const eng = _engines.find((e) => e.id === btnRoute) || {};
|
|
953
|
+
const color = ENGINE_COLORS[btnRoute] || eng.color || "#6b7280";
|
|
954
|
+
const isActive = btnRoute === route;
|
|
955
|
+
|
|
956
|
+
btn.style.borderColor = isActive ? color : "var(--border)";
|
|
957
|
+
btn.style.background = isActive ? `${color}20` : "var(--surface-2)";
|
|
958
|
+
btn.style.color = isActive ? color : "var(--text-2)";
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// Show/hide model configuration rows
|
|
963
|
+
const anyEngine = route !== "direct";
|
|
964
|
+
const ocRow = document.getElementById("oc-model-row-" + agentId);
|
|
965
|
+
const ocFbRow = document.getElementById("oc-fallback-row-" + agentId);
|
|
966
|
+
const cursorRow = document.getElementById("cursor-model-row-" + agentId);
|
|
967
|
+
const ccRow = document.getElementById("claudecode-model-row-" + agentId);
|
|
968
|
+
const codexRow = document.getElementById("codex-model-row-" + agentId);
|
|
969
|
+
const geminiRow = document.getElementById("gemini-model-row-" + agentId);
|
|
970
|
+
const crewCliRow = document.getElementById("crew-cli-config-row-" + agentId);
|
|
971
|
+
const loopRow = document.getElementById("loop-row-" + agentId);
|
|
972
|
+
|
|
973
|
+
if (ocRow) ocRow.style.display = route === "opencode" ? "flex" : "none";
|
|
974
|
+
if (ocFbRow) ocFbRow.style.display = route === "opencode" ? "flex" : "none";
|
|
975
|
+
if (cursorRow) cursorRow.style.display = route === "cursor" ? "flex" : "none";
|
|
976
|
+
if (ccRow)
|
|
977
|
+
ccRow.style.display =
|
|
978
|
+
route === "claude-code" || route === "claudecode" ? "flex" : "none";
|
|
979
|
+
if (codexRow) codexRow.style.display = route === "codex" ? "flex" : "none";
|
|
980
|
+
if (geminiRow)
|
|
981
|
+
geminiRow.style.display =
|
|
982
|
+
route === "gemini-cli" || route === "gemini" ? "flex" : "none";
|
|
983
|
+
if (crewCliRow)
|
|
984
|
+
crewCliRow.style.display = route === "crew-cli" ? "flex" : "none";
|
|
985
|
+
if (loopRow) loopRow.style.display = anyEngine ? "flex" : "none";
|
|
986
|
+
|
|
987
|
+
// Save to backend
|
|
988
|
+
try {
|
|
989
|
+
await postJSON("/api/agents-config/update", payload);
|
|
990
|
+
const eng = _engines.find((e) => e.id === route) || { label: route };
|
|
991
|
+
showNotification(`${agentId} β ${eng.label || route}`);
|
|
992
|
+
} catch (e) {
|
|
993
|
+
showNotification("Failed: " + e.message, true);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
async function saveCursorCliConfig(agentId) {
|
|
998
|
+
const cursorCliModel = (
|
|
999
|
+
document.getElementById("cursor-model-txt-" + agentId)?.value || ""
|
|
1000
|
+
).trim();
|
|
1001
|
+
try {
|
|
1002
|
+
await postJSON("/api/agents-config/update", { agentId, cursorCliModel });
|
|
1003
|
+
showNotification(agentId + " Cursor model β " + (cursorCliModel || "auto"));
|
|
1004
|
+
} catch (e) {
|
|
1005
|
+
showNotification("Failed: " + e.message, true);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
async function saveClaudeCodeConfig(agentId) {
|
|
1010
|
+
const claudeCodeModel = (
|
|
1011
|
+
document.getElementById("claudecode-model-txt-" + agentId)?.value || ""
|
|
1012
|
+
).trim();
|
|
1013
|
+
try {
|
|
1014
|
+
await postJSON("/api/agents-config/update", { agentId, claudeCodeModel });
|
|
1015
|
+
showNotification(
|
|
1016
|
+
agentId + " Claude Code model β " + (claudeCodeModel || "auto"),
|
|
1017
|
+
);
|
|
1018
|
+
} catch (e) {
|
|
1019
|
+
showNotification("Failed: " + e.message, true);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
function syncCodexModelText(agentId) {
|
|
1024
|
+
const sel = document.getElementById("codex-model-sel-" + agentId);
|
|
1025
|
+
const txt = document.getElementById("codex-model-txt-" + agentId);
|
|
1026
|
+
if (sel && txt) txt.value = sel.value;
|
|
1027
|
+
}
|
|
1028
|
+
window.syncCodexModelText = syncCodexModelText;
|
|
1029
|
+
|
|
1030
|
+
async function saveCodexConfig(agentId) {
|
|
1031
|
+
const codexModel = (
|
|
1032
|
+
document.getElementById("codex-model-txt-" + agentId)?.value || ""
|
|
1033
|
+
).trim();
|
|
1034
|
+
try {
|
|
1035
|
+
await postJSON("/api/agents-config/update", { agentId, codexModel });
|
|
1036
|
+
showNotification(agentId + " Codex model β " + (codexModel || "auto"));
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
showNotification("Failed: " + e.message, true);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function syncGeminiModelText(agentId) {
|
|
1043
|
+
const sel = document.getElementById("gemini-model-sel-" + agentId);
|
|
1044
|
+
const txt = document.getElementById("gemini-model-txt-" + agentId);
|
|
1045
|
+
if (sel && txt) txt.value = sel.value;
|
|
1046
|
+
}
|
|
1047
|
+
window.syncGeminiModelText = syncGeminiModelText;
|
|
1048
|
+
|
|
1049
|
+
async function saveGeminiCliConfig(agentId) {
|
|
1050
|
+
const geminiCliModel = (
|
|
1051
|
+
document.getElementById("gemini-model-txt-" + agentId)?.value || ""
|
|
1052
|
+
).trim();
|
|
1053
|
+
try {
|
|
1054
|
+
await postJSON("/api/agents-config/update", { agentId, geminiCliModel });
|
|
1055
|
+
showNotification(agentId + " Gemini model β " + (geminiCliModel || "auto"));
|
|
1056
|
+
} catch (e) {
|
|
1057
|
+
showNotification("Failed: " + e.message, true);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
async function saveCrewCLIConfig(agentId) {
|
|
1062
|
+
const crewCliModel = (
|
|
1063
|
+
document.getElementById("crew-cli-model-txt-" + agentId)?.value || ""
|
|
1064
|
+
).trim();
|
|
1065
|
+
try {
|
|
1066
|
+
await postJSON("/api/agents-config/update", {
|
|
1067
|
+
agentId,
|
|
1068
|
+
useCrewCLI: true,
|
|
1069
|
+
crewCliModel,
|
|
1070
|
+
});
|
|
1071
|
+
showNotification(
|
|
1072
|
+
agentId + " Crew CLI model β " + (crewCliModel || "default"),
|
|
1073
|
+
);
|
|
1074
|
+
} catch (e) {
|
|
1075
|
+
showNotification("Failed: " + e.message, true);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function syncCrewCliModelText(agentId) {
|
|
1080
|
+
const sel = document.getElementById("crew-cli-model-sel-" + agentId);
|
|
1081
|
+
const txt = document.getElementById("crew-cli-model-txt-" + agentId);
|
|
1082
|
+
if (sel && txt) txt.value = sel.value;
|
|
1083
|
+
}
|
|
1084
|
+
window.syncCrewCliModelText = syncCrewCliModelText;
|
|
1085
|
+
|
|
1086
|
+
async function saveAgentLoop(agentId) {
|
|
1087
|
+
const enabled =
|
|
1088
|
+
document.getElementById("loop-toggle-" + agentId)?.checked ?? false;
|
|
1089
|
+
const maxRoundsRaw = document.getElementById("loop-rounds-" + agentId)?.value;
|
|
1090
|
+
const opencodeLoopMaxRounds = Math.min(
|
|
1091
|
+
20,
|
|
1092
|
+
Math.max(1, parseInt(maxRoundsRaw || "10", 10)),
|
|
1093
|
+
);
|
|
1094
|
+
try {
|
|
1095
|
+
await postJSON("/api/agents-config/update", {
|
|
1096
|
+
agentId,
|
|
1097
|
+
opencodeLoop: enabled,
|
|
1098
|
+
opencodeLoopMaxRounds,
|
|
1099
|
+
});
|
|
1100
|
+
showNotification(
|
|
1101
|
+
agentId +
|
|
1102
|
+
" loop " +
|
|
1103
|
+
(enabled ? `ON (${opencodeLoopMaxRounds} rounds max)` : "OFF"),
|
|
1104
|
+
);
|
|
1105
|
+
} catch (e) {
|
|
1106
|
+
showNotification("Failed: " + e.message, true);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
window.saveAgentLoop = saveAgentLoop;
|
|
1110
|
+
|
|
1111
|
+
function syncClaudeCodeModelText(agentId) {
|
|
1112
|
+
const sel = document.getElementById("claudecode-model-sel-" + agentId);
|
|
1113
|
+
const txt = document.getElementById("claudecode-model-txt-" + agentId);
|
|
1114
|
+
if (sel && txt) txt.value = sel.value;
|
|
1115
|
+
}
|
|
1116
|
+
window.syncClaudeCodeModelText = syncClaudeCodeModelText;
|
|
1117
|
+
|
|
1118
|
+
function toggleOpenCodeUI(agentId) {
|
|
1119
|
+
// Legacy β kept for any stale references; use setRoute instead
|
|
1120
|
+
const checked = document.getElementById("oc-toggle-" + agentId)?.checked;
|
|
1121
|
+
if (checked !== undefined) setRoute(agentId, checked ? "opencode" : "direct");
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function syncOcModelText(agentId) {
|
|
1125
|
+
const sel = document.getElementById("oc-model-" + agentId);
|
|
1126
|
+
const txt = document.getElementById("oc-modeltext-" + agentId);
|
|
1127
|
+
if (sel && txt && sel.value) txt.value = sel.value;
|
|
1128
|
+
}
|
|
1129
|
+
window.syncOcModelText = syncOcModelText;
|
|
1130
|
+
|
|
1131
|
+
function syncOcFallbackText(agentId) {
|
|
1132
|
+
const sel = document.getElementById("oc-fallback-sel-" + agentId);
|
|
1133
|
+
const txt = document.getElementById("oc-fallback-" + agentId);
|
|
1134
|
+
if (sel && txt && sel.value) txt.value = sel.value;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
async function saveOpenCodeFallback(agentId) {
|
|
1138
|
+
const opencodeFallbackModel = (
|
|
1139
|
+
document.getElementById("oc-fallback-" + agentId)?.value || ""
|
|
1140
|
+
).trim();
|
|
1141
|
+
try {
|
|
1142
|
+
await postJSON("/api/agents-config/update", {
|
|
1143
|
+
agentId,
|
|
1144
|
+
opencodeFallbackModel,
|
|
1145
|
+
});
|
|
1146
|
+
showNotification(
|
|
1147
|
+
opencodeFallbackModel
|
|
1148
|
+
? agentId + " OC fallback β " + opencodeFallbackModel
|
|
1149
|
+
: "OC fallback cleared for " + agentId,
|
|
1150
|
+
);
|
|
1151
|
+
} catch (e) {
|
|
1152
|
+
showNotification("Failed: " + e.message, true);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
async function saveOpenCodeConfig(agentId) {
|
|
1157
|
+
// Only saves the opencodeModel β route (useOpenCode flag) is set by the route buttons via setRoute().
|
|
1158
|
+
// Reading the old oc-toggle checkbox here was a bug: the checkbox no longer exists, causing it
|
|
1159
|
+
// to always send useOpenCode:false and toast "β direct LLM" even when OpenCode route was active.
|
|
1160
|
+
const opencodeModel = (
|
|
1161
|
+
document.getElementById("oc-modeltext-" + agentId)?.value || ""
|
|
1162
|
+
).trim();
|
|
1163
|
+
try {
|
|
1164
|
+
await postJSON("/api/agents-config/update", { agentId, opencodeModel });
|
|
1165
|
+
const chatModel =
|
|
1166
|
+
document.getElementById("modeltext-" + agentId)?.value.trim() || "";
|
|
1167
|
+
refreshModelHeader(agentId, chatModel, opencodeModel);
|
|
1168
|
+
showNotification(agentId + " OC model β " + (opencodeModel || "default"));
|
|
1169
|
+
} catch (e) {
|
|
1170
|
+
showNotification("Failed: " + e.message, true);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
async function saveCursorCliToggle(agentId) {
|
|
1175
|
+
// Legacy shim β delegates to setRoute
|
|
1176
|
+
const useCursorCli =
|
|
1177
|
+
document.getElementById("cursor-cli-toggle-" + agentId)?.checked || false;
|
|
1178
|
+
await setRoute(agentId, useCursorCli ? "cursor" : "direct");
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Bulk route setter β apply a route to all coding agents at once
|
|
1182
|
+
async function bulkSetRoute(route, model) {
|
|
1183
|
+
// All agents that write code or docs (have write_file/mkdir access)
|
|
1184
|
+
const CODING_AGENTS = [
|
|
1185
|
+
"crew-coder",
|
|
1186
|
+
"crew-coder-front",
|
|
1187
|
+
"crew-coder-back",
|
|
1188
|
+
"crew-frontend",
|
|
1189
|
+
"crew-fixer",
|
|
1190
|
+
"crew-architect",
|
|
1191
|
+
"crew-ml",
|
|
1192
|
+
"crew-copywriter",
|
|
1193
|
+
"crew-main",
|
|
1194
|
+
"crew-pm",
|
|
1195
|
+
"crew-mega",
|
|
1196
|
+
"crew-lead",
|
|
1197
|
+
];
|
|
1198
|
+
|
|
1199
|
+
// Get engine info from API
|
|
1200
|
+
const enginesData = await getJSON("/api/engines").catch(() => ({
|
|
1201
|
+
engines: [],
|
|
1202
|
+
}));
|
|
1203
|
+
const engine = enginesData.engines.find((e) => e.id === route);
|
|
1204
|
+
const label = engine
|
|
1205
|
+
? engine.label
|
|
1206
|
+
: route === "direct"
|
|
1207
|
+
? "Direct API"
|
|
1208
|
+
: route;
|
|
1209
|
+
|
|
1210
|
+
showNotification("Applying " + label + " to all coding agentsβ¦");
|
|
1211
|
+
for (const agentId of CODING_AGENTS) {
|
|
1212
|
+
try {
|
|
1213
|
+
const payload = {
|
|
1214
|
+
agentId,
|
|
1215
|
+
useOpenCode: false,
|
|
1216
|
+
useCursorCli: false,
|
|
1217
|
+
useClaudeCode: false,
|
|
1218
|
+
useCodex: false,
|
|
1219
|
+
useGeminiCli: false,
|
|
1220
|
+
useCrewCLI: false,
|
|
1221
|
+
useDockerSandbox: false,
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
// Set the selected route flag to true
|
|
1225
|
+
if (route === "opencode") payload.useOpenCode = true;
|
|
1226
|
+
else if (route === "cursor") payload.useCursorCli = true;
|
|
1227
|
+
else if (route === "claudecode" || route === "claude-code")
|
|
1228
|
+
payload.useClaudeCode = true;
|
|
1229
|
+
else if (route === "codex") payload.useCodex = true;
|
|
1230
|
+
else if (route === "gemini" || route === "gemini-cli")
|
|
1231
|
+
payload.useGeminiCli = true;
|
|
1232
|
+
else if (route === "crew-cli") payload.useCrewCLI = true;
|
|
1233
|
+
else if (route === "docker-sandbox") payload.useDockerSandbox = true;
|
|
1234
|
+
|
|
1235
|
+
if (model && route === "cursor") payload.cursorCliModel = model;
|
|
1236
|
+
if (model && route === "opencode") payload.opencodeModel = model;
|
|
1237
|
+
if (model && route === "claudecode") payload.claudeCodeModel = model;
|
|
1238
|
+
if (model && route === "codex") payload.codexModel = model;
|
|
1239
|
+
if (model && route === "gemini") payload.geminiCliModel = model;
|
|
1240
|
+
if (model && route === "crew-cli") payload.crewCliModel = model;
|
|
1241
|
+
await postJSON("/api/agents-config/update", payload);
|
|
1242
|
+
// Small delay to prevent rapid-fire saves that create backup storms
|
|
1243
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1244
|
+
} catch (e) {
|
|
1245
|
+
console.error("bulkSetRoute failed for", agentId, e.message);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
showNotification(
|
|
1249
|
+
"Done β " +
|
|
1250
|
+
CODING_AGENTS.length +
|
|
1251
|
+
" agents set to " +
|
|
1252
|
+
label +
|
|
1253
|
+
(model ? " (" + model + ")" : ""),
|
|
1254
|
+
);
|
|
1255
|
+
// Reload config to refresh UI with new routes
|
|
1256
|
+
await loadAgents_cfg();
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const AGENT_EMOJIS = [
|
|
1260
|
+
"π€",
|
|
1261
|
+
"π§ ",
|
|
1262
|
+
"β‘",
|
|
1263
|
+
"π₯",
|
|
1264
|
+
"π―",
|
|
1265
|
+
"π‘οΈ",
|
|
1266
|
+
"π§",
|
|
1267
|
+
"π",
|
|
1268
|
+
"π¬",
|
|
1269
|
+
"π",
|
|
1270
|
+
"βοΈ",
|
|
1271
|
+
"π",
|
|
1272
|
+
"π¨",
|
|
1273
|
+
"π₯οΈ",
|
|
1274
|
+
"π±",
|
|
1275
|
+
"π",
|
|
1276
|
+
"π",
|
|
1277
|
+
"π",
|
|
1278
|
+
"π‘",
|
|
1279
|
+
"π",
|
|
1280
|
+
"βοΈ",
|
|
1281
|
+
"π¦",
|
|
1282
|
+
"π¦Ύ",
|
|
1283
|
+
"π»",
|
|
1284
|
+
"ποΈ",
|
|
1285
|
+
"π",
|
|
1286
|
+
"π",
|
|
1287
|
+
"π¬",
|
|
1288
|
+
"π§ͺ",
|
|
1289
|
+
"π",
|
|
1290
|
+
];
|
|
1291
|
+
|
|
1292
|
+
function toggleEmojiPicker(agentId) {
|
|
1293
|
+
const panel = document.getElementById("aemoji-panel-" + agentId);
|
|
1294
|
+
const grid = document.getElementById("aemoji-grid-" + agentId);
|
|
1295
|
+
const isOpen = panel.classList.contains("open");
|
|
1296
|
+
document
|
|
1297
|
+
.querySelectorAll(".emoji-picker-panel.open")
|
|
1298
|
+
.forEach((p) => p.classList.remove("open"));
|
|
1299
|
+
if (isOpen) return;
|
|
1300
|
+
if (!grid.hasChildNodes()) {
|
|
1301
|
+
grid.innerHTML = AGENT_EMOJIS.map(
|
|
1302
|
+
(e) =>
|
|
1303
|
+
'<div class="emoji-opt" data-agent="' +
|
|
1304
|
+
agentId +
|
|
1305
|
+
'" data-emoji="' +
|
|
1306
|
+
e +
|
|
1307
|
+
'" title="' +
|
|
1308
|
+
e +
|
|
1309
|
+
'">' +
|
|
1310
|
+
e +
|
|
1311
|
+
"</div>",
|
|
1312
|
+
).join("");
|
|
1313
|
+
grid.addEventListener("click", function (ev) {
|
|
1314
|
+
const opt = ev.target.closest(".emoji-opt");
|
|
1315
|
+
if (opt) selectEmoji(opt.dataset.agent, opt.dataset.emoji);
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
panel.classList.add("open");
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function selectEmoji(agentId, emoji) {
|
|
1322
|
+
const isNew = agentId === "__new__";
|
|
1323
|
+
const inputEl = isNew
|
|
1324
|
+
? document.getElementById("naEmoji")
|
|
1325
|
+
: document.getElementById("aemoji-" + agentId);
|
|
1326
|
+
const btnEl = isNew
|
|
1327
|
+
? document.getElementById("naEmoji-btn")
|
|
1328
|
+
: document.getElementById("aemoji-btn-" + agentId);
|
|
1329
|
+
if (inputEl) inputEl.value = emoji;
|
|
1330
|
+
if (btnEl) btnEl.textContent = emoji;
|
|
1331
|
+
document.getElementById("aemoji-panel-" + agentId).classList.remove("open");
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// close picker when clicking outside
|
|
1335
|
+
document.addEventListener("click", (e) => {
|
|
1336
|
+
if (!e.target.closest(".emoji-picker-wrap")) {
|
|
1337
|
+
document
|
|
1338
|
+
.querySelectorAll(".emoji-picker-panel.open")
|
|
1339
|
+
.forEach((p) => p.classList.remove("open"));
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
async function saveAgentIdentity(agentId) {
|
|
1344
|
+
const name = document.getElementById("aname-" + agentId).value.trim();
|
|
1345
|
+
const emoji = document.getElementById("aemoji-" + agentId).value.trim();
|
|
1346
|
+
const theme = document.getElementById("atheme-" + agentId)?.value.trim();
|
|
1347
|
+
try {
|
|
1348
|
+
await postJSON("/api/agents-config/update", {
|
|
1349
|
+
agentId,
|
|
1350
|
+
name,
|
|
1351
|
+
emoji,
|
|
1352
|
+
theme,
|
|
1353
|
+
});
|
|
1354
|
+
showNotification("Identity saved for " + agentId);
|
|
1355
|
+
} catch (e) {
|
|
1356
|
+
showNotification("Failed: " + e.message, true);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
window.applyAgentPromptPreset = function (agentId, preset) {
|
|
1361
|
+
if (!preset || !PROMPT_PRESETS[preset]) return;
|
|
1362
|
+
const ta = document.getElementById("prompt-" + agentId);
|
|
1363
|
+
if (ta) ta.value = PROMPT_PRESETS[preset];
|
|
1364
|
+
// Auto-fill the theme/role field with the preset's display name (strip leading emoji + whitespace)
|
|
1365
|
+
const themeEl = document.getElementById("atheme-" + agentId);
|
|
1366
|
+
if (themeEl) {
|
|
1367
|
+
const opt = PRESET_OPTIONS.find((p) => p.value === preset);
|
|
1368
|
+
if (opt)
|
|
1369
|
+
themeEl.value = opt.label
|
|
1370
|
+
.replace(
|
|
1371
|
+
/^[\u{1F000}-\u{1FFFF}\u2600-\u27BF\uFE0F\u20D0-\u20FF\s]+/u,
|
|
1372
|
+
"",
|
|
1373
|
+
)
|
|
1374
|
+
.trim();
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1378
|
+
async function saveAgentPrompt(agentId) {
|
|
1379
|
+
const systemPrompt = document.getElementById("prompt-" + agentId).value;
|
|
1380
|
+
try {
|
|
1381
|
+
await postJSON("/api/agents-config/update", { agentId, systemPrompt });
|
|
1382
|
+
showNotification("Prompt saved for " + agentId);
|
|
1383
|
+
} catch (e) {
|
|
1384
|
+
showNotification("Failed: " + e.message, true);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
async function startCrew() {
|
|
1389
|
+
try {
|
|
1390
|
+
showNotification("Starting crew bridge daemonsβ¦");
|
|
1391
|
+
const r = await postJSON("/api/crew/start", {});
|
|
1392
|
+
showNotification(r.message || "Crew started");
|
|
1393
|
+
} catch (e) {
|
|
1394
|
+
showNotification("Crew start failed: " + e.message, true);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
const NEW_AGENT_TOOL_PRESETS = {
|
|
1399
|
+
coder: ["write_file", "read_file", "mkdir", "run_cmd"], // frontend, backend, fullstack, ios, android, data, aiml, api, db, rn, web3, automation, fixer
|
|
1400
|
+
writer: ["write_file", "read_file"], // copywriter, docs, design (no shell exec)
|
|
1401
|
+
reviewer: ["read_file"], // qa, strict read-only audit
|
|
1402
|
+
security: ["read_file", "run_cmd"], // security auditor β run scanners but never write
|
|
1403
|
+
orchestrator: ["read_file", "dispatch"], // pm, planner β routes tasks but doesn't write files
|
|
1404
|
+
coordinator: ["write_file", "read_file", "run_cmd", "dispatch"], // main/lead β full access + dispatch, no git
|
|
1405
|
+
devops: ["read_file", "run_cmd", "git"], // devops, github ops
|
|
1406
|
+
comms: ["telegram", "read_file"], // telegram notification agent
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
export function applyNewAgentToolPreset() {
|
|
1410
|
+
const preset = document.getElementById("naToolPreset").value;
|
|
1411
|
+
if (!preset || !NEW_AGENT_TOOL_PRESETS[preset]) return;
|
|
1412
|
+
const allowed = NEW_AGENT_TOOL_PRESETS[preset];
|
|
1413
|
+
document.querySelectorAll(".naToolCheck").forEach((cb) => {
|
|
1414
|
+
cb.checked = allowed.includes(cb.dataset.tool);
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
async function saveAgentTools(agentId) {
|
|
1419
|
+
const container = document.getElementById("tools-" + agentId);
|
|
1420
|
+
const checked = [
|
|
1421
|
+
...container.querySelectorAll("input[type=checkbox]:checked"),
|
|
1422
|
+
].map((el) => el.dataset.tool);
|
|
1423
|
+
try {
|
|
1424
|
+
await postJSON("/api/agents-config/update", {
|
|
1425
|
+
agentId,
|
|
1426
|
+
alsoAllow: checked,
|
|
1427
|
+
});
|
|
1428
|
+
showNotification("Tools saved for " + agentId);
|
|
1429
|
+
} catch (e) {
|
|
1430
|
+
showNotification("Failed: " + e.message, true);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// Single source of truth for all preset options β used by both new-agent form and edit cards
|
|
1435
|
+
const PRESET_OPTIONS = [
|
|
1436
|
+
{ value: "frontend", label: "π¨ Frontend (HTML/CSS/JS)" },
|
|
1437
|
+
{ value: "backend", label: "βοΈ Backend (Node/API/scripts)" },
|
|
1438
|
+
{ value: "fullstack", label: "π§± Full-stack coder" },
|
|
1439
|
+
{ value: "ios", label: "π± iOS / Swift developer" },
|
|
1440
|
+
{ value: "android", label: "π€ Android / Kotlin developer" },
|
|
1441
|
+
{ value: "devops", label: "π§ DevOps / Infrastructure" },
|
|
1442
|
+
{ value: "data", label: "π Data / Analytics / Python" },
|
|
1443
|
+
{ value: "security", label: "π‘οΈ Security auditor" },
|
|
1444
|
+
{ value: "qa", label: "π§ͺ QA / tester" },
|
|
1445
|
+
{ value: "github", label: "π Git & GitHub ops" },
|
|
1446
|
+
{ value: "writer", label: "βοΈ Content / copywriter" },
|
|
1447
|
+
{ value: "design", label: "ποΈ UI/UX designer" },
|
|
1448
|
+
{ value: "pm", label: "π Product manager / planner" },
|
|
1449
|
+
{ value: "aiml", label: "π€ AI / ML engineer" },
|
|
1450
|
+
{ value: "api", label: "π API designer (REST/GraphQL)" },
|
|
1451
|
+
{ value: "database", label: "ποΈ Database specialist" },
|
|
1452
|
+
{ value: "reactnative", label: "π± React Native (cross-platform)" },
|
|
1453
|
+
{ value: "web3", label: "π Web3 / Blockchain (Solidity)" },
|
|
1454
|
+
{ value: "automation", label: "π·οΈ Automation / scraping" },
|
|
1455
|
+
{ value: "docs", label: "π Technical docs writer" },
|
|
1456
|
+
{ value: "orchestrator", label: "π§ Orchestrator / PM loop" },
|
|
1457
|
+
{ value: "lead", label: "π¦ Team lead / coordinator" },
|
|
1458
|
+
{ value: "main", label: "β‘ Main agent (general)" },
|
|
1459
|
+
];
|
|
1460
|
+
function buildPresetOptions(placeholder) {
|
|
1461
|
+
var ph = placeholder || "Presets\u2026";
|
|
1462
|
+
var opts = PRESET_OPTIONS.map(function (p) {
|
|
1463
|
+
return '<option value="' + p.value + '">' + p.label + "</option>";
|
|
1464
|
+
}).join("");
|
|
1465
|
+
return '<option value="">' + ph + "</option>" + opts;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
const PROMPT_PRESETS = {
|
|
1469
|
+
frontend: `Frontend implementation specialist. Apple/Linear/Vercel-level polish is the baseline.
|
|
1470
|
+
|
|
1471
|
+
## Design standard
|
|
1472
|
+
- Typography: system font stack or Inter. 16-18px body, 1.5 line-height. Weight hierarchy (400/500/600/700).
|
|
1473
|
+
- Spacing: 8px grid. Section padding 48-96px. Let content breathe.
|
|
1474
|
+
- Color: muted neutrals + one accent. Dark mode via CSS custom properties. No pure black (#000).
|
|
1475
|
+
- Motion: 200-300ms ease-out. Fade + translateY for reveals. Respect prefers-reduced-motion.
|
|
1476
|
+
- Layout: mobile-first, CSS Grid + Flexbox, max-width 1200px. Full-bleed hero sections.
|
|
1477
|
+
- Components: rounded corners (8-12px), soft layered shadows, no hard borders.
|
|
1478
|
+
- Accessibility: semantic HTML, focus-visible, 4.5:1 contrast, aria-labels.
|
|
1479
|
+
|
|
1480
|
+
## Research β use these sources
|
|
1481
|
+
- @@WEB_FETCH https://developer.apple.com/design/human-interface-guidelines for Apple HIG
|
|
1482
|
+
- @@WEB_SEARCH site:uiverse.io [component] for copy-pasteable HTML/CSS examples (7000+ free)
|
|
1483
|
+
- @@WEB_SEARCH site:css-tricks.com [technique] for CSS guides
|
|
1484
|
+
- @@WEB_SEARCH awwwards [page type] OR onepagelove [page type] for design inspiration
|
|
1485
|
+
- @@WEB_FETCH https://developer.mozilla.org/en-US/docs/Web/CSS/[property] for CSS reference
|
|
1486
|
+
- @@WEB_SEARCH site:codepen.io [component] vanilla CSS for interactive examples
|
|
1487
|
+
|
|
1488
|
+
## Rules
|
|
1489
|
+
- ALWAYS read existing files before editing. Match the design system in place.
|
|
1490
|
+
- If no design system exists, establish CSS custom properties (--color-*, --space-*, --radius-*).
|
|
1491
|
+
- Test mental model: 375px, 768px, 1440px β all three must look intentional.`,
|
|
1492
|
+
|
|
1493
|
+
backend: `Backend specialist. Node.js, APIs, databases, server logic.
|
|
1494
|
+
|
|
1495
|
+
## Standards
|
|
1496
|
+
- ES modules, async/await, no callbacks. Prefer native Node APIs over dependencies.
|
|
1497
|
+
- Every endpoint: input validation, error handling, proper HTTP status codes, structured JSON responses.
|
|
1498
|
+
- Database: parameterized queries only (never string interpolation), connection pooling, transactions for multi-step writes.
|
|
1499
|
+
- Auth: bcrypt/argon2 for passwords, JWT with short expiry + refresh tokens. Never plaintext.
|
|
1500
|
+
- Logging: structured (JSON), include request ID, timestamp, level.
|
|
1501
|
+
- Config via env vars, never hardcoded secrets. Validate required env vars at startup.
|
|
1502
|
+
- @@WEB_SEARCH for library APIs and docs when using packages you haven't used recently.
|
|
1503
|
+
|
|
1504
|
+
## Rules
|
|
1505
|
+
- ALWAYS read existing files before editing. Match patterns and naming.
|
|
1506
|
+
- Think about failures: what happens when the request fails, DB is down, or input is malformed?`,
|
|
1507
|
+
|
|
1508
|
+
fullstack: `Full-stack coding specialist. Clean, readable code across the entire stack.
|
|
1509
|
+
|
|
1510
|
+
## Standards
|
|
1511
|
+
- Small functions, clear names, no dead code. Error handling everywhere.
|
|
1512
|
+
- ES modules (import/export), async/await. Match existing code patterns.
|
|
1513
|
+
- Frontend: semantic HTML, accessible, responsive. Backend: validate inputs, handle errors, proper status codes.
|
|
1514
|
+
- @@WEB_SEARCH for API docs and library usage when using unfamiliar packages.
|
|
1515
|
+
|
|
1516
|
+
## Rules
|
|
1517
|
+
- ALWAYS read existing files before editing β understand what exists.
|
|
1518
|
+
- Surgical edits only β change what's asked, nothing else.
|
|
1519
|
+
- Trace the happy path and one error path mentally before reporting done.`,
|
|
1520
|
+
|
|
1521
|
+
qa: `QA specialist. Systematic audits backed by evidence from the actual code.
|
|
1522
|
+
|
|
1523
|
+
## Process
|
|
1524
|
+
1. @@READ_FILE every file you audit β no exceptions
|
|
1525
|
+
2. Check against: error handling, input validation, edge cases, security, performance, correctness
|
|
1526
|
+
3. Report ONLY issues you can point to in the actual code with real line numbers
|
|
1527
|
+
|
|
1528
|
+
## Output format
|
|
1529
|
+
### CRITICAL β Line N: [issue] β Fix: [exact code]
|
|
1530
|
+
### HIGH β Line N: [issue] β Fix: [exact code]
|
|
1531
|
+
### MEDIUM / LOW
|
|
1532
|
+
### Summary: X issues. Verdict: PASS / PASS WITH WARNINGS / FAIL
|
|
1533
|
+
|
|
1534
|
+
## Rules
|
|
1535
|
+
- Do NOT invent line numbers. Only cite what you read.
|
|
1536
|
+
- CRITICAL issues = FAIL verdict. No exceptions.
|
|
1537
|
+
- You are NOT a coordinator β do NOT use @@DISPATCH.
|
|
1538
|
+
- @@WEB_SEARCH best practices or known vulnerability patterns when unsure.`,
|
|
1539
|
+
|
|
1540
|
+
github: `Git and GitHub specialist.
|
|
1541
|
+
|
|
1542
|
+
## Before any operation
|
|
1543
|
+
- git status, git config user.name, git config user.email
|
|
1544
|
+
- For PRs: gh auth status
|
|
1545
|
+
|
|
1546
|
+
## Commit standard
|
|
1547
|
+
- Conventional commits: feat(scope):, fix(scope):, chore:, docs:, refactor:, test:
|
|
1548
|
+
- Subject β€72 chars. Body explains WHY, not what.
|
|
1549
|
+
- Stage specific files β never git add -A unless asked.
|
|
1550
|
+
- Never commit: .env, *.pem, *credentials*, API keys.
|
|
1551
|
+
|
|
1552
|
+
## Rules
|
|
1553
|
+
- Never force-push to main or master.
|
|
1554
|
+
- Always git diff --stat before committing.
|
|
1555
|
+
- One logical change per commit.`,
|
|
1556
|
+
|
|
1557
|
+
writer: `Content and copywriting specialist.
|
|
1558
|
+
|
|
1559
|
+
## Voice
|
|
1560
|
+
- Clear, confident, human. Short sentences. Active voice. Cut every word that doesn't earn its place.
|
|
1561
|
+
- Headlines: benefit-first, specific, no jargon. "Ship 10x faster" beats "Leverage AI-powered solutions."
|
|
1562
|
+
- No buzzwords: leverage, synergy, cutting-edge, revolutionary, seamless, robust.
|
|
1563
|
+
- No filler: "In today's fast-paced world..." β delete it.
|
|
1564
|
+
- Numbers > adjectives. "3 agents, 12 seconds" beats "multiple agents, incredibly fast."
|
|
1565
|
+
|
|
1566
|
+
## Research β mandatory
|
|
1567
|
+
- @@WEB_SEARCH competitors, market positioning, and facts BEFORE writing. Never invent claims.
|
|
1568
|
+
- @@WEB_FETCH reference sites for tone/style inspiration.
|
|
1569
|
+
|
|
1570
|
+
## Rules
|
|
1571
|
+
- ALWAYS @@WRITE_FILE your output β never just show text in chat.
|
|
1572
|
+
- Read existing content first to match voice. After draft, cut 30%.`,
|
|
1573
|
+
|
|
1574
|
+
ios: `iOS/Swift specialist. SwiftUI, UIKit, and native Apple platform code.
|
|
1575
|
+
|
|
1576
|
+
## Standards
|
|
1577
|
+
- SwiftUI for new views unless the project uses UIKit exclusively.
|
|
1578
|
+
- Swift naming: camelCase vars, PascalCase types. async/await over completion handlers.
|
|
1579
|
+
- Use @MainActor for UI updates. Structured concurrency with TaskGroup when appropriate.
|
|
1580
|
+
- Follow MVVM with ObservableObject/Observable. Keep views thin.
|
|
1581
|
+
- @@WEB_SEARCH Apple developer docs and WWDC sessions for current APIs.
|
|
1582
|
+
|
|
1583
|
+
## Rules
|
|
1584
|
+
- ALWAYS read existing Swift files before editing.
|
|
1585
|
+
- Handle optionals safely β guard let / if let, never force-unwrap in production.
|
|
1586
|
+
- Support Dynamic Type and VoiceOver accessibility.`,
|
|
1587
|
+
|
|
1588
|
+
android: `Android/Kotlin specialist. Jetpack Compose, Android SDK, and modern Android architecture.
|
|
1589
|
+
|
|
1590
|
+
## Standards
|
|
1591
|
+
- Jetpack Compose for new UI unless the project uses XML layouts.
|
|
1592
|
+
- Architecture: MVVM with ViewModel, StateFlow/SharedFlow, Hilt for DI.
|
|
1593
|
+
- Coroutines and Flow for async. Structured concurrency with viewModelScope.
|
|
1594
|
+
- Follow Material 3 design guidelines.
|
|
1595
|
+
- @@WEB_SEARCH Android developer docs for current API patterns and Compose components.
|
|
1596
|
+
|
|
1597
|
+
## Rules
|
|
1598
|
+
- ALWAYS read existing files before editing. Match architecture patterns.
|
|
1599
|
+
- Handle configuration changes properly. Test on multiple screen sizes.`,
|
|
1600
|
+
|
|
1601
|
+
devops: `DevOps and infrastructure specialist. CI/CD, Docker, shell scripts, IaC.
|
|
1602
|
+
|
|
1603
|
+
## Standards
|
|
1604
|
+
- Idempotent scripts β safe to run multiple times.
|
|
1605
|
+
- Dockerfiles: multi-stage builds, non-root user, minimal base images, .dockerignore.
|
|
1606
|
+
- CI/CD: fail fast, cache dependencies, pin action versions.
|
|
1607
|
+
- IaC: Terraform state management, modular configs, no hardcoded values.
|
|
1608
|
+
- @@WEB_SEARCH current best practices for tools and cloud services.
|
|
1609
|
+
|
|
1610
|
+
## Rules
|
|
1611
|
+
- ALWAYS read existing configs before editing. Never blindly overwrite deployment configs.
|
|
1612
|
+
- Secrets in env vars or secret managers, never in source.
|
|
1613
|
+
- Write clear inline comments in all scripts and configs.`,
|
|
1614
|
+
|
|
1615
|
+
data: `Data and analytics specialist. Python, SQL, pandas, data pipelines.
|
|
1616
|
+
|
|
1617
|
+
## Standards
|
|
1618
|
+
- Clean Python with type hints and docstrings. Validate inputs, handle nulls explicitly.
|
|
1619
|
+
- pandas/polars for transformation, matplotlib/plotly for visualization.
|
|
1620
|
+
- SQL: parameterized queries, CTEs for readability, explain plans for optimization.
|
|
1621
|
+
- @@WEB_SEARCH for library APIs, dataset documentation, and statistical methods.
|
|
1622
|
+
|
|
1623
|
+
## Rules
|
|
1624
|
+
- ALWAYS read existing data files and schemas before writing code.
|
|
1625
|
+
- NEVER overwrite raw data. Transform into new files/tables.
|
|
1626
|
+
- Reproducibility: set random seeds, log parameters, version datasets.`,
|
|
1627
|
+
|
|
1628
|
+
security: `Security auditor. OWASP-aware, evidence-based.
|
|
1629
|
+
|
|
1630
|
+
## Audit checklist
|
|
1631
|
+
- Secrets: hardcoded API keys/tokens/passwords, .env in source, secrets in logs or client code
|
|
1632
|
+
- Injection: SQL string concat, unescaped user input (XSS), user input in exec/spawn, path traversal
|
|
1633
|
+
- Auth: missing auth on protected routes, broken sessions, privilege escalation, CORS misconfiguration
|
|
1634
|
+
- Data: plaintext passwords, sensitive data in URLs, missing rate limiting, no input validation
|
|
1635
|
+
- @@WEB_SEARCH to verify if a pattern is actually exploitable when unsure
|
|
1636
|
+
|
|
1637
|
+
## Rules
|
|
1638
|
+
- @@READ_FILE every file before reporting. Never guess.
|
|
1639
|
+
- Report only β NEVER modify files.
|
|
1640
|
+
- Output: severity + file:line + vulnerability + exact remediation.
|
|
1641
|
+
- Overall risk: CRITICAL / HIGH / MODERATE / LOW.`,
|
|
1642
|
+
|
|
1643
|
+
design: `UI/UX design and implementation specialist. You ship premium, production-ready interfaces.
|
|
1644
|
+
|
|
1645
|
+
## Design DNA β Apple.com, Linear.app, Vercel.com, Stripe.com level quality.
|
|
1646
|
+
- Reduction: remove every element that doesn't serve the user's goal. White space is a feature.
|
|
1647
|
+
- Typography: Inter or system stack. Scale 14/16/20/28/40/56px. Weight 400/500/600/700. Line-height 1.5 body, 1.2 display.
|
|
1648
|
+
- Color: neutrals (gray-50β950) + one accent. Dark mode first via custom properties. No pure #000.
|
|
1649
|
+
- Spacing: 8px grid. Sections 64-96px vertical pad. Cards 24-32px. CSS gap everywhere.
|
|
1650
|
+
- Shadows: layered β sm (0 1px 2px), md (0 4px 16px), lg (0 12px 48px). rgba(0,0,0,0.06-0.12).
|
|
1651
|
+
- Motion: 200ms ease-out on interactive elements. Fade + translateY(8px) for reveals. Skeleton screens over spinners.
|
|
1652
|
+
- Layout: mobile-first (640/768/1024/1280). Max-width 1200px. CSS Grid pages, Flexbox components.
|
|
1653
|
+
|
|
1654
|
+
## Research β use these sources
|
|
1655
|
+
- @@WEB_FETCH https://developer.apple.com/design/human-interface-guidelines for Apple HIG
|
|
1656
|
+
- @@WEB_SEARCH site:uiverse.io [component] for copy-pasteable HTML/CSS examples (7000+ free)
|
|
1657
|
+
- @@WEB_SEARCH site:css-tricks.com [technique] for CSS technique guides
|
|
1658
|
+
- @@WEB_SEARCH awwwards [page type] OR onepagelove [page type] for design inspiration
|
|
1659
|
+
- @@WEB_SEARCH site:codepen.io [component] vanilla CSS for interactive examples
|
|
1660
|
+
|
|
1661
|
+
## Rules
|
|
1662
|
+
- Accessible: focus-visible, aria-labels, 4.5:1 contrast, semantic HTML.`,
|
|
1663
|
+
|
|
1664
|
+
pm: `Product manager and project planner. Task decomposition and roadmap management.
|
|
1665
|
+
|
|
1666
|
+
## Planning principles
|
|
1667
|
+
- Every task: independently deliverable. If it can't be tested alone, split it.
|
|
1668
|
+
- Imperative form: "Create X", "Add Y to Z", "Fix W in file F". Never "Improve" or "Look into."
|
|
1669
|
+
- Each task β one agent, one file path, one deliverable.
|
|
1670
|
+
- Include acceptance criteria: what does done look like? What should the agent verify?
|
|
1671
|
+
- Task size: completable in 1-2 minutes of LLM work. Bigger = split.
|
|
1672
|
+
|
|
1673
|
+
## Anti-patterns
|
|
1674
|
+
- "Improve the landing page" β too vague. Which section? What's wrong?
|
|
1675
|
+
- "Set up the backend" β too broad. Which endpoint? What data? What auth?
|
|
1676
|
+
- Tasks without file paths β agent won't know where to work.
|
|
1677
|
+
|
|
1678
|
+
## Rules
|
|
1679
|
+
- Flag missing requirements before handoff.
|
|
1680
|
+
- @@WEB_SEARCH to research approaches for unfamiliar features.
|
|
1681
|
+
- Update ROADMAP.md with [ ] checkboxes.`,
|
|
1682
|
+
|
|
1683
|
+
aiml: `AI/ML engineering specialist. Model training, fine-tuning, eval, and MLOps.
|
|
1684
|
+
|
|
1685
|
+
## Standards
|
|
1686
|
+
- Reproducibility: set random seeds, log all hyperparameters, version datasets.
|
|
1687
|
+
- Data: validate schema before training. Check for nulls, duplicates, class imbalance.
|
|
1688
|
+
- Training: early stopping, gradient clipping, learning rate scheduling.
|
|
1689
|
+
- Evaluation: never eval on training data. Hold out test set. Report confidence intervals.
|
|
1690
|
+
- Code: type hints, docstrings on public APIs, structured logging.
|
|
1691
|
+
|
|
1692
|
+
## Research β critical for ML
|
|
1693
|
+
- @@WEB_SEARCH for model cards, API docs, library versions before implementation.
|
|
1694
|
+
- @@WEB_FETCH HuggingFace docs, paper abstracts, or API references.
|
|
1695
|
+
- @@WEB_SEARCH "[library] breaking changes" when using specific versions.
|
|
1696
|
+
|
|
1697
|
+
## Rules
|
|
1698
|
+
- ALWAYS read existing code before modifying. Pin dependency versions.
|
|
1699
|
+
- Never hardcode paths to datasets or models β use env vars or config.`,
|
|
1700
|
+
|
|
1701
|
+
api: `API design specialist. REST and GraphQL APIs.
|
|
1702
|
+
|
|
1703
|
+
## Standards
|
|
1704
|
+
- OpenAPI/Swagger specs for all new endpoints. Schema-first design.
|
|
1705
|
+
- REST: correct HTTP verbs (GET=read, POST=create, PUT=replace, PATCH=update, DELETE=remove).
|
|
1706
|
+
- Status codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 422 Unprocessable, 429 Rate Limited, 500 Server Error.
|
|
1707
|
+
- Consistent naming: plural nouns for resources (/users, /orders), kebab-case.
|
|
1708
|
+
- Pagination: cursor-based for large datasets. Include total count and next/prev links.
|
|
1709
|
+
- Versioning: URL prefix (/v1/) or Accept header.
|
|
1710
|
+
- @@WEB_SEARCH site:swagger.io/docs [topic] for OpenAPI spec reference.
|
|
1711
|
+
- @@WEB_FETCH https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for status codes.
|
|
1712
|
+
|
|
1713
|
+
## Rules
|
|
1714
|
+
- ALWAYS read existing routes and schemas before adding new ones. Match patterns.
|
|
1715
|
+
- Output both the spec and a working implementation stub.`,
|
|
1716
|
+
|
|
1717
|
+
database: `Database specialist. SQL, migrations, indexes, and query optimization.
|
|
1718
|
+
|
|
1719
|
+
## Standards
|
|
1720
|
+
- Idempotent migrations (safe to re-run). Use IF NOT EXISTS / IF EXISTS guards.
|
|
1721
|
+
- Indexes: all foreign keys, frequently queried columns, composite indexes for common WHERE+ORDER BY.
|
|
1722
|
+
- Naming: snake_case tables, singular (user not users). FK: target_table_id. Index: idx_table_column.
|
|
1723
|
+
- Always explain query plans for optimization changes.
|
|
1724
|
+
- @@WEB_SEARCH site:use-the-index-luke.com [topic] for SQL indexing best practices.
|
|
1725
|
+
- @@WEB_SEARCH [database engine] documentation [topic] for engine-specific syntax.
|
|
1726
|
+
|
|
1727
|
+
## Rules
|
|
1728
|
+
- ALWAYS read existing schema before writing migrations.
|
|
1729
|
+
- NEVER drop columns or tables without explicit instruction.
|
|
1730
|
+
- Transactions for multi-table changes. Rollback strategy for every migration.`,
|
|
1731
|
+
|
|
1732
|
+
reactnative: `React Native specialist. Cross-platform mobile with Expo or bare RN.
|
|
1733
|
+
|
|
1734
|
+
## Standards
|
|
1735
|
+
- Functional components with hooks. StyleSheet.create for all styles.
|
|
1736
|
+
- Navigation: React Navigation with typed routes. Deep linking support.
|
|
1737
|
+
- State: Zustand or React Query for server state. Context sparingly.
|
|
1738
|
+
- Platform differences: Platform.select, Platform.OS checks, platform-specific files (.ios.tsx/.android.tsx).
|
|
1739
|
+
- @@WEB_SEARCH React Native docs and Expo SDK for current APIs.
|
|
1740
|
+
|
|
1741
|
+
## Rules
|
|
1742
|
+
- ALWAYS read existing components and navigation before editing.
|
|
1743
|
+
- Test mental model on both iOS and Android.
|
|
1744
|
+
- Handle safe areas, keyboard avoidance, and different screen sizes.`,
|
|
1745
|
+
|
|
1746
|
+
web3: `Web3 and blockchain specialist. Solidity smart contracts and dApp frontends.
|
|
1747
|
+
|
|
1748
|
+
## Standards
|
|
1749
|
+
- Storage layout: NEVER change variable order in upgradeable contracts.
|
|
1750
|
+
- NatSpec comments on all public and external functions.
|
|
1751
|
+
- OpenZeppelin for standard patterns (ERC20, ERC721, AccessControl, Ownable).
|
|
1752
|
+
- Gas optimization: pack storage vars, use calldata over memory for read-only, avoid loops over unbounded arrays.
|
|
1753
|
+
- @@WEB_SEARCH site:docs.openzeppelin.com [pattern] for audited contract implementations.
|
|
1754
|
+
- @@WEB_SEARCH EIP-[number] for Ethereum standard specifications.
|
|
1755
|
+
|
|
1756
|
+
## Rules
|
|
1757
|
+
- ALWAYS read existing contracts before editing.
|
|
1758
|
+
- Test all contracts with Hardhat or Foundry before reporting done.
|
|
1759
|
+
- Check: reentrancy guards, integer overflow (Solidity 0.8+ safe), access control on state-changing functions.`,
|
|
1760
|
+
|
|
1761
|
+
automation: `Automation and web scraping specialist. Playwright, Puppeteer, Python scrapers.
|
|
1762
|
+
|
|
1763
|
+
## Standards
|
|
1764
|
+
- Playwright for JS-heavy sites, requests+BeautifulSoup for static HTML.
|
|
1765
|
+
- Always check for APIs first (@@WEB_SEARCH) β scraping is the fallback, not the default.
|
|
1766
|
+
- Handle: pagination, login flows, dynamic content, CAPTCHAs (flag, don't bypass).
|
|
1767
|
+
- Retry logic with exponential backoff for flaky requests.
|
|
1768
|
+
- @@WEB_FETCH to read a page before deciding the scraping approach.
|
|
1769
|
+
|
|
1770
|
+
## Rules
|
|
1771
|
+
- Store raw data before transforming β never lose the source.
|
|
1772
|
+
- Respect robots.txt and rate-limit requests (1-2 req/sec default).
|
|
1773
|
+
- Output structured data (JSON/CSV) with clear field names.`,
|
|
1774
|
+
|
|
1775
|
+
docs: `Technical documentation writer. API docs, READMEs, developer guides.
|
|
1776
|
+
|
|
1777
|
+
## Standards
|
|
1778
|
+
- Write for the reader β assume minimal context, include working examples.
|
|
1779
|
+
- Structure: Overview β Installation β Quick Start β Usage β API Reference β Examples β Troubleshooting.
|
|
1780
|
+
- Code examples must be copy-pasteable and actually work.
|
|
1781
|
+
- @@WEB_SEARCH for prior art, best practices, or similar docs for reference.
|
|
1782
|
+
- @@WEB_FETCH specific doc pages before paraphrasing or referencing.
|
|
1783
|
+
|
|
1784
|
+
## Rules
|
|
1785
|
+
- ALWAYS read the code you're documenting before writing.
|
|
1786
|
+
- Keep docs in sync with implementation β flag discrepancies.
|
|
1787
|
+
- Markdown output unless another format is requested.
|
|
1788
|
+
- No fluff paragraphs. Scannable: headers, bullets, code blocks.`,
|
|
1789
|
+
|
|
1790
|
+
orchestrator: `PM loop orchestrator. Roadmap reading, task expansion, specialist routing.
|
|
1791
|
+
|
|
1792
|
+
## Standards
|
|
1793
|
+
- Break each roadmap item into a single, scoped, actionable task.
|
|
1794
|
+
- Include exact file paths and acceptance criteria in every task.
|
|
1795
|
+
- Route to the right specialist based on work type.
|
|
1796
|
+
- @@WEB_SEARCH to research approaches for unfamiliar features.
|
|
1797
|
+
|
|
1798
|
+
## Rules
|
|
1799
|
+
- NEVER implement tasks yourself β planning and delegation only.
|
|
1800
|
+
- Keep task descriptions under 200 words.
|
|
1801
|
+
- Mark items done only after confirmation from the executing agent.`,
|
|
1802
|
+
|
|
1803
|
+
lead: `Team lead and coordinator. Delegation, progress tracking, blocker escalation.
|
|
1804
|
+
|
|
1805
|
+
## Rules
|
|
1806
|
+
- Assign tasks to the right agent based on their specialty.
|
|
1807
|
+
- Track what's in progress and what's blocked.
|
|
1808
|
+
- Escalate failures to crew-fixer and report status.
|
|
1809
|
+
- Do NOT implement tasks yourself β delegate everything.
|
|
1810
|
+
- Communicate clearly: who is doing what, and what's blocked.`,
|
|
1811
|
+
|
|
1812
|
+
main: `Main agent and general-purpose coordinator. Fallback for tasks that don't fit a specialist.
|
|
1813
|
+
|
|
1814
|
+
## Rules
|
|
1815
|
+
- Triage requests β handle directly or delegate to the right specialist.
|
|
1816
|
+
- @@WEB_SEARCH and @@WEB_FETCH for research tasks.
|
|
1817
|
+
- Write and edit files directly for general tasks.
|
|
1818
|
+
- Keep responses concise and action-oriented.
|
|
1819
|
+
- You're the catch-all β if something falls through the cracks, you handle it.`,
|
|
1820
|
+
};
|
|
1821
|
+
|
|
1822
|
+
const PRESET_META = {
|
|
1823
|
+
frontend: { id: "crew-coder-front", name: "Frontend Coder", emoji: "π¨" },
|
|
1824
|
+
backend: { id: "crew-coder-back", name: "Backend Coder", emoji: "βοΈ" },
|
|
1825
|
+
fullstack: { id: "crew-coder", name: "Full-stack Coder", emoji: "π§±" },
|
|
1826
|
+
ios: { id: "crew-coder-ios", name: "iOS Coder", emoji: "π±" },
|
|
1827
|
+
android: { id: "crew-coder-android", name: "Android Coder", emoji: "π€" },
|
|
1828
|
+
devops: { id: "crew-devops", name: "DevOps Engineer", emoji: "π§" },
|
|
1829
|
+
data: { id: "crew-data", name: "Data Engineer", emoji: "π" },
|
|
1830
|
+
security: { id: "crew-security", name: "Security Auditor", emoji: "π‘οΈ" },
|
|
1831
|
+
qa: { id: "crew-qa", name: "QA Tester", emoji: "π§ͺ" },
|
|
1832
|
+
github: { id: "crew-github", name: "Git Ops", emoji: "π" },
|
|
1833
|
+
writer: { id: "crew-copywriter", name: "Copywriter", emoji: "βοΈ" },
|
|
1834
|
+
design: { id: "crew-design", name: "UI/UX Designer", emoji: "ποΈ" },
|
|
1835
|
+
pm: { id: "crew-pm-agent", name: "Product Manager", emoji: "π" },
|
|
1836
|
+
aiml: { id: "crew-aiml", name: "AI/ML Engineer", emoji: "π€" },
|
|
1837
|
+
api: { id: "crew-api", name: "API Designer", emoji: "π" },
|
|
1838
|
+
database: { id: "crew-database", name: "Database Specialist", emoji: "ποΈ" },
|
|
1839
|
+
reactnative: { id: "crew-rn", name: "React Native Dev", emoji: "π±" },
|
|
1840
|
+
web3: { id: "crew-web3", name: "Web3 Engineer", emoji: "π" },
|
|
1841
|
+
automation: { id: "crew-automation", name: "Automation Bot", emoji: "π·οΈ" },
|
|
1842
|
+
docs: { id: "crew-docs", name: "Docs Writer", emoji: "π" },
|
|
1843
|
+
orchestrator: {
|
|
1844
|
+
id: "crew-orchestrator",
|
|
1845
|
+
name: "Orchestrator",
|
|
1846
|
+
emoji: "π§ ",
|
|
1847
|
+
},
|
|
1848
|
+
lead: { id: "crew-lead", name: "Crew Lead", emoji: "π¦" },
|
|
1849
|
+
main: { id: "crew-main", name: "Main Agent", emoji: "β‘" },
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
export function applyPromptPreset() {
|
|
1853
|
+
const val = document.getElementById("naPromptPreset").value;
|
|
1854
|
+
if (!val || !PROMPT_PRESETS[val]) return;
|
|
1855
|
+
document.getElementById("naPrompt").value = PROMPT_PRESETS[val];
|
|
1856
|
+
const meta = PRESET_META[val];
|
|
1857
|
+
if (meta) {
|
|
1858
|
+
const idEl = document.getElementById("naId");
|
|
1859
|
+
const nameEl = document.getElementById("naName");
|
|
1860
|
+
const emojiEl = document.getElementById("naEmoji");
|
|
1861
|
+
if (idEl && !idEl.value) idEl.value = meta.id;
|
|
1862
|
+
if (nameEl && !nameEl.value) nameEl.value = meta.name;
|
|
1863
|
+
if (emojiEl && !emojiEl.value) emojiEl.value = meta.emoji;
|
|
1864
|
+
}
|
|
1865
|
+
// Auto-fill role/theme from the preset's display label (strip leading emoji)
|
|
1866
|
+
const themeEl = document.getElementById("naTheme");
|
|
1867
|
+
if (themeEl) {
|
|
1868
|
+
const opt = PRESET_OPTIONS.find((p) => p.value === val);
|
|
1869
|
+
if (opt)
|
|
1870
|
+
themeEl.value = opt.label
|
|
1871
|
+
.replace(
|
|
1872
|
+
/^[\u{1F000}-\u{1FFFF}\u2600-\u27BF\uFE0F\u20D0-\u20FF\s]+/u,
|
|
1873
|
+
"",
|
|
1874
|
+
)
|
|
1875
|
+
.trim();
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// Models confirmed broken via API testing β return empty strings
|
|
1880
|
+
const BROKEN_MODELS = new Set([
|
|
1881
|
+
"groq/openai/gpt-oss-120b",
|
|
1882
|
+
"groq/openai/gpt-oss-20b",
|
|
1883
|
+
]);
|
|
1884
|
+
|
|
1885
|
+
// Role classification for badge display (matches MODEL-ROLE-OPTIMIZATION.md)
|
|
1886
|
+
const MODEL_ROLE = {
|
|
1887
|
+
// PLANNER β task decomposition, routing
|
|
1888
|
+
"crew-pm": "PLANNER",
|
|
1889
|
+
"crew-orchestrator": "PLANNER",
|
|
1890
|
+
orchestrator: "PLANNER",
|
|
1891
|
+
// WORKER β code generation, implementation
|
|
1892
|
+
"crew-coder": "WORKER",
|
|
1893
|
+
"crew-coder-back": "WORKER",
|
|
1894
|
+
"crew-coder-front": "WORKER",
|
|
1895
|
+
"crew-frontend": "WORKER",
|
|
1896
|
+
"crew-fixer": "WORKER",
|
|
1897
|
+
// JUDGE β cycle decisions
|
|
1898
|
+
"crew-judge": "JUDGE",
|
|
1899
|
+
// ANALYST β QA, security, review
|
|
1900
|
+
"crew-qa": "ANALYST",
|
|
1901
|
+
"crew-security": "ANALYST",
|
|
1902
|
+
// COORDINATOR β triage, delegation
|
|
1903
|
+
"crew-lead": "COORDINATOR",
|
|
1904
|
+
"crew-main": "COORDINATOR",
|
|
1905
|
+
// OTHER
|
|
1906
|
+
"crew-architect": "COORDINATOR",
|
|
1907
|
+
"crew-ml": "ANALYST",
|
|
1908
|
+
"crew-mega": "COORDINATOR",
|
|
1909
|
+
"crew-researcher": "COORDINATOR",
|
|
1910
|
+
"crew-copywriter": "WORKER",
|
|
1911
|
+
"crew-github": "WORKER",
|
|
1912
|
+
"crew-seo": "COORDINATOR",
|
|
1913
|
+
};
|
|
1914
|
+
const ROLE_STYLE = {
|
|
1915
|
+
PLANNER:
|
|
1916
|
+
"background:rgba(139,92,246,0.12);border:1px solid rgba(139,92,246,0.35);color:#a78bfa;",
|
|
1917
|
+
WORKER:
|
|
1918
|
+
"background:rgba(34,197,94,0.10);border:1px solid rgba(34,197,94,0.30);color:var(--green-hi);",
|
|
1919
|
+
JUDGE:
|
|
1920
|
+
"background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.30);color:var(--yellow);",
|
|
1921
|
+
ANALYST:
|
|
1922
|
+
"background:rgba(239,68,68,0.10);border:1px solid rgba(239,68,68,0.30);color:var(--red-hi);",
|
|
1923
|
+
COORDINATOR:
|
|
1924
|
+
"background:rgba(56,189,248,0.10);border:1px solid rgba(56,189,248,0.30);color:var(--accent);",
|
|
1925
|
+
};
|
|
1926
|
+
|
|
1927
|
+
// Top 10 models per role (from MODEL-ROLE-OPTIMIZATION.md)
|
|
1928
|
+
const TOP_MODELS_BY_ROLE = {
|
|
1929
|
+
PLANNER: [
|
|
1930
|
+
"groq/llama-3.3-70b-versatile",
|
|
1931
|
+
"cerebras/llama-3.3-70b",
|
|
1932
|
+
"groq/llama-3.1-8b-instant",
|
|
1933
|
+
"google/gemini-2.0-flash",
|
|
1934
|
+
"openai/gpt-4o-mini",
|
|
1935
|
+
"xai/grok-3-mini-fast",
|
|
1936
|
+
"mistral/mistral-small-latest",
|
|
1937
|
+
"deepseek/deepseek-coder",
|
|
1938
|
+
"anthropic/claude-haiku-4-5",
|
|
1939
|
+
"cerebras/llama-3.1-8b",
|
|
1940
|
+
],
|
|
1941
|
+
WORKER: [
|
|
1942
|
+
"anthropic/claude-sonnet-4-20250514",
|
|
1943
|
+
"openai/codex-mini-latest",
|
|
1944
|
+
"deepseek/deepseek-chat",
|
|
1945
|
+
"opencode/gpt-5.2-codex",
|
|
1946
|
+
"opencode/big-pickle",
|
|
1947
|
+
"mistral/codestral-latest",
|
|
1948
|
+
"openai/gpt-4.1",
|
|
1949
|
+
"google/models/gemini-2.5-flash",
|
|
1950
|
+
"xai/grok-3",
|
|
1951
|
+
"anthropic/claude-sonnet-3-5",
|
|
1952
|
+
],
|
|
1953
|
+
JUDGE: [
|
|
1954
|
+
"groq/llama-3.3-70b-versatile",
|
|
1955
|
+
"cerebras/llama-3.3-70b",
|
|
1956
|
+
"deepseek/deepseek-reasoner",
|
|
1957
|
+
"openai/o4-mini",
|
|
1958
|
+
"xai/grok-3-mini",
|
|
1959
|
+
"google/gemini-2.0-flash",
|
|
1960
|
+
"groq/llama-3.1-8b-instant",
|
|
1961
|
+
"mistral/mistral-small-latest",
|
|
1962
|
+
"anthropic/claude-haiku-4-5",
|
|
1963
|
+
"deepseek/deepseek-chat",
|
|
1964
|
+
],
|
|
1965
|
+
ANALYST: [
|
|
1966
|
+
"anthropic/claude-sonnet-4-20250514",
|
|
1967
|
+
"deepseek/deepseek-reasoner",
|
|
1968
|
+
"openai/o4-mini",
|
|
1969
|
+
"deepseek/deepseek-chat",
|
|
1970
|
+
"xai/grok-3",
|
|
1971
|
+
"mistral/mistral-large-latest",
|
|
1972
|
+
"google/models/gemini-2.5-flash",
|
|
1973
|
+
"groq/llama-3.3-70b-versatile",
|
|
1974
|
+
"anthropic/claude-haiku-4-5",
|
|
1975
|
+
"google/gemini-2.0-flash",
|
|
1976
|
+
],
|
|
1977
|
+
COORDINATOR: [
|
|
1978
|
+
"anthropic/claude-sonnet-4-20250514",
|
|
1979
|
+
"openai/gpt-4.1",
|
|
1980
|
+
"xai/grok-3",
|
|
1981
|
+
"google/models/gemini-2.5-flash",
|
|
1982
|
+
"deepseek/deepseek-chat",
|
|
1983
|
+
"perplexity/sonar-pro",
|
|
1984
|
+
"opencode/big-pickle",
|
|
1985
|
+
"openai/gpt-4o",
|
|
1986
|
+
"groq/llama-3.3-70b-versatile",
|
|
1987
|
+
"xai/grok-3-mini",
|
|
1988
|
+
],
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
export function populateModelDropdown(selectId, currentVal, agentRole = null) {
|
|
1992
|
+
const sel = document.getElementById(selectId);
|
|
1993
|
+
sel.innerHTML = '<option value="">β select a model β</option>';
|
|
1994
|
+
|
|
1995
|
+
// Get top models for this role
|
|
1996
|
+
const topModels =
|
|
1997
|
+
agentRole && TOP_MODELS_BY_ROLE[agentRole]
|
|
1998
|
+
? TOP_MODELS_BY_ROLE[agentRole]
|
|
1999
|
+
: [];
|
|
2000
|
+
const topModelsSet = new Set(topModels);
|
|
2001
|
+
|
|
2002
|
+
if (Object.keys(_modelsByProvider).length) {
|
|
2003
|
+
// If we have role-specific recommendations, show them first
|
|
2004
|
+
if (topModels.length > 0) {
|
|
2005
|
+
const grp = document.createElement("optgroup");
|
|
2006
|
+
grp.label = `β RECOMMENDED FOR ${agentRole}`;
|
|
2007
|
+
topModels.forEach((modelId) => {
|
|
2008
|
+
const broken = BROKEN_MODELS.has(modelId);
|
|
2009
|
+
const opt = document.createElement("option");
|
|
2010
|
+
opt.value = modelId;
|
|
2011
|
+
opt.textContent = "β " + (broken ? "β BROKEN β " : "") + modelId;
|
|
2012
|
+
if (broken) opt.style.color = "var(--red-hi)";
|
|
2013
|
+
if (modelId === currentVal) opt.selected = true;
|
|
2014
|
+
grp.appendChild(opt);
|
|
2015
|
+
});
|
|
2016
|
+
sel.appendChild(grp);
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// Then show all models grouped by provider
|
|
2020
|
+
for (const [provider, models] of Object.entries(_modelsByProvider)) {
|
|
2021
|
+
const grp = document.createElement("optgroup");
|
|
2022
|
+
grp.label =
|
|
2023
|
+
provider.toUpperCase() + (topModels.length > 0 ? " (All Models)" : "");
|
|
2024
|
+
models.forEach(({ id, name }) => {
|
|
2025
|
+
const full = provider + "/" + id;
|
|
2026
|
+
// Skip if already in recommended list
|
|
2027
|
+
if (topModelsSet.has(full)) return;
|
|
2028
|
+
const broken = BROKEN_MODELS.has(full);
|
|
2029
|
+
const opt = document.createElement("option");
|
|
2030
|
+
opt.value = full;
|
|
2031
|
+
opt.textContent =
|
|
2032
|
+
(broken ? "β BROKEN β " : "") +
|
|
2033
|
+
(name ? name + " (" + id + ")" : full);
|
|
2034
|
+
if (broken) opt.style.color = "var(--red-hi)";
|
|
2035
|
+
if (full === currentVal) opt.selected = true;
|
|
2036
|
+
grp.appendChild(opt);
|
|
2037
|
+
});
|
|
2038
|
+
sel.appendChild(grp);
|
|
2039
|
+
}
|
|
2040
|
+
} else {
|
|
2041
|
+
// Fallback to flat list
|
|
2042
|
+
if (topModels.length > 0) {
|
|
2043
|
+
const grp = document.createElement("optgroup");
|
|
2044
|
+
grp.label = `β RECOMMENDED FOR ${agentRole}`;
|
|
2045
|
+
topModels.forEach((modelId) => {
|
|
2046
|
+
const broken = BROKEN_MODELS.has(modelId);
|
|
2047
|
+
const opt = document.createElement("option");
|
|
2048
|
+
opt.value = modelId;
|
|
2049
|
+
opt.textContent = "β " + (broken ? "β BROKEN β " : "") + modelId;
|
|
2050
|
+
if (broken) opt.style.color = "var(--red-hi)";
|
|
2051
|
+
if (modelId === currentVal) opt.selected = true;
|
|
2052
|
+
grp.appendChild(opt);
|
|
2053
|
+
});
|
|
2054
|
+
sel.appendChild(grp);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
_allModels.forEach((m) => {
|
|
2058
|
+
if (topModelsSet.has(m)) return; // Skip recommended ones
|
|
2059
|
+
const broken = BROKEN_MODELS.has(m);
|
|
2060
|
+
const opt = document.createElement("option");
|
|
2061
|
+
opt.value = m;
|
|
2062
|
+
opt.textContent = (broken ? "β BROKEN β " : "") + m;
|
|
2063
|
+
if (broken) opt.style.color = "var(--red-hi)";
|
|
2064
|
+
if (m === currentVal) opt.selected = true;
|
|
2065
|
+
sel.appendChild(opt);
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
// If current value not in list, add it as custom
|
|
2069
|
+
if (
|
|
2070
|
+
currentVal &&
|
|
2071
|
+
!_allModels.includes(currentVal) &&
|
|
2072
|
+
!topModelsSet.has(currentVal)
|
|
2073
|
+
) {
|
|
2074
|
+
const opt = document.createElement("option");
|
|
2075
|
+
opt.value = currentVal;
|
|
2076
|
+
opt.textContent = currentVal + " (custom)";
|
|
2077
|
+
opt.selected = true;
|
|
2078
|
+
sel.prepend(opt);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
document.getElementById("newAgentBtn").onclick = () => {
|
|
2083
|
+
document.getElementById("newAgentForm").style.display = "block";
|
|
2084
|
+
populateModelDropdown("naModel", "");
|
|
2085
|
+
// Populate preset dropdown dynamically (PRESET_OPTIONS is client-side only)
|
|
2086
|
+
const sel = document.getElementById("naPromptPreset");
|
|
2087
|
+
if (sel && sel.options.length <= 1) {
|
|
2088
|
+
PRESET_OPTIONS.forEach((p) => {
|
|
2089
|
+
const opt = document.createElement("option");
|
|
2090
|
+
opt.value = p.value;
|
|
2091
|
+
opt.textContent = p.label;
|
|
2092
|
+
sel.appendChild(opt);
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
// Populate tool checkboxes dynamically (CREWSWARM_TOOLS is not available at HTML parse time)
|
|
2096
|
+
const grid = document.getElementById("naToolsGrid");
|
|
2097
|
+
if (grid && grid.querySelectorAll(".naToolCheck").length === 0) {
|
|
2098
|
+
grid.innerHTML = CREWSWARM_TOOLS.map(
|
|
2099
|
+
(t) => `
|
|
2100
|
+
<label style="display:flex; align-items:flex-start; gap:7px; font-size:12px; color:var(--text-2); cursor:pointer; padding:6px 8px; border-radius:5px; border:1px solid var(--border); background:var(--bg-card2);">
|
|
2101
|
+
<input type="checkbox" class="naToolCheck" data-tool="${t.id}" style="accent-color:var(--accent); margin-top:2px; flex-shrink:0;" />
|
|
2102
|
+
<div>
|
|
2103
|
+
<code style="font-size:11px; color:var(--text-1);">${t.id}</code>
|
|
2104
|
+
<div style="font-size:10px; color:var(--text-3); margin-top:2px; line-height:1.3;">${t.desc}</div>
|
|
2105
|
+
</div>
|
|
2106
|
+
</label>
|
|
2107
|
+
`,
|
|
2108
|
+
).join("");
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
document.getElementById("naCancelBtn").onclick = () => {
|
|
2112
|
+
document.getElementById("newAgentForm").style.display = "none";
|
|
2113
|
+
};
|
|
2114
|
+
document.getElementById("naCreateBtn").onclick = async () => {
|
|
2115
|
+
const rawId = document.getElementById("naId").value.trim();
|
|
2116
|
+
const id =
|
|
2117
|
+
rawId && !rawId.startsWith("crew-") && rawId !== "orchestrator"
|
|
2118
|
+
? `crew-${rawId}`
|
|
2119
|
+
: rawId;
|
|
2120
|
+
const model = document.getElementById("naModel").value.trim();
|
|
2121
|
+
const name = document.getElementById("naName").value.trim();
|
|
2122
|
+
const emoji = document.getElementById("naEmoji").value.trim();
|
|
2123
|
+
const theme = document.getElementById("naTheme").value.trim();
|
|
2124
|
+
const systemPrompt = document.getElementById("naPrompt").value.trim();
|
|
2125
|
+
const naTools = [...document.querySelectorAll(".naToolCheck:checked")].map(
|
|
2126
|
+
(cb) => cb.dataset.tool,
|
|
2127
|
+
);
|
|
2128
|
+
const alsoAllow = naTools.length ? naTools : getToolDefaults(id);
|
|
2129
|
+
if (!id || !model) {
|
|
2130
|
+
showNotification("Agent ID and model are required", true);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
try {
|
|
2134
|
+
await postJSON("/api/agents-config/create", {
|
|
2135
|
+
id,
|
|
2136
|
+
model,
|
|
2137
|
+
name,
|
|
2138
|
+
emoji,
|
|
2139
|
+
theme,
|
|
2140
|
+
systemPrompt,
|
|
2141
|
+
alsoAllow,
|
|
2142
|
+
});
|
|
2143
|
+
showNotification(
|
|
2144
|
+
`Agent "${id}" created β restart gateway-bridge to activate it on the RT bus.`,
|
|
2145
|
+
);
|
|
2146
|
+
document.getElementById("newAgentForm").style.display = "none";
|
|
2147
|
+
["naId", "naName", "naTheme", "naPrompt"].forEach((x) => {
|
|
2148
|
+
document.getElementById(x).value = "";
|
|
2149
|
+
});
|
|
2150
|
+
document.getElementById("naEmoji").value = "π₯";
|
|
2151
|
+
document.getElementById("naEmoji-btn").textContent = "π₯";
|
|
2152
|
+
document.getElementById("naModel").innerHTML =
|
|
2153
|
+
'<option value="">β select a model β</option>';
|
|
2154
|
+
document.getElementById("naPromptPreset").value = "";
|
|
2155
|
+
loadAgents_cfg();
|
|
2156
|
+
} catch (e) {
|
|
2157
|
+
showNotification("Failed: " + e.message, true);
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
document.getElementById("refreshAgentsBtn").onclick = loadAgents_cfg;
|
|
2161
|
+
|
|
2162
|
+
document.getElementById("bulkOptimizeBtn").onclick = async () => {
|
|
2163
|
+
if (
|
|
2164
|
+
!confirm(
|
|
2165
|
+
"Apply role-optimized models to all agents?\n\nβ’ Planners β Fast, cheap models\nβ’ Workers β Precision models\nβ’ Judges β Fast reasoning\nβ’ Analysts β Reasoning models\nβ’ Coordinators β Balanced\n\nSee docs/MODEL-ROLE-OPTIMIZATION.md for details.",
|
|
2166
|
+
)
|
|
2167
|
+
)
|
|
2168
|
+
return;
|
|
2169
|
+
|
|
2170
|
+
const mode = "value"; // Could add UI to pick free/quality/value
|
|
2171
|
+
const preset = {
|
|
2172
|
+
"crew-pm": "groq/llama-3.3-70b-versatile",
|
|
2173
|
+
"crew-orchestrator": "groq/llama-3.3-70b-versatile",
|
|
2174
|
+
"crew-judge": "groq/llama-3.3-70b-versatile",
|
|
2175
|
+
"crew-coder": "google/models/gemini-2.5-flash-lite",
|
|
2176
|
+
"crew-coder-front": "google/models/gemini-2.5-flash-lite",
|
|
2177
|
+
"crew-coder-back": "google/models/gemini-2.5-flash-lite",
|
|
2178
|
+
"crew-fixer": "deepseek/deepseek-reasoner",
|
|
2179
|
+
"crew-qa": "deepseek/deepseek-reasoner",
|
|
2180
|
+
"crew-security": "deepseek/deepseek-reasoner",
|
|
2181
|
+
"crew-main": "google/models/gemini-2.5-flash-lite",
|
|
2182
|
+
"crew-lead": "google/models/gemini-2.5-flash-lite",
|
|
2183
|
+
"crew-frontend": "google/models/gemini-2.5-flash-lite",
|
|
2184
|
+
"crew-copywriter": "groq/llama-3.3-70b-versatile",
|
|
2185
|
+
"crew-researcher": "perplexity/sonar-pro",
|
|
2186
|
+
"crew-architect": "deepseek/deepseek-reasoner",
|
|
2187
|
+
"crew-seo": "groq/llama-3.3-70b-versatile",
|
|
2188
|
+
"crew-ml": "deepseek/deepseek-reasoner",
|
|
2189
|
+
"crew-github": "groq/llama-3.3-70b-versatile",
|
|
2190
|
+
};
|
|
2191
|
+
|
|
2192
|
+
showNotification("Applying role-optimized modelsβ¦");
|
|
2193
|
+
let updated = 0;
|
|
2194
|
+
|
|
2195
|
+
for (const [agentId, model] of Object.entries(preset)) {
|
|
2196
|
+
try {
|
|
2197
|
+
await postJSON("/api/agents-config/update", { agentId, model });
|
|
2198
|
+
updated++;
|
|
2199
|
+
// Small delay to prevent rapid-fire saves that create backup storms
|
|
2200
|
+
await new Promise(r => setTimeout(r, 50));
|
|
2201
|
+
} catch (e) {
|
|
2202
|
+
console.error(`Failed to update ${agentId}:`, e.message);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
showNotification(
|
|
2207
|
+
`β
Updated ${updated} agents β Restart bridges to activate`,
|
|
2208
|
+
);
|
|
2209
|
+
await loadAgents_cfg();
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
// ββ End agents UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
2213
|
+
|
|
2214
|
+
export {
|
|
2215
|
+
loadAgents_cfg,
|
|
2216
|
+
applyToolPreset,
|
|
2217
|
+
toggleAgentBody,
|
|
2218
|
+
deleteAgent,
|
|
2219
|
+
saveAgentModel,
|
|
2220
|
+
saveAgentFallback,
|
|
2221
|
+
saveAgentVoice,
|
|
2222
|
+
toggleEmojiPicker,
|
|
2223
|
+
saveAgentIdentity,
|
|
2224
|
+
saveAgentPrompt,
|
|
2225
|
+
resetAgentSession,
|
|
2226
|
+
saveAgentTools,
|
|
2227
|
+
setRoute,
|
|
2228
|
+
saveOpenCodeConfig,
|
|
2229
|
+
saveOpenCodeFallback,
|
|
2230
|
+
saveCursorCliConfig,
|
|
2231
|
+
saveClaudeCodeConfig,
|
|
2232
|
+
saveCodexConfig,
|
|
2233
|
+
saveGeminiCliConfig,
|
|
2234
|
+
saveCrewCLIConfig,
|
|
2235
|
+
bulkSetRoute,
|
|
2236
|
+
startCrew,
|
|
2237
|
+
};
|