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,122 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+
3
+ // Import directly from the helper module (not server.js), so this test
4
+ // doesn't boot the full MCP server — server.ts has top-level side
5
+ // effects (env load, TELEGRAM_BOT_TOKEN check, session-tail spawn) that
6
+ // kill the test process when run under bun-test without a real env.
7
+ import { guardSilentReply, isSilentReplyMarker } from '../silent-reply.js'
8
+
9
+ /**
10
+ * Regression coverage for sprint1 review finding #6: the reply /
11
+ * stream_reply tool handlers must call `assertAllowedChat(chat_id)`
12
+ * BEFORE returning the silent-reply ack, so an unauthorised chat
13
+ * cannot bypass the outbound allowlist by having the agent emit
14
+ * `NO_REPLY` / `HEARTBEAT_OK`.
15
+ *
16
+ * We exercise the extracted `guardSilentReply` helper directly rather
17
+ * than the full MCP tool handler; the handler scaffolding (IPC + Bot
18
+ * harness + history DB) is heavy, and the ordering contract — "assert
19
+ * first, then ack" — lives entirely inside the helper.
20
+ */
21
+ describe('guardSilentReply — allowlist ordering', () => {
22
+ it('asserts the allowlist BEFORE returning a silent ack (NO_REPLY)', () => {
23
+ const assertAllowed = vi.fn()
24
+ const result = guardSilentReply({
25
+ chat_id: '123',
26
+ text: 'NO_REPLY',
27
+ hasFiles: false,
28
+ assertAllowed,
29
+ })
30
+ expect(assertAllowed).toHaveBeenCalledOnce()
31
+ expect(assertAllowed).toHaveBeenCalledWith('123')
32
+ expect(result).toEqual({ kind: 'silent', markerText: 'NO_REPLY' })
33
+ })
34
+
35
+ it('recognises HEARTBEAT_OK as a silent marker and still gates on allowlist', () => {
36
+ const assertAllowed = vi.fn()
37
+ const result = guardSilentReply({
38
+ chat_id: '456',
39
+ text: ' heartbeat_ok ',
40
+ hasFiles: false,
41
+ assertAllowed,
42
+ })
43
+ expect(assertAllowed).toHaveBeenCalledWith('456')
44
+ expect(result).toEqual({ kind: 'silent', markerText: 'heartbeat_ok' })
45
+ })
46
+
47
+ it('throws (does NOT return a silent ack) when the chat is disallowed', () => {
48
+ // This is the exact bypass the fix prevents: an unauthorised chat_id
49
+ // using NO_REPLY must NOT receive a successful silent-reply ack.
50
+ const assertAllowed = vi.fn((chat_id: string) => {
51
+ throw new Error(`chat ${chat_id} is not allowlisted`)
52
+ })
53
+ expect(() =>
54
+ guardSilentReply({
55
+ chat_id: '999',
56
+ text: 'NO_REPLY',
57
+ hasFiles: false,
58
+ assertAllowed,
59
+ }),
60
+ ).toThrow(/not allowlisted/)
61
+ expect(assertAllowed).toHaveBeenCalledOnce()
62
+ })
63
+
64
+ it('does not short-circuit (or call assertAllowed) when files are attached', () => {
65
+ // Silent-reply semantics are text-only; a NO_REPLY payload with files
66
+ // is a real send and must flow through the normal allowlist path
67
+ // inside the send codepath, not the silent short-circuit.
68
+ const assertAllowed = vi.fn()
69
+ const result = guardSilentReply({
70
+ chat_id: '123',
71
+ text: 'NO_REPLY',
72
+ hasFiles: true,
73
+ assertAllowed,
74
+ })
75
+ expect(assertAllowed).not.toHaveBeenCalled()
76
+ expect(result).toEqual({ kind: 'continue' })
77
+ })
78
+
79
+ it('does not short-circuit for normal prose that merely mentions the marker', () => {
80
+ const assertAllowed = vi.fn()
81
+ const result = guardSilentReply({
82
+ chat_id: '123',
83
+ text: 'the agent suggested NO_REPLY earlier',
84
+ hasFiles: false,
85
+ assertAllowed,
86
+ })
87
+ expect(assertAllowed).not.toHaveBeenCalled()
88
+ expect(result).toEqual({ kind: 'continue' })
89
+ })
90
+
91
+ it('does not short-circuit on undefined/empty text', () => {
92
+ const assertAllowed = vi.fn()
93
+ expect(
94
+ guardSilentReply({
95
+ chat_id: '123',
96
+ text: undefined,
97
+ hasFiles: false,
98
+ assertAllowed,
99
+ }),
100
+ ).toEqual({ kind: 'continue' })
101
+ expect(
102
+ guardSilentReply({
103
+ chat_id: '123',
104
+ text: '',
105
+ hasFiles: false,
106
+ assertAllowed,
107
+ }),
108
+ ).toEqual({ kind: 'continue' })
109
+ expect(assertAllowed).not.toHaveBeenCalled()
110
+ })
111
+
112
+ // Sanity: the helper the guard delegates to is exported for direct
113
+ // marker-recognition testing. Keep a thin smoke assertion so future
114
+ // marker-set tweaks break here rather than at the ordering layer.
115
+ it('isSilentReplyMarker recognises the documented marker set', () => {
116
+ expect(isSilentReplyMarker('NO_REPLY')).toBe(true)
117
+ expect(isSilentReplyMarker('HEARTBEAT_OK')).toBe(true)
118
+ expect(isSilentReplyMarker('no_reply')).toBe(true)
119
+ expect(isSilentReplyMarker('hello')).toBe(false)
120
+ expect(isSilentReplyMarker(undefined)).toBe(false)
121
+ })
122
+ })
@@ -0,0 +1,350 @@
1
+ /**
2
+ * End-to-end tests for the slot-banner driver (#421).
3
+ *
4
+ * Drives `refreshBanner` against the chat-model-tracking
5
+ * `fake-bot-api` so we lock in the actual Bot API call sequence the
6
+ * gateway emits — not just the pure decision in `slot-banner.ts`.
7
+ *
8
+ * Each test walks one transition (no-banner → pinned, pinned → edited,
9
+ * pinned → unpinned) and asserts both the call shape and the returned
10
+ * BannerState the gateway will hold for the next refresh.
11
+ */
12
+
13
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
14
+ import { refreshBanner } from '../slot-banner-driver.js';
15
+ import type { BannerState } from '../slot-banner.js';
16
+ import { createFakeBotApi, errors, type FakeBot } from './fake-bot-api.js';
17
+
18
+ const OWNER = 'chat-owner';
19
+ const AGENT = 'clerk';
20
+ const DEFAULT = 'default';
21
+
22
+ let bot: FakeBot;
23
+
24
+ beforeEach(() => {
25
+ bot = createFakeBotApi({ startMessageId: 100 });
26
+ });
27
+
28
+ describe('refreshBanner — transitions from clean state', () => {
29
+ it('on default slot with no prior state, does nothing (no API calls, returns null)', async () => {
30
+ const next = await refreshBanner({
31
+ bot,
32
+ ownerChatId: OWNER,
33
+ agentName: AGENT,
34
+ currentSlot: DEFAULT,
35
+ defaultSlot: DEFAULT,
36
+ prevState: null,
37
+ });
38
+ expect(next).toBeNull();
39
+ expect(bot.api.sendMessage).not.toHaveBeenCalled();
40
+ expect(bot.api.pinChatMessage).not.toHaveBeenCalled();
41
+ });
42
+
43
+ it('on null active slot (failed to read), treats as default — no banner', async () => {
44
+ const next = await refreshBanner({
45
+ bot,
46
+ ownerChatId: OWNER,
47
+ agentName: AGENT,
48
+ currentSlot: null,
49
+ defaultSlot: DEFAULT,
50
+ prevState: null,
51
+ });
52
+ expect(next).toBeNull();
53
+ expect(bot.api.sendMessage).not.toHaveBeenCalled();
54
+ });
55
+
56
+ it('on non-default slot with no prior state, sends + pins, returns state', async () => {
57
+ const next = await refreshBanner({
58
+ bot,
59
+ ownerChatId: OWNER,
60
+ agentName: AGENT,
61
+ currentSlot: 'personal',
62
+ defaultSlot: DEFAULT,
63
+ prevState: null,
64
+ });
65
+ expect(next).not.toBeNull();
66
+ expect(next?.slot).toBe('personal');
67
+ expect(next?.messageId).toBe(100);
68
+ // sendMessage + pinChatMessage called in that order.
69
+ expect(bot.api.sendMessage).toHaveBeenCalledTimes(1);
70
+ expect(bot.api.pinChatMessage).toHaveBeenCalledTimes(1);
71
+ const sendArgs = bot.api.sendMessage.mock.calls[0];
72
+ expect(sendArgs[0]).toBe(OWNER);
73
+ expect(sendArgs[1]).toContain('clerk');
74
+ expect(sendArgs[1]).toContain('personal');
75
+ // Pin call should disable notifications so users don't get pinged.
76
+ const pinArgs = bot.api.pinChatMessage.mock.calls[0];
77
+ expect(pinArgs[2]).toMatchObject({ disable_notification: true });
78
+ // Chat model also reflects the pin.
79
+ expect(bot.isPinned(OWNER, 100)).toBe(true);
80
+ });
81
+ });
82
+
83
+ describe('refreshBanner — transitions from pinned state', () => {
84
+ // Seed the chat model with a real pin (matches gateway's first-pin path),
85
+ // then run subsequent transitions against it. Avoids the fake-bot's
86
+ // realistic "edit on unknown message_id throws not-found" behaviour
87
+ // that would fire if we hand-rolled a BannerState referencing an id
88
+ // the fake never saw.
89
+ let prior: BannerState;
90
+ beforeEach(async () => {
91
+ const seeded = await refreshBanner({
92
+ bot,
93
+ ownerChatId: OWNER,
94
+ agentName: AGENT,
95
+ currentSlot: 'personal',
96
+ defaultSlot: DEFAULT,
97
+ prevState: null,
98
+ });
99
+ if (!seeded) throw new Error('failed to seed prior state');
100
+ prior = seeded;
101
+ // Reset the spy counts so the under-test transition starts from zero.
102
+ bot.api.sendMessage.mockClear();
103
+ bot.api.editMessageText.mockClear();
104
+ bot.api.pinChatMessage.mockClear();
105
+ bot.api.unpinChatMessage.mockClear();
106
+ });
107
+
108
+ it('same slot is a noop (no API calls, state preserved)', async () => {
109
+ const next = await refreshBanner({
110
+ bot,
111
+ ownerChatId: OWNER,
112
+ agentName: AGENT,
113
+ currentSlot: 'personal',
114
+ defaultSlot: DEFAULT,
115
+ prevState: prior,
116
+ });
117
+ expect(next).toEqual(prior);
118
+ expect(bot.api.sendMessage).not.toHaveBeenCalled();
119
+ expect(bot.api.editMessageText).not.toHaveBeenCalled();
120
+ expect(bot.api.unpinChatMessage).not.toHaveBeenCalled();
121
+ });
122
+
123
+ it('different non-default slot edits in place, advances state', async () => {
124
+ const next = await refreshBanner({
125
+ bot,
126
+ ownerChatId: OWNER,
127
+ agentName: AGENT,
128
+ currentSlot: 'work',
129
+ defaultSlot: DEFAULT,
130
+ prevState: prior,
131
+ });
132
+ expect(next).toEqual({ messageId: prior.messageId, slot: 'work' });
133
+ expect(bot.api.editMessageText).toHaveBeenCalledTimes(1);
134
+ expect(bot.api.sendMessage).not.toHaveBeenCalled();
135
+ const editArgs = bot.api.editMessageText.mock.calls[0];
136
+ expect(editArgs[0]).toBe(OWNER);
137
+ expect(editArgs[1]).toBe(prior.messageId);
138
+ expect(editArgs[2]).toContain('work');
139
+ });
140
+
141
+ it('return to default slot unpins, clears state', async () => {
142
+ const next = await refreshBanner({
143
+ bot,
144
+ ownerChatId: OWNER,
145
+ agentName: AGENT,
146
+ currentSlot: DEFAULT,
147
+ defaultSlot: DEFAULT,
148
+ prevState: prior,
149
+ });
150
+ expect(next).toBeNull();
151
+ expect(bot.api.unpinChatMessage).toHaveBeenCalledTimes(1);
152
+ expect(bot.api.unpinChatMessage.mock.calls[0]).toEqual([OWNER, prior.messageId]);
153
+ });
154
+
155
+ it('null active slot (failure to read) also unpins', async () => {
156
+ const next = await refreshBanner({
157
+ bot,
158
+ ownerChatId: OWNER,
159
+ agentName: AGENT,
160
+ currentSlot: null,
161
+ defaultSlot: DEFAULT,
162
+ prevState: prior,
163
+ });
164
+ expect(next).toBeNull();
165
+ expect(bot.api.unpinChatMessage).toHaveBeenCalledTimes(1);
166
+ });
167
+ });
168
+
169
+ describe('refreshBanner — full lifecycle (sequenced)', () => {
170
+ it('walks no-banner → personal → work → default → noop', async () => {
171
+ let state: BannerState | null = null;
172
+
173
+ // 1. Initial — quota fired, swapped to personal.
174
+ state = await refreshBanner({
175
+ bot,
176
+ ownerChatId: OWNER,
177
+ agentName: AGENT,
178
+ currentSlot: 'personal',
179
+ defaultSlot: DEFAULT,
180
+ prevState: state,
181
+ });
182
+ expect(state?.slot).toBe('personal');
183
+
184
+ // 2. Slot moved again (manual /auth use work).
185
+ state = await refreshBanner({
186
+ bot,
187
+ ownerChatId: OWNER,
188
+ agentName: AGENT,
189
+ currentSlot: 'work',
190
+ defaultSlot: DEFAULT,
191
+ prevState: state,
192
+ });
193
+ expect(state?.slot).toBe('work');
194
+ expect(state?.messageId).toBe(100); // edited in place, same id
195
+
196
+ // 3. Operator returned to default.
197
+ state = await refreshBanner({
198
+ bot,
199
+ ownerChatId: OWNER,
200
+ agentName: AGENT,
201
+ currentSlot: DEFAULT,
202
+ defaultSlot: DEFAULT,
203
+ prevState: state,
204
+ });
205
+ expect(state).toBeNull();
206
+
207
+ // 4. Repeat default → still noop.
208
+ state = await refreshBanner({
209
+ bot,
210
+ ownerChatId: OWNER,
211
+ agentName: AGENT,
212
+ currentSlot: DEFAULT,
213
+ defaultSlot: DEFAULT,
214
+ prevState: state,
215
+ });
216
+ expect(state).toBeNull();
217
+
218
+ // Final tally on the chat model.
219
+ expect(bot.api.sendMessage).toHaveBeenCalledTimes(1);
220
+ expect(bot.api.editMessageText).toHaveBeenCalledTimes(1);
221
+ expect(bot.api.unpinChatMessage).toHaveBeenCalledTimes(1);
222
+ expect(bot.api.pinChatMessage).toHaveBeenCalledTimes(1);
223
+ // After unpin, isPinned returns false.
224
+ expect(bot.isPinned(OWNER, 100)).toBe(false);
225
+ });
226
+ });
227
+
228
+ describe('refreshBanner — error paths', () => {
229
+ it('pinChatMessage failure after sendMessage success preserves prior state', async () => {
230
+ // Why: gateway must not claim a message_id it never managed to pin —
231
+ // the next refresh would think a banner exists and try to edit it.
232
+ bot.faults.next('pinChatMessage', errors.badRequest('chat not found', 'pinChatMessage'));
233
+ const onError = vi.fn();
234
+ const next = await refreshBanner({
235
+ bot,
236
+ ownerChatId: OWNER,
237
+ agentName: AGENT,
238
+ currentSlot: 'personal',
239
+ defaultSlot: DEFAULT,
240
+ prevState: null,
241
+ onError,
242
+ });
243
+ expect(next).toBeNull(); // prior state was null, preserved
244
+ expect(bot.api.sendMessage).toHaveBeenCalledTimes(1);
245
+ expect(bot.api.pinChatMessage).toHaveBeenCalledTimes(1);
246
+ expect(onError).toHaveBeenCalledWith('pin', expect.any(Error));
247
+ });
248
+
249
+ it('sendMessage failure surfaces but does not throw', async () => {
250
+ bot.faults.next('sendMessage', errors.forbidden('sendMessage'));
251
+ const onError = vi.fn();
252
+ const next = await refreshBanner({
253
+ bot,
254
+ ownerChatId: OWNER,
255
+ agentName: AGENT,
256
+ currentSlot: 'personal',
257
+ defaultSlot: DEFAULT,
258
+ prevState: null,
259
+ onError,
260
+ });
261
+ expect(next).toBeNull();
262
+ expect(onError).toHaveBeenCalledWith('pin', expect.any(Error));
263
+ // pinChatMessage never reached because sendMessage failed first.
264
+ expect(bot.api.pinChatMessage).not.toHaveBeenCalled();
265
+ });
266
+
267
+ it('editMessageText failure preserves prior state (next refresh tries again)', async () => {
268
+ const PRIOR: BannerState = { messageId: 7, slot: 'personal' };
269
+ bot.faults.next('editMessageText', errors.messageToEditNotFound());
270
+ const onError = vi.fn();
271
+ const next = await refreshBanner({
272
+ bot,
273
+ ownerChatId: OWNER,
274
+ agentName: AGENT,
275
+ currentSlot: 'work',
276
+ defaultSlot: DEFAULT,
277
+ prevState: PRIOR,
278
+ onError,
279
+ });
280
+ expect(next).toEqual(PRIOR); // unchanged
281
+ expect(onError).toHaveBeenCalledWith('edit', expect.any(Error));
282
+ });
283
+
284
+ it('unpinChatMessage failure still drops the claim (returns null)', async () => {
285
+ // Out-of-band unpin (operator did it) is the most common cause —
286
+ // re-pinning would surprise the user.
287
+ const PRIOR: BannerState = { messageId: 7, slot: 'personal' };
288
+ bot.faults.next('unpinChatMessage', errors.badRequest('message to unpin not found', 'unpinChatMessage'));
289
+ const onError = vi.fn();
290
+ const next = await refreshBanner({
291
+ bot,
292
+ ownerChatId: OWNER,
293
+ agentName: AGENT,
294
+ currentSlot: DEFAULT,
295
+ defaultSlot: DEFAULT,
296
+ prevState: PRIOR,
297
+ onError,
298
+ });
299
+ expect(next).toBeNull();
300
+ expect(onError).toHaveBeenCalledWith('unpin', expect.any(Error));
301
+ });
302
+ });
303
+
304
+ describe('refreshBanner — banner content', () => {
305
+ it('contains agent name, current slot, and failover-from default', async () => {
306
+ await refreshBanner({
307
+ bot,
308
+ ownerChatId: OWNER,
309
+ agentName: 'klanker',
310
+ currentSlot: 'backup',
311
+ defaultSlot: 'default',
312
+ prevState: null,
313
+ });
314
+ const text = bot.api.sendMessage.mock.calls[0][1] as string;
315
+ expect(text).toContain('klanker');
316
+ expect(text).toContain('backup');
317
+ expect(text).toContain('default');
318
+ expect(text.toLowerCase()).toMatch(/failover/);
319
+ });
320
+
321
+ it('escapes HTML in agent and slot names (no XSS via slot rename)', async () => {
322
+ await refreshBanner({
323
+ bot,
324
+ ownerChatId: OWNER,
325
+ agentName: '<bad>',
326
+ currentSlot: '"hax"',
327
+ defaultSlot: '&def',
328
+ prevState: null,
329
+ });
330
+ const text = bot.api.sendMessage.mock.calls[0][1] as string;
331
+ expect(text).not.toContain('<bad>');
332
+ expect(text).toContain('&lt;bad&gt;');
333
+ expect(text).toContain('&quot;hax&quot;');
334
+ expect(text).toContain('&amp;def');
335
+ });
336
+
337
+ it('uses HTML parse_mode + disables link preview', async () => {
338
+ await refreshBanner({
339
+ bot,
340
+ ownerChatId: OWNER,
341
+ agentName: AGENT,
342
+ currentSlot: 'personal',
343
+ defaultSlot: DEFAULT,
344
+ prevState: null,
345
+ });
346
+ const opts = bot.api.sendMessage.mock.calls[0][2] as Record<string, unknown>;
347
+ expect(opts.parse_mode).toBe('HTML');
348
+ expect(opts.link_preview_options).toEqual({ is_disabled: true });
349
+ });
350
+ });
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { decideBannerAction, formatBannerHtml, type BannerState } from '../slot-banner';
3
+
4
+ const DEFAULT = 'default';
5
+
6
+ describe('decideBannerAction — default state', () => {
7
+ it('noop when on default and nothing pinned', () => {
8
+ const action = decideBannerAction(null, DEFAULT, 'clerk', DEFAULT);
9
+ expect(action.kind).toBe('noop');
10
+ });
11
+
12
+ it('noop when slot is null and nothing pinned', () => {
13
+ const action = decideBannerAction(null, null, 'clerk', DEFAULT);
14
+ expect(action.kind).toBe('noop');
15
+ });
16
+
17
+ it('unpins existing banner when slot returns to default', () => {
18
+ const prev: BannerState = { messageId: 42, slot: 'personal' };
19
+ const action = decideBannerAction(prev, DEFAULT, 'clerk', DEFAULT);
20
+ expect(action).toEqual({ kind: 'unpin', messageId: 42 });
21
+ });
22
+
23
+ it('unpins existing banner when active slot becomes null', () => {
24
+ const prev: BannerState = { messageId: 7, slot: 'work' };
25
+ const action = decideBannerAction(prev, null, 'clerk', DEFAULT);
26
+ expect(action).toEqual({ kind: 'unpin', messageId: 7 });
27
+ });
28
+ });
29
+
30
+ describe('decideBannerAction — non-default state', () => {
31
+ it('pins fresh banner when nothing currently pinned', () => {
32
+ const action = decideBannerAction(null, 'personal', 'clerk', DEFAULT);
33
+ expect(action.kind).toBe('pin');
34
+ if (action.kind === 'pin') {
35
+ expect(action.slot).toBe('personal');
36
+ expect(action.text).toContain('clerk');
37
+ expect(action.text).toContain('personal');
38
+ expect(action.text).toContain('default');
39
+ }
40
+ });
41
+
42
+ it('edits when banner exists for a different slot', () => {
43
+ const prev: BannerState = { messageId: 99, slot: 'personal' };
44
+ const action = decideBannerAction(prev, 'work', 'clerk', DEFAULT);
45
+ expect(action.kind).toBe('edit');
46
+ if (action.kind === 'edit') {
47
+ expect(action.messageId).toBe(99);
48
+ expect(action.slot).toBe('work');
49
+ expect(action.text).toContain('work');
50
+ }
51
+ });
52
+
53
+ it('noops when banner already reflects current slot', () => {
54
+ const prev: BannerState = { messageId: 12, slot: 'personal' };
55
+ const action = decideBannerAction(prev, 'personal', 'clerk', DEFAULT);
56
+ expect(action.kind).toBe('noop');
57
+ });
58
+ });
59
+
60
+ describe('formatBannerHtml', () => {
61
+ it('escapes HTML in agent and slot names', () => {
62
+ const text = formatBannerHtml('<bad>', '"hax"', '&def');
63
+ expect(text).not.toContain('<bad>');
64
+ expect(text).toContain('&lt;bad&gt;');
65
+ expect(text).toContain('&quot;hax&quot;');
66
+ expect(text).toContain('&amp;def');
67
+ });
68
+
69
+ it('mentions the failover-from default for context', () => {
70
+ const text = formatBannerHtml('clerk', 'personal', 'default');
71
+ expect(text).toMatch(/failover from/i);
72
+ expect(text).toContain('default');
73
+ });
74
+ });
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Normalizing serializer for HTML / progress-card snapshot tests.
3
+ *
4
+ * Problem: the raw progress-card render contains
5
+ * - elapsed-time strings like "12s elapsed" / "1m34s"
6
+ * - ISO timestamps like "2026-04-18T12:34:56.789Z"
7
+ * - message-ids, toolUseIds, runtime-generated UUIDs
8
+ *
9
+ * Any of these flip on every test run, so a naive snapshot rots the
10
+ * moment it lands. This serializer canonicalizes those fields to stable
11
+ * tokens (`<ELAPSED>`, `<TIME>`, `<ID>`, `<TOOL_USE_ID>`) so the
12
+ * snapshot diff reflects *semantic* changes only.
13
+ *
14
+ * Use by passing `snapshotSerializer` to vitest's `expect.addSnapshotSerializer`
15
+ * in a test's `beforeAll`, or globally via `vitest.config.ts`
16
+ * (`test.snapshotSerializers`).
17
+ */
18
+
19
+ const PATTERNS: Array<{ re: RegExp; replacement: string }> = [
20
+ // ISO8601 timestamps
21
+ { re: /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z\b/g, replacement: '<TIME>' },
22
+ // Unix seconds (10 digits) — only within JSON-like contexts, prefixed by `date":` or `"date":`
23
+ { re: /"date"\s*:\s*\d{10}\b/g, replacement: '"date":<UNIX_SEC>' },
24
+ // Elapsed-time strings rendered by progress-card (hhh? mm ss).
25
+ // Match "NNs elapsed", "NNmNNs", "NNhNNm".
26
+ { re: /\b\d+m\d+s\b/g, replacement: '<ELAPSED>' },
27
+ { re: /\b\d+h\d+m\b/g, replacement: '<ELAPSED>' },
28
+ { re: /\b\d+s\s+elapsed\b/g, replacement: '<ELAPSED> elapsed' },
29
+ // Claude Code tool_use ids: `toolu_01ABC...` (base62, 20+ chars).
30
+ { re: /\btoolu_[A-Za-z0-9_]{10,}\b/g, replacement: '<TOOL_USE_ID>' },
31
+ // grammy/telegram file_ids: long alphanumerics starting with AgACAgI / BQACAgI etc.
32
+ { re: /\b(?:AgAC|BQAC|CQAC|DQAC)[A-Za-z0-9_-]{10,}\b/g, replacement: '<FILE_ID>' },
33
+ // UUIDs
34
+ { re: /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g, replacement: '<UUID>' },
35
+ ]
36
+
37
+ /**
38
+ * Apply all normalizing replacements. Safe to call on any string —
39
+ * non-matching content passes through unchanged.
40
+ */
41
+ export function normalizeSnapshot(input: string): string {
42
+ let out = input
43
+ for (const { re, replacement } of PATTERNS) {
44
+ out = out.replace(re, replacement)
45
+ }
46
+ return out
47
+ }
48
+
49
+ /**
50
+ * Vitest snapshot serializer compatible with `expect.addSnapshotSerializer`.
51
+ *
52
+ * Only applies to string values that clearly look like HTML / Telegram
53
+ * rendered content. Everything else passes through without interference
54
+ * so we don't accidentally mangle unrelated assertions.
55
+ */
56
+ export const snapshotSerializer = {
57
+ test(val: unknown): boolean {
58
+ if (typeof val !== 'string') return false
59
+ // Cheap pre-filter: only normalize when at least one pattern would hit.
60
+ for (const { re } of PATTERNS) {
61
+ re.lastIndex = 0
62
+ if (re.test(val)) {
63
+ re.lastIndex = 0
64
+ return true
65
+ }
66
+ re.lastIndex = 0
67
+ }
68
+ return false
69
+ },
70
+ serialize(val: unknown): string {
71
+ return JSON.stringify(normalizeSnapshot(val as string))
72
+ },
73
+ }
74
+
75
+ /** Convenience: normalize + stringify with stable indent. Good for
76
+ * inline-snapshot-style assertions without invoking the serializer. */
77
+ export function stableHtmlSnapshot(html: string): string {
78
+ return normalizeSnapshot(html)
79
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Regression test for #177: detached spawns from the gateway must escape
3
+ * the gateway's cgroup so a gateway restart (kicked off by the same
4
+ * detached child as part of /new or auto-failover) doesn't cgroup-kill
5
+ * the child mid-flight before it can reach the second systemctl call.
6
+ *
7
+ * The fix wraps every spawn through `systemd-run --user --scope --collect`
8
+ * when systemd-run is available, falling back to direct spawn on hosts
9
+ * without it (dev containers, CI without systemd-user).
10
+ *
11
+ * This is a structural source-grep test rather than a runtime exercise
12
+ * because spawnSwitchroomDetached isn't exported and exercising the
13
+ * actual cgroup behaviour requires a real systemd session.
14
+ */
15
+ import { describe, it, expect } from 'vitest'
16
+ import { readFileSync } from 'node:fs'
17
+ import { resolve, join } from 'node:path'
18
+ import { fileURLToPath } from 'node:url'
19
+
20
+ const TEST_DIR = resolve(fileURLToPath(import.meta.url), '..')
21
+ const REPO_ROOT = resolve(TEST_DIR, '..', '..')
22
+
23
+ describe('spawnSwitchroomDetached cgroup-escape (#177)', () => {
24
+ const src = readFileSync(
25
+ join(REPO_ROOT, 'telegram-plugin/gateway/gateway.ts'),
26
+ 'utf-8',
27
+ )
28
+
29
+ it('declares a resolveSystemdRunPath helper', () => {
30
+ expect(src).toContain('function resolveSystemdRunPath(')
31
+ })
32
+
33
+ it('uses --scope --collect via systemd-run when available', () => {
34
+ expect(src).toContain("'--user', '--scope', '--collect'")
35
+ })
36
+
37
+ it('uses --quiet so the wrapper does not log a banner per spawn', () => {
38
+ expect(src).toMatch(/'--user',\s*'--scope',\s*'--collect',\s*'--quiet'/)
39
+ })
40
+
41
+ it('falls back to direct spawn when systemd-run is unavailable', () => {
42
+ // The fallback path should still pass SWITCHROOM_CLI as the spawn
43
+ // binary; the systemd-run path adds it as an arg via `--`.
44
+ expect(src).toMatch(/spawnBin\s*=\s*systemdRun\s*\?\?\s*SWITCHROOM_CLI/)
45
+ })
46
+
47
+ it('caches the resolution per process (no command -v on every spawn)', () => {
48
+ expect(src).toContain('let _systemdRunPath')
49
+ expect(src).toMatch(/if\s*\(_systemdRunPath\s*!==\s*undefined\)\s*return/)
50
+ })
51
+ })