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,272 @@
1
+ /**
2
+ * Refactor regression: every per-chat close site must end in the SAME
3
+ * post-conditions on the driver's internal state. Pre-refactor, three
4
+ * code paths (turn_end → completeTurnFully, heartbeat zombie ceiling →
5
+ * closeZombie, Gap-8 deferred-completion timeout → inline) reproduced
6
+ * the cleanup tail by hand and diverged on edge cases. The refactor
7
+ * funnels them all through `closePerChat(reason)` so the only remaining
8
+ * deltas are:
9
+ *
10
+ * - 'turn-end': no sub-agent force-close (none are running).
11
+ * - 'zombie' : force-close running sub-agents; preserve
12
+ * pendingSyncEchoes (echo may still arrive).
13
+ * - 'stalled' : force-close running sub-agents; flush(stalledClose=true).
14
+ *
15
+ * This test drives all three reasons against a fresh driver instance
16
+ * and asserts the convergent post-conditions. It is the load-bearing
17
+ * test for the unified close path — if it fails, the refactor regressed.
18
+ */
19
+ import { describe, it, expect } from 'vitest'
20
+ import { createProgressDriver } from '../progress-card-driver.js'
21
+ import type { SessionEvent } from '../session-tail.js'
22
+
23
+ let nextMsgId = 7000
24
+
25
+ function harness(opts?: { maxIdleMs?: number; deferredCompletionTimeoutMs?: number }) {
26
+ let now = 1000
27
+ const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
28
+ let nextRef = 0
29
+ const emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }> = []
30
+
31
+ const driver = createProgressDriver({
32
+ emit: (a) => emits.push(a),
33
+ minIntervalMs: 0,
34
+ coalesceMs: 0,
35
+ initialDelayMs: 0,
36
+ heartbeatMs: 1_000,
37
+ maxIdleMs: opts?.maxIdleMs ?? 30_000,
38
+ deferredCompletionTimeoutMs: opts?.deferredCompletionTimeoutMs ?? 10_000,
39
+ now: () => now,
40
+ setTimeout: (fn, ms) => {
41
+ const ref = nextRef++
42
+ timers.push({ fireAt: now + ms, fn, ref })
43
+ return { ref }
44
+ },
45
+ clearTimeout: (handle) => {
46
+ const target = (handle as { ref: number }).ref
47
+ const idx = timers.findIndex((t) => t.ref === target)
48
+ if (idx !== -1) timers.splice(idx, 1)
49
+ },
50
+ setInterval: (fn, ms) => {
51
+ const ref = nextRef++
52
+ timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
53
+ return { ref }
54
+ },
55
+ clearInterval: (handle) => {
56
+ const target = (handle as { ref: number }).ref
57
+ const idx = timers.findIndex((t) => t.ref === target)
58
+ if (idx !== -1) timers.splice(idx, 1)
59
+ },
60
+ })
61
+
62
+ const advance = (ms: number): void => {
63
+ now += ms
64
+ for (;;) {
65
+ timers.sort((a, b) => a.fireAt - b.fireAt)
66
+ const next = timers[0]
67
+ if (!next || next.fireAt > now) break
68
+ if (next.repeat != null) {
69
+ next.fireAt += next.repeat
70
+ next.fn()
71
+ } else {
72
+ timers.shift()
73
+ next.fn()
74
+ }
75
+ }
76
+ }
77
+
78
+ return { driver, emits, advance, getNow: () => now }
79
+ }
80
+
81
+ function enqueue(chatId: string): SessionEvent {
82
+ return {
83
+ kind: 'enqueue',
84
+ chatId,
85
+ messageId: String(nextMsgId++),
86
+ threadId: null,
87
+ rawContent: `<channel chat_id="${chatId}">go</channel>`,
88
+ }
89
+ }
90
+
91
+ describe('progress-card-driver: all close paths converge on identical final state', () => {
92
+ it("'turn-end' path: chats empty, baseTurnSeqs cleaned, heartbeat stopped", () => {
93
+ const { driver } = harness()
94
+ const maps = driver._debugGetMaps!()
95
+
96
+ driver.ingest(enqueue('cA'), null)
97
+ expect(maps.chats.size).toBe(1)
98
+ expect(maps.baseTurnSeqs.has('cA')).toBe(true)
99
+
100
+ driver.ingest({ kind: 'turn_end', durationMs: 50 }, 'cA')
101
+
102
+ expect(maps.chats.size).toBe(0)
103
+ expect(maps.baseTurnSeqs.has('cA')).toBe(false)
104
+ expect(maps.chatRunningSubagents.has('cA')).toBe(false)
105
+ })
106
+
107
+ it("'zombie' path (heartbeat maxIdle ceiling): same convergence + pendingSyncEchoes preserved", () => {
108
+ const { driver, advance } = harness({ maxIdleMs: 5_000 })
109
+ const maps = driver._debugGetMaps!()
110
+
111
+ driver.ingest(enqueue('cA'), null)
112
+ expect(maps.chats.size).toBe(1)
113
+
114
+ // Seed a pending sync-echo so we can assert the zombie path leaves it
115
+ // in place (the echo may still arrive after close).
116
+ maps.pendingSyncEchoes.set('cA:fake', 1000)
117
+
118
+ // Idle past maxIdleMs so the heartbeat reclassifies the card as zombie.
119
+ advance(20_000)
120
+
121
+ expect(maps.chats.size).toBe(0)
122
+ expect(maps.baseTurnSeqs.has('cA')).toBe(false)
123
+ expect(maps.chatRunningSubagents.has('cA')).toBe(false)
124
+ // CRITICAL invariant: zombie close must NOT clear pendingSyncEchoes.
125
+ // The dedup map's TTL eviction (maybeEvict) reaps it later.
126
+ expect(maps.pendingSyncEchoes.has('cA:fake')).toBe(true)
127
+ })
128
+
129
+ it("'zombie' path also force-closes running sub-agents (sync registry drained)", () => {
130
+ const { driver, advance } = harness({ maxIdleMs: 5_000 })
131
+ const maps = driver._debugGetMaps!()
132
+
133
+ driver.ingest(enqueue('cA'), null)
134
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'sa1', firstPromptText: 'work' }, 'cA')
135
+ expect(maps.chats.size).toBe(1)
136
+
137
+ // Idle past maxIdleMs without ever reporting sub_agent_turn_end.
138
+ advance(20_000)
139
+
140
+ expect(maps.chats.size).toBe(0)
141
+ // Issue #399: sync registry must be drained even when sub-agents
142
+ // never reported their own turn_end.
143
+ expect(maps.chatRunningSubagents.has('cA')).toBe(false)
144
+ })
145
+
146
+ it("'stalled' path (Gap-8 deferred-completion timeout): same convergence", () => {
147
+ const { driver, advance } = harness({
148
+ maxIdleMs: 999_999, // disable zombie ceiling so we hit the stalled branch
149
+ deferredCompletionTimeoutMs: 5_000,
150
+ })
151
+ const maps = driver._debugGetMaps!()
152
+
153
+ driver.ingest(enqueue('cA'), null)
154
+ // Spawn a background sub-agent so parent turn_end defers instead of
155
+ // closing immediately.
156
+ driver.ingest(
157
+ {
158
+ kind: 'tool_use',
159
+ toolName: 'Agent',
160
+ toolUseId: 'tu1',
161
+ input: { prompt: 'bg', run_in_background: true },
162
+ },
163
+ 'cA',
164
+ )
165
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'sa1', firstPromptText: 'bg' }, 'cA')
166
+ driver.ingest({ kind: 'turn_end', durationMs: 100 }, 'cA')
167
+ // After parent turn_end: card alive in pendingCompletion.
168
+ expect(maps.chats.size).toBe(1)
169
+
170
+ // Sub-agent never reports done; advance past the deferred timeout so
171
+ // the heartbeat's stalled-cards branch fires.
172
+ advance(15_000)
173
+
174
+ expect(maps.chats.size).toBe(0)
175
+ expect(maps.baseTurnSeqs.has('cA')).toBe(false)
176
+ expect(maps.chatRunningSubagents.has('cA')).toBe(false)
177
+ })
178
+
179
+ it('all three paths fire onTurnComplete callback exactly once', () => {
180
+ // The completion callback is the externally-visible side-effect that
181
+ // gates everything downstream (Stop hook, summary writer). Every
182
+ // close path must fire it; the unified path makes that automatic
183
+ // because the cleanup tail in completeTurnFully gates on
184
+ // completionFired.
185
+ const calls: string[] = []
186
+ const opts = {
187
+ onTurnComplete: (a: { turnKey: string }) => {
188
+ calls.push(a.turnKey)
189
+ },
190
+ }
191
+ let now = 1000
192
+ const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
193
+ let nextRef = 0
194
+ const driver = createProgressDriver({
195
+ emit: () => {},
196
+ minIntervalMs: 0,
197
+ coalesceMs: 0,
198
+ initialDelayMs: 0,
199
+ heartbeatMs: 1_000,
200
+ maxIdleMs: 5_000,
201
+ deferredCompletionTimeoutMs: 5_000,
202
+ now: () => now,
203
+ setTimeout: (fn, ms) => {
204
+ const ref = nextRef++
205
+ timers.push({ fireAt: now + ms, fn, ref })
206
+ return { ref }
207
+ },
208
+ clearTimeout: (h) => {
209
+ const ref = (h as { ref: number }).ref
210
+ const idx = timers.findIndex((t) => t.ref === ref)
211
+ if (idx !== -1) timers.splice(idx, 1)
212
+ },
213
+ setInterval: (fn, ms) => {
214
+ const ref = nextRef++
215
+ timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
216
+ return { ref }
217
+ },
218
+ clearInterval: (h) => {
219
+ const ref = (h as { ref: number }).ref
220
+ const idx = timers.findIndex((t) => t.ref === ref)
221
+ if (idx !== -1) timers.splice(idx, 1)
222
+ },
223
+ ...opts,
224
+ })
225
+ const advance = (ms: number): void => {
226
+ now += ms
227
+ for (;;) {
228
+ timers.sort((a, b) => a.fireAt - b.fireAt)
229
+ const next = timers[0]
230
+ if (!next || next.fireAt > now) break
231
+ if (next.repeat != null) {
232
+ next.fireAt += next.repeat
233
+ next.fn()
234
+ } else {
235
+ timers.shift()
236
+ next.fn()
237
+ }
238
+ }
239
+ }
240
+
241
+ // turn-end
242
+ driver.ingest(enqueue('cA'), null)
243
+ driver.ingest({ kind: 'turn_end', durationMs: 10 }, 'cA')
244
+ // zombie
245
+ driver.ingest(enqueue('cB'), null)
246
+ advance(20_000)
247
+ // stalled (after time has advanced to satisfy the deferred timeout)
248
+ driver.ingest(enqueue('cC'), null)
249
+ driver.ingest(
250
+ {
251
+ kind: 'tool_use',
252
+ toolName: 'Agent',
253
+ toolUseId: 'tu',
254
+ input: { prompt: 'bg', run_in_background: true },
255
+ },
256
+ 'cC',
257
+ )
258
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'sa', firstPromptText: 'bg' }, 'cC')
259
+ driver.ingest({ kind: 'turn_end', durationMs: 10 }, 'cC')
260
+ advance(20_000)
261
+
262
+ // Each chat got exactly one completion callback.
263
+ const byChat = new Map<string, number>()
264
+ for (const tk of calls) {
265
+ const chat = tk.split(':')[0]
266
+ byChat.set(chat, (byChat.get(chat) ?? 0) + 1)
267
+ }
268
+ expect(byChat.get('cA')).toBe(1)
269
+ expect(byChat.get('cB')).toBe(1)
270
+ expect(byChat.get('cC')).toBe(1)
271
+ })
272
+ })
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Tests for issue #334 — cross-turn sub-agent visibility.
3
+ *
4
+ * A background sub-agent dispatched in turn N (via Agent({run_in_background:true}))
5
+ * must remain visible on the new progress card that appears when turn N+1 starts.
6
+ */
7
+ import { describe, it, expect } from 'vitest'
8
+ import { createProgressDriver } from '../progress-card-driver.js'
9
+ import type { SessionEvent } from '../session-tail.js'
10
+
11
+ let nextMsgId = 100
12
+
13
+ function harness(
14
+ initialDelayMs = 0,
15
+ opts: { coldSubAgentThresholdMs?: number; heartbeatMs?: number } = {},
16
+ ) {
17
+ let now = 1000
18
+ const timers: Array<{ fireAt: number; fn: () => void; ref: number; repeat?: number }> = []
19
+ let nextRef = 0
20
+ const emits: Array<{ chatId: string; threadId?: string; turnKey: string; html: string; done: boolean }> = []
21
+
22
+ const driver = createProgressDriver({
23
+ emit: (a) => emits.push(a),
24
+ minIntervalMs: 0,
25
+ coalesceMs: 0,
26
+ initialDelayMs,
27
+ coldSubAgentThresholdMs: opts.coldSubAgentThresholdMs,
28
+ heartbeatMs: opts.heartbeatMs,
29
+ now: () => now,
30
+ setTimeout: (fn, ms) => {
31
+ const ref = nextRef++
32
+ timers.push({ fireAt: now + ms, fn, ref })
33
+ return { ref }
34
+ },
35
+ clearTimeout: (handle) => {
36
+ const target = (handle as { ref: number }).ref
37
+ const idx = timers.findIndex((t) => t.ref === target)
38
+ if (idx !== -1) timers.splice(idx, 1)
39
+ },
40
+ setInterval: (fn, ms) => {
41
+ const ref = nextRef++
42
+ timers.push({ fireAt: now + ms, fn, ref, repeat: ms })
43
+ return { ref }
44
+ },
45
+ clearInterval: (handle) => {
46
+ const target = (handle as { ref: number }).ref
47
+ const idx = timers.findIndex((t) => t.ref === target)
48
+ if (idx !== -1) timers.splice(idx, 1)
49
+ },
50
+ })
51
+
52
+ const advance = (ms: number): void => {
53
+ now += ms
54
+ for (;;) {
55
+ timers.sort((a, b) => a.fireAt - b.fireAt)
56
+ const next = timers[0]
57
+ if (!next || next.fireAt > now) break
58
+ if (next.repeat != null) {
59
+ next.fireAt += next.repeat
60
+ next.fn()
61
+ } else {
62
+ timers.shift()
63
+ next.fn()
64
+ }
65
+ }
66
+ }
67
+
68
+ return { driver, emits, advance }
69
+ }
70
+
71
+ function enqueue(chatId: string, text = 'hi'): SessionEvent {
72
+ return {
73
+ kind: 'enqueue',
74
+ chatId,
75
+ messageId: String(nextMsgId++),
76
+ threadId: null,
77
+ rawContent: `<channel chat_id="${chatId}">${text}</channel>`,
78
+ }
79
+ }
80
+
81
+ describe('cross-turn sub-agent visibility (#334)', () => {
82
+ it('Test 1: closeZombie on turn-1 force-close removes sub-agent from registry (fix #399)', () => {
83
+ // When turn 2 starts while turn 1 has a pending background sub-agent,
84
+ // the ingest enqueue path calls closeZombie on turn 1's card. closeZombie
85
+ // explicitly abandons all running sub-agents (marks them done for display),
86
+ // and — after fix #399 — also removes them from chatRunningSubagents.
87
+ // Therefore turn 2 starts clean (no carry-over of abandoned agents).
88
+ const { driver } = harness()
89
+
90
+ // Turn 1: dispatch a background sub-agent, then turn ends.
91
+ driver.ingest(enqueue('c1'), null)
92
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'bg-agent', firstPromptText: 'do work' }, 'c1')
93
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
94
+
95
+ // Turn 2 starts — triggers closeZombie on turn 1 → removes bg-agent from registry.
96
+ driver.startTurn({ chatId: 'c1', userText: 'new prompt' })
97
+
98
+ const turn2State = driver.peek('c1', undefined)
99
+ expect(turn2State).toBeDefined()
100
+ // bg-agent was abandoned by closeZombie; it must NOT carry over into turn 2.
101
+ expect(turn2State!.subAgents.has('bg-agent')).toBe(false)
102
+ })
103
+
104
+ it('Test 2: sub-agent finishing naturally before new turn does not appear on turn 2', () => {
105
+ // When a sub-agent finishes via sub_agent_turn_end (natural completion),
106
+ // it is removed from chatRunningSubagents by the ingest sync (fix #399
107
+ // also keeps this path correct). Turn 2 starts clean.
108
+ const { driver } = harness()
109
+
110
+ // Turn 1: dispatch background sub-agent.
111
+ driver.ingest(enqueue('c1'), null)
112
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'bg-agent', firstPromptText: 'do work' }, 'c1')
113
+ // Sub-agent finishes naturally before turn 2 starts.
114
+ driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'bg-agent', durationMs: 5000 }, 'c1')
115
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
116
+
117
+ // Turn 2 starts.
118
+ driver.startTurn({ chatId: 'c1', userText: 'next prompt' })
119
+
120
+ const turn2State = driver.peek('c1', undefined)
121
+ expect(turn2State).toBeDefined()
122
+ // bg-agent finished before turn 2 — must NOT appear.
123
+ expect(turn2State!.subAgents.has('bg-agent')).toBe(false)
124
+ })
125
+
126
+ it('Test 3: foreground sub-agent (completes mid-turn 1) does NOT appear on turn 2', () => {
127
+ const { driver } = harness()
128
+
129
+ // Turn 1: foreground sub-agent — starts and finishes before turn ends.
130
+ driver.ingest(enqueue('c1'), null)
131
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'fg-agent', firstPromptText: 'quick task' }, 'c1')
132
+ driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'fg-agent', durationMs: 200 }, 'c1')
133
+ driver.ingest({ kind: 'turn_end', durationMs: 800 }, 'c1')
134
+
135
+ // Turn 2 starts.
136
+ driver.startTurn({ chatId: 'c1', userText: 'next prompt' })
137
+
138
+ const turn2State = driver.peek('c1', undefined)
139
+ expect(turn2State).toBeDefined()
140
+ // Foreground sub-agent completed in turn 1 — must NOT bleed into turn 2.
141
+ expect(turn2State!.subAgents.has('fg-agent')).toBe(false)
142
+ })
143
+
144
+ it('multiple background sub-agents: closeZombie removes all from registry (fix #399)', () => {
145
+ // When closeZombie abandons all running sub-agents, they are all removed
146
+ // from chatRunningSubagents. Turn 2 starts with an empty sub-agent map.
147
+ const { driver } = harness()
148
+
149
+ driver.ingest(enqueue('c1'), null)
150
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'bg1', firstPromptText: 'task 1' }, 'c1')
151
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'bg2', firstPromptText: 'task 2' }, 'c1')
152
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
153
+
154
+ // New turn triggers closeZombie → all running agents marked done → removed from registry.
155
+ driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
156
+
157
+ const state = driver.peek('c1', undefined)
158
+ // Both abandoned agents must NOT carry over.
159
+ expect(state!.subAgents.has('bg1')).toBe(false)
160
+ expect(state!.subAgents.has('bg2')).toBe(false)
161
+ })
162
+
163
+ it('different chats do not cross-contaminate', () => {
164
+ const { driver } = harness()
165
+
166
+ // Chat A has a background sub-agent.
167
+ driver.ingest(enqueue('chatA'), null)
168
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'agentA', firstPromptText: 'A' }, 'chatA')
169
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'chatA')
170
+
171
+ // Chat B starts a new turn (no sub-agents in chat B).
172
+ driver.startTurn({ chatId: 'chatB', userText: 'hello' })
173
+
174
+ const stateB = driver.peek('chatB', undefined)
175
+ expect(stateB!.subAgents.has('agentA')).toBe(false)
176
+ expect(stateB!.subAgents.size).toBe(0)
177
+ })
178
+
179
+ it('cold-jsonl-synth path syncs registry: turn 2 does NOT inherit cold-synth-terminated agent (fix #399)', () => {
180
+ // Forensic case from the live klanker bug: sub-agent ada7c3d07c28158f5
181
+ // hit its turn limit mid-tool-call and never wrote system.turn_duration.
182
+ // The cold-jsonl-synth heartbeat path (Gap 4 #313) marks it done
183
+ // synthetically. BEFORE fix #399 the registry was never synced from
184
+ // this path, so the agent appeared as a phantom on every subsequent
185
+ // turn's card. AFTER fix #399 the registry is synced and turn 2 is clean.
186
+ const { driver, advance } = harness(0, { coldSubAgentThresholdMs: 30_000, heartbeatMs: 5_000 })
187
+
188
+ // Turn 1: dispatch background sub-agent, parent turn ends → pendingCompletion=true
189
+ driver.ingest(enqueue('c1'), null)
190
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'cold-agent', firstPromptText: 'long task' }, 'c1')
191
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
192
+
193
+ // Sub-agent goes cold (no events for > coldSubAgentThresholdMs).
194
+ // Heartbeat ticks fire repeatedly; once lastEventAt is older than the
195
+ // threshold, the cold-jsonl-synth path runs and synthesises sub_agent_turn_end.
196
+ advance(35_000)
197
+
198
+ // Turn 2 starts in the same chat. WITHOUT #399's fix, cold-agent would
199
+ // re-seed into turn 2's PerChatState.subAgents (the bug). WITH the fix,
200
+ // syncChatRunningSubagents fired from the cold-synth path and removed it.
201
+ driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
202
+
203
+ const turn2State = driver.peek('c1', undefined)
204
+ expect(turn2State).toBeDefined()
205
+ expect(turn2State!.subAgents.has('cold-agent')).toBe(false)
206
+ })
207
+
208
+ it('counter-test: still-running background sub-agent DOES carry over (preserves #334)', () => {
209
+ // The carry-over feature from #334 must continue to work for legitimate
210
+ // still-running sub-agents. If syncChatRunningSubagents over-removes,
211
+ // this test catches the regression. Asserts:
212
+ // 1. A bg sub-agent that started in turn 1 and never went terminal
213
+ // 2. After turn 2 starts (closeZombie fires on turn 1's card), the
214
+ // sub-agent is correctly REMOVED (closeZombie marks it done)
215
+ // Since closeZombie is the post-turn-1 cleanup path, "still running
216
+ // across turns" actually means "running while turn 1 is in pendingCompletion
217
+ // BEFORE turn 2 enqueues". The carry-over visibility happens during the
218
+ // pendingCompletion window — verified here by peeking BEFORE turn 2.
219
+ const { driver } = harness()
220
+
221
+ driver.ingest(enqueue('c1'), null)
222
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'still-running', firstPromptText: 'long task' }, 'c1')
223
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
224
+
225
+ // During pendingCompletion the sub-agent is visible on the card.
226
+ const duringPending = driver.peek('c1', undefined)
227
+ expect(duringPending).toBeDefined()
228
+ expect(duringPending!.subAgents.has('still-running')).toBe(true)
229
+ expect(duringPending!.subAgents.get('still-running')?.state).toBe('running')
230
+ })
231
+
232
+ it('sub-agent finishes naturally between turns: turn 3 starts clean', () => {
233
+ // Verifies that a sub-agent finishing via sub_agent_turn_end (natural
234
+ // completion via the ingest path) is removed from chatRunningSubagents
235
+ // so subsequent turns do not see it.
236
+ const { driver } = harness()
237
+
238
+ // Turn 1: background sub-agent dispatched.
239
+ driver.ingest(enqueue('c1'), null)
240
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'bg1', firstPromptText: 'shared?' }, 'c1')
241
+ driver.ingest({ kind: 'turn_end', durationMs: 500 }, 'c1')
242
+
243
+ // Sub-agent finishes naturally BEFORE turn 2 starts.
244
+ driver.ingest({ kind: 'sub_agent_turn_end', agentId: 'bg1', durationMs: 3000 }, 'c1')
245
+
246
+ // Turn 2 starts — bg1 already finished, so registry is empty.
247
+ driver.startTurn({ chatId: 'c1', userText: 'turn 2' })
248
+ expect(driver.peek('c1', undefined)!.subAgents.has('bg1')).toBe(false)
249
+
250
+ driver.ingest({ kind: 'turn_end', durationMs: 1000 }, 'c1')
251
+
252
+ // Turn 3: the finished sub-agent must NOT appear.
253
+ driver.startTurn({ chatId: 'c1', userText: 'turn 3' })
254
+ const stateT3 = driver.peek('c1', undefined)
255
+ expect(stateT3).toBeDefined()
256
+ expect(stateT3!.subAgents.has('bg1')).toBe(false)
257
+ })
258
+ })
@@ -0,0 +1,81 @@
1
+ /**
2
+ * PR-C2 — `dispose({ preservePending: true })` must NOT remove chats
3
+ * whose `pendingCompletion === true`.
4
+ *
5
+ * Regression: commit 4c0186d introduced a dispose() that wiped all
6
+ * in-flight card state on every bridge disconnect. The selective
7
+ * dispose path was added to keep cards with running background
8
+ * sub-agents alive across the disconnect/reconnect cycle.
9
+ *
10
+ * fails when: dispose's preservePending branch unconditionally clears
11
+ * `chats`, OR forgets to leave the heartbeat running while a pending
12
+ * chat survives.
13
+ */
14
+ import { describe, it, expect } from 'vitest'
15
+ import { makeHarness, enqueue } from './_progress-card-harness.js'
16
+
17
+ describe('PR-C2: dispose({ preservePending: true })', () => {
18
+ it('chat with pendingCompletion survives dispose; heartbeat-driven completion still fires after a "reconnect"', () => {
19
+ const completions: string[] = []
20
+ const { driver, advance } = makeHarness({
21
+ minIntervalMs: 0,
22
+ coalesceMs: 0,
23
+ heartbeatMs: 1_000,
24
+ maxIdleMs: 999_999,
25
+ deferredCompletionTimeoutMs: 5_000,
26
+ promoteAfterMs: 999_999,
27
+ onTurnComplete: (s) => completions.push(s.turnKey),
28
+ })
29
+ const maps = driver._debugGetMaps!()
30
+ const CHAT = 'cA'
31
+
32
+ // Set up a turn with a background sub-agent so parent turn_end
33
+ // produces pendingCompletion=true.
34
+ driver.ingest(enqueue(CHAT), null)
35
+ driver.ingest(
36
+ {
37
+ kind: 'tool_use',
38
+ toolName: 'Agent',
39
+ toolUseId: 'tu1',
40
+ input: { prompt: 'bg', run_in_background: true },
41
+ },
42
+ CHAT,
43
+ )
44
+ driver.ingest({ kind: 'sub_agent_started', agentId: 'saBG', firstPromptText: 'bg' }, CHAT)
45
+ driver.ingest({ kind: 'tool_use', toolName: 'mcp__switchroom-telegram__reply' }, CHAT)
46
+ driver.recordOutboundDelivered(CHAT)
47
+ driver.ingest({ kind: 'turn_end', durationMs: 100 }, CHAT)
48
+
49
+ // Confirm pendingCompletion shape.
50
+ expect(maps.chats.size).toBe(1)
51
+ const csBefore = [...maps.chats.values()][0] as { pendingCompletion: boolean }
52
+ expect(csBefore.pendingCompletion).toBe(true)
53
+
54
+ // Bridge disconnect: dispose preserving pending.
55
+ driver.dispose!({ preservePending: true })
56
+
57
+ // Chat must survive.
58
+ expect(maps.chats.size).toBe(1)
59
+ const csAfter = [...maps.chats.values()][0] as { pendingCompletion: boolean }
60
+ expect(csAfter.pendingCompletion).toBe(true)
61
+
62
+ // Now simulate "bridge reconnect" — nothing to do at the driver level
63
+ // for that, but the heartbeat must still be wired so the deferred
64
+ // completion timeout eventually fires.
65
+ advance(15_000)
66
+
67
+ // Stalled-cards heartbeat branch should have closed the chat by now.
68
+ expect(maps.chats.size).toBe(0)
69
+ expect(completions.length).toBe(1)
70
+ })
71
+
72
+ it('chats WITHOUT pendingCompletion are dropped by preservePending dispose', () => {
73
+ const { driver } = makeHarness()
74
+ const maps = driver._debugGetMaps!()
75
+ driver.ingest(enqueue('cActive'), null)
76
+ expect(maps.chats.size).toBe(1)
77
+
78
+ driver.dispose!({ preservePending: true })
79
+ expect(maps.chats.size).toBe(0)
80
+ })
81
+ })
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Pin the wiring for #354's PROGRESS_CARD_DRAFT_TRANSPORT env flag.
3
+ *
4
+ * Two regressions this guards against:
5
+ *
6
+ * 1. The flag drifting silently — someone refactors the progress-
7
+ * card emit and forgets to thread isPrivateChat / sendMessageDraft
8
+ * when the flag is on. Result: the card stays on the legacy edit
9
+ * path even though the operator opted in.
10
+ *
11
+ * 2. The flag turning on by default before the spike unknowns are
12
+ * validated. Default-OFF is load-bearing: until we know drafts
13
+ * can be pinned and survive bot crashes, the legacy path is the
14
+ * safe one.
15
+ *
16
+ * Source-level pinning rather than behavioural — the gateway emit
17
+ * lives inside the bot startup wiring and is hard to drive in
18
+ * isolation. The contract here is the literal env-var check + the
19
+ * fact that draft deps are conditional on it.
20
+ */
21
+
22
+ import { describe, it, expect } from 'vitest'
23
+ import { readFileSync } from 'node:fs'
24
+ import { resolve } from 'node:path'
25
+
26
+ const gatewaySrc = readFileSync(
27
+ resolve(__dirname, '..', 'gateway', 'gateway.ts'),
28
+ 'utf-8',
29
+ )
30
+
31
+ describe('progress-card draft transport flag (#354)', () => {
32
+ it('is gated behind PROGRESS_CARD_DRAFT_TRANSPORT=1 (default OFF)', () => {
33
+ // The flag check must be an explicit `=== '1'` not a truthy check
34
+ // — `process.env.X === '1'` is the project convention, and a
35
+ // truthy check would mis-fire on `=0` or `=false`.
36
+ expect(gatewaySrc).toMatch(
37
+ /process\.env\.PROGRESS_CARD_DRAFT_TRANSPORT\s*===\s*['"]1['"]/,
38
+ )
39
+ })
40
+
41
+ it('only enables draft when the chat is a DM (no threads, isDmChatId)', () => {
42
+ // Drafts don't support forum topics. The flag-check block must
43
+ // gate on isDmChatId(chatId) and threadId == null — otherwise a
44
+ // forum-topic message would be sent via draft and Telegram would
45
+ // reject with DRAFT_CHAT_UNSUPPORTED.
46
+ const block = extractDraftBlock(gatewaySrc)
47
+ expect(block).toMatch(/isDmChatId\(chatId\)/)
48
+ expect(block).toMatch(/threadId\s*==\s*null/)
49
+ })
50
+
51
+ it('passes both isPrivateChat AND sendMessageDraft when eligible', () => {
52
+ // Without sendMessageDraft, stream-reply-handler resolves transport
53
+ // to 'message' (line 432: `isForumTopic || deps.sendMessageDraft == null`).
54
+ // Both deps must be threaded together for the draft path to fire.
55
+ const block = extractDraftBlock(gatewaySrc)
56
+ expect(block).toMatch(/isPrivateChat:\s*true/)
57
+ expect(block).toMatch(/sendMessageDraft:\s*sendMessageDraftFn/)
58
+ })
59
+
60
+ it('documents the spike unknowns from #354 inline so an operator can validate', () => {
61
+ // The flag exists because pinning + crash behavior are unverified.
62
+ // The block comment must call out both so a future contributor
63
+ // doesn't flip the default to ON without doing the spike first.
64
+ const block = extractDraftBlock(gatewaySrc)
65
+ expect(block.toLowerCase()).toMatch(/pin/)
66
+ expect(block.toLowerCase()).toMatch(/spike|unknown/)
67
+ })
68
+ })
69
+
70
+ /** Pull the #354 spike block out of gateway.ts for source-level assertions. */
71
+ function extractDraftBlock(src: string): string {
72
+ // The block starts at the spike comment marker and ends at the next
73
+ // `handleStreamReply(` invocation. Big enough to span the gate +
74
+ // the conditional draft-deps spread.
75
+ const start = src.indexOf('// #354 spike')
76
+ expect(start, '#354 spike block not found in gateway.ts').toBeGreaterThan(0)
77
+ const end = src.indexOf(').then', start)
78
+ expect(end, '#354 spike block end not found').toBeGreaterThan(start)
79
+ return src.slice(start, end)
80
+ }