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,203 @@
1
+ /**
2
+ * Unit tests for the pure helpers behind the `ask_user` MCP tool (#574).
3
+ *
4
+ * Coverage targets:
5
+ * - validateAskUserArgs: every error path + every clamp + every
6
+ * valid edge case.
7
+ * - encodeAskCallback / decodeAskCallback: round-trip, length budget,
8
+ * malformed inputs, prefix non-match returns null (caller falls
9
+ * through to next dispatcher arm).
10
+ * - generateAskId: shape correct, low collision rate.
11
+ *
12
+ * The runtime side (executor + grammY + TTL timer + callback
13
+ * resolution) lives in gateway.ts and is exercised by the integration
14
+ * tests in real-gateway harness — these tests stay pure / fast.
15
+ */
16
+
17
+ import { describe, it, expect } from 'bun:test'
18
+ import {
19
+ validateAskUserArgs,
20
+ generateAskId,
21
+ encodeAskCallback,
22
+ decodeAskCallback,
23
+ ASK_USER_DEFAULT_TIMEOUT_MS,
24
+ ASK_USER_MAX_TIMEOUT_MS,
25
+ ASK_USER_MIN_TIMEOUT_MS,
26
+ ASK_USER_MAX_OPTIONS,
27
+ } from '../ask-user.js'
28
+
29
+ describe('validateAskUserArgs — required fields', () => {
30
+ it('accepts the minimal valid input (chat_id + question + 2 options)', () => {
31
+ const r = validateAskUserArgs({ chat_id: '123', question: 'OK?', options: ['Yes', 'No'] })
32
+ expect(r.chatId).toBe('123')
33
+ expect(r.question).toBe('OK?')
34
+ expect(r.options).toEqual(['Yes', 'No'])
35
+ expect(r.threadId).toBeUndefined()
36
+ expect(r.replyTo).toBeUndefined()
37
+ expect(r.timeoutMs).toBe(ASK_USER_DEFAULT_TIMEOUT_MS)
38
+ })
39
+
40
+ it('rejects empty chat_id', () => {
41
+ expect(() => validateAskUserArgs({ chat_id: '', question: 'q', options: ['a', 'b'] }))
42
+ .toThrow(/chat_id is required/)
43
+ })
44
+
45
+ it('rejects missing question', () => {
46
+ expect(() => validateAskUserArgs({ chat_id: '1', question: ' ', options: ['a', 'b'] }))
47
+ .toThrow(/question is required/)
48
+ })
49
+
50
+ it('rejects question over 3500 chars (forced-choice should be short)', () => {
51
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'x'.repeat(3501), options: ['a', 'b'] }))
52
+ .toThrow(/question too long/)
53
+ })
54
+ })
55
+
56
+ describe('validateAskUserArgs — options', () => {
57
+ it('rejects fewer than 2 options', () => {
58
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['only'] }))
59
+ .toThrow(/at least 2/)
60
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: [] }))
61
+ .toThrow(/at least 2/)
62
+ })
63
+
64
+ it(`accepts exactly ${ASK_USER_MAX_OPTIONS} options`, () => {
65
+ const opts = Array.from({ length: ASK_USER_MAX_OPTIONS }, (_, i) => `o${i}`)
66
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: opts })
67
+ expect(r.options).toEqual(opts)
68
+ })
69
+
70
+ it(`rejects more than ${ASK_USER_MAX_OPTIONS} options`, () => {
71
+ const opts = Array.from({ length: ASK_USER_MAX_OPTIONS + 1 }, (_, i) => `o${i}`)
72
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: opts }))
73
+ .toThrow(/too many options/)
74
+ })
75
+
76
+ it('rejects empty option string', () => {
77
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['Yes', ''] }))
78
+ .toThrow(/options\[1\] must be a non-empty string/)
79
+ })
80
+
81
+ it('rejects whitespace-only option', () => {
82
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['Yes', ' '] }))
83
+ .toThrow(/options\[1\] must be a non-empty string/)
84
+ })
85
+
86
+ it('rejects option label longer than 64 chars', () => {
87
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['Yes', 'x'.repeat(65)] }))
88
+ .toThrow(/options\[1\] too long/)
89
+ })
90
+ })
91
+
92
+ describe('validateAskUserArgs — optional fields', () => {
93
+ it('parses message_thread_id to integer', () => {
94
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], message_thread_id: '42' })
95
+ expect(r.threadId).toBe(42)
96
+ })
97
+
98
+ it('rejects non-positive message_thread_id', () => {
99
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], message_thread_id: '0' }))
100
+ .toThrow(/positive integer/)
101
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], message_thread_id: '-5' }))
102
+ .toThrow(/positive integer/)
103
+ })
104
+
105
+ it('parses reply_to to integer', () => {
106
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], reply_to: '99' })
107
+ expect(r.replyTo).toBe(99)
108
+ })
109
+ })
110
+
111
+ describe('validateAskUserArgs — timeout clamping', () => {
112
+ it('uses default when timeout_ms is omitted', () => {
113
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'] })
114
+ expect(r.timeoutMs).toBe(ASK_USER_DEFAULT_TIMEOUT_MS)
115
+ })
116
+
117
+ it('floors timeouts below the minimum', () => {
118
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], timeout_ms: 100 })
119
+ expect(r.timeoutMs).toBe(ASK_USER_MIN_TIMEOUT_MS)
120
+ })
121
+
122
+ it('caps timeouts above the maximum', () => {
123
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], timeout_ms: 99_999_999 })
124
+ expect(r.timeoutMs).toBe(ASK_USER_MAX_TIMEOUT_MS)
125
+ })
126
+
127
+ it('passes mid-range timeouts through unchanged', () => {
128
+ const r = validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], timeout_ms: 60_000 })
129
+ expect(r.timeoutMs).toBe(60_000)
130
+ })
131
+
132
+ it('rejects non-numeric timeout_ms', () => {
133
+ expect(() => validateAskUserArgs({ chat_id: '1', question: 'q', options: ['a', 'b'], timeout_ms: NaN }))
134
+ .toThrow(/timeout_ms must be a number/)
135
+ })
136
+ })
137
+
138
+ describe('generateAskId', () => {
139
+ it('returns 8 lowercase hex chars', () => {
140
+ for (let i = 0; i < 50; i++) {
141
+ const id = generateAskId()
142
+ expect(id).toMatch(/^[0-9a-f]{8}$/)
143
+ }
144
+ })
145
+
146
+ it('produces distinct ids across many calls (no obvious bias)', () => {
147
+ const seen = new Set<string>()
148
+ for (let i = 0; i < 1000; i++) seen.add(generateAskId())
149
+ // 32 bits of entropy: collision in 1000 draws would be a real bug.
150
+ expect(seen.size).toBeGreaterThanOrEqual(999)
151
+ })
152
+ })
153
+
154
+ describe('encodeAskCallback / decodeAskCallback round-trip', () => {
155
+ it('round-trips for every valid index', () => {
156
+ const id = '1a2b3c4d'
157
+ for (let i = 0; i < ASK_USER_MAX_OPTIONS; i++) {
158
+ const data = encodeAskCallback(id, i)
159
+ const decoded = decodeAskCallback(data)
160
+ expect(decoded).toEqual({ askId: id, idx: i })
161
+ }
162
+ })
163
+
164
+ it('encoded callback stays under Telegram 64-byte budget', () => {
165
+ const id = generateAskId()
166
+ for (let i = 0; i < ASK_USER_MAX_OPTIONS; i++) {
167
+ const data = encodeAskCallback(id, i)
168
+ expect(Buffer.byteLength(data, 'utf-8')).toBeLessThanOrEqual(64)
169
+ }
170
+ })
171
+ })
172
+
173
+ describe('encodeAskCallback — input validation', () => {
174
+ it('rejects malformed askId', () => {
175
+ expect(() => encodeAskCallback('NOTHEX', 0)).toThrow(/invalid askId/)
176
+ expect(() => encodeAskCallback('1a2b3c4', 0)).toThrow(/invalid askId/) // 7 chars
177
+ expect(() => encodeAskCallback('1a2b3c4d5', 0)).toThrow(/invalid askId/) // 9 chars
178
+ expect(() => encodeAskCallback('1A2B3C4D', 0)).toThrow(/invalid askId/) // uppercase
179
+ })
180
+
181
+ it('rejects out-of-range index', () => {
182
+ expect(() => encodeAskCallback('1a2b3c4d', -1)).toThrow(/invalid option index/)
183
+ expect(() => encodeAskCallback('1a2b3c4d', ASK_USER_MAX_OPTIONS)).toThrow(/invalid option index/)
184
+ expect(() => encodeAskCallback('1a2b3c4d', 1.5)).toThrow(/invalid option index/)
185
+ })
186
+ })
187
+
188
+ describe('decodeAskCallback — non-match falls through', () => {
189
+ it('returns null for non-aq prefixes (caller dispatches to next arm)', () => {
190
+ expect(decodeAskCallback('perm:allow:abcde')).toBeNull()
191
+ expect(decodeAskCallback('op:dismiss:agent')).toBeNull()
192
+ expect(decodeAskCallback('vd:unlock:key')).toBeNull()
193
+ expect(decodeAskCallback('')).toBeNull()
194
+ })
195
+
196
+ it('returns null for malformed aq: data', () => {
197
+ expect(decodeAskCallback('aq:0:short')).toBeNull()
198
+ expect(decodeAskCallback('aq:abc:1a2b3c4d')).toBeNull() // non-numeric idx
199
+ expect(decodeAskCallback('aq::1a2b3c4d')).toBeNull() // empty idx
200
+ expect(decodeAskCallback('aq:9:1a2b3c4d')).toBeNull() // out of range (>=8)
201
+ expect(decodeAskCallback('aq:0:1a2b3c4d:extra')).toBeNull() // trailing junk
202
+ })
203
+ })
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Exhaustive tests for the attachment-path sanitizer.
3
+ *
4
+ * Covers the specific path-traversal and adversarial-input shapes that
5
+ * the prior inline implementations either handled inconsistently or
6
+ * missed. Complements the integration tests (which use the bot handler
7
+ * end-to-end) — this file verifies the building block in isolation.
8
+ */
9
+
10
+ import { describe, it, expect } from 'vitest'
11
+ import { mkdtempSync, mkdirSync } from 'fs'
12
+ import { tmpdir } from 'os'
13
+ import { join, sep } from 'path'
14
+ import {
15
+ buildAttachmentPath,
16
+ sanitizeExtension,
17
+ sanitizeUniqueId,
18
+ extractExtension,
19
+ assertInsideInbox,
20
+ } from '../attachment-path.js'
21
+
22
+ describe('sanitizeExtension', () => {
23
+ it.each([
24
+ ['jpg', 'jpg'],
25
+ ['png', 'png'],
26
+ ['mp4', 'mp4'],
27
+ ['tar.gz', 'targz'], // dots stripped
28
+ ['../../etc', 'etc'], // traversal attempt stripped
29
+ ['sh; rm -rf', 'shrmrf'], // shell metacharacters stripped
30
+ ['jpg/../etc', 'jpgetc'], // mixed stripped
31
+ ['', 'bin'], // empty → fallback
32
+ [' ', 'bin'], // whitespace-only → fallback
33
+ ['...', 'bin'], // punctuation-only → fallback
34
+ ['JPG', 'JPG'], // case preserved
35
+ ['a1b2c3', 'a1b2c3'],
36
+ ])('%j → %j', (input, expected) => {
37
+ expect(sanitizeExtension(input)).toBe(expected)
38
+ })
39
+
40
+ it('undefined → fallback', () => {
41
+ expect(sanitizeExtension(undefined)).toBe('bin')
42
+ })
43
+ })
44
+
45
+ describe('sanitizeUniqueId', () => {
46
+ it.each([
47
+ ['AgACAgI123', 'AgACAgI123'],
48
+ ['abc_DEF-123', 'abc_DEF-123'], // underscore + dash allowed
49
+ ['../../etc', 'etc'], // dots and slashes stripped
50
+ ['id/with/slash', 'idwithslash'],
51
+ ['id\\back\\slash', 'idbackslash'],
52
+ ['id with space', 'idwithspace'],
53
+ ['id;rm -rf /', 'idrm-rf'], // dash preserved, shell stripped
54
+ ['', 'dl'],
55
+ ['...', 'dl'],
56
+ ['%00null', '00null'],
57
+ ['a.b.c', 'abc'],
58
+ ])('%j → %j', (input, expected) => {
59
+ expect(sanitizeUniqueId(input)).toBe(expected)
60
+ })
61
+
62
+ it('undefined → fallback', () => {
63
+ expect(sanitizeUniqueId(undefined)).toBe('dl')
64
+ })
65
+ })
66
+
67
+ describe('extractExtension', () => {
68
+ it.each([
69
+ ['photos/123.jpg', 'jpg'],
70
+ ['documents/file.png', 'png'],
71
+ ['a/b.c/d.tar.gz', 'gz'], // last dot wins, sanitized
72
+ ['no-dot-here', 'bin'], // no dot → fallback
73
+ ['', 'bin'],
74
+ ['.hiddenfile', 'hiddenfile'], // leading dot → that's the extension
75
+ ['file.', 'bin'], // trailing dot, empty ext → fallback
76
+ ['photos/123.JPG', 'JPG'], // case preserved
77
+ ])('%j → %j', (input, expected) => {
78
+ expect(extractExtension(input)).toBe(expected)
79
+ })
80
+
81
+ it('undefined → fallback', () => {
82
+ expect(extractExtension(undefined)).toBe('bin')
83
+ })
84
+ })
85
+
86
+ describe('buildAttachmentPath', () => {
87
+ it('composes a stable filename under inboxDir', () => {
88
+ const path = buildAttachmentPath({
89
+ inboxDir: '/inbox',
90
+ telegramFilePath: 'photos/123.jpg',
91
+ fileUniqueId: 'AgACAgI456',
92
+ now: 1700000000000,
93
+ })
94
+ expect(path).toBe(join('/inbox', '1700000000000-AgACAgI456.jpg'))
95
+ })
96
+
97
+ it('sanitizes adversarial file_unique_id', () => {
98
+ const path = buildAttachmentPath({
99
+ inboxDir: '/inbox',
100
+ telegramFilePath: 'photos/123.jpg',
101
+ fileUniqueId: '../../etc/passwd',
102
+ now: 1700000000000,
103
+ })
104
+ // The traversal chars are stripped; filename is plain text.
105
+ expect(path).toBe(join('/inbox', '1700000000000-etcpasswd.jpg'))
106
+ expect(path).not.toContain('..')
107
+ })
108
+
109
+ it('sanitizes adversarial extension', () => {
110
+ const path = buildAttachmentPath({
111
+ inboxDir: '/inbox',
112
+ telegramFilePath: 'file.sh; rm -rf /',
113
+ fileUniqueId: 'x',
114
+ now: 1700000000000,
115
+ })
116
+ expect(path).toBe(join('/inbox', '1700000000000-x.shrmrf'))
117
+ })
118
+
119
+ it('handles missing file_path gracefully', () => {
120
+ const path = buildAttachmentPath({
121
+ inboxDir: '/inbox',
122
+ telegramFilePath: undefined,
123
+ fileUniqueId: 'x',
124
+ now: 1700000000000,
125
+ })
126
+ expect(path).toBe(join('/inbox', '1700000000000-x.bin'))
127
+ })
128
+
129
+ it('handles missing file_unique_id gracefully', () => {
130
+ const path = buildAttachmentPath({
131
+ inboxDir: '/inbox',
132
+ telegramFilePath: 'photos/123.jpg',
133
+ fileUniqueId: undefined,
134
+ now: 1700000000000,
135
+ })
136
+ expect(path).toBe(join('/inbox', '1700000000000-dl.jpg'))
137
+ })
138
+
139
+ it('null-byte in file_unique_id is stripped', () => {
140
+ const path = buildAttachmentPath({
141
+ inboxDir: '/inbox',
142
+ telegramFilePath: 'a.jpg',
143
+ fileUniqueId: 'id\u0000poison',
144
+ now: 1,
145
+ })
146
+ expect(path).toBe(join('/inbox', '1-idpoison.jpg'))
147
+ expect(path.includes('\u0000')).toBe(false)
148
+ })
149
+ })
150
+
151
+ describe('assertInsideInbox', () => {
152
+ const inbox = mkdtempSync(join(tmpdir(), 'inbox-test-'))
153
+
154
+ it('allows a path directly inside inbox', () => {
155
+ expect(() => assertInsideInbox(inbox, join(inbox, 'safe.jpg'))).not.toThrow()
156
+ })
157
+
158
+ it('allows the inbox itself', () => {
159
+ expect(() => assertInsideInbox(inbox, inbox)).not.toThrow()
160
+ })
161
+
162
+ it('rejects a parent-dir path', () => {
163
+ expect(() => assertInsideInbox(inbox, join(inbox, '..', 'evil.jpg'))).toThrow(
164
+ /escape/,
165
+ )
166
+ })
167
+
168
+ it('rejects a sibling path', () => {
169
+ const sibling = join(inbox, '..', 'sibling.jpg')
170
+ expect(() => assertInsideInbox(inbox, sibling)).toThrow()
171
+ })
172
+
173
+ it('rejects an absolute path unrelated to inbox', () => {
174
+ expect(() => assertInsideInbox(inbox, '/etc/passwd')).toThrow()
175
+ })
176
+
177
+ it('rejects a path whose prefix matches but is a different directory (ambiguity guard)', () => {
178
+ // `/tmp/inbox-test-ABC` vs `/tmp/inbox-test-ABC-other` — the former
179
+ // should NOT accept paths in the latter. This catches a naive
180
+ // `startsWith` check without the path separator.
181
+ const similar = inbox + '-other'
182
+ mkdirSync(similar, { recursive: true })
183
+ expect(() => assertInsideInbox(inbox, join(similar, 'x.jpg'))).toThrow()
184
+ })
185
+ })
186
+
187
+ describe('end-to-end: build + assert', () => {
188
+ it('adversarial inputs still produce inbox-safe paths', () => {
189
+ const tempInbox = mkdtempSync(join(tmpdir(), 'inbox-e2e-'))
190
+ const path = buildAttachmentPath({
191
+ inboxDir: tempInbox,
192
+ telegramFilePath: '../../../etc/passwd',
193
+ fileUniqueId: '../../root/.ssh/id_rsa',
194
+ now: 1700000000000,
195
+ })
196
+ expect(() => assertInsideInbox(tempInbox, path)).not.toThrow()
197
+ expect(path.startsWith(tempInbox + sep)).toBe(true)
198
+ })
199
+ })
@@ -0,0 +1,118 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ buildDashboardText,
4
+ formatRateLimitTier,
5
+ type DashboardState,
6
+ type DashboardSlot,
7
+ } from "../auth-dashboard";
8
+
9
+ function slot(o: Partial<DashboardSlot> = {}): DashboardSlot {
10
+ return { slot: "default", active: false, health: "healthy", quotaExhaustedUntil: null, fiveHourPct: null, sevenDayPct: null, ...o };
11
+ }
12
+
13
+ /**
14
+ * 2026-04-22 — account-identity surface.
15
+ *
16
+ * Context: a user reauths an agent onto their Max 20x account, but the
17
+ * OAuth browser flow gets hijacked by Telegram's in-app WebView (which
18
+ * uses a separate cookie jar from their main browser) and the saved
19
+ * token ends up for a different account (e.g. a Max 5x) instead. The
20
+ * dashboard header showed 'Plan: max' \u2014 indistinguishable between
21
+ * 5x and 20x \u2014 so the mismatch was silent until the user hit a quota wall
22
+ * hours later.
23
+ *
24
+ * Fix: surface the full `rateLimitTier` string on the dashboard so a
25
+ * wrong-account reauth is IMMEDIATELY visible. User expected max_20x,
26
+ * sees max_5x, acts.
27
+ *
28
+ * Pair fixes (out of scope for these tests but covered in the PR):
29
+ * - Auth response now includes a \ud83d\udccb Copy URL button so the user can
30
+ * paste into their main browser instead of Telegram's WebView.
31
+ * - Auth response text includes a tip about the in-app-browser pitfall.
32
+ */
33
+
34
+ describe("formatRateLimitTier", () => {
35
+ it("shortens default_claude_max_5x to max_5x", () => {
36
+ expect(formatRateLimitTier("default_claude_max_5x")).toBe("max_5x");
37
+ });
38
+
39
+ it("shortens default_claude_max_20x to max_20x", () => {
40
+ expect(formatRateLimitTier("default_claude_max_20x")).toBe("max_20x");
41
+ });
42
+
43
+ it("shortens default_claude_pro to pro", () => {
44
+ expect(formatRateLimitTier("default_claude_pro")).toBe("pro");
45
+ });
46
+
47
+ it("passes unknown tiers through unchanged", () => {
48
+ // We don't pretend to understand every future tier string. Passthrough
49
+ // means a new Anthropic tier name is visible verbatim until we
50
+ // update the formatter.
51
+ expect(formatRateLimitTier("team_custom_42")).toBe("team_custom_42");
52
+ expect(formatRateLimitTier("enterprise_unlimited")).toBe("enterprise_unlimited");
53
+ });
54
+
55
+ it("handles empty/null-ish input gracefully", () => {
56
+ expect(formatRateLimitTier("")).toBe("");
57
+ });
58
+ });
59
+
60
+ describe("dashboard header surfaces rateLimitTier when present", () => {
61
+ const base: DashboardState = {
62
+ agent: "lawgpt",
63
+ bankId: "lawgpt",
64
+ plan: "max",
65
+ slots: [slot({ active: true })],
66
+ quotaHot: false,
67
+ };
68
+
69
+ it("shows max_20x when on the bigger plan", () => {
70
+ const text = buildDashboardText({ ...base, rateLimitTier: "default_claude_max_20x" });
71
+ expect(text).toContain("Plan: <b>max_20x</b>");
72
+ // Should NOT just say 'max' \u2014 that's the ambiguous label that
73
+ // hid the account mismatch in the incident.
74
+ expect(text).not.toContain("Plan: <b>max</b>");
75
+ });
76
+
77
+ it("shows max_5x when on the smaller plan", () => {
78
+ const text = buildDashboardText({ ...base, rateLimitTier: "default_claude_max_5x" });
79
+ expect(text).toContain("Plan: <b>max_5x</b>");
80
+ });
81
+
82
+ it("falls back to plan label when rateLimitTier missing", () => {
83
+ const text = buildDashboardText({ ...base, rateLimitTier: null });
84
+ expect(text).toContain("Plan: <b>max</b>");
85
+ });
86
+
87
+ it("falls back to plan label when rateLimitTier undefined", () => {
88
+ const text = buildDashboardText({ ...base });
89
+ expect(text).toContain("Plan: <b>max</b>");
90
+ });
91
+
92
+ it("omits Plan: when neither tier nor plan are known", () => {
93
+ const text = buildDashboardText({ ...base, plan: null, rateLimitTier: null });
94
+ expect(text).not.toContain("Plan:");
95
+ expect(text).toContain("Bank: <code>lawgpt</code>");
96
+ });
97
+
98
+ it("escapes HTML in tier (injection guard)", () => {
99
+ const text = buildDashboardText({
100
+ ...base,
101
+ rateLimitTier: "<script>alert(1)</script>",
102
+ });
103
+ expect(text).not.toContain("<script>");
104
+ expect(text).toContain("&lt;script&gt;");
105
+ });
106
+
107
+ it("pair assertion: user can distinguish 5x from 20x without hunting", () => {
108
+ // Regression anchor: this was the exact confusion in the incident.
109
+ // The user saw 'Plan: max' for both accounts and couldn't tell
110
+ // which got authorized. With the tier string present, 5x and 20x
111
+ // look different in a glance.
112
+ const fivex = buildDashboardText({ ...base, rateLimitTier: "default_claude_max_5x" });
113
+ const twentyx = buildDashboardText({ ...base, rateLimitTier: "default_claude_max_20x" });
114
+ expect(fivex).not.toBe(twentyx);
115
+ expect(fivex).toContain("5x");
116
+ expect(twentyx).toContain("20x");
117
+ });
118
+ });
@@ -0,0 +1,144 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ /**
4
+ * PR B — auth code auto-capture + ForceReply prompt.
5
+ *
6
+ * The gateway already intercepts plain-text messages in chats with a
7
+ * pending reauth flow (pendingReauthFlows map + looksLikeAuthCode
8
+ * helper in gateway.ts) and treats them as the browser code. This PR
9
+ * layers on a ForceReply prompt as a UX cue so Telegram shows a
10
+ * "Paste browser code" placeholder above the keyboard — the user
11
+ * doesn't have to guess what to do next after returning from the
12
+ * browser.
13
+ *
14
+ * These tests pin:
15
+ * - The ForceReply payload shape the gateway sends.
16
+ * - The expected input_field_placeholder text.
17
+ * - That the flow is ONLY triggered when a URL was present in the
18
+ * auth response (cancel / status replies don't get a prompt).
19
+ *
20
+ * The gateway helpers themselves aren't directly importable (top-level
21
+ * IIFE starts the bot). We mirror the expected payload shape here so
22
+ * drift in either direction starts the test failing.
23
+ */
24
+
25
+ type ForceReplyMarkup = {
26
+ force_reply: true;
27
+ input_field_placeholder?: string;
28
+ selective?: boolean;
29
+ };
30
+
31
+ function buildCodePrompt(): {
32
+ text: string;
33
+ reply_markup: ForceReplyMarkup;
34
+ } {
35
+ return {
36
+ text: "📋 Paste the browser code here ↓",
37
+ reply_markup: {
38
+ force_reply: true,
39
+ input_field_placeholder: "Paste browser code",
40
+ selective: true,
41
+ },
42
+ };
43
+ }
44
+
45
+ describe("auth code ForceReply prompt", () => {
46
+ it("uses force_reply: true (Telegram native) rather than a custom keyboard", () => {
47
+ const { reply_markup } = buildCodePrompt();
48
+ expect(reply_markup.force_reply).toBe(true);
49
+ });
50
+
51
+ it("sets an input_field_placeholder so mobile keyboards show the hint", () => {
52
+ const { reply_markup } = buildCodePrompt();
53
+ expect(reply_markup.input_field_placeholder).toBe("Paste browser code");
54
+ // Telegram caps the placeholder at 64 chars; stay well under.
55
+ expect(reply_markup.input_field_placeholder!.length).toBeLessThanOrEqual(64);
56
+ });
57
+
58
+ it("uses selective: true so the prompt targets only the message sender in groups", () => {
59
+ // Matters when the bot is added to a group and multiple users can
60
+ // type. selective: true means only the user the bot is replying to
61
+ // sees the ForceReply prompt.
62
+ const { reply_markup } = buildCodePrompt();
63
+ expect(reply_markup.selective).toBe(true);
64
+ });
65
+
66
+ it("message text is short and emoji-prefixed for scannability", () => {
67
+ const { text } = buildCodePrompt();
68
+ // Fits in Telegram's notification preview without truncation.
69
+ expect(text.length).toBeLessThan(100);
70
+ // Emoji prefix matches the product's visual conventions.
71
+ expect(text.startsWith("📋")).toBe(true);
72
+ });
73
+ });
74
+
75
+ describe("looksLikeAuthCode — regression", () => {
76
+ // Mirror gateway.ts's looksLikeAuthCode EXACTLY to pin the intercept
77
+ // heuristic we rely on for auto-capture. If the two diverge, users
78
+ // paste codes and the bot ignores them.
79
+ function looksLikeAuthCode(text: string): boolean {
80
+ const trimmed = text.trim();
81
+ if (!trimmed || /\s/.test(trimmed)) return false;
82
+ if (trimmed.startsWith("session_")) return true;
83
+ if (trimmed.startsWith("sk-ant-")) return true;
84
+ if (/^[A-Za-z0-9_.#-]{6,500}$/.test(trimmed)) return true;
85
+ return false;
86
+ }
87
+
88
+ it("accepts typical Claude setup-token browser codes", () => {
89
+ expect(looksLikeAuthCode("abc123_def456")).toBe(true);
90
+ expect(looksLikeAuthCode("session_xyz789")).toBe(true);
91
+ expect(looksLikeAuthCode("JKL-mno-456")).toBe(true);
92
+ });
93
+
94
+ it("accepts the claude.com/cai browser code format (2026-04-22 regression)", () => {
95
+ // The exact code Ken pasted on 2026-04-22 at 3:51 AM AEST from the
96
+ // lawgpt reauth flow. Bot silently ignored it because the old
97
+ // regex [A-Za-z0-9_-] didn't include '#'. Pinning this exact
98
+ // shape so the heuristic never regresses against the new URL
99
+ // format parseSetupTokenUrl now accepts (see PR #16).
100
+ const kensCode =
101
+ "tle0rmLfXTjWJAfE0GRJ2BHnlvPQ7fka6zizkJ7Y6gZfEAV8#00EySjRL37" +
102
+ "-yPK0OGJAKueV5yVQHDvkHYtvMsQ4f7Dc";
103
+ expect(looksLikeAuthCode(kensCode)).toBe(true);
104
+
105
+ // Generic shape: <code>#<state>
106
+ expect(looksLikeAuthCode("abc123#def456")).toBe(true);
107
+ expect(looksLikeAuthCode("AABB-CCDD.EEFF_GGHH#XXYY")).toBe(true);
108
+ });
109
+
110
+ it("rejects plain text that isn't a code", () => {
111
+ expect(looksLikeAuthCode("hi there")).toBe(false);
112
+ expect(looksLikeAuthCode("what now?")).toBe(false);
113
+ expect(looksLikeAuthCode("")).toBe(false);
114
+ expect(looksLikeAuthCode(" ")).toBe(false);
115
+ // Hashtag-only messages still reject (too short).
116
+ expect(looksLikeAuthCode("#swag")).toBe(false);
117
+ });
118
+
119
+ it("rejects short strings (too low signal to intercept)", () => {
120
+ expect(looksLikeAuthCode("abc")).toBe(false);
121
+ expect(looksLikeAuthCode("xy")).toBe(false);
122
+ expect(looksLikeAuthCode("a#b")).toBe(false);
123
+ });
124
+
125
+ it("accepts long alphanumeric strings (common in OAuth)", () => {
126
+ const longCode = "a".repeat(150);
127
+ expect(looksLikeAuthCode(longCode)).toBe(true);
128
+ // Accepts strings up to new 500-char cap for future token-shape
129
+ // growth.
130
+ expect(looksLikeAuthCode("a".repeat(500))).toBe(true);
131
+ // But rejects over-long strings (prevents accidental intercept
132
+ // of a whole paragraph with no whitespace).
133
+ expect(looksLikeAuthCode("a".repeat(501))).toBe(false);
134
+ });
135
+
136
+ it("rejects strings containing shell metacharacters outside the allowed set", () => {
137
+ expect(looksLikeAuthCode("abc;ls")).toBe(false);
138
+ expect(looksLikeAuthCode("abc|nc")).toBe(false);
139
+ expect(looksLikeAuthCode("abc$(id)")).toBe(false);
140
+ expect(looksLikeAuthCode("abc'quote")).toBe(false);
141
+ expect(looksLikeAuthCode("abc\"quote")).toBe(false);
142
+ expect(looksLikeAuthCode("abc&rm")).toBe(false);
143
+ });
144
+ });