@llblab/pi-kit 0.1.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 (523) hide show
  1. package/AGENTS.md +35 -0
  2. package/BACKLOG.md +3 -0
  3. package/CHANGELOG.md +9 -0
  4. package/LICENSE +21 -0
  5. package/README.md +43 -0
  6. package/node_modules/@llblab/pi-actors/AGENTS.md +150 -0
  7. package/node_modules/@llblab/pi-actors/BACKLOG.md +6 -0
  8. package/node_modules/@llblab/pi-actors/CHANGELOG.md +865 -0
  9. package/node_modules/@llblab/pi-actors/README.md +231 -0
  10. package/node_modules/@llblab/pi-actors/banner.jpg +0 -0
  11. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/artifact-manifest.json +9 -0
  12. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/control-endpoint.json +6 -0
  13. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/control-record.json +9 -0
  14. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/recipe-summary.json +8 -0
  15. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/run-state.json +10 -0
  16. package/node_modules/@llblab/pi-actors/dist/fixtures/protocol/trace-event.json +9 -0
  17. package/node_modules/@llblab/pi-actors/dist/index.d.ts +7 -0
  18. package/node_modules/@llblab/pi-actors/dist/index.js +17 -0
  19. package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +146 -0
  20. package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +800 -0
  21. package/node_modules/@llblab/pi-actors/dist/lib/automatic-review-runtime.d.ts +20 -0
  22. package/node_modules/@llblab/pi-actors/dist/lib/automatic-review-runtime.js +103 -0
  23. package/node_modules/@llblab/pi-actors/dist/lib/command-templates.d.ts +85 -0
  24. package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +804 -0
  25. package/node_modules/@llblab/pi-actors/dist/lib/config.d.ts +34 -0
  26. package/node_modules/@llblab/pi-actors/dist/lib/config.js +216 -0
  27. package/node_modules/@llblab/pi-actors/dist/lib/control-projection.d.ts +20 -0
  28. package/node_modules/@llblab/pi-actors/dist/lib/control-projection.js +66 -0
  29. package/node_modules/@llblab/pi-actors/dist/lib/control.d.ts +15 -0
  30. package/node_modules/@llblab/pi-actors/dist/lib/control.js +97 -0
  31. package/node_modules/@llblab/pi-actors/dist/lib/draft-consolidation-transaction.d.ts +65 -0
  32. package/node_modules/@llblab/pi-actors/dist/lib/draft-consolidation-transaction.js +610 -0
  33. package/node_modules/@llblab/pi-actors/dist/lib/draft-consolidation.d.ts +35 -0
  34. package/node_modules/@llblab/pi-actors/dist/lib/draft-consolidation.js +126 -0
  35. package/node_modules/@llblab/pi-actors/dist/lib/draft-review.d.ts +56 -0
  36. package/node_modules/@llblab/pi-actors/dist/lib/draft-review.js +254 -0
  37. package/node_modules/@llblab/pi-actors/dist/lib/draft-sleep.d.ts +65 -0
  38. package/node_modules/@llblab/pi-actors/dist/lib/draft-sleep.js +467 -0
  39. package/node_modules/@llblab/pi-actors/dist/lib/execution-output.d.ts +25 -0
  40. package/node_modules/@llblab/pi-actors/dist/lib/execution-output.js +88 -0
  41. package/node_modules/@llblab/pi-actors/dist/lib/execution-sessions.d.ts +17 -0
  42. package/node_modules/@llblab/pi-actors/dist/lib/execution-sessions.js +85 -0
  43. package/node_modules/@llblab/pi-actors/dist/lib/execution.d.ts +99 -0
  44. package/node_modules/@llblab/pi-actors/dist/lib/execution.js +725 -0
  45. package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.d.ts +21 -0
  46. package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +141 -0
  47. package/node_modules/@llblab/pi-actors/dist/lib/file-state.d.ts +18 -0
  48. package/node_modules/@llblab/pi-actors/dist/lib/file-state.js +310 -0
  49. package/node_modules/@llblab/pi-actors/dist/lib/identity.d.ts +9 -0
  50. package/node_modules/@llblab/pi-actors/dist/lib/identity.js +27 -0
  51. package/node_modules/@llblab/pi-actors/dist/lib/inspector-actions.d.ts +16 -0
  52. package/node_modules/@llblab/pi-actors/dist/lib/inspector-actions.js +57 -0
  53. package/node_modules/@llblab/pi-actors/dist/lib/inspector-command.d.ts +7 -0
  54. package/node_modules/@llblab/pi-actors/dist/lib/inspector-command.js +37 -0
  55. package/node_modules/@llblab/pi-actors/dist/lib/inspector-overlay.d.ts +100 -0
  56. package/node_modules/@llblab/pi-actors/dist/lib/inspector-overlay.js +824 -0
  57. package/node_modules/@llblab/pi-actors/dist/lib/inspector.d.ts +20 -0
  58. package/node_modules/@llblab/pi-actors/dist/lib/inspector.js +136 -0
  59. package/node_modules/@llblab/pi-actors/dist/lib/limits.d.ts +28 -0
  60. package/node_modules/@llblab/pi-actors/dist/lib/limits.js +28 -0
  61. package/node_modules/@llblab/pi-actors/dist/lib/model-context.d.ts +56 -0
  62. package/node_modules/@llblab/pi-actors/dist/lib/model-context.js +220 -0
  63. package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +186 -0
  64. package/node_modules/@llblab/pi-actors/dist/lib/observability.js +951 -0
  65. package/node_modules/@llblab/pi-actors/dist/lib/paths.d.ts +27 -0
  66. package/node_modules/@llblab/pi-actors/dist/lib/paths.js +72 -0
  67. package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +19 -0
  68. package/node_modules/@llblab/pi-actors/dist/lib/pi.js +21 -0
  69. package/node_modules/@llblab/pi-actors/dist/lib/preflight-diagnostics.d.ts +27 -0
  70. package/node_modules/@llblab/pi-actors/dist/lib/preflight-diagnostics.js +86 -0
  71. package/node_modules/@llblab/pi-actors/dist/lib/prompts.d.ts +24 -0
  72. package/node_modules/@llblab/pi-actors/dist/lib/prompts.js +49 -0
  73. package/node_modules/@llblab/pi-actors/dist/lib/recipe-control.d.ts +7 -0
  74. package/node_modules/@llblab/pi-actors/dist/lib/recipe-control.js +43 -0
  75. package/node_modules/@llblab/pi-actors/dist/lib/recipes-context.d.ts +36 -0
  76. package/node_modules/@llblab/pi-actors/dist/lib/recipes-context.js +197 -0
  77. package/node_modules/@llblab/pi-actors/dist/lib/recipes-discovery.d.ts +77 -0
  78. package/node_modules/@llblab/pi-actors/dist/lib/recipes-discovery.js +783 -0
  79. package/node_modules/@llblab/pi-actors/dist/lib/recipes-references.d.ts +113 -0
  80. package/node_modules/@llblab/pi-actors/dist/lib/recipes-references.js +1108 -0
  81. package/node_modules/@llblab/pi-actors/dist/lib/recipes-usage.d.ts +21 -0
  82. package/node_modules/@llblab/pi-actors/dist/lib/recipes-usage.js +494 -0
  83. package/node_modules/@llblab/pi-actors/dist/lib/registry.d.ts +67 -0
  84. package/node_modules/@llblab/pi-actors/dist/lib/registry.js +504 -0
  85. package/node_modules/@llblab/pi-actors/dist/lib/review-control.d.ts +14 -0
  86. package/node_modules/@llblab/pi-actors/dist/lib/review-control.js +110 -0
  87. package/node_modules/@llblab/pi-actors/dist/lib/review-diagnostics.d.ts +11 -0
  88. package/node_modules/@llblab/pi-actors/dist/lib/review-diagnostics.js +148 -0
  89. package/node_modules/@llblab/pi-actors/dist/lib/review-projection.d.ts +14 -0
  90. package/node_modules/@llblab/pi-actors/dist/lib/review-projection.js +166 -0
  91. package/node_modules/@llblab/pi-actors/dist/lib/run-evidence-policy.d.ts +95 -0
  92. package/node_modules/@llblab/pi-actors/dist/lib/run-evidence-policy.js +177 -0
  93. package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.d.ts +18 -0
  94. package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +125 -0
  95. package/node_modules/@llblab/pi-actors/dist/lib/runs-artifacts.d.ts +21 -0
  96. package/node_modules/@llblab/pi-actors/dist/lib/runs-artifacts.js +68 -0
  97. package/node_modules/@llblab/pi-actors/dist/lib/runs-control-delivery.d.ts +28 -0
  98. package/node_modules/@llblab/pi-actors/dist/lib/runs-control-delivery.js +150 -0
  99. package/node_modules/@llblab/pi-actors/dist/lib/runs-control.d.ts +21 -0
  100. package/node_modules/@llblab/pi-actors/dist/lib/runs-control.js +89 -0
  101. package/node_modules/@llblab/pi-actors/dist/lib/runs-controls.d.ts +39 -0
  102. package/node_modules/@llblab/pi-actors/dist/lib/runs-controls.js +283 -0
  103. package/node_modules/@llblab/pi-actors/dist/lib/runs-identity.d.ts +5 -0
  104. package/node_modules/@llblab/pi-actors/dist/lib/runs-identity.js +11 -0
  105. package/node_modules/@llblab/pi-actors/dist/lib/runs-index.d.ts +27 -0
  106. package/node_modules/@llblab/pi-actors/dist/lib/runs-index.js +142 -0
  107. package/node_modules/@llblab/pi-actors/dist/lib/runs-ownership.d.ts +7 -0
  108. package/node_modules/@llblab/pi-actors/dist/lib/runs-ownership.js +96 -0
  109. package/node_modules/@llblab/pi-actors/dist/lib/runs-parent-teardown.d.ts +51 -0
  110. package/node_modules/@llblab/pi-actors/dist/lib/runs-parent-teardown.js +172 -0
  111. package/node_modules/@llblab/pi-actors/dist/lib/runs-process.d.ts +22 -0
  112. package/node_modules/@llblab/pi-actors/dist/lib/runs-process.js +125 -0
  113. package/node_modules/@llblab/pi-actors/dist/lib/runs-retention.d.ts +18 -0
  114. package/node_modules/@llblab/pi-actors/dist/lib/runs-retention.js +108 -0
  115. package/node_modules/@llblab/pi-actors/dist/lib/runs-start.d.ts +12 -0
  116. package/node_modules/@llblab/pi-actors/dist/lib/runs-start.js +68 -0
  117. package/node_modules/@llblab/pi-actors/dist/lib/runs-status.d.ts +10 -0
  118. package/node_modules/@llblab/pi-actors/dist/lib/runs-status.js +68 -0
  119. package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.d.ts +48 -0
  120. package/node_modules/@llblab/pi-actors/dist/lib/runs-trace.js +491 -0
  121. package/node_modules/@llblab/pi-actors/dist/lib/runtime-identity.d.ts +7 -0
  122. package/node_modules/@llblab/pi-actors/dist/lib/runtime-identity.js +35 -0
  123. package/node_modules/@llblab/pi-actors/dist/lib/runtime-triage.d.ts +29 -0
  124. package/node_modules/@llblab/pi-actors/dist/lib/runtime-triage.js +60 -0
  125. package/node_modules/@llblab/pi-actors/dist/lib/runtime.d.ts +69 -0
  126. package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +339 -0
  127. package/node_modules/@llblab/pi-actors/dist/lib/schema.d.ts +60 -0
  128. package/node_modules/@llblab/pi-actors/dist/lib/schema.js +428 -0
  129. package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.d.ts +45 -0
  130. package/node_modules/@llblab/pi-actors/dist/lib/session-evidence.js +204 -0
  131. package/node_modules/@llblab/pi-actors/dist/lib/state-readers.d.ts +21 -0
  132. package/node_modules/@llblab/pi-actors/dist/lib/state-readers.js +74 -0
  133. package/node_modules/@llblab/pi-actors/dist/lib/temp.d.ts +10 -0
  134. package/node_modules/@llblab/pi-actors/dist/lib/temp.js +90 -0
  135. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-lineage-transaction.d.ts +27 -0
  136. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-lineage-transaction.js +597 -0
  137. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-lineage.d.ts +24 -0
  138. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-lineage.js +98 -0
  139. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-scheduler.d.ts +80 -0
  140. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-scheduler.js +494 -0
  141. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-transaction.d.ts +50 -0
  142. package/node_modules/@llblab/pi-actors/dist/lib/tool-review-transaction.js +362 -0
  143. package/node_modules/@llblab/pi-actors/dist/lib/tool-review.d.ts +56 -0
  144. package/node_modules/@llblab/pi-actors/dist/lib/tool-review.js +197 -0
  145. package/node_modules/@llblab/pi-actors/dist/lib/tools-access.d.ts +19 -0
  146. package/node_modules/@llblab/pi-actors/dist/lib/tools-access.js +44 -0
  147. package/node_modules/@llblab/pi-actors/dist/lib/tools-inspect.d.ts +15 -0
  148. package/node_modules/@llblab/pi-actors/dist/lib/tools-inspect.js +532 -0
  149. package/node_modules/@llblab/pi-actors/dist/lib/tools-local.d.ts +17 -0
  150. package/node_modules/@llblab/pi-actors/dist/lib/tools-local.js +191 -0
  151. package/node_modules/@llblab/pi-actors/dist/lib/tools-message.d.ts +9 -0
  152. package/node_modules/@llblab/pi-actors/dist/lib/tools-message.js +139 -0
  153. package/node_modules/@llblab/pi-actors/dist/lib/tools-register.d.ts +18 -0
  154. package/node_modules/@llblab/pi-actors/dist/lib/tools-register.js +41 -0
  155. package/node_modules/@llblab/pi-actors/dist/lib/tools-response.d.ts +20 -0
  156. package/node_modules/@llblab/pi-actors/dist/lib/tools-response.js +243 -0
  157. package/node_modules/@llblab/pi-actors/dist/lib/tools-spawn.d.ts +15 -0
  158. package/node_modules/@llblab/pi-actors/dist/lib/tools-spawn.js +133 -0
  159. package/node_modules/@llblab/pi-actors/dist/lib/tools.d.ts +28 -0
  160. package/node_modules/@llblab/pi-actors/dist/lib/tools.js +49 -0
  161. package/node_modules/@llblab/pi-actors/dist/lib/trace-projection.d.ts +22 -0
  162. package/node_modules/@llblab/pi-actors/dist/lib/trace-projection.js +231 -0
  163. package/node_modules/@llblab/pi-actors/dist/pi-actors/index.js +1 -0
  164. package/node_modules/@llblab/pi-actors/dist/scripts/async-runner.mjs +555 -0
  165. package/node_modules/@llblab/pi-actors/dist/scripts/build-dist.mjs +60 -0
  166. package/node_modules/@llblab/pi-actors/dist/scripts/conformance.mjs +63 -0
  167. package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +126 -0
  168. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/command-validate.json +13 -0
  169. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/jsonl-tail.json +10 -0
  170. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/recipe-validate.json +11 -0
  171. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/resource-locker-snapshot.json +11 -0
  172. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/resource-locker.json +25 -0
  173. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/run-ops-snapshot.json +15 -0
  174. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/run-state-files.json +13 -0
  175. package/node_modules/@llblab/pi-actors/dist/skills/actors/recipes/run-summary.json +9 -0
  176. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/diagnostics.md +44 -0
  177. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/persistent-tools.md +76 -0
  178. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/recipes.md +51 -0
  179. package/node_modules/@llblab/pi-actors/dist/skills/actors/references/runs.md +41 -0
  180. package/node_modules/@llblab/pi-actors/dist/skills/actors/scripts/resource-locker.mjs +425 -0
  181. package/node_modules/@llblab/pi-actors/dist/skills/actors/scripts/run-utils.mjs +164 -0
  182. package/node_modules/@llblab/pi-actors/dist/skills/actors/scripts/validate-recipe.mjs +370 -0
  183. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/SKILL.md +33 -0
  184. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/recipes/bundle.json +77 -0
  185. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/recipes/file-write.json +14 -0
  186. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/recipes/manifest.json +14 -0
  187. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/recipes/report.json +39 -0
  188. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/recipes/write.json +53 -0
  189. package/node_modules/@llblab/pi-actors/dist/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  190. package/node_modules/@llblab/pi-actors/dist/skills/music-player/SKILL.md +57 -0
  191. package/node_modules/@llblab/pi-actors/dist/skills/music-player/genapps/music-player.mjs +360 -0
  192. package/node_modules/@llblab/pi-actors/dist/skills/music-player/recipes/playback.json +29 -0
  193. package/node_modules/@llblab/pi-actors/dist/skills/music-player/scripts/playback-client.mjs +143 -0
  194. package/node_modules/@llblab/pi-actors/dist/skills/music-player/scripts/playback.mjs +1446 -0
  195. package/node_modules/@llblab/pi-actors/dist/skills/project-work/SKILL.md +37 -0
  196. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/changelog-head.json +11 -0
  197. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/changelog-section.json +11 -0
  198. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/docs-maintenance.json +66 -0
  199. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/git-log.json +11 -0
  200. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/git-status.json +9 -0
  201. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/markdown-index.json +14 -0
  202. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/package-summary.json +9 -0
  203. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/release-readiness.json +115 -0
  204. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/release-summary.json +74 -0
  205. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/repo-health.json +75 -0
  206. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/run-ops.json +56 -0
  207. package/node_modules/@llblab/pi-actors/dist/skills/project-work/recipes/skill-summary.json +11 -0
  208. package/node_modules/@llblab/pi-actors/dist/skills/project-work/scripts/project-utils.mjs +89 -0
  209. package/node_modules/@llblab/pi-actors/dist/skills/recipe-memory/SKILL.md +36 -0
  210. package/node_modules/@llblab/pi-actors/dist/skills/recipe-memory/recipes/draft-review.json +14 -0
  211. package/node_modules/@llblab/pi-actors/dist/skills/recipe-memory/recipes/tool-review.json +14 -0
  212. package/node_modules/@llblab/pi-actors/dist/skills/swarm/SKILL.md +84 -0
  213. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/architect.json +82 -0
  214. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/checkpoint-continuation.json +51 -0
  215. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/development-tasking.json +67 -0
  216. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/lens-review.json +63 -0
  217. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/quorum-review.json +65 -0
  218. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/research-synthesis.json +81 -0
  219. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/review-readiness.json +59 -0
  220. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-artifact.json +21 -0
  221. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-checkpoint.json +21 -0
  222. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-conflict-report.json +19 -0
  223. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-contradiction-map.json +20 -0
  224. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-critic.json +22 -0
  225. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-evidence-map.json +20 -0
  226. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-followup.json +21 -0
  227. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-judge.json +22 -0
  228. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-merge.json +24 -0
  229. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-normalize.json +20 -0
  230. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-plan.json +20 -0
  231. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-preflight.json +18 -0
  232. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-prompt.json +16 -0
  233. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-prompts.json +39 -0
  234. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-quorum.json +31 -0
  235. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-review-coordinator.json +170 -0
  236. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-review.json +26 -0
  237. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-task-card.json +22 -0
  238. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-tools.json +15 -0
  239. package/node_modules/@llblab/pi-actors/dist/skills/swarm/recipes/subagent-verify.json +23 -0
  240. package/node_modules/@llblab/pi-actors/dist/skills/swarm/references/development-swarm.md +189 -0
  241. package/node_modules/@llblab/pi-actors/dist/skills/swarm/references/review-swarms.md +115 -0
  242. package/node_modules/@llblab/pi-actors/docs/README.md +20 -0
  243. package/node_modules/@llblab/pi-actors/docs/actor-inspector.md +50 -0
  244. package/node_modules/@llblab/pi-actors/docs/async-runs.md +141 -0
  245. package/node_modules/@llblab/pi-actors/docs/command-templates.md +321 -0
  246. package/node_modules/@llblab/pi-actors/docs/recipe-library.md +89 -0
  247. package/node_modules/@llblab/pi-actors/docs/releasing.md +28 -0
  248. package/node_modules/@llblab/pi-actors/docs/template-recipes.md +175 -0
  249. package/node_modules/@llblab/pi-actors/docs/tool-registry.md +90 -0
  250. package/node_modules/@llblab/pi-actors/fixtures/protocol/artifact-manifest.json +9 -0
  251. package/node_modules/@llblab/pi-actors/fixtures/protocol/control-endpoint.json +6 -0
  252. package/node_modules/@llblab/pi-actors/fixtures/protocol/control-record.json +9 -0
  253. package/node_modules/@llblab/pi-actors/fixtures/protocol/recipe-summary.json +8 -0
  254. package/node_modules/@llblab/pi-actors/fixtures/protocol/run-state.json +10 -0
  255. package/node_modules/@llblab/pi-actors/fixtures/protocol/trace-event.json +9 -0
  256. package/node_modules/@llblab/pi-actors/index.ts +30 -0
  257. package/node_modules/@llblab/pi-actors/lib/async-runs.ts +1245 -0
  258. package/node_modules/@llblab/pi-actors/lib/automatic-review-runtime.ts +148 -0
  259. package/node_modules/@llblab/pi-actors/lib/command-templates.ts +1116 -0
  260. package/node_modules/@llblab/pi-actors/lib/config.ts +261 -0
  261. package/node_modules/@llblab/pi-actors/lib/control-projection.ts +105 -0
  262. package/node_modules/@llblab/pi-actors/lib/control.ts +117 -0
  263. package/node_modules/@llblab/pi-actors/lib/draft-consolidation-transaction.ts +821 -0
  264. package/node_modules/@llblab/pi-actors/lib/draft-consolidation.ts +181 -0
  265. package/node_modules/@llblab/pi-actors/lib/draft-review.ts +325 -0
  266. package/node_modules/@llblab/pi-actors/lib/draft-sleep.ts +575 -0
  267. package/node_modules/@llblab/pi-actors/lib/execution-output.ts +123 -0
  268. package/node_modules/@llblab/pi-actors/lib/execution-sessions.ts +111 -0
  269. package/node_modules/@llblab/pi-actors/lib/execution.ts +1153 -0
  270. package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +181 -0
  271. package/node_modules/@llblab/pi-actors/lib/file-state.ts +306 -0
  272. package/node_modules/@llblab/pi-actors/lib/identity.ts +29 -0
  273. package/node_modules/@llblab/pi-actors/lib/inspector-actions.ts +79 -0
  274. package/node_modules/@llblab/pi-actors/lib/inspector-command.ts +54 -0
  275. package/node_modules/@llblab/pi-actors/lib/inspector-overlay.ts +901 -0
  276. package/node_modules/@llblab/pi-actors/lib/inspector.ts +204 -0
  277. package/node_modules/@llblab/pi-actors/lib/limits.ts +29 -0
  278. package/node_modules/@llblab/pi-actors/lib/model-context.ts +359 -0
  279. package/node_modules/@llblab/pi-actors/lib/observability.ts +1337 -0
  280. package/node_modules/@llblab/pi-actors/lib/paths.ts +111 -0
  281. package/node_modules/@llblab/pi-actors/lib/pi.ts +47 -0
  282. package/node_modules/@llblab/pi-actors/lib/preflight-diagnostics.ts +132 -0
  283. package/node_modules/@llblab/pi-actors/lib/prompts.ts +69 -0
  284. package/node_modules/@llblab/pi-actors/lib/recipe-control.ts +52 -0
  285. package/node_modules/@llblab/pi-actors/lib/recipes-context.ts +268 -0
  286. package/node_modules/@llblab/pi-actors/lib/recipes-discovery.ts +1060 -0
  287. package/node_modules/@llblab/pi-actors/lib/recipes-references.ts +1598 -0
  288. package/node_modules/@llblab/pi-actors/lib/recipes-usage.ts +638 -0
  289. package/node_modules/@llblab/pi-actors/lib/registry.ts +806 -0
  290. package/node_modules/@llblab/pi-actors/lib/review-control.ts +136 -0
  291. package/node_modules/@llblab/pi-actors/lib/review-diagnostics.ts +164 -0
  292. package/node_modules/@llblab/pi-actors/lib/review-projection.ts +196 -0
  293. package/node_modules/@llblab/pi-actors/lib/run-evidence-policy.ts +242 -0
  294. package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +155 -0
  295. package/node_modules/@llblab/pi-actors/lib/runs-artifacts.ts +96 -0
  296. package/node_modules/@llblab/pi-actors/lib/runs-control-delivery.ts +209 -0
  297. package/node_modules/@llblab/pi-actors/lib/runs-control.ts +148 -0
  298. package/node_modules/@llblab/pi-actors/lib/runs-controls.ts +282 -0
  299. package/node_modules/@llblab/pi-actors/lib/runs-identity.ts +14 -0
  300. package/node_modules/@llblab/pi-actors/lib/runs-index.ts +192 -0
  301. package/node_modules/@llblab/pi-actors/lib/runs-ownership.ts +136 -0
  302. package/node_modules/@llblab/pi-actors/lib/runs-parent-teardown.ts +257 -0
  303. package/node_modules/@llblab/pi-actors/lib/runs-process.ts +174 -0
  304. package/node_modules/@llblab/pi-actors/lib/runs-retention.ts +140 -0
  305. package/node_modules/@llblab/pi-actors/lib/runs-start.ts +113 -0
  306. package/node_modules/@llblab/pi-actors/lib/runs-status.ts +101 -0
  307. package/node_modules/@llblab/pi-actors/lib/runs-trace.ts +611 -0
  308. package/node_modules/@llblab/pi-actors/lib/runtime-identity.ts +39 -0
  309. package/node_modules/@llblab/pi-actors/lib/runtime-triage.ts +106 -0
  310. package/node_modules/@llblab/pi-actors/lib/runtime.ts +430 -0
  311. package/node_modules/@llblab/pi-actors/lib/schema.ts +525 -0
  312. package/node_modules/@llblab/pi-actors/lib/session-evidence.ts +302 -0
  313. package/node_modules/@llblab/pi-actors/lib/state-readers.ts +93 -0
  314. package/node_modules/@llblab/pi-actors/lib/temp.ts +99 -0
  315. package/node_modules/@llblab/pi-actors/lib/tool-review-lineage-transaction.ts +881 -0
  316. package/node_modules/@llblab/pi-actors/lib/tool-review-lineage.ts +145 -0
  317. package/node_modules/@llblab/pi-actors/lib/tool-review-scheduler.ts +635 -0
  318. package/node_modules/@llblab/pi-actors/lib/tool-review-transaction.ts +563 -0
  319. package/node_modules/@llblab/pi-actors/lib/tool-review.ts +270 -0
  320. package/node_modules/@llblab/pi-actors/lib/tools-access.ts +68 -0
  321. package/node_modules/@llblab/pi-actors/lib/tools-inspect.ts +673 -0
  322. package/node_modules/@llblab/pi-actors/lib/tools-local.ts +303 -0
  323. package/node_modules/@llblab/pi-actors/lib/tools-message.ts +176 -0
  324. package/node_modules/@llblab/pi-actors/lib/tools-register.ts +63 -0
  325. package/node_modules/@llblab/pi-actors/lib/tools-response.ts +285 -0
  326. package/node_modules/@llblab/pi-actors/lib/tools-spawn.ts +212 -0
  327. package/node_modules/@llblab/pi-actors/lib/tools.ts +88 -0
  328. package/node_modules/@llblab/pi-actors/lib/trace-projection.ts +306 -0
  329. package/node_modules/@llblab/pi-actors/package.json +76 -0
  330. package/node_modules/@llblab/pi-actors/scripts/async-runner.mjs +555 -0
  331. package/node_modules/@llblab/pi-actors/scripts/build-dist.mjs +60 -0
  332. package/node_modules/@llblab/pi-actors/scripts/conformance.mjs +63 -0
  333. package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +126 -0
  334. package/node_modules/@llblab/pi-actors/skills/actors/recipes/command-validate.json +13 -0
  335. package/node_modules/@llblab/pi-actors/skills/actors/recipes/jsonl-tail.json +10 -0
  336. package/node_modules/@llblab/pi-actors/skills/actors/recipes/recipe-validate.json +11 -0
  337. package/node_modules/@llblab/pi-actors/skills/actors/recipes/resource-locker-snapshot.json +11 -0
  338. package/node_modules/@llblab/pi-actors/skills/actors/recipes/resource-locker.json +25 -0
  339. package/node_modules/@llblab/pi-actors/skills/actors/recipes/run-ops-snapshot.json +15 -0
  340. package/node_modules/@llblab/pi-actors/skills/actors/recipes/run-state-files.json +13 -0
  341. package/node_modules/@llblab/pi-actors/skills/actors/recipes/run-summary.json +9 -0
  342. package/node_modules/@llblab/pi-actors/skills/actors/references/diagnostics.md +44 -0
  343. package/node_modules/@llblab/pi-actors/skills/actors/references/persistent-tools.md +76 -0
  344. package/node_modules/@llblab/pi-actors/skills/actors/references/recipes.md +51 -0
  345. package/node_modules/@llblab/pi-actors/skills/actors/references/runs.md +41 -0
  346. package/node_modules/@llblab/pi-actors/skills/actors/scripts/resource-locker.mjs +425 -0
  347. package/node_modules/@llblab/pi-actors/skills/actors/scripts/run-utils.mjs +164 -0
  348. package/node_modules/@llblab/pi-actors/skills/actors/scripts/validate-recipe.mjs +370 -0
  349. package/node_modules/@llblab/pi-actors/skills/artifacts/SKILL.md +33 -0
  350. package/node_modules/@llblab/pi-actors/skills/artifacts/recipes/bundle.json +77 -0
  351. package/node_modules/@llblab/pi-actors/skills/artifacts/recipes/file-write.json +14 -0
  352. package/node_modules/@llblab/pi-actors/skills/artifacts/recipes/manifest.json +14 -0
  353. package/node_modules/@llblab/pi-actors/skills/artifacts/recipes/report.json +39 -0
  354. package/node_modules/@llblab/pi-actors/skills/artifacts/recipes/write.json +53 -0
  355. package/node_modules/@llblab/pi-actors/skills/artifacts/scripts/artifact-utils.mjs +63 -0
  356. package/node_modules/@llblab/pi-actors/skills/music-player/SKILL.md +57 -0
  357. package/node_modules/@llblab/pi-actors/skills/music-player/genapps/music-player.mjs +360 -0
  358. package/node_modules/@llblab/pi-actors/skills/music-player/recipes/playback.json +29 -0
  359. package/node_modules/@llblab/pi-actors/skills/music-player/scripts/playback-client.mjs +143 -0
  360. package/node_modules/@llblab/pi-actors/skills/music-player/scripts/playback.mjs +1446 -0
  361. package/node_modules/@llblab/pi-actors/skills/project-work/SKILL.md +37 -0
  362. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/changelog-head.json +11 -0
  363. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/changelog-section.json +11 -0
  364. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/docs-maintenance.json +66 -0
  365. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/git-log.json +11 -0
  366. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/git-status.json +9 -0
  367. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/markdown-index.json +14 -0
  368. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/package-summary.json +9 -0
  369. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/release-readiness.json +115 -0
  370. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/release-summary.json +74 -0
  371. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/repo-health.json +75 -0
  372. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/run-ops.json +56 -0
  373. package/node_modules/@llblab/pi-actors/skills/project-work/recipes/skill-summary.json +11 -0
  374. package/node_modules/@llblab/pi-actors/skills/project-work/scripts/project-utils.mjs +89 -0
  375. package/node_modules/@llblab/pi-actors/skills/recipe-memory/SKILL.md +36 -0
  376. package/node_modules/@llblab/pi-actors/skills/recipe-memory/recipes/draft-review.json +14 -0
  377. package/node_modules/@llblab/pi-actors/skills/recipe-memory/recipes/tool-review.json +14 -0
  378. package/node_modules/@llblab/pi-actors/skills/swarm/SKILL.md +84 -0
  379. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/architect.json +82 -0
  380. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/checkpoint-continuation.json +51 -0
  381. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/development-tasking.json +67 -0
  382. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/lens-review.json +63 -0
  383. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/quorum-review.json +65 -0
  384. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/research-synthesis.json +81 -0
  385. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/review-readiness.json +59 -0
  386. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-artifact.json +21 -0
  387. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-checkpoint.json +21 -0
  388. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-conflict-report.json +19 -0
  389. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-contradiction-map.json +20 -0
  390. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-critic.json +22 -0
  391. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-evidence-map.json +20 -0
  392. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-followup.json +21 -0
  393. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-judge.json +22 -0
  394. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-merge.json +24 -0
  395. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-normalize.json +20 -0
  396. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-plan.json +20 -0
  397. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-preflight.json +18 -0
  398. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-prompt.json +16 -0
  399. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-prompts.json +39 -0
  400. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-quorum.json +31 -0
  401. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-review-coordinator.json +170 -0
  402. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-review.json +26 -0
  403. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-task-card.json +22 -0
  404. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-tools.json +15 -0
  405. package/node_modules/@llblab/pi-actors/skills/swarm/recipes/subagent-verify.json +23 -0
  406. package/node_modules/@llblab/pi-actors/skills/swarm/references/development-swarm.md +189 -0
  407. package/node_modules/@llblab/pi-actors/skills/swarm/references/review-swarms.md +115 -0
  408. package/node_modules/@llblab/pi-codex-usage/AGENTS.md +17 -0
  409. package/node_modules/@llblab/pi-codex-usage/BACKLOG.md +8 -0
  410. package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +80 -0
  411. package/node_modules/@llblab/pi-codex-usage/LICENSE +22 -0
  412. package/node_modules/@llblab/pi-codex-usage/README.md +113 -0
  413. package/node_modules/@llblab/pi-codex-usage/banner.jpg +0 -0
  414. package/node_modules/@llblab/pi-codex-usage/index.ts +1525 -0
  415. package/node_modules/@llblab/pi-codex-usage/package.json +59 -0
  416. package/node_modules/@llblab/pi-grow-loop/AGENTS.md +56 -0
  417. package/node_modules/@llblab/pi-grow-loop/BACKLOG.md +3 -0
  418. package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +126 -0
  419. package/node_modules/@llblab/pi-grow-loop/README.md +216 -0
  420. package/node_modules/@llblab/pi-grow-loop/banner.jpg +0 -0
  421. package/node_modules/@llblab/pi-grow-loop/index.ts +219 -0
  422. package/node_modules/@llblab/pi-grow-loop/package.json +64 -0
  423. package/node_modules/@llblab/pi-grow-loop/skills/grow-loop/SKILL.md +131 -0
  424. package/node_modules/@llblab/pi-grow-loop/skills/while-true/SKILL.md +233 -0
  425. package/node_modules/@llblab/pi-telegram/AGENTS.md +157 -0
  426. package/node_modules/@llblab/pi-telegram/BACKLOG.md +15 -0
  427. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +886 -0
  428. package/node_modules/@llblab/pi-telegram/README.md +330 -0
  429. package/node_modules/@llblab/pi-telegram/api/activity.ts +15 -0
  430. package/node_modules/@llblab/pi-telegram/api/commands.ts +11 -0
  431. package/node_modules/@llblab/pi-telegram/api/delivery.ts +21 -0
  432. package/node_modules/@llblab/pi-telegram/api/inbound.ts +14 -0
  433. package/node_modules/@llblab/pi-telegram/api/keyboard.ts +11 -0
  434. package/node_modules/@llblab/pi-telegram/api/outbound.ts +11 -0
  435. package/node_modules/@llblab/pi-telegram/api/sections.ts +17 -0
  436. package/node_modules/@llblab/pi-telegram/api/status.ts +12 -0
  437. package/node_modules/@llblab/pi-telegram/api/updates.ts +16 -0
  438. package/node_modules/@llblab/pi-telegram/api/voice.ts +24 -0
  439. package/node_modules/@llblab/pi-telegram/docs/README.md +25 -0
  440. package/node_modules/@llblab/pi-telegram/docs/activity.md +304 -0
  441. package/node_modules/@llblab/pi-telegram/docs/architecture.md +418 -0
  442. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +48 -0
  443. package/node_modules/@llblab/pi-telegram/docs/command-templates.md +365 -0
  444. package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +176 -0
  445. package/node_modules/@llblab/pi-telegram/docs/delivery.md +225 -0
  446. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +310 -0
  447. package/node_modules/@llblab/pi-telegram/docs/inbound.md +136 -0
  448. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +467 -0
  449. package/node_modules/@llblab/pi-telegram/docs/outbound.md +193 -0
  450. package/node_modules/@llblab/pi-telegram/docs/public-api.md +599 -0
  451. package/node_modules/@llblab/pi-telegram/docs/sections.md +452 -0
  452. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +256 -0
  453. package/node_modules/@llblab/pi-telegram/docs/updates.md +171 -0
  454. package/node_modules/@llblab/pi-telegram/docs/voice.md +217 -0
  455. package/node_modules/@llblab/pi-telegram/index.ts +1336 -0
  456. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +714 -0
  457. package/node_modules/@llblab/pi-telegram/lib/activity.ts +782 -0
  458. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +121 -0
  459. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +1129 -0
  460. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +379 -0
  461. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +1941 -0
  462. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2119 -0
  463. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +244 -0
  464. package/node_modules/@llblab/pi-telegram/lib/bus.ts +2645 -0
  465. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +954 -0
  466. package/node_modules/@llblab/pi-telegram/lib/commands.ts +1854 -0
  467. package/node_modules/@llblab/pi-telegram/lib/config.ts +1143 -0
  468. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +891 -0
  469. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +104 -0
  470. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +958 -0
  471. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +824 -0
  472. package/node_modules/@llblab/pi-telegram/lib/journal.ts +3140 -0
  473. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +59 -0
  474. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +623 -0
  475. package/node_modules/@llblab/pi-telegram/lib/locks.ts +1422 -0
  476. package/node_modules/@llblab/pi-telegram/lib/logs.ts +393 -0
  477. package/node_modules/@llblab/pi-telegram/lib/media.ts +921 -0
  478. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +1182 -0
  479. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +926 -0
  480. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +876 -0
  481. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +274 -0
  482. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +185 -0
  483. package/node_modules/@llblab/pi-telegram/lib/menu.ts +867 -0
  484. package/node_modules/@llblab/pi-telegram/lib/model.ts +672 -0
  485. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +981 -0
  486. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +331 -0
  487. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +515 -0
  488. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +285 -0
  489. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +1007 -0
  490. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +298 -0
  491. package/node_modules/@llblab/pi-telegram/lib/paths.ts +127 -0
  492. package/node_modules/@llblab/pi-telegram/lib/pi.ts +197 -0
  493. package/node_modules/@llblab/pi-telegram/lib/polling.ts +1588 -0
  494. package/node_modules/@llblab/pi-telegram/lib/preview.ts +846 -0
  495. package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +158 -0
  496. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +218 -0
  497. package/node_modules/@llblab/pi-telegram/lib/queue.ts +3075 -0
  498. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +464 -0
  499. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1213 -0
  500. package/node_modules/@llblab/pi-telegram/lib/replies.ts +892 -0
  501. package/node_modules/@llblab/pi-telegram/lib/routing.ts +2889 -0
  502. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +628 -0
  503. package/node_modules/@llblab/pi-telegram/lib/sections.ts +710 -0
  504. package/node_modules/@llblab/pi-telegram/lib/setup.ts +220 -0
  505. package/node_modules/@llblab/pi-telegram/lib/skills.ts +21 -0
  506. package/node_modules/@llblab/pi-telegram/lib/status.ts +1550 -0
  507. package/node_modules/@llblab/pi-telegram/lib/sync.ts +883 -0
  508. package/node_modules/@llblab/pi-telegram/lib/target.ts +49 -0
  509. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +1747 -0
  510. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +487 -0
  511. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +1002 -0
  512. package/node_modules/@llblab/pi-telegram/lib/threads.ts +3183 -0
  513. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +78 -0
  514. package/node_modules/@llblab/pi-telegram/lib/turns.ts +753 -0
  515. package/node_modules/@llblab/pi-telegram/lib/updates.ts +4950 -0
  516. package/node_modules/@llblab/pi-telegram/lib/voice.ts +340 -0
  517. package/node_modules/@llblab/pi-telegram/package.json +83 -0
  518. package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
  519. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +435 -0
  520. package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +241 -0
  521. package/node_modules/@llblab/pi-telegram/skills/generative-apps/SKILL.md +114 -0
  522. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +149 -0
  523. package/package.json +67 -0
