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,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the session-handoff continuity line.
|
|
3
|
+
*
|
|
4
|
+
* On session start, the telegram plugin reads `$AGENT_DIR/.handoff-topic`
|
|
5
|
+
* (written by the summarizer Stop hook). On the FIRST assistant reply
|
|
6
|
+
* of the new session the plugin prepends a subtle one-liner:
|
|
7
|
+
*
|
|
8
|
+
* ↩️ Picked up where we left off — <topic>
|
|
9
|
+
*
|
|
10
|
+
* The sidecar is consumed (read + deleted) so the line only fires once.
|
|
11
|
+
* All helpers here are filesystem-only or env-only — no Telegram side
|
|
12
|
+
* effects — which keeps them unit-testable in isolation.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync, unlinkSync, existsSync, writeFileSync, renameSync } from "node:fs";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
|
|
18
|
+
export const TOPIC_DISPLAY_MAX = 117;
|
|
19
|
+
export const HANDOFF_TOPIC_FILENAME = ".handoff-topic";
|
|
20
|
+
/**
|
|
21
|
+
* Secondary sidecar written by the progress-card driver on every
|
|
22
|
+
* successful turn_end. The file is overwritten each turn so it always
|
|
23
|
+
* reflects the most-recent turn. Used as a fallback source for the
|
|
24
|
+
* continuity line when the Stop-hook summarizer hasn't run yet (e.g.
|
|
25
|
+
* crash, mid-session restart, summarizer failure). Deleted alongside
|
|
26
|
+
* `.handoff-topic` in `consumeHandoffTopic`.
|
|
27
|
+
*/
|
|
28
|
+
export const LAST_TURN_SUMMARY_FILENAME = ".last-turn-summary";
|
|
29
|
+
|
|
30
|
+
export function resolveAgentDirFromEnv(): string | null {
|
|
31
|
+
const state = process.env.TELEGRAM_STATE_DIR;
|
|
32
|
+
if (!state || state.trim().length === 0) return null;
|
|
33
|
+
return dirname(state);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Read the handoff topic file if present. Returns the trimmed first
|
|
38
|
+
* non-empty line, truncated to TOPIC_DISPLAY_MAX with an ellipsis.
|
|
39
|
+
* Missing, empty, or unreadable → null.
|
|
40
|
+
*/
|
|
41
|
+
export function readHandoffTopic(agentDir: string): string | null {
|
|
42
|
+
const p = join(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
43
|
+
if (!existsSync(p)) return null;
|
|
44
|
+
let raw: string;
|
|
45
|
+
try {
|
|
46
|
+
raw = readFileSync(p, "utf-8");
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
51
|
+
if (lines.length === 0) return null;
|
|
52
|
+
let topic = lines[0];
|
|
53
|
+
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
54
|
+
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "…";
|
|
55
|
+
}
|
|
56
|
+
return topic;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read the per-turn summary file if present (written by the progress-
|
|
61
|
+
* card driver on every turn_end). Returns the trimmed first non-empty
|
|
62
|
+
* line, truncated like `readHandoffTopic`. The file is always
|
|
63
|
+
* overwritten so it reflects the most-recent completed turn.
|
|
64
|
+
*/
|
|
65
|
+
export function readLastTurnSummary(agentDir: string): string | null {
|
|
66
|
+
const p = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
67
|
+
if (!existsSync(p)) return null;
|
|
68
|
+
let raw: string;
|
|
69
|
+
try {
|
|
70
|
+
raw = readFileSync(p, "utf-8");
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
75
|
+
if (lines.length === 0) return null;
|
|
76
|
+
let topic = lines[0];
|
|
77
|
+
if (topic.length > TOPIC_DISPLAY_MAX) {
|
|
78
|
+
topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "…";
|
|
79
|
+
}
|
|
80
|
+
return topic;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Read + delete the topic file atomically (best-effort). A second call
|
|
85
|
+
* returns null even if the first succeeded — the sidecar is one-shot.
|
|
86
|
+
*
|
|
87
|
+
* Fallback: if no `.handoff-topic` is present (summarizer didn't run,
|
|
88
|
+
* crashed, or the session was restarted mid-loop), try the
|
|
89
|
+
* `.last-turn-summary` sidecar written by the progress-card driver.
|
|
90
|
+
* Both sidecars get removed on consume so the continuity line only
|
|
91
|
+
* fires once per resume.
|
|
92
|
+
*/
|
|
93
|
+
export function consumeHandoffTopic(agentDir: string): string | null {
|
|
94
|
+
const primary = readHandoffTopic(agentDir);
|
|
95
|
+
const primaryPath = join(agentDir, HANDOFF_TOPIC_FILENAME);
|
|
96
|
+
const fallbackPath = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
97
|
+
|
|
98
|
+
// Always remove the per-turn summary when we consume — otherwise a
|
|
99
|
+
// later session restart would still see a stale entry even after the
|
|
100
|
+
// continuity line fired.
|
|
101
|
+
const removeFallback = (): void => {
|
|
102
|
+
try {
|
|
103
|
+
unlinkSync(fallbackPath);
|
|
104
|
+
} catch {
|
|
105
|
+
/* already gone */
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (primary !== null) {
|
|
110
|
+
try {
|
|
111
|
+
unlinkSync(primaryPath);
|
|
112
|
+
} catch {
|
|
113
|
+
/* already gone */
|
|
114
|
+
}
|
|
115
|
+
removeFallback();
|
|
116
|
+
return primary;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const fallback = readLastTurnSummary(agentDir);
|
|
120
|
+
if (fallback !== null) {
|
|
121
|
+
removeFallback();
|
|
122
|
+
return fallback;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Atomically overwrite `.last-turn-summary` with a single-line summary.
|
|
129
|
+
* Called by the progress-card driver on every turn_end. Best-effort: any
|
|
130
|
+
* write failure is swallowed (logged by the caller if desired) — a
|
|
131
|
+
* missing fallback file is recoverable, a half-written one is not.
|
|
132
|
+
*
|
|
133
|
+
* The summary is the natural plain-text signature of a completed turn:
|
|
134
|
+
* `<N tools, Ys> — <user request (truncated)>`
|
|
135
|
+
* Callers should pass a pre-built line; this function handles only the
|
|
136
|
+
* atomic write + first-line discipline.
|
|
137
|
+
*/
|
|
138
|
+
export function writeLastTurnSummary(agentDir: string, summary: string): void {
|
|
139
|
+
const line = summary.split(/\r?\n/)[0]?.trim() ?? "";
|
|
140
|
+
if (line.length === 0) return;
|
|
141
|
+
const final = line.length > TOPIC_DISPLAY_MAX
|
|
142
|
+
? line.slice(0, TOPIC_DISPLAY_MAX) + "…"
|
|
143
|
+
: line;
|
|
144
|
+
const p = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
|
|
145
|
+
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
146
|
+
try {
|
|
147
|
+
writeFileSync(tmp, final + "\n", "utf-8");
|
|
148
|
+
renameSync(tmp, p);
|
|
149
|
+
} catch {
|
|
150
|
+
/* best-effort — continuity line is purely cosmetic */
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Reads SWITCHROOM_HANDOFF_SHOW_LINE. Defaults to true when unset so users
|
|
156
|
+
* opt out explicitly via switchroom.yaml rather than opt in.
|
|
157
|
+
*/
|
|
158
|
+
export function shouldShowHandoffLine(): boolean {
|
|
159
|
+
const v = process.env.SWITCHROOM_HANDOFF_SHOW_LINE;
|
|
160
|
+
if (v === undefined) return true;
|
|
161
|
+
return v.toLowerCase() !== "false";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type HandoffFormat = "html" | "markdownv2" | "text";
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Format the continuity line for the requested outbound format. The
|
|
168
|
+
* returned string already includes the trailing `\n\n` separator so the
|
|
169
|
+
* caller can concatenate directly with the assistant's reply body.
|
|
170
|
+
*
|
|
171
|
+
* HTML: wraps in <i>…</i>. MarkdownV2: wraps in _…_ with escaping.
|
|
172
|
+
* text: plain. All variants prefix the ↩️ emoji.
|
|
173
|
+
*/
|
|
174
|
+
export function formatHandoffLine(
|
|
175
|
+
topic: string,
|
|
176
|
+
format: HandoffFormat,
|
|
177
|
+
): string {
|
|
178
|
+
const prefix = "↩️ Picked up where we left off — ";
|
|
179
|
+
if (format === "html") {
|
|
180
|
+
return `<i>${prefix}${escapeHtml(topic)}</i>\n\n`;
|
|
181
|
+
}
|
|
182
|
+
if (format === "markdownv2") {
|
|
183
|
+
const escaped = escapeMarkdownV2(topic);
|
|
184
|
+
const prefixEsc = escapeMarkdownV2(prefix);
|
|
185
|
+
return `_${prefixEsc}${escaped}_\n\n`;
|
|
186
|
+
}
|
|
187
|
+
return `${prefix}${topic}\n\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function escapeHtml(s: string): string {
|
|
191
|
+
return s
|
|
192
|
+
.replace(/&/g, "&")
|
|
193
|
+
.replace(/</g, "<")
|
|
194
|
+
.replace(/>/g, ">");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const MDV2_SPECIALS = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
|
|
198
|
+
function escapeMarkdownV2(s: string): string {
|
|
199
|
+
return s.replace(MDV2_SPECIALS, (m) => "\\" + m);
|
|
200
|
+
}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent message history for the Telegram plugin.
|
|
3
|
+
*
|
|
4
|
+
* Telegram's Bot API exposes no chat history endpoint, so the agent has no
|
|
5
|
+
* way to recover "what were we just talking about" after a Claude Code
|
|
6
|
+
* restart without asking the user. This module fills that gap by writing
|
|
7
|
+
* every inbound and outbound message that flows through the plugin to a
|
|
8
|
+
* local SQLite database, queryable via the `get_recent_messages` MCP tool.
|
|
9
|
+
*
|
|
10
|
+
* Storage is `bun:sqlite` (Bun's bundled SQLite, no extra dep). The DB file
|
|
11
|
+
* lives at `${STATE_DIR}/history.db` and is chmod'd to 0600 to match the
|
|
12
|
+
* plugin's existing credential-handling pattern.
|
|
13
|
+
*
|
|
14
|
+
* Capture is gated:
|
|
15
|
+
* - Inbound: only writes after the access gate, topic filter, and the
|
|
16
|
+
* permission-reply intercept have passed (see server.ts handleInbound).
|
|
17
|
+
* - Outbound: the reply/stream_reply/forward_message/edit_message handlers
|
|
18
|
+
* all assertAllowedChat() before reaching the capture call sites.
|
|
19
|
+
*
|
|
20
|
+
* Per-chunk rows: when a long `reply` is split into multiple Telegram
|
|
21
|
+
* messages by the chunker, each chunk lands as its own row keyed by its
|
|
22
|
+
* real message_id, with a shared `group_id` (the first chunk's id) so
|
|
23
|
+
* the logical reply can be reconstructed if needed. This keeps
|
|
24
|
+
* `before_message_id` pagination working — every visible message in the
|
|
25
|
+
* chat exists in the DB.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { chmodSync, mkdirSync } from 'fs'
|
|
29
|
+
import { join } from 'path'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `bun:sqlite` is a Bun built-in — Vite/Node loaders can't resolve it
|
|
33
|
+
* statically, which would crash any vitest test that transitively
|
|
34
|
+
* imports this module (every test that imports server.ts). Hide the
|
|
35
|
+
* require behind an eval so static analysis passes; runtime resolution
|
|
36
|
+
* is per-Bun and works fine.
|
|
37
|
+
*
|
|
38
|
+
* The Database class is loaded lazily on the first initHistory() call.
|
|
39
|
+
* If we're somehow running under non-Bun (vitest fallback, ts-node, etc.)
|
|
40
|
+
* the lazy load throws — but only when the caller actually tries to use
|
|
41
|
+
* the history feature, not on module import.
|
|
42
|
+
*
|
|
43
|
+
* The only Database APIs we touch are constructor(path, opts), exec,
|
|
44
|
+
* prepare, transaction, close — all stable across bun:sqlite versions.
|
|
45
|
+
*/
|
|
46
|
+
type SqliteDatabase = {
|
|
47
|
+
exec(sql: string): void
|
|
48
|
+
prepare(sql: string): {
|
|
49
|
+
run(...params: unknown[]): unknown
|
|
50
|
+
all(...params: unknown[]): unknown[]
|
|
51
|
+
get(...params: unknown[]): unknown
|
|
52
|
+
}
|
|
53
|
+
transaction(fn: (...args: unknown[]) => unknown): (...args: unknown[]) => unknown
|
|
54
|
+
close(): void
|
|
55
|
+
}
|
|
56
|
+
type SqliteDatabaseConstructor = new (path: string, opts?: { create?: boolean }) => SqliteDatabase
|
|
57
|
+
|
|
58
|
+
let DatabaseClass: SqliteDatabaseConstructor | null = null
|
|
59
|
+
function loadDatabaseClass(): SqliteDatabaseConstructor {
|
|
60
|
+
if (DatabaseClass != null) return DatabaseClass
|
|
61
|
+
try {
|
|
62
|
+
// Bun exposes a `require` on `import.meta` that works in ESM modules
|
|
63
|
+
// and resolves built-in `bun:*` modules. Vite/esbuild's static
|
|
64
|
+
// analyzer doesn't follow `import.meta.require(...)` calls (it only
|
|
65
|
+
// resolves static `import` statements), so this hides the bun:sqlite
|
|
66
|
+
// import from the test loader. Under non-Bun runtimes
|
|
67
|
+
// `import.meta.require` is undefined and we throw a clear error.
|
|
68
|
+
const metaRequire = (import.meta as { require?: (id: string) => unknown }).require
|
|
69
|
+
if (typeof metaRequire !== 'function') {
|
|
70
|
+
throw new Error('import.meta.require not available — Bun runtime required')
|
|
71
|
+
}
|
|
72
|
+
const mod = metaRequire('bun:sqlite') as { Database?: SqliteDatabaseConstructor }
|
|
73
|
+
if (!mod.Database) throw new Error('bun:sqlite did not export Database')
|
|
74
|
+
DatabaseClass = mod.Database
|
|
75
|
+
return DatabaseClass
|
|
76
|
+
} catch (err) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`history.ts requires Bun runtime (bun:sqlite). Caller: ${(err as Error).message}`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type MessageRole = 'user' | 'assistant'
|
|
84
|
+
|
|
85
|
+
export interface RecordedMessage {
|
|
86
|
+
chat_id: string
|
|
87
|
+
thread_id: number | null
|
|
88
|
+
message_id: number
|
|
89
|
+
role: MessageRole
|
|
90
|
+
user: string | null
|
|
91
|
+
user_id: string | null
|
|
92
|
+
ts: number
|
|
93
|
+
text: string
|
|
94
|
+
attachment_kind: string | null
|
|
95
|
+
group_id: number | null
|
|
96
|
+
/**
|
|
97
|
+
* Set when the inbound user message was a Telegram-native reply to a
|
|
98
|
+
* prior message. Lets get_recent_messages surface the reply context
|
|
99
|
+
* the agent saw at delivery time.
|
|
100
|
+
*/
|
|
101
|
+
reply_to_message_id: number | null
|
|
102
|
+
reply_to_text: string | null
|
|
103
|
+
/**
|
|
104
|
+
* The most recent emoji reaction the user placed on this (assistant)
|
|
105
|
+
* message. Null means no reaction or reaction was removed. Updated by
|
|
106
|
+
* the message_reaction handler when Telegram delivers reaction events.
|
|
107
|
+
* Only emoji reactions are tracked — custom emoji are ignored for v1.
|
|
108
|
+
*/
|
|
109
|
+
user_reaction: string | null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface QueryOptions {
|
|
113
|
+
chat_id: string
|
|
114
|
+
thread_id?: number | null
|
|
115
|
+
limit?: number
|
|
116
|
+
before_message_id?: number
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const DEFAULT_LIMIT = 10
|
|
120
|
+
const MAX_LIMIT = 50
|
|
121
|
+
|
|
122
|
+
let db: SqliteDatabase | null = null
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Open (or create) the history DB and run migrations + retention sweep.
|
|
126
|
+
* Idempotent — safe to call once at server startup.
|
|
127
|
+
*
|
|
128
|
+
* `retentionDays` deletes rows older than the cutoff. 0 disables retention.
|
|
129
|
+
*/
|
|
130
|
+
export function initHistory(stateDir: string, retentionDays = 30): void {
|
|
131
|
+
if (db != null) return
|
|
132
|
+
const Database = loadDatabaseClass()
|
|
133
|
+
mkdirSync(stateDir, { recursive: true, mode: 0o700 })
|
|
134
|
+
const path = join(stateDir, 'history.db')
|
|
135
|
+
db = new Database(path, { create: true })
|
|
136
|
+
// WAL is friendlier for concurrent reads while a long transaction writes,
|
|
137
|
+
// and survives crashes more cleanly than rollback journal.
|
|
138
|
+
db.exec('PRAGMA journal_mode = WAL')
|
|
139
|
+
db.exec('PRAGMA synchronous = NORMAL')
|
|
140
|
+
db.exec(`
|
|
141
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
142
|
+
chat_id TEXT NOT NULL,
|
|
143
|
+
thread_id INTEGER,
|
|
144
|
+
message_id INTEGER NOT NULL,
|
|
145
|
+
role TEXT NOT NULL,
|
|
146
|
+
user TEXT,
|
|
147
|
+
user_id TEXT,
|
|
148
|
+
ts INTEGER NOT NULL,
|
|
149
|
+
text TEXT NOT NULL,
|
|
150
|
+
attachment_kind TEXT,
|
|
151
|
+
group_id INTEGER,
|
|
152
|
+
reply_to_message_id INTEGER,
|
|
153
|
+
reply_to_text TEXT,
|
|
154
|
+
PRIMARY KEY (chat_id, thread_id, message_id)
|
|
155
|
+
)
|
|
156
|
+
`)
|
|
157
|
+
db.exec(`
|
|
158
|
+
CREATE INDEX IF NOT EXISTS idx_messages_recent
|
|
159
|
+
ON messages (chat_id, thread_id, ts DESC)
|
|
160
|
+
`)
|
|
161
|
+
// Migration: add reply_to columns to existing DBs that pre-date issue #119.
|
|
162
|
+
// SQLite has no IF NOT EXISTS for ALTER TABLE ADD COLUMN, so we tolerate
|
|
163
|
+
// "duplicate column name" errors and re-throw anything else.
|
|
164
|
+
for (const column of ["reply_to_message_id INTEGER", "reply_to_text TEXT", "user_reaction TEXT"]) {
|
|
165
|
+
try {
|
|
166
|
+
db.exec(`ALTER TABLE messages ADD COLUMN ${column}`)
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
169
|
+
if (!/duplicate column name/i.test(msg)) throw err
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Lock the file to owner-only. Same pattern the plugin uses for .env at
|
|
174
|
+
// server.ts:52. No-op on Windows (would need ACLs).
|
|
175
|
+
try {
|
|
176
|
+
chmodSync(path, 0o600)
|
|
177
|
+
} catch {
|
|
178
|
+
/* ignore — chmod not supported, e.g. some FUSE mounts */
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (retentionDays > 0) {
|
|
182
|
+
const cutoff = Math.floor(Date.now() / 1000) - retentionDays * 86400
|
|
183
|
+
db.prepare('DELETE FROM messages WHERE ts < ?').run(cutoff)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* For tests — close the singleton and forget it. Production code never
|
|
189
|
+
* needs this; the DB is held open for the lifetime of the process.
|
|
190
|
+
*/
|
|
191
|
+
export function _resetForTests(): void {
|
|
192
|
+
if (db != null) {
|
|
193
|
+
db.close()
|
|
194
|
+
db = null
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function requireDb(): SqliteDatabase {
|
|
199
|
+
if (db == null) {
|
|
200
|
+
throw new Error('history: initHistory() must be called before any record/query operation')
|
|
201
|
+
}
|
|
202
|
+
return db
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface RecordInboundArgs {
|
|
206
|
+
chat_id: string
|
|
207
|
+
thread_id: number | null | undefined
|
|
208
|
+
message_id: number | null | undefined
|
|
209
|
+
user: string | null | undefined
|
|
210
|
+
user_id: string | null | undefined
|
|
211
|
+
ts: number
|
|
212
|
+
text: string
|
|
213
|
+
attachment_kind?: string | null | undefined
|
|
214
|
+
/**
|
|
215
|
+
* If the user replied to a prior message via Telegram's native reply
|
|
216
|
+
* feature, the original message_id and a (truncated) preview of its
|
|
217
|
+
* text. Populated from `ctx.message.reply_to_message` in the gateway
|
|
218
|
+
* handler. See issue #119.
|
|
219
|
+
*/
|
|
220
|
+
reply_to_message_id?: number | null | undefined
|
|
221
|
+
reply_to_text?: string | null | undefined
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Record an inbound (user → bot) message. Called from server.ts handleInbound
|
|
226
|
+
* right next to the `notifications/claude/channel` emit, so the stored row
|
|
227
|
+
* matches what the agent actually saw.
|
|
228
|
+
*
|
|
229
|
+
* If `message_id` is missing (which shouldn't happen for normal Telegram
|
|
230
|
+
* messages, but defensively...) we silently skip — the PK requires it and
|
|
231
|
+
* we'd rather lose the row than crash the inbound path.
|
|
232
|
+
*/
|
|
233
|
+
export function recordInbound(args: RecordInboundArgs): void {
|
|
234
|
+
if (args.message_id == null) return
|
|
235
|
+
const stmt = requireDb().prepare(`
|
|
236
|
+
INSERT OR REPLACE INTO messages
|
|
237
|
+
(chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id, reply_to_message_id, reply_to_text)
|
|
238
|
+
VALUES (?, ?, ?, 'user', ?, ?, ?, ?, ?, NULL, ?, ?)
|
|
239
|
+
`)
|
|
240
|
+
stmt.run(
|
|
241
|
+
args.chat_id,
|
|
242
|
+
args.thread_id ?? null,
|
|
243
|
+
args.message_id,
|
|
244
|
+
args.user ?? null,
|
|
245
|
+
args.user_id ?? null,
|
|
246
|
+
args.ts,
|
|
247
|
+
args.text,
|
|
248
|
+
args.attachment_kind ?? null,
|
|
249
|
+
args.reply_to_message_id ?? null,
|
|
250
|
+
args.reply_to_text ?? null,
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface RecordOutboundArgs {
|
|
255
|
+
chat_id: string
|
|
256
|
+
thread_id: number | null | undefined
|
|
257
|
+
message_ids: number[] // one entry per chunk
|
|
258
|
+
texts: string[] // parallel array, same length as message_ids
|
|
259
|
+
ts?: number // defaults to now
|
|
260
|
+
attachment_kinds?: (string | null | undefined)[]
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Record an outbound (bot → user) message. The `reply` handler chunks long
|
|
265
|
+
* text into multiple Telegram sends; pass them all here in one call so they
|
|
266
|
+
* share a `group_id` (the first chunk's message_id). The other handlers
|
|
267
|
+
* (`stream_reply`, `forward_message`, orphaned-reply backstop) typically
|
|
268
|
+
* pass a single-element array.
|
|
269
|
+
*/
|
|
270
|
+
export function recordOutbound(args: RecordOutboundArgs): void {
|
|
271
|
+
if (args.message_ids.length === 0) return
|
|
272
|
+
const ts = args.ts ?? Math.floor(Date.now() / 1000)
|
|
273
|
+
const groupId = args.message_ids[0]!
|
|
274
|
+
const stmt = requireDb().prepare(`
|
|
275
|
+
INSERT OR REPLACE INTO messages
|
|
276
|
+
(chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id)
|
|
277
|
+
VALUES (?, ?, ?, 'assistant', NULL, NULL, ?, ?, ?, ?)
|
|
278
|
+
`)
|
|
279
|
+
// bun:sqlite has a transaction() helper. Cheap insurance against partial
|
|
280
|
+
// writes if the process dies mid-loop. The transaction signature is
|
|
281
|
+
// typed as variadic-unknown for genericity; cast the typed callback
|
|
282
|
+
// through the wider shape.
|
|
283
|
+
const tx = requireDb().transaction(((rows: Array<[number, string, string | null]>) => {
|
|
284
|
+
for (const [msgId, text, attachKind] of rows) {
|
|
285
|
+
stmt.run(
|
|
286
|
+
args.chat_id,
|
|
287
|
+
args.thread_id ?? null,
|
|
288
|
+
msgId,
|
|
289
|
+
ts,
|
|
290
|
+
text,
|
|
291
|
+
attachKind,
|
|
292
|
+
groupId,
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
}) as (...args: unknown[]) => unknown)
|
|
296
|
+
const rows: Array<[number, string, string | null]> = args.message_ids.map((id, i) => [
|
|
297
|
+
id,
|
|
298
|
+
args.texts[i] ?? '',
|
|
299
|
+
args.attachment_kinds?.[i] ?? null,
|
|
300
|
+
])
|
|
301
|
+
tx(rows)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
interface RecordEditArgs {
|
|
305
|
+
chat_id: string
|
|
306
|
+
message_id: number
|
|
307
|
+
text: string
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Update an existing outbound message's text in place. Called from the
|
|
312
|
+
* `edit_message` tool handler. If the row doesn't exist (the bot is editing
|
|
313
|
+
* a message it sent before history was enabled, or before this version
|
|
314
|
+
* shipped), we silently no-op rather than inventing a row with role guessed.
|
|
315
|
+
*
|
|
316
|
+
* Telegram message_ids are unique within a chat regardless of thread, so
|
|
317
|
+
* we don't filter on thread_id — that lets edits work even when the
|
|
318
|
+
* original send-time thread isn't known at edit time.
|
|
319
|
+
*/
|
|
320
|
+
export function recordEdit(args: RecordEditArgs): void {
|
|
321
|
+
requireDb()
|
|
322
|
+
.prepare(`
|
|
323
|
+
UPDATE messages
|
|
324
|
+
SET text = ?
|
|
325
|
+
WHERE chat_id = ? AND message_id = ?
|
|
326
|
+
`)
|
|
327
|
+
.run(args.text, args.chat_id, args.message_id)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface RecordReactionArgs {
|
|
331
|
+
chat_id: string
|
|
332
|
+
message_id: number
|
|
333
|
+
/** The emoji string to store, or null to clear the reaction field. */
|
|
334
|
+
emoji: string | null
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Update (or clear) the user_reaction field for a message row. Called from
|
|
339
|
+
* the message_reaction gateway handler when Telegram delivers a reaction
|
|
340
|
+
* event. If the row doesn't exist (the message predates history, or the
|
|
341
|
+
* reaction was placed on an inbound message), silently no-ops.
|
|
342
|
+
*
|
|
343
|
+
* Telegram message_ids are unique within a chat regardless of thread, so
|
|
344
|
+
* we match on (chat_id, message_id) and ignore thread_id — same as recordEdit.
|
|
345
|
+
*/
|
|
346
|
+
export function recordReaction(args: RecordReactionArgs): void {
|
|
347
|
+
requireDb()
|
|
348
|
+
.prepare(`
|
|
349
|
+
UPDATE messages
|
|
350
|
+
SET user_reaction = ?
|
|
351
|
+
WHERE chat_id = ? AND message_id = ?
|
|
352
|
+
`)
|
|
353
|
+
.run(args.emoji, args.chat_id, args.message_id)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface DeleteFromHistoryArgs {
|
|
357
|
+
chat_id: string
|
|
358
|
+
message_id: number
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Remove a single row from the local history buffer. Called after a
|
|
363
|
+
* successful `bot.api.deleteMessage` so the `get_recent_messages` tool
|
|
364
|
+
* reflects the deletion. Best-effort: callers should catch errors and
|
|
365
|
+
* log them rather than failing the deletion request.
|
|
366
|
+
*
|
|
367
|
+
* Telegram message_ids are unique within a chat regardless of thread, so
|
|
368
|
+
* we match on (chat_id, message_id) and ignore thread.
|
|
369
|
+
*/
|
|
370
|
+
export function deleteFromHistory(args: DeleteFromHistoryArgs): void {
|
|
371
|
+
requireDb()
|
|
372
|
+
.prepare(`
|
|
373
|
+
DELETE FROM messages
|
|
374
|
+
WHERE chat_id = ? AND message_id = ?
|
|
375
|
+
`)
|
|
376
|
+
.run(args.chat_id, args.message_id)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Fetch recent messages for a chat (or chat+thread).
|
|
381
|
+
*
|
|
382
|
+
* Returns oldest-first so the caller can paste them into a prompt without
|
|
383
|
+
* reversing. `before_message_id` lets the caller paginate further back —
|
|
384
|
+
* pass the smallest `message_id` from the previous page.
|
|
385
|
+
*
|
|
386
|
+
* `thread_id` filter semantics:
|
|
387
|
+
* - omitted / undefined → all messages in the chat across all threads
|
|
388
|
+
* - explicit number → only that thread
|
|
389
|
+
* - explicit null → only the chat-root (non-thread) messages
|
|
390
|
+
*/
|
|
391
|
+
/**
|
|
392
|
+
* Count outbound messages sent to a chat within the last `withinSeconds`.
|
|
393
|
+
* Used by the orphaned-reply backstop to detect if a reply tool handler
|
|
394
|
+
* already sent a message during the backstop's delay window, avoiding
|
|
395
|
+
* a duplicate send.
|
|
396
|
+
*/
|
|
397
|
+
/**
|
|
398
|
+
* Look up the most recent inbound (user → bot) message id for a chat, optionally
|
|
399
|
+
* scoped to a forum-topic thread. Returns `null` if no inbound message exists
|
|
400
|
+
* yet (fresh chat, or history was disabled when the message arrived).
|
|
401
|
+
*
|
|
402
|
+
* Used by the `reply` and `stream_reply` tool handlers to auto-populate
|
|
403
|
+
* `reply_parameters` so outbound messages quote-thread under whatever the
|
|
404
|
+
* user last said — the common case for a conversational bot — without the
|
|
405
|
+
* agent having to pass `reply_to` explicitly every turn.
|
|
406
|
+
*
|
|
407
|
+
* `thread_id` filter semantics match `query()`:
|
|
408
|
+
* - omitted / undefined → any message in the chat
|
|
409
|
+
* - explicit number → only that thread
|
|
410
|
+
* - explicit null → only chat-root (non-thread)
|
|
411
|
+
*/
|
|
412
|
+
export function getLatestInboundMessageId(
|
|
413
|
+
chatId: string,
|
|
414
|
+
threadId?: number | null,
|
|
415
|
+
): number | null {
|
|
416
|
+
const params: unknown[] = [chatId]
|
|
417
|
+
let sql = "SELECT message_id FROM messages WHERE chat_id = ? AND role = 'user'"
|
|
418
|
+
if (threadId !== undefined) {
|
|
419
|
+
if (threadId === null) {
|
|
420
|
+
sql += ' AND thread_id IS NULL'
|
|
421
|
+
} else {
|
|
422
|
+
sql += ' AND thread_id = ?'
|
|
423
|
+
params.push(threadId)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
sql += ' ORDER BY ts DESC, message_id DESC LIMIT 1'
|
|
427
|
+
const row = requireDb().prepare(sql).get(...params as any[]) as
|
|
428
|
+
| { message_id: number }
|
|
429
|
+
| undefined
|
|
430
|
+
return row?.message_id ?? null
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function getRecentOutboundCount(
|
|
434
|
+
chatId: string,
|
|
435
|
+
withinSeconds: number,
|
|
436
|
+
): number {
|
|
437
|
+
const cutoff = Math.floor(Date.now() / 1000) - withinSeconds
|
|
438
|
+
const row = requireDb()
|
|
439
|
+
.prepare(
|
|
440
|
+
'SELECT COUNT(*) as cnt FROM messages WHERE chat_id = ? AND role = ? AND ts >= ?',
|
|
441
|
+
)
|
|
442
|
+
.get(chatId, 'assistant', cutoff) as { cnt: number } | undefined
|
|
443
|
+
return row?.cnt ?? 0
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function query(opts: QueryOptions): RecordedMessage[] {
|
|
447
|
+
const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT))
|
|
448
|
+
const params: unknown[] = [opts.chat_id]
|
|
449
|
+
let sql = 'SELECT * FROM messages WHERE chat_id = ?'
|
|
450
|
+
if (opts.thread_id !== undefined) {
|
|
451
|
+
if (opts.thread_id === null) {
|
|
452
|
+
sql += ' AND thread_id IS NULL'
|
|
453
|
+
} else {
|
|
454
|
+
sql += ' AND thread_id = ?'
|
|
455
|
+
params.push(opts.thread_id)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (opts.before_message_id != null) {
|
|
459
|
+
sql += ' AND message_id < ?'
|
|
460
|
+
params.push(opts.before_message_id)
|
|
461
|
+
}
|
|
462
|
+
sql += ' ORDER BY ts DESC, message_id DESC LIMIT ?'
|
|
463
|
+
params.push(limit)
|
|
464
|
+
const rows = requireDb().prepare(sql).all(...params as any[]) as RecordedMessage[]
|
|
465
|
+
// SELECT was DESC; flip to oldest-first for the caller.
|
|
466
|
+
rows.reverse()
|
|
467
|
+
return rows
|
|
468
|
+
}
|