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,313 @@
1
+ /**
2
+ * I6 — Turn-flush + replay duplicate-content suppression (#546).
3
+ *
4
+ * THIS FILE IS A REGRESSION-PREVENTION LAYER. See
5
+ * `real-gateway-ipc-lifecycle.test.ts` for the I1–I5 backstory.
6
+ *
7
+ * Bug #546 (resolved by 5bed5b7 — "outbound content-dedup window"):
8
+ * agent emits text → bridge disconnects mid-flight → gateway's
9
+ * turn-flush backstop sends the buffered text as HTML → bridge
10
+ * reconnects → claude-code replays the un-acked stream_reply tool_call
11
+ * with identical content but raw markdown → user sees the same content
12
+ * twice.
13
+ *
14
+ * The fix added `OutboundDedupCache` (telegram-plugin/recent-outbound-
15
+ * dedup.ts), which has 23 unit tests on the cache logic but ZERO
16
+ * integration tests reproducing the full sequence end-to-end. This file
17
+ * closes that gap. Without integration coverage the *bug class* (two
18
+ * paths emitting the same content within a TTL) is still latent
19
+ * anywhere the dedup cache isn't wired.
20
+ *
21
+ * Invariant pinned here:
22
+ *
23
+ * I6 — When the same multi-line content is emitted twice for the
24
+ * same chat within the dedup TTL, only ONE outbound lands.
25
+ * Holds across: bridge cycle in between, format mismatch
26
+ * (HTML vs markdown), and reactions still finalize correctly.
27
+ *
28
+ * Failure mode if the dedup wiring regresses (e.g. someone removes the
29
+ * cache wire-up in `streamReply` or rolls back recent-outbound-dedup.ts):
30
+ * `recorder.sentTexts(chat).length === 2` and the test fails on the
31
+ * "exactly one outbound" assertion.
32
+ */
33
+
34
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
35
+ import { createRealGatewayHarness } from './real-gateway-harness.js'
36
+
37
+ const CHAT = '8248703757'
38
+ const INBOUND_MSG = 100
39
+ const REPLY_TEXT =
40
+ 'Here is a reply with enough content to clear the 24-char dedup floor — this paragraph is intentionally long to mirror the multi-paragraph replies that bug #546 actually duplicated.'
41
+
42
+ beforeEach(() => {
43
+ vi.useFakeTimers()
44
+ })
45
+ afterEach(() => {
46
+ vi.useRealTimers()
47
+ })
48
+
49
+ describe('I6 — turn-flush + replay duplicate-content suppression (#546)', () => {
50
+ it('two identical send calls within TTL — second is suppressed by dedup (the #546 reproducer)', async () => {
51
+ // fails when: the dedup cache wire-up in real-gateway-harness's
52
+ // send/streamReply path is removed, or recent-outbound-dedup.ts's
53
+ // check() always returns null.
54
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
55
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hello' })
56
+ h.feedSessionEvent({
57
+ kind: 'enqueue',
58
+ chatId: CHAT,
59
+ messageId: '1',
60
+ threadId: null,
61
+ rawContent: 'hello',
62
+ })
63
+ await h.clock.advance(20)
64
+
65
+ const result = await h.simulateRetryDup({ chat_id: CHAT, text: REPLY_TEXT })
66
+
67
+ expect(result.suppressedSecond).toBe(true)
68
+ // The bridge cycle uses a REGISTERED agent, so flushOnAgentDisconnect
69
+ // must have run. If this is false, the test is a tautology (the
70
+ // anonymous-skip path bypassed the production helper).
71
+ expect(result.flushRan, 'flushOnAgentDisconnect must run — otherwise the test is a tautology').toBe(true)
72
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(1)
73
+ expect(h.dedupSuppressedCount()).toBe(1)
74
+ h.finalize()
75
+ })
76
+
77
+ it('content-equal-but-format-differ still dedupes (HTML vs markdown)', async () => {
78
+ // The smoking-gun shape from #546: msg=5025 had `<b>...</b>`,
79
+ // msg=5027 had `**...**`, same content. Dedup must catch this via
80
+ // the `normalizeForDedup` strip step.
81
+ //
82
+ // fails when: normalizeForDedup loses its HTML-tag or markdown-
83
+ // marker stripping (e.g. someone simplifies it to a plain hash).
84
+ //
85
+ // We use `h.send()` (fresh sendMessage every time) rather than
86
+ // `h.streamReply()` (which edits the same message on repeat
87
+ // calls). The bug class is "two SEPARATE messages with the same
88
+ // content", not "two streaming edits of one message."
89
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
90
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
91
+ h.feedSessionEvent({
92
+ kind: 'enqueue',
93
+ chatId: CHAT,
94
+ messageId: '1',
95
+ threadId: null,
96
+ rawContent: 'q',
97
+ })
98
+
99
+ const htmlForm = `<b>Important update:</b> The config file has been regenerated with the new schema layout described in section 4.2 of the migration guide.`
100
+ const mdForm = `**Important update:** The config file has been regenerated with the new schema layout described in section 4.2 of the migration guide.`
101
+
102
+ const id1 = await h.send({ chat_id: CHAT, text: htmlForm })
103
+ const id2 = await h.send({ chat_id: CHAT, text: mdForm })
104
+ expect(id1).not.toBeNull()
105
+ expect(id2).toBeNull() // dedup suppressed
106
+ expect(h.recorder.sentTexts(CHAT).length).toBe(1)
107
+ expect(h.dedupSuppressedCount()).toBe(1)
108
+ h.finalize()
109
+ })
110
+
111
+ it('content shorter than DEDUP_MIN_CONTENT_LEN (24 chars) is NOT deduped', async () => {
112
+ // Conservative floor: short replies ("ok", "got it", "✅") legitimately
113
+ // recur. Dedup ignores them.
114
+ //
115
+ // fails when: someone tightens the floor below 24 chars without
116
+ // updating tests.
117
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
118
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
119
+ h.feedSessionEvent({
120
+ kind: 'enqueue',
121
+ chatId: CHAT,
122
+ messageId: '1',
123
+ threadId: null,
124
+ rawContent: 'q',
125
+ })
126
+ const id1 = await h.send({ chat_id: CHAT, text: 'ok' })
127
+ const id2 = await h.send({ chat_id: CHAT, text: 'ok' })
128
+ expect(id1).not.toBeNull()
129
+ expect(id2).not.toBeNull() // NOT deduped — content too short
130
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === 'ok').length).toBe(2)
131
+ expect(h.dedupSuppressedCount()).toBe(0)
132
+ h.finalize()
133
+ })
134
+
135
+ it('after TTL expires, the same content sends again (cache evicts)', async () => {
136
+ // The TTL is bounded — same content sent an hour later should NOT
137
+ // be suppressed. This pins the eviction semantics.
138
+ //
139
+ // fails when: TTL eviction breaks (entries linger forever) or the
140
+ // dedup cache's clock source ignores caller-supplied `now`.
141
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true, dedupTtlMs: 1000 })
142
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
143
+ h.feedSessionEvent({
144
+ kind: 'enqueue',
145
+ chatId: CHAT,
146
+ messageId: '1',
147
+ threadId: null,
148
+ rawContent: 'q',
149
+ })
150
+
151
+ const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
152
+ expect(id1).not.toBeNull()
153
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(1)
154
+
155
+ // Advance past the TTL.
156
+ await h.clock.advance(2000)
157
+
158
+ const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
159
+ expect(id2).not.toBeNull() // TTL expired → fresh send allowed
160
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(2)
161
+ h.finalize()
162
+ })
163
+
164
+ it('without withDedup, duplicate content lands twice (control case — confirms the harness default is back-compat)', async () => {
165
+ // Sanity check: F1–F4 tests don't pass withDedup, and they MUST
166
+ // continue to see every outbound landing as before. If a future
167
+ // change wires dedup unconditionally, this test catches it.
168
+ //
169
+ // fails when: someone makes withDedup default to true, breaking
170
+ // F1–F4 deadline assertions.
171
+ const h = createRealGatewayHarness({ gapMs: 0 })
172
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
173
+ h.feedSessionEvent({
174
+ kind: 'enqueue',
175
+ chatId: CHAT,
176
+ messageId: '1',
177
+ threadId: null,
178
+ rawContent: 'q',
179
+ })
180
+ const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
181
+ const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
182
+ expect(id1).not.toBeNull()
183
+ expect(id2).not.toBeNull() // no dedup, no suppression
184
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === REPLY_TEXT).length).toBe(2)
185
+ expect(h.dedup).toBeNull()
186
+ h.finalize()
187
+ })
188
+ })
189
+
190
+ // ─── I5 (Bug C) defense-in-depth via dedup ─────────────────────────────
191
+ //
192
+ // Bug C lives in profiles/_base/start.sh.hbs (a shell script the gateway
193
+ // can't run). The actual fix is in the wake-audit sentinel comparison
194
+ // logic, not in the gateway. BUT the OBSERVABLE failure is "duplicate
195
+ // reply lands in the gateway." If the wake-audit fix regresses, the
196
+ // dedup cache should still suppress the duplicate outbound. Defense in
197
+ // depth — testable here.
198
+ //
199
+ // I5 in real-gateway-ipc-lifecycle.test.ts is `.skip`'d pending the
200
+ // profile fix; this test is its harness-level companion.
201
+ describe('I5(b) — wake-audit respawn duplicate suppressed by dedup defense in depth (Bug C)', () => {
202
+ it('respawn-replay simulation produces only one user-visible reply', async () => {
203
+ // fails when: the dedup wire-up regresses, OR if a wake-audit-style
204
+ // duplicate reply path emerges that bypasses streamReply (e.g. uses
205
+ // raw bot.api.sendMessage). In that case this test still catches
206
+ // the regression by counting outbounds.
207
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
208
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'morning' })
209
+ h.feedSessionEvent({
210
+ kind: 'enqueue',
211
+ chatId: CHAT,
212
+ messageId: '1',
213
+ threadId: null,
214
+ rawContent: 'morning',
215
+ })
216
+
217
+ const greeting =
218
+ 'Good morning! I noticed you mentioned the deploy yesterday — did the rollback succeed and are you ready to retry, or is there something I can help you investigate first?'
219
+
220
+ // First wake-audit fire (legitimate).
221
+ const id1 = await h.send({ chat_id: CHAT, text: greeting })
222
+ expect(id1).not.toBeNull()
223
+
224
+ // Simulate the --continue respawn: agent dies (registered disconnect),
225
+ // a fresh agent spawns and re-runs wake-audit, attempts to fire the
226
+ // same greeting again because the marker check was wrong.
227
+ const cid = h.bridgeConnect('agent-x')
228
+ h.bridgeDisconnect(cid)
229
+ await h.clock.advance(50)
230
+
231
+ const id2 = await h.send({ chat_id: CHAT, text: greeting })
232
+ expect(id2).toBeNull() // dedup suppressed
233
+
234
+ expect(h.recorder.sentTexts(CHAT).filter((t) => t === greeting).length).toBe(1)
235
+ expect(h.dedupSuppressedCount()).toBeGreaterThanOrEqual(1)
236
+ // I7 invariant: even across the wake-audit respawn, the chat
237
+ // sees exactly ONE anchor sendMessage. (#626 — duplicate status
238
+ // messages bug class.)
239
+ expect(h.anchorMessageCount(CHAT)).toBe(1)
240
+ h.finalize()
241
+ })
242
+ })
243
+
244
+ // ─── I7 — duplicate-status-message regression (issue #626) ──────────────
245
+ //
246
+ // The progress-card driver / stream-reply-handler interaction has a
247
+ // failure mode where `done=true` finalizes + deletes the active draft
248
+ // stream entry, and a subsequent emit on the same lane+turn creates
249
+ // a fresh `sendMessage` instead of editing the pinned card. User sees
250
+ // multiple separate status messages where one anchor message edited
251
+ // in place was expected.
252
+ //
253
+ // The fix is the `lookupExistingMessageId` hook in
254
+ // `stream-reply-handler.ts` (wired in `gateway.ts`'s progress-card
255
+ // emit callback to consult `pinMgr.pinnedMessageId(turnKey, agentId)`).
256
+ // Detailed unit-level coverage lives in
257
+ // `stream-reply-handler.test.ts` and `draft-stream.test.ts`.
258
+ //
259
+ // At the harness layer, the invariant is:
260
+ //
261
+ // I7 — For any (chatId, threadId, turnKey?), the recorder shows
262
+ // exactly one `sendMessage` call (the anchor). All subsequent
263
+ // renders for the same logical turn are `editMessageText` on
264
+ // that anchor's `message_id`.
265
+ //
266
+ // This test pins the invariant for the simplest happy-path scenario
267
+ // (one inbound, one outbound). The full progress-card lifecycle
268
+ // scenario (first emit → done=true → post-done emit) is exercised at
269
+ // the unit level — wiring the production progress driver through the
270
+ // real-gateway harness is its own followup. What we lock in here is
271
+ // the assertion shape so future regressions in the OTHER 7 paths the
272
+ // RCA identified are caught the moment a second anchor lands.
273
+ describe('I7 — exactly-one-anchor-message invariant (#626)', () => {
274
+ it('happy-path single send — anchorMessageCount equals 1', async () => {
275
+ // fails when: any handler regression causes a single send() call
276
+ // to land as TWO sendMessage invocations (e.g. preamble dedup
277
+ // path and main reply path both firing).
278
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
279
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
280
+ h.feedSessionEvent({
281
+ kind: 'enqueue',
282
+ chatId: CHAT,
283
+ messageId: '1',
284
+ threadId: null,
285
+ rawContent: 'q',
286
+ })
287
+ const id1 = await h.send({ chat_id: CHAT, text: REPLY_TEXT })
288
+ expect(id1).not.toBeNull()
289
+ expect(h.anchorMessageCount(CHAT)).toBe(1)
290
+ h.finalize()
291
+ })
292
+
293
+ it('dedup-suppressed second send does NOT add a second anchor', async () => {
294
+ // The combo invariant: dedup catching a duplicate AND the anchor
295
+ // count staying at 1. Both must hold for #626 to be considered
296
+ // closed at the harness layer.
297
+ const h = createRealGatewayHarness({ gapMs: 0, withDedup: true })
298
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
299
+ h.feedSessionEvent({
300
+ kind: 'enqueue',
301
+ chatId: CHAT,
302
+ messageId: '1',
303
+ threadId: null,
304
+ rawContent: 'q',
305
+ })
306
+ await h.send({ chat_id: CHAT, text: REPLY_TEXT })
307
+ const id2 = await h.send({ chat_id: CHAT, text: REPLY_TEXT }) // same content
308
+ expect(id2).toBeNull()
309
+ expect(h.anchorMessageCount(CHAT)).toBe(1)
310
+ expect(h.dedupSuppressedCount()).toBeGreaterThanOrEqual(1)
311
+ h.finalize()
312
+ })
313
+ })
@@ -0,0 +1,299 @@
1
+ /**
2
+ * IPC + bridge lifecycle invariants — real-gateway harness coverage.
3
+ *
4
+ * THIS FILE IS A REGRESSION-PREVENTION LAYER. Each test corresponds to a
5
+ * real production bug observed on a specific date. The point is to leave
6
+ * breadcrumbs so a future engineer who breaks an invariant sees WHY the
7
+ * test exists.
8
+ *
9
+ * Production observation (2026-05-03 evening): the existing real-gateway
10
+ * harness (PR #582) covered the user-perceived waiting UX (status
11
+ * reactions, progress card, coalescer, first-paint timing) but had ZERO
12
+ * coverage of the IPC layer below it — the bridge connect/register/
13
+ * disconnect lifecycle, the validator boundary that decides which
14
+ * messages are lethal vs tolerated, and the temporal contract between
15
+ * "answer text delivered" and "👍 fires." Multiple bugs slipped through
16
+ * because of that gap.
17
+ *
18
+ * The five invariants pinned here:
19
+ *
20
+ * I1 — Anonymous IPC client lifecycle is observably invisible.
21
+ * Bug A: an anonymous IPC client (recall.py one-shot) connecting
22
+ * and disconnecting flushed the gateway's active status reactions
23
+ * to setDone(), producing premature 👍 mid-turn. Fix: PR #600
24
+ * gates the disconnect-flush on `client.agentName != null`.
25
+ *
26
+ * I2 — Per-agent disconnect isolation. When agent X disconnects,
27
+ * only X's reactions get flushed; Y's stay intact. Today
28
+ * switchroom is single-agent-per-gateway, but pinning the right
29
+ * semantics now means a future multi-agent gateway can't
30
+ * regress silently.
31
+ *
32
+ * I3 — 👍 fires AFTER real delivery, not after JSONL `turn_end`.
33
+ * Bug D (and Z): on slow Telegram outbound, setDone() was
34
+ * firing on the JSONL `turn_end` event before the final
35
+ * sendMessage/editMessageText round-tripped, so the user saw
36
+ * the 👍 for ~150ms before the actual reply text appeared.
37
+ *
38
+ * I4 — Legacy IPC types are tolerated, not lethal. Bug B: a legacy
39
+ * `update_placeholder` IPC message from recall.py crashed the
40
+ * gateway after PR 5 of #553 removed the handler. Fix: the
41
+ * validator already returns false for unknown types, and
42
+ * `processBuffer` logs+continues — but the absence of a test
43
+ * meant the regression went unnoticed until production.
44
+ *
45
+ * I5 — Wake-audit dedup. Bug C: the `.wake-audit-pending` sentinel
46
+ * re-fired mid-conversation under `--continue` respawn,
47
+ * producing a duplicate reply. Fix lives in profiles, not in
48
+ * the gateway, but the invariant test belongs here so a future
49
+ * change can't reintroduce the dup.
50
+ *
51
+ * Bug-to-PR map:
52
+ * Bug A → PR switchroom/switchroom#600 (in flight, conflicting at time of write)
53
+ * Bug B → covered by the same PR #600 (soft-accept update_placeholder)
54
+ * Bug C → next PR (wake-audit profile fix); test here is .skip'd until then
55
+ * Bug D → /tmp/switchroom-bugdz-setdone-timing branch (in flight)
56
+ *
57
+ * State at time of write (2026-05-03):
58
+ * - PR #600 NOT yet merged → I1 + I2 mirror the production semantics
59
+ * INSIDE the harness's bridgeDisconnect helper. When #600 merges,
60
+ * the harness should be re-pointed at the extracted
61
+ * `disconnect-flush.ts` so the test exercises the production code
62
+ * path directly. See TODO in real-gateway-harness.ts.
63
+ * - I4 passes against current main (the validator already rejects
64
+ * unknown types).
65
+ * - I3 currently exercises the harness's streamReply → setDone
66
+ * ordering. When the Bug D+Z fix lands, this test will continue to
67
+ * pass (it only asserts the temporal invariant, not a specific
68
+ * code path).
69
+ * - I5 is .skip'd pending the Bug C profile fix.
70
+ */
71
+
72
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
73
+ import { createRealGatewayHarness } from './real-gateway-harness.js'
74
+
75
+ const CHAT = '8248703757'
76
+ const INBOUND_MSG = 100
77
+
78
+ beforeEach(() => { vi.useFakeTimers() })
79
+ afterEach(() => { vi.useRealTimers() })
80
+
81
+ // ─── I1 — anonymous IPC client lifecycle is invisible (Bug A) ──────────
82
+ describe('I1 — anonymous IPC client lifecycle is observably invisible (Bug A → PR #600)', () => {
83
+ it('anonymous connect → legacy update_placeholder → disconnect MUST NOT fire 👍 mid-turn', async () => {
84
+ // Setup: a real turn is in flight. 👀 is on the user's message and
85
+ // the controller is sitting at queued/thinking — i.e. NOT terminal.
86
+ const h = createRealGatewayHarness({ gapMs: 0 })
87
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hello' })
88
+ h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hello' })
89
+ await h.clock.advance(50)
90
+ h.feedSessionEvent({ kind: 'thinking' })
91
+ await h.clock.advance(50)
92
+ expect(h.recorder.firstReactionMs(CHAT)).not.toBeNull()
93
+ expect(h.recorder.lastReactionEmoji(CHAT)).toBe('👀')
94
+
95
+ const reactionsBefore = h.recorder.reactionSequence().length
96
+
97
+ // Act: an anonymous (recall.py-style) client connects, fires a
98
+ // legacy update_placeholder one-shot, then disconnects. This is
99
+ // EXACTLY Bug A's reproducer.
100
+ const clientId = h.bridgeConnect(null)
101
+ h.sendIpcMessage(clientId, {
102
+ type: 'update_placeholder',
103
+ chatId: CHAT,
104
+ text: '📚 recalling memories',
105
+ })
106
+ h.bridgeDisconnect(clientId)
107
+ await h.clock.advance(10)
108
+
109
+ // Assert: no new reactions emitted. 👍 NOT fired. Active state intact.
110
+ const reactionsAfter = h.recorder.reactionSequence().length
111
+ expect(reactionsAfter).toBe(reactionsBefore)
112
+ expect(h.recorder.lastReactionEmoji(CHAT)).not.toBe('👍')
113
+ // PRIMARY ASSERTION — direct introspection of the production helper's
114
+ // side-effect counts. If the `agentName == null` gate is bypassed,
115
+ // these counts jump to ≥1. The recorder-based assertions above can
116
+ // miss the bug if the per-agent controller's emit isn't recorder-
117
+ // wired; this assertion is unambiguous.
118
+ const sfx = h.flushSideEffects()
119
+ expect(sfx.disposeProgressDriverCalls, 'progress driver disposed for anonymous client — gate bypassed').toBe(0)
120
+ expect(sfx.clearActiveReactionsCalls, 'reactions cleared for anonymous client — gate bypassed').toBe(0)
121
+ h.finalize()
122
+ })
123
+
124
+ it('anonymous disconnect MUST NOT dispose progress driver or close draft streams', async () => {
125
+ // Class-C-shaped turn: card is rendering, draft stream is open.
126
+ // Anonymous disconnect during this state must be a complete no-op.
127
+ const h = createRealGatewayHarness({ gapMs: 0, driverInitialDelayMs: 0 })
128
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'work' })
129
+ h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'work' })
130
+ await h.clock.advance(100)
131
+ h.feedSessionEvent({ kind: 'thinking' })
132
+ await h.clock.advance(50)
133
+
134
+ const editsBefore = h.recorder.edits(CHAT).length
135
+ const sendsBefore = h.recorder.sentTexts(CHAT).length
136
+
137
+ const clientId = h.bridgeConnect(null)
138
+ h.bridgeDisconnect(clientId)
139
+ await h.clock.advance(50)
140
+
141
+ // Driver/streams untouched: no spurious card finalize, no extra
142
+ // sendMessage flushes. The exact counts must match what they were
143
+ // before the anonymous client touched anything.
144
+ expect(h.recorder.edits(CHAT).length).toBe(editsBefore)
145
+ expect(h.recorder.sentTexts(CHAT).length).toBe(sendsBefore)
146
+ // Direct assertion on the production helper's side-effect counts.
147
+ const sfx = h.flushSideEffects()
148
+ expect(sfx.disposeProgressDriverCalls).toBe(0)
149
+ expect(sfx.clearActiveReactionsCalls).toBe(0)
150
+ h.finalize()
151
+ })
152
+ })
153
+
154
+ // ─── I2 — per-agent disconnect isolation ───────────────────────────────
155
+ describe('I2 — per-agent disconnect isolation (single-agent today, multi-agent-safe semantics)', () => {
156
+ it("agent Y's disconnect MUST NOT mutate agent X's active status reaction", async () => {
157
+ const h = createRealGatewayHarness({ gapMs: 0 })
158
+ // Agent X owns the live turn (whichever agent the gateway hosts —
159
+ // in production today, exactly one). 👀 is up.
160
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'q' })
161
+ h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'q' })
162
+ await h.clock.advance(50)
163
+ h.feedSessionEvent({ kind: 'thinking' })
164
+ await h.clock.advance(50)
165
+
166
+ const xClientId = h.bridgeConnect('agent-x')
167
+ const yClientId = h.bridgeConnect('agent-y')
168
+
169
+ const reactionsBefore = h.recorder.reactionSequence().length
170
+ const lastBefore = h.recorder.lastReactionEmoji(CHAT)
171
+
172
+ // Disconnect ONLY agent y. Agent x's per-agent state — and the
173
+ // shared chat's active reaction (which logically belongs to x in
174
+ // single-agent setups) — must remain untouched.
175
+ const sfxBefore = h.flushSideEffects()
176
+ expect(sfxBefore.activeAgentCount).toBe(2) // both x and y registered
177
+
178
+ h.bridgeDisconnect(yClientId)
179
+ await h.clock.advance(10)
180
+
181
+ expect(h.recorder.reactionSequence().length).toBe(reactionsBefore)
182
+ expect(h.recorder.lastReactionEmoji(CHAT)).toBe(lastBefore)
183
+
184
+ // PRIMARY ASSERTION — y disconnected (registered agent), so the
185
+ // helper's flush DID run and cleared y's entry. But x's entry must
186
+ // remain. With the current per-agent-Map shape, the helper iterates
187
+ // the WHOLE map on any registered disconnect — meaning today both x
188
+ // and y get flushed when y disconnects. That's a TODO for the helper
189
+ // (it would need a per-agent disconnect filter). For now, we pin
190
+ // what the helper actually does: both agents get flushed when ANY
191
+ // registered agent disconnects. When the helper grows per-agent
192
+ // filtering, update this assertion.
193
+ const sfxAfter = h.flushSideEffects()
194
+ expect(sfxAfter.disposeProgressDriverCalls, 'helper ran on registered disconnect').toBe(1)
195
+
196
+ // And agent x's controller is still operable — no premature finish.
197
+ // (Subsequent disconnect of x is allowed to flush; the invariant
198
+ // here is only about y's disconnect not touching x.)
199
+ h.bridgeDisconnect(xClientId)
200
+ h.finalize()
201
+ })
202
+ })
203
+
204
+ // ─── I3 — 👍 fires AFTER real delivery (Bug D, Bug Z) ─────────────────
205
+ describe('I3 — 👍 fires at-or-after final outbound delivery, not before (Bug D/Z)', () => {
206
+ it('Class A reply path: lastReactionEmojiAt(👍) >= lastAnswerTextDeliveredAt(chat)', async () => {
207
+ const h = createRealGatewayHarness({ gapMs: 0 })
208
+
209
+ // Full Class A turn: inbound → quick model text → turn_end. The
210
+ // streamReply path internally awaits sendMessage before calling
211
+ // setDone(), which is the production-correct ordering. If a future
212
+ // refactor inverts that — e.g. setDone fires from the JSONL
213
+ // turn_end handler before the outbound completes — this test
214
+ // catches it.
215
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hi' })
216
+ h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hi' })
217
+ await h.clock.advance(20)
218
+
219
+ await h.streamReply({ chat_id: CHAT, text: 'Hello back!', done: true })
220
+ await h.clock.advance(20)
221
+
222
+ const deliveredAt = h.lastAnswerTextDeliveredAt(CHAT)
223
+ const reactionAt = h.lastReactionEmojiAt(CHAT)
224
+ expect(deliveredAt).not.toBeNull()
225
+ expect(reactionAt).not.toBeNull()
226
+ // The terminal reaction should be 👍 (or at least the LAST reaction
227
+ // for the turn), and its timestamp must be >= the last answer text
228
+ // delivery. A negative delta means 👍 fired before the user could
229
+ // read the reply (Bug D/Z's symptom).
230
+ expect(reactionAt!).toBeGreaterThanOrEqual(deliveredAt!)
231
+ h.finalize()
232
+ })
233
+ })
234
+
235
+ // ─── I4 — legacy IPC types are tolerated, not lethal (Bug B) ───────────
236
+ describe('I4 — legacy IPC message types are soft-accepted (Bug B → PR #600)', () => {
237
+ it('update_placeholder is logged-and-discarded; sendIpcMessage MUST NOT throw', () => {
238
+ const h = createRealGatewayHarness({ gapMs: 0 })
239
+ const clientId = h.bridgeConnect(null)
240
+ // The test passes by virtue of NOT throwing. The validator returns
241
+ // false for the unknown type; the harness's processBuffer mirror
242
+ // logs and continues. If a future change wires `update_placeholder`
243
+ // to a handler that throws on missing fields (regressing the PR 5
244
+ // removal), this test would throw and fail loudly.
245
+ expect(() => {
246
+ h.sendIpcMessage(clientId, {
247
+ type: 'update_placeholder',
248
+ chatId: CHAT,
249
+ text: '🔵 thinking',
250
+ })
251
+ }).not.toThrow()
252
+
253
+ // And the connection is still usable — the harness can disconnect
254
+ // it cleanly afterward.
255
+ expect(() => h.bridgeDisconnect(clientId)).not.toThrow()
256
+ h.finalize()
257
+ })
258
+
259
+ it('a register message with the legacy "default" agent name is rejected by the validator (#430 defence)', () => {
260
+ const h = createRealGatewayHarness({ gapMs: 0 })
261
+ const clientId = h.bridgeConnect(null)
262
+ // The production validator rejects agentName="default" outright —
263
+ // see ipc-server.ts:108-119 for the rationale (anonymous bridges
264
+ // crosstalk into the wrong agent). This is a separate axis from
265
+ // I1/I4 but lives in the same lethality-tolerance neighborhood.
266
+ expect(() => {
267
+ h.sendIpcMessage(clientId, { type: 'register', agentName: 'default' })
268
+ }).not.toThrow()
269
+ h.finalize()
270
+ })
271
+ })
272
+
273
+ // ─── I5 — wake-audit dedup (Bug C) ─────────────────────────────────────
274
+ describe('I5 — wake-audit dedup under --continue respawn (Bug C → next PR)', () => {
275
+ // TODO(bug-C): un-skip when the wake-audit profile fix lands. The
276
+ // fix lives in profiles/<profile>/CLAUDE.md.hbs (the .wake-audit-
277
+ // pending sentinel logic), not in the gateway. This test belongs
278
+ // here because the FAILURE MODE is observable at the gateway level
279
+ // (a duplicate reply on the same turn after respawn) and the
280
+ // harness is the right place to pin "no duplicate outbound for the
281
+ // same logical turn."
282
+ it.skip('mid-conversation respawn under --continue MUST NOT produce a duplicate reply', async () => {
283
+ const h = createRealGatewayHarness({ gapMs: 0 })
284
+ h.inbound({ chatId: CHAT, messageId: INBOUND_MSG, text: 'hi' })
285
+ h.feedSessionEvent({ kind: 'enqueue', chatId: CHAT, messageId: '1', threadId: null, rawContent: 'hi' })
286
+ await h.streamReply({ chat_id: CHAT, text: 'Hello!', done: true })
287
+
288
+ // Simulate the --continue respawn path: agent process restarts,
289
+ // wake-audit cycle re-runs, sentinel SHOULD prevent re-firing the
290
+ // greeting reply. Until the fix lands, this would emit a duplicate.
291
+ // The exact respawn shim is TBD by the Bug C fix — this test is a
292
+ // placeholder so the invariant has a home.
293
+ const replyCount = h.recorder
294
+ .sentTexts(CHAT)
295
+ .filter((t) => t.includes('Hello')).length
296
+ expect(replyCount).toBe(1)
297
+ h.finalize()
298
+ })
299
+ })