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,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refactor regression: every per-chat close site must end in the SAME
|
|
3
|
+
* post-conditions on the driver's internal state. Pre-refactor, three
|
|
4
|
+
* code paths (turn_end → completeTurnFully, heartbeat zombie ceiling →
|
|
5
|
+
* closeZombie, Gap-8 deferred-completion timeout → inline) reproduced
|
|
6
|
+
* the cleanup tail by hand and diverged on edge cases. The refactor
|
|
7
|
+
* funnels them all through `closePerChat(reason)` so the only remaining
|
|
8
|
+
* deltas are:
|
|
9
|
+
*
|
|
10
|
+
* - 'turn-end': no sub-agent force-close (none are running).
|
|
11
|
+
* - 'zombie' : force-close running sub-agents; preserve
|
|
12
|
+
* pendingSyncEchoes (echo may still arrive).
|
|
13
|
+
* - 'stalled' : force-close running sub-agents; flush(stalledClose=true).
|
|
14
|
+
*
|
|
15
|
+
* This test drives all three reasons against a fresh driver instance
|
|
16
|
+
* and asserts the convergent post-conditions. It is the load-bearing
|
|
17
|
+
* test for the unified close path — if it fails, the refactor regressed.
|
|
18
|
+
*/
|
|
19
|
+
import { describe, it, expect } from 'vitest'
|
|
20
|
+
import { createProgressDriver } from '../progress-card-driver.js'
|
|
21
|
+
import type { SessionEvent } from '../session-tail.js'
|
|
22
|
+
|
|
23
|
+
let nextMsgId = 7000
|
|
24
|
+
|
|
25
|
+
function harness(opts?: { maxIdleMs?: number; deferredCompletionTimeoutMs?: number }) {
|
|
26
|
+
let now = 1000
|
|
27
|
+
const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
|
|
28
|
+
let nextRef = 0
|
|
29
|
+
const emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }> = []
|
|
30
|
+
|
|
31
|
+
const driver = createProgressDriver({
|
|
32
|
+
emit: (a) => emits.push(a),
|
|
33
|
+
minIntervalMs: 0,
|
|
34
|
+
coalesceMs: 0,
|
|
35
|
+
initialDelayMs: 0,
|
|
36
|
+
heartbeatMs: 1_000,
|
|
37
|
+
maxIdleMs: opts?.maxIdleMs ?? 30_000,
|
|
38
|
+
deferredCompletionTimeoutMs: opts?.deferredCompletionTimeoutMs ?? 10_000,
|
|
39
|
+
now: () => now,
|
|
40
|
+
setTimeout: (fn, ms) => {
|
|
41
|
+
const ref = nextRef++
|
|
42
|
+
timers.push({ fireAt: now + ms, fn, ref })
|
|
43
|
+
return { ref }
|
|
44
|
+
},
|
|
45
|
+
clearTimeout: (handle) => {
|
|
46
|
+
const target = (handle as { ref: number }).ref
|
|
47
|
+
const idx = timers.findIndex((t) => t.ref === target)
|
|
48
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
49
|
+
},
|
|
50
|
+
setInterval: (fn, ms) => {
|
|
51
|
+
const ref = nextRef++
|
|
52
|
+
timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
|
|
53
|
+
return { ref }
|
|
54
|
+
},
|
|
55
|
+
clearInterval: (handle) => {
|
|
56
|
+
const target = (handle as { ref: number }).ref
|
|
57
|
+
const idx = timers.findIndex((t) => t.ref === target)
|
|
58
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const advance = (ms: number): void => {
|
|
63
|
+
now += ms
|
|
64
|
+
for (;;) {
|
|
65
|
+
timers.sort((a, b) => a.fireAt - b.fireAt)
|
|
66
|
+
const next = timers[0]
|
|
67
|
+
if (!next || next.fireAt > now) break
|
|
68
|
+
if (next.repeat != null) {
|
|
69
|
+
next.fireAt += next.repeat
|
|
70
|
+
next.fn()
|
|
71
|
+
} else {
|
|
72
|
+
timers.shift()
|
|
73
|
+
next.fn()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { driver, emits, advance, getNow: () => now }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function enqueue(chatId: string): SessionEvent {
|
|
82
|
+
return {
|
|
83
|
+
kind: 'enqueue',
|
|
84
|
+
chatId,
|
|
85
|
+
messageId: String(nextMsgId++),
|
|
86
|
+
threadId: null,
|
|
87
|
+
rawContent: `<channel chat_id="${chatId}">go</channel>`,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe('progress-card-driver: all close paths converge on identical final state', () => {
|
|
92
|
+
it("'turn-end' path: chats empty, baseTurnSeqs cleaned, heartbeat stopped", () => {
|
|
93
|
+
const { driver } = harness()
|
|
94
|
+
const maps = driver._debugGetMaps!()
|
|
95
|
+
|
|
96
|
+
driver.ingest(enqueue('cA'), null)
|
|
97
|
+
expect(maps.chats.size).toBe(1)
|
|
98
|
+
expect(maps.baseTurnSeqs.has('cA')).toBe(true)
|
|
99
|
+
|
|
100
|
+
driver.ingest({ kind: 'turn_end', durationMs: 50 }, 'cA')
|
|
101
|
+
|
|
102
|
+
expect(maps.chats.size).toBe(0)
|
|
103
|
+
expect(maps.baseTurnSeqs.has('cA')).toBe(false)
|
|
104
|
+
expect(maps.chatRunningSubagents.has('cA')).toBe(false)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it("'zombie' path (heartbeat maxIdle ceiling): same convergence + pendingSyncEchoes preserved", () => {
|
|
108
|
+
const { driver, advance } = harness({ maxIdleMs: 5_000 })
|
|
109
|
+
const maps = driver._debugGetMaps!()
|
|
110
|
+
|
|
111
|
+
driver.ingest(enqueue('cA'), null)
|
|
112
|
+
expect(maps.chats.size).toBe(1)
|
|
113
|
+
|
|
114
|
+
// Seed a pending sync-echo so we can assert the zombie path leaves it
|
|
115
|
+
// in place (the echo may still arrive after close).
|
|
116
|
+
maps.pendingSyncEchoes.set('cA:fake', 1000)
|
|
117
|
+
|
|
118
|
+
// Idle past maxIdleMs so the heartbeat reclassifies the card as zombie.
|
|
119
|
+
advance(20_000)
|
|
120
|
+
|
|
121
|
+
expect(maps.chats.size).toBe(0)
|
|
122
|
+
expect(maps.baseTurnSeqs.has('cA')).toBe(false)
|
|
123
|
+
expect(maps.chatRunningSubagents.has('cA')).toBe(false)
|
|
124
|
+
// CRITICAL invariant: zombie close must NOT clear pendingSyncEchoes.
|
|
125
|
+
// The dedup map's TTL eviction (maybeEvict) reaps it later.
|
|
126
|
+
expect(maps.pendingSyncEchoes.has('cA:fake')).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("'zombie' path also force-closes running sub-agents (sync registry drained)", () => {
|
|
130
|
+
const { driver, advance } = harness({ maxIdleMs: 5_000 })
|
|
131
|
+
const maps = driver._debugGetMaps!()
|
|
132
|
+
|
|
133
|
+
driver.ingest(enqueue('cA'), null)
|
|
134
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'sa1', firstPromptText: 'work' }, 'cA')
|
|
135
|
+
expect(maps.chats.size).toBe(1)
|
|
136
|
+
|
|
137
|
+
// Idle past maxIdleMs without ever reporting sub_agent_turn_end.
|
|
138
|
+
advance(20_000)
|
|
139
|
+
|
|
140
|
+
expect(maps.chats.size).toBe(0)
|
|
141
|
+
// Issue #399: sync registry must be drained even when sub-agents
|
|
142
|
+
// never reported their own turn_end.
|
|
143
|
+
expect(maps.chatRunningSubagents.has('cA')).toBe(false)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it("'stalled' path (Gap-8 deferred-completion timeout): same convergence", () => {
|
|
147
|
+
const { driver, advance } = harness({
|
|
148
|
+
maxIdleMs: 999_999, // disable zombie ceiling so we hit the stalled branch
|
|
149
|
+
deferredCompletionTimeoutMs: 5_000,
|
|
150
|
+
})
|
|
151
|
+
const maps = driver._debugGetMaps!()
|
|
152
|
+
|
|
153
|
+
driver.ingest(enqueue('cA'), null)
|
|
154
|
+
// Spawn a background sub-agent so parent turn_end defers instead of
|
|
155
|
+
// closing immediately.
|
|
156
|
+
driver.ingest(
|
|
157
|
+
{
|
|
158
|
+
kind: 'tool_use',
|
|
159
|
+
toolName: 'Agent',
|
|
160
|
+
toolUseId: 'tu1',
|
|
161
|
+
input: { prompt: 'bg', run_in_background: true },
|
|
162
|
+
},
|
|
163
|
+
'cA',
|
|
164
|
+
)
|
|
165
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'sa1', firstPromptText: 'bg' }, 'cA')
|
|
166
|
+
driver.ingest({ kind: 'turn_end', durationMs: 100 }, 'cA')
|
|
167
|
+
// After parent turn_end: card alive in pendingCompletion.
|
|
168
|
+
expect(maps.chats.size).toBe(1)
|
|
169
|
+
|
|
170
|
+
// Sub-agent never reports done; advance past the deferred timeout so
|
|
171
|
+
// the heartbeat's stalled-cards branch fires.
|
|
172
|
+
advance(15_000)
|
|
173
|
+
|
|
174
|
+
expect(maps.chats.size).toBe(0)
|
|
175
|
+
expect(maps.baseTurnSeqs.has('cA')).toBe(false)
|
|
176
|
+
expect(maps.chatRunningSubagents.has('cA')).toBe(false)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('all three paths fire onTurnComplete callback exactly once', () => {
|
|
180
|
+
// The completion callback is the externally-visible side-effect that
|
|
181
|
+
// gates everything downstream (Stop hook, summary writer). Every
|
|
182
|
+
// close path must fire it; the unified path makes that automatic
|
|
183
|
+
// because the cleanup tail in completeTurnFully gates on
|
|
184
|
+
// completionFired.
|
|
185
|
+
const calls: string[] = []
|
|
186
|
+
const opts = {
|
|
187
|
+
onTurnComplete: (a: { turnKey: string }) => {
|
|
188
|
+
calls.push(a.turnKey)
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
let now = 1000
|
|
192
|
+
const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
|
|
193
|
+
let nextRef = 0
|
|
194
|
+
const driver = createProgressDriver({
|
|
195
|
+
emit: () => {},
|
|
196
|
+
minIntervalMs: 0,
|
|
197
|
+
coalesceMs: 0,
|
|
198
|
+
initialDelayMs: 0,
|
|
199
|
+
heartbeatMs: 1_000,
|
|
200
|
+
maxIdleMs: 5_000,
|
|
201
|
+
deferredCompletionTimeoutMs: 5_000,
|
|
202
|
+
now: () => now,
|
|
203
|
+
setTimeout: (fn, ms) => {
|
|
204
|
+
const ref = nextRef++
|
|
205
|
+
timers.push({ fireAt: now + ms, fn, ref })
|
|
206
|
+
return { ref }
|
|
207
|
+
},
|
|
208
|
+
clearTimeout: (h) => {
|
|
209
|
+
const ref = (h as { ref: number }).ref
|
|
210
|
+
const idx = timers.findIndex((t) => t.ref === ref)
|
|
211
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
212
|
+
},
|
|
213
|
+
setInterval: (fn, ms) => {
|
|
214
|
+
const ref = nextRef++
|
|
215
|
+
timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
|
|
216
|
+
return { ref }
|
|
217
|
+
},
|
|
218
|
+
clearInterval: (h) => {
|
|
219
|
+
const ref = (h as { ref: number }).ref
|
|
220
|
+
const idx = timers.findIndex((t) => t.ref === ref)
|
|
221
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
222
|
+
},
|
|
223
|
+
...opts,
|
|
224
|
+
})
|
|
225
|
+
const advance = (ms: number): void => {
|
|
226
|
+
now += ms
|
|
227
|
+
for (;;) {
|
|
228
|
+
timers.sort((a, b) => a.fireAt - b.fireAt)
|
|
229
|
+
const next = timers[0]
|
|
230
|
+
if (!next || next.fireAt > now) break
|
|
231
|
+
if (next.repeat != null) {
|
|
232
|
+
next.fireAt += next.repeat
|
|
233
|
+
next.fn()
|
|
234
|
+
} else {
|
|
235
|
+
timers.shift()
|
|
236
|
+
next.fn()
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// turn-end
|
|
242
|
+
driver.ingest(enqueue('cA'), null)
|
|
243
|
+
driver.ingest({ kind: 'turn_end', durationMs: 10 }, 'cA')
|
|
244
|
+
// zombie
|
|
245
|
+
driver.ingest(enqueue('cB'), null)
|
|
246
|
+
advance(20_000)
|
|
247
|
+
// stalled (after time has advanced to satisfy the deferred timeout)
|
|
248
|
+
driver.ingest(enqueue('cC'), null)
|
|
249
|
+
driver.ingest(
|
|
250
|
+
{
|
|
251
|
+
kind: 'tool_use',
|
|
252
|
+
toolName: 'Agent',
|
|
253
|
+
toolUseId: 'tu',
|
|
254
|
+
input: { prompt: 'bg', run_in_background: true },
|
|
255
|
+
},
|
|
256
|
+
'cC',
|
|
257
|
+
)
|
|
258
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'sa', firstPromptText: 'bg' }, 'cC')
|
|
259
|
+
driver.ingest({ kind: 'turn_end', durationMs: 10 }, 'cC')
|
|
260
|
+
advance(20_000)
|
|
261
|
+
|
|
262
|
+
// Each chat got exactly one completion callback.
|
|
263
|
+
const byChat = new Map<string, number>()
|
|
264
|
+
for (const tk of calls) {
|
|
265
|
+
const chat = tk.split(':')[0]
|
|
266
|
+
byChat.set(chat, (byChat.get(chat) ?? 0) + 1)
|
|
267
|
+
}
|
|
268
|
+
expect(byChat.get('cA')).toBe(1)
|
|
269
|
+
expect(byChat.get('cB')).toBe(1)
|
|
270
|
+
expect(byChat.get('cC')).toBe(1)
|
|
271
|
+
})
|
|
272
|
+
})
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for issue #334 — cross-turn sub-agent visibility.
|
|
3
|
+
*
|
|
4
|
+
* A background sub-agent dispatched in turn N (via Agent({run_in_background:true}))
|
|
5
|
+
* must remain visible on the new progress card that appears when turn N+1 starts.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from 'vitest'
|
|
8
|
+
import { createProgressDriver } from '../progress-card-driver.js'
|
|
9
|
+
import type { SessionEvent } from '../session-tail.js'
|
|
10
|
+
|
|
11
|
+
let nextMsgId = 100
|
|
12
|
+
|
|
13
|
+
function harness(
|
|
14
|
+
initialDelayMs = 0,
|
|
15
|
+
opts: { coldSubAgentThresholdMs?: number; heartbeatMs?: number } = {},
|
|
16
|
+
) {
|
|
17
|
+
let now = 1000
|
|
18
|
+
const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
|
|
19
|
+
let nextRef = 0
|
|
20
|
+
const emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }> = []
|
|
21
|
+
|
|
22
|
+
const driver = createProgressDriver({
|
|
23
|
+
emit: (a) => emits.push(a),
|
|
24
|
+
minIntervalMs: 0,
|
|
25
|
+
coalesceMs: 0,
|
|
26
|
+
initialDelayMs,
|
|
27
|
+
coldSubAgentThresholdMs: opts.coldSubAgentThresholdMs,
|
|
28
|
+
heartbeatMs: opts.heartbeatMs,
|
|
29
|
+
now: () => now,
|
|
30
|
+
setTimeout: (fn, ms) => {
|
|
31
|
+
const ref = nextRef++
|
|
32
|
+
timers.push({ fireAt: now + ms, fn, ref })
|
|
33
|
+
return { ref }
|
|
34
|
+
},
|
|
35
|
+
clearTimeout: (handle) => {
|
|
36
|
+
const target = (handle as { ref: number }).ref
|
|
37
|
+
const idx = timers.findIndex((t) => t.ref === target)
|
|
38
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
39
|
+
},
|
|
40
|
+
setInterval: (fn, ms) => {
|
|
41
|
+
const ref = nextRef++
|
|
42
|
+
timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
|
|
43
|
+
return { ref }
|
|
44
|
+
},
|
|
45
|
+
clearInterval: (handle) => {
|
|
46
|
+
const target = (handle as { ref: number }).ref
|
|
47
|
+
const idx = timers.findIndex((t) => t.ref === target)
|
|
48
|
+
if (idx !== -1) timers.splice(idx, 1)
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const advance = (ms: number): void => {
|
|
53
|
+
now += ms
|
|
54
|
+
for (;;) {
|
|
55
|
+
timers.sort((a, b) => a.fireAt - b.fireAt)
|
|
56
|
+
const next = timers[0]
|
|
57
|
+
if (!next || next.fireAt > now) break
|
|
58
|
+
if (next.repeat != null) {
|
|
59
|
+
next.fireAt += next.repeat
|
|
60
|
+
next.fn()
|
|
61
|
+
} else {
|
|
62
|
+
timers.shift()
|
|
63
|
+
next.fn()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { driver, emits, advance }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function enqueue(chatId: string, text = 'hi'): SessionEvent {
|
|
72
|
+
return {
|
|
73
|
+
kind: 'enqueue',
|
|
74
|
+
chatId,
|
|
75
|
+
messageId: String(nextMsgId++),
|
|
76
|
+
threadId: null,
|
|
77
|
+
rawContent: `<channel chat_id="${chatId}">${text}</channel>`,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('cross-turn sub-agent visibility (#334)', () => {
|
|
82
|
+
it('Test 1: closeZombie on turn-1 force-close removes sub-agent from registry (fix #399)', () => {
|
|
83
|
+
// When turn 2 starts while turn 1 has a pending background sub-agent,
|
|
84
|
+
// the ingest enqueue path calls closeZombie on turn 1's card. closeZombie
|
|
85
|
+
// explicitly abandons all running sub-agents (marks them done for display),
|
|
86
|
+
// and — after fix #399 — also removes them from chatRunningSubagents.
|
|
87
|
+
// Therefore turn 2 starts clean (no carry-over of abandoned agents).
|
|
88
|
+
const { driver } = harness()
|
|
89
|
+
|
|
90
|
+
// Turn 1: dispatch a background sub-agent, then turn ends.
|
|
91
|
+
driver.ingest(enqueue('c1'), null)
|
|
92
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'bg-agent', firstPromptText: 'do work' }, 'c1')
|
|
93
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
94
|
+
|
|
95
|
+
// Turn 2 starts — triggers closeZombie on turn 1 → removes bg-agent from registry.
|
|
96
|
+
driver.startTurn({ chatId: 'c1', userText: 'new prompt' })
|
|
97
|
+
|
|
98
|
+
const turn2State = driver.peek('c1', undefined)
|
|
99
|
+
expect(turn2State).toBeDefined()
|
|
100
|
+
// bg-agent was abandoned by closeZombie; it must NOT carry over into turn 2.
|
|
101
|
+
expect(turn2State!.subAgents.has('bg-agent')).toBe(false)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('Test 2: sub-agent finishing naturally before new turn does not appear on turn 2', () => {
|
|
105
|
+
// When a sub-agent finishes via sub_agent_turn_end (natural completion),
|
|
106
|
+
// it is removed from chatRunningSubagents by the ingest sync (fix #399
|
|
107
|
+
// also keeps this path correct). Turn 2 starts clean.
|
|
108
|
+
const { driver } = harness()
|
|
109
|
+
|
|
110
|
+
// Turn 1: dispatch background sub-agent.
|
|
111
|
+
driver.ingest(enqueue('c1'), null)
|
|
112
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'bg-agent', firstPromptText: 'do work' }, 'c1')
|
|
113
|
+
// Sub-agent finishes naturally before turn 2 starts.
|
|
114
|
+
driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'bg-agent', durationMs: 5000 }, 'c1')
|
|
115
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
116
|
+
|
|
117
|
+
// Turn 2 starts.
|
|
118
|
+
driver.startTurn({ chatId: 'c1', userText: 'next prompt' })
|
|
119
|
+
|
|
120
|
+
const turn2State = driver.peek('c1', undefined)
|
|
121
|
+
expect(turn2State).toBeDefined()
|
|
122
|
+
// bg-agent finished before turn 2 — must NOT appear.
|
|
123
|
+
expect(turn2State!.subAgents.has('bg-agent')).toBe(false)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('Test 3: foreground sub-agent (completes mid-turn 1) does NOT appear on turn 2', () => {
|
|
127
|
+
const { driver } = harness()
|
|
128
|
+
|
|
129
|
+
// Turn 1: foreground sub-agent — starts and finishes before turn ends.
|
|
130
|
+
driver.ingest(enqueue('c1'), null)
|
|
131
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'fg-agent', firstPromptText: 'quick task' }, 'c1')
|
|
132
|
+
driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'fg-agent', durationMs: 200 }, 'c1')
|
|
133
|
+
driver.ingest({ kind: 'turn_end', durationMs: 800 }, 'c1')
|
|
134
|
+
|
|
135
|
+
// Turn 2 starts.
|
|
136
|
+
driver.startTurn({ chatId: 'c1', userText: 'next prompt' })
|
|
137
|
+
|
|
138
|
+
const turn2State = driver.peek('c1', undefined)
|
|
139
|
+
expect(turn2State).toBeDefined()
|
|
140
|
+
// Foreground sub-agent completed in turn 1 — must NOT bleed into turn 2.
|
|
141
|
+
expect(turn2State!.subAgents.has('fg-agent')).toBe(false)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('multiple background sub-agents: closeZombie removes all from registry (fix #399)', () => {
|
|
145
|
+
// When closeZombie abandons all running sub-agents, they are all removed
|
|
146
|
+
// from chatRunningSubagents. Turn 2 starts with an empty sub-agent map.
|
|
147
|
+
const { driver } = harness()
|
|
148
|
+
|
|
149
|
+
driver.ingest(enqueue('c1'), null)
|
|
150
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'bg1', firstPromptText: 'task 1' }, 'c1')
|
|
151
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'bg2', firstPromptText: 'task 2' }, 'c1')
|
|
152
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
153
|
+
|
|
154
|
+
// New turn triggers closeZombie → all running agents marked done → removed from registry.
|
|
155
|
+
driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
|
|
156
|
+
|
|
157
|
+
const state = driver.peek('c1', undefined)
|
|
158
|
+
// Both abandoned agents must NOT carry over.
|
|
159
|
+
expect(state!.subAgents.has('bg1')).toBe(false)
|
|
160
|
+
expect(state!.subAgents.has('bg2')).toBe(false)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('different chats do not cross-contaminate', () => {
|
|
164
|
+
const { driver } = harness()
|
|
165
|
+
|
|
166
|
+
// Chat A has a background sub-agent.
|
|
167
|
+
driver.ingest(enqueue('chatA'), null)
|
|
168
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'agentA', firstPromptText: 'A' }, 'chatA')
|
|
169
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'chatA')
|
|
170
|
+
|
|
171
|
+
// Chat B starts a new turn (no sub-agents in chat B).
|
|
172
|
+
driver.startTurn({ chatId: 'chatB', userText: 'hello' })
|
|
173
|
+
|
|
174
|
+
const stateB = driver.peek('chatB', undefined)
|
|
175
|
+
expect(stateB!.subAgents.has('agentA')).toBe(false)
|
|
176
|
+
expect(stateB!.subAgents.size).toBe(0)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('cold-jsonl-synth path syncs registry: turn 2 does NOT inherit cold-synth-terminated agent (fix #399)', () => {
|
|
180
|
+
// Forensic case from the live klanker bug: sub-agent ada7c3d07c28158f5
|
|
181
|
+
// hit its turn limit mid-tool-call and never wrote system.turn_duration.
|
|
182
|
+
// The cold-jsonl-synth heartbeat path (Gap 4 #313) marks it done
|
|
183
|
+
// synthetically. BEFORE fix #399 the registry was never synced from
|
|
184
|
+
// this path, so the agent appeared as a phantom on every subsequent
|
|
185
|
+
// turn's card. AFTER fix #399 the registry is synced and turn 2 is clean.
|
|
186
|
+
const { driver, advance } = harness(0, { coldSubAgentThresholdMs: 30_000, heartbeatMs: 5_000 })
|
|
187
|
+
|
|
188
|
+
// Turn 1: dispatch background sub-agent, parent turn ends → pendingCompletion=true
|
|
189
|
+
driver.ingest(enqueue('c1'), null)
|
|
190
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'cold-agent', firstPromptText: 'long task' }, 'c1')
|
|
191
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
192
|
+
|
|
193
|
+
// Sub-agent goes cold (no events for > coldSubAgentThresholdMs).
|
|
194
|
+
// Heartbeat ticks fire repeatedly; once lastEventAt is older than the
|
|
195
|
+
// threshold, the cold-jsonl-synth path runs and synthesises sub_agent_turn_end.
|
|
196
|
+
advance(35_000)
|
|
197
|
+
|
|
198
|
+
// Turn 2 starts in the same chat. WITHOUT #399's fix, cold-agent would
|
|
199
|
+
// re-seed into turn 2's PerChatState.subAgents (the bug). WITH the fix,
|
|
200
|
+
// syncChatRunningSubagents fired from the cold-synth path and removed it.
|
|
201
|
+
driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
|
|
202
|
+
|
|
203
|
+
const turn2State = driver.peek('c1', undefined)
|
|
204
|
+
expect(turn2State).toBeDefined()
|
|
205
|
+
expect(turn2State!.subAgents.has('cold-agent')).toBe(false)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('counter-test: still-running background sub-agent DOES carry over (preserves #334)', () => {
|
|
209
|
+
// The carry-over feature from #334 must continue to work for legitimate
|
|
210
|
+
// still-running sub-agents. If syncChatRunningSubagents over-removes,
|
|
211
|
+
// this test catches the regression. Asserts:
|
|
212
|
+
// 1. A bg sub-agent that started in turn 1 and never went terminal
|
|
213
|
+
// 2. After turn 2 starts (closeZombie fires on turn 1's card), the
|
|
214
|
+
// sub-agent is correctly REMOVED (closeZombie marks it done)
|
|
215
|
+
// Since closeZombie is the post-turn-1 cleanup path, "still running
|
|
216
|
+
// across turns" actually means "running while turn 1 is in pendingCompletion
|
|
217
|
+
// BEFORE turn 2 enqueues". The carry-over visibility happens during the
|
|
218
|
+
// pendingCompletion window — verified here by peeking BEFORE turn 2.
|
|
219
|
+
const { driver } = harness()
|
|
220
|
+
|
|
221
|
+
driver.ingest(enqueue('c1'), null)
|
|
222
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'still-running', firstPromptText: 'long task' }, 'c1')
|
|
223
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
224
|
+
|
|
225
|
+
// During pendingCompletion the sub-agent is visible on the card.
|
|
226
|
+
const duringPending = driver.peek('c1', undefined)
|
|
227
|
+
expect(duringPending).toBeDefined()
|
|
228
|
+
expect(duringPending!.subAgents.has('still-running')).toBe(true)
|
|
229
|
+
expect(duringPending!.subAgents.get('still-running')?.state).toBe('running')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('sub-agent finishes naturally between turns: turn 3 starts clean', () => {
|
|
233
|
+
// Verifies that a sub-agent finishing via sub_agent_turn_end (natural
|
|
234
|
+
// completion via the ingest path) is removed from chatRunningSubagents
|
|
235
|
+
// so subsequent turns do not see it.
|
|
236
|
+
const { driver } = harness()
|
|
237
|
+
|
|
238
|
+
// Turn 1: background sub-agent dispatched.
|
|
239
|
+
driver.ingest(enqueue('c1'), null)
|
|
240
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'bg1', firstPromptText: 'shared?' }, 'c1')
|
|
241
|
+
driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
|
|
242
|
+
|
|
243
|
+
// Sub-agent finishes naturally BEFORE turn 2 starts.
|
|
244
|
+
driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'bg1', durationMs: 3000 }, 'c1')
|
|
245
|
+
|
|
246
|
+
// Turn 2 starts — bg1 already finished, so registry is empty.
|
|
247
|
+
driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
|
|
248
|
+
expect(driver.peek('c1', undefined)!.subAgents.has('bg1')).toBe(false)
|
|
249
|
+
|
|
250
|
+
driver.ingest({ kind: 'turn_end', durationMs: 1000 }, 'c1')
|
|
251
|
+
|
|
252
|
+
// Turn 3: the finished sub-agent must NOT appear.
|
|
253
|
+
driver.startTurn({ chatId: 'c1', userText: 'turn 3' })
|
|
254
|
+
const stateT3 = driver.peek('c1', undefined)
|
|
255
|
+
expect(stateT3).toBeDefined()
|
|
256
|
+
expect(stateT3!.subAgents.has('bg1')).toBe(false)
|
|
257
|
+
})
|
|
258
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR-C2 — `dispose({ preservePending: true })` must NOT remove chats
|
|
3
|
+
* whose `pendingCompletion === true`.
|
|
4
|
+
*
|
|
5
|
+
* Regression: commit 4c0186d introduced a dispose() that wiped all
|
|
6
|
+
* in-flight card state on every bridge disconnect. The selective
|
|
7
|
+
* dispose path was added to keep cards with running background
|
|
8
|
+
* sub-agents alive across the disconnect/reconnect cycle.
|
|
9
|
+
*
|
|
10
|
+
* fails when: dispose's preservePending branch unconditionally clears
|
|
11
|
+
* `chats`, OR forgets to leave the heartbeat running while a pending
|
|
12
|
+
* chat survives.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect } from 'vitest'
|
|
15
|
+
import { makeHarness, enqueue } from './_progress-card-harness.js'
|
|
16
|
+
|
|
17
|
+
describe('PR-C2: dispose({ preservePending: true })', () => {
|
|
18
|
+
it('chat with pendingCompletion survives dispose; heartbeat-driven completion still fires after a "reconnect"', () => {
|
|
19
|
+
const completions: string[] = []
|
|
20
|
+
const { driver, advance } = makeHarness({
|
|
21
|
+
minIntervalMs: 0,
|
|
22
|
+
coalesceMs: 0,
|
|
23
|
+
heartbeatMs: 1_000,
|
|
24
|
+
maxIdleMs: 999_999,
|
|
25
|
+
deferredCompletionTimeoutMs: 5_000,
|
|
26
|
+
promoteAfterMs: 999_999,
|
|
27
|
+
onTurnComplete: (s) => completions.push(s.turnKey),
|
|
28
|
+
})
|
|
29
|
+
const maps = driver._debugGetMaps!()
|
|
30
|
+
const CHAT = 'cA'
|
|
31
|
+
|
|
32
|
+
// Set up a turn with a background sub-agent so parent turn_end
|
|
33
|
+
// produces pendingCompletion=true.
|
|
34
|
+
driver.ingest(enqueue(CHAT), null)
|
|
35
|
+
driver.ingest(
|
|
36
|
+
{
|
|
37
|
+
kind: 'tool_use',
|
|
38
|
+
toolName: 'Agent',
|
|
39
|
+
toolUseId: 'tu1',
|
|
40
|
+
input: { prompt: 'bg', run_in_background: true },
|
|
41
|
+
},
|
|
42
|
+
CHAT,
|
|
43
|
+
)
|
|
44
|
+
driver.ingest({ kind: 'sub_agent_started', agentId: 'saBG', firstPromptText: 'bg' }, CHAT)
|
|
45
|
+
driver.ingest({ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__reply' }, CHAT)
|
|
46
|
+
driver.recordOutboundDelivered(CHAT)
|
|
47
|
+
driver.ingest({ kind: 'turn_end', durationMs: 100 }, CHAT)
|
|
48
|
+
|
|
49
|
+
// Confirm pendingCompletion shape.
|
|
50
|
+
expect(maps.chats.size).toBe(1)
|
|
51
|
+
const csBefore = [...maps.chats.values()][0] as { pendingCompletion: boolean }
|
|
52
|
+
expect(csBefore.pendingCompletion).toBe(true)
|
|
53
|
+
|
|
54
|
+
// Bridge disconnect: dispose preserving pending.
|
|
55
|
+
driver.dispose!({ preservePending: true })
|
|
56
|
+
|
|
57
|
+
// Chat must survive.
|
|
58
|
+
expect(maps.chats.size).toBe(1)
|
|
59
|
+
const csAfter = [...maps.chats.values()][0] as { pendingCompletion: boolean }
|
|
60
|
+
expect(csAfter.pendingCompletion).toBe(true)
|
|
61
|
+
|
|
62
|
+
// Now simulate "bridge reconnect" — nothing to do at the driver level
|
|
63
|
+
// for that, but the heartbeat must still be wired so the deferred
|
|
64
|
+
// completion timeout eventually fires.
|
|
65
|
+
advance(15_000)
|
|
66
|
+
|
|
67
|
+
// Stalled-cards heartbeat branch should have closed the chat by now.
|
|
68
|
+
expect(maps.chats.size).toBe(0)
|
|
69
|
+
expect(completions.length).toBe(1)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('chats WITHOUT pendingCompletion are dropped by preservePending dispose', () => {
|
|
73
|
+
const { driver } = makeHarness()
|
|
74
|
+
const maps = driver._debugGetMaps!()
|
|
75
|
+
driver.ingest(enqueue('cActive'), null)
|
|
76
|
+
expect(maps.chats.size).toBe(1)
|
|
77
|
+
|
|
78
|
+
driver.dispose!({ preservePending: true })
|
|
79
|
+
expect(maps.chats.size).toBe(0)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pin the wiring for #354's PROGRESS_CARD_DRAFT_TRANSPORT env flag.
|
|
3
|
+
*
|
|
4
|
+
* Two regressions this guards against:
|
|
5
|
+
*
|
|
6
|
+
* 1. The flag drifting silently — someone refactors the progress-
|
|
7
|
+
* card emit and forgets to thread isPrivateChat / sendMessageDraft
|
|
8
|
+
* when the flag is on. Result: the card stays on the legacy edit
|
|
9
|
+
* path even though the operator opted in.
|
|
10
|
+
*
|
|
11
|
+
* 2. The flag turning on by default before the spike unknowns are
|
|
12
|
+
* validated. Default-OFF is load-bearing: until we know drafts
|
|
13
|
+
* can be pinned and survive bot crashes, the legacy path is the
|
|
14
|
+
* safe one.
|
|
15
|
+
*
|
|
16
|
+
* Source-level pinning rather than behavioural — the gateway emit
|
|
17
|
+
* lives inside the bot startup wiring and is hard to drive in
|
|
18
|
+
* isolation. The contract here is the literal env-var check + the
|
|
19
|
+
* fact that draft deps are conditional on it.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { describe, it, expect } from 'vitest'
|
|
23
|
+
import { readFileSync } from 'node:fs'
|
|
24
|
+
import { resolve } from 'node:path'
|
|
25
|
+
|
|
26
|
+
const gatewaySrc = readFileSync(
|
|
27
|
+
resolve(__dirname, '..', 'gateway', 'gateway.ts'),
|
|
28
|
+
'utf-8',
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
describe('progress-card draft transport flag (#354)', () => {
|
|
32
|
+
it('is gated behind PROGRESS_CARD_DRAFT_TRANSPORT=1 (default OFF)', () => {
|
|
33
|
+
// The flag check must be an explicit `=== '1'` not a truthy check
|
|
34
|
+
// — `process.env.X === '1'` is the project convention, and a
|
|
35
|
+
// truthy check would mis-fire on `=0` or `=false`.
|
|
36
|
+
expect(gatewaySrc).toMatch(
|
|
37
|
+
/process\.env\.PROGRESS_CARD_DRAFT_TRANSPORT\s*===\s*['"]1['"]/,
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('only enables draft when the chat is a DM (no threads, isDmChatId)', () => {
|
|
42
|
+
// Drafts don't support forum topics. The flag-check block must
|
|
43
|
+
// gate on isDmChatId(chatId) and threadId == null — otherwise a
|
|
44
|
+
// forum-topic message would be sent via draft and Telegram would
|
|
45
|
+
// reject with DRAFT_CHAT_UNSUPPORTED.
|
|
46
|
+
const block = extractDraftBlock(gatewaySrc)
|
|
47
|
+
expect(block).toMatch(/isDmChatId\(chatId\)/)
|
|
48
|
+
expect(block).toMatch(/threadId\s*==\s*null/)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('passes both isPrivateChat AND sendMessageDraft when eligible', () => {
|
|
52
|
+
// Without sendMessageDraft, stream-reply-handler resolves transport
|
|
53
|
+
// to 'message' (line 432: `isForumTopic || deps.sendMessageDraft == null`).
|
|
54
|
+
// Both deps must be threaded together for the draft path to fire.
|
|
55
|
+
const block = extractDraftBlock(gatewaySrc)
|
|
56
|
+
expect(block).toMatch(/isPrivateChat:\s*true/)
|
|
57
|
+
expect(block).toMatch(/sendMessageDraft:\s*sendMessageDraftFn/)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('documents the spike unknowns from #354 inline so an operator can validate', () => {
|
|
61
|
+
// The flag exists because pinning + crash behavior are unverified.
|
|
62
|
+
// The block comment must call out both so a future contributor
|
|
63
|
+
// doesn't flip the default to ON without doing the spike first.
|
|
64
|
+
const block = extractDraftBlock(gatewaySrc)
|
|
65
|
+
expect(block.toLowerCase()).toMatch(/pin/)
|
|
66
|
+
expect(block.toLowerCase()).toMatch(/spike|unknown/)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
/** Pull the #354 spike block out of gateway.ts for source-level assertions. */
|
|
71
|
+
function extractDraftBlock(src: string): string {
|
|
72
|
+
// The block starts at the spike comment marker and ends at the next
|
|
73
|
+
// `handleStreamReply(` invocation. Big enough to span the gate +
|
|
74
|
+
// the conditional draft-deps spread.
|
|
75
|
+
const start = src.indexOf('// #354 spike')
|
|
76
|
+
expect(start, '#354 spike block not found in gateway.ts').toBeGreaterThan(0)
|
|
77
|
+
const end = src.indexOf(').then', start)
|
|
78
|
+
expect(end, '#354 spike block end not found').toBeGreaterThan(start)
|
|
79
|
+
return src.slice(start, end)
|
|
80
|
+
}
|