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.
Files changed (718) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +447 -0
  3. package/bin/autoaccept.exp +81 -0
  4. package/bin/boot-self-test.sh +149 -0
  5. package/bin/bridge-watchdog.sh +967 -0
  6. package/bin/handoff-briefing.sh +206 -0
  7. package/bin/run-hook.sh +228 -0
  8. package/bin/switchroom.ts +4 -0
  9. package/bin/timezone-hook.sh +67 -0
  10. package/bin/user-profile-refresh-hook.sh +38 -0
  11. package/bin/workspace-dynamic-hook.sh +142 -0
  12. package/bin/workspace-stable-hook.sh +57 -0
  13. package/dist/cli/autoaccept-poll.js +118 -0
  14. package/dist/cli/switchroom.js +48557 -0
  15. package/package.json +95 -0
  16. package/profiles/_base/settings.json.hbs +15 -0
  17. package/profiles/_base/start.sh.hbs +383 -0
  18. package/profiles/_shared/telegram-style.md.hbs +140 -0
  19. package/profiles/coding/CLAUDE.md.hbs +57 -0
  20. package/profiles/coding/skills/architecture/SKILL.md +70 -0
  21. package/profiles/coding/skills/code-review/SKILL.md +58 -0
  22. package/profiles/coding/workspace/SOUL.md.hbs +25 -0
  23. package/profiles/default/CLAUDE.md +238 -0
  24. package/profiles/default/CLAUDE.md.hbs +113 -0
  25. package/profiles/default/workspace/CLAUDE.md.hbs +126 -0
  26. package/profiles/default/workspace/HEARTBEAT.md.hbs +40 -0
  27. package/profiles/default/workspace/IDENTITY.md.hbs +32 -0
  28. package/profiles/default/workspace/MEMORY.md.hbs +29 -0
  29. package/profiles/default/workspace/SOUL.md.hbs +61 -0
  30. package/profiles/default/workspace/TOOLS.md.hbs +29 -0
  31. package/profiles/default/workspace/USER.md.hbs +52 -0
  32. package/profiles/default/workspace/memory/.gitkeep +0 -0
  33. package/profiles/executive-assistant/CLAUDE.md.hbs +51 -0
  34. package/profiles/executive-assistant/skills/daily-briefing/SKILL.md +55 -0
  35. package/profiles/executive-assistant/skills/meeting-prep/SKILL.md +58 -0
  36. package/profiles/executive-assistant/workspace/SOUL.md.hbs +25 -0
  37. package/profiles/health-coach/CLAUDE.md.hbs +45 -0
  38. package/profiles/health-coach/skills/check-in/SKILL.md +41 -0
  39. package/profiles/health-coach/skills/weekly-review/SKILL.md +53 -0
  40. package/profiles/health-coach/workspace/SOUL.md.hbs +25 -0
  41. package/skills/buildkite-agent-infrastructure/SKILL.md +302 -0
  42. package/skills/buildkite-agent-infrastructure/agents/openai.yaml +6 -0
  43. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
  44. package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
  45. package/skills/buildkite-agent-infrastructure/references/audit-logging.md +87 -0
  46. package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +690 -0
  47. package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +38 -0
  48. package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +73 -0
  49. package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +137 -0
  50. package/skills/buildkite-agent-infrastructure/references/sso-saml.md +92 -0
  51. package/skills/buildkite-agent-runtime/SKILL.md +476 -0
  52. package/skills/buildkite-agent-runtime/agents/openai.yaml +6 -0
  53. package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
  54. package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
  55. package/skills/buildkite-agent-runtime/references/flag-reference.md +417 -0
  56. package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +555 -0
  57. package/skills/buildkite-api/SKILL.md +285 -0
  58. package/skills/buildkite-api/agents/openai.yaml +6 -0
  59. package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
  60. package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
  61. package/skills/buildkite-api/references/graphql-reference.md +195 -0
  62. package/skills/buildkite-api/references/patterns.md +44 -0
  63. package/skills/buildkite-api/references/webhooks.md +161 -0
  64. package/skills/buildkite-cli/SKILL.md +379 -0
  65. package/skills/buildkite-cli/agents/openai.yaml +6 -0
  66. package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
  67. package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
  68. package/skills/buildkite-cli/references/command-reference.md +181 -0
  69. package/skills/buildkite-migration/SKILL.md +182 -0
  70. package/skills/buildkite-pipelines/SKILL.md +464 -0
  71. package/skills/buildkite-pipelines/agents/openai.yaml +6 -0
  72. package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
  73. package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
  74. package/skills/buildkite-pipelines/examples/basic-pipeline.yml +24 -0
  75. package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +100 -0
  76. package/skills/buildkite-pipelines/references/advanced-patterns.md +286 -0
  77. package/skills/buildkite-pipelines/references/retry-and-error-codes.md +131 -0
  78. package/skills/buildkite-pipelines/references/step-types-reference.md +225 -0
  79. package/skills/buildkite-secure-delivery/SKILL.md +168 -0
  80. package/skills/buildkite-secure-delivery/agents/openai.yaml +6 -0
  81. package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
  82. package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
  83. package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +83 -0
  84. package/skills/buildkite-secure-delivery/references/package-publishing.md +100 -0
  85. package/skills/buildkite-test-engine/SKILL.md +239 -0
  86. package/skills/buildkite-test-engine/agents/openai.yaml +6 -0
  87. package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
  88. package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
  89. package/skills/buildkite-test-engine/examples/bktec-splitting.yml +16 -0
  90. package/skills/buildkite-test-engine/examples/collector-pipeline.yml +11 -0
  91. package/skills/buildkite-test-engine/references/collectors.md +198 -0
  92. package/skills/buildkite-test-engine/references/splitting-examples.md +93 -0
  93. package/skills/docx/LICENSE.txt +30 -0
  94. package/skills/docx/SKILL.md +590 -0
  95. package/skills/docx/VENDORED.md +32 -0
  96. package/skills/docx/scripts/__init__.py +1 -0
  97. package/skills/docx/scripts/accept_changes.py +135 -0
  98. package/skills/docx/scripts/comment.py +318 -0
  99. package/skills/docx/scripts/office/helpers/__init__.py +0 -0
  100. package/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  101. package/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  102. package/skills/docx/scripts/office/pack.py +159 -0
  103. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  104. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  105. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  106. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  107. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  108. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  109. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  110. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  111. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  112. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  113. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  114. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  115. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  116. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  117. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  118. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  119. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  120. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  121. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  122. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  123. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  124. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  125. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  126. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  127. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  128. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  129. package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  130. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  131. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  132. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  133. package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  134. package/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  135. package/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  136. package/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  137. package/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  138. package/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  139. package/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  140. package/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  141. package/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  142. package/skills/docx/scripts/office/soffice.py +183 -0
  143. package/skills/docx/scripts/office/unpack.py +132 -0
  144. package/skills/docx/scripts/office/validate.py +111 -0
  145. package/skills/docx/scripts/office/validators/__init__.py +15 -0
  146. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
  147. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
  148. package/skills/docx/scripts/office/validators/base.py +847 -0
  149. package/skills/docx/scripts/office/validators/docx.py +446 -0
  150. package/skills/docx/scripts/office/validators/pptx.py +275 -0
  151. package/skills/docx/scripts/office/validators/redlining.py +247 -0
  152. package/skills/docx/scripts/templates/comments.xml +3 -0
  153. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  154. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  155. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  156. package/skills/docx/scripts/templates/people.xml +3 -0
  157. package/skills/file-bug/SKILL.md +129 -0
  158. package/skills/humanizer/LICENSE +21 -0
  159. package/skills/humanizer/SKILL.md +559 -0
  160. package/skills/humanizer/VENDORED.md +38 -0
  161. package/skills/humanizer-calibrate/SKILL.md +144 -0
  162. package/skills/mcp-builder/LICENSE.txt +202 -0
  163. package/skills/mcp-builder/SKILL.md +236 -0
  164. package/skills/mcp-builder/VENDORED.md +32 -0
  165. package/skills/mcp-builder/reference/evaluation.md +602 -0
  166. package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  167. package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  168. package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  169. package/skills/mcp-builder/scripts/connections.py +151 -0
  170. package/skills/mcp-builder/scripts/evaluation.py +373 -0
  171. package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  172. package/skills/mcp-builder/scripts/requirements.txt +2 -0
  173. package/skills/pdf/LICENSE.txt +30 -0
  174. package/skills/pdf/SKILL.md +314 -0
  175. package/skills/pdf/VENDORED.md +32 -0
  176. package/skills/pdf/forms.md +294 -0
  177. package/skills/pdf/reference.md +612 -0
  178. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  179. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  180. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  181. package/skills/pdf/scripts/create_validation_image.py +37 -0
  182. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  183. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  184. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  185. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  186. package/skills/pptx/LICENSE.txt +30 -0
  187. package/skills/pptx/SKILL.md +232 -0
  188. package/skills/pptx/VENDORED.md +32 -0
  189. package/skills/pptx/editing.md +205 -0
  190. package/skills/pptx/pptxgenjs.md +420 -0
  191. package/skills/pptx/scripts/__init__.py +0 -0
  192. package/skills/pptx/scripts/add_slide.py +195 -0
  193. package/skills/pptx/scripts/clean.py +286 -0
  194. package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  195. package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  196. package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  197. package/skills/pptx/scripts/office/pack.py +159 -0
  198. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  199. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  200. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  201. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  202. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  203. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  204. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  205. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  206. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  207. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  208. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  209. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  210. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  211. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  212. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  213. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  214. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  215. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  216. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  217. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  218. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  219. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  220. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  221. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  222. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  223. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  224. package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  225. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  226. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  227. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  228. package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  229. package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  230. package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  231. package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  232. package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  233. package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  234. package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  235. package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  236. package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  237. package/skills/pptx/scripts/office/soffice.py +183 -0
  238. package/skills/pptx/scripts/office/unpack.py +132 -0
  239. package/skills/pptx/scripts/office/validate.py +111 -0
  240. package/skills/pptx/scripts/office/validators/__init__.py +15 -0
  241. package/skills/pptx/scripts/office/validators/base.py +847 -0
  242. package/skills/pptx/scripts/office/validators/docx.py +446 -0
  243. package/skills/pptx/scripts/office/validators/pptx.py +275 -0
  244. package/skills/pptx/scripts/office/validators/redlining.py +247 -0
  245. package/skills/pptx/scripts/thumbnail.py +289 -0
  246. package/skills/skill-creator/LICENSE.txt +202 -0
  247. package/skills/skill-creator/SKILL.md +485 -0
  248. package/skills/skill-creator/VENDORED.md +32 -0
  249. package/skills/skill-creator/agents/analyzer.md +274 -0
  250. package/skills/skill-creator/agents/comparator.md +202 -0
  251. package/skills/skill-creator/agents/grader.md +223 -0
  252. package/skills/skill-creator/assets/eval_review.html +146 -0
  253. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  254. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  255. package/skills/skill-creator/references/schemas.md +430 -0
  256. package/skills/skill-creator/scripts/__init__.py +0 -0
  257. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  258. package/skills/skill-creator/scripts/generate_report.py +326 -0
  259. package/skills/skill-creator/scripts/improve_description.py +247 -0
  260. package/skills/skill-creator/scripts/package_skill.py +136 -0
  261. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  262. package/skills/skill-creator/scripts/run_eval.py +310 -0
  263. package/skills/skill-creator/scripts/run_loop.py +328 -0
  264. package/skills/skill-creator/scripts/utils.py +47 -0
  265. package/skills/switchroom-architecture/SKILL.md +60 -0
  266. package/skills/switchroom-architecture/cascade.md +112 -0
  267. package/skills/switchroom-architecture/sub-agents.md +87 -0
  268. package/skills/switchroom-architecture/telegram.md +94 -0
  269. package/skills/switchroom-cli/SKILL.md +274 -0
  270. package/skills/switchroom-health/SKILL.md +101 -0
  271. package/skills/switchroom-install/SKILL.md +116 -0
  272. package/skills/switchroom-manage/SKILL.md +90 -0
  273. package/skills/switchroom-status/SKILL.md +69 -0
  274. package/skills/switchroom-status/scripts/status.sh +69 -0
  275. package/skills/telegram-test-harness/SKILL.md +191 -0
  276. package/skills/token-helpers/SKILL.md +73 -0
  277. package/skills/token-helpers/scripts/google-cal-token.sh +62 -0
  278. package/skills/token-helpers/scripts/ms-graph-token.sh +70 -0
  279. package/skills/webapp-testing/LICENSE.txt +202 -0
  280. package/skills/webapp-testing/SKILL.md +96 -0
  281. package/skills/webapp-testing/VENDORED.md +32 -0
  282. package/skills/webapp-testing/examples/console_logging.py +35 -0
  283. package/skills/webapp-testing/examples/element_discovery.py +40 -0
  284. package/skills/webapp-testing/examples/static_html_automation.py +33 -0
  285. package/skills/webapp-testing/scripts/with_server.py +106 -0
  286. package/skills/xlsx/LICENSE.txt +30 -0
  287. package/skills/xlsx/SKILL.md +292 -0
  288. package/skills/xlsx/VENDORED.md +32 -0
  289. package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  290. package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  291. package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  292. package/skills/xlsx/scripts/office/pack.py +159 -0
  293. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  294. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  295. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  296. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  297. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  298. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  299. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  300. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  301. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  302. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  303. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  304. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  305. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  306. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  307. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  308. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  309. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  310. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  311. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  312. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  313. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  314. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  315. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  316. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  317. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  318. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  319. package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  320. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  321. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  322. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  323. package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  324. package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  325. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  326. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  327. package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  328. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  329. package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  330. package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  331. package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  332. package/skills/xlsx/scripts/office/soffice.py +183 -0
  333. package/skills/xlsx/scripts/office/unpack.py +132 -0
  334. package/skills/xlsx/scripts/office/validate.py +111 -0
  335. package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
  336. package/skills/xlsx/scripts/office/validators/base.py +847 -0
  337. package/skills/xlsx/scripts/office/validators/docx.py +446 -0
  338. package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
  339. package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
  340. package/skills/xlsx/scripts/recalc.py +184 -0
  341. package/telegram-plugin/.claude-plugin/plugin.json +20 -0
  342. package/telegram-plugin/.mcp.json +14 -0
  343. package/telegram-plugin/LICENSE +21 -0
  344. package/telegram-plugin/README.md +352 -0
  345. package/telegram-plugin/active-pins-sweep.ts +204 -0
  346. package/telegram-plugin/active-pins.ts +146 -0
  347. package/telegram-plugin/active-reactions-sweep.ts +79 -0
  348. package/telegram-plugin/active-reactions.ts +134 -0
  349. package/telegram-plugin/admin-commands/dispatch.test.ts +149 -0
  350. package/telegram-plugin/admin-commands/index.ts +106 -0
  351. package/telegram-plugin/answer-stream.ts +565 -0
  352. package/telegram-plugin/ask-user.ts +179 -0
  353. package/telegram-plugin/attachment-path.ts +80 -0
  354. package/telegram-plugin/auth-code-redact.ts +83 -0
  355. package/telegram-plugin/auth-dashboard.ts +1104 -0
  356. package/telegram-plugin/auth-slot-parser.ts +497 -0
  357. package/telegram-plugin/auto-fallback-dispatcher.ts +68 -0
  358. package/telegram-plugin/auto-fallback.ts +348 -0
  359. package/telegram-plugin/bridge/bridge.ts +687 -0
  360. package/telegram-plugin/bridge/ipc-client.ts +326 -0
  361. package/telegram-plugin/bun.lock +218 -0
  362. package/telegram-plugin/card-format.ts +62 -0
  363. package/telegram-plugin/channel-envelope-safety.test.ts +56 -0
  364. package/telegram-plugin/channel-envelope-safety.ts +56 -0
  365. package/telegram-plugin/chat-lock.ts +65 -0
  366. package/telegram-plugin/context-exhaustion.ts +38 -0
  367. package/telegram-plugin/credits-watch.ts +220 -0
  368. package/telegram-plugin/dist/bridge/bridge.js +24758 -0
  369. package/telegram-plugin/dist/foreman/foreman.js +30723 -0
  370. package/telegram-plugin/dist/gateway/gateway.js +46497 -0
  371. package/telegram-plugin/dist/server.js +24551 -0
  372. package/telegram-plugin/dm-command-gate.ts +56 -0
  373. package/telegram-plugin/docs/gateway-server-split.md +133 -0
  374. package/telegram-plugin/docs/multi-agent-card-design.md +847 -0
  375. package/telegram-plugin/docs/pinned-progress-card-reliability.md +144 -0
  376. package/telegram-plugin/docs/stream-json-daemon-mode.md +477 -0
  377. package/telegram-plugin/docs/waiting-ux-spec.md +233 -0
  378. package/telegram-plugin/draft-stream.ts +442 -0
  379. package/telegram-plugin/draft-transport.ts +72 -0
  380. package/telegram-plugin/first-paint.ts +246 -0
  381. package/telegram-plugin/fleet-state.ts +246 -0
  382. package/telegram-plugin/foreman/foreman-create-flow.ts +202 -0
  383. package/telegram-plugin/foreman/foreman-handlers.ts +493 -0
  384. package/telegram-plugin/foreman/foreman.ts +1130 -0
  385. package/telegram-plugin/foreman/setup-flow.ts +345 -0
  386. package/telegram-plugin/foreman/setup-state.ts +239 -0
  387. package/telegram-plugin/foreman/state.ts +203 -0
  388. package/telegram-plugin/format.ts +685 -0
  389. package/telegram-plugin/gateway/access-validator.test.ts +95 -0
  390. package/telegram-plugin/gateway/access-validator.ts +37 -0
  391. package/telegram-plugin/gateway/boot-card.ts +582 -0
  392. package/telegram-plugin/gateway/boot-probes.ts +863 -0
  393. package/telegram-plugin/gateway/boot-reason.ts +51 -0
  394. package/telegram-plugin/gateway/boot-sweep-filter.test.ts +54 -0
  395. package/telegram-plugin/gateway/boot-sweep-filter.ts +32 -0
  396. package/telegram-plugin/gateway/clean-shutdown-marker.ts +183 -0
  397. package/telegram-plugin/gateway/disconnect-flush.ts +109 -0
  398. package/telegram-plugin/gateway/gateway.ts +10202 -0
  399. package/telegram-plugin/gateway/inbound-coalesce.ts +147 -0
  400. package/telegram-plugin/gateway/inject-handler.test.ts +221 -0
  401. package/telegram-plugin/gateway/inject-handler.ts +190 -0
  402. package/telegram-plugin/gateway/ipc-protocol.ts +151 -0
  403. package/telegram-plugin/gateway/ipc-server.ts +494 -0
  404. package/telegram-plugin/gateway/pid-file.ts +103 -0
  405. package/telegram-plugin/gateway/poll-health.ts +156 -0
  406. package/telegram-plugin/gateway/preamble-suppressor.ts +154 -0
  407. package/telegram-plugin/gateway/quota-cache.ts +125 -0
  408. package/telegram-plugin/gateway/resolve-calling-subagent.ts +78 -0
  409. package/telegram-plugin/gateway/restart-watchdog.ts +200 -0
  410. package/telegram-plugin/gateway/session-marker.ts +83 -0
  411. package/telegram-plugin/gateway/shutdown-drain.ts +162 -0
  412. package/telegram-plugin/gateway/startup-mutex.ts +285 -0
  413. package/telegram-plugin/gateway/startup-network-retry.ts +142 -0
  414. package/telegram-plugin/gateway/turn-active-marker.ts +176 -0
  415. package/telegram-plugin/gateway/unhandled-rejection-policy.ts +78 -0
  416. package/telegram-plugin/handoff-continuity.ts +200 -0
  417. package/telegram-plugin/history.ts +468 -0
  418. package/telegram-plugin/hooks/hooks.json +58 -0
  419. package/telegram-plugin/hooks/secret-guard-pretool.mjs +208 -0
  420. package/telegram-plugin/hooks/secret-scrub-stop.mjs +98 -0
  421. package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +111 -0
  422. package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +296 -0
  423. package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +261 -0
  424. package/telegram-plugin/html-sanitize.ts +244 -0
  425. package/telegram-plugin/idle-footer.ts +65 -0
  426. package/telegram-plugin/inline-keyboard-callbacks.ts +166 -0
  427. package/telegram-plugin/interrupt-marker.ts +66 -0
  428. package/telegram-plugin/issues-card.ts +371 -0
  429. package/telegram-plugin/issues-watcher.ts +125 -0
  430. package/telegram-plugin/model-unavailable.ts +325 -0
  431. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  432. package/telegram-plugin/operator-events-history.ts +94 -0
  433. package/telegram-plugin/operator-events.fixtures.json +161 -0
  434. package/telegram-plugin/operator-events.ts +421 -0
  435. package/telegram-plugin/package.json +55 -0
  436. package/telegram-plugin/permission-rule.ts +133 -0
  437. package/telegram-plugin/permission-title.ts +117 -0
  438. package/telegram-plugin/pin-event-log.ts +76 -0
  439. package/telegram-plugin/plugin-logger.ts +136 -0
  440. package/telegram-plugin/progress-card-driver.ts +2697 -0
  441. package/telegram-plugin/progress-card-pin-manager.ts +589 -0
  442. package/telegram-plugin/progress-card-pin-watchdog.ts +98 -0
  443. package/telegram-plugin/progress-card.ts +1409 -0
  444. package/telegram-plugin/pty-partial-handler.ts +247 -0
  445. package/telegram-plugin/pty-tail.ts +730 -0
  446. package/telegram-plugin/quota-check.ts +474 -0
  447. package/telegram-plugin/recent-outbound-dedup.ts +169 -0
  448. package/telegram-plugin/registry/api-registry.test.ts +201 -0
  449. package/telegram-plugin/registry/subagents-bugs.test.ts +454 -0
  450. package/telegram-plugin/registry/subagents-schema.ts +509 -0
  451. package/telegram-plugin/registry/subagents.test.ts +476 -0
  452. package/telegram-plugin/registry/turns-schema.test.ts +101 -0
  453. package/telegram-plugin/registry/turns-schema.ts +417 -0
  454. package/telegram-plugin/retry-api-call.ts +172 -0
  455. package/telegram-plugin/scripts/build.mjs +78 -0
  456. package/telegram-plugin/secret-detect/audit.ts +66 -0
  457. package/telegram-plugin/secret-detect/chunker.ts +37 -0
  458. package/telegram-plugin/secret-detect/entropy.ts +20 -0
  459. package/telegram-plugin/secret-detect/gitleaks-loader.ts +74 -0
  460. package/telegram-plugin/secret-detect/gitleaks.toml +27 -0
  461. package/telegram-plugin/secret-detect/index.ts +218 -0
  462. package/telegram-plugin/secret-detect/kv-scanner.ts +60 -0
  463. package/telegram-plugin/secret-detect/mask.ts +13 -0
  464. package/telegram-plugin/secret-detect/patterns.ts +115 -0
  465. package/telegram-plugin/secret-detect/pipeline.ts +144 -0
  466. package/telegram-plugin/secret-detect/rewrite.ts +26 -0
  467. package/telegram-plugin/secret-detect/secretlint-source.ts +95 -0
  468. package/telegram-plugin/secret-detect/slug.ts +44 -0
  469. package/telegram-plugin/secret-detect/staging.ts +85 -0
  470. package/telegram-plugin/secret-detect/suppressor.ts +34 -0
  471. package/telegram-plugin/secret-detect/url-redact.ts +60 -0
  472. package/telegram-plugin/secret-detect/vault-write.ts +56 -0
  473. package/telegram-plugin/server.js +41795 -0
  474. package/telegram-plugin/server.ts +171 -0
  475. package/telegram-plugin/session-tail.ts +884 -0
  476. package/telegram-plugin/shared/bot-runtime.ts +324 -0
  477. package/telegram-plugin/silent-reply.ts +58 -0
  478. package/telegram-plugin/slot-banner-driver.ts +147 -0
  479. package/telegram-plugin/slot-banner.ts +86 -0
  480. package/telegram-plugin/start.js +26 -0
  481. package/telegram-plugin/startup-reset.ts +45 -0
  482. package/telegram-plugin/status-reactions.ts +332 -0
  483. package/telegram-plugin/steering.ts +155 -0
  484. package/telegram-plugin/sticker-aliases.ts +249 -0
  485. package/telegram-plugin/stream-controller.ts +311 -0
  486. package/telegram-plugin/stream-reply-handler.ts +664 -0
  487. package/telegram-plugin/streaming-metrics.ts +134 -0
  488. package/telegram-plugin/streaming-report.ts +204 -0
  489. package/telegram-plugin/subagent-watcher.ts +880 -0
  490. package/telegram-plugin/telegram-button-constraints.ts +191 -0
  491. package/telegram-plugin/telegraph.ts +381 -0
  492. package/telegram-plugin/tests/HARNESS.md +340 -0
  493. package/telegram-plugin/tests/_progress-card-harness.ts +105 -0
  494. package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +211 -0
  495. package/telegram-plugin/tests/active-pins-sweep.test.ts +309 -0
  496. package/telegram-plugin/tests/active-pins.test.ts +187 -0
  497. package/telegram-plugin/tests/active-reactions-sweep.test.ts +116 -0
  498. package/telegram-plugin/tests/active-reactions.test.ts +198 -0
  499. package/telegram-plugin/tests/answer-stream-dedup.test.ts +352 -0
  500. package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +236 -0
  501. package/telegram-plugin/tests/answer-stream.test.ts +878 -0
  502. package/telegram-plugin/tests/ask-user.test.ts +203 -0
  503. package/telegram-plugin/tests/attachment-path.test.ts +199 -0
  504. package/telegram-plugin/tests/auth-account-identity-surface.test.ts +118 -0
  505. package/telegram-plugin/tests/auth-code-auto-capture.test.ts +144 -0
  506. package/telegram-plugin/tests/auth-code-redact.test.ts +248 -0
  507. package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +260 -0
  508. package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +140 -0
  509. package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +559 -0
  510. package/telegram-plugin/tests/auth-dashboard.test.ts +1045 -0
  511. package/telegram-plugin/tests/auth-login-url-button.test.ts +122 -0
  512. package/telegram-plugin/tests/auth-slot-commands.test.ts +640 -0
  513. package/telegram-plugin/tests/auto-fallback-dispatcher.e2e.test.ts +183 -0
  514. package/telegram-plugin/tests/auto-fallback.test.ts +381 -0
  515. package/telegram-plugin/tests/boot-card-account-quota.test.ts +137 -0
  516. package/telegram-plugin/tests/boot-card-dedupe.test.ts +154 -0
  517. package/telegram-plugin/tests/boot-card-probe-target.test.ts +194 -0
  518. package/telegram-plugin/tests/boot-card-reason.test.ts +103 -0
  519. package/telegram-plugin/tests/boot-card-render.test.ts +219 -0
  520. package/telegram-plugin/tests/boot-probes.test.ts +451 -0
  521. package/telegram-plugin/tests/bot-api.harness.ts +116 -0
  522. package/telegram-plugin/tests/bot-runtime.test.ts +190 -0
  523. package/telegram-plugin/tests/bridge-anonymous-refuse.test.ts +60 -0
  524. package/telegram-plugin/tests/context-exhaustion.test.ts +114 -0
  525. package/telegram-plugin/tests/credits-watch.test.ts +221 -0
  526. package/telegram-plugin/tests/dm-command-gate.test.ts +176 -0
  527. package/telegram-plugin/tests/draft-stream.test.ts +752 -0
  528. package/telegram-plugin/tests/draft-transport.test.ts +141 -0
  529. package/telegram-plugin/tests/e2e.test.ts +436 -0
  530. package/telegram-plugin/tests/fake-bot-api.test.ts +213 -0
  531. package/telegram-plugin/tests/fake-bot-api.ts +617 -0
  532. package/telegram-plugin/tests/false-restart-banner.test.ts +253 -0
  533. package/telegram-plugin/tests/first-paint.test.ts +257 -0
  534. package/telegram-plugin/tests/fixtures/pty-tail-tmux-fragment.bin +6 -0
  535. package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +3624 -0
  536. package/telegram-plugin/tests/fleet-state-watcher.test.ts +101 -0
  537. package/telegram-plugin/tests/fleet-state.test.ts +185 -0
  538. package/telegram-plugin/tests/foreman-create-flow.test.ts +359 -0
  539. package/telegram-plugin/tests/foreman-handlers.test.ts +347 -0
  540. package/telegram-plugin/tests/foreman-state.test.ts +164 -0
  541. package/telegram-plugin/tests/foreman-write-ops.test.ts +214 -0
  542. package/telegram-plugin/tests/gateway-409-retry-banner.test.ts +173 -0
  543. package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +72 -0
  544. package/telegram-plugin/tests/gateway-bridge.test.ts +811 -0
  545. package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +414 -0
  546. package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +144 -0
  547. package/telegram-plugin/tests/gateway-message-validator.test.ts +133 -0
  548. package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +103 -0
  549. package/telegram-plugin/tests/gateway-secret-detect.test.ts +127 -0
  550. package/telegram-plugin/tests/gateway-startup-mutex.test.ts +284 -0
  551. package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +185 -0
  552. package/telegram-plugin/tests/gateway-startup-reset.test.ts +72 -0
  553. package/telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts +125 -0
  554. package/telegram-plugin/tests/handoff-continuity.test.ts +249 -0
  555. package/telegram-plugin/tests/harness-ordering-invariants.test.ts +243 -0
  556. package/telegram-plugin/tests/harness-parse-mode-validation.test.ts +114 -0
  557. package/telegram-plugin/tests/history.test.ts +364 -0
  558. package/telegram-plugin/tests/html-balanced.ts +63 -0
  559. package/telegram-plugin/tests/html-sanitize.test.ts +146 -0
  560. package/telegram-plugin/tests/idle-footer-wiring.test.ts +88 -0
  561. package/telegram-plugin/tests/idle-footer.test.ts +66 -0
  562. package/telegram-plugin/tests/inbound-coalesce.test.ts +127 -0
  563. package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +150 -0
  564. package/telegram-plugin/tests/interrupt-marker.test.ts +126 -0
  565. package/telegram-plugin/tests/ipc-protocol.test.ts +218 -0
  566. package/telegram-plugin/tests/ipc-server-anonymous-refuse.test.ts +82 -0
  567. package/telegram-plugin/tests/ipc-server-client.test.ts +323 -0
  568. package/telegram-plugin/tests/ipc-server-race.test.ts +183 -0
  569. package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +91 -0
  570. package/telegram-plugin/tests/ipc-server-validate-pty-partial.test.ts +64 -0
  571. package/telegram-plugin/tests/ipc-server-validate-update-placeholder.test.ts +77 -0
  572. package/telegram-plugin/tests/ipc-validator.test.ts +274 -0
  573. package/telegram-plugin/tests/issues-card.test.ts +495 -0
  574. package/telegram-plugin/tests/issues-watcher.test.ts +165 -0
  575. package/telegram-plugin/tests/model-unavailable.test.ts +303 -0
  576. package/telegram-plugin/tests/multi-turn-continuity.test.ts +159 -0
  577. package/telegram-plugin/tests/operator-events-history.test.ts +125 -0
  578. package/telegram-plugin/tests/operator-events-session-tail.test.ts +192 -0
  579. package/telegram-plugin/tests/operator-events.test.ts +331 -0
  580. package/telegram-plugin/tests/outbound-ordering.test.ts +293 -0
  581. package/telegram-plugin/tests/parse-mode-rotation.test.ts +164 -0
  582. package/telegram-plugin/tests/permission-rule.test.ts +121 -0
  583. package/telegram-plugin/tests/permission-title.test.ts +106 -0
  584. package/telegram-plugin/tests/pin-event-log.test.ts +124 -0
  585. package/telegram-plugin/tests/plugin-logger.test.ts +97 -0
  586. package/telegram-plugin/tests/poll-health.test.ts +86 -0
  587. package/telegram-plugin/tests/preamble-suppressor.test.ts +285 -0
  588. package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +73 -0
  589. package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +272 -0
  590. package/telegram-plugin/tests/progress-card-cross-turn.test.ts +258 -0
  591. package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +81 -0
  592. package/telegram-plugin/tests/progress-card-draft-flag.test.ts +80 -0
  593. package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +215 -0
  594. package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +123 -0
  595. package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +76 -0
  596. package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +62 -0
  597. package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +84 -0
  598. package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +139 -0
  599. package/telegram-plugin/tests/progress-card-pin-manager.test.ts +773 -0
  600. package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +66 -0
  601. package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +64 -0
  602. package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +190 -0
  603. package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +146 -0
  604. package/telegram-plugin/tests/progress-update.test.ts +236 -0
  605. package/telegram-plugin/tests/protocol-fixtures.test.ts +59 -0
  606. package/telegram-plugin/tests/protocol-fixtures.ts +198 -0
  607. package/telegram-plugin/tests/pty-partial-handler.test.ts +326 -0
  608. package/telegram-plugin/tests/pty-tail-real-fixture.test.ts +114 -0
  609. package/telegram-plugin/tests/pty-tail-tmux-fragment.test.ts +71 -0
  610. package/telegram-plugin/tests/pty-tail.test.ts +525 -0
  611. package/telegram-plugin/tests/quota-cache.test.ts +187 -0
  612. package/telegram-plugin/tests/quota-check.test.ts +622 -0
  613. package/telegram-plugin/tests/races.test.ts +842 -0
  614. package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +123 -0
  615. package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +82 -0
  616. package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +114 -0
  617. package/telegram-plugin/tests/real-gateway-harness.ts +699 -0
  618. package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +313 -0
  619. package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +299 -0
  620. package/telegram-plugin/tests/real-gateway-spec.test.ts +487 -0
  621. package/telegram-plugin/tests/real-gateway.smoke.test.ts +101 -0
  622. package/telegram-plugin/tests/recent-outbound-dedup.test.ts +192 -0
  623. package/telegram-plugin/tests/registry-turns.test.ts +432 -0
  624. package/telegram-plugin/tests/reply-terminal-reaction.test.ts +149 -0
  625. package/telegram-plugin/tests/resolve-calling-subagent.test.ts +269 -0
  626. package/telegram-plugin/tests/restart-watchdog.test.ts +224 -0
  627. package/telegram-plugin/tests/retry-api-call.test.ts +287 -0
  628. package/telegram-plugin/tests/secret-detect-audit.test.ts +58 -0
  629. package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +83 -0
  630. package/telegram-plugin/tests/secret-detect-gitleaks.test.ts +32 -0
  631. package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +308 -0
  632. package/telegram-plugin/tests/secret-detect-pipeline.test.ts +123 -0
  633. package/telegram-plugin/tests/secret-detect-secretlint.test.ts +101 -0
  634. package/telegram-plugin/tests/secret-detect-staging.test.ts +45 -0
  635. package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +67 -0
  636. package/telegram-plugin/tests/secret-detect.test.ts +223 -0
  637. package/telegram-plugin/tests/secret-guard-pretool.test.ts +194 -0
  638. package/telegram-plugin/tests/send-typing-action-validation.test.ts +61 -0
  639. package/telegram-plugin/tests/session-tail-capped.test.ts +285 -0
  640. package/telegram-plugin/tests/session-tail.test.ts +524 -0
  641. package/telegram-plugin/tests/setup-flow.test.ts +510 -0
  642. package/telegram-plugin/tests/setup-state.test.ts +146 -0
  643. package/telegram-plugin/tests/silent-reply-guard.test.ts +122 -0
  644. package/telegram-plugin/tests/slot-banner-driver.e2e.test.ts +350 -0
  645. package/telegram-plugin/tests/slot-banner.test.ts +74 -0
  646. package/telegram-plugin/tests/snapshot-serializer.ts +79 -0
  647. package/telegram-plugin/tests/spawn-detached-cgroup-escape.test.ts +51 -0
  648. package/telegram-plugin/tests/status-accent.test.ts +186 -0
  649. package/telegram-plugin/tests/status-reactions-allowed-filter.test.ts +132 -0
  650. package/telegram-plugin/tests/status-reactions.test.ts +314 -0
  651. package/telegram-plugin/tests/steering.test.ts +282 -0
  652. package/telegram-plugin/tests/sticker-aliases.test.ts +232 -0
  653. package/telegram-plugin/tests/stream-controller-html-fallback.test.ts +127 -0
  654. package/telegram-plugin/tests/stream-controller.test.ts +262 -0
  655. package/telegram-plugin/tests/stream-reply-error-paths.test.ts +208 -0
  656. package/telegram-plugin/tests/stream-reply-handler.test.ts +1292 -0
  657. package/telegram-plugin/tests/streaming-e2e.test.ts +389 -0
  658. package/telegram-plugin/tests/streaming-metrics.test.ts +201 -0
  659. package/telegram-plugin/tests/streaming-orchestration.test.ts +756 -0
  660. package/telegram-plugin/tests/subagent-registry-bugs.test.ts +725 -0
  661. package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +213 -0
  662. package/telegram-plugin/tests/subagent-watcher-parent-marker.test.ts +274 -0
  663. package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +243 -0
  664. package/telegram-plugin/tests/subagent-watcher.test.ts +877 -0
  665. package/telegram-plugin/tests/subagents-schema-init-order.test.ts +109 -0
  666. package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +116 -0
  667. package/telegram-plugin/tests/telegram-button-constraints.test.ts +194 -0
  668. package/telegram-plugin/tests/telegram-format.test.ts +1093 -0
  669. package/telegram-plugin/tests/telegraph.test.ts +246 -0
  670. package/telegram-plugin/tests/tool-labels.test.ts +383 -0
  671. package/telegram-plugin/tests/turn-active-marker.test.ts +195 -0
  672. package/telegram-plugin/tests/turn-end-regressions.test.ts +489 -0
  673. package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +218 -0
  674. package/telegram-plugin/tests/turn-flush-dedup-controller.test.ts +144 -0
  675. package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +78 -0
  676. package/telegram-plugin/tests/turn-flush-safety.test.ts +189 -0
  677. package/telegram-plugin/tests/turn-signal-tracker.test.ts +107 -0
  678. package/telegram-plugin/tests/turns-writer.test.ts +323 -0
  679. package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +131 -0
  680. package/telegram-plugin/tests/two-zone-bg-detection.test.ts +120 -0
  681. package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +114 -0
  682. package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +87 -0
  683. package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +211 -0
  684. package/telegram-plugin/tests/two-zone-card-cap.test.ts +62 -0
  685. package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +101 -0
  686. package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +68 -0
  687. package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +110 -0
  688. package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +128 -0
  689. package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +58 -0
  690. package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +133 -0
  691. package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +155 -0
  692. package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +117 -0
  693. package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +143 -0
  694. package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +149 -0
  695. package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +101 -0
  696. package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +114 -0
  697. package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +105 -0
  698. package/telegram-plugin/tests/typing-wrap.test.ts +141 -0
  699. package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +147 -0
  700. package/telegram-plugin/tests/update-factory-edited-and-reactions.test.ts +108 -0
  701. package/telegram-plugin/tests/update-factory.ts +305 -0
  702. package/telegram-plugin/tests/vault-grant-wizard.test.ts +84 -0
  703. package/telegram-plugin/tests/vault-grants-revoke.test.ts +265 -0
  704. package/telegram-plugin/tests/vault-subcommands.test.ts +234 -0
  705. package/telegram-plugin/tests/voice-transcribe.test.ts +196 -0
  706. package/telegram-plugin/tests/waiting-ux-harness.ts +381 -0
  707. package/telegram-plugin/tests/waiting-ux.e2e.test.ts +233 -0
  708. package/telegram-plugin/tests/welcome-text.test.ts +407 -0
  709. package/telegram-plugin/tool-error-filter.ts +89 -0
  710. package/telegram-plugin/tool-labels.ts +330 -0
  711. package/telegram-plugin/tool-names.ts +53 -0
  712. package/telegram-plugin/turn-flush-prose-recovery.ts +40 -0
  713. package/telegram-plugin/turn-flush-safety.ts +109 -0
  714. package/telegram-plugin/turn-signal-tracker.ts +79 -0
  715. package/telegram-plugin/two-zone-card.ts +249 -0
  716. package/telegram-plugin/typing-wrap.ts +92 -0
  717. package/telegram-plugin/voice-transcribe.ts +166 -0
  718. package/telegram-plugin/welcome-text.ts +359 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Unit tests for the turn-active marker (#412 fix). The marker file
3
+ * exists exactly during in-flight turns; the bash watchdog reads its
4
+ * mtime to distinguish "wedged mid-turn" from "healthy idle".
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
8
+ import { mkdtempSync, rmSync, existsSync, readFileSync, statSync, utimesSync } from 'node:fs'
9
+ import { tmpdir } from 'node:os'
10
+ import { join } from 'node:path'
11
+ import {
12
+ TURN_ACTIVE_MARKER_FILE,
13
+ writeTurnActiveMarker,
14
+ touchTurnActiveMarker,
15
+ removeTurnActiveMarker,
16
+ sweepStaleTurnActiveMarker,
17
+ } from '../gateway/turn-active-marker.js'
18
+
19
+ describe('turn-active-marker (#412)', () => {
20
+ let tmp: string
21
+
22
+ beforeEach(() => {
23
+ tmp = mkdtempSync(join(tmpdir(), 'turn-active-'))
24
+ })
25
+
26
+ afterEach(() => {
27
+ rmSync(tmp, { recursive: true, force: true })
28
+ })
29
+
30
+ it('writeTurnActiveMarker creates a JSON file with the expected payload', () => {
31
+ writeTurnActiveMarker(tmp, {
32
+ turnKey: 'chat:1:1700000000000',
33
+ chatId: 'chat',
34
+ threadId: null,
35
+ startedAt: 1700000000000,
36
+ })
37
+ const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
38
+ expect(existsSync(path)).toBe(true)
39
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'))
40
+ expect(parsed.turnKey).toBe('chat:1:1700000000000')
41
+ expect(parsed.chatId).toBe('chat')
42
+ expect(parsed.startedAt).toBe(1700000000000)
43
+ })
44
+
45
+ it('writeTurnActiveMarker is idempotent (overwrites existing)', () => {
46
+ writeTurnActiveMarker(tmp, {
47
+ turnKey: 'k1',
48
+ chatId: 'c',
49
+ threadId: null,
50
+ startedAt: 1,
51
+ })
52
+ writeTurnActiveMarker(tmp, {
53
+ turnKey: 'k2',
54
+ chatId: 'c',
55
+ threadId: null,
56
+ startedAt: 2,
57
+ })
58
+ const parsed = JSON.parse(readFileSync(join(tmp, TURN_ACTIVE_MARKER_FILE), 'utf-8'))
59
+ expect(parsed.turnKey).toBe('k2')
60
+ })
61
+
62
+ it('touchTurnActiveMarker bumps the mtime', async () => {
63
+ writeTurnActiveMarker(tmp, {
64
+ turnKey: 'k',
65
+ chatId: 'c',
66
+ threadId: null,
67
+ startedAt: 1,
68
+ })
69
+ const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
70
+ // Force the mtime to 5 minutes in the past so the touch is visible.
71
+ const past = new Date(Date.now() - 5 * 60 * 1000)
72
+ utimesSync(path, past, past)
73
+ const before = statSync(path).mtimeMs
74
+
75
+ touchTurnActiveMarker(tmp)
76
+ const after = statSync(path).mtimeMs
77
+ expect(after).toBeGreaterThan(before)
78
+ })
79
+
80
+ it('touchTurnActiveMarker is a no-op when no marker exists', () => {
81
+ // Must not throw, must not create the file.
82
+ touchTurnActiveMarker(tmp)
83
+ expect(existsSync(join(tmp, TURN_ACTIVE_MARKER_FILE))).toBe(false)
84
+ })
85
+
86
+ it('removeTurnActiveMarker deletes the file', () => {
87
+ writeTurnActiveMarker(tmp, {
88
+ turnKey: 'k',
89
+ chatId: 'c',
90
+ threadId: null,
91
+ startedAt: 1,
92
+ })
93
+ const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
94
+ expect(existsSync(path)).toBe(true)
95
+ removeTurnActiveMarker(tmp)
96
+ expect(existsSync(path)).toBe(false)
97
+ })
98
+
99
+ it('removeTurnActiveMarker is idempotent (no throw on missing file)', () => {
100
+ expect(() => removeTurnActiveMarker(tmp)).not.toThrow()
101
+ expect(() => removeTurnActiveMarker(tmp)).not.toThrow()
102
+ })
103
+
104
+ it('writeTurnActiveMarker creates the state dir if missing', () => {
105
+ const fresh = join(tmp, 'fresh', 'subdir')
106
+ writeTurnActiveMarker(fresh, {
107
+ turnKey: 'k',
108
+ chatId: 'c',
109
+ threadId: null,
110
+ startedAt: 1,
111
+ })
112
+ expect(existsSync(join(fresh, TURN_ACTIVE_MARKER_FILE))).toBe(true)
113
+ })
114
+
115
+ // #550: defence-in-depth sweeper for orphaned markers.
116
+ describe('sweepStaleTurnActiveMarker (#550)', () => {
117
+ const writeWithMtime = (ageMs: number) => {
118
+ writeTurnActiveMarker(tmp, {
119
+ turnKey: 'k',
120
+ chatId: 'c',
121
+ threadId: null,
122
+ startedAt: 1,
123
+ })
124
+ const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
125
+ const past = new Date(Date.now() - ageMs)
126
+ utimesSync(path, past, past)
127
+ return path
128
+ }
129
+
130
+ it('returns false when no marker file exists', () => {
131
+ const swept = sweepStaleTurnActiveMarker(tmp, {
132
+ turnInFlight: false,
133
+ idleSweepMs: 60_000,
134
+ hardTtlMs: 600_000,
135
+ })
136
+ expect(swept).toBe(false)
137
+ })
138
+
139
+ it('removes a stale marker when no turn is in flight (idle path)', () => {
140
+ const path = writeWithMtime(120_000) // 2 min old
141
+ const swept = sweepStaleTurnActiveMarker(tmp, {
142
+ turnInFlight: false,
143
+ idleSweepMs: 60_000,
144
+ hardTtlMs: 600_000,
145
+ })
146
+ expect(swept).toBe(true)
147
+ expect(existsSync(path)).toBe(false)
148
+ })
149
+
150
+ it('does NOT remove a fresh marker even when no turn is in flight', () => {
151
+ const path = writeWithMtime(5_000) // 5s old
152
+ const swept = sweepStaleTurnActiveMarker(tmp, {
153
+ turnInFlight: false,
154
+ idleSweepMs: 60_000,
155
+ hardTtlMs: 600_000,
156
+ })
157
+ expect(swept).toBe(false)
158
+ expect(existsSync(path)).toBe(true)
159
+ })
160
+
161
+ it('does NOT remove a recent marker while a turn is in flight (idle bound)', () => {
162
+ const path = writeWithMtime(120_000) // 2 min old
163
+ const swept = sweepStaleTurnActiveMarker(tmp, {
164
+ turnInFlight: true,
165
+ idleSweepMs: 60_000,
166
+ hardTtlMs: 600_000,
167
+ })
168
+ expect(swept).toBe(false)
169
+ expect(existsSync(path)).toBe(true)
170
+ })
171
+
172
+ it('removes a marker past hardTtl even if a turn claims to be in flight', () => {
173
+ const path = writeWithMtime(20 * 60_000) // 20 min old
174
+ const swept = sweepStaleTurnActiveMarker(tmp, {
175
+ turnInFlight: true,
176
+ idleSweepMs: 60_000,
177
+ hardTtlMs: 10 * 60_000,
178
+ })
179
+ expect(swept).toBe(true)
180
+ expect(existsSync(path)).toBe(false)
181
+ })
182
+ })
183
+
184
+ it('writes mode 0600 (operator-only readable)', () => {
185
+ writeTurnActiveMarker(tmp, {
186
+ turnKey: 'k',
187
+ chatId: 'c',
188
+ threadId: null,
189
+ startedAt: 1,
190
+ })
191
+ const path = join(tmp, TURN_ACTIVE_MARKER_FILE)
192
+ const mode = statSync(path).mode & 0o777
193
+ expect(mode).toBe(0o600)
194
+ })
195
+ })
@@ -0,0 +1,489 @@
1
+ /**
2
+ * Regression tests for two bugs reported 2026-04-13 / 2026-04-14:
3
+ *
4
+ * Bug 1 (TG-DONE): the progress-card message stays stuck on
5
+ * "⚙️ Working…" after a turn completes. Root cause: session-tail
6
+ * onEvent ran `handleSessionEvent` (which calls `closeProgressLane`
7
+ * and deletes + finalizes the progress-lane stream) BEFORE
8
+ * `progressDriver.ingest` (which synchronously emits the final
9
+ * "Done" render via handleStreamReply). By the time the driver
10
+ * tried to edit the existing stream, it was already gone.
11
+ *
12
+ * Bug 2 (TG-IDLE-LEAK): bare text emitted by the model after
13
+ * `stream_reply(done=true)` (e.g. "Idle; awaiting next instruction.")
14
+ * leaks as a separate Telegram message. Root cause: server.ts
15
+ * identified its own MCP tools by exact-prefix match against
16
+ * `mcp__switchroom-telegram__…`, but Claude Code prefixes tool
17
+ * names with whatever registration key the host .mcp.json used.
18
+ * Existing agents still register as `clerk-telegram`, so no
19
+ * `reply`-family tool call ever set `currentTurnReplyCalled` and
20
+ * the orphaned-reply backstop fired on every post-reply idle text.
21
+ */
22
+
23
+ import { describe, it, expect } from 'vitest'
24
+ import { readFileSync } from 'fs'
25
+ import { join, dirname } from 'path'
26
+ import { fileURLToPath } from 'url'
27
+ import { createProgressDriver } from '../progress-card-driver.js'
28
+ import type { SessionEvent } from '../session-tail.js'
29
+ import { isTelegramReplyTool, isTelegramSurfaceTool } from '../tool-names.js'
30
+
31
+ const __dirname = dirname(fileURLToPath(import.meta.url))
32
+
33
+ // ─── Bug 1 integration harness ────────────────────────────────────────────
34
+ //
35
+ // Simulates the server.ts wiring: one session-tail event callback that
36
+ // fans out to (a) progressDriver.ingest and (b) a lightweight stand-in
37
+ // for handleSessionEvent's closeProgressLane. The real bug showed up as
38
+ // an ordering race between these two consumers, so the harness exercises
39
+ // both orders and asserts which one delivers the Done render.
40
+
41
+ interface FakeStream {
42
+ updates: string[]
43
+ finalized: boolean
44
+ finalize(): void
45
+ update(text: string): void
46
+ }
47
+
48
+ function makeStream(): FakeStream {
49
+ const s: FakeStream = {
50
+ updates: [],
51
+ finalized: false,
52
+ finalize() {
53
+ this.finalized = true
54
+ },
55
+ update(text: string) {
56
+ if (this.finalized) return
57
+ this.updates.push(text)
58
+ },
59
+ }
60
+ return s
61
+ }
62
+
63
+ function wireServer(order: 'driver-first' | 'handler-first') {
64
+ const streams = new Map<string, FakeStream>()
65
+ const allStreams: FakeStream[] = [] // never forgets — includes deleted ones
66
+ const events: SessionEvent[] = []
67
+
68
+ // Stand-in for handleStreamReply's progress-lane emit: if a stream for
69
+ // this key already exists, push the update into it; otherwise create
70
+ // one. Creating a fresh stream here simulates "posted a NEW message
71
+ // instead of updating the original card" — the user-visible bug 1
72
+ // symptom.
73
+ const emit = (args: { chatId: string; threadId?: string; html: string; done: boolean }): void => {
74
+ const key = `${args.chatId}:${args.threadId ?? '_'}:progress`
75
+ let s = streams.get(key)
76
+ if (!s) {
77
+ s = makeStream()
78
+ streams.set(key, s)
79
+ allStreams.push(s)
80
+ }
81
+ s.update(args.html)
82
+ if (args.done) s.finalize()
83
+ }
84
+
85
+ const driver = createProgressDriver({ emit, minIntervalMs: 0, coalesceMs: 0, initialDelayMs: 0 })
86
+
87
+ // Emulate server.ts closeProgressLane: delete + finalize.
88
+ function closeProgressLane(chatId: string, threadId?: string): void {
89
+ const key = `${chatId}:${threadId ?? '_'}:progress`
90
+ const s = streams.get(key)
91
+ if (!s) return
92
+ streams.delete(key)
93
+ s.finalize()
94
+ }
95
+
96
+ function handleSessionEvent(ev: SessionEvent): void {
97
+ if (ev.kind === 'turn_end') {
98
+ // This is the line that caused bug 1: closeProgressLane deletes +
99
+ // finalizes before the driver has emitted its Done render.
100
+ closeProgressLane('c1')
101
+ }
102
+ }
103
+
104
+ // The fix is to run progressDriver.ingest FIRST on turn_end so the
105
+ // driver's synchronous flush reaches the existing stream before the
106
+ // handler tears it down. Expose both orders so the test can compare.
107
+ const onEvent = (ev: SessionEvent): void => {
108
+ events.push(ev)
109
+ if (order === 'driver-first') {
110
+ driver.ingest(ev, null)
111
+ handleSessionEvent(ev)
112
+ } else {
113
+ handleSessionEvent(ev)
114
+ driver.ingest(ev, null)
115
+ }
116
+ }
117
+
118
+ return { onEvent, streams, allStreams, events, driver }
119
+ }
120
+
121
+ // #235 Wave 3 F4: server.ts monolith removed. The bug-1 driver-first
122
+ // wiring pin (was a regex match on the onEvent callback in server.ts)
123
+ // no longer applies — gateway.ts is the only file with the wiring,
124
+ // and its driver-first ordering is pinned by the wireServer-driver-first
125
+ // behaviour tests below.
126
+
127
+ describe('bug 1 — Done transition reaches the original progress card', () => {
128
+ it('driver-first order: Done render lands in the ORIGINAL stream', () => {
129
+ const { onEvent, allStreams, driver } = wireServer('driver-first')
130
+
131
+ // Minimal turn: enqueue → one tool_use+result → turn_end
132
+ onEvent({
133
+ kind: 'enqueue',
134
+ chatId: 'c1',
135
+ messageId: '1',
136
+ threadId: null,
137
+ rawContent: '<channel chat_id="c1">hi</channel>',
138
+ })
139
+ onEvent({ kind: 'tool_use', toolName: 'Read', toolUseId: 't1', input: { file_path: '/x' } })
140
+ onEvent({ kind: 'tool_result', toolUseId: 't1', toolName: 'Read' })
141
+ // Issue #132: simulate the agent calling the reply tool so the final
142
+ // render lands on "✅ Done" rather than "🙊 Ended without reply". This
143
+ // test is about the bug-1 ordering between handler and driver, not
144
+ // the silent-end UX, so we explicitly opt into the happy path.
145
+ onEvent({ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__reply', toolUseId: 't2' })
146
+ onEvent({ kind: 'tool_result', toolUseId: 't2', toolName: 'mcp__switchroom-telegram__reply' })
147
+ // Issue #137: also simulate a successful outbound delivery so the renderer
148
+ // doesn't downgrade to "⚠️ Reply attempted but not delivered" — gateway's
149
+ // executeReply path calls recordOutboundDelivered after the message lands.
150
+ driver.recordOutboundDelivered('c1')
151
+ onEvent({ kind: 'turn_end', durationMs: 500 })
152
+
153
+ // With driver-first, only ONE stream is ever created — the original
154
+ // card. The handler's closeProgressLane runs AFTER the driver has
155
+ // already pushed the Done render into it, so no duplicate message
156
+ // is needed.
157
+ expect(allStreams).toHaveLength(1)
158
+ const [stream] = allStreams
159
+ expect(stream.finalized).toBe(true)
160
+ const lastUpdate = stream.updates.at(-1) ?? ''
161
+ expect(lastUpdate).toContain('✅ <b>Done</b>')
162
+ expect(lastUpdate).not.toContain('⚙️ <b>Working…</b>')
163
+ })
164
+
165
+ it('handler-first order reproduces bug 1: Done lands on a NEW stream', () => {
166
+ // This is the PRE-FIX behaviour — kept as a negative control so the
167
+ // fix's value is visible. The handler tears down the stream
168
+ // synchronously on turn_end; the driver's subsequent emit can't find
169
+ // the original stream, so the emit callback has to create a new
170
+ // stream (new Telegram message) — leaving the original progress
171
+ // card stuck on "⚙️ Working…".
172
+ const { onEvent, allStreams } = wireServer('handler-first')
173
+
174
+ onEvent({
175
+ kind: 'enqueue',
176
+ chatId: 'c1',
177
+ messageId: '1',
178
+ threadId: null,
179
+ rawContent: '<channel chat_id="c1">hi</channel>',
180
+ })
181
+ onEvent({ kind: 'tool_use', toolName: 'Read', toolUseId: 't1', input: { file_path: '/x' } })
182
+ onEvent({ kind: 'tool_result', toolUseId: 't1', toolName: 'Read' })
183
+ const originalSnapshotUpdates = [...(allStreams[0]?.updates ?? [])]
184
+
185
+ onEvent({ kind: 'turn_end', durationMs: 500 })
186
+
187
+ // Bug surface: the ORIGINAL stream got NO further updates past the
188
+ // pre-turn_end render, so its last frame still says Working.
189
+ const original = allStreams[0]
190
+ expect(original).toBeDefined()
191
+ const originalLast = original.updates.at(-1) ?? ''
192
+ expect(originalLast).toContain('⚙️ <b>Working…</b>')
193
+ expect(originalLast).not.toContain('✅ <b>Done</b>')
194
+ // And a SECOND stream had to be created for the Done render —
195
+ // visible to the user as a duplicate/new card.
196
+ expect(allStreams.length).toBeGreaterThanOrEqual(2)
197
+ expect(originalSnapshotUpdates.length).toBe(original.updates.length)
198
+ })
199
+ })
200
+
201
+ // ─── Bug 2 unit tests for isTelegramReplyTool ─────────────────────────────
202
+
203
+ // #235 Wave 3 F4: server.ts monolith removed. The bug-2 import + dead-
204
+ // branch pin no longer applies — gateway.ts owns the classification
205
+ // path now, exercised by the unit tests below.
206
+
207
+ describe('bug 2 — telegram tool-name classification is robust to MCP registration key', () => {
208
+ it('matches the historical `clerk-telegram` registration', () => {
209
+ expect(isTelegramReplyTool('mcp__clerk-telegram__reply')).toBe(true)
210
+ expect(isTelegramReplyTool('mcp__clerk-telegram__stream_reply')).toBe(true)
211
+ expect(isTelegramSurfaceTool('mcp__clerk-telegram__edit_message')).toBe(true)
212
+ expect(isTelegramSurfaceTool('mcp__clerk-telegram__react')).toBe(true)
213
+ })
214
+
215
+ it('matches the current `switchroom-telegram` registration', () => {
216
+ expect(isTelegramReplyTool('mcp__switchroom-telegram__reply')).toBe(true)
217
+ expect(isTelegramReplyTool('mcp__switchroom-telegram__stream_reply')).toBe(true)
218
+ })
219
+
220
+ it('matches fork-style registration keys that still contain `telegram`', () => {
221
+ expect(isTelegramReplyTool('mcp__my-fork-telegram__stream_reply')).toBe(true)
222
+ })
223
+
224
+ it('does NOT match unrelated MCP tool names', () => {
225
+ expect(isTelegramReplyTool('mcp__hindsight__recall')).toBe(false)
226
+ expect(isTelegramReplyTool('Read')).toBe(false)
227
+ expect(isTelegramReplyTool('mcp__switchroom-telegram__download_attachment')).toBe(false)
228
+ expect(isTelegramSurfaceTool('mcp__hindsight__retain')).toBe(false)
229
+ })
230
+
231
+ it("does NOT match the plugin's own non-reply tools (bugs past and future)", () => {
232
+ // `get_recent_messages`, `send_typing`, `pin_message`, `forward_message`,
233
+ // `delete_message`, `download_attachment` — none of these own the
234
+ // answer surface, so they must NOT set currentTurnReplyCalled, or the
235
+ // backstop would fail to fire for a turn that only called one of
236
+ // them and then emitted text.
237
+ expect(isTelegramReplyTool('mcp__clerk-telegram__get_recent_messages')).toBe(false)
238
+ expect(isTelegramReplyTool('mcp__clerk-telegram__send_typing')).toBe(false)
239
+ expect(isTelegramReplyTool('mcp__clerk-telegram__pin_message')).toBe(false)
240
+ expect(isTelegramReplyTool('mcp__clerk-telegram__forward_message')).toBe(false)
241
+ expect(isTelegramReplyTool('mcp__clerk-telegram__delete_message')).toBe(false)
242
+ })
243
+ })
244
+
245
+ // ─── Bug 2 end-to-end simulation of the turn-end backstop decision ────────
246
+ //
247
+ // The real server-side code path we need to guard: after a
248
+ // `stream_reply(done=true)` tool_use event arrives in the session-tail,
249
+ // the tool_use handler must set `currentTurnReplyCalled = true`. If it
250
+ // doesn't, a subsequent `text` event (e.g. "Idle; awaiting next
251
+ // instruction.") gets captured, and at turn_end the backstop fires and
252
+ // forwards it as a separate Telegram message.
253
+ //
254
+ // We simulate just the flag transition here — it's the cleanest way to
255
+ // pin down the exact bug without importing server.ts (which has
256
+ // top-level side-effects that require env vars).
257
+
258
+ describe('bug 2 — stream_reply tool call sets reply-called flag regardless of MCP key', () => {
259
+ // Mirror of the real session-tail tool_use event shape
260
+ type ToolUseEv = { kind: 'tool_use'; toolName: string }
261
+
262
+ function simulateTurn(events: Array<ToolUseEv | { kind: 'text'; text: string } | { kind: 'turn_end' }>): {
263
+ replyCalled: boolean
264
+ capturedText: string[]
265
+ wouldFireBackstop: boolean
266
+ } {
267
+ let replyCalled = false
268
+ const captured: string[] = []
269
+ for (const ev of events) {
270
+ if (ev.kind === 'tool_use' && isTelegramReplyTool(ev.toolName)) {
271
+ replyCalled = true
272
+ } else if (ev.kind === 'text') {
273
+ captured.push(ev.text)
274
+ }
275
+ }
276
+ return {
277
+ replyCalled,
278
+ capturedText: captured,
279
+ // This mirrors the exact server.ts turn_end guard.
280
+ wouldFireBackstop: !replyCalled && captured.length > 0,
281
+ }
282
+ }
283
+
284
+ it('with `clerk-telegram` key: stream_reply followed by idle text does NOT fire the backstop', () => {
285
+ const result = simulateTurn([
286
+ { kind: 'tool_use', toolName: 'mcp__clerk-telegram__stream_reply' },
287
+ { kind: 'text', text: 'Idle; awaiting next instruction.' },
288
+ { kind: 'turn_end' },
289
+ ])
290
+ expect(result.replyCalled).toBe(true)
291
+ // Post-fix: backstop does NOT fire, so no duplicate message leaks.
292
+ expect(result.wouldFireBackstop).toBe(false)
293
+ })
294
+
295
+ it('with `switchroom-telegram` key: unchanged — reply-called recognized', () => {
296
+ const result = simulateTurn([
297
+ { kind: 'tool_use', toolName: 'mcp__switchroom-telegram__stream_reply' },
298
+ { kind: 'text', text: 'idle' },
299
+ { kind: 'turn_end' },
300
+ ])
301
+ expect(result.replyCalled).toBe(true)
302
+ expect(result.wouldFireBackstop).toBe(false)
303
+ })
304
+
305
+ it('genuine orphan (NO reply tool call, only text) — backstop is DISABLED (issues #251, #269)', () => {
306
+ // The orphaned-reply backstop was removed in issue #269. All Telegram
307
+ // delivery now goes through the MCP reply tool, so a turn that ends
308
+ // without calling the reply tool is treated as a silent turn (no message
309
+ // sent), not a forwarding opportunity.
310
+ const result = simulateTurn([
311
+ { kind: 'tool_use', toolName: 'Read' },
312
+ { kind: 'text', text: "here's the answer" },
313
+ { kind: 'turn_end' },
314
+ ])
315
+ expect(result.replyCalled).toBe(false)
316
+ // The wouldFireBackstop flag reflects the old logic; the actual backstop
317
+ // in server.ts is now commented out. This value is kept for diagnostic
318
+ // purposes but the block no longer fires.
319
+ expect(result.wouldFireBackstop).toBe(true) // still true logically, but never executed
320
+ })
321
+ })
322
+
323
+ // ─── Bug 3 — orphan progress card (TG-ORPHAN-CARD) ───────────────────────
324
+ //
325
+ // Progress cards stayed pinned in "Working…" when turn_end was delayed,
326
+ // missed, or arrived after the model already sent its final reply.
327
+ //
328
+ // Fix: unpin fires on the FIRST of (turn_end, stream_reply(done=true), reply())
329
+ // via a shared `unpinProgressCard` helper guarded by `unpinnedTurnKeys`.
330
+ //
331
+ // These tests simulate the pin/unpin lifecycle that server.ts manages.
332
+ // They exercise the guard logic directly rather than importing server.ts
333
+ // (which has top-level side-effects requiring env vars).
334
+
335
+ describe('bug 3 — progress-card unpin fires on first of turn_end / reply / stream_reply(done)', () => {
336
+ /**
337
+ * Miniature simulation of the pin/unpin lifecycle from server.ts.
338
+ * Models the progressPinnedMsgIds map, unpinnedTurnKeys set, and the
339
+ * unpinProgressCard + unpinProgressCardForChat helpers.
340
+ */
341
+ function makeUnpinHarness() {
342
+ const progressPinnedMsgIds = new Map<string, number>()
343
+ const unpinnedTurnKeys = new Set<string>()
344
+ const unpinCalls: Array<{ turnKey: string; pinnedId: number }> = []
345
+
346
+ function unpinProgressCard(turnKey: string, _chatId: string, pinnedId: number): void {
347
+ if (unpinnedTurnKeys.has(turnKey)) return
348
+ unpinnedTurnKeys.add(turnKey)
349
+ progressPinnedMsgIds.delete(turnKey)
350
+ unpinCalls.push({ turnKey, pinnedId })
351
+ }
352
+
353
+ function unpinProgressCardForChat(chatId: string, threadId: number | undefined): void {
354
+ const base = threadId != null ? `${chatId}:${threadId}` : chatId
355
+ for (const [turnKey, pinnedId] of progressPinnedMsgIds) {
356
+ if (turnKey.startsWith(`${base}:`)) {
357
+ unpinProgressCard(turnKey, chatId, pinnedId)
358
+ }
359
+ }
360
+ }
361
+
362
+ function pinCard(turnKey: string, chatId: string, messageId: number): void {
363
+ progressPinnedMsgIds.set(turnKey, messageId)
364
+ }
365
+
366
+ function onTurnComplete(turnKey: string, chatId: string): void {
367
+ const pinnedId = progressPinnedMsgIds.get(turnKey)
368
+ if (pinnedId != null) {
369
+ unpinProgressCard(turnKey, chatId, pinnedId)
370
+ }
371
+ unpinnedTurnKeys.delete(turnKey)
372
+ }
373
+
374
+ return {
375
+ pinCard,
376
+ unpinProgressCardForChat,
377
+ onTurnComplete,
378
+ unpinCalls,
379
+ unpinnedTurnKeys,
380
+ }
381
+ }
382
+
383
+ it('reply() triggers unpin before turn_end arrives', () => {
384
+ const h = makeUnpinHarness()
385
+ // Card pinned at turn start
386
+ h.pinCard('100:1', '100', 42)
387
+
388
+ // model calls reply() → early unpin
389
+ h.unpinProgressCardForChat('100', undefined)
390
+
391
+ expect(h.unpinCalls).toHaveLength(1)
392
+ expect(h.unpinCalls[0]).toMatchObject({ turnKey: '100:1', pinnedId: 42 })
393
+ })
394
+
395
+ it('stream_reply(done=true) triggers unpin before turn_end arrives', () => {
396
+ const h = makeUnpinHarness()
397
+ h.pinCard('200:1', '200', 55)
398
+
399
+ // model calls stream_reply(done=true) → early unpin via chat lookup
400
+ h.unpinProgressCardForChat('200', undefined)
401
+
402
+ expect(h.unpinCalls).toHaveLength(1)
403
+ expect(h.unpinCalls[0]).toMatchObject({ turnKey: '200:1', pinnedId: 55 })
404
+ })
405
+
406
+ it('turn_end after reply-triggered unpin is a no-op (double-unpin guard)', () => {
407
+ const h = makeUnpinHarness()
408
+ h.pinCard('300:1', '300', 77)
409
+
410
+ // reply() fires first
411
+ h.unpinProgressCardForChat('300', undefined)
412
+ expect(h.unpinCalls).toHaveLength(1)
413
+
414
+ // turn_end fires later — onTurnComplete guard should make it a no-op
415
+ h.onTurnComplete('300:1', '300')
416
+
417
+ // Still only one unpin call
418
+ expect(h.unpinCalls).toHaveLength(1)
419
+ // Guard entry cleaned up by onTurnComplete
420
+ expect(h.unpinnedTurnKeys.has('300:1')).toBe(false)
421
+ })
422
+
423
+ it('turn_end fires first (normal path) — guard prevents double-unpin on late reply()', () => {
424
+ const h = makeUnpinHarness()
425
+ h.pinCard('400:1', '400', 88)
426
+
427
+ // turn_end fires first (normal path)
428
+ h.onTurnComplete('400:1', '400')
429
+ expect(h.unpinCalls).toHaveLength(1)
430
+ // Guard cleaned up by onTurnComplete
431
+ expect(h.unpinnedTurnKeys.has('400:1')).toBe(false)
432
+
433
+ // Late reply() fires (e.g. async handler after turn_end)
434
+ // unpinProgressCardForChat checks progressPinnedMsgIds which is already empty
435
+ h.unpinProgressCardForChat('400', undefined)
436
+ // No second unpin
437
+ expect(h.unpinCalls).toHaveLength(1)
438
+ })
439
+
440
+ it('concurrent turns on same chat each get their own unpin', () => {
441
+ const h = makeUnpinHarness()
442
+ // Two concurrent turns on chat 500 (e.g. two parallel sub-agents)
443
+ h.pinCard('500:1', '500', 10)
444
+ h.pinCard('500:2', '500', 11)
445
+
446
+ // reply() for the chat — unpins all matching cards
447
+ h.unpinProgressCardForChat('500', undefined)
448
+
449
+ expect(h.unpinCalls).toHaveLength(2)
450
+ const turnKeys = h.unpinCalls.map((c) => c.turnKey)
451
+ expect(turnKeys).toContain('500:1')
452
+ expect(turnKeys).toContain('500:2')
453
+ })
454
+
455
+ it('unpinProgressCardForChat only unpins cards for the matching chat+thread', () => {
456
+ const h = makeUnpinHarness()
457
+ h.pinCard('600:1', '600', 20) // chat 600, no thread
458
+ h.pinCard('601:1', '601', 21) // chat 601, no thread (different chat)
459
+
460
+ // Unpin for chat 600 only
461
+ h.unpinProgressCardForChat('600', undefined)
462
+
463
+ expect(h.unpinCalls).toHaveLength(1)
464
+ expect(h.unpinCalls[0]!.turnKey).toBe('600:1')
465
+ })
466
+
467
+ it('unpinProgressCardForChat scopes to thread when threadId is set', () => {
468
+ const h = makeUnpinHarness()
469
+ h.pinCard('700:99:1', '700', 30) // chat 700, thread 99
470
+ h.pinCard('700:88:1', '700', 31) // chat 700, thread 88
471
+
472
+ // Unpin for thread 99 only
473
+ h.unpinProgressCardForChat('700', 99)
474
+
475
+ expect(h.unpinCalls).toHaveLength(1)
476
+ expect(h.unpinCalls[0]!.turnKey).toBe('700:99:1')
477
+ })
478
+
479
+ // #235 Wave 3 F4: server.ts monolith removed. The maxIdleMs +
480
+ // early-unpin source-text pins for server.ts no longer apply —
481
+ // gateway.ts is the source of truth, pinned by the gateway.ts
482
+ // assertion below.
483
+
484
+ it('gateway.ts executeReply/executeStreamReply do NOT early-unpin', () => {
485
+ const gatewaySrc = readFileSync(join(__dirname, '..', 'gateway', 'gateway.ts'), 'utf-8')
486
+ expect(gatewaySrc).not.toContain('unpinProgressCardForChat?.(chat_id, threadId)')
487
+ expect(gatewaySrc).not.toContain('unpinProgressCardForChat?.(srChatId, srThreadId)')
488
+ })
489
+ })