@@ -0,0 +1,1213 @@
1
+ /**
2
+ * Telegram UI/compat rendering helpers
3
+ * Zones: telegram rendering, shared text utils
4
+ * Converts bridge-owned UI/status/menu/interactive text into Telegram-safe plain text and HTML chunks with chunk-boundary handling
5
+ */
6
+
7
+ export const MAX_MESSAGE_LENGTH = 4096;
8
+
9
+ const TELEGRAM_TABLE_GRAPHEME_SEGMENTER =
10
+ typeof Intl.Segmenter === "function"
11
+ ? new Intl.Segmenter(undefined, { granularity: "grapheme" })
12
+ : undefined;
13
+ const TELEGRAM_TABLE_EMOJI_GRAPHEME_PATTERN =
14
+ /\p{Extended_Pictographic}|\p{Emoji_Presentation}|\p{Regional_Indicator}/u;
15
+
16
+ // --- HTML Helpers ---
17
+
18
+ interface OpenHtmlTag {
19
+ name: string;
20
+ openTag: string;
21
+ }
22
+
23
+ const TELEGRAM_VOID_HTML_TAGS = new Set(["br", "hr"]);
24
+
25
+ export function escapeHtml(text: string): string {
26
+ return text
27
+ .replace(/&/g, "&")
28
+ .replace(/</g, "&lt;")
29
+ .replace(/>/g, "&gt;");
30
+ }
31
+
32
+ export function escapeHtmlAttribute(text: string): string {
33
+ return escapeHtml(text).replace(/"/g, "&quot;").replace(/'/g, "&#39;");
34
+ }
35
+
36
+ function getHtmlTagName(tag: string): string | undefined {
37
+ return tag.match(/^<\/?\s*([a-zA-Z][\w-]*)/)?.[1]?.toLowerCase();
38
+ }
39
+
40
+ function isHtmlClosingTag(tag: string): boolean {
41
+ return /^<\//.test(tag);
42
+ }
43
+
44
+ function isHtmlSelfClosingTag(tag: string): boolean {
45
+ return /\/\s*>$/.test(tag);
46
+ }
47
+
48
+ function getHtmlClosingTags(openTags: OpenHtmlTag[]): string {
49
+ return [...openTags]
50
+ .reverse()
51
+ .map((tag) => `</${tag.name}>`)
52
+ .join("");
53
+ }
54
+
55
+ function getHtmlOpeningTags(openTags: OpenHtmlTag[]): string {
56
+ return openTags.map((tag) => tag.openTag).join("");
57
+ }
58
+
59
+ function updateOpenHtmlTags(tag: string, openTags: OpenHtmlTag[]): void {
60
+ const name = getHtmlTagName(tag);
61
+ if (!name || TELEGRAM_VOID_HTML_TAGS.has(name)) return;
62
+ if (isHtmlClosingTag(tag)) {
63
+ const index = openTags.map((openTag) => openTag.name).lastIndexOf(name);
64
+ if (index !== -1) openTags.splice(index, 1);
65
+ return;
66
+ }
67
+ if (isHtmlSelfClosingTag(tag)) return;
68
+ openTags.push({ name, openTag: tag });
69
+ }
70
+
71
+ export function chunkHtmlPreservingTags(
72
+ html: string,
73
+ maxLength: number,
74
+ ): string[] {
75
+ if (html.length <= maxLength) return [html];
76
+ const chunks: string[] = [];
77
+ const openTags: OpenHtmlTag[] = [];
78
+ const tagPattern = /<\/?[a-zA-Z][^>]*>/g;
79
+ let current = "";
80
+ let index = 0;
81
+ const flushCurrent = (): void => {
82
+ if (current.length === 0) return;
83
+ chunks.push(`${current}${getHtmlClosingTags(openTags)}`);
84
+ current = getHtmlOpeningTags(openTags);
85
+ };
86
+ const appendText = (text: string): void => {
87
+ let remaining = text;
88
+ while (remaining.length > 0) {
89
+ const closingTags = getHtmlClosingTags(openTags);
90
+ const available = maxLength - current.length - closingTags.length;
91
+ if (available <= 0) {
92
+ flushCurrent();
93
+ continue;
94
+ }
95
+ const slice = remaining.slice(0, available);
96
+ current += slice;
97
+ remaining = remaining.slice(slice.length);
98
+ if (remaining.length > 0) flushCurrent();
99
+ }
100
+ };
101
+ const appendTag = (tag: string): void => {
102
+ const closingTags = isHtmlClosingTag(tag)
103
+ ? ""
104
+ : getHtmlClosingTags(openTags);
105
+ if (current.length + tag.length + closingTags.length > maxLength) {
106
+ flushCurrent();
107
+ }
108
+ current += tag;
109
+ updateOpenHtmlTags(tag, openTags);
110
+ };
111
+ for (const match of html.matchAll(tagPattern)) {
112
+ appendText(html.slice(index, match.index));
113
+ appendTag(match[0]);
114
+ index = match.index + match[0].length;
115
+ }
116
+ appendText(html.slice(index));
117
+ if (current.length > 0) chunks.push(current);
118
+ return chunks;
119
+ }
120
+
121
+ // --- Plain Preview Rendering ---
122
+
123
+ function splitPlainMarkdownLine(line: string, maxLength = 1500): string[] {
124
+ if (line.length <= maxLength) return [line];
125
+ const words = line.split(/\s+/).filter(Boolean);
126
+ if (words.length === 0) return [line];
127
+ const parts: string[] = [];
128
+ let current = "";
129
+ for (const word of words) {
130
+ const candidate = current.length === 0 ? word : `${current} ${word}`;
131
+ if (candidate.length <= maxLength) {
132
+ current = candidate;
133
+ continue;
134
+ }
135
+ if (current.length > 0) {
136
+ parts.push(current);
137
+ current = "";
138
+ }
139
+ if (word.length <= maxLength) {
140
+ current = word;
141
+ continue;
142
+ }
143
+ for (let i = 0; i < word.length; i += maxLength) {
144
+ parts.push(word.slice(i, i + maxLength));
145
+ }
146
+ }
147
+ if (current.length > 0) {
148
+ parts.push(current);
149
+ }
150
+ return parts.length > 0 ? parts : [line];
151
+ }
152
+
153
+ interface ParsedMarkdownInlineLink {
154
+ startIndex: number;
155
+ endIndex: number;
156
+ label: string;
157
+ destination: string;
158
+ isImage: boolean;
159
+ }
160
+
161
+ interface ParsedMarkdownAutolink {
162
+ startIndex: number;
163
+ endIndex: number;
164
+ destination: string;
165
+ }
166
+
167
+ function isEscapedMarkdownCharacter(text: string, index: number): boolean {
168
+ let backslashCount = 0;
169
+ for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) {
170
+ backslashCount += 1;
171
+ }
172
+ return backslashCount % 2 === 1;
173
+ }
174
+
175
+ function findMarkdownClosingBracket(
176
+ text: string,
177
+ startIndex: number,
178
+ ): number | undefined {
179
+ let depth = 0;
180
+ for (let index = startIndex; index < text.length; index += 1) {
181
+ if (isEscapedMarkdownCharacter(text, index)) continue;
182
+ const char = text[index] ?? "";
183
+ if (char === "[") {
184
+ depth += 1;
185
+ continue;
186
+ }
187
+ if (char !== "]") continue;
188
+ depth -= 1;
189
+ if (depth === 0) return index;
190
+ }
191
+ return undefined;
192
+ }
193
+
194
+ function parseMarkdownLinkTarget(
195
+ text: string,
196
+ openParenIndex: number,
197
+ ): { destination: string; endIndex: number } | undefined {
198
+ let index = openParenIndex + 1;
199
+ while (index < text.length && /\s/.test(text[index] ?? "")) {
200
+ index += 1;
201
+ }
202
+ if (index >= text.length) return undefined;
203
+ let destination = "";
204
+ if (text[index] === "<") {
205
+ const destinationStart = index + 1;
206
+ index += 1;
207
+ while (index < text.length) {
208
+ if (!isEscapedMarkdownCharacter(text, index) && text[index] === ">") {
209
+ break;
210
+ }
211
+ index += 1;
212
+ }
213
+ if (index >= text.length) return undefined;
214
+ destination = text.slice(destinationStart, index).trim();
215
+ index += 1;
216
+ } else {
217
+ const destinationStart = index;
218
+ let parenDepth = 0;
219
+ while (index < text.length) {
220
+ if (isEscapedMarkdownCharacter(text, index)) {
221
+ index += 1;
222
+ continue;
223
+ }
224
+ const char = text[index] ?? "";
225
+ if (/\s/.test(char) && parenDepth === 0) break;
226
+ if (char === "(") {
227
+ parenDepth += 1;
228
+ index += 1;
229
+ continue;
230
+ }
231
+ if (char === ")") {
232
+ if (parenDepth === 0) break;
233
+ parenDepth -= 1;
234
+ index += 1;
235
+ continue;
236
+ }
237
+ index += 1;
238
+ }
239
+ destination = text.slice(destinationStart, index).trim();
240
+ }
241
+ if (!destination) return undefined;
242
+ while (index < text.length && /\s/.test(text[index] ?? "")) {
243
+ index += 1;
244
+ }
245
+ if (
246
+ index < text.length &&
247
+ (text[index] === '"' || text[index] === "'" || text[index] === "(")
248
+ ) {
249
+ const titleDelimiter = text[index] ?? '"';
250
+ const closingTitleDelimiter = titleDelimiter === "(" ? ")" : titleDelimiter;
251
+ index += 1;
252
+ while (index < text.length) {
253
+ if (
254
+ !isEscapedMarkdownCharacter(text, index) &&
255
+ text[index] === closingTitleDelimiter
256
+ ) {
257
+ break;
258
+ }
259
+ index += 1;
260
+ }
261
+ if (index >= text.length) return undefined;
262
+ index += 1;
263
+ while (index < text.length && /\s/.test(text[index] ?? "")) {
264
+ index += 1;
265
+ }
266
+ }
267
+ if (text[index] !== ")") return undefined;
268
+ return { destination, endIndex: index };
269
+ }
270
+
271
+ function isSupportedMarkdownLinkDestination(destination: string): boolean {
272
+ return /^(?:https?:\/\/|mailto:)/i.test(destination.trim());
273
+ }
274
+
275
+ function parseMarkdownInlineLinkAt(
276
+ text: string,
277
+ index: number,
278
+ ): ParsedMarkdownInlineLink | undefined {
279
+ const isImage = text[index] === "!" && text[index + 1] === "[";
280
+ const labelStartIndex = isImage ? index + 1 : index;
281
+ if (text[labelStartIndex] !== "[") return undefined;
282
+ if (
283
+ isEscapedMarkdownCharacter(text, labelStartIndex) ||
284
+ (isImage && isEscapedMarkdownCharacter(text, index))
285
+ ) {
286
+ return undefined;
287
+ }
288
+ const labelEndIndex = findMarkdownClosingBracket(text, labelStartIndex);
289
+ if (labelEndIndex === undefined || text[labelEndIndex + 1] !== "(") {
290
+ return undefined;
291
+ }
292
+ const target = parseMarkdownLinkTarget(text, labelEndIndex + 1);
293
+ if (!target) return undefined;
294
+ return {
295
+ startIndex: index,
296
+ endIndex: target.endIndex,
297
+ label: text.slice(labelStartIndex + 1, labelEndIndex),
298
+ destination: target.destination,
299
+ isImage,
300
+ };
301
+ }
302
+
303
+ function parseMarkdownAutolinkAt(
304
+ text: string,
305
+ index: number,
306
+ ): ParsedMarkdownAutolink | undefined {
307
+ if (text[index] !== "<" || isEscapedMarkdownCharacter(text, index)) {
308
+ return undefined;
309
+ }
310
+ let endIndex = index + 1;
311
+ while (endIndex < text.length) {
312
+ if (!isEscapedMarkdownCharacter(text, endIndex) && text[endIndex] === ">") {
313
+ break;
314
+ }
315
+ endIndex += 1;
316
+ }
317
+ if (endIndex >= text.length) return undefined;
318
+ const destination = text.slice(index + 1, endIndex).trim();
319
+ if (!isSupportedMarkdownLinkDestination(destination)) {
320
+ return undefined;
321
+ }
322
+ return { startIndex: index, endIndex, destination };
323
+ }
324
+
325
+ function replaceMarkdownLink(
326
+ text: string,
327
+ options: {
328
+ renderInlineLink: (
329
+ link: ParsedMarkdownInlineLink,
330
+ supported: boolean,
331
+ ) => string;
332
+ renderAutolink: (link: ParsedMarkdownAutolink) => string;
333
+ },
334
+ ): string {
335
+ let result = "";
336
+ for (let index = 0; index < text.length; ) {
337
+ const inlineLink = parseMarkdownInlineLinkAt(text, index);
338
+ if (inlineLink) {
339
+ result += options.renderInlineLink(
340
+ inlineLink,
341
+ isSupportedMarkdownLinkDestination(inlineLink.destination),
342
+ );
343
+ index = inlineLink.endIndex + 1;
344
+ continue;
345
+ }
346
+ const autolink = parseMarkdownAutolinkAt(text, index);
347
+ if (autolink) {
348
+ result += options.renderAutolink(autolink);
349
+ index = autolink.endIndex + 1;
350
+ continue;
351
+ }
352
+ result += text[index] ?? "";
353
+ index += 1;
354
+ }
355
+ return result;
356
+ }
357
+
358
+ function stripInlineMarkdownToPlainText(text: string): string {
359
+ let result = replaceMarkdownLink(text, {
360
+ renderInlineLink: (link, supported) => {
361
+ const plainLabel = stripInlineMarkdownToPlainText(link.label).trim();
362
+ if (plainLabel.length > 0) return plainLabel;
363
+ return supported ? link.destination : "";
364
+ },
365
+ renderAutolink: (link) => link.destination,
366
+ });
367
+ result = result.replace(/`([^`\n]+)`/g, "$1");
368
+ result = result.replace(/(\*\*\*|___)(.+?)\1/g, "$2");
369
+ result = result.replace(/(\*\*|__)(.+?)\1/g, "$2");
370
+ result = result.replace(/(\*|_)(.+?)\1/g, "$2");
371
+ result = result.replace(/~~(.+?)~~/g, "$1");
372
+ result = result.replace(/\\([\\`*_{}\[\]()#+\-.!>~|])/g, "$1");
373
+ return result;
374
+ }
375
+
376
+ function isMarkdownTableSeparator(line: string): boolean {
377
+ return /^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$/.test(line);
378
+ }
379
+
380
+ function parseMarkdownFence(
381
+ line: string,
382
+ ): { marker: "`" | "~"; length: number; info?: string } | undefined {
383
+ const match = line.match(/^\s*([`~]{3,})(.*)$/);
384
+ if (!match) return undefined;
385
+ const fence = match[1] ?? "";
386
+ const marker = fence[0];
387
+ if ((marker !== "`" && marker !== "~") || /[^`~]/.test(fence)) {
388
+ return undefined;
389
+ }
390
+ if (!fence.split("").every((char) => char === marker)) return undefined;
391
+ return {
392
+ marker,
393
+ length: fence.length,
394
+ info: (match[2] ?? "").trim() || undefined,
395
+ };
396
+ }
397
+
398
+ function isFencedCodeStart(line: string): boolean {
399
+ return parseMarkdownFence(line) !== undefined;
400
+ }
401
+
402
+ function isMatchingMarkdownFence(
403
+ line: string,
404
+ fence: { marker: "`" | "~"; length: number },
405
+ ): boolean {
406
+ const match = line.match(/^\s*([`~]{3,})\s*$/);
407
+ if (!match) return false;
408
+ const candidate = match[1] ?? "";
409
+ return (
410
+ candidate.length >= fence.length &&
411
+ candidate[0] === fence.marker &&
412
+ candidate.split("").every((char) => char === fence.marker)
413
+ );
414
+ }
415
+
416
+ function isIndentedCodeLine(line: string): boolean {
417
+ return /^(?:\t| {4,})/.test(line);
418
+ }
419
+
420
+ function isIndentedMarkdownStructureLine(line: string): boolean {
421
+ const trimmed = line.trimStart();
422
+ return (
423
+ /^(?:[-*+]|\d+\.)\s+\[([ xX])\]\s+/.test(trimmed) ||
424
+ /^(?:[-*+]|\d+\.)\s+/.test(trimmed) ||
425
+ /^>\s?/.test(trimmed) ||
426
+ /^#{1,6}\s+/.test(trimmed) ||
427
+ parseMarkdownFence(trimmed) !== undefined
428
+ );
429
+ }
430
+
431
+ function canStartIndentedCodeBlock(lines: string[], index: number): boolean {
432
+ const line = lines[index] ?? "";
433
+ if (!isIndentedCodeLine(line)) return false;
434
+ if (isIndentedMarkdownStructureLine(line)) return false;
435
+ if (index === 0) return true;
436
+ return (lines[index - 1] ?? "").trim().length === 0;
437
+ }
438
+
439
+ function stripIndentedCodePrefix(line: string): string {
440
+ if (line.startsWith("\t")) return line.slice(1);
441
+ if (line.startsWith(" ")) return line.slice(4);
442
+ return line;
443
+ }
444
+
445
+ function normalizeMarkdownDocument(markdown: string): string {
446
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
447
+ let start = 0;
448
+ while (start < lines.length && (lines[start] ?? "").trim().length === 0) {
449
+ start += 1;
450
+ }
451
+ let end = lines.length;
452
+ while (end > start && (lines[end - 1] ?? "").trim().length === 0) {
453
+ end -= 1;
454
+ }
455
+ return lines.slice(start, end).join("\n");
456
+ }
457
+
458
+ function isMarkdownNumberedListMarker(marker: string): boolean {
459
+ return /^\d+\.$/.test(marker);
460
+ }
461
+
462
+ function matchMarkdownHeadingLine(line: string): RegExpMatchArray | null {
463
+ return line.match(/^(\s*)#{1,6}\s+(.+)$/);
464
+ }
465
+
466
+ // --- UI Markdown-to-Telegram-HTML Rendering ---
467
+
468
+ function renderDelimitedInlineStyle(
469
+ text: string,
470
+ delimiter: string,
471
+ render: (content: string) => string,
472
+ ): string {
473
+ const escapedDelimiter = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
474
+ const pattern = new RegExp(
475
+ `(^|[^\\p{L}\\p{N}\\\\])(${escapedDelimiter})(?=\\S)([\\s\\S]+?)(?<=\\S)\\2(?=[^\\p{L}\\p{N}]|$)`,
476
+ "gu",
477
+ );
478
+ return text.replace(
479
+ pattern,
480
+ (_match, prefix: string, _wrapped: string, content: string) => {
481
+ return `${prefix}${render(content)}`;
482
+ },
483
+ );
484
+ }
485
+
486
+ interface InlineMarkdownTokenState {
487
+ tokens: string[];
488
+ }
489
+
490
+ function makeInlineMarkdownToken(
491
+ state: InlineMarkdownTokenState,
492
+ html: string,
493
+ ): string {
494
+ const token = `\uE000${state.tokens.length}\uE001`;
495
+ state.tokens.push(html);
496
+ return token;
497
+ }
498
+
499
+ function stashInlineMarkdownLinks(
500
+ text: string,
501
+ state: InlineMarkdownTokenState,
502
+ allowLinks: boolean,
503
+ ): string {
504
+ return replaceMarkdownLink(text, {
505
+ renderInlineLink: (link, supported) => {
506
+ const plainLabel = stripInlineMarkdownToPlainText(link.label).trim();
507
+ if (!allowLinks || !supported)
508
+ return plainLabel.length > 0 ? plainLabel : link.destination;
509
+ const renderedLabel =
510
+ plainLabel.length > 0 ? plainLabel : link.destination;
511
+ return makeInlineMarkdownToken(
512
+ state,
513
+ `<a href="${escapeHtmlAttribute(link.destination)}">${escapeHtml(renderedLabel)}</a>`,
514
+ );
515
+ },
516
+ renderAutolink: (link) => {
517
+ if (!allowLinks) return link.destination;
518
+ return makeInlineMarkdownToken(
519
+ state,
520
+ `<a href="${escapeHtmlAttribute(link.destination)}">${escapeHtml(link.destination)}</a>`,
521
+ );
522
+ },
523
+ });
524
+ }
525
+
526
+ function stashInlineMarkdownCodeSpans(
527
+ text: string,
528
+ state: InlineMarkdownTokenState,
529
+ ): string {
530
+ return text.replace(/`([^`\n]+)`/g, (_match, code: string) => {
531
+ return makeInlineMarkdownToken(state, `<code>${escapeHtml(code)}</code>`);
532
+ });
533
+ }
534
+
535
+ function applyInlineMarkdownStyles(text: string): string {
536
+ let result = renderDelimitedInlineStyle(text, "***", (content) => {
537
+ return `<b><i>${content}</i></b>`;
538
+ });
539
+ result = renderDelimitedInlineStyle(result, "___", (content) => {
540
+ return `<b><i>${content}</i></b>`;
541
+ });
542
+ result = renderDelimitedInlineStyle(result, "~~", (content) => {
543
+ return `<s>${content}</s>`;
544
+ });
545
+ result = renderDelimitedInlineStyle(result, "**", (content) => {
546
+ return `<b>${content}</b>`;
547
+ });
548
+ result = renderDelimitedInlineStyle(result, "__", (content) => {
549
+ return `<b>${content}</b>`;
550
+ });
551
+ result = renderDelimitedInlineStyle(result, "*", (content) => {
552
+ return `<i>${content}</i>`;
553
+ });
554
+ return renderDelimitedInlineStyle(result, "_", (content) => {
555
+ return `<i>${content}</i>`;
556
+ });
557
+ }
558
+
559
+ function restoreInlineMarkdownTokens(
560
+ text: string,
561
+ state: InlineMarkdownTokenState,
562
+ ): string {
563
+ return text.replace(
564
+ /\uE000(\d+)\uE001/g,
565
+ (_match, index: string) => state.tokens[Number(index)] ?? "",
566
+ );
567
+ }
568
+
569
+ function renderInlineMarkdown(
570
+ text: string,
571
+ options: { allowLinks?: boolean } = {},
572
+ ): string {
573
+ const tokenState: InlineMarkdownTokenState = { tokens: [] };
574
+ let result = stashInlineMarkdownLinks(
575
+ text,
576
+ tokenState,
577
+ options.allowLinks !== false,
578
+ );
579
+ result = stashInlineMarkdownCodeSpans(result, tokenState);
580
+ result = escapeHtml(result);
581
+ result = applyInlineMarkdownStyles(result);
582
+ result = result.replace(/\\([\\`*_{}\[\]()#+\-.!>~|])/g, "$1");
583
+ return restoreInlineMarkdownTokens(result, tokenState);
584
+ }
585
+
586
+ export function renderTelegramInlineMarkdownHtml(
587
+ text: string,
588
+ options: { allowLinks?: boolean } = {},
589
+ ): string {
590
+ return renderInlineMarkdown(text, options);
591
+ }
592
+
593
+ function buildListIndent(level: number): string {
594
+ return "\u00A0".repeat(Math.max(0, level) * 2);
595
+ }
596
+
597
+ function parseMarkdownTableRow(line: string): string[] {
598
+ const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
599
+ return trimmed
600
+ .split("|")
601
+ .map((cell) => stripInlineMarkdownToPlainText(cell.trim()));
602
+ }
603
+
604
+ function parseMarkdownQuoteLine(
605
+ line: string,
606
+ ): { depth: number; content: string } | undefined {
607
+ const match = line.match(/^\s*((?:>\s*)+)(.*)$/);
608
+ if (!match) return undefined;
609
+ const markers = match[1] ?? "";
610
+ const depth = (markers.match(/>/g) ?? []).length;
611
+ return {
612
+ depth,
613
+ content: match[2] ?? "",
614
+ };
615
+ }
616
+
617
+ function renderMarkdownTextPiece(piece: string): string {
618
+ const heading = matchMarkdownHeadingLine(piece);
619
+ if (heading) {
620
+ const indent = buildListIndent(Math.floor((heading[1] ?? "").length / 2));
621
+ return `${indent}<b>${renderInlineMarkdown(heading[2] ?? "")}</b>`;
622
+ }
623
+ const task = piece.match(/^(\s*)([-*+]|\d+\.)\s+\[([ xX])\]\s+(.+)$/);
624
+ if (task) {
625
+ const indent = buildListIndent(Math.floor((task[1] ?? "").length / 2));
626
+ const listMarker = task[2] ?? "-";
627
+ const checkboxMarker =
628
+ (task[3] ?? " ").toLowerCase() === "x" ? "[x]" : "[ ]";
629
+ const taskPrefix = isMarkdownNumberedListMarker(listMarker)
630
+ ? `<code>${listMarker}</code> <code>${checkboxMarker}</code>`
631
+ : `<code>${checkboxMarker}</code>`;
632
+ return `${indent}${taskPrefix} ${renderInlineMarkdown(task[4] ?? "")}`;
633
+ }
634
+ const bullet = piece.match(/^(\s*)[-*+]\s+(.+)$/);
635
+ if (bullet) {
636
+ const indent = buildListIndent(Math.floor((bullet[1] ?? "").length / 2));
637
+ return `${indent}<code>-</code> ${renderInlineMarkdown(bullet[2] ?? "")}`;
638
+ }
639
+ const numbered = piece.match(/^(\s*)(\d+)\.\s+(.+)$/);
640
+ if (numbered) {
641
+ const indent = buildListIndent(Math.floor((numbered[1] ?? "").length / 2));
642
+ return `${indent}<code>${numbered[2]}.</code> ${renderInlineMarkdown(numbered[3] ?? "")}`;
643
+ }
644
+ const quote = piece.match(/^>\s?(.+)$/);
645
+ if (quote) {
646
+ return `<blockquote>${renderInlineMarkdown(quote[1] ?? "")}</blockquote>`;
647
+ }
648
+ if (/^([-*_]\s*){3,}$/.test(piece.trim())) return "────────────";
649
+ return renderInlineMarkdown(piece);
650
+ }
651
+
652
+ function isPlainInlineMarkdownLine(line: string): boolean {
653
+ if (line.trim().length === 0) return false;
654
+ return (
655
+ matchMarkdownHeadingLine(line) === null &&
656
+ !/^(\s*)([-*+]|\d+\.)\s+\[([ xX])\]\s+(.+)$/.test(line) &&
657
+ !/^(\s*)[-*+]\s+(.+)$/.test(line) &&
658
+ !/^(\s*)(\d+)\.\s+(.+)$/.test(line) &&
659
+ !/^>\s?(.+)$/.test(line) &&
660
+ !/^([-*_]\s*){3,}$/.test(line.trim())
661
+ );
662
+ }
663
+
664
+ function renderMarkdownTextLines(block: string): string[] {
665
+ const rendered: string[] = [];
666
+ const lines = block.split("\n");
667
+ const nonBlankLines = lines.filter((line) => line.trim().length > 0);
668
+ if (
669
+ nonBlankLines.length > 1 &&
670
+ nonBlankLines.every(isPlainInlineMarkdownLine)
671
+ ) {
672
+ return renderInlineMarkdown(nonBlankLines.join("\n")).split("\n");
673
+ }
674
+ for (const line of lines) {
675
+ if (line.trim().length === 0) continue;
676
+ for (const piece of splitPlainMarkdownLine(line)) {
677
+ rendered.push(renderMarkdownTextPiece(piece));
678
+ }
679
+ }
680
+ return rendered;
681
+ }
682
+
683
+ function sanitizeTelegramCodeLanguage(language: string): string {
684
+ return language.split(/\s+/)[0]?.replace(/[^A-Za-z0-9_+.-]/g, "") ?? "";
685
+ }
686
+
687
+ function renderMarkdownCodeBlock(code: string, language?: string): string[] {
688
+ const safeLanguage = language ? sanitizeTelegramCodeLanguage(language) : "";
689
+ const open = safeLanguage
690
+ ? `<pre><code class="language-${escapeHtmlAttribute(safeLanguage)}">`
691
+ : "<pre><code>";
692
+ const close = "</code></pre>";
693
+ const maxContentLength = MAX_MESSAGE_LENGTH - open.length - close.length;
694
+ const chunks: string[] = [];
695
+ let current = "";
696
+ const pushCurrent = (): void => {
697
+ if (current.length === 0) return;
698
+ chunks.push(`${open}${current}${close}`);
699
+ current = "";
700
+ };
701
+ const appendEscapedLine = (escapedLine: string): void => {
702
+ if (escapedLine.length <= maxContentLength) {
703
+ const candidate =
704
+ current.length === 0 ? escapedLine : `${current}\n${escapedLine}`;
705
+ if (candidate.length <= maxContentLength) {
706
+ current = candidate;
707
+ return;
708
+ }
709
+ pushCurrent();
710
+ current = escapedLine;
711
+ return;
712
+ }
713
+ pushCurrent();
714
+ for (let i = 0; i < escapedLine.length; i += maxContentLength) {
715
+ chunks.push(
716
+ `${open}${escapedLine.slice(i, i + maxContentLength)}${close}`,
717
+ );
718
+ }
719
+ };
720
+ for (const line of code.split("\n")) {
721
+ appendEscapedLine(escapeHtml(line));
722
+ }
723
+ pushCurrent();
724
+ return chunks.length > 0 ? chunks : [`${open}${close}`];
725
+ }
726
+
727
+ function isTelegramTableEmojiGrapheme(grapheme: string): boolean {
728
+ return (
729
+ TELEGRAM_TABLE_EMOJI_GRAPHEME_PATTERN.test(grapheme) ||
730
+ grapheme.includes("\u20e3")
731
+ );
732
+ }
733
+
734
+ function getTelegramTableCodePointWidth(char: string): number {
735
+ const codePoint = char.codePointAt(0) ?? 0;
736
+ if (codePoint === 0 || codePoint < 32) return 0;
737
+ if (/\p{Mark}/u.test(char)) return 0;
738
+ if ((codePoint >= 0xfe00 && codePoint <= 0xfe0f) || codePoint === 0x200d)
739
+ return 0;
740
+ if (
741
+ (codePoint >= 0x1100 && codePoint <= 0x115f) ||
742
+ codePoint === 0x2329 ||
743
+ codePoint === 0x232a ||
744
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||
745
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
746
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
747
+ (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
748
+ (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
749
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
750
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6)
751
+ ) {
752
+ return 2;
753
+ }
754
+ return 1;
755
+ }
756
+
757
+ function getTelegramTableGraphemes(text: string): string[] {
758
+ if (TELEGRAM_TABLE_GRAPHEME_SEGMENTER) {
759
+ return Array.from(
760
+ TELEGRAM_TABLE_GRAPHEME_SEGMENTER.segment(text),
761
+ (segment) => segment.segment,
762
+ );
763
+ }
764
+ return Array.from(text);
765
+ }
766
+
767
+ function getTelegramTableCellWidth(text: string): number {
768
+ return getTelegramTableGraphemes(text).reduce((width, grapheme) => {
769
+ if (isTelegramTableEmojiGrapheme(grapheme)) return width + 2;
770
+ return (
771
+ width +
772
+ Array.from(grapheme).reduce(
773
+ (sum, char) => sum + getTelegramTableCodePointWidth(char),
774
+ 0,
775
+ )
776
+ );
777
+ }, 0);
778
+ }
779
+
780
+ function padTelegramTableCellEnd(cell: string, width: number): string {
781
+ const padding = width - getTelegramTableCellWidth(cell);
782
+ return padding > 0 ? `${cell}${" ".repeat(padding)}` : cell;
783
+ }
784
+
785
+ function renderMarkdownTableBlock(lines: string[]): string[] {
786
+ const rows = lines.map(parseMarkdownTableRow);
787
+ const columnCount = Math.max(...rows.map((row) => row.length), 0);
788
+ const normalizedRows = rows.map((row) => {
789
+ const next = [...row];
790
+ while (next.length < columnCount) {
791
+ next.push("");
792
+ }
793
+ return next;
794
+ });
795
+ const widths = Array.from({ length: columnCount }, (_, columnIndex) => {
796
+ return Math.max(
797
+ 3,
798
+ ...normalizedRows.map((row) =>
799
+ getTelegramTableCellWidth(row[columnIndex] ?? ""),
800
+ ),
801
+ );
802
+ });
803
+ const formatRow = (row: string[]): string => {
804
+ return row
805
+ .map((cell, columnIndex) =>
806
+ padTelegramTableCellEnd(cell ?? "", widths[columnIndex] ?? 3),
807
+ )
808
+ .join(" | ");
809
+ };
810
+ const separator = widths.map((width) => "-".repeat(width)).join(" | ");
811
+ const [header, ...body] = normalizedRows;
812
+ const tableLines = [
813
+ formatRow(header ?? []),
814
+ separator,
815
+ ...body.map(formatRow),
816
+ ];
817
+ return renderMarkdownCodeBlock(tableLines.join("\n"), "markdown");
818
+ }
819
+
820
+ function chunkRenderedHtmlLines(
821
+ lines: string[],
822
+ wrapper?: { open: string; close: string },
823
+ ): string[] {
824
+ if (lines.length === 0) return [];
825
+ const open = wrapper?.open ?? "";
826
+ const close = wrapper?.close ?? "";
827
+ const maxContentLength = MAX_MESSAGE_LENGTH - open.length - close.length;
828
+ const chunks: string[] = [];
829
+ let current = "";
830
+ const pushCurrent = (): void => {
831
+ if (current.length === 0) return;
832
+ chunks.push(`${open}${current}${close}`);
833
+ current = "";
834
+ };
835
+ for (const line of lines) {
836
+ const candidate = current.length === 0 ? line : `${current}\n${line}`;
837
+ if (candidate.length <= maxContentLength) {
838
+ current = candidate;
839
+ continue;
840
+ }
841
+ pushCurrent();
842
+ if (line.length <= maxContentLength) {
843
+ current = line;
844
+ continue;
845
+ }
846
+ for (let i = 0; i < line.length; i += maxContentLength) {
847
+ chunks.push(`${open}${line.slice(i, i + maxContentLength)}${close}`);
848
+ }
849
+ }
850
+ pushCurrent();
851
+ return chunks;
852
+ }
853
+
854
+ function renderMarkdownTextBlock(block: string): string[] {
855
+ return chunkRenderedHtmlLines(renderMarkdownTextLines(block));
856
+ }
857
+
858
+ function renderMarkdownQuoteBlock(lines: string[]): string[] {
859
+ const inner = lines
860
+ .map((line) => {
861
+ const parsed = parseMarkdownQuoteLine(line);
862
+ if (!parsed) return line;
863
+ const nestedIndent = "\u00A0".repeat(Math.max(0, parsed.depth - 1) * 2);
864
+ return `${nestedIndent}${parsed.content}`;
865
+ })
866
+ .join("\n");
867
+ return chunkRenderedHtmlLines(renderMarkdownTextLines(inner), {
868
+ open: "<blockquote>",
869
+ close: "</blockquote>",
870
+ });
871
+ }
872
+
873
+ interface TelegramRenderedBlockWithSpacing {
874
+ text: string;
875
+ blankLinesBefore: number;
876
+ }
877
+
878
+ function collectFencedMarkdownCodeLines(
879
+ lines: string[],
880
+ index: number,
881
+ fence: { marker: "`" | "~"; length: number },
882
+ ): { codeLines: string[]; nextIndex: number; closed: boolean } {
883
+ const codeLines: string[] = [];
884
+ let nextIndex = index + 1;
885
+ while (
886
+ nextIndex < lines.length &&
887
+ !isMatchingMarkdownFence(lines[nextIndex] ?? "", fence)
888
+ ) {
889
+ codeLines.push(lines[nextIndex] ?? "");
890
+ nextIndex += 1;
891
+ }
892
+ const closed = nextIndex < lines.length;
893
+ if (closed) nextIndex += 1;
894
+ return { codeLines, nextIndex, closed };
895
+ }
896
+
897
+ function collectMarkdownTableBlockLines(
898
+ lines: string[],
899
+ index: number,
900
+ ): { tableLines: string[]; nextIndex: number } {
901
+ const tableLines = [lines[index] ?? ""];
902
+ let nextIndex = index + 2;
903
+ while (nextIndex < lines.length) {
904
+ const tableLine = lines[nextIndex] ?? "";
905
+ if (tableLine.trim().length === 0 || !tableLine.includes("|")) break;
906
+ tableLines.push(tableLine);
907
+ nextIndex += 1;
908
+ }
909
+ return { tableLines, nextIndex };
910
+ }
911
+
912
+ function collectIndentedMarkdownCodeLines(
913
+ lines: string[],
914
+ index: number,
915
+ ): { codeLines: string[]; nextIndex: number } {
916
+ const codeLines: string[] = [];
917
+ let nextIndex = index;
918
+ while (nextIndex < lines.length) {
919
+ const rawLine = lines[nextIndex] ?? "";
920
+ if (rawLine.trim().length === 0) {
921
+ codeLines.push("");
922
+ nextIndex += 1;
923
+ continue;
924
+ }
925
+ if (!isIndentedCodeLine(rawLine)) break;
926
+ codeLines.push(stripIndentedCodePrefix(rawLine));
927
+ nextIndex += 1;
928
+ }
929
+ return { codeLines, nextIndex };
930
+ }
931
+
932
+ function collectMarkdownQuoteBlockLines(
933
+ lines: string[],
934
+ index: number,
935
+ ): { quoteLines: string[]; nextIndex: number } {
936
+ const quoteLines: string[] = [];
937
+ let nextIndex = index;
938
+ while (nextIndex < lines.length && /^\s*>/.test(lines[nextIndex] ?? "")) {
939
+ quoteLines.push(lines[nextIndex] ?? "");
940
+ nextIndex += 1;
941
+ }
942
+ return { quoteLines, nextIndex };
943
+ }
944
+
945
+ function isMarkdownTextBlockBoundary(lines: string[], index: number): boolean {
946
+ const current = lines[index] ?? "";
947
+ const following = lines[index + 1] ?? "";
948
+ if (current.trim().length === 0) return true;
949
+ if (isFencedCodeStart(current)) return true;
950
+ if (canStartIndentedCodeBlock(lines, index)) return true;
951
+ if (/^\s*>/.test(current)) return true;
952
+ return current.includes("|") && isMarkdownTableSeparator(following);
953
+ }
954
+
955
+ function collectMarkdownTextBlockLines(
956
+ lines: string[],
957
+ index: number,
958
+ ): { textLines: string[]; nextIndex: number } {
959
+ const textLines: string[] = [];
960
+ let nextIndex = index;
961
+ while (nextIndex < lines.length) {
962
+ if (isMarkdownTextBlockBoundary(lines, nextIndex)) break;
963
+ textLines.push(lines[nextIndex] ?? "");
964
+ nextIndex += 1;
965
+ }
966
+ return { textLines, nextIndex };
967
+ }
968
+
969
+ function renderMarkdownDocumentBlocks(
970
+ normalizedMarkdown: string,
971
+ ): TelegramRenderedBlockWithSpacing[] {
972
+ const renderedBlocks: TelegramRenderedBlockWithSpacing[] = [];
973
+ let minimumBlankLinesBeforeNextBlock = 0;
974
+ const pushRenderedBlocks = (
975
+ blocks: string[],
976
+ blankLinesBefore: number,
977
+ ): void => {
978
+ const effectiveBlankLinesBefore =
979
+ renderedBlocks.length === 0
980
+ ? blankLinesBefore
981
+ : Math.max(blankLinesBefore, minimumBlankLinesBeforeNextBlock);
982
+ for (const [blockIndex, block] of blocks.entries()) {
983
+ renderedBlocks.push({
984
+ text: block,
985
+ blankLinesBefore: blockIndex === 0 ? effectiveBlankLinesBefore : 0,
986
+ });
987
+ }
988
+ minimumBlankLinesBeforeNextBlock = 0;
989
+ };
990
+ const lines = normalizedMarkdown.split("\n");
991
+ let index = 0;
992
+ let pendingBlankLines = 0;
993
+ while (index < lines.length) {
994
+ const line = lines[index] ?? "";
995
+ const nextLine = lines[index + 1] ?? "";
996
+ if (line.trim().length === 0) {
997
+ pendingBlankLines += 1;
998
+ index += 1;
999
+ continue;
1000
+ }
1001
+ const heading = matchMarkdownHeadingLine(line);
1002
+ if (heading) {
1003
+ pushRenderedBlocks(
1004
+ renderMarkdownTextBlock(line),
1005
+ renderedBlocks.length === 0
1006
+ ? pendingBlankLines
1007
+ : Math.max(pendingBlankLines, 1),
1008
+ );
1009
+ pendingBlankLines = 0;
1010
+ minimumBlankLinesBeforeNextBlock = 1;
1011
+ index += 1;
1012
+ continue;
1013
+ }
1014
+ const fence = parseMarkdownFence(line);
1015
+ if (fence) {
1016
+ const block = collectFencedMarkdownCodeLines(lines, index, fence);
1017
+ index = block.nextIndex;
1018
+ pushRenderedBlocks(
1019
+ renderMarkdownCodeBlock(block.codeLines.join("\n"), fence.info),
1020
+ pendingBlankLines,
1021
+ );
1022
+ pendingBlankLines = 0;
1023
+ continue;
1024
+ }
1025
+ if (line.includes("|") && isMarkdownTableSeparator(nextLine)) {
1026
+ const block = collectMarkdownTableBlockLines(lines, index);
1027
+ index = block.nextIndex;
1028
+ pushRenderedBlocks(
1029
+ renderMarkdownTableBlock(block.tableLines),
1030
+ pendingBlankLines,
1031
+ );
1032
+ pendingBlankLines = 0;
1033
+ continue;
1034
+ }
1035
+ if (canStartIndentedCodeBlock(lines, index)) {
1036
+ const block = collectIndentedMarkdownCodeLines(lines, index);
1037
+ index = block.nextIndex;
1038
+ pushRenderedBlocks(
1039
+ renderMarkdownCodeBlock(block.codeLines.join("\n")),
1040
+ pendingBlankLines,
1041
+ );
1042
+ pendingBlankLines = 0;
1043
+ continue;
1044
+ }
1045
+ if (/^\s*>/.test(line)) {
1046
+ const block = collectMarkdownQuoteBlockLines(lines, index);
1047
+ index = block.nextIndex;
1048
+ pushRenderedBlocks(
1049
+ renderMarkdownQuoteBlock(block.quoteLines),
1050
+ pendingBlankLines,
1051
+ );
1052
+ pendingBlankLines = 0;
1053
+ continue;
1054
+ }
1055
+ const block = collectMarkdownTextBlockLines(lines, index);
1056
+ index = block.nextIndex;
1057
+ pushRenderedBlocks(
1058
+ renderMarkdownTextBlock(block.textLines.join("\n")),
1059
+ pendingBlankLines,
1060
+ );
1061
+ pendingBlankLines = 0;
1062
+ }
1063
+ return renderedBlocks;
1064
+ }
1065
+
1066
+ interface TelegramMarkdownChunkAccumulator {
1067
+ chunks: string[];
1068
+ current: string;
1069
+ }
1070
+
1071
+ function flushTelegramMarkdownChunkAccumulator(
1072
+ accumulator: TelegramMarkdownChunkAccumulator,
1073
+ ): void {
1074
+ if (accumulator.current.length === 0) return;
1075
+ accumulator.chunks.push(accumulator.current);
1076
+ accumulator.current = "";
1077
+ }
1078
+
1079
+ function splitOversizedTelegramMarkdownBlock(text: string): string[] {
1080
+ const chunks: string[] = [];
1081
+ for (let i = 0; i < text.length; i += MAX_MESSAGE_LENGTH) {
1082
+ chunks.push(text.slice(i, i + MAX_MESSAGE_LENGTH));
1083
+ }
1084
+ return chunks;
1085
+ }
1086
+
1087
+ function appendTelegramRenderedMarkdownBlock(
1088
+ accumulator: TelegramMarkdownChunkAccumulator,
1089
+ block: TelegramRenderedBlockWithSpacing,
1090
+ ): void {
1091
+ const separator = "\n".repeat(block.blankLinesBefore + 1);
1092
+ const candidate =
1093
+ accumulator.current.length === 0
1094
+ ? block.text
1095
+ : `${accumulator.current}${separator}${block.text}`;
1096
+ if (candidate.length <= MAX_MESSAGE_LENGTH) {
1097
+ accumulator.current = candidate;
1098
+ return;
1099
+ }
1100
+ flushTelegramMarkdownChunkAccumulator(accumulator);
1101
+ if (block.text.length <= MAX_MESSAGE_LENGTH) {
1102
+ accumulator.current = block.text;
1103
+ return;
1104
+ }
1105
+ accumulator.chunks.push(...splitOversizedTelegramMarkdownBlock(block.text));
1106
+ }
1107
+
1108
+ function chunkTelegramRenderedMarkdownBlocks(
1109
+ renderedBlocks: TelegramRenderedBlockWithSpacing[],
1110
+ ): string[] {
1111
+ const accumulator: TelegramMarkdownChunkAccumulator = {
1112
+ chunks: [],
1113
+ current: "",
1114
+ };
1115
+ for (const block of renderedBlocks) {
1116
+ appendTelegramRenderedMarkdownBlock(accumulator, block);
1117
+ }
1118
+ flushTelegramMarkdownChunkAccumulator(accumulator);
1119
+ return accumulator.chunks;
1120
+ }
1121
+
1122
+ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
1123
+ const normalized = normalizeMarkdownDocument(markdown);
1124
+ if (normalized.length === 0) return [];
1125
+ return chunkTelegramRenderedMarkdownBlocks(
1126
+ renderMarkdownDocumentBlocks(normalized),
1127
+ );
1128
+ }
1129
+
1130
+ // --- Unified Telegram Rendering ---
1131
+
1132
+ export type TelegramRenderMode = "plain" | "markdown" | "html";
1133
+
1134
+ export interface TelegramRenderedChunk {
1135
+ text: string;
1136
+ parseMode?: "HTML";
1137
+ }
1138
+
1139
+ function chunkParagraphs(text: string): string[] {
1140
+ if (text.length <= MAX_MESSAGE_LENGTH) return [text];
1141
+ const normalized = text.replace(/\r\n/g, "\n");
1142
+ const paragraphs = normalized.split(/\n\n+/);
1143
+ const chunks: string[] = [];
1144
+ let current = "";
1145
+ const flushCurrent = (): void => {
1146
+ if (current.trim().length > 0) chunks.push(current);
1147
+ current = "";
1148
+ };
1149
+ const splitLongBlock = (block: string): string[] => {
1150
+ if (block.length <= MAX_MESSAGE_LENGTH) return [block];
1151
+ const lines = block.split("\n");
1152
+ const lineChunks: string[] = [];
1153
+ let lineCurrent = "";
1154
+ for (const line of lines) {
1155
+ const candidate =
1156
+ lineCurrent.length === 0 ? line : `${lineCurrent}\n${line}`;
1157
+ if (candidate.length <= MAX_MESSAGE_LENGTH) {
1158
+ lineCurrent = candidate;
1159
+ continue;
1160
+ }
1161
+ if (lineCurrent.length > 0) {
1162
+ lineChunks.push(lineCurrent);
1163
+ lineCurrent = "";
1164
+ }
1165
+ if (line.length <= MAX_MESSAGE_LENGTH) {
1166
+ lineCurrent = line;
1167
+ continue;
1168
+ }
1169
+ for (let i = 0; i < line.length; i += MAX_MESSAGE_LENGTH) {
1170
+ lineChunks.push(line.slice(i, i + MAX_MESSAGE_LENGTH));
1171
+ }
1172
+ }
1173
+ if (lineCurrent.length > 0) {
1174
+ lineChunks.push(lineCurrent);
1175
+ }
1176
+ return lineChunks;
1177
+ };
1178
+ for (const paragraph of paragraphs) {
1179
+ if (paragraph.length === 0) continue;
1180
+ const parts = splitLongBlock(paragraph);
1181
+ for (const part of parts) {
1182
+ const candidate = current.length === 0 ? part : `${current}\n\n${part}`;
1183
+ if (candidate.length <= MAX_MESSAGE_LENGTH) {
1184
+ current = candidate;
1185
+ } else {
1186
+ flushCurrent();
1187
+ current = part;
1188
+ }
1189
+ }
1190
+ }
1191
+ flushCurrent();
1192
+ return chunks;
1193
+ }
1194
+
1195
+ export function renderTelegramMessage(
1196
+ text: string,
1197
+ options?: { mode?: TelegramRenderMode },
1198
+ ): TelegramRenderedChunk[] {
1199
+ const mode = options?.mode ?? "plain";
1200
+ if (mode === "plain") {
1201
+ return chunkParagraphs(text).map((chunk) => ({ text: chunk }));
1202
+ }
1203
+ if (mode === "html") {
1204
+ return chunkHtmlPreservingTags(text, MAX_MESSAGE_LENGTH).map((chunk) => ({
1205
+ text: chunk,
1206
+ parseMode: "HTML",
1207
+ }));
1208
+ }
1209
+ return renderMarkdownToTelegramHtmlChunks(text).map((chunk) => ({
1210
+ text: chunk,
1211
+ parseMode: "HTML",
1212
+ }));
1213
+ }