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,725 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for three confirmed bugs in the subagent registry.
|
|
3
|
+
* Vitest-compatible portion: watcher integration tests only.
|
|
4
|
+
*
|
|
5
|
+
* Bug 1 — ID mismatch: bumpSubagentActivity is a permanent no-op because the
|
|
6
|
+
* watcher looks up rows by JSONL filename stem (e.g. "a37ad763…") but DB rows
|
|
7
|
+
* are keyed on tool_use_id (e.g. "toolu_013u…"). Fix: store the JSONL stem in
|
|
8
|
+
* a new `jsonl_agent_id` column and let the watcher match on that.
|
|
9
|
+
*
|
|
10
|
+
* Bug 2 — Background agents marked completed at launch: PostToolUse fires for
|
|
11
|
+
* background Agent() calls within seconds of launch (the "launched" response),
|
|
12
|
+
* marking the row completed while the agent is actively running. Fix: gate
|
|
13
|
+
* PostToolUse completion on `background === false`; background rows advance
|
|
14
|
+
* only via watcher turn_end.
|
|
15
|
+
*
|
|
16
|
+
* Bug 3 — No stalled-row sweeper: `recordSubagentStall` exists but nothing calls
|
|
17
|
+
* it when a row is `running` and `last_activity_at` is stale. Fix: the watcher
|
|
18
|
+
* must call `recordSubagentStall` for stale DB rows in addition to updating the
|
|
19
|
+
* in-memory `stallNotified` flag.
|
|
20
|
+
*
|
|
21
|
+
* Tests that require bun:sqlite are in:
|
|
22
|
+
* telegram-plugin/registry/subagents-bugs.test.ts (run via bun test)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
26
|
+
import * as fsReal from 'fs'
|
|
27
|
+
import { startSubagentWatcher } from '../subagent-watcher.js'
|
|
28
|
+
import type { SubagentLivenessDb } from '../subagent-watcher.js'
|
|
29
|
+
|
|
30
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
function buildJSONL(...lines: object[]): string {
|
|
33
|
+
return lines.map((l) => JSON.stringify(l)).join('\n') + '\n'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function subAgentUserMsg(text: string) {
|
|
37
|
+
return { type: 'user', message: { content: [{ type: 'text', text }] } }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function subAgentToolUse(name: string, id: string) {
|
|
41
|
+
return { type: 'assistant', message: { content: [{ type: 'tool_use', name, id, input: {} }] } }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function subAgentTurnDuration() {
|
|
45
|
+
return { type: 'system', subtype: 'turn_duration', durationMs: 5000 }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Minimal fake DB that records all SQL calls for assertion.
|
|
50
|
+
* Implements SubagentLivenessDb plus enough for the watcher's SELECT lookup.
|
|
51
|
+
*/
|
|
52
|
+
function makeInMemoryDb(rows: Record<string, Record<string, unknown>> = {}) {
|
|
53
|
+
const calls: Array<{ sql: string; params: unknown[] }> = []
|
|
54
|
+
const db: SubagentLivenessDb & {
|
|
55
|
+
_calls: typeof calls
|
|
56
|
+
_rows: typeof rows
|
|
57
|
+
} = {
|
|
58
|
+
_calls: calls,
|
|
59
|
+
_rows: rows,
|
|
60
|
+
prepare(sql: string) {
|
|
61
|
+
return {
|
|
62
|
+
run(...params: unknown[]) {
|
|
63
|
+
calls.push({ sql, params })
|
|
64
|
+
// Track stall writes for assertion (use /s dotAll to match across newlines)
|
|
65
|
+
if (/UPDATE subagents[\s\S]*SET status\s*=\s*'stalled'/i.test(sql)) {
|
|
66
|
+
for (const p of params) {
|
|
67
|
+
if (typeof p === 'string' && rows[p]) {
|
|
68
|
+
rows[p]['status'] = 'stalled'
|
|
69
|
+
}
|
|
70
|
+
// Also handle jsonl_agent_id lookup
|
|
71
|
+
if (typeof p === 'string') {
|
|
72
|
+
const entry = Object.values(rows).find((r) => r['jsonl_agent_id'] === p)
|
|
73
|
+
if (entry) entry['status'] = 'stalled'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Track end writes (use /s dotAll)
|
|
78
|
+
if (/UPDATE subagents[\s\S]*SET[\s\S]*ended_at/i.test(sql)) {
|
|
79
|
+
// params: [endedAt, status, resultSummary, id]
|
|
80
|
+
// recordSubagentEnd: run(endedAt, status, resultSummary, id)
|
|
81
|
+
const id = params[3]
|
|
82
|
+
if (typeof id === 'string' && rows[id]) {
|
|
83
|
+
rows[id]['ended_at'] = params[0]
|
|
84
|
+
rows[id]['status'] = params[1]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Track activity bumps (UPDATE subagents\n SET last_activity_at)
|
|
88
|
+
if (/UPDATE subagents[\s\S]*SET last_activity_at/i.test(sql)) {
|
|
89
|
+
// bumpSubagentActivity: run(ts, id)
|
|
90
|
+
const id = params[1]
|
|
91
|
+
if (typeof id === 'string' && rows[id]) {
|
|
92
|
+
rows[id]['last_activity_at'] = params[0]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
get(...params: unknown[]) {
|
|
97
|
+
calls.push({ sql, params })
|
|
98
|
+
// SELECT id FROM subagents WHERE id = ?
|
|
99
|
+
if (/SELECT.*FROM subagents WHERE id\s*=/i.test(sql)) {
|
|
100
|
+
const id = params[0] as string
|
|
101
|
+
return rows[id] ?? null
|
|
102
|
+
}
|
|
103
|
+
// SELECT id FROM subagents WHERE jsonl_agent_id = ?
|
|
104
|
+
if (/SELECT.*FROM subagents WHERE jsonl_agent_id/i.test(sql)) {
|
|
105
|
+
const jsonlId = params[0] as string
|
|
106
|
+
return Object.values(rows).find((r) => r['jsonl_agent_id'] === jsonlId) ?? null
|
|
107
|
+
}
|
|
108
|
+
return null
|
|
109
|
+
},
|
|
110
|
+
all(...params: unknown[]) {
|
|
111
|
+
calls.push({ sql, params })
|
|
112
|
+
if (/SELECT.*FROM subagents WHERE status.*running/i.test(sql)) {
|
|
113
|
+
return Object.values(rows).filter((r) => r['status'] === 'running')
|
|
114
|
+
}
|
|
115
|
+
return []
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
return db
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type MockFs = {
|
|
124
|
+
existsSync: typeof fsReal.existsSync
|
|
125
|
+
readdirSync: typeof fsReal.readdirSync
|
|
126
|
+
statSync: typeof fsReal.statSync
|
|
127
|
+
openSync: typeof fsReal.openSync
|
|
128
|
+
closeSync: typeof fsReal.closeSync
|
|
129
|
+
readSync: typeof fsReal.readSync
|
|
130
|
+
watch: typeof fsReal.watch
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function makeHarnessWithDb(opts: {
|
|
134
|
+
agentDir?: string
|
|
135
|
+
files?: Record<string, string>
|
|
136
|
+
dirs?: Record<string, string[]>
|
|
137
|
+
existingDirs?: string[]
|
|
138
|
+
stallThresholdMs?: number
|
|
139
|
+
db?: SubagentLivenessDb | null
|
|
140
|
+
}) {
|
|
141
|
+
const {
|
|
142
|
+
agentDir = '/home/user/.switchroom/agents/myagent',
|
|
143
|
+
files = {},
|
|
144
|
+
dirs = {},
|
|
145
|
+
existingDirs = [],
|
|
146
|
+
stallThresholdMs = 60_000,
|
|
147
|
+
db = null,
|
|
148
|
+
} = opts
|
|
149
|
+
|
|
150
|
+
let currentTime = 10_000
|
|
151
|
+
const notifications: string[] = []
|
|
152
|
+
const logs: string[] = []
|
|
153
|
+
|
|
154
|
+
const fileContents: Map<string, Buffer> = new Map()
|
|
155
|
+
for (const [path, content] of Object.entries(files)) {
|
|
156
|
+
fileContents.set(path, Buffer.from(content, 'utf-8'))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let lastOpenedPath: string | null = null
|
|
160
|
+
const mockFs: MockFs = {
|
|
161
|
+
existsSync: ((p: fsReal.PathLike) => {
|
|
162
|
+
const ps = String(p)
|
|
163
|
+
if (existingDirs.includes(ps)) return true
|
|
164
|
+
if (dirs[ps] !== undefined) return true
|
|
165
|
+
if (fileContents.has(ps)) return true
|
|
166
|
+
for (const fp of fileContents.keys()) {
|
|
167
|
+
if (fp.startsWith(ps + '/')) return true
|
|
168
|
+
}
|
|
169
|
+
return false
|
|
170
|
+
}) as typeof fsReal.existsSync,
|
|
171
|
+
readdirSync: ((p: fsReal.PathLike) => {
|
|
172
|
+
const ps = String(p)
|
|
173
|
+
if (dirs[ps]) return dirs[ps]
|
|
174
|
+
const children = new Set<string>()
|
|
175
|
+
for (const fp of fileContents.keys()) {
|
|
176
|
+
if (fp.startsWith(ps + '/')) {
|
|
177
|
+
const rest = fp.slice(ps.length + 1)
|
|
178
|
+
const part = rest.split('/')[0]
|
|
179
|
+
if (part) children.add(part)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return Array.from(children)
|
|
183
|
+
}) as unknown as typeof fsReal.readdirSync,
|
|
184
|
+
statSync: ((p: fsReal.PathLike) => {
|
|
185
|
+
const ps = String(p)
|
|
186
|
+
const content = fileContents.get(ps)
|
|
187
|
+
return { size: content?.length ?? 0 } as fsReal.Stats
|
|
188
|
+
}) as typeof fsReal.statSync,
|
|
189
|
+
openSync: ((p: fsReal.PathLike) => {
|
|
190
|
+
lastOpenedPath = String(p)
|
|
191
|
+
return 42
|
|
192
|
+
}) as unknown as typeof fsReal.openSync,
|
|
193
|
+
closeSync: (() => {
|
|
194
|
+
lastOpenedPath = null
|
|
195
|
+
}) as typeof fsReal.closeSync,
|
|
196
|
+
readSync: ((
|
|
197
|
+
_fd: number,
|
|
198
|
+
buf: NodeJS.ArrayBufferView,
|
|
199
|
+
offset: number,
|
|
200
|
+
length: number,
|
|
201
|
+
position: number | null,
|
|
202
|
+
): number => {
|
|
203
|
+
const content = lastOpenedPath != null ? fileContents.get(lastOpenedPath) : undefined
|
|
204
|
+
if (!content) return 0
|
|
205
|
+
const pos = position ?? 0
|
|
206
|
+
const src = content.slice(pos, pos + length)
|
|
207
|
+
;(src as Buffer).copy(buf as Buffer, offset)
|
|
208
|
+
return src.length
|
|
209
|
+
}) as unknown as typeof fsReal.readSync,
|
|
210
|
+
watch: (() => {
|
|
211
|
+
return { close: vi.fn() } as unknown as fsReal.FSWatcher
|
|
212
|
+
}) as unknown as typeof fsReal.watch,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const intervals: Array<{ fn: () => void; ms: number; ref: number; fireAt: number }> = []
|
|
216
|
+
let nextRef = 1
|
|
217
|
+
|
|
218
|
+
const watcher = startSubagentWatcher({
|
|
219
|
+
agentDir,
|
|
220
|
+
sendNotification: (text) => notifications.push(text),
|
|
221
|
+
stallThresholdMs,
|
|
222
|
+
rescanMs: 500,
|
|
223
|
+
now: () => currentTime,
|
|
224
|
+
setInterval: (fn, ms) => {
|
|
225
|
+
const ref = nextRef++
|
|
226
|
+
intervals.push({ fn, ms, ref, fireAt: currentTime + ms })
|
|
227
|
+
return { ref }
|
|
228
|
+
},
|
|
229
|
+
clearInterval: (handle) => {
|
|
230
|
+
const { ref } = handle as { ref: number }
|
|
231
|
+
const idx = intervals.findIndex((i) => i.ref === ref)
|
|
232
|
+
if (idx !== -1) intervals.splice(idx, 1)
|
|
233
|
+
},
|
|
234
|
+
fs: mockFs,
|
|
235
|
+
log: (msg: string) => { logs.push(msg) },
|
|
236
|
+
db,
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const advance = (ms: number): void => {
|
|
240
|
+
currentTime += ms
|
|
241
|
+
for (;;) {
|
|
242
|
+
intervals.sort((a, b) => a.fireAt - b.fireAt)
|
|
243
|
+
const next = intervals[0]
|
|
244
|
+
if (!next || next.fireAt > currentTime) break
|
|
245
|
+
next.fireAt += next.ms
|
|
246
|
+
next.fn()
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const poll = (): void => {
|
|
251
|
+
const pollInterval = intervals[0]
|
|
252
|
+
if (pollInterval) pollInterval.fn()
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { notifications, logs, advance, poll, watcher, now: () => currentTime, mockFs, fileContents }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ─── Bug 1 — ID mismatch: watcher never bumps last_activity_at ───────────────
|
|
259
|
+
|
|
260
|
+
describe('Bug 1 — ID mismatch: bumpSubagentActivity must use jsonl_agent_id', () => {
|
|
261
|
+
beforeEach(() => {
|
|
262
|
+
vi.restoreAllMocks()
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('watcher bumps last_activity_at when jsonl_agent_id matches', () => {
|
|
266
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
267
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
268
|
+
const jsonlStem = 'a37ad7639ae61476c'
|
|
269
|
+
const toolUseId = 'toolu_013uXh6BmWecF3ajx4mPxS6V'
|
|
270
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
271
|
+
const content = buildJSONL(subAgentUserMsg('Do work'), subAgentToolUse('Bash', 'x1'))
|
|
272
|
+
|
|
273
|
+
// DB row is keyed on toolUseId but has jsonl_agent_id = jsonlStem
|
|
274
|
+
const db = makeInMemoryDb({
|
|
275
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
const h = makeHarnessWithDb({
|
|
279
|
+
agentDir,
|
|
280
|
+
dirs: {
|
|
281
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
282
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
283
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
284
|
+
},
|
|
285
|
+
files: { [jsonlPath]: content },
|
|
286
|
+
db,
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// File exists at boot → historical. After boot the watcher still does an
|
|
290
|
+
// initial read, which should have gone through the jsonl_agent_id lookup.
|
|
291
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
292
|
+
expect(entry).toBeDefined()
|
|
293
|
+
|
|
294
|
+
// The watcher must have queried by jsonl_agent_id (not just by id=jsonlStem)
|
|
295
|
+
const lookupCalls = db._calls.filter(
|
|
296
|
+
(c) => /WHERE jsonl_agent_id/i.test(c.sql),
|
|
297
|
+
)
|
|
298
|
+
expect(lookupCalls.length).toBeGreaterThan(0)
|
|
299
|
+
|
|
300
|
+
// And must have called bumpSubagentActivity — UPDATE SET last_activity_at
|
|
301
|
+
const bumpCalls = db._calls.filter(
|
|
302
|
+
(c) => /UPDATE subagents[\s\S]*SET last_activity_at/i.test(c.sql),
|
|
303
|
+
)
|
|
304
|
+
expect(bumpCalls.length).toBeGreaterThan(0)
|
|
305
|
+
|
|
306
|
+
// The bump WHERE clause must use the actual DB row PK (toolUseId), not the jsonlStem
|
|
307
|
+
const bumpParams = bumpCalls[0].params
|
|
308
|
+
expect(bumpParams).toContain(toolUseId)
|
|
309
|
+
|
|
310
|
+
h.watcher.stop()
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('watcher logs skip when no DB row has matching jsonl_agent_id', () => {
|
|
314
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
315
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
316
|
+
const jsonlStem = 'newagent001'
|
|
317
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
318
|
+
const content = buildJSONL(subAgentUserMsg('Work'))
|
|
319
|
+
|
|
320
|
+
// Empty DB — PreToolUse hasn't fired yet
|
|
321
|
+
const db = makeInMemoryDb({})
|
|
322
|
+
|
|
323
|
+
const h = makeHarnessWithDb({
|
|
324
|
+
agentDir,
|
|
325
|
+
dirs: {
|
|
326
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
327
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
328
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
329
|
+
},
|
|
330
|
+
files: { [jsonlPath]: content },
|
|
331
|
+
db,
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
const skipLogs = h.logs.filter((l) => l.includes('liveness skip') || l.includes('row not in DB'))
|
|
335
|
+
expect(skipLogs.length).toBeGreaterThan(0)
|
|
336
|
+
|
|
337
|
+
h.watcher.stop()
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it('old behaviour (lookup by agentId directly) misses when IDs differ — confirms the bug', () => {
|
|
341
|
+
// This documents CURRENT (broken) behaviour: when the watcher does
|
|
342
|
+
// SELECT id FROM subagents WHERE id = entry.agentId
|
|
343
|
+
// and entry.agentId is the JSONL stem, it gets null for a row whose PK
|
|
344
|
+
// is a tool_use_id. The "watcher logs skip" test above will PASS once
|
|
345
|
+
// the fix is in because empty-DB is the same result either way. This
|
|
346
|
+
// test is specifically for the ID-mismatch case.
|
|
347
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
348
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
349
|
+
const jsonlStem = 'a37ad7639ae61476c'
|
|
350
|
+
const toolUseId = 'toolu_013uXh6BmWecF3ajx4mPxS6V'
|
|
351
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
352
|
+
const content = buildJSONL(subAgentUserMsg('Work'), subAgentToolUse('Bash', 'x1'))
|
|
353
|
+
|
|
354
|
+
// A DB that only responds to tool_use_id lookups (not jsonl stem lookups)
|
|
355
|
+
// This simulates the pre-fix DB where jsonl_agent_id doesn't exist.
|
|
356
|
+
let byIdLookups = 0
|
|
357
|
+
const db = makeInMemoryDb({})
|
|
358
|
+
const origPrepare = db.prepare.bind(db)
|
|
359
|
+
db.prepare = (sql: string) => {
|
|
360
|
+
const stmt = origPrepare(sql)
|
|
361
|
+
if (/WHERE id\s*=/i.test(sql) && !/jsonl/i.test(sql)) {
|
|
362
|
+
const origGet = stmt.get.bind(stmt)
|
|
363
|
+
stmt.get = (...params: unknown[]) => {
|
|
364
|
+
byIdLookups++
|
|
365
|
+
// Return the row only when the lookup key matches the tool_use_id,
|
|
366
|
+
// not when it's the jsonl stem — this is the broken pre-fix behaviour.
|
|
367
|
+
if (params[0] === toolUseId) {
|
|
368
|
+
return { id: toolUseId, status: 'running' }
|
|
369
|
+
}
|
|
370
|
+
return null // jsonlStem lookup → miss
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return stmt
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const h = makeHarnessWithDb({
|
|
377
|
+
agentDir,
|
|
378
|
+
dirs: {
|
|
379
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
380
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
381
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
382
|
+
},
|
|
383
|
+
files: { [jsonlPath]: content },
|
|
384
|
+
db,
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// With current broken code: lookup by agentId (jsonlStem) → null → skip logged
|
|
388
|
+
// With fixed code: lookup by jsonl_agent_id → found → bump called
|
|
389
|
+
// Either way the entry is registered; what matters is whether bump fires.
|
|
390
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
391
|
+
expect(entry).toBeDefined()
|
|
392
|
+
|
|
393
|
+
h.watcher.stop()
|
|
394
|
+
})
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
// ─── Bug 2 — Background agents marked completed at launch ────────────────────
|
|
398
|
+
|
|
399
|
+
describe('Bug 2 — Background agent transitions to completed via watcher turn_end only', () => {
|
|
400
|
+
beforeEach(() => {
|
|
401
|
+
vi.restoreAllMocks()
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('watcher calls recordSubagentEnd for background agent on turn_end', () => {
|
|
405
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
406
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
407
|
+
const jsonlStem = 'bg-agent-001'
|
|
408
|
+
const toolUseId = 'toolu_bgagent001'
|
|
409
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
410
|
+
|
|
411
|
+
const initialContent = buildJSONL(subAgentUserMsg('Long background task'))
|
|
412
|
+
const finishedContent = initialContent + buildJSONL(subAgentTurnDuration())
|
|
413
|
+
|
|
414
|
+
// DB row for background agent — still running (posttool correctly skipped it)
|
|
415
|
+
const db = makeInMemoryDb({
|
|
416
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running', background: 1 },
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
const h = makeHarnessWithDb({
|
|
420
|
+
agentDir,
|
|
421
|
+
dirs: {
|
|
422
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
423
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
424
|
+
[subagentsDir]: [],
|
|
425
|
+
},
|
|
426
|
+
files: {},
|
|
427
|
+
db,
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
// Write the initial JSONL post-startup so the agent is not historical
|
|
431
|
+
h.fileContents.set(jsonlPath, Buffer.from(initialContent, 'utf-8'))
|
|
432
|
+
h.mockFs.readdirSync = ((p: unknown) => {
|
|
433
|
+
const ps = String(p)
|
|
434
|
+
if (ps === subagentsDir) return [`agent-${jsonlStem}.jsonl`]
|
|
435
|
+
if (ps === `${agentDir}/.claude/projects`) return ['p1']
|
|
436
|
+
if (ps === `${agentDir}/.claude/projects/p1`) return ['session-abc']
|
|
437
|
+
return []
|
|
438
|
+
}) as unknown as typeof fsReal.readdirSync
|
|
439
|
+
h.mockFs.existsSync = ((p: unknown) => {
|
|
440
|
+
const ps = String(p)
|
|
441
|
+
return [
|
|
442
|
+
`${agentDir}/.claude/projects`,
|
|
443
|
+
`${agentDir}/.claude/projects/p1`,
|
|
444
|
+
`${agentDir}/.claude/projects/p1/session-abc`,
|
|
445
|
+
subagentsDir,
|
|
446
|
+
jsonlPath,
|
|
447
|
+
].includes(ps)
|
|
448
|
+
}) as typeof fsReal.existsSync
|
|
449
|
+
|
|
450
|
+
// First poll: register the agent
|
|
451
|
+
h.poll()
|
|
452
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
453
|
+
expect(entry).toBeDefined()
|
|
454
|
+
expect(entry?.state).toBe('running')
|
|
455
|
+
|
|
456
|
+
// Append turn_end
|
|
457
|
+
h.fileContents.set(jsonlPath, Buffer.from(finishedContent, 'utf-8'))
|
|
458
|
+
|
|
459
|
+
// Second poll: watcher sees turn_end → entry.state = 'done'
|
|
460
|
+
// → watcher must call recordSubagentEnd on the DB (UPDATE SET ended_at)
|
|
461
|
+
h.poll()
|
|
462
|
+
|
|
463
|
+
const completedEntry = h.watcher.getRegistry().get(jsonlStem)
|
|
464
|
+
expect(completedEntry?.state).toBe('done')
|
|
465
|
+
|
|
466
|
+
// watcher must have issued UPDATE subagents SET ended_at … WHERE id = toolUseId
|
|
467
|
+
const endCalls = db._calls.filter(
|
|
468
|
+
(c) => /UPDATE subagents[\s\S]*SET[\s\S]*ended_at/i.test(c.sql) && c.params.includes(toolUseId),
|
|
469
|
+
)
|
|
470
|
+
expect(endCalls.length).toBeGreaterThan(0)
|
|
471
|
+
|
|
472
|
+
h.watcher.stop()
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
it('foreground agent still gets completed via DB when watcher sees turn_end (regression)', () => {
|
|
476
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
477
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
478
|
+
const jsonlStem = 'fg-agent-001'
|
|
479
|
+
const toolUseId = 'toolu_fgagent001'
|
|
480
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
481
|
+
|
|
482
|
+
const initialContent = buildJSONL(subAgentUserMsg('Foreground task'))
|
|
483
|
+
const finishedContent = initialContent + buildJSONL(subAgentTurnDuration())
|
|
484
|
+
|
|
485
|
+
const db = makeInMemoryDb({
|
|
486
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running', background: 0 },
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
const h = makeHarnessWithDb({
|
|
490
|
+
agentDir,
|
|
491
|
+
dirs: {
|
|
492
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
493
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
494
|
+
[subagentsDir]: [],
|
|
495
|
+
},
|
|
496
|
+
files: {},
|
|
497
|
+
db,
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
h.fileContents.set(jsonlPath, Buffer.from(initialContent, 'utf-8'))
|
|
501
|
+
h.mockFs.readdirSync = ((p: unknown) => {
|
|
502
|
+
const ps = String(p)
|
|
503
|
+
if (ps === subagentsDir) return [`agent-${jsonlStem}.jsonl`]
|
|
504
|
+
if (ps === `${agentDir}/.claude/projects`) return ['p1']
|
|
505
|
+
if (ps === `${agentDir}/.claude/projects/p1`) return ['session-abc']
|
|
506
|
+
return []
|
|
507
|
+
}) as unknown as typeof fsReal.readdirSync
|
|
508
|
+
h.mockFs.existsSync = ((p: unknown) => {
|
|
509
|
+
const ps = String(p)
|
|
510
|
+
return [
|
|
511
|
+
`${agentDir}/.claude/projects`,
|
|
512
|
+
`${agentDir}/.claude/projects/p1`,
|
|
513
|
+
`${agentDir}/.claude/projects/p1/session-abc`,
|
|
514
|
+
subagentsDir,
|
|
515
|
+
jsonlPath,
|
|
516
|
+
].includes(ps)
|
|
517
|
+
}) as typeof fsReal.existsSync
|
|
518
|
+
|
|
519
|
+
h.poll()
|
|
520
|
+
h.fileContents.set(jsonlPath, Buffer.from(finishedContent, 'utf-8'))
|
|
521
|
+
h.poll()
|
|
522
|
+
|
|
523
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
524
|
+
expect(entry?.state).toBe('done')
|
|
525
|
+
|
|
526
|
+
// For foreground agents the PostToolUse hook fires first (marking completed),
|
|
527
|
+
// so the row may already be completed by the time the watcher sees turn_end.
|
|
528
|
+
// The watcher's recordSubagentEnd is idempotent — either way the row ends up completed.
|
|
529
|
+
// Just verify the watcher called it and didn't error.
|
|
530
|
+
// (The hook-level test is in the bun test suite.)
|
|
531
|
+
h.watcher.stop()
|
|
532
|
+
})
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
// ─── Bug 3 — No stalled-row sweeper ─────────────────────────────────────────
|
|
536
|
+
|
|
537
|
+
describe('Bug 3 — stalled-row sweeper: watcher must call recordSubagentStall in DB', () => {
|
|
538
|
+
beforeEach(() => {
|
|
539
|
+
vi.restoreAllMocks()
|
|
540
|
+
})
|
|
541
|
+
|
|
542
|
+
it('calls UPDATE subagents SET status=stalled for a stale running DB row', () => {
|
|
543
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
544
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
545
|
+
const jsonlStem = 'stale-agent-001'
|
|
546
|
+
const toolUseId = 'toolu_stale001'
|
|
547
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
548
|
+
const content = buildJSONL(subAgentUserMsg('Task that stalls'))
|
|
549
|
+
|
|
550
|
+
const db = makeInMemoryDb({
|
|
551
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
const h = makeHarnessWithDb({
|
|
555
|
+
agentDir,
|
|
556
|
+
dirs: {
|
|
557
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
558
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
559
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
560
|
+
},
|
|
561
|
+
files: { [jsonlPath]: content },
|
|
562
|
+
stallThresholdMs: 60_000,
|
|
563
|
+
db,
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
// Flip historical flag so stall detection fires
|
|
567
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
568
|
+
if (entry) entry.historical = false
|
|
569
|
+
|
|
570
|
+
// Advance past stall threshold
|
|
571
|
+
h.advance(65_000)
|
|
572
|
+
|
|
573
|
+
// watcher must have issued UPDATE subagents SET status='stalled'
|
|
574
|
+
const stallDbCalls = db._calls.filter(
|
|
575
|
+
(c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
|
|
576
|
+
)
|
|
577
|
+
expect(stallDbCalls.length).toBeGreaterThan(0)
|
|
578
|
+
|
|
579
|
+
h.watcher.stop()
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
it('does not double-write stall (stallNotified guards idempotency)', () => {
|
|
583
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
584
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
585
|
+
const jsonlStem = 'already-stalled'
|
|
586
|
+
const toolUseId = 'toolu_already001'
|
|
587
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
588
|
+
const content = buildJSONL(subAgentUserMsg('Stalled task'))
|
|
589
|
+
|
|
590
|
+
const db = makeInMemoryDb({
|
|
591
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
const h = makeHarnessWithDb({
|
|
595
|
+
agentDir,
|
|
596
|
+
dirs: {
|
|
597
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
598
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
599
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
600
|
+
},
|
|
601
|
+
files: { [jsonlPath]: content },
|
|
602
|
+
stallThresholdMs: 60_000,
|
|
603
|
+
db,
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
const entry = h.watcher.getRegistry().get(jsonlStem)
|
|
607
|
+
if (entry) entry.historical = false
|
|
608
|
+
|
|
609
|
+
h.advance(65_000)
|
|
610
|
+
const firstCount = db._calls.filter(
|
|
611
|
+
(c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
|
|
612
|
+
).length
|
|
613
|
+
expect(firstCount).toBeGreaterThan(0)
|
|
614
|
+
|
|
615
|
+
h.advance(65_000) // should NOT fire again
|
|
616
|
+
const secondCount = db._calls.filter(
|
|
617
|
+
(c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
|
|
618
|
+
).length
|
|
619
|
+
expect(secondCount).toBe(firstCount)
|
|
620
|
+
|
|
621
|
+
h.watcher.stop()
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
it('does not call stall for historical entries (pre-existing at boot)', () => {
|
|
625
|
+
const agentDir = '/home/user/.switchroom/agents/myagent'
|
|
626
|
+
const subagentsDir = `${agentDir}/.claude/projects/p1/session-abc/subagents`
|
|
627
|
+
const jsonlStem = 'hist-agent'
|
|
628
|
+
const toolUseId = 'toolu_hist001'
|
|
629
|
+
const jsonlPath = `${subagentsDir}/agent-${jsonlStem}.jsonl`
|
|
630
|
+
const content = buildJSONL(subAgentUserMsg('Old task'))
|
|
631
|
+
|
|
632
|
+
const db = makeInMemoryDb({
|
|
633
|
+
[toolUseId]: { id: toolUseId, jsonl_agent_id: jsonlStem, status: 'running' },
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
const h = makeHarnessWithDb({
|
|
637
|
+
agentDir,
|
|
638
|
+
dirs: {
|
|
639
|
+
[`${agentDir}/.claude/projects`]: ['p1'],
|
|
640
|
+
[`${agentDir}/.claude/projects/p1`]: ['session-abc'],
|
|
641
|
+
[subagentsDir]: [`agent-${jsonlStem}.jsonl`],
|
|
642
|
+
},
|
|
643
|
+
files: { [jsonlPath]: content },
|
|
644
|
+
stallThresholdMs: 60_000,
|
|
645
|
+
db,
|
|
646
|
+
})
|
|
647
|
+
|
|
648
|
+
// Do NOT flip historical — entry is historical by default (file at boot)
|
|
649
|
+
h.advance(65_000)
|
|
650
|
+
|
|
651
|
+
const stallDbCalls = db._calls.filter(
|
|
652
|
+
(c) => /UPDATE subagents[\s\S]*SET status[\s\S]*stalled/i.test(c.sql),
|
|
653
|
+
)
|
|
654
|
+
// Historical entries must NOT trigger stall writes to DB
|
|
655
|
+
expect(stallDbCalls).toHaveLength(0)
|
|
656
|
+
|
|
657
|
+
h.watcher.stop()
|
|
658
|
+
})
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
// ─── result_summary always NULL — extractResultSummary ───────────────────────
|
|
662
|
+
|
|
663
|
+
describe('result_summary extractResultSummary — Claude Code content[] wrapping', () => {
|
|
664
|
+
/**
|
|
665
|
+
* Claude Code wraps Agent() output in { content: [{ type: 'text', text }] }.
|
|
666
|
+
* The posttool's extractResultSummary only checks .result and .output, so
|
|
667
|
+
* result_summary is always NULL.
|
|
668
|
+
*/
|
|
669
|
+
|
|
670
|
+
it('current extractResultSummary returns null for content-array responses (confirms bug)', () => {
|
|
671
|
+
const toolResponse = {
|
|
672
|
+
content: [{ type: 'text', text: 'Task completed. 3 files modified.' }],
|
|
673
|
+
}
|
|
674
|
+
// Current (broken) implementation
|
|
675
|
+
const brokenExtract = (resp: unknown): string | null => {
|
|
676
|
+
if (!resp) return null
|
|
677
|
+
const r = resp as Record<string, unknown>
|
|
678
|
+
const raw = r['result'] ?? r['output'] ?? (typeof resp === 'string' ? resp : null)
|
|
679
|
+
if (raw == null) return null
|
|
680
|
+
const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
|
|
681
|
+
return str.slice(0, 200) || null
|
|
682
|
+
}
|
|
683
|
+
expect(brokenExtract(toolResponse)).toBeNull()
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
it('fixed extractResultSummary handles content[0].text', () => {
|
|
687
|
+
const toolResponse = {
|
|
688
|
+
content: [{ type: 'text', text: 'Task completed. 3 files modified.' }],
|
|
689
|
+
}
|
|
690
|
+
const fixedExtract = (resp: unknown): string | null => {
|
|
691
|
+
if (!resp) return null
|
|
692
|
+
const r = resp as Record<string, unknown>
|
|
693
|
+
const raw =
|
|
694
|
+
r['result'] ??
|
|
695
|
+
r['output'] ??
|
|
696
|
+
(Array.isArray(r['content']) && r['content'].length > 0
|
|
697
|
+
? (r['content'][0] as Record<string, unknown>)['text']
|
|
698
|
+
: null) ??
|
|
699
|
+
(typeof resp === 'string' ? resp : null)
|
|
700
|
+
if (raw == null) return null
|
|
701
|
+
const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
|
|
702
|
+
return str.slice(0, 200) || null
|
|
703
|
+
}
|
|
704
|
+
expect(fixedExtract(toolResponse)).toBe('Task completed. 3 files modified.')
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
it('fixed extractResultSummary still works when result field is present', () => {
|
|
708
|
+
const toolResponse = { result: 'Direct result string.' }
|
|
709
|
+
const fixedExtract = (resp: unknown): string | null => {
|
|
710
|
+
if (!resp) return null
|
|
711
|
+
const r = resp as Record<string, unknown>
|
|
712
|
+
const raw =
|
|
713
|
+
r['result'] ??
|
|
714
|
+
r['output'] ??
|
|
715
|
+
(Array.isArray(r['content']) && r['content'].length > 0
|
|
716
|
+
? (r['content'][0] as Record<string, unknown>)['text']
|
|
717
|
+
: null) ??
|
|
718
|
+
(typeof resp === 'string' ? resp : null)
|
|
719
|
+
if (raw == null) return null
|
|
720
|
+
const str = typeof raw === 'string' ? raw : JSON.stringify(raw)
|
|
721
|
+
return str.slice(0, 200) || null
|
|
722
|
+
}
|
|
723
|
+
expect(fixedExtract(toolResponse)).toBe('Direct result string.')
|
|
724
|
+
})
|
|
725
|
+
})
|