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,103 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
import { createAnswerStream, __resetDraftIdForTests } from '../answer-stream.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* #656 — gateway turn_end no-reply path.
|
|
6
|
+
*
|
|
7
|
+
* Background: gateway.ts at the turn_end handler used to materialize() the
|
|
8
|
+
* answer-lane stream when no reply tool was called, in parallel with the
|
|
9
|
+
* turn-flush emitter (gateway.ts ~3475). Both gates fire on the same
|
|
10
|
+
* `!replyCalled && capturedText.length > 0` condition, no dedup between
|
|
11
|
+
* them, and materialize() posts the raw model text without HTML conversion
|
|
12
|
+
* — producing a visible duplicate where one copy shows raw <b> tags and
|
|
13
|
+
* the other renders cleanly.
|
|
14
|
+
*
|
|
15
|
+
* Fix: drop the materialize branch entirely. Always retract() the
|
|
16
|
+
* answer-stream at turn_end. Turn-flush is the sole canonical emitter for
|
|
17
|
+
* no-reply turns (it runs markdownToHtml + records to outboundDedup).
|
|
18
|
+
*
|
|
19
|
+
* This test pins the contract that retract() does NOT emit a fresh
|
|
20
|
+
* sendMessage — only deletes any preliminary message previously sent. The
|
|
21
|
+
* gateway's no-reply path now relies on this property.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
async function flushMicrotasks(times = 10): Promise<void> {
|
|
25
|
+
for (let i = 0; i < times; i++) {
|
|
26
|
+
await Promise.resolve()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let nextMessageId = 5000
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
__resetDraftIdForTests()
|
|
34
|
+
nextMessageId = 5000
|
|
35
|
+
vi.useFakeTimers()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
vi.useRealTimers()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('#656 — answer-stream retract() at turn_end emits nothing', () => {
|
|
43
|
+
it('retract before any preliminary send: no sendMessage, no deleteMessage', async () => {
|
|
44
|
+
const sendMessage = vi.fn(async () => ({ message_id: nextMessageId++ }))
|
|
45
|
+
const editMessageText = vi.fn(async () => {})
|
|
46
|
+
const deleteMessage = vi.fn(async () => {})
|
|
47
|
+
|
|
48
|
+
const stream = createAnswerStream({
|
|
49
|
+
chatId: 'chat-no-reply',
|
|
50
|
+
isPrivateChat: false,
|
|
51
|
+
minInitialChars: 400,
|
|
52
|
+
throttleMs: 250,
|
|
53
|
+
sendMessage: sendMessage as never,
|
|
54
|
+
editMessageText: editMessageText as never,
|
|
55
|
+
deleteMessage: deleteMessage as never,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Seed a sub-threshold blob so nothing has been sent yet
|
|
59
|
+
stream.update('x'.repeat(50))
|
|
60
|
+
await flushMicrotasks()
|
|
61
|
+
expect(sendMessage).not.toHaveBeenCalled()
|
|
62
|
+
|
|
63
|
+
await stream.retract()
|
|
64
|
+
|
|
65
|
+
// Critically: retract() must NOT emit a fresh sendMessage. The gateway's
|
|
66
|
+
// no-reply path delegates the user-visible emit to turn-flush.
|
|
67
|
+
expect(sendMessage).not.toHaveBeenCalled()
|
|
68
|
+
expect(deleteMessage).not.toHaveBeenCalled()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('retract after a preliminary send: deletes the prelim, no fresh sendMessage', async () => {
|
|
72
|
+
const THROTTLE = 1000
|
|
73
|
+
const sendMessage = vi.fn(async () => ({ message_id: nextMessageId++ }))
|
|
74
|
+
const editMessageText = vi.fn(async () => {})
|
|
75
|
+
const deleteMessage = vi.fn(async () => {})
|
|
76
|
+
|
|
77
|
+
const stream = createAnswerStream({
|
|
78
|
+
chatId: 'chat-no-reply',
|
|
79
|
+
isPrivateChat: false,
|
|
80
|
+
minInitialChars: 10,
|
|
81
|
+
throttleMs: THROTTLE,
|
|
82
|
+
sendMessage: sendMessage as never,
|
|
83
|
+
editMessageText: editMessageText as never,
|
|
84
|
+
deleteMessage: deleteMessage as never,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Cross threshold so a preliminary send fires
|
|
88
|
+
stream.update('x'.repeat(50))
|
|
89
|
+
await flushMicrotasks()
|
|
90
|
+
vi.advanceTimersByTime(THROTTLE)
|
|
91
|
+
await flushMicrotasks()
|
|
92
|
+
expect(sendMessage).toHaveBeenCalledTimes(1)
|
|
93
|
+
|
|
94
|
+
const sendCallsBefore = sendMessage.mock.calls.length
|
|
95
|
+
|
|
96
|
+
await stream.retract()
|
|
97
|
+
|
|
98
|
+
// Retract should delete the preliminary message (cleanup) but must NOT
|
|
99
|
+
// emit any new sendMessage — turn-flush owns the user-visible emit.
|
|
100
|
+
expect(deleteMessage).toHaveBeenCalledTimes(1)
|
|
101
|
+
expect(sendMessage.mock.calls.length).toBe(sendCallsBefore)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Structural integration test for the secret-detect intercept in the
|
|
6
|
+
* gateway's handleInbound.
|
|
7
|
+
*
|
|
8
|
+
* Why structural: gateway/gateway.ts does not export handleInbound, so a
|
|
9
|
+
* pure-functional invocation would require either a refactor (risky for
|
|
10
|
+
* a security fix) or end-to-end harnessing of Bot/Grammy/Context. The
|
|
11
|
+
* pipeline itself is exhaustively unit-tested in
|
|
12
|
+
* `secret-detect-pipeline.test.ts` and `secret-detect.test.ts`, and the
|
|
13
|
+
* fail-closed contract has its own structural test
|
|
14
|
+
* (`secret-detect-fail-closed.test.ts`). What's left to assert here is:
|
|
15
|
+
* 1. The gateway actually wires runPipeline into handleInbound.
|
|
16
|
+
* 2. The intercept sits AFTER the auth/vault intercepts (those bypass
|
|
17
|
+
* the LLM entirely; secret-detect must run on text that survived
|
|
18
|
+
* them).
|
|
19
|
+
* 3. The intercept sits BEFORE recordInbound() and ipcServer.broadcast()
|
|
20
|
+
* (those are the sinks that would leak raw bytes to SQLite + agent).
|
|
21
|
+
* 4. Both the cached-passphrase store path and the no-passphrase
|
|
22
|
+
* deferred-warning path are present.
|
|
23
|
+
*
|
|
24
|
+
* If a future refactor moves the intercept into the wrong slot, these
|
|
25
|
+
* assertions break and the security regression is caught at build time
|
|
26
|
+
* rather than live.
|
|
27
|
+
*/
|
|
28
|
+
describe('gateway secret-detect intercept — structural wiring', () => {
|
|
29
|
+
const src = readFileSync(
|
|
30
|
+
new URL('../gateway/gateway.ts', import.meta.url),
|
|
31
|
+
'utf8',
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
it('imports the pipeline + staging modules', () => {
|
|
35
|
+
expect(src).toMatch(/from '\.\.\/secret-detect\/pipeline\.js'/)
|
|
36
|
+
expect(src).toMatch(/from '\.\.\/secret-detect\/staging\.js'/)
|
|
37
|
+
expect(src).toMatch(/from '\.\.\/secret-detect\/vault-write\.js'/)
|
|
38
|
+
expect(src).toMatch(/from '\.\.\/secret-detect\/index\.js'/)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('invokes runPipeline inside handleInbound', () => {
|
|
42
|
+
const handleInboundIdx = src.indexOf('async function handleInbound(')
|
|
43
|
+
expect(handleInboundIdx).toBeGreaterThan(0)
|
|
44
|
+
const pipelineIdx = src.indexOf('runPipeline({', handleInboundIdx)
|
|
45
|
+
expect(pipelineIdx).toBeGreaterThan(0)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('runPipeline call sits AFTER the vault intercept', () => {
|
|
49
|
+
const vaultIdx = src.indexOf('// Vault intercept')
|
|
50
|
+
const pipelineIdx = src.indexOf('runPipeline({')
|
|
51
|
+
expect(vaultIdx).toBeGreaterThan(0)
|
|
52
|
+
expect(pipelineIdx).toBeGreaterThan(0)
|
|
53
|
+
expect(pipelineIdx).toBeGreaterThan(vaultIdx)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('runPipeline call sits BEFORE recordInbound() and ipcServer.broadcast(inboundMsg)', () => {
|
|
57
|
+
const pipelineIdx = src.indexOf('runPipeline({')
|
|
58
|
+
const recordIdx = src.indexOf('recordInbound(', pipelineIdx)
|
|
59
|
+
const broadcastIdx = src.indexOf('ipcServer.broadcast(inboundMsg)', pipelineIdx)
|
|
60
|
+
expect(pipelineIdx).toBeGreaterThan(0)
|
|
61
|
+
expect(recordIdx).toBeGreaterThan(pipelineIdx)
|
|
62
|
+
expect(broadcastIdx).toBeGreaterThan(pipelineIdx)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('cached-passphrase path: rewrites effectiveText, deletes message, posts masked summary', () => {
|
|
66
|
+
const pipelineIdx = src.indexOf('runPipeline({')
|
|
67
|
+
const tail = src.slice(pipelineIdx, pipelineIdx + 4000)
|
|
68
|
+
// Rewrites effectiveText so the broadcast carries the redacted text.
|
|
69
|
+
expect(tail).toMatch(/effectiveText = pipeRes\.rewritten_text/)
|
|
70
|
+
// Deletes the original Telegram message containing the raw bytes.
|
|
71
|
+
expect(tail).toMatch(/bot\.api\.deleteMessage\(chat_id, msgId\)/)
|
|
72
|
+
// Tells the user what was captured (masked).
|
|
73
|
+
expect(tail).toMatch(/captured \$\{pipeRes\.stored\.length\} secret/)
|
|
74
|
+
// Surfaces the masked form (s.masked is computed via maskToken in the pipeline).
|
|
75
|
+
expect(tail).toMatch(/s\.masked/)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('no-passphrase deferred path: prompts user, deletes message, returns (no broadcast)', () => {
|
|
79
|
+
// Issue #44 turned the deferred path into a one-tap inline-button
|
|
80
|
+
// flow. The structural invariants we still want to pin:
|
|
81
|
+
// 1. The deferred record is set in `deferredSecrets` so the
|
|
82
|
+
// post-unlock callback can find it.
|
|
83
|
+
// 2. The original message containing the raw bytes is deleted.
|
|
84
|
+
// 3. The path returns before falling through to recordInbound /
|
|
85
|
+
// broadcast.
|
|
86
|
+
// 4. The reply uses the deferred-secret keyboard so a one-tap
|
|
87
|
+
// unlock is offered instead of the legacy "/vault list +
|
|
88
|
+
// re-paste" instructions.
|
|
89
|
+
const pipelineIdx = src.indexOf('runPipeline({')
|
|
90
|
+
const tail = src.slice(pipelineIdx, pipelineIdx + 8000)
|
|
91
|
+
// 1. Deferred record set with the suggested slug captured up-front.
|
|
92
|
+
expect(tail).toMatch(/deferredSecrets\.set\(/)
|
|
93
|
+
expect(tail).toMatch(/suggested_slug:/)
|
|
94
|
+
// 2. The original message is deleted (so the raw bytes are scrubbed
|
|
95
|
+
// from the chat client even before the user reacts).
|
|
96
|
+
expect(tail).toMatch(/bot\.api\.deleteMessage\(chat_id, msgId\)/)
|
|
97
|
+
// 4. The new inline keyboard helper is used in lieu of the legacy
|
|
98
|
+
// plain-text "run /vault list" warning.
|
|
99
|
+
expect(tail).toMatch(/buildDeferredSecretKeyboard\(/)
|
|
100
|
+
// 3. `return` after the reply so the no-broadcast contract holds.
|
|
101
|
+
const keyboardIdx = tail.indexOf('buildDeferredSecretKeyboard(')
|
|
102
|
+
const afterKeyboard = tail.slice(keyboardIdx)
|
|
103
|
+
expect(afterKeyboard).toMatch(/\n\s*return\b/)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('issue #44: deferred-secret callback handler + auto-write helper exist', () => {
|
|
107
|
+
// Static wiring check — the inline buttons need a dispatcher branch
|
|
108
|
+
// and a write helper, otherwise tapping the card does nothing.
|
|
109
|
+
expect(src).toMatch(/handleVaultDeferCallback\b/)
|
|
110
|
+
expect(src).toMatch(/executeDeferredSecretSave\b/)
|
|
111
|
+
expect(src).toMatch(/passphrase-for-deferred/)
|
|
112
|
+
// Dispatcher routes vd: prefix to the new handler.
|
|
113
|
+
const dispatcherIdx = src.indexOf("data.startsWith('vd:')")
|
|
114
|
+
expect(dispatcherIdx).toBeGreaterThan(0)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('detectSecrets is used for the deferred peek (no-passphrase path)', () => {
|
|
118
|
+
expect(src).toMatch(/const detections = detectSecrets\(effectiveText\)/)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('staging follow-up commands (stash/ignore/rename/forget) are wired', () => {
|
|
122
|
+
const handleInboundIdx = src.indexOf('async function handleInbound(')
|
|
123
|
+
const tail = src.slice(handleInboundIdx, handleInboundIdx + 30000)
|
|
124
|
+
expect(tail).toMatch(/\(stash\|ignore\|rename\|forget\)/)
|
|
125
|
+
expect(tail).toMatch(/secretStaging\.latestForChat\(chat_id\)/)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for the gateway startup mutex + SIGTERM drain.
|
|
3
|
+
*
|
|
4
|
+
* Background — 2026-04-23 incident:
|
|
5
|
+
* Two clerk-gateway processes were alive simultaneously after a
|
|
6
|
+
* restart. The new one looped 13+ times on `409: Conflict: terminated
|
|
7
|
+
* by other getUpdates request` because the OLD one's long-poll TCP
|
|
8
|
+
* connection hadn't FIN'd yet. PRs #45–#50 added a PID-file probe
|
|
9
|
+
* and SIGTERM marker but no real startup mutex.
|
|
10
|
+
*
|
|
11
|
+
* These tests pin the invariants of the architectural fix:
|
|
12
|
+
* - Startup mutex prevents double-start when a live PID holds the file
|
|
13
|
+
* - Startup mutex auto-recovers when the holder PID is dead
|
|
14
|
+
* - Lock releases on clean shutdown
|
|
15
|
+
* - SIGTERM drain returns within budget for cooperative in-flight
|
|
16
|
+
* - SIGTERM drain reports timed_out=true when in-flight never drops
|
|
17
|
+
*
|
|
18
|
+
* Run with:
|
|
19
|
+
* bun test telegram-plugin/tests/gateway-startup-mutex.test.ts
|
|
20
|
+
*/
|
|
21
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
22
|
+
import { mkdtempSync, writeFileSync, existsSync, readFileSync, rmSync } from "fs";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
import { tmpdir } from "os";
|
|
25
|
+
import {
|
|
26
|
+
acquireStartupLock,
|
|
27
|
+
releaseStartupLock,
|
|
28
|
+
type MutexRecord,
|
|
29
|
+
} from "../gateway/startup-mutex.js";
|
|
30
|
+
import { drainShutdown } from "../gateway/shutdown-drain.js";
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
function tmpLockPath(): { dir: string; path: string } {
|
|
37
|
+
const dir = mkdtempSync(join(tmpdir(), "gw-mutex-test-"));
|
|
38
|
+
return { dir, path: join(dir, "gateway.pid.json") };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function makeRecord(pid: number, startedAtMs = Date.now()): MutexRecord {
|
|
42
|
+
return { pid, startedAtMs };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function noopLog(): { lines: string[]; log: (s: string) => void } {
|
|
46
|
+
const lines: string[] = [];
|
|
47
|
+
return { lines, log: (s: string) => { lines.push(s); } };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Mutex tests
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
describe("acquireStartupLock", () => {
|
|
55
|
+
const cleanups: string[] = [];
|
|
56
|
+
|
|
57
|
+
afterEach(() => {
|
|
58
|
+
while (cleanups.length > 0) {
|
|
59
|
+
const dir = cleanups.pop();
|
|
60
|
+
if (dir) {
|
|
61
|
+
try { rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("(a) blocks when an alive holder owns the file", async () => {
|
|
67
|
+
// Setup: someone else holds the lock with PID 12345 (which our
|
|
68
|
+
// injected isPidAlive will report as alive).
|
|
69
|
+
const { dir, path } = tmpLockPath();
|
|
70
|
+
cleanups.push(dir);
|
|
71
|
+
const incumbent = makeRecord(12345, Date.now() - 5000);
|
|
72
|
+
writeFileSync(path, JSON.stringify(incumbent), "utf-8");
|
|
73
|
+
|
|
74
|
+
const { lines, log } = noopLog();
|
|
75
|
+
const result = await acquireStartupLock({
|
|
76
|
+
path,
|
|
77
|
+
record: makeRecord(99999),
|
|
78
|
+
isPidAlive: (pid) => pid === 12345, // incumbent is alive
|
|
79
|
+
log,
|
|
80
|
+
agentName: "test-agent",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.status).toBe("blocked");
|
|
84
|
+
if (result.status !== "blocked") throw new Error("unreachable");
|
|
85
|
+
expect(result.holder.pid).toBe(12345);
|
|
86
|
+
expect(result.holderAgeSec).toBeGreaterThanOrEqual(4);
|
|
87
|
+
|
|
88
|
+
// File should still contain the incumbent record (not overwritten).
|
|
89
|
+
const onDisk = JSON.parse(readFileSync(path, "utf-8")) as MutexRecord;
|
|
90
|
+
expect(onDisk.pid).toBe(12345);
|
|
91
|
+
|
|
92
|
+
// Should have logged a boot.lock_blocked line for journalctl.
|
|
93
|
+
const blocked = lines.find((l) => l.includes("boot.lock_blocked"));
|
|
94
|
+
expect(blocked).toBeDefined();
|
|
95
|
+
expect(blocked).toContain("holder_pid=12345");
|
|
96
|
+
expect(blocked).toContain("agent=test-agent");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("(b) releases the lock on shutdown — file is gone afterwards", async () => {
|
|
100
|
+
const { dir, path } = tmpLockPath();
|
|
101
|
+
cleanups.push(dir);
|
|
102
|
+
|
|
103
|
+
const { log } = noopLog();
|
|
104
|
+
const acquired = await acquireStartupLock({
|
|
105
|
+
path,
|
|
106
|
+
record: makeRecord(process.pid),
|
|
107
|
+
isPidAlive: () => true,
|
|
108
|
+
log,
|
|
109
|
+
});
|
|
110
|
+
expect(acquired.status).toBe("acquired");
|
|
111
|
+
expect(existsSync(path)).toBe(true);
|
|
112
|
+
|
|
113
|
+
const releaseLog = noopLog();
|
|
114
|
+
await releaseStartupLock({
|
|
115
|
+
path,
|
|
116
|
+
pid: process.pid,
|
|
117
|
+
log: releaseLog.log,
|
|
118
|
+
agentName: "test-agent",
|
|
119
|
+
});
|
|
120
|
+
expect(existsSync(path)).toBe(false);
|
|
121
|
+
const released = releaseLog.lines.find((l) => l.includes("shutdown.lock_released"));
|
|
122
|
+
expect(released).toBeDefined();
|
|
123
|
+
expect(released).toContain(`pid=${process.pid}`);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("(c) auto-recovers a stale lock (dead PID) and writes our own", async () => {
|
|
127
|
+
const { dir, path } = tmpLockPath();
|
|
128
|
+
cleanups.push(dir);
|
|
129
|
+
// Pre-populate with a "dead" PID. 999999 is effectively impossible
|
|
130
|
+
// to allocate on Linux (kernel.pid_max default is 4194304 on
|
|
131
|
+
// x86_64 but most systems use the older 32768 ceiling — either way,
|
|
132
|
+
// 999999 is almost certainly unallocated). We override isPidAlive
|
|
133
|
+
// to make the test deterministic regardless of kernel config.
|
|
134
|
+
const stalePid = 999999;
|
|
135
|
+
const stale = makeRecord(stalePid, Date.now() - 600_000); // 10min old
|
|
136
|
+
writeFileSync(path, JSON.stringify(stale), "utf-8");
|
|
137
|
+
|
|
138
|
+
const ourPid = 4242;
|
|
139
|
+
const { lines, log } = noopLog();
|
|
140
|
+
const result = await acquireStartupLock({
|
|
141
|
+
path,
|
|
142
|
+
record: makeRecord(ourPid),
|
|
143
|
+
isPidAlive: (pid) => pid !== stalePid, // stale is dead, all else alive
|
|
144
|
+
log,
|
|
145
|
+
agentName: "test-agent",
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result.status).toBe("acquired");
|
|
149
|
+
if (result.status !== "acquired") throw new Error("unreachable");
|
|
150
|
+
expect(result.recoveredFrom).toBeDefined();
|
|
151
|
+
expect(result.recoveredFrom?.pid).toBe(stalePid);
|
|
152
|
+
|
|
153
|
+
// File should now contain OUR record.
|
|
154
|
+
const onDisk = JSON.parse(readFileSync(path, "utf-8")) as MutexRecord;
|
|
155
|
+
expect(onDisk.pid).toBe(ourPid);
|
|
156
|
+
|
|
157
|
+
// Audit log: must include the stale-recovered line so an operator
|
|
158
|
+
// grepping journalctl can see what happened.
|
|
159
|
+
const recovered = lines.find((l) => l.includes("boot.lock_stale_recovered"));
|
|
160
|
+
expect(recovered).toBeDefined();
|
|
161
|
+
expect(recovered).toContain(`prior_pid=${stalePid}`);
|
|
162
|
+
expect(recovered).toContain("agent=test-agent");
|
|
163
|
+
|
|
164
|
+
const acquired = lines.find((l) => l.includes("boot.lock_acquired"));
|
|
165
|
+
expect(acquired).toBeDefined();
|
|
166
|
+
expect(acquired).toContain(`pid=${ourPid}`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("(c.bonus) double-acquire by the SAME process does NOT corrupt state", async () => {
|
|
170
|
+
// Defensive: if the boot path somehow runs acquireStartupLock twice
|
|
171
|
+
// in the same process, we should detect ourselves as the holder and
|
|
172
|
+
// NOT mutate the file. (Currently we report blocked, which is the
|
|
173
|
+
// safe failure mode — caller exits non-zero.)
|
|
174
|
+
const { dir, path } = tmpLockPath();
|
|
175
|
+
cleanups.push(dir);
|
|
176
|
+
const { log } = noopLog();
|
|
177
|
+
const first = await acquireStartupLock({
|
|
178
|
+
path,
|
|
179
|
+
record: makeRecord(process.pid),
|
|
180
|
+
isPidAlive: () => true,
|
|
181
|
+
log,
|
|
182
|
+
});
|
|
183
|
+
expect(first.status).toBe("acquired");
|
|
184
|
+
|
|
185
|
+
const second = await acquireStartupLock({
|
|
186
|
+
path,
|
|
187
|
+
record: makeRecord(process.pid),
|
|
188
|
+
isPidAlive: () => true,
|
|
189
|
+
log,
|
|
190
|
+
});
|
|
191
|
+
expect(second.status).toBe("blocked");
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Drain tests
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
describe("drainShutdown", () => {
|
|
200
|
+
// Real-time tests for the drain. We use real setTimeout (the module
|
|
201
|
+
// default) and small budgets so the suite stays under a second total.
|
|
202
|
+
// Mocking the clock is fragile here because Promise.race against the
|
|
203
|
+
// budget sleep tangles with the in-flight poll loop's clock.
|
|
204
|
+
|
|
205
|
+
it("(d.1) returns timed_out=false when in-flight drains within budget", async () => {
|
|
206
|
+
// In-flight starts at 3, drops to 0 after ~200ms of real time.
|
|
207
|
+
let count = 3;
|
|
208
|
+
setTimeout(() => { count = 0; }, 200);
|
|
209
|
+
|
|
210
|
+
const { lines, log } = noopLog();
|
|
211
|
+
const result = await drainShutdown({
|
|
212
|
+
signal: "SIGTERM",
|
|
213
|
+
stopPolling: async () => { /* cooperative — returns immediately */ },
|
|
214
|
+
inFlight: () => count,
|
|
215
|
+
budgetMs: 1000,
|
|
216
|
+
pollIntervalMs: 25,
|
|
217
|
+
log,
|
|
218
|
+
agentName: "test-agent",
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
expect(result.timedOut).toBe(false);
|
|
222
|
+
expect(result.inFlightRemaining).toBe(0);
|
|
223
|
+
expect(result.elapsedMs).toBeLessThan(1000);
|
|
224
|
+
expect(result.elapsedMs).toBeGreaterThanOrEqual(150);
|
|
225
|
+
|
|
226
|
+
const start = lines.find((l) => l.includes("shutdown.drain_start"));
|
|
227
|
+
expect(start).toBeDefined();
|
|
228
|
+
expect(start).toContain("signal=SIGTERM");
|
|
229
|
+
expect(start).toContain("in_flight=3");
|
|
230
|
+
|
|
231
|
+
const complete = lines.find((l) => l.includes("shutdown.drain_complete"));
|
|
232
|
+
expect(complete).toBeDefined();
|
|
233
|
+
expect(complete).toContain("timed_out=false");
|
|
234
|
+
expect(complete).toContain("in_flight_remaining=0");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("(d.2) returns timed_out=true when in-flight never drops", async () => {
|
|
238
|
+
const { lines, log } = noopLog();
|
|
239
|
+
const result = await drainShutdown({
|
|
240
|
+
signal: "SIGTERM",
|
|
241
|
+
stopPolling: async () => { /* returns instantly; in-flight never clears */ },
|
|
242
|
+
inFlight: () => 5,
|
|
243
|
+
budgetMs: 250, // small budget keeps the test fast
|
|
244
|
+
pollIntervalMs: 25,
|
|
245
|
+
log,
|
|
246
|
+
agentName: "test-agent",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(result.timedOut).toBe(true);
|
|
250
|
+
expect(result.inFlightRemaining).toBe(5);
|
|
251
|
+
expect(result.elapsedMs).toBeGreaterThanOrEqual(250);
|
|
252
|
+
// Tight upper bound — 2× headroom over budget catches obvious
|
|
253
|
+
// slow-drain regressions without flaking under normal load.
|
|
254
|
+
expect(result.elapsedMs).toBeLessThan(500);
|
|
255
|
+
|
|
256
|
+
const complete = lines.find((l) => l.includes("shutdown.drain_complete"));
|
|
257
|
+
expect(complete).toBeDefined();
|
|
258
|
+
expect(complete).toContain("timed_out=true");
|
|
259
|
+
expect(complete).toContain("in_flight_remaining=5");
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("(d.3) survives stopPolling throwing without aborting the drain", async () => {
|
|
263
|
+
let count = 2;
|
|
264
|
+
setTimeout(() => { count = 0; }, 100);
|
|
265
|
+
|
|
266
|
+
const { lines, log } = noopLog();
|
|
267
|
+
const result = await drainShutdown({
|
|
268
|
+
signal: "SIGTERM",
|
|
269
|
+
stopPolling: async () => {
|
|
270
|
+
throw new Error("simulated bot.stop() failure");
|
|
271
|
+
},
|
|
272
|
+
inFlight: () => count,
|
|
273
|
+
budgetMs: 1000,
|
|
274
|
+
pollIntervalMs: 25,
|
|
275
|
+
log,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
expect(result.timedOut).toBe(false);
|
|
279
|
+
expect(result.inFlightRemaining).toBe(0);
|
|
280
|
+
const errLine = lines.find((l) => l.includes("stop_polling_error"));
|
|
281
|
+
expect(errLine).toBeDefined();
|
|
282
|
+
expect(errLine).toContain("simulated bot.stop() failure");
|
|
283
|
+
});
|
|
284
|
+
});
|