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,233 @@
1
+ # Waiting-for-reply UX — v2 spec (three-class contract)
2
+
3
+ Tracks: [#545](https://github.com/mekenthompson/switchroom/issues/545),
4
+ [#553](https://github.com/mekenthompson/switchroom/issues/553) (PR series)
5
+
6
+ This document codifies the user-perceived contract for what happens
7
+ between "I sent a Telegram message" and "the agent's reply is locked
8
+ in." The contract varies by **turn class**. The v2 rewrite (#553)
9
+ sharpens the gates: tools alone never trigger the progress card,
10
+ placeholder text is removed entirely, and sub-agents (= background
11
+ workers) are the single concept for parallel work.
12
+
13
+ ## Three turn classes
14
+
15
+ ### Class A — Instant (<2s, NO tools)
16
+
17
+ | Surface | Contract |
18
+ | ----------------- | ---------------------------------------------------------------------- |
19
+ | Status reaction | 👀 within 800ms of inbound. Terminates with 👍. |
20
+ | Progress card | **Never rendered.** Suppressed regardless of `initialDelayMs`. |
21
+ | Placeholder text | **Never sent.** No `🔵 thinking` / `📚 recalling memories` / `💭 thinking`. |
22
+ | Answer text | First answer-text edit lands within **1500ms** of inbound (Class A budget; pinned in #553 PR 3). |
23
+ | Ladder | 👀 → 👍. Optional 🤔 if the controller debounce window is crossed. |
24
+
25
+ User experience: feels like a chat partner typing back instantly.
26
+
27
+ ### Class B — Short (2–60s, tools, NO sub-agents)
28
+
29
+ | Surface | Contract |
30
+ | ----------------- | ---------------------------------------------------------------------- |
31
+ | Status reaction | 👀 within 800ms. Ladder progresses through 🤔 / tool-glyphs (🔥/✍/👨‍💻/⚡) before 👍. **Must NOT collapse straight to 👍.** |
32
+ | Progress card | **Never rendered.** The card gate is `(elapsed >= 60s) OR (sub-agent appeared)` — tools alone do NOT trigger it. |
33
+ | Placeholder text | **Never sent.** |
34
+ | Answer text | First answer-text edit lands within **3000ms** of inbound (Class B/C budget; pinned in #553 PR 3). Streams progressively as the model produces tokens. |
35
+ | Final | 👍 + locked stream answer. |
36
+
37
+ User experience: live ladder of tool reactions, answer text starts
38
+ streaming as soon as the model resumes, no fake "thinking" spacers.
39
+
40
+ ### Class C — Long-running (>60s OR sub-agents/background workers)
41
+
42
+ | Surface | Contract |
43
+ | ----------------- | ---------------------------------------------------------------------- |
44
+ | Status reaction | 👀 within 800ms. Ladder throughout. Settles to 👍 only after full quiescence (all sub-agents terminal). |
45
+ | Progress card | Renders the moment the gate trips: `(elapsed >= 60s) OR (any sub-agent has appeared)`. Stays pinned-feel and stable. |
46
+ | Card "Done" stamp | **≥ last sub-agent terminal timestamp.** Card never marks Done while a sub-agent is still in flight. |
47
+ | Sub-agent header | Header count == rendered-list-length. **No drift between summary and bullets.** |
48
+ | Placeholder text | **Never sent.** |
49
+
50
+ A "background worker" ≡ a sub-agent dispatched with
51
+ `Agent({ run_in_background: true })`. There is no separate concept —
52
+ the card gate, the bullet list, and the quiescence check all key on
53
+ the sub-agent stream.
54
+
55
+ ## Key invariants (v2)
56
+
57
+ 1. **No placeholder strings** — `🔵 thinking`, `📚 recalling memories`,
58
+ and `💭 thinking` must never appear in any `sendMessage` /
59
+ `editMessageText` payload at any point in any turn class. PR 5
60
+ removes the production code that emits them.
61
+ 2. **Card gate** — `(elapsed >= 60s) OR (any sub-agent has appeared)`.
62
+ Tool-use count, tool category, and parent narrative content are
63
+ NOT inputs to the gate.
64
+ 3. **First-answer-text deadline** — Class A: <1500ms. Class B/C:
65
+ <3000ms (committed in #553 PR 3). Budget: 500ms inbound coalesce +
66
+ ~1s minInitialChars-driven first send + ~1.5s model TTFT for short
67
+ replies.
68
+ 4. **Sub-agent header == list length** — every render of the card.
69
+
70
+ ## PR 1 — foundation: spec + harness extensions (this PR)
71
+
72
+ PR 1 ships:
73
+
74
+ - This rewritten spec — supersedes the v1 four-failure-mode framing.
75
+ - Three new helpers on `tests/real-gateway-harness.ts`:
76
+ - `expectNoPlaceholderEdits(chatId)` — returns recorded calls whose
77
+ payload matches a banned placeholder string. Tests assert
78
+ `toEqual([])`.
79
+ - `expectNoCardSent(chatId)` — wraps `progressCardSendMs` for
80
+ assertion-friendly use (`.toBeNull()`).
81
+ - `firstAnswerTextMs(chatId)` — first `sendMessage` /
82
+ `editMessageText` whose payload is neither a card payload nor a
83
+ placeholder string.
84
+ - A new RED test file `tests/real-gateway-spec.test.ts` (all
85
+ `describe.skip`'d) pinning the three-class contract:
86
+ - Class A — 5 tests
87
+ - Class B — 4 tests
88
+ - Class C — 5 tests
89
+ Each carries a `// TODO(#553-PR-N)` marker for which subsequent PR
90
+ un-skips it.
91
+
92
+ PR 1 does NOT change production code. The existing F1/F2/F3/F4
93
+ regression tests stay green; the new spec tests are skipped, so they
94
+ do not gate CI yet.
95
+
96
+ ## PR 2–5 — implementation roadmap
97
+
98
+ | PR | Scope | Un-skips | Status |
99
+ | --- | -------------------------------------------------------------------- | --------------------------------------------------- | -------- |
100
+ | 2 | Kill instant-draft placeholder; preserve early-ack 👀 | Class A no-placeholder, Class B no-placeholder | shipped |
101
+ | 3 | First-answer-text deadline implementation; tighten <Ns numbers | Class A/B/C answer-text-deadline assertions | shipped |
102
+ | 4 | Card-gate rewrite to `(>=60s) OR (sub-agent appeared)` | Class B no-card; Class C card-gate tests | shipped |
103
+ | 5 | Remove `🔵 thinking` / `📚 recalling memories` / `💭 thinking` strings; sub-agent header = list length | Remaining no-placeholder + sub-agent count tests | shipped |
104
+
105
+ **PR 4 status:** shipped the card-gate rewrite. Defaults change
106
+ from `initialDelayMs=30_000, promoteAfterMs=5_000, promoteOnParentToolCount=3`
107
+ to `initialDelayMs=60_000, promoteAfterMs=0 (disabled), promoteOnParentToolCount=0
108
+ (disabled)`. The Class B "no card rendered" and Class C "card renders on
109
+ sub-agent" / "card renders after 60s" tests are un-skipped. F3's late-card
110
+ symptom (long single-tool turn shows no card) is now intentional spec
111
+ behaviour rather than a bug — see `real-gateway-f3-late-card.test.ts`
112
+ header for the reframe.
113
+
114
+ **PR 5 status (this PR):** ships the placeholder-text removal. The
115
+ pre-allocated draft path, the `update_placeholder` IPC handler, the
116
+ placeholder heartbeat (`placeholder-heartbeat.ts`), the phase enrichment
117
+ (`placeholder-phase.ts`), the `forum-topic-placeholder.ts` substitute,
118
+ and all associated state in `gateway.ts` are deleted. recall.py's
119
+ `update_placeholder` IPC calls now no-op cleanly because the gateway
120
+ no longer registers an `onUpdatePlaceholder` handler — the IPC client
121
+ emits a fire-and-forget JSON line, and the gateway dispatch silently
122
+ ignores the unknown message type. Hindsight (`vendor/hindsight-memory/`)
123
+ is untouched. The Class A/B/C no-placeholder assertions and the Class C
124
+ sub-agent-header-equals-list-length assertion are un-skipped.
125
+
126
+ ## IPC + bridge lifecycle invariants
127
+
128
+ Pinned by `tests/real-gateway-ipc-lifecycle.test.ts`. These five
129
+ invariants codify behaviors that the user-perceived waiting-UX spec
130
+ above implicitly relies on but which live one layer down — at the
131
+ IPC server / bridge connect-disconnect boundary. Multiple production
132
+ bugs in 2026-05 traced back to the absence of test coverage at this
133
+ layer; this section is the canonical write-up of what the gateway
134
+ must guarantee.
135
+
136
+ - **I1 — Anonymous IPC client lifecycle is observably invisible.**
137
+ A client that connects but never registers an agent (e.g. recall.py
138
+ sending a one-shot legacy IPC message) MUST NOT mutate any active
139
+ status reactions, MUST NOT dispose the progress driver, and MUST
140
+ NOT close any draft streams. The disconnect path of an unregistered
141
+ client is a no-op on user-visible state. (Bug A — premature 👍 on
142
+ recall.py disconnect, fixed in PR #600.)
143
+
144
+ - **I2 — Per-agent disconnect isolation.** When agent X's bridge
145
+ disconnects, only X's status reactions get flushed to `setDone()`.
146
+ Agent Y's active reactions stay untouched. Switchroom is single-
147
+ agent-per-gateway today, but the invariant pins the right
148
+ semantics so multi-agent gateways cannot regress silently.
149
+
150
+ - **I3 — 👍 fires AFTER real delivery, not after JSONL `turn_end`.**
151
+ For any reply tool path (`reply`, `stream_reply done=true`), the
152
+ controller's `setDone()` MUST happen at-or-after the timestamp of
153
+ the final outbound to Telegram for that reply. Firing on the
154
+ JSONL `turn_end` event before the round-trip completes produces a
155
+ visible flash of 👍 with no reply text. (Bug D + Bug Z.)
156
+
157
+ - **I4 — Legacy IPC types are tolerated, not lethal.** Any message
158
+ type the gateway no longer handles (e.g. `update_placeholder`
159
+ after #553 PR 5) MUST be soft-accepted: the validator returns
160
+ false, `processBuffer` logs and continues, the connection stays
161
+ open, and no active state is mutated. (Bug B — gateway crash on
162
+ recall.py's `update_placeholder` after PR 5; fixed in PR #600.)
163
+
164
+ - **I5 — Wake-audit dedup.** The `.wake-audit-pending` sentinel and
165
+ audit cycle MUST NOT re-fire mid-conversation under `--continue`
166
+ respawn. (Bug C — duplicate greeting reply on respawn. The actual
167
+ fix lives in profiles, not in the gateway, but the regression
168
+ surface is observable here as "duplicate outbound for the same
169
+ logical turn" and so the test invariant lives in this file.)
170
+
171
+ The test file documents which invariants currently have green tests,
172
+ which depend on in-flight PRs to land first, and which are `.skip`'d
173
+ pending the matching fix branch. See the file header for the
174
+ bug-to-PR map.
175
+
176
+ ## Failure-mode history (F1–F4, fixed in earlier #553 PRs)
177
+
178
+ The v1 spec framed the rewrite around four observed regressions from
179
+ the 2026-04-30 live demo. They are all fixed; the regression tests
180
+ (`tests/real-gateway-f1-ladder-integrity.test.ts`,
181
+ `real-gateway-f2-instant-draft.test.ts`, `real-gateway-f3-late-card.test.ts`,
182
+ `real-gateway-f4-interim-text.test.ts`) stay in place to keep the gaps closed.
183
+
184
+ | ID | Symptom | Class | Status |
185
+ | --- | -------------------------------------------------------------------------- | ----- | ------------------------------------------------- |
186
+ | F1 | Ladder collapses straight to 👍 (skips 👀 → 🤔 → 🔥) | B | Fixed — `StatusReactionController.finishWithState` flushes pending pre-terminal emoji. |
187
+ | F2 | No instant draft / typing signal — chat sits silent "for ages" | All | Fixed — `handleInboundCoalesced` fires 👀 directly on raw arrival before the coalesce buffer. |
188
+ | F3 | Progress card renders late (after turn_end, or never on long turns) | C | Fixed under v1 rules with a 5s time-promote in the driver; **superseded by PR 4**, which replaces the gate with `(>=60s) OR (sub-agent)`. |
189
+ | F4 | Pre-tool preamble static — one preamble then silence | B | Regression-guarded only; not reproducible deterministically. The v2 contract sidesteps F4 by tightening the first-answer-text deadline (Class B/C, PR 3). |
190
+
191
+ The F1/F2/F3/F4 tests remain green throughout the v2 rewrite — they
192
+ encode tighter invariants than the v2 spec relaxes. PR 4's gate
193
+ change does not regress F3 (the F3 long-single-tool case crosses the
194
+ 60s threshold).
195
+
196
+ ## Test methodology
197
+
198
+ - **Time control**: `vi.useFakeTimers()` + `vi.setSystemTime` for
199
+ deterministic wall-clock assertions. The harness records every
200
+ outbound `bot.api` call with `Date.now()` at invocation, so all
201
+ deadlines are reproducibly measurable.
202
+ - **Recorder** (existing): `firstReactionMs`, `progressCardSendMs`,
203
+ `reactionSequence`, `lastReactionEmoji`, `edits`, `sentTexts`.
204
+ - **Recorder** (PR 1 additions): `expectNoPlaceholderEdits`,
205
+ `expectNoCardSent`, `firstAnswerTextMs`.
206
+ - **Production wiring**: the harness uses the actual
207
+ `StatusReactionController`, `createProgressDriver`, and
208
+ `createInboundCoalescer` from production — not mocks. The bot.api
209
+ layer below is a recording fake.
210
+ - **Out of scope**: foreman queue, IPC bridge, auth, history. Those
211
+ do not influence the user-perceived waiting UX.
212
+
213
+ ## CI gate
214
+
215
+ The harness runs as part of the root vitest suite via `npm test` →
216
+ `vitest run`. PR 1's spec tests are `describe.skip`'d and do not
217
+ fail CI; PRs 2–5 un-skip them as the production code lands.
218
+
219
+ ## Phase 1 / Phase 3 history (legacy)
220
+
221
+ For posterity:
222
+
223
+ - **Phase 1** (#547): `tests/waiting-ux.e2e.test.ts` — controller +
224
+ driver in isolation, hand-written `feedSessionEvent` adapter. F2
225
+ passed trivially because the harness called `setQueued()`
226
+ synchronously inside `inbound()`.
227
+ - **Phase 3** (#553 PR 1, original): `tests/real-gateway-harness.ts`
228
+ composed the production `InboundCoalescer` before the Phase 1
229
+ controller stack, exposing the real F2 gap (👀 only fired after
230
+ the coalesce window). F2's fix landed against this harness.
231
+ - **v2 rewrite** (this PR series, also numbered #553): same Phase 3
232
+ harness, plus three v2 helpers; new spec test file pins the
233
+ three-class contract.
@@ -0,0 +1,442 @@
1
+ /**
2
+ * Edit-in-place streaming for Telegram messages.
3
+ *
4
+ * Ports the throttle/flush pattern from openclaw's
5
+ * src/channels/draft-stream-loop.ts. The loop holds a single `pendingText`
6
+ * snapshot (NOT a queue — only the latest matters) plus a single in-flight
7
+ * promise. update(text) either fires immediately if the throttle window
8
+ * is open, or schedules a setTimeout for the remaining ms. When the
9
+ * in-flight call resolves, if pendingText changed during flight it loops
10
+ * once more without waiting.
11
+ *
12
+ * This is what makes the experience feel responsive without burning
13
+ * Telegram's 1-edit-per-second-per-message rate limit. The latest delta
14
+ * always lands within ~1s, with at most one outstanding API call.
15
+ *
16
+ * In our model-driven architecture (no inference hooks), the controller
17
+ * is driven by the model calling stream_reply(text, done) multiple times
18
+ * during a long task. First call → sendMessage (or sendMessageDraft in DMs).
19
+ * Subsequent calls → throttled editMessageText (or sendMessageDraft). done=true
20
+ * → flush, materialize as a fresh sendMessage (push notification), clear draft.
21
+ *
22
+ * Transport selection:
23
+ * - previewTransport: "auto" (default) — use draft in DMs only
24
+ * - previewTransport: "draft" — always use draft (if API available)
25
+ * - previewTransport: "message" — always use sendMessage/editMessageText
26
+ *
27
+ * Forum topics (message_thread_id set) force message transport because
28
+ * sendMessageDraft does not support threads. The caller (stream-controller.ts)
29
+ * handles this by passing previewTransport: "message" for threaded chats.
30
+ */
31
+
32
+ import {
33
+ shouldFallbackFromDraftTransport,
34
+ allocateDraftId,
35
+ } from './draft-transport.js'
36
+
37
+ const TELEGRAM_MAX_CHARS = 4096
38
+ const DEFAULT_THROTTLE_MS = 1000
39
+ const MIN_THROTTLE_MS = 250
40
+
41
+ /**
42
+ * Send the first message in a stream. Receives the rendered text plus a
43
+ * thread_id (forum topic) and returns the new Telegram message_id.
44
+ */
45
+ export type StreamSendFn = (text: string) => Promise<number>
46
+
47
+ /**
48
+ * Edit an existing stream message. Receives the message_id and rendered text.
49
+ */
50
+ export type StreamEditFn = (messageId: number, text: string) => Promise<void>
51
+
52
+ /**
53
+ * Optional sendMessageDraft callback. When present and the transport is
54
+ * "draft", this is called instead of sendMessage/editMessageText.
55
+ * Signature mirrors Telegram's sendMessageDraft Bot API method.
56
+ */
57
+ export type StreamDraftFn = (
58
+ chatId: string,
59
+ draftId: number,
60
+ text: string,
61
+ params?: { message_thread_id?: number },
62
+ ) => Promise<unknown>
63
+
64
+ export interface DraftStreamConfig {
65
+ /** Throttle window in ms. Floored at 250. Default 1000. */
66
+ throttleMs?: number
67
+ /**
68
+ * Maximum total characters before hard-stopping the stream. Default 4096
69
+ * (Telegram's limit). When exceeded, future updates are ignored — the
70
+ * caller should fall back to a fresh sendMessage.
71
+ */
72
+ maxChars?: number
73
+ /**
74
+ * Optional debounce window applied BEFORE the first send of a stream.
75
+ * When > 0, the first update() defers the send by idleMs, restarting
76
+ * the timer on each additional update that arrives during the window.
77
+ * Useful when the caller bursts several update() calls at turn start
78
+ * and you'd rather collapse them into a single send than pay the
79
+ * latency of an immediate first-fire + follow-up edit.
80
+ *
81
+ * Default 0 (no pre-send debounce — first update fires immediately).
82
+ * Only affects the first send; subsequent edits use throttleMs.
83
+ *
84
+ * NOTE: This debounce only applies to message transport. Draft transport
85
+ * fires immediately on the first update because drafts are ephemeral —
86
+ * the throttle/flush loop already collapses bursts into 1 API call/sec
87
+ * via throttleMs.
88
+ */
89
+ idleMs?: number
90
+ /**
91
+ * Transport selector.
92
+ * - "auto" (default): use draft transport when isPrivateChat=true AND
93
+ * sendMessageDraft is provided; otherwise use message transport.
94
+ * - "draft": always prefer draft (falls back to message if sendMessageDraft absent).
95
+ * - "message": always use sendMessage/editMessageText.
96
+ */
97
+ previewTransport?: 'auto' | 'message' | 'draft'
98
+ /**
99
+ * True if the current chat is a private DM. Used by "auto" transport to
100
+ * decide whether to activate draft. Has no effect when previewTransport
101
+ * is "draft" or "message".
102
+ */
103
+ isPrivateChat?: boolean
104
+ /**
105
+ * sendMessageDraft callback. When absent, the stream falls back to
106
+ * sendMessage/editMessageText regardless of previewTransport.
107
+ */
108
+ sendMessageDraft?: StreamDraftFn
109
+ /**
110
+ * The Telegram chat id string — required when sendMessageDraft is provided,
111
+ * so the draft can be cleared on finalize.
112
+ */
113
+ chatId?: string
114
+ /** Optional logger for debugging. Receives one string per event. */
115
+ log?: (msg: string) => void
116
+ /** Optional warning logger. Used for transport fallback notices. */
117
+ warn?: (msg: string) => void
118
+ /**
119
+ * If set, the stream is initialized as if a previous send had landed
120
+ * with this `message_id` — the FIRST update() call invokes `edit`
121
+ * against this id rather than `send`. Used by callers (notably the
122
+ * gateway's progress-card emit) that know the anchor message id from
123
+ * an external source (e.g. the pin manager) and want to guarantee a
124
+ * subsequent emit edits in place rather than creating a fresh
125
+ * sendMessage. This closes the "done=true → activeDraftStreams entry
126
+ * deleted → next emit creates fresh sendMessage" duplicate-message
127
+ * class (issue #626). The not-found fallback at the edit site
128
+ * (line ~280: re-send on `MESSAGE_ID_INVALID`) gracefully handles a
129
+ * stale id — the bad edit fails once, then a fresh send fires.
130
+ */
131
+ initialMessageId?: number | null
132
+ }
133
+
134
+ export interface DraftStreamHandle {
135
+ /**
136
+ * Push a new full-text snapshot. The loop holds only the latest. Returns
137
+ * a promise that resolves once this update has either (a) been sent or
138
+ * (b) been superseded by a later update.
139
+ */
140
+ update(text: string): Promise<void>
141
+
142
+ /**
143
+ * Mark the stream as final. Flushes any pending text and rejects all
144
+ * future update() calls. Returns a promise that resolves once the final
145
+ * edit has landed (or the initial send if no edits ever fired).
146
+ */
147
+ finalize(): Promise<void>
148
+
149
+ /** Returns the captured Telegram message_id, or null if nothing has sent yet. */
150
+ getMessageId(): number | null
151
+
152
+ /** True if finalize() has been called. */
153
+ isFinal(): boolean
154
+ }
155
+
156
+ /**
157
+ * Create a draft stream bound to a specific Telegram chat+thread.
158
+ *
159
+ * The first update() call invokes `send` to create the message. All
160
+ * subsequent calls invoke `edit` against the captured message_id.
161
+ *
162
+ * When sendMessageDraft is provided (and transport allows it), intermediate
163
+ * updates use the draft API instead of sendMessage/editMessageText. On
164
+ * finalize(), a real sendMessage is sent for push notification, then the
165
+ * draft is cleared best-effort.
166
+ */
167
+ export function createDraftStream(
168
+ send: StreamSendFn,
169
+ edit: StreamEditFn,
170
+ config: DraftStreamConfig = {},
171
+ ): DraftStreamHandle {
172
+ const throttleMs = Math.max(MIN_THROTTLE_MS, config.throttleMs ?? DEFAULT_THROTTLE_MS)
173
+ const maxChars = config.maxChars ?? TELEGRAM_MAX_CHARS
174
+ const idleMs = Math.max(0, config.idleMs ?? 0)
175
+ const log = config.log
176
+ const warn = config.warn
177
+ const draftApi = config.sendMessageDraft
178
+ const chatId = config.chatId ?? ''
179
+
180
+ // Resolve transport
181
+ const requestedTransport = config.previewTransport ?? 'auto'
182
+ const prefersDraft =
183
+ requestedTransport === 'draft'
184
+ ? true
185
+ : requestedTransport === 'message'
186
+ ? false
187
+ : (config.isPrivateChat === true) // 'auto': DM only
188
+
189
+ // Footgun guard: caller asked for "auto" + provided sendMessageDraft but
190
+ // forgot isPrivateChat. They almost certainly wanted draft in DMs but will
191
+ // silently get message transport everywhere. Warn so the bug is visible.
192
+ if (
193
+ requestedTransport === 'auto'
194
+ && draftApi != null
195
+ && config.isPrivateChat === undefined
196
+ ) {
197
+ warn?.('draft-stream: previewTransport="auto" with sendMessageDraft but isPrivateChat undefined — defaulting to message transport')
198
+ }
199
+
200
+ // Use draft transport only if we have the API
201
+ let usesDraftTransport = prefersDraft && draftApi != null
202
+ let draftId: number | undefined = usesDraftTransport
203
+ ? allocateDraftId()
204
+ : undefined
205
+
206
+ if (prefersDraft && !usesDraftTransport) {
207
+ warn?.('draft-stream: sendMessageDraft unavailable; falling back to sendMessage/editMessageText')
208
+ }
209
+
210
+ let messageId: number | null = config.initialMessageId ?? null
211
+ let pendingText: string | null = null
212
+ let lastSentText: string | null = null
213
+ let lastSentAt = 0
214
+ let inFlight: Promise<void> | null = null
215
+ let scheduledTimer: ReturnType<typeof setTimeout> | null = null
216
+ let final = false
217
+ let stopped = false
218
+
219
+ // Tracks pending update() calls so caller can `await` the next flush
220
+ const waiters: Array<() => void> = []
221
+
222
+ function notifyWaiters(): void {
223
+ const w = waiters.splice(0)
224
+ for (const fn of w) {
225
+ try {
226
+ fn()
227
+ } catch { /* ignore waiter errors */ }
228
+ }
229
+ }
230
+
231
+ async function sendViaDraft(textToSend: string): Promise<boolean> {
232
+ if (!draftApi || draftId == null) return false
233
+ try {
234
+ await draftApi(chatId, draftId, textToSend)
235
+ log?.(`stream → draft (id: ${draftId}, ${textToSend.length} chars)`)
236
+ return true
237
+ } catch (err) {
238
+ if (shouldFallbackFromDraftTransport(err)) {
239
+ const msg = err instanceof Error ? err.message : String(err)
240
+ warn?.(`draft-stream: sendMessageDraft rejected — falling back to sendMessage/editMessageText (${msg})`)
241
+ usesDraftTransport = false
242
+ draftId = undefined
243
+ return false
244
+ }
245
+ throw err
246
+ }
247
+ }
248
+
249
+ async function flush(): Promise<void> {
250
+ if (stopped) {
251
+ notifyWaiters()
252
+ return
253
+ }
254
+ if (pendingText == null) {
255
+ notifyWaiters()
256
+ return
257
+ }
258
+ const textToSend = pendingText
259
+ pendingText = null
260
+
261
+ if (textToSend === lastSentText) {
262
+ // Nothing actually changed — skip the API call but free waiters
263
+ notifyWaiters()
264
+ return
265
+ }
266
+
267
+ if (textToSend.length > maxChars) {
268
+ log?.(`stream stopped: text exceeds ${maxChars} chars`)
269
+ stopped = true
270
+ notifyWaiters()
271
+ return
272
+ }
273
+
274
+ try {
275
+ if (usesDraftTransport) {
276
+ const ok = await sendViaDraft(textToSend)
277
+ if (!ok) {
278
+ // Draft failed with a permanent error → fell back to message transport.
279
+ // Replay this text via message transport.
280
+ await sendViaMessage(textToSend)
281
+ }
282
+ } else {
283
+ await sendViaMessage(textToSend)
284
+ }
285
+ lastSentText = textToSend
286
+ lastSentAt = Date.now()
287
+ } catch (err) {
288
+ const msg = (err as Error).message ?? String(err)
289
+ if (/\bmessage is not modified\b/i.test(msg)) {
290
+ lastSentText = textToSend
291
+ lastSentAt = Date.now()
292
+ log?.(`stream → not modified (id: ${messageId})`)
293
+ } else if (
294
+ /\bmessage to edit not found\b/i.test(msg)
295
+ || /\bMESSAGE_ID_INVALID\b/i.test(msg)
296
+ ) {
297
+ log?.(`stream → message not found (id: ${messageId}), re-sending`)
298
+ messageId = null
299
+ lastSentText = null
300
+ if (pendingText == null) pendingText = textToSend
301
+ } else {
302
+ log?.(`stream → edit failed: ${msg}`)
303
+ }
304
+ }
305
+
306
+ notifyWaiters()
307
+ }
308
+
309
+ async function sendViaMessage(textToSend: string): Promise<void> {
310
+ if (messageId == null) {
311
+ messageId = await send(textToSend)
312
+ log?.(`stream → sent (id: ${messageId}, ${textToSend.length} chars)`)
313
+ } else {
314
+ await edit(messageId, textToSend)
315
+ log?.(`stream → edited (id: ${messageId}, ${textToSend.length} chars)`)
316
+ }
317
+ }
318
+
319
+ async function flushLoop(): Promise<void> {
320
+ // Drain any updates that arrived during the in-flight call.
321
+ while (pendingText != null && !stopped) {
322
+ await flush()
323
+ }
324
+ }
325
+
326
+ function schedule(): void {
327
+ if (scheduledTimer != null) return
328
+ if (stopped) return
329
+ const sinceLast = Date.now() - lastSentAt
330
+ const delay = Math.max(0, throttleMs - sinceLast)
331
+ scheduledTimer = setTimeout(() => {
332
+ scheduledTimer = null
333
+ if (inFlight) {
334
+ // The in-flight loop will pick up pendingText after it resolves.
335
+ return
336
+ }
337
+ inFlight = flushLoop().finally(() => {
338
+ inFlight = null
339
+ })
340
+ }, delay)
341
+ }
342
+
343
+ return {
344
+ update(text: string): Promise<void> {
345
+ if (final || stopped) return Promise.resolve()
346
+ pendingText = text
347
+ const waitPromise = new Promise<void>(resolve => {
348
+ waiters.push(resolve)
349
+ })
350
+
351
+ // Pre-send idle debounce: for the FIRST send of a stream, optionally
352
+ // defer by idleMs so a burst of update() calls collapses into one
353
+ // send. Each incoming update resets the timer. Once the initial
354
+ // send has landed (messageId != null OR draft has fired), this path
355
+ // is skipped and the regular throttle kicks in.
356
+ if (idleMs > 0 && messageId == null && !usesDraftTransport && inFlight == null) {
357
+ if (scheduledTimer != null) clearTimeout(scheduledTimer)
358
+ scheduledTimer = setTimeout(() => {
359
+ scheduledTimer = null
360
+ inFlight = flushLoop().finally(() => { inFlight = null })
361
+ }, idleMs)
362
+ return waitPromise
363
+ }
364
+
365
+ // If nothing in flight and the throttle window is open, fire now.
366
+ if (inFlight == null && Date.now() - lastSentAt >= throttleMs) {
367
+ inFlight = flushLoop().finally(() => {
368
+ inFlight = null
369
+ })
370
+ } else if (inFlight == null) {
371
+ schedule()
372
+ } else {
373
+ // inFlight is set — the current flushLoop is running. Previous
374
+ // versions of this code relied on flushLoop's while(pendingText
375
+ // != null) to pick up the new text, but there's a race: if
376
+ // update() fires AFTER the while's final (null) check but
377
+ // BEFORE the flushLoop promise settles, the new pendingText
378
+ // lands in a shell with no one looking at it, and the waiter
379
+ // hangs forever. Chain a follow-up flush off the current
380
+ // flushLoop so the new text is guaranteed to be drained.
381
+ inFlight.then(() => {
382
+ if (stopped || pendingText == null) return
383
+ if (inFlight != null) return // a new flushLoop already started
384
+ if (Date.now() - lastSentAt >= throttleMs) {
385
+ inFlight = flushLoop().finally(() => { inFlight = null })
386
+ } else {
387
+ schedule()
388
+ }
389
+ })
390
+ }
391
+ return waitPromise
392
+ },
393
+
394
+ async finalize(): Promise<void> {
395
+ if (final) return
396
+ final = true
397
+ // Drain any pending updates
398
+ if (scheduledTimer != null) {
399
+ clearTimeout(scheduledTimer)
400
+ scheduledTimer = null
401
+ }
402
+ if (inFlight) {
403
+ await inFlight
404
+ }
405
+ if (pendingText != null && !stopped) {
406
+ await flush()
407
+ }
408
+
409
+ // Draft transport: materialize as a real sendMessage for push notification,
410
+ // then clear the draft best-effort.
411
+ if (usesDraftTransport && draftApi != null) {
412
+ const textToMaterialize = lastSentText
413
+ if (textToMaterialize) {
414
+ try {
415
+ messageId = await send(textToMaterialize)
416
+ log?.(`stream → materialized (id: ${messageId}, ${textToMaterialize.length} chars)`)
417
+ } catch (err) {
418
+ warn?.(`draft-stream: materialize sendMessage failed: ${err instanceof Error ? err.message : String(err)}`)
419
+ }
420
+ // Clear draft best-effort (cosmetic — Telegram input area cleanup)
421
+ if (draftId != null) {
422
+ try {
423
+ await draftApi(chatId, draftId, '')
424
+ } catch {
425
+ // Best-effort — ignore failures
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ log?.(`stream finalized (id: ${messageId})`)
432
+ },
433
+
434
+ getMessageId(): number | null {
435
+ return messageId
436
+ },
437
+
438
+ isFinal(): boolean {
439
+ return final
440
+ },
441
+ }
442
+ }