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,324 @@
1
+ /**
2
+ * Shared bot runtime helpers — extracted from gateway.ts so both the
3
+ * per-agent gateway and the foreman bot can share the same core plumbing
4
+ * without duplicating code.
5
+ *
6
+ * What lives here:
7
+ * - `createRobustApiCall` — thin re-export of createRetryApiCall pre-wired
8
+ * with stderr logging (mirrors how gateway.ts constructs `robustApiCall`).
9
+ * - `makeSwitchroomExec` / `makeSwitchroomExecCombined` — factory fns for
10
+ * the switchroom CLI exec helpers (callers pass their own CLI path / config
11
+ * env so each process can be configured independently).
12
+ * - `escapeHtmlForTg`, `preBlock`, `stripAnsi`, `formatSwitchroomOutput` —
13
+ * pure text-formatting helpers used by both gateways.
14
+ * - `makeSwitchroomReply` — factory that returns a `switchroomReply`-like
15
+ * function bound to a thread-resolver; gateway keeps its own resolver.
16
+ * - `runPollingLoop` — thin wrapper around the grammyjs/runner `run()` call
17
+ * with built-in 409 retry logic, matching the loop in gateway.ts.
18
+ *
19
+ * IMPORTANT: This module MUST NOT import anything from gateway.ts — the
20
+ * dependency is the other way around. Only import from grammy, node builtins,
21
+ * or other telegram-plugin/shared or telegram-plugin/*.ts modules.
22
+ */
23
+
24
+ import { GrammyError, type Bot, type Context } from 'grammy'
25
+ import { run, type RunnerHandle } from '@grammyjs/runner'
26
+ import { execFileSync, spawnSync } from 'child_process'
27
+ import { createHash } from 'crypto'
28
+ import { clearStaleTelegramPollingState } from '../startup-reset.js'
29
+ import { createRetryApiCall } from '../retry-api-call.js'
30
+
31
+ // ─── tg-post observability transformer ────────────────────────────────────
32
+
33
+ /**
34
+ * Installs an API transformer on the bot that emits one stderr line per
35
+ * outbound Telegram Bot API POST. This is the single catchment point for
36
+ * correlating user-visible duplicate-message reports (switchroom #656,
37
+ * #657) against the actual outbound calls — the transformer runs inside
38
+ * grammY immediately before each HTTP POST and again on the response, so
39
+ * it sees every call regardless of whether it was routed through the
40
+ * `robustApiCall` retry helper or made directly via `bot.api.*`.
41
+ *
42
+ * Log shape (one line per POST, on both success and failure):
43
+ *
44
+ * tg-post method=<m> chat=<id> thread=<id|-> parse_mode=<HTML|MarkdownV2|none> bytes=<n> hash=<sha1-12> status=<ok|err> err=<class-or--> code=<http-or--> desc=<short|-->
45
+ *
46
+ * Body content is never logged — only its length and a 12-char sha1 prefix
47
+ * so we can recognise repeated identical sends without leaking PII. The
48
+ * `code` field carries the Telegram error_code (400/403/429/etc.) on
49
+ * failure and the short `desc` is the first ~80 chars of the API
50
+ * description — together these let us correlate "duplicate message"
51
+ * reports with the precise rejection reason (issue #657).
52
+ *
53
+ * Pure observability: no behaviour change, no error swallowing, no retry
54
+ * effects. The transformer always re-throws after logging.
55
+ */
56
+ export function installTgPostLogger(bot: Bot): void {
57
+ bot.api.config.use(async (prev, method, payload, signal) => {
58
+ const p = (payload ?? {}) as Record<string, unknown>
59
+ const chat = p.chat_id != null ? String(p.chat_id) : '-'
60
+ const thread = p.message_thread_id != null ? String(p.message_thread_id) : '-'
61
+ const parseMode = (p.parse_mode as string | undefined) ?? 'none'
62
+ const text = typeof p.text === 'string' ? p.text : ''
63
+ const bytes = text.length
64
+ const hash = bytes > 0
65
+ ? createHash('sha1').update(text).digest('hex').slice(0, 12)
66
+ : '-'
67
+ try {
68
+ const res = await prev(method, payload, signal)
69
+ process.stderr.write(
70
+ `tg-post method=${method} chat=${chat} thread=${thread} parse_mode=${parseMode} bytes=${bytes} hash=${hash} status=ok err=- code=- desc=-\n`,
71
+ )
72
+ return res
73
+ } catch (err) {
74
+ const errClass = err instanceof GrammyError
75
+ ? `grammy_${(err as GrammyError).error_code}`
76
+ : (err as { constructor?: { name?: string } } | null)?.constructor?.name ?? 'Error'
77
+ const code = err instanceof GrammyError ? String((err as GrammyError).error_code) : '-'
78
+ const rawDesc = err instanceof GrammyError
79
+ ? (err as GrammyError).description
80
+ : (err instanceof Error ? err.message : '')
81
+ // Sanitise the description for single-line log output — collapse
82
+ // whitespace, strip newlines, cap at 80 chars. PII-safe: Telegram
83
+ // error descriptions are server-generated and don't echo body.
84
+ const desc = rawDesc
85
+ ? rawDesc.replace(/\s+/g, ' ').slice(0, 80).replace(/[\r\n]/g, ' ') || '-'
86
+ : '-'
87
+ process.stderr.write(
88
+ `tg-post method=${method} chat=${chat} thread=${thread} parse_mode=${parseMode} bytes=${bytes} hash=${hash} status=err err=${errClass} code=${code} desc=${desc}\n`,
89
+ )
90
+ throw err
91
+ }
92
+ })
93
+ }
94
+
95
+ // ─── robustApiCall factory ────────────────────────────────────────────────
96
+
97
+ /**
98
+ * Creates a robust API call wrapper pre-wired with stderr logging.
99
+ * This is exactly how gateway.ts constructs its `robustApiCall`.
100
+ *
101
+ * Usage:
102
+ * const robustApiCall = createRobustApiCall()
103
+ */
104
+ export function createRobustApiCall() {
105
+ return createRetryApiCall({
106
+ log: (line) => process.stderr.write(line),
107
+ })
108
+ }
109
+
110
+ // ─── HTML escape helpers ─────────────────────────────────────────────────
111
+
112
+ export function escapeHtmlForTg(text: string): string {
113
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
114
+ }
115
+
116
+ export function preBlock(text: string): string {
117
+ return '<pre>' + escapeHtmlForTg(text) + '</pre>'
118
+ }
119
+
120
+ export function stripAnsi(text: string): string {
121
+ return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '')
122
+ }
123
+
124
+ export function formatSwitchroomOutput(output: string, maxLen = 4000): string {
125
+ const trimmed = output.trim()
126
+ if (trimmed.length <= maxLen) return trimmed
127
+ return trimmed.slice(0, maxLen - 20) + '\n... (truncated)'
128
+ }
129
+
130
+ // ─── CLI exec factories ───────────────────────────────────────────────────
131
+
132
+ export interface CliConfig {
133
+ /** Path to the switchroom CLI binary. Defaults to 'switchroom'. */
134
+ cliPath?: string
135
+ /** Optional --config path forwarded to every CLI invocation. */
136
+ configPath?: string
137
+ }
138
+
139
+ /** Returns a function that calls the CLI and returns stdout. */
140
+ export function makeSwitchroomExec(cfg: CliConfig = {}) {
141
+ const cli = cfg.cliPath ?? process.env.SWITCHROOM_CLI_PATH ?? 'switchroom'
142
+ const config = cfg.configPath ?? process.env.SWITCHROOM_CONFIG
143
+
144
+ return function switchroomExec(args: string[], timeoutMs = 15000): string {
145
+ const fullArgs = config ? ['--config', config, ...args] : args
146
+ return execFileSync(cli, fullArgs, {
147
+ encoding: 'utf-8',
148
+ timeout: timeoutMs,
149
+ env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
150
+ maxBuffer: 4 * 1024 * 1024,
151
+ })
152
+ }
153
+ }
154
+
155
+ /** Returns a function that calls the CLI with stderr merged into stdout. */
156
+ export function makeSwitchroomExecCombined(cfg: CliConfig = {}) {
157
+ const cli = cfg.cliPath ?? process.env.SWITCHROOM_CLI_PATH ?? 'switchroom'
158
+ const config = cfg.configPath ?? process.env.SWITCHROOM_CONFIG
159
+
160
+ // Pre-#28 fix this used `execSync(\`${quoted} 2>&1\`, { shell: '/bin/bash' })`,
161
+ // hand-quoting each argument. The shell-quoting was correct today, but the
162
+ // structural shape meant any future caller passing user-controlled input
163
+ // would re-introduce a command-injection class of bug. spawnSync with
164
+ // argv array eliminates the shell entirely; we then concat stdout + stderr
165
+ // ourselves to preserve the merged-output contract callers depend on.
166
+ return function switchroomExecCombined(args: string[], timeoutMs = 15000): string {
167
+ const fullArgs = config ? ['--config', config, ...args] : args
168
+ const result = spawnSync(cli, fullArgs, {
169
+ encoding: 'utf-8',
170
+ timeout: timeoutMs,
171
+ env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
172
+ maxBuffer: 4 * 1024 * 1024,
173
+ })
174
+ const stdout = (result.stdout as string | undefined) ?? ''
175
+ const stderr = (result.stderr as string | undefined) ?? ''
176
+ const merged = stderr.length > 0 ? stdout + stderr : stdout
177
+ if (result.error) throw result.error
178
+ if (result.status !== 0) {
179
+ // Mirror execSync's behaviour: throw on non-zero exit, attaching the
180
+ // merged output so callers (which catch and inspect .stdout) can read it.
181
+ const err = new Error(`Command failed: ${cli} ${fullArgs.join(' ')}`) as Error & {
182
+ stdout?: string
183
+ stderr?: string
184
+ status?: number | null
185
+ }
186
+ err.stdout = merged
187
+ err.stderr = stderr
188
+ err.status = result.status
189
+ throw err
190
+ }
191
+ return merged
192
+ }
193
+ }
194
+
195
+ /** Returns a CLI exec wrapper that parses JSON output (--json flag). */
196
+ export function makeSwitchroomExecJson(cfg: CliConfig = {}) {
197
+ const exec = makeSwitchroomExec(cfg)
198
+ return function switchroomExecJson<T = unknown>(args: string[]): T | null {
199
+ try {
200
+ const output = exec([...args, '--json'])
201
+ return JSON.parse(output) as T
202
+ } catch {
203
+ return null
204
+ }
205
+ }
206
+ }
207
+
208
+ // ─── Reply helper factory ─────────────────────────────────────────────────
209
+
210
+ import { InlineKeyboard } from 'grammy'
211
+
212
+ export type SwitchroomReplyMarkup =
213
+ | InlineKeyboard
214
+ | { force_reply: true; input_field_placeholder?: string; selective?: boolean }
215
+
216
+ /**
217
+ * Creates a `switchroomReply` function that sends an HTML reply to the
218
+ * chat in `ctx`, optionally threaded.
219
+ *
220
+ * @param resolveThreadId - returns the thread ID to use for the given
221
+ * chat_id + optional explicit thread (mirrors gateway's resolveThreadId).
222
+ * Pass `() => undefined` for bots that don't use forum topics.
223
+ */
224
+ export function makeSwitchroomReply(
225
+ resolveThreadId: (chatId: string, explicit?: number | null) => number | undefined,
226
+ ) {
227
+ return async function switchroomReply(
228
+ ctx: Context,
229
+ text: string,
230
+ options: { html?: boolean; reply_markup?: SwitchroomReplyMarkup } = {},
231
+ ): Promise<void> {
232
+ const chatId = String(ctx.chat!.id)
233
+ const threadId = resolveThreadId(chatId, ctx.message?.message_thread_id)
234
+ await ctx.reply(text, {
235
+ ...(threadId != null ? { message_thread_id: threadId } : {}),
236
+ ...(options.html ? { parse_mode: 'HTML' as const, link_preview_options: { is_disabled: true } } : {}),
237
+ ...(options.reply_markup ? { reply_markup: options.reply_markup } : {}),
238
+ })
239
+ }
240
+ }
241
+
242
+ // ─── Polling loop ─────────────────────────────────────────────────────────
243
+
244
+ export interface PollingLoopCallbacks {
245
+ /** Fired once after `getMe()` on the first (non-409) attempt. */
246
+ onReady?: (botUsername: string, botId: number) => void | Promise<void>
247
+ /**
248
+ * Fired exactly once per process lifetime (not on 409 retries) after
249
+ * `onReady`. Use for one-time startup work (command registration, sweeps,
250
+ * intervals).
251
+ */
252
+ onOneTimeSetup?: (botUsername: string) => void | Promise<void>
253
+ /** Fired when the polling loop exits cleanly (runner task resolved). */
254
+ onStop?: () => void | Promise<void>
255
+ /** Called each time a 409 is detected (useful for logging). */
256
+ on409?: (attempt: number, delayMs: number) => void
257
+ }
258
+
259
+ /**
260
+ * Runs a grammyjs/runner polling loop with built-in 409 retry backoff,
261
+ * matching the loop structure in gateway.ts.
262
+ *
263
+ * Returns the RunnerHandle so callers can call `.stop()` on SIGTERM.
264
+ *
265
+ * The promise resolves when the polling loop exits cleanly.
266
+ * The promise rejects on non-409, non-Aborted errors.
267
+ */
268
+ export async function runPollingLoop(
269
+ bot: Bot,
270
+ callbacks: PollingLoopCallbacks = {},
271
+ ): Promise<void> {
272
+ let didOneTimeSetup = false
273
+
274
+ for (let attempt = 1; ; attempt++) {
275
+ try {
276
+ await clearStaleTelegramPollingState(bot.api)
277
+
278
+ const me = await bot.api.getMe()
279
+ process.stderr.write(`bot-runtime: polling as @${me.username}\n`)
280
+
281
+ if (callbacks.onReady) {
282
+ await callbacks.onReady(me.username ?? '', me.id)
283
+ }
284
+
285
+ if (!didOneTimeSetup) {
286
+ didOneTimeSetup = true
287
+ if (callbacks.onOneTimeSetup) {
288
+ await callbacks.onOneTimeSetup(me.username ?? '')
289
+ }
290
+ }
291
+
292
+ process.stderr.write(`bot-runtime: starting runner pid=${process.pid}\n`)
293
+ const handle: RunnerHandle = run(bot)
294
+ await handle.task()
295
+ if (callbacks.onStop) await callbacks.onStop()
296
+ return
297
+ } catch (err) {
298
+ if (err instanceof GrammyError && err.error_code === 409) {
299
+ const delay = Math.min(1000 * attempt, 15000)
300
+ if (callbacks.on409) callbacks.on409(attempt, delay)
301
+ process.stderr.write(
302
+ `bot-runtime: 409 Conflict attempt=${attempt} retry_in_ms=${delay}\n`,
303
+ )
304
+ await new Promise(r => setTimeout(r, delay))
305
+ continue
306
+ }
307
+ if (err instanceof Error && err.message === 'Aborted delay') return
308
+ process.stderr.write(`bot-runtime: polling failed: ${err}\n`)
309
+ throw err
310
+ }
311
+ }
312
+ }
313
+
314
+ // ─── Access guard ─────────────────────────────────────────────────────────
315
+
316
+ /**
317
+ * Returns true if the sender's user ID is in the allowFrom list.
318
+ * Used by both gateway and foreman for auth gating.
319
+ */
320
+ export function isAllowedSender(ctx: Context, allowFrom: string[]): boolean {
321
+ const from = ctx.from
322
+ if (!from) return false
323
+ return allowFrom.includes(String(from.id))
324
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Silent-reply markers + allowlist guard.
3
+ *
4
+ * Lives in its own module (separate from server.ts) so that tests and
5
+ * other importers can pull these helpers in without booting the
6
+ * full MCP server — server.ts has top-level side effects (env load,
7
+ * TELEGRAM_BOT_TOKEN check, history.db open, session-tail spawn) that
8
+ * are inappropriate for a unit-test import boundary.
9
+ *
10
+ * Sprint1 review finding #6: an earlier revision of the reply /
11
+ * stream_reply tool handlers returned the silent-reply ack BEFORE
12
+ * calling `assertAllowedChat`, so unauthorised chats could bypass the
13
+ * outbound allowlist by having the agent emit `NO_REPLY`. The ack
14
+ * itself is a cross-chat signal (it confirms to the LLM that the chat
15
+ * exists and is reachable) even though no Telegram message is sent, so
16
+ * we must refuse disallowed chats *before* producing it. The
17
+ * guardSilentReply helper locks that ordering in.
18
+ */
19
+
20
+ const SILENT_REPLY_MARKERS = new Set(['NO_REPLY', 'HEARTBEAT_OK'])
21
+
22
+ // Derive the char-length bound from the marker set so adding a new
23
+ // marker doesn't silently desync with a hand-tuned constant.
24
+ const SILENT_REPLY_MAX_LEN = Math.max(
25
+ ...Array.from(SILENT_REPLY_MARKERS, (m) => m.length),
26
+ ) + 2 // small buffer for trailing punctuation callers might add accidentally
27
+
28
+ export function isSilentReplyMarker(text: string | undefined): boolean {
29
+ if (typeof text !== 'string') return false
30
+ const trimmed = text.trim()
31
+ if (trimmed.length === 0) return false
32
+ if (trimmed.length > SILENT_REPLY_MAX_LEN) return false
33
+ // Case-insensitive match: models occasionally emit `no_reply` or
34
+ // `NoReply`. Require letters/underscores/digits only so legitimate
35
+ // prose that happens to contain "NO_REPLY was suggested" still sends.
36
+ return SILENT_REPLY_MARKERS.has(trimmed.toUpperCase())
37
+ }
38
+
39
+ /**
40
+ * Decide whether a `reply`/`stream_reply` invocation should be short-
41
+ * circuited as a silent-reply ack, enforcing the allowlist FIRST.
42
+ *
43
+ * `assertAllowed` throws when `chat_id` is not on the allowlist; callers
44
+ * let that propagate so the MCP tool call fails loudly.
45
+ */
46
+ export function guardSilentReply(params: {
47
+ chat_id: string
48
+ text: string | undefined
49
+ hasFiles: boolean
50
+ assertAllowed: (chat_id: string) => void
51
+ }): { kind: 'silent'; markerText: string } | { kind: 'continue' } {
52
+ const { chat_id, text, hasFiles, assertAllowed } = params
53
+ if (hasFiles) return { kind: 'continue' }
54
+ if (!isSilentReplyMarker(text)) return { kind: 'continue' }
55
+ // Allowlist check BEFORE returning the ack — see docblock above.
56
+ assertAllowed(chat_id)
57
+ return { kind: 'silent', markerText: (text as string).trim() }
58
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Slot-banner driver — executes the BannerAction state transition
3
+ * against a Telegram Bot API. Extracted from gateway.ts so the
4
+ * dispatch is testable end-to-end via `tests/fake-bot-api.ts`.
5
+ *
6
+ * The pure decision lives in `slot-banner.ts` (decideBannerAction).
7
+ * This module is the side-effecting half: takes a `bot` dependency,
8
+ * executes the action, returns the next state. The state itself
9
+ * stays in the caller (gateway.ts holds a module-global `let
10
+ * pinnedBannerState` and re-passes it on every call).
11
+ *
12
+ * Error-handling contract: API failures are reported via `onError`
13
+ * but never throw. The caller decides logging cadence (gateway logs
14
+ * to stderr; tests can assert via the callback). On a pin failure
15
+ * mid-sequence (sendMessage succeeded but pinChatMessage failed),
16
+ * the prior state is preserved so we don't claim ownership of an
17
+ * unpinned message.
18
+ *
19
+ * See #421 (banner pin lifecycle) and JTBD
20
+ * `reference/track-plan-quota-live.md` ("at a glance").
21
+ */
22
+
23
+ import type { BannerState } from './slot-banner.js';
24
+ import { decideBannerAction } from './slot-banner.js';
25
+
26
+ /** Minimal subset of grammy's `bot.api` we depend on. Letting tests
27
+ * swap in `fake-bot-api.ts` without dragging in the full Bot type. */
28
+ export interface BannerBotApi {
29
+ sendMessage(
30
+ chat_id: string | number,
31
+ text: string,
32
+ opts?: Record<string, unknown>,
33
+ ): Promise<{ message_id: number }>;
34
+ editMessageText(
35
+ chat_id: string | number,
36
+ message_id: number,
37
+ text: string,
38
+ opts?: Record<string, unknown>,
39
+ ): Promise<unknown>;
40
+ pinChatMessage(
41
+ chat_id: string | number,
42
+ message_id: number,
43
+ opts?: Record<string, unknown>,
44
+ ): Promise<unknown>;
45
+ unpinChatMessage(
46
+ chat_id: string | number,
47
+ message_id: number,
48
+ ): Promise<unknown>;
49
+ }
50
+
51
+ export interface BannerBot {
52
+ api: BannerBotApi;
53
+ }
54
+
55
+ export interface RefreshBannerArgs {
56
+ bot: BannerBot;
57
+ ownerChatId: string;
58
+ agentName: string;
59
+ /** Active slot reported by `currentActiveSlot(agentDir)`. `null`
60
+ * means we couldn't read one — treated like default state (unpins
61
+ * any existing banner; never pins). */
62
+ currentSlot: string | null;
63
+ defaultSlot: string;
64
+ /** State the gateway is holding from the last call. Pass `null`
65
+ * on first call. */
66
+ prevState: BannerState | null;
67
+ /** Optional API-failure observer. Phase identifies which Bot API
68
+ * call failed so the caller can log meaningfully. Default: silent. */
69
+ onError?: (phase: 'pin' | 'edit' | 'unpin', err: unknown) => void;
70
+ }
71
+
72
+ /**
73
+ * Execute the next banner-state transition. Returns the new
74
+ * `BannerState` (or `null` when unpinned). Always resolves; never
75
+ * throws — API errors are routed through `onError`.
76
+ *
77
+ * On pin-mid-sequence failure (sendMessage succeeded but
78
+ * pinChatMessage failed), the function returns the *prior* state
79
+ * unchanged. Otherwise the gateway would track a message_id it
80
+ * never managed to pin, and the next refresh would think a banner
81
+ * exists and try to edit/unpin it.
82
+ */
83
+ export async function refreshBanner(
84
+ args: RefreshBannerArgs,
85
+ ): Promise<BannerState | null> {
86
+ const action = decideBannerAction(
87
+ args.prevState,
88
+ args.currentSlot,
89
+ args.agentName,
90
+ args.defaultSlot,
91
+ );
92
+
93
+ if (action.kind === 'noop') return args.prevState;
94
+
95
+ if (action.kind === 'unpin') {
96
+ try {
97
+ await args.bot.api.unpinChatMessage(args.ownerChatId, action.messageId);
98
+ } catch (err) {
99
+ args.onError?.('unpin', err);
100
+ }
101
+ // Even if unpin failed, drop our claim — the message may have been
102
+ // unpinned out-of-band (operator did it manually) and re-pinning
103
+ // would be more confusing than surfacing it again later.
104
+ return null;
105
+ }
106
+
107
+ if (action.kind === 'pin') {
108
+ let sent: { message_id: number };
109
+ try {
110
+ sent = await args.bot.api.sendMessage(args.ownerChatId, action.text, {
111
+ parse_mode: 'HTML',
112
+ link_preview_options: { is_disabled: true },
113
+ });
114
+ } catch (err) {
115
+ args.onError?.('pin', err);
116
+ return args.prevState;
117
+ }
118
+ try {
119
+ await args.bot.api.pinChatMessage(args.ownerChatId, sent.message_id, {
120
+ disable_notification: true,
121
+ });
122
+ } catch (err) {
123
+ args.onError?.('pin', err);
124
+ // sendMessage succeeded but pin failed — don't claim the message.
125
+ return args.prevState;
126
+ }
127
+ return { messageId: sent.message_id, slot: action.slot };
128
+ }
129
+
130
+ // action.kind === 'edit'
131
+ try {
132
+ await args.bot.api.editMessageText(
133
+ args.ownerChatId,
134
+ action.messageId,
135
+ action.text,
136
+ {
137
+ parse_mode: 'HTML',
138
+ link_preview_options: { is_disabled: true },
139
+ },
140
+ );
141
+ return { messageId: action.messageId, slot: action.slot };
142
+ } catch (err) {
143
+ args.onError?.('edit', err);
144
+ // Edit failed — keep the prior state so the next refresh tries again.
145
+ return args.prevState;
146
+ }
147
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Pinned slot banner — pure decision logic.
3
+ *
4
+ * The gateway pins a banner in the owner chat when the agent is
5
+ * running on a non-default account slot (e.g. after auto-fallback
6
+ * swapped away from `default`). The banner unpins when the agent
7
+ * returns to `default`. This gives the user an always-visible answer
8
+ * to "what slot am I on right now?" exactly when it's not what they
9
+ * expect, and zero noise when everything is normal.
10
+ *
11
+ * This module is dependency-free so it's testable in isolation; the
12
+ * gateway translates `BannerAction` into actual Telegram API calls.
13
+ *
14
+ * v1 scope: one banner per gateway process, in the owner chat
15
+ * (access.allowFrom[0]). Per-topic forum support and multi-chat
16
+ * pinning are tracked as #421 follow-ups.
17
+ *
18
+ * See #421 (Switchroom).
19
+ */
20
+
21
+ export type BannerState = {
22
+ /** Telegram message_id of the currently pinned banner. */
23
+ messageId: number;
24
+ /** The slot name shown by the pinned message — used to skip
25
+ * redundant edits when the slot hasn't changed. */
26
+ slot: string;
27
+ };
28
+
29
+ export type BannerAction =
30
+ | { kind: 'noop'; reason: string }
31
+ /** Pin a fresh banner. Caller sends + pins, then records the
32
+ * resulting message_id back into BannerState. */
33
+ | { kind: 'pin'; text: string; slot: string }
34
+ /** Edit the existing pinned banner's text. */
35
+ | { kind: 'edit'; messageId: number; text: string; slot: string }
36
+ /** Unpin + forget. Caller unpins (best-effort) and clears state. */
37
+ | { kind: 'unpin'; messageId: number };
38
+
39
+ /**
40
+ * Decide what to do with the banner given the current active slot,
41
+ * the default slot, and the previously-pinned banner state.
42
+ */
43
+ export function decideBannerAction(
44
+ prev: BannerState | null,
45
+ currentSlot: string | null,
46
+ agentName: string,
47
+ defaultSlot: string,
48
+ ): BannerAction {
49
+ // Default state (or no slot yet): no banner needed. If one is
50
+ // pinned from a prior failover, unpin so the chat is clean again.
51
+ if (currentSlot === null || currentSlot === defaultSlot) {
52
+ if (prev) return { kind: 'unpin', messageId: prev.messageId };
53
+ return { kind: 'noop', reason: 'on default slot, nothing pinned' };
54
+ }
55
+
56
+ // Non-default state. Either pin fresh or edit existing.
57
+ const text = formatBannerHtml(agentName, currentSlot, defaultSlot);
58
+ if (!prev) return { kind: 'pin', text, slot: currentSlot };
59
+ if (prev.slot === currentSlot) {
60
+ return { kind: 'noop', reason: 'banner already shows current slot' };
61
+ }
62
+ return { kind: 'edit', messageId: prev.messageId, text, slot: currentSlot };
63
+ }
64
+
65
+ /**
66
+ * The banner body. Kept short — the user reads this at a glance,
67
+ * and pinned messages eat vertical space at the top of the chat.
68
+ */
69
+ export function formatBannerHtml(
70
+ agentName: string,
71
+ currentSlot: string,
72
+ defaultSlot: string,
73
+ ): string {
74
+ return [
75
+ `📌 <b>${escapeHtml(agentName)}</b> is running on slot <code>${escapeHtml(currentSlot)}</code>`,
76
+ `<i>(failover from <code>${escapeHtml(defaultSlot)}</code>)</i>`,
77
+ ].join(' ');
78
+ }
79
+
80
+ function escapeHtml(text: string): string {
81
+ return text
82
+ .replace(/&/g, '&amp;')
83
+ .replace(/</g, '&lt;')
84
+ .replace(/>/g, '&gt;')
85
+ .replace(/"/g, '&quot;');
86
+ }
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bun
2
+ // MCP-server entry shim. Resolved by `bun run start` (see package.json).
3
+ //
4
+ // Strategic packaging fix (#634): the production MCP launcher invokes
5
+ // `bun run --cwd <pluginDir> --silent start`, which executes this file.
6
+ // We prefer the bundled `dist/server.js` because the npm package's
7
+ // `files` array doesn't include `src/` — direct `.ts` execution from
8
+ // the global install fails with `Cannot find module '../../src/...'`
9
+ // against the bundle's cross-imports. The bundle resolves them at
10
+ // build time so dist runs everywhere.
11
+ //
12
+ // Dev workspaces that haven't run `bun run build` yet (or operators
13
+ // running pre-#634 packages) fall back to the .ts source. The fallback
14
+ // is the legacy behavior — preserves dev ergonomics where editing
15
+ // .ts and restarting an agent picks up changes without an explicit
16
+ // build step (modulo the documented `bun run build` cycle).
17
+ import { existsSync } from "node:fs";
18
+ import { resolve, dirname } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+
21
+ const here = dirname(fileURLToPath(import.meta.url));
22
+ const distPath = resolve(here, "dist/server.js");
23
+ const sourcePath = resolve(here, "server.ts");
24
+
25
+ const target = existsSync(distPath) ? distPath : sourcePath;
26
+ await import(target);
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Clear stale Telegram polling state on gateway startup.
3
+ *
4
+ * The Telegram Bot API tracks one active long-poll session per bot
5
+ * token. If the previous gateway process crashed mid-poll (e.g. systemd
6
+ * killed it with SIGKILL after a start-timeout, which is exactly what
7
+ * happened to klanker on 2026-04-21 at 16:35 AEST), the API-side slot
8
+ * can remain occupied for several minutes. The new gateway then races
9
+ * that orphan on every getUpdates call and gets back a 409 Conflict
10
+ * ("terminated by other getUpdates request"). Grammy retries with
11
+ * backoff but the loop can persist for hours.
12
+ *
13
+ * deleteWebhook is a no-op when no webhook is configured but it also
14
+ * invalidates any active long-poll claim. Combined with
15
+ * drop_pending_updates, it gives us a clean slate regardless of how
16
+ * the previous process died. Safe to call on every startup — it's
17
+ * idempotent and has no user-visible side effects beyond clearing the
18
+ * (probably-empty) pending-updates queue.
19
+ *
20
+ * Reference: reference/restart-and-know-what-im-running.md — "silent
21
+ * respawn. Agent comes back and the user has to guess whether it's
22
+ * the same agent." A gateway stuck in a 409 loop is exactly that
23
+ * failure mode.
24
+ *
25
+ * Lives in a separate module (not gateway.ts) so the test file can
26
+ * import it without firing gateway.ts's top-level IIFE that starts
27
+ * the bot.
28
+ */
29
+
30
+ export interface DeleteWebhookCapable {
31
+ deleteWebhook: (opts: { drop_pending_updates: boolean }) => Promise<unknown>;
32
+ }
33
+
34
+ export async function clearStaleTelegramPollingState(
35
+ api: DeleteWebhookCapable,
36
+ ): Promise<void> {
37
+ try {
38
+ await api.deleteWebhook({ drop_pending_updates: true });
39
+ } catch (err) {
40
+ // Best-effort — if deleteWebhook fails the runner will still start
41
+ // and grammy's built-in 409 handling kicks in. Log so we can
42
+ // diagnose if this becomes a recurring failure.
43
+ process.stderr.write(`telegram gateway: deleteWebhook on startup failed: ${err}\n`);
44
+ }
45
+ }