switchroom 0.5.0
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/LICENSE +21 -0
- package/README.md +447 -0
- package/bin/autoaccept.exp +81 -0
- package/bin/boot-self-test.sh +149 -0
- package/bin/bridge-watchdog.sh +967 -0
- package/bin/handoff-briefing.sh +206 -0
- package/bin/run-hook.sh +228 -0
- package/bin/switchroom.ts +4 -0
- package/bin/timezone-hook.sh +67 -0
- package/bin/user-profile-refresh-hook.sh +38 -0
- package/bin/workspace-dynamic-hook.sh +142 -0
- package/bin/workspace-stable-hook.sh +57 -0
- package/dist/cli/autoaccept-poll.js +118 -0
- package/dist/cli/switchroom.js +48557 -0
- package/package.json +95 -0
- package/profiles/_base/settings.json.hbs +15 -0
- package/profiles/_base/start.sh.hbs +383 -0
- package/profiles/_shared/telegram-style.md.hbs +140 -0
- package/profiles/coding/CLAUDE.md.hbs +57 -0
- package/profiles/coding/skills/architecture/SKILL.md +70 -0
- package/profiles/coding/skills/code-review/SKILL.md +58 -0
- package/profiles/coding/workspace/SOUL.md.hbs +25 -0
- package/profiles/default/CLAUDE.md +238 -0
- package/profiles/default/CLAUDE.md.hbs +113 -0
- package/profiles/default/workspace/CLAUDE.md.hbs +126 -0
- package/profiles/default/workspace/HEARTBEAT.md.hbs +40 -0
- package/profiles/default/workspace/IDENTITY.md.hbs +32 -0
- package/profiles/default/workspace/MEMORY.md.hbs +29 -0
- package/profiles/default/workspace/SOUL.md.hbs +61 -0
- package/profiles/default/workspace/TOOLS.md.hbs +29 -0
- package/profiles/default/workspace/USER.md.hbs +52 -0
- package/profiles/default/workspace/memory/.gitkeep +0 -0
- package/profiles/executive-assistant/CLAUDE.md.hbs +51 -0
- package/profiles/executive-assistant/skills/daily-briefing/SKILL.md +55 -0
- package/profiles/executive-assistant/skills/meeting-prep/SKILL.md +58 -0
- package/profiles/executive-assistant/workspace/SOUL.md.hbs +25 -0
- package/profiles/health-coach/CLAUDE.md.hbs +45 -0
- package/profiles/health-coach/skills/check-in/SKILL.md +41 -0
- package/profiles/health-coach/skills/weekly-review/SKILL.md +53 -0
- package/profiles/health-coach/workspace/SOUL.md.hbs +25 -0
- package/skills/buildkite-agent-infrastructure/SKILL.md +302 -0
- package/skills/buildkite-agent-infrastructure/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-infrastructure/references/audit-logging.md +87 -0
- package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +690 -0
- package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +38 -0
- package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +73 -0
- package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +137 -0
- package/skills/buildkite-agent-infrastructure/references/sso-saml.md +92 -0
- package/skills/buildkite-agent-runtime/SKILL.md +476 -0
- package/skills/buildkite-agent-runtime/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-runtime/references/flag-reference.md +417 -0
- package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +555 -0
- package/skills/buildkite-api/SKILL.md +285 -0
- package/skills/buildkite-api/agents/openai.yaml +6 -0
- package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-api/references/graphql-reference.md +195 -0
- package/skills/buildkite-api/references/patterns.md +44 -0
- package/skills/buildkite-api/references/webhooks.md +161 -0
- package/skills/buildkite-cli/SKILL.md +379 -0
- package/skills/buildkite-cli/agents/openai.yaml +6 -0
- package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-cli/references/command-reference.md +181 -0
- package/skills/buildkite-migration/SKILL.md +182 -0
- package/skills/buildkite-pipelines/SKILL.md +464 -0
- package/skills/buildkite-pipelines/agents/openai.yaml +6 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-pipelines/examples/basic-pipeline.yml +24 -0
- package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +100 -0
- package/skills/buildkite-pipelines/references/advanced-patterns.md +286 -0
- package/skills/buildkite-pipelines/references/retry-and-error-codes.md +131 -0
- package/skills/buildkite-pipelines/references/step-types-reference.md +225 -0
- package/skills/buildkite-secure-delivery/SKILL.md +168 -0
- package/skills/buildkite-secure-delivery/agents/openai.yaml +6 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +83 -0
- package/skills/buildkite-secure-delivery/references/package-publishing.md +100 -0
- package/skills/buildkite-test-engine/SKILL.md +239 -0
- package/skills/buildkite-test-engine/agents/openai.yaml +6 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-test-engine/examples/bktec-splitting.yml +16 -0
- package/skills/buildkite-test-engine/examples/collector-pipeline.yml +11 -0
- package/skills/buildkite-test-engine/references/collectors.md +198 -0
- package/skills/buildkite-test-engine/references/splitting-examples.md +93 -0
- package/skills/docx/LICENSE.txt +30 -0
- package/skills/docx/SKILL.md +590 -0
- package/skills/docx/VENDORED.md +32 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/accept_changes.py +135 -0
- package/skills/docx/scripts/comment.py +318 -0
- package/skills/docx/scripts/office/helpers/__init__.py +0 -0
- package/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/docx/scripts/office/pack.py +159 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/scripts/office/soffice.py +183 -0
- package/skills/docx/scripts/office/unpack.py +132 -0
- package/skills/docx/scripts/office/validate.py +111 -0
- package/skills/docx/scripts/office/validators/__init__.py +15 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/base.py +847 -0
- package/skills/docx/scripts/office/validators/docx.py +446 -0
- package/skills/docx/scripts/office/validators/pptx.py +275 -0
- package/skills/docx/scripts/office/validators/redlining.py +247 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/file-bug/SKILL.md +129 -0
- package/skills/humanizer/LICENSE +21 -0
- package/skills/humanizer/SKILL.md +559 -0
- package/skills/humanizer/VENDORED.md +38 -0
- package/skills/humanizer-calibrate/SKILL.md +144 -0
- package/skills/mcp-builder/LICENSE.txt +202 -0
- package/skills/mcp-builder/SKILL.md +236 -0
- package/skills/mcp-builder/VENDORED.md +32 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +314 -0
- package/skills/pdf/VENDORED.md +32 -0
- package/skills/pdf/forms.md +294 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
- package/skills/pdf/scripts/check_fillable_fields.py +11 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/skills/pdf/scripts/create_validation_image.py +37 -0
- package/skills/pdf/scripts/extract_form_field_info.py +122 -0
- package/skills/pdf/scripts/extract_form_structure.py +115 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/skills/pptx/LICENSE.txt +30 -0
- package/skills/pptx/SKILL.md +232 -0
- package/skills/pptx/VENDORED.md +32 -0
- package/skills/pptx/editing.md +205 -0
- package/skills/pptx/pptxgenjs.md +420 -0
- package/skills/pptx/scripts/__init__.py +0 -0
- package/skills/pptx/scripts/add_slide.py +195 -0
- package/skills/pptx/scripts/clean.py +286 -0
- package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
- package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/pptx/scripts/office/pack.py +159 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/scripts/office/soffice.py +183 -0
- package/skills/pptx/scripts/office/unpack.py +132 -0
- package/skills/pptx/scripts/office/validate.py +111 -0
- package/skills/pptx/scripts/office/validators/__init__.py +15 -0
- package/skills/pptx/scripts/office/validators/base.py +847 -0
- package/skills/pptx/scripts/office/validators/docx.py +446 -0
- package/skills/pptx/scripts/office/validators/pptx.py +275 -0
- package/skills/pptx/scripts/office/validators/redlining.py +247 -0
- package/skills/pptx/scripts/thumbnail.py +289 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/VENDORED.md +32 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/skills/switchroom-architecture/SKILL.md +60 -0
- package/skills/switchroom-architecture/cascade.md +112 -0
- package/skills/switchroom-architecture/sub-agents.md +87 -0
- package/skills/switchroom-architecture/telegram.md +94 -0
- package/skills/switchroom-cli/SKILL.md +274 -0
- package/skills/switchroom-health/SKILL.md +101 -0
- package/skills/switchroom-install/SKILL.md +116 -0
- package/skills/switchroom-manage/SKILL.md +90 -0
- package/skills/switchroom-status/SKILL.md +69 -0
- package/skills/switchroom-status/scripts/status.sh +69 -0
- package/skills/telegram-test-harness/SKILL.md +191 -0
- package/skills/token-helpers/SKILL.md +73 -0
- package/skills/token-helpers/scripts/google-cal-token.sh +62 -0
- package/skills/token-helpers/scripts/ms-graph-token.sh +70 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/VENDORED.md +32 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/skills/xlsx/LICENSE.txt +30 -0
- package/skills/xlsx/SKILL.md +292 -0
- package/skills/xlsx/VENDORED.md +32 -0
- package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/xlsx/scripts/office/pack.py +159 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/xlsx/scripts/office/soffice.py +183 -0
- package/skills/xlsx/scripts/office/unpack.py +132 -0
- package/skills/xlsx/scripts/office/validate.py +111 -0
- package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
- package/skills/xlsx/scripts/office/validators/base.py +847 -0
- package/skills/xlsx/scripts/office/validators/docx.py +446 -0
- package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
- package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
- package/skills/xlsx/scripts/recalc.py +184 -0
- package/telegram-plugin/.claude-plugin/plugin.json +20 -0
- package/telegram-plugin/.mcp.json +14 -0
- package/telegram-plugin/LICENSE +21 -0
- package/telegram-plugin/README.md +352 -0
- package/telegram-plugin/active-pins-sweep.ts +204 -0
- package/telegram-plugin/active-pins.ts +146 -0
- package/telegram-plugin/active-reactions-sweep.ts +79 -0
- package/telegram-plugin/active-reactions.ts +134 -0
- package/telegram-plugin/admin-commands/dispatch.test.ts +149 -0
- package/telegram-plugin/admin-commands/index.ts +106 -0
- package/telegram-plugin/answer-stream.ts +565 -0
- package/telegram-plugin/ask-user.ts +179 -0
- package/telegram-plugin/attachment-path.ts +80 -0
- package/telegram-plugin/auth-code-redact.ts +83 -0
- package/telegram-plugin/auth-dashboard.ts +1104 -0
- package/telegram-plugin/auth-slot-parser.ts +497 -0
- package/telegram-plugin/auto-fallback-dispatcher.ts +68 -0
- package/telegram-plugin/auto-fallback.ts +348 -0
- package/telegram-plugin/bridge/bridge.ts +687 -0
- package/telegram-plugin/bridge/ipc-client.ts +326 -0
- package/telegram-plugin/bun.lock +218 -0
- package/telegram-plugin/card-format.ts +62 -0
- package/telegram-plugin/channel-envelope-safety.test.ts +56 -0
- package/telegram-plugin/channel-envelope-safety.ts +56 -0
- package/telegram-plugin/chat-lock.ts +65 -0
- package/telegram-plugin/context-exhaustion.ts +38 -0
- package/telegram-plugin/credits-watch.ts +220 -0
- package/telegram-plugin/dist/bridge/bridge.js +24758 -0
- package/telegram-plugin/dist/foreman/foreman.js +30723 -0
- package/telegram-plugin/dist/gateway/gateway.js +46497 -0
- package/telegram-plugin/dist/server.js +24551 -0
- package/telegram-plugin/dm-command-gate.ts +56 -0
- package/telegram-plugin/docs/gateway-server-split.md +133 -0
- package/telegram-plugin/docs/multi-agent-card-design.md +847 -0
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +144 -0
- package/telegram-plugin/docs/stream-json-daemon-mode.md +477 -0
- package/telegram-plugin/docs/waiting-ux-spec.md +233 -0
- package/telegram-plugin/draft-stream.ts +442 -0
- package/telegram-plugin/draft-transport.ts +72 -0
- package/telegram-plugin/first-paint.ts +246 -0
- package/telegram-plugin/fleet-state.ts +246 -0
- package/telegram-plugin/foreman/foreman-create-flow.ts +202 -0
- package/telegram-plugin/foreman/foreman-handlers.ts +493 -0
- package/telegram-plugin/foreman/foreman.ts +1130 -0
- package/telegram-plugin/foreman/setup-flow.ts +345 -0
- package/telegram-plugin/foreman/setup-state.ts +239 -0
- package/telegram-plugin/foreman/state.ts +203 -0
- package/telegram-plugin/format.ts +685 -0
- package/telegram-plugin/gateway/access-validator.test.ts +95 -0
- package/telegram-plugin/gateway/access-validator.ts +37 -0
- package/telegram-plugin/gateway/boot-card.ts +582 -0
- package/telegram-plugin/gateway/boot-probes.ts +863 -0
- package/telegram-plugin/gateway/boot-reason.ts +51 -0
- package/telegram-plugin/gateway/boot-sweep-filter.test.ts +54 -0
- package/telegram-plugin/gateway/boot-sweep-filter.ts +32 -0
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +183 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +109 -0
- package/telegram-plugin/gateway/gateway.ts +10202 -0
- package/telegram-plugin/gateway/inbound-coalesce.ts +147 -0
- package/telegram-plugin/gateway/inject-handler.test.ts +221 -0
- package/telegram-plugin/gateway/inject-handler.ts +190 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +151 -0
- package/telegram-plugin/gateway/ipc-server.ts +494 -0
- package/telegram-plugin/gateway/pid-file.ts +103 -0
- package/telegram-plugin/gateway/poll-health.ts +156 -0
- package/telegram-plugin/gateway/preamble-suppressor.ts +154 -0
- package/telegram-plugin/gateway/quota-cache.ts +125 -0
- package/telegram-plugin/gateway/resolve-calling-subagent.ts +78 -0
- package/telegram-plugin/gateway/restart-watchdog.ts +200 -0
- package/telegram-plugin/gateway/session-marker.ts +83 -0
- package/telegram-plugin/gateway/shutdown-drain.ts +162 -0
- package/telegram-plugin/gateway/startup-mutex.ts +285 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +142 -0
- package/telegram-plugin/gateway/turn-active-marker.ts +176 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +78 -0
- package/telegram-plugin/handoff-continuity.ts +200 -0
- package/telegram-plugin/history.ts +468 -0
- package/telegram-plugin/hooks/hooks.json +58 -0
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +208 -0
- package/telegram-plugin/hooks/secret-scrub-stop.mjs +98 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +111 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +296 -0
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +261 -0
- package/telegram-plugin/html-sanitize.ts +244 -0
- package/telegram-plugin/idle-footer.ts +65 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +166 -0
- package/telegram-plugin/interrupt-marker.ts +66 -0
- package/telegram-plugin/issues-card.ts +371 -0
- package/telegram-plugin/issues-watcher.ts +125 -0
- package/telegram-plugin/model-unavailable.ts +325 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/operator-events-history.ts +94 -0
- package/telegram-plugin/operator-events.fixtures.json +161 -0
- package/telegram-plugin/operator-events.ts +421 -0
- package/telegram-plugin/package.json +55 -0
- package/telegram-plugin/permission-rule.ts +133 -0
- package/telegram-plugin/permission-title.ts +117 -0
- package/telegram-plugin/pin-event-log.ts +76 -0
- package/telegram-plugin/plugin-logger.ts +136 -0
- package/telegram-plugin/progress-card-driver.ts +2697 -0
- package/telegram-plugin/progress-card-pin-manager.ts +589 -0
- package/telegram-plugin/progress-card-pin-watchdog.ts +98 -0
- package/telegram-plugin/progress-card.ts +1409 -0
- package/telegram-plugin/pty-partial-handler.ts +247 -0
- package/telegram-plugin/pty-tail.ts +730 -0
- package/telegram-plugin/quota-check.ts +474 -0
- package/telegram-plugin/recent-outbound-dedup.ts +169 -0
- package/telegram-plugin/registry/api-registry.test.ts +201 -0
- package/telegram-plugin/registry/subagents-bugs.test.ts +454 -0
- package/telegram-plugin/registry/subagents-schema.ts +509 -0
- package/telegram-plugin/registry/subagents.test.ts +476 -0
- package/telegram-plugin/registry/turns-schema.test.ts +101 -0
- package/telegram-plugin/registry/turns-schema.ts +417 -0
- package/telegram-plugin/retry-api-call.ts +172 -0
- package/telegram-plugin/scripts/build.mjs +78 -0
- package/telegram-plugin/secret-detect/audit.ts +66 -0
- package/telegram-plugin/secret-detect/chunker.ts +37 -0
- package/telegram-plugin/secret-detect/entropy.ts +20 -0
- package/telegram-plugin/secret-detect/gitleaks-loader.ts +74 -0
- package/telegram-plugin/secret-detect/gitleaks.toml +27 -0
- package/telegram-plugin/secret-detect/index.ts +218 -0
- package/telegram-plugin/secret-detect/kv-scanner.ts +60 -0
- package/telegram-plugin/secret-detect/mask.ts +13 -0
- package/telegram-plugin/secret-detect/patterns.ts +115 -0
- package/telegram-plugin/secret-detect/pipeline.ts +144 -0
- package/telegram-plugin/secret-detect/rewrite.ts +26 -0
- package/telegram-plugin/secret-detect/secretlint-source.ts +95 -0
- package/telegram-plugin/secret-detect/slug.ts +44 -0
- package/telegram-plugin/secret-detect/staging.ts +85 -0
- package/telegram-plugin/secret-detect/suppressor.ts +34 -0
- package/telegram-plugin/secret-detect/url-redact.ts +60 -0
- package/telegram-plugin/secret-detect/vault-write.ts +56 -0
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/server.ts +171 -0
- package/telegram-plugin/session-tail.ts +884 -0
- package/telegram-plugin/shared/bot-runtime.ts +324 -0
- package/telegram-plugin/silent-reply.ts +58 -0
- package/telegram-plugin/slot-banner-driver.ts +147 -0
- package/telegram-plugin/slot-banner.ts +86 -0
- package/telegram-plugin/start.js +26 -0
- package/telegram-plugin/startup-reset.ts +45 -0
- package/telegram-plugin/status-reactions.ts +332 -0
- package/telegram-plugin/steering.ts +155 -0
- package/telegram-plugin/sticker-aliases.ts +249 -0
- package/telegram-plugin/stream-controller.ts +311 -0
- package/telegram-plugin/stream-reply-handler.ts +664 -0
- package/telegram-plugin/streaming-metrics.ts +134 -0
- package/telegram-plugin/streaming-report.ts +204 -0
- package/telegram-plugin/subagent-watcher.ts +880 -0
- package/telegram-plugin/telegram-button-constraints.ts +191 -0
- package/telegram-plugin/telegraph.ts +381 -0
- package/telegram-plugin/tests/HARNESS.md +340 -0
- package/telegram-plugin/tests/_progress-card-harness.ts +105 -0
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +211 -0
- package/telegram-plugin/tests/active-pins-sweep.test.ts +309 -0
- package/telegram-plugin/tests/active-pins.test.ts +187 -0
- package/telegram-plugin/tests/active-reactions-sweep.test.ts +116 -0
- package/telegram-plugin/tests/active-reactions.test.ts +198 -0
- package/telegram-plugin/tests/answer-stream-dedup.test.ts +352 -0
- package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +236 -0
- package/telegram-plugin/tests/answer-stream.test.ts +878 -0
- package/telegram-plugin/tests/ask-user.test.ts +203 -0
- package/telegram-plugin/tests/attachment-path.test.ts +199 -0
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +118 -0
- package/telegram-plugin/tests/auth-code-auto-capture.test.ts +144 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +248 -0
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +260 -0
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +140 -0
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +559 -0
- package/telegram-plugin/tests/auth-dashboard.test.ts +1045 -0
- package/telegram-plugin/tests/auth-login-url-button.test.ts +122 -0
- package/telegram-plugin/tests/auth-slot-commands.test.ts +640 -0
- package/telegram-plugin/tests/auto-fallback-dispatcher.e2e.test.ts +183 -0
- package/telegram-plugin/tests/auto-fallback.test.ts +381 -0
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +137 -0
- package/telegram-plugin/tests/boot-card-dedupe.test.ts +154 -0
- package/telegram-plugin/tests/boot-card-probe-target.test.ts +194 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +103 -0
- package/telegram-plugin/tests/boot-card-render.test.ts +219 -0
- package/telegram-plugin/tests/boot-probes.test.ts +451 -0
- package/telegram-plugin/tests/bot-api.harness.ts +116 -0
- package/telegram-plugin/tests/bot-runtime.test.ts +190 -0
- package/telegram-plugin/tests/bridge-anonymous-refuse.test.ts +60 -0
- package/telegram-plugin/tests/context-exhaustion.test.ts +114 -0
- package/telegram-plugin/tests/credits-watch.test.ts +221 -0
- package/telegram-plugin/tests/dm-command-gate.test.ts +176 -0
- package/telegram-plugin/tests/draft-stream.test.ts +752 -0
- package/telegram-plugin/tests/draft-transport.test.ts +141 -0
- package/telegram-plugin/tests/e2e.test.ts +436 -0
- package/telegram-plugin/tests/fake-bot-api.test.ts +213 -0
- package/telegram-plugin/tests/fake-bot-api.ts +617 -0
- package/telegram-plugin/tests/false-restart-banner.test.ts +253 -0
- package/telegram-plugin/tests/first-paint.test.ts +257 -0
- package/telegram-plugin/tests/fixtures/pty-tail-tmux-fragment.bin +6 -0
- package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +3624 -0
- package/telegram-plugin/tests/fleet-state-watcher.test.ts +101 -0
- package/telegram-plugin/tests/fleet-state.test.ts +185 -0
- package/telegram-plugin/tests/foreman-create-flow.test.ts +359 -0
- package/telegram-plugin/tests/foreman-handlers.test.ts +347 -0
- package/telegram-plugin/tests/foreman-state.test.ts +164 -0
- package/telegram-plugin/tests/foreman-write-ops.test.ts +214 -0
- package/telegram-plugin/tests/gateway-409-retry-banner.test.ts +173 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +72 -0
- package/telegram-plugin/tests/gateway-bridge.test.ts +811 -0
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +414 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +144 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +133 -0
- package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +103 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +127 -0
- package/telegram-plugin/tests/gateway-startup-mutex.test.ts +284 -0
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +185 -0
- package/telegram-plugin/tests/gateway-startup-reset.test.ts +72 -0
- package/telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts +125 -0
- package/telegram-plugin/tests/handoff-continuity.test.ts +249 -0
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +243 -0
- package/telegram-plugin/tests/harness-parse-mode-validation.test.ts +114 -0
- package/telegram-plugin/tests/history.test.ts +364 -0
- package/telegram-plugin/tests/html-balanced.ts +63 -0
- package/telegram-plugin/tests/html-sanitize.test.ts +146 -0
- package/telegram-plugin/tests/idle-footer-wiring.test.ts +88 -0
- package/telegram-plugin/tests/idle-footer.test.ts +66 -0
- package/telegram-plugin/tests/inbound-coalesce.test.ts +127 -0
- package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +150 -0
- package/telegram-plugin/tests/interrupt-marker.test.ts +126 -0
- package/telegram-plugin/tests/ipc-protocol.test.ts +218 -0
- package/telegram-plugin/tests/ipc-server-anonymous-refuse.test.ts +82 -0
- package/telegram-plugin/tests/ipc-server-client.test.ts +323 -0
- package/telegram-plugin/tests/ipc-server-race.test.ts +183 -0
- package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +91 -0
- package/telegram-plugin/tests/ipc-server-validate-pty-partial.test.ts +64 -0
- package/telegram-plugin/tests/ipc-server-validate-update-placeholder.test.ts +77 -0
- package/telegram-plugin/tests/ipc-validator.test.ts +274 -0
- package/telegram-plugin/tests/issues-card.test.ts +495 -0
- package/telegram-plugin/tests/issues-watcher.test.ts +165 -0
- package/telegram-plugin/tests/model-unavailable.test.ts +303 -0
- package/telegram-plugin/tests/multi-turn-continuity.test.ts +159 -0
- package/telegram-plugin/tests/operator-events-history.test.ts +125 -0
- package/telegram-plugin/tests/operator-events-session-tail.test.ts +192 -0
- package/telegram-plugin/tests/operator-events.test.ts +331 -0
- package/telegram-plugin/tests/outbound-ordering.test.ts +293 -0
- package/telegram-plugin/tests/parse-mode-rotation.test.ts +164 -0
- package/telegram-plugin/tests/permission-rule.test.ts +121 -0
- package/telegram-plugin/tests/permission-title.test.ts +106 -0
- package/telegram-plugin/tests/pin-event-log.test.ts +124 -0
- package/telegram-plugin/tests/plugin-logger.test.ts +97 -0
- package/telegram-plugin/tests/poll-health.test.ts +86 -0
- package/telegram-plugin/tests/preamble-suppressor.test.ts +285 -0
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +73 -0
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +272 -0
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +258 -0
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +81 -0
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +80 -0
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +215 -0
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +123 -0
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +76 -0
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +62 -0
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +84 -0
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +139 -0
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +773 -0
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +66 -0
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +64 -0
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +190 -0
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +146 -0
- package/telegram-plugin/tests/progress-update.test.ts +236 -0
- package/telegram-plugin/tests/protocol-fixtures.test.ts +59 -0
- package/telegram-plugin/tests/protocol-fixtures.ts +198 -0
- package/telegram-plugin/tests/pty-partial-handler.test.ts +326 -0
- package/telegram-plugin/tests/pty-tail-real-fixture.test.ts +114 -0
- package/telegram-plugin/tests/pty-tail-tmux-fragment.test.ts +71 -0
- package/telegram-plugin/tests/pty-tail.test.ts +525 -0
- package/telegram-plugin/tests/quota-cache.test.ts +187 -0
- package/telegram-plugin/tests/quota-check.test.ts +622 -0
- package/telegram-plugin/tests/races.test.ts +842 -0
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +123 -0
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +82 -0
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +114 -0
- package/telegram-plugin/tests/real-gateway-harness.ts +699 -0
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +313 -0
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +299 -0
- package/telegram-plugin/tests/real-gateway-spec.test.ts +487 -0
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +101 -0
- package/telegram-plugin/tests/recent-outbound-dedup.test.ts +192 -0
- package/telegram-plugin/tests/registry-turns.test.ts +432 -0
- package/telegram-plugin/tests/reply-terminal-reaction.test.ts +149 -0
- package/telegram-plugin/tests/resolve-calling-subagent.test.ts +269 -0
- package/telegram-plugin/tests/restart-watchdog.test.ts +224 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +287 -0
- package/telegram-plugin/tests/secret-detect-audit.test.ts +58 -0
- package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +83 -0
- package/telegram-plugin/tests/secret-detect-gitleaks.test.ts +32 -0
- package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +308 -0
- package/telegram-plugin/tests/secret-detect-pipeline.test.ts +123 -0
- package/telegram-plugin/tests/secret-detect-secretlint.test.ts +101 -0
- package/telegram-plugin/tests/secret-detect-staging.test.ts +45 -0
- package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +67 -0
- package/telegram-plugin/tests/secret-detect.test.ts +223 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +194 -0
- package/telegram-plugin/tests/send-typing-action-validation.test.ts +61 -0
- package/telegram-plugin/tests/session-tail-capped.test.ts +285 -0
- package/telegram-plugin/tests/session-tail.test.ts +524 -0
- package/telegram-plugin/tests/setup-flow.test.ts +510 -0
- package/telegram-plugin/tests/setup-state.test.ts +146 -0
- package/telegram-plugin/tests/silent-reply-guard.test.ts +122 -0
- package/telegram-plugin/tests/slot-banner-driver.e2e.test.ts +350 -0
- package/telegram-plugin/tests/slot-banner.test.ts +74 -0
- package/telegram-plugin/tests/snapshot-serializer.ts +79 -0
- package/telegram-plugin/tests/spawn-detached-cgroup-escape.test.ts +51 -0
- package/telegram-plugin/tests/status-accent.test.ts +186 -0
- package/telegram-plugin/tests/status-reactions-allowed-filter.test.ts +132 -0
- package/telegram-plugin/tests/status-reactions.test.ts +314 -0
- package/telegram-plugin/tests/steering.test.ts +282 -0
- package/telegram-plugin/tests/sticker-aliases.test.ts +232 -0
- package/telegram-plugin/tests/stream-controller-html-fallback.test.ts +127 -0
- package/telegram-plugin/tests/stream-controller.test.ts +262 -0
- package/telegram-plugin/tests/stream-reply-error-paths.test.ts +208 -0
- package/telegram-plugin/tests/stream-reply-handler.test.ts +1292 -0
- package/telegram-plugin/tests/streaming-e2e.test.ts +389 -0
- package/telegram-plugin/tests/streaming-metrics.test.ts +201 -0
- package/telegram-plugin/tests/streaming-orchestration.test.ts +756 -0
- package/telegram-plugin/tests/subagent-registry-bugs.test.ts +725 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +213 -0
- package/telegram-plugin/tests/subagent-watcher-parent-marker.test.ts +274 -0
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +243 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +877 -0
- package/telegram-plugin/tests/subagents-schema-init-order.test.ts +109 -0
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +116 -0
- package/telegram-plugin/tests/telegram-button-constraints.test.ts +194 -0
- package/telegram-plugin/tests/telegram-format.test.ts +1093 -0
- package/telegram-plugin/tests/telegraph.test.ts +246 -0
- package/telegram-plugin/tests/tool-labels.test.ts +383 -0
- package/telegram-plugin/tests/turn-active-marker.test.ts +195 -0
- package/telegram-plugin/tests/turn-end-regressions.test.ts +489 -0
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +218 -0
- package/telegram-plugin/tests/turn-flush-dedup-controller.test.ts +144 -0
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +78 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +189 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +107 -0
- package/telegram-plugin/tests/turns-writer.test.ts +323 -0
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +131 -0
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +120 -0
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +87 -0
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +211 -0
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +62 -0
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +68 -0
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +110 -0
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +128 -0
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +58 -0
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +133 -0
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +155 -0
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +117 -0
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +143 -0
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +149 -0
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +105 -0
- package/telegram-plugin/tests/typing-wrap.test.ts +141 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +147 -0
- package/telegram-plugin/tests/update-factory-edited-and-reactions.test.ts +108 -0
- package/telegram-plugin/tests/update-factory.ts +305 -0
- package/telegram-plugin/tests/vault-grant-wizard.test.ts +84 -0
- package/telegram-plugin/tests/vault-grants-revoke.test.ts +265 -0
- package/telegram-plugin/tests/vault-subcommands.test.ts +234 -0
- package/telegram-plugin/tests/voice-transcribe.test.ts +196 -0
- package/telegram-plugin/tests/waiting-ux-harness.ts +381 -0
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +233 -0
- package/telegram-plugin/tests/welcome-text.test.ts +407 -0
- package/telegram-plugin/tool-error-filter.ts +89 -0
- package/telegram-plugin/tool-labels.ts +330 -0
- package/telegram-plugin/tool-names.ts +53 -0
- package/telegram-plugin/turn-flush-prose-recovery.ts +40 -0
- package/telegram-plugin/turn-flush-safety.ts +109 -0
- package/telegram-plugin/turn-signal-tracker.ts +79 -0
- package/telegram-plugin/two-zone-card.ts +249 -0
- package/telegram-plugin/typing-wrap.ts +92 -0
- package/telegram-plugin/voice-transcribe.ts +166 -0
- package/telegram-plugin/welcome-text.ts +359 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the `send_sticker` MCP tool's alias-resolution path.
|
|
3
|
+
*
|
|
4
|
+
* Stickers in Telegram are addressed by `file_id` — opaque, ~70-char,
|
|
5
|
+
* agent-unfriendly. To make outbound stickers usable by personas we
|
|
6
|
+
* let operators declare named aliases in switchroom.yaml under
|
|
7
|
+
* `telegram.stickers: { mood_happy: "<file_id>", thinking: "<file_id>" }`.
|
|
8
|
+
* The agent then calls `send_sticker(chat_id, alias='mood_happy')` and
|
|
9
|
+
* we resolve to the file_id at send time.
|
|
10
|
+
*
|
|
11
|
+
* This file owns the resolution + validation. Pure — no Telegram API
|
|
12
|
+
* calls, no I/O, fully unit-testable.
|
|
13
|
+
*
|
|
14
|
+
* Why operator-curated and not Tenor/Giphy: subscription-honest
|
|
15
|
+
* principle says no third-party API keys. Stickers the agent wants to
|
|
16
|
+
* send must be ones an operator has explicitly approved by capturing
|
|
17
|
+
* a file_id and putting it in config. Same model the existing access
|
|
18
|
+
* surface uses: trust-by-explicit-listing.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface StickerAliasMap {
|
|
22
|
+
/** alias name → Telegram file_id. Both validated on read. */
|
|
23
|
+
[alias: string]: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface StickerSendArgs {
|
|
27
|
+
chat_id: string
|
|
28
|
+
/** Either a raw Telegram file_id OR an alias declared in
|
|
29
|
+
* `telegram.stickers`. We accept both to match the agent's
|
|
30
|
+
* conversational intent: it might pass `'mood_happy'` from the
|
|
31
|
+
* config, or echo back a file_id it saw on inbound. */
|
|
32
|
+
sticker: string
|
|
33
|
+
message_thread_id?: string
|
|
34
|
+
reply_to?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ValidatedStickerSendArgs {
|
|
38
|
+
chatId: string
|
|
39
|
+
fileId: string
|
|
40
|
+
/** What the resolution path was, for log lines + telemetry. Lets
|
|
41
|
+
* operators see "agent used alias 'happy' which mapped to file_id
|
|
42
|
+
* CAACAg..." vs "agent passed raw file_id." */
|
|
43
|
+
resolution: 'alias' | 'raw'
|
|
44
|
+
/** Original alias name if resolution === 'alias'. */
|
|
45
|
+
aliasName?: string
|
|
46
|
+
threadId?: number
|
|
47
|
+
replyTo?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Telegram file_ids are alphanumeric + `-` + `_`, typically 50-100
|
|
52
|
+
* chars. Reject anything outside that shape early so the agent gets
|
|
53
|
+
* a clear error rather than Telegram's generic 400.
|
|
54
|
+
*
|
|
55
|
+
* The 200-char ceiling matches the existing `download_attachment`
|
|
56
|
+
* tool's validator — same shape across the surface.
|
|
57
|
+
*/
|
|
58
|
+
export function looksLikeFileId(s: string): boolean {
|
|
59
|
+
return /^[A-Za-z0-9_-]{10,200}$/.test(s)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Aliases are operator-typed config keys. Restrict to safe shell-ish
|
|
64
|
+
* characters so a misconfigured alias can't accidentally collide with
|
|
65
|
+
* a file_id on the boundary, and so config files stay diff-friendly.
|
|
66
|
+
*/
|
|
67
|
+
export function isValidAliasName(s: string): boolean {
|
|
68
|
+
return /^[a-z0-9][a-z0-9_-]{0,63}$/i.test(s)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate raw sticker-send args + resolve any alias against the
|
|
73
|
+
* configured map. Throws with an actionable error on every failure
|
|
74
|
+
* mode so the agent's tool-error reply tells the user (or operator)
|
|
75
|
+
* exactly what to fix.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveStickerSendArgs(
|
|
78
|
+
raw: StickerSendArgs,
|
|
79
|
+
aliasMap: StickerAliasMap,
|
|
80
|
+
): ValidatedStickerSendArgs {
|
|
81
|
+
if (typeof raw.chat_id !== 'string' || raw.chat_id.length === 0) {
|
|
82
|
+
throw new Error('send_sticker: chat_id is required')
|
|
83
|
+
}
|
|
84
|
+
if (typeof raw.sticker !== 'string' || raw.sticker.length === 0) {
|
|
85
|
+
throw new Error('send_sticker: sticker (file_id or alias) is required')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let fileId: string
|
|
89
|
+
let resolution: 'alias' | 'raw'
|
|
90
|
+
let aliasName: string | undefined
|
|
91
|
+
|
|
92
|
+
// Distinguishing alias vs file_id: file_ids include only alnum + `-`
|
|
93
|
+
// + `_`, but they're long (50-100). Aliases are short (under 64).
|
|
94
|
+
// First check: is it a known alias? If yes, use that. Otherwise, is
|
|
95
|
+
// it a valid file_id? If yes, pass through. Otherwise, error.
|
|
96
|
+
//
|
|
97
|
+
// The "known alias" check first means an operator can never have an
|
|
98
|
+
// alias collide with a file_id by accident — config wins.
|
|
99
|
+
if (Object.prototype.hasOwnProperty.call(aliasMap, raw.sticker)) {
|
|
100
|
+
aliasName = raw.sticker
|
|
101
|
+
fileId = aliasMap[raw.sticker]
|
|
102
|
+
resolution = 'alias'
|
|
103
|
+
if (!looksLikeFileId(fileId)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`send_sticker: alias '${raw.sticker}' resolves to malformed file_id ` +
|
|
106
|
+
`in switchroom.yaml — fix telegram.stickers.${raw.sticker}`,
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
} else if (looksLikeFileId(raw.sticker)) {
|
|
110
|
+
fileId = raw.sticker
|
|
111
|
+
resolution = 'raw'
|
|
112
|
+
} else if (isValidAliasName(raw.sticker)) {
|
|
113
|
+
// The agent asked for an alias that doesn't exist. Tell it
|
|
114
|
+
// explicitly so it can ask the user to add one or pick a
|
|
115
|
+
// different one.
|
|
116
|
+
const known = Object.keys(aliasMap).sort()
|
|
117
|
+
const hint = known.length > 0
|
|
118
|
+
? ` Available aliases: ${known.join(', ')}.`
|
|
119
|
+
: ' No sticker aliases are configured for this agent.'
|
|
120
|
+
throw new Error(`send_sticker: unknown alias '${raw.sticker}'.${hint}`)
|
|
121
|
+
} else {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`send_sticker: '${raw.sticker}' is neither a valid Telegram file_id ` +
|
|
124
|
+
`nor a configured alias name`,
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let threadId: number | undefined
|
|
129
|
+
if (raw.message_thread_id != null) {
|
|
130
|
+
threadId = Number(raw.message_thread_id)
|
|
131
|
+
if (!Number.isFinite(threadId) || threadId <= 0) {
|
|
132
|
+
throw new Error('send_sticker: message_thread_id must be a positive integer string')
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let replyTo: number | undefined
|
|
136
|
+
if (raw.reply_to != null) {
|
|
137
|
+
replyTo = Number(raw.reply_to)
|
|
138
|
+
if (!Number.isFinite(replyTo) || replyTo <= 0) {
|
|
139
|
+
throw new Error('send_sticker: reply_to must be a positive integer string')
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
chatId: raw.chat_id,
|
|
145
|
+
fileId,
|
|
146
|
+
resolution,
|
|
147
|
+
aliasName,
|
|
148
|
+
threadId,
|
|
149
|
+
replyTo,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface GifSendArgs {
|
|
154
|
+
chat_id: string
|
|
155
|
+
/** Either a Telegram file_id OR an https URL pointing at an
|
|
156
|
+
* animated mp4/gif. URLs let agents embed GIFs from operator-
|
|
157
|
+
* curated sources without the alias-config overhead — but we
|
|
158
|
+
* validate the URL is https + has a known media extension. */
|
|
159
|
+
gif: string
|
|
160
|
+
caption?: string
|
|
161
|
+
message_thread_id?: string
|
|
162
|
+
reply_to?: string
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface ValidatedGifSendArgs {
|
|
166
|
+
chatId: string
|
|
167
|
+
/** Either a file_id or a URL — Telegram's sendAnimation accepts
|
|
168
|
+
* both via the same `animation` field. */
|
|
169
|
+
animationRef: string
|
|
170
|
+
refKind: 'file_id' | 'url'
|
|
171
|
+
caption?: string
|
|
172
|
+
threadId?: number
|
|
173
|
+
replyTo?: number
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Animated-media URLs: https, ends in `.mp4` / `.gif` / `.webm`.
|
|
178
|
+
* Telegram accepts these via sendAnimation's URL form. Reject other
|
|
179
|
+
* URLs so the agent can't accidentally try to embed an HTML page.
|
|
180
|
+
*/
|
|
181
|
+
export function isAcceptableGifUrl(s: string): boolean {
|
|
182
|
+
if (!/^https:\/\//.test(s)) return false
|
|
183
|
+
if (s.length > 1024) return false
|
|
184
|
+
// Strip query string before extension check so e.g.
|
|
185
|
+
// ?cache=123 doesn't trip us up.
|
|
186
|
+
const noQuery = s.split('?')[0].split('#')[0]
|
|
187
|
+
return /\.(mp4|gif|webm)$/i.test(noQuery)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function resolveGifSendArgs(raw: GifSendArgs): ValidatedGifSendArgs {
|
|
191
|
+
if (typeof raw.chat_id !== 'string' || raw.chat_id.length === 0) {
|
|
192
|
+
throw new Error('send_gif: chat_id is required')
|
|
193
|
+
}
|
|
194
|
+
if (typeof raw.gif !== 'string' || raw.gif.length === 0) {
|
|
195
|
+
throw new Error('send_gif: gif (file_id or url) is required')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let animationRef: string
|
|
199
|
+
let refKind: 'file_id' | 'url'
|
|
200
|
+
|
|
201
|
+
if (raw.gif.startsWith('http')) {
|
|
202
|
+
if (!isAcceptableGifUrl(raw.gif)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
'send_gif: url must be https with .mp4 / .gif / .webm extension',
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
animationRef = raw.gif
|
|
208
|
+
refKind = 'url'
|
|
209
|
+
} else if (looksLikeFileId(raw.gif)) {
|
|
210
|
+
animationRef = raw.gif
|
|
211
|
+
refKind = 'file_id'
|
|
212
|
+
} else {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`send_gif: '${raw.gif.slice(0, 40)}...' is neither a valid Telegram ` +
|
|
215
|
+
`file_id nor an acceptable https URL`,
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (raw.caption != null && typeof raw.caption !== 'string') {
|
|
220
|
+
throw new Error('send_gif: caption must be a string')
|
|
221
|
+
}
|
|
222
|
+
if (raw.caption != null && raw.caption.length > 1024) {
|
|
223
|
+
throw new Error('send_gif: caption too long (max 1024 chars)')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let threadId: number | undefined
|
|
227
|
+
if (raw.message_thread_id != null) {
|
|
228
|
+
threadId = Number(raw.message_thread_id)
|
|
229
|
+
if (!Number.isFinite(threadId) || threadId <= 0) {
|
|
230
|
+
throw new Error('send_gif: message_thread_id must be a positive integer string')
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
let replyTo: number | undefined
|
|
234
|
+
if (raw.reply_to != null) {
|
|
235
|
+
replyTo = Number(raw.reply_to)
|
|
236
|
+
if (!Number.isFinite(replyTo) || replyTo <= 0) {
|
|
237
|
+
throw new Error('send_gif: reply_to must be a positive integer string')
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
chatId: raw.chat_id,
|
|
243
|
+
animationRef,
|
|
244
|
+
refKind,
|
|
245
|
+
caption: raw.caption,
|
|
246
|
+
threadId,
|
|
247
|
+
replyTo,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin integration layer between `createDraftStream` and grammy's `bot.api`.
|
|
3
|
+
*
|
|
4
|
+
* Deduplicates the send/edit closure wiring that previously lived inline in
|
|
5
|
+
* two places in server.ts:
|
|
6
|
+
* - the `stream_reply` MCP case block (model-driven streaming)
|
|
7
|
+
* - `handlePtyPartial` (PTY-tail TUI extractor → live preview)
|
|
8
|
+
*
|
|
9
|
+
* Both paths do the same thing: given a chat/thread/parseMode, create a
|
|
10
|
+
* draft stream whose `send` closure calls `bot.api.sendMessage` and whose
|
|
11
|
+
* `edit` closure calls `bot.api.editMessageText`, both wrapped in the
|
|
12
|
+
* shared retry/429/not-modified policy (`robustApiCall`).
|
|
13
|
+
*
|
|
14
|
+
* This module exists primarily so that wiring can be exercised by
|
|
15
|
+
* integration tests against a mock bot.api, without having to mock the
|
|
16
|
+
* entire server.ts top-level initialization.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { createDraftStream, type DraftStreamHandle, type StreamDraftFn } from './draft-stream.js'
|
|
20
|
+
import { htmlToPlainText } from './html-sanitize.js'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Telegram returns `400 Bad Request: can't parse entities: …` when the
|
|
24
|
+
* body contains malformed HTML / MarkdownV2 / nested unbalanced tags.
|
|
25
|
+
* Detect that specific error class so we can fall back to plain text
|
|
26
|
+
* without confusing it with other 400s (rate-limit, message-not-found,
|
|
27
|
+
* thread-not-found, etc.).
|
|
28
|
+
*/
|
|
29
|
+
function isParseEntitiesError(err: unknown): boolean {
|
|
30
|
+
const msg =
|
|
31
|
+
typeof err === 'string'
|
|
32
|
+
? err
|
|
33
|
+
: err instanceof Error
|
|
34
|
+
? err.message
|
|
35
|
+
: typeof err === 'object' && err != null && 'description' in err
|
|
36
|
+
? typeof (err as { description: unknown }).description === 'string'
|
|
37
|
+
? (err as { description: string }).description
|
|
38
|
+
: ''
|
|
39
|
+
: ''
|
|
40
|
+
return /can't parse entities|can't find end of the entity|unsupported start tag|unmatched end tag/i.test(msg)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Minimal bot.api surface the controller needs. Real callers pass grammy's
|
|
45
|
+
* `bot.api`; tests pass a mock with just these two methods.
|
|
46
|
+
*/
|
|
47
|
+
export interface StreamBotApi {
|
|
48
|
+
sendMessage(
|
|
49
|
+
chat_id: string,
|
|
50
|
+
text: string,
|
|
51
|
+
opts: StreamSendOpts,
|
|
52
|
+
): Promise<{ message_id: number }>
|
|
53
|
+
editMessageText(
|
|
54
|
+
chat_id: string,
|
|
55
|
+
message_id: number,
|
|
56
|
+
text: string,
|
|
57
|
+
opts: StreamSendOpts,
|
|
58
|
+
): Promise<unknown>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface StreamSendOpts {
|
|
62
|
+
parse_mode?: 'HTML' | 'MarkdownV2'
|
|
63
|
+
message_thread_id?: number
|
|
64
|
+
link_preview_options?: { is_disabled: boolean }
|
|
65
|
+
/**
|
|
66
|
+
* Telegram's reply_parameters, used for quote-replying to an earlier
|
|
67
|
+
* message. Only meaningful on the initial `sendMessage` — `editMessageText`
|
|
68
|
+
* cannot add a quote reference to an existing message, so the controller
|
|
69
|
+
* strips this from edit opts internally.
|
|
70
|
+
*/
|
|
71
|
+
reply_parameters?: { message_id: number; quote?: { text: string; position: number } }
|
|
72
|
+
/**
|
|
73
|
+
* Inline keyboard markup. Included in both sendMessage and editMessageText
|
|
74
|
+
* so that inline buttons persist through text edits. Without this,
|
|
75
|
+
* editMessageText strips any previously attached keyboard.
|
|
76
|
+
*/
|
|
77
|
+
reply_markup?: unknown
|
|
78
|
+
/**
|
|
79
|
+
* When true, Telegram prevents the message from being forwarded or saved.
|
|
80
|
+
* Only meaningful on the initial `sendMessage` — `editMessageText` does not
|
|
81
|
+
* accept this parameter, so the controller omits it from edit opts.
|
|
82
|
+
*/
|
|
83
|
+
protect_content?: boolean
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type RetryPolicy = <T>(
|
|
87
|
+
fn: () => Promise<T>,
|
|
88
|
+
opts?: { threadId?: number; chat_id?: string },
|
|
89
|
+
) => Promise<T>
|
|
90
|
+
|
|
91
|
+
export interface StreamControllerConfig {
|
|
92
|
+
bot: { api: StreamBotApi }
|
|
93
|
+
chatId: string
|
|
94
|
+
threadId?: number
|
|
95
|
+
parseMode?: 'HTML' | 'MarkdownV2'
|
|
96
|
+
disableLinkPreview?: boolean
|
|
97
|
+
/**
|
|
98
|
+
* Optional quote-reply target. When set, the initial send attaches
|
|
99
|
+
* `reply_parameters: { message_id: replyToMessageId }` so the first
|
|
100
|
+
* streamed message quote-threads under the referenced message. Edits
|
|
101
|
+
* don't include it (Telegram rejects reply_parameters on edit).
|
|
102
|
+
*/
|
|
103
|
+
replyToMessageId?: number
|
|
104
|
+
/**
|
|
105
|
+
* Optional quote text for surgical quoting. When set along with
|
|
106
|
+
* `replyToMessageId`, the initial send includes
|
|
107
|
+
* `reply_parameters: { message_id, quote: { text, position: 0 } }`.
|
|
108
|
+
*/
|
|
109
|
+
quoteText?: string
|
|
110
|
+
/**
|
|
111
|
+
* When true, Telegram prevents the message from being forwarded or saved.
|
|
112
|
+
* Applied on the initial `sendMessage` only — editMessageText does not
|
|
113
|
+
* accept protect_content.
|
|
114
|
+
*/
|
|
115
|
+
protectContent?: boolean
|
|
116
|
+
/**
|
|
117
|
+
* Inline keyboard markup attached to every send and edit. Without this,
|
|
118
|
+
* editMessageText strips any previously attached keyboard. The progress-
|
|
119
|
+
* card driver passes the Steer button here so it persists through edits.
|
|
120
|
+
*/
|
|
121
|
+
replyMarkup?: unknown
|
|
122
|
+
throttleMs?: number
|
|
123
|
+
/** Pre-send idle debounce. See DraftStreamConfig.idleMs. */
|
|
124
|
+
idleMs?: number
|
|
125
|
+
/**
|
|
126
|
+
* Retry wrapper around bot.api calls. Defaults to calling `fn` directly
|
|
127
|
+
* (no retry) so tests that don't care about policy can omit it. In
|
|
128
|
+
* production, pass server.ts's `robustApiCall`.
|
|
129
|
+
*/
|
|
130
|
+
retry?: RetryPolicy
|
|
131
|
+
/** Observers — fire after each successful send/edit. Optional. */
|
|
132
|
+
onSend?: (messageId: number, chars: number) => void
|
|
133
|
+
onEdit?: (messageId: number, chars: number) => void
|
|
134
|
+
/**
|
|
135
|
+
* Optional diagnostic logger. Receives the draft-stream's internal
|
|
136
|
+
* status lines (edit-failed, not-modified, re-sending, finalize).
|
|
137
|
+
* When omitted, those lines are dropped — which is what the plugin
|
|
138
|
+
* did for its entire history, silently hiding transient edit errors.
|
|
139
|
+
* Pass a stderr writer in production to surface them.
|
|
140
|
+
*/
|
|
141
|
+
log?: (msg: string) => void
|
|
142
|
+
/**
|
|
143
|
+
* Optional warning logger. Used for transport fallback notices.
|
|
144
|
+
*/
|
|
145
|
+
warn?: (msg: string) => void
|
|
146
|
+
/**
|
|
147
|
+
* Transport selector passed to createDraftStream.
|
|
148
|
+
* - "auto" (default): use draft transport for DMs only
|
|
149
|
+
* - "draft": always prefer draft (if sendMessageDraft is available)
|
|
150
|
+
* - "message": always use sendMessage/editMessageText
|
|
151
|
+
*
|
|
152
|
+
* The gateway forces "message" for forum topics (threads), since
|
|
153
|
+
* sendMessageDraft does not support threaded chats.
|
|
154
|
+
*/
|
|
155
|
+
previewTransport?: 'auto' | 'message' | 'draft'
|
|
156
|
+
/**
|
|
157
|
+
* True when the chat is a private DM. Passed to createDraftStream so
|
|
158
|
+
* "auto" transport knows whether to activate draft.
|
|
159
|
+
*/
|
|
160
|
+
isPrivateChat?: boolean
|
|
161
|
+
/**
|
|
162
|
+
* sendMessageDraft callback. When provided (and transport allows it),
|
|
163
|
+
* intermediate stream updates use the draft API. On finalize(), a real
|
|
164
|
+
* sendMessage is posted for push notification and the draft is cleared.
|
|
165
|
+
*/
|
|
166
|
+
sendMessageDraft?: StreamDraftFn
|
|
167
|
+
/**
|
|
168
|
+
* If set, the controller is initialized as if a previous send had
|
|
169
|
+
* landed with this `message_id`. The first `update()` invokes
|
|
170
|
+
* `editMessageText` against this id rather than `sendMessage`.
|
|
171
|
+
* Threads through to `createDraftStream`'s `initialMessageId`. Used
|
|
172
|
+
* by callers that know the anchor message id from an external source
|
|
173
|
+
* (e.g. the gateway's pin manager) to guarantee subsequent emits
|
|
174
|
+
* edit in place rather than create a fresh sendMessage. Closes the
|
|
175
|
+
* "done=true → activeDraftStreams entry deleted → next emit creates
|
|
176
|
+
* fresh sendMessage" duplicate-message class (issue #626).
|
|
177
|
+
*/
|
|
178
|
+
initialMessageId?: number | null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Build a draft stream that writes to Telegram via `bot.api`.
|
|
183
|
+
*
|
|
184
|
+
* The returned handle is the standard `DraftStreamHandle` contract:
|
|
185
|
+
* `update(text)`, `finalize()`, `getMessageId()`, `isFinal()`.
|
|
186
|
+
*/
|
|
187
|
+
export function createStreamController(cfg: StreamControllerConfig): DraftStreamHandle {
|
|
188
|
+
const {
|
|
189
|
+
bot,
|
|
190
|
+
chatId,
|
|
191
|
+
threadId,
|
|
192
|
+
parseMode,
|
|
193
|
+
disableLinkPreview = true,
|
|
194
|
+
throttleMs,
|
|
195
|
+
idleMs,
|
|
196
|
+
retry = <T>(fn: () => Promise<T>) => fn(),
|
|
197
|
+
onSend,
|
|
198
|
+
onEdit,
|
|
199
|
+
log,
|
|
200
|
+
warn,
|
|
201
|
+
replyToMessageId,
|
|
202
|
+
quoteText,
|
|
203
|
+
protectContent,
|
|
204
|
+
replyMarkup,
|
|
205
|
+
previewTransport,
|
|
206
|
+
isPrivateChat,
|
|
207
|
+
sendMessageDraft,
|
|
208
|
+
initialMessageId,
|
|
209
|
+
} = cfg
|
|
210
|
+
|
|
211
|
+
// Base opts shared by send + edit. The initial send adds reply_parameters
|
|
212
|
+
// and protect_content on top (see below); edits must NOT carry those —
|
|
213
|
+
// Telegram's editMessageText rejects them.
|
|
214
|
+
const baseOpts: StreamSendOpts = {
|
|
215
|
+
...(parseMode ? { parse_mode: parseMode } : {}),
|
|
216
|
+
...(threadId != null ? { message_thread_id: threadId } : {}),
|
|
217
|
+
...(disableLinkPreview ? { link_preview_options: { is_disabled: true } } : {}),
|
|
218
|
+
...(replyMarkup != null ? { reply_markup: replyMarkup } : {}),
|
|
219
|
+
}
|
|
220
|
+
const sendOpts: StreamSendOpts = {
|
|
221
|
+
...baseOpts,
|
|
222
|
+
...(replyToMessageId != null
|
|
223
|
+
? {
|
|
224
|
+
reply_parameters: {
|
|
225
|
+
message_id: replyToMessageId,
|
|
226
|
+
...(quoteText != null ? { quote: { text: quoteText, position: 0 } } : {}),
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
: {}),
|
|
230
|
+
...(protectContent === true ? { protect_content: true } : {}),
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Strip parse_mode from a copy of opts — used for the parse-entities
|
|
234
|
+
// fallback path. We keep thread/preview/reply markup untouched.
|
|
235
|
+
const sendOptsPlain: StreamSendOpts = { ...sendOpts }
|
|
236
|
+
delete sendOptsPlain.parse_mode
|
|
237
|
+
const baseOptsPlain: StreamSendOpts = { ...baseOpts }
|
|
238
|
+
delete baseOptsPlain.parse_mode
|
|
239
|
+
|
|
240
|
+
return createDraftStream(
|
|
241
|
+
async (text) => {
|
|
242
|
+
try {
|
|
243
|
+
const sent = await retry(
|
|
244
|
+
() => bot.api.sendMessage(chatId, text, sendOpts),
|
|
245
|
+
{ threadId, chat_id: chatId },
|
|
246
|
+
)
|
|
247
|
+
onSend?.(sent.message_id, text.length)
|
|
248
|
+
return sent.message_id
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (parseMode != null && isParseEntitiesError(err)) {
|
|
251
|
+
// First send rejected for parse_mode error. There is no
|
|
252
|
+
// message_id to edit (the send 400'd before any message was
|
|
253
|
+
// created), so a single fresh send in plain-text is the
|
|
254
|
+
// correct recovery — see issue #657. Strip tags from the
|
|
255
|
+
// body so the user sees readable prose, not raw markup.
|
|
256
|
+
warn?.(
|
|
257
|
+
`stream-controller: sendMessage parse-entities rejected — retrying once as plain text (${err instanceof Error ? err.message : String(err)})`,
|
|
258
|
+
)
|
|
259
|
+
const plainText = htmlToPlainText(text)
|
|
260
|
+
const sent = await retry(
|
|
261
|
+
() => bot.api.sendMessage(chatId, plainText, sendOptsPlain),
|
|
262
|
+
{ threadId, chat_id: chatId },
|
|
263
|
+
)
|
|
264
|
+
onSend?.(sent.message_id, plainText.length)
|
|
265
|
+
return sent.message_id
|
|
266
|
+
}
|
|
267
|
+
throw err
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
async (id, text) => {
|
|
271
|
+
try {
|
|
272
|
+
await retry(
|
|
273
|
+
() => bot.api.editMessageText(chatId, id, text, baseOpts),
|
|
274
|
+
{ threadId, chat_id: chatId },
|
|
275
|
+
)
|
|
276
|
+
onEdit?.(id, text.length)
|
|
277
|
+
} catch (err) {
|
|
278
|
+
if (parseMode != null && isParseEntitiesError(err)) {
|
|
279
|
+
// Edit rejected for parse_mode error — DO NOT send a fresh
|
|
280
|
+
// message. The whole point of issue #657 is that the previous
|
|
281
|
+
// implementation sent a duplicate plain-text message every
|
|
282
|
+
// time HTML rejection fired. Retry the edit on the SAME
|
|
283
|
+
// message_id with parse_mode stripped and the body
|
|
284
|
+
// tag-stripped to plain text.
|
|
285
|
+
warn?.(
|
|
286
|
+
`stream-controller: editMessageText parse-entities rejected — retrying same id=${id} as plain text (${err instanceof Error ? err.message : String(err)})`,
|
|
287
|
+
)
|
|
288
|
+
const plainText = htmlToPlainText(text)
|
|
289
|
+
await retry(
|
|
290
|
+
() => bot.api.editMessageText(chatId, id, plainText, baseOptsPlain),
|
|
291
|
+
{ threadId, chat_id: chatId },
|
|
292
|
+
)
|
|
293
|
+
onEdit?.(id, plainText.length)
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
throw err
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
...(throttleMs != null ? { throttleMs } : {}),
|
|
301
|
+
...(idleMs != null ? { idleMs } : {}),
|
|
302
|
+
...(log != null ? { log } : {}),
|
|
303
|
+
...(warn != null ? { warn } : {}),
|
|
304
|
+
...(previewTransport != null ? { previewTransport } : {}),
|
|
305
|
+
...(isPrivateChat != null ? { isPrivateChat } : {}),
|
|
306
|
+
...(sendMessageDraft != null ? { sendMessageDraft } : {}),
|
|
307
|
+
...(initialMessageId != null ? { initialMessageId } : {}),
|
|
308
|
+
chatId,
|
|
309
|
+
},
|
|
310
|
+
)
|
|
311
|
+
}
|