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,664 @@
1
+ /**
2
+ * `stream_reply` MCP tool handler — extracted from server.ts.
3
+ *
4
+ * The server.ts case block was ~110 lines of state-machine + I/O mixed
5
+ * together. This module pulls the logic into a pure-ish function with
6
+ * injected deps so it can be exercised by an integration test against
7
+ * the mock bot harness.
8
+ *
9
+ * Contract:
10
+ * - First call for a chat+thread: creates a stream via
11
+ * createStreamController, optionally prepending a handoff prefix.
12
+ * - Subsequent calls: reuse the existing stream, push the new text.
13
+ * - `done=true`: finalize, delete the map entry, fire status-reaction
14
+ * completion, and (if history enabled) record the final message.
15
+ * - Returns the message id + terminal status text that server.ts
16
+ * wraps into an MCP content response.
17
+ */
18
+
19
+ import type { DraftStreamHandle, StreamDraftFn } from './draft-stream.js'
20
+ import {
21
+ createStreamController,
22
+ type StreamBotApi,
23
+ type RetryPolicy,
24
+ } from './stream-controller.js'
25
+ import { sanitizeTelegramHtml } from './html-sanitize.js'
26
+
27
+ /**
28
+ * Builds the inline status-accent header line for `reply` / `stream_reply`.
29
+ *
30
+ * Returns a string to prepend to the effective (already-rendered) message
31
+ * body, including a trailing blank line so the header is visually separated.
32
+ * Returns an empty string when accent is undefined or unrecognised (silent
33
+ * ignore) so calls without `accent` produce identical output to today.
34
+ *
35
+ * The header uses Telegram HTML tags. Callers must ensure parseMode is HTML
36
+ * (or that the body has already been rendered to HTML) before prepending.
37
+ */
38
+ export function buildAccentHeader(accent: string | undefined): string {
39
+ switch (accent) {
40
+ case 'in-progress':
41
+ return '🔵 <i>In progress…</i>\n\n'
42
+ case 'done':
43
+ return '✅ <b>Done</b>\n\n'
44
+ case 'issue':
45
+ return '⚠️ <b>Issue</b>\n\n'
46
+ default:
47
+ return ''
48
+ }
49
+ }
50
+
51
+ export interface StreamReplyArgs {
52
+ chat_id: string
53
+ text: string
54
+ done?: boolean
55
+ message_thread_id?: string
56
+ format?: string
57
+ /**
58
+ * Optional named lane. Each lane gets its own Telegram message per
59
+ * chat+thread — useful for surfacing "thinking" alongside the main
60
+ * "answer" stream. Lane names are caller-defined. Omit for the
61
+ * default (unnamed) lane, which preserves legacy behavior.
62
+ */
63
+ lane?: string
64
+ /**
65
+ * Explicit quote-reply target. When set, the initial streamed message
66
+ * quote-threads under this message_id. Overrides the default auto-quote
67
+ * behavior and ignores `quote`.
68
+ */
69
+ reply_to?: string
70
+ /**
71
+ * Opt out of the default quote-reply behavior. The handler's default
72
+ * (when `reply_to` is unset) is to look up the latest inbound user
73
+ * message via `getLatestInboundMessageId` and quote-reply to it. Pass
74
+ * `false` to send a bare (non-quoted) streamed message.
75
+ *
76
+ * The default is `undefined` (treated as true) so callers that pre-date
77
+ * this feature keep working. Only the progress-card / activity-lane
78
+ * internal callers routinely opt out, since those aren't user-visible
79
+ * conversation replies.
80
+ */
81
+ quote?: boolean
82
+ /**
83
+ * Optional turn identifier used to multiplex concurrent turns on the
84
+ * same chat+thread+lane. Without this, two concurrent turns emitting
85
+ * on the same lane (e.g. both on lane:'progress') collapse into a
86
+ * single draft stream and their Telegram messages flap between each
87
+ * other. The progress-card driver passes its unique per-turn key here
88
+ * so each active turn gets its own draft stream + pinned card. Other
89
+ * lane callers may leave this undefined to preserve legacy behavior.
90
+ */
91
+ turnKey?: string
92
+ /**
93
+ * Inline keyboard markup. When set, every send and edit for this stream
94
+ * includes it so Telegram doesn't strip an attached keyboard on text
95
+ * updates. Used by the progress-card driver to persist the Steer button.
96
+ */
97
+ reply_markup?: unknown
98
+ /**
99
+ * When true, Telegram prevents the message from being forwarded or saved.
100
+ * Applied on the initial send only (editMessageText ignores it).
101
+ */
102
+ protect_content?: boolean
103
+ /**
104
+ * Optional surgical quote text. When set along with `reply_to`, the initial
105
+ * send includes `reply_parameters: { message_id, quote: { text, position: 0 } }`
106
+ * so Telegram highlights the specific quoted sentence rather than the whole
107
+ * referenced message. Ignored when `reply_to` is absent.
108
+ */
109
+ quote_text?: string
110
+ /**
111
+ * Optional status accent prepended as a leading header line (issue #320
112
+ * fallback for missing Telegram quote-bar color API).
113
+ *
114
+ * - `'in-progress'` → `🔵 <i>In progress…</i>\n\n`
115
+ * - `'done'` → `✅ <b>Done</b>\n\n`
116
+ * - `'issue'` → `⚠️ <b>Issue</b>\n\n`
117
+ *
118
+ * Unrecognised values are silently ignored. Omit for plain reply (default
119
+ * behavior — identical to today's output).
120
+ */
121
+ accent?: string
122
+ }
123
+
124
+ export interface StreamReplyState {
125
+ activeDraftStreams: Map<string, DraftStreamHandle>
126
+ /**
127
+ * Tracks the parseMode each active stream was created with, keyed the
128
+ * same way as `activeDraftStreams`. Used by `handleStreamReply` to
129
+ * detect when a subsequent call's resolved parseMode differs from the
130
+ * one baked into the existing stream controller — in that case the
131
+ * stale stream is finalized + discarded and a fresh one is created
132
+ * with the new parseMode (see bug 1: PTY-tail creates an activity-lane
133
+ * stream with parseMode=undefined; a later explicit stream_reply on
134
+ * the same key with format:'html' would otherwise inherit undefined
135
+ * and send literal markdown).
136
+ *
137
+ * Optional for backwards compatibility with external callers that
138
+ * construct a StreamReplyState without it.
139
+ */
140
+ activeDraftParseModes?: Map<string, 'HTML' | 'MarkdownV2' | undefined>
141
+ /**
142
+ * Chats whose PTY preview is claimed by an in-flight reply/stream_reply
143
+ * handler. PTY-tail partials for these keys are dropped to avoid
144
+ * duplicate messages. Historically only the `reply` tool added to this
145
+ * set; `stream_reply` did not, so a PTY partial firing after a
146
+ * finalized stream would create a duplicate message with the raw TUI
147
+ * text (see regression in telegram-plugin.log where msg 559 was
148
+ * followed by a duplicate msg 560 via path=pty_preview). stream_reply
149
+ * now claims the slot on the first call so later PTY partials are
150
+ * suppressed for the rest of the turn.
151
+ *
152
+ * Optional for backwards compatibility with callers that don't yet
153
+ * thread this state through — without it the bug reopens silently.
154
+ */
155
+ suppressPtyPreview?: Set<string>
156
+ }
157
+
158
+ export interface StreamReplyDeps {
159
+ bot: { api: StreamBotApi }
160
+ retry?: RetryPolicy
161
+ /** Markdown → HTML renderer (used when format === 'html'). */
162
+ markdownToHtml: (text: string) => string
163
+ /** MarkdownV2 escaper (used when format === 'markdownv2'). */
164
+ escapeMarkdownV2: (text: string) => string
165
+ /** Whitespace repair applied to the raw caller text. */
166
+ repairEscapedWhitespace: (text: string) => string
167
+ /** Resolves the handoff prefix for a first-chunk stream. Empty string if none. */
168
+ takeHandoffPrefix: (format: 'html' | 'markdownv2' | 'text') => string
169
+ /** Validates the chat id against the access list. Throws on deny. */
170
+ assertAllowedChat: (chatId: string) => void
171
+ /** Resolves the effective thread id (explicit, last-inbound, or undefined). */
172
+ resolveThreadId: (chatId: string, explicit?: string) => number | undefined
173
+ /**
174
+ * Resolves the default quote-reply target: the message_id of the latest
175
+ * inbound user message in this chat+thread, or null if none (empty
176
+ * history, or history disabled). Called only when the caller didn't
177
+ * pass `reply_to` and didn't opt out via `quote:false`. Optional —
178
+ * omit to disable the auto-quote default (legacy behavior).
179
+ */
180
+ getLatestInboundMessageId?: (chatId: string, threadId: number | null) => number | null
181
+ /** Config: disable link previews. Default true. */
182
+ disableLinkPreview: boolean
183
+ /** Config: fallback parse mode when args.format is omitted ('html' | 'markdownv2' | 'text'). */
184
+ defaultFormat: string
185
+ /** Observability: per-call event. */
186
+ logStreamingEvent: (ev: {
187
+ kind: 'stream_reply_called'
188
+ chatId: string
189
+ charCount: number
190
+ done: boolean
191
+ streamExisted: boolean
192
+ } | {
193
+ kind: 'draft_send'
194
+ chatId: string
195
+ messageId: number
196
+ charCount: number
197
+ } | {
198
+ kind: 'draft_edit'
199
+ chatId: string
200
+ messageId: number
201
+ charCount: number
202
+ sameAsLast: boolean
203
+ }) => void
204
+ /** Called on done=true to transition the status reaction controller. */
205
+ endStatusReaction: (chatId: string, threadId: number | undefined, verdict: 'done') => void
206
+ /**
207
+ * Optional: progress-card driver completion hook. Wired by the gateway
208
+ * to `progressDriver.forceCompleteTurn(...)`. Invoked after a
209
+ * `stream_reply(done=true)` on the default (unnamed) lane finalizes,
210
+ * so the final-answer delivery acts as an authoritative turn-complete
211
+ * signal equal to session-tail `turn_end`. Skipped when args.lane is
212
+ * 'progress' (that's the driver's own emit — calling this would cause
213
+ * re-entry). Safe to leave unset for callers that don't use the driver.
214
+ */
215
+ forceCompleteTurn?: (chatId: string, threadId: number | undefined) => void
216
+ /**
217
+ * Optional: progress-card driver delivery counter hook. Wired by the
218
+ * gateway to `progressDriver.recordOutboundDelivered(...)`. Called
219
+ * BEFORE `forceCompleteTurn` so the driver's per-turn outbound counter
220
+ * is non-zero when the terminal render fires. Without this ordering
221
+ * guarantee, `forceCompleteTurn` flushes the card while
222
+ * `outboundDeliveredCount === 0` → ⚠️ false positive (issue #310).
223
+ * Only called on the default (unnamed) lane when `done=true` and the
224
+ * stream produced a non-null messageId. Safe to leave unset for callers
225
+ * that don't use the driver.
226
+ */
227
+ recordOutboundDelivered?: (chatId: string, threadId: number | undefined) => void
228
+ /** Whether to persist outbound history. */
229
+ historyEnabled: boolean
230
+ /** History row writer. Only called when historyEnabled && done && messageId != null. */
231
+ recordOutbound: (row: {
232
+ chat_id: string
233
+ thread_id: number | null
234
+ message_ids: number[]
235
+ texts: string[]
236
+ }) => void
237
+ /** Error-path stderr. */
238
+ writeError: (line: string) => void
239
+ throttleMs?: number
240
+ /**
241
+ * sendMessageDraft callback. When provided, stream_reply uses the draft
242
+ * API for intermediate updates (DM transport). On done=true, a real
243
+ * sendMessage fires for push notification, then the draft is cleared.
244
+ * Optional — omit to keep the existing sendMessage/editMessageText path.
245
+ */
246
+ sendMessageDraft?: StreamDraftFn
247
+ /**
248
+ * Idempotency hook for the duplicate-message class (issue #626).
249
+ *
250
+ * On every call where the handler would CREATE a new stream (no entry
251
+ * in `state.activeDraftStreams[sKey]`), this callback is consulted to
252
+ * see whether an external authority already knows the anchor message
253
+ * id for this lane+turn. If it returns a number, the new stream is
254
+ * initialized as if a previous send had landed with that id — the
255
+ * very next update fires `editMessageText` instead of `sendMessage`.
256
+ *
257
+ * Wired by the gateway to `pinMgr.pinnedMessageId(turnKey, agentId)`
258
+ * for the progress card. Without this hook, a `done=true` finalize
259
+ * deletes `activeDraftStreams[sKey]`, and the next emit on the same
260
+ * turn creates a fresh sendMessage — visible to the user as a second
261
+ * "status message" landing instead of an edit. The not-found
262
+ * fallback in draft-stream gracefully handles a stale id.
263
+ *
264
+ * Safe to omit. Optional. Returns null/undefined to fall through to
265
+ * the standard "first call sends" behavior.
266
+ */
267
+ lookupExistingMessageId?: (key: {
268
+ chatId: string
269
+ threadId: number | undefined
270
+ lane: string | undefined
271
+ turnKey: string | undefined
272
+ }) => number | null | undefined
273
+ /**
274
+ * True when the current chat is a private DM. Passed to the stream
275
+ * controller so "auto" transport activates draft in DMs only.
276
+ */
277
+ isPrivateChat?: boolean
278
+ /**
279
+ * True when the current chat is a forum topic. Forum topics do not
280
+ * support sendMessageDraft — this forces message transport.
281
+ */
282
+ isForumTopic?: boolean
283
+ /**
284
+ * When true, the progress-card driver is emitting a live checklist on
285
+ * the `progress` lane and owns mid-turn display. In that mode, a
286
+ * caller-initiated `stream_reply` on the default (unnamed) lane with
287
+ * `done=false` is suppressed — the card already shows what's happening,
288
+ * and a parallel default-lane message is visible noise (a duplicate
289
+ * surface for the same turn). The final `done=true` call still posts
290
+ * as the answer message.
291
+ *
292
+ * Named-lane calls (lane: 'progress', 'thinking', etc.) are always
293
+ * honored — this flag only gates the default lane. Omit or leave false
294
+ * to preserve legacy behavior.
295
+ */
296
+ progressCardActive?: boolean
297
+ }
298
+
299
+ export interface StreamReplyResult {
300
+ messageId: number | null
301
+ status: 'updated' | 'finalized'
302
+ }
303
+
304
+ function streamKey(
305
+ chatId: string,
306
+ threadId?: number,
307
+ lane?: string,
308
+ turnKey?: string,
309
+ ): string {
310
+ const base = `${chatId}:${threadId ?? '_'}`
311
+ const withLane = lane != null && lane.length > 0 ? `${base}:${lane}` : base
312
+ return turnKey != null && turnKey.length > 0 ? `${withLane}:${turnKey}` : withLane
313
+ }
314
+
315
+ export async function handleStreamReply(
316
+ args: StreamReplyArgs,
317
+ state: StreamReplyState,
318
+ deps: StreamReplyDeps,
319
+ ): Promise<StreamReplyResult> {
320
+ const chat_id = args.chat_id
321
+ const rawText = deps.repairEscapedWhitespace(args.text)
322
+ const done = Boolean(args.done)
323
+ const format = args.format ?? deps.defaultFormat
324
+ if (done) {
325
+ process.stderr.write(`telegram channel: stream_reply: invoked done=true chatId=${chat_id} lane=${args.lane ?? 'default'} charCount=${rawText.length}\n`)
326
+ }
327
+
328
+ // Access check runs BEFORE any other branch: a denied chat id must
329
+ // throw regardless of streaming mode. Previously the suppression path
330
+ // silently "succeeded" for unauthorized chats.
331
+ deps.assertAllowedChat(chat_id)
332
+ const threadId = deps.resolveThreadId(chat_id, args.message_thread_id)
333
+
334
+ // Note: caller-initiated stream_reply(done=false) used to be rejected
335
+ // when the progress card was active. The card and the answer message
336
+ // live on different lanes (progress vs default) and render different
337
+ // content (tool structure vs model prose), so they don't actually
338
+ // collide — the rejection forced single-shot final replies and broke
339
+ // the progressive-streaming contract documented in
340
+ // profiles/default/CLAUDE.md. See #481.
341
+
342
+ let parseMode: 'HTML' | 'MarkdownV2' | undefined
343
+ let effectiveText: string
344
+ if (format === 'html') {
345
+ parseMode = 'HTML'
346
+ // Pre-validate the rendered HTML against Telegram's tag allowlist
347
+ // before send. The sanitizer escapes unknown tags, drops disallowed
348
+ // attributes, and auto-closes unbalanced tags so we don't trip
349
+ // Telegram's `400 Bad Request: can't parse entities` (issue #657).
350
+ effectiveText = sanitizeTelegramHtml(deps.markdownToHtml(rawText))
351
+ } else if (format === 'markdownv2') {
352
+ parseMode = 'MarkdownV2'
353
+ effectiveText = deps.escapeMarkdownV2(rawText)
354
+ } else {
355
+ parseMode = undefined
356
+ effectiveText = rawText
357
+ }
358
+
359
+ // Inline status-accent header (issue #320 fallback). Prepended AFTER
360
+ // format rendering so it leads the fully-rendered body. Since
361
+ // stream_reply callers pass the full text snapshot each call, the
362
+ // header is prepended on every call that supplies `accent` — this
363
+ // keeps the rendered message consistent across edits. Callers that
364
+ // don't want the header on a subsequent edit simply omit `accent`.
365
+ // Unrecognised values are silently ignored (empty string returned).
366
+ if (args.accent != null) {
367
+ const accentHeader = buildAccentHeader(args.accent)
368
+ if (accentHeader.length > 0) {
369
+ effectiveText = accentHeader + effectiveText
370
+ // Re-sanitize after prepending the HTML header so the combined
371
+ // body is still guaranteed parse-mode=HTML safe.
372
+ if (parseMode === 'HTML') {
373
+ effectiveText = sanitizeTelegramHtml(effectiveText)
374
+ }
375
+ }
376
+ }
377
+
378
+ // Over-limit pre-check. Throws BEFORE touching stream state so that
379
+ // (a) a first call over 4096 fails cleanly instead of creating a
380
+ // half-initialized stream, and (b) a mid-stream update over 4096
381
+ // fails loudly instead of setting the internal `stopped=true` flag
382
+ // and silently dropping all subsequent text. Either way the caller
383
+ // sees isError:true and can fall back to `reply`, which chunks.
384
+ // Check the rendered text (post-markdown-to-HTML) because that's
385
+ // what actually goes to Telegram's 4096-char wire limit.
386
+ if (effectiveText.length > 4096) {
387
+ throw new Error(
388
+ `stream_reply rejected: text exceeds Telegram's 4096-char limit ` +
389
+ `(length=${effectiveText.length}, format=${format}). stream_reply does not ` +
390
+ `auto-chunk — split the text or use \`reply\`, which chunks.`,
391
+ )
392
+ }
393
+
394
+ const sKey = streamKey(chat_id, threadId, args.lane, args.turnKey)
395
+ // Claim the PTY-preview slot so any PTY-tail partial that fires mid-
396
+ // or post-turn for this chat+thread is dropped. Keyed WITHOUT lane
397
+ // because the PTY handler uses the lane-less key and we need to
398
+ // suppress its default lane regardless of which lane stream_reply
399
+ // targets. Cleared on turn_end by server.ts.
400
+ state.suppressPtyPreview?.add(streamKey(chat_id, threadId))
401
+ let stream = state.activeDraftStreams.get(sKey)
402
+
403
+ // Bug 1 fix: parseMode is baked into the stream controller at creation
404
+ // time. If a prior call created the stream with a different parseMode
405
+ // (e.g. PTY-tail auto-stream using 'text' → undefined, followed by an
406
+ // explicit stream_reply with format:'html'), reusing it would send
407
+ // literal markdown. Finalize + discard the stale stream so the block
408
+ // below creates a fresh one with the correct parseMode.
409
+ if (stream != null && state.activeDraftParseModes != null) {
410
+ const existingParseMode = state.activeDraftParseModes.get(sKey)
411
+ if (existingParseMode !== parseMode) {
412
+ try {
413
+ await stream.finalize()
414
+ } catch (err) {
415
+ // Best-effort: the in-flight edit may 429 or race. Surface to
416
+ // stderr so the orphaned message id isn't invisible.
417
+ deps.writeError(
418
+ `telegram channel: stream_reply parseMode-rotation finalize failed: ${err}\n`,
419
+ )
420
+ }
421
+ state.activeDraftStreams.delete(sKey)
422
+ state.activeDraftParseModes.delete(sKey)
423
+ stream = undefined
424
+ }
425
+ }
426
+
427
+ const streamExisted = stream != null
428
+
429
+ deps.logStreamingEvent({
430
+ kind: 'stream_reply_called',
431
+ chatId: chat_id,
432
+ charCount: effectiveText.length,
433
+ done,
434
+ streamExisted,
435
+ })
436
+
437
+ // First chunk of a session: consume any pending handoff prefix.
438
+ if (!stream) {
439
+ const prefix = deps.takeHandoffPrefix(
440
+ format === 'html' ? 'html' : format === 'markdownv2' ? 'markdownv2' : 'text',
441
+ )
442
+ if (prefix.length > 0) effectiveText = prefix + effectiveText
443
+ }
444
+
445
+ if (!stream) {
446
+ // Resolve the effective quote-reply target. Explicit `reply_to` wins;
447
+ // otherwise (unless the caller opted out with `quote:false`) fall back
448
+ // to the latest inbound user message in this chat+thread. Resolved
449
+ // only on stream creation — subsequent `stream_reply` calls for the
450
+ // same turn edit the existing message, which Telegram doesn't allow
451
+ // us to add a quote reference to retroactively.
452
+ let replyToMessageId: number | undefined
453
+ if (args.reply_to != null) {
454
+ replyToMessageId = Number(args.reply_to)
455
+ } else if (args.quote !== false && deps.getLatestInboundMessageId != null) {
456
+ try {
457
+ const latest = deps.getLatestInboundMessageId(chat_id, threadId ?? null)
458
+ if (latest != null) replyToMessageId = latest
459
+ } catch (err) {
460
+ deps.writeError(
461
+ `telegram channel: stream_reply quote-lookup failed: ${err}\n`,
462
+ )
463
+ }
464
+ }
465
+
466
+ // Resolve draft-transport options. Forum topics force message transport
467
+ // because sendMessageDraft does not support threads.
468
+ const isForumTopic = deps.isForumTopic === true
469
+ const resolvedTransport: 'auto' | 'message' | 'draft' =
470
+ isForumTopic || deps.sendMessageDraft == null
471
+ ? 'message'
472
+ : 'auto'
473
+
474
+ // Idempotency hook (#626): if an external authority (e.g. the
475
+ // gateway's pin manager) already knows the anchor message id for
476
+ // this lane+turn, initialize the stream with it so the next update
477
+ // edits in place rather than creating a fresh sendMessage. This
478
+ // closes the "done=true → activeDraftStreams entry deleted → next
479
+ // emit creates fresh sendMessage" path that produced multiple
480
+ // status messages per turn.
481
+ let initialMessageId: number | undefined
482
+ if (deps.lookupExistingMessageId != null) {
483
+ try {
484
+ const looked = deps.lookupExistingMessageId({
485
+ chatId: chat_id,
486
+ threadId,
487
+ lane: args.lane,
488
+ turnKey: args.turnKey,
489
+ })
490
+ if (typeof looked === 'number' && Number.isFinite(looked)) {
491
+ initialMessageId = looked
492
+ }
493
+ } catch (err) {
494
+ deps.writeError(
495
+ `telegram channel: stream_reply lookupExistingMessageId failed: ${err}\n`,
496
+ )
497
+ }
498
+ }
499
+
500
+ stream = createStreamController({
501
+ bot: deps.bot,
502
+ chatId: chat_id,
503
+ threadId,
504
+ parseMode,
505
+ disableLinkPreview: deps.disableLinkPreview,
506
+ throttleMs: deps.throttleMs ?? 600,
507
+ retry: deps.retry,
508
+ ...(replyToMessageId != null ? { replyToMessageId } : {}),
509
+ ...(args.quote_text != null && replyToMessageId != null ? { quoteText: args.quote_text } : {}),
510
+ ...(args.protect_content === true ? { protectContent: true } : {}),
511
+ ...(args.reply_markup != null ? { replyMarkup: args.reply_markup } : {}),
512
+ previewTransport: resolvedTransport,
513
+ isPrivateChat: deps.isPrivateChat === true,
514
+ ...(deps.sendMessageDraft != null ? { sendMessageDraft: deps.sendMessageDraft } : {}),
515
+ ...(initialMessageId != null ? { initialMessageId } : {}),
516
+ onSend: (messageId, charCount) =>
517
+ deps.logStreamingEvent({ kind: 'draft_send', chatId: chat_id, messageId, charCount }),
518
+ onEdit: (messageId, charCount) =>
519
+ deps.logStreamingEvent({
520
+ kind: 'draft_edit',
521
+ chatId: chat_id,
522
+ messageId,
523
+ charCount,
524
+ sameAsLast: false,
525
+ }),
526
+ // Route draft-stream diagnostics through the handler's stderr
527
+ // writer so transient failures are observable. Filter routine
528
+ // success chatter (sent/edited/finalized) — those are already
529
+ // captured by the structured onSend/onEdit observers — and only
530
+ // surface warnings/errors (stopped, edit failed, not-found
531
+ // recovery).
532
+ log: (msg) => {
533
+ if (
534
+ msg.startsWith('stream → sent')
535
+ || msg.startsWith('stream → edited')
536
+ || msg.startsWith('stream → not modified')
537
+ || msg.startsWith('stream finalized')
538
+ || msg.startsWith('stream → draft')
539
+ || msg.startsWith('stream → materialized')
540
+ ) return
541
+ deps.writeError(`telegram channel: stream_reply ${msg}\n`)
542
+ },
543
+ warn: (msg) => {
544
+ deps.writeError(`telegram channel: stream_reply ${msg}\n`)
545
+ },
546
+ })
547
+ state.activeDraftStreams.set(sKey, stream)
548
+ state.activeDraftParseModes?.set(sKey, parseMode)
549
+ }
550
+
551
+ await stream.update(effectiveText)
552
+
553
+ if (done) {
554
+ await stream.finalize()
555
+ state.activeDraftStreams.delete(sKey)
556
+ state.activeDraftParseModes?.delete(sKey)
557
+ // Bug Z fix: fire the terminal 👍 here, gated to the default
558
+ // (unnamed) lane and only after finalize() resolves so the emoji is
559
+ // tied to the final edit actually landing in Telegram.
560
+ //
561
+ // History (see prior comment removed in this commit): we previously
562
+ // deferred the 👍 to turn_end on the theory that a turn might call
563
+ // stream_reply(done=true) mid-flight and continue working. In
564
+ // practice the dedup-suppress branch in turn_end was firing setDone
565
+ // off a 500ms-lagged read of local history rather than from a real
566
+ // delivery confirmation, and the disconnect-flush path was leaving
567
+ // 👍 firing on disconnect even when the final edit had failed.
568
+ // Wiring endStatusReaction at the post-finalize callback keeps the
569
+ // emoji honest — it now means "the final draft edit hit Telegram".
570
+ //
571
+ // Gated to the default lane: named lanes (lane:'progress',
572
+ // lane:'thinking', lane:'activity', etc.) are internal driver
573
+ // emits, not user-visible answers — they must not be allowed to
574
+ // claim turn-completion. A lane:'progress' emit firing setDone
575
+ // would race the actual answer.
576
+ //
577
+ // Mid-turn done=true tradeoff: a turn that calls
578
+ // stream_reply(done=true) on the default lane and then continues
579
+ // working will fire 👍 early; subsequent intermediate emojis become
580
+ // no-ops (setDone is terminal). This is the contract: done=true on
581
+ // the default lane is "the answer is delivered". Agents that need
582
+ // to continue should use additional `reply` calls or named lanes.
583
+ const isDefaultLaneForCompletion = args.lane == null || args.lane.length === 0
584
+ if (isDefaultLaneForCompletion && stream.getMessageId() != null) {
585
+ try {
586
+ deps.endStatusReaction(chat_id, threadId, 'done')
587
+ } catch (err) {
588
+ deps.writeError(`telegram channel: stream_reply: endStatusReaction hook threw: ${err}\n`)
589
+ }
590
+ }
591
+
592
+ // Hard-fail surface: if the stream finalized without ever assigning
593
+ // a message id, the initial send never landed (4096+ chars hits
594
+ // draft-stream's length guard and silently stops). Throw so the MCP
595
+ // caller sees isError:true instead of a misleading "finalized
596
+ // (id: pending)". The caller can fall back to `reply`, which chunks.
597
+ if (stream.getMessageId() == null) {
598
+ throw new Error(
599
+ `stream_reply finalized without sending any message (length=${rawText.length}, ` +
600
+ `max=4096). Telegram's per-message limit is 4096 chars and stream_reply does not ` +
601
+ `auto-chunk. Split the text or use \`reply\` (which chunks).`,
602
+ )
603
+ }
604
+
605
+ if (deps.historyEnabled) {
606
+ const finalId = stream.getMessageId()
607
+ if (finalId != null) {
608
+ try {
609
+ deps.recordOutbound({
610
+ chat_id,
611
+ thread_id: threadId ?? null,
612
+ message_ids: [finalId],
613
+ texts: [rawText],
614
+ })
615
+ } catch (err) {
616
+ deps.writeError(
617
+ `telegram channel: history recordOutbound (stream_reply) failed: ${err}\n`,
618
+ )
619
+ }
620
+ }
621
+ }
622
+ }
623
+
624
+ const finalMessageId = stream.getMessageId()
625
+ if (done) {
626
+ process.stderr.write(`telegram channel: stream_reply: finalized done=true chatId=${chat_id} lane=${args.lane ?? 'default'} messageId=${finalMessageId ?? 'null'}\n`)
627
+ const isDefaultLaneForCompletion = args.lane == null || args.lane.length === 0
628
+ // Issue #310: record delivery BEFORE forceCompleteTurn so the
629
+ // progress-card driver's outboundDeliveredCount is non-zero when the
630
+ // terminal render fires. forceCompleteTurn synchronously flushes the
631
+ // card; if we recorded after that flush, outboundDeliveredCount would
632
+ // still be 0 at render time → ⚠️ false positive even though the
633
+ // message landed. Only record when a messageId is confirmed — a null
634
+ // id means the send never succeeded and the ⚠️ branch is correct.
635
+ if (
636
+ deps.recordOutboundDelivered != null
637
+ && finalMessageId != null
638
+ && isDefaultLaneForCompletion
639
+ ) {
640
+ try {
641
+ deps.recordOutboundDelivered(chat_id, threadId)
642
+ } catch (err) {
643
+ deps.writeError(`telegram channel: stream_reply: recordOutboundDelivered hook threw: ${err}\n`)
644
+ }
645
+ }
646
+ // Fire the authoritative turn-complete signal to the progress-card
647
+ // driver so any in-flight card for this chat is closed out alongside
648
+ // the final answer landing. Only for the default (unnamed) lane —
649
+ // the progress lane is the driver's own emit path and routing
650
+ // completion through it would re-enter the driver from within its
651
+ // own flush. Idempotent on the driver side: first caller wins.
652
+ if (deps.forceCompleteTurn != null && isDefaultLaneForCompletion) {
653
+ try {
654
+ deps.forceCompleteTurn(chat_id, threadId)
655
+ } catch (err) {
656
+ deps.writeError(`telegram channel: stream_reply: forceCompleteTurn hook threw: ${err}\n`)
657
+ }
658
+ }
659
+ }
660
+ return {
661
+ messageId: finalMessageId,
662
+ status: done ? 'finalized' : 'updated',
663
+ }
664
+ }