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,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the turn-active marker (#412 fix). The marker file
|
|
3
|
+
* exists exactly during in-flight turns; the bash watchdog reads its
|
|
4
|
+
* mtime to distinguish "wedged mid-turn" from "healthy idle".
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
8
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync, statSync, utimesSync } from 'node:fs'
|
|
9
|
+
import { tmpdir } from 'node:os'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
import {
|
|
12
|
+
TURN_ACTIVE_MARKER_FILE,
|
|
13
|
+
writeTurnActiveMarker,
|
|
14
|
+
touchTurnActiveMarker,
|
|
15
|
+
removeTurnActiveMarker,
|
|
16
|
+
sweepStaleTurnActiveMarker,
|
|
17
|
+
} from '../gateway/turn-active-marker.js'
|
|
18
|
+
|
|
19
|
+
describe('turn-active-marker (#412)', () => {
|
|
20
|
+
let tmp: string
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmp = mkdtempSync(join(tmpdir(), 'turn-active-'))
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('writeTurnActiveMarker creates a JSON file with the expected payload', () => {
|
|
31
|
+
writeTurnActiveMarker(tmp, {
|
|
32
|
+
turnKey: 'chat:1:1700000000000',
|
|
33
|
+
chatId: 'chat',
|
|
34
|
+
threadId: null,
|
|
35
|
+
startedAt: 1700000000000,
|
|
36
|
+
})
|
|
37
|
+
const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
|
|
38
|
+
expect(existsSync(path)).toBe(true)
|
|
39
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'))
|
|
40
|
+
expect(parsed.turnKey).toBe('chat:1:1700000000000')
|
|
41
|
+
expect(parsed.chatId).toBe('chat')
|
|
42
|
+
expect(parsed.startedAt).toBe(1700000000000)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('writeTurnActiveMarker is idempotent (overwrites existing)', () => {
|
|
46
|
+
writeTurnActiveMarker(tmp, {
|
|
47
|
+
turnKey: 'k1',
|
|
48
|
+
chatId: 'c',
|
|
49
|
+
threadId: null,
|
|
50
|
+
startedAt: 1,
|
|
51
|
+
})
|
|
52
|
+
writeTurnActiveMarker(tmp, {
|
|
53
|
+
turnKey: 'k2',
|
|
54
|
+
chatId: 'c',
|
|
55
|
+
threadId: null,
|
|
56
|
+
startedAt: 2,
|
|
57
|
+
})
|
|
58
|
+
const parsed = JSON.parse(readFileSync(join(tmp, TURN_ACTIVE_MARKER_FILE), 'utf-8'))
|
|
59
|
+
expect(parsed.turnKey).toBe('k2')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('touchTurnActiveMarker bumps the mtime', async () => {
|
|
63
|
+
writeTurnActiveMarker(tmp, {
|
|
64
|
+
turnKey: 'k',
|
|
65
|
+
chatId: 'c',
|
|
66
|
+
threadId: null,
|
|
67
|
+
startedAt: 1,
|
|
68
|
+
})
|
|
69
|
+
const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
|
|
70
|
+
// Force the mtime to 5 minutes in the past so the touch is visible.
|
|
71
|
+
const past = new Date(Date.now() - 5 * 60 * 1000)
|
|
72
|
+
utimesSync(path, past, past)
|
|
73
|
+
const before = statSync(path).mtimeMs
|
|
74
|
+
|
|
75
|
+
touchTurnActiveMarker(tmp)
|
|
76
|
+
const after = statSync(path).mtimeMs
|
|
77
|
+
expect(after).toBeGreaterThan(before)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('touchTurnActiveMarker is a no-op when no marker exists', () => {
|
|
81
|
+
// Must not throw, must not create the file.
|
|
82
|
+
touchTurnActiveMarker(tmp)
|
|
83
|
+
expect(existsSync(join(tmp, TURN_ACTIVE_MARKER_FILE))).toBe(false)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('removeTurnActiveMarker deletes the file', () => {
|
|
87
|
+
writeTurnActiveMarker(tmp, {
|
|
88
|
+
turnKey: 'k',
|
|
89
|
+
chatId: 'c',
|
|
90
|
+
threadId: null,
|
|
91
|
+
startedAt: 1,
|
|
92
|
+
})
|
|
93
|
+
const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
|
|
94
|
+
expect(existsSync(path)).toBe(true)
|
|
95
|
+
removeTurnActiveMarker(tmp)
|
|
96
|
+
expect(existsSync(path)).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('removeTurnActiveMarker is idempotent (no throw on missing file)', () => {
|
|
100
|
+
expect(() => removeTurnActiveMarker(tmp)).not.toThrow()
|
|
101
|
+
expect(() => removeTurnActiveMarker(tmp)).not.toThrow()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('writeTurnActiveMarker creates the state dir if missing', () => {
|
|
105
|
+
const fresh = join(tmp, 'fresh', 'subdir')
|
|
106
|
+
writeTurnActiveMarker(fresh, {
|
|
107
|
+
turnKey: 'k',
|
|
108
|
+
chatId: 'c',
|
|
109
|
+
threadId: null,
|
|
110
|
+
startedAt: 1,
|
|
111
|
+
})
|
|
112
|
+
expect(existsSync(join(fresh, TURN_ACTIVE_MARKER_FILE))).toBe(true)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// #550: defence-in-depth sweeper for orphaned markers.
|
|
116
|
+
describe('sweepStaleTurnActiveMarker (#550)', () => {
|
|
117
|
+
const writeWithMtime = (ageMs: number) => {
|
|
118
|
+
writeTurnActiveMarker(tmp, {
|
|
119
|
+
turnKey: 'k',
|
|
120
|
+
chatId: 'c',
|
|
121
|
+
threadId: null,
|
|
122
|
+
startedAt: 1,
|
|
123
|
+
})
|
|
124
|
+
const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
|
|
125
|
+
const past = new Date(Date.now() - ageMs)
|
|
126
|
+
utimesSync(path, past, past)
|
|
127
|
+
return path
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
it('returns false when no marker file exists', () => {
|
|
131
|
+
const swept = sweepStaleTurnActiveMarker(tmp, {
|
|
132
|
+
turnInFlight: false,
|
|
133
|
+
idleSweepMs: 60_000,
|
|
134
|
+
hardTtlMs: 600_000,
|
|
135
|
+
})
|
|
136
|
+
expect(swept).toBe(false)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('removes a stale marker when no turn is in flight (idle path)', () => {
|
|
140
|
+
const path = writeWithMtime(120_000) // 2 min old
|
|
141
|
+
const swept = sweepStaleTurnActiveMarker(tmp, {
|
|
142
|
+
turnInFlight: false,
|
|
143
|
+
idleSweepMs: 60_000,
|
|
144
|
+
hardTtlMs: 600_000,
|
|
145
|
+
})
|
|
146
|
+
expect(swept).toBe(true)
|
|
147
|
+
expect(existsSync(path)).toBe(false)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('does NOT remove a fresh marker even when no turn is in flight', () => {
|
|
151
|
+
const path = writeWithMtime(5_000) // 5s old
|
|
152
|
+
const swept = sweepStaleTurnActiveMarker(tmp, {
|
|
153
|
+
turnInFlight: false,
|
|
154
|
+
idleSweepMs: 60_000,
|
|
155
|
+
hardTtlMs: 600_000,
|
|
156
|
+
})
|
|
157
|
+
expect(swept).toBe(false)
|
|
158
|
+
expect(existsSync(path)).toBe(true)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('does NOT remove a recent marker while a turn is in flight (idle bound)', () => {
|
|
162
|
+
const path = writeWithMtime(120_000) // 2 min old
|
|
163
|
+
const swept = sweepStaleTurnActiveMarker(tmp, {
|
|
164
|
+
turnInFlight: true,
|
|
165
|
+
idleSweepMs: 60_000,
|
|
166
|
+
hardTtlMs: 600_000,
|
|
167
|
+
})
|
|
168
|
+
expect(swept).toBe(false)
|
|
169
|
+
expect(existsSync(path)).toBe(true)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('removes a marker past hardTtl even if a turn claims to be in flight', () => {
|
|
173
|
+
const path = writeWithMtime(20 * 60_000) // 20 min old
|
|
174
|
+
const swept = sweepStaleTurnActiveMarker(tmp, {
|
|
175
|
+
turnInFlight: true,
|
|
176
|
+
idleSweepMs: 60_000,
|
|
177
|
+
hardTtlMs: 10 * 60_000,
|
|
178
|
+
})
|
|
179
|
+
expect(swept).toBe(true)
|
|
180
|
+
expect(existsSync(path)).toBe(false)
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('writes mode 0600 (operator-only readable)', () => {
|
|
185
|
+
writeTurnActiveMarker(tmp, {
|
|
186
|
+
turnKey: 'k',
|
|
187
|
+
chatId: 'c',
|
|
188
|
+
threadId: null,
|
|
189
|
+
startedAt: 1,
|
|
190
|
+
})
|
|
191
|
+
const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
|
|
192
|
+
const mode = statSync(path).mode & 0o777
|
|
193
|
+
expect(mode).toBe(0o600)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for two bugs reported 2026-04-13 / 2026-04-14:
|
|
3
|
+
*
|
|
4
|
+
* Bug 1 (TG-DONE): the progress-card message stays stuck on
|
|
5
|
+
* "⚙️ Working…" after a turn completes. Root cause: session-tail
|
|
6
|
+
* onEvent ran `handleSessionEvent` (which calls `closeProgressLane`
|
|
7
|
+
* and deletes + finalizes the progress-lane stream) BEFORE
|
|
8
|
+
* `progressDriver.ingest` (which synchronously emits the final
|
|
9
|
+
* "Done" render via handleStreamReply). By the time the driver
|
|
10
|
+
* tried to edit the existing stream, it was already gone.
|
|
11
|
+
*
|
|
12
|
+
* Bug 2 (TG-IDLE-LEAK): bare text emitted by the model after
|
|
13
|
+
* `stream_reply(done=true)` (e.g. "Idle; awaiting next instruction.")
|
|
14
|
+
* leaks as a separate Telegram message. Root cause: server.ts
|
|
15
|
+
* identified its own MCP tools by exact-prefix match against
|
|
16
|
+
* `mcp__switchroom-telegram__…`, but Claude Code prefixes tool
|
|
17
|
+
* names with whatever registration key the host .mcp.json used.
|
|
18
|
+
* Existing agents still register as `clerk-telegram`, so no
|
|
19
|
+
* `reply`-family tool call ever set `currentTurnReplyCalled` and
|
|
20
|
+
* the orphaned-reply backstop fired on every post-reply idle text.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { describe, it, expect } from 'vitest'
|
|
24
|
+
import { readFileSync } from 'fs'
|
|
25
|
+
import { join, dirname } from 'path'
|
|
26
|
+
import { fileURLToPath } from 'url'
|
|
27
|
+
import { createProgressDriver } from '../progress-card-driver.js'
|
|
28
|
+
import type { SessionEvent } from '../session-tail.js'
|
|
29
|
+
import { isTelegramReplyTool, isTelegramSurfaceTool } from '../tool-names.js'
|
|
30
|
+
|
|
31
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
32
|
+
|
|
33
|
+
// ─── Bug 1 integration harness ────────────────────────────────────────────
|
|
34
|
+
//
|
|
35
|
+
// Simulates the server.ts wiring: one session-tail event callback that
|
|
36
|
+
// fans out to (a) progressDriver.ingest and (b) a lightweight stand-in
|
|
37
|
+
// for handleSessionEvent's closeProgressLane. The real bug showed up as
|
|
38
|
+
// an ordering race between these two consumers, so the harness exercises
|
|
39
|
+
// both orders and asserts which one delivers the Done render.
|
|
40
|
+
|
|
41
|
+
interface FakeStream {
|
|
42
|
+
updates: string[]
|
|
43
|
+
finalized: boolean
|
|
44
|
+
finalize(): void
|
|
45
|
+
update(text: string): void
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeStream(): FakeStream {
|
|
49
|
+
const s: FakeStream = {
|
|
50
|
+
updates: [],
|
|
51
|
+
finalized: false,
|
|
52
|
+
finalize() {
|
|
53
|
+
this.finalized = true
|
|
54
|
+
},
|
|
55
|
+
update(text: string) {
|
|
56
|
+
if (this.finalized) return
|
|
57
|
+
this.updates.push(text)
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
return s
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function wireServer(order: 'driver-first' | 'handler-first') {
|
|
64
|
+
const streams = new Map<string, FakeStream>()
|
|
65
|
+
const allStreams: FakeStream[] = [] // never forgets — includes deleted ones
|
|
66
|
+
const events: SessionEvent[] = []
|
|
67
|
+
|
|
68
|
+
// Stand-in for handleStreamReply's progress-lane emit: if a stream for
|
|
69
|
+
// this key already exists, push the update into it; otherwise create
|
|
70
|
+
// one. Creating a fresh stream here simulates "posted a NEW message
|
|
71
|
+
// instead of updating the original card" — the user-visible bug 1
|
|
72
|
+
// symptom.
|
|
73
|
+
const emit = (args: { chatId: string; threadId?: string; html: string; done: boolean }): void => {
|
|
74
|
+
const key = `${args.chatId}:${args.threadId ?? '_'}:progress`
|
|
75
|
+
let s = streams.get(key)
|
|
76
|
+
if (!s) {
|
|
77
|
+
s = makeStream()
|
|
78
|
+
streams.set(key, s)
|
|
79
|
+
allStreams.push(s)
|
|
80
|
+
}
|
|
81
|
+
s.update(args.html)
|
|
82
|
+
if (args.done) s.finalize()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const driver = createProgressDriver({ emit, minIntervalMs: 0, coalesceMs: 0, initialDelayMs: 0 })
|
|
86
|
+
|
|
87
|
+
// Emulate server.ts closeProgressLane: delete + finalize.
|
|
88
|
+
function closeProgressLane(chatId: string, threadId?: string): void {
|
|
89
|
+
const key = `${chatId}:${threadId ?? '_'}:progress`
|
|
90
|
+
const s = streams.get(key)
|
|
91
|
+
if (!s) return
|
|
92
|
+
streams.delete(key)
|
|
93
|
+
s.finalize()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function handleSessionEvent(ev: SessionEvent): void {
|
|
97
|
+
if (ev.kind === 'turn_end') {
|
|
98
|
+
// This is the line that caused bug 1: closeProgressLane deletes +
|
|
99
|
+
// finalizes before the driver has emitted its Done render.
|
|
100
|
+
closeProgressLane('c1')
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The fix is to run progressDriver.ingest FIRST on turn_end so the
|
|
105
|
+
// driver's synchronous flush reaches the existing stream before the
|
|
106
|
+
// handler tears it down. Expose both orders so the test can compare.
|
|
107
|
+
const onEvent = (ev: SessionEvent): void => {
|
|
108
|
+
events.push(ev)
|
|
109
|
+
if (order === 'driver-first') {
|
|
110
|
+
driver.ingest(ev, null)
|
|
111
|
+
handleSessionEvent(ev)
|
|
112
|
+
} else {
|
|
113
|
+
handleSessionEvent(ev)
|
|
114
|
+
driver.ingest(ev, null)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { onEvent, streams, allStreams, events, driver }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// #235 Wave 3 F4: server.ts monolith removed. The bug-1 driver-first
|
|
122
|
+
// wiring pin (was a regex match on the onEvent callback in server.ts)
|
|
123
|
+
// no longer applies — gateway.ts is the only file with the wiring,
|
|
124
|
+
// and its driver-first ordering is pinned by the wireServer-driver-first
|
|
125
|
+
// behaviour tests below.
|
|
126
|
+
|
|
127
|
+
describe('bug 1 — Done transition reaches the original progress card', () => {
|
|
128
|
+
it('driver-first order: Done render lands in the ORIGINAL stream', () => {
|
|
129
|
+
const { onEvent, allStreams, driver } = wireServer('driver-first')
|
|
130
|
+
|
|
131
|
+
// Minimal turn: enqueue → one tool_use+result → turn_end
|
|
132
|
+
onEvent({
|
|
133
|
+
kind: 'enqueue',
|
|
134
|
+
chatId: 'c1',
|
|
135
|
+
messageId: '1',
|
|
136
|
+
threadId: null,
|
|
137
|
+
rawContent: '<channel chat_id="c1">hi</channel>',
|
|
138
|
+
})
|
|
139
|
+
onEvent({ kind: 'tool_use', toolName: 'Read', toolUseId: 't1', input: { file_path: '/x' } })
|
|
140
|
+
onEvent({ kind: 'tool_result', toolUseId: 't1', toolName: 'Read' })
|
|
141
|
+
// Issue #132: simulate the agent calling the reply tool so the final
|
|
142
|
+
// render lands on "✅ Done" rather than "🙊 Ended without reply". This
|
|
143
|
+
// test is about the bug-1 ordering between handler and driver, not
|
|
144
|
+
// the silent-end UX, so we explicitly opt into the happy path.
|
|
145
|
+
onEvent({ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__reply', toolUseId: 't2' })
|
|
146
|
+
onEvent({ kind: 'tool_result', toolUseId: 't2', toolName: 'mcp__switchroom-telegram__reply' })
|
|
147
|
+
// Issue #137: also simulate a successful outbound delivery so the renderer
|
|
148
|
+
// doesn't downgrade to "⚠️ Reply attempted but not delivered" — gateway's
|
|
149
|
+
// executeReply path calls recordOutboundDelivered after the message lands.
|
|
150
|
+
driver.recordOutboundDelivered('c1')
|
|
151
|
+
onEvent({ kind: 'turn_end', durationMs: 500 })
|
|
152
|
+
|
|
153
|
+
// With driver-first, only ONE stream is ever created — the original
|
|
154
|
+
// card. The handler's closeProgressLane runs AFTER the driver has
|
|
155
|
+
// already pushed the Done render into it, so no duplicate message
|
|
156
|
+
// is needed.
|
|
157
|
+
expect(allStreams).toHaveLength(1)
|
|
158
|
+
const [stream] = allStreams
|
|
159
|
+
expect(stream.finalized).toBe(true)
|
|
160
|
+
const lastUpdate = stream.updates.at(-1) ?? ''
|
|
161
|
+
expect(lastUpdate).toContain('✅ <b>Done</b>')
|
|
162
|
+
expect(lastUpdate).not.toContain('⚙️ <b>Working…</b>')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('handler-first order reproduces bug 1: Done lands on a NEW stream', () => {
|
|
166
|
+
// This is the PRE-FIX behaviour — kept as a negative control so the
|
|
167
|
+
// fix's value is visible. The handler tears down the stream
|
|
168
|
+
// synchronously on turn_end; the driver's subsequent emit can't find
|
|
169
|
+
// the original stream, so the emit callback has to create a new
|
|
170
|
+
// stream (new Telegram message) — leaving the original progress
|
|
171
|
+
// card stuck on "⚙️ Working…".
|
|
172
|
+
const { onEvent, allStreams } = wireServer('handler-first')
|
|
173
|
+
|
|
174
|
+
onEvent({
|
|
175
|
+
kind: 'enqueue',
|
|
176
|
+
chatId: 'c1',
|
|
177
|
+
messageId: '1',
|
|
178
|
+
threadId: null,
|
|
179
|
+
rawContent: '<channel chat_id="c1">hi</channel>',
|
|
180
|
+
})
|
|
181
|
+
onEvent({ kind: 'tool_use', toolName: 'Read', toolUseId: 't1', input: { file_path: '/x' } })
|
|
182
|
+
onEvent({ kind: 'tool_result', toolUseId: 't1', toolName: 'Read' })
|
|
183
|
+
const originalSnapshotUpdates = [...(allStreams[0]?.updates ?? [])]
|
|
184
|
+
|
|
185
|
+
onEvent({ kind: 'turn_end', durationMs: 500 })
|
|
186
|
+
|
|
187
|
+
// Bug surface: the ORIGINAL stream got NO further updates past the
|
|
188
|
+
// pre-turn_end render, so its last frame still says Working.
|
|
189
|
+
const original = allStreams[0]
|
|
190
|
+
expect(original).toBeDefined()
|
|
191
|
+
const originalLast = original.updates.at(-1) ?? ''
|
|
192
|
+
expect(originalLast).toContain('⚙️ <b>Working…</b>')
|
|
193
|
+
expect(originalLast).not.toContain('✅ <b>Done</b>')
|
|
194
|
+
// And a SECOND stream had to be created for the Done render —
|
|
195
|
+
// visible to the user as a duplicate/new card.
|
|
196
|
+
expect(allStreams.length).toBeGreaterThanOrEqual(2)
|
|
197
|
+
expect(originalSnapshotUpdates.length).toBe(original.updates.length)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// ─── Bug 2 unit tests for isTelegramReplyTool ─────────────────────────────
|
|
202
|
+
|
|
203
|
+
// #235 Wave 3 F4: server.ts monolith removed. The bug-2 import + dead-
|
|
204
|
+
// branch pin no longer applies — gateway.ts owns the classification
|
|
205
|
+
// path now, exercised by the unit tests below.
|
|
206
|
+
|
|
207
|
+
describe('bug 2 — telegram tool-name classification is robust to MCP registration key', () => {
|
|
208
|
+
it('matches the historical `clerk-telegram` registration', () => {
|
|
209
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__reply')).toBe(true)
|
|
210
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__stream_reply')).toBe(true)
|
|
211
|
+
expect(isTelegramSurfaceTool('mcp__clerk-telegram__edit_message')).toBe(true)
|
|
212
|
+
expect(isTelegramSurfaceTool('mcp__clerk-telegram__react')).toBe(true)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('matches the current `switchroom-telegram` registration', () => {
|
|
216
|
+
expect(isTelegramReplyTool('mcp__switchroom-telegram__reply')).toBe(true)
|
|
217
|
+
expect(isTelegramReplyTool('mcp__switchroom-telegram__stream_reply')).toBe(true)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('matches fork-style registration keys that still contain `telegram`', () => {
|
|
221
|
+
expect(isTelegramReplyTool('mcp__my-fork-telegram__stream_reply')).toBe(true)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('does NOT match unrelated MCP tool names', () => {
|
|
225
|
+
expect(isTelegramReplyTool('mcp__hindsight__recall')).toBe(false)
|
|
226
|
+
expect(isTelegramReplyTool('Read')).toBe(false)
|
|
227
|
+
expect(isTelegramReplyTool('mcp__switchroom-telegram__download_attachment')).toBe(false)
|
|
228
|
+
expect(isTelegramSurfaceTool('mcp__hindsight__retain')).toBe(false)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it("does NOT match the plugin's own non-reply tools (bugs past and future)", () => {
|
|
232
|
+
// `get_recent_messages`, `send_typing`, `pin_message`, `forward_message`,
|
|
233
|
+
// `delete_message`, `download_attachment` — none of these own the
|
|
234
|
+
// answer surface, so they must NOT set currentTurnReplyCalled, or the
|
|
235
|
+
// backstop would fail to fire for a turn that only called one of
|
|
236
|
+
// them and then emitted text.
|
|
237
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__get_recent_messages')).toBe(false)
|
|
238
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__send_typing')).toBe(false)
|
|
239
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__pin_message')).toBe(false)
|
|
240
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__forward_message')).toBe(false)
|
|
241
|
+
expect(isTelegramReplyTool('mcp__clerk-telegram__delete_message')).toBe(false)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// ─── Bug 2 end-to-end simulation of the turn-end backstop decision ────────
|
|
246
|
+
//
|
|
247
|
+
// The real server-side code path we need to guard: after a
|
|
248
|
+
// `stream_reply(done=true)` tool_use event arrives in the session-tail,
|
|
249
|
+
// the tool_use handler must set `currentTurnReplyCalled = true`. If it
|
|
250
|
+
// doesn't, a subsequent `text` event (e.g. "Idle; awaiting next
|
|
251
|
+
// instruction.") gets captured, and at turn_end the backstop fires and
|
|
252
|
+
// forwards it as a separate Telegram message.
|
|
253
|
+
//
|
|
254
|
+
// We simulate just the flag transition here — it's the cleanest way to
|
|
255
|
+
// pin down the exact bug without importing server.ts (which has
|
|
256
|
+
// top-level side-effects that require env vars).
|
|
257
|
+
|
|
258
|
+
describe('bug 2 — stream_reply tool call sets reply-called flag regardless of MCP key', () => {
|
|
259
|
+
// Mirror of the real session-tail tool_use event shape
|
|
260
|
+
type ToolUseEv = { kind: 'tool_use'; toolName: string }
|
|
261
|
+
|
|
262
|
+
function simulateTurn(events: Array<ToolUseEv | { kind: 'text'; text: string } | { kind: 'turn_end' }>): {
|
|
263
|
+
replyCalled: boolean
|
|
264
|
+
capturedText: string[]
|
|
265
|
+
wouldFireBackstop: boolean
|
|
266
|
+
} {
|
|
267
|
+
let replyCalled = false
|
|
268
|
+
const captured: string[] = []
|
|
269
|
+
for (const ev of events) {
|
|
270
|
+
if (ev.kind === 'tool_use' && isTelegramReplyTool(ev.toolName)) {
|
|
271
|
+
replyCalled = true
|
|
272
|
+
} else if (ev.kind === 'text') {
|
|
273
|
+
captured.push(ev.text)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
replyCalled,
|
|
278
|
+
capturedText: captured,
|
|
279
|
+
// This mirrors the exact server.ts turn_end guard.
|
|
280
|
+
wouldFireBackstop: !replyCalled && captured.length > 0,
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
it('with `clerk-telegram` key: stream_reply followed by idle text does NOT fire the backstop', () => {
|
|
285
|
+
const result = simulateTurn([
|
|
286
|
+
{ kind: 'tool_use', toolName: 'mcp__clerk-telegram__stream_reply' },
|
|
287
|
+
{ kind: 'text', text: 'Idle; awaiting next instruction.' },
|
|
288
|
+
{ kind: 'turn_end' },
|
|
289
|
+
])
|
|
290
|
+
expect(result.replyCalled).toBe(true)
|
|
291
|
+
// Post-fix: backstop does NOT fire, so no duplicate message leaks.
|
|
292
|
+
expect(result.wouldFireBackstop).toBe(false)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('with `switchroom-telegram` key: unchanged — reply-called recognized', () => {
|
|
296
|
+
const result = simulateTurn([
|
|
297
|
+
{ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__stream_reply' },
|
|
298
|
+
{ kind: 'text', text: 'idle' },
|
|
299
|
+
{ kind: 'turn_end' },
|
|
300
|
+
])
|
|
301
|
+
expect(result.replyCalled).toBe(true)
|
|
302
|
+
expect(result.wouldFireBackstop).toBe(false)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('genuine orphan (NO reply tool call, only text) — backstop is DISABLED (issues #251, #269)', () => {
|
|
306
|
+
// The orphaned-reply backstop was removed in issue #269. All Telegram
|
|
307
|
+
// delivery now goes through the MCP reply tool, so a turn that ends
|
|
308
|
+
// without calling the reply tool is treated as a silent turn (no message
|
|
309
|
+
// sent), not a forwarding opportunity.
|
|
310
|
+
const result = simulateTurn([
|
|
311
|
+
{ kind: 'tool_use', toolName: 'Read' },
|
|
312
|
+
{ kind: 'text', text: "here's the answer" },
|
|
313
|
+
{ kind: 'turn_end' },
|
|
314
|
+
])
|
|
315
|
+
expect(result.replyCalled).toBe(false)
|
|
316
|
+
// The wouldFireBackstop flag reflects the old logic; the actual backstop
|
|
317
|
+
// in server.ts is now commented out. This value is kept for diagnostic
|
|
318
|
+
// purposes but the block no longer fires.
|
|
319
|
+
expect(result.wouldFireBackstop).toBe(true) // still true logically, but never executed
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
// ─── Bug 3 — orphan progress card (TG-ORPHAN-CARD) ───────────────────────
|
|
324
|
+
//
|
|
325
|
+
// Progress cards stayed pinned in "Working…" when turn_end was delayed,
|
|
326
|
+
// missed, or arrived after the model already sent its final reply.
|
|
327
|
+
//
|
|
328
|
+
// Fix: unpin fires on the FIRST of (turn_end, stream_reply(done=true), reply())
|
|
329
|
+
// via a shared `unpinProgressCard` helper guarded by `unpinnedTurnKeys`.
|
|
330
|
+
//
|
|
331
|
+
// These tests simulate the pin/unpin lifecycle that server.ts manages.
|
|
332
|
+
// They exercise the guard logic directly rather than importing server.ts
|
|
333
|
+
// (which has top-level side-effects requiring env vars).
|
|
334
|
+
|
|
335
|
+
describe('bug 3 — progress-card unpin fires on first of turn_end / reply / stream_reply(done)', () => {
|
|
336
|
+
/**
|
|
337
|
+
* Miniature simulation of the pin/unpin lifecycle from server.ts.
|
|
338
|
+
* Models the progressPinnedMsgIds map, unpinnedTurnKeys set, and the
|
|
339
|
+
* unpinProgressCard + unpinProgressCardForChat helpers.
|
|
340
|
+
*/
|
|
341
|
+
function makeUnpinHarness() {
|
|
342
|
+
const progressPinnedMsgIds = new Map<string, number>()
|
|
343
|
+
const unpinnedTurnKeys = new Set<string>()
|
|
344
|
+
const unpinCalls: Array<{ turnKey: string; pinnedId: number }> = []
|
|
345
|
+
|
|
346
|
+
function unpinProgressCard(turnKey: string, _chatId: string, pinnedId: number): void {
|
|
347
|
+
if (unpinnedTurnKeys.has(turnKey)) return
|
|
348
|
+
unpinnedTurnKeys.add(turnKey)
|
|
349
|
+
progressPinnedMsgIds.delete(turnKey)
|
|
350
|
+
unpinCalls.push({ turnKey, pinnedId })
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function unpinProgressCardForChat(chatId: string, threadId: number | undefined): void {
|
|
354
|
+
const base = threadId != null ? `${chatId}:${threadId}` : chatId
|
|
355
|
+
for (const [turnKey, pinnedId] of progressPinnedMsgIds) {
|
|
356
|
+
if (turnKey.startsWith(`${base}:`)) {
|
|
357
|
+
unpinProgressCard(turnKey, chatId, pinnedId)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function pinCard(turnKey: string, chatId: string, messageId: number): void {
|
|
363
|
+
progressPinnedMsgIds.set(turnKey, messageId)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function onTurnComplete(turnKey: string, chatId: string): void {
|
|
367
|
+
const pinnedId = progressPinnedMsgIds.get(turnKey)
|
|
368
|
+
if (pinnedId != null) {
|
|
369
|
+
unpinProgressCard(turnKey, chatId, pinnedId)
|
|
370
|
+
}
|
|
371
|
+
unpinnedTurnKeys.delete(turnKey)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
pinCard,
|
|
376
|
+
unpinProgressCardForChat,
|
|
377
|
+
onTurnComplete,
|
|
378
|
+
unpinCalls,
|
|
379
|
+
unpinnedTurnKeys,
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
it('reply() triggers unpin before turn_end arrives', () => {
|
|
384
|
+
const h = makeUnpinHarness()
|
|
385
|
+
// Card pinned at turn start
|
|
386
|
+
h.pinCard('100:1', '100', 42)
|
|
387
|
+
|
|
388
|
+
// model calls reply() → early unpin
|
|
389
|
+
h.unpinProgressCardForChat('100', undefined)
|
|
390
|
+
|
|
391
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
392
|
+
expect(h.unpinCalls[0]).toMatchObject({ turnKey: '100:1', pinnedId: 42 })
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('stream_reply(done=true) triggers unpin before turn_end arrives', () => {
|
|
396
|
+
const h = makeUnpinHarness()
|
|
397
|
+
h.pinCard('200:1', '200', 55)
|
|
398
|
+
|
|
399
|
+
// model calls stream_reply(done=true) → early unpin via chat lookup
|
|
400
|
+
h.unpinProgressCardForChat('200', undefined)
|
|
401
|
+
|
|
402
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
403
|
+
expect(h.unpinCalls[0]).toMatchObject({ turnKey: '200:1', pinnedId: 55 })
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
it('turn_end after reply-triggered unpin is a no-op (double-unpin guard)', () => {
|
|
407
|
+
const h = makeUnpinHarness()
|
|
408
|
+
h.pinCard('300:1', '300', 77)
|
|
409
|
+
|
|
410
|
+
// reply() fires first
|
|
411
|
+
h.unpinProgressCardForChat('300', undefined)
|
|
412
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
413
|
+
|
|
414
|
+
// turn_end fires later — onTurnComplete guard should make it a no-op
|
|
415
|
+
h.onTurnComplete('300:1', '300')
|
|
416
|
+
|
|
417
|
+
// Still only one unpin call
|
|
418
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
419
|
+
// Guard entry cleaned up by onTurnComplete
|
|
420
|
+
expect(h.unpinnedTurnKeys.has('300:1')).toBe(false)
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
it('turn_end fires first (normal path) — guard prevents double-unpin on late reply()', () => {
|
|
424
|
+
const h = makeUnpinHarness()
|
|
425
|
+
h.pinCard('400:1', '400', 88)
|
|
426
|
+
|
|
427
|
+
// turn_end fires first (normal path)
|
|
428
|
+
h.onTurnComplete('400:1', '400')
|
|
429
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
430
|
+
// Guard cleaned up by onTurnComplete
|
|
431
|
+
expect(h.unpinnedTurnKeys.has('400:1')).toBe(false)
|
|
432
|
+
|
|
433
|
+
// Late reply() fires (e.g. async handler after turn_end)
|
|
434
|
+
// unpinProgressCardForChat checks progressPinnedMsgIds which is already empty
|
|
435
|
+
h.unpinProgressCardForChat('400', undefined)
|
|
436
|
+
// No second unpin
|
|
437
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it('concurrent turns on same chat each get their own unpin', () => {
|
|
441
|
+
const h = makeUnpinHarness()
|
|
442
|
+
// Two concurrent turns on chat 500 (e.g. two parallel sub-agents)
|
|
443
|
+
h.pinCard('500:1', '500', 10)
|
|
444
|
+
h.pinCard('500:2', '500', 11)
|
|
445
|
+
|
|
446
|
+
// reply() for the chat — unpins all matching cards
|
|
447
|
+
h.unpinProgressCardForChat('500', undefined)
|
|
448
|
+
|
|
449
|
+
expect(h.unpinCalls).toHaveLength(2)
|
|
450
|
+
const turnKeys = h.unpinCalls.map((c) => c.turnKey)
|
|
451
|
+
expect(turnKeys).toContain('500:1')
|
|
452
|
+
expect(turnKeys).toContain('500:2')
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('unpinProgressCardForChat only unpins cards for the matching chat+thread', () => {
|
|
456
|
+
const h = makeUnpinHarness()
|
|
457
|
+
h.pinCard('600:1', '600', 20) // chat 600, no thread
|
|
458
|
+
h.pinCard('601:1', '601', 21) // chat 601, no thread (different chat)
|
|
459
|
+
|
|
460
|
+
// Unpin for chat 600 only
|
|
461
|
+
h.unpinProgressCardForChat('600', undefined)
|
|
462
|
+
|
|
463
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
464
|
+
expect(h.unpinCalls[0]!.turnKey).toBe('600:1')
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
it('unpinProgressCardForChat scopes to thread when threadId is set', () => {
|
|
468
|
+
const h = makeUnpinHarness()
|
|
469
|
+
h.pinCard('700:99:1', '700', 30) // chat 700, thread 99
|
|
470
|
+
h.pinCard('700:88:1', '700', 31) // chat 700, thread 88
|
|
471
|
+
|
|
472
|
+
// Unpin for thread 99 only
|
|
473
|
+
h.unpinProgressCardForChat('700', 99)
|
|
474
|
+
|
|
475
|
+
expect(h.unpinCalls).toHaveLength(1)
|
|
476
|
+
expect(h.unpinCalls[0]!.turnKey).toBe('700:99:1')
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
// #235 Wave 3 F4: server.ts monolith removed. The maxIdleMs +
|
|
480
|
+
// early-unpin source-text pins for server.ts no longer apply —
|
|
481
|
+
// gateway.ts is the source of truth, pinned by the gateway.ts
|
|
482
|
+
// assertion below.
|
|
483
|
+
|
|
484
|
+
it('gateway.ts executeReply/executeStreamReply do NOT early-unpin', () => {
|
|
485
|
+
const gatewaySrc = readFileSync(join(__dirname, '..', 'gateway', 'gateway.ts'), 'utf-8')
|
|
486
|
+
expect(gatewaySrc).not.toContain('unpinProgressCardForChat?.(chat_id, threadId)')
|
|
487
|
+
expect(gatewaySrc).not.toContain('unpinProgressCardForChat?.(srChatId, srThreadId)')
|
|
488
|
+
})
|
|
489
|
+
})
|