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,340 @@
1
+ # Telegram test harness
2
+
3
+ How to write deterministic integration tests for switchroom code that
4
+ talks to Telegram. Use this when you're touching anything that calls
5
+ `bot.api.*` or processes incoming Telegram updates.
6
+
7
+ ## What's in the box
8
+
9
+ | File | Purpose |
10
+ |---|---|
11
+ | [`fake-bot-api.ts`](./fake-bot-api.ts) | Full mock of `bot.api.*`. Tracks chat model (sent[], pinned, reactions, deleted), supports fault injection with real `GrammyError` shapes, optional `holdNext` for in-flight ordering tests, optional `validateParseMode` for catching malformed MarkdownV2. **Use this for sequence/lifecycle tests.** |
12
+ | [`bot-api.harness.ts`](./bot-api.harness.ts) | Lighter mock — just `vi.fn()` stubs with sensible defaults. **Use this when you only need to assert on call shapes**, not chat-model state. |
13
+ | [`update-factory.ts`](./update-factory.ts) | Typed factories for Telegram `Update` objects: text messages, edited messages, message reactions, callback queries, photos, documents, my_chat_member events, forum-topic messages. |
14
+ | [`waiting-ux-harness.ts`](./waiting-ux-harness.ts) | Phase 1: real `StatusReactionController` + real `ProgressDriver` + recording fake bot + fake clock. Pin the four waiting-UX deadlines (Class A/B/C/F1–F4) under `vi.useFakeTimers()`. |
15
+ | [`real-gateway-harness.ts`](./real-gateway-harness.ts) | Phase 3: wraps `waiting-ux-harness` with the real production `InboundCoalescer` and real `flushOnAgentDisconnect`. IPC lifecycle simulation (`bridgeConnect`/`bridgeDisconnect`), opt-in `withDedup` for replay-suppression tests. **The default home for new lifecycle/timing tests.** |
16
+ | [`fake-bot-api.test.ts`](./fake-bot-api.test.ts) | Self-test of the fake bot — if this ever breaks, every test that depends on it is suspect. |
17
+
18
+ ## Validation rule for new tests
19
+
20
+ **Every regression test must carry a `// fails when:` comment**
21
+ indicating the production change that would break the invariant. Then
22
+ mentally `git stash` that change and confirm the test fails. Without
23
+ this round-trip the test is theatre — it asserts what the code already
24
+ does, not what it must continue to do.
25
+
26
+ Example:
27
+
28
+ ```ts
29
+ it('👍 fires AT-OR-AFTER last delivery', async () => {
30
+ // fails when: a future refactor moves setDone() from the streamReply
31
+ // post-await branch back to the JSONL turn_end handler — exactly
32
+ // Bug D's failure mode.
33
+ ...
34
+ })
35
+ ```
36
+
37
+ ## Decision: which mock?
38
+
39
+ ```
40
+ Are you asserting on a sequence/state that spans multiple API calls?
41
+ ├── yes → fake-bot-api.ts
42
+ │ (use bot.messagesIn(), bot.isPinned(), bot.faults.next(...))
43
+ └── no → bot-api.harness.ts
44
+ (use bot.api.sendMessage.mock.calls, .toHaveBeenCalledWith)
45
+ ```
46
+
47
+ The two are intentionally separate so existing tests don't pay the
48
+ chat-model overhead and new tests don't lose realism.
49
+
50
+ ## Patterns
51
+
52
+ ### Pattern 1 — assert on outbound API calls
53
+
54
+ Most common. You're testing a function that sends/edits messages.
55
+
56
+ ```ts
57
+ import { createFakeBotApi, errors } from './fake-bot-api.js';
58
+
59
+ it('pins a banner when slot changes', async () => {
60
+ const bot = createFakeBotApi();
61
+ await refreshBanner({ bot, ownerChatId: 'c', /* ... */ });
62
+ expect(bot.api.sendMessage).toHaveBeenCalledTimes(1);
63
+ expect(bot.api.pinChatMessage).toHaveBeenCalledTimes(1);
64
+ expect(bot.isPinned('c', 500)).toBe(true);
65
+ });
66
+ ```
67
+
68
+ ### Pattern 2 — drive a real grammy bot via injected updates
69
+
70
+ When you want to test the *dispatcher* (which command handler fires for
71
+ which Update). The pattern: create a real `new Bot(token, { client })`
72
+ with a fetch-shim, then call `bot.handleUpdate(makeMessageUpdate(...))`.
73
+
74
+ See `streaming-e2e.test.ts` for a worked example. (Not always needed —
75
+ most tests can target the extracted handler directly with a fake bot.)
76
+
77
+ ### Pattern 3 — fault injection
78
+
79
+ `fake-bot-api.ts` ships pre-built error factories matching real
80
+ GrammyError shapes:
81
+
82
+ ```ts
83
+ bot.faults.next('sendMessage', errors.floodWait(15)); // 429
84
+ bot.faults.next('editMessageText', errors.notModified()); // 400
85
+ bot.faults.next('pinChatMessage', errors.forbidden()); // 403
86
+ bot.faults.next('sendMessage', errors.networkError()); // fetch fail
87
+ ```
88
+
89
+ Faults are FIFO per method. Pull-once semantics — a fault fires on the
90
+ next matching call and is consumed.
91
+
92
+ ### Pattern 4 — time control
93
+
94
+ Pair `vi.useFakeTimers()` with `microtaskFlush()` from
95
+ `bot-api.harness.ts` for deterministic async settling:
96
+
97
+ ```ts
98
+ vi.useFakeTimers();
99
+ fireStreamReply({ chat_id: 'c', text: 'partial' }); // doesn't await
100
+ await microtaskFlush();
101
+ vi.advanceTimersByTime(300);
102
+ await microtaskFlush();
103
+ expect(bot.api.editMessageText).toHaveBeenCalled();
104
+ ```
105
+
106
+ ### Pattern 5 — multi-chat / forum-topic isolation
107
+
108
+ `update-factory.ts` exposes both private and forum chat defaults:
109
+
110
+ ```ts
111
+ import { makeMessageUpdate, makeTopicMessageUpdate } from './update-factory.js';
112
+
113
+ const dm = makeMessageUpdate({ text: '/auth status' });
114
+ const topicA = makeTopicMessageUpdate({ text: '/auth status', message_thread_id: 10 });
115
+ const topicB = makeTopicMessageUpdate({ text: '/auth status', message_thread_id: 20 });
116
+ ```
117
+
118
+ `fake-bot-api.ts` keys its chat-model by `chat_id`, not `(chat_id,
119
+ thread_id)`. For per-thread isolation tests, assert on the `args` of
120
+ the call (e.g. `expect(call[2].message_thread_id).toBe(10)`).
121
+
122
+ ## Coverage gaps & TODOs
123
+
124
+ These are deliberately not covered by the harness today; revisit when
125
+ the underlying feature lands or stabilises:
126
+
127
+ - **#479 — pre-alloc placeholder in groups**: write the test once
128
+ PR #487's gateway fix lands. Without the fix, asserting "placeholder
129
+ fires in groups" fails on main.
130
+ - **Forum-topic per-pin isolation**: `slot-banner.ts` is single-chat,
131
+ single-banner per gateway process (v1 scope of #421). When per-topic
132
+ pinning lands, extend `BannerState` to be keyed by `(chat,thread)` and
133
+ add isolation tests.
134
+ - **Real Telegram rendering** (markdown/HTML parse, link previews,
135
+ emoji reflow): not catchable by any HTTP-level mock. A nightly real-
136
+ test-bot smoke job is the proper home; out of scope for this harness.
137
+
138
+ ## Where to add new e2e tests
139
+
140
+ Naming convention: `<feature>.e2e.test.ts` for tests that drive an
141
+ extracted handler against `fake-bot-api.ts`. Examples already in repo:
142
+
143
+ - `slot-banner-driver.e2e.test.ts` — banner pin/edit/unpin lifecycle
144
+ - `auto-fallback-dispatcher.e2e.test.ts` — quota notification dispatch
145
+ - `streaming-e2e.test.ts` — PTY → stream_reply → done sequencing
146
+
147
+ Keep pure-logic tests in `<feature>.test.ts` (no `.e2e`). Examples:
148
+
149
+ - `slot-banner.test.ts` — pure `decideBannerAction` state machine
150
+ - `auto-fallback.test.ts` — pure `evaluateFallbackTrigger` /
151
+ `performAutoFallback` plan logic
152
+ - `auth-slot-commands.test.ts` — `parseAuthSubCommand` decoder
153
+
154
+ The split keeps the e2e tests fast (no harness boot per pure-logic
155
+ case) and the pure tests honest (no accidental coupling to bot calls).
156
+
157
+ ## Anti-patterns
158
+
159
+ - **Don't hand-roll a `BannerState` with an arbitrary `messageId`** and
160
+ expect editMessageText to succeed. The fake bot tracks sent ids and
161
+ throws `messageToEditNotFound` for unknown ones (this is realistic).
162
+ Either send a message first to seed the chat model, or use the
163
+ natural sequence (call refreshBanner once to pin, then test the next
164
+ transition).
165
+ - **Don't bypass `fake-bot-api.ts` and patch `globalThis.fetch`** to
166
+ intercept the real Bot API. Tests that do this couple to grammy
167
+ internals and break on grammy version bumps.
168
+ - **Don't assert on the entire Telegram payload** — assert on the
169
+ semantic fields (chat_id, text, parse_mode). Bot API adds optional
170
+ fields over time and full-payload snapshots churn.
171
+
172
+ ## Pattern 7 — `holdNext`: park a call mid-flight
173
+
174
+ Some bugs are about ordering between an in-flight outbound and an
175
+ inbound event — the canonical example is Bug D (👍 fired while
176
+ `editMessageText` was still pending). Asserting "X happens BEFORE Y
177
+ resolves" with `vi.advanceTimersByTime` is fragile because the
178
+ production code's await boundaries shift with refactors.
179
+
180
+ `holdNext` parks the next matching call at a gate. The test fires the
181
+ unrelated event while the call is parked, then explicitly releases:
182
+
183
+ ```ts
184
+ import { createFakeBotApi } from './fake-bot-api.js'
185
+
186
+ const bot = createFakeBotApi()
187
+ const r = await bot.api.sendMessage('c1', 'long enough text content', {})
188
+
189
+ // Park the next editMessageText call.
190
+ const hold = bot.holdNext('editMessageText', 'c1')
191
+
192
+ // Start the edit — promise pending until release.
193
+ const editPromise = bot.api.editMessageText('c1', r.message_id, 'updated', {})
194
+ await Promise.resolve() // let the call enter its await
195
+
196
+ expect(hold.triggered()).toBe(true)
197
+
198
+ // Fire the unrelated event while the edit is parked.
199
+ await bot.api.setMessageReaction('c1', r.message_id, [{ type: 'emoji', emoji: '👍' }])
200
+ expect(bot.state.reactions.length).toBe(1)
201
+ // Edit hasn't landed yet:
202
+ expect(bot.textOf(r.message_id)).toBe('long enough text content')
203
+
204
+ // Release — edit completes.
205
+ hold.release()
206
+ await editPromise
207
+ expect(bot.textOf(r.message_id)).toBe('updated')
208
+ ```
209
+
210
+ `hold.release()` is the happy path; `hold.fail(err)` rejects the held
211
+ call with `err` if the test wants to simulate an in-flight failure.
212
+ `hold.triggered()` returns true once the held call enters its await —
213
+ useful for confirming "yes, the call is parked here" before firing the
214
+ follow-up event.
215
+
216
+ Holds are FIFO per method, just like faults. `bot.reset()` rejects any
217
+ unreleased holds so a leaked hold from one test doesn't hang the next.
218
+
219
+ ## Pattern 8 — wired-in `OutboundDedupCache` for replay tests
220
+
221
+ The #546 bug class is "two paths emit the same content." The fix is
222
+ `OutboundDedupCache` (`telegram-plugin/recent-outbound-dedup.ts`) — a
223
+ process-wide cache keyed by `(chatId, threadId)` that suppresses
224
+ duplicate normalized content within a TTL.
225
+
226
+ The real-gateway harness wires this in opt-in:
227
+
228
+ ```ts
229
+ import { createRealGatewayHarness } from './real-gateway-harness.js'
230
+
231
+ const h = createRealGatewayHarness({ withDedup: true })
232
+ const r1 = await h.send({ chat_id: CHAT, text: 'long content...' })
233
+ const r2 = await h.send({ chat_id: CHAT, text: 'long content...' }) // suppressed
234
+ expect(r2).toBeNull()
235
+ expect(h.dedupSuppressedCount()).toBe(1)
236
+ expect(h.dedup!.size(Date.now())).toBe(1)
237
+ ```
238
+
239
+ `harness.send()` is the dedup-aware "fresh send" path — always issues
240
+ a new `sendMessage`, never edits. Use it in replay-dup tests where
241
+ "two messages with the same content" means two distinct user-visible
242
+ messages, not a streaming edit-in-place. (For streaming-edit
243
+ behavior, use `harness.streamReply()` as before.)
244
+
245
+ `simulateRetryDup({ chat_id, text })` is a one-line scenario for the
246
+ full #546 reproducer: send → bridge cycle → send again → assert
247
+ suppressed. See `real-gateway-i6-turn-flush-replay-dedup.test.ts`.
248
+
249
+ ## Pattern 9 — opt-in `validateParseMode` lenient validator
250
+
251
+ Real Telegram returns 400 on unbalanced MarkdownV2. The fake accepts
252
+ any string by default to keep 167 existing tests passing. New tests
253
+ opt in:
254
+
255
+ ```ts
256
+ const bot = createFakeBotApi({ validateParseMode: 'lenient' })
257
+ await expect(
258
+ bot.api.sendMessage('c1', '*unbalanced markdown', { parse_mode: 'MarkdownV2' }),
259
+ ).rejects.toMatchObject({ error_code: 400 })
260
+ ```
261
+
262
+ Lenient mode catches the most common failure: unbalanced count of
263
+ marker characters (`*`, `_`, `` ` ``, `[`). Backslash-escaped markers
264
+ and content inside inline-code / fenced code blocks are exempt. It is
265
+ NOT a full Telegram parser — corner cases like nested entities or
266
+ custom emoji escapes won't trigger it. For those, use a real-DC
267
+ nightly job (out of scope for the in-process fake).
268
+
269
+ ## Bug-class catalog
270
+
271
+ Each shipped bug class has a regression home. When fixing a new bug,
272
+ add the test next to its class. Update this table.
273
+
274
+ | Class | Example | Test home |
275
+ |---|---|---|
276
+ | Reaction timing desync | Bug D (👍 before delivery) | `real-gateway-ipc-lifecycle.test.ts` I3, `harness-ordering-invariants.test.ts` INV-1/INV-2 |
277
+ | IPC lifecycle leak | Bug A (anon disconnect flush) | `real-gateway-ipc-lifecycle.test.ts` I1, I2 |
278
+ | Legacy IPC type lethality | Bug B (`update_placeholder` crash) | `real-gateway-ipc-lifecycle.test.ts` I4 |
279
+ | Content-dup retry | #546 (turn-flush + replay) | `real-gateway-i6-turn-flush-replay-dedup.test.ts` |
280
+ | Respawn dedup defense | Bug C (wake-audit) | `real-gateway-i6-turn-flush-replay-dedup.test.ts` I5(b); profile-side fix lives elsewhere |
281
+ | Edit-on-deleted | latent | `harness-ordering-invariants.test.ts` INV-3 |
282
+ | Parse-mode malformed | latent | `harness-parse-mode-validation.test.ts` |
283
+ | Update factory shape | latent | `update-factory-edited-and-reactions.test.ts` |
284
+
285
+ ## Pattern 6 — fixture-based integration tests for external-format parsers
286
+
287
+ When code parses output produced by an **external system you don't
288
+ control** (Claude Code's TUI, the Anthropic API stream-json, journalctl
289
+ output, `git log` text), unit tests with synthesised input are not
290
+ enough. The synthesis matches the test author's mental model of the
291
+ format — but the real format drifts on every upstream release, and
292
+ synthesised tests can't catch the drift.
293
+
294
+ **Lesson learned the hard way (PR #486):** `pty-tail.ts`'s
295
+ `V1Extractor` was tested against synthesised Claude Code TUI output
296
+ that "matched the real shape." Then Claude Code collapsed tool-call
297
+ rendering by default, the marker `switchroom-telegram - reply` stopped
298
+ appearing in the buffer, and V1Extractor silently started returning
299
+ null on every call. The IPC plumbing tests still passed (they fed
300
+ mock data); the bridge → gateway wiring tests still passed (they fed
301
+ mock partials). The only failure mode that matters — "in production,
302
+ does this actually emit anything?" — wasn't covered by any test.
303
+
304
+ **Pattern**: capture a real chunk of the external format as a fixture
305
+ and assert the parser produces a non-null result.
306
+
307
+ ```ts
308
+ // telegram-plugin/tests/fixtures/service-log-current-claude-code.bin
309
+ // ← captured via: tail -c 30000 ~/.switchroom/agents/<agent>/service.log
310
+
311
+ import { readFileSync } from 'node:fs'
312
+ const FIXTURE = readFileSync(
313
+ resolve(__dirname, 'fixtures', 'service-log-current-claude-code.bin'),
314
+ 'utf8',
315
+ )
316
+
317
+ it('extractor handles current production output', async () => {
318
+ const term = await feedToTerm(FIXTURE)
319
+ const result = new V1Extractor().extract(term)
320
+ expect(result).not.toBeNull()
321
+ })
322
+ ```
323
+
324
+ **Maintenance**: when upstream's format changes the test fails. The
325
+ failure tells you exactly what changed (message includes the byte
326
+ range that no longer matches). Either:
327
+
328
+ 1. The format reverted (CI flake, just rerun)
329
+ 2. The format drifted (update the parser AND recapture the fixture)
330
+ 3. The feature stops working (remove the parser + dependents,
331
+ document why)
332
+
333
+ **Where to capture from**: the canonical source for each external
334
+ format. For PTY-tail it's `~/.switchroom/agents/<agent>/service.log`.
335
+ For Anthropic API stream-json it'd be a saved `--output-format
336
+ stream-json` dump. For journalctl, a captured `--since … -o cat`
337
+ window.
338
+
339
+ See `telegram-plugin/tests/pty-tail-real-fixture.test.ts` for the
340
+ worked example.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Shared harness for progress-card-driver tests added in PR-C2.
3
+ *
4
+ * Mirrors the inline harness used by progress-card-close-paths-converge,
5
+ * progress-card-driver-eviction, and the two-zone-* tests so the new
6
+ * tests don't drift in fake-clock semantics.
7
+ */
8
+
9
+ import { createProgressDriver, type ProgressDriver } from '../progress-card-driver.js'
10
+ import type { SessionEvent } from '../session-tail.js'
11
+
12
+ export interface DriverHarness {
13
+ driver: ProgressDriver
14
+ emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }>
15
+ completions: string[]
16
+ advance: (ms: number) => void
17
+ getNow: () => number
18
+ }
19
+
20
+ export interface HarnessOpts {
21
+ minIntervalMs?: number
22
+ coalesceMs?: number
23
+ initialDelayMs?: number
24
+ heartbeatMs?: number
25
+ maxIdleMs?: number
26
+ deferredCompletionTimeoutMs?: number
27
+ promoteAfterMs?: number
28
+ editBudgetThreshold?: number
29
+ editBudgetCoalesceMs?: number
30
+ maxConsecutive4xx?: number
31
+ onTurnComplete?: (s: { turnKey: string }) => void
32
+ }
33
+
34
+ export function makeHarness(opts: HarnessOpts = {}): DriverHarness {
35
+ let now = 1000
36
+ const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
37
+ let nextRef = 0
38
+ const emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }> = []
39
+ const completions: string[] = []
40
+
41
+ const driver = createProgressDriver({
42
+ emit: (a) => emits.push(a),
43
+ minIntervalMs: opts.minIntervalMs ?? 0,
44
+ coalesceMs: opts.coalesceMs ?? 0,
45
+ initialDelayMs: opts.initialDelayMs ?? 0,
46
+ heartbeatMs: opts.heartbeatMs ?? 1_000,
47
+ maxIdleMs: opts.maxIdleMs ?? 30_000,
48
+ deferredCompletionTimeoutMs: opts.deferredCompletionTimeoutMs ?? 10_000,
49
+ promoteAfterMs: opts.promoteAfterMs,
50
+ editBudgetThreshold: opts.editBudgetThreshold,
51
+ editBudgetCoalesceMs: opts.editBudgetCoalesceMs,
52
+ maxConsecutive4xx: opts.maxConsecutive4xx,
53
+ onTurnComplete: opts.onTurnComplete ?? ((s) => completions.push(s.turnKey)),
54
+ now: () => now,
55
+ setTimeout: (fn, ms) => {
56
+ const ref = nextRef++
57
+ timers.push({ fireAt: now + ms, fn, ref })
58
+ return { ref }
59
+ },
60
+ clearTimeout: (h) => {
61
+ const ref = (h as { ref: number }).ref
62
+ const idx = timers.findIndex((t) => t.ref === ref)
63
+ if (idx !== -1) timers.splice(idx, 1)
64
+ },
65
+ setInterval: (fn, ms) => {
66
+ const ref = nextRef++
67
+ timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
68
+ return { ref }
69
+ },
70
+ clearInterval: (h) => {
71
+ const ref = (h as { ref: number }).ref
72
+ const idx = timers.findIndex((t) => t.ref === ref)
73
+ if (idx !== -1) timers.splice(idx, 1)
74
+ },
75
+ })
76
+
77
+ const advance = (ms: number): void => {
78
+ now += ms
79
+ for (;;) {
80
+ timers.sort((a, b) => a.fireAt - b.fireAt)
81
+ const next = timers[0]
82
+ if (!next || next.fireAt > now) break
83
+ if (next.repeat != null) {
84
+ next.fireAt += next.repeat
85
+ next.fn()
86
+ } else {
87
+ timers.shift()
88
+ next.fn()
89
+ }
90
+ }
91
+ }
92
+
93
+ return { driver, emits, completions, advance, getNow: () => now }
94
+ }
95
+
96
+ let nextMsgId = 50_000
97
+ export function enqueue(chatId: string, threadId?: string): SessionEvent {
98
+ return {
99
+ kind: 'enqueue',
100
+ chatId,
101
+ messageId: String(nextMsgId++),
102
+ threadId: threadId ?? null,
103
+ rawContent: `<channel chat_id="${chatId}">go</channel>`,
104
+ }
105
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Regression test for #689 — boot-time orphan-pin reaper.
3
+ *
4
+ * Backstop for SIGKILL/OOM/panic where the SIGTERM handler (PR #690)
5
+ * never ran. PR #690 covers clean shutdowns: it walks `pinnedEntries()`
6
+ * in-memory before the process exits. But on a hard kill the in-memory
7
+ * map dies with the process, so the next boot must rely on the disk
8
+ * sidecar (`active-pins.ts`) to find the orphans and finalize them.
9
+ *
10
+ * Shape under test (the new `editBeforeUnpin` hook on `sweepActivePins`):
11
+ *
12
+ * 1. Pin lifecycle records to sidecar (already covered by
13
+ * active-pins.test.ts) — we just simulate "process died with
14
+ * sidecar populated" by calling `addActivePin` directly.
15
+ * 2. On next boot, `sweepActivePins` is invoked with an
16
+ * `editBeforeUnpin` callback that renders the banner.
17
+ * 3. Each entry: editFn runs first; unpin runs after; sidecar gets
18
+ * cleared.
19
+ * 4. Banner edit failure (e.g. "message to edit not found") is caught
20
+ * and the unpin still fires — frozen card is worse than no card.
21
+ * 5. Clean-shutdown unpin path (via pinManager.completeTurn) removes
22
+ * the sidecar entry, so a clean shutdown leaves nothing for the
23
+ * reaper to find — only crashes leave entries behind.
24
+ */
25
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
26
+ import { mkdtempSync, rmSync, existsSync } from 'node:fs'
27
+ import { tmpdir } from 'node:os'
28
+ import { join } from 'node:path'
29
+ import {
30
+ addActivePin,
31
+ readActivePins,
32
+ writeActivePins,
33
+ ACTIVE_PINS_FILENAME,
34
+ type ActivePin,
35
+ } from '../active-pins.js'
36
+ import { sweepActivePins } from '../active-pins-sweep.js'
37
+ import {
38
+ createPinManager,
39
+ type PinManagerDeps,
40
+ type TimerHandle,
41
+ } from '../progress-card-pin-manager.js'
42
+
43
+ interface PendingTimer { fn: () => void; cancelled: boolean; fired: boolean }
44
+
45
+ function mkPinManagerHarness(overrides: Partial<PinManagerDeps> = {}) {
46
+ const timers: PendingTimer[] = []
47
+ const deps = {
48
+ pin: vi.fn(async () => true),
49
+ unpin: vi.fn(async () => true),
50
+ deleteMessage: vi.fn(async () => true),
51
+ addPin: vi.fn(),
52
+ removePin: vi.fn(),
53
+ log: vi.fn(),
54
+ }
55
+ const scheduleTimer = (fn: () => void): TimerHandle => {
56
+ const entry: PendingTimer = { fn, cancelled: false, fired: false }
57
+ timers.push(entry)
58
+ return { cancel() { entry.cancelled = true } }
59
+ }
60
+ const mgr = createPinManager({ ...deps, now: () => 10_000, scheduleTimer, ...overrides })
61
+ const fireTimers = (): void => {
62
+ for (const t of [...timers]) {
63
+ if (t.cancelled || t.fired) continue
64
+ t.fired = true
65
+ t.fn()
66
+ }
67
+ }
68
+ return { mgr, deps, fireTimers }
69
+ }
70
+
71
+ describe('boot-time orphan-pin reaper (#689)', () => {
72
+ let tmp: string
73
+
74
+ beforeEach(() => {
75
+ tmp = mkdtempSync(join(tmpdir(), 'pin-reaper-'))
76
+ })
77
+
78
+ afterEach(() => {
79
+ rmSync(tmp, { recursive: true, force: true })
80
+ })
81
+
82
+ const makePin = (overrides: Partial<ActivePin> = {}): ActivePin => ({
83
+ chatId: '100',
84
+ messageId: 42,
85
+ turnKey: '100:0:1',
86
+ pinnedAt: 1_700_000_000_000,
87
+ agentId: '__parent__',
88
+ ...overrides,
89
+ })
90
+
91
+ it('hard-kill mid-turn: next boot sees the sidecar entry, edits banner, then unpins', async () => {
92
+ // Simulate: prior process pinned but never got to unpin (SIGKILL/OOM).
93
+ addActivePin(tmp, makePin({ chatId: 'A', messageId: 101 }))
94
+ addActivePin(tmp, makePin({ chatId: 'B', messageId: 202, turnKey: 'B:0:1' }))
95
+
96
+ // Now boot: invoke the reaper.
97
+ const editCalls: Array<{ chatId: string; messageId: number; banner: string }> = []
98
+ const unpinCalls: Array<[string, number]> = []
99
+ const banner = '⚠️ <b>Restart interrupted this work</b>\n<i>SIGKILL: oom</i>'
100
+
101
+ const result = await sweepActivePins(
102
+ tmp,
103
+ async (chatId, messageId) => { unpinCalls.push([chatId, messageId]) },
104
+ {
105
+ editBeforeUnpin: async (pin) => {
106
+ editCalls.push({ chatId: pin.chatId, messageId: pin.messageId, banner })
107
+ },
108
+ },
109
+ )
110
+
111
+ // Both pins were edited THEN unpinned.
112
+ expect(editCalls.sort((a, b) => a.messageId - b.messageId)).toEqual([
113
+ { chatId: 'A', messageId: 101, banner },
114
+ { chatId: 'B', messageId: 202, banner },
115
+ ])
116
+ expect(unpinCalls.sort()).toEqual([['A', 101], ['B', 202]])
117
+ expect(result.swept).toHaveLength(2)
118
+ expect(existsSync(join(tmp, ACTIVE_PINS_FILENAME))).toBe(false)
119
+ })
120
+
121
+ it('banner edit failure does not block the unpin (frozen card is worse than no card)', async () => {
122
+ addActivePin(tmp, makePin({ chatId: 'A', messageId: 101 }))
123
+
124
+ const unpinCalls: Array<[string, number]> = []
125
+ const result = await sweepActivePins(
126
+ tmp,
127
+ async (chatId, messageId) => { unpinCalls.push([chatId, messageId]) },
128
+ {
129
+ editBeforeUnpin: async () => {
130
+ throw new Error('Bad Request: message to edit not found')
131
+ },
132
+ },
133
+ )
134
+
135
+ expect(unpinCalls).toEqual([['A', 101]])
136
+ expect(result.swept).toHaveLength(1)
137
+ expect(existsSync(join(tmp, ACTIVE_PINS_FILENAME))).toBe(false)
138
+ })
139
+
140
+ it('edit fires BEFORE unpin (ordering matters — never unpin a card with stale Working… body)', async () => {
141
+ addActivePin(tmp, makePin({ chatId: 'A', messageId: 101 }))
142
+
143
+ const order: string[] = []
144
+ await sweepActivePins(
145
+ tmp,
146
+ async () => { order.push('unpin') },
147
+ {
148
+ editBeforeUnpin: async () => { order.push('edit') },
149
+ },
150
+ )
151
+
152
+ expect(order).toEqual(['edit', 'unpin'])
153
+ })
154
+
155
+ it('clean unpin path removes sidecar entry, leaving nothing for the reaper', async () => {
156
+ // Simulate the pinManager addPin/removePin sidecar wiring used in
157
+ // gateway.ts: addPin appends to disk, removePin filters out.
158
+ const h = mkPinManagerHarness({
159
+ addPin: (entry) => addActivePin(tmp, entry),
160
+ removePin: (chatId, messageId) => {
161
+ const next = readActivePins(tmp).filter(
162
+ (p) => !(p.chatId === chatId && p.messageId === messageId),
163
+ )
164
+ writeActivePins(tmp, next)
165
+ },
166
+ })
167
+
168
+ // Pin
169
+ h.mgr.considerPin({
170
+ chatId: 'A', threadId: '7', turnKey: 'A:7:1',
171
+ messageId: 101, isFirstEmit: true,
172
+ })
173
+ h.fireTimers()
174
+ await h.mgr.drainInFlight()
175
+ expect(readActivePins(tmp)).toHaveLength(1)
176
+
177
+ // Clean unpin (e.g. via completeTurn at end of turn)
178
+ h.mgr.completeTurn({ chatId: 'A', threadId: '7', turnKey: 'A:7:1' })
179
+ await h.mgr.drainInFlight()
180
+
181
+ // Sidecar is empty — the boot reaper would find nothing.
182
+ expect(readActivePins(tmp)).toEqual([])
183
+
184
+ // And confirm the reaper would no-op:
185
+ const editCalls: ActivePin[] = []
186
+ const result = await sweepActivePins(
187
+ tmp,
188
+ async () => {},
189
+ { editBeforeUnpin: async (p) => { editCalls.push(p) } },
190
+ )
191
+ expect(editCalls).toEqual([])
192
+ expect(result.swept).toEqual([])
193
+ })
194
+
195
+ it('respects the timeout budget when banner edits hang', async () => {
196
+ addActivePin(tmp, makePin({ chatId: 'A', messageId: 101 }))
197
+
198
+ const result = await sweepActivePins(
199
+ tmp,
200
+ async () => {},
201
+ {
202
+ timeoutMs: 50,
203
+ editBeforeUnpin: () => new Promise(() => {}), // never resolves
204
+ },
205
+ )
206
+
207
+ expect(result.timedOut).toBe(true)
208
+ // Sidecar still cleared — Telegram unpin is idempotent.
209
+ expect(existsSync(join(tmp, ACTIVE_PINS_FILENAME))).toBe(false)
210
+ })
211
+ })