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,124 @@
1
+ /**
2
+ * Tests for the structured pin/unpin event logger — the module that
3
+ * emits one JSON line per pin API interaction so operators can audit
4
+ * the pin lifecycle without parsing free-form log text.
5
+ *
6
+ * Covers spec `docs/pinned-progress-card-reliability.md` §6.1 and T9.
7
+ */
8
+
9
+ import { describe, it, expect } from "vitest";
10
+ import {
11
+ logPinEvent,
12
+ classifyPinError,
13
+ errorMessage,
14
+ type PinEvent,
15
+ } from "../pin-event-log.js";
16
+
17
+ describe("logPinEvent", () => {
18
+ it("writes one JSON line prefixed with 'pin-event: '", () => {
19
+ const lines: string[] = [];
20
+ const ev: PinEvent = {
21
+ event: "pin",
22
+ chatId: "100",
23
+ messageId: 42,
24
+ turnKey: "100::1",
25
+ outcome: "ok",
26
+ durationMs: 123,
27
+ };
28
+ logPinEvent(ev, (line) => lines.push(line));
29
+ expect(lines).toHaveLength(1);
30
+ expect(lines[0]).toMatch(/^pin-event: /);
31
+ expect(lines[0].endsWith("\n")).toBe(true);
32
+ const payload = JSON.parse(lines[0].replace(/^pin-event: /, "").trimEnd());
33
+ expect(payload).toEqual(ev);
34
+ });
35
+
36
+ it("preserves all optional fields that are set", () => {
37
+ const lines: string[] = [];
38
+ logPinEvent(
39
+ {
40
+ event: "unpin-retry",
41
+ chatId: "c",
42
+ messageId: 7,
43
+ turnKey: "c::2",
44
+ outcome: "rate-limited",
45
+ error: "Too Many Requests",
46
+ durationMs: 1050,
47
+ },
48
+ (line) => lines.push(line),
49
+ );
50
+ const payload = JSON.parse(lines[0].replace(/^pin-event: /, "").trimEnd());
51
+ expect(payload.error).toBe("Too Many Requests");
52
+ expect(payload.outcome).toBe("rate-limited");
53
+ expect(payload.event).toBe("unpin-retry");
54
+ });
55
+
56
+ it("omits undefined optional fields cleanly", () => {
57
+ const lines: string[] = [];
58
+ logPinEvent(
59
+ {
60
+ event: "sweep-pin",
61
+ chatId: "c",
62
+ outcome: "ok",
63
+ },
64
+ (line) => lines.push(line),
65
+ );
66
+ const raw = lines[0].replace(/^pin-event: /, "").trimEnd();
67
+ expect(raw).not.toContain("undefined");
68
+ const payload = JSON.parse(raw);
69
+ expect(payload.error).toBeUndefined();
70
+ expect(payload.messageId).toBeUndefined();
71
+ });
72
+ });
73
+
74
+ describe("classifyPinError", () => {
75
+ it("returns 'rate-limited' for Telegram 429", () => {
76
+ expect(
77
+ classifyPinError({ error_code: 429, description: "Too Many Requests: retry after 2" }),
78
+ ).toBe("rate-limited");
79
+ });
80
+
81
+ it("returns 'forbidden' for Telegram 403", () => {
82
+ expect(classifyPinError({ error_code: 403, description: "Forbidden: bot was kicked" })).toBe(
83
+ "forbidden",
84
+ );
85
+ });
86
+
87
+ it("falls back to message substring match when error_code is absent", () => {
88
+ expect(classifyPinError(new Error("Bad Request: not enough rights to manage pins"))).toBe(
89
+ "forbidden",
90
+ );
91
+ expect(classifyPinError(new Error("connect ETIMEDOUT 1.2.3.4:443"))).toBe("timeout");
92
+ });
93
+
94
+ it("returns 'fail' for any unrecognised error shape", () => {
95
+ expect(classifyPinError(new Error("weird unexpected failure"))).toBe("fail");
96
+ expect(classifyPinError("random string")).toBe("fail");
97
+ expect(classifyPinError({})).toBe("fail");
98
+ });
99
+
100
+ it("returns 'fail' for null/undefined", () => {
101
+ expect(classifyPinError(null)).toBe("fail");
102
+ expect(classifyPinError(undefined)).toBe("fail");
103
+ });
104
+ });
105
+
106
+ describe("errorMessage", () => {
107
+ it("extracts message from Error instance", () => {
108
+ expect(errorMessage(new Error("boom"))).toBe("boom");
109
+ });
110
+
111
+ it("prefers description over message on grammy-shaped objects", () => {
112
+ expect(errorMessage({ description: "Telegram says no", message: "generic" })).toBe(
113
+ "Telegram says no",
114
+ );
115
+ });
116
+
117
+ it("returns empty string for null", () => {
118
+ expect(errorMessage(null)).toBe("");
119
+ });
120
+
121
+ it("stringifies unknown shapes", () => {
122
+ expect(errorMessage(42)).toBe("42");
123
+ });
124
+ });
@@ -0,0 +1,97 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import { mkdtempSync, readFileSync, existsSync, rmSync, statSync } from 'fs'
3
+ import { tmpdir } from 'os'
4
+ import { join } from 'path'
5
+ import {
6
+ installPluginLogger,
7
+ resolveLogPath,
8
+ _resetForTests,
9
+ } from '../plugin-logger.js'
10
+
11
+ let tmpDir: string
12
+ let logPath: string
13
+
14
+ beforeEach(() => {
15
+ tmpDir = mkdtempSync(join(tmpdir(), 'plugin-logger-'))
16
+ logPath = join(tmpDir, 'nested', 'subdir', 'telegram-plugin.log')
17
+ process.env.SWITCHROOM_TELEGRAM_LOG_PATH = logPath
18
+ _resetForTests()
19
+ })
20
+
21
+ afterEach(() => {
22
+ _resetForTests()
23
+ delete process.env.SWITCHROOM_TELEGRAM_LOG_PATH
24
+ try { rmSync(tmpDir, { recursive: true, force: true }) } catch { /* ignore */ }
25
+ })
26
+
27
+ describe('resolveLogPath', () => {
28
+ it('uses SWITCHROOM_TELEGRAM_LOG_PATH when set', () => {
29
+ expect(resolveLogPath({ SWITCHROOM_TELEGRAM_LOG_PATH: '/tmp/custom.log' })).toBe(
30
+ '/tmp/custom.log',
31
+ )
32
+ })
33
+
34
+ it('falls back to a path under ~/.switchroom/logs when env is unset', () => {
35
+ const out = resolveLogPath({})
36
+ expect(out).toMatch(/\.switchroom\/logs\/telegram-plugin\.log$/)
37
+ })
38
+ })
39
+
40
+ describe('installPluginLogger — stderr interception', () => {
41
+ it('creates the log directory if missing', () => {
42
+ installPluginLogger()
43
+ process.stderr.write('telegram channel: boot\n')
44
+ expect(existsSync(logPath)).toBe(true)
45
+ })
46
+
47
+ it('appends every stderr.write call to the logfile', () => {
48
+ installPluginLogger()
49
+ process.stderr.write('telegram channel: line one\n')
50
+ process.stderr.write('[streaming-metrics] {"kind":"x"}\n')
51
+ process.stderr.write('stream → edit failed: whatever\n')
52
+ const contents = readFileSync(logPath, 'utf8')
53
+ expect(contents).toContain('telegram channel: line one')
54
+ expect(contents).toContain('[streaming-metrics]')
55
+ expect(contents).toContain('stream → edit failed')
56
+ })
57
+
58
+ it('forwards to the original stderr (behavior unchanged for other readers)', () => {
59
+ // We can't easily observe bun's real stderr, but we can verify the
60
+ // wrapped writer returns a boolean like the original does.
61
+ installPluginLogger()
62
+ const result = process.stderr.write('probe\n')
63
+ expect(typeof result).toBe('boolean')
64
+ })
65
+
66
+ it('uninstall() restores the original writer and stops capturing', () => {
67
+ const handle = installPluginLogger()
68
+ process.stderr.write('before\n')
69
+ handle.uninstall()
70
+ process.stderr.write('after\n')
71
+ const contents = readFileSync(logPath, 'utf8')
72
+ expect(contents).toContain('before')
73
+ expect(contents).not.toContain('after')
74
+ })
75
+
76
+ it('is idempotent — second install returns the first handle', () => {
77
+ const a = installPluginLogger()
78
+ const b = installPluginLogger()
79
+ expect(b).toBe(a)
80
+ })
81
+
82
+ it('accepts Uint8Array chunks and decodes them to utf8 in the file', () => {
83
+ installPluginLogger()
84
+ const encoder = new TextEncoder()
85
+ process.stderr.write(encoder.encode('binary-path: ok\n'))
86
+ const contents = readFileSync(logPath, 'utf8')
87
+ expect(contents).toContain('binary-path: ok')
88
+ })
89
+
90
+ it('swallows filesystem errors and never throws from stderr.write', () => {
91
+ // Point at an unwritable path (a file that cannot become a directory).
92
+ process.env.SWITCHROOM_TELEGRAM_LOG_PATH = '/proc/1/cannot-write-here.log'
93
+ _resetForTests()
94
+ installPluginLogger()
95
+ expect(() => process.stderr.write('still ok\n')).not.toThrow()
96
+ })
97
+ })
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Tests for telegram-plugin/gateway/poll-health.ts (issue #56).
3
+ */
4
+
5
+ import { describe, it, expect, vi } from "vitest";
6
+ import { createPollHealthCheck } from "../gateway/poll-health.js";
7
+
8
+ describe("createPollHealthCheck", () => {
9
+ it("does not call onStall on success", async () => {
10
+ const onStall = vi.fn().mockResolvedValue(undefined);
11
+ let tickFn: () => void = () => {};
12
+ const hc = createPollHealthCheck({
13
+ ping: async () => undefined,
14
+ onStall,
15
+ failureThreshold: 3,
16
+ setIntervalFn: (fn) => { tickFn = fn; return 1 as unknown as ReturnType<typeof setInterval>; },
17
+ clearIntervalFn: () => {},
18
+ log: () => {},
19
+ });
20
+ hc.start();
21
+ for (let i = 0; i < 5; i++) {
22
+ tickFn();
23
+ await Promise.resolve();
24
+ }
25
+ expect(onStall).not.toHaveBeenCalled();
26
+ expect(hc.consecutiveFailures()).toBe(0);
27
+ hc.stop();
28
+ });
29
+
30
+ it("counts consecutive failures and fires onStall at threshold", async () => {
31
+ const onStall = vi.fn().mockResolvedValue(undefined);
32
+ let tickFn: () => void = () => {};
33
+ const hc = createPollHealthCheck({
34
+ ping: async () => { throw new Error("network down"); },
35
+ onStall,
36
+ failureThreshold: 3,
37
+ setIntervalFn: (fn) => { tickFn = fn; return 1 as unknown as ReturnType<typeof setInterval>; },
38
+ clearIntervalFn: () => {},
39
+ log: () => {},
40
+ });
41
+ hc.start();
42
+ tickFn(); await Promise.resolve();
43
+ tickFn(); await Promise.resolve();
44
+ tickFn(); await Promise.resolve();
45
+ await new Promise((r) => setTimeout(r, 10));
46
+ expect(onStall).toHaveBeenCalledTimes(1);
47
+ });
48
+
49
+ it("resets failure count on a successful ping", async () => {
50
+ let pingShouldFail = true;
51
+ let tickFn: () => void = () => {};
52
+ const hc = createPollHealthCheck({
53
+ ping: async () => { if (pingShouldFail) throw new Error("oops"); },
54
+ onStall: async () => {},
55
+ failureThreshold: 3,
56
+ setIntervalFn: (fn) => { tickFn = fn; return 1 as unknown as ReturnType<typeof setInterval>; },
57
+ clearIntervalFn: () => {},
58
+ log: () => {},
59
+ });
60
+ hc.start();
61
+ tickFn(); await Promise.resolve();
62
+ tickFn(); await Promise.resolve();
63
+ expect(hc.consecutiveFailures()).toBeGreaterThan(0);
64
+ pingShouldFail = false;
65
+ tickFn(); await Promise.resolve();
66
+ await new Promise((r) => setTimeout(r, 10));
67
+ expect(hc.consecutiveFailures()).toBe(0);
68
+ hc.stop();
69
+ });
70
+
71
+ it("stop() cancels the interval", () => {
72
+ const onStall = vi.fn();
73
+ let cleared = false;
74
+ const hc = createPollHealthCheck({
75
+ ping: async () => { throw new Error("x"); },
76
+ onStall,
77
+ failureThreshold: 1,
78
+ setIntervalFn: () => 99 as unknown as ReturnType<typeof setInterval>,
79
+ clearIntervalFn: (id) => { if (id === 99) cleared = true; },
80
+ log: () => {},
81
+ });
82
+ hc.start();
83
+ hc.stop();
84
+ expect(cleared).toBe(true);
85
+ });
86
+ });
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Unit tests for #549's preamble-suppressor.
3
+ *
4
+ * The suppressor is the buffering policy that decides whether assistant
5
+ * text emitted during a turn is "preamble" (consumed by the next tool's
6
+ * progress-card narrative) or "answer text" (sent to chat). The bug
7
+ * #549 was: every text event went to BOTH surfaces because there was no
8
+ * gate to distinguish preamble from answer.
9
+ *
10
+ * Tests inject a synthetic timer so vi.useFakeTimers gives deterministic
11
+ * windowed behavior.
12
+ */
13
+
14
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
15
+ import { PreambleSuppressor } from '../gateway/preamble-suppressor.js'
16
+
17
+ describe('PreambleSuppressor', () => {
18
+ beforeEach(() => {
19
+ vi.useFakeTimers()
20
+ })
21
+ afterEach(() => {
22
+ vi.useRealTimers()
23
+ })
24
+
25
+ // ─── Core flush-after-window behavior ────────────────────────────────
26
+
27
+ it('text with no tool: flushes as answer text after bufferMs', () => {
28
+ // The agent emits text and ends the turn without any tool. That
29
+ // text IS the answer; flush to chat after the buffer window.
30
+ const emits: string[] = []
31
+ const sup = new PreambleSuppressor({
32
+ emitAnswer: (t) => emits.push(t),
33
+ bufferMs: 150,
34
+ })
35
+ sup.onText('Hello there!')
36
+ expect(emits).toEqual([])
37
+ vi.advanceTimersByTime(149)
38
+ expect(emits).toEqual([])
39
+ vi.advanceTimersByTime(2)
40
+ expect(emits).toEqual(['Hello there!'])
41
+ })
42
+
43
+ it('text-then-non-reply-tool: drops without emitting (the #549 reproducer)', () => {
44
+ // fails when: a regression makes onTool() flush preamble text to
45
+ // chat instead of dropping it. Exact bug class #549 was reporting.
46
+ const emits: string[] = []
47
+ const sup = new PreambleSuppressor({
48
+ emitAnswer: (t) => emits.push(t),
49
+ bufferMs: 150,
50
+ })
51
+ sup.onText('Looking it up.')
52
+ // Tool arrives within the buffer window — this text was preamble.
53
+ vi.advanceTimersByTime(50)
54
+ sup.onTool({ isReplyTool: false })
55
+ // Even after the would-be-flush time elapses, no emission.
56
+ vi.advanceTimersByTime(200)
57
+ expect(emits).toEqual([])
58
+ expect(sup.hasPending()).toBe(false)
59
+ })
60
+
61
+ it('text-then-reply-tool: flushes (reply tool is the answer surface)', () => {
62
+ // The reply / stream_reply tool's text payload IS the answer. If
63
+ // the agent also emitted plain text just before, that text is
64
+ // also part of the answer — flush it. (The reply-tool's own dedup
65
+ // path handles overlap with the stream's payload.)
66
+ const emits: string[] = []
67
+ const sup = new PreambleSuppressor({
68
+ emitAnswer: (t) => emits.push(t),
69
+ bufferMs: 150,
70
+ })
71
+ sup.onText('Here is what I found:')
72
+ sup.onTool({ isReplyTool: true })
73
+ expect(emits).toEqual(['Here is what I found:'])
74
+ })
75
+
76
+ it('multiple text chunks before tool: all dropped together', () => {
77
+ // The model streams text in chunks. If a tool follows, ALL chunks
78
+ // were preamble — drop the whole accumulated buffer.
79
+ const emits: string[] = []
80
+ const sup = new PreambleSuppressor({
81
+ emitAnswer: (t) => emits.push(t),
82
+ bufferMs: 150,
83
+ })
84
+ sup.onText('Looking ')
85
+ sup.onText('it ')
86
+ sup.onText('up...')
87
+ vi.advanceTimersByTime(50)
88
+ sup.onTool({ isReplyTool: false })
89
+ vi.advanceTimersByTime(200)
90
+ expect(emits).toEqual([])
91
+ })
92
+
93
+ it('multiple text chunks with no tool: flushes all together', () => {
94
+ const emits: string[] = []
95
+ const sup = new PreambleSuppressor({
96
+ emitAnswer: (t) => emits.push(t),
97
+ bufferMs: 150,
98
+ })
99
+ sup.onText('The answer ')
100
+ vi.advanceTimersByTime(50)
101
+ sup.onText('is 42.')
102
+ vi.advanceTimersByTime(200)
103
+ expect(emits).toEqual(['The answer is 42.'])
104
+ })
105
+
106
+ // ─── Multi-segment turn (preamble → tool → answer) ──────────────────
107
+
108
+ it('preamble, tool, then answer text: only the answer flushes', () => {
109
+ // The realistic shape: agent thinks-out-loud ("Looking it up..."),
110
+ // calls a tool, then emits the actual answer ("Found it: 42.").
111
+ // Preamble drops; answer flushes.
112
+ const emits: string[] = []
113
+ const sup = new PreambleSuppressor({
114
+ emitAnswer: (t) => emits.push(t),
115
+ bufferMs: 150,
116
+ })
117
+ sup.onText('Looking it up...')
118
+ sup.onTool({ isReplyTool: false })
119
+ sup.onText('Found it: 42.')
120
+ vi.advanceTimersByTime(200)
121
+ expect(emits).toEqual(['Found it: 42.'])
122
+ })
123
+
124
+ it('cumulative answer text accumulates across multiple non-tool flushes', () => {
125
+ // If the agent emits text, waits, emits more text (no tool in
126
+ // between) — both flush as answer text and the cumulative payload
127
+ // grows. The emit callback receives the cumulative text each time.
128
+ const emits: string[] = []
129
+ const sup = new PreambleSuppressor({
130
+ emitAnswer: (t) => emits.push(t),
131
+ bufferMs: 150,
132
+ })
133
+ sup.onText('Part one. ')
134
+ vi.advanceTimersByTime(200)
135
+ expect(emits).toEqual(['Part one. '])
136
+ sup.onText('Part two.')
137
+ vi.advanceTimersByTime(200)
138
+ expect(emits).toEqual(['Part one. ', 'Part one. Part two.'])
139
+ })
140
+
141
+ // ─── flushNow / dropNow / reset ──────────────────────────────────────
142
+
143
+ it('flushNow: forces immediate flush of pending text', () => {
144
+ const emits: string[] = []
145
+ const sup = new PreambleSuppressor({
146
+ emitAnswer: (t) => emits.push(t),
147
+ bufferMs: 150,
148
+ })
149
+ sup.onText('Pending text.')
150
+ expect(emits).toEqual([])
151
+ sup.flushNow()
152
+ expect(emits).toEqual(['Pending text.'])
153
+ // Idempotent — second flushNow is a no-op (nothing pending).
154
+ sup.flushNow()
155
+ expect(emits).toEqual(['Pending text.'])
156
+ })
157
+
158
+ it('dropNow: discards pending text without emitting', () => {
159
+ const emits: string[] = []
160
+ const sup = new PreambleSuppressor({
161
+ emitAnswer: (t) => emits.push(t),
162
+ bufferMs: 150,
163
+ })
164
+ sup.onText('Pending text.')
165
+ sup.dropNow()
166
+ vi.advanceTimersByTime(200)
167
+ expect(emits).toEqual([])
168
+ expect(sup.hasPending()).toBe(false)
169
+ })
170
+
171
+ it('reset: clears cumulative answer text + pending', () => {
172
+ // Used at fresh-turn enqueue. After reset, the cumulative answer
173
+ // text is empty — next flush starts the answer payload from scratch.
174
+ const emits: string[] = []
175
+ const sup = new PreambleSuppressor({
176
+ emitAnswer: (t) => emits.push(t),
177
+ bufferMs: 150,
178
+ })
179
+ sup.onText('Turn 1 text.')
180
+ vi.advanceTimersByTime(200)
181
+ expect(emits).toEqual(['Turn 1 text.'])
182
+ expect(sup.currentAnswerText()).toBe('Turn 1 text.')
183
+
184
+ sup.reset()
185
+ expect(sup.currentAnswerText()).toBe('')
186
+
187
+ sup.onText('Turn 2 text.')
188
+ vi.advanceTimersByTime(200)
189
+ expect(emits).toEqual(['Turn 1 text.', 'Turn 2 text.'])
190
+ })
191
+
192
+ // ─── Edge cases ──────────────────────────────────────────────────────
193
+
194
+ it('empty text chunk: no-op, timer not started', () => {
195
+ const emits: string[] = []
196
+ const sup = new PreambleSuppressor({
197
+ emitAnswer: (t) => emits.push(t),
198
+ bufferMs: 150,
199
+ })
200
+ sup.onText('')
201
+ vi.advanceTimersByTime(200)
202
+ expect(emits).toEqual([])
203
+ expect(sup.hasPending()).toBe(false)
204
+ })
205
+
206
+ it('flushNow with empty buffer: no emit', () => {
207
+ const emits: string[] = []
208
+ const sup = new PreambleSuppressor({
209
+ emitAnswer: (t) => emits.push(t),
210
+ bufferMs: 150,
211
+ })
212
+ sup.flushNow()
213
+ expect(emits).toEqual([])
214
+ })
215
+
216
+ it('chunks arriving inside buffer window reset the timer (debounce-style)', () => {
217
+ // A second text chunk before the timer fires should EXTEND the
218
+ // window, not start a separate timer. Otherwise rapid streaming
219
+ // chunks would flush prematurely as separate answer-text emissions.
220
+ const emits: string[] = []
221
+ const sup = new PreambleSuppressor({
222
+ emitAnswer: (t) => emits.push(t),
223
+ bufferMs: 150,
224
+ })
225
+ sup.onText('chunk 1 ')
226
+ vi.advanceTimersByTime(100)
227
+ sup.onText('chunk 2')
228
+ vi.advanceTimersByTime(100) // 200ms total since first chunk, but only 100ms since last
229
+ expect(emits).toEqual([]) // not flushed yet
230
+ vi.advanceTimersByTime(60)
231
+ expect(emits).toEqual(['chunk 1 chunk 2'])
232
+ })
233
+
234
+ it('tool then text then tool: middle text dropped (it was preamble for second tool)', () => {
235
+ const emits: string[] = []
236
+ const sup = new PreambleSuppressor({
237
+ emitAnswer: (t) => emits.push(t),
238
+ bufferMs: 150,
239
+ })
240
+ sup.onTool({ isReplyTool: false }) // first tool — nothing pending
241
+ sup.onText('between tools text')
242
+ sup.onTool({ isReplyTool: false }) // second tool consumes the text as preamble
243
+ vi.advanceTimersByTime(200)
244
+ expect(emits).toEqual([])
245
+ })
246
+
247
+ // ─── Cross-turn isolation (regression: dropNow used to leak answerTextOnly) ───
248
+
249
+ it('dropNow clears answerTextOnly so next turn does not inherit stale text', () => {
250
+ // Turn-flush silent-marker / context-exhaust teardown calls dropNow().
251
+ // If dropNow left `answerTextOnly` populated, the next turn's first
252
+ // flush would prepend the previous turn's content.
253
+ //
254
+ // fails when: dropNow goes back to clearing only the pending buffer.
255
+ const emits: string[] = []
256
+ const sup = new PreambleSuppressor({
257
+ emitAnswer: (t) => emits.push(t),
258
+ bufferMs: 150,
259
+ })
260
+ sup.onText('Turn 1 answer.')
261
+ vi.advanceTimersByTime(200)
262
+ expect(emits).toEqual(['Turn 1 answer.'])
263
+ expect(sup.currentAnswerText()).toBe('Turn 1 answer.')
264
+
265
+ // Silent-marker teardown — drop without flushing.
266
+ sup.dropNow()
267
+ expect(sup.currentAnswerText()).toBe('')
268
+
269
+ // Turn 2 begins. Without the clear, the next flush would emit
270
+ // "Turn 1 answer.Turn 2 answer." — which is wrong.
271
+ sup.onText('Turn 2 answer.')
272
+ vi.advanceTimersByTime(200)
273
+ expect(emits[emits.length - 1]).toBe('Turn 2 answer.')
274
+ })
275
+
276
+ it('default bufferMs (150) is used when not specified', () => {
277
+ const emits: string[] = []
278
+ const sup = new PreambleSuppressor({ emitAnswer: (t) => emits.push(t) })
279
+ sup.onText('default-window text')
280
+ vi.advanceTimersByTime(149)
281
+ expect(emits).toEqual([])
282
+ vi.advanceTimersByTime(2)
283
+ expect(emits).toEqual(['default-window text'])
284
+ })
285
+ })
@@ -0,0 +1,73 @@
1
+ /**
2
+ * PR-C2 — reportApiFailure crossing its threshold while a chat is in
3
+ * `pendingCompletion` state must not corrupt the deferred-completion
4
+ * resolution path.
5
+ *
6
+ * Setup: parent turn_end while a bg sub-agent is still running →
7
+ * chatState.pendingCompletion=true. Then `maxConsecutive4xx` permanent
8
+ * 4xx failures arrive (the card is being abandoned locally). We then
9
+ * resolve the bg sub-agent via sub_agent_turn_end. The driver must:
10
+ *
11
+ * - Fire onTurnComplete exactly once for the originating turnKey.
12
+ * - Not double-flush.
13
+ *
14
+ * fails when: the terminal-apiFailure branch races with the
15
+ * pendingCompletion resolution path and either swallows or duplicates
16
+ * the completion callback.
17
+ */
18
+ import { describe, it, expect } from 'vitest'
19
+ import { makeHarness, enqueue } from './_progress-card-harness.js'
20
+
21
+ describe('PR-C2: API failure crossing threshold during pendingCompletion', () => {
22
+ it('deferred completion still resolves exactly once; no double-flush', () => {
23
+ const completions: string[] = []
24
+ const { driver } = makeHarness({
25
+ minIntervalMs: 0,
26
+ coalesceMs: 0,
27
+ heartbeatMs: 999_999, // keep heartbeat from racing the test
28
+ maxConsecutive4xx: 3,
29
+ promoteAfterMs: 999_999,
30
+ onTurnComplete: (s) => completions.push(s.turnKey),
31
+ })
32
+ const maps = driver._debugGetMaps!()
33
+ const CHAT = 'cA'
34
+
35
+ driver.ingest(enqueue(CHAT), null)
36
+ driver.ingest(
37
+ {
38
+ kind: 'tool_use',
39
+ toolName: 'Agent',
40
+ toolUseId: 'tu1',
41
+ input: { prompt: 'bg', run_in_background: true },
42
+ },
43
+ CHAT,
44
+ )
45
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'saBG', firstPromptText: 'bg' }, CHAT)
46
+ driver.ingest({ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__reply' }, CHAT)
47
+ driver.recordOutboundDelivered(CHAT)
48
+ driver.ingest({ kind: 'turn_end', durationMs: 100 }, CHAT)
49
+
50
+ expect(maps.chats.size).toBe(1)
51
+ const turnKey = [...maps.chats.keys()][0]
52
+ const cs = maps.chats.get(turnKey) as { pendingCompletion: boolean; apiFailures: { terminal: boolean } }
53
+ expect(cs.pendingCompletion).toBe(true)
54
+
55
+ // Hammer reportApiFailure past the threshold (3).
56
+ for (let i = 0; i < 5; i++) {
57
+ driver.reportApiFailure(turnKey, {
58
+ kind: 'permanent_4xx',
59
+ code: 400,
60
+ description: 'bad request',
61
+ })
62
+ }
63
+ expect(cs.apiFailures.terminal).toBe(true)
64
+ // No completion fired yet — bg still running.
65
+ expect(completions.length).toBe(0)
66
+
67
+ // Resolve the bg sub-agent. The originating turn must complete
68
+ // exactly once.
69
+ driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'saBG' }, CHAT)
70
+ expect(completions.length).toBe(1)
71
+ expect(completions[0]).toBe(turnKey)
72
+ })
73
+ })