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,752 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { createDraftStream } from '../draft-stream.js'
|
|
3
|
+
import { __resetDraftIdForTests } from '../draft-transport.js'
|
|
4
|
+
|
|
5
|
+
interface MockTelegram {
|
|
6
|
+
send: (text: string) => Promise<number>
|
|
7
|
+
edit: (id: number, text: string) => Promise<void>
|
|
8
|
+
sendCalls: Array<{ text: string; t: number }>
|
|
9
|
+
editCalls: Array<{ id: number; text: string; t: number }>
|
|
10
|
+
nextId: number
|
|
11
|
+
failNext: 'never' | 'send' | 'edit' | 'notModified'
|
|
12
|
+
startTime: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function makeMock(): MockTelegram {
|
|
16
|
+
const m: MockTelegram = {
|
|
17
|
+
send: async () => 0,
|
|
18
|
+
edit: async () => {},
|
|
19
|
+
sendCalls: [],
|
|
20
|
+
editCalls: [],
|
|
21
|
+
nextId: 100,
|
|
22
|
+
failNext: 'never',
|
|
23
|
+
startTime: Date.now(),
|
|
24
|
+
}
|
|
25
|
+
m.send = async (text: string) => {
|
|
26
|
+
if (m.failNext === 'send') {
|
|
27
|
+
m.failNext = 'never'
|
|
28
|
+
throw new Error('send failed')
|
|
29
|
+
}
|
|
30
|
+
const id = m.nextId++
|
|
31
|
+
m.sendCalls.push({ text, t: Date.now() - m.startTime })
|
|
32
|
+
return id
|
|
33
|
+
}
|
|
34
|
+
m.edit = async (id: number, text: string) => {
|
|
35
|
+
if (m.failNext === 'notModified') {
|
|
36
|
+
m.failNext = 'never'
|
|
37
|
+
throw new Error('Bad Request: message is not modified')
|
|
38
|
+
}
|
|
39
|
+
if (m.failNext === 'edit') {
|
|
40
|
+
m.failNext = 'never'
|
|
41
|
+
throw new Error('edit failed')
|
|
42
|
+
}
|
|
43
|
+
m.editCalls.push({ id, text, t: Date.now() - m.startTime })
|
|
44
|
+
}
|
|
45
|
+
return m
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function microtaskFlush(times = 8): Promise<void> {
|
|
49
|
+
for (let i = 0; i < times; i++) {
|
|
50
|
+
await Promise.resolve()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('createDraftStream', () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.useFakeTimers()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
vi.useRealTimers()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('first update calls send, captures the message id', async () => {
|
|
64
|
+
const m = makeMock()
|
|
65
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
66
|
+
|
|
67
|
+
void stream.update('Hello world')
|
|
68
|
+
await microtaskFlush()
|
|
69
|
+
|
|
70
|
+
expect(m.sendCalls.length).toBe(1)
|
|
71
|
+
expect(m.sendCalls[0].text).toBe('Hello world')
|
|
72
|
+
expect(m.editCalls.length).toBe(0)
|
|
73
|
+
expect(stream.getMessageId()).toBe(100)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('subsequent updates call edit on the same message id', async () => {
|
|
77
|
+
const m = makeMock()
|
|
78
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
79
|
+
|
|
80
|
+
void stream.update('Step 1')
|
|
81
|
+
await microtaskFlush()
|
|
82
|
+
|
|
83
|
+
// Need to wait for throttle window
|
|
84
|
+
vi.advanceTimersByTime(1000)
|
|
85
|
+
void stream.update('Step 1 → Step 2')
|
|
86
|
+
await microtaskFlush()
|
|
87
|
+
|
|
88
|
+
expect(m.sendCalls.length).toBe(1)
|
|
89
|
+
expect(m.editCalls.length).toBe(1)
|
|
90
|
+
expect(m.editCalls[0].id).toBe(100)
|
|
91
|
+
expect(m.editCalls[0].text).toBe('Step 1 → Step 2')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('rapid updates within throttle window collapse to the latest', async () => {
|
|
95
|
+
const m = makeMock()
|
|
96
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
97
|
+
|
|
98
|
+
void stream.update('initial')
|
|
99
|
+
await microtaskFlush()
|
|
100
|
+
expect(m.sendCalls.length).toBe(1)
|
|
101
|
+
|
|
102
|
+
// Three rapid updates within ~100ms
|
|
103
|
+
void stream.update('a')
|
|
104
|
+
void stream.update('b')
|
|
105
|
+
void stream.update('c')
|
|
106
|
+
await microtaskFlush()
|
|
107
|
+
|
|
108
|
+
// Throttle window not yet open
|
|
109
|
+
expect(m.editCalls.length).toBe(0)
|
|
110
|
+
|
|
111
|
+
// Open the window
|
|
112
|
+
vi.advanceTimersByTime(1000)
|
|
113
|
+
await microtaskFlush()
|
|
114
|
+
|
|
115
|
+
// Only the latest text lands
|
|
116
|
+
expect(m.editCalls.length).toBe(1)
|
|
117
|
+
expect(m.editCalls[0].text).toBe('c')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('finalize flushes pending text immediately', async () => {
|
|
121
|
+
const m = makeMock()
|
|
122
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
123
|
+
|
|
124
|
+
void stream.update('initial')
|
|
125
|
+
await microtaskFlush()
|
|
126
|
+
expect(m.sendCalls.length).toBe(1)
|
|
127
|
+
|
|
128
|
+
// Update during throttle window — would normally wait
|
|
129
|
+
void stream.update('final answer')
|
|
130
|
+
await microtaskFlush()
|
|
131
|
+
expect(m.editCalls.length).toBe(0)
|
|
132
|
+
|
|
133
|
+
// finalize() should bypass the wait and edit immediately
|
|
134
|
+
await stream.finalize()
|
|
135
|
+
|
|
136
|
+
expect(m.editCalls.length).toBe(1)
|
|
137
|
+
expect(m.editCalls[0].text).toBe('final answer')
|
|
138
|
+
expect(stream.isFinal()).toBe(true)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('updates after finalize are silently dropped', async () => {
|
|
142
|
+
const m = makeMock()
|
|
143
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
144
|
+
|
|
145
|
+
void stream.update('initial')
|
|
146
|
+
await microtaskFlush()
|
|
147
|
+
await stream.finalize()
|
|
148
|
+
|
|
149
|
+
void stream.update('too late')
|
|
150
|
+
vi.advanceTimersByTime(5000)
|
|
151
|
+
await microtaskFlush()
|
|
152
|
+
|
|
153
|
+
expect(m.editCalls.length).toBe(0)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('treats "message is not modified" as success', async () => {
|
|
157
|
+
const m = makeMock()
|
|
158
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
159
|
+
|
|
160
|
+
void stream.update('first')
|
|
161
|
+
await microtaskFlush()
|
|
162
|
+
expect(m.sendCalls.length).toBe(1)
|
|
163
|
+
|
|
164
|
+
// Force the next edit to throw "not modified"
|
|
165
|
+
m.failNext = 'notModified'
|
|
166
|
+
vi.advanceTimersByTime(1000)
|
|
167
|
+
void stream.update('second')
|
|
168
|
+
await microtaskFlush()
|
|
169
|
+
|
|
170
|
+
// The edit attempt happened (and threw, then we caught it)
|
|
171
|
+
// No exception bubbled out
|
|
172
|
+
expect(stream.isFinal()).toBe(false)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('skips edit when text is unchanged from last sent', async () => {
|
|
176
|
+
const m = makeMock()
|
|
177
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
178
|
+
|
|
179
|
+
void stream.update('hello')
|
|
180
|
+
await microtaskFlush()
|
|
181
|
+
expect(m.sendCalls.length).toBe(1)
|
|
182
|
+
|
|
183
|
+
vi.advanceTimersByTime(1000)
|
|
184
|
+
void stream.update('hello') // same text
|
|
185
|
+
await microtaskFlush()
|
|
186
|
+
|
|
187
|
+
expect(m.editCalls.length).toBe(0)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('hard-stops when text exceeds maxChars', async () => {
|
|
191
|
+
const m = makeMock()
|
|
192
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
193
|
+
throttleMs: 1000,
|
|
194
|
+
maxChars: 100,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
void stream.update('short')
|
|
198
|
+
await microtaskFlush()
|
|
199
|
+
expect(m.sendCalls.length).toBe(1)
|
|
200
|
+
|
|
201
|
+
vi.advanceTimersByTime(1000)
|
|
202
|
+
void stream.update('x'.repeat(200))
|
|
203
|
+
await microtaskFlush()
|
|
204
|
+
|
|
205
|
+
// The over-limit edit was suppressed
|
|
206
|
+
expect(m.editCalls.length).toBe(0)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('throttle window opens at lastSent + throttleMs', async () => {
|
|
210
|
+
const m = makeMock()
|
|
211
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
212
|
+
|
|
213
|
+
void stream.update('a')
|
|
214
|
+
await microtaskFlush()
|
|
215
|
+
const sendT = m.sendCalls[0].t
|
|
216
|
+
|
|
217
|
+
void stream.update('b')
|
|
218
|
+
await microtaskFlush()
|
|
219
|
+
expect(m.editCalls.length).toBe(0)
|
|
220
|
+
|
|
221
|
+
// Wait until just before the throttle window opens
|
|
222
|
+
vi.advanceTimersByTime(999)
|
|
223
|
+
await microtaskFlush()
|
|
224
|
+
expect(m.editCalls.length).toBe(0)
|
|
225
|
+
|
|
226
|
+
// Cross the boundary
|
|
227
|
+
vi.advanceTimersByTime(1)
|
|
228
|
+
await microtaskFlush()
|
|
229
|
+
expect(m.editCalls.length).toBe(1)
|
|
230
|
+
expect(m.editCalls[0].text).toBe('b')
|
|
231
|
+
// Edit should land at roughly sendT + throttleMs
|
|
232
|
+
expect(m.editCalls[0].t).toBeGreaterThanOrEqual(sendT + 1000)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('recovers from "message to edit not found" by re-sending', async () => {
|
|
236
|
+
const m = makeMock()
|
|
237
|
+
m.edit = async (_id: number, _text: string) => {
|
|
238
|
+
throw new Error('Bad Request: message to edit not found')
|
|
239
|
+
}
|
|
240
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
241
|
+
|
|
242
|
+
void stream.update('first')
|
|
243
|
+
await microtaskFlush()
|
|
244
|
+
expect(m.sendCalls.length).toBe(1)
|
|
245
|
+
expect(stream.getMessageId()).toBe(100)
|
|
246
|
+
|
|
247
|
+
vi.advanceTimersByTime(1000)
|
|
248
|
+
void stream.update('second')
|
|
249
|
+
await microtaskFlush()
|
|
250
|
+
// Edit fails → stream requeues for a fresh send on next iteration
|
|
251
|
+
await microtaskFlush()
|
|
252
|
+
|
|
253
|
+
// After recovery, the stream should have a NEW message id from the re-send
|
|
254
|
+
expect(m.sendCalls.length).toBe(2)
|
|
255
|
+
expect(m.sendCalls[1].text).toBe('second')
|
|
256
|
+
expect(stream.getMessageId()).toBe(101)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('also recognizes MESSAGE_ID_INVALID as a not-found signal', async () => {
|
|
260
|
+
const m = makeMock()
|
|
261
|
+
m.edit = async () => { throw new Error('MESSAGE_ID_INVALID') }
|
|
262
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
263
|
+
|
|
264
|
+
void stream.update('first')
|
|
265
|
+
await microtaskFlush()
|
|
266
|
+
vi.advanceTimersByTime(1000)
|
|
267
|
+
void stream.update('second')
|
|
268
|
+
await microtaskFlush()
|
|
269
|
+
await microtaskFlush()
|
|
270
|
+
|
|
271
|
+
expect(m.sendCalls.length).toBe(2)
|
|
272
|
+
expect(stream.getMessageId()).toBe(101)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('non-recoverable edit errors do NOT requeue (no infinite retry loop)', async () => {
|
|
276
|
+
const m = makeMock()
|
|
277
|
+
m.edit = async () => { throw new Error('Forbidden: bot was blocked by user') }
|
|
278
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
279
|
+
|
|
280
|
+
void stream.update('first')
|
|
281
|
+
await microtaskFlush()
|
|
282
|
+
expect(m.sendCalls.length).toBe(1)
|
|
283
|
+
|
|
284
|
+
vi.advanceTimersByTime(1000)
|
|
285
|
+
void stream.update('second')
|
|
286
|
+
await microtaskFlush()
|
|
287
|
+
|
|
288
|
+
// The generic error path doesn't re-send; messageId stays captured,
|
|
289
|
+
// and the next update can retry edit normally.
|
|
290
|
+
expect(m.sendCalls.length).toBe(1)
|
|
291
|
+
expect(stream.getMessageId()).toBe(100)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// ─── Regex-tightening regression (2026-04-13) ─────────────────────────
|
|
295
|
+
//
|
|
296
|
+
// The recoverable-edit-error regex previously used /not found/i and
|
|
297
|
+
// /not modified/i, which would also match Telegram errors like
|
|
298
|
+
// "chat not found" or "thread not found" — misclassifying them as
|
|
299
|
+
// "preview was deleted" and re-sending into a dead chat. These tests
|
|
300
|
+
// pin the tightened behavior: ONLY the two real Telegram strings
|
|
301
|
+
// ("message is not modified" / "message to edit not found") trigger
|
|
302
|
+
// the recovery path.
|
|
303
|
+
|
|
304
|
+
it('does NOT recover on "chat not found" — treated as generic failure', async () => {
|
|
305
|
+
const m = makeMock()
|
|
306
|
+
m.edit = async () => { throw new Error('Bad Request: chat not found') }
|
|
307
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
308
|
+
|
|
309
|
+
void stream.update('first')
|
|
310
|
+
await microtaskFlush()
|
|
311
|
+
expect(m.sendCalls.length).toBe(1)
|
|
312
|
+
|
|
313
|
+
vi.advanceTimersByTime(1000)
|
|
314
|
+
void stream.update('second')
|
|
315
|
+
await microtaskFlush()
|
|
316
|
+
await microtaskFlush()
|
|
317
|
+
|
|
318
|
+
// No re-send, messageId stays captured — this is the generic
|
|
319
|
+
// "don't retry forever" path.
|
|
320
|
+
expect(m.sendCalls.length).toBe(1)
|
|
321
|
+
expect(stream.getMessageId()).toBe(100)
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('does NOT recover on "thread not found" — treated as generic failure', async () => {
|
|
325
|
+
const m = makeMock()
|
|
326
|
+
m.edit = async () => { throw new Error('Bad Request: message thread not found') }
|
|
327
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
328
|
+
|
|
329
|
+
void stream.update('first')
|
|
330
|
+
await microtaskFlush()
|
|
331
|
+
vi.advanceTimersByTime(1000)
|
|
332
|
+
void stream.update('second')
|
|
333
|
+
await microtaskFlush()
|
|
334
|
+
await microtaskFlush()
|
|
335
|
+
|
|
336
|
+
expect(m.sendCalls.length).toBe(1)
|
|
337
|
+
expect(stream.getMessageId()).toBe(100)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('does NOT treat "user not modified" or similar as success', async () => {
|
|
341
|
+
const m = makeMock()
|
|
342
|
+
m.edit = async () => {
|
|
343
|
+
throw new Error('Bad Request: user not modified since last check')
|
|
344
|
+
}
|
|
345
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000 })
|
|
346
|
+
|
|
347
|
+
void stream.update('first')
|
|
348
|
+
await microtaskFlush()
|
|
349
|
+
vi.advanceTimersByTime(1000)
|
|
350
|
+
void stream.update('second')
|
|
351
|
+
await microtaskFlush()
|
|
352
|
+
await microtaskFlush()
|
|
353
|
+
|
|
354
|
+
// Generic error path: messageId stays, no re-send.
|
|
355
|
+
expect(m.sendCalls.length).toBe(1)
|
|
356
|
+
expect(stream.getMessageId()).toBe(100)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
// ─── Pre-send idle coalesce (idleMs) ──────────────────────────────────
|
|
360
|
+
|
|
361
|
+
it('idleMs=0 preserves legacy behavior (first update fires immediately)', async () => {
|
|
362
|
+
const m = makeMock()
|
|
363
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000, idleMs: 0 })
|
|
364
|
+
void stream.update('hi')
|
|
365
|
+
await microtaskFlush()
|
|
366
|
+
expect(m.sendCalls.length).toBe(1)
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('idleMs defers the first send, collapsing a burst into one API call', async () => {
|
|
370
|
+
const m = makeMock()
|
|
371
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000, idleMs: 200 })
|
|
372
|
+
|
|
373
|
+
// Rapid burst — none should fire yet
|
|
374
|
+
void stream.update('a')
|
|
375
|
+
void stream.update('ab')
|
|
376
|
+
void stream.update('abc')
|
|
377
|
+
await microtaskFlush()
|
|
378
|
+
expect(m.sendCalls.length).toBe(0)
|
|
379
|
+
|
|
380
|
+
// Advance past the idle window
|
|
381
|
+
vi.advanceTimersByTime(200)
|
|
382
|
+
await microtaskFlush()
|
|
383
|
+
|
|
384
|
+
// Exactly one send with the LATEST text
|
|
385
|
+
expect(m.sendCalls.length).toBe(1)
|
|
386
|
+
expect(m.sendCalls[0].text).toBe('abc')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('idleMs timer is reset on each subsequent update within the window', async () => {
|
|
390
|
+
const m = makeMock()
|
|
391
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000, idleMs: 200 })
|
|
392
|
+
|
|
393
|
+
void stream.update('a')
|
|
394
|
+
vi.advanceTimersByTime(150)
|
|
395
|
+
void stream.update('ab') // resets the 200ms clock
|
|
396
|
+
vi.advanceTimersByTime(150)
|
|
397
|
+
void stream.update('abc') // resets again
|
|
398
|
+
await microtaskFlush()
|
|
399
|
+
expect(m.sendCalls.length).toBe(0)
|
|
400
|
+
|
|
401
|
+
// Now let the clock run out
|
|
402
|
+
vi.advanceTimersByTime(200)
|
|
403
|
+
await microtaskFlush()
|
|
404
|
+
expect(m.sendCalls.length).toBe(1)
|
|
405
|
+
expect(m.sendCalls[0].text).toBe('abc')
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('idleMs only applies to the FIRST send; subsequent edits use throttleMs', async () => {
|
|
409
|
+
const m = makeMock()
|
|
410
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000, idleMs: 200 })
|
|
411
|
+
|
|
412
|
+
void stream.update('first')
|
|
413
|
+
vi.advanceTimersByTime(200)
|
|
414
|
+
await microtaskFlush()
|
|
415
|
+
expect(m.sendCalls.length).toBe(1)
|
|
416
|
+
expect(m.editCalls.length).toBe(0)
|
|
417
|
+
|
|
418
|
+
// A subsequent update should NOT wait for idleMs — it waits for
|
|
419
|
+
// throttleMs - (time since last send) instead.
|
|
420
|
+
vi.advanceTimersByTime(1000)
|
|
421
|
+
void stream.update('second')
|
|
422
|
+
await microtaskFlush()
|
|
423
|
+
expect(m.editCalls.length).toBe(1)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it('finalize() during idle wait flushes the pending text immediately', async () => {
|
|
427
|
+
const m = makeMock()
|
|
428
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 1000, idleMs: 500 })
|
|
429
|
+
|
|
430
|
+
void stream.update('x')
|
|
431
|
+
// Don't advance past idleMs — finalize while still waiting
|
|
432
|
+
await stream.finalize()
|
|
433
|
+
|
|
434
|
+
expect(m.sendCalls.length).toBe(1)
|
|
435
|
+
expect(m.sendCalls[0].text).toBe('x')
|
|
436
|
+
expect(stream.isFinal()).toBe(true)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('floors throttleMs at 250ms', async () => {
|
|
440
|
+
const m = makeMock()
|
|
441
|
+
const stream = createDraftStream(m.send, m.edit, { throttleMs: 50 })
|
|
442
|
+
|
|
443
|
+
void stream.update('a')
|
|
444
|
+
await microtaskFlush()
|
|
445
|
+
void stream.update('b')
|
|
446
|
+
await microtaskFlush()
|
|
447
|
+
expect(m.editCalls.length).toBe(0)
|
|
448
|
+
|
|
449
|
+
// 50ms is below the floor; the real wait should be 250ms
|
|
450
|
+
vi.advanceTimersByTime(50)
|
|
451
|
+
await microtaskFlush()
|
|
452
|
+
expect(m.editCalls.length).toBe(0)
|
|
453
|
+
|
|
454
|
+
vi.advanceTimersByTime(200)
|
|
455
|
+
await microtaskFlush()
|
|
456
|
+
expect(m.editCalls.length).toBe(1)
|
|
457
|
+
})
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
// ─── Draft transport (sendMessageDraft) ───────────────────────────────────
|
|
461
|
+
|
|
462
|
+
describe('createDraftStream — draft transport', () => {
|
|
463
|
+
beforeEach(() => {
|
|
464
|
+
vi.useFakeTimers()
|
|
465
|
+
__resetDraftIdForTests()
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
afterEach(() => {
|
|
469
|
+
vi.useRealTimers()
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
it('DM happy path: sendMessageDraft called per update, sendMessage NOT called during stream', async () => {
|
|
473
|
+
const m = makeMock()
|
|
474
|
+
const draftCalls: Array<{ chatId: string; draftId: number; text: string }> = []
|
|
475
|
+
const sendMessageDraft = vi.fn(async (chatId: string, draftId: number, text: string) => {
|
|
476
|
+
draftCalls.push({ chatId, draftId, text })
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
480
|
+
throttleMs: 1000,
|
|
481
|
+
previewTransport: 'auto',
|
|
482
|
+
isPrivateChat: true,
|
|
483
|
+
sendMessageDraft,
|
|
484
|
+
chatId: 'chat1',
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
void stream.update('First update')
|
|
488
|
+
await microtaskFlush()
|
|
489
|
+
|
|
490
|
+
// Draft called, not sendMessage
|
|
491
|
+
expect(sendMessageDraft).toHaveBeenCalledTimes(1)
|
|
492
|
+
expect(draftCalls[0].text).toBe('First update')
|
|
493
|
+
expect(m.sendCalls.length).toBe(0)
|
|
494
|
+
expect(m.editCalls.length).toBe(0)
|
|
495
|
+
|
|
496
|
+
// Second update after throttle
|
|
497
|
+
vi.advanceTimersByTime(1000)
|
|
498
|
+
void stream.update('Second update')
|
|
499
|
+
await microtaskFlush()
|
|
500
|
+
|
|
501
|
+
expect(sendMessageDraft).toHaveBeenCalledTimes(2)
|
|
502
|
+
expect(draftCalls[1].text).toBe('Second update')
|
|
503
|
+
expect(m.sendCalls.length).toBe(0)
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
it('materialize on finalize: sends real sendMessage for push notification + clears draft', async () => {
|
|
507
|
+
const m = makeMock()
|
|
508
|
+
const draftClearCalls: string[] = []
|
|
509
|
+
const sendMessageDraft = vi.fn(async (_chatId: string, _draftId: number, text: string) => {
|
|
510
|
+
if (text === '') draftClearCalls.push(text)
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
514
|
+
throttleMs: 1000,
|
|
515
|
+
previewTransport: 'draft',
|
|
516
|
+
isPrivateChat: true,
|
|
517
|
+
sendMessageDraft,
|
|
518
|
+
chatId: 'chat1',
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
void stream.update('Final answer')
|
|
522
|
+
await microtaskFlush()
|
|
523
|
+
expect(sendMessageDraft).toHaveBeenCalledTimes(1)
|
|
524
|
+
|
|
525
|
+
await stream.finalize()
|
|
526
|
+
|
|
527
|
+
// sendMessage should have been called to materialize
|
|
528
|
+
expect(m.sendCalls.length).toBe(1)
|
|
529
|
+
expect(m.sendCalls[0].text).toBe('Final answer')
|
|
530
|
+
expect(stream.getMessageId()).toBe(100)
|
|
531
|
+
|
|
532
|
+
// Draft should have been cleared (empty string call)
|
|
533
|
+
expect(draftClearCalls.length).toBe(1)
|
|
534
|
+
expect(stream.isFinal()).toBe(true)
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
it('init-time fallback when sendMessageDraft is undefined → uses sendMessage/editMessageText', async () => {
|
|
538
|
+
const m = makeMock()
|
|
539
|
+
|
|
540
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
541
|
+
throttleMs: 1000,
|
|
542
|
+
previewTransport: 'draft',
|
|
543
|
+
isPrivateChat: true,
|
|
544
|
+
// No sendMessageDraft provided
|
|
545
|
+
chatId: 'chat1',
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
void stream.update('Hello')
|
|
549
|
+
await microtaskFlush()
|
|
550
|
+
|
|
551
|
+
expect(m.sendCalls.length).toBe(1)
|
|
552
|
+
expect(m.sendCalls[0].text).toBe('Hello')
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
it('runtime fallback on rejection matching DRAFT_METHOD_UNAVAILABLE_RE', async () => {
|
|
556
|
+
const m = makeMock()
|
|
557
|
+
let draftCallCount = 0
|
|
558
|
+
const sendMessageDraft = vi.fn(async () => {
|
|
559
|
+
draftCallCount++
|
|
560
|
+
throw new Error('sendMessageDraft: unknown method')
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
564
|
+
throttleMs: 1000,
|
|
565
|
+
previewTransport: 'draft',
|
|
566
|
+
sendMessageDraft,
|
|
567
|
+
chatId: 'chat1',
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
void stream.update('Hello')
|
|
571
|
+
await microtaskFlush()
|
|
572
|
+
|
|
573
|
+
// Draft tried once, then fell back to sendMessage
|
|
574
|
+
expect(draftCallCount).toBe(1)
|
|
575
|
+
expect(m.sendCalls.length).toBe(1)
|
|
576
|
+
expect(m.sendCalls[0].text).toBe('Hello')
|
|
577
|
+
|
|
578
|
+
// Subsequent updates should use editMessageText, not draft
|
|
579
|
+
vi.advanceTimersByTime(1000)
|
|
580
|
+
void stream.update('Follow-up')
|
|
581
|
+
await microtaskFlush()
|
|
582
|
+
|
|
583
|
+
expect(draftCallCount).toBe(1) // no more draft calls
|
|
584
|
+
expect(m.editCalls.length).toBe(1)
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
it('runtime fallback on rejection matching DRAFT_CHAT_UNSUPPORTED_RE', async () => {
|
|
588
|
+
const m = makeMock()
|
|
589
|
+
const sendMessageDraft = vi.fn(async () => {
|
|
590
|
+
throw new Error("sendMessageDraft can't be used in this type of chat")
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
594
|
+
throttleMs: 1000,
|
|
595
|
+
previewTransport: 'draft',
|
|
596
|
+
sendMessageDraft,
|
|
597
|
+
chatId: 'chat1',
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
void stream.update('Hello')
|
|
601
|
+
await microtaskFlush()
|
|
602
|
+
|
|
603
|
+
expect(sendMessageDraft).toHaveBeenCalledTimes(1)
|
|
604
|
+
expect(m.sendCalls.length).toBe(1)
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
it('non-matching rejection bubbles up — does not silently swap to message transport', async () => {
|
|
608
|
+
const m = makeMock()
|
|
609
|
+
const sendMessageDraft = vi.fn(async () => {
|
|
610
|
+
throw new Error('sendMessageDraft: internal server error 500')
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
614
|
+
throttleMs: 1000,
|
|
615
|
+
previewTransport: 'draft',
|
|
616
|
+
sendMessageDraft,
|
|
617
|
+
chatId: 'chat1',
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
// The error should NOT trigger fallback — it should propagate (draft-stream
|
|
621
|
+
// logs it but doesn't swap; subsequent update can retry).
|
|
622
|
+
void stream.update('Hello')
|
|
623
|
+
await microtaskFlush()
|
|
624
|
+
|
|
625
|
+
// Did not fall through to sendMessage
|
|
626
|
+
expect(m.sendCalls.length).toBe(0)
|
|
627
|
+
// Draft was called
|
|
628
|
+
expect(sendMessageDraft).toHaveBeenCalledTimes(1)
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
it('group chat (isPrivateChat=false with auto transport) → never tries draft', async () => {
|
|
632
|
+
const m = makeMock()
|
|
633
|
+
const sendMessageDraft = vi.fn(async () => {})
|
|
634
|
+
|
|
635
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
636
|
+
throttleMs: 1000,
|
|
637
|
+
previewTransport: 'auto',
|
|
638
|
+
isPrivateChat: false,
|
|
639
|
+
sendMessageDraft,
|
|
640
|
+
chatId: 'chat1',
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
void stream.update('Hello group')
|
|
644
|
+
await microtaskFlush()
|
|
645
|
+
|
|
646
|
+
expect(sendMessageDraft).not.toHaveBeenCalled()
|
|
647
|
+
expect(m.sendCalls.length).toBe(1)
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
it('forum topic (message transport) → never tries draft', async () => {
|
|
651
|
+
const m = makeMock()
|
|
652
|
+
const sendMessageDraft = vi.fn(async () => {})
|
|
653
|
+
|
|
654
|
+
// Caller forces message transport for forum topics
|
|
655
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
656
|
+
throttleMs: 1000,
|
|
657
|
+
previewTransport: 'message',
|
|
658
|
+
isPrivateChat: true, // even if DM, message transport wins
|
|
659
|
+
sendMessageDraft,
|
|
660
|
+
chatId: 'chat1',
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
void stream.update('Hello forum')
|
|
664
|
+
await microtaskFlush()
|
|
665
|
+
|
|
666
|
+
expect(sendMessageDraft).not.toHaveBeenCalled()
|
|
667
|
+
expect(m.sendCalls.length).toBe(1)
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
it('initialMessageId — first update edits in place instead of sendMessage (#626)', async () => {
|
|
671
|
+
// Closes the duplicate-status-message regression: when a previous
|
|
672
|
+
// done=true finalized + deleted activeDraftStreams[sKey], the next
|
|
673
|
+
// emit creates a fresh stream. With initialMessageId, that fresh
|
|
674
|
+
// stream is initialized as if a previous send had landed with the
|
|
675
|
+
// given id, so the very first update fires editMessageText
|
|
676
|
+
// instead of sendMessage. No new "anchor message" lands.
|
|
677
|
+
const m = makeMock()
|
|
678
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
679
|
+
throttleMs: 200,
|
|
680
|
+
initialMessageId: 999,
|
|
681
|
+
})
|
|
682
|
+
void stream.update('Edit-only payload')
|
|
683
|
+
await stream.finalize()
|
|
684
|
+
expect(m.sendCalls.length).toBe(0)
|
|
685
|
+
expect(m.editCalls.length).toBe(1)
|
|
686
|
+
expect(m.editCalls[0].id).toBe(999)
|
|
687
|
+
expect(m.editCalls[0].text).toBe('Edit-only payload')
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
it('initialMessageId — stale id falls back to sendMessage on not-found error', async () => {
|
|
691
|
+
// Defense in depth: if the externally-supplied id no longer exists
|
|
692
|
+
// (message deleted, chat moved, race), the edit returns
|
|
693
|
+
// "message to edit not found" and the draft stream re-sends. The
|
|
694
|
+
// user sees one fresh anchor — degraded but never silent failure.
|
|
695
|
+
const m = makeMock()
|
|
696
|
+
let editAttempts = 0
|
|
697
|
+
m.edit = async (_id: number, _text: string) => {
|
|
698
|
+
editAttempts++
|
|
699
|
+
throw new Error('Bad Request: message to edit not found')
|
|
700
|
+
}
|
|
701
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
702
|
+
throttleMs: 200,
|
|
703
|
+
initialMessageId: 99999,
|
|
704
|
+
})
|
|
705
|
+
void stream.update('Recovery text')
|
|
706
|
+
await stream.finalize()
|
|
707
|
+
expect(editAttempts).toBeGreaterThanOrEqual(1)
|
|
708
|
+
expect(m.sendCalls.length).toBe(1)
|
|
709
|
+
expect(m.sendCalls[0].text).toBe('Recovery text')
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
it('initialMessageId — null/undefined behaves identically to omitted (back-compat)', async () => {
|
|
713
|
+
// The hook is opt-in. A caller that doesn't supply it (or supplies
|
|
714
|
+
// null) must observe identical behavior to the legacy path:
|
|
715
|
+
// first update sends, subsequent updates edit.
|
|
716
|
+
const m = makeMock()
|
|
717
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
718
|
+
throttleMs: 200,
|
|
719
|
+
initialMessageId: null,
|
|
720
|
+
})
|
|
721
|
+
void stream.update('First call')
|
|
722
|
+
await stream.finalize()
|
|
723
|
+
expect(m.sendCalls.length).toBe(1)
|
|
724
|
+
expect(m.editCalls.length).toBe(0)
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
it('draft-clear failure is swallowed (best-effort)', async () => {
|
|
728
|
+
const m = makeMock()
|
|
729
|
+
let callCount = 0
|
|
730
|
+
const sendMessageDraft = vi.fn(async (_chatId: string, _draftId: number, text: string) => {
|
|
731
|
+
callCount++
|
|
732
|
+
if (text === '') throw new Error('Draft clear failed')
|
|
733
|
+
// Normal update — succeeds
|
|
734
|
+
})
|
|
735
|
+
|
|
736
|
+
const stream = createDraftStream(m.send, m.edit, {
|
|
737
|
+
throttleMs: 1000,
|
|
738
|
+
previewTransport: 'draft',
|
|
739
|
+
sendMessageDraft,
|
|
740
|
+
chatId: 'chat1',
|
|
741
|
+
})
|
|
742
|
+
|
|
743
|
+
void stream.update('Content')
|
|
744
|
+
await microtaskFlush()
|
|
745
|
+
|
|
746
|
+
// finalize should not throw even if draft-clear fails
|
|
747
|
+
await expect(stream.finalize()).resolves.toBeUndefined()
|
|
748
|
+
expect(m.sendCalls.length).toBe(1) // materialized
|
|
749
|
+
expect(callCount).toBeGreaterThan(1) // draft update + failed clear attempt
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
})
|