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,589 @@
1
+ /**
2
+ * Extracted pin-lifecycle manager for the progress-card driver.
3
+ *
4
+ * Previously lived inline in gateway.ts (progressDriver setup block) —
5
+ * reaching into module-level state (`progressPinnedMsgIds`,
6
+ * `unpinnedTurnKeys`, `lockedBot`, `resolveAgentDirFromEnv`) and making
7
+ * the full first-emit → pin → edit → turn-end → unpin sequence
8
+ * unreachable from tests without spinning up the whole bot runner.
9
+ *
10
+ * This module exposes the same behaviour behind a pure interface, with
11
+ * bot-API + sidecar writes injected as callbacks. Contract (tested in
12
+ * progress-card-pin-manager.test.ts):
13
+ *
14
+ * considerPin(candidate)
15
+ * - On `isFirstEmit === true` for a (turnKey, agentId) pair not yet
16
+ * pinned:
17
+ * records the pinned message id
18
+ * records an active-pin sidecar entry (if addPin is wired)
19
+ * calls bot.pin(chatId, messageId, { disable_notification: true })
20
+ * on pin rejection: calls removePin to keep the sidecar consistent
21
+ * - On subsequent emits for the same (turnKey, agentId): no-op
22
+ * (idempotent).
23
+ *
24
+ * completeTurn({ chatId, threadId, turnKey, agentId? })
25
+ * - Looks up pinnedMessageId for (turnKey, agentId); if present:
26
+ * unpins exactly once (duplicate completeTurn calls are safe)
27
+ * calls removePin to clear the sidecar
28
+ * - The unpinned set entry is cleared so a future re-use of the same
29
+ * composite key (unlikely but cheap) starts fresh.
30
+ *
31
+ * completeAllForTurn({ turnKey })
32
+ * - Catastrophic-cleanup helper. Unpins every pinned card for a turn,
33
+ * across all agentIds. Used on bridge-disconnect / forced shutdown
34
+ * paths where individual sub_agent_turn_end events may never land.
35
+ *
36
+ * unpinForChat(chatId, threadId)
37
+ * - External hook for context-exhaustion / /restart: unpins every
38
+ * currently-pinned (turnKey, agentId) matching the chat+thread
39
+ * prefix.
40
+ *
41
+ * Per-agent cards (#per-agent-cards): the manager keys pin state on the
42
+ * composite (turnKey, agentId) so a parent card and its sub-agent cards
43
+ * can co-exist independently in the same turn. Callers that don't yet
44
+ * thread agentId through (e.g. legacy single-card-per-turn callers) get
45
+ * a stable default sentinel — see `PARENT_AGENT_ID` below — so existing
46
+ * behaviour is preserved without modification.
47
+ */
48
+
49
+ /**
50
+ * Sentinel agent id for the "parent" / single-card-per-turn case. Used
51
+ * as the default when callers don't yet pass an explicit agentId, so the
52
+ * composite-key bookkeeping degrades to the original turnKey-only
53
+ * behaviour for legacy call sites.
54
+ */
55
+ export const PARENT_AGENT_ID = '__parent__'
56
+
57
+ export interface PinCandidate {
58
+ readonly chatId: string
59
+ readonly threadId?: string
60
+ readonly turnKey: string
61
+ readonly messageId: number
62
+ readonly isFirstEmit: boolean
63
+ /**
64
+ * Per-agent identity. Defaults to {@link PARENT_AGENT_ID} when omitted
65
+ * — callers that haven't yet been threaded for per-agent cards behave
66
+ * as before (one pin per turnKey).
67
+ */
68
+ readonly agentId?: string
69
+ }
70
+
71
+ export interface ActivePinEntry {
72
+ readonly chatId: string
73
+ readonly messageId: number
74
+ readonly turnKey: string
75
+ readonly pinnedAt: number
76
+ /**
77
+ * Stored verbatim alongside the entry. Existing sidecar files written
78
+ * before the per-agent split have no agentId; readers should treat a
79
+ * missing field as {@link PARENT_AGENT_ID}.
80
+ */
81
+ readonly agentId?: string
82
+ }
83
+
84
+ export interface TimerHandle {
85
+ cancel(): void
86
+ }
87
+
88
+ export interface PinManagerDeps {
89
+ /** Underlying `bot.api.pinChatMessage` wrapper. */
90
+ pin: (
91
+ chatId: string,
92
+ messageId: number,
93
+ opts?: { disable_notification?: boolean },
94
+ ) => Promise<unknown>
95
+ /** Underlying `bot.api.unpinChatMessage` wrapper. */
96
+ unpin: (chatId: string, messageId: number) => Promise<unknown>
97
+ /** Optional: persist a pin to the sidecar. Skipped when not wired. */
98
+ addPin?: (entry: ActivePinEntry) => void
99
+ /** Optional: remove from the sidecar. Skipped when not wired. */
100
+ removePin?: (chatId: string, messageId: number) => void
101
+ /**
102
+ * Optional: `bot.api.deleteMessage` wrapper. When wired, the manager
103
+ * deletes the "Clerk pinned ..." service message that Telegram posts
104
+ * automatically after each pin. Skipped when not wired.
105
+ */
106
+ deleteMessage?: (chatId: string, messageId: number) => Promise<unknown>
107
+ /** Logger for pin/unpin failures. Receives lines with trailing newline. */
108
+ log?: (line: string) => void
109
+ /** Clock injection for test determinism. Defaults to `Date.now`. */
110
+ now?: () => number
111
+ /**
112
+ * How long to wait after the first emit before actually pinning. The
113
+ * driver's `initialDelayMs` already suppresses the card entirely for
114
+ * fast turns, so by the time first-emit fires the turn is already
115
+ * considered slow and the pin can follow immediately. Defaults to 0.
116
+ */
117
+ pinDelayMs?: number
118
+ /**
119
+ * Injectable timer scheduler. Defaults to `setTimeout` + `clearTimeout`.
120
+ * Tests pass a fake that captures callbacks and fires them manually so
121
+ * they can assert on the before/after states without real clocks.
122
+ */
123
+ scheduleTimer?: (fn: () => void, ms: number) => TimerHandle
124
+ }
125
+
126
+ export interface CompleteTurnArgs {
127
+ chatId: string
128
+ threadId?: string
129
+ turnKey: string
130
+ /** Defaults to {@link PARENT_AGENT_ID}. */
131
+ agentId?: string
132
+ }
133
+
134
+ export interface PinManager {
135
+ /** Decide whether to pin based on an emit's metadata. Idempotent. */
136
+ considerPin(candidate: PinCandidate): void
137
+ /**
138
+ * Called from `onTurnComplete` (parent) or from `sub_agent_turn_end`
139
+ * (per-agent) — unpins the (turnKey, agentId) composite's pinned card.
140
+ * `agentId` defaults to {@link PARENT_AGENT_ID} for legacy single-card
141
+ * call sites.
142
+ */
143
+ completeTurn(args: CompleteTurnArgs): void
144
+ /**
145
+ * Catastrophic-cleanup helper. Unpins every pinned card under a turnKey
146
+ * — across all agentIds. Used when a parent turn ends without per-agent
147
+ * sub_agent_turn_end events arriving (bridge disconnect, gateway crash,
148
+ * forced shutdown). Distinct from `completeTurn`, which targets a single
149
+ * card.
150
+ */
151
+ completeAllForTurn(args: { chatId: string; threadId?: string; turnKey: string }): void
152
+ /**
153
+ * External hook. Unpins every currently-pinned (turnKey, agentId)
154
+ * matching the chat (and optional thread). Used by
155
+ * context-exhaustion / external cancellation paths that need to clear
156
+ * all active pins for a chat.
157
+ */
158
+ unpinForChat(chatId: string, threadId: number | undefined): void
159
+ /**
160
+ * Hook for the grammY `message:pinned_message` update. When Telegram
161
+ * auto-posts the "Clerk pinned ..." service message after a pin we
162
+ * made, the gateway calls this with the service message id and the id
163
+ * of the pinned message it wraps. The manager deletes the service
164
+ * message immediately if it matches one of our tracked pins.
165
+ */
166
+ captureServiceMessage(args: {
167
+ chatId: string
168
+ pinnedMessageId: number
169
+ serviceMessageId: number
170
+ }): void
171
+ /**
172
+ * Register a pin made outside the per-turn `considerPin()` path so
173
+ * `captureServiceMessage()` will recognise its service message and
174
+ * delete it. Used by the worker / sub-agent card (issue #94), which
175
+ * pins through the gateway directly rather than the progress-card
176
+ * pin candidate flow. Idempotent — calling twice with the same
177
+ * (chatId, messageId) is a no-op.
178
+ */
179
+ trackExternalPin(chatId: string, messageId: number): void
180
+ /**
181
+ * Drop an external pin from tracking. Call when the corresponding
182
+ * pinned message is unpinned/deleted by its owner so the manager
183
+ * doesn't keep a stale reference. Idempotent.
184
+ */
185
+ untrackExternalPin(chatId: string, messageId: number): void
186
+ /**
187
+ * Test-only: snapshot the unique turnKeys that currently have at
188
+ * least one pinned card. With per-agent cards this may collapse
189
+ * multiple composite entries down to a single turnKey.
190
+ */
191
+ pinnedTurnKeys(): ReadonlyArray<string>
192
+ /**
193
+ * Test-only: snapshot the agentIds currently pinned under a turnKey.
194
+ * Empty array when nothing is pinned for that turn.
195
+ */
196
+ pinnedAgentIds(turnKey: string): ReadonlyArray<string>
197
+ /**
198
+ * Test-only: look up the pinned message id for a (turnKey, agentId).
199
+ * `agentId` defaults to {@link PARENT_AGENT_ID} for backward compat.
200
+ */
201
+ pinnedMessageId(turnKey: string, agentId?: string): number | undefined
202
+ /**
203
+ * Snapshot of every pinned card currently tracked by the manager.
204
+ * Used by the gateway shutdown path (#689) to render a final
205
+ * "Restart interrupted" frame on each pinned card and unpin it
206
+ * synchronously, so cards don't freeze on "Working…" forever after
207
+ * a SIGTERM mid-turn.
208
+ */
209
+ pinnedEntries(): ReadonlyArray<{
210
+ turnKey: string
211
+ agentId: string
212
+ chatId: string
213
+ threadId?: string
214
+ messageId: number
215
+ }>
216
+ /**
217
+ * Test hook to await all in-flight pin/unpin promises. Production
218
+ * callers don't need this; tests can call it to drain the fire-and-
219
+ * forget `.catch()` chains before asserting on side effects.
220
+ */
221
+ drainInFlight(): Promise<void>
222
+ }
223
+
224
+ /**
225
+ * Composite key for the per-agent pin maps. Uses a `::` separator that
226
+ * cannot appear in a turnKey (which is `${chatId}:${threadId}:${seq}`)
227
+ * or in any agentId (JSONL filename stems are slug-safe).
228
+ */
229
+ function pinKey(turnKey: string, agentId: string): string {
230
+ return `${turnKey}::${agentId}`
231
+ }
232
+
233
+ export function createPinManager(deps: PinManagerDeps): PinManager {
234
+ const now = deps.now ?? Date.now
235
+ const log = deps.log ?? (() => {})
236
+ const pinDelayMs = deps.pinDelayMs ?? 0
237
+ const scheduleTimer: (fn: () => void, ms: number) => TimerHandle =
238
+ deps.scheduleTimer ??
239
+ ((fn, ms) => {
240
+ const t = setTimeout(fn, ms)
241
+ return { cancel: () => clearTimeout(t) }
242
+ })
243
+
244
+ // (turnKey, agentId) -> pinned message id. Populated only after a
245
+ // successful pin call returns; cleared on completeTurn for that
246
+ // composite.
247
+ const pinned = new Map<string, number>()
248
+ // Parallel to `pinned`: (turnKey, agentId) -> {chatId, threadId?}. Lets
249
+ // the shutdown flush (#689) reconstruct the chat coordinates without
250
+ // parsing the turnKey (which is ambiguous when no threadId is present:
251
+ // `chatId:seq` vs `chatId:threadId:seq`).
252
+ const pinnedMeta = new Map<string, { chatId: string; threadId?: string }>()
253
+ // (turnKey, agentId) -> pending pin state. Holds the candidate +
254
+ // timer handle while we wait pinDelayMs before actually calling the
255
+ // Telegram pin API. If completeTurn fires before the timer, we cancel
256
+ // and never pin — fast turns stay silent. Removed when the timer
257
+ // fires (moved to `pinned`) or when completeTurn cancels it.
258
+ const pendingPins = new Map<
259
+ string,
260
+ { chatId: string; threadId?: string; messageId: number; turnKey: string; agentId: string; timer: TimerHandle }
261
+ >()
262
+ // Composite keys whose unpin has already fired. Guards against
263
+ // duplicate completeTurn calls causing a second unpin.
264
+ const unpinned = new Set<string>()
265
+ // `${chatId}:${pinnedMessageId}` -> service-message id. Populated when
266
+ // the grammY `pinned_message` update handler forwards the wrapper
267
+ // message to us. We delete on capture; this map exists only so a late
268
+ // unpin path can also scrub the service message if the capture-delete
269
+ // somehow failed.
270
+ const serviceMessages = new Map<string, number>()
271
+ // Pins that the manager DIDN'T make through `considerPin()` but should
272
+ // still recognise so their "Clerk pinned …" service message gets
273
+ // deleted. Issue #94: the worker / sub-agent card pins via the gateway
274
+ // directly; without this set, captureServiceMessage would skip its
275
+ // service message and the user sees the system-message noise that the
276
+ // main card already suppresses. Keyed by `${chatId}:${messageId}`.
277
+ const externalPins = new Set<string>()
278
+ // Fire-and-forget promises we want tests to be able to drain.
279
+ const inFlight = new Set<Promise<unknown>>()
280
+
281
+ function serviceKey(chatId: string, pinnedMessageId: number): string {
282
+ return `${chatId}:${pinnedMessageId}`
283
+ }
284
+
285
+ function deleteServiceMessage(chatId: string, serviceMessageId: number): void {
286
+ if (!deps.deleteMessage) return
287
+ const p = deps.deleteMessage(chatId, serviceMessageId).catch((err: Error) => {
288
+ log(`telegram gateway: progress-card pin service-msg delete failed: ${err?.message ?? err}\n`)
289
+ })
290
+ track(p)
291
+ }
292
+
293
+ function track(p: Promise<unknown>): void {
294
+ inFlight.add(p)
295
+ // Remove on settle — including rejections, which are caught by the
296
+ // callers. Using `then(..., ...)` rather than `finally` so a late
297
+ // thrown error inside finally can't corrupt the tracking set.
298
+ p.then(
299
+ () => { inFlight.delete(p) },
300
+ () => { inFlight.delete(p) },
301
+ )
302
+ }
303
+
304
+ function doUnpin(turnKey: string, agentId: string, chatId: string, pinnedId: number): void {
305
+ const key = pinKey(turnKey, agentId)
306
+ if (unpinned.has(key)) return
307
+ unpinned.add(key)
308
+ log(`telegram gateway: progress-card: unpin turnKey=${turnKey} agentId=${agentId} msgId=${pinnedId}\n`)
309
+ pinned.delete(key)
310
+ pinnedMeta.delete(key)
311
+ const svcKey = serviceKey(chatId, pinnedId)
312
+ const svcId = serviceMessages.get(svcKey)
313
+ if (svcId != null) {
314
+ serviceMessages.delete(svcKey)
315
+ deleteServiceMessage(chatId, svcId)
316
+ }
317
+ const unpinStart = now()
318
+ const p = deps.unpin(chatId, pinnedId)
319
+ .then(() => {
320
+ const ms = now() - unpinStart
321
+ log(`telegram gateway: progress-card: unpin OK turnKey=${turnKey} agentId=${agentId} msgId=${pinnedId} durationMs=${ms}\n`)
322
+ })
323
+ .catch((err: Error) => {
324
+ const ms = now() - unpinStart
325
+ log(`telegram gateway: progress-card unpin failed turnKey=${turnKey} agentId=${agentId} msgId=${pinnedId} durationMs=${ms} error="${err?.message ?? err}"\n`)
326
+ })
327
+ .finally(() => {
328
+ // Keep the sidecar consistent whether the API call succeeded
329
+ // or failed — the sidecar exists to recover after we lose the
330
+ // in-memory map across a restart, so removing on unpin-attempt
331
+ // is the correct boundary.
332
+ if (deps.removePin) deps.removePin(chatId, pinnedId)
333
+ })
334
+ track(p)
335
+ }
336
+
337
+ function firePin(turnKey: string, agentId: string, chatId: string, threadId: string | undefined, messageId: number): void {
338
+ // Called when the pin-delay timer fires. Promote from pendingPins
339
+ // into pinned, then issue the Telegram pin API call.
340
+ const key = pinKey(turnKey, agentId)
341
+ pendingPins.delete(key)
342
+ if (pinned.has(key) || unpinned.has(key)) {
343
+ // Either we already pinned (shouldn't happen — timer is the only
344
+ // path that sets `pinned`) or the turn completed between scheduling
345
+ // and firing and the pending entry was cleared elsewhere. Bail.
346
+ return
347
+ }
348
+ pinned.set(key, messageId)
349
+ pinnedMeta.set(key, threadId != null ? { chatId, threadId } : { chatId })
350
+ log(`telegram gateway: progress-card: pinned turnKey=${turnKey} agentId=${agentId} msgId=${messageId}\n`)
351
+ if (deps.addPin) {
352
+ deps.addPin({
353
+ chatId,
354
+ messageId,
355
+ turnKey,
356
+ pinnedAt: now(),
357
+ agentId,
358
+ })
359
+ }
360
+ const pinStart = now()
361
+ const p = deps.pin(chatId, messageId, { disable_notification: true })
362
+ .then(() => {
363
+ const ms = now() - pinStart
364
+ log(`telegram gateway: progress-card: pin OK turnKey=${turnKey} agentId=${agentId} msgId=${messageId} durationMs=${ms}\n`)
365
+ })
366
+ .catch(
367
+ (err: Error) => {
368
+ const ms = now() - pinStart
369
+ const errMsg = err?.message ?? String(err)
370
+ const line = `telegram gateway: progress-card pin failed chatId=${chatId} msgId=${messageId} turnKey=${turnKey} agentId=${agentId} durationMs=${ms} error="${errMsg}"\n`
371
+ log(line)
372
+ console.warn(line.replace(/\n$/, ''))
373
+ // Pin API failed — drop from the in-memory map so a later
374
+ // unpin attempt doesn't fire `deps.unpin` for a message we
375
+ // never actually pinned. Do NOT add to `unpinned` — we never
376
+ // issued an unpin. Sidecar is also cleared for consistency.
377
+ pinned.delete(key)
378
+ pinnedMeta.delete(key)
379
+ if (deps.removePin) deps.removePin(chatId, messageId)
380
+ },
381
+ )
382
+ track(p)
383
+ }
384
+
385
+ return {
386
+ considerPin(c) {
387
+ if (!c.isFirstEmit) return
388
+ const agentId = c.agentId ?? PARENT_AGENT_ID
389
+ const key = pinKey(c.turnKey, agentId)
390
+ if (pinned.has(key)) return
391
+ if (pendingPins.has(key)) return
392
+ // Schedule the pin via the injected timer. Fast-turn suppression is
393
+ // owned upstream by the driver's `initialDelayMs` — by the time
394
+ // considerPin sees isFirstEmit=true the card has already been
395
+ // published, so pinDelayMs defaults to 0 (fire on next tick).
396
+ // The indirection remains so tests and callers can still override
397
+ // with a positive value if they want a pre-pin visual buffer.
398
+ const timer = scheduleTimer(() => {
399
+ firePin(c.turnKey, agentId, c.chatId, c.threadId, c.messageId)
400
+ }, pinDelayMs)
401
+ pendingPins.set(key, {
402
+ chatId: c.chatId,
403
+ ...(c.threadId != null ? { threadId: c.threadId } : {}),
404
+ messageId: c.messageId,
405
+ turnKey: c.turnKey,
406
+ agentId,
407
+ timer,
408
+ })
409
+ },
410
+
411
+ completeTurn({ chatId, turnKey, agentId }) {
412
+ const aid = agentId ?? PARENT_AGENT_ID
413
+ const key = pinKey(turnKey, aid)
414
+ // Fast-turn path: if the pin is still pending, cancel the timer
415
+ // and we're done — no pin ever landed, no unpin needed.
416
+ const pending = pendingPins.get(key)
417
+ if (pending != null) {
418
+ pending.timer.cancel()
419
+ pendingPins.delete(key)
420
+ }
421
+ const pinnedId = pinned.get(key)
422
+ if (pinnedId != null) doUnpin(turnKey, aid, chatId, pinnedId)
423
+ // Once the turn is complete we never see the same composite again
424
+ // (driver generates a fresh sequence for the turn, agentIds are
425
+ // stable per agent lifetime). Clearing the flag keeps the set from
426
+ // growing unbounded over a long-running gateway.
427
+ unpinned.delete(key)
428
+ },
429
+
430
+ completeAllForTurn({ chatId, turnKey }) {
431
+ // Snapshot composites for this turn before mutating — pendingPins
432
+ // and pinned will both shrink as we go.
433
+ const matchingPending: Array<{ key: string; agentId: string }> = []
434
+ for (const [key, entry] of pendingPins) {
435
+ if (entry.turnKey === turnKey) matchingPending.push({ key, agentId: entry.agentId })
436
+ }
437
+ for (const { key } of matchingPending) {
438
+ const pending = pendingPins.get(key)
439
+ if (pending != null) {
440
+ pending.timer.cancel()
441
+ pendingPins.delete(key)
442
+ }
443
+ }
444
+ const matchingPinned: Array<{ agentId: string; pinnedId: number }> = []
445
+ for (const [key, pinnedId] of pinned) {
446
+ // Composite keys are `${turnKey}::${agentId}` — split on `::`.
447
+ const sep = key.lastIndexOf('::')
448
+ if (sep < 0) continue
449
+ const tk = key.slice(0, sep)
450
+ if (tk !== turnKey) continue
451
+ const agentId = key.slice(sep + 2)
452
+ matchingPinned.push({ agentId, pinnedId })
453
+ }
454
+ for (const { agentId, pinnedId } of matchingPinned) {
455
+ doUnpin(turnKey, agentId, chatId, pinnedId)
456
+ }
457
+ // Mirror completeTurn's housekeeping: clear unpinned-set entries
458
+ // for this turn so a future reuse (unlikely) starts fresh.
459
+ for (const key of [...unpinned]) {
460
+ const sep = key.lastIndexOf('::')
461
+ if (sep < 0) continue
462
+ if (key.slice(0, sep) === turnKey) unpinned.delete(key)
463
+ }
464
+ },
465
+
466
+ unpinForChat(chatId, threadId) {
467
+ const base = threadId != null ? `${chatId}:${threadId}` : chatId
468
+ // Cancel any pending (not-yet-fired) timers for this chat/thread
469
+ // first — otherwise they'd pin after we thought we cleaned up.
470
+ const pendingMatching: string[] = []
471
+ for (const [key, entry] of pendingPins) {
472
+ if (entry.turnKey.startsWith(`${base}:`)) pendingMatching.push(key)
473
+ }
474
+ for (const key of pendingMatching) {
475
+ const pending = pendingPins.get(key)
476
+ if (pending != null) {
477
+ pending.timer.cancel()
478
+ pendingPins.delete(key)
479
+ }
480
+ }
481
+ // Snapshot the entries so doUnpin's map mutation doesn't invalidate
482
+ // iteration mid-loop.
483
+ const matching: Array<{ turnKey: string; agentId: string; pinnedId: number }> = []
484
+ for (const [key, pinnedId] of pinned) {
485
+ const sep = key.lastIndexOf('::')
486
+ if (sep < 0) continue
487
+ const tk = key.slice(0, sep)
488
+ if (!tk.startsWith(`${base}:`)) continue
489
+ const agentId = key.slice(sep + 2)
490
+ matching.push({ turnKey: tk, agentId, pinnedId })
491
+ }
492
+ for (const { turnKey, agentId, pinnedId } of matching) {
493
+ doUnpin(turnKey, agentId, chatId, pinnedId)
494
+ }
495
+ },
496
+
497
+ captureServiceMessage({ chatId, pinnedMessageId, serviceMessageId }) {
498
+ // Only act on service messages that wrap one of our tracked pins —
499
+ // otherwise we'd be deleting arbitrary pin-service messages in the
500
+ // chat, which could include user-initiated pins.
501
+ //
502
+ // We match against two sets: per-turn progress-card pins (managed
503
+ // through `considerPin`) AND externally-registered pins (the
504
+ // worker / sub-agent card, registered via `trackExternalPin` —
505
+ // see issue #94).
506
+ let matched = false
507
+ for (const [, msgId] of pinned) {
508
+ if (msgId === pinnedMessageId) { matched = true; break }
509
+ }
510
+ if (!matched && externalPins.has(serviceKey(chatId, pinnedMessageId))) {
511
+ matched = true
512
+ }
513
+ if (!matched) return
514
+ const key = serviceKey(chatId, pinnedMessageId)
515
+ serviceMessages.set(key, serviceMessageId)
516
+ deleteServiceMessage(chatId, serviceMessageId)
517
+ // Also drop the tracked id — if the delete succeeded there's
518
+ // nothing left to scrub on unpin. If it failed the catch logged it
519
+ // and a retry on unpin wouldn't help (Telegram pin-service messages
520
+ // don't age back into existence).
521
+ serviceMessages.delete(key)
522
+ },
523
+
524
+ trackExternalPin(chatId, messageId) {
525
+ externalPins.add(serviceKey(chatId, messageId))
526
+ },
527
+
528
+ untrackExternalPin(chatId, messageId) {
529
+ externalPins.delete(serviceKey(chatId, messageId))
530
+ },
531
+
532
+ pinnedTurnKeys() {
533
+ const seen = new Set<string>()
534
+ for (const key of pinned.keys()) {
535
+ const sep = key.lastIndexOf('::')
536
+ if (sep < 0) continue
537
+ seen.add(key.slice(0, sep))
538
+ }
539
+ return [...seen]
540
+ },
541
+
542
+ pinnedAgentIds(turnKey) {
543
+ const out: string[] = []
544
+ for (const key of pinned.keys()) {
545
+ const sep = key.lastIndexOf('::')
546
+ if (sep < 0) continue
547
+ if (key.slice(0, sep) !== turnKey) continue
548
+ out.push(key.slice(sep + 2))
549
+ }
550
+ return out
551
+ },
552
+
553
+ pinnedMessageId(turnKey, agentId) {
554
+ return pinned.get(pinKey(turnKey, agentId ?? PARENT_AGENT_ID))
555
+ },
556
+
557
+ pinnedEntries() {
558
+ const out: Array<{
559
+ turnKey: string
560
+ agentId: string
561
+ chatId: string
562
+ threadId?: string
563
+ messageId: number
564
+ }> = []
565
+ for (const [key, messageId] of pinned) {
566
+ const sep = key.lastIndexOf('::')
567
+ if (sep < 0) continue
568
+ const turnKey = key.slice(0, sep)
569
+ const agentId = key.slice(sep + 2)
570
+ const meta = pinnedMeta.get(key)
571
+ if (meta == null) continue
572
+ out.push({
573
+ turnKey,
574
+ agentId,
575
+ chatId: meta.chatId,
576
+ ...(meta.threadId != null ? { threadId: meta.threadId } : {}),
577
+ messageId,
578
+ })
579
+ }
580
+ return out
581
+ },
582
+
583
+ async drainInFlight() {
584
+ // Copy so we don't race with track() adding more while we await.
585
+ const snapshot = [...inFlight]
586
+ await Promise.allSettled(snapshot)
587
+ },
588
+ }
589
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Progress-card pin watchdog.
3
+ *
4
+ * The pin manager (progress-card-pin-manager.ts) owns the pin/unpin
5
+ * sequence at turn boundaries. Between those boundaries, nothing in
6
+ * our code path re-checks whether the pin Telegram actually shows the
7
+ * user still matches the one we pinned — but in practice it can
8
+ * drift:
9
+ *
10
+ * - Another user (or the bot itself via a different surface) pins
11
+ * a message, displacing ours. Telegram keeps ours in history but
12
+ * the chat header now points elsewhere.
13
+ * - A stale early-unpin fires on a pending turn (the bug this
14
+ * watchdog backstops) — we've removed the known code paths but
15
+ * want defense in depth.
16
+ * - The user manually unpins mid-turn.
17
+ *
18
+ * The watchdog runs on every progress-card heartbeat emit. It is
19
+ * rate-limited per turnKey (default 30s) so we don't hammer
20
+ * getChat. When it sees a mismatch, it re-pins the card so the user
21
+ * keeps seeing "work in progress" at the top of the chat until the
22
+ * turn actually completes.
23
+ *
24
+ * Contract (covered by progress-card-pin-watchdog.test.ts):
25
+ *
26
+ * verify({ chatId, turnKey, expectedMessageId })
27
+ * - First call for a turnKey always probes.
28
+ * - Subsequent calls within `intervalMs` are silent no-ops.
29
+ * - On probe: if getCurrentPinned returns the expected id, no
30
+ * action. Otherwise re-pin. Errors from either API call are
31
+ * caught and logged — never thrown.
32
+ *
33
+ * clear(turnKey)
34
+ * - Drops the rate-limit timestamp so a future turn reusing the
35
+ * key starts fresh. Called from onTurnComplete.
36
+ */
37
+
38
+ export interface PinWatchdogDeps {
39
+ /**
40
+ * Read the id of whatever Telegram currently shows pinned at the
41
+ * top of `chatId`. Implementations typically call
42
+ * `bot.api.getChat(chatId).pinned_message?.message_id`. Return
43
+ * undefined when nothing is pinned.
44
+ */
45
+ getCurrentPinned: (chatId: string) => Promise<number | undefined>
46
+ /** Re-pin if we detect a mismatch. Mirrors `pinChatMessage`. */
47
+ pin: (
48
+ chatId: string,
49
+ messageId: number,
50
+ opts?: { disable_notification?: boolean },
51
+ ) => Promise<unknown>
52
+ /** Minimum interval between getChat probes per turnKey. Default 30s. */
53
+ intervalMs?: number
54
+ /** Clock injection for test determinism. Defaults to `Date.now`. */
55
+ now?: () => number
56
+ /** Receives log lines with trailing newline. */
57
+ log?: (line: string) => void
58
+ }
59
+
60
+ export interface PinWatchdog {
61
+ verify(args: {
62
+ chatId: string
63
+ turnKey: string
64
+ expectedMessageId: number
65
+ }): Promise<void>
66
+ /** Clear state for a turnKey (call on turn completion). */
67
+ clear(turnKey: string): void
68
+ }
69
+
70
+ export function createPinWatchdog(deps: PinWatchdogDeps): PinWatchdog {
71
+ const interval = deps.intervalMs ?? 30_000
72
+ const now = deps.now ?? Date.now
73
+ const log = deps.log ?? (() => {})
74
+ // turnKey -> last probe timestamp. Cleared on turn completion.
75
+ const lastProbedAt = new Map<string, number>()
76
+
77
+ return {
78
+ async verify({ chatId, turnKey, expectedMessageId }) {
79
+ const last = lastProbedAt.get(turnKey)
80
+ const t = now()
81
+ // First call always probes. Subsequent calls wait for the
82
+ // interval so a chatty turn doesn't saturate getChat.
83
+ if (last != null && t - last < interval) return
84
+ lastProbedAt.set(turnKey, t)
85
+ try {
86
+ const currentPinned = await deps.getCurrentPinned(chatId)
87
+ if (currentPinned === expectedMessageId) return
88
+ await deps.pin(chatId, expectedMessageId, { disable_notification: true })
89
+ } catch (err) {
90
+ const msg = err instanceof Error ? err.message : String(err)
91
+ log(`telegram gateway: progress-card watchdog failed: ${msg}\n`)
92
+ }
93
+ },
94
+ clear(turnKey) {
95
+ lastProbedAt.delete(turnKey)
96
+ },
97
+ }
98
+ }