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,878 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
+ import {
3
+ createAnswerStream,
4
+ __resetDraftIdForTests,
5
+ MIN_INITIAL_CHARS,
6
+ DRAFT_METHOD_UNAVAILABLE_RE,
7
+ DRAFT_CHAT_UNSUPPORTED_RE,
8
+ } from '../answer-stream.js'
9
+
10
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
11
+
12
+ /** Flush microtask queue N times. */
13
+ async function flushMicrotasks(times = 10): Promise<void> {
14
+ for (let i = 0; i < times; i++) {
15
+ await Promise.resolve()
16
+ }
17
+ }
18
+
19
+ type SendMessageFn = (
20
+ chatId: string,
21
+ text: string,
22
+ params?: {
23
+ parse_mode?: 'HTML'
24
+ message_thread_id?: number
25
+ link_preview_options?: { is_disabled: boolean }
26
+ reply_parameters?: { message_id: number }
27
+ },
28
+ ) => Promise<{ message_id: number }>
29
+
30
+ type EditMessageTextFn = (
31
+ chatId: string,
32
+ messageId: number,
33
+ text: string,
34
+ params?: {
35
+ parse_mode?: 'HTML'
36
+ message_thread_id?: number
37
+ link_preview_options?: { is_disabled: boolean }
38
+ },
39
+ ) => Promise<unknown>
40
+
41
+ type SendMessageDraftFn = (
42
+ chatId: string,
43
+ draftId: number,
44
+ text: string,
45
+ params?: { message_thread_id?: number },
46
+ ) => Promise<unknown>
47
+
48
+ let nextMessageId = 1000
49
+
50
+ function makeSendMessage(): ReturnType<typeof vi.fn> & SendMessageFn {
51
+ const fn = vi.fn(async (_chatId: string, _text: string) => {
52
+ return { message_id: nextMessageId++ }
53
+ })
54
+ return fn as unknown as ReturnType<typeof vi.fn> & SendMessageFn
55
+ }
56
+
57
+ function makeEditMessageText(): ReturnType<typeof vi.fn> & EditMessageTextFn {
58
+ return vi.fn(async () => {}) as unknown as ReturnType<typeof vi.fn> & EditMessageTextFn
59
+ }
60
+
61
+ function makeSendMessageDraft(): ReturnType<typeof vi.fn> & SendMessageDraftFn {
62
+ return vi.fn(async () => {}) as unknown as ReturnType<typeof vi.fn> & SendMessageDraftFn
63
+ }
64
+
65
+ // ─── Tests ────────────────────────────────────────────────────────────────────
66
+
67
+ beforeEach(() => {
68
+ __resetDraftIdForTests()
69
+ nextMessageId = 1000
70
+ vi.useFakeTimers()
71
+ })
72
+
73
+ afterEach(() => {
74
+ vi.useRealTimers()
75
+ })
76
+
77
+ describe('answer-stream — minInitialChars threshold', () => {
78
+ it('does not call transport when text is below minInitialChars', async () => {
79
+ const sendMessage = makeSendMessage()
80
+ const editMessageText = makeEditMessageText()
81
+ const stream = createAnswerStream({
82
+ chatId: 'chat1',
83
+ isPrivateChat: false,
84
+ minInitialChars: 400,
85
+ throttleMs: 250,
86
+ sendMessage,
87
+ editMessageText,
88
+ })
89
+
90
+ // 200 chars < 400 threshold
91
+ stream.update('x'.repeat(200))
92
+ vi.advanceTimersByTime(1000)
93
+ await flushMicrotasks()
94
+
95
+ expect(sendMessage).not.toHaveBeenCalled()
96
+ expect(editMessageText).not.toHaveBeenCalled()
97
+ })
98
+
99
+ it('calls transport exactly once when text meets minInitialChars', async () => {
100
+ const sendMessage = makeSendMessage()
101
+ const editMessageText = makeEditMessageText()
102
+ const stream = createAnswerStream({
103
+ chatId: 'chat1',
104
+ isPrivateChat: false,
105
+ minInitialChars: 400,
106
+ throttleMs: 250,
107
+ sendMessage,
108
+ editMessageText,
109
+ })
110
+
111
+ // 500 chars >= 400 threshold
112
+ stream.update('y'.repeat(500))
113
+ // effectiveThrottle = max(250, 250) = 250; lastSentAt=0 so fires immediately
114
+ await flushMicrotasks()
115
+
116
+ expect(sendMessage).toHaveBeenCalledTimes(1)
117
+ expect(sendMessage).toHaveBeenCalledWith(
118
+ 'chat1',
119
+ 'y'.repeat(500),
120
+ expect.objectContaining({ parse_mode: 'HTML' }),
121
+ )
122
+ expect(editMessageText).not.toHaveBeenCalled()
123
+ })
124
+ })
125
+
126
+ describe('answer-stream — draft transport selection', () => {
127
+ it('uses sendMessageDraft for DMs (isPrivateChat: true)', async () => {
128
+ const sendMessage = makeSendMessage()
129
+ const editMessageText = makeEditMessageText()
130
+ const sendMessageDraft = makeSendMessageDraft()
131
+ const stream = createAnswerStream({
132
+ chatId: 'chat1',
133
+ isPrivateChat: true,
134
+ throttleMs: 250,
135
+ sendMessage,
136
+ editMessageText,
137
+ sendMessageDraft,
138
+ })
139
+
140
+ // Draft transport bypasses minInitialChars gate — any non-empty text goes
141
+ stream.update('Hello from DM!')
142
+ await flushMicrotasks()
143
+
144
+ expect(sendMessageDraft).toHaveBeenCalledTimes(1)
145
+ expect(sendMessageDraft).toHaveBeenCalledWith(
146
+ 'chat1',
147
+ expect.any(Number),
148
+ 'Hello from DM!',
149
+ undefined,
150
+ )
151
+ expect(sendMessage).not.toHaveBeenCalled()
152
+ })
153
+
154
+ it('uses sendMessage for non-DM chats even when sendMessageDraft is provided', async () => {
155
+ const sendMessage = makeSendMessage()
156
+ const editMessageText = makeEditMessageText()
157
+ const sendMessageDraft = makeSendMessageDraft()
158
+ const stream = createAnswerStream({
159
+ chatId: 'chat1',
160
+ isPrivateChat: false,
161
+ minInitialChars: 10,
162
+ throttleMs: 250,
163
+ sendMessage,
164
+ editMessageText,
165
+ sendMessageDraft,
166
+ })
167
+
168
+ stream.update('x'.repeat(50))
169
+ await flushMicrotasks()
170
+
171
+ expect(sendMessage).toHaveBeenCalledTimes(1)
172
+ expect(sendMessageDraft).not.toHaveBeenCalled()
173
+ })
174
+ })
175
+
176
+ describe('answer-stream — runtime fallback when sendMessageDraft rejects', () => {
177
+ it('falls back to sendMessage when sendMessageDraft throws DRAFT_METHOD_UNAVAILABLE_RE pattern', async () => {
178
+ const sendMessage = makeSendMessage()
179
+ const editMessageText = makeEditMessageText()
180
+ // The shouldFallbackFromDraftTransport helper checks for "sendMessageDraft" in the
181
+ // error message plus the regex pattern.
182
+ const sendMessageDraft = vi.fn(async () => {
183
+ throw new Error('sendMessageDraft: unknown method')
184
+ })
185
+
186
+ const stream = createAnswerStream({
187
+ chatId: 'chat1',
188
+ isPrivateChat: true,
189
+ throttleMs: 250,
190
+ sendMessage,
191
+ editMessageText,
192
+ sendMessageDraft,
193
+ })
194
+
195
+ // First update — draft throws, falls back
196
+ stream.update('Hello DM!')
197
+ await flushMicrotasks()
198
+
199
+ expect(sendMessageDraft).toHaveBeenCalledTimes(1)
200
+ // After fallback, sendMessage should have been called with the same text
201
+ expect(sendMessage).toHaveBeenCalledTimes(1)
202
+ expect(sendMessage).toHaveBeenCalledWith('chat1', 'Hello DM!', expect.any(Object))
203
+
204
+ // Subsequent update should use sendMessage+editMessageText, not draft
205
+ sendMessageDraft.mockClear()
206
+ sendMessage.mockClear()
207
+ vi.advanceTimersByTime(1000)
208
+ stream.update('Follow-up!')
209
+ await flushMicrotasks()
210
+
211
+ expect(sendMessageDraft).not.toHaveBeenCalled()
212
+ expect(editMessageText).toHaveBeenCalledTimes(1)
213
+ })
214
+
215
+ it('falls back when sendMessageDraft throws DRAFT_CHAT_UNSUPPORTED_RE pattern', async () => {
216
+ const sendMessage = makeSendMessage()
217
+ const editMessageText = makeEditMessageText()
218
+ const sendMessageDraft = vi.fn(async () => {
219
+ throw new Error("sendMessageDraft can't be used in this chat")
220
+ })
221
+
222
+ const stream = createAnswerStream({
223
+ chatId: 'chat1',
224
+ isPrivateChat: true,
225
+ throttleMs: 250,
226
+ sendMessage,
227
+ editMessageText,
228
+ sendMessageDraft,
229
+ })
230
+
231
+ stream.update('Hello!')
232
+ await flushMicrotasks()
233
+
234
+ expect(sendMessageDraft).toHaveBeenCalledTimes(1)
235
+ expect(sendMessage).toHaveBeenCalledTimes(1)
236
+ expect(sendMessage).toHaveBeenCalledWith('chat1', 'Hello!', expect.any(Object))
237
+ })
238
+ })
239
+
240
+ describe('answer-stream — throttling', () => {
241
+ it('three rapid updates within throttleMs result in at most two transport calls', async () => {
242
+ const sendMessage = makeSendMessage()
243
+ const editMessageText = makeEditMessageText()
244
+ const THROTTLE = 1000
245
+ const stream = createAnswerStream({
246
+ chatId: 'chat1',
247
+ isPrivateChat: false,
248
+ minInitialChars: 10,
249
+ throttleMs: THROTTLE,
250
+ sendMessage,
251
+ editMessageText,
252
+ })
253
+
254
+ // First update — fires immediately (lastSentAt=0)
255
+ stream.update('a'.repeat(50))
256
+ await flushMicrotasks()
257
+ expect(sendMessage).toHaveBeenCalledTimes(1)
258
+
259
+ // Three rapid updates within the throttle window
260
+ stream.update('b'.repeat(50))
261
+ stream.update('c'.repeat(50))
262
+ stream.update('d'.repeat(50))
263
+ await flushMicrotasks()
264
+ // Not yet — throttle window not elapsed
265
+ expect(editMessageText).toHaveBeenCalledTimes(0)
266
+
267
+ // Advance past the throttle window
268
+ vi.advanceTimersByTime(THROTTLE)
269
+ await flushMicrotasks()
270
+
271
+ // Only the latest (coalesced) text lands — at most one edit call
272
+ expect(editMessageText.mock.calls.length).toBeLessThanOrEqual(1)
273
+ if (editMessageText.mock.calls.length === 1) {
274
+ expect(editMessageText.mock.calls[0][2]).toBe('d'.repeat(50))
275
+ }
276
+
277
+ // Total transport calls: 1 initial send + at most 1 coalesced edit
278
+ const totalCalls = sendMessage.mock.calls.length + editMessageText.mock.calls.length
279
+ expect(totalCalls).toBeLessThanOrEqual(2)
280
+ })
281
+ })
282
+
283
+ describe('answer-stream — materialize()', () => {
284
+ it('sends a fresh sendMessage (not editMessageText) on materialize', async () => {
285
+ const sendMessage = makeSendMessage()
286
+ const editMessageText = makeEditMessageText()
287
+ const THROTTLE = 1000
288
+ const stream = createAnswerStream({
289
+ chatId: 'chat1',
290
+ isPrivateChat: false,
291
+ minInitialChars: 10,
292
+ throttleMs: THROTTLE,
293
+ sendMessage,
294
+ editMessageText,
295
+ })
296
+
297
+ // Stream some text
298
+ stream.update('x'.repeat(50))
299
+ await flushMicrotasks()
300
+ // Advance so the initial send lands
301
+ vi.advanceTimersByTime(THROTTLE)
302
+ await flushMicrotasks()
303
+
304
+ const sendCallsBefore = sendMessage.mock.calls.length
305
+ editMessageText.mockClear()
306
+
307
+ // materialize() should fire a fresh sendMessage
308
+ const msgId = await stream.materialize()
309
+
310
+ expect(typeof msgId).toBe('number')
311
+ expect(sendMessage.mock.calls.length).toBeGreaterThan(sendCallsBefore)
312
+ // materialize always uses sendMessage, never editMessageText
313
+ expect(editMessageText).not.toHaveBeenCalled()
314
+ })
315
+
316
+ it('materialize() is idempotent — second call returns same id, no extra sendMessage', async () => {
317
+ const sendMessage = makeSendMessage()
318
+ const editMessageText = makeEditMessageText()
319
+ const stream = createAnswerStream({
320
+ chatId: 'chat1',
321
+ isPrivateChat: false,
322
+ minInitialChars: 10,
323
+ throttleMs: 250,
324
+ sendMessage,
325
+ editMessageText,
326
+ })
327
+
328
+ stream.update('x'.repeat(50))
329
+ await flushMicrotasks()
330
+
331
+ const id1 = await stream.materialize()
332
+ const sendCallsAfterFirst = sendMessage.mock.calls.length
333
+
334
+ const id2 = await stream.materialize()
335
+
336
+ expect(id1).toBe(id2)
337
+ // No additional sendMessage calls on the second call
338
+ expect(sendMessage.mock.calls.length).toBe(sendCallsAfterFirst)
339
+ })
340
+ })
341
+
342
+ describe('answer-stream — forceNewMessage() supersession', () => {
343
+ it('after forceNewMessage(), next update sends a fresh message not an edit', async () => {
344
+ const sendMessage = makeSendMessage()
345
+ const editMessageText = makeEditMessageText()
346
+ const THROTTLE = 1000
347
+ const stream = createAnswerStream({
348
+ chatId: 'chat1',
349
+ isPrivateChat: false,
350
+ minInitialChars: 10,
351
+ throttleMs: THROTTLE,
352
+ sendMessage,
353
+ editMessageText,
354
+ })
355
+
356
+ // Stream initial text
357
+ stream.update('x'.repeat(50))
358
+ await flushMicrotasks()
359
+ expect(sendMessage).toHaveBeenCalledTimes(1)
360
+ const firstMsgId = sendMessage.mock.calls[0]
361
+
362
+ // Advance past throttle so a new update is ready to send
363
+ vi.advanceTimersByTime(THROTTLE)
364
+ await flushMicrotasks()
365
+
366
+ // Now force a new message
367
+ stream.forceNewMessage()
368
+ sendMessage.mockClear()
369
+ editMessageText.mockClear()
370
+
371
+ // Next update should NOT edit the old message — should send a fresh one
372
+ stream.update('y'.repeat(50))
373
+ await flushMicrotasks()
374
+
375
+ expect(sendMessage).toHaveBeenCalledTimes(1)
376
+ expect(editMessageText).not.toHaveBeenCalled()
377
+ })
378
+ })
379
+
380
+ describe('answer-stream — stop() cancels pending throttled edits', () => {
381
+ it('stop() before timer fires prevents any transport call', async () => {
382
+ const sendMessage = makeSendMessage()
383
+ const editMessageText = makeEditMessageText()
384
+ const THROTTLE = 1000
385
+ const stream = createAnswerStream({
386
+ chatId: 'chat1',
387
+ isPrivateChat: false,
388
+ minInitialChars: 10,
389
+ throttleMs: THROTTLE,
390
+ sendMessage,
391
+ editMessageText,
392
+ })
393
+
394
+ // Send initial to get msgId established
395
+ stream.update('x'.repeat(50))
396
+ await flushMicrotasks()
397
+ expect(sendMessage).toHaveBeenCalledTimes(1)
398
+
399
+ // Schedule a throttled edit
400
+ stream.update('y'.repeat(50))
401
+ await flushMicrotasks()
402
+ // Not yet sent (within throttle window)
403
+ expect(editMessageText).toHaveBeenCalledTimes(0)
404
+
405
+ // Stop before the timer fires
406
+ stream.stop()
407
+ sendMessage.mockClear()
408
+ editMessageText.mockClear()
409
+
410
+ // Advance well past the throttle window
411
+ vi.advanceTimersByTime(THROTTLE * 3)
412
+ await flushMicrotasks()
413
+
414
+ // The pending edit should NOT have fired
415
+ expect(editMessageText).not.toHaveBeenCalled()
416
+ expect(sendMessage).not.toHaveBeenCalled()
417
+ })
418
+ })
419
+
420
+ describe('answer-stream — empty / whitespace-only text is a no-op', () => {
421
+ it('update("") does not trigger any transport call', async () => {
422
+ const sendMessage = makeSendMessage()
423
+ const editMessageText = makeEditMessageText()
424
+ const stream = createAnswerStream({
425
+ chatId: 'chat1',
426
+ isPrivateChat: false,
427
+ minInitialChars: 0,
428
+ throttleMs: 250,
429
+ sendMessage,
430
+ editMessageText,
431
+ })
432
+
433
+ stream.update('')
434
+ vi.advanceTimersByTime(1000)
435
+ await flushMicrotasks()
436
+
437
+ expect(sendMessage).not.toHaveBeenCalled()
438
+ expect(editMessageText).not.toHaveBeenCalled()
439
+ })
440
+
441
+ it('update(" ") does not trigger any transport call', async () => {
442
+ const sendMessage = makeSendMessage()
443
+ const editMessageText = makeEditMessageText()
444
+ const stream = createAnswerStream({
445
+ chatId: 'chat1',
446
+ isPrivateChat: false,
447
+ minInitialChars: 0,
448
+ throttleMs: 250,
449
+ sendMessage,
450
+ editMessageText,
451
+ })
452
+
453
+ stream.update(' ')
454
+ vi.advanceTimersByTime(1000)
455
+ await flushMicrotasks()
456
+
457
+ expect(sendMessage).not.toHaveBeenCalled()
458
+ expect(editMessageText).not.toHaveBeenCalled()
459
+ })
460
+ })
461
+
462
+ describe('answer-stream — maxChars guard', () => {
463
+ it('does not send when text exceeds 4096 chars', async () => {
464
+ const sendMessage = makeSendMessage()
465
+ const editMessageText = makeEditMessageText()
466
+ const stream = createAnswerStream({
467
+ chatId: 'chat1',
468
+ isPrivateChat: false,
469
+ minInitialChars: 0,
470
+ throttleMs: 250,
471
+ sendMessage,
472
+ editMessageText,
473
+ })
474
+
475
+ // 4097 chars exceeds Telegram's 4096 limit
476
+ stream.update('z'.repeat(4097))
477
+ vi.advanceTimersByTime(1000)
478
+ await flushMicrotasks()
479
+
480
+ expect(sendMessage).not.toHaveBeenCalled()
481
+ expect(editMessageText).not.toHaveBeenCalled()
482
+ })
483
+ })
484
+
485
+ describe('answer-stream — materialize() on draft transport', () => {
486
+ it('clears the draft and sends a fresh sendMessage on materialize', async () => {
487
+ const sendMessage = makeSendMessage()
488
+ const editMessageText = makeEditMessageText()
489
+ const sendMessageDraft = makeSendMessageDraft()
490
+ const stream = createAnswerStream({
491
+ chatId: 'chat1',
492
+ isPrivateChat: true,
493
+ minInitialChars: 0,
494
+ throttleMs: 250,
495
+ sendMessage,
496
+ editMessageText,
497
+ sendMessageDraft,
498
+ })
499
+
500
+ stream.update('hello world')
501
+ vi.advanceTimersByTime(500)
502
+ await flushMicrotasks()
503
+
504
+ expect(sendMessageDraft).toHaveBeenCalled()
505
+ const draftCallsBeforeMaterialize = sendMessageDraft.mock.calls.length
506
+
507
+ const finalId = await stream.materialize()
508
+
509
+ expect(sendMessage).toHaveBeenCalledTimes(1)
510
+ expect(sendMessage.mock.calls[0][1]).toBe('hello world')
511
+ expect(typeof finalId).toBe('number')
512
+
513
+ expect(sendMessageDraft.mock.calls.length).toBeGreaterThan(draftCallsBeforeMaterialize)
514
+ const lastDraftCall = sendMessageDraft.mock.calls[sendMessageDraft.mock.calls.length - 1]
515
+ expect(lastDraftCall[2]).toBe('')
516
+ })
517
+ })
518
+
519
+ describe('answer-stream — onSuperseded callback', () => {
520
+ it('invokes onSuperseded when a send resolves after forceNewMessage', async () => {
521
+ const onSuperseded = vi.fn()
522
+
523
+ let resolveSend: ((value: { message_id: number }) => void) | undefined
524
+ const sendMessage = vi.fn(
525
+ () =>
526
+ new Promise<{ message_id: number }>((resolve) => {
527
+ resolveSend = resolve
528
+ }),
529
+ ) as unknown as ReturnType<typeof vi.fn> & SendMessageFn
530
+
531
+ const editMessageText = makeEditMessageText()
532
+ const stream = createAnswerStream({
533
+ chatId: 'chat1',
534
+ isPrivateChat: false,
535
+ minInitialChars: 0,
536
+ throttleMs: 250,
537
+ sendMessage,
538
+ editMessageText,
539
+ onSuperseded,
540
+ })
541
+
542
+ stream.update('first message text')
543
+ vi.advanceTimersByTime(500)
544
+ await flushMicrotasks()
545
+
546
+ expect(sendMessage).toHaveBeenCalledTimes(1)
547
+ expect(resolveSend).toBeDefined()
548
+
549
+ stream.forceNewMessage()
550
+
551
+ resolveSend!({ message_id: 9999 })
552
+ await flushMicrotasks()
553
+
554
+ expect(onSuperseded).toHaveBeenCalledTimes(1)
555
+ expect(onSuperseded.mock.calls[0][0]).toEqual({
556
+ messageId: 9999,
557
+ textSnapshot: 'first message text',
558
+ })
559
+ expect(stream.messageId()).toBeUndefined()
560
+ })
561
+ })
562
+
563
+ describe('answer-stream — materialize() max-chars guard', () => {
564
+ it('does not send when buffered text exceeds 4096 chars', async () => {
565
+ const sendMessage = makeSendMessage()
566
+ const editMessageText = makeEditMessageText()
567
+ const warn = vi.fn()
568
+ const stream = createAnswerStream({
569
+ chatId: 'chat1',
570
+ isPrivateChat: false,
571
+ minInitialChars: 0,
572
+ throttleMs: 250,
573
+ sendMessage,
574
+ editMessageText,
575
+ warn,
576
+ })
577
+
578
+ stream.update('a'.repeat(4000))
579
+ vi.advanceTimersByTime(500)
580
+ await flushMicrotasks()
581
+ const sendsAfterStreaming = sendMessage.mock.calls.length
582
+
583
+ stream.update('b'.repeat(4097))
584
+ const result = await stream.materialize()
585
+
586
+ expect(result).toBeUndefined()
587
+ expect(sendMessage.mock.calls.length).toBe(sendsAfterStreaming)
588
+ expect(warn).toHaveBeenCalled()
589
+ expect(warn.mock.calls.some((c) => /4096|exceeds/i.test(String(c[0])))).toBe(true)
590
+ })
591
+ })
592
+
593
+ // ─── Issue #251: retract() — delete preliminary message when reply path wins ──
594
+ describe('answer-stream — retract() (#251)', () => {
595
+ it('calls deleteMessage with (chatId, messageId) after a preliminary message was sent', async () => {
596
+ const sendMessage = makeSendMessage()
597
+ const editMessageText = makeEditMessageText()
598
+ const deleteMessage = vi.fn(async () => {})
599
+ const stream = createAnswerStream({
600
+ chatId: 'chat251',
601
+ isPrivateChat: false,
602
+ minInitialChars: 0,
603
+ throttleMs: 250,
604
+ sendMessage,
605
+ editMessageText,
606
+ deleteMessage,
607
+ })
608
+
609
+ // Push enough text to exceed the threshold and actually send a message
610
+ stream.update('x'.repeat(500))
611
+ await flushMicrotasks()
612
+
613
+ // Verify a message was sent and we know its id
614
+ expect(sendMessage).toHaveBeenCalledTimes(1)
615
+ const sentMsgId = (sendMessage.mock.results[0].value as Promise<{ message_id: number }>)
616
+ const resolved = await sentMsgId
617
+ expect(typeof resolved.message_id).toBe('number')
618
+
619
+ const msgId = stream.messageId()
620
+ expect(typeof msgId).toBe('number')
621
+
622
+ // Now retract — simulates the reply path winning
623
+ await stream.retract()
624
+
625
+ expect(deleteMessage).toHaveBeenCalledTimes(1)
626
+ expect(deleteMessage).toHaveBeenCalledWith('chat251', msgId)
627
+ })
628
+
629
+ it('does not call deleteMessage when no preliminary message was sent', async () => {
630
+ const sendMessage = makeSendMessage()
631
+ const editMessageText = makeEditMessageText()
632
+ const deleteMessage = vi.fn(async () => {})
633
+ const stream = createAnswerStream({
634
+ chatId: 'chat251',
635
+ isPrivateChat: false,
636
+ // High threshold so the text below never triggers a send
637
+ minInitialChars: 5000,
638
+ throttleMs: 250,
639
+ sendMessage,
640
+ editMessageText,
641
+ deleteMessage,
642
+ })
643
+
644
+ // Short text — stays below minInitialChars, no message sent
645
+ stream.update('x'.repeat(100))
646
+ vi.advanceTimersByTime(1000)
647
+ await flushMicrotasks()
648
+
649
+ expect(sendMessage).not.toHaveBeenCalled()
650
+ expect(stream.messageId()).toBeUndefined()
651
+
652
+ // retract() should be a no-op — no delete attempt
653
+ await stream.retract()
654
+
655
+ expect(deleteMessage).not.toHaveBeenCalled()
656
+ })
657
+
658
+ it('retract() stops the stream — subsequent update() calls are no-ops', async () => {
659
+ const sendMessage = makeSendMessage()
660
+ const editMessageText = makeEditMessageText()
661
+ const deleteMessage = vi.fn(async () => {})
662
+ const stream = createAnswerStream({
663
+ chatId: 'chat251',
664
+ isPrivateChat: false,
665
+ minInitialChars: 0,
666
+ throttleMs: 250,
667
+ sendMessage,
668
+ editMessageText,
669
+ deleteMessage,
670
+ })
671
+
672
+ // Establish an initial message
673
+ stream.update('hello world')
674
+ await flushMicrotasks()
675
+ expect(sendMessage).toHaveBeenCalledTimes(1)
676
+
677
+ // Retract
678
+ await stream.retract()
679
+
680
+ // Any subsequent update should be silently dropped
681
+ sendMessage.mockClear()
682
+ editMessageText.mockClear()
683
+ stream.update('this should be ignored after retract')
684
+ vi.advanceTimersByTime(2000)
685
+ await flushMicrotasks()
686
+
687
+ expect(sendMessage).not.toHaveBeenCalled()
688
+ expect(editMessageText).not.toHaveBeenCalled()
689
+ })
690
+
691
+ it('retract() is best-effort — deleteMessage failure does not throw', async () => {
692
+ const sendMessage = makeSendMessage()
693
+ const editMessageText = makeEditMessageText()
694
+ const deleteMessage = vi.fn(async () => {
695
+ throw new Error('deleteMessage: message to delete not found')
696
+ })
697
+ const warn = vi.fn()
698
+ const stream = createAnswerStream({
699
+ chatId: 'chat251',
700
+ isPrivateChat: false,
701
+ minInitialChars: 0,
702
+ throttleMs: 250,
703
+ sendMessage,
704
+ editMessageText,
705
+ deleteMessage,
706
+ warn,
707
+ })
708
+
709
+ stream.update('x'.repeat(500))
710
+ await flushMicrotasks()
711
+ expect(sendMessage).toHaveBeenCalledTimes(1)
712
+
713
+ // retract() must not throw even when deleteMessage rejects
714
+ await expect(stream.retract()).resolves.toBeUndefined()
715
+
716
+ // The failure should have been logged via warn
717
+ expect(warn).toHaveBeenCalled()
718
+ expect(warn.mock.calls.some((c) => /retract.*failed|deleteMessage.*failed/i.test(String(c[0])))).toBe(true)
719
+ })
720
+
721
+ it('retract() without deleteMessage wired is a no-op (no error)', async () => {
722
+ const sendMessage = makeSendMessage()
723
+ const editMessageText = makeEditMessageText()
724
+ const stream = createAnswerStream({
725
+ chatId: 'chat251',
726
+ isPrivateChat: false,
727
+ minInitialChars: 0,
728
+ throttleMs: 250,
729
+ sendMessage,
730
+ editMessageText,
731
+ // deleteMessage intentionally omitted
732
+ })
733
+
734
+ stream.update('hello world')
735
+ await flushMicrotasks()
736
+ expect(sendMessage).toHaveBeenCalledTimes(1)
737
+
738
+ // retract() with no deleteMessage callback — should resolve without error
739
+ await expect(stream.retract()).resolves.toBeUndefined()
740
+ })
741
+ })
742
+
743
+ // ─── Issue #203: onMetric callback ──────────────────────────────────────────
744
+ describe('answer-stream — onMetric callback (#203)', () => {
745
+ it('fires answer_lane_update on first sendMessage (non-DM, message transport)', async () => {
746
+ const onMetric = vi.fn()
747
+ const sendMessage = makeSendMessage()
748
+ const editMessageText = makeEditMessageText()
749
+ const stream = createAnswerStream({
750
+ chatId: 'chatX',
751
+ isPrivateChat: false,
752
+ minInitialChars: 0,
753
+ throttleMs: 250,
754
+ sendMessage,
755
+ editMessageText,
756
+ onMetric,
757
+ })
758
+
759
+ stream.update('hello there friend, this is some answer text')
760
+ vi.advanceTimersByTime(500)
761
+ await flushMicrotasks()
762
+
763
+ expect(onMetric).toHaveBeenCalledTimes(1)
764
+ const ev = onMetric.mock.calls[0][0] as { kind: string; chatId: string; transport: string; charCount: number }
765
+ expect(ev.kind).toBe('answer_lane_update')
766
+ expect(ev.chatId).toBe('chatX')
767
+ expect(ev.transport).toBe('message')
768
+ expect(typeof ev.charCount).toBe('number')
769
+ expect(ev.charCount).toBeGreaterThan(0)
770
+ })
771
+
772
+ it('fires answer_lane_update on edit (transport: edit)', async () => {
773
+ const onMetric = vi.fn()
774
+ const sendMessage = makeSendMessage()
775
+ const editMessageText = makeEditMessageText()
776
+ const stream = createAnswerStream({
777
+ chatId: 'chatX',
778
+ isPrivateChat: false,
779
+ minInitialChars: 0,
780
+ throttleMs: 250,
781
+ sendMessage,
782
+ editMessageText,
783
+ onMetric,
784
+ })
785
+
786
+ stream.update('initial text')
787
+ vi.advanceTimersByTime(500)
788
+ await flushMicrotasks()
789
+
790
+ stream.update('initial text plus more')
791
+ vi.advanceTimersByTime(1500)
792
+ await flushMicrotasks()
793
+
794
+ const transports = onMetric.mock.calls.map((c) => (c[0] as { transport: string }).transport)
795
+ expect(transports).toContain('message')
796
+ expect(transports).toContain('edit')
797
+ })
798
+
799
+ it('fires answer_lane_update on draft transport for DMs', async () => {
800
+ const onMetric = vi.fn()
801
+ const sendMessage = makeSendMessage()
802
+ const editMessageText = makeEditMessageText()
803
+ const sendMessageDraft = makeSendMessageDraft()
804
+ const stream = createAnswerStream({
805
+ chatId: 'chatX',
806
+ isPrivateChat: true,
807
+ minInitialChars: 0,
808
+ throttleMs: 250,
809
+ sendMessage,
810
+ editMessageText,
811
+ sendMessageDraft,
812
+ onMetric,
813
+ })
814
+
815
+ stream.update('streaming via draft')
816
+ vi.advanceTimersByTime(500)
817
+ await flushMicrotasks()
818
+
819
+ const draftEvents = onMetric.mock.calls
820
+ .map((c) => c[0] as { kind: string; transport?: string })
821
+ .filter((ev) => ev.kind === 'answer_lane_update' && ev.transport === 'draft')
822
+ expect(draftEvents.length).toBeGreaterThan(0)
823
+ })
824
+
825
+ it('fires answer_lane_materialized on materialize success', async () => {
826
+ const onMetric = vi.fn()
827
+ const sendMessage = makeSendMessage()
828
+ const editMessageText = makeEditMessageText()
829
+ const stream = createAnswerStream({
830
+ chatId: 'chatX',
831
+ isPrivateChat: false,
832
+ minInitialChars: 0,
833
+ throttleMs: 250,
834
+ sendMessage,
835
+ editMessageText,
836
+ onMetric,
837
+ })
838
+
839
+ stream.update('full answer text')
840
+ vi.advanceTimersByTime(500)
841
+ await flushMicrotasks()
842
+
843
+ onMetric.mockClear()
844
+ const id = await stream.materialize()
845
+
846
+ expect(typeof id).toBe('number')
847
+ expect(onMetric).toHaveBeenCalledTimes(1)
848
+ const ev = onMetric.mock.calls[0][0] as { kind: string; chatId: string; messageId: number }
849
+ expect(ev.kind).toBe('answer_lane_materialized')
850
+ expect(ev.chatId).toBe('chatX')
851
+ expect(ev.messageId).toBe(id)
852
+ })
853
+
854
+ it('does not fire answer_lane_materialized when oversize guard rejects', async () => {
855
+ const onMetric = vi.fn()
856
+ const sendMessage = makeSendMessage()
857
+ const editMessageText = makeEditMessageText()
858
+ const stream = createAnswerStream({
859
+ chatId: 'chatX',
860
+ isPrivateChat: false,
861
+ minInitialChars: 0,
862
+ throttleMs: 250,
863
+ sendMessage,
864
+ editMessageText,
865
+ onMetric,
866
+ })
867
+
868
+ stream.update('x'.repeat(4097))
869
+ onMetric.mockClear()
870
+ const id = await stream.materialize()
871
+
872
+ expect(id).toBeUndefined()
873
+ const matEvents = onMetric.mock.calls
874
+ .map((c) => c[0] as { kind: string })
875
+ .filter((ev) => ev.kind === 'answer_lane_materialized')
876
+ expect(matEvents).toHaveLength(0)
877
+ })
878
+ })