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,313 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* multimodal.mjs — Shared image/audio processing for crewswarm
|
|
4
|
+
*
|
|
5
|
+
* Supports:
|
|
6
|
+
* - Image analysis via Groq Vision (Llama 4 Scout) or Gemini 2.0 Flash
|
|
7
|
+
* - Audio transcription via Groq Whisper or Gemini
|
|
8
|
+
* - Used by Telegram, WhatsApp, dashboard, and CrewChat
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { loadSwarmConfig } from "../runtime/config.mjs";
|
|
15
|
+
|
|
16
|
+
// ── Config ────────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
const cfg = loadSwarmConfig();
|
|
19
|
+
const providers = cfg.providers || {};
|
|
20
|
+
|
|
21
|
+
// Provider selection priority: Groq (fast/cheap) → Gemini → OpenAI → fail
|
|
22
|
+
const GROQ_KEY = providers.groq?.apiKey || "";
|
|
23
|
+
const GEMINI_KEY = providers.google?.apiKey || providers.gemini?.apiKey || "";
|
|
24
|
+
const OPENAI_KEY = providers.openai?.apiKey || "";
|
|
25
|
+
|
|
26
|
+
const GROQ_VISION_MODEL = "meta-llama/llama-4-scout-17b-16e-instruct";
|
|
27
|
+
const GROQ_WHISPER_MODEL = "whisper-large-v3-turbo"; // 216x realtime, $0.04/hr
|
|
28
|
+
const GEMINI_MODEL = "gemini-2.0-flash-exp";
|
|
29
|
+
|
|
30
|
+
// ── Image Analysis ────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Analyze an image via URL or base64
|
|
34
|
+
* @param {string} imageUrlOrBase64 - Public URL or base64 data URI
|
|
35
|
+
* @param {string} prompt - Question/instruction for the model
|
|
36
|
+
* @param {object} options - { provider: 'groq'|'gemini'|'auto' }
|
|
37
|
+
* @returns {Promise<string>} - Model's response
|
|
38
|
+
*/
|
|
39
|
+
export async function analyzeImage(imageUrlOrBase64, prompt = "Describe this image in detail.", options = {}) {
|
|
40
|
+
const provider = options.provider || "auto";
|
|
41
|
+
|
|
42
|
+
// Auto-select: prefer Groq (cheaper), fallback to Gemini
|
|
43
|
+
if (provider === "auto") {
|
|
44
|
+
if (GROQ_KEY) return analyzeImageGroq(imageUrlOrBase64, prompt);
|
|
45
|
+
if (GEMINI_KEY) return analyzeImageGemini(imageUrlOrBase64, prompt);
|
|
46
|
+
throw new Error("No vision provider configured (need Groq or Gemini API key)");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (provider === "groq") return analyzeImageGroq(imageUrlOrBase64, prompt);
|
|
50
|
+
if (provider === "gemini") return analyzeImageGemini(imageUrlOrBase64, prompt);
|
|
51
|
+
throw new Error(`Unknown vision provider: ${provider}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function analyzeImageGroq(imageUrlOrBase64, prompt) {
|
|
55
|
+
if (!GROQ_KEY) throw new Error("Groq API key missing (providers.groq.apiKey)");
|
|
56
|
+
|
|
57
|
+
// Convert to message format Groq expects
|
|
58
|
+
const isUrl = imageUrlOrBase64.startsWith("http://") || imageUrlOrBase64.startsWith("https://");
|
|
59
|
+
const imageContent = isUrl
|
|
60
|
+
? { type: "image_url", image_url: { url: imageUrlOrBase64 } }
|
|
61
|
+
: { type: "image_url", image_url: { url: imageUrlOrBase64 } }; // base64 also uses image_url
|
|
62
|
+
|
|
63
|
+
const res = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: {
|
|
66
|
+
"Authorization": `Bearer ${GROQ_KEY}`,
|
|
67
|
+
"Content-Type": "application/json"
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
model: GROQ_VISION_MODEL,
|
|
71
|
+
messages: [
|
|
72
|
+
{
|
|
73
|
+
role: "user",
|
|
74
|
+
content: [
|
|
75
|
+
{ type: "text", text: prompt },
|
|
76
|
+
imageContent
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
temperature: 0.7,
|
|
81
|
+
max_tokens: 1024
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
const error = await res.text();
|
|
87
|
+
throw new Error(`Groq vision API error: ${res.status} ${error}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const data = await res.json();
|
|
91
|
+
return data.choices?.[0]?.message?.content || "No response from vision model";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function analyzeImageGemini(imageUrlOrBase64, prompt) {
|
|
95
|
+
if (!GEMINI_KEY) throw new Error("Gemini API key missing (providers.google.apiKey)");
|
|
96
|
+
|
|
97
|
+
// Gemini expects base64 inline parts
|
|
98
|
+
let imagePart;
|
|
99
|
+
if (imageUrlOrBase64.startsWith("data:image/")) {
|
|
100
|
+
// Extract mime and base64 from data URI
|
|
101
|
+
const [mime, base64] = imageUrlOrBase64.replace(/^data:/, "").split(";base64,");
|
|
102
|
+
imagePart = {
|
|
103
|
+
inline_data: {
|
|
104
|
+
mime_type: mime,
|
|
105
|
+
data: base64
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
} else if (imageUrlOrBase64.startsWith("http")) {
|
|
109
|
+
// Download and convert to base64
|
|
110
|
+
const imgRes = await fetch(imageUrlOrBase64);
|
|
111
|
+
const buffer = Buffer.from(await imgRes.arrayBuffer());
|
|
112
|
+
const base64 = buffer.toString("base64");
|
|
113
|
+
const mime = imgRes.headers.get("content-type") || "image/jpeg";
|
|
114
|
+
imagePart = {
|
|
115
|
+
inline_data: {
|
|
116
|
+
mime_type: mime,
|
|
117
|
+
data: base64
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
// Already base64
|
|
122
|
+
imagePart = {
|
|
123
|
+
inline_data: {
|
|
124
|
+
mime_type: "image/jpeg",
|
|
125
|
+
data: imageUrlOrBase64
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const res = await fetch(
|
|
131
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_KEY}`,
|
|
132
|
+
{
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers: { "Content-Type": "application/json" },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
contents: [
|
|
137
|
+
{
|
|
138
|
+
parts: [
|
|
139
|
+
{ text: prompt },
|
|
140
|
+
imagePart
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
const error = await res.text();
|
|
150
|
+
throw new Error(`Gemini API error: ${res.status} ${error}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const data = await res.json();
|
|
154
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || "No response from Gemini";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── Audio Transcription ───────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Transcribe audio file to text
|
|
161
|
+
* @param {Buffer} audioBuffer - Raw audio file data
|
|
162
|
+
* @param {object} options - { provider, language, mimeType: 'audio/m4a'|'audio/webm'|... }
|
|
163
|
+
* @returns {Promise<string>} - Transcribed text
|
|
164
|
+
*/
|
|
165
|
+
export async function transcribeAudio(audioBuffer, options = {}) {
|
|
166
|
+
const provider = options.provider || "auto";
|
|
167
|
+
|
|
168
|
+
// Auto-select: Groq → Gemini → OpenAI (Whisper handles m4a well)
|
|
169
|
+
if (provider === "auto") {
|
|
170
|
+
if (GROQ_KEY) return transcribeAudioGroq(audioBuffer, options);
|
|
171
|
+
if (GEMINI_KEY) return transcribeAudioGemini(audioBuffer, options);
|
|
172
|
+
if (OPENAI_KEY) return transcribeAudioOpenAI(audioBuffer, options);
|
|
173
|
+
throw new Error("No audio provider configured (need Groq, Gemini, or OpenAI API key)");
|
|
174
|
+
}
|
|
175
|
+
if (provider === "groq") return transcribeAudioGroq(audioBuffer, options);
|
|
176
|
+
if (provider === "gemini") return transcribeAudioGemini(audioBuffer, options);
|
|
177
|
+
if (provider === "openai") return transcribeAudioOpenAI(audioBuffer, options);
|
|
178
|
+
throw new Error(`Unknown audio provider: ${provider}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function transcribeAudioGroq(audioBuffer, options) {
|
|
182
|
+
if (!GROQ_KEY) throw new Error("Groq API key missing (providers.groq.apiKey)");
|
|
183
|
+
const mime = options.mimeType || "audio/m4a";
|
|
184
|
+
const filename = mime.includes("m4a") || mime === "audio/mp4" ? "audio.m4a" : mime.includes("webm") ? "audio.webm" : "audio.m4a";
|
|
185
|
+
const formData = new FormData();
|
|
186
|
+
formData.append("file", new Blob([audioBuffer], { type: mime }), filename);
|
|
187
|
+
formData.append("model", GROQ_WHISPER_MODEL);
|
|
188
|
+
formData.append("response_format", "json"); // Explicit per Groq docs
|
|
189
|
+
if (options.language && options.language !== "auto") {
|
|
190
|
+
formData.append("language", options.language);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const res = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: {
|
|
196
|
+
"Authorization": `Bearer ${GROQ_KEY}`
|
|
197
|
+
},
|
|
198
|
+
body: formData
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
const error = await res.text();
|
|
203
|
+
throw new Error(`Groq Whisper API error: ${res.status} ${error}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const data = await res.json();
|
|
207
|
+
const text = (data.text || "").trim();
|
|
208
|
+
if (!text) throw new Error("Groq returned empty transcription (check audio format and length)");
|
|
209
|
+
return text;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function transcribeAudioGemini(audioBuffer, options) {
|
|
213
|
+
if (!GEMINI_KEY) throw new Error("Gemini API key missing (providers.google.apiKey)");
|
|
214
|
+
// Gemini expects audio/mp4 for m4a, not audio/m4a
|
|
215
|
+
const raw = options.mimeType || "audio/m4a";
|
|
216
|
+
const mimeType = raw === "audio/m4a" || raw.includes("m4a") ? "audio/mp4" : raw;
|
|
217
|
+
const base64 = audioBuffer.toString("base64");
|
|
218
|
+
const res = await fetch(
|
|
219
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_KEY}`,
|
|
220
|
+
{
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: { "Content-Type": "application/json" },
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
contents: [
|
|
225
|
+
{
|
|
226
|
+
parts: [
|
|
227
|
+
{ text: "Transcribe this audio to text. Only return the transcription, no other commentary." },
|
|
228
|
+
{
|
|
229
|
+
inline_data: {
|
|
230
|
+
mime_type: mimeType,
|
|
231
|
+
data: base64
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (!res.ok) {
|
|
242
|
+
const error = await res.text();
|
|
243
|
+
throw new Error(`Gemini audio API error: ${res.status} ${error}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const data = await res.json();
|
|
247
|
+
const text = (data.candidates?.[0]?.content?.parts?.[0]?.text || "").trim();
|
|
248
|
+
if (!text) throw new Error("Gemini returned empty transcription (check audio format and length)");
|
|
249
|
+
return text;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function transcribeAudioOpenAI(audioBuffer, options) {
|
|
253
|
+
if (!OPENAI_KEY) throw new Error("OpenAI API key missing (providers.openai.apiKey)");
|
|
254
|
+
const mime = options.mimeType || "audio/m4a";
|
|
255
|
+
const filename = mime.includes("m4a") || mime === "audio/mp4" ? "audio.m4a" : mime.includes("webm") ? "audio.webm" : "audio.m4a";
|
|
256
|
+
const formData = new FormData();
|
|
257
|
+
formData.append("file", new Blob([audioBuffer], { type: mime }), filename);
|
|
258
|
+
formData.append("model", "whisper-1");
|
|
259
|
+
const res = await fetch("https://api.openai.com/v1/audio/transcriptions", {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: { "Authorization": `Bearer ${OPENAI_KEY}` },
|
|
262
|
+
body: formData
|
|
263
|
+
});
|
|
264
|
+
if (!res.ok) {
|
|
265
|
+
const err = await res.text();
|
|
266
|
+
throw new Error(`OpenAI Whisper API error: ${res.status} ${err}`);
|
|
267
|
+
}
|
|
268
|
+
const data = await res.json();
|
|
269
|
+
const text = (data.text || "").trim();
|
|
270
|
+
if (!text) throw new Error("OpenAI returned empty transcription (check audio format and length)");
|
|
271
|
+
return text;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Download file from URL to Buffer
|
|
278
|
+
*/
|
|
279
|
+
export async function downloadToBuffer(url) {
|
|
280
|
+
const res = await fetch(url);
|
|
281
|
+
if (!res.ok) throw new Error(`Download failed: ${res.status}`);
|
|
282
|
+
return Buffer.from(await res.arrayBuffer());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Convert file path to base64 data URI
|
|
287
|
+
*/
|
|
288
|
+
export function fileToBase64DataUri(filePath, mimeType = "image/jpeg") {
|
|
289
|
+
const buffer = readFileSync(filePath);
|
|
290
|
+
const base64 = buffer.toString("base64");
|
|
291
|
+
return `data:${mimeType};base64,${base64}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Check if provider is configured
|
|
296
|
+
*/
|
|
297
|
+
export function hasVisionProvider() {
|
|
298
|
+
return !!(GROQ_KEY || GEMINI_KEY);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function hasAudioProvider() {
|
|
302
|
+
return !!(GROQ_KEY || GEMINI_KEY || OPENAI_KEY);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get active provider names
|
|
307
|
+
*/
|
|
308
|
+
export function getActiveProviders() {
|
|
309
|
+
const providers = [];
|
|
310
|
+
if (GROQ_KEY) providers.push("groq");
|
|
311
|
+
if (GEMINI_KEY) providers.push("gemini");
|
|
312
|
+
return providers;
|
|
313
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Native Streaming Handler
|
|
3
|
+
* Uses sendMessageDraft (Bot API 9.5) for progressive text display
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Stream LLM response progressively to Telegram using sendMessageDraft
|
|
8
|
+
* @param {number} chatId - Telegram chat ID
|
|
9
|
+
* @param {number|null} threadId - Topic ID (if in forum group)
|
|
10
|
+
* @param {string} targetAgent - Agent identifier (for logging)
|
|
11
|
+
* @param {Object} provider - Provider config { baseUrl, apiKey }
|
|
12
|
+
* @param {string} modelId - Model identifier
|
|
13
|
+
* @param {Array} messages - Message array for LLM
|
|
14
|
+
* @param {Function} tgRequest - Telegram API request function
|
|
15
|
+
* @param {Function} log - Logger function
|
|
16
|
+
* @returns {Promise<string>} Final response text
|
|
17
|
+
*/
|
|
18
|
+
export async function streamToTelegram({
|
|
19
|
+
chatId,
|
|
20
|
+
threadId = null,
|
|
21
|
+
targetAgent,
|
|
22
|
+
provider,
|
|
23
|
+
modelId,
|
|
24
|
+
messages,
|
|
25
|
+
tgRequest,
|
|
26
|
+
log
|
|
27
|
+
}) {
|
|
28
|
+
const draftId = Date.now(); // Unique draft ID for this stream
|
|
29
|
+
let fullText = "";
|
|
30
|
+
let lastUpdate = 0;
|
|
31
|
+
const UPDATE_INTERVAL_MS = 100; // Update every 100ms (smooth but not spammy)
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Call LLM with streaming enabled
|
|
35
|
+
const response = await fetch(provider.baseUrl + "/chat/completions", {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"Authorization": `Bearer ${provider.apiKey}`
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify({
|
|
42
|
+
model: modelId,
|
|
43
|
+
messages,
|
|
44
|
+
temperature: 0.7,
|
|
45
|
+
stream: true // Enable streaming
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`LLM API returned ${response.status}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const reader = response.body.getReader();
|
|
54
|
+
const decoder = new TextDecoder();
|
|
55
|
+
let buffer = "";
|
|
56
|
+
|
|
57
|
+
while (true) {
|
|
58
|
+
const { done, value } = await reader.read();
|
|
59
|
+
if (done) break;
|
|
60
|
+
|
|
61
|
+
buffer += decoder.decode(value, { stream: true });
|
|
62
|
+
const lines = buffer.split("\n");
|
|
63
|
+
buffer = lines.pop() || "";
|
|
64
|
+
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
if (!line.trim() || !line.startsWith("data: ")) continue;
|
|
67
|
+
|
|
68
|
+
const data = line.slice(6);
|
|
69
|
+
if (data === "[DONE]") continue;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(data);
|
|
73
|
+
const content = parsed.choices?.[0]?.delta?.content;
|
|
74
|
+
|
|
75
|
+
if (content) {
|
|
76
|
+
fullText += content;
|
|
77
|
+
|
|
78
|
+
// Throttle updates - only send every 100ms
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
if (now - lastUpdate >= UPDATE_INTERVAL_MS) {
|
|
81
|
+
await tgRequest("sendMessageDraft", {
|
|
82
|
+
chat_id: chatId,
|
|
83
|
+
...(threadId && { message_thread_id: threadId }),
|
|
84
|
+
draft_id: draftId,
|
|
85
|
+
text: fullText,
|
|
86
|
+
parse_mode: "Markdown"
|
|
87
|
+
}).catch(err => {
|
|
88
|
+
// Fallback: if Markdown fails, try without parse_mode
|
|
89
|
+
return tgRequest("sendMessageDraft", {
|
|
90
|
+
chat_id: chatId,
|
|
91
|
+
...(threadId && { message_thread_id: threadId }),
|
|
92
|
+
draft_id: draftId,
|
|
93
|
+
text: fullText
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
lastUpdate = now;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (parseErr) {
|
|
101
|
+
log("warn", "Failed to parse SSE chunk", { error: parseErr.message });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Send final update (in case last chunk didn't trigger threshold)
|
|
107
|
+
if (fullText && Date.now() - lastUpdate >= 50) {
|
|
108
|
+
await tgRequest("sendMessageDraft", {
|
|
109
|
+
chat_id: chatId,
|
|
110
|
+
...(threadId && { message_thread_id: threadId }),
|
|
111
|
+
draft_id: draftId,
|
|
112
|
+
text: fullText,
|
|
113
|
+
parse_mode: "Markdown"
|
|
114
|
+
}).catch(err => {
|
|
115
|
+
return tgRequest("sendMessageDraft", {
|
|
116
|
+
chat_id: chatId,
|
|
117
|
+
...(threadId && { message_thread_id: threadId }),
|
|
118
|
+
draft_id: draftId,
|
|
119
|
+
text: fullText
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
log("info", "Streaming complete", {
|
|
125
|
+
targetAgent,
|
|
126
|
+
chatId,
|
|
127
|
+
threadId,
|
|
128
|
+
length: fullText.length
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return fullText;
|
|
132
|
+
|
|
133
|
+
} catch (err) {
|
|
134
|
+
log("error", "Streaming failed", {
|
|
135
|
+
targetAgent,
|
|
136
|
+
error: err.message
|
|
137
|
+
});
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if native streaming is supported for this chat
|
|
144
|
+
* sendMessageDraft only works in private chats (DMs and private topics)
|
|
145
|
+
* @param {number} chatId - Telegram chat ID
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
export function supportsNativeStreaming(chatId) {
|
|
149
|
+
// Private chats have positive IDs
|
|
150
|
+
// Groups/supergroups have negative IDs
|
|
151
|
+
// BUT: Private chats with topics ARE supported
|
|
152
|
+
return chatId > 0;
|
|
153
|
+
}
|