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,144 @@
1
+ # Pinned progress card โ€” reliability spec
2
+
3
+ Status: **documenting existing system + closing gaps.** The pin/unpin machinery already exists (see ยง3). This spec formalizes the invariants it must hold, enumerates failure modes, specifies the test matrix, and lists the residual gaps that still need closing to hit "insanely reliable UX."
4
+
5
+ ## 1. Goal
6
+
7
+ When the agent is working on a turn, the user always sees a **single, live-updating, pinned** status message for that turn. When the turn ends, that message is either marked `โœ… Done` and unpinned, or (for very fast turns) never shown at all. No stale pins, no orphan pins, no duplicate pins, no silent failures.
8
+
9
+ "Insanely reliable" = every one of the invariants in ยง4 holds under crash, kill, restart, rate-limit, race, and parallel-turn conditions, with automated tests covering each.
10
+
11
+ ## 2. Non-goals
12
+
13
+ - Per-tool progress granularity beyond what the event-driven card already renders.
14
+ - Pinning arbitrary bot messages (only the per-turn progress card).
15
+ - Fighting the user for pin real-estate: a user-pinned message is always a barrier (see I8).
16
+ - Pinning in group chats with many admins (Telegram restricts `pinChatMessage` to bots with `can_pin_messages`; failure is logged and swallowed, card still updates in place).
17
+
18
+ ## 3. Existing implementation (ground truth)
19
+
20
+ Files and their load-bearing roles:
21
+
22
+ | File | Role |
23
+ |---|---|
24
+ | `progress-card.ts` | Pure reducer + renderer. Turn-scoped state; event โ†’ HTML. |
25
+ | `progress-card-driver.ts` | Cadence controller. Coalesce + min-interval + heartbeat + zombie ceiling. Fires `emit` with `isFirstEmit` flag and `onTurnComplete` with `turnKey`. |
26
+ | `active-pins.ts` | Sidecar (`$AGENT_DIR/.active-pins.json`) โ€” add/remove/read/write. Atomic rename on write. Shape-validated reads. |
27
+ | `active-pins-sweep.ts` | Two sweeps: (a) `sweepActivePins` drains the sidecar and unpins each; (b) `sweepBotAuthoredPins` walks `getChat().pinned_message` and unpins anything authored by our bot, stopping at the first user-authored pin. |
28
+ | `server.ts` (streamMode='checklist' block) | Wires driver โ†’ Telegram API. Owns `progressPinnedMsgIds`, `unpinnedTurnKeys`, and the idempotent `unpinProgressCard` closure. Boot-time + pre-restart sweeps wired. |
29
+
30
+ Current lifecycle:
31
+
32
+ ```
33
+ enqueue / startTurn
34
+ โ””โ”€โ–บ driver allocates turnKey (chatId:threadId:seq)
35
+ โ””โ”€โ–บ render(state) โ†’ emit(isFirstEmit=true)
36
+ โ””โ”€โ–บ handleStreamReply creates message โ†’ messageId
37
+ โ”œโ”€โ–บ addActivePin(sidecar) โ† WRITE BEFORE API CALL
38
+ โ””โ”€โ–บ pinChatMessage(disable_notification=true)
39
+ โ””โ”€โ–บ on failure: removeActivePin (roll back)
40
+
41
+ ...live edits via coalesced flush (400ms) + heartbeat (5s)...
42
+
43
+ turn_end OR stream_reply(done=true) OR reply(final=true)
44
+ โ””โ”€โ–บ unpinProgressCard(turnKey) โ€” idempotent via unpinnedTurnKeys
45
+ โ””โ”€โ–บ unpinChatMessage
46
+ โ””โ”€โ–บ finally: removeActivePin (regardless of outcome)
47
+ ```
48
+
49
+ Crash / kill paths:
50
+ - `SIGKILL` mid-turn โ†’ sidecar retains entry โ†’ next boot's `sweepActivePins` unpins.
51
+ - `/restart`, `/update`, `/reconcile --restart` โ†’ proactive pre-SIGTERM sweep (see server.ts:2841).
52
+ - Sidecar lost but pin still on Telegram โ†’ next boot's `sweepBotAuthoredPins` walks `getChat` and removes bot-authored pins until a user-authored pin acts as barrier.
53
+
54
+ ## 4. Invariants
55
+
56
+ All must be tested (see ยง7). `I*` numbers are referenced elsewhere in this spec.
57
+
58
+ | ID | Invariant |
59
+ |---|---|
60
+ | **I1** | Every `pinChatMessage` call is preceded by a successful `addActivePin` sidecar write. |
61
+ | **I2** | Every successful pin produces exactly one `unpinChatMessage` call over the card's lifetime (across in-session unpin + restart sweep). |
62
+ | **I3** | `unpinProgressCard(turnKey, โ€ฆ)` is idempotent โ€” first call fires the API, all subsequent calls for the same `turnKey` are no-ops. |
63
+ | **I4** | On process start, any sidecar entry from a prior session is consumed (attempt unpin, then clear) before new traffic is accepted. |
64
+ | **I5** | The final render of the card (before unpin) shows `stage === 'done'` โ†’ `โœ… Done` header. |
65
+ | **I6** | Turns that complete faster than `initialDelayMs` (default 30s) produce **no** pin and **no** card โ€” suppressed, not deferred-then-cancelled. |
66
+ | **I7** | Parallel active turns on the same `(chatId, threadId)` each have independent `turnKey`, `pin`, `unpin`, and `sidecar` entries. The second `enqueue` force-closes the first (including its unpin) before creating the new pin. |
67
+ | **I8** | `sweepBotAuthoredPins` stops at the first non-bot pinned message for a chat โ€” never unpins a user's pin. |
68
+ | **I9** | Zombie ceiling: a card whose `lastEventAt` is older than `maxIdleMs` (5 min) is force-closed via the same path as `turn_end` โ€” unpin + `onTurnComplete` + state delete. |
69
+ | **I10** | `pinChatMessage` failure does not leave the sidecar polluted โ€” `removeActivePin` is called in the failure branch. |
70
+ | **I11** | Completion notification (`โœ… Done โ€” <summary>` top-level message) only sent in forum-topic turns (`threadId != null`); never in plain DMs. |
71
+
72
+ ## 5. Failure modes & mitigations
73
+
74
+ | # | Failure mode | Current mitigation | Residual gap |
75
+ |---|---|---|---|
76
+ | F1 | SIGKILL between `addActivePin` and `pinChatMessage` | Boot-time `sweepBotAuthoredPins` (no pin to remove, sidecar cleared) | None. |
77
+ | F2 | SIGKILL between `pinChatMessage` success and `turn_end` | Boot-time `sweepActivePins` | None. |
78
+ | F3 | SIGKILL between `unpinChatMessage` in-flight and `removeActivePin` | Next boot re-attempts unpin; Telegram unpin is idempotent (400 is harmless) | **Gap:** no structured telemetry distinguishes "real stale pin swept" from "redundant sweep on already-unpinned message" โ€” both log generic failure. |
79
+ | F4 | `pinChatMessage` 429 (rate limit) | Error logged + sidecar rolled back; card continues to live-update in place (unpinned) | **Gap:** no retry with backoff. User silently loses the pin for that turn. Should at least surface status-reaction signal differently. |
80
+ | F5 | `unpinChatMessage` 429 | Error logged + sidecar cleared via `.finally()`; next boot will see stale pin via `sweepBotAuthoredPins` | **Gap:** mid-session stale pin persists until next restart. Consider best-effort retry (1 retry after 1s) before giving up. |
81
+ | F6 | Bot lacks `can_pin_messages` in group | Error logged and swallowed; card still live-updates inline | None โ€” graceful degradation. |
82
+ | F7 | Session restart while turn still live | Pre-restart sweep unpins; new process's startup sweep is redundant but harmless; the resumed turn creates a fresh pin via the `--continue` path | **Gap:** user sees "Done โ†’ unpin โ†’ new pin" flicker during ~1โ€“3s restart. Consider deferring pre-restart unpin until the new process confirms it has taken over (out of scope for this spec; needs handoff-protocol work). |
83
+ | F8 | Two parallel turns on same `chatId:threadId` | `turnKey` allocator + `isSync` guards in enqueue handler | None (tests cover). |
84
+ | F9 | Duplicate enqueue echoes from session-tail (JSONL rotation, reconnect) | `seenEnqueueMsgIds` 60s dedup + `pendingSyncEchoes` sync marker | None โ€” well tested. |
85
+ | F10 | Heartbeat keeps ticking a card whose `turn_end` was dropped | `maxIdleMs` zombie ceiling (5 min) force-closes | **Gap:** 5 min is long. Surface a warning in the card header after ~2 min of no events: `โš ๏ธ No events for 2m โ€” likely stuck.` |
86
+ | F11 | User manually unpins the card mid-turn | Next `pinChatMessage` in this session is never called (pin is one-shot per turn); sidecar holds stale entry until `onTurnComplete` fires `removeActivePin` after a harmless `unpinChatMessage` 400 | **Gap:** card stops being pinned but user has no visual indication the card is still live. Low priority โ€” if they unpinned it they chose to. |
87
+ | F12 | Two bots in the chat both managing pins | `sweepBotAuthoredPins` filters by `botUserId` | None. |
88
+ | F13 | `getChat().pinned_message` returns only the top pin, so a stack of bot pins requires iteration | `sweepBotAuthoredPins` loops up to `maxPerChat=32` | None. |
89
+
90
+ ## 6. Observability requirements
91
+
92
+ Current state: `process.stderr.write` lines for pin/unpin failure. That's insufficient for "insanely reliable."
93
+
94
+ Required:
95
+
96
+ 1. **Structured log event per pin/unpin**, one line JSON on stderr with prefix `pin-event:`. Fields: `event` (`pin|unpin|sweep-pin|sweep-auth`), `chatId`, `messageId`, `turnKey`, `outcome` (`ok|fail|rate-limited|forbidden`), `error?`, `durationMs`.
97
+ 2. **`/pins-status` admin command** (or extend `/status`): report current sidecar entries + in-memory `progressPinnedMsgIds` + any divergence.
98
+ 3. **Weekly self-audit** (or on boot): call `sweepBotAuthoredPins` in read-only mode across allowlisted chats and report count of bot pins not tracked in sidecar. Alarm if > 0 after a steady-state period.
99
+ 4. **Metric: pin-to-first-edit latency** โ€” time from `pinChatMessage` returning to the first subsequent `editMessageText`. Should stay under ~1s; breach indicates rate-limit pressure.
100
+ 5. **Metric: orphan sweep frequency** โ€” count of pins cleaned up by startup / bot-authored sweep per boot. Steady-state should be 0.
101
+
102
+ ## 7. Test matrix
103
+
104
+ Existing tests to keep (enumerate and reference in CI):
105
+ - `active-pins.test.ts` โ€” sidecar add/remove/read/write/idempotency/corruption.
106
+ - `active-pins-sweep.test.ts` โ€” timeout bounds, barrier semantics, max-per-chat loop.
107
+ - `progress-card.test.ts` โ€” reducer covers all `turn_end` paths, renderer produces `โœ… Done`.
108
+ - `progress-card-driver.test.ts` โ€” `isFirstEmit` fires exactly once, `onTurnComplete` fires exactly once, `initialDelayMs` suppression, parallel-turn force-close.
109
+
110
+ New tests required for this spec:
111
+
112
+ | ID | Test | Covers |
113
+ |---|---|---|
114
+ | T1 | Integration: simulate pin API failure โ†’ assert `removeActivePin` called and no stale sidecar entry | I10 |
115
+ | T2 | Integration: simulate unpin API failure โ†’ assert sidecar cleared in `.finally()`, assert next-boot sweep picks up Telegram-side stale pin via `sweepBotAuthoredPins` | F3, F5 |
116
+ | T3 | Unit: `sweepBotAuthoredPins` stops on first user-authored pin (barrier) | I8 |
117
+ | T4 | Integration: two parallel `startTurn` calls on same `chatId:threadId` โ†’ two distinct `turnKey`s, two pins, two unpins, no orphan sidecar entries at end | I7 |
118
+ | T5 | Integration: `turn_end` before `initialDelayMs` โ†’ zero emits, zero pins, sidecar untouched | I6 |
119
+ | T6 | Integration: heartbeat ticks 2 min past last event โ†’ header shows stuck-warning; 5 min โ†’ zombie close fires unpin | F10, I9 |
120
+ | T7 | Integration: boot with non-empty sidecar โ†’ sweep runs before first inbound message is processed | I4 |
121
+ | T8 | Integration: rate-limit simulation โ€” 20 rapid turns โ†’ each gets pin + unpin, no 429 surfaces to user visibly; degraded path logs structured event | F4, F5, ยง6.1 |
122
+ | T9 | Structured log assertion: every pin/unpin emits exactly one `pin-event:` JSON line with all required fields | ยง6.1 |
123
+ | T10 | Self-audit: boot-time read-only sweep reports 0 orphan pins on a clean chat | ยง6.3 |
124
+
125
+ ## 8. Implementation plan (gap-closing)
126
+
127
+ Order of work, smallest-first:
128
+
129
+ 1. **Structured pin-event logging** (~30 LOC in `server.ts` + one helper) โ€” closes ยง6.1, enables T9.
130
+ 2. **T1โ€“T5 tests** โ€” no production code changes, just formalizes existing behavior.
131
+ 3. **Stuck-warning in card header** (~15 LOC in `progress-card.ts` renderer + driver signal) โ€” closes F10 lower tier.
132
+ 4. **Unpin retry (single attempt, 1s backoff)** (~20 LOC in `unpinProgressCard`) โ€” closes F5.
133
+ 5. **`/pins-status` admin command** (~40 LOC) โ€” closes ยง6.2.
134
+ 6. **Boot-time read-only audit + metric** (~30 LOC) โ€” closes ยง6.3โ€“6.5.
135
+ 7. **T6โ€“T10 tests.**
136
+
137
+ Total estimate: ~150 LOC production + ~400 LOC tests. No schema changes. No config migration. Backwards compatible with existing sidecars.
138
+
139
+ ## 9. Out of scope (future work)
140
+
141
+ - Handoff-protocol for restart flicker (F7): needs new-process-confirms-takeover handshake. Large.
142
+ - Multi-pin stacking UX (one pin per sub-agent task): current model is one pin per parent turn; changing it requires reworking `turnKey` allocation.
143
+ - Pinning arbitrary user-selected bot messages.
144
+ - Fallback to a "sticky last message" non-pin display when `can_pin_messages` is absent.
@@ -0,0 +1,477 @@
1
+ # stream-json daemon mode โ€” architectural design
2
+
3
+ Status: **DRAFT โ€” design only, no code**.
4
+ Author: investigation + writeup by `assistant` agent, 2026-05-01.
5
+ Companion: `docs/streaming-deterministic.md` (April 2026 research notes that
6
+ informed the current `stream_reply` heuristic โ€” superseded by the findings here
7
+ for the per-token streaming question).
8
+
9
+ ---
10
+
11
+ ## 0. TL;DR
12
+
13
+ Switchroom agents currently run interactive Claude Code (`--dangerously-load-development-channels server:switchroom-telegram`) and observe model output via two derived sources: session-tail (whole assistant messages, no token-level granularity) and PTY-tail (parses the TUI rendering, structurally fragile). Per-token streaming of the assistant's reply text โ€” the Claude.ai/mobile UX Ken asked for โ€” is impossible from either source.
14
+
15
+ Verified live in this investigation: Claude Code's `--print --output-format=stream-json --include-partial-messages` mode emits raw Anthropic-API `content_block_delta` events to stdout in real time, while supporting MCP servers, sub-agents, slash commands, and skills. Multi-turn input over stdin works (`--input-format=stream-json`).
16
+
17
+ **The catch**: `--dangerously-load-development-channels server:NAME` does not surface the channel's MCP tools (`reply`, `stream_reply`, `react`, ...) to the model when the session runs in `--print` mode. Re-registering the same plugin via `--mcp-config` exposes the tools but loses the channels-as-inbound integration โ€” there's no longer a path for Telegram messages to reach the agent.
18
+
19
+ Path C โ€” "migrate switchroom agents to stream-json daemon mode" โ€” is therefore a re-architecture, not a flag change. The bridge becomes the inbound + outbound IO loop that today is split between `--channels` (inbound) and the MCP tool calls (outbound). Everything that touches the agent lifecycle (sub-agents, hooks, permission prompts, OAuth flow, slash commands, vault, restart semantics, slot-pool failover) needs revalidation under the new IO shape.
20
+
21
+ This document captures what's true today, what would change, what's at risk, and how a spike could de-risk the unknowns before committing to a migration.
22
+
23
+ ---
24
+
25
+ ## 1. What was validated in this investigation
26
+
27
+ ### 1.1 Claude Code surfaces (verified against `claude --version` 2.1.126)
28
+
29
+ | Flag | Behaviour |
30
+ |---|---|
31
+ | `--print` | One-shot mode (originally), but accepts multi-turn input when paired with `--input-format=stream-json` |
32
+ | `--input-format text\|stream-json` | Text default; stream-json reads `{"type":"user","message":...}` JSON lines from stdin and processes each as a turn |
33
+ | `--output-format text\|json\|stream-json` | stream-json emits one event per line to stdout |
34
+ | `--include-partial-messages` | Emits `stream_event` lines containing Anthropic-API `content_block_delta` events (per-token text deltas) |
35
+ | `--include-hook-events` | Emits hook lifecycle events (`PreToolUse`, `PostToolUse`, `Stop`, etc.) on the same stdout stream |
36
+ | `--replay-user-messages` | Re-emits user messages back on stdout for ack tracking |
37
+ | `--mcp-config <file>` | Loads MCP servers as regular tools the model can call |
38
+ | `--strict-mcp-config` | Restricts to MCP servers from `--mcp-config` only (excludes user/global/.mcp.json) |
39
+ | `--dangerously-load-development-channels server:NAME` | Loads an MCP plugin **as a channel** (inbound notifications + chat) |
40
+
41
+ ### 1.2 Per-token streaming verified
42
+
43
+ ```
44
+ $ echo "say one word" | claude --print --verbose \
45
+ --output-format stream-json --include-partial-messages "say hi"
46
+ {"type":"system","subtype":"init","tools":[...],"mcp_servers":[...],...}
47
+ {"type":"stream_event","event":{"type":"message_start",...}}
48
+ {"type":"stream_event","event":{"type":"content_block_delta",
49
+ "delta":{"type":"text_delta","text":"Hi there"}},...}
50
+ {"type":"stream_event","event":{"type":"content_block_delta",
51
+ "delta":{"type":"text_delta","text":", how's it going?"}},...}
52
+ {"type":"stream_event","event":{"type":"message_stop"},...}
53
+ {"type":"result","subtype":"success","result":"Hi there, how's it going?",...}
54
+ ```
55
+
56
+ Two `content_block_delta` events for a single sentence. Real per-token streaming, not whole-message atomic writes. This is the Anthropic API SSE shape forwarded verbatim to stdout.
57
+
58
+ ### 1.3 Multi-turn over stdin verified
59
+
60
+ ```
61
+ $ printf '{"type":"user","message":{"role":"user","content":"say one"}}\n{"type":"user","message":{"role":"user","content":"say two"}}\n' \
62
+ | claude --print --verbose --output-format stream-json --input-format stream-json
63
+ # Both turns get processed; per-token deltas emit for each.
64
+ ```
65
+
66
+ ### 1.4 The `--channels` blocker (negative finding)
67
+
68
+ When the same MCP server is registered as `--dangerously-load-development-channels server:switchroom-telegram` rather than `--mcp-config`:
69
+
70
+ - `init.tools` only contains built-ins + claude.ai HTTP MCP. **No `reply`, `stream_reply`, `react`, etc. exposed to the model.**
71
+ - The model, asked directly: *"No `switchroom-telegram` MCP server is connected to this session."*
72
+
73
+ Re-registering via `--mcp-config /tmp/test-mcp.json --strict-mcp-config`:
74
+
75
+ - `mcp_servers` lists `switchroom-telegram status=failed`.
76
+ - The MCP server crashes on boot โ€” likely because `server.ts` looks for the gateway IPC socket and short-circuits when missing, and the test environment has no live gateway socket.
77
+
78
+ So: no single flag invocation today both (a) surfaces the channel's tools to the model AND (b) routes Telegram inbound to it via stdin. Today's `--channels` mode does both for interactive sessions but neither path is wired for `--print --output-format=stream-json`.
79
+
80
+ ### 1.5 What the on-disk JSONL contains in stream-json mode
81
+
82
+ Inspected after the test run:
83
+
84
+ ```
85
+ $ jq -r 'type' < ~/.claude/projects/.../.../<id>.jsonl | sort -u
86
+ queue-operation
87
+ user
88
+ attachment
89
+ ai-title
90
+ assistant
91
+ last-prompt
92
+ ```
93
+
94
+ **No `stream_event` lines on disk.** Token deltas are stdout-only. `session-tail` watching the JSONL cannot become a streaming source no matter what flags get passed at invocation. This confirms `session-tail.ts:16` is right that "per-token text deltas are NOT in this file" โ€” but `session-tail.ts:5` is wrong about the *reason*. Streaming events exist, just on a different transport.
95
+
96
+ ### 1.6 Codebase docstrings to update if/when this lands
97
+
98
+ `telegram-plugin/session-tail.ts:5-8` and `telegram-plugin/pty-tail.ts:5-9` both
99
+ state Claude Code doesn't support `--output-format stream-json` in `--channels`
100
+ mode. The corrected nuance: it doesn't expose stream-json from a `--channels`
101
+ **interactive** session, but `--print` mode does support stream-json end-to-end
102
+ when MCP servers are registered via `--mcp-config`. Path C is exactly the
103
+ migration to that mode.
104
+
105
+ ---
106
+
107
+ ## 2. The architectural change in detail
108
+
109
+ ### 2.1 Current architecture (today)
110
+
111
+ ```
112
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
113
+ โ”‚ switchroom-{name}.service โ”‚ โ”‚ switchroom-{name}- โ”‚
114
+ โ”‚ โ”‚ โ”‚ gateway.service โ”‚
115
+ โ”‚ start.sh โ”‚ โ”‚ โ”‚
116
+ โ”‚ โ””โ”€ exec claude โ”‚ โ”‚ bun gateway.ts โ”‚
117
+ โ”‚ --channels server: โ”‚ โ”‚ โ”œโ”€ Telegram bot โ”‚
118
+ โ”‚ switchroom-telegram โ”‚ โ”‚ โ”œโ”€ progress card โ”‚
119
+ โ”‚ ... โ”‚ โ”‚ โ”œโ”€ slot banner โ”‚
120
+ โ”‚ โ””โ”€ child MCP plugin โ”‚โ—€โ”€โ”€โ”€โ”€โ”ค โ””โ”€ IPC socket โ”‚
121
+ โ”‚ (server.ts dynamic โ”‚ IPC โ”‚ โ”‚
122
+ โ”‚ imports bridge.ts โ”‚ โ”‚ โ”‚
123
+ โ”‚ when gateway โ”‚ โ”‚ โ”‚
124
+ โ”‚ socket detected) โ”‚ โ”‚ โ”‚
125
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
126
+ โ”‚ โ–ฒ
127
+ โ–ผ โ”‚
128
+ service.log (TUI capture) Telegram inbound
129
+ โ”œโ”€ session-tail watches JSONL polls bot.getUpdates()
130
+ โ”‚ (whole messages)
131
+ โ””โ”€ pty-tail watches log
132
+ (TUI extraction โ€” broken
133
+ on current Claude Code)
134
+ ```
135
+
136
+ Inbound Telegram โ†’ gateway polls โ†’ IPC โ†’ bridge.ts โ†’ MCP NotificationHandler โ†’ claude session.
137
+ Outbound: model calls `reply` MCP tool โ†’ server.ts/bridge.ts โ†’ IPC โ†’ gateway โ†’ Telegram API.
138
+
139
+ The MCP plugin lives inside Claude Code as a child process. It has dual identity โ€” as a "channel" it receives inbound notifications; as an "MCP server" it exposes the `reply`/`stream_reply`/`react` tools. Both come from the same `server.ts` codebase, switched on whether the gateway socket is reachable (when reachable: act as bridge; when not: legacy monolith).
140
+
141
+ ### 2.2 Proposed architecture (Path C)
142
+
143
+ ```
144
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
145
+ โ”‚ switchroom-{name}- โ”‚ โ”‚ switchroom-{name}- โ”‚
146
+ โ”‚ daemon.service โ”‚ โ”‚ gateway.service โ”‚
147
+ โ”‚ โ”‚ โ”‚ โ”‚
148
+ โ”‚ bridge-daemon.ts โ”‚ โ”‚ bun gateway.ts โ”‚
149
+ โ”‚ โ”œโ”€ spawns claude --print โ”‚ โ”‚ โ”œโ”€ Telegram bot โ”‚
150
+ โ”‚ โ”‚ --input-format โ”‚ โ”‚ โ”œโ”€ progress card โ”‚
151
+ โ”‚ โ”‚ stream-json โ”‚ โ”‚ โ”œโ”€ slot banner โ”‚
152
+ โ”‚ โ”‚ --output-format โ”‚โ—€โ”€โ”€โ”€โ”€โ”ค โ””โ”€ IPC socket โ”‚
153
+ โ”‚ โ”‚ stream-json โ”‚ IPC โ”‚ โ”‚
154
+ โ”‚ โ”‚ --include-partial- โ”‚ โ”‚ โ”‚
155
+ โ”‚ โ”‚ messages โ”‚ โ”‚ โ”‚
156
+ โ”‚ โ”‚ --include-hook-events โ”‚ โ”‚ โ”‚
157
+ โ”‚ โ”‚ --mcp-config <path> โ”‚ โ”‚ โ”‚
158
+ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
159
+ โ”‚ โ”œโ”€ claude.stdin โ”‚ โ”‚ โ”‚
160
+ โ”‚ โ”‚ โ—€โ”€โ”€ Telegram inbound โ”‚ โ”‚ โ”‚
161
+ โ”‚ โ”‚ (via gateway IPC) โ”‚ โ”‚ โ”‚
162
+ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
163
+ โ”‚ โ””โ”€ claude.stdout โ”‚ โ”‚ โ”‚
164
+ โ”‚ โ”€โ”€โ†’ stream events โ”‚ โ”‚ โ”‚
165
+ โ”‚ โ”œโ”€ content_block_ โ”‚ โ”‚ โ”‚
166
+ โ”‚ โ”‚ delta โ†’ live โ”‚ โ”‚ โ”‚
167
+ โ”‚ โ”‚ text streaming โ”‚ โ”‚ โ”‚
168
+ โ”‚ โ”œโ”€ tool_use โ†’ โ”‚ โ”‚ โ”‚
169
+ โ”‚ โ”‚ progress card โ”‚ โ”‚ โ”‚
170
+ โ”‚ โ””โ”€ result โ†’ โ”‚ โ”‚ โ”‚
171
+ โ”‚ turn complete โ”‚ โ”‚ โ”‚
172
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
173
+ ```
174
+
175
+ Key change: a new **bridge-daemon** process owns the claude lifecycle. It spawns claude with stream-json mode, pumps Telegram inbound to its stdin, and consumes stream events from its stdout. The MCP plugin is loaded via `--mcp-config` so the model still has `reply`/`stream_reply`/`react` tools โ€” but those tools now only push to Telegram via the gateway IPC (no channels-as-inbound path needed because the daemon owns inbound).
176
+
177
+ Per-token streaming: the bridge-daemon edits the pre-allocated Telegram draft on each `content_block_delta` event. The model's eventual `reply`/`stream_reply` tool call is the canonical "this is the answer" signal that finalizes the message; until then, the deltas drive a live preview.
178
+
179
+ ### 2.3 The model-vs-deltas coordination problem
180
+
181
+ The model emits text via `content_block_delta` (every token) AND eventually calls `reply(text="<full text>")` or `stream_reply(text="<full text>", done=true)`. The bridge has the same text from two sources. Choices:
182
+
183
+ **A. Deltas-only.** Render text as it streams; ignore the eventual `reply`/`stream_reply` text content (just observe the tool call as "the model has decided this turn is done"). Risk: the model's tool call is the explicit "send this" signal โ€” bypassing it changes semantics. Some agents may rely on the tool's `format`/`reply_to`/`message_thread_id` parameters for correctness (e.g. forum-topic threading). Lose those if we ignore the tool.
184
+
185
+ **B. Tool-call-only.** Stream nothing live; wait for the model's final `reply`/`stream_reply`. Loses the streaming UX entirely โ€” defeats the whole exercise.
186
+
187
+ **C. Deltas-as-preview, tool-call-as-finalize.** Stream deltas into the placeholder draft as they arrive; when the model calls `reply`/`stream_reply`, finalize the draft with that tool's text + parameters (potentially identical text, but with the tool's `format`/`reply_to`/etc. applied). This is the Claude.ai pattern. Pre-existing draft consumption logic in `stream-reply-handler.ts` is close to what's needed.
188
+
189
+ Recommendation: **C**. Most flexible, preserves tool semantics, gives the streaming UX.
190
+
191
+ Edge case: model emits a partial reply via deltas, then *doesn't* call `reply` (e.g. interrupted, error, refused). Need a turn-end watchdog that converts the partial preview into a finalized message (or deletes the placeholder). The existing orphan-cleanup logic on `turn_end` handles a similar case today.
192
+
193
+ ### 2.4 Inbound message format
194
+
195
+ Claude Code expects `--input-format=stream-json` lines like:
196
+
197
+ ```json
198
+ {"type":"user","message":{"role":"user","content":"hello"}}
199
+ ```
200
+
201
+ The bridge-daemon translates Telegram inbound to this format. Image/document attachments need to be inlined as base64 or referenced by file path (Anthropic API supports both). The MCP `download_attachment` tool currently handles this lazily; the daemon needs to either inline at inbound time or keep the tool.
202
+
203
+ Special cases the existing `--channels` integration handles that need explicit migration:
204
+
205
+ - **Forum topic threading** (`message_thread_id` is a Telegram concept; Claude has no native field for it). Today wrapped in the channel's metadata. Daemon needs to inject it into the prompt as an XML wrapper or system note so the model knows what topic to reply to.
206
+ - **Reply-to context** (Telegram quote-reply). Same treatment.
207
+ - **Multi-message coalescing** (gateway buffers rapid messages). Already happens upstream of the daemon.
208
+
209
+ ### 2.5 Why the bridge-daemon needs to be a NEW service
210
+
211
+ The existing `bridge.ts` is a child process of `claude` (loaded via dynamic import from `server.ts` when the gateway socket is detected). It can't spawn the parent claude โ€” that's the wrong direction. The daemon mode requires reversing the parent/child relationship: the bridge owns claude, claude is the child.
212
+
213
+ Practical implication: switchroom adds a third systemd unit per agent (`switchroom-{name}-daemon.service`), distinct from both the gateway and the legacy agent service. Per-agent restart semantics need rework.
214
+
215
+ ---
216
+
217
+ ## 3. Per-feature impact analysis
218
+
219
+ Each switchroom feature is currently implemented somewhere along the path `Telegram โ†’ gateway โ†’ IPC โ†’ bridge โ†’ claude โ†’ MCP tool โ†’ IPC โ†’ gateway โ†’ Telegram`. Migrating to stream-json daemon mode shifts who owns what. This table is the work.
220
+
221
+ | Feature | Current home | Daemon-mode home | Risk |
222
+ |---|---|---|---|
223
+ | **Inbound Telegram โ†’ claude** | `--channels` MCP NotificationHandler | bridge-daemon writes stream-json to claude's stdin | Medium โ€” wrap forum-topic + reply-to context as prompt prefix |
224
+ | **Outbound `reply` / `stream_reply` / `react` tools** | MCP server inside claude's child process | Same MCP server, registered via `--mcp-config`; communicates with gateway via existing IPC | Low โ€” gateway IPC contract unchanged |
225
+ | **Status reactions** (๐Ÿ‘€ ๐Ÿค” ๐Ÿ‘จโ€๐Ÿ’ป ๐Ÿ‘) | Driven by session-tail JSONL events | Driven by stream-json `tool_use` / `result` events from claude's stdout | Low โ€” equivalent event shapes, different source |
226
+ | **Pre-alloc placeholder** (`๐Ÿ”ต thinking`) | Gateway pre-allocates on inbound | Same โ€” gateway still owns chat-id-keyed pre-alloc map | Low |
227
+ | **Per-token streaming** | Doesn't work | bridge-daemon edits placeholder on each `content_block_delta` | New capability, low risk |
228
+ | **`update_placeholder` IPC** (recall.py) | Hindsight hook โ†’ IPC โ†’ gateway | Unchanged โ€” Hindsight hook still fires via `--include-hook-events` consumption OR directly via IPC | Low |
229
+ | **Progress card** | session-tail `tool_use` events โ†’ reducer โ†’ renderer | stream-json `tool_use` events โ†’ same reducer โ†’ same renderer | Low โ€” change input source, same reducer |
230
+ | **Sub-agents (Task)** | Spawned as child claude processes inheriting parent env | Verify they inherit stream-json mode; verify `parent_tool_use_id` correlation works | **MEDIUM โ€” needs spike validation** |
231
+ | **Hooks (Pre/PostToolUse, Stop)** | Run in claude's process, fire to disk + IPC | `--include-hook-events` exposes lifecycle on stream | Low โ€” better visibility |
232
+ | **Slash commands** | Typed in TUI, executed by claude | Need to verify how they're invoked via stream-json input | **MEDIUM โ€” needs spike** |
233
+ | **Permission prompts** | Interactive TUI โ†’ user types y/n | stream-json must emit a permission_request event the bridge can route to Telegram inline-keyboard, then write back the answer to stdin | **HIGH โ€” needs spike, may not exist** |
234
+ | **OAuth flow** (login, reauth, code paste) | `claude` invokes browser, user pastes code into TUI | Daemon-mode browsers? Pasting into stdin? | **HIGH โ€” likely needs separate `--print` invocation for OAuth, or daemon kept on legacy mode for auth flows** |
235
+ | **Slot-pool failover** (auto-fallback on quota) | Restart agent service with new slot env | Restart bridge-daemon with new slot env (same shape) | Low |
236
+ | **Vault** (passphrase entry, secret materialization) | Telegram inbound โ†’ MCP plugin โ†’ vault CLI | Same โ€” vault is separate process; daemon just routes | Low |
237
+ | **OAuth code redaction** (PR #490) | MCP plugin's `redactAuthCodeMessage` after exchange | Same โ€” runs in MCP server which still exists | Low |
238
+ | **`/restart`, `/reset`, `/new` commands** | Gateway intercepts, sends signal to agent service | Same intercept, restart bridge-daemon instead | Low |
239
+ | **Resume / continue** | `claude --continue` re-attaches to most recent session in cwd | Verify `--continue` works for stream-json daemon (session_id in init event suggests yes, but unverified) | **MEDIUM โ€” needs spike** |
240
+ | **Sub-agent SQLite registry** | `bridge.ts` watches sub-agent JSONLs | Daemon emits sub-agent events on stream OR daemon also runs the JSONL watchers | Medium โ€” coordinate event sources |
241
+ | **`SWITCHROOM_HANDOFF_SHOW_LINE`** (handoff briefing) | Read by start.sh, baked into `--append-system-prompt` | Same โ€” daemon constructs the same prompt prefix | Low |
242
+
243
+ Five rows flagged MEDIUM or HIGH risk. Each needs a spike or a design decision before commitment.
244
+
245
+ ---
246
+
247
+ ## 4. Migration strategy options
248
+
249
+ ### 4.1 Big-bang migration
250
+
251
+ All agents flip to daemon mode in one release. New systemd unit replaces old. Rollback = revert + restart.
252
+
253
+ **Pros**: clean codebase post-migration; no dual-mode bookkeeping.
254
+ **Cons**: blast radius is the entire fleet; one regression breaks every agent simultaneously; harder to A/B compare.
255
+
256
+ ### 4.2 Per-agent opt-in via config
257
+
258
+ Add `agent.streaming_mode: 'classic' | 'daemon'` to `switchroom.yaml`. New agents default to daemon; existing agents stay classic until manually flipped.
259
+
260
+ **Pros**: incremental rollout; easy to revert per-agent; can validate on one chat-class agent before rolling to all.
261
+ **Cons**: codebase has to support both modes for a long time; tests need to cover both; config knob is one more thing to misuse.
262
+
263
+ ### 4.3 Side-by-side A/B (parallel daemon for streaming preview only)
264
+
265
+ Keep classic agent doing all the work; spawn a SECOND `claude --print` process per agent JUST for the streaming text preview. The preview stream feeds the placeholder; the canonical message comes from the classic agent's `reply` tool.
266
+
267
+ **Pros**: zero risk to existing functionality; pure additive.
268
+ **Cons**: ~2ร— compute per agent (two model sessions); coordination is fragile (the two sessions can drift); MCP state would need to mirror; rejected on cost grounds.
269
+
270
+ **Recommended: 4.2 (per-agent opt-in)** with a chat-class agent (e.g. `clerk` or `klanker`) as the first migrant. After 1-2 weeks of stability, expand. After 4-6 weeks of the daemon being the default for all new agents and most existing ones, deprecate classic mode in a release that bumps the major version.
271
+
272
+ ---
273
+
274
+ ## 5. Spike plan โ€” what must work end-to-end
275
+
276
+ Before committing to a migration, build a spike that proves the core path. Spike acceptance:
277
+
278
+ 1. **Spawn-and-converse**: bridge-daemon spawns claude in stream-json mode, sends a Telegram-equivalent inbound to its stdin, receives `content_block_delta` events on stdout, sends an outbound to Telegram via the MCP `reply` tool. Round-trip works on a real Telegram bot.
279
+
280
+ 2. **Per-token preview**: as the model generates, the placeholder draft updates character-by-character in Telegram (verified by client screenshots/recording). The 600ms throttle from `draft-stream.ts` keeps under Telegram's edit-rate limit.
281
+
282
+ 3. **Tool-call finalize**: when the model calls `reply` or `stream_reply` after streaming via deltas, the placeholder is finalized with the tool's text + format. No duplicate messages.
283
+
284
+ 4. **Sub-agent dispatch**: model dispatches a `Task(subagent_type='worker', ...)` and gets a result. The sub-agent's events (parent_tool_use_id correlation) flow correctly.
285
+
286
+ 5. **Hook firing**: a UserPromptSubmit hook (recall.py shape) fires and emits an event the bridge can act on.
287
+
288
+ 6. **Permission prompt**: a tool requiring permission triggers something the bridge can present to Telegram (inline keyboard) and feed an answer back.
289
+
290
+ 7. **`--continue` resumption**: kill the bridge-daemon mid-conversation, restart it with `--continue`, send a new turn โ€” model has prior context.
291
+
292
+ 8. **OAuth flow**: log in, paste code, restart, agent runs. May require keeping classic mode for the auth flow itself (daemon mode after auth is established).
293
+
294
+ If all 8 work in a 1-2 week spike, Path C is feasible and the per-agent migration can begin. If 6 (permissions) or 8 (OAuth) hit a hard blocker, Path C is shelved or scoped down to "streaming-only sidecar" patterns.
295
+
296
+ ---
297
+
298
+ ## 6. Open questions / unknowns
299
+
300
+ These need answers before commit:
301
+
302
+ 1. **Does `--include-hook-events` emit hooks as inline events that can replace the file-based hook lifecycle?** Or do hooks still need to run as subprocesses and write to disk? If the latter, the daemon doesn't simplify hooks.
303
+ 2. **What does a permission prompt look like in stream-json?** Need to test interactively or find docs. If there's no event for it, switchroom needs `--permission-mode auto` or `bypassPermissions` for daemon-mode agents (loss of safety surface for some users).
304
+ 3. **How does `Task(subagent_type)` interact with stream-json?** Does the sub-agent get its own stream? Does the parent see the sub-agent's events? `parent_tool_use_id` in the init event suggests yes but unverified.
305
+ 4. **What's the real performance / cost difference?** Stream-json mode emits more events to stdout โ€” does that increase per-turn latency or memory usage? Probably negligible but unmeasured.
306
+ 5. **What happens on session compaction?** Claude Code automatically compacts long sessions; verify the daemon survives compaction without losing context.
307
+ 6. **What happens if the Hindsight HTTP server isn't ready when claude boots?** Today `start.sh` has a `HINDSIGHT_WAIT` loop. The daemon needs the same.
308
+ 7. **What about IDE integration?** Switchroom doesn't need it but verifying it doesn't conflict.
309
+ 8. **Does `switchroom auth code` still work?** OAuth code paste likely needs to remain on a non-daemon code path.
310
+
311
+ ---
312
+
313
+ ## 7. Risks
314
+
315
+ | Risk | Likelihood | Impact | Mitigation |
316
+ |---|---|---|---|
317
+ | Permission prompts can't be plumbed through stream-json | Medium | High โ€” would force `bypassPermissions` for daemon agents | Spike early; if blocked, daemon mode is opt-in for sandboxed agents only |
318
+ | Sub-agent correlation breaks | Low | High โ€” sub-agent UX is core | Spike early; existing `parent_tool_use_id` field suggests this works |
319
+ | OAuth flow incompatible with daemon mode | High | Medium โ€” auth happens once, can be a separate code path | Keep classic mode for auth flows; daemon mode after auth is established |
320
+ | Claude Code stream-json shape changes between versions | Medium | Medium โ€” Anthropic occasionally refines | Pin to a Claude Code version; integration test against captured fixtures (per `HARNESS.md` Pattern 6) |
321
+ | Daemon process crash leaves orphaned session | Medium | Low โ€” `--continue` recovers | systemd `Restart=on-failure` + boot-time `--continue` (already pattern) |
322
+ | Telegram edit-rate limit hits during fast streams | Low | Low โ€” existing 600ms throttle handles | Reuse `draft-stream.ts` throttle; same code path |
323
+ | Coordination between deltas + model's `reply` call ships duplicate messages | Medium | Medium โ€” primary UX bug class | "Deltas as preview, reply as finalize" pattern (3.3 option C); regression tests |
324
+ | Migration breaks existing PR review queue (open branches assume classic mode) | Medium | Low | Coordinate with active PR authors before flipping defaults |
325
+
326
+ ---
327
+
328
+ ## 8. Decision points
329
+
330
+ Before this PR could be merged, the following need to be decided:
331
+
332
+ 1. **Is per-token streaming worth this scope?** If "good enough" UX comes from Path A (heartbeat placeholder with semantic chunks every 3-5s), Path C may be over-engineering for the stated goal.
333
+ 2. **Is switchroom willing to own the IO loop?** Today switchroom inherits Claude Code's TUI lifecycle and just instruments around it. Path C makes switchroom the parent process โ€” closer to OpenClaw's model. Architectural shift.
334
+ 3. **Per-agent opt-in vs big-bang?** ยง4 lays out the options; recommendation is opt-in.
335
+ 4. **How long does the spike get?** Suggest 1-2 weeks for the 8 acceptance items in ยง5; if they all work, commit; if not, retreat to Path A with confidence.
336
+ 5. **What's the deprecation timeline for classic mode?** If we're keeping it long-term, code complexity stays; if we're aiming to delete it, deprecation announcement is a separate user-facing comm.
337
+
338
+ ---
339
+
340
+ ## 9. Alternatives considered
341
+
342
+ ### 9.1 Path A โ€” heartbeat placeholder (RECOMMENDED for now)
343
+
344
+ 50 LOC, no architectural change. Edit the placeholder every 3-5s with elapsed time + last tool used. User sees `๐Ÿ”ต thinking ยท 5s ยท reading CLAUDE.md` etc. Doesn't deliver per-token streaming but never silent for >5s โ€” matches the OpenClaw "no silent gap > 2s" rule from #303.
345
+
346
+ **Why prefer over Path C**: ships in a day; zero migration risk; achieves the stated UX goal ("not silent for so long"); doesn't preclude Path C later.
347
+
348
+ ### 9.2 Path B โ€” stream-json sidecar reader
349
+
350
+ Run a SECOND claude process per agent in `--print --output-format=stream-json` mode just to capture stream events for streaming UX. Verified during this investigation: this doesn't work because there's no way to attach a stream reader to an existing interactive session โ€” the secondary instance would be a separate conversation, not an observer of the primary.
351
+
352
+ **Rejected**.
353
+
354
+ ### 9.3 Fix V1Extractor for new TUI format
355
+
356
+ Reverse-engineer current Claude Code TUI rendering, update `pty-tail.ts:111-216` to match. Would restore PTY-based streaming.
357
+
358
+ **Rejected**: structurally fragile; the test added in PR #507 already documents this pattern as the wrong tool. Even fixed, breaks again on next Claude Code TUI change.
359
+
360
+ ### 9.4 Force model to use `stream_reply` via stricter prompting / hooks
361
+
362
+ Already the default `profiles/default/CLAUDE.md` instruction. Model compliance is variable. A `Stop` hook could veto turns that didn't call `stream_reply` โ€” heavyweight, brittle, and PR #483 already removed the rejection that punished it.
363
+
364
+ **Rejected as standalone**, but compatible with Path A or Path C.
365
+
366
+ ### 9.5 OpenClaw model โ€” custom runtime
367
+
368
+ Re-implement Claude's agent loop with full SDK control. Switchroom explicitly chose NOT to do this (`docs/vs-openclaw.md`); reversing that decision is a much bigger conversation than streaming UX.
369
+
370
+ **Out of scope** โ€” would not be the same product.
371
+
372
+ ---
373
+
374
+ ## 10. Recommended path forward
375
+
376
+ 1. **Ship Path A (heartbeat placeholder) immediately** โ€” closes the user-visible gap with low risk. Tracks in a separate small PR.
377
+ 2. **File this design doc as an architectural issue** linking to all the validation evidence. Solicit feedback before any spike.
378
+ 3. **Schedule a 1-2 week spike** if/when the team commits to Path C, against the 8 acceptance items in ยง5. Spike outcome is binary: green-light migration, or shelve and stick with Path A long-term.
379
+ 4. **Update the codebase docstrings** (`session-tail.ts:5`, `pty-tail.ts:5`) with the corrected nuance about stream-json availability โ€” independent of any migration commitment.
380
+ 5. **Capture this investigation in `HARNESS.md`** as a worked example of the "validate assumptions against the upstream surface" pattern.
381
+
382
+ The validation finding that Claude Code DOES support per-token streaming changes the long-term architecture conversation. Whether to act on it now is a strategic question, not a tactical one.
383
+
384
+ ---
385
+
386
+ ## 11. Spike results โ€” 2026-05-01
387
+
388
+ Spike executed against the 8 acceptance items in ยง5. Each test was a real
389
+ shell invocation against `claude` 2.1.126 with shell-captured output.
390
+ Findings supersede earlier theory.
391
+
392
+ ### 11.1 Pass/fail summary
393
+
394
+ | # | Item | Result | Notes |
395
+ |---|---|---|---|
396
+ | 1 | Spawn-and-converse | โœ… PASS | Multi-turn over stdin works; init event lists tools+MCP servers correctly |
397
+ | 2 | Per-token preview | โœ… PASS | 200-word reply: 21 deltas at ~3/sec, ~50 chars/delta. With 600ms throttle = ~1-2 edits/sec, well under Telegram's 30/sec hard cap |
398
+ | 3 | Tool-call finalize coordination | โœ… PASS | `content_block_start.event.content_block.type` distinguishes text/thinking/tool_use blocks โ†’ bridge can stream the right content |
399
+ | 4 | Sub-agents (Task) | โœ… PASS | `parent_tool_use_id` correlates sub-agent events to their parent dispatch; full event lifecycle observable |
400
+ | 5 | Hooks fire and emit events | โœ… PASS | `--include-hook-events` produces `system/hook_started` + `system/hook_response` with full stdout/stderr/exit. Richer than today's IPC-only path |
401
+ | 6 | **Permission prompts** | โŒ **HARD BLOCKER** | `--print --permission-mode default` auto-denies tools requiring permission. NO event in stream surfaces the request. The `notifications/claude/channel/permission_request` side-channel does NOT fire when running in `--print` mode, even with `--dangerously-load-development-channels` set |
402
+ | 7 | `--resume <session_id>` | โœ… PASS | Conversation context survives across `claude --print` invocations. Tested: set "favourite color is teal" in turn 1, killed session, restarted with `--resume`, model recalled "teal" correctly |
403
+ | 8 | OAuth flow | โš  DEFERRED | Likely requires keeping classic mode for the auth flow itself. Not blocking the architectural decision but adds operational complexity |
404
+
405
+ **6 of 7 testable items pass. 1 hard blocker (#6).**
406
+
407
+ ### 11.2 Spike #6 โ€” the permission blocker in detail
408
+
409
+ Test setup: `claude --print --verbose --output-format=stream-json --input-format=stream-json --include-partial-messages --include-hook-events --dangerously-load-development-channels server:switchroom-telegram --permission-mode default` with a prompt asking the model to write a file via the Write tool.
410
+
411
+ Result:
412
+ ```
413
+ permission_denials: [{
414
+ "tool_name": "Write",
415
+ "tool_use_id": "toolu_...",
416
+ "tool_input": {"file_path": "...", "content": "hello spike"}
417
+ }]
418
+
419
+ tool_result.is_error: true
420
+ tool_result.content: "Claude requested permissions to write to ...,
421
+ but you haven't granted it yet."
422
+ ```
423
+
424
+ The Write tool was auto-denied. There was NO event in the stream that:
425
+ - Asked for user approval
426
+ - Allowed the bridge to route the request to Telegram
427
+ - Took an answer back to grant permission
428
+
429
+ The existing switchroom Telegram inline-keyboard permission flow at `server.ts:1192-1228` depends on the `notifications/claude/channel/permission_request` MCP notification. **That notification doesn't fire in `--print` mode** โ€” even when `--dangerously-load-development-channels` is set. The denial happens upstream of the channel notification, so there's no way to surface the prompt to the user.
430
+
431
+ ### 11.3 What this means for Path C
432
+
433
+ The blocker is real but not necessarily fatal. Three response strategies, in order of practical viability:
434
+
435
+ **Strategy A โ€” `--permission-mode bypassPermissions` for daemon-mode agents**
436
+ - Simplest: agents in daemon mode skip all permission checks.
437
+ - Acceptable if all agents run on a trusted single-user host and tools are limited to a known-safe set (Telegram MCP, Hindsight MCP, Read/Grep/Glob).
438
+ - Real safety regression for agents that might use Bash/Edit/Write with broad access. Today's interactive Telegram approval flow stops the model from accidentally `rm -rf` or sending stupid emails; bypassPermissions removes that gate.
439
+ - For switchroom's chat-class agents (clerk, klanker, gymbro, finn โ€” mostly use MCP tools, rarely Bash/Edit) this is probably fine.
440
+ - For development-y agents that legitimately edit code or run arbitrary commands, this is a meaningful loss.
441
+
442
+ **Strategy B โ€” pre-approve every needed tool in `settings.json`**
443
+ - `permissions.allow` array can pre-approve tool patterns: `["Read", "Grep", "Bash(git status)", ...]`
444
+ - Agent never asks the user because everything is pre-approved.
445
+ - High maintenance burden โ€” every new tool/command needs to be added explicitly.
446
+ - Defeats the dynamic-approval benefit but preserves "no surprise approvals" safety.
447
+
448
+ **Strategy C โ€” file an upstream feature request to Anthropic**
449
+ - Ask Claude Code to emit `permission_request` as a stream event in `--print` mode that an external tool (the bridge) can answer via stdin.
450
+ - This is the architecturally correct fix. Other tools using `--print --output-format=stream-json` (the agent SDK pattern) would benefit too.
451
+ - Realistic timeline: weeks-to-months, with non-zero risk of "won't ship."
452
+ - Recommended regardless of which path switchroom takes locally.
453
+
454
+ ### 11.4 Updated verdict on Path C
455
+
456
+ **Pre-spike confidence**: 70% reliable, conditional on 3 unknowns clearing.
457
+ **Post-spike confidence**: ~85% for chat-class agents in `bypassPermissions` mode. ~50% for general-purpose agents without an upstream change.
458
+
459
+ The remaining items (sub-agents, --resume, hooks) all came in cleaner than expected. Hooks in particular are a meaningful upgrade โ€” `system/hook_started` + `system/hook_response` events expose the lifecycle on the wire, replacing the file-based IPC patterns with first-class stream observability.
460
+
461
+ But the permission-prompt blocker is a real architectural cost. Path C's quality depends on which of A/B/C in ยง11.3 the team accepts.
462
+
463
+ ### 11.5 Revised recommendation (post-spike)
464
+
465
+ 1. **Ship Path A (heartbeat placeholder) immediately** โ€” unchanged from ยง10.
466
+ 2. **Defer Path C migration** until either:
467
+ - Anthropic adds permission events to stream-json (Strategy C in ยง11.3), OR
468
+ - Switchroom commits to `bypassPermissions` mode for chat-class agents and accepts the safety tradeoff.
469
+ 3. **Update the architectural risk register** โ€” the design doc previously listed permission prompts as MEDIUM risk requiring spike validation. Spike confirms it's HIGH risk requiring an explicit policy decision before commitment.
470
+
471
+ The spike was worth running: it converted a HIGH-risk unknown into a HIGH-risk known, which is exactly what spikes are for. Path C remains feasible but the quality bar is now lower than originally projected, and the chat-class-only scope is more honest than "all agents."
472
+
473
+ ### 11.6 What's been actively retired by this validation
474
+
475
+ - The PTY-tail / V1Extractor approach is now confirmed as the wrong architectural direction (it predates the per-token streaming surface that already exists).
476
+ - The "session-tail can become a streaming source" idea is also retired โ€” verified by inspecting the on-disk JSONL: it contains `assistant`/`user`/`system` events but no `stream_event` lines, regardless of `--include-partial-messages` flag at invocation.
477
+ - The middle-ground "heartbeat + tool labels via session-tail" hybrid is the WORST option (paying maintenance cost for moderate UX). Either pure heartbeat (no extractor) or commit to Path C.