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,699 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real-gateway harness — Phase 3 of #545 / first PR of #553.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the Phase 1 `waiting-ux-harness` with the real production
|
|
5
|
+
* `InboundCoalescer` so the F1–F4 user-perceived UX deadlines are
|
|
6
|
+
* asserted against the same coalescing timing the live gateway uses,
|
|
7
|
+
* not a parallel reimplementation.
|
|
8
|
+
*
|
|
9
|
+
* The Phase 1 harness called `controller.setQueued()` (👀) synchronously
|
|
10
|
+
* in `inbound()` — that's why F2 ("👀 within 800ms") passed trivially
|
|
11
|
+
* there. Production code routes inbound through `handleInboundCoalesced`
|
|
12
|
+
* first, which buffers messages for `gapMs` (default 1500ms) and only
|
|
13
|
+
* THEN calls the first-paint flow that fires the reaction. This harness
|
|
14
|
+
* exposes that gap to tests so the F2 deadline becomes catchable.
|
|
15
|
+
*
|
|
16
|
+
* Composition (top-down):
|
|
17
|
+
* inbound(chatId, msgId, text)
|
|
18
|
+
* → inboundCoalescer.enqueue(key, payload)
|
|
19
|
+
* → after gapMs, onFlush() runs:
|
|
20
|
+
* → controller.setQueued() (👀)
|
|
21
|
+
* → driver.startTurn()
|
|
22
|
+
* feedSessionEvent(ev)
|
|
23
|
+
* → controller.setThinking() / setTool() / setDone()
|
|
24
|
+
* → driver.ingest()
|
|
25
|
+
*
|
|
26
|
+
* `gapMs` defaults to 1500 (production value). Tests can pass `gapMs: 0`
|
|
27
|
+
* to disable coalescing and verify the upper-bound on first-paint
|
|
28
|
+
* latency without the coalesce wait, or `gapMs: 500` to mimic an
|
|
29
|
+
* operator who tuned it down.
|
|
30
|
+
*
|
|
31
|
+
* F1–F4 deadlines this harness lets us assert:
|
|
32
|
+
* - F1 ladder collapse: reaction sequence over a multi-tool turn
|
|
33
|
+
* - F2 no instant draft: firstReactionMs - inboundAt
|
|
34
|
+
* - F3 late progress card: progressCardSendMs - firstToolUseMs
|
|
35
|
+
* - F4 static interim text: edits per session-event step transition
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
createWaitingUxHarness,
|
|
40
|
+
type CreateHarnessOpts,
|
|
41
|
+
type HarnessHandle,
|
|
42
|
+
type RecordedCall,
|
|
43
|
+
} from './waiting-ux-harness.js'
|
|
44
|
+
import type { SessionEvent } from '../session-tail.js'
|
|
45
|
+
import {
|
|
46
|
+
createInboundCoalescer,
|
|
47
|
+
inboundCoalesceKey,
|
|
48
|
+
type InboundCoalescer,
|
|
49
|
+
} from '../gateway/inbound-coalesce.js'
|
|
50
|
+
import { validateClientMessage } from '../gateway/ipc-server.js'
|
|
51
|
+
import { flushOnAgentDisconnect } from '../gateway/disconnect-flush.js'
|
|
52
|
+
import { StatusReactionController } from '../status-reactions.js'
|
|
53
|
+
import { OutboundDedupCache } from '../recent-outbound-dedup.js'
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Literal placeholder strings the v2 spec contract forbids. Listed
|
|
57
|
+
* centrally so the harness helpers and PR-5 removal sweep stay in
|
|
58
|
+
* sync. Must match the exact emoji + text used by production today —
|
|
59
|
+
* see `pre-alloc-decision.ts`, `placeholder-phase.ts`,
|
|
60
|
+
* `forum-topic-placeholder.ts`.
|
|
61
|
+
*/
|
|
62
|
+
export const PLACEHOLDER_STRINGS = [
|
|
63
|
+
'🔵 thinking',
|
|
64
|
+
'📚 recalling memories',
|
|
65
|
+
'💭 thinking',
|
|
66
|
+
] as const
|
|
67
|
+
|
|
68
|
+
function isPlaceholderPayload(payload: string | undefined): boolean {
|
|
69
|
+
if (payload == null) return false
|
|
70
|
+
for (const s of PLACEHOLDER_STRINGS) {
|
|
71
|
+
if (payload === s || payload === `${s}…` || payload.startsWith(`${s} `)) {
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Mirror of the recorder's progress-card heuristic from
|
|
80
|
+
* `waiting-ux-harness.ts`. Kept in sync by hand — change both if the
|
|
81
|
+
* card text glyphs shift.
|
|
82
|
+
*/
|
|
83
|
+
function isCardPayload(text: string | undefined): boolean {
|
|
84
|
+
return (
|
|
85
|
+
text != null &&
|
|
86
|
+
(text.includes('Working') ||
|
|
87
|
+
text.includes('⚙') ||
|
|
88
|
+
text.includes('⏳') ||
|
|
89
|
+
text.includes('• '))
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface RealGatewayHarnessOpts extends CreateHarnessOpts {
|
|
94
|
+
/**
|
|
95
|
+
* Inbound coalesce window in ms. Production reads this per-call from
|
|
96
|
+
* the access file (default 1500). Tests can pass 0 to disable
|
|
97
|
+
* coalescing entirely.
|
|
98
|
+
*/
|
|
99
|
+
gapMs?: number
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Wire the production `OutboundDedupCache` into the harness's
|
|
103
|
+
* `streamReply` path. When enabled, repeated streamReply calls with
|
|
104
|
+
* the same normalized content within the TTL are suppressed —
|
|
105
|
+
* mimicking the #546 fix in production. Default false (preserves
|
|
106
|
+
* back-compat with F1–F4 tests that don't care about dedup).
|
|
107
|
+
*
|
|
108
|
+
* When true, tests can introspect the cache via `harness.dedup`.
|
|
109
|
+
*/
|
|
110
|
+
withDedup?: boolean
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* TTL for the wired-in dedup cache. Only used when `withDedup` is
|
|
114
|
+
* true. Default matches production (`DEFAULT_DEDUP_TTL_MS = 60_000`).
|
|
115
|
+
*/
|
|
116
|
+
dedupTtlMs?: number
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface CoalescePayload {
|
|
120
|
+
chatId: string
|
|
121
|
+
messageId: number
|
|
122
|
+
text: string
|
|
123
|
+
userId: string
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface RealGatewayHarnessHandle extends HarnessHandle {
|
|
127
|
+
/**
|
|
128
|
+
* Total inbound messages currently buffered by the coalescer (across
|
|
129
|
+
* all keys). For tests asserting that flush actually fired.
|
|
130
|
+
*/
|
|
131
|
+
coalesceBufferSize(): number
|
|
132
|
+
/** Underlying coalescer — exposed for tests that need direct introspection. */
|
|
133
|
+
coalescer: InboundCoalescer<CoalescePayload>
|
|
134
|
+
/**
|
|
135
|
+
* Effective gapMs the harness was configured with. Pinned for tests
|
|
136
|
+
* that compute deadlines relative to the coalesce window.
|
|
137
|
+
*/
|
|
138
|
+
gapMs: number
|
|
139
|
+
|
|
140
|
+
// ─── v2 spec helpers (PR 1 of #553 series) ──────────────────────────
|
|
141
|
+
// The waiting-UX v2 contract forbids placeholder-text edits ("🔵
|
|
142
|
+
// thinking", "📚 recalling memories", "💭 thinking"), suppresses the
|
|
143
|
+
// progress card for Class A/B turns, and pins a first-answer-text
|
|
144
|
+
// deadline. These three helpers expose those checks in a form that
|
|
145
|
+
// reads cleanly inside `expect(...)` assertions.
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns recorded `sendMessage` and `editMessageText` calls for
|
|
149
|
+
* `chat_id` whose payload matches one of the literal placeholder
|
|
150
|
+
* strings the v2 spec bans. Class A and B tests assert
|
|
151
|
+
* `expect(h.recorder.expectNoPlaceholderEdits(CHAT)).toEqual([])`.
|
|
152
|
+
*
|
|
153
|
+
* NOTE: this name is a slight misnomer — it returns hits to
|
|
154
|
+
* inspect, not throws. A non-empty array IS the failure signal.
|
|
155
|
+
*/
|
|
156
|
+
expectNoPlaceholderEdits(chatId: string): RecordedCall[]
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Returns the timestamp of the first progress-card render for
|
|
160
|
+
* `chat_id`, or null if none. Thin wrapper around
|
|
161
|
+
* `recorder.progressCardSendMs` so spec tests can write
|
|
162
|
+
* `expect(h.recorder.expectNoCardSent(CHAT)).toBeNull()` for the
|
|
163
|
+
* Class A/B "no card" invariant without poking at the underlying
|
|
164
|
+
* recorder helper directly.
|
|
165
|
+
*/
|
|
166
|
+
expectNoCardSent(chatId: string): number | null
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Returns the timestamp of the first `sendMessage` or
|
|
170
|
+
* `editMessageText` for `chat_id` whose payload is plausibly
|
|
171
|
+
* answer text — i.e. NOT a progress-card payload (per
|
|
172
|
+
* `isCardPayload` heuristic) and NOT a placeholder string.
|
|
173
|
+
* Returns null if no such call has been recorded.
|
|
174
|
+
*
|
|
175
|
+
* Used to pin the v2 first-answer-text deadline (Class A: <800ms
|
|
176
|
+
* for 👀 and answer text bounded TBD by PR 3; Class B/C: TBD).
|
|
177
|
+
*/
|
|
178
|
+
firstAnswerTextMs(chatId: string): number | null
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Issue #626 invariant — exactly one anchor `sendMessage` per
|
|
182
|
+
* (chatId, threadId, turnKey?). Returns the count of fresh
|
|
183
|
+
* `sendMessage` calls (NOT edits) for the chat. The anchor is the
|
|
184
|
+
* single message that subsequent edits target. Multiple anchors for
|
|
185
|
+
* the same logical turn = the duplicate-status-message bug.
|
|
186
|
+
*
|
|
187
|
+
* Usage: `expect(h.anchorMessageCount(CHAT)).toBe(1)` after a
|
|
188
|
+
* complete turn. Pass `threadId` to disambiguate forum topics.
|
|
189
|
+
*
|
|
190
|
+
* Returns -1 if the recorder isn't tracking calls (defensive — the
|
|
191
|
+
* harness shouldn't reach this state, but a -1 is more actionable
|
|
192
|
+
* than a silent 0 if it does).
|
|
193
|
+
*/
|
|
194
|
+
anchorMessageCount(chatId: string, threadId?: number): number
|
|
195
|
+
|
|
196
|
+
// ─── IPC + bridge lifecycle helpers (ships with PR for I1–I5) ───────
|
|
197
|
+
// The IPC lifecycle (clients connecting, registering, sending typed
|
|
198
|
+
// messages, disconnecting) is invisible to the existing waiting-UX
|
|
199
|
+
// helpers above. Production bugs in this layer (Bug A premature 👍 on
|
|
200
|
+
// anonymous disconnect, Bug B `update_placeholder` lethality, Bug D
|
|
201
|
+
// 👍-before-delivery) all share a root cause: the harness had no way
|
|
202
|
+
// to express "a client just connected/sent/disconnected." These
|
|
203
|
+
// helpers route through PRODUCTION code paths where possible —
|
|
204
|
+
// `validateClientMessage` is the real validator from
|
|
205
|
+
// `gateway/ipc-server.ts`; the disconnect handler is mirrored from
|
|
206
|
+
// `gateway.ts`'s `onClientDisconnected` (extracted helper landing in
|
|
207
|
+
// PR #600 — until that merges, the harness mirror keeps the same
|
|
208
|
+
// semantics so the invariants are testable now).
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Simulate a client opening an IPC connection. If `agentName` is
|
|
212
|
+
* provided, the harness immediately routes a `register` message
|
|
213
|
+
* through the production validator so subsequent
|
|
214
|
+
* `bridgeDisconnect()` cleans up the right per-agent state. If
|
|
215
|
+
* `agentName == null`, the connection stays anonymous (recall.py-
|
|
216
|
+
* style one-shot caller). Returns the synthetic `clientId`.
|
|
217
|
+
*/
|
|
218
|
+
bridgeConnect(agentName: string | null): string
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Simulate a client closing its IPC connection. Routes through the
|
|
222
|
+
* harness's mirror of `onClientDisconnected` — flushes per-agent
|
|
223
|
+
* status reactions to setDone() and disposes that agent's draft
|
|
224
|
+
* streams. **Crucially: anonymous clients (agentName=null) flow
|
|
225
|
+
* through the same handler but MUST NOT mutate any active state.**
|
|
226
|
+
* That's invariant I1 (Bug A's failure mode).
|
|
227
|
+
*/
|
|
228
|
+
bridgeDisconnect(clientId: string): void
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Simulate a client sending an IPC message. The payload is run
|
|
232
|
+
* through the production `validateClientMessage` validator — if it
|
|
233
|
+
* fails validation (e.g. legacy `update_placeholder` type), the
|
|
234
|
+
* harness logs and discards, mirroring `processBuffer`'s loop. The
|
|
235
|
+
* connection stays open; no state is mutated. That's invariant I4.
|
|
236
|
+
*/
|
|
237
|
+
sendIpcMessage(clientId: string, message: object): void
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Timestamp of the last `setMessageReaction` for `chatId`, or null.
|
|
241
|
+
* Used by I3 to compare reaction-fired-at against
|
|
242
|
+
* answer-text-delivered-at.
|
|
243
|
+
*/
|
|
244
|
+
lastReactionEmojiAt(chatId: string): number | null
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Timestamp of the LAST `sendMessage` / `editMessageText` whose
|
|
248
|
+
* payload looks like real model text (not card, not placeholder).
|
|
249
|
+
* Used by I3 to assert 👍 fires AFTER delivery, not before.
|
|
250
|
+
*/
|
|
251
|
+
lastAnswerTextDeliveredAt(chatId: string): number | null
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Test introspection of the side-effect callbacks fired by the real
|
|
255
|
+
* `flushOnAgentDisconnect` helper since the harness was created. Tests
|
|
256
|
+
* that want to assert "no flush ran" check `clearActiveReactionsCalls`
|
|
257
|
+
* and `disposeProgressDriverCalls` are still 0 after a sequence of
|
|
258
|
+
* anonymous bridge cycles. `activeAgentCount` is the live size of the
|
|
259
|
+
* harness's mirror of `activeStatusReactions` — non-zero means at
|
|
260
|
+
* least one registered agent is still active.
|
|
261
|
+
*/
|
|
262
|
+
flushSideEffects(): {
|
|
263
|
+
clearActiveReactionsCalls: number
|
|
264
|
+
disposeProgressDriverCalls: number
|
|
265
|
+
flushLog: ReadonlyArray<string>
|
|
266
|
+
activeAgentCount: number
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ─── #546 dedup integration ─────────────────────────────────────────
|
|
270
|
+
/**
|
|
271
|
+
* Real `OutboundDedupCache` wired into the harness's `streamReply`
|
|
272
|
+
* path when `opts.withDedup === true`. Null otherwise. Tests assert
|
|
273
|
+
* on `harness.dedup.size(now)` to confirm a record landed; or invoke
|
|
274
|
+
* `harness.dedup.check(...)` directly to verify a hit before the
|
|
275
|
+
* second send is attempted.
|
|
276
|
+
*/
|
|
277
|
+
dedup: OutboundDedupCache | null
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Count of dedup-suppressed sends since harness creation. When the
|
|
281
|
+
* cache catches a retry, the harness records this so I6 / replay
|
|
282
|
+
* tests can assert "yes, dedup actually fired" without poking at
|
|
283
|
+
* counters in the cache.
|
|
284
|
+
*/
|
|
285
|
+
dedupSuppressedCount(): number
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Convenience scenario: simulate the #546 turn-flush + replay
|
|
289
|
+
* sequence end-to-end:
|
|
290
|
+
* 1. First send(text) — lands as a fresh sendMessage and records
|
|
291
|
+
* into the dedup cache.
|
|
292
|
+
* 2. Bridge disconnects + a fresh agent reconnects. Disconnect is
|
|
293
|
+
* REGISTERED (not anonymous) so `flushOnAgentDisconnect` actually
|
|
294
|
+
* runs — proving dedup survives the production cleanup path.
|
|
295
|
+
* Anonymous disconnects are a no-op (I1) and would let this
|
|
296
|
+
* scenario pass even if dedup were broken in flush.
|
|
297
|
+
* 3. Second send(text) — claude-code's preserved tool_call replay.
|
|
298
|
+
* Should be suppressed by dedup (no second outbound landed).
|
|
299
|
+
*
|
|
300
|
+
* Returns `{ firstMessageId, suppressedSecond, flushRan }`. Tests
|
|
301
|
+
* assert `suppressedSecond === true` AND `flushRan === true` so the
|
|
302
|
+
* full path is exercised, not a tautology.
|
|
303
|
+
*/
|
|
304
|
+
simulateRetryDup(args: {
|
|
305
|
+
chat_id: string
|
|
306
|
+
text: string
|
|
307
|
+
}): Promise<{
|
|
308
|
+
firstMessageId: number | null
|
|
309
|
+
suppressedSecond: boolean
|
|
310
|
+
flushRan: boolean
|
|
311
|
+
}>
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* "Fresh send" — always issues a new `sendMessage` for `chat_id`,
|
|
315
|
+
* does NOT update the harness's stream-edit cache. Routes through
|
|
316
|
+
* the dedup cache when wired. Mirrors production's turn-flush
|
|
317
|
+
* backstop and the wake-audit greeting path: every fire is a
|
|
318
|
+
* fresh user-visible message, not a streaming edit. Use this in
|
|
319
|
+
* dedup tests where "the same content emitted twice" means two
|
|
320
|
+
* NEW messages, not a streaming edit-in-place.
|
|
321
|
+
*
|
|
322
|
+
* Returns the new `message_id`, or null if dedup suppressed the send.
|
|
323
|
+
*/
|
|
324
|
+
send(args: { chat_id: string; text: string; parse_mode?: string }): Promise<number | null>
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const DEFAULT_GAP_MS = 1500
|
|
328
|
+
|
|
329
|
+
export function createRealGatewayHarness(
|
|
330
|
+
opts: RealGatewayHarnessOpts = {},
|
|
331
|
+
): RealGatewayHarnessHandle {
|
|
332
|
+
const gapMs = opts.gapMs ?? DEFAULT_GAP_MS
|
|
333
|
+
|
|
334
|
+
// Phase 1 harness: controller + driver + recorder + clock.
|
|
335
|
+
const inner = createWaitingUxHarness(opts)
|
|
336
|
+
|
|
337
|
+
// Track which (chatId) keys have an active turn — mirrors gateway.ts's
|
|
338
|
+
// `activeTurnStartedAt` for the F2 early-ack mid-turn check. Set on
|
|
339
|
+
// flush (when inner.inbound runs); cleared by the `turn_end` session
|
|
340
|
+
// event so subsequent fresh inbounds get the early-ack again.
|
|
341
|
+
const activeTurns = new Set<string>()
|
|
342
|
+
|
|
343
|
+
// Wrap inner.inbound() with the real coalescer so the test surface
|
|
344
|
+
// matches what production sees end-to-end.
|
|
345
|
+
const coalescer = createInboundCoalescer<CoalescePayload>({
|
|
346
|
+
gapMs,
|
|
347
|
+
merge: (entries) => {
|
|
348
|
+
const last = entries[entries.length - 1]
|
|
349
|
+
return {
|
|
350
|
+
chatId: last.chatId,
|
|
351
|
+
messageId: last.messageId,
|
|
352
|
+
userId: last.userId,
|
|
353
|
+
text: entries.map((e) => e.text).join('\n'),
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
onFlush: (_key, merged) => {
|
|
357
|
+
// The flush is the moment first-paint runs in production —
|
|
358
|
+
// controller.setQueued() (👀) and driver.startTurn(). Delegate
|
|
359
|
+
// to the inner harness's inbound() which already wires both.
|
|
360
|
+
activeTurns.add(merged.chatId)
|
|
361
|
+
inner.inbound({ chatId: merged.chatId, messageId: merged.messageId, text: merged.text })
|
|
362
|
+
},
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
function inbound(args: { chatId: string; messageId: number; text?: string; userId?: string }): void {
|
|
366
|
+
const userId = args.userId ?? '777' // matches update-factory's default sender
|
|
367
|
+
|
|
368
|
+
// F2 fix mirror: fire 👀 directly via bot.api on raw arrival, BEFORE
|
|
369
|
+
// the coalescer's gap window. Production runs `maybeEarlyAckReaction`
|
|
370
|
+
// here for paired DM users on a fresh turn. The harness skips the
|
|
371
|
+
// access/chatType checks (the harness has no access file) and gates
|
|
372
|
+
// only on "no active turn" so the mid-turn-flash case stays catchable.
|
|
373
|
+
const turnKey = args.chatId
|
|
374
|
+
if (!activeTurns.has(turnKey)) {
|
|
375
|
+
void inner.bot.api.setMessageReaction(args.chatId, args.messageId, [
|
|
376
|
+
{ type: 'emoji', emoji: '👀' },
|
|
377
|
+
])
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const payload: CoalescePayload = {
|
|
381
|
+
chatId: args.chatId,
|
|
382
|
+
messageId: args.messageId,
|
|
383
|
+
text: args.text ?? '',
|
|
384
|
+
userId,
|
|
385
|
+
}
|
|
386
|
+
const key = inboundCoalesceKey(args.chatId, userId)
|
|
387
|
+
const result = coalescer.enqueue(key, payload)
|
|
388
|
+
if (result.bypass) {
|
|
389
|
+
// gapMs <= 0 — production calls handleInbound directly; mirror
|
|
390
|
+
// by calling the inner harness's first-paint immediately.
|
|
391
|
+
activeTurns.add(turnKey)
|
|
392
|
+
inner.inbound({ chatId: args.chatId, messageId: args.messageId, text: args.text })
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function feedSessionEvent(ev: SessionEvent): void {
|
|
397
|
+
if (ev.kind === 'turn_end') {
|
|
398
|
+
// Turn complete — clear the active-turn marker so the next inbound
|
|
399
|
+
// gets the early-ack again. Mirrors gateway.ts clearing
|
|
400
|
+
// activeTurnStartedAt on turn-end (production tracks it per
|
|
401
|
+
// statusKey but the harness collapses to per-chat).
|
|
402
|
+
activeTurns.clear()
|
|
403
|
+
}
|
|
404
|
+
inner.feedSessionEvent(ev)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function finalize(): void {
|
|
408
|
+
coalescer.reset()
|
|
409
|
+
inner.finalize()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function expectNoPlaceholderEdits(chatId: string): RecordedCall[] {
|
|
413
|
+
return inner.recorder.calls.filter(
|
|
414
|
+
(c) =>
|
|
415
|
+
(c.kind === 'sendMessage' || c.kind === 'editMessageText') &&
|
|
416
|
+
c.chat_id === chatId &&
|
|
417
|
+
isPlaceholderPayload(c.payload),
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function expectNoCardSent(chatId: string): number | null {
|
|
422
|
+
return inner.recorder.progressCardSendMs(chatId)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function firstAnswerTextMs(chatId: string): number | null {
|
|
426
|
+
const hit = inner.recorder.calls.find(
|
|
427
|
+
(c) =>
|
|
428
|
+
(c.kind === 'sendMessage' || c.kind === 'editMessageText') &&
|
|
429
|
+
c.chat_id === chatId &&
|
|
430
|
+
!isCardPayload(c.payload) &&
|
|
431
|
+
!isPlaceholderPayload(c.payload),
|
|
432
|
+
)
|
|
433
|
+
return hit ? hit.ts : null
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function anchorMessageCount(chatId: string, threadId?: number): number {
|
|
437
|
+
if (!Array.isArray(inner.recorder.calls)) return -1
|
|
438
|
+
return inner.recorder.calls.filter((c) => {
|
|
439
|
+
if (c.kind !== 'sendMessage') return false
|
|
440
|
+
if (c.chat_id !== chatId) return false
|
|
441
|
+
if (threadId == null) return true
|
|
442
|
+
// RecordedCall payload may carry message_thread_id when the
|
|
443
|
+
// production code passed one — match if requested.
|
|
444
|
+
const opts = (c as { opts?: { message_thread_id?: number } }).opts
|
|
445
|
+
return opts?.message_thread_id === threadId
|
|
446
|
+
}).length
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function lastReactionEmojiAt(chatId: string): number | null {
|
|
450
|
+
const hits = inner.recorder.calls.filter(
|
|
451
|
+
(c) => c.kind === 'setMessageReaction' && c.chat_id === chatId,
|
|
452
|
+
)
|
|
453
|
+
return hits.length === 0 ? null : hits[hits.length - 1].ts
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function lastAnswerTextDeliveredAt(chatId: string): number | null {
|
|
457
|
+
const hits = inner.recorder.calls.filter(
|
|
458
|
+
(c) =>
|
|
459
|
+
(c.kind === 'sendMessage' || c.kind === 'editMessageText') &&
|
|
460
|
+
c.chat_id === chatId &&
|
|
461
|
+
!isCardPayload(c.payload) &&
|
|
462
|
+
!isPlaceholderPayload(c.payload),
|
|
463
|
+
)
|
|
464
|
+
return hits.length === 0 ? null : hits[hits.length - 1].ts
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ─── #546 dedup wiring (opt-in) ───────────────────────────────────────
|
|
468
|
+
// When `withDedup` is set, wrap the inner harness's `streamReply` so
|
|
469
|
+
// the same content sent twice within the TTL only lands once. Mirrors
|
|
470
|
+
// the production fix at `gateway.ts:2233` (`executeStreamReply`) and
|
|
471
|
+
// `gateway.ts:1893` (`executeReply`): both check the cache, return
|
|
472
|
+
// early on hit (NOT calling setDone — the original send already
|
|
473
|
+
// finalized), and record after a successful send. F1–F4 tests don't
|
|
474
|
+
// care about dedup so it stays opt-in.
|
|
475
|
+
//
|
|
476
|
+
// INVARIANT MIRROR: production's dedup-hit branch returns
|
|
477
|
+
// { content: [{ type: 'text', text: 'sent (deduped — ...)' }] }
|
|
478
|
+
// without firing setDone. The harness wrap matches this — the
|
|
479
|
+
// controller is left untouched on suppression. If production
|
|
480
|
+
// changes (e.g. fires setDone on suppression for some reason),
|
|
481
|
+
// update both sites together.
|
|
482
|
+
const dedup = opts.withDedup === true ? new OutboundDedupCache({ ttlMs: opts.dedupTtlMs }) : null
|
|
483
|
+
let dedupSuppressed = 0
|
|
484
|
+
const innerStreamReply = inner.streamReply
|
|
485
|
+
const streamReply = dedup == null
|
|
486
|
+
? innerStreamReply
|
|
487
|
+
: async (args: { chat_id: string; text: string; done?: boolean }): Promise<void> => {
|
|
488
|
+
const now = Date.now()
|
|
489
|
+
const hit = dedup.check(args.chat_id, undefined, args.text, now)
|
|
490
|
+
if (hit != null) {
|
|
491
|
+
dedupSuppressed++
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
await innerStreamReply(args)
|
|
495
|
+
dedup.record(args.chat_id, undefined, args.text, Date.now())
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Fresh-send wrapper. Routes through the dedup cache (if wired) and
|
|
500
|
+
* always calls `bot.api.sendMessage` directly — bypasses the inner
|
|
501
|
+
* harness's stream-edit cache. Returns the new message_id, or null
|
|
502
|
+
* if dedup suppressed the send.
|
|
503
|
+
*/
|
|
504
|
+
async function send(args: {
|
|
505
|
+
chat_id: string
|
|
506
|
+
text: string
|
|
507
|
+
parse_mode?: string
|
|
508
|
+
}): Promise<number | null> {
|
|
509
|
+
const now = Date.now()
|
|
510
|
+
if (dedup != null) {
|
|
511
|
+
const hit = dedup.check(args.chat_id, undefined, args.text, now)
|
|
512
|
+
if (hit != null) {
|
|
513
|
+
dedupSuppressed++
|
|
514
|
+
return null
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const result = (await inner.bot.api.sendMessage(args.chat_id, args.text, {
|
|
518
|
+
parse_mode: args.parse_mode ?? 'HTML',
|
|
519
|
+
})) as { message_id: number }
|
|
520
|
+
if (dedup != null) {
|
|
521
|
+
dedup.record(args.chat_id, undefined, args.text, Date.now())
|
|
522
|
+
}
|
|
523
|
+
return result.message_id
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function simulateRetryDup(args: {
|
|
527
|
+
chat_id: string
|
|
528
|
+
text: string
|
|
529
|
+
}): Promise<{
|
|
530
|
+
firstMessageId: number | null
|
|
531
|
+
suppressedSecond: boolean
|
|
532
|
+
flushRan: boolean
|
|
533
|
+
}> {
|
|
534
|
+
if (dedup == null) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
'simulateRetryDup requires withDedup: true on createRealGatewayHarness',
|
|
537
|
+
)
|
|
538
|
+
}
|
|
539
|
+
const firstMessageId = await send(args)
|
|
540
|
+
|
|
541
|
+
// Bridge cycle with a REGISTERED agent so flushOnAgentDisconnect
|
|
542
|
+
// actually runs. Anonymous disconnects no-op (I1) — using one
|
|
543
|
+
// there would make the scenario a tautology (the bridge cycle
|
|
544
|
+
// wouldn't touch state at all). Production's #546 reproducer
|
|
545
|
+
// involves the claude-code bridge (registered) crashing, so the
|
|
546
|
+
// registered path is the one we need to prove dedup survives.
|
|
547
|
+
const flushBefore = disposeProgressDriverCalls
|
|
548
|
+
const cid = bridgeConnect('agent-claude')
|
|
549
|
+
bridgeDisconnect(cid)
|
|
550
|
+
const flushRan = disposeProgressDriverCalls > flushBefore
|
|
551
|
+
|
|
552
|
+
const suppressedBefore = dedupSuppressed
|
|
553
|
+
const secondMessageId = await send(args)
|
|
554
|
+
const suppressedSecond = dedupSuppressed > suppressedBefore && secondMessageId == null
|
|
555
|
+
|
|
556
|
+
return { firstMessageId, suppressedSecond, flushRan }
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ─── IPC + bridge lifecycle simulation ────────────────────────────────
|
|
560
|
+
// The harness wires `bridgeDisconnect` through the REAL production
|
|
561
|
+
// helper `flushOnAgentDisconnect` from `gateway/disconnect-flush.ts`
|
|
562
|
+
// (extracted in PR #600). Tests against this harness exercise actual
|
|
563
|
+
// production code, not a parallel reimplementation — so the I1/I2
|
|
564
|
+
// invariants would catch a regression if someone reverted the
|
|
565
|
+
// `if (agentName == null) return false` gate in the helper.
|
|
566
|
+
//
|
|
567
|
+
// Per-agent state mirrors what `gateway.ts` holds at module scope:
|
|
568
|
+
// `activeStatusReactions` keyed by agent name. Each registered agent
|
|
569
|
+
// gets a fresh per-agent controller; the production helper mutates
|
|
570
|
+
// the Map in place when an agent disconnects.
|
|
571
|
+
//
|
|
572
|
+
// The shared `inner.controller` (from waiting-ux-harness) stays as
|
|
573
|
+
// the default for back-compat with tests that don't go through the
|
|
574
|
+
// bridge surface — `bridgeConnect(null)` doesn't touch it either.
|
|
575
|
+
const clientsById = new Map<string, { agentName: string | null }>()
|
|
576
|
+
// Production-shaped Maps for the helper. Keyed by agent name (one
|
|
577
|
+
// entry per registered agent in the harness; production keys by
|
|
578
|
+
// chat:thread:msgId but the helper's behavior is per-entry-iteration
|
|
579
|
+
// either way, so the key shape doesn't change semantics).
|
|
580
|
+
const activeStatusReactions = new Map<string, StatusReactionController>()
|
|
581
|
+
const activeReactionMsgIds = new Map<string, { chatId: string; messageId: number }>()
|
|
582
|
+
const activeTurnStartedAt = new Map<string, number>()
|
|
583
|
+
const activeDraftStreams = new Map<string, { isFinal: () => boolean; finalize: () => Promise<void> }>()
|
|
584
|
+
const activeDraftParseModes = new Map<string, 'HTML' | 'MarkdownV2' | undefined>()
|
|
585
|
+
let clearActiveReactionsCalls = 0
|
|
586
|
+
let disposeProgressDriverCalls = 0
|
|
587
|
+
const flushLog: string[] = []
|
|
588
|
+
const ipcLog: Array<{ kind: 'invalid' | 'unknown' | 'accepted'; raw: unknown }> = []
|
|
589
|
+
|
|
590
|
+
function bridgeConnect(agentName: string | null): string {
|
|
591
|
+
const clientId = `client-${Math.random().toString(36).slice(2, 10)}`
|
|
592
|
+
clientsById.set(clientId, { agentName })
|
|
593
|
+
if (agentName != null) {
|
|
594
|
+
// Validate the synthetic register message through the real validator.
|
|
595
|
+
const reg = { type: 'register', agentName }
|
|
596
|
+
if (!validateClientMessage(reg)) {
|
|
597
|
+
// Should never happen with a sane agentName — surface loudly.
|
|
598
|
+
throw new Error(`harness bug: register validation failed for ${agentName}`)
|
|
599
|
+
}
|
|
600
|
+
// Per-agent controller mirrors what gateway.ts puts in
|
|
601
|
+
// `activeStatusReactions.set(key, ctrl)`. The production helper
|
|
602
|
+
// iterates the Map and calls setDone on each entry; per-agent
|
|
603
|
+
// isolation comes from the helper's `agentName == null` gate
|
|
604
|
+
// (anonymous = skip everything) — NOT from selective deletion.
|
|
605
|
+
const ctrl = new StatusReactionController(
|
|
606
|
+
async () => { /* harness uses inner.controller for the shared chat */ },
|
|
607
|
+
opts.allowedReactions ?? null,
|
|
608
|
+
{ debounceMs: opts.debounceMs ?? 700 },
|
|
609
|
+
)
|
|
610
|
+
activeStatusReactions.set(agentName, ctrl)
|
|
611
|
+
}
|
|
612
|
+
return clientId
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function bridgeDisconnect(clientId: string): void {
|
|
616
|
+
const meta = clientsById.get(clientId)
|
|
617
|
+
if (meta == null) return
|
|
618
|
+
clientsById.delete(clientId)
|
|
619
|
+
|
|
620
|
+
// I1 + I2 contract: route through the REAL production helper.
|
|
621
|
+
// `agentName == null` ⇒ helper's anonymous-skip gate fires and the
|
|
622
|
+
// Map is untouched. `agentName != null` ⇒ helper iterates the Map
|
|
623
|
+
// and flushes setDone on every entry. The Map keys mean: in the
|
|
624
|
+
// I1 test, ANY remaining controller would prove the gate was
|
|
625
|
+
// bypassed; in the I2 test, the Map will have entries for OTHER
|
|
626
|
+
// agents that get incorrectly cleared if the helper is buggy.
|
|
627
|
+
flushOnAgentDisconnect({
|
|
628
|
+
agentName: meta.agentName,
|
|
629
|
+
activeStatusReactions,
|
|
630
|
+
activeReactionMsgIds,
|
|
631
|
+
activeTurnStartedAt,
|
|
632
|
+
activeDraftStreams,
|
|
633
|
+
activeDraftParseModes,
|
|
634
|
+
clearActiveReactions: () => { clearActiveReactionsCalls++ },
|
|
635
|
+
disposeProgressDriver: () => { disposeProgressDriverCalls++ },
|
|
636
|
+
log: (msg) => { flushLog.push(msg) },
|
|
637
|
+
})
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** Test introspection — counts of side-effect callbacks fired by the helper. */
|
|
641
|
+
function flushSideEffects(): {
|
|
642
|
+
clearActiveReactionsCalls: number
|
|
643
|
+
disposeProgressDriverCalls: number
|
|
644
|
+
flushLog: ReadonlyArray<string>
|
|
645
|
+
activeAgentCount: number
|
|
646
|
+
} {
|
|
647
|
+
return {
|
|
648
|
+
clearActiveReactionsCalls,
|
|
649
|
+
disposeProgressDriverCalls,
|
|
650
|
+
flushLog,
|
|
651
|
+
activeAgentCount: activeStatusReactions.size,
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function sendIpcMessage(clientId: string, message: object): void {
|
|
656
|
+
if (!clientsById.has(clientId)) {
|
|
657
|
+
throw new Error(`harness: unknown clientId ${clientId} (was bridgeConnect called?)`)
|
|
658
|
+
}
|
|
659
|
+
// I4: legacy IPC types must be tolerated — the validator returns
|
|
660
|
+
// false, processBuffer logs+continues, the connection stays open.
|
|
661
|
+
// The harness records the outcome so tests can assert "logged and
|
|
662
|
+
// discarded, not thrown."
|
|
663
|
+
if (!validateClientMessage(message)) {
|
|
664
|
+
ipcLog.push({ kind: 'invalid', raw: message })
|
|
665
|
+
return
|
|
666
|
+
}
|
|
667
|
+
// Validated messages would normally route to the gateway's
|
|
668
|
+
// per-type handler. The harness doesn't replay every dispatch —
|
|
669
|
+
// tests that exercise the full session-event path use
|
|
670
|
+
// `feedSessionEvent` directly. This helper exists to exercise the
|
|
671
|
+
// VALIDATOR boundary, which is where the lethality lives.
|
|
672
|
+
ipcLog.push({ kind: 'accepted', raw: message })
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
...inner,
|
|
677
|
+
inbound,
|
|
678
|
+
feedSessionEvent,
|
|
679
|
+
finalize,
|
|
680
|
+
coalescer,
|
|
681
|
+
coalesceBufferSize: () => coalescer.size(),
|
|
682
|
+
gapMs,
|
|
683
|
+
expectNoPlaceholderEdits,
|
|
684
|
+
expectNoCardSent,
|
|
685
|
+
anchorMessageCount,
|
|
686
|
+
firstAnswerTextMs,
|
|
687
|
+
bridgeConnect,
|
|
688
|
+
bridgeDisconnect,
|
|
689
|
+
sendIpcMessage,
|
|
690
|
+
lastReactionEmojiAt,
|
|
691
|
+
lastAnswerTextDeliveredAt,
|
|
692
|
+
flushSideEffects,
|
|
693
|
+
streamReply,
|
|
694
|
+
dedup,
|
|
695
|
+
dedupSuppressedCount: () => dedupSuppressed,
|
|
696
|
+
simulateRetryDup,
|
|
697
|
+
send,
|
|
698
|
+
}
|
|
699
|
+
}
|