switchroom 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +447 -0
- package/bin/autoaccept.exp +81 -0
- package/bin/boot-self-test.sh +149 -0
- package/bin/bridge-watchdog.sh +967 -0
- package/bin/handoff-briefing.sh +206 -0
- package/bin/run-hook.sh +228 -0
- package/bin/switchroom.ts +4 -0
- package/bin/timezone-hook.sh +67 -0
- package/bin/user-profile-refresh-hook.sh +38 -0
- package/bin/workspace-dynamic-hook.sh +142 -0
- package/bin/workspace-stable-hook.sh +57 -0
- package/dist/cli/autoaccept-poll.js +118 -0
- package/dist/cli/switchroom.js +48557 -0
- package/package.json +95 -0
- package/profiles/_base/settings.json.hbs +15 -0
- package/profiles/_base/start.sh.hbs +383 -0
- package/profiles/_shared/telegram-style.md.hbs +140 -0
- package/profiles/coding/CLAUDE.md.hbs +57 -0
- package/profiles/coding/skills/architecture/SKILL.md +70 -0
- package/profiles/coding/skills/code-review/SKILL.md +58 -0
- package/profiles/coding/workspace/SOUL.md.hbs +25 -0
- package/profiles/default/CLAUDE.md +238 -0
- package/profiles/default/CLAUDE.md.hbs +113 -0
- package/profiles/default/workspace/CLAUDE.md.hbs +126 -0
- package/profiles/default/workspace/HEARTBEAT.md.hbs +40 -0
- package/profiles/default/workspace/IDENTITY.md.hbs +32 -0
- package/profiles/default/workspace/MEMORY.md.hbs +29 -0
- package/profiles/default/workspace/SOUL.md.hbs +61 -0
- package/profiles/default/workspace/TOOLS.md.hbs +29 -0
- package/profiles/default/workspace/USER.md.hbs +52 -0
- package/profiles/default/workspace/memory/.gitkeep +0 -0
- package/profiles/executive-assistant/CLAUDE.md.hbs +51 -0
- package/profiles/executive-assistant/skills/daily-briefing/SKILL.md +55 -0
- package/profiles/executive-assistant/skills/meeting-prep/SKILL.md +58 -0
- package/profiles/executive-assistant/workspace/SOUL.md.hbs +25 -0
- package/profiles/health-coach/CLAUDE.md.hbs +45 -0
- package/profiles/health-coach/skills/check-in/SKILL.md +41 -0
- package/profiles/health-coach/skills/weekly-review/SKILL.md +53 -0
- package/profiles/health-coach/workspace/SOUL.md.hbs +25 -0
- package/skills/buildkite-agent-infrastructure/SKILL.md +302 -0
- package/skills/buildkite-agent-infrastructure/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-infrastructure/references/audit-logging.md +87 -0
- package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +690 -0
- package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +38 -0
- package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +73 -0
- package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +137 -0
- package/skills/buildkite-agent-infrastructure/references/sso-saml.md +92 -0
- package/skills/buildkite-agent-runtime/SKILL.md +476 -0
- package/skills/buildkite-agent-runtime/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-runtime/references/flag-reference.md +417 -0
- package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +555 -0
- package/skills/buildkite-api/SKILL.md +285 -0
- package/skills/buildkite-api/agents/openai.yaml +6 -0
- package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-api/references/graphql-reference.md +195 -0
- package/skills/buildkite-api/references/patterns.md +44 -0
- package/skills/buildkite-api/references/webhooks.md +161 -0
- package/skills/buildkite-cli/SKILL.md +379 -0
- package/skills/buildkite-cli/agents/openai.yaml +6 -0
- package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-cli/references/command-reference.md +181 -0
- package/skills/buildkite-migration/SKILL.md +182 -0
- package/skills/buildkite-pipelines/SKILL.md +464 -0
- package/skills/buildkite-pipelines/agents/openai.yaml +6 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-pipelines/examples/basic-pipeline.yml +24 -0
- package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +100 -0
- package/skills/buildkite-pipelines/references/advanced-patterns.md +286 -0
- package/skills/buildkite-pipelines/references/retry-and-error-codes.md +131 -0
- package/skills/buildkite-pipelines/references/step-types-reference.md +225 -0
- package/skills/buildkite-secure-delivery/SKILL.md +168 -0
- package/skills/buildkite-secure-delivery/agents/openai.yaml +6 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +83 -0
- package/skills/buildkite-secure-delivery/references/package-publishing.md +100 -0
- package/skills/buildkite-test-engine/SKILL.md +239 -0
- package/skills/buildkite-test-engine/agents/openai.yaml +6 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-test-engine/examples/bktec-splitting.yml +16 -0
- package/skills/buildkite-test-engine/examples/collector-pipeline.yml +11 -0
- package/skills/buildkite-test-engine/references/collectors.md +198 -0
- package/skills/buildkite-test-engine/references/splitting-examples.md +93 -0
- package/skills/docx/LICENSE.txt +30 -0
- package/skills/docx/SKILL.md +590 -0
- package/skills/docx/VENDORED.md +32 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/accept_changes.py +135 -0
- package/skills/docx/scripts/comment.py +318 -0
- package/skills/docx/scripts/office/helpers/__init__.py +0 -0
- package/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/docx/scripts/office/pack.py +159 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/scripts/office/soffice.py +183 -0
- package/skills/docx/scripts/office/unpack.py +132 -0
- package/skills/docx/scripts/office/validate.py +111 -0
- package/skills/docx/scripts/office/validators/__init__.py +15 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/base.py +847 -0
- package/skills/docx/scripts/office/validators/docx.py +446 -0
- package/skills/docx/scripts/office/validators/pptx.py +275 -0
- package/skills/docx/scripts/office/validators/redlining.py +247 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/file-bug/SKILL.md +129 -0
- package/skills/humanizer/LICENSE +21 -0
- package/skills/humanizer/SKILL.md +559 -0
- package/skills/humanizer/VENDORED.md +38 -0
- package/skills/humanizer-calibrate/SKILL.md +144 -0
- package/skills/mcp-builder/LICENSE.txt +202 -0
- package/skills/mcp-builder/SKILL.md +236 -0
- package/skills/mcp-builder/VENDORED.md +32 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +314 -0
- package/skills/pdf/VENDORED.md +32 -0
- package/skills/pdf/forms.md +294 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
- package/skills/pdf/scripts/check_fillable_fields.py +11 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/skills/pdf/scripts/create_validation_image.py +37 -0
- package/skills/pdf/scripts/extract_form_field_info.py +122 -0
- package/skills/pdf/scripts/extract_form_structure.py +115 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/skills/pptx/LICENSE.txt +30 -0
- package/skills/pptx/SKILL.md +232 -0
- package/skills/pptx/VENDORED.md +32 -0
- package/skills/pptx/editing.md +205 -0
- package/skills/pptx/pptxgenjs.md +420 -0
- package/skills/pptx/scripts/__init__.py +0 -0
- package/skills/pptx/scripts/add_slide.py +195 -0
- package/skills/pptx/scripts/clean.py +286 -0
- package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
- package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/pptx/scripts/office/pack.py +159 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/scripts/office/soffice.py +183 -0
- package/skills/pptx/scripts/office/unpack.py +132 -0
- package/skills/pptx/scripts/office/validate.py +111 -0
- package/skills/pptx/scripts/office/validators/__init__.py +15 -0
- package/skills/pptx/scripts/office/validators/base.py +847 -0
- package/skills/pptx/scripts/office/validators/docx.py +446 -0
- package/skills/pptx/scripts/office/validators/pptx.py +275 -0
- package/skills/pptx/scripts/office/validators/redlining.py +247 -0
- package/skills/pptx/scripts/thumbnail.py +289 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/VENDORED.md +32 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/skills/switchroom-architecture/SKILL.md +60 -0
- package/skills/switchroom-architecture/cascade.md +112 -0
- package/skills/switchroom-architecture/sub-agents.md +87 -0
- package/skills/switchroom-architecture/telegram.md +94 -0
- package/skills/switchroom-cli/SKILL.md +274 -0
- package/skills/switchroom-health/SKILL.md +101 -0
- package/skills/switchroom-install/SKILL.md +116 -0
- package/skills/switchroom-manage/SKILL.md +90 -0
- package/skills/switchroom-status/SKILL.md +69 -0
- package/skills/switchroom-status/scripts/status.sh +69 -0
- package/skills/telegram-test-harness/SKILL.md +191 -0
- package/skills/token-helpers/SKILL.md +73 -0
- package/skills/token-helpers/scripts/google-cal-token.sh +62 -0
- package/skills/token-helpers/scripts/ms-graph-token.sh +70 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/VENDORED.md +32 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/skills/xlsx/LICENSE.txt +30 -0
- package/skills/xlsx/SKILL.md +292 -0
- package/skills/xlsx/VENDORED.md +32 -0
- package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/xlsx/scripts/office/pack.py +159 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/xlsx/scripts/office/soffice.py +183 -0
- package/skills/xlsx/scripts/office/unpack.py +132 -0
- package/skills/xlsx/scripts/office/validate.py +111 -0
- package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
- package/skills/xlsx/scripts/office/validators/base.py +847 -0
- package/skills/xlsx/scripts/office/validators/docx.py +446 -0
- package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
- package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
- package/skills/xlsx/scripts/recalc.py +184 -0
- package/telegram-plugin/.claude-plugin/plugin.json +20 -0
- package/telegram-plugin/.mcp.json +14 -0
- package/telegram-plugin/LICENSE +21 -0
- package/telegram-plugin/README.md +352 -0
- package/telegram-plugin/active-pins-sweep.ts +204 -0
- package/telegram-plugin/active-pins.ts +146 -0
- package/telegram-plugin/active-reactions-sweep.ts +79 -0
- package/telegram-plugin/active-reactions.ts +134 -0
- package/telegram-plugin/admin-commands/dispatch.test.ts +149 -0
- package/telegram-plugin/admin-commands/index.ts +106 -0
- package/telegram-plugin/answer-stream.ts +565 -0
- package/telegram-plugin/ask-user.ts +179 -0
- package/telegram-plugin/attachment-path.ts +80 -0
- package/telegram-plugin/auth-code-redact.ts +83 -0
- package/telegram-plugin/auth-dashboard.ts +1104 -0
- package/telegram-plugin/auth-slot-parser.ts +497 -0
- package/telegram-plugin/auto-fallback-dispatcher.ts +68 -0
- package/telegram-plugin/auto-fallback.ts +348 -0
- package/telegram-plugin/bridge/bridge.ts +687 -0
- package/telegram-plugin/bridge/ipc-client.ts +326 -0
- package/telegram-plugin/bun.lock +218 -0
- package/telegram-plugin/card-format.ts +62 -0
- package/telegram-plugin/channel-envelope-safety.test.ts +56 -0
- package/telegram-plugin/channel-envelope-safety.ts +56 -0
- package/telegram-plugin/chat-lock.ts +65 -0
- package/telegram-plugin/context-exhaustion.ts +38 -0
- package/telegram-plugin/credits-watch.ts +220 -0
- package/telegram-plugin/dist/bridge/bridge.js +24758 -0
- package/telegram-plugin/dist/foreman/foreman.js +30723 -0
- package/telegram-plugin/dist/gateway/gateway.js +46497 -0
- package/telegram-plugin/dist/server.js +24551 -0
- package/telegram-plugin/dm-command-gate.ts +56 -0
- package/telegram-plugin/docs/gateway-server-split.md +133 -0
- package/telegram-plugin/docs/multi-agent-card-design.md +847 -0
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +144 -0
- package/telegram-plugin/docs/stream-json-daemon-mode.md +477 -0
- package/telegram-plugin/docs/waiting-ux-spec.md +233 -0
- package/telegram-plugin/draft-stream.ts +442 -0
- package/telegram-plugin/draft-transport.ts +72 -0
- package/telegram-plugin/first-paint.ts +246 -0
- package/telegram-plugin/fleet-state.ts +246 -0
- package/telegram-plugin/foreman/foreman-create-flow.ts +202 -0
- package/telegram-plugin/foreman/foreman-handlers.ts +493 -0
- package/telegram-plugin/foreman/foreman.ts +1130 -0
- package/telegram-plugin/foreman/setup-flow.ts +345 -0
- package/telegram-plugin/foreman/setup-state.ts +239 -0
- package/telegram-plugin/foreman/state.ts +203 -0
- package/telegram-plugin/format.ts +685 -0
- package/telegram-plugin/gateway/access-validator.test.ts +95 -0
- package/telegram-plugin/gateway/access-validator.ts +37 -0
- package/telegram-plugin/gateway/boot-card.ts +582 -0
- package/telegram-plugin/gateway/boot-probes.ts +863 -0
- package/telegram-plugin/gateway/boot-reason.ts +51 -0
- package/telegram-plugin/gateway/boot-sweep-filter.test.ts +54 -0
- package/telegram-plugin/gateway/boot-sweep-filter.ts +32 -0
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +183 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +109 -0
- package/telegram-plugin/gateway/gateway.ts +10202 -0
- package/telegram-plugin/gateway/inbound-coalesce.ts +147 -0
- package/telegram-plugin/gateway/inject-handler.test.ts +221 -0
- package/telegram-plugin/gateway/inject-handler.ts +190 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +151 -0
- package/telegram-plugin/gateway/ipc-server.ts +494 -0
- package/telegram-plugin/gateway/pid-file.ts +103 -0
- package/telegram-plugin/gateway/poll-health.ts +156 -0
- package/telegram-plugin/gateway/preamble-suppressor.ts +154 -0
- package/telegram-plugin/gateway/quota-cache.ts +125 -0
- package/telegram-plugin/gateway/resolve-calling-subagent.ts +78 -0
- package/telegram-plugin/gateway/restart-watchdog.ts +200 -0
- package/telegram-plugin/gateway/session-marker.ts +83 -0
- package/telegram-plugin/gateway/shutdown-drain.ts +162 -0
- package/telegram-plugin/gateway/startup-mutex.ts +285 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +142 -0
- package/telegram-plugin/gateway/turn-active-marker.ts +176 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +78 -0
- package/telegram-plugin/handoff-continuity.ts +200 -0
- package/telegram-plugin/history.ts +468 -0
- package/telegram-plugin/hooks/hooks.json +58 -0
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +208 -0
- package/telegram-plugin/hooks/secret-scrub-stop.mjs +98 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +111 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +296 -0
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +261 -0
- package/telegram-plugin/html-sanitize.ts +244 -0
- package/telegram-plugin/idle-footer.ts +65 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +166 -0
- package/telegram-plugin/interrupt-marker.ts +66 -0
- package/telegram-plugin/issues-card.ts +371 -0
- package/telegram-plugin/issues-watcher.ts +125 -0
- package/telegram-plugin/model-unavailable.ts +325 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/operator-events-history.ts +94 -0
- package/telegram-plugin/operator-events.fixtures.json +161 -0
- package/telegram-plugin/operator-events.ts +421 -0
- package/telegram-plugin/package.json +55 -0
- package/telegram-plugin/permission-rule.ts +133 -0
- package/telegram-plugin/permission-title.ts +117 -0
- package/telegram-plugin/pin-event-log.ts +76 -0
- package/telegram-plugin/plugin-logger.ts +136 -0
- package/telegram-plugin/progress-card-driver.ts +2697 -0
- package/telegram-plugin/progress-card-pin-manager.ts +589 -0
- package/telegram-plugin/progress-card-pin-watchdog.ts +98 -0
- package/telegram-plugin/progress-card.ts +1409 -0
- package/telegram-plugin/pty-partial-handler.ts +247 -0
- package/telegram-plugin/pty-tail.ts +730 -0
- package/telegram-plugin/quota-check.ts +474 -0
- package/telegram-plugin/recent-outbound-dedup.ts +169 -0
- package/telegram-plugin/registry/api-registry.test.ts +201 -0
- package/telegram-plugin/registry/subagents-bugs.test.ts +454 -0
- package/telegram-plugin/registry/subagents-schema.ts +509 -0
- package/telegram-plugin/registry/subagents.test.ts +476 -0
- package/telegram-plugin/registry/turns-schema.test.ts +101 -0
- package/telegram-plugin/registry/turns-schema.ts +417 -0
- package/telegram-plugin/retry-api-call.ts +172 -0
- package/telegram-plugin/scripts/build.mjs +78 -0
- package/telegram-plugin/secret-detect/audit.ts +66 -0
- package/telegram-plugin/secret-detect/chunker.ts +37 -0
- package/telegram-plugin/secret-detect/entropy.ts +20 -0
- package/telegram-plugin/secret-detect/gitleaks-loader.ts +74 -0
- package/telegram-plugin/secret-detect/gitleaks.toml +27 -0
- package/telegram-plugin/secret-detect/index.ts +218 -0
- package/telegram-plugin/secret-detect/kv-scanner.ts +60 -0
- package/telegram-plugin/secret-detect/mask.ts +13 -0
- package/telegram-plugin/secret-detect/patterns.ts +115 -0
- package/telegram-plugin/secret-detect/pipeline.ts +144 -0
- package/telegram-plugin/secret-detect/rewrite.ts +26 -0
- package/telegram-plugin/secret-detect/secretlint-source.ts +95 -0
- package/telegram-plugin/secret-detect/slug.ts +44 -0
- package/telegram-plugin/secret-detect/staging.ts +85 -0
- package/telegram-plugin/secret-detect/suppressor.ts +34 -0
- package/telegram-plugin/secret-detect/url-redact.ts +60 -0
- package/telegram-plugin/secret-detect/vault-write.ts +56 -0
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/server.ts +171 -0
- package/telegram-plugin/session-tail.ts +884 -0
- package/telegram-plugin/shared/bot-runtime.ts +324 -0
- package/telegram-plugin/silent-reply.ts +58 -0
- package/telegram-plugin/slot-banner-driver.ts +147 -0
- package/telegram-plugin/slot-banner.ts +86 -0
- package/telegram-plugin/start.js +26 -0
- package/telegram-plugin/startup-reset.ts +45 -0
- package/telegram-plugin/status-reactions.ts +332 -0
- package/telegram-plugin/steering.ts +155 -0
- package/telegram-plugin/sticker-aliases.ts +249 -0
- package/telegram-plugin/stream-controller.ts +311 -0
- package/telegram-plugin/stream-reply-handler.ts +664 -0
- package/telegram-plugin/streaming-metrics.ts +134 -0
- package/telegram-plugin/streaming-report.ts +204 -0
- package/telegram-plugin/subagent-watcher.ts +880 -0
- package/telegram-plugin/telegram-button-constraints.ts +191 -0
- package/telegram-plugin/telegraph.ts +381 -0
- package/telegram-plugin/tests/HARNESS.md +340 -0
- package/telegram-plugin/tests/_progress-card-harness.ts +105 -0
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +211 -0
- package/telegram-plugin/tests/active-pins-sweep.test.ts +309 -0
- package/telegram-plugin/tests/active-pins.test.ts +187 -0
- package/telegram-plugin/tests/active-reactions-sweep.test.ts +116 -0
- package/telegram-plugin/tests/active-reactions.test.ts +198 -0
- package/telegram-plugin/tests/answer-stream-dedup.test.ts +352 -0
- package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +236 -0
- package/telegram-plugin/tests/answer-stream.test.ts +878 -0
- package/telegram-plugin/tests/ask-user.test.ts +203 -0
- package/telegram-plugin/tests/attachment-path.test.ts +199 -0
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +118 -0
- package/telegram-plugin/tests/auth-code-auto-capture.test.ts +144 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +248 -0
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +260 -0
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +140 -0
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +559 -0
- package/telegram-plugin/tests/auth-dashboard.test.ts +1045 -0
- package/telegram-plugin/tests/auth-login-url-button.test.ts +122 -0
- package/telegram-plugin/tests/auth-slot-commands.test.ts +640 -0
- package/telegram-plugin/tests/auto-fallback-dispatcher.e2e.test.ts +183 -0
- package/telegram-plugin/tests/auto-fallback.test.ts +381 -0
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +137 -0
- package/telegram-plugin/tests/boot-card-dedupe.test.ts +154 -0
- package/telegram-plugin/tests/boot-card-probe-target.test.ts +194 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +103 -0
- package/telegram-plugin/tests/boot-card-render.test.ts +219 -0
- package/telegram-plugin/tests/boot-probes.test.ts +451 -0
- package/telegram-plugin/tests/bot-api.harness.ts +116 -0
- package/telegram-plugin/tests/bot-runtime.test.ts +190 -0
- package/telegram-plugin/tests/bridge-anonymous-refuse.test.ts +60 -0
- package/telegram-plugin/tests/context-exhaustion.test.ts +114 -0
- package/telegram-plugin/tests/credits-watch.test.ts +221 -0
- package/telegram-plugin/tests/dm-command-gate.test.ts +176 -0
- package/telegram-plugin/tests/draft-stream.test.ts +752 -0
- package/telegram-plugin/tests/draft-transport.test.ts +141 -0
- package/telegram-plugin/tests/e2e.test.ts +436 -0
- package/telegram-plugin/tests/fake-bot-api.test.ts +213 -0
- package/telegram-plugin/tests/fake-bot-api.ts +617 -0
- package/telegram-plugin/tests/false-restart-banner.test.ts +253 -0
- package/telegram-plugin/tests/first-paint.test.ts +257 -0
- package/telegram-plugin/tests/fixtures/pty-tail-tmux-fragment.bin +6 -0
- package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +3624 -0
- package/telegram-plugin/tests/fleet-state-watcher.test.ts +101 -0
- package/telegram-plugin/tests/fleet-state.test.ts +185 -0
- package/telegram-plugin/tests/foreman-create-flow.test.ts +359 -0
- package/telegram-plugin/tests/foreman-handlers.test.ts +347 -0
- package/telegram-plugin/tests/foreman-state.test.ts +164 -0
- package/telegram-plugin/tests/foreman-write-ops.test.ts +214 -0
- package/telegram-plugin/tests/gateway-409-retry-banner.test.ts +173 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +72 -0
- package/telegram-plugin/tests/gateway-bridge.test.ts +811 -0
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +414 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +144 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +133 -0
- package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +103 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +127 -0
- package/telegram-plugin/tests/gateway-startup-mutex.test.ts +284 -0
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +185 -0
- package/telegram-plugin/tests/gateway-startup-reset.test.ts +72 -0
- package/telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts +125 -0
- package/telegram-plugin/tests/handoff-continuity.test.ts +249 -0
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +243 -0
- package/telegram-plugin/tests/harness-parse-mode-validation.test.ts +114 -0
- package/telegram-plugin/tests/history.test.ts +364 -0
- package/telegram-plugin/tests/html-balanced.ts +63 -0
- package/telegram-plugin/tests/html-sanitize.test.ts +146 -0
- package/telegram-plugin/tests/idle-footer-wiring.test.ts +88 -0
- package/telegram-plugin/tests/idle-footer.test.ts +66 -0
- package/telegram-plugin/tests/inbound-coalesce.test.ts +127 -0
- package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +150 -0
- package/telegram-plugin/tests/interrupt-marker.test.ts +126 -0
- package/telegram-plugin/tests/ipc-protocol.test.ts +218 -0
- package/telegram-plugin/tests/ipc-server-anonymous-refuse.test.ts +82 -0
- package/telegram-plugin/tests/ipc-server-client.test.ts +323 -0
- package/telegram-plugin/tests/ipc-server-race.test.ts +183 -0
- package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +91 -0
- package/telegram-plugin/tests/ipc-server-validate-pty-partial.test.ts +64 -0
- package/telegram-plugin/tests/ipc-server-validate-update-placeholder.test.ts +77 -0
- package/telegram-plugin/tests/ipc-validator.test.ts +274 -0
- package/telegram-plugin/tests/issues-card.test.ts +495 -0
- package/telegram-plugin/tests/issues-watcher.test.ts +165 -0
- package/telegram-plugin/tests/model-unavailable.test.ts +303 -0
- package/telegram-plugin/tests/multi-turn-continuity.test.ts +159 -0
- package/telegram-plugin/tests/operator-events-history.test.ts +125 -0
- package/telegram-plugin/tests/operator-events-session-tail.test.ts +192 -0
- package/telegram-plugin/tests/operator-events.test.ts +331 -0
- package/telegram-plugin/tests/outbound-ordering.test.ts +293 -0
- package/telegram-plugin/tests/parse-mode-rotation.test.ts +164 -0
- package/telegram-plugin/tests/permission-rule.test.ts +121 -0
- package/telegram-plugin/tests/permission-title.test.ts +106 -0
- package/telegram-plugin/tests/pin-event-log.test.ts +124 -0
- package/telegram-plugin/tests/plugin-logger.test.ts +97 -0
- package/telegram-plugin/tests/poll-health.test.ts +86 -0
- package/telegram-plugin/tests/preamble-suppressor.test.ts +285 -0
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +73 -0
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +272 -0
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +258 -0
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +81 -0
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +80 -0
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +215 -0
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +123 -0
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +76 -0
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +62 -0
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +84 -0
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +139 -0
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +773 -0
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +66 -0
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +64 -0
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +190 -0
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +146 -0
- package/telegram-plugin/tests/progress-update.test.ts +236 -0
- package/telegram-plugin/tests/protocol-fixtures.test.ts +59 -0
- package/telegram-plugin/tests/protocol-fixtures.ts +198 -0
- package/telegram-plugin/tests/pty-partial-handler.test.ts +326 -0
- package/telegram-plugin/tests/pty-tail-real-fixture.test.ts +114 -0
- package/telegram-plugin/tests/pty-tail-tmux-fragment.test.ts +71 -0
- package/telegram-plugin/tests/pty-tail.test.ts +525 -0
- package/telegram-plugin/tests/quota-cache.test.ts +187 -0
- package/telegram-plugin/tests/quota-check.test.ts +622 -0
- package/telegram-plugin/tests/races.test.ts +842 -0
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +123 -0
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +82 -0
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +114 -0
- package/telegram-plugin/tests/real-gateway-harness.ts +699 -0
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +313 -0
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +299 -0
- package/telegram-plugin/tests/real-gateway-spec.test.ts +487 -0
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +101 -0
- package/telegram-plugin/tests/recent-outbound-dedup.test.ts +192 -0
- package/telegram-plugin/tests/registry-turns.test.ts +432 -0
- package/telegram-plugin/tests/reply-terminal-reaction.test.ts +149 -0
- package/telegram-plugin/tests/resolve-calling-subagent.test.ts +269 -0
- package/telegram-plugin/tests/restart-watchdog.test.ts +224 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +287 -0
- package/telegram-plugin/tests/secret-detect-audit.test.ts +58 -0
- package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +83 -0
- package/telegram-plugin/tests/secret-detect-gitleaks.test.ts +32 -0
- package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +308 -0
- package/telegram-plugin/tests/secret-detect-pipeline.test.ts +123 -0
- package/telegram-plugin/tests/secret-detect-secretlint.test.ts +101 -0
- package/telegram-plugin/tests/secret-detect-staging.test.ts +45 -0
- package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +67 -0
- package/telegram-plugin/tests/secret-detect.test.ts +223 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +194 -0
- package/telegram-plugin/tests/send-typing-action-validation.test.ts +61 -0
- package/telegram-plugin/tests/session-tail-capped.test.ts +285 -0
- package/telegram-plugin/tests/session-tail.test.ts +524 -0
- package/telegram-plugin/tests/setup-flow.test.ts +510 -0
- package/telegram-plugin/tests/setup-state.test.ts +146 -0
- package/telegram-plugin/tests/silent-reply-guard.test.ts +122 -0
- package/telegram-plugin/tests/slot-banner-driver.e2e.test.ts +350 -0
- package/telegram-plugin/tests/slot-banner.test.ts +74 -0
- package/telegram-plugin/tests/snapshot-serializer.ts +79 -0
- package/telegram-plugin/tests/spawn-detached-cgroup-escape.test.ts +51 -0
- package/telegram-plugin/tests/status-accent.test.ts +186 -0
- package/telegram-plugin/tests/status-reactions-allowed-filter.test.ts +132 -0
- package/telegram-plugin/tests/status-reactions.test.ts +314 -0
- package/telegram-plugin/tests/steering.test.ts +282 -0
- package/telegram-plugin/tests/sticker-aliases.test.ts +232 -0
- package/telegram-plugin/tests/stream-controller-html-fallback.test.ts +127 -0
- package/telegram-plugin/tests/stream-controller.test.ts +262 -0
- package/telegram-plugin/tests/stream-reply-error-paths.test.ts +208 -0
- package/telegram-plugin/tests/stream-reply-handler.test.ts +1292 -0
- package/telegram-plugin/tests/streaming-e2e.test.ts +389 -0
- package/telegram-plugin/tests/streaming-metrics.test.ts +201 -0
- package/telegram-plugin/tests/streaming-orchestration.test.ts +756 -0
- package/telegram-plugin/tests/subagent-registry-bugs.test.ts +725 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +213 -0
- package/telegram-plugin/tests/subagent-watcher-parent-marker.test.ts +274 -0
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +243 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +877 -0
- package/telegram-plugin/tests/subagents-schema-init-order.test.ts +109 -0
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +116 -0
- package/telegram-plugin/tests/telegram-button-constraints.test.ts +194 -0
- package/telegram-plugin/tests/telegram-format.test.ts +1093 -0
- package/telegram-plugin/tests/telegraph.test.ts +246 -0
- package/telegram-plugin/tests/tool-labels.test.ts +383 -0
- package/telegram-plugin/tests/turn-active-marker.test.ts +195 -0
- package/telegram-plugin/tests/turn-end-regressions.test.ts +489 -0
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +218 -0
- package/telegram-plugin/tests/turn-flush-dedup-controller.test.ts +144 -0
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +78 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +189 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +107 -0
- package/telegram-plugin/tests/turns-writer.test.ts +323 -0
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +131 -0
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +120 -0
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +87 -0
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +211 -0
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +62 -0
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +68 -0
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +110 -0
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +128 -0
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +58 -0
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +133 -0
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +155 -0
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +117 -0
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +143 -0
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +149 -0
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +105 -0
- package/telegram-plugin/tests/typing-wrap.test.ts +141 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +147 -0
- package/telegram-plugin/tests/update-factory-edited-and-reactions.test.ts +108 -0
- package/telegram-plugin/tests/update-factory.ts +305 -0
- package/telegram-plugin/tests/vault-grant-wizard.test.ts +84 -0
- package/telegram-plugin/tests/vault-grants-revoke.test.ts +265 -0
- package/telegram-plugin/tests/vault-subcommands.test.ts +234 -0
- package/telegram-plugin/tests/voice-transcribe.test.ts +196 -0
- package/telegram-plugin/tests/waiting-ux-harness.ts +381 -0
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +233 -0
- package/telegram-plugin/tests/welcome-text.test.ts +407 -0
- package/telegram-plugin/tool-error-filter.ts +89 -0
- package/telegram-plugin/tool-labels.ts +330 -0
- package/telegram-plugin/tool-names.ts +53 -0
- package/telegram-plugin/turn-flush-prose-recovery.ts +40 -0
- package/telegram-plugin/turn-flush-safety.ts +109 -0
- package/telegram-plugin/turn-signal-tracker.ts +79 -0
- package/telegram-plugin/two-zone-card.ts +249 -0
- package/telegram-plugin/typing-wrap.ts +92 -0
- package/telegram-plugin/voice-transcribe.ts +166 -0
- package/telegram-plugin/welcome-text.ts +359 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway PID file — records the live gateway's PID so the in-agent
|
|
3
|
+
* plugin (server.ts dual-mode probe) can distinguish "gateway is
|
|
4
|
+
* genuinely gone, fall back to legacy monolith" from "gateway is alive
|
|
5
|
+
* but the socket blinked (EAGAIN, accept-backlog, handshake race)".
|
|
6
|
+
*
|
|
7
|
+
* 2026-04-22 incident: a transient Bun.connect failure against a LIVE
|
|
8
|
+
* gateway tripped the legacy-fallback path in server.ts, spawning a
|
|
9
|
+
* second grammY long-poll client. That client 409'd against the
|
|
10
|
+
* gateway's own poller, which fired a grammY poll-restart, which fired
|
|
11
|
+
* the "⚡ Recovered from unexpected restart" banner — without any
|
|
12
|
+
* actual process restart (same PID across 8 minutes, systemd logged
|
|
13
|
+
* zero lifecycle events).
|
|
14
|
+
*
|
|
15
|
+
* Rule: fall back to legacy ONLY if no PID file exists OR the recorded
|
|
16
|
+
* PID is dead. If the PID is alive, retry the socket with backoff.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { writeFileSync, readFileSync, unlinkSync, renameSync } from "node:fs";
|
|
20
|
+
|
|
21
|
+
/** Shape of the PID file on disk. */
|
|
22
|
+
export interface GatewayPidRecord {
|
|
23
|
+
pid: number;
|
|
24
|
+
/** epoch ms when this process started (Date.now() at write time) */
|
|
25
|
+
startedAtMs: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Atomic write via tmp+rename so a crashed writer can't leave a
|
|
30
|
+
* half-written file that subsequent readers mistake for a live PID.
|
|
31
|
+
*/
|
|
32
|
+
export function writePidFile(path: string, record: GatewayPidRecord): void {
|
|
33
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
34
|
+
writeFileSync(tmp, JSON.stringify(record), "utf-8");
|
|
35
|
+
renameSync(tmp, path);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function readPidFile(path: string): GatewayPidRecord | null {
|
|
39
|
+
try {
|
|
40
|
+
const raw = readFileSync(path, "utf-8");
|
|
41
|
+
const parsed = JSON.parse(raw) as Partial<GatewayPidRecord>;
|
|
42
|
+
if (
|
|
43
|
+
typeof parsed.pid === "number" &&
|
|
44
|
+
typeof parsed.startedAtMs === "number" &&
|
|
45
|
+
Number.isFinite(parsed.pid) &&
|
|
46
|
+
Number.isFinite(parsed.startedAtMs)
|
|
47
|
+
) {
|
|
48
|
+
return { pid: parsed.pid, startedAtMs: parsed.startedAtMs };
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function clearPidFile(path: string): void {
|
|
57
|
+
try {
|
|
58
|
+
unlinkSync(path);
|
|
59
|
+
} catch {
|
|
60
|
+
/* best effort */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns true if the PID refers to a live process. Uses kill(pid, 0)
|
|
66
|
+
* which sends no signal but throws ESRCH if the process is gone.
|
|
67
|
+
* EPERM means the process exists but is owned by another user — we
|
|
68
|
+
* conservatively treat that as alive (don't fall back to legacy).
|
|
69
|
+
*/
|
|
70
|
+
export function isPidAlive(
|
|
71
|
+
pid: number,
|
|
72
|
+
killFn: (p: number, sig: number) => void = (p, s) => process.kill(p, s),
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
75
|
+
if (pid === process.pid) return true;
|
|
76
|
+
try {
|
|
77
|
+
killFn(pid, 0);
|
|
78
|
+
return true;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
81
|
+
if (code === "EPERM") return true;
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Pure decision function for the dual-mode probe. Extracted so the
|
|
88
|
+
* fallback policy is testable in isolation.
|
|
89
|
+
*
|
|
90
|
+
* Fall back to legacy ONLY when we have strong evidence the gateway is
|
|
91
|
+
* truly gone — no PID file OR a dead PID. A transient socket miss
|
|
92
|
+
* against a live PID means "retry the socket", not "spawn a second
|
|
93
|
+
* poller".
|
|
94
|
+
*/
|
|
95
|
+
export function shouldFallBackToLegacy(input: {
|
|
96
|
+
socketReachable: boolean;
|
|
97
|
+
pidFileExists: boolean;
|
|
98
|
+
pidAlive: boolean;
|
|
99
|
+
}): boolean {
|
|
100
|
+
if (input.socketReachable) return false;
|
|
101
|
+
if (!input.pidFileExists) return true;
|
|
102
|
+
return !input.pidAlive;
|
|
103
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Long-poll health-check for the Telegram gateway.
|
|
3
|
+
*
|
|
4
|
+
* Motivation (issue #56):
|
|
5
|
+
* grammY's runner holds a permanent HTTPS connection to Telegram's
|
|
6
|
+
* getUpdates endpoint. On some network paths this connection can
|
|
7
|
+
* silently freeze — the TCP socket stays open but no bytes flow.
|
|
8
|
+
* The runner's `isRunning()` stays true and the gateway appears
|
|
9
|
+
* alive, but Telegram messages never arrive.
|
|
10
|
+
*
|
|
11
|
+
* Fix:
|
|
12
|
+
* A separate setInterval calls `getMe()` (a lightweight Bot API
|
|
13
|
+
* endpoint) every HEALTH_INTERVAL_MS. Three consecutive failures
|
|
14
|
+
* constitute a stall: we stop the runner, wait RESTART_GRACE_MS
|
|
15
|
+
* for the in-flight request to die, then let the caller restart it.
|
|
16
|
+
*
|
|
17
|
+
* A single failure doesn't count — transient network blips happen.
|
|
18
|
+
* The threshold must be >= 3 so a brief Telegram outage (e.g. a
|
|
19
|
+
* data-centre hiccup) doesn't cause thrashing.
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* const hc = createPollHealthCheck({
|
|
23
|
+
* ping: () => bot.api.getMe(),
|
|
24
|
+
* onStall: async () => { await runnerHandle.stop(); … restart … },
|
|
25
|
+
* log: (msg) => process.stderr.write(msg),
|
|
26
|
+
* });
|
|
27
|
+
* // start after the runner is up:
|
|
28
|
+
* hc.start();
|
|
29
|
+
* // on clean shutdown:
|
|
30
|
+
* hc.stop();
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export interface PollHealthCheckOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Lightweight Bot API probe. Implementations should call `bot.api.getMe()`
|
|
36
|
+
* or similar. The check is marked as a failure if this rejects.
|
|
37
|
+
*/
|
|
38
|
+
ping: () => Promise<unknown>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Called when `failureThreshold` consecutive pings have failed.
|
|
42
|
+
* The health check stops itself before calling this, so `onStall`
|
|
43
|
+
* is called at most once per `start()`. The implementation should
|
|
44
|
+
* restart the runner and call `healthCheck.start()` again when ready.
|
|
45
|
+
*/
|
|
46
|
+
onStall: () => Promise<void>;
|
|
47
|
+
|
|
48
|
+
/** Interval between health pings. Defaults to 5 minutes. */
|
|
49
|
+
intervalMs?: number;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Number of consecutive failures before `onStall` fires.
|
|
53
|
+
* Defaults to 3.
|
|
54
|
+
*/
|
|
55
|
+
failureThreshold?: number;
|
|
56
|
+
|
|
57
|
+
/** Logger. Defaults to process.stderr. */
|
|
58
|
+
log?: (msg: string) => void;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Injectable timer (for tests). Defaults to setInterval / clearInterval.
|
|
62
|
+
*/
|
|
63
|
+
setIntervalFn?: (fn: () => void, ms: number) => ReturnType<typeof setInterval>;
|
|
64
|
+
clearIntervalFn?: (id: ReturnType<typeof setInterval>) => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface PollHealthCheckHandle {
|
|
68
|
+
/** Start the periodic health-check interval. Idempotent. */
|
|
69
|
+
start(): void;
|
|
70
|
+
/** Stop the interval without triggering onStall. Idempotent. */
|
|
71
|
+
stop(): void;
|
|
72
|
+
/** Current count of consecutive failures (exposed for testing). */
|
|
73
|
+
consecutiveFailures(): number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const DEFAULT_LOG = (msg: string): void => {
|
|
77
|
+
process.stderr.write(msg.endsWith("\n") ? msg : msg + "\n");
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export function createPollHealthCheck(
|
|
81
|
+
options: PollHealthCheckOptions,
|
|
82
|
+
): PollHealthCheckHandle {
|
|
83
|
+
const {
|
|
84
|
+
ping,
|
|
85
|
+
onStall,
|
|
86
|
+
intervalMs = 5 * 60_000,
|
|
87
|
+
failureThreshold = 3,
|
|
88
|
+
log = DEFAULT_LOG,
|
|
89
|
+
setIntervalFn = setInterval,
|
|
90
|
+
clearIntervalFn = clearInterval,
|
|
91
|
+
} = options;
|
|
92
|
+
|
|
93
|
+
let timer: ReturnType<typeof setInterval> | null = null;
|
|
94
|
+
let failures = 0;
|
|
95
|
+
let active = false;
|
|
96
|
+
|
|
97
|
+
async function tick(): Promise<void> {
|
|
98
|
+
try {
|
|
99
|
+
await ping();
|
|
100
|
+
if (failures > 0) {
|
|
101
|
+
log(`telegram gateway: poll.health_check recovered after ${failures} failure(s)`);
|
|
102
|
+
}
|
|
103
|
+
failures = 0;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
failures++;
|
|
106
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
log(
|
|
108
|
+
`telegram gateway: poll.health_check ping failed (${failures}/${failureThreshold}): ${msg}`,
|
|
109
|
+
);
|
|
110
|
+
if (failures >= failureThreshold) {
|
|
111
|
+
log(
|
|
112
|
+
`telegram gateway: poll.health_check stall detected after ${failures} consecutive failures — triggering recovery`,
|
|
113
|
+
);
|
|
114
|
+
// Stop before calling onStall so we don't fire again during recovery.
|
|
115
|
+
doStop();
|
|
116
|
+
try {
|
|
117
|
+
await onStall();
|
|
118
|
+
} catch (stallErr) {
|
|
119
|
+
log(
|
|
120
|
+
`telegram gateway: poll.health_check onStall error: ${stallErr instanceof Error ? stallErr.message : String(stallErr)}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function doStop(): void {
|
|
128
|
+
active = false;
|
|
129
|
+
if (timer !== null) {
|
|
130
|
+
clearIntervalFn(timer);
|
|
131
|
+
timer = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
start(): void {
|
|
137
|
+
if (active) return; // idempotent
|
|
138
|
+
active = true;
|
|
139
|
+
failures = 0;
|
|
140
|
+
timer = setIntervalFn(() => { void tick(); }, intervalMs);
|
|
141
|
+
// unref so the interval doesn't prevent process exit on clean shutdown
|
|
142
|
+
if (typeof (timer as any)?.unref === "function") {
|
|
143
|
+
(timer as any).unref();
|
|
144
|
+
}
|
|
145
|
+
log(`telegram gateway: poll.health_check started interval=${intervalMs}ms threshold=${failureThreshold}`);
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
stop(): void {
|
|
149
|
+
doStop();
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
consecutiveFailures(): number {
|
|
153
|
+
return failures;
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preamble-text suppression for the answer-stream path (#549).
|
|
3
|
+
*
|
|
4
|
+
* When the agent emits assistant text BEFORE a tool call within the
|
|
5
|
+
* same turn, that text is "preamble" — think-out-loud guidance about
|
|
6
|
+
* what's about to happen. The progress-card driver consumes preamble
|
|
7
|
+
* text as a narrative row for the upcoming tool. Independently, the
|
|
8
|
+
* answer-stream path was also sending the same text to chat as a
|
|
9
|
+
* standalone message — so the user saw the same line twice (#549).
|
|
10
|
+
*
|
|
11
|
+
* This module isolates the buffering policy:
|
|
12
|
+
*
|
|
13
|
+
* - `onText(chunk)` — append to the pending buffer; (re)start the
|
|
14
|
+
* flush timer. If no `onTool` arrives within `bufferMs`, the timer
|
|
15
|
+
* fires and treats the buffered text as ANSWER text (the agent
|
|
16
|
+
* emitted it as the reply, not as preamble).
|
|
17
|
+
*
|
|
18
|
+
* - `onTool({ isReplyTool })` — a non-reply tool consumes the
|
|
19
|
+
* pending buffer as preamble (drop). A reply/stream_reply tool is
|
|
20
|
+
* itself the answer surface; flush so its text isn't suppressed
|
|
21
|
+
* when delivered via separate paths.
|
|
22
|
+
*
|
|
23
|
+
* - `flushNow()` — force-flush whatever's pending (used at turn_end
|
|
24
|
+
* when no preamble-claiming tool ever arrived).
|
|
25
|
+
*
|
|
26
|
+
* - `dropNow()` — drop without flushing (used when the answer
|
|
27
|
+
* stream is being torn down for an unrelated reason).
|
|
28
|
+
*
|
|
29
|
+
* - `reset()` — clear ALL state including `answerTextOnly` (called
|
|
30
|
+
* at fresh-turn enqueue).
|
|
31
|
+
*
|
|
32
|
+
* The class owns its own state so the gateway's module-level mutable
|
|
33
|
+
* state stays bounded. Pure aside from the supplied `flushFn` and
|
|
34
|
+
* `setTimer` / `clearTimer` (injected for tests so vi.useFakeTimers
|
|
35
|
+
* can drive them).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export interface PreambleSuppressorDeps {
|
|
39
|
+
/**
|
|
40
|
+
* Called when a chunk has been promoted from "pending" to "answer
|
|
41
|
+
* text." Receives the cumulative answer text since the last reset()
|
|
42
|
+
* — caller should treat it as the full answer-stream payload.
|
|
43
|
+
*/
|
|
44
|
+
emitAnswer: (cumulativeAnswerText: string) => void
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Buffer window in ms. Default 150ms — long enough that a
|
|
48
|
+
* tool_use following a text emit comfortably arrives within the
|
|
49
|
+
* window, short enough that a true answer-text reply still feels
|
|
50
|
+
* snappy. Override in tests with `vi.useFakeTimers()`.
|
|
51
|
+
*/
|
|
52
|
+
bufferMs?: number
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Injected for testability. Defaults to global setTimeout/clearTimeout.
|
|
56
|
+
*/
|
|
57
|
+
setTimer?: (fn: () => void, ms: number) => unknown
|
|
58
|
+
clearTimer?: (handle: unknown) => void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const DEFAULT_BUFFER_MS = 150
|
|
62
|
+
|
|
63
|
+
export class PreambleSuppressor {
|
|
64
|
+
private readonly emitAnswer: (text: string) => void
|
|
65
|
+
private readonly bufferMs: number
|
|
66
|
+
private readonly setTimer: (fn: () => void, ms: number) => unknown
|
|
67
|
+
private readonly clearTimer: (handle: unknown) => void
|
|
68
|
+
private answerTextOnly = ''
|
|
69
|
+
private pendingBuffer = ''
|
|
70
|
+
private pendingTimer: unknown = null
|
|
71
|
+
|
|
72
|
+
constructor(deps: PreambleSuppressorDeps) {
|
|
73
|
+
this.emitAnswer = deps.emitAnswer
|
|
74
|
+
this.bufferMs = deps.bufferMs ?? DEFAULT_BUFFER_MS
|
|
75
|
+
this.setTimer = deps.setTimer ?? ((fn, ms) => setTimeout(fn, ms))
|
|
76
|
+
this.clearTimer = deps.clearTimer ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Append a text chunk and (re)arm the flush timer. */
|
|
80
|
+
onText(chunk: string): void {
|
|
81
|
+
if (chunk.length === 0) return
|
|
82
|
+
this.pendingBuffer += chunk
|
|
83
|
+
if (this.pendingTimer != null) this.clearTimer(this.pendingTimer)
|
|
84
|
+
this.pendingTimer = this.setTimer(() => this.flushNow(), this.bufferMs)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Tool-use signal. Non-reply tools consume the buffer as preamble
|
|
89
|
+
* (drop). Reply/stream_reply tools flush the buffer as answer text
|
|
90
|
+
* (their own payload IS the answer surface; flushing here keeps the
|
|
91
|
+
* chat-side state consistent).
|
|
92
|
+
*/
|
|
93
|
+
onTool(opts: { isReplyTool: boolean }): void {
|
|
94
|
+
if (opts.isReplyTool) {
|
|
95
|
+
this.flushNow()
|
|
96
|
+
} else {
|
|
97
|
+
this.dropNow()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Force-flush any pending text as answer text. Idempotent. */
|
|
102
|
+
flushNow(): void {
|
|
103
|
+
if (this.pendingTimer != null) {
|
|
104
|
+
this.clearTimer(this.pendingTimer)
|
|
105
|
+
this.pendingTimer = null
|
|
106
|
+
}
|
|
107
|
+
if (this.pendingBuffer.length === 0) return
|
|
108
|
+
this.answerTextOnly += this.pendingBuffer
|
|
109
|
+
this.pendingBuffer = ''
|
|
110
|
+
this.emitAnswer(this.answerTextOnly)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Drop pending text without flushing. Idempotent. Also clears
|
|
115
|
+
* `answerTextOnly` to prevent cross-turn contamination — the
|
|
116
|
+
* silent-marker / turn-flush teardown sites use dropNow because
|
|
117
|
+
* the text is being handed off to other paths (currentTurnCapturedText
|
|
118
|
+
* → turn-flush; silent suppression). Leaving stale answer text in
|
|
119
|
+
* the suppressor would prepend it to the next turn's first flush.
|
|
120
|
+
*/
|
|
121
|
+
dropNow(): void {
|
|
122
|
+
if (this.pendingTimer != null) {
|
|
123
|
+
this.clearTimer(this.pendingTimer)
|
|
124
|
+
this.pendingTimer = null
|
|
125
|
+
}
|
|
126
|
+
this.pendingBuffer = ''
|
|
127
|
+
this.answerTextOnly = ''
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Full reset — clears the cumulative answer-text-only memory plus
|
|
132
|
+
* the pending buffer. Call on fresh-turn enqueue.
|
|
133
|
+
*/
|
|
134
|
+
reset(): void {
|
|
135
|
+
if (this.pendingTimer != null) {
|
|
136
|
+
this.clearTimer(this.pendingTimer)
|
|
137
|
+
this.pendingTimer = null
|
|
138
|
+
}
|
|
139
|
+
this.pendingBuffer = ''
|
|
140
|
+
this.answerTextOnly = ''
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Test introspection ───────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/** True if the buffer holds text waiting for a flush decision. */
|
|
146
|
+
hasPending(): boolean {
|
|
147
|
+
return this.pendingBuffer.length > 0
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Current cumulative answer-text-only payload. */
|
|
151
|
+
currentAnswerText(): string {
|
|
152
|
+
return this.answerTextOnly
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared quota probe cache.
|
|
3
|
+
*
|
|
4
|
+
* Background: every gateway boot AND every bridge-reconnect calls
|
|
5
|
+
* `probeQuota` which hits Anthropic's /api/oauth/usage endpoint. With
|
|
6
|
+
* four agents on one OAuth account and reconnects happening multiple
|
|
7
|
+
* times per minute (per the boot-card-on-every-start design), the
|
|
8
|
+
* endpoint returns 429 and the boot card shows 🟡 "rate limited" — a
|
|
9
|
+
* cosmetic alarm caused by the boot card itself.
|
|
10
|
+
*
|
|
11
|
+
* Cache the result for 5 min in a single file shared across all agents.
|
|
12
|
+
* On a hit, the cached ProbeResult is returned instead of making the
|
|
13
|
+
* HTTP call. 429 (rate-limited) results are cached for a shorter 30 s
|
|
14
|
+
* window so fleet-restart bursts are absorbed without holding stale state
|
|
15
|
+
* for 5 min. Fail results are never cached.
|
|
16
|
+
*
|
|
17
|
+
* Cache location: `~/.switchroom/quota-cache.json` (mode 0600). Format:
|
|
18
|
+
* { capturedAt: string, ttlMs: number, result: ProbeResult }
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
|
|
22
|
+
import { join, dirname } from 'path'
|
|
23
|
+
import type { ProbeResult } from './boot-probes.js'
|
|
24
|
+
|
|
25
|
+
export interface QuotaCacheEntry {
|
|
26
|
+
capturedAt: string // ISO 8601
|
|
27
|
+
ttlMs: number
|
|
28
|
+
result: ProbeResult
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const DEFAULT_TTL_MS = 5 * 60 * 1000 // 5 min
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Short TTL used when caching a 429 result. 30 s is long enough to absorb
|
|
35
|
+
* a simultaneous fleet restart (all N agents firing their quota probe within
|
|
36
|
+
* the same second) while clearing fast enough that any real retry or the
|
|
37
|
+
* next scheduled boot gets a live result.
|
|
38
|
+
*/
|
|
39
|
+
export const RATE_LIMIT_TTL_MS = 30 * 1000 // 30 s
|
|
40
|
+
export const DEFAULT_CACHE_PATH =
|
|
41
|
+
process.env.SWITCHROOM_QUOTA_CACHE_PATH
|
|
42
|
+
?? join(process.env.HOME ?? '/tmp', '.switchroom', 'quota-cache.json')
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read a cached probe result if one exists and is still within TTL.
|
|
46
|
+
* Returns null on:
|
|
47
|
+
* - file missing
|
|
48
|
+
* - JSON parse error
|
|
49
|
+
* - cache entry past its TTL
|
|
50
|
+
*
|
|
51
|
+
* Pure helper — accepts a clock so tests can advance time.
|
|
52
|
+
*/
|
|
53
|
+
export function readQuotaCache(opts: {
|
|
54
|
+
path?: string
|
|
55
|
+
now?: number
|
|
56
|
+
} = {}): ProbeResult | null {
|
|
57
|
+
const path = opts.path ?? DEFAULT_CACHE_PATH
|
|
58
|
+
const now = opts.now ?? Date.now()
|
|
59
|
+
|
|
60
|
+
if (!existsSync(path)) return null
|
|
61
|
+
|
|
62
|
+
let entry: QuotaCacheEntry
|
|
63
|
+
try {
|
|
64
|
+
entry = JSON.parse(readFileSync(path, 'utf8')) as QuotaCacheEntry
|
|
65
|
+
} catch {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const capturedAtMs = Date.parse(entry.capturedAt)
|
|
70
|
+
if (!isFinite(capturedAtMs)) return null
|
|
71
|
+
|
|
72
|
+
const ageMs = now - capturedAtMs
|
|
73
|
+
if (ageMs < 0) return null // clock went backwards; treat as miss
|
|
74
|
+
if (ageMs >= entry.ttlMs) return null // expired
|
|
75
|
+
|
|
76
|
+
return entry.result
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Write a probe result to the cache.
|
|
81
|
+
*
|
|
82
|
+
* Normal results (ok / degraded non-rate-limit) use the standard 5-min TTL.
|
|
83
|
+
* A result with `rateLimited: true` (HTTP 429) uses the short
|
|
84
|
+
* RATE_LIMIT_TTL_MS (30 s) so back-to-back fleet restarts read the cached
|
|
85
|
+
* 'ok' row instead of piling up on the endpoint, while still clearing fast
|
|
86
|
+
* enough for any real next-boot probe to see a live result.
|
|
87
|
+
*
|
|
88
|
+
* Fail results are still not cached — they indicate a real error and the
|
|
89
|
+
* next boot should always retry.
|
|
90
|
+
*
|
|
91
|
+
* Writes are best-effort: any IO error is swallowed (cache is an
|
|
92
|
+
* optimization, not a correctness requirement).
|
|
93
|
+
*/
|
|
94
|
+
export function writeQuotaCache(
|
|
95
|
+
result: ProbeResult,
|
|
96
|
+
opts: {
|
|
97
|
+
path?: string
|
|
98
|
+
ttlMs?: number
|
|
99
|
+
now?: number
|
|
100
|
+
} = {},
|
|
101
|
+
): void {
|
|
102
|
+
// Don't cache hard failures — let the next boot retry clean.
|
|
103
|
+
if (result.status === 'fail') return
|
|
104
|
+
|
|
105
|
+
const path = opts.path ?? DEFAULT_CACHE_PATH
|
|
106
|
+
// Rate-limit results use a shorter TTL: long enough to absorb a fleet
|
|
107
|
+
// restart burst, short enough that subsequent boots get a live probe.
|
|
108
|
+
// Use the structured `rateLimited` field rather than string-matching on
|
|
109
|
+
// `detail` — the detail string is user-facing and may change.
|
|
110
|
+
const ttlMs = opts.ttlMs ?? (result.rateLimited ? RATE_LIMIT_TTL_MS : DEFAULT_TTL_MS)
|
|
111
|
+
const now = opts.now ?? Date.now()
|
|
112
|
+
|
|
113
|
+
const entry: QuotaCacheEntry = {
|
|
114
|
+
capturedAt: new Date(now).toISOString(),
|
|
115
|
+
ttlMs,
|
|
116
|
+
result,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
mkdirSync(dirname(path), { recursive: true })
|
|
121
|
+
writeFileSync(path, JSON.stringify(entry, null, 2), { mode: 0o600 })
|
|
122
|
+
} catch {
|
|
123
|
+
// Best-effort. Swallow.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue #305 Option A — resolve which sub-agent (by jsonl_agent_id) is
|
|
3
|
+
* calling progress_update.
|
|
4
|
+
*
|
|
5
|
+
* Three resolution strategies, in priority order:
|
|
6
|
+
* 1. agentIdHint — exact match on subagents.jsonl_agent_id
|
|
7
|
+
* 2. toolUseIdHint — exact match on subagents.id (parent's Agent tool_use_id)
|
|
8
|
+
* 3. Heuristic: most-recently-started running sub-agent in the active turn
|
|
9
|
+
* for this chat. Logs a stderr warning when multiple candidates exist.
|
|
10
|
+
*
|
|
11
|
+
* Returns null if no match (caller falls through to message-send).
|
|
12
|
+
* Never throws; SQL errors return null.
|
|
13
|
+
*
|
|
14
|
+
* Extracted from gateway.ts so the resolver can be unit-tested against an
|
|
15
|
+
* in-memory SQLite DB without spinning up the full grammY bot harness.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Minimal duck-typed interface that matches both bun:sqlite's `Database`
|
|
20
|
+
* and the `SqliteDatabase` shape returned by `openTurnsDb`. We accept the
|
|
21
|
+
* narrowed shape so the resolver can run against any equivalent handle.
|
|
22
|
+
*/
|
|
23
|
+
export interface ResolverDb {
|
|
24
|
+
prepare(sql: string): {
|
|
25
|
+
get(...params: unknown[]): unknown
|
|
26
|
+
all(...params: unknown[]): unknown[]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ResolveCallingSubagentOpts {
|
|
31
|
+
db: ResolverDb | null
|
|
32
|
+
chatId: string
|
|
33
|
+
threadId?: number | string
|
|
34
|
+
agentIdHint: string | null
|
|
35
|
+
toolUseIdHint: string | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ResolveCallingSubagentResult = { agentId: string } | null
|
|
39
|
+
|
|
40
|
+
export function resolveCallingSubagent(
|
|
41
|
+
opts: ResolveCallingSubagentOpts,
|
|
42
|
+
): ResolveCallingSubagentResult {
|
|
43
|
+
if (opts.db == null) return null
|
|
44
|
+
try {
|
|
45
|
+
if (opts.agentIdHint != null) {
|
|
46
|
+
const row = opts.db.prepare(
|
|
47
|
+
"SELECT jsonl_agent_id FROM subagents WHERE jsonl_agent_id = ? AND status = 'running'",
|
|
48
|
+
).get(opts.agentIdHint) as { jsonl_agent_id: string } | undefined
|
|
49
|
+
if (row?.jsonl_agent_id) return { agentId: row.jsonl_agent_id }
|
|
50
|
+
}
|
|
51
|
+
if (opts.toolUseIdHint != null) {
|
|
52
|
+
const row = opts.db.prepare(
|
|
53
|
+
"SELECT jsonl_agent_id FROM subagents WHERE id = ? AND status = 'running'",
|
|
54
|
+
).get(opts.toolUseIdHint) as { jsonl_agent_id: string | null } | undefined
|
|
55
|
+
if (row?.jsonl_agent_id) return { agentId: row.jsonl_agent_id }
|
|
56
|
+
}
|
|
57
|
+
// Heuristic fallback.
|
|
58
|
+
const turnRow = opts.db.prepare(
|
|
59
|
+
"SELECT turn_key FROM turns WHERE chat_id = ? AND ended_at IS NULL ORDER BY started_at DESC LIMIT 1",
|
|
60
|
+
).get(opts.chatId) as { turn_key: string } | undefined
|
|
61
|
+
if (turnRow?.turn_key == null) return null
|
|
62
|
+
const candidates = opts.db.prepare(
|
|
63
|
+
"SELECT jsonl_agent_id FROM subagents WHERE parent_turn_key = ? AND status = 'running' AND jsonl_agent_id IS NOT NULL ORDER BY started_at DESC",
|
|
64
|
+
).all(turnRow.turn_key) as Array<{ jsonl_agent_id: string }>
|
|
65
|
+
if (candidates.length === 0) return null
|
|
66
|
+
if (candidates.length > 1) {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.warn(
|
|
69
|
+
`progress_update: heuristic resolution selected most-recent of ${candidates.length} running sub-agents (chat=${opts.chatId}); pass agent_id explicitly to avoid mis-attribution`,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
return { agentId: candidates[0].jsonl_agent_id }
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.warn('progress_update: resolveCallingSubagent SQL error', err)
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
}
|