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,218 @@
1
+ /**
2
+ * #654 regression tests — deterministic double-message fix via card
3
+ * takeover.
4
+ *
5
+ * Bug: when an agent's turn took longer than `initialDelayMs` (60s) AND
6
+ * the agent emitted assistant text without calling `reply` /
7
+ * `stream_reply` (turn-flush path), the user saw TWO outbound Telegram
8
+ * messages — the pinned progress card AND the turn-flush bubble — for
9
+ * one logical reply.
10
+ *
11
+ * Root cause: the gateway's turn-flush path issued a fresh
12
+ * `bot.api.sendMessage` even when a progress card was already on screen
13
+ * for that turn. The driver's `forceCompleteTurn` couldn't help because
14
+ * once the deferred-emit timer had fired, no path existed to retract
15
+ * the posted card — `flush()` would only edit it to "Done".
16
+ *
17
+ * Fix: add a `takeOverCard` method to the driver that:
18
+ * - cancels the pending deferred-emit timer if not yet fired
19
+ * - sets `cardTakenOver = true` so subsequent `flush()` calls
20
+ * short-circuit (no further "Done" edit)
21
+ * - returns `{ wasEmitted, turnKey }` so the caller (turn-flush)
22
+ * can look up the pinned messageId and rewrite it in place via
23
+ * `editMessageText` instead of creating a second message.
24
+ *
25
+ * The harness gap that hid the bug: no existing test wired a real
26
+ * driver into a long-turn scenario. `turn-flush-safety.test.ts`
27
+ * covered `decideTurnFlush()` only; `real-gateway-i6` covered turn-
28
+ * flush replay/dedup but never modeled a card already on screen.
29
+ *
30
+ * These tests pin the driver-level contract. The gateway integration
31
+ * is exercised in the bridged scenario at the bottom of this file.
32
+ */
33
+
34
+ import { describe, it, expect } from 'vitest'
35
+ import { createProgressDriver } from '../progress-card-driver.js'
36
+ import type { SessionEvent } from '../session-tail.js'
37
+
38
+ function harness(opts?: { initialDelayMs?: number }) {
39
+ let now = 1000
40
+ const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
41
+ let nextRef = 0
42
+ const emits: Array<{
43
+ chatId: string
44
+ threadId?: string
45
+ turnKey: string
46
+ html: string
47
+ done: boolean
48
+ isFirstEmit: boolean
49
+ }> = []
50
+
51
+ const driver = createProgressDriver({
52
+ emit: (a) => emits.push(a),
53
+ minIntervalMs: 0,
54
+ coalesceMs: 0,
55
+ initialDelayMs: opts?.initialDelayMs ?? 60_000,
56
+ promoteAfterMs: 999_999,
57
+ now: () => now,
58
+ setTimeout: (fn, ms) => {
59
+ const ref = nextRef++
60
+ timers.push({ fireAt: now + ms, fn, ref })
61
+ return { ref }
62
+ },
63
+ clearTimeout: (handle) => {
64
+ const target = (handle as { ref: number }).ref
65
+ const idx = timers.findIndex((t) => t.ref === target)
66
+ if (idx !== -1) timers.splice(idx, 1)
67
+ },
68
+ setInterval: (fn, ms) => {
69
+ const ref = nextRef++
70
+ timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
71
+ return { ref }
72
+ },
73
+ clearInterval: (handle) => {
74
+ const target = (handle as { ref: number }).ref
75
+ const idx = timers.findIndex((t) => t.ref === target)
76
+ if (idx !== -1) timers.splice(idx, 1)
77
+ },
78
+ })
79
+
80
+ const advance = (ms: number): void => {
81
+ now += ms
82
+ for (;;) {
83
+ timers.sort((a, b) => a.fireAt - b.fireAt)
84
+ const next = timers[0]
85
+ if (!next || next.fireAt > now) break
86
+ if (next.repeat != null) {
87
+ next.fireAt += next.repeat
88
+ next.fn()
89
+ } else {
90
+ timers.shift()
91
+ next.fn()
92
+ }
93
+ }
94
+ }
95
+
96
+ return { driver, emits, advance }
97
+ }
98
+
99
+ let nextMsgId = 1
100
+ function enqueue(chatId: string, text = 'q', threadId: string | null = null): SessionEvent {
101
+ return {
102
+ kind: 'enqueue',
103
+ chatId,
104
+ messageId: String(nextMsgId++),
105
+ threadId,
106
+ rawContent: `<channel chat_id="${chatId}">${text}</channel>`,
107
+ } as unknown as SessionEvent
108
+ }
109
+
110
+ describe('takeOverCard — #654 regression', () => {
111
+ it('returns wasEmitted=false when card has not yet emitted (pre-60s turn)', () => {
112
+ // Fast-turn case: turn-flush fires before the deferred-emit timer.
113
+ // Driver suppresses the card; turn-flush sends fresh.
114
+ const { driver } = harness({ initialDelayMs: 60_000 })
115
+ driver.ingest(enqueue('c1'), 'c1')
116
+ // Don't advance — timer still pending.
117
+
118
+ const result = driver.takeOverCard({ chatId: 'c1' })
119
+ expect(result.wasEmitted).toBe(false)
120
+ expect(result.turnKey).not.toBeNull()
121
+ expect(typeof result.turnKey).toBe('string')
122
+ })
123
+
124
+ it('returns wasEmitted=true when deferred-emit timer has fired (the #654 path)', () => {
125
+ // Slow-turn case: the card has been emitted to the chat. takeOverCard
126
+ // signals that the caller should edit-in-place rather than send fresh.
127
+ const { driver, emits, advance } = harness({ initialDelayMs: 60_000 })
128
+ driver.ingest(
129
+ enqueue('c1'),
130
+ 'c1',
131
+ )
132
+ advance(60_000)
133
+ expect(emits.length).toBeGreaterThan(0) // card emitted
134
+
135
+ const result = driver.takeOverCard({ chatId: 'c1' })
136
+ expect(result.wasEmitted).toBe(true)
137
+ expect(typeof result.turnKey).toBe('string')
138
+ expect(result.turnKey).toContain('c1')
139
+ })
140
+
141
+ it('cancels the pending deferred-emit timer (no late card emission)', () => {
142
+ // After takeOverCard cancels the timer, advancing past the original
143
+ // delay must NOT produce a card emit.
144
+ const { driver, emits, advance } = harness({ initialDelayMs: 60_000 })
145
+ driver.ingest(
146
+ enqueue('c1'),
147
+ 'c1',
148
+ )
149
+ expect(emits.length).toBe(0) // suppressed by initial delay
150
+
151
+ driver.takeOverCard({ chatId: 'c1' })
152
+ advance(120_000) // way past 60s
153
+
154
+ expect(emits.length).toBe(0) // timer was cancelled — no late emit
155
+ })
156
+
157
+ it('blocks subsequent flushes — driver.ingest(turn_end) does NOT emit a "Done" edit', () => {
158
+ // The bug case: after card is on screen, gateway calls takeOverCard,
159
+ // then session-tail dispatches turn_end which the driver ingests.
160
+ // Without the cardTakenOver guard, turn_end would call flush(forceDone)
161
+ // → editMessageText("Done") — wasted edit. With the guard, no emit.
162
+ const { driver, emits, advance } = harness({ initialDelayMs: 60_000 })
163
+ driver.ingest(
164
+ enqueue('c1'),
165
+ 'c1',
166
+ )
167
+ advance(60_000)
168
+ const emitsAfterCard = emits.length
169
+ expect(emitsAfterCard).toBeGreaterThan(0)
170
+
171
+ driver.takeOverCard({ chatId: 'c1' })
172
+
173
+ // Now simulate the driver receiving turn_end (as session-tail would
174
+ // dispatch synchronously upstream of the gateway's turn-flush block).
175
+ driver.ingest(
176
+ { kind: 'turn_end', durationMs: 70_000 } as unknown as SessionEvent,
177
+ 'c1',
178
+ )
179
+ expect(emits.length).toBe(emitsAfterCard) // no additional edits
180
+ })
181
+
182
+ it('idempotent — second call returns same shape, no double-cancel side-effects', () => {
183
+ const { driver, emits, advance } = harness({ initialDelayMs: 60_000 })
184
+ driver.ingest(
185
+ enqueue('c1'),
186
+ 'c1',
187
+ )
188
+ advance(60_000)
189
+ const emitsAfter1 = emits.length
190
+
191
+ const r1 = driver.takeOverCard({ chatId: 'c1' })
192
+ const r2 = driver.takeOverCard({ chatId: 'c1' })
193
+ expect(r1).toEqual(r2)
194
+ expect(emits.length).toBe(emitsAfter1)
195
+ })
196
+
197
+ it('returns null turnKey when no active card exists for (chatId, threadId)', () => {
198
+ const { driver } = harness({ initialDelayMs: 60_000 })
199
+ const result = driver.takeOverCard({ chatId: 'never-enqueued' })
200
+ expect(result).toEqual({ wasEmitted: false, turnKey: null })
201
+ })
202
+
203
+ it('routes by chatId+threadId — separate chats do not clobber each other', () => {
204
+ const { driver } = harness({ initialDelayMs: 60_000 })
205
+ driver.ingest(enqueue('c1'), 'c1')
206
+ driver.ingest(enqueue('c2'), 'c2')
207
+
208
+ // Take over c1 — c2's card must remain untouched.
209
+ const r1 = driver.takeOverCard({ chatId: 'c1' })
210
+ expect(r1.turnKey).toContain('c1')
211
+ expect(r1.turnKey).not.toContain('c2')
212
+
213
+ // c2 still has its own active card, distinct turnKey.
214
+ const r2 = driver.takeOverCard({ chatId: 'c2' })
215
+ expect(r2.turnKey).toContain('c2')
216
+ expect(r2.turnKey).not.toContain('c1')
217
+ })
218
+ })
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Bug D — turn-flush dedup branch must NOT prematurely fire setDone on
3
+ * the status-reaction controller.
4
+ *
5
+ * Background. The gateway's turn_end handler has three exits:
6
+ * (1) `silent-marker` skip — drops streams, fires ctrl.setDone(), purges.
7
+ * (2) `flush` — schedules an async IIFE that waits 500ms and either:
8
+ * (2a) suppresses the flush because `getRecentOutboundCount > 0`
9
+ * (the reply tool already sent something), OR
10
+ * (2b) fires the captured text via bot.api.sendMessage as a backstop.
11
+ * (3) main path — ctrl.setDone(), purge, telemetry.
12
+ *
13
+ * Path (2a) used to call `backstopCtrl.setDone()` on a 500ms-lagged read
14
+ * of local history — not a confirmation that anything actually reached
15
+ * Telegram. With Bug Z's fix, stream_reply(done=true) on the default
16
+ * lane fires `endStatusReaction('done')` AFTER `stream.finalize()`
17
+ * resolves (meaning the final draft edit landed in Telegram). So the
18
+ * authoritative 👍 source is the post-finalize callback, not the
19
+ * dedup-branch heuristic.
20
+ *
21
+ * This test pins the contract for the dedup branch: when
22
+ * `getRecentOutboundCount > 0`, the dedup branch must NOT fire setDone
23
+ * on the controller. The controller's terminal transition is left to
24
+ * the stream_reply post-finalize callback (already exercised in
25
+ * stream-reply-handler.test.ts).
26
+ *
27
+ * The gateway IIFE is too entangled to import directly. Rather than
28
+ * refactor the entire turn_end handler for testability in this PR, we
29
+ * model the dedup-branch logic as a pure function below and pin the
30
+ * post-fix invariant. If the gateway is ever refactored to extract
31
+ * this branch, the same assertions apply unchanged.
32
+ */
33
+ import { describe, it, expect, vi } from 'vitest'
34
+
35
+ interface MockController {
36
+ setDone: () => void
37
+ setError: () => void
38
+ }
39
+
40
+ interface DedupBranchDeps {
41
+ getRecentOutboundCount: (chatId: string, withinSec: number) => number
42
+ purgeReactionTracking: (key: string) => void
43
+ ctrl: MockController | null
44
+ }
45
+
46
+ /**
47
+ * Extract of the gateway turn-flush IIFE's dedup branch. Mirrors the
48
+ * code at `telegram-plugin/gateway/gateway.ts` lines ~3365-3376
49
+ * (post-fix). Returns true if the flush should be suppressed.
50
+ */
51
+ function applyDedupBranch(
52
+ chatId: string,
53
+ statusKeyValue: string,
54
+ deps: DedupBranchDeps,
55
+ ): boolean {
56
+ const recentCount = deps.getRecentOutboundCount(chatId, 2)
57
+ if (recentCount > 0) {
58
+ // Bug D fix: do NOT fire setDone here. The post-finalize callback
59
+ // in stream-reply-handler.ts owns the terminal 👍 transition.
60
+ deps.purgeReactionTracking(statusKeyValue)
61
+ return true
62
+ }
63
+ return false
64
+ }
65
+
66
+ describe('Bug D — turn-flush dedup branch (post-fix)', () => {
67
+ it('suppresses flush when recentCount > 0 AND does NOT prematurely fire setDone', () => {
68
+ const setDone = vi.fn()
69
+ const setError = vi.fn()
70
+ const purgeReactionTracking = vi.fn()
71
+ const getRecentOutboundCount = vi.fn(() => 1) // dedup case: 1 outbound landed within 2s
72
+
73
+ const suppressed = applyDedupBranch(
74
+ '-100',
75
+ '-100:_',
76
+ {
77
+ getRecentOutboundCount,
78
+ purgeReactionTracking,
79
+ ctrl: { setDone, setError },
80
+ },
81
+ )
82
+
83
+ expect(suppressed).toBe(true)
84
+ expect(getRecentOutboundCount).toHaveBeenCalledWith('-100', 2)
85
+ expect(purgeReactionTracking).toHaveBeenCalledWith('-100:_')
86
+ // Critical: the controller is NOT touched here. Bug Z's
87
+ // post-finalize callback in stream-reply-handler.ts is what
88
+ // transitions the controller to terminal 👍 — and only after the
89
+ // final draft edit confirmedly lands in Telegram.
90
+ expect(setDone).not.toHaveBeenCalled()
91
+ expect(setError).not.toHaveBeenCalled()
92
+ })
93
+
94
+ it('falls through (returns false) when recentCount == 0 — backstop send required', () => {
95
+ // The other branch path: no recent outbound, so the IIFE will
96
+ // proceed to bot.api.sendMessage and (on success) call setDone
97
+ // there. This test pins that the dedup branch does NOT short-
98
+ // circuit when there's nothing to dedup against.
99
+ const setDone = vi.fn()
100
+ const purgeReactionTracking = vi.fn()
101
+ const getRecentOutboundCount = vi.fn(() => 0)
102
+
103
+ const suppressed = applyDedupBranch(
104
+ '-200',
105
+ '-200:_',
106
+ {
107
+ getRecentOutboundCount,
108
+ purgeReactionTracking,
109
+ ctrl: { setDone, setError: vi.fn() },
110
+ },
111
+ )
112
+
113
+ expect(suppressed).toBe(false)
114
+ expect(getRecentOutboundCount).toHaveBeenCalledWith('-200', 2)
115
+ // Neither side-effect should fire — the IIFE's backstop-send arm
116
+ // owns those (purge in the finally block, setDone after sendMessage
117
+ // resolves).
118
+ expect(purgeReactionTracking).not.toHaveBeenCalled()
119
+ expect(setDone).not.toHaveBeenCalled()
120
+ })
121
+
122
+ it('handles a missing controller gracefully (no throw)', () => {
123
+ // Defence-in-depth: the IIFE captures `backstopCtrl = ctrl` which
124
+ // may be null if no status-reaction was ever queued for this
125
+ // chat+thread. The guard previously read `if (backstopCtrl)
126
+ // backstopCtrl.setDone()`. With setDone removed entirely this is a
127
+ // non-issue, but pin the invariant anyway.
128
+ const purgeReactionTracking = vi.fn()
129
+ const getRecentOutboundCount = vi.fn(() => 5)
130
+
131
+ expect(() =>
132
+ applyDedupBranch(
133
+ '-300',
134
+ '-300:_',
135
+ {
136
+ getRecentOutboundCount,
137
+ purgeReactionTracking,
138
+ ctrl: null,
139
+ },
140
+ ),
141
+ ).not.toThrow()
142
+ expect(purgeReactionTracking).toHaveBeenCalledWith('-300:_')
143
+ })
144
+ })
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Tests for the prose-recovery helper used by the turn-flush backstop
3
+ * to bridge the divergence between the gateway's `capturedText`
4
+ * accumulator and the progress-card driver's narrative state. See #51.
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest'
8
+ import type { ProgressCardState, NarrativeStep } from '../progress-card.js'
9
+ import { recoverProseFromProgressCard } from '../turn-flush-prose-recovery.js'
10
+
11
+ function narrative(id: number, text: string): NarrativeStep {
12
+ return { id, text, state: 'done', startedAt: 0, toolCount: 0 }
13
+ }
14
+
15
+ function stateWith(narratives: NarrativeStep[]): ProgressCardState {
16
+ return {
17
+ turnStartedAt: 0,
18
+ items: [],
19
+ stage: 'idle',
20
+ thinking: false,
21
+ narratives,
22
+ subAgents: new Map(),
23
+ pendingAgentSpawns: new Map(),
24
+ } as unknown as ProgressCardState
25
+ }
26
+
27
+ describe('recoverProseFromProgressCard', () => {
28
+ it('returns empty string for undefined state', () => {
29
+ expect(recoverProseFromProgressCard(undefined)).toBe('')
30
+ })
31
+
32
+ it('returns empty string when there are no narratives', () => {
33
+ expect(recoverProseFromProgressCard(stateWith([]))).toBe('')
34
+ })
35
+
36
+ it('returns empty string when the narratives field is missing', () => {
37
+ // Defensive: partial state (e.g. older persisted shape) must not throw.
38
+ const partial = { turnStartedAt: 0, items: [], stage: 'idle', thinking: false } as unknown as ProgressCardState
39
+ expect(recoverProseFromProgressCard(partial)).toBe('')
40
+ })
41
+
42
+ it('joins narrative text in order, newline-separated', () => {
43
+ const state = stateWith([
44
+ narrative(1, 'Reading the file.'),
45
+ narrative(2, 'Found the issue.'),
46
+ narrative(3, 'Patching gateway.ts.'),
47
+ ])
48
+ expect(recoverProseFromProgressCard(state)).toBe(
49
+ 'Reading the file.\nFound the issue.\nPatching gateway.ts.',
50
+ )
51
+ })
52
+
53
+ it('skips empty-string narratives but preserves order of the rest', () => {
54
+ const state = stateWith([
55
+ narrative(1, 'first'),
56
+ narrative(2, ''),
57
+ narrative(3, 'third'),
58
+ ])
59
+ expect(recoverProseFromProgressCard(state)).toBe('first\nthird')
60
+ })
61
+
62
+ it('trims surrounding whitespace from the joined result', () => {
63
+ const state = stateWith([narrative(1, ' prose with edges ')])
64
+ expect(recoverProseFromProgressCard(state)).toBe('prose with edges')
65
+ })
66
+
67
+ it('recovers the original incident — single narrative line that should have flushed', () => {
68
+ // Mirrors the #45/#51 incident transcript: the assistant emitted
69
+ // prose-as-step but never called reply. Recovery must surface that
70
+ // text so the flush backstop can send it.
71
+ const state = stateWith([
72
+ narrative(1, 'Just the caption swap — the Klanker body stays.'),
73
+ ])
74
+ expect(recoverProseFromProgressCard(state)).toBe(
75
+ 'Just the caption swap — the Klanker body stays.',
76
+ )
77
+ })
78
+ })
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Unit coverage for the gateway-side turn-end flush safety net.
3
+ *
4
+ * The fix restores a deterministic "if the model produced assistant text
5
+ * but never called reply/stream_reply, send that text to Telegram at
6
+ * turn_end" rule, after it was silently gated off in the gateway by an
7
+ * earlier `progressDriver == null` condition. Ken hit this live three
8
+ * times in a row — turns ended with visible reasoning in the transcript
9
+ * but no Telegram bubble.
10
+ *
11
+ * These tests pin the pure decision function. The actual send path in
12
+ * gateway.ts is the same one used by the `reply` tool, so if this policy
13
+ * returns `{kind: 'flush', text}` we know the message will reach the chat.
14
+ */
15
+
16
+ import { describe, it, expect } from 'vitest'
17
+ import {
18
+ decideTurnFlush,
19
+ isSilentFlushMarker,
20
+ isTurnFlushSafetyEnabled,
21
+ } from '../turn-flush-safety.js'
22
+
23
+ describe('decideTurnFlush', () => {
24
+ it('(a) does NOT flush when the reply tool was called', () => {
25
+ const decision = decideTurnFlush({
26
+ chatId: '100',
27
+ replyCalled: true,
28
+ capturedText: ['here is the answer'],
29
+ })
30
+ expect(decision).toEqual({ kind: 'skip', reason: 'reply-called' })
31
+ })
32
+
33
+ it('(b) flushes when the turn produced text but the reply tool was never called', () => {
34
+ const decision = decideTurnFlush({
35
+ chatId: '200',
36
+ replyCalled: false,
37
+ capturedText: ['here is the answer', 'more detail'],
38
+ })
39
+ expect(decision).toEqual({
40
+ kind: 'flush',
41
+ text: 'here is the answer\nmore detail',
42
+ })
43
+ })
44
+
45
+ it('(c) HEARTBEAT_OK is a silent marker — flush suppressed', () => {
46
+ const decision = decideTurnFlush({
47
+ chatId: '300',
48
+ replyCalled: false,
49
+ capturedText: ['HEARTBEAT_OK'],
50
+ })
51
+ expect(decision).toEqual({ kind: 'skip', reason: 'silent-marker' })
52
+ })
53
+
54
+ it('NO_REPLY is a silent marker — flush suppressed', () => {
55
+ const decision = decideTurnFlush({
56
+ chatId: '301',
57
+ replyCalled: false,
58
+ capturedText: ['NO_REPLY'],
59
+ })
60
+ expect(decision).toEqual({ kind: 'skip', reason: 'silent-marker' })
61
+ })
62
+
63
+ it('silent markers match case-insensitively with whitespace trim', () => {
64
+ expect(
65
+ decideTurnFlush({
66
+ chatId: '302',
67
+ replyCalled: false,
68
+ capturedText: [' heartbeat_ok '],
69
+ }),
70
+ ).toEqual({ kind: 'skip', reason: 'silent-marker' })
71
+ })
72
+
73
+ it('(d) background sub-agent turn (chatId == null) is NOT flushed', () => {
74
+ // Sub-agent turns never populate `currentSessionChatId` — we surface
75
+ // that here as `chatId: null` and the decision must be skip.
76
+ const decision = decideTurnFlush({
77
+ chatId: null,
78
+ replyCalled: false,
79
+ capturedText: ['child agent result'],
80
+ })
81
+ expect(decision).toEqual({ kind: 'skip', reason: 'no-inbound-chat' })
82
+ })
83
+
84
+ it('empty captured text is NOT flushed', () => {
85
+ expect(
86
+ decideTurnFlush({
87
+ chatId: '400',
88
+ replyCalled: false,
89
+ capturedText: [],
90
+ }),
91
+ ).toEqual({ kind: 'skip', reason: 'empty-text' })
92
+ expect(
93
+ decideTurnFlush({
94
+ chatId: '400',
95
+ replyCalled: false,
96
+ capturedText: ['', ' ', '\n'],
97
+ }),
98
+ ).toEqual({ kind: 'skip', reason: 'empty-text' })
99
+ })
100
+
101
+ it('feature flag off disables flush even on a legitimate orphan', () => {
102
+ const decision = decideTurnFlush({
103
+ chatId: '500',
104
+ replyCalled: false,
105
+ capturedText: ['an answer'],
106
+ flushEnabled: false,
107
+ })
108
+ expect(decision).toEqual({ kind: 'skip', reason: 'flag-disabled' })
109
+ })
110
+
111
+ it('flushEnabled defaults to true when omitted', () => {
112
+ const decision = decideTurnFlush({
113
+ chatId: '600',
114
+ replyCalled: false,
115
+ capturedText: ['some answer'],
116
+ })
117
+ expect(decision.kind).toBe('flush')
118
+ })
119
+
120
+ it('prioritises flag-disabled over every other skip reason', () => {
121
+ expect(
122
+ decideTurnFlush({
123
+ chatId: null,
124
+ replyCalled: true,
125
+ capturedText: [],
126
+ flushEnabled: false,
127
+ }),
128
+ ).toEqual({ kind: 'skip', reason: 'flag-disabled' })
129
+ })
130
+
131
+ it('prioritises reply-called over chatId/empty checks', () => {
132
+ expect(
133
+ decideTurnFlush({
134
+ chatId: null,
135
+ replyCalled: true,
136
+ capturedText: [],
137
+ }),
138
+ ).toEqual({ kind: 'skip', reason: 'reply-called' })
139
+ })
140
+ })
141
+
142
+ describe('isSilentFlushMarker', () => {
143
+ it('recognises NO_REPLY and HEARTBEAT_OK exactly', () => {
144
+ expect(isSilentFlushMarker('NO_REPLY')).toBe(true)
145
+ expect(isSilentFlushMarker('HEARTBEAT_OK')).toBe(true)
146
+ expect(isSilentFlushMarker('no_reply')).toBe(true)
147
+ expect(isSilentFlushMarker(' HEARTBEAT_OK ')).toBe(true)
148
+ })
149
+
150
+ it('does NOT match prose containing the marker', () => {
151
+ expect(isSilentFlushMarker('the model said NO_REPLY earlier')).toBe(false)
152
+ expect(isSilentFlushMarker('HEARTBEAT_OK; shutting down')).toBe(false)
153
+ })
154
+
155
+ it('does not match undefined/empty', () => {
156
+ expect(isSilentFlushMarker(undefined)).toBe(false)
157
+ expect(isSilentFlushMarker('')).toBe(false)
158
+ expect(isSilentFlushMarker(' ')).toBe(false)
159
+ })
160
+
161
+ it('tolerates a single trailing non-word character (#299 review feedback)', () => {
162
+ expect(isSilentFlushMarker('NO_REPLY.')).toBe(true)
163
+ expect(isSilentFlushMarker('NO_REPLY!')).toBe(true)
164
+ expect(isSilentFlushMarker('HEARTBEAT_OK,')).toBe(true)
165
+ expect(isSilentFlushMarker(' HEARTBEAT_OK. ')).toBe(true)
166
+ // Two trailing punctuation chars → not stripped, no match
167
+ expect(isSilentFlushMarker('NO_REPLY!!')).toBe(false)
168
+ })
169
+ })
170
+
171
+ describe('isTurnFlushSafetyEnabled', () => {
172
+ it('defaults to true when the env var is absent', () => {
173
+ expect(isTurnFlushSafetyEnabled({})).toBe(true)
174
+ })
175
+
176
+ it('disables on explicit false-y values', () => {
177
+ for (const v of ['0', 'false', 'off', 'no', 'FALSE', ' OFF ']) {
178
+ expect(isTurnFlushSafetyEnabled({ SWITCHROOM_TG_TURN_FLUSH_SAFETY: v }))
179
+ .toBe(false)
180
+ }
181
+ })
182
+
183
+ it('stays enabled for anything else (including "1", "true", unknown)', () => {
184
+ for (const v of ['1', 'true', 'on', 'yes', 'anything']) {
185
+ expect(isTurnFlushSafetyEnabled({ SWITCHROOM_TG_TURN_FLUSH_SAFETY: v }))
186
+ .toBe(true)
187
+ }
188
+ })
189
+ })