@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,1854 @@
1
+ /**
2
+ * Telegram command routing helpers
3
+ * Zones: telegram controls, pi agent commands, queue controls
4
+ * Owns Telegram slash-command normalization, bot command metadata, and pi-side command registration behind runtime ports
5
+ */
6
+
7
+ import {
8
+ pairTelegramUserIfNeeded,
9
+ TELEGRAM_DEFAULT_PROFILE_NAME,
10
+ } from "./config.ts";
11
+ import type { ExtensionAPI, ExtensionCommandContext } from "./pi.ts";
12
+ import type { TelegramBridgeStatusLineOptions } from "./status.ts";
13
+ import {
14
+ createTelegramControlItemBuilder,
15
+ createTelegramControlQueueController,
16
+ createTelegramQueueAdmissionReceipt,
17
+ type PendingTelegramControlItem,
18
+ type TelegramQueueAdmissionReceipt,
19
+ } from "./queue.ts";
20
+
21
+ export interface ParsedTelegramCommand {
22
+ name: string;
23
+ args: string;
24
+ }
25
+
26
+ export interface TelegramBotCommandDefinition {
27
+ command: string;
28
+ description: string;
29
+ }
30
+
31
+ export interface TelegramPromptTemplateMenuCommand {
32
+ command: string;
33
+ description?: string;
34
+ }
35
+
36
+ const TELEGRAM_EXTENSION_COMMAND_REGISTRY_KEY = "__piTelegramCommandRegistry__";
37
+ const TELEGRAM_BOT_COMMAND_NAME_PATTERN = /^[a-z0-9_]{1,32}$/;
38
+
39
+ export interface TelegramExtensionCommandContext {
40
+ name: string;
41
+ args: string;
42
+ reply: (text: string) => Promise<void>;
43
+ enqueuePrompt: (prompt: string) => Promise<void>;
44
+ }
45
+
46
+ export interface TelegramExtensionCommandRegistration {
47
+ name: string;
48
+ description?: string;
49
+ order?: number;
50
+ showInMenu?: boolean;
51
+ emoji?: string;
52
+ handler: (ctx: TelegramExtensionCommandContext) => Promise<void> | void;
53
+ }
54
+
55
+ interface RegisteredTelegramExtensionCommand {
56
+ name: string;
57
+ description?: string;
58
+ order: number;
59
+ showInMenu: boolean;
60
+ emoji?: string;
61
+ handler: TelegramExtensionCommandRegistration["handler"];
62
+ }
63
+
64
+ interface TelegramExtensionCommandRegistry {
65
+ commands: Map<string, RegisteredTelegramExtensionCommand>;
66
+ }
67
+
68
+ function getOrCreateTelegramCommandRegistry(): TelegramExtensionCommandRegistry {
69
+ const existing = (globalThis as Record<string, unknown>)[
70
+ TELEGRAM_EXTENSION_COMMAND_REGISTRY_KEY
71
+ ];
72
+ if (
73
+ existing &&
74
+ typeof existing === "object" &&
75
+ existing !== null &&
76
+ "commands" in existing &&
77
+ existing.commands instanceof Map
78
+ ) {
79
+ return existing as TelegramExtensionCommandRegistry;
80
+ }
81
+ const registry: TelegramExtensionCommandRegistry = { commands: new Map() };
82
+ (globalThis as Record<string, unknown>)[
83
+ TELEGRAM_EXTENSION_COMMAND_REGISTRY_KEY
84
+ ] = registry;
85
+ return registry;
86
+ }
87
+
88
+ export function normalizeTelegramExtensionCommandName(name: string): string {
89
+ return name.trim().replace(/^\/+/, "").toLowerCase();
90
+ }
91
+
92
+ export function isTelegramExtensionCommandName(name: string): boolean {
93
+ return TELEGRAM_BOT_COMMAND_NAME_PATTERN.test(name);
94
+ }
95
+
96
+ function normalizeTelegramExtensionCommandEmoji(
97
+ emoji: string | undefined,
98
+ ): string | undefined {
99
+ const normalized = emoji?.trim();
100
+ return normalized ? normalized : undefined;
101
+ }
102
+
103
+ export function registerTelegramCommand(
104
+ registration: TelegramExtensionCommandRegistration,
105
+ ): () => void {
106
+ const name = normalizeTelegramExtensionCommandName(registration.name);
107
+ const showInMenu = registration.showInMenu ?? false;
108
+ const emoji = normalizeTelegramExtensionCommandEmoji(registration.emoji);
109
+ if (!isTelegramExtensionCommandName(name)) {
110
+ throw new Error(`Invalid Telegram command name: ${registration.name}`);
111
+ }
112
+ if (showInMenu && !emoji) {
113
+ throw new Error(`Visible Telegram command requires emoji: ${name}`);
114
+ }
115
+ if (emoji && emoji.length > 8) {
116
+ throw new Error(`Telegram command emoji is too long: ${name}`);
117
+ }
118
+ if (isTelegramReservedCommandName(name)) {
119
+ throw new Error(
120
+ `Telegram command conflicts with built-in command: ${name}`,
121
+ );
122
+ }
123
+ const registry = getOrCreateTelegramCommandRegistry();
124
+ if (registry.commands.has(name)) {
125
+ throw new Error(`Telegram command is already registered: ${name}`);
126
+ }
127
+ const command: RegisteredTelegramExtensionCommand = {
128
+ name,
129
+ description: registration.description,
130
+ order: registration.order ?? 0,
131
+ showInMenu,
132
+ emoji,
133
+ handler: registration.handler,
134
+ };
135
+ registry.commands.set(name, command);
136
+ return () => {
137
+ if (registry.commands.get(name) === command) registry.commands.delete(name);
138
+ };
139
+ }
140
+
141
+ export function getTelegramExtensionCommands(): RegisteredTelegramExtensionCommand[] {
142
+ return Array.from(
143
+ getOrCreateTelegramCommandRegistry().commands.values(),
144
+ ).sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));
145
+ }
146
+
147
+ export function findTelegramExtensionCommand(
148
+ name: string | undefined,
149
+ ): RegisteredTelegramExtensionCommand | undefined {
150
+ if (!name) return undefined;
151
+ return getOrCreateTelegramCommandRegistry().commands.get(
152
+ normalizeTelegramExtensionCommandName(name),
153
+ );
154
+ }
155
+
156
+ export function clearTelegramExtensionCommands(): void {
157
+ getOrCreateTelegramCommandRegistry().commands.clear();
158
+ }
159
+
160
+ export const TELEGRAM_COMMAND_EMOJI = {
161
+ start: "🟢",
162
+ status: "📊",
163
+ model: "🤖",
164
+ thinking: "🧠",
165
+ compact: "🗜",
166
+ queue: "🔢",
167
+ thread: "🧵",
168
+ next: "⏩",
169
+ continue: "▶️",
170
+ abort: "⏹️",
171
+ stop: "🟥",
172
+ } as const;
173
+
174
+ export type TelegramCommandEmojiName = keyof typeof TELEGRAM_COMMAND_EMOJI;
175
+
176
+ export function getTelegramCommandEmoji(
177
+ command: TelegramCommandEmojiName,
178
+ ): string {
179
+ return TELEGRAM_COMMAND_EMOJI[command];
180
+ }
181
+
182
+ export function formatTelegramCommandEmojiPrefix(
183
+ command: TelegramCommandEmojiName,
184
+ ): string {
185
+ return `${getTelegramCommandEmoji(command)} `;
186
+ }
187
+
188
+ export const TELEGRAM_COMPACTION_STARTED_TEXT =
189
+ `${formatTelegramCommandEmojiPrefix("compact")}Compaction started.`;
190
+ export const TELEGRAM_COMPACTION_COMPLETED_TEXT = "✅ Compaction completed.";
191
+
192
+ function formatTelegramBotCommandDescription(
193
+ command: TelegramCommandEmojiName,
194
+ description: string,
195
+ ): string {
196
+ return `${formatTelegramCommandEmojiPrefix(command)}${description}`;
197
+ }
198
+
199
+ export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinition[] =
200
+ [
201
+ {
202
+ command: "start",
203
+ description: formatTelegramBotCommandDescription(
204
+ "start",
205
+ "Open menu / Pair bridge",
206
+ ),
207
+ },
208
+ {
209
+ command: "compact",
210
+ description: formatTelegramBotCommandDescription(
211
+ "compact",
212
+ "Compact current session",
213
+ ),
214
+ },
215
+ {
216
+ command: "next",
217
+ description: formatTelegramBotCommandDescription(
218
+ "next",
219
+ "Force next turn",
220
+ ),
221
+ },
222
+ {
223
+ command: "continue",
224
+ description: formatTelegramBotCommandDescription(
225
+ "continue",
226
+ "Queue continue prompt",
227
+ ),
228
+ },
229
+ {
230
+ command: "abort",
231
+ description: formatTelegramBotCommandDescription("abort", "Abort Pi"),
232
+ },
233
+ {
234
+ command: "stop",
235
+ description: formatTelegramBotCommandDescription(
236
+ "stop",
237
+ "Abort Pi & Clear queue",
238
+ ),
239
+ },
240
+ ];
241
+
242
+ export const TELEGRAM_BOT_COMMANDS = TELEGRAM_BUILTIN_BOT_COMMANDS;
243
+
244
+ function getVisibleTelegramExtensionBotCommands(): TelegramBotCommandDefinition[] {
245
+ return getTelegramExtensionCommands()
246
+ .filter((command) => command.showInMenu && command.description)
247
+ .map((command) => ({
248
+ command: command.name,
249
+ description: `${command.emoji} ${command.description ?? command.name}`,
250
+ }));
251
+ }
252
+
253
+ export function getTelegramReservedCommandNames(): string[] {
254
+ return [
255
+ ...TELEGRAM_RESERVED_COMMAND_NAMES,
256
+ ...getTelegramExtensionCommands().map((command) => command.name),
257
+ ];
258
+ }
259
+
260
+ export interface TelegramBotCommandRegistrationDeps {
261
+ setMyCommands: (
262
+ commands: readonly TelegramBotCommandDefinition[],
263
+ ) => Promise<unknown>;
264
+ }
265
+
266
+ export async function registerTelegramBotCommands(
267
+ deps: TelegramBotCommandRegistrationDeps,
268
+ ): Promise<void> {
269
+ const extensionCommands = getVisibleTelegramExtensionBotCommands();
270
+ if (extensionCommands.length === 0) {
271
+ await deps.setMyCommands(TELEGRAM_BOT_COMMANDS);
272
+ return;
273
+ }
274
+ const compactCommandIndex = TELEGRAM_BOT_COMMANDS.findIndex(
275
+ (command) => command.command === "compact",
276
+ );
277
+ if (compactCommandIndex === -1) {
278
+ await deps.setMyCommands([...TELEGRAM_BOT_COMMANDS, ...extensionCommands]);
279
+ return;
280
+ }
281
+ await deps.setMyCommands([
282
+ ...TELEGRAM_BOT_COMMANDS.slice(0, compactCommandIndex + 1),
283
+ ...extensionCommands,
284
+ ...TELEGRAM_BOT_COMMANDS.slice(compactCommandIndex + 1),
285
+ ]);
286
+ }
287
+
288
+ export function createTelegramBotCommandRegistrar(
289
+ deps: TelegramBotCommandRegistrationDeps,
290
+ ): () => Promise<void> {
291
+ let pending: Promise<void> | undefined;
292
+ return () => {
293
+ if (pending) return pending;
294
+ let request: Promise<void>;
295
+ request = registerTelegramBotCommands(deps).finally(() => {
296
+ if (pending === request) pending = undefined;
297
+ });
298
+ pending = request;
299
+ return request;
300
+ };
301
+ }
302
+
303
+ export interface TelegramBridgeCommandStartPollingOptions {
304
+ force?: boolean;
305
+ forceFreshLeaderThread?: boolean;
306
+ }
307
+
308
+ export interface TelegramBridgeCommandStartPollingResult {
309
+ ok: boolean;
310
+ message?: string;
311
+ canTakeover?: boolean;
312
+ owner?: string;
313
+ }
314
+
315
+ export type TelegramPollingStartRecoveryResult =
316
+ | { kind: "unhandled" }
317
+ | { kind: "retry"; message: string }
318
+ | { kind: "blocked"; message: string };
319
+
320
+ export interface TelegramBridgeCommandRegistrationDeps {
321
+ promptForConfig: (ctx: ExtensionCommandContext, profileName?: string) => Promise<void>;
322
+ getStatusLines: (options?: TelegramBridgeStatusLineOptions) => string[];
323
+ reloadConfig: () => Promise<void>;
324
+ hasBotToken: () => boolean;
325
+ startPolling: (
326
+ ctx: ExtensionCommandContext,
327
+ options?: TelegramBridgeCommandStartPollingOptions,
328
+ ) =>
329
+ | void
330
+ | Promise<void | TelegramBridgeCommandStartPollingResult>
331
+ | TelegramBridgeCommandStartPollingResult;
332
+ stopPolling: () => Promise<void | string>;
333
+ recoverPollingStart?: (
334
+ error: unknown,
335
+ ) => Promise<TelegramPollingStartRecoveryResult>;
336
+ getDisconnectThreadName?: () => string | undefined;
337
+ updateStatus: (ctx: ExtensionCommandContext) => void;
338
+ getProfileNames?: () => string[];
339
+ activateDefaultProfileConfig?: (ctx: ExtensionCommandContext) => Promise<void>;
340
+ activateProfileConfig?: (
341
+ ctx: ExtensionCommandContext,
342
+ profileName: string,
343
+ ) => Promise<boolean>;
344
+ }
345
+
346
+ function parseTelegramProfileArg(args: string): string | undefined {
347
+ const word = args.trim().split(/\s+/)[0];
348
+ if (!word || word.length === 0) return undefined;
349
+ if (word.startsWith("-")) return undefined;
350
+ return word === TELEGRAM_DEFAULT_PROFILE_NAME ? undefined : word;
351
+ }
352
+
353
+ function formatTelegramTakeoverTitle(ctx: ExtensionCommandContext): string {
354
+ return ctx.ui.theme.fg("accent", "pi-telegram");
355
+ }
356
+
357
+ function formatTelegramTakeoverPrompt(
358
+ ctx: ExtensionCommandContext,
359
+ owner?: string,
360
+ ): string {
361
+ const theme = ctx.ui.theme;
362
+ const action = theme.fg("warning", "move singleton lock here?");
363
+ const from = theme.fg("muted", "from:");
364
+ const to = theme.fg("muted", "to:");
365
+ const source = owner ?? "another Pi instance";
366
+ return `${action}\n\n${from} ${source}\n${to} ${ctx.cwd}`;
367
+ }
368
+
369
+ export function registerTelegramBridgeCommands(
370
+ pi: ExtensionAPI,
371
+ deps: TelegramBridgeCommandRegistrationDeps,
372
+ ): void {
373
+ pi.registerCommand("telegram-setup", {
374
+ description: "Configure Telegram bot token. Use /telegram-setup <name> for named profiles.",
375
+ handler: async (args, ctx) => {
376
+ await deps.promptForConfig(ctx, parseTelegramProfileArg(args));
377
+ },
378
+ });
379
+ pi.registerCommand("telegram-status", {
380
+ description: "Show Telegram bridge status",
381
+ handler: async (args, ctx) => {
382
+ const verbose = /(^|\s)(--debug|debug|--verbose|verbose)(\s|$)/i.test(
383
+ args,
384
+ );
385
+ ctx.ui.notify(deps.getStatusLines({ verbose }).join("\n"), "info");
386
+ },
387
+ });
388
+ pi.registerCommand("telegram-connect", {
389
+ description: "Start the Telegram bridge. Use /telegram-connect <name> for named profiles.",
390
+ handler: async (args, ctx) => {
391
+ const profileName = parseTelegramProfileArg(args);
392
+ if (profileName && deps.activateProfileConfig) {
393
+ const ok = await deps.activateProfileConfig(ctx, profileName);
394
+ if (!ok) {
395
+ ctx.ui.notify(`Profile "${profileName}" not found.`, "error");
396
+ deps.updateStatus(ctx);
397
+ return;
398
+ }
399
+ ctx.ui.notify(`Activated profile "${profileName}".`, "info");
400
+ } else {
401
+ await (deps.activateDefaultProfileConfig?.(ctx) ?? deps.reloadConfig());
402
+ }
403
+ if (!deps.hasBotToken()) {
404
+ const profileNames = deps.getProfileNames?.() ?? [];
405
+ if (!profileName && profileNames.length > 0) {
406
+ ctx.ui.notify(
407
+ `No default Telegram profile configured. Available profiles: ${profileNames.join(", ")}. Use /telegram-connect <profileName> or /telegram-setup to create a default profile.`,
408
+ "info",
409
+ );
410
+ deps.updateStatus(ctx);
411
+ return;
412
+ }
413
+ await deps.promptForConfig(ctx, profileName);
414
+ return;
415
+ }
416
+ let recoveryUsed = false;
417
+ const startWithRecovery = async (
418
+ options: TelegramBridgeCommandStartPollingOptions,
419
+ ): Promise<void | TelegramBridgeCommandStartPollingResult> => {
420
+ try {
421
+ return await deps.startPolling(ctx, options);
422
+ } catch (error) {
423
+ if (!deps.recoverPollingStart || recoveryUsed) throw error;
424
+ const recovery = await deps.recoverPollingStart(error);
425
+ if (recovery.kind === "unhandled") throw error;
426
+ if (recovery.kind === "blocked") {
427
+ return { ok: false, message: recovery.message };
428
+ }
429
+ recoveryUsed = true;
430
+ try {
431
+ const retry = await deps.startPolling(ctx, options);
432
+ if (!retry) {
433
+ return { ok: true, message: recovery.message };
434
+ }
435
+ return {
436
+ ...retry,
437
+ message: retry.ok
438
+ ? `${recovery.message} ${retry.message ?? "Telegram bridge connected."}`
439
+ : retry.message,
440
+ };
441
+ } catch {
442
+ return {
443
+ ok: false,
444
+ message:
445
+ "Telegram temporary state was recovered, but the bridge could not restart. Restart this Pi instance and run /telegram-connect again.",
446
+ };
447
+ }
448
+ }
449
+ };
450
+ let result = await startWithRecovery({
451
+ forceFreshLeaderThread: true,
452
+ });
453
+ if (result && !result.ok && result.canTakeover) {
454
+ const confirmed = await ctx.ui.confirm(
455
+ formatTelegramTakeoverTitle(ctx),
456
+ formatTelegramTakeoverPrompt(ctx, result.owner),
457
+ );
458
+ if (!confirmed) {
459
+ ctx.ui.notify("Telegram bridge takeover cancelled.", "info");
460
+ deps.updateStatus(ctx);
461
+ return;
462
+ }
463
+ result = await startWithRecovery({
464
+ force: true,
465
+ forceFreshLeaderThread: true,
466
+ });
467
+ }
468
+ if (result?.message) {
469
+ ctx.ui.notify(result.message, result.ok ? "info" : "warning");
470
+ }
471
+ deps.updateStatus(ctx);
472
+ },
473
+ });
474
+ pi.registerCommand("telegram-disconnect", {
475
+ description:
476
+ "Stop Telegram; in Threaded Mode, delete this instance's current thread",
477
+ handler: async (_args, ctx) => {
478
+ const threadName = deps.getDisconnectThreadName?.();
479
+ if (threadName) {
480
+ const confirmed = await ctx.ui.confirm(
481
+ ctx.ui.theme.fg("accent", "pi-telegram"),
482
+ `Delete Telegram thread ${ctx.ui.theme.fg("warning", threadName)} and disconnect this Pi session?`,
483
+ );
484
+ if (!confirmed) {
485
+ ctx.ui.notify("Telegram disconnect cancelled.", "info");
486
+ deps.updateStatus(ctx);
487
+ return;
488
+ }
489
+ }
490
+ try {
491
+ const message = await deps.stopPolling();
492
+ if (message) ctx.ui.notify(message, "info");
493
+ } catch (error) {
494
+ const detail = error instanceof Error ? error.message : String(error);
495
+ ctx.ui.notify(
496
+ `Telegram disconnect did not complete: ${detail} Keep this Pi session open, restore leader connectivity, inspect /telegram-status --debug, and retry /telegram-disconnect.`,
497
+ "warning",
498
+ );
499
+ throw error;
500
+ } finally {
501
+ deps.updateStatus(ctx);
502
+ }
503
+ },
504
+ });
505
+ }
506
+
507
+ export const TELEGRAM_RESERVED_COMMAND_NAMES = [
508
+ "stop",
509
+ "abort",
510
+ "next",
511
+ "continue",
512
+ "status",
513
+ "queue",
514
+ "compact",
515
+ "model",
516
+ "thinking",
517
+ "settings",
518
+ "help",
519
+ "start",
520
+ ] as const;
521
+
522
+ export type TelegramReservedCommandName =
523
+ (typeof TELEGRAM_RESERVED_COMMAND_NAMES)[number];
524
+
525
+ const TELEGRAM_RESERVED_COMMAND_NAME_SET = new Set<string>(
526
+ TELEGRAM_RESERVED_COMMAND_NAMES,
527
+ );
528
+
529
+ export function isTelegramReservedCommandName(
530
+ commandName: string | undefined,
531
+ ): commandName is TelegramReservedCommandName {
532
+ return (
533
+ commandName !== undefined &&
534
+ TELEGRAM_RESERVED_COMMAND_NAME_SET.has(commandName)
535
+ );
536
+ }
537
+
538
+ export type TelegramCommandAction =
539
+ | { kind: "ignore"; executionMode: "ignored" }
540
+ | { kind: "stop"; executionMode: "immediate" }
541
+ | { kind: "abort"; executionMode: "immediate" }
542
+ | { kind: "next"; executionMode: "immediate" }
543
+ | { kind: "continue"; executionMode: "immediate" }
544
+ | { kind: "queue"; executionMode: "immediate" }
545
+ | { kind: "compact"; executionMode: "immediate" }
546
+ | { kind: "status"; executionMode: "immediate" }
547
+ | { kind: "model"; executionMode: "immediate" }
548
+ | { kind: "thinking"; executionMode: "immediate" }
549
+ | { kind: "settings"; executionMode: "immediate" }
550
+ | {
551
+ kind: "help";
552
+ commandName: "help" | "start";
553
+ executionMode: "immediate";
554
+ };
555
+
556
+ export type TelegramCommandExecutionMode = "ignored" | "immediate";
557
+
558
+ export interface TelegramCommandActionDeps<TMessage, TContext> {
559
+ handleStop: (message: TMessage, ctx: TContext) => Promise<void>;
560
+ handleAbort: (message: TMessage, ctx: TContext) => Promise<void>;
561
+ handleNext: (message: TMessage, ctx: TContext) => Promise<void>;
562
+ handleContinue: (message: TMessage, ctx: TContext) => Promise<void>;
563
+ handleQueue: (message: TMessage, ctx: TContext) => Promise<void>;
564
+ handleCompact: (message: TMessage, ctx: TContext) => Promise<void>;
565
+ handleStatus: (message: TMessage, ctx: TContext) => Promise<void>;
566
+ handleModel: (message: TMessage, ctx: TContext) => Promise<void>;
567
+ handleThinking: (message: TMessage, ctx: TContext) => Promise<void>;
568
+ handleSettings?: (message: TMessage, ctx: TContext) => Promise<void>;
569
+ handleHelp: (
570
+ message: TMessage,
571
+ commandName: "help" | "start",
572
+ ctx: TContext,
573
+ ) => Promise<void>;
574
+ }
575
+
576
+ export interface TelegramStopCommandDeps {
577
+ hasAbortHandler: () => boolean;
578
+ clearPendingModelSwitch: () => void;
579
+ clearQueuedTelegramItems: () => number;
580
+ setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
581
+ abortCurrentTurn: () => void;
582
+ updateStatus: () => void;
583
+ sendTextReply: (text: string) => Promise<void>;
584
+ }
585
+
586
+ export interface TelegramRuntimeEventRecorderPort {
587
+ recordRuntimeEvent?: (
588
+ category: string,
589
+ error: unknown,
590
+ details?: Record<string, unknown>,
591
+ ) => void;
592
+ }
593
+
594
+ export interface TelegramCompactConfirmationReplyMarkup {
595
+ inline_keyboard: { text: string; callback_data: string }[][];
596
+ }
597
+
598
+ export interface TelegramCompactCommandDeps extends TelegramRuntimeEventRecorderPort {
599
+ isIdle: () => boolean;
600
+ hasPendingMessages: () => boolean;
601
+ hasActiveTelegramTurn: () => boolean;
602
+ hasDispatchPending: () => boolean;
603
+ hasQueuedTelegramItems: () => boolean;
604
+ isCompactionInProgress: () => boolean;
605
+ setCompactionInProgress: (inProgress: boolean) => void;
606
+ updateStatus: () => void;
607
+ dispatchNextQueuedTelegramTurn: () => void;
608
+ requestDeferredDispatchNextQueuedTelegramTurn?: (
609
+ dispatch: () => void,
610
+ ) => void;
611
+ startTypingLoop?: () => void;
612
+ stopTypingLoop?: () => void;
613
+ compact: (callbacks: {
614
+ onComplete: () => void;
615
+ onError: (error: unknown) => void;
616
+ }) => void;
617
+ sendTextReply: (text: string) => Promise<void>;
618
+ suppressStartNotice?: boolean;
619
+ }
620
+
621
+ export interface TelegramCompactConfirmationDeps {
622
+ sendInteractiveMessage: (
623
+ chatId: number,
624
+ text: string,
625
+ mode: "markdown" | "html" | "plain",
626
+ replyMarkup: TelegramCompactConfirmationReplyMarkup,
627
+ options?: { target?: { chatId: number; threadId?: number } },
628
+ ) => Promise<number | undefined>;
629
+ }
630
+
631
+ export interface TelegramCompactConfirmationCallbackQuery {
632
+ id: string;
633
+ data?: string;
634
+ message?: {
635
+ chat?: { id?: number };
636
+ message_id?: number;
637
+ message_thread_id?: number;
638
+ };
639
+ }
640
+
641
+ export interface TelegramCompactConfirmationCallbackDeps<TContext> {
642
+ ctx: TContext;
643
+ answerCallbackQuery: (
644
+ callbackQueryId: string,
645
+ text?: string,
646
+ ) => Promise<void>;
647
+ editInteractiveMessage: (
648
+ chatId: number,
649
+ messageId: number,
650
+ text: string,
651
+ mode: "markdown" | "html" | "plain",
652
+ replyMarkup: TelegramCompactConfirmationReplyMarkup,
653
+ ) => Promise<void>;
654
+ runCompact: (
655
+ ctx: TContext,
656
+ chatId: number,
657
+ replyToMessageId: number,
658
+ target?: { chatId: number; threadId?: number },
659
+ ) => Promise<void>;
660
+ }
661
+
662
+ export type TelegramControlCommandType =
663
+ PendingTelegramControlItem<unknown>["controlType"];
664
+
665
+ export interface TelegramCommandRuntimeMessage {
666
+ chat: { id: number; type?: string; title?: string };
667
+ message_id: number;
668
+ message_thread_id?: number;
669
+ from?: { id?: number };
670
+ pi_telegram_source_update_id?: number;
671
+ }
672
+
673
+ export interface TelegramCommandMessageTarget {
674
+ chatId: number;
675
+ threadId?: number;
676
+ replyToMessageId: number;
677
+ }
678
+
679
+ function canPairTelegramUserFromCommandMessage(
680
+ message: TelegramCommandRuntimeMessage,
681
+ ): boolean {
682
+ return message.chat.type === undefined || message.chat.type === "private";
683
+ }
684
+
685
+ export interface TelegramCommandTargetRuntimeDeps<TContext> {
686
+ enqueueControlItem: (
687
+ target: TelegramCommandMessageTarget,
688
+ ctx: TContext,
689
+ controlType: TelegramControlCommandType,
690
+ statusSummary: string,
691
+ execute: (ctx: TContext) => Promise<void>,
692
+ admissionReceipts?: TelegramQueueAdmissionReceipt[],
693
+ onQueued?: (item: PendingTelegramControlItem<TContext>) => void,
694
+ ) => void;
695
+ getAdmissionScope?: () => string | undefined;
696
+ getAdmissionJournalBinding?: () => string | undefined;
697
+ onControlQueued?: (
698
+ message: TelegramCommandRuntimeMessage,
699
+ receipt: TelegramQueueAdmissionReceipt,
700
+ ) => void;
701
+ showStatus: (
702
+ chatId: number,
703
+ replyToMessageId: number,
704
+ ctx: TContext,
705
+ threadId?: number,
706
+ ) => Promise<void>;
707
+ openModelMenu: (
708
+ chatId: number,
709
+ replyToMessageId: number,
710
+ ctx: TContext,
711
+ threadId?: number,
712
+ ) => Promise<void>;
713
+ openSettingsMenu?: (
714
+ chatId: number,
715
+ replyToMessageId: number,
716
+ ctx: TContext,
717
+ threadId?: number,
718
+ ) => Promise<void>;
719
+ sendTextReply: (
720
+ chatId: number,
721
+ replyToMessageId: number,
722
+ text: string,
723
+ options?: {
724
+ parseMode?: "HTML";
725
+ target?: { chatId: number; threadId?: number };
726
+ },
727
+ ) => Promise<unknown>;
728
+ }
729
+
730
+ export interface TelegramCommandTargetRuntime<
731
+ TMessage extends TelegramCommandRuntimeMessage,
732
+ TContext,
733
+ > {
734
+ enqueueControlItem: (
735
+ message: TMessage,
736
+ ctx: TContext,
737
+ controlType: TelegramControlCommandType,
738
+ statusSummary: string,
739
+ execute: (ctx: TContext) => Promise<void>,
740
+ ) => void;
741
+ showStatus: (message: TMessage, ctx: TContext) => Promise<void>;
742
+ openModelMenu: (message: TMessage, ctx: TContext) => Promise<void>;
743
+ openSettingsMenu: (message: TMessage, ctx: TContext) => Promise<void>;
744
+ sendTextReply: (
745
+ message: TMessage,
746
+ text: string,
747
+ options?: { parseMode?: "HTML" },
748
+ ) => Promise<void>;
749
+ }
750
+
751
+ export function getTelegramCommandMessageTarget(
752
+ message: TelegramCommandRuntimeMessage,
753
+ ): TelegramCommandMessageTarget {
754
+ return {
755
+ chatId: message.chat.id,
756
+ threadId:
757
+ typeof message.message_thread_id === "number"
758
+ ? message.message_thread_id
759
+ : undefined,
760
+ replyToMessageId: message.message_id,
761
+ };
762
+ }
763
+
764
+ export interface TelegramCommandControlQueueRuntimeDeps<TContext> {
765
+ createControlItem: (options: {
766
+ chatId: number;
767
+ target?: { chatId: number; threadId?: number };
768
+ replyToMessageId: number;
769
+ controlType: TelegramControlCommandType;
770
+ statusSummary: string;
771
+ admissionReceipts?: TelegramQueueAdmissionReceipt[];
772
+ execute: (ctx: TContext) => Promise<void>;
773
+ }) => PendingTelegramControlItem<TContext>;
774
+ appendControlItem: (
775
+ item: PendingTelegramControlItem<TContext>,
776
+ ctx: TContext,
777
+ ) => void;
778
+ dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
779
+ }
780
+
781
+ export function createTelegramCommandControlQueueRuntime<TContext>(
782
+ deps: TelegramCommandControlQueueRuntimeDeps<TContext>,
783
+ ): TelegramCommandTargetRuntimeDeps<TContext>["enqueueControlItem"] {
784
+ const controlQueueController = createTelegramControlQueueController({
785
+ appendControlItem: deps.appendControlItem,
786
+ dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
787
+ });
788
+ return createTelegramCommandControlEnqueueAdapter({
789
+ createControlItem: deps.createControlItem,
790
+ enqueueControlItem: controlQueueController.enqueue,
791
+ });
792
+ }
793
+
794
+ export function createTelegramCommandControlEnqueueAdapter<TContext>(deps: {
795
+ createControlItem: (options: {
796
+ chatId: number;
797
+ target?: { chatId: number; threadId?: number };
798
+ replyToMessageId: number;
799
+ controlType: TelegramControlCommandType;
800
+ statusSummary: string;
801
+ admissionReceipts?: TelegramQueueAdmissionReceipt[];
802
+ execute: (ctx: TContext) => Promise<void>;
803
+ }) => PendingTelegramControlItem<TContext>;
804
+ enqueueControlItem: (
805
+ item: PendingTelegramControlItem<TContext>,
806
+ ctx: TContext,
807
+ onQueued?: (item: PendingTelegramControlItem<TContext>) => void,
808
+ ) => void;
809
+ }): TelegramCommandTargetRuntimeDeps<TContext>["enqueueControlItem"] {
810
+ return (
811
+ target,
812
+ ctx,
813
+ controlType,
814
+ statusSummary,
815
+ execute,
816
+ admissionReceipts,
817
+ onQueued,
818
+ ) => {
819
+ deps.enqueueControlItem(
820
+ deps.createControlItem({
821
+ ...target,
822
+ controlType,
823
+ statusSummary,
824
+ ...(admissionReceipts?.length ? { admissionReceipts } : {}),
825
+ execute,
826
+ }),
827
+ ctx,
828
+ onQueued,
829
+ );
830
+ };
831
+ }
832
+
833
+ export type TelegramCommandTargetQueueRuntimeDeps<TContext> =
834
+ TelegramCommandControlQueueRuntimeDeps<TContext> &
835
+ Omit<TelegramCommandTargetRuntimeDeps<TContext>, "enqueueControlItem">;
836
+
837
+ export function createTelegramCommandTargetQueueRuntime<
838
+ TMessage extends TelegramCommandRuntimeMessage,
839
+ TContext,
840
+ >(
841
+ deps: TelegramCommandTargetQueueRuntimeDeps<TContext>,
842
+ ): TelegramCommandTargetRuntime<TMessage, TContext> {
843
+ return createTelegramCommandTargetRuntime({
844
+ enqueueControlItem: createTelegramCommandControlQueueRuntime({
845
+ createControlItem: deps.createControlItem,
846
+ appendControlItem: deps.appendControlItem,
847
+ dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
848
+ }),
849
+ getAdmissionScope: deps.getAdmissionScope,
850
+ getAdmissionJournalBinding: deps.getAdmissionJournalBinding,
851
+ onControlQueued: deps.onControlQueued,
852
+ showStatus: deps.showStatus,
853
+ openModelMenu: deps.openModelMenu,
854
+ openSettingsMenu: deps.openSettingsMenu,
855
+ sendTextReply: deps.sendTextReply,
856
+ });
857
+ }
858
+
859
+ export function createTelegramCommandTargetRuntime<
860
+ TMessage extends TelegramCommandRuntimeMessage,
861
+ TContext,
862
+ >(
863
+ deps: TelegramCommandTargetRuntimeDeps<TContext>,
864
+ ): TelegramCommandTargetRuntime<TMessage, TContext> {
865
+ return {
866
+ enqueueControlItem: (message, ctx, controlType, statusSummary, execute) => {
867
+ const sourceUpdateId = message.pi_telegram_source_update_id;
868
+ const baseReceipt =
869
+ typeof sourceUpdateId === "number"
870
+ ? createTelegramQueueAdmissionReceipt({
871
+ queueKind: "control",
872
+ scope: deps.getAdmissionScope?.() ?? "",
873
+ sourceUpdateIds: [sourceUpdateId],
874
+ })
875
+ : undefined;
876
+ const journalBindingKey = deps.getAdmissionJournalBinding?.();
877
+ const receipt = baseReceipt
878
+ ? {
879
+ ...baseReceipt,
880
+ ...(journalBindingKey ? { journalBindingKey } : {}),
881
+ }
882
+ : undefined;
883
+ deps.enqueueControlItem(
884
+ getTelegramCommandMessageTarget(message),
885
+ ctx,
886
+ controlType,
887
+ statusSummary,
888
+ execute,
889
+ receipt ? [receipt] : undefined,
890
+ receipt
891
+ ? () => deps.onControlQueued?.(message, receipt)
892
+ : undefined,
893
+ );
894
+ },
895
+ showStatus: (message, ctx) => {
896
+ const target = getTelegramCommandMessageTarget(message);
897
+ return deps.showStatus(
898
+ target.chatId,
899
+ target.replyToMessageId,
900
+ ctx,
901
+ target.threadId,
902
+ );
903
+ },
904
+ openModelMenu: (message, ctx) => {
905
+ const target = getTelegramCommandMessageTarget(message);
906
+ return deps.openModelMenu(
907
+ target.chatId,
908
+ target.replyToMessageId,
909
+ ctx,
910
+ target.threadId,
911
+ );
912
+ },
913
+ openSettingsMenu: async (message, ctx) => {
914
+ const target = getTelegramCommandMessageTarget(message);
915
+ if (!deps.openSettingsMenu) {
916
+ await deps.sendTextReply(
917
+ target.chatId,
918
+ target.replyToMessageId,
919
+ "Settings menu is unavailable.",
920
+ { target },
921
+ );
922
+ return;
923
+ }
924
+ await deps.openSettingsMenu(
925
+ target.chatId,
926
+ target.replyToMessageId,
927
+ ctx,
928
+ target.threadId,
929
+ );
930
+ },
931
+ sendTextReply: async (message, text, options) => {
932
+ const target = getTelegramCommandMessageTarget(message);
933
+ await deps.sendTextReply(target.chatId, target.replyToMessageId, text, {
934
+ ...options,
935
+ target,
936
+ });
937
+ },
938
+ };
939
+ }
940
+
941
+ export interface TelegramCommandOrPromptRuntimeDeps<TMessage, TContext> {
942
+ extractRawText: (messages: TMessage[]) => string;
943
+ shouldIgnoreMessages?: (messages: TMessage[]) => boolean;
944
+ handleCommand: (
945
+ commandName: string | undefined,
946
+ message: TMessage,
947
+ ctx: TContext,
948
+ ) => Promise<boolean>;
949
+ executeExtensionCommand?: (
950
+ command: ParsedTelegramCommand,
951
+ message: TMessage,
952
+ ctx: TContext,
953
+ ) => Promise<boolean>;
954
+ expandPromptTemplateCommand?: (
955
+ commandName: string,
956
+ args: string,
957
+ ) => string | undefined;
958
+ replaceMessageText: (message: TMessage, text: string) => TMessage;
959
+ enqueueTurn: (messages: TMessage[], ctx: TContext) => Promise<void>;
960
+ assertExecutionCurrent?: (message: TMessage) => void;
961
+ }
962
+
963
+ export interface TelegramCommandRuntimeDeps<
964
+ TMessage extends TelegramCommandRuntimeMessage,
965
+ TContext,
966
+ > extends TelegramRuntimeEventRecorderPort {
967
+ hasAbortHandler: () => boolean;
968
+ clearPendingModelSwitch: () => void;
969
+ hasQueuedTelegramItems: () => boolean;
970
+ clearQueuedTelegramItems: (ctx: TContext) => number;
971
+ setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
972
+ abortCurrentTurn: () => void;
973
+ isIdle: (ctx: TContext) => boolean;
974
+ hasPendingMessages: (ctx: TContext) => boolean;
975
+ hasActiveTelegramTurn: () => boolean;
976
+ hasDispatchPending: () => boolean;
977
+ isCompactionInProgress: () => boolean;
978
+ setCompactionInProgress: (inProgress: boolean) => void;
979
+ updateStatus: (ctx: TContext) => void;
980
+ isContextActive?: (ctx: TContext) => boolean;
981
+ dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
982
+ requestDeferredDispatchNextQueuedTelegramTurn?: (
983
+ dispatch: (ctx: TContext) => void,
984
+ ) => void;
985
+ startTypingLoop?: (
986
+ ctx: TContext,
987
+ chatId?: number,
988
+ options?: { target?: { chatId: number; threadId?: number } },
989
+ ) => void;
990
+ stopTypingLoop?: () => void;
991
+ enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
992
+ compact: (
993
+ ctx: TContext,
994
+ callbacks: { onComplete: () => void; onError: (error: unknown) => void },
995
+ ) => void;
996
+ enqueueControlItem: (
997
+ message: TMessage,
998
+ ctx: TContext,
999
+ controlType: TelegramControlCommandType,
1000
+ statusSummary: string,
1001
+ execute: (ctx: TContext) => Promise<void>,
1002
+ ) => void;
1003
+ showStatus: (message: TMessage, ctx: TContext) => Promise<void>;
1004
+ handleForumBootstrap?: (
1005
+ message: TMessage,
1006
+ ctx: TContext,
1007
+ ) => Promise<string | undefined>;
1008
+ openModelMenu: (message: TMessage, ctx: TContext) => Promise<void>;
1009
+ openThinkingMenu: (message: TMessage, ctx: TContext) => Promise<void>;
1010
+ openQueueMenu: (message: TMessage, ctx: TContext) => Promise<void>;
1011
+ openSettingsMenu?: (message: TMessage, ctx: TContext) => Promise<void>;
1012
+ getAllowedUserId: () => number | undefined;
1013
+ setAllowedUserId: (userId: number) => void;
1014
+ registerBotCommands: () => Promise<void>;
1015
+ getPromptTemplateCommands?: () => readonly TelegramPromptTemplateMenuCommand[];
1016
+ persistConfig: () => Promise<void>;
1017
+ sendTextReply: (
1018
+ message: TMessage,
1019
+ text: string,
1020
+ options?: { parseMode?: "HTML" },
1021
+ ) => Promise<void>;
1022
+ sendInteractiveMessage?: TelegramCompactConfirmationDeps["sendInteractiveMessage"];
1023
+ assertExecutionCurrent?: (message: TMessage) => void;
1024
+ }
1025
+
1026
+ export const TELEGRAM_APP_MENU_INTRO_HTML = [
1027
+ "<b>Pi Telegram</b>",
1028
+ "",
1029
+ `${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
1030
+ `${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
1031
+ `${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
1032
+ `${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
1033
+ `${formatTelegramCommandEmojiPrefix("abort")}/abort — Abort Pi`,
1034
+ `${formatTelegramCommandEmojiPrefix("stop")}/stop — Abort Pi & Clear queue`,
1035
+ ].join("\n");
1036
+
1037
+ function escapeTelegramCommandMenuHtml(text: string): string {
1038
+ return text
1039
+ .replace(/&/g, "&amp;")
1040
+ .replace(/</g, "&lt;")
1041
+ .replace(/>/g, "&gt;");
1042
+ }
1043
+
1044
+ function buildTelegramPromptTemplateMenuHtml(
1045
+ promptTemplates: readonly TelegramPromptTemplateMenuCommand[] = [],
1046
+ ): string {
1047
+ if (promptTemplates.length === 0) return "";
1048
+ return promptTemplates
1049
+ .map((template) => `🧩 /${escapeTelegramCommandMenuHtml(template.command)}`)
1050
+ .join("\n");
1051
+ }
1052
+
1053
+ function buildTelegramExtensionCommandMenuLines(): string[] {
1054
+ return getTelegramExtensionCommands()
1055
+ .filter((command) => command.showInMenu)
1056
+ .map((command) => {
1057
+ const prefix = `${escapeTelegramCommandMenuHtml(command.emoji ?? "")} /${escapeTelegramCommandMenuHtml(command.name)}`;
1058
+ if (!command.description) return prefix;
1059
+ return `${prefix} — ${escapeTelegramCommandMenuHtml(command.description)}`;
1060
+ });
1061
+ }
1062
+
1063
+ function buildTelegramAppMenuIntroHtml(): string {
1064
+ const extensionLines = buildTelegramExtensionCommandMenuLines();
1065
+ if (extensionLines.length === 0) return TELEGRAM_APP_MENU_INTRO_HTML;
1066
+ return [
1067
+ "<b>Pi Telegram</b>",
1068
+ "",
1069
+ `${formatTelegramCommandEmojiPrefix("start")}/start — Open menu / Pair bridge`,
1070
+ `${formatTelegramCommandEmojiPrefix("compact")}/compact — Compact current session`,
1071
+ ...extensionLines,
1072
+ `${formatTelegramCommandEmojiPrefix("next")}/next — Force next turn`,
1073
+ `${formatTelegramCommandEmojiPrefix("continue")}/continue — Queue continue prompt`,
1074
+ `${formatTelegramCommandEmojiPrefix("abort")}/abort — Abort Pi`,
1075
+ `${formatTelegramCommandEmojiPrefix("stop")}/stop — Abort Pi & Clear queue`,
1076
+ ].join("\n");
1077
+ }
1078
+
1079
+ export function buildTelegramAppMenuHtml(
1080
+ statusHtml: string,
1081
+ promptTemplates: readonly TelegramPromptTemplateMenuCommand[] = [],
1082
+ ): string {
1083
+ const introHtml = buildTelegramAppMenuIntroHtml();
1084
+ const promptTemplateHtml =
1085
+ buildTelegramPromptTemplateMenuHtml(promptTemplates);
1086
+ if (!promptTemplateHtml) return `${introHtml}\n\n${statusHtml}`;
1087
+ return `${introHtml}\n\n${promptTemplateHtml}\n\n${statusHtml}`;
1088
+ }
1089
+
1090
+ export function createTelegramAppMenuHtmlBuilder<TContext>(deps: {
1091
+ buildStatusHtml: (ctx: TContext) => string;
1092
+ getPromptTemplateCommands?: () => readonly TelegramPromptTemplateMenuCommand[];
1093
+ }): (ctx: TContext) => string {
1094
+ return (ctx) => {
1095
+ return buildTelegramAppMenuHtml(
1096
+ deps.buildStatusHtml(ctx),
1097
+ deps.getPromptTemplateCommands?.(),
1098
+ );
1099
+ };
1100
+ }
1101
+
1102
+ function getTelegramCommandErrorMessage(error: unknown): string {
1103
+ return error instanceof Error ? error.message : String(error);
1104
+ }
1105
+
1106
+ export function parseTelegramCommand(
1107
+ text: string,
1108
+ ): ParsedTelegramCommand | undefined {
1109
+ const trimmed = text.trim();
1110
+ if (!trimmed.startsWith("/")) return undefined;
1111
+ const [head, ...tail] = trimmed.split(/\s+/);
1112
+ const name = head.slice(1).split("@")[0]?.toLowerCase();
1113
+ if (!name) return undefined;
1114
+ return { name, args: tail.join(" ").trim() };
1115
+ }
1116
+
1117
+ export const TELEGRAM_COMMAND_ACTIONS = {
1118
+ stop: { kind: "stop", executionMode: "immediate" },
1119
+ abort: { kind: "abort", executionMode: "immediate" },
1120
+ next: { kind: "next", executionMode: "immediate" },
1121
+ continue: { kind: "continue", executionMode: "immediate" },
1122
+ status: { kind: "status", executionMode: "immediate" },
1123
+ queue: { kind: "queue", executionMode: "immediate" },
1124
+ compact: { kind: "compact", executionMode: "immediate" },
1125
+ model: { kind: "model", executionMode: "immediate" },
1126
+ thinking: { kind: "thinking", executionMode: "immediate" },
1127
+ settings: { kind: "settings", executionMode: "immediate" },
1128
+ help: { kind: "help", commandName: "help", executionMode: "immediate" },
1129
+ start: { kind: "help", commandName: "start", executionMode: "immediate" },
1130
+ } as const satisfies Record<TelegramReservedCommandName, TelegramCommandAction>;
1131
+
1132
+ export function buildTelegramCommandAction(
1133
+ commandName: string | undefined,
1134
+ ): TelegramCommandAction {
1135
+ if (!isTelegramReservedCommandName(commandName)) {
1136
+ return { kind: "ignore", executionMode: "ignored" };
1137
+ }
1138
+ return TELEGRAM_COMMAND_ACTIONS[commandName];
1139
+ }
1140
+
1141
+ export function getTelegramCommandExecutionMode(
1142
+ action: TelegramCommandAction,
1143
+ ): TelegramCommandExecutionMode {
1144
+ return action.executionMode;
1145
+ }
1146
+
1147
+ function formatTelegramQueuedTurnCount(count: number): string {
1148
+ return count === 1 ? "1 queued turn" : `${count} queued turns`;
1149
+ }
1150
+
1151
+ export async function handleTelegramStopCommand(
1152
+ deps: TelegramStopCommandDeps,
1153
+ ): Promise<void> {
1154
+ deps.clearPendingModelSwitch();
1155
+ const clearedCount = deps.clearQueuedTelegramItems();
1156
+ deps.setFoldQueuedPromptsIntoHistory(false);
1157
+ if (!deps.hasAbortHandler()) {
1158
+ const clearedSuffix =
1159
+ clearedCount > 0
1160
+ ? ` Cleared ${formatTelegramQueuedTurnCount(clearedCount)}.`
1161
+ : "";
1162
+ if (clearedCount > 0) deps.updateStatus();
1163
+ await deps.sendTextReply(`No active turn.${clearedSuffix}`);
1164
+ return;
1165
+ }
1166
+ deps.abortCurrentTurn();
1167
+ deps.updateStatus();
1168
+ const clearedSuffix =
1169
+ clearedCount > 0
1170
+ ? ` Cleared ${formatTelegramQueuedTurnCount(clearedCount)}.`
1171
+ : "";
1172
+ await deps.sendTextReply(`Aborted current turn.${clearedSuffix}`);
1173
+ }
1174
+
1175
+ export async function handleTelegramAbortCommand(deps: {
1176
+ hasAbortHandler: () => boolean;
1177
+ hasActiveTelegramTurn: () => boolean;
1178
+ clearPendingModelSwitch: () => void;
1179
+ abortCurrentTurn: () => void;
1180
+ setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
1181
+ updateStatus: () => void;
1182
+ sendTextReply: (text: string) => Promise<void>;
1183
+ }): Promise<void> {
1184
+ deps.clearPendingModelSwitch();
1185
+ if (!deps.hasAbortHandler()) {
1186
+ await deps.sendTextReply("No active turn.");
1187
+ return;
1188
+ }
1189
+ deps.setFoldQueuedPromptsIntoHistory(deps.hasActiveTelegramTurn());
1190
+ deps.abortCurrentTurn();
1191
+ deps.updateStatus();
1192
+ await deps.sendTextReply("Aborted current turn.");
1193
+ }
1194
+
1195
+ export async function handleTelegramNextCommand(deps: {
1196
+ hasAbortHandler: () => boolean;
1197
+ isIdle: () => boolean;
1198
+ hasQueuedItems: () => boolean;
1199
+ clearPendingModelSwitch: () => void;
1200
+ abortCurrentTurn: () => void;
1201
+ dispatchNextQueuedTurn: () => void;
1202
+ clearFoldForDispatch: () => void;
1203
+ updateStatus: () => void;
1204
+ sendTextReply: (
1205
+ text: string,
1206
+ options?: { parseMode?: "HTML" },
1207
+ ) => Promise<void>;
1208
+ }): Promise<void> {
1209
+ deps.clearPendingModelSwitch();
1210
+ if (!deps.hasQueuedItems()) {
1211
+ await deps.sendTextReply("<b>Queue is empty.</b>", { parseMode: "HTML" });
1212
+ return;
1213
+ }
1214
+ if (!deps.isIdle() && deps.hasAbortHandler()) {
1215
+ deps.clearFoldForDispatch();
1216
+ deps.abortCurrentTurn();
1217
+ deps.updateStatus();
1218
+ await deps.sendTextReply(
1219
+ "Aborted current turn. Dispatching next queued turn.",
1220
+ );
1221
+ return;
1222
+ }
1223
+ if (!deps.isIdle()) {
1224
+ await deps.sendTextReply("Pi is busy. Send /abort or /stop first.");
1225
+ return;
1226
+ }
1227
+ deps.dispatchNextQueuedTurn();
1228
+ deps.updateStatus();
1229
+ await deps.sendTextReply("Dispatching next queued turn.");
1230
+ }
1231
+
1232
+ export async function handleTelegramContinueCommand<TMessage, TContext>(
1233
+ message: TMessage,
1234
+ ctx: TContext,
1235
+ deps: {
1236
+ enqueueContinueTurn: (message: TMessage, ctx: TContext) => Promise<void>;
1237
+ },
1238
+ ): Promise<void> {
1239
+ await deps.enqueueContinueTurn(message, ctx);
1240
+ }
1241
+
1242
+ function dispatchNextQueuedTelegramTurnAfterCompact(
1243
+ deps: Pick<
1244
+ TelegramCompactCommandDeps,
1245
+ | "dispatchNextQueuedTelegramTurn"
1246
+ | "requestDeferredDispatchNextQueuedTelegramTurn"
1247
+ >,
1248
+ ): void {
1249
+ if (deps.requestDeferredDispatchNextQueuedTelegramTurn) {
1250
+ deps.requestDeferredDispatchNextQueuedTelegramTurn(
1251
+ deps.dispatchNextQueuedTelegramTurn,
1252
+ );
1253
+ return;
1254
+ }
1255
+ deps.dispatchNextQueuedTelegramTurn();
1256
+ }
1257
+
1258
+ export function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
1259
+ return {
1260
+ inline_keyboard: [
1261
+ [
1262
+ { text: "🗜 Yes, compact", callback_data: "compact:confirm" },
1263
+ { text: "❌ No", callback_data: "compact:cancel" },
1264
+ ],
1265
+ ],
1266
+ };
1267
+ }
1268
+
1269
+ export function getTelegramCompactConfirmationHtml(): string {
1270
+ return "<b>Compact session?</b>";
1271
+ }
1272
+
1273
+ export async function openTelegramCompactConfirmation(
1274
+ target: TelegramCommandMessageTarget,
1275
+ deps: TelegramCompactConfirmationDeps,
1276
+ ): Promise<void> {
1277
+ await deps.sendInteractiveMessage(
1278
+ target.chatId,
1279
+ getTelegramCompactConfirmationHtml(),
1280
+ "html",
1281
+ buildTelegramCompactConfirmationReplyMarkup(),
1282
+ target.threadId !== undefined
1283
+ ? { target: { chatId: target.chatId, threadId: target.threadId } }
1284
+ : undefined,
1285
+ );
1286
+ }
1287
+
1288
+ export async function handleTelegramCompactConfirmationCallback<TContext>(
1289
+ query: TelegramCompactConfirmationCallbackQuery,
1290
+ deps: TelegramCompactConfirmationCallbackDeps<TContext>,
1291
+ ): Promise<boolean> {
1292
+ if (query.data !== "compact:confirm" && query.data !== "compact:cancel") {
1293
+ return false;
1294
+ }
1295
+ const callbackMessage = query.message;
1296
+ const chatId = callbackMessage?.chat?.id;
1297
+ const messageId = callbackMessage?.message_id;
1298
+ if (typeof chatId !== "number" || typeof messageId !== "number") {
1299
+ await deps.answerCallbackQuery(query.id, "Interactive message expired.");
1300
+ return true;
1301
+ }
1302
+ if (query.data === "compact:cancel") {
1303
+ await deps.editInteractiveMessage(
1304
+ chatId,
1305
+ messageId,
1306
+ "Compaction cancelled.",
1307
+ "plain",
1308
+ { inline_keyboard: [] },
1309
+ );
1310
+ await deps.answerCallbackQuery(query.id);
1311
+ return true;
1312
+ }
1313
+ await deps.editInteractiveMessage(
1314
+ chatId,
1315
+ messageId,
1316
+ TELEGRAM_COMPACTION_STARTED_TEXT,
1317
+ "plain",
1318
+ { inline_keyboard: [] },
1319
+ );
1320
+ await deps.answerCallbackQuery(query.id);
1321
+ const threadId = callbackMessage?.message_thread_id;
1322
+ await deps.runCompact(
1323
+ deps.ctx,
1324
+ chatId,
1325
+ messageId,
1326
+ typeof threadId === "number" ? { chatId, threadId } : { chatId },
1327
+ );
1328
+ return true;
1329
+ }
1330
+
1331
+ export async function handleTelegramCompactCommand(
1332
+ deps: TelegramCompactCommandDeps,
1333
+ ): Promise<void> {
1334
+ if (
1335
+ !deps.isIdle() ||
1336
+ deps.hasPendingMessages() ||
1337
+ deps.hasActiveTelegramTurn() ||
1338
+ deps.hasDispatchPending() ||
1339
+ deps.hasQueuedTelegramItems() ||
1340
+ deps.isCompactionInProgress()
1341
+ ) {
1342
+ await deps.sendTextReply(
1343
+ "Cannot compact while Pi or the Telegram queue is busy. Wait for queued turns to finish or send /abort first.",
1344
+ );
1345
+ return;
1346
+ }
1347
+ deps.setCompactionInProgress(true);
1348
+ deps.updateStatus();
1349
+ deps.startTypingLoop?.();
1350
+ try {
1351
+ deps.compact({
1352
+ onComplete: () => {
1353
+ deps.stopTypingLoop?.();
1354
+ deps.setCompactionInProgress(false);
1355
+ deps.updateStatus();
1356
+ dispatchNextQueuedTelegramTurnAfterCompact(deps);
1357
+ void deps.sendTextReply(TELEGRAM_COMPACTION_COMPLETED_TEXT);
1358
+ },
1359
+ onError: (error) => {
1360
+ deps.stopTypingLoop?.();
1361
+ deps.setCompactionInProgress(false);
1362
+ deps.updateStatus();
1363
+ dispatchNextQueuedTelegramTurnAfterCompact(deps);
1364
+ deps.recordRuntimeEvent?.("compact", error);
1365
+ const errorMessage = getTelegramCommandErrorMessage(error);
1366
+ void deps.sendTextReply(`Compaction failed: ${errorMessage}`);
1367
+ },
1368
+ });
1369
+ } catch (error) {
1370
+ deps.stopTypingLoop?.();
1371
+ deps.setCompactionInProgress(false);
1372
+ deps.updateStatus();
1373
+ deps.recordRuntimeEvent?.("compact", error);
1374
+ const errorMessage = getTelegramCommandErrorMessage(error);
1375
+ await deps.sendTextReply(`Compaction failed: ${errorMessage}`);
1376
+ return;
1377
+ }
1378
+ if (!deps.suppressStartNotice) {
1379
+ await deps.sendTextReply(
1380
+ TELEGRAM_COMPACTION_STARTED_TEXT,
1381
+ );
1382
+ }
1383
+ }
1384
+
1385
+ function isTelegramStaleContextError(error: unknown): boolean {
1386
+ return (
1387
+ error instanceof Error &&
1388
+ (error.message.includes("stale after session") ||
1389
+ error.message.includes("stale ctx"))
1390
+ );
1391
+ }
1392
+
1393
+ export async function handleTelegramStatusCommand<TContext>(deps: {
1394
+ ctx: TContext;
1395
+ showStatus: (ctx: TContext) => Promise<void>;
1396
+ }): Promise<void> {
1397
+ try {
1398
+ await deps.showStatus(deps.ctx);
1399
+ } catch (error) {
1400
+ if (!isTelegramStaleContextError(error)) throw error;
1401
+ }
1402
+ }
1403
+
1404
+ export async function handleTelegramModelCommand<TContext>(deps: {
1405
+ ctx: TContext;
1406
+ openModelMenu: (ctx: TContext) => Promise<void>;
1407
+ }): Promise<void> {
1408
+ try {
1409
+ await deps.openModelMenu(deps.ctx);
1410
+ } catch (error) {
1411
+ if (!isTelegramStaleContextError(error)) throw error;
1412
+ }
1413
+ }
1414
+
1415
+ export async function executeTelegramCommandAction<TMessage, TContext>(
1416
+ action: TelegramCommandAction,
1417
+ message: TMessage,
1418
+ ctx: TContext,
1419
+ deps: TelegramCommandActionDeps<TMessage, TContext>,
1420
+ ): Promise<boolean> {
1421
+ switch (action.kind) {
1422
+ case "ignore":
1423
+ return false;
1424
+ case "stop":
1425
+ await deps.handleStop(message, ctx);
1426
+ return true;
1427
+ case "abort":
1428
+ await deps.handleAbort(message, ctx);
1429
+ return true;
1430
+ case "next":
1431
+ await deps.handleNext(message, ctx);
1432
+ return true;
1433
+ case "continue":
1434
+ await deps.handleContinue(message, ctx);
1435
+ return true;
1436
+ case "queue":
1437
+ await deps.handleQueue(message, ctx);
1438
+ return true;
1439
+ case "compact":
1440
+ await deps.handleCompact(message, ctx);
1441
+ return true;
1442
+ case "status":
1443
+ await deps.handleStatus(message, ctx);
1444
+ return true;
1445
+ case "model":
1446
+ await deps.handleModel(message, ctx);
1447
+ return true;
1448
+ case "thinking":
1449
+ await deps.handleThinking(message, ctx);
1450
+ return true;
1451
+ case "settings":
1452
+ if (!deps.handleSettings) return false;
1453
+ await deps.handleSettings(message, ctx);
1454
+ return true;
1455
+ case "help":
1456
+ await deps.handleHelp(message, action.commandName, ctx);
1457
+ return true;
1458
+ }
1459
+ }
1460
+
1461
+ export interface TelegramCommandHandlerTargetRuntimeDeps<
1462
+ TMessage extends TelegramCommandRuntimeMessage,
1463
+ TContext,
1464
+ >
1465
+ extends
1466
+ Omit<
1467
+ TelegramCommandRuntimeDeps<TMessage, TContext>,
1468
+ | "enqueueControlItem"
1469
+ | "showStatus"
1470
+ | "openModelMenu"
1471
+ | "openSettingsMenu"
1472
+ | "sendTextReply"
1473
+ | "registerBotCommands"
1474
+ >,
1475
+ Omit<TelegramCommandTargetQueueRuntimeDeps<TContext>, "createControlItem">,
1476
+ TelegramBotCommandRegistrationDeps {
1477
+ allocateItemOrder: () => number;
1478
+ allocateControlOrder: () => number;
1479
+ }
1480
+
1481
+ export function createTelegramCommandHandlerTargetRuntime<
1482
+ TMessage extends TelegramCommandRuntimeMessage,
1483
+ TContext,
1484
+ >(
1485
+ deps: TelegramCommandHandlerTargetRuntimeDeps<TMessage, TContext>,
1486
+ ): (
1487
+ commandName: string | undefined,
1488
+ message: TMessage,
1489
+ ctx: TContext,
1490
+ ) => Promise<boolean> {
1491
+ const commandTargetRuntime = createTelegramCommandTargetQueueRuntime<
1492
+ TMessage,
1493
+ TContext
1494
+ >({
1495
+ createControlItem: createTelegramControlItemBuilder<TContext>({
1496
+ allocateItemOrder: deps.allocateItemOrder,
1497
+ allocateControlOrder: deps.allocateControlOrder,
1498
+ }),
1499
+ appendControlItem: deps.appendControlItem,
1500
+ dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
1501
+ getAdmissionScope: deps.getAdmissionScope,
1502
+ getAdmissionJournalBinding: deps.getAdmissionJournalBinding,
1503
+ onControlQueued: deps.onControlQueued,
1504
+ showStatus: deps.showStatus,
1505
+ openModelMenu: deps.openModelMenu,
1506
+ openSettingsMenu: deps.openSettingsMenu,
1507
+ sendTextReply: deps.sendTextReply,
1508
+ });
1509
+ return createTelegramCommandHandler({
1510
+ hasAbortHandler: deps.hasAbortHandler,
1511
+ clearPendingModelSwitch: deps.clearPendingModelSwitch,
1512
+ hasQueuedTelegramItems: deps.hasQueuedTelegramItems,
1513
+ clearQueuedTelegramItems: deps.clearQueuedTelegramItems,
1514
+ setFoldQueuedPromptsIntoHistory: deps.setFoldQueuedPromptsIntoHistory,
1515
+ abortCurrentTurn: deps.abortCurrentTurn,
1516
+ isIdle: deps.isIdle,
1517
+ hasPendingMessages: deps.hasPendingMessages,
1518
+ hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
1519
+ hasDispatchPending: deps.hasDispatchPending,
1520
+ isCompactionInProgress: deps.isCompactionInProgress,
1521
+ setCompactionInProgress: deps.setCompactionInProgress,
1522
+ updateStatus: deps.updateStatus,
1523
+ isContextActive: deps.isContextActive,
1524
+ dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
1525
+ startTypingLoop: deps.startTypingLoop,
1526
+ stopTypingLoop: deps.stopTypingLoop,
1527
+ enqueueContinueTurn: deps.enqueueContinueTurn,
1528
+ compact: deps.compact,
1529
+ sendInteractiveMessage: deps.sendInteractiveMessage,
1530
+ enqueueControlItem: commandTargetRuntime.enqueueControlItem,
1531
+ showStatus: commandTargetRuntime.showStatus,
1532
+ openModelMenu: commandTargetRuntime.openModelMenu,
1533
+ openThinkingMenu: deps.openThinkingMenu,
1534
+ openQueueMenu: deps.openQueueMenu,
1535
+ openSettingsMenu: commandTargetRuntime.openSettingsMenu,
1536
+ handleForumBootstrap: deps.handleForumBootstrap,
1537
+ getAllowedUserId: deps.getAllowedUserId,
1538
+ setAllowedUserId: deps.setAllowedUserId,
1539
+ registerBotCommands: createTelegramBotCommandRegistrar({
1540
+ setMyCommands: deps.setMyCommands,
1541
+ }),
1542
+ persistConfig: deps.persistConfig,
1543
+ sendTextReply: commandTargetRuntime.sendTextReply,
1544
+ recordRuntimeEvent: deps.recordRuntimeEvent,
1545
+ });
1546
+ }
1547
+
1548
+ export function createTelegramCommandHandler<
1549
+ TMessage extends TelegramCommandRuntimeMessage,
1550
+ TContext,
1551
+ >(deps: TelegramCommandRuntimeDeps<TMessage, TContext>) {
1552
+ return async (
1553
+ commandName: string | undefined,
1554
+ message: TMessage,
1555
+ ctx: TContext,
1556
+ ): Promise<boolean> => {
1557
+ return handleTelegramCommandRuntime(commandName, message, ctx, deps);
1558
+ };
1559
+ }
1560
+
1561
+ export function createTelegramCommandOrPromptRuntime<TMessage, TContext>(
1562
+ deps: TelegramCommandOrPromptRuntimeDeps<TMessage, TContext>,
1563
+ ) {
1564
+ return {
1565
+ dispatchMessages: async (
1566
+ messages: TMessage[],
1567
+ ctx: TContext,
1568
+ ): Promise<void> => {
1569
+ const firstMessage = messages[0];
1570
+ if (!firstMessage) return;
1571
+ if (deps.shouldIgnoreMessages?.(messages)) return;
1572
+ deps.assertExecutionCurrent?.(firstMessage);
1573
+ const command = parseTelegramCommand(deps.extractRawText(messages));
1574
+ const handled = await deps.handleCommand(
1575
+ command?.name,
1576
+ firstMessage,
1577
+ ctx,
1578
+ );
1579
+ deps.assertExecutionCurrent?.(firstMessage);
1580
+ if (handled) return;
1581
+ if (command && deps.executeExtensionCommand) {
1582
+ const handledByExtension = await deps.executeExtensionCommand(
1583
+ command,
1584
+ messages[0]!,
1585
+ ctx,
1586
+ );
1587
+ deps.assertExecutionCurrent?.(firstMessage);
1588
+ if (handledByExtension) return;
1589
+ }
1590
+ if (command?.name && deps.expandPromptTemplateCommand) {
1591
+ const expanded = deps.expandPromptTemplateCommand(
1592
+ command.name,
1593
+ command.args,
1594
+ );
1595
+ if (expanded !== undefined) {
1596
+ deps.assertExecutionCurrent?.(firstMessage);
1597
+ await deps.enqueueTurn(
1598
+ [
1599
+ deps.replaceMessageText(firstMessage, expanded),
1600
+ ...messages.slice(1),
1601
+ ],
1602
+ ctx,
1603
+ );
1604
+ return;
1605
+ }
1606
+ }
1607
+ deps.assertExecutionCurrent?.(firstMessage);
1608
+ await deps.enqueueTurn(messages, ctx);
1609
+ },
1610
+ };
1611
+ }
1612
+
1613
+ function scheduleTelegramCommandEffect<TContext>(
1614
+ ctx: TContext,
1615
+ command: string,
1616
+ phase: string,
1617
+ deps: TelegramRuntimeEventRecorderPort & {
1618
+ isContextActive?: (ctx: TContext) => boolean;
1619
+ },
1620
+ effect: () => Promise<void>,
1621
+ assertExecutionCurrent?: () => void,
1622
+ ): void {
1623
+ void Promise.resolve()
1624
+ .then(async () => {
1625
+ if (deps.isContextActive?.(ctx) === false) return;
1626
+ assertExecutionCurrent?.();
1627
+ await effect();
1628
+ assertExecutionCurrent?.();
1629
+ })
1630
+ .catch((error) => {
1631
+ try {
1632
+ deps.recordRuntimeEvent?.("telegram-command", error, {
1633
+ command,
1634
+ phase,
1635
+ });
1636
+ } catch {
1637
+ // Effect diagnostics cannot create an unhandled detached Promise.
1638
+ }
1639
+ });
1640
+ }
1641
+
1642
+ async function handleTelegramCommandRuntime<
1643
+ TMessage extends TelegramCommandRuntimeMessage,
1644
+ TContext,
1645
+ >(
1646
+ commandName: string | undefined,
1647
+ message: TMessage,
1648
+ ctx: TContext,
1649
+ deps: TelegramCommandRuntimeDeps<TMessage, TContext>,
1650
+ ): Promise<boolean> {
1651
+ const assertExecutionCurrentFor = (nextMessage: TMessage) => (): void =>
1652
+ deps.assertExecutionCurrent?.(nextMessage);
1653
+ const sendReplyFor =
1654
+ (nextMessage: TMessage) =>
1655
+ async (text: string, options?: { parseMode?: "HTML" }) => {
1656
+ deps.assertExecutionCurrent?.(nextMessage);
1657
+ await deps.sendTextReply(nextMessage, text, options);
1658
+ deps.assertExecutionCurrent?.(nextMessage);
1659
+ };
1660
+ const updateStatusFor = (commandCtx: TContext) => () =>
1661
+ deps.updateStatus(commandCtx);
1662
+ return executeTelegramCommandAction(
1663
+ buildTelegramCommandAction(commandName),
1664
+ message,
1665
+ ctx,
1666
+ {
1667
+ handleStop: async (nextMessage, commandCtx) => {
1668
+ await handleTelegramStopCommand({
1669
+ hasAbortHandler: deps.hasAbortHandler,
1670
+ clearPendingModelSwitch: deps.clearPendingModelSwitch,
1671
+ clearQueuedTelegramItems: () =>
1672
+ deps.clearQueuedTelegramItems(commandCtx),
1673
+ setFoldQueuedPromptsIntoHistory: deps.setFoldQueuedPromptsIntoHistory,
1674
+ abortCurrentTurn: deps.abortCurrentTurn,
1675
+ updateStatus: updateStatusFor(commandCtx),
1676
+ sendTextReply: sendReplyFor(nextMessage),
1677
+ });
1678
+ },
1679
+ handleAbort: async (nextMessage, commandCtx) => {
1680
+ await handleTelegramAbortCommand({
1681
+ hasAbortHandler: deps.hasAbortHandler,
1682
+ hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
1683
+ clearPendingModelSwitch: deps.clearPendingModelSwitch,
1684
+ abortCurrentTurn: deps.abortCurrentTurn,
1685
+ setFoldQueuedPromptsIntoHistory: deps.setFoldQueuedPromptsIntoHistory,
1686
+ updateStatus: updateStatusFor(commandCtx),
1687
+ sendTextReply: sendReplyFor(nextMessage),
1688
+ });
1689
+ },
1690
+ handleNext: async (nextMessage, commandCtx) => {
1691
+ await handleTelegramNextCommand({
1692
+ hasAbortHandler: deps.hasAbortHandler,
1693
+ isIdle: () => deps.isIdle(commandCtx),
1694
+ hasQueuedItems: deps.hasQueuedTelegramItems,
1695
+ clearPendingModelSwitch: deps.clearPendingModelSwitch,
1696
+ abortCurrentTurn: deps.abortCurrentTurn,
1697
+ dispatchNextQueuedTurn: () =>
1698
+ deps.dispatchNextQueuedTelegramTurn(commandCtx),
1699
+ clearFoldForDispatch: () =>
1700
+ deps.setFoldQueuedPromptsIntoHistory(false),
1701
+ updateStatus: updateStatusFor(commandCtx),
1702
+ sendTextReply: sendReplyFor(nextMessage),
1703
+ });
1704
+ },
1705
+ handleContinue: async (nextMessage, commandCtx) => {
1706
+ await handleTelegramContinueCommand(nextMessage, commandCtx, {
1707
+ enqueueContinueTurn: deps.enqueueContinueTurn,
1708
+ });
1709
+ },
1710
+ handleQueue: async (nextMessage, commandCtx) => {
1711
+ scheduleTelegramCommandEffect(
1712
+ commandCtx,
1713
+ "queue",
1714
+ "menu-render",
1715
+ deps,
1716
+ () => deps.openQueueMenu(nextMessage, commandCtx),
1717
+ assertExecutionCurrentFor(nextMessage),
1718
+ );
1719
+ },
1720
+ handleCompact: async (nextMessage, commandCtx) => {
1721
+ if (deps.sendInteractiveMessage) {
1722
+ await openTelegramCompactConfirmation(
1723
+ getTelegramCommandMessageTarget(nextMessage),
1724
+ { sendInteractiveMessage: deps.sendInteractiveMessage },
1725
+ );
1726
+ return;
1727
+ }
1728
+ await handleTelegramCompactCommand({
1729
+ isIdle: () => deps.isIdle(commandCtx),
1730
+ hasPendingMessages: () => deps.hasPendingMessages(commandCtx),
1731
+ hasActiveTelegramTurn: deps.hasActiveTelegramTurn,
1732
+ hasDispatchPending: deps.hasDispatchPending,
1733
+ hasQueuedTelegramItems: deps.hasQueuedTelegramItems,
1734
+ isCompactionInProgress: deps.isCompactionInProgress,
1735
+ setCompactionInProgress: deps.setCompactionInProgress,
1736
+ updateStatus: updateStatusFor(commandCtx),
1737
+ dispatchNextQueuedTelegramTurn: () =>
1738
+ deps.dispatchNextQueuedTelegramTurn(commandCtx),
1739
+ requestDeferredDispatchNextQueuedTelegramTurn:
1740
+ deps.requestDeferredDispatchNextQueuedTelegramTurn
1741
+ ? (dispatch) =>
1742
+ deps.requestDeferredDispatchNextQueuedTelegramTurn?.(() =>
1743
+ dispatch(),
1744
+ )
1745
+ : undefined,
1746
+ compact: (callbacks) => deps.compact(commandCtx, callbacks),
1747
+ startTypingLoop: deps.startTypingLoop
1748
+ ? () =>
1749
+ deps.startTypingLoop?.(commandCtx, nextMessage.chat.id, {
1750
+ target: getTelegramCommandMessageTarget(nextMessage),
1751
+ })
1752
+ : undefined,
1753
+ stopTypingLoop: deps.stopTypingLoop,
1754
+ sendTextReply: sendReplyFor(nextMessage),
1755
+ recordRuntimeEvent: deps.recordRuntimeEvent,
1756
+ });
1757
+ },
1758
+ handleStatus: async (nextMessage, commandCtx) => {
1759
+ scheduleTelegramCommandEffect(
1760
+ commandCtx,
1761
+ "status",
1762
+ "menu-render",
1763
+ deps,
1764
+ () => deps.showStatus(nextMessage, commandCtx),
1765
+ assertExecutionCurrentFor(nextMessage),
1766
+ );
1767
+ },
1768
+ handleModel: async (nextMessage, commandCtx) => {
1769
+ scheduleTelegramCommandEffect(
1770
+ commandCtx,
1771
+ "model",
1772
+ "menu-render",
1773
+ deps,
1774
+ () =>
1775
+ handleTelegramModelCommand<TContext>({
1776
+ ctx: commandCtx,
1777
+ openModelMenu: (controlCtx) =>
1778
+ deps.openModelMenu(nextMessage, controlCtx),
1779
+ }),
1780
+ assertExecutionCurrentFor(nextMessage),
1781
+ );
1782
+ },
1783
+ handleThinking: async (nextMessage, commandCtx) => {
1784
+ scheduleTelegramCommandEffect(
1785
+ commandCtx,
1786
+ "thinking",
1787
+ "menu-render",
1788
+ deps,
1789
+ () => deps.openThinkingMenu(nextMessage, commandCtx),
1790
+ assertExecutionCurrentFor(nextMessage),
1791
+ );
1792
+ },
1793
+ handleSettings: deps.openSettingsMenu
1794
+ ? async (nextMessage, commandCtx) => {
1795
+ scheduleTelegramCommandEffect(
1796
+ commandCtx,
1797
+ "settings",
1798
+ "menu-render",
1799
+ deps,
1800
+ () => deps.openSettingsMenu!(nextMessage, commandCtx),
1801
+ assertExecutionCurrentFor(nextMessage),
1802
+ );
1803
+ }
1804
+ : undefined,
1805
+ handleHelp: async (nextMessage, nextCommandName, commandCtx) => {
1806
+ if (
1807
+ nextMessage.from?.id !== undefined &&
1808
+ canPairTelegramUserFromCommandMessage(nextMessage)
1809
+ ) {
1810
+ await pairTelegramUserIfNeeded(nextMessage.from.id, {
1811
+ allowedUserId: deps.getAllowedUserId(),
1812
+ ctx: undefined,
1813
+ setAllowedUserId: deps.setAllowedUserId,
1814
+ persistConfig: deps.persistConfig,
1815
+ updateStatus: updateStatusFor(commandCtx),
1816
+ assertExecutionCurrent: assertExecutionCurrentFor(nextMessage),
1817
+ });
1818
+ }
1819
+ const isContextActive = () =>
1820
+ deps.isContextActive?.(commandCtx) !== false;
1821
+ scheduleTelegramCommandEffect(
1822
+ commandCtx,
1823
+ nextCommandName,
1824
+ "menu-render",
1825
+ deps,
1826
+ async () => {
1827
+ let forumBootstrapMessage: string | undefined;
1828
+ if (nextCommandName === "start" && deps.handleForumBootstrap) {
1829
+ forumBootstrapMessage = await deps.handleForumBootstrap(
1830
+ nextMessage,
1831
+ commandCtx,
1832
+ );
1833
+ }
1834
+ if (!isContextActive()) return;
1835
+ if (forumBootstrapMessage) {
1836
+ await deps.sendTextReply(nextMessage, forumBootstrapMessage);
1837
+ }
1838
+ if (!isContextActive()) return;
1839
+ await deps.showStatus(nextMessage, commandCtx);
1840
+ },
1841
+ assertExecutionCurrentFor(nextMessage),
1842
+ );
1843
+ scheduleTelegramCommandEffect(
1844
+ commandCtx,
1845
+ nextCommandName,
1846
+ "bot-command-sync",
1847
+ deps,
1848
+ deps.registerBotCommands,
1849
+ assertExecutionCurrentFor(nextMessage),
1850
+ );
1851
+ },
1852
+ },
1853
+ );
1854
+ }