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,664 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stream_reply` MCP tool handler — extracted from server.ts.
|
|
3
|
+
*
|
|
4
|
+
* The server.ts case block was ~110 lines of state-machine + I/O mixed
|
|
5
|
+
* together. This module pulls the logic into a pure-ish function with
|
|
6
|
+
* injected deps so it can be exercised by an integration test against
|
|
7
|
+
* the mock bot harness.
|
|
8
|
+
*
|
|
9
|
+
* Contract:
|
|
10
|
+
* - First call for a chat+thread: creates a stream via
|
|
11
|
+
* createStreamController, optionally prepending a handoff prefix.
|
|
12
|
+
* - Subsequent calls: reuse the existing stream, push the new text.
|
|
13
|
+
* - `done=true`: finalize, delete the map entry, fire status-reaction
|
|
14
|
+
* completion, and (if history enabled) record the final message.
|
|
15
|
+
* - Returns the message id + terminal status text that server.ts
|
|
16
|
+
* wraps into an MCP content response.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { DraftStreamHandle, StreamDraftFn } from './draft-stream.js'
|
|
20
|
+
import {
|
|
21
|
+
createStreamController,
|
|
22
|
+
type StreamBotApi,
|
|
23
|
+
type RetryPolicy,
|
|
24
|
+
} from './stream-controller.js'
|
|
25
|
+
import { sanitizeTelegramHtml } from './html-sanitize.js'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Builds the inline status-accent header line for `reply` / `stream_reply`.
|
|
29
|
+
*
|
|
30
|
+
* Returns a string to prepend to the effective (already-rendered) message
|
|
31
|
+
* body, including a trailing blank line so the header is visually separated.
|
|
32
|
+
* Returns an empty string when accent is undefined or unrecognised (silent
|
|
33
|
+
* ignore) so calls without `accent` produce identical output to today.
|
|
34
|
+
*
|
|
35
|
+
* The header uses Telegram HTML tags. Callers must ensure parseMode is HTML
|
|
36
|
+
* (or that the body has already been rendered to HTML) before prepending.
|
|
37
|
+
*/
|
|
38
|
+
export function buildAccentHeader(accent: string | undefined): string {
|
|
39
|
+
switch (accent) {
|
|
40
|
+
case 'in-progress':
|
|
41
|
+
return '🔵 <i>In progress…</i>\n\n'
|
|
42
|
+
case 'done':
|
|
43
|
+
return '✅ <b>Done</b>\n\n'
|
|
44
|
+
case 'issue':
|
|
45
|
+
return '⚠️ <b>Issue</b>\n\n'
|
|
46
|
+
default:
|
|
47
|
+
return ''
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StreamReplyArgs {
|
|
52
|
+
chat_id: string
|
|
53
|
+
text: string
|
|
54
|
+
done?: boolean
|
|
55
|
+
message_thread_id?: string
|
|
56
|
+
format?: string
|
|
57
|
+
/**
|
|
58
|
+
* Optional named lane. Each lane gets its own Telegram message per
|
|
59
|
+
* chat+thread — useful for surfacing "thinking" alongside the main
|
|
60
|
+
* "answer" stream. Lane names are caller-defined. Omit for the
|
|
61
|
+
* default (unnamed) lane, which preserves legacy behavior.
|
|
62
|
+
*/
|
|
63
|
+
lane?: string
|
|
64
|
+
/**
|
|
65
|
+
* Explicit quote-reply target. When set, the initial streamed message
|
|
66
|
+
* quote-threads under this message_id. Overrides the default auto-quote
|
|
67
|
+
* behavior and ignores `quote`.
|
|
68
|
+
*/
|
|
69
|
+
reply_to?: string
|
|
70
|
+
/**
|
|
71
|
+
* Opt out of the default quote-reply behavior. The handler's default
|
|
72
|
+
* (when `reply_to` is unset) is to look up the latest inbound user
|
|
73
|
+
* message via `getLatestInboundMessageId` and quote-reply to it. Pass
|
|
74
|
+
* `false` to send a bare (non-quoted) streamed message.
|
|
75
|
+
*
|
|
76
|
+
* The default is `undefined` (treated as true) so callers that pre-date
|
|
77
|
+
* this feature keep working. Only the progress-card / activity-lane
|
|
78
|
+
* internal callers routinely opt out, since those aren't user-visible
|
|
79
|
+
* conversation replies.
|
|
80
|
+
*/
|
|
81
|
+
quote?: boolean
|
|
82
|
+
/**
|
|
83
|
+
* Optional turn identifier used to multiplex concurrent turns on the
|
|
84
|
+
* same chat+thread+lane. Without this, two concurrent turns emitting
|
|
85
|
+
* on the same lane (e.g. both on lane:'progress') collapse into a
|
|
86
|
+
* single draft stream and their Telegram messages flap between each
|
|
87
|
+
* other. The progress-card driver passes its unique per-turn key here
|
|
88
|
+
* so each active turn gets its own draft stream + pinned card. Other
|
|
89
|
+
* lane callers may leave this undefined to preserve legacy behavior.
|
|
90
|
+
*/
|
|
91
|
+
turnKey?: string
|
|
92
|
+
/**
|
|
93
|
+
* Inline keyboard markup. When set, every send and edit for this stream
|
|
94
|
+
* includes it so Telegram doesn't strip an attached keyboard on text
|
|
95
|
+
* updates. Used by the progress-card driver to persist the Steer button.
|
|
96
|
+
*/
|
|
97
|
+
reply_markup?: unknown
|
|
98
|
+
/**
|
|
99
|
+
* When true, Telegram prevents the message from being forwarded or saved.
|
|
100
|
+
* Applied on the initial send only (editMessageText ignores it).
|
|
101
|
+
*/
|
|
102
|
+
protect_content?: boolean
|
|
103
|
+
/**
|
|
104
|
+
* Optional surgical quote text. When set along with `reply_to`, the initial
|
|
105
|
+
* send includes `reply_parameters: { message_id, quote: { text, position: 0 } }`
|
|
106
|
+
* so Telegram highlights the specific quoted sentence rather than the whole
|
|
107
|
+
* referenced message. Ignored when `reply_to` is absent.
|
|
108
|
+
*/
|
|
109
|
+
quote_text?: string
|
|
110
|
+
/**
|
|
111
|
+
* Optional status accent prepended as a leading header line (issue #320
|
|
112
|
+
* fallback for missing Telegram quote-bar color API).
|
|
113
|
+
*
|
|
114
|
+
* - `'in-progress'` → `🔵 <i>In progress…</i>\n\n`
|
|
115
|
+
* - `'done'` → `✅ <b>Done</b>\n\n`
|
|
116
|
+
* - `'issue'` → `⚠️ <b>Issue</b>\n\n`
|
|
117
|
+
*
|
|
118
|
+
* Unrecognised values are silently ignored. Omit for plain reply (default
|
|
119
|
+
* behavior — identical to today's output).
|
|
120
|
+
*/
|
|
121
|
+
accent?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface StreamReplyState {
|
|
125
|
+
activeDraftStreams: Map<string, DraftStreamHandle>
|
|
126
|
+
/**
|
|
127
|
+
* Tracks the parseMode each active stream was created with, keyed the
|
|
128
|
+
* same way as `activeDraftStreams`. Used by `handleStreamReply` to
|
|
129
|
+
* detect when a subsequent call's resolved parseMode differs from the
|
|
130
|
+
* one baked into the existing stream controller — in that case the
|
|
131
|
+
* stale stream is finalized + discarded and a fresh one is created
|
|
132
|
+
* with the new parseMode (see bug 1: PTY-tail creates an activity-lane
|
|
133
|
+
* stream with parseMode=undefined; a later explicit stream_reply on
|
|
134
|
+
* the same key with format:'html' would otherwise inherit undefined
|
|
135
|
+
* and send literal markdown).
|
|
136
|
+
*
|
|
137
|
+
* Optional for backwards compatibility with external callers that
|
|
138
|
+
* construct a StreamReplyState without it.
|
|
139
|
+
*/
|
|
140
|
+
activeDraftParseModes?: Map<string, 'HTML' | 'MarkdownV2' | undefined>
|
|
141
|
+
/**
|
|
142
|
+
* Chats whose PTY preview is claimed by an in-flight reply/stream_reply
|
|
143
|
+
* handler. PTY-tail partials for these keys are dropped to avoid
|
|
144
|
+
* duplicate messages. Historically only the `reply` tool added to this
|
|
145
|
+
* set; `stream_reply` did not, so a PTY partial firing after a
|
|
146
|
+
* finalized stream would create a duplicate message with the raw TUI
|
|
147
|
+
* text (see regression in telegram-plugin.log where msg 559 was
|
|
148
|
+
* followed by a duplicate msg 560 via path=pty_preview). stream_reply
|
|
149
|
+
* now claims the slot on the first call so later PTY partials are
|
|
150
|
+
* suppressed for the rest of the turn.
|
|
151
|
+
*
|
|
152
|
+
* Optional for backwards compatibility with callers that don't yet
|
|
153
|
+
* thread this state through — without it the bug reopens silently.
|
|
154
|
+
*/
|
|
155
|
+
suppressPtyPreview?: Set<string>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface StreamReplyDeps {
|
|
159
|
+
bot: { api: StreamBotApi }
|
|
160
|
+
retry?: RetryPolicy
|
|
161
|
+
/** Markdown → HTML renderer (used when format === 'html'). */
|
|
162
|
+
markdownToHtml: (text: string) => string
|
|
163
|
+
/** MarkdownV2 escaper (used when format === 'markdownv2'). */
|
|
164
|
+
escapeMarkdownV2: (text: string) => string
|
|
165
|
+
/** Whitespace repair applied to the raw caller text. */
|
|
166
|
+
repairEscapedWhitespace: (text: string) => string
|
|
167
|
+
/** Resolves the handoff prefix for a first-chunk stream. Empty string if none. */
|
|
168
|
+
takeHandoffPrefix: (format: 'html' | 'markdownv2' | 'text') => string
|
|
169
|
+
/** Validates the chat id against the access list. Throws on deny. */
|
|
170
|
+
assertAllowedChat: (chatId: string) => void
|
|
171
|
+
/** Resolves the effective thread id (explicit, last-inbound, or undefined). */
|
|
172
|
+
resolveThreadId: (chatId: string, explicit?: string) => number | undefined
|
|
173
|
+
/**
|
|
174
|
+
* Resolves the default quote-reply target: the message_id of the latest
|
|
175
|
+
* inbound user message in this chat+thread, or null if none (empty
|
|
176
|
+
* history, or history disabled). Called only when the caller didn't
|
|
177
|
+
* pass `reply_to` and didn't opt out via `quote:false`. Optional —
|
|
178
|
+
* omit to disable the auto-quote default (legacy behavior).
|
|
179
|
+
*/
|
|
180
|
+
getLatestInboundMessageId?: (chatId: string, threadId: number | null) => number | null
|
|
181
|
+
/** Config: disable link previews. Default true. */
|
|
182
|
+
disableLinkPreview: boolean
|
|
183
|
+
/** Config: fallback parse mode when args.format is omitted ('html' | 'markdownv2' | 'text'). */
|
|
184
|
+
defaultFormat: string
|
|
185
|
+
/** Observability: per-call event. */
|
|
186
|
+
logStreamingEvent: (ev: {
|
|
187
|
+
kind: 'stream_reply_called'
|
|
188
|
+
chatId: string
|
|
189
|
+
charCount: number
|
|
190
|
+
done: boolean
|
|
191
|
+
streamExisted: boolean
|
|
192
|
+
} | {
|
|
193
|
+
kind: 'draft_send'
|
|
194
|
+
chatId: string
|
|
195
|
+
messageId: number
|
|
196
|
+
charCount: number
|
|
197
|
+
} | {
|
|
198
|
+
kind: 'draft_edit'
|
|
199
|
+
chatId: string
|
|
200
|
+
messageId: number
|
|
201
|
+
charCount: number
|
|
202
|
+
sameAsLast: boolean
|
|
203
|
+
}) => void
|
|
204
|
+
/** Called on done=true to transition the status reaction controller. */
|
|
205
|
+
endStatusReaction: (chatId: string, threadId: number | undefined, verdict: 'done') => void
|
|
206
|
+
/**
|
|
207
|
+
* Optional: progress-card driver completion hook. Wired by the gateway
|
|
208
|
+
* to `progressDriver.forceCompleteTurn(...)`. Invoked after a
|
|
209
|
+
* `stream_reply(done=true)` on the default (unnamed) lane finalizes,
|
|
210
|
+
* so the final-answer delivery acts as an authoritative turn-complete
|
|
211
|
+
* signal equal to session-tail `turn_end`. Skipped when args.lane is
|
|
212
|
+
* 'progress' (that's the driver's own emit — calling this would cause
|
|
213
|
+
* re-entry). Safe to leave unset for callers that don't use the driver.
|
|
214
|
+
*/
|
|
215
|
+
forceCompleteTurn?: (chatId: string, threadId: number | undefined) => void
|
|
216
|
+
/**
|
|
217
|
+
* Optional: progress-card driver delivery counter hook. Wired by the
|
|
218
|
+
* gateway to `progressDriver.recordOutboundDelivered(...)`. Called
|
|
219
|
+
* BEFORE `forceCompleteTurn` so the driver's per-turn outbound counter
|
|
220
|
+
* is non-zero when the terminal render fires. Without this ordering
|
|
221
|
+
* guarantee, `forceCompleteTurn` flushes the card while
|
|
222
|
+
* `outboundDeliveredCount === 0` → ⚠️ false positive (issue #310).
|
|
223
|
+
* Only called on the default (unnamed) lane when `done=true` and the
|
|
224
|
+
* stream produced a non-null messageId. Safe to leave unset for callers
|
|
225
|
+
* that don't use the driver.
|
|
226
|
+
*/
|
|
227
|
+
recordOutboundDelivered?: (chatId: string, threadId: number | undefined) => void
|
|
228
|
+
/** Whether to persist outbound history. */
|
|
229
|
+
historyEnabled: boolean
|
|
230
|
+
/** History row writer. Only called when historyEnabled && done && messageId != null. */
|
|
231
|
+
recordOutbound: (row: {
|
|
232
|
+
chat_id: string
|
|
233
|
+
thread_id: number | null
|
|
234
|
+
message_ids: number[]
|
|
235
|
+
texts: string[]
|
|
236
|
+
}) => void
|
|
237
|
+
/** Error-path stderr. */
|
|
238
|
+
writeError: (line: string) => void
|
|
239
|
+
throttleMs?: number
|
|
240
|
+
/**
|
|
241
|
+
* sendMessageDraft callback. When provided, stream_reply uses the draft
|
|
242
|
+
* API for intermediate updates (DM transport). On done=true, a real
|
|
243
|
+
* sendMessage fires for push notification, then the draft is cleared.
|
|
244
|
+
* Optional — omit to keep the existing sendMessage/editMessageText path.
|
|
245
|
+
*/
|
|
246
|
+
sendMessageDraft?: StreamDraftFn
|
|
247
|
+
/**
|
|
248
|
+
* Idempotency hook for the duplicate-message class (issue #626).
|
|
249
|
+
*
|
|
250
|
+
* On every call where the handler would CREATE a new stream (no entry
|
|
251
|
+
* in `state.activeDraftStreams[sKey]`), this callback is consulted to
|
|
252
|
+
* see whether an external authority already knows the anchor message
|
|
253
|
+
* id for this lane+turn. If it returns a number, the new stream is
|
|
254
|
+
* initialized as if a previous send had landed with that id — the
|
|
255
|
+
* very next update fires `editMessageText` instead of `sendMessage`.
|
|
256
|
+
*
|
|
257
|
+
* Wired by the gateway to `pinMgr.pinnedMessageId(turnKey, agentId)`
|
|
258
|
+
* for the progress card. Without this hook, a `done=true` finalize
|
|
259
|
+
* deletes `activeDraftStreams[sKey]`, and the next emit on the same
|
|
260
|
+
* turn creates a fresh sendMessage — visible to the user as a second
|
|
261
|
+
* "status message" landing instead of an edit. The not-found
|
|
262
|
+
* fallback in draft-stream gracefully handles a stale id.
|
|
263
|
+
*
|
|
264
|
+
* Safe to omit. Optional. Returns null/undefined to fall through to
|
|
265
|
+
* the standard "first call sends" behavior.
|
|
266
|
+
*/
|
|
267
|
+
lookupExistingMessageId?: (key: {
|
|
268
|
+
chatId: string
|
|
269
|
+
threadId: number | undefined
|
|
270
|
+
lane: string | undefined
|
|
271
|
+
turnKey: string | undefined
|
|
272
|
+
}) => number | null | undefined
|
|
273
|
+
/**
|
|
274
|
+
* True when the current chat is a private DM. Passed to the stream
|
|
275
|
+
* controller so "auto" transport activates draft in DMs only.
|
|
276
|
+
*/
|
|
277
|
+
isPrivateChat?: boolean
|
|
278
|
+
/**
|
|
279
|
+
* True when the current chat is a forum topic. Forum topics do not
|
|
280
|
+
* support sendMessageDraft — this forces message transport.
|
|
281
|
+
*/
|
|
282
|
+
isForumTopic?: boolean
|
|
283
|
+
/**
|
|
284
|
+
* When true, the progress-card driver is emitting a live checklist on
|
|
285
|
+
* the `progress` lane and owns mid-turn display. In that mode, a
|
|
286
|
+
* caller-initiated `stream_reply` on the default (unnamed) lane with
|
|
287
|
+
* `done=false` is suppressed — the card already shows what's happening,
|
|
288
|
+
* and a parallel default-lane message is visible noise (a duplicate
|
|
289
|
+
* surface for the same turn). The final `done=true` call still posts
|
|
290
|
+
* as the answer message.
|
|
291
|
+
*
|
|
292
|
+
* Named-lane calls (lane: 'progress', 'thinking', etc.) are always
|
|
293
|
+
* honored — this flag only gates the default lane. Omit or leave false
|
|
294
|
+
* to preserve legacy behavior.
|
|
295
|
+
*/
|
|
296
|
+
progressCardActive?: boolean
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export interface StreamReplyResult {
|
|
300
|
+
messageId: number | null
|
|
301
|
+
status: 'updated' | 'finalized'
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function streamKey(
|
|
305
|
+
chatId: string,
|
|
306
|
+
threadId?: number,
|
|
307
|
+
lane?: string,
|
|
308
|
+
turnKey?: string,
|
|
309
|
+
): string {
|
|
310
|
+
const base = `${chatId}:${threadId ?? '_'}`
|
|
311
|
+
const withLane = lane != null && lane.length > 0 ? `${base}:${lane}` : base
|
|
312
|
+
return turnKey != null && turnKey.length > 0 ? `${withLane}:${turnKey}` : withLane
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export async function handleStreamReply(
|
|
316
|
+
args: StreamReplyArgs,
|
|
317
|
+
state: StreamReplyState,
|
|
318
|
+
deps: StreamReplyDeps,
|
|
319
|
+
): Promise<StreamReplyResult> {
|
|
320
|
+
const chat_id = args.chat_id
|
|
321
|
+
const rawText = deps.repairEscapedWhitespace(args.text)
|
|
322
|
+
const done = Boolean(args.done)
|
|
323
|
+
const format = args.format ?? deps.defaultFormat
|
|
324
|
+
if (done) {
|
|
325
|
+
process.stderr.write(`telegram channel: stream_reply: invoked done=true chatId=${chat_id} lane=${args.lane ?? 'default'} charCount=${rawText.length}\n`)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Access check runs BEFORE any other branch: a denied chat id must
|
|
329
|
+
// throw regardless of streaming mode. Previously the suppression path
|
|
330
|
+
// silently "succeeded" for unauthorized chats.
|
|
331
|
+
deps.assertAllowedChat(chat_id)
|
|
332
|
+
const threadId = deps.resolveThreadId(chat_id, args.message_thread_id)
|
|
333
|
+
|
|
334
|
+
// Note: caller-initiated stream_reply(done=false) used to be rejected
|
|
335
|
+
// when the progress card was active. The card and the answer message
|
|
336
|
+
// live on different lanes (progress vs default) and render different
|
|
337
|
+
// content (tool structure vs model prose), so they don't actually
|
|
338
|
+
// collide — the rejection forced single-shot final replies and broke
|
|
339
|
+
// the progressive-streaming contract documented in
|
|
340
|
+
// profiles/default/CLAUDE.md. See #481.
|
|
341
|
+
|
|
342
|
+
let parseMode: 'HTML' | 'MarkdownV2' | undefined
|
|
343
|
+
let effectiveText: string
|
|
344
|
+
if (format === 'html') {
|
|
345
|
+
parseMode = 'HTML'
|
|
346
|
+
// Pre-validate the rendered HTML against Telegram's tag allowlist
|
|
347
|
+
// before send. The sanitizer escapes unknown tags, drops disallowed
|
|
348
|
+
// attributes, and auto-closes unbalanced tags so we don't trip
|
|
349
|
+
// Telegram's `400 Bad Request: can't parse entities` (issue #657).
|
|
350
|
+
effectiveText = sanitizeTelegramHtml(deps.markdownToHtml(rawText))
|
|
351
|
+
} else if (format === 'markdownv2') {
|
|
352
|
+
parseMode = 'MarkdownV2'
|
|
353
|
+
effectiveText = deps.escapeMarkdownV2(rawText)
|
|
354
|
+
} else {
|
|
355
|
+
parseMode = undefined
|
|
356
|
+
effectiveText = rawText
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Inline status-accent header (issue #320 fallback). Prepended AFTER
|
|
360
|
+
// format rendering so it leads the fully-rendered body. Since
|
|
361
|
+
// stream_reply callers pass the full text snapshot each call, the
|
|
362
|
+
// header is prepended on every call that supplies `accent` — this
|
|
363
|
+
// keeps the rendered message consistent across edits. Callers that
|
|
364
|
+
// don't want the header on a subsequent edit simply omit `accent`.
|
|
365
|
+
// Unrecognised values are silently ignored (empty string returned).
|
|
366
|
+
if (args.accent != null) {
|
|
367
|
+
const accentHeader = buildAccentHeader(args.accent)
|
|
368
|
+
if (accentHeader.length > 0) {
|
|
369
|
+
effectiveText = accentHeader + effectiveText
|
|
370
|
+
// Re-sanitize after prepending the HTML header so the combined
|
|
371
|
+
// body is still guaranteed parse-mode=HTML safe.
|
|
372
|
+
if (parseMode === 'HTML') {
|
|
373
|
+
effectiveText = sanitizeTelegramHtml(effectiveText)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Over-limit pre-check. Throws BEFORE touching stream state so that
|
|
379
|
+
// (a) a first call over 4096 fails cleanly instead of creating a
|
|
380
|
+
// half-initialized stream, and (b) a mid-stream update over 4096
|
|
381
|
+
// fails loudly instead of setting the internal `stopped=true` flag
|
|
382
|
+
// and silently dropping all subsequent text. Either way the caller
|
|
383
|
+
// sees isError:true and can fall back to `reply`, which chunks.
|
|
384
|
+
// Check the rendered text (post-markdown-to-HTML) because that's
|
|
385
|
+
// what actually goes to Telegram's 4096-char wire limit.
|
|
386
|
+
if (effectiveText.length > 4096) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
`stream_reply rejected: text exceeds Telegram's 4096-char limit ` +
|
|
389
|
+
`(length=${effectiveText.length}, format=${format}). stream_reply does not ` +
|
|
390
|
+
`auto-chunk — split the text or use \`reply\`, which chunks.`,
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const sKey = streamKey(chat_id, threadId, args.lane, args.turnKey)
|
|
395
|
+
// Claim the PTY-preview slot so any PTY-tail partial that fires mid-
|
|
396
|
+
// or post-turn for this chat+thread is dropped. Keyed WITHOUT lane
|
|
397
|
+
// because the PTY handler uses the lane-less key and we need to
|
|
398
|
+
// suppress its default lane regardless of which lane stream_reply
|
|
399
|
+
// targets. Cleared on turn_end by server.ts.
|
|
400
|
+
state.suppressPtyPreview?.add(streamKey(chat_id, threadId))
|
|
401
|
+
let stream = state.activeDraftStreams.get(sKey)
|
|
402
|
+
|
|
403
|
+
// Bug 1 fix: parseMode is baked into the stream controller at creation
|
|
404
|
+
// time. If a prior call created the stream with a different parseMode
|
|
405
|
+
// (e.g. PTY-tail auto-stream using 'text' → undefined, followed by an
|
|
406
|
+
// explicit stream_reply with format:'html'), reusing it would send
|
|
407
|
+
// literal markdown. Finalize + discard the stale stream so the block
|
|
408
|
+
// below creates a fresh one with the correct parseMode.
|
|
409
|
+
if (stream != null && state.activeDraftParseModes != null) {
|
|
410
|
+
const existingParseMode = state.activeDraftParseModes.get(sKey)
|
|
411
|
+
if (existingParseMode !== parseMode) {
|
|
412
|
+
try {
|
|
413
|
+
await stream.finalize()
|
|
414
|
+
} catch (err) {
|
|
415
|
+
// Best-effort: the in-flight edit may 429 or race. Surface to
|
|
416
|
+
// stderr so the orphaned message id isn't invisible.
|
|
417
|
+
deps.writeError(
|
|
418
|
+
`telegram channel: stream_reply parseMode-rotation finalize failed: ${err}\n`,
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
state.activeDraftStreams.delete(sKey)
|
|
422
|
+
state.activeDraftParseModes.delete(sKey)
|
|
423
|
+
stream = undefined
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const streamExisted = stream != null
|
|
428
|
+
|
|
429
|
+
deps.logStreamingEvent({
|
|
430
|
+
kind: 'stream_reply_called',
|
|
431
|
+
chatId: chat_id,
|
|
432
|
+
charCount: effectiveText.length,
|
|
433
|
+
done,
|
|
434
|
+
streamExisted,
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
// First chunk of a session: consume any pending handoff prefix.
|
|
438
|
+
if (!stream) {
|
|
439
|
+
const prefix = deps.takeHandoffPrefix(
|
|
440
|
+
format === 'html' ? 'html' : format === 'markdownv2' ? 'markdownv2' : 'text',
|
|
441
|
+
)
|
|
442
|
+
if (prefix.length > 0) effectiveText = prefix + effectiveText
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (!stream) {
|
|
446
|
+
// Resolve the effective quote-reply target. Explicit `reply_to` wins;
|
|
447
|
+
// otherwise (unless the caller opted out with `quote:false`) fall back
|
|
448
|
+
// to the latest inbound user message in this chat+thread. Resolved
|
|
449
|
+
// only on stream creation — subsequent `stream_reply` calls for the
|
|
450
|
+
// same turn edit the existing message, which Telegram doesn't allow
|
|
451
|
+
// us to add a quote reference to retroactively.
|
|
452
|
+
let replyToMessageId: number | undefined
|
|
453
|
+
if (args.reply_to != null) {
|
|
454
|
+
replyToMessageId = Number(args.reply_to)
|
|
455
|
+
} else if (args.quote !== false && deps.getLatestInboundMessageId != null) {
|
|
456
|
+
try {
|
|
457
|
+
const latest = deps.getLatestInboundMessageId(chat_id, threadId ?? null)
|
|
458
|
+
if (latest != null) replyToMessageId = latest
|
|
459
|
+
} catch (err) {
|
|
460
|
+
deps.writeError(
|
|
461
|
+
`telegram channel: stream_reply quote-lookup failed: ${err}\n`,
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Resolve draft-transport options. Forum topics force message transport
|
|
467
|
+
// because sendMessageDraft does not support threads.
|
|
468
|
+
const isForumTopic = deps.isForumTopic === true
|
|
469
|
+
const resolvedTransport: 'auto' | 'message' | 'draft' =
|
|
470
|
+
isForumTopic || deps.sendMessageDraft == null
|
|
471
|
+
? 'message'
|
|
472
|
+
: 'auto'
|
|
473
|
+
|
|
474
|
+
// Idempotency hook (#626): if an external authority (e.g. the
|
|
475
|
+
// gateway's pin manager) already knows the anchor message id for
|
|
476
|
+
// this lane+turn, initialize the stream with it so the next update
|
|
477
|
+
// edits in place rather than creating a fresh sendMessage. This
|
|
478
|
+
// closes the "done=true → activeDraftStreams entry deleted → next
|
|
479
|
+
// emit creates fresh sendMessage" path that produced multiple
|
|
480
|
+
// status messages per turn.
|
|
481
|
+
let initialMessageId: number | undefined
|
|
482
|
+
if (deps.lookupExistingMessageId != null) {
|
|
483
|
+
try {
|
|
484
|
+
const looked = deps.lookupExistingMessageId({
|
|
485
|
+
chatId: chat_id,
|
|
486
|
+
threadId,
|
|
487
|
+
lane: args.lane,
|
|
488
|
+
turnKey: args.turnKey,
|
|
489
|
+
})
|
|
490
|
+
if (typeof looked === 'number' && Number.isFinite(looked)) {
|
|
491
|
+
initialMessageId = looked
|
|
492
|
+
}
|
|
493
|
+
} catch (err) {
|
|
494
|
+
deps.writeError(
|
|
495
|
+
`telegram channel: stream_reply lookupExistingMessageId failed: ${err}\n`,
|
|
496
|
+
)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
stream = createStreamController({
|
|
501
|
+
bot: deps.bot,
|
|
502
|
+
chatId: chat_id,
|
|
503
|
+
threadId,
|
|
504
|
+
parseMode,
|
|
505
|
+
disableLinkPreview: deps.disableLinkPreview,
|
|
506
|
+
throttleMs: deps.throttleMs ?? 600,
|
|
507
|
+
retry: deps.retry,
|
|
508
|
+
...(replyToMessageId != null ? { replyToMessageId } : {}),
|
|
509
|
+
...(args.quote_text != null && replyToMessageId != null ? { quoteText: args.quote_text } : {}),
|
|
510
|
+
...(args.protect_content === true ? { protectContent: true } : {}),
|
|
511
|
+
...(args.reply_markup != null ? { replyMarkup: args.reply_markup } : {}),
|
|
512
|
+
previewTransport: resolvedTransport,
|
|
513
|
+
isPrivateChat: deps.isPrivateChat === true,
|
|
514
|
+
...(deps.sendMessageDraft != null ? { sendMessageDraft: deps.sendMessageDraft } : {}),
|
|
515
|
+
...(initialMessageId != null ? { initialMessageId } : {}),
|
|
516
|
+
onSend: (messageId, charCount) =>
|
|
517
|
+
deps.logStreamingEvent({ kind: 'draft_send', chatId: chat_id, messageId, charCount }),
|
|
518
|
+
onEdit: (messageId, charCount) =>
|
|
519
|
+
deps.logStreamingEvent({
|
|
520
|
+
kind: 'draft_edit',
|
|
521
|
+
chatId: chat_id,
|
|
522
|
+
messageId,
|
|
523
|
+
charCount,
|
|
524
|
+
sameAsLast: false,
|
|
525
|
+
}),
|
|
526
|
+
// Route draft-stream diagnostics through the handler's stderr
|
|
527
|
+
// writer so transient failures are observable. Filter routine
|
|
528
|
+
// success chatter (sent/edited/finalized) — those are already
|
|
529
|
+
// captured by the structured onSend/onEdit observers — and only
|
|
530
|
+
// surface warnings/errors (stopped, edit failed, not-found
|
|
531
|
+
// recovery).
|
|
532
|
+
log: (msg) => {
|
|
533
|
+
if (
|
|
534
|
+
msg.startsWith('stream → sent')
|
|
535
|
+
|| msg.startsWith('stream → edited')
|
|
536
|
+
|| msg.startsWith('stream → not modified')
|
|
537
|
+
|| msg.startsWith('stream finalized')
|
|
538
|
+
|| msg.startsWith('stream → draft')
|
|
539
|
+
|| msg.startsWith('stream → materialized')
|
|
540
|
+
) return
|
|
541
|
+
deps.writeError(`telegram channel: stream_reply ${msg}\n`)
|
|
542
|
+
},
|
|
543
|
+
warn: (msg) => {
|
|
544
|
+
deps.writeError(`telegram channel: stream_reply ${msg}\n`)
|
|
545
|
+
},
|
|
546
|
+
})
|
|
547
|
+
state.activeDraftStreams.set(sKey, stream)
|
|
548
|
+
state.activeDraftParseModes?.set(sKey, parseMode)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
await stream.update(effectiveText)
|
|
552
|
+
|
|
553
|
+
if (done) {
|
|
554
|
+
await stream.finalize()
|
|
555
|
+
state.activeDraftStreams.delete(sKey)
|
|
556
|
+
state.activeDraftParseModes?.delete(sKey)
|
|
557
|
+
// Bug Z fix: fire the terminal 👍 here, gated to the default
|
|
558
|
+
// (unnamed) lane and only after finalize() resolves so the emoji is
|
|
559
|
+
// tied to the final edit actually landing in Telegram.
|
|
560
|
+
//
|
|
561
|
+
// History (see prior comment removed in this commit): we previously
|
|
562
|
+
// deferred the 👍 to turn_end on the theory that a turn might call
|
|
563
|
+
// stream_reply(done=true) mid-flight and continue working. In
|
|
564
|
+
// practice the dedup-suppress branch in turn_end was firing setDone
|
|
565
|
+
// off a 500ms-lagged read of local history rather than from a real
|
|
566
|
+
// delivery confirmation, and the disconnect-flush path was leaving
|
|
567
|
+
// 👍 firing on disconnect even when the final edit had failed.
|
|
568
|
+
// Wiring endStatusReaction at the post-finalize callback keeps the
|
|
569
|
+
// emoji honest — it now means "the final draft edit hit Telegram".
|
|
570
|
+
//
|
|
571
|
+
// Gated to the default lane: named lanes (lane:'progress',
|
|
572
|
+
// lane:'thinking', lane:'activity', etc.) are internal driver
|
|
573
|
+
// emits, not user-visible answers — they must not be allowed to
|
|
574
|
+
// claim turn-completion. A lane:'progress' emit firing setDone
|
|
575
|
+
// would race the actual answer.
|
|
576
|
+
//
|
|
577
|
+
// Mid-turn done=true tradeoff: a turn that calls
|
|
578
|
+
// stream_reply(done=true) on the default lane and then continues
|
|
579
|
+
// working will fire 👍 early; subsequent intermediate emojis become
|
|
580
|
+
// no-ops (setDone is terminal). This is the contract: done=true on
|
|
581
|
+
// the default lane is "the answer is delivered". Agents that need
|
|
582
|
+
// to continue should use additional `reply` calls or named lanes.
|
|
583
|
+
const isDefaultLaneForCompletion = args.lane == null || args.lane.length === 0
|
|
584
|
+
if (isDefaultLaneForCompletion && stream.getMessageId() != null) {
|
|
585
|
+
try {
|
|
586
|
+
deps.endStatusReaction(chat_id, threadId, 'done')
|
|
587
|
+
} catch (err) {
|
|
588
|
+
deps.writeError(`telegram channel: stream_reply: endStatusReaction hook threw: ${err}\n`)
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Hard-fail surface: if the stream finalized without ever assigning
|
|
593
|
+
// a message id, the initial send never landed (4096+ chars hits
|
|
594
|
+
// draft-stream's length guard and silently stops). Throw so the MCP
|
|
595
|
+
// caller sees isError:true instead of a misleading "finalized
|
|
596
|
+
// (id: pending)". The caller can fall back to `reply`, which chunks.
|
|
597
|
+
if (stream.getMessageId() == null) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
`stream_reply finalized without sending any message (length=${rawText.length}, ` +
|
|
600
|
+
`max=4096). Telegram's per-message limit is 4096 chars and stream_reply does not ` +
|
|
601
|
+
`auto-chunk. Split the text or use \`reply\` (which chunks).`,
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (deps.historyEnabled) {
|
|
606
|
+
const finalId = stream.getMessageId()
|
|
607
|
+
if (finalId != null) {
|
|
608
|
+
try {
|
|
609
|
+
deps.recordOutbound({
|
|
610
|
+
chat_id,
|
|
611
|
+
thread_id: threadId ?? null,
|
|
612
|
+
message_ids: [finalId],
|
|
613
|
+
texts: [rawText],
|
|
614
|
+
})
|
|
615
|
+
} catch (err) {
|
|
616
|
+
deps.writeError(
|
|
617
|
+
`telegram channel: history recordOutbound (stream_reply) failed: ${err}\n`,
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const finalMessageId = stream.getMessageId()
|
|
625
|
+
if (done) {
|
|
626
|
+
process.stderr.write(`telegram channel: stream_reply: finalized done=true chatId=${chat_id} lane=${args.lane ?? 'default'} messageId=${finalMessageId ?? 'null'}\n`)
|
|
627
|
+
const isDefaultLaneForCompletion = args.lane == null || args.lane.length === 0
|
|
628
|
+
// Issue #310: record delivery BEFORE forceCompleteTurn so the
|
|
629
|
+
// progress-card driver's outboundDeliveredCount is non-zero when the
|
|
630
|
+
// terminal render fires. forceCompleteTurn synchronously flushes the
|
|
631
|
+
// card; if we recorded after that flush, outboundDeliveredCount would
|
|
632
|
+
// still be 0 at render time → ⚠️ false positive even though the
|
|
633
|
+
// message landed. Only record when a messageId is confirmed — a null
|
|
634
|
+
// id means the send never succeeded and the ⚠️ branch is correct.
|
|
635
|
+
if (
|
|
636
|
+
deps.recordOutboundDelivered != null
|
|
637
|
+
&& finalMessageId != null
|
|
638
|
+
&& isDefaultLaneForCompletion
|
|
639
|
+
) {
|
|
640
|
+
try {
|
|
641
|
+
deps.recordOutboundDelivered(chat_id, threadId)
|
|
642
|
+
} catch (err) {
|
|
643
|
+
deps.writeError(`telegram channel: stream_reply: recordOutboundDelivered hook threw: ${err}\n`)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
// Fire the authoritative turn-complete signal to the progress-card
|
|
647
|
+
// driver so any in-flight card for this chat is closed out alongside
|
|
648
|
+
// the final answer landing. Only for the default (unnamed) lane —
|
|
649
|
+
// the progress lane is the driver's own emit path and routing
|
|
650
|
+
// completion through it would re-enter the driver from within its
|
|
651
|
+
// own flush. Idempotent on the driver side: first caller wins.
|
|
652
|
+
if (deps.forceCompleteTurn != null && isDefaultLaneForCompletion) {
|
|
653
|
+
try {
|
|
654
|
+
deps.forceCompleteTurn(chat_id, threadId)
|
|
655
|
+
} catch (err) {
|
|
656
|
+
deps.writeError(`telegram channel: stream_reply: forceCompleteTurn hook threw: ${err}\n`)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
messageId: finalMessageId,
|
|
662
|
+
status: done ? 'finalized' : 'updated',
|
|
663
|
+
}
|
|
664
|
+
}
|