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,200 @@
1
+ /**
2
+ * Pure helpers for the session-handoff continuity line.
3
+ *
4
+ * On session start, the telegram plugin reads `$AGENT_DIR/.handoff-topic`
5
+ * (written by the summarizer Stop hook). On the FIRST assistant reply
6
+ * of the new session the plugin prepends a subtle one-liner:
7
+ *
8
+ * ↩️ Picked up where we left off — <topic>
9
+ *
10
+ * The sidecar is consumed (read + deleted) so the line only fires once.
11
+ * All helpers here are filesystem-only or env-only — no Telegram side
12
+ * effects — which keeps them unit-testable in isolation.
13
+ */
14
+
15
+ import { readFileSync, unlinkSync, existsSync, writeFileSync, renameSync } from "node:fs";
16
+ import { dirname, join } from "node:path";
17
+
18
+ export const TOPIC_DISPLAY_MAX = 117;
19
+ export const HANDOFF_TOPIC_FILENAME = ".handoff-topic";
20
+ /**
21
+ * Secondary sidecar written by the progress-card driver on every
22
+ * successful turn_end. The file is overwritten each turn so it always
23
+ * reflects the most-recent turn. Used as a fallback source for the
24
+ * continuity line when the Stop-hook summarizer hasn't run yet (e.g.
25
+ * crash, mid-session restart, summarizer failure). Deleted alongside
26
+ * `.handoff-topic` in `consumeHandoffTopic`.
27
+ */
28
+ export const LAST_TURN_SUMMARY_FILENAME = ".last-turn-summary";
29
+
30
+ export function resolveAgentDirFromEnv(): string | null {
31
+ const state = process.env.TELEGRAM_STATE_DIR;
32
+ if (!state || state.trim().length === 0) return null;
33
+ return dirname(state);
34
+ }
35
+
36
+ /**
37
+ * Read the handoff topic file if present. Returns the trimmed first
38
+ * non-empty line, truncated to TOPIC_DISPLAY_MAX with an ellipsis.
39
+ * Missing, empty, or unreadable → null.
40
+ */
41
+ export function readHandoffTopic(agentDir: string): string | null {
42
+ const p = join(agentDir, HANDOFF_TOPIC_FILENAME);
43
+ if (!existsSync(p)) return null;
44
+ let raw: string;
45
+ try {
46
+ raw = readFileSync(p, "utf-8");
47
+ } catch {
48
+ return null;
49
+ }
50
+ const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
51
+ if (lines.length === 0) return null;
52
+ let topic = lines[0];
53
+ if (topic.length > TOPIC_DISPLAY_MAX) {
54
+ topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "…";
55
+ }
56
+ return topic;
57
+ }
58
+
59
+ /**
60
+ * Read the per-turn summary file if present (written by the progress-
61
+ * card driver on every turn_end). Returns the trimmed first non-empty
62
+ * line, truncated like `readHandoffTopic`. The file is always
63
+ * overwritten so it reflects the most-recent completed turn.
64
+ */
65
+ export function readLastTurnSummary(agentDir: string): string | null {
66
+ const p = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
67
+ if (!existsSync(p)) return null;
68
+ let raw: string;
69
+ try {
70
+ raw = readFileSync(p, "utf-8");
71
+ } catch {
72
+ return null;
73
+ }
74
+ const lines = raw.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
75
+ if (lines.length === 0) return null;
76
+ let topic = lines[0];
77
+ if (topic.length > TOPIC_DISPLAY_MAX) {
78
+ topic = topic.slice(0, TOPIC_DISPLAY_MAX) + "…";
79
+ }
80
+ return topic;
81
+ }
82
+
83
+ /**
84
+ * Read + delete the topic file atomically (best-effort). A second call
85
+ * returns null even if the first succeeded — the sidecar is one-shot.
86
+ *
87
+ * Fallback: if no `.handoff-topic` is present (summarizer didn't run,
88
+ * crashed, or the session was restarted mid-loop), try the
89
+ * `.last-turn-summary` sidecar written by the progress-card driver.
90
+ * Both sidecars get removed on consume so the continuity line only
91
+ * fires once per resume.
92
+ */
93
+ export function consumeHandoffTopic(agentDir: string): string | null {
94
+ const primary = readHandoffTopic(agentDir);
95
+ const primaryPath = join(agentDir, HANDOFF_TOPIC_FILENAME);
96
+ const fallbackPath = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
97
+
98
+ // Always remove the per-turn summary when we consume — otherwise a
99
+ // later session restart would still see a stale entry even after the
100
+ // continuity line fired.
101
+ const removeFallback = (): void => {
102
+ try {
103
+ unlinkSync(fallbackPath);
104
+ } catch {
105
+ /* already gone */
106
+ }
107
+ };
108
+
109
+ if (primary !== null) {
110
+ try {
111
+ unlinkSync(primaryPath);
112
+ } catch {
113
+ /* already gone */
114
+ }
115
+ removeFallback();
116
+ return primary;
117
+ }
118
+
119
+ const fallback = readLastTurnSummary(agentDir);
120
+ if (fallback !== null) {
121
+ removeFallback();
122
+ return fallback;
123
+ }
124
+ return null;
125
+ }
126
+
127
+ /**
128
+ * Atomically overwrite `.last-turn-summary` with a single-line summary.
129
+ * Called by the progress-card driver on every turn_end. Best-effort: any
130
+ * write failure is swallowed (logged by the caller if desired) — a
131
+ * missing fallback file is recoverable, a half-written one is not.
132
+ *
133
+ * The summary is the natural plain-text signature of a completed turn:
134
+ * `<N tools, Ys> — <user request (truncated)>`
135
+ * Callers should pass a pre-built line; this function handles only the
136
+ * atomic write + first-line discipline.
137
+ */
138
+ export function writeLastTurnSummary(agentDir: string, summary: string): void {
139
+ const line = summary.split(/\r?\n/)[0]?.trim() ?? "";
140
+ if (line.length === 0) return;
141
+ const final = line.length > TOPIC_DISPLAY_MAX
142
+ ? line.slice(0, TOPIC_DISPLAY_MAX) + "…"
143
+ : line;
144
+ const p = join(agentDir, LAST_TURN_SUMMARY_FILENAME);
145
+ const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
146
+ try {
147
+ writeFileSync(tmp, final + "\n", "utf-8");
148
+ renameSync(tmp, p);
149
+ } catch {
150
+ /* best-effort — continuity line is purely cosmetic */
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Reads SWITCHROOM_HANDOFF_SHOW_LINE. Defaults to true when unset so users
156
+ * opt out explicitly via switchroom.yaml rather than opt in.
157
+ */
158
+ export function shouldShowHandoffLine(): boolean {
159
+ const v = process.env.SWITCHROOM_HANDOFF_SHOW_LINE;
160
+ if (v === undefined) return true;
161
+ return v.toLowerCase() !== "false";
162
+ }
163
+
164
+ export type HandoffFormat = "html" | "markdownv2" | "text";
165
+
166
+ /**
167
+ * Format the continuity line for the requested outbound format. The
168
+ * returned string already includes the trailing `\n\n` separator so the
169
+ * caller can concatenate directly with the assistant's reply body.
170
+ *
171
+ * HTML: wraps in <i>…</i>. MarkdownV2: wraps in _…_ with escaping.
172
+ * text: plain. All variants prefix the ↩️ emoji.
173
+ */
174
+ export function formatHandoffLine(
175
+ topic: string,
176
+ format: HandoffFormat,
177
+ ): string {
178
+ const prefix = "↩️ Picked up where we left off — ";
179
+ if (format === "html") {
180
+ return `<i>${prefix}${escapeHtml(topic)}</i>\n\n`;
181
+ }
182
+ if (format === "markdownv2") {
183
+ const escaped = escapeMarkdownV2(topic);
184
+ const prefixEsc = escapeMarkdownV2(prefix);
185
+ return `_${prefixEsc}${escaped}_\n\n`;
186
+ }
187
+ return `${prefix}${topic}\n\n`;
188
+ }
189
+
190
+ function escapeHtml(s: string): string {
191
+ return s
192
+ .replace(/&/g, "&amp;")
193
+ .replace(/</g, "&lt;")
194
+ .replace(/>/g, "&gt;");
195
+ }
196
+
197
+ const MDV2_SPECIALS = /[_*\[\]()~`>#+\-=|{}.!\\]/g;
198
+ function escapeMarkdownV2(s: string): string {
199
+ return s.replace(MDV2_SPECIALS, (m) => "\\" + m);
200
+ }
@@ -0,0 +1,468 @@
1
+ /**
2
+ * Persistent message history for the Telegram plugin.
3
+ *
4
+ * Telegram's Bot API exposes no chat history endpoint, so the agent has no
5
+ * way to recover "what were we just talking about" after a Claude Code
6
+ * restart without asking the user. This module fills that gap by writing
7
+ * every inbound and outbound message that flows through the plugin to a
8
+ * local SQLite database, queryable via the `get_recent_messages` MCP tool.
9
+ *
10
+ * Storage is `bun:sqlite` (Bun's bundled SQLite, no extra dep). The DB file
11
+ * lives at `${STATE_DIR}/history.db` and is chmod'd to 0600 to match the
12
+ * plugin's existing credential-handling pattern.
13
+ *
14
+ * Capture is gated:
15
+ * - Inbound: only writes after the access gate, topic filter, and the
16
+ * permission-reply intercept have passed (see server.ts handleInbound).
17
+ * - Outbound: the reply/stream_reply/forward_message/edit_message handlers
18
+ * all assertAllowedChat() before reaching the capture call sites.
19
+ *
20
+ * Per-chunk rows: when a long `reply` is split into multiple Telegram
21
+ * messages by the chunker, each chunk lands as its own row keyed by its
22
+ * real message_id, with a shared `group_id` (the first chunk's id) so
23
+ * the logical reply can be reconstructed if needed. This keeps
24
+ * `before_message_id` pagination working — every visible message in the
25
+ * chat exists in the DB.
26
+ */
27
+
28
+ import { chmodSync, mkdirSync } from 'fs'
29
+ import { join } from 'path'
30
+
31
+ /**
32
+ * `bun:sqlite` is a Bun built-in — Vite/Node loaders can't resolve it
33
+ * statically, which would crash any vitest test that transitively
34
+ * imports this module (every test that imports server.ts). Hide the
35
+ * require behind an eval so static analysis passes; runtime resolution
36
+ * is per-Bun and works fine.
37
+ *
38
+ * The Database class is loaded lazily on the first initHistory() call.
39
+ * If we're somehow running under non-Bun (vitest fallback, ts-node, etc.)
40
+ * the lazy load throws — but only when the caller actually tries to use
41
+ * the history feature, not on module import.
42
+ *
43
+ * The only Database APIs we touch are constructor(path, opts), exec,
44
+ * prepare, transaction, close — all stable across bun:sqlite versions.
45
+ */
46
+ type SqliteDatabase = {
47
+ exec(sql: string): void
48
+ prepare(sql: string): {
49
+ run(...params: unknown[]): unknown
50
+ all(...params: unknown[]): unknown[]
51
+ get(...params: unknown[]): unknown
52
+ }
53
+ transaction(fn: (...args: unknown[]) => unknown): (...args: unknown[]) => unknown
54
+ close(): void
55
+ }
56
+ type SqliteDatabaseConstructor = new (path: string, opts?: { create?: boolean }) => SqliteDatabase
57
+
58
+ let DatabaseClass: SqliteDatabaseConstructor | null = null
59
+ function loadDatabaseClass(): SqliteDatabaseConstructor {
60
+ if (DatabaseClass != null) return DatabaseClass
61
+ try {
62
+ // Bun exposes a `require` on `import.meta` that works in ESM modules
63
+ // and resolves built-in `bun:*` modules. Vite/esbuild's static
64
+ // analyzer doesn't follow `import.meta.require(...)` calls (it only
65
+ // resolves static `import` statements), so this hides the bun:sqlite
66
+ // import from the test loader. Under non-Bun runtimes
67
+ // `import.meta.require` is undefined and we throw a clear error.
68
+ const metaRequire = (import.meta as { require?: (id: string) => unknown }).require
69
+ if (typeof metaRequire !== 'function') {
70
+ throw new Error('import.meta.require not available — Bun runtime required')
71
+ }
72
+ const mod = metaRequire('bun:sqlite') as { Database?: SqliteDatabaseConstructor }
73
+ if (!mod.Database) throw new Error('bun:sqlite did not export Database')
74
+ DatabaseClass = mod.Database
75
+ return DatabaseClass
76
+ } catch (err) {
77
+ throw new Error(
78
+ `history.ts requires Bun runtime (bun:sqlite). Caller: ${(err as Error).message}`,
79
+ )
80
+ }
81
+ }
82
+
83
+ export type MessageRole = 'user' | 'assistant'
84
+
85
+ export interface RecordedMessage {
86
+ chat_id: string
87
+ thread_id: number | null
88
+ message_id: number
89
+ role: MessageRole
90
+ user: string | null
91
+ user_id: string | null
92
+ ts: number
93
+ text: string
94
+ attachment_kind: string | null
95
+ group_id: number | null
96
+ /**
97
+ * Set when the inbound user message was a Telegram-native reply to a
98
+ * prior message. Lets get_recent_messages surface the reply context
99
+ * the agent saw at delivery time.
100
+ */
101
+ reply_to_message_id: number | null
102
+ reply_to_text: string | null
103
+ /**
104
+ * The most recent emoji reaction the user placed on this (assistant)
105
+ * message. Null means no reaction or reaction was removed. Updated by
106
+ * the message_reaction handler when Telegram delivers reaction events.
107
+ * Only emoji reactions are tracked — custom emoji are ignored for v1.
108
+ */
109
+ user_reaction: string | null
110
+ }
111
+
112
+ export interface QueryOptions {
113
+ chat_id: string
114
+ thread_id?: number | null
115
+ limit?: number
116
+ before_message_id?: number
117
+ }
118
+
119
+ const DEFAULT_LIMIT = 10
120
+ const MAX_LIMIT = 50
121
+
122
+ let db: SqliteDatabase | null = null
123
+
124
+ /**
125
+ * Open (or create) the history DB and run migrations + retention sweep.
126
+ * Idempotent — safe to call once at server startup.
127
+ *
128
+ * `retentionDays` deletes rows older than the cutoff. 0 disables retention.
129
+ */
130
+ export function initHistory(stateDir: string, retentionDays = 30): void {
131
+ if (db != null) return
132
+ const Database = loadDatabaseClass()
133
+ mkdirSync(stateDir, { recursive: true, mode: 0o700 })
134
+ const path = join(stateDir, 'history.db')
135
+ db = new Database(path, { create: true })
136
+ // WAL is friendlier for concurrent reads while a long transaction writes,
137
+ // and survives crashes more cleanly than rollback journal.
138
+ db.exec('PRAGMA journal_mode = WAL')
139
+ db.exec('PRAGMA synchronous = NORMAL')
140
+ db.exec(`
141
+ CREATE TABLE IF NOT EXISTS messages (
142
+ chat_id TEXT NOT NULL,
143
+ thread_id INTEGER,
144
+ message_id INTEGER NOT NULL,
145
+ role TEXT NOT NULL,
146
+ user TEXT,
147
+ user_id TEXT,
148
+ ts INTEGER NOT NULL,
149
+ text TEXT NOT NULL,
150
+ attachment_kind TEXT,
151
+ group_id INTEGER,
152
+ reply_to_message_id INTEGER,
153
+ reply_to_text TEXT,
154
+ PRIMARY KEY (chat_id, thread_id, message_id)
155
+ )
156
+ `)
157
+ db.exec(`
158
+ CREATE INDEX IF NOT EXISTS idx_messages_recent
159
+ ON messages (chat_id, thread_id, ts DESC)
160
+ `)
161
+ // Migration: add reply_to columns to existing DBs that pre-date issue #119.
162
+ // SQLite has no IF NOT EXISTS for ALTER TABLE ADD COLUMN, so we tolerate
163
+ // "duplicate column name" errors and re-throw anything else.
164
+ for (const column of ["reply_to_message_id INTEGER", "reply_to_text TEXT", "user_reaction TEXT"]) {
165
+ try {
166
+ db.exec(`ALTER TABLE messages ADD COLUMN ${column}`)
167
+ } catch (err) {
168
+ const msg = err instanceof Error ? err.message : String(err)
169
+ if (!/duplicate column name/i.test(msg)) throw err
170
+ }
171
+ }
172
+
173
+ // Lock the file to owner-only. Same pattern the plugin uses for .env at
174
+ // server.ts:52. No-op on Windows (would need ACLs).
175
+ try {
176
+ chmodSync(path, 0o600)
177
+ } catch {
178
+ /* ignore — chmod not supported, e.g. some FUSE mounts */
179
+ }
180
+
181
+ if (retentionDays > 0) {
182
+ const cutoff = Math.floor(Date.now() / 1000) - retentionDays * 86400
183
+ db.prepare('DELETE FROM messages WHERE ts < ?').run(cutoff)
184
+ }
185
+ }
186
+
187
+ /**
188
+ * For tests — close the singleton and forget it. Production code never
189
+ * needs this; the DB is held open for the lifetime of the process.
190
+ */
191
+ export function _resetForTests(): void {
192
+ if (db != null) {
193
+ db.close()
194
+ db = null
195
+ }
196
+ }
197
+
198
+ function requireDb(): SqliteDatabase {
199
+ if (db == null) {
200
+ throw new Error('history: initHistory() must be called before any record/query operation')
201
+ }
202
+ return db
203
+ }
204
+
205
+ interface RecordInboundArgs {
206
+ chat_id: string
207
+ thread_id: number | null | undefined
208
+ message_id: number | null | undefined
209
+ user: string | null | undefined
210
+ user_id: string | null | undefined
211
+ ts: number
212
+ text: string
213
+ attachment_kind?: string | null | undefined
214
+ /**
215
+ * If the user replied to a prior message via Telegram's native reply
216
+ * feature, the original message_id and a (truncated) preview of its
217
+ * text. Populated from `ctx.message.reply_to_message` in the gateway
218
+ * handler. See issue #119.
219
+ */
220
+ reply_to_message_id?: number | null | undefined
221
+ reply_to_text?: string | null | undefined
222
+ }
223
+
224
+ /**
225
+ * Record an inbound (user → bot) message. Called from server.ts handleInbound
226
+ * right next to the `notifications/claude/channel` emit, so the stored row
227
+ * matches what the agent actually saw.
228
+ *
229
+ * If `message_id` is missing (which shouldn't happen for normal Telegram
230
+ * messages, but defensively...) we silently skip — the PK requires it and
231
+ * we'd rather lose the row than crash the inbound path.
232
+ */
233
+ export function recordInbound(args: RecordInboundArgs): void {
234
+ if (args.message_id == null) return
235
+ const stmt = requireDb().prepare(`
236
+ INSERT OR REPLACE INTO messages
237
+ (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id, reply_to_message_id, reply_to_text)
238
+ VALUES (?, ?, ?, 'user', ?, ?, ?, ?, ?, NULL, ?, ?)
239
+ `)
240
+ stmt.run(
241
+ args.chat_id,
242
+ args.thread_id ?? null,
243
+ args.message_id,
244
+ args.user ?? null,
245
+ args.user_id ?? null,
246
+ args.ts,
247
+ args.text,
248
+ args.attachment_kind ?? null,
249
+ args.reply_to_message_id ?? null,
250
+ args.reply_to_text ?? null,
251
+ )
252
+ }
253
+
254
+ interface RecordOutboundArgs {
255
+ chat_id: string
256
+ thread_id: number | null | undefined
257
+ message_ids: number[] // one entry per chunk
258
+ texts: string[] // parallel array, same length as message_ids
259
+ ts?: number // defaults to now
260
+ attachment_kinds?: (string | null | undefined)[]
261
+ }
262
+
263
+ /**
264
+ * Record an outbound (bot → user) message. The `reply` handler chunks long
265
+ * text into multiple Telegram sends; pass them all here in one call so they
266
+ * share a `group_id` (the first chunk's message_id). The other handlers
267
+ * (`stream_reply`, `forward_message`, orphaned-reply backstop) typically
268
+ * pass a single-element array.
269
+ */
270
+ export function recordOutbound(args: RecordOutboundArgs): void {
271
+ if (args.message_ids.length === 0) return
272
+ const ts = args.ts ?? Math.floor(Date.now() / 1000)
273
+ const groupId = args.message_ids[0]!
274
+ const stmt = requireDb().prepare(`
275
+ INSERT OR REPLACE INTO messages
276
+ (chat_id, thread_id, message_id, role, user, user_id, ts, text, attachment_kind, group_id)
277
+ VALUES (?, ?, ?, 'assistant', NULL, NULL, ?, ?, ?, ?)
278
+ `)
279
+ // bun:sqlite has a transaction() helper. Cheap insurance against partial
280
+ // writes if the process dies mid-loop. The transaction signature is
281
+ // typed as variadic-unknown for genericity; cast the typed callback
282
+ // through the wider shape.
283
+ const tx = requireDb().transaction(((rows: Array<[number, string, string | null]>) => {
284
+ for (const [msgId, text, attachKind] of rows) {
285
+ stmt.run(
286
+ args.chat_id,
287
+ args.thread_id ?? null,
288
+ msgId,
289
+ ts,
290
+ text,
291
+ attachKind,
292
+ groupId,
293
+ )
294
+ }
295
+ }) as (...args: unknown[]) => unknown)
296
+ const rows: Array<[number, string, string | null]> = args.message_ids.map((id, i) => [
297
+ id,
298
+ args.texts[i] ?? '',
299
+ args.attachment_kinds?.[i] ?? null,
300
+ ])
301
+ tx(rows)
302
+ }
303
+
304
+ interface RecordEditArgs {
305
+ chat_id: string
306
+ message_id: number
307
+ text: string
308
+ }
309
+
310
+ /**
311
+ * Update an existing outbound message's text in place. Called from the
312
+ * `edit_message` tool handler. If the row doesn't exist (the bot is editing
313
+ * a message it sent before history was enabled, or before this version
314
+ * shipped), we silently no-op rather than inventing a row with role guessed.
315
+ *
316
+ * Telegram message_ids are unique within a chat regardless of thread, so
317
+ * we don't filter on thread_id — that lets edits work even when the
318
+ * original send-time thread isn't known at edit time.
319
+ */
320
+ export function recordEdit(args: RecordEditArgs): void {
321
+ requireDb()
322
+ .prepare(`
323
+ UPDATE messages
324
+ SET text = ?
325
+ WHERE chat_id = ? AND message_id = ?
326
+ `)
327
+ .run(args.text, args.chat_id, args.message_id)
328
+ }
329
+
330
+ export interface RecordReactionArgs {
331
+ chat_id: string
332
+ message_id: number
333
+ /** The emoji string to store, or null to clear the reaction field. */
334
+ emoji: string | null
335
+ }
336
+
337
+ /**
338
+ * Update (or clear) the user_reaction field for a message row. Called from
339
+ * the message_reaction gateway handler when Telegram delivers a reaction
340
+ * event. If the row doesn't exist (the message predates history, or the
341
+ * reaction was placed on an inbound message), silently no-ops.
342
+ *
343
+ * Telegram message_ids are unique within a chat regardless of thread, so
344
+ * we match on (chat_id, message_id) and ignore thread_id — same as recordEdit.
345
+ */
346
+ export function recordReaction(args: RecordReactionArgs): void {
347
+ requireDb()
348
+ .prepare(`
349
+ UPDATE messages
350
+ SET user_reaction = ?
351
+ WHERE chat_id = ? AND message_id = ?
352
+ `)
353
+ .run(args.emoji, args.chat_id, args.message_id)
354
+ }
355
+
356
+ export interface DeleteFromHistoryArgs {
357
+ chat_id: string
358
+ message_id: number
359
+ }
360
+
361
+ /**
362
+ * Remove a single row from the local history buffer. Called after a
363
+ * successful `bot.api.deleteMessage` so the `get_recent_messages` tool
364
+ * reflects the deletion. Best-effort: callers should catch errors and
365
+ * log them rather than failing the deletion request.
366
+ *
367
+ * Telegram message_ids are unique within a chat regardless of thread, so
368
+ * we match on (chat_id, message_id) and ignore thread.
369
+ */
370
+ export function deleteFromHistory(args: DeleteFromHistoryArgs): void {
371
+ requireDb()
372
+ .prepare(`
373
+ DELETE FROM messages
374
+ WHERE chat_id = ? AND message_id = ?
375
+ `)
376
+ .run(args.chat_id, args.message_id)
377
+ }
378
+
379
+ /**
380
+ * Fetch recent messages for a chat (or chat+thread).
381
+ *
382
+ * Returns oldest-first so the caller can paste them into a prompt without
383
+ * reversing. `before_message_id` lets the caller paginate further back —
384
+ * pass the smallest `message_id` from the previous page.
385
+ *
386
+ * `thread_id` filter semantics:
387
+ * - omitted / undefined → all messages in the chat across all threads
388
+ * - explicit number → only that thread
389
+ * - explicit null → only the chat-root (non-thread) messages
390
+ */
391
+ /**
392
+ * Count outbound messages sent to a chat within the last `withinSeconds`.
393
+ * Used by the orphaned-reply backstop to detect if a reply tool handler
394
+ * already sent a message during the backstop's delay window, avoiding
395
+ * a duplicate send.
396
+ */
397
+ /**
398
+ * Look up the most recent inbound (user → bot) message id for a chat, optionally
399
+ * scoped to a forum-topic thread. Returns `null` if no inbound message exists
400
+ * yet (fresh chat, or history was disabled when the message arrived).
401
+ *
402
+ * Used by the `reply` and `stream_reply` tool handlers to auto-populate
403
+ * `reply_parameters` so outbound messages quote-thread under whatever the
404
+ * user last said — the common case for a conversational bot — without the
405
+ * agent having to pass `reply_to` explicitly every turn.
406
+ *
407
+ * `thread_id` filter semantics match `query()`:
408
+ * - omitted / undefined → any message in the chat
409
+ * - explicit number → only that thread
410
+ * - explicit null → only chat-root (non-thread)
411
+ */
412
+ export function getLatestInboundMessageId(
413
+ chatId: string,
414
+ threadId?: number | null,
415
+ ): number | null {
416
+ const params: unknown[] = [chatId]
417
+ let sql = "SELECT message_id FROM messages WHERE chat_id = ? AND role = 'user'"
418
+ if (threadId !== undefined) {
419
+ if (threadId === null) {
420
+ sql += ' AND thread_id IS NULL'
421
+ } else {
422
+ sql += ' AND thread_id = ?'
423
+ params.push(threadId)
424
+ }
425
+ }
426
+ sql += ' ORDER BY ts DESC, message_id DESC LIMIT 1'
427
+ const row = requireDb().prepare(sql).get(...params as any[]) as
428
+ | { message_id: number }
429
+ | undefined
430
+ return row?.message_id ?? null
431
+ }
432
+
433
+ export function getRecentOutboundCount(
434
+ chatId: string,
435
+ withinSeconds: number,
436
+ ): number {
437
+ const cutoff = Math.floor(Date.now() / 1000) - withinSeconds
438
+ const row = requireDb()
439
+ .prepare(
440
+ 'SELECT COUNT(*) as cnt FROM messages WHERE chat_id = ? AND role = ? AND ts >= ?',
441
+ )
442
+ .get(chatId, 'assistant', cutoff) as { cnt: number } | undefined
443
+ return row?.cnt ?? 0
444
+ }
445
+
446
+ export function query(opts: QueryOptions): RecordedMessage[] {
447
+ const limit = Math.min(MAX_LIMIT, Math.max(1, opts.limit ?? DEFAULT_LIMIT))
448
+ const params: unknown[] = [opts.chat_id]
449
+ let sql = 'SELECT * FROM messages WHERE chat_id = ?'
450
+ if (opts.thread_id !== undefined) {
451
+ if (opts.thread_id === null) {
452
+ sql += ' AND thread_id IS NULL'
453
+ } else {
454
+ sql += ' AND thread_id = ?'
455
+ params.push(opts.thread_id)
456
+ }
457
+ }
458
+ if (opts.before_message_id != null) {
459
+ sql += ' AND message_id < ?'
460
+ params.push(opts.before_message_id)
461
+ }
462
+ sql += ' ORDER BY ts DESC, message_id DESC LIMIT ?'
463
+ params.push(limit)
464
+ const rows = requireDb().prepare(sql).all(...params as any[]) as RecordedMessage[]
465
+ // SELECT was DESC; flip to oldest-first for the caller.
466
+ rows.reverse()
467
+ return rows
468
+ }