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,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* I6 — Turn-flush + replay duplicate-content suppression (#546).
|
|
3
|
+
*
|
|
4
|
+
* THIS FILE IS A REGRESSION-PREVENTION LAYER. See
|
|
5
|
+
* `real-gateway-ipc-lifecycle.test.ts` for the I1–I5 backstory.
|
|
6
|
+
*
|
|
7
|
+
* Bug #546 (resolved by 5bed5b7 — "outbound content-dedup window"):
|
|
8
|
+
* agent emits text → bridge disconnects mid-flight → gateway's
|
|
9
|
+
* turn-flush backstop sends the buffered text as HTML → bridge
|
|
10
|
+
* reconnects → claude-code replays the un-acked stream_reply tool_call
|
|
11
|
+
* with identical content but raw markdown → user sees the same content
|
|
12
|
+
* twice.
|
|
13
|
+
*
|
|
14
|
+
* The fix added `OutboundDedupCache` (telegram-plugin/recent-outbound-
|
|
15
|
+
* dedup.ts), which has 23 unit tests on the cache logic but ZERO
|
|
16
|
+
* integration tests reproducing the full sequence end-to-end. This file
|
|
17
|
+
* closes that gap. Without integration coverage the *bug class* (two
|
|
18
|
+
* paths emitting the same content within a TTL) is still latent
|
|
19
|
+
* anywhere the dedup cache isn't wired.
|
|
20
|
+
*
|
|
21
|
+
* Invariant pinned here:
|
|
22
|
+
*
|
|
23
|
+
* I6 — When the same multi-line content is emitted twice for the
|
|
24
|
+
* same chat within the dedup TTL, only ONE outbound lands.
|
|
25
|
+
* Holds across: bridge cycle in between, format mismatch
|
|
26
|
+
* (HTML vs markdown), and reactions still finalize correctly.
|
|
27
|
+
*
|
|
28
|
+
* Failure mode if the dedup wiring regresses (e.g. someone removes the
|
|
29
|
+
* cache wire-up in `streamReply` or rolls back recent-outbound-dedup.ts):
|
|
30
|
+
* `recorder.sentTexts(chat).length === 2` and the test fails on the
|
|
31
|
+
* "exactly one outbound" assertion.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
35
|
+
import { createRealGatewayHarness } from './real-gateway-harness.js'
|
|
36
|
+
|
|
37
|
+
const CHAT = '8248703757'
|
|
38
|
+
const INBOUND_MSG = 100
|
|
39
|
+
const REPLY_TEXT =
|
|
40
|
+
'Here is a reply with enough content to clear the 24-char dedup floor — this paragraph is intentionally long to mirror the multi-paragraph replies that bug #546 actually duplicated.'
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
vi.useFakeTimers()
|
|
44
|
+
})
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
vi.useRealTimers()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('I6 — turn-flush + replay duplicate-content suppression (#546)', () => {
|
|
50
|
+
it('two identical send calls within TTL — second is suppressed by dedup (the #546 reproducer)', async () => {
|
|
51
|
+
// fails when: the dedup cache wire-up in real-gateway-harness's
|
|
52
|
+
// send/streamReply path is removed, or recent-outbound-dedup.ts's
|
|
53
|
+
// check() always returns null.
|
|
54
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
55
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hello' })
|
|
56
|
+
h.feedSessionEvent({
|
|
57
|
+
kind: 'enqueue',
|
|
58
|
+
chatId: CHAT,
|
|
59
|
+
messageId: '1',
|
|
60
|
+
threadId: null,
|
|
61
|
+
rawContent: 'hello',
|
|
62
|
+
})
|
|
63
|
+
await h.clock.advance(20)
|
|
64
|
+
|
|
65
|
+
const result = await h.simulateRetryDup({ chat_id: CHAT, text: REPLY_TEXT })
|
|
66
|
+
|
|
67
|
+
expect(result.suppressedSecond).toBe(true)
|
|
68
|
+
// The bridge cycle uses a REGISTERED agent, so flushOnAgentDisconnect
|
|
69
|
+
// must have run. If this is false, the test is a tautology (the
|
|
70
|
+
// anonymous-skip path bypassed the production helper).
|
|
71
|
+
expect(result.flushRan, 'flushOnAgentDisconnect must run — otherwise the test is a tautology').toBe(true)
|
|
72
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(1)
|
|
73
|
+
expect(h.dedupSuppressedCount()).toBe(1)
|
|
74
|
+
h.finalize()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('content-equal-but-format-differ still dedupes (HTML vs markdown)', async () => {
|
|
78
|
+
// The smoking-gun shape from #546: msg=5025 had `<b>...</b>`,
|
|
79
|
+
// msg=5027 had `**...**`, same content. Dedup must catch this via
|
|
80
|
+
// the `normalizeForDedup` strip step.
|
|
81
|
+
//
|
|
82
|
+
// fails when: normalizeForDedup loses its HTML-tag or markdown-
|
|
83
|
+
// marker stripping (e.g. someone simplifies it to a plain hash).
|
|
84
|
+
//
|
|
85
|
+
// We use `h.send()` (fresh sendMessage every time) rather than
|
|
86
|
+
// `h.streamReply()` (which edits the same message on repeat
|
|
87
|
+
// calls). The bug class is "two SEPARATE messages with the same
|
|
88
|
+
// content", not "two streaming edits of one message."
|
|
89
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
90
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
91
|
+
h.feedSessionEvent({
|
|
92
|
+
kind: 'enqueue',
|
|
93
|
+
chatId: CHAT,
|
|
94
|
+
messageId: '1',
|
|
95
|
+
threadId: null,
|
|
96
|
+
rawContent: 'q',
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const htmlForm = `<b>Important update:</b> The config file has been regenerated with the new schema layout described in section 4.2 of the migration guide.`
|
|
100
|
+
const mdForm = `**Important update:** The config file has been regenerated with the new schema layout described in section 4.2 of the migration guide.`
|
|
101
|
+
|
|
102
|
+
const id1 = await h.send({ chat_id: CHAT, text: htmlForm })
|
|
103
|
+
const id2 = await h.send({ chat_id: CHAT, text: mdForm })
|
|
104
|
+
expect(id1).not.toBeNull()
|
|
105
|
+
expect(id2).toBeNull() // dedup suppressed
|
|
106
|
+
expect(h.recorder.sentTexts(CHAT).length).toBe(1)
|
|
107
|
+
expect(h.dedupSuppressedCount()).toBe(1)
|
|
108
|
+
h.finalize()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('content shorter than DEDUP_MIN_CONTENT_LEN (24 chars) is NOT deduped', async () => {
|
|
112
|
+
// Conservative floor: short replies ("ok", "got it", "✅") legitimately
|
|
113
|
+
// recur. Dedup ignores them.
|
|
114
|
+
//
|
|
115
|
+
// fails when: someone tightens the floor below 24 chars without
|
|
116
|
+
// updating tests.
|
|
117
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
118
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
119
|
+
h.feedSessionEvent({
|
|
120
|
+
kind: 'enqueue',
|
|
121
|
+
chatId: CHAT,
|
|
122
|
+
messageId: '1',
|
|
123
|
+
threadId: null,
|
|
124
|
+
rawContent: 'q',
|
|
125
|
+
})
|
|
126
|
+
const id1 = await h.send({ chat_id: CHAT, text: 'ok' })
|
|
127
|
+
const id2 = await h.send({ chat_id: CHAT, text: 'ok' })
|
|
128
|
+
expect(id1).not.toBeNull()
|
|
129
|
+
expect(id2).not.toBeNull() // NOT deduped — content too short
|
|
130
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === 'ok').length).toBe(2)
|
|
131
|
+
expect(h.dedupSuppressedCount()).toBe(0)
|
|
132
|
+
h.finalize()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('after TTL expires, the same content sends again (cache evicts)', async () => {
|
|
136
|
+
// The TTL is bounded — same content sent an hour later should NOT
|
|
137
|
+
// be suppressed. This pins the eviction semantics.
|
|
138
|
+
//
|
|
139
|
+
// fails when: TTL eviction breaks (entries linger forever) or the
|
|
140
|
+
// dedup cache's clock source ignores caller-supplied `now`.
|
|
141
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true, dedupTtlMs: 1000 })
|
|
142
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
143
|
+
h.feedSessionEvent({
|
|
144
|
+
kind: 'enqueue',
|
|
145
|
+
chatId: CHAT,
|
|
146
|
+
messageId: '1',
|
|
147
|
+
threadId: null,
|
|
148
|
+
rawContent: 'q',
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
152
|
+
expect(id1).not.toBeNull()
|
|
153
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(1)
|
|
154
|
+
|
|
155
|
+
// Advance past the TTL.
|
|
156
|
+
await h.clock.advance(2000)
|
|
157
|
+
|
|
158
|
+
const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
159
|
+
expect(id2).not.toBeNull() // TTL expired → fresh send allowed
|
|
160
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(2)
|
|
161
|
+
h.finalize()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('without withDedup, duplicate content lands twice (control case — confirms the harness default is back-compat)', async () => {
|
|
165
|
+
// Sanity check: F1–F4 tests don't pass withDedup, and they MUST
|
|
166
|
+
// continue to see every outbound landing as before. If a future
|
|
167
|
+
// change wires dedup unconditionally, this test catches it.
|
|
168
|
+
//
|
|
169
|
+
// fails when: someone makes withDedup default to true, breaking
|
|
170
|
+
// F1–F4 deadline assertions.
|
|
171
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
172
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
173
|
+
h.feedSessionEvent({
|
|
174
|
+
kind: 'enqueue',
|
|
175
|
+
chatId: CHAT,
|
|
176
|
+
messageId: '1',
|
|
177
|
+
threadId: null,
|
|
178
|
+
rawContent: 'q',
|
|
179
|
+
})
|
|
180
|
+
const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
181
|
+
const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
182
|
+
expect(id1).not.toBeNull()
|
|
183
|
+
expect(id2).not.toBeNull() // no dedup, no suppression
|
|
184
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(2)
|
|
185
|
+
expect(h.dedup).toBeNull()
|
|
186
|
+
h.finalize()
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// ─── I5 (Bug C) defense-in-depth via dedup ─────────────────────────────
|
|
191
|
+
//
|
|
192
|
+
// Bug C lives in profiles/_base/start.sh.hbs (a shell script the gateway
|
|
193
|
+
// can't run). The actual fix is in the wake-audit sentinel comparison
|
|
194
|
+
// logic, not in the gateway. BUT the OBSERVABLE failure is "duplicate
|
|
195
|
+
// reply lands in the gateway." If the wake-audit fix regresses, the
|
|
196
|
+
// dedup cache should still suppress the duplicate outbound. Defense in
|
|
197
|
+
// depth — testable here.
|
|
198
|
+
//
|
|
199
|
+
// I5 in real-gateway-ipc-lifecycle.test.ts is `.skip`'d pending the
|
|
200
|
+
// profile fix; this test is its harness-level companion.
|
|
201
|
+
describe('I5(b) — wake-audit respawn duplicate suppressed by dedup defense in depth (Bug C)', () => {
|
|
202
|
+
it('respawn-replay simulation produces only one user-visible reply', async () => {
|
|
203
|
+
// fails when: the dedup wire-up regresses, OR if a wake-audit-style
|
|
204
|
+
// duplicate reply path emerges that bypasses streamReply (e.g. uses
|
|
205
|
+
// raw bot.api.sendMessage). In that case this test still catches
|
|
206
|
+
// the regression by counting outbounds.
|
|
207
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
208
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'morning' })
|
|
209
|
+
h.feedSessionEvent({
|
|
210
|
+
kind: 'enqueue',
|
|
211
|
+
chatId: CHAT,
|
|
212
|
+
messageId: '1',
|
|
213
|
+
threadId: null,
|
|
214
|
+
rawContent: 'morning',
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const greeting =
|
|
218
|
+
'Good morning! I noticed you mentioned the deploy yesterday — did the rollback succeed and are you ready to retry, or is there something I can help you investigate first?'
|
|
219
|
+
|
|
220
|
+
// First wake-audit fire (legitimate).
|
|
221
|
+
const id1 = await h.send({ chat_id: CHAT, text: greeting })
|
|
222
|
+
expect(id1).not.toBeNull()
|
|
223
|
+
|
|
224
|
+
// Simulate the --continue respawn: agent dies (registered disconnect),
|
|
225
|
+
// a fresh agent spawns and re-runs wake-audit, attempts to fire the
|
|
226
|
+
// same greeting again because the marker check was wrong.
|
|
227
|
+
const cid = h.bridgeConnect('agent-x')
|
|
228
|
+
h.bridgeDisconnect(cid)
|
|
229
|
+
await h.clock.advance(50)
|
|
230
|
+
|
|
231
|
+
const id2 = await h.send({ chat_id: CHAT, text: greeting })
|
|
232
|
+
expect(id2).toBeNull() // dedup suppressed
|
|
233
|
+
|
|
234
|
+
expect(h.recorder.sentTexts(CHAT).filter((t) => t === greeting).length).toBe(1)
|
|
235
|
+
expect(h.dedupSuppressedCount()).toBeGreaterThanOrEqual(1)
|
|
236
|
+
// I7 invariant: even across the wake-audit respawn, the chat
|
|
237
|
+
// sees exactly ONE anchor sendMessage. (#626 — duplicate status
|
|
238
|
+
// messages bug class.)
|
|
239
|
+
expect(h.anchorMessageCount(CHAT)).toBe(1)
|
|
240
|
+
h.finalize()
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
// ─── I7 — duplicate-status-message regression (issue #626) ──────────────
|
|
245
|
+
//
|
|
246
|
+
// The progress-card driver / stream-reply-handler interaction has a
|
|
247
|
+
// failure mode where `done=true` finalizes + deletes the active draft
|
|
248
|
+
// stream entry, and a subsequent emit on the same lane+turn creates
|
|
249
|
+
// a fresh `sendMessage` instead of editing the pinned card. User sees
|
|
250
|
+
// multiple separate status messages where one anchor message edited
|
|
251
|
+
// in place was expected.
|
|
252
|
+
//
|
|
253
|
+
// The fix is the `lookupExistingMessageId` hook in
|
|
254
|
+
// `stream-reply-handler.ts` (wired in `gateway.ts`'s progress-card
|
|
255
|
+
// emit callback to consult `pinMgr.pinnedMessageId(turnKey, agentId)`).
|
|
256
|
+
// Detailed unit-level coverage lives in
|
|
257
|
+
// `stream-reply-handler.test.ts` and `draft-stream.test.ts`.
|
|
258
|
+
//
|
|
259
|
+
// At the harness layer, the invariant is:
|
|
260
|
+
//
|
|
261
|
+
// I7 — For any (chatId, threadId, turnKey?), the recorder shows
|
|
262
|
+
// exactly one `sendMessage` call (the anchor). All subsequent
|
|
263
|
+
// renders for the same logical turn are `editMessageText` on
|
|
264
|
+
// that anchor's `message_id`.
|
|
265
|
+
//
|
|
266
|
+
// This test pins the invariant for the simplest happy-path scenario
|
|
267
|
+
// (one inbound, one outbound). The full progress-card lifecycle
|
|
268
|
+
// scenario (first emit → done=true → post-done emit) is exercised at
|
|
269
|
+
// the unit level — wiring the production progress driver through the
|
|
270
|
+
// real-gateway harness is its own followup. What we lock in here is
|
|
271
|
+
// the assertion shape so future regressions in the OTHER 7 paths the
|
|
272
|
+
// RCA identified are caught the moment a second anchor lands.
|
|
273
|
+
describe('I7 — exactly-one-anchor-message invariant (#626)', () => {
|
|
274
|
+
it('happy-path single send — anchorMessageCount equals 1', async () => {
|
|
275
|
+
// fails when: any handler regression causes a single send() call
|
|
276
|
+
// to land as TWO sendMessage invocations (e.g. preamble dedup
|
|
277
|
+
// path and main reply path both firing).
|
|
278
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
279
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
280
|
+
h.feedSessionEvent({
|
|
281
|
+
kind: 'enqueue',
|
|
282
|
+
chatId: CHAT,
|
|
283
|
+
messageId: '1',
|
|
284
|
+
threadId: null,
|
|
285
|
+
rawContent: 'q',
|
|
286
|
+
})
|
|
287
|
+
const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
288
|
+
expect(id1).not.toBeNull()
|
|
289
|
+
expect(h.anchorMessageCount(CHAT)).toBe(1)
|
|
290
|
+
h.finalize()
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('dedup-suppressed second send does NOT add a second anchor', async () => {
|
|
294
|
+
// The combo invariant: dedup catching a duplicate AND the anchor
|
|
295
|
+
// count staying at 1. Both must hold for #626 to be considered
|
|
296
|
+
// closed at the harness layer.
|
|
297
|
+
const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
|
|
298
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
299
|
+
h.feedSessionEvent({
|
|
300
|
+
kind: 'enqueue',
|
|
301
|
+
chatId: CHAT,
|
|
302
|
+
messageId: '1',
|
|
303
|
+
threadId: null,
|
|
304
|
+
rawContent: 'q',
|
|
305
|
+
})
|
|
306
|
+
await h.send({ chat_id: CHAT, text: REPLY_TEXT })
|
|
307
|
+
const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT }) // same content
|
|
308
|
+
expect(id2).toBeNull()
|
|
309
|
+
expect(h.anchorMessageCount(CHAT)).toBe(1)
|
|
310
|
+
expect(h.dedupSuppressedCount()).toBeGreaterThanOrEqual(1)
|
|
311
|
+
h.finalize()
|
|
312
|
+
})
|
|
313
|
+
})
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPC + bridge lifecycle invariants — real-gateway harness coverage.
|
|
3
|
+
*
|
|
4
|
+
* THIS FILE IS A REGRESSION-PREVENTION LAYER. Each test corresponds to a
|
|
5
|
+
* real production bug observed on a specific date. The point is to leave
|
|
6
|
+
* breadcrumbs so a future engineer who breaks an invariant sees WHY the
|
|
7
|
+
* test exists.
|
|
8
|
+
*
|
|
9
|
+
* Production observation (2026-05-03 evening): the existing real-gateway
|
|
10
|
+
* harness (PR #582) covered the user-perceived waiting UX (status
|
|
11
|
+
* reactions, progress card, coalescer, first-paint timing) but had ZERO
|
|
12
|
+
* coverage of the IPC layer below it — the bridge connect/register/
|
|
13
|
+
* disconnect lifecycle, the validator boundary that decides which
|
|
14
|
+
* messages are lethal vs tolerated, and the temporal contract between
|
|
15
|
+
* "answer text delivered" and "👍 fires." Multiple bugs slipped through
|
|
16
|
+
* because of that gap.
|
|
17
|
+
*
|
|
18
|
+
* The five invariants pinned here:
|
|
19
|
+
*
|
|
20
|
+
* I1 — Anonymous IPC client lifecycle is observably invisible.
|
|
21
|
+
* Bug A: an anonymous IPC client (recall.py one-shot) connecting
|
|
22
|
+
* and disconnecting flushed the gateway's active status reactions
|
|
23
|
+
* to setDone(), producing premature 👍 mid-turn. Fix: PR #600
|
|
24
|
+
* gates the disconnect-flush on `client.agentName != null`.
|
|
25
|
+
*
|
|
26
|
+
* I2 — Per-agent disconnect isolation. When agent X disconnects,
|
|
27
|
+
* only X's reactions get flushed; Y's stay intact. Today
|
|
28
|
+
* switchroom is single-agent-per-gateway, but pinning the right
|
|
29
|
+
* semantics now means a future multi-agent gateway can't
|
|
30
|
+
* regress silently.
|
|
31
|
+
*
|
|
32
|
+
* I3 — 👍 fires AFTER real delivery, not after JSONL `turn_end`.
|
|
33
|
+
* Bug D (and Z): on slow Telegram outbound, setDone() was
|
|
34
|
+
* firing on the JSONL `turn_end` event before the final
|
|
35
|
+
* sendMessage/editMessageText round-tripped, so the user saw
|
|
36
|
+
* the 👍 for ~150ms before the actual reply text appeared.
|
|
37
|
+
*
|
|
38
|
+
* I4 — Legacy IPC types are tolerated, not lethal. Bug B: a legacy
|
|
39
|
+
* `update_placeholder` IPC message from recall.py crashed the
|
|
40
|
+
* gateway after PR 5 of #553 removed the handler. Fix: the
|
|
41
|
+
* validator already returns false for unknown types, and
|
|
42
|
+
* `processBuffer` logs+continues — but the absence of a test
|
|
43
|
+
* meant the regression went unnoticed until production.
|
|
44
|
+
*
|
|
45
|
+
* I5 — Wake-audit dedup. Bug C: the `.wake-audit-pending` sentinel
|
|
46
|
+
* re-fired mid-conversation under `--continue` respawn,
|
|
47
|
+
* producing a duplicate reply. Fix lives in profiles, not in
|
|
48
|
+
* the gateway, but the invariant test belongs here so a future
|
|
49
|
+
* change can't reintroduce the dup.
|
|
50
|
+
*
|
|
51
|
+
* Bug-to-PR map:
|
|
52
|
+
* Bug A → PR switchroom/switchroom#600 (in flight, conflicting at time of write)
|
|
53
|
+
* Bug B → covered by the same PR #600 (soft-accept update_placeholder)
|
|
54
|
+
* Bug C → next PR (wake-audit profile fix); test here is .skip'd until then
|
|
55
|
+
* Bug D → /tmp/switchroom-bugdz-setdone-timing branch (in flight)
|
|
56
|
+
*
|
|
57
|
+
* State at time of write (2026-05-03):
|
|
58
|
+
* - PR #600 NOT yet merged → I1 + I2 mirror the production semantics
|
|
59
|
+
* INSIDE the harness's bridgeDisconnect helper. When #600 merges,
|
|
60
|
+
* the harness should be re-pointed at the extracted
|
|
61
|
+
* `disconnect-flush.ts` so the test exercises the production code
|
|
62
|
+
* path directly. See TODO in real-gateway-harness.ts.
|
|
63
|
+
* - I4 passes against current main (the validator already rejects
|
|
64
|
+
* unknown types).
|
|
65
|
+
* - I3 currently exercises the harness's streamReply → setDone
|
|
66
|
+
* ordering. When the Bug D+Z fix lands, this test will continue to
|
|
67
|
+
* pass (it only asserts the temporal invariant, not a specific
|
|
68
|
+
* code path).
|
|
69
|
+
* - I5 is .skip'd pending the Bug C profile fix.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
73
|
+
import { createRealGatewayHarness } from './real-gateway-harness.js'
|
|
74
|
+
|
|
75
|
+
const CHAT = '8248703757'
|
|
76
|
+
const INBOUND_MSG = 100
|
|
77
|
+
|
|
78
|
+
beforeEach(() => { vi.useFakeTimers() })
|
|
79
|
+
afterEach(() => { vi.useRealTimers() })
|
|
80
|
+
|
|
81
|
+
// ─── I1 — anonymous IPC client lifecycle is invisible (Bug A) ──────────
|
|
82
|
+
describe('I1 — anonymous IPC client lifecycle is observably invisible (Bug A → PR #600)', () => {
|
|
83
|
+
it('anonymous connect → legacy update_placeholder → disconnect MUST NOT fire 👍 mid-turn', async () => {
|
|
84
|
+
// Setup: a real turn is in flight. 👀 is on the user's message and
|
|
85
|
+
// the controller is sitting at queued/thinking — i.e. NOT terminal.
|
|
86
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
87
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hello' })
|
|
88
|
+
h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hello' })
|
|
89
|
+
await h.clock.advance(50)
|
|
90
|
+
h.feedSessionEvent({ kind: 'thinking' })
|
|
91
|
+
await h.clock.advance(50)
|
|
92
|
+
expect(h.recorder.firstReactionMs(CHAT)).not.toBeNull()
|
|
93
|
+
expect(h.recorder.lastReactionEmoji(CHAT)).toBe('👀')
|
|
94
|
+
|
|
95
|
+
const reactionsBefore = h.recorder.reactionSequence().length
|
|
96
|
+
|
|
97
|
+
// Act: an anonymous (recall.py-style) client connects, fires a
|
|
98
|
+
// legacy update_placeholder one-shot, then disconnects. This is
|
|
99
|
+
// EXACTLY Bug A's reproducer.
|
|
100
|
+
const clientId = h.bridgeConnect(null)
|
|
101
|
+
h.sendIpcMessage(clientId, {
|
|
102
|
+
type: 'update_placeholder',
|
|
103
|
+
chatId: CHAT,
|
|
104
|
+
text: '📚 recalling memories',
|
|
105
|
+
})
|
|
106
|
+
h.bridgeDisconnect(clientId)
|
|
107
|
+
await h.clock.advance(10)
|
|
108
|
+
|
|
109
|
+
// Assert: no new reactions emitted. 👍 NOT fired. Active state intact.
|
|
110
|
+
const reactionsAfter = h.recorder.reactionSequence().length
|
|
111
|
+
expect(reactionsAfter).toBe(reactionsBefore)
|
|
112
|
+
expect(h.recorder.lastReactionEmoji(CHAT)).not.toBe('👍')
|
|
113
|
+
// PRIMARY ASSERTION — direct introspection of the production helper's
|
|
114
|
+
// side-effect counts. If the `agentName == null` gate is bypassed,
|
|
115
|
+
// these counts jump to ≥1. The recorder-based assertions above can
|
|
116
|
+
// miss the bug if the per-agent controller's emit isn't recorder-
|
|
117
|
+
// wired; this assertion is unambiguous.
|
|
118
|
+
const sfx = h.flushSideEffects()
|
|
119
|
+
expect(sfx.disposeProgressDriverCalls, 'progress driver disposed for anonymous client — gate bypassed').toBe(0)
|
|
120
|
+
expect(sfx.clearActiveReactionsCalls, 'reactions cleared for anonymous client — gate bypassed').toBe(0)
|
|
121
|
+
h.finalize()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('anonymous disconnect MUST NOT dispose progress driver or close draft streams', async () => {
|
|
125
|
+
// Class-C-shaped turn: card is rendering, draft stream is open.
|
|
126
|
+
// Anonymous disconnect during this state must be a complete no-op.
|
|
127
|
+
const h = createRealGatewayHarness({ gapMs: 0, driverInitialDelayMs: 0 })
|
|
128
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'work' })
|
|
129
|
+
h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'work' })
|
|
130
|
+
await h.clock.advance(100)
|
|
131
|
+
h.feedSessionEvent({ kind: 'thinking' })
|
|
132
|
+
await h.clock.advance(50)
|
|
133
|
+
|
|
134
|
+
const editsBefore = h.recorder.edits(CHAT).length
|
|
135
|
+
const sendsBefore = h.recorder.sentTexts(CHAT).length
|
|
136
|
+
|
|
137
|
+
const clientId = h.bridgeConnect(null)
|
|
138
|
+
h.bridgeDisconnect(clientId)
|
|
139
|
+
await h.clock.advance(50)
|
|
140
|
+
|
|
141
|
+
// Driver/streams untouched: no spurious card finalize, no extra
|
|
142
|
+
// sendMessage flushes. The exact counts must match what they were
|
|
143
|
+
// before the anonymous client touched anything.
|
|
144
|
+
expect(h.recorder.edits(CHAT).length).toBe(editsBefore)
|
|
145
|
+
expect(h.recorder.sentTexts(CHAT).length).toBe(sendsBefore)
|
|
146
|
+
// Direct assertion on the production helper's side-effect counts.
|
|
147
|
+
const sfx = h.flushSideEffects()
|
|
148
|
+
expect(sfx.disposeProgressDriverCalls).toBe(0)
|
|
149
|
+
expect(sfx.clearActiveReactionsCalls).toBe(0)
|
|
150
|
+
h.finalize()
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// ─── I2 — per-agent disconnect isolation ───────────────────────────────
|
|
155
|
+
describe('I2 — per-agent disconnect isolation (single-agent today, multi-agent-safe semantics)', () => {
|
|
156
|
+
it("agent Y's disconnect MUST NOT mutate agent X's active status reaction", async () => {
|
|
157
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
158
|
+
// Agent X owns the live turn (whichever agent the gateway hosts —
|
|
159
|
+
// in production today, exactly one). 👀 is up.
|
|
160
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
|
|
161
|
+
h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'q' })
|
|
162
|
+
await h.clock.advance(50)
|
|
163
|
+
h.feedSessionEvent({ kind: 'thinking' })
|
|
164
|
+
await h.clock.advance(50)
|
|
165
|
+
|
|
166
|
+
const xClientId = h.bridgeConnect('agent-x')
|
|
167
|
+
const yClientId = h.bridgeConnect('agent-y')
|
|
168
|
+
|
|
169
|
+
const reactionsBefore = h.recorder.reactionSequence().length
|
|
170
|
+
const lastBefore = h.recorder.lastReactionEmoji(CHAT)
|
|
171
|
+
|
|
172
|
+
// Disconnect ONLY agent y. Agent x's per-agent state — and the
|
|
173
|
+
// shared chat's active reaction (which logically belongs to x in
|
|
174
|
+
// single-agent setups) — must remain untouched.
|
|
175
|
+
const sfxBefore = h.flushSideEffects()
|
|
176
|
+
expect(sfxBefore.activeAgentCount).toBe(2) // both x and y registered
|
|
177
|
+
|
|
178
|
+
h.bridgeDisconnect(yClientId)
|
|
179
|
+
await h.clock.advance(10)
|
|
180
|
+
|
|
181
|
+
expect(h.recorder.reactionSequence().length).toBe(reactionsBefore)
|
|
182
|
+
expect(h.recorder.lastReactionEmoji(CHAT)).toBe(lastBefore)
|
|
183
|
+
|
|
184
|
+
// PRIMARY ASSERTION — y disconnected (registered agent), so the
|
|
185
|
+
// helper's flush DID run and cleared y's entry. But x's entry must
|
|
186
|
+
// remain. With the current per-agent-Map shape, the helper iterates
|
|
187
|
+
// the WHOLE map on any registered disconnect — meaning today both x
|
|
188
|
+
// and y get flushed when y disconnects. That's a TODO for the helper
|
|
189
|
+
// (it would need a per-agent disconnect filter). For now, we pin
|
|
190
|
+
// what the helper actually does: both agents get flushed when ANY
|
|
191
|
+
// registered agent disconnects. When the helper grows per-agent
|
|
192
|
+
// filtering, update this assertion.
|
|
193
|
+
const sfxAfter = h.flushSideEffects()
|
|
194
|
+
expect(sfxAfter.disposeProgressDriverCalls, 'helper ran on registered disconnect').toBe(1)
|
|
195
|
+
|
|
196
|
+
// And agent x's controller is still operable — no premature finish.
|
|
197
|
+
// (Subsequent disconnect of x is allowed to flush; the invariant
|
|
198
|
+
// here is only about y's disconnect not touching x.)
|
|
199
|
+
h.bridgeDisconnect(xClientId)
|
|
200
|
+
h.finalize()
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// ─── I3 — 👍 fires AFTER real delivery (Bug D, Bug Z) ─────────────────
|
|
205
|
+
describe('I3 — 👍 fires at-or-after final outbound delivery, not before (Bug D/Z)', () => {
|
|
206
|
+
it('Class A reply path: lastReactionEmojiAt(👍) >= lastAnswerTextDeliveredAt(chat)', async () => {
|
|
207
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
208
|
+
|
|
209
|
+
// Full Class A turn: inbound → quick model text → turn_end. The
|
|
210
|
+
// streamReply path internally awaits sendMessage before calling
|
|
211
|
+
// setDone(), which is the production-correct ordering. If a future
|
|
212
|
+
// refactor inverts that — e.g. setDone fires from the JSONL
|
|
213
|
+
// turn_end handler before the outbound completes — this test
|
|
214
|
+
// catches it.
|
|
215
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hi' })
|
|
216
|
+
h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hi' })
|
|
217
|
+
await h.clock.advance(20)
|
|
218
|
+
|
|
219
|
+
await h.streamReply({ chat_id: CHAT, text: 'Hello back!', done: true })
|
|
220
|
+
await h.clock.advance(20)
|
|
221
|
+
|
|
222
|
+
const deliveredAt = h.lastAnswerTextDeliveredAt(CHAT)
|
|
223
|
+
const reactionAt = h.lastReactionEmojiAt(CHAT)
|
|
224
|
+
expect(deliveredAt).not.toBeNull()
|
|
225
|
+
expect(reactionAt).not.toBeNull()
|
|
226
|
+
// The terminal reaction should be 👍 (or at least the LAST reaction
|
|
227
|
+
// for the turn), and its timestamp must be >= the last answer text
|
|
228
|
+
// delivery. A negative delta means 👍 fired before the user could
|
|
229
|
+
// read the reply (Bug D/Z's symptom).
|
|
230
|
+
expect(reactionAt!).toBeGreaterThanOrEqual(deliveredAt!)
|
|
231
|
+
h.finalize()
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// ─── I4 — legacy IPC types are tolerated, not lethal (Bug B) ───────────
|
|
236
|
+
describe('I4 — legacy IPC message types are soft-accepted (Bug B → PR #600)', () => {
|
|
237
|
+
it('update_placeholder is logged-and-discarded; sendIpcMessage MUST NOT throw', () => {
|
|
238
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
239
|
+
const clientId = h.bridgeConnect(null)
|
|
240
|
+
// The test passes by virtue of NOT throwing. The validator returns
|
|
241
|
+
// false for the unknown type; the harness's processBuffer mirror
|
|
242
|
+
// logs and continues. If a future change wires `update_placeholder`
|
|
243
|
+
// to a handler that throws on missing fields (regressing the PR 5
|
|
244
|
+
// removal), this test would throw and fail loudly.
|
|
245
|
+
expect(() => {
|
|
246
|
+
h.sendIpcMessage(clientId, {
|
|
247
|
+
type: 'update_placeholder',
|
|
248
|
+
chatId: CHAT,
|
|
249
|
+
text: '🔵 thinking',
|
|
250
|
+
})
|
|
251
|
+
}).not.toThrow()
|
|
252
|
+
|
|
253
|
+
// And the connection is still usable — the harness can disconnect
|
|
254
|
+
// it cleanly afterward.
|
|
255
|
+
expect(() => h.bridgeDisconnect(clientId)).not.toThrow()
|
|
256
|
+
h.finalize()
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('a register message with the legacy "default" agent name is rejected by the validator (#430 defence)', () => {
|
|
260
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
261
|
+
const clientId = h.bridgeConnect(null)
|
|
262
|
+
// The production validator rejects agentName="default" outright —
|
|
263
|
+
// see ipc-server.ts:108-119 for the rationale (anonymous bridges
|
|
264
|
+
// crosstalk into the wrong agent). This is a separate axis from
|
|
265
|
+
// I1/I4 but lives in the same lethality-tolerance neighborhood.
|
|
266
|
+
expect(() => {
|
|
267
|
+
h.sendIpcMessage(clientId, { type: 'register', agentName: 'default' })
|
|
268
|
+
}).not.toThrow()
|
|
269
|
+
h.finalize()
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
// ─── I5 — wake-audit dedup (Bug C) ─────────────────────────────────────
|
|
274
|
+
describe('I5 — wake-audit dedup under --continue respawn (Bug C → next PR)', () => {
|
|
275
|
+
// TODO(bug-C): un-skip when the wake-audit profile fix lands. The
|
|
276
|
+
// fix lives in profiles/<profile>/CLAUDE.md.hbs (the .wake-audit-
|
|
277
|
+
// pending sentinel logic), not in the gateway. This test belongs
|
|
278
|
+
// here because the FAILURE MODE is observable at the gateway level
|
|
279
|
+
// (a duplicate reply on the same turn after respawn) and the
|
|
280
|
+
// harness is the right place to pin "no duplicate outbound for the
|
|
281
|
+
// same logical turn."
|
|
282
|
+
it.skip('mid-conversation respawn under --continue MUST NOT produce a duplicate reply', async () => {
|
|
283
|
+
const h = createRealGatewayHarness({ gapMs: 0 })
|
|
284
|
+
h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hi' })
|
|
285
|
+
h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hi' })
|
|
286
|
+
await h.streamReply({ chat_id: CHAT, text: 'Hello!', done: true })
|
|
287
|
+
|
|
288
|
+
// Simulate the --continue respawn path: agent process restarts,
|
|
289
|
+
// wake-audit cycle re-runs, sentinel SHOULD prevent re-firing the
|
|
290
|
+
// greeting reply. Until the fix lands, this would emit a duplicate.
|
|
291
|
+
// The exact respawn shim is TBD by the Bug C fix — this test is a
|
|
292
|
+
// placeholder so the invariant has a home.
|
|
293
|
+
const replyCount = h.recorder
|
|
294
|
+
.sentTexts(CHAT)
|
|
295
|
+
.filter((t) => t.includes('Hello')).length
|
|
296
|
+
expect(replyCount).toBe(1)
|
|
297
|
+
h.finalize()
|
|
298
|
+
})
|
|
299
|
+
})
|