charness 0.0.0-stage → 3.0.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 (988) hide show
  1. package/.env.example +88 -0
  2. package/DEVELOPMENT.md +110 -0
  3. package/LICENSE +21 -0
  4. package/MIGRATION_1_TO_2.md +24 -0
  5. package/README.md +1024 -2
  6. package/dist/accessControl.d.ts +71 -0
  7. package/dist/accessControl.d.ts.map +1 -0
  8. package/dist/accessControl.js +108 -0
  9. package/dist/accessControl.js.map +1 -0
  10. package/dist/adapters/adapterEventRouting.d.ts +51 -0
  11. package/dist/adapters/adapterEventRouting.d.ts.map +1 -0
  12. package/dist/adapters/adapterEventRouting.js +74 -0
  13. package/dist/adapters/adapterEventRouting.js.map +1 -0
  14. package/dist/adapters/adapterNames.d.ts +19 -0
  15. package/dist/adapters/adapterNames.d.ts.map +1 -0
  16. package/dist/adapters/adapterNames.js +25 -0
  17. package/dist/adapters/adapterNames.js.map +1 -0
  18. package/dist/adapters/claudeCliAdapter.d.ts +1336 -0
  19. package/dist/adapters/claudeCliAdapter.d.ts.map +1 -0
  20. package/dist/adapters/claudeCliAdapter.js +4204 -0
  21. package/dist/adapters/claudeCliAdapter.js.map +1 -0
  22. package/dist/adapters/claudeJsonStreamAdapter.d.ts +548 -0
  23. package/dist/adapters/claudeJsonStreamAdapter.d.ts.map +1 -0
  24. package/dist/adapters/claudeJsonStreamAdapter.js +1810 -0
  25. package/dist/adapters/claudeJsonStreamAdapter.js.map +1 -0
  26. package/dist/adapters/claudePlatformFlags.d.ts +35 -0
  27. package/dist/adapters/claudePlatformFlags.d.ts.map +1 -0
  28. package/dist/adapters/claudePlatformFlags.js +46 -0
  29. package/dist/adapters/claudePlatformFlags.js.map +1 -0
  30. package/dist/adapters/createAdapter.d.ts +252 -0
  31. package/dist/adapters/createAdapter.d.ts.map +1 -0
  32. package/dist/adapters/createAdapter.js +475 -0
  33. package/dist/adapters/createAdapter.js.map +1 -0
  34. package/dist/adapters/openCodeAdapter.d.ts +1446 -0
  35. package/dist/adapters/openCodeAdapter.d.ts.map +1 -0
  36. package/dist/adapters/openCodeAdapter.js +4540 -0
  37. package/dist/adapters/openCodeAdapter.js.map +1 -0
  38. package/dist/adapters/sessionLaunchDefaults.d.ts +28 -0
  39. package/dist/adapters/sessionLaunchDefaults.d.ts.map +1 -0
  40. package/dist/adapters/sessionLaunchDefaults.js +15 -0
  41. package/dist/adapters/sessionLaunchDefaults.js.map +1 -0
  42. package/dist/adapters/terminalAdapter.d.ts +68 -0
  43. package/dist/adapters/terminalAdapter.d.ts.map +1 -0
  44. package/dist/adapters/terminalAdapter.js +371 -0
  45. package/dist/adapters/terminalAdapter.js.map +1 -0
  46. package/dist/agentLogin/agentLogin.d.ts +31 -0
  47. package/dist/agentLogin/agentLogin.d.ts.map +1 -0
  48. package/dist/agentLogin/agentLogin.js +493 -0
  49. package/dist/agentLogin/agentLogin.js.map +1 -0
  50. package/dist/agentTrigger.d.ts +28 -0
  51. package/dist/agentTrigger.d.ts.map +1 -0
  52. package/dist/agentTrigger.js +53 -0
  53. package/dist/agentTrigger.js.map +1 -0
  54. package/dist/apiErrorRetry.d.ts +209 -0
  55. package/dist/apiErrorRetry.d.ts.map +1 -0
  56. package/dist/apiErrorRetry.js +351 -0
  57. package/dist/apiErrorRetry.js.map +1 -0
  58. package/dist/apiRetryKick.d.ts +148 -0
  59. package/dist/apiRetryKick.d.ts.map +1 -0
  60. package/dist/apiRetryKick.js +158 -0
  61. package/dist/apiRetryKick.js.map +1 -0
  62. package/dist/bootClassifier.d.ts +46 -0
  63. package/dist/bootClassifier.d.ts.map +1 -0
  64. package/dist/bootClassifier.js +43 -0
  65. package/dist/bootClassifier.js.map +1 -0
  66. package/dist/bot.d.ts +207 -0
  67. package/dist/bot.d.ts.map +1 -0
  68. package/dist/bot.js +10611 -0
  69. package/dist/bot.js.map +1 -0
  70. package/dist/botFileStorage.d.ts +60 -0
  71. package/dist/botFileStorage.d.ts.map +1 -0
  72. package/dist/botFileStorage.js +205 -0
  73. package/dist/botFileStorage.js.map +1 -0
  74. package/dist/cli/applyDnsFix.d.ts +7 -0
  75. package/dist/cli/applyDnsFix.d.ts.map +1 -0
  76. package/dist/cli/applyDnsFix.js +46 -0
  77. package/dist/cli/applyDnsFix.js.map +1 -0
  78. package/dist/cli/bot.d.ts +27 -0
  79. package/dist/cli/bot.d.ts.map +1 -0
  80. package/dist/cli/bot.js +98 -0
  81. package/dist/cli/bot.js.map +1 -0
  82. package/dist/cli/botEntry.d.ts +3 -0
  83. package/dist/cli/botEntry.d.ts.map +1 -0
  84. package/dist/cli/botEntry.js +11 -0
  85. package/dist/cli/botEntry.js.map +1 -0
  86. package/dist/cli/connectorGuards.d.ts +31 -0
  87. package/dist/cli/connectorGuards.d.ts.map +1 -0
  88. package/dist/cli/connectorGuards.js +159 -0
  89. package/dist/cli/connectorGuards.js.map +1 -0
  90. package/dist/cli/envLoader.d.ts +34 -0
  91. package/dist/cli/envLoader.d.ts.map +1 -0
  92. package/dist/cli/envLoader.js +132 -0
  93. package/dist/cli/envLoader.js.map +1 -0
  94. package/dist/cli/hot.d.ts +82 -0
  95. package/dist/cli/hot.d.ts.map +1 -0
  96. package/dist/cli/hot.js +323 -0
  97. package/dist/cli/hot.js.map +1 -0
  98. package/dist/cli.d.ts +3 -0
  99. package/dist/cli.d.ts.map +1 -0
  100. package/dist/cli.js +130 -0
  101. package/dist/cli.js.map +1 -0
  102. package/dist/connectors/jira/adf.d.ts +72 -0
  103. package/dist/connectors/jira/adf.d.ts.map +1 -0
  104. package/dist/connectors/jira/adf.js +407 -0
  105. package/dist/connectors/jira/adf.js.map +1 -0
  106. package/dist/connectors/jira/agentBinaries.d.ts +14 -0
  107. package/dist/connectors/jira/agentBinaries.d.ts.map +1 -0
  108. package/dist/connectors/jira/agentBinaries.js +75 -0
  109. package/dist/connectors/jira/agentBinaries.js.map +1 -0
  110. package/dist/connectors/jira/answerSink.d.ts +52 -0
  111. package/dist/connectors/jira/answerSink.d.ts.map +1 -0
  112. package/dist/connectors/jira/answerSink.js +220 -0
  113. package/dist/connectors/jira/answerSink.js.map +1 -0
  114. package/dist/connectors/jira/attachmentDownload.d.ts +41 -0
  115. package/dist/connectors/jira/attachmentDownload.d.ts.map +1 -0
  116. package/dist/connectors/jira/attachmentDownload.js +92 -0
  117. package/dist/connectors/jira/attachmentDownload.js.map +1 -0
  118. package/dist/connectors/jira/attachmentTool.d.ts +43 -0
  119. package/dist/connectors/jira/attachmentTool.d.ts.map +1 -0
  120. package/dist/connectors/jira/attachmentTool.js +150 -0
  121. package/dist/connectors/jira/attachmentTool.js.map +1 -0
  122. package/dist/connectors/jira/client.d.ts +490 -0
  123. package/dist/connectors/jira/client.d.ts.map +1 -0
  124. package/dist/connectors/jira/client.js +453 -0
  125. package/dist/connectors/jira/client.js.map +1 -0
  126. package/dist/connectors/jira/config.d.ts +117 -0
  127. package/dist/connectors/jira/config.d.ts.map +1 -0
  128. package/dist/connectors/jira/config.js +350 -0
  129. package/dist/connectors/jira/config.js.map +1 -0
  130. package/dist/connectors/jira/configFile.d.ts +10 -0
  131. package/dist/connectors/jira/configFile.d.ts.map +1 -0
  132. package/dist/connectors/jira/configFile.js +50 -0
  133. package/dist/connectors/jira/configFile.js.map +1 -0
  134. package/dist/connectors/jira/connector.d.ts +68 -0
  135. package/dist/connectors/jira/connector.d.ts.map +1 -0
  136. package/dist/connectors/jira/connector.js +257 -0
  137. package/dist/connectors/jira/connector.js.map +1 -0
  138. package/dist/connectors/jira/contextLedger.d.ts +59 -0
  139. package/dist/connectors/jira/contextLedger.d.ts.map +1 -0
  140. package/dist/connectors/jira/contextLedger.js +194 -0
  141. package/dist/connectors/jira/contextLedger.js.map +1 -0
  142. package/dist/connectors/jira/contextPreamble.d.ts +18 -0
  143. package/dist/connectors/jira/contextPreamble.d.ts.map +1 -0
  144. package/dist/connectors/jira/contextPreamble.js +31 -0
  145. package/dist/connectors/jira/contextPreamble.js.map +1 -0
  146. package/dist/connectors/jira/inbound.d.ts +132 -0
  147. package/dist/connectors/jira/inbound.d.ts.map +1 -0
  148. package/dist/connectors/jira/inbound.js +316 -0
  149. package/dist/connectors/jira/inbound.js.map +1 -0
  150. package/dist/connectors/jira/issueBlocks.d.ts +51 -0
  151. package/dist/connectors/jira/issueBlocks.d.ts.map +1 -0
  152. package/dist/connectors/jira/issueBlocks.js +182 -0
  153. package/dist/connectors/jira/issueBlocks.js.map +1 -0
  154. package/dist/connectors/jira/issueContext.d.ts +25 -0
  155. package/dist/connectors/jira/issueContext.d.ts.map +1 -0
  156. package/dist/connectors/jira/issueContext.js +53 -0
  157. package/dist/connectors/jira/issueContext.js.map +1 -0
  158. package/dist/connectors/jira/issueDelta.d.ts +35 -0
  159. package/dist/connectors/jira/issueDelta.d.ts.map +1 -0
  160. package/dist/connectors/jira/issueDelta.js +50 -0
  161. package/dist/connectors/jira/issueDelta.js.map +1 -0
  162. package/dist/connectors/jira/mediaPlaceholders.d.ts +20 -0
  163. package/dist/connectors/jira/mediaPlaceholders.d.ts.map +1 -0
  164. package/dist/connectors/jira/mediaPlaceholders.js +92 -0
  165. package/dist/connectors/jira/mediaPlaceholders.js.map +1 -0
  166. package/dist/connectors/jira/outbound.d.ts +6 -0
  167. package/dist/connectors/jira/outbound.d.ts.map +1 -0
  168. package/dist/connectors/jira/outbound.js +38 -0
  169. package/dist/connectors/jira/outbound.js.map +1 -0
  170. package/dist/connectors/jira/prompt.d.ts +37 -0
  171. package/dist/connectors/jira/prompt.d.ts.map +1 -0
  172. package/dist/connectors/jira/prompt.js +99 -0
  173. package/dist/connectors/jira/prompt.js.map +1 -0
  174. package/dist/connectors/jira/promptSpill.d.ts +50 -0
  175. package/dist/connectors/jira/promptSpill.d.ts.map +1 -0
  176. package/dist/connectors/jira/promptSpill.js +157 -0
  177. package/dist/connectors/jira/promptSpill.js.map +1 -0
  178. package/dist/connectors/jira/promptText.d.ts +7 -0
  179. package/dist/connectors/jira/promptText.d.ts.map +1 -0
  180. package/dist/connectors/jira/promptText.js +26 -0
  181. package/dist/connectors/jira/promptText.js.map +1 -0
  182. package/dist/connectors/jira/sessionKeyCodec.d.ts +16 -0
  183. package/dist/connectors/jira/sessionKeyCodec.d.ts.map +1 -0
  184. package/dist/connectors/jira/sessionKeyCodec.js +101 -0
  185. package/dist/connectors/jira/sessionKeyCodec.js.map +1 -0
  186. package/dist/connectors/jira/trigger.d.ts +50 -0
  187. package/dist/connectors/jira/trigger.d.ts.map +1 -0
  188. package/dist/connectors/jira/trigger.js +93 -0
  189. package/dist/connectors/jira/trigger.js.map +1 -0
  190. package/dist/connectors/jira/triggerLog.d.ts +51 -0
  191. package/dist/connectors/jira/triggerLog.d.ts.map +1 -0
  192. package/dist/connectors/jira/triggerLog.js +78 -0
  193. package/dist/connectors/jira/triggerLog.js.map +1 -0
  194. package/dist/connectors/jira/unconfirmedPosts.d.ts +58 -0
  195. package/dist/connectors/jira/unconfirmedPosts.d.ts.map +1 -0
  196. package/dist/connectors/jira/unconfirmedPosts.js +105 -0
  197. package/dist/connectors/jira/unconfirmedPosts.js.map +1 -0
  198. package/dist/connectors/telegram/answerSink.d.ts +46 -0
  199. package/dist/connectors/telegram/answerSink.d.ts.map +1 -0
  200. package/dist/connectors/telegram/answerSink.js +71 -0
  201. package/dist/connectors/telegram/answerSink.js.map +1 -0
  202. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts +31 -0
  203. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts.map +1 -0
  204. package/dist/connectors/telegram/commands/autoContinueLimits.js +142 -0
  205. package/dist/connectors/telegram/commands/autoContinueLimits.js.map +1 -0
  206. package/dist/connectors/telegram/commands/botCore.d.ts +46 -0
  207. package/dist/connectors/telegram/commands/botCore.d.ts.map +1 -0
  208. package/dist/connectors/telegram/commands/botCore.js +3 -0
  209. package/dist/connectors/telegram/commands/botCore.js.map +1 -0
  210. package/dist/connectors/telegram/commands/callbackMessage.d.ts +12 -0
  211. package/dist/connectors/telegram/commands/callbackMessage.d.ts.map +1 -0
  212. package/dist/connectors/telegram/commands/callbackMessage.js +13 -0
  213. package/dist/connectors/telegram/commands/callbackMessage.js.map +1 -0
  214. package/dist/connectors/telegram/commands/compaction.d.ts +47 -0
  215. package/dist/connectors/telegram/commands/compaction.d.ts.map +1 -0
  216. package/dist/connectors/telegram/commands/compaction.js +868 -0
  217. package/dist/connectors/telegram/commands/compaction.js.map +1 -0
  218. package/dist/connectors/telegram/commands/displayModes.d.ts +13 -0
  219. package/dist/connectors/telegram/commands/displayModes.d.ts.map +1 -0
  220. package/dist/connectors/telegram/commands/displayModes.js +335 -0
  221. package/dist/connectors/telegram/commands/displayModes.js.map +1 -0
  222. package/dist/connectors/telegram/commands/modelProviders.d.ts +95 -0
  223. package/dist/connectors/telegram/commands/modelProviders.d.ts.map +1 -0
  224. package/dist/connectors/telegram/commands/modelProviders.js +841 -0
  225. package/dist/connectors/telegram/commands/modelProviders.js.map +1 -0
  226. package/dist/connectors/telegram/commands/reminders.d.ts +54 -0
  227. package/dist/connectors/telegram/commands/reminders.d.ts.map +1 -0
  228. package/dist/connectors/telegram/commands/reminders.js +624 -0
  229. package/dist/connectors/telegram/commands/reminders.js.map +1 -0
  230. package/dist/connectors/telegram/fileIntake.d.ts +166 -0
  231. package/dist/connectors/telegram/fileIntake.d.ts.map +1 -0
  232. package/dist/connectors/telegram/fileIntake.js +304 -0
  233. package/dist/connectors/telegram/fileIntake.js.map +1 -0
  234. package/dist/connectors/telegram/fileSendGateway.d.ts +57 -0
  235. package/dist/connectors/telegram/fileSendGateway.d.ts.map +1 -0
  236. package/dist/connectors/telegram/fileSendGateway.js +221 -0
  237. package/dist/connectors/telegram/fileSendGateway.js.map +1 -0
  238. package/dist/connectors/telegram/foreignKeyFallbacks.d.ts +52 -0
  239. package/dist/connectors/telegram/foreignKeyFallbacks.d.ts.map +1 -0
  240. package/dist/connectors/telegram/foreignKeyFallbacks.js +71 -0
  241. package/dist/connectors/telegram/foreignKeyFallbacks.js.map +1 -0
  242. package/dist/connectors/telegram/inbound.d.ts +131 -0
  243. package/dist/connectors/telegram/inbound.d.ts.map +1 -0
  244. package/dist/connectors/telegram/inbound.js +278 -0
  245. package/dist/connectors/telegram/inbound.js.map +1 -0
  246. package/dist/connectors/telegram/languagePicker.d.ts +32 -0
  247. package/dist/connectors/telegram/languagePicker.d.ts.map +1 -0
  248. package/dist/connectors/telegram/languagePicker.js +55 -0
  249. package/dist/connectors/telegram/languagePicker.js.map +1 -0
  250. package/dist/connectors/telegram/messageSplit.d.ts +62 -0
  251. package/dist/connectors/telegram/messageSplit.d.ts.map +1 -0
  252. package/dist/connectors/telegram/messageSplit.js +119 -0
  253. package/dist/connectors/telegram/messageSplit.js.map +1 -0
  254. package/dist/connectors/telegram/outbound.d.ts +62 -0
  255. package/dist/connectors/telegram/outbound.d.ts.map +1 -0
  256. package/dist/connectors/telegram/outbound.js +116 -0
  257. package/dist/connectors/telegram/outbound.js.map +1 -0
  258. package/dist/connectors/telegram/output/createOutputTransport.d.ts +34 -0
  259. package/dist/connectors/telegram/output/createOutputTransport.d.ts.map +1 -0
  260. package/dist/connectors/telegram/output/createOutputTransport.js +77 -0
  261. package/dist/connectors/telegram/output/createOutputTransport.js.map +1 -0
  262. package/dist/connectors/telegram/output/dmOutputTransport.d.ts +47 -0
  263. package/dist/connectors/telegram/output/dmOutputTransport.d.ts.map +1 -0
  264. package/dist/connectors/telegram/output/dmOutputTransport.js +412 -0
  265. package/dist/connectors/telegram/output/dmOutputTransport.js.map +1 -0
  266. package/dist/connectors/telegram/outputBacklogGlue.d.ts +38 -0
  267. package/dist/connectors/telegram/outputBacklogGlue.d.ts.map +1 -0
  268. package/dist/connectors/telegram/outputBacklogGlue.js +49 -0
  269. package/dist/connectors/telegram/outputBacklogGlue.js.map +1 -0
  270. package/dist/connectors/telegram/outputFlushPlan.d.ts +93 -0
  271. package/dist/connectors/telegram/outputFlushPlan.d.ts.map +1 -0
  272. package/dist/connectors/telegram/outputFlushPlan.js +120 -0
  273. package/dist/connectors/telegram/outputFlushPlan.js.map +1 -0
  274. package/dist/connectors/telegram/questionKeyboards.d.ts +30 -0
  275. package/dist/connectors/telegram/questionKeyboards.d.ts.map +1 -0
  276. package/dist/connectors/telegram/questionKeyboards.js +37 -0
  277. package/dist/connectors/telegram/questionKeyboards.js.map +1 -0
  278. package/dist/connectors/telegram/renderAgentHtml.d.ts +27 -0
  279. package/dist/connectors/telegram/renderAgentHtml.d.ts.map +1 -0
  280. package/dist/connectors/telegram/renderAgentHtml.js +97 -0
  281. package/dist/connectors/telegram/renderAgentHtml.js.map +1 -0
  282. package/dist/connectors/telegram/sessionKeyCodec.d.ts +28 -0
  283. package/dist/connectors/telegram/sessionKeyCodec.d.ts.map +1 -0
  284. package/dist/connectors/telegram/sessionKeyCodec.js +107 -0
  285. package/dist/connectors/telegram/sessionKeyCodec.js.map +1 -0
  286. package/dist/connectors/telegram/telegramCallGuard.d.ts +19 -0
  287. package/dist/connectors/telegram/telegramCallGuard.d.ts.map +1 -0
  288. package/dist/connectors/telegram/telegramCallGuard.js +35 -0
  289. package/dist/connectors/telegram/telegramCallGuard.js.map +1 -0
  290. package/dist/connectors/telegram/timezonePicker.d.ts +85 -0
  291. package/dist/connectors/telegram/timezonePicker.d.ts.map +1 -0
  292. package/dist/connectors/telegram/timezonePicker.js +142 -0
  293. package/dist/connectors/telegram/timezonePicker.js.map +1 -0
  294. package/dist/connectors/telegram/updateDispatcher.d.ts +85 -0
  295. package/dist/connectors/telegram/updateDispatcher.d.ts.map +1 -0
  296. package/dist/connectors/telegram/updateDispatcher.js +159 -0
  297. package/dist/connectors/telegram/updateDispatcher.js.map +1 -0
  298. package/dist/connectors/test/connector.d.ts +52 -0
  299. package/dist/connectors/test/connector.d.ts.map +1 -0
  300. package/dist/connectors/test/connector.js +143 -0
  301. package/dist/connectors/test/connector.js.map +1 -0
  302. package/dist/connectors/test/sessionKeyCodec.d.ts +25 -0
  303. package/dist/connectors/test/sessionKeyCodec.d.ts.map +1 -0
  304. package/dist/connectors/test/sessionKeyCodec.js +50 -0
  305. package/dist/connectors/test/sessionKeyCodec.js.map +1 -0
  306. package/dist/diagLog.d.ts +6 -0
  307. package/dist/diagLog.d.ts.map +1 -0
  308. package/dist/diagLog.js +58 -0
  309. package/dist/diagLog.js.map +1 -0
  310. package/dist/effortLevels.d.ts +68 -0
  311. package/dist/effortLevels.d.ts.map +1 -0
  312. package/dist/effortLevels.js +114 -0
  313. package/dist/effortLevels.js.map +1 -0
  314. package/dist/folderName.d.ts +42 -0
  315. package/dist/folderName.d.ts.map +1 -0
  316. package/dist/folderName.js +51 -0
  317. package/dist/folderName.js.map +1 -0
  318. package/dist/i18n/de.d.ts +2 -0
  319. package/dist/i18n/de.d.ts.map +1 -0
  320. package/dist/i18n/de.js +537 -0
  321. package/dist/i18n/de.js.map +1 -0
  322. package/dist/i18n/en.d.ts +7 -0
  323. package/dist/i18n/en.d.ts.map +1 -0
  324. package/dist/i18n/en.js +566 -0
  325. package/dist/i18n/en.js.map +1 -0
  326. package/dist/i18n/es.d.ts +2 -0
  327. package/dist/i18n/es.d.ts.map +1 -0
  328. package/dist/i18n/es.js +537 -0
  329. package/dist/i18n/es.js.map +1 -0
  330. package/dist/i18n/fr.d.ts +2 -0
  331. package/dist/i18n/fr.d.ts.map +1 -0
  332. package/dist/i18n/fr.js +537 -0
  333. package/dist/i18n/fr.js.map +1 -0
  334. package/dist/i18n/hi.d.ts +2 -0
  335. package/dist/i18n/hi.d.ts.map +1 -0
  336. package/dist/i18n/hi.js +537 -0
  337. package/dist/i18n/hi.js.map +1 -0
  338. package/dist/i18n/ja.d.ts +2 -0
  339. package/dist/i18n/ja.d.ts.map +1 -0
  340. package/dist/i18n/ja.js +537 -0
  341. package/dist/i18n/ja.js.map +1 -0
  342. package/dist/i18n/ka.d.ts +2 -0
  343. package/dist/i18n/ka.d.ts.map +1 -0
  344. package/dist/i18n/ka.js +537 -0
  345. package/dist/i18n/ka.js.map +1 -0
  346. package/dist/i18n/pt.d.ts +2 -0
  347. package/dist/i18n/pt.d.ts.map +1 -0
  348. package/dist/i18n/pt.js +537 -0
  349. package/dist/i18n/pt.js.map +1 -0
  350. package/dist/i18n/ru.d.ts +6 -0
  351. package/dist/i18n/ru.d.ts.map +1 -0
  352. package/dist/i18n/ru.js +540 -0
  353. package/dist/i18n/ru.js.map +1 -0
  354. package/dist/i18n/uk.d.ts +8 -0
  355. package/dist/i18n/uk.d.ts.map +1 -0
  356. package/dist/i18n/uk.js +542 -0
  357. package/dist/i18n/uk.js.map +1 -0
  358. package/dist/i18n/uz.d.ts +2 -0
  359. package/dist/i18n/uz.d.ts.map +1 -0
  360. package/dist/i18n/uz.js +537 -0
  361. package/dist/i18n/uz.js.map +1 -0
  362. package/dist/i18n/zh.d.ts +2 -0
  363. package/dist/i18n/zh.d.ts.map +1 -0
  364. package/dist/i18n/zh.js +537 -0
  365. package/dist/i18n/zh.js.map +1 -0
  366. package/dist/i18n.d.ts +115 -0
  367. package/dist/i18n.d.ts.map +1 -0
  368. package/dist/i18n.js +233 -0
  369. package/dist/i18n.js.map +1 -0
  370. package/dist/index.d.ts +3 -0
  371. package/dist/index.d.ts.map +1 -0
  372. package/dist/index.js +10 -0
  373. package/dist/index.js.map +1 -0
  374. package/dist/installManager.d.ts +99 -0
  375. package/dist/installManager.d.ts.map +1 -0
  376. package/dist/installManager.js +943 -0
  377. package/dist/installManager.js.map +1 -0
  378. package/dist/instanceLock.d.ts +161 -0
  379. package/dist/instanceLock.d.ts.map +1 -0
  380. package/dist/instanceLock.js +388 -0
  381. package/dist/instanceLock.js.map +1 -0
  382. package/dist/mcpConfig.d.ts +76 -0
  383. package/dist/mcpConfig.d.ts.map +1 -0
  384. package/dist/mcpConfig.js +270 -0
  385. package/dist/mcpConfig.js.map +1 -0
  386. package/dist/openCodeOpenQuestion.d.ts +44 -0
  387. package/dist/openCodeOpenQuestion.d.ts.map +1 -0
  388. package/dist/openCodeOpenQuestion.js +64 -0
  389. package/dist/openCodeOpenQuestion.js.map +1 -0
  390. package/dist/openCodeQuestionFlow.d.ts +105 -0
  391. package/dist/openCodeQuestionFlow.d.ts.map +1 -0
  392. package/dist/openCodeQuestionFlow.js +143 -0
  393. package/dist/openCodeQuestionFlow.js.map +1 -0
  394. package/dist/openCodeSessionRouting.d.ts +133 -0
  395. package/dist/openCodeSessionRouting.d.ts.map +1 -0
  396. package/dist/openCodeSessionRouting.js +253 -0
  397. package/dist/openCodeSessionRouting.js.map +1 -0
  398. package/dist/openCodeSessionTitle.d.ts +50 -0
  399. package/dist/openCodeSessionTitle.d.ts.map +1 -0
  400. package/dist/openCodeSessionTitle.js +84 -0
  401. package/dist/openCodeSessionTitle.js.map +1 -0
  402. package/dist/outputTrace.d.ts +108 -0
  403. package/dist/outputTrace.d.ts.map +1 -0
  404. package/dist/outputTrace.js +430 -0
  405. package/dist/outputTrace.js.map +1 -0
  406. package/dist/pendingQuestionRepost.d.ts +47 -0
  407. package/dist/pendingQuestionRepost.d.ts.map +1 -0
  408. package/dist/pendingQuestionRepost.js +29 -0
  409. package/dist/pendingQuestionRepost.js.map +1 -0
  410. package/dist/pinnedStatus.d.ts +52 -0
  411. package/dist/pinnedStatus.d.ts.map +1 -0
  412. package/dist/pinnedStatus.js +36 -0
  413. package/dist/pinnedStatus.js.map +1 -0
  414. package/dist/platform/answerSink.d.ts +93 -0
  415. package/dist/platform/answerSink.d.ts.map +1 -0
  416. package/dist/platform/answerSink.js +31 -0
  417. package/dist/platform/answerSink.js.map +1 -0
  418. package/dist/platform/capabilityFallback.d.ts +39 -0
  419. package/dist/platform/capabilityFallback.d.ts.map +1 -0
  420. package/dist/platform/capabilityFallback.js +55 -0
  421. package/dist/platform/capabilityFallback.js.map +1 -0
  422. package/dist/platform/commandRouter.d.ts +51 -0
  423. package/dist/platform/commandRouter.d.ts.map +1 -0
  424. package/dist/platform/commandRouter.js +53 -0
  425. package/dist/platform/commandRouter.js.map +1 -0
  426. package/dist/platform/connectorSet.d.ts +39 -0
  427. package/dist/platform/connectorSet.d.ts.map +1 -0
  428. package/dist/platform/connectorSet.js +55 -0
  429. package/dist/platform/connectorSet.js.map +1 -0
  430. package/dist/platform/inbound.d.ts +147 -0
  431. package/dist/platform/inbound.d.ts.map +1 -0
  432. package/dist/platform/inbound.js +18 -0
  433. package/dist/platform/inbound.js.map +1 -0
  434. package/dist/platform/outbound.d.ts +225 -0
  435. package/dist/platform/outbound.d.ts.map +1 -0
  436. package/dist/platform/outbound.js +30 -0
  437. package/dist/platform/outbound.js.map +1 -0
  438. package/dist/platform/unservedStateGuard.d.ts +27 -0
  439. package/dist/platform/unservedStateGuard.d.ts.map +1 -0
  440. package/dist/platform/unservedStateGuard.js +42 -0
  441. package/dist/platform/unservedStateGuard.js.map +1 -0
  442. package/dist/postToSession.d.ts +94 -0
  443. package/dist/postToSession.d.ts.map +1 -0
  444. package/dist/postToSession.js +56 -0
  445. package/dist/postToSession.js.map +1 -0
  446. package/dist/progressLine.d.ts +180 -0
  447. package/dist/progressLine.d.ts.map +1 -0
  448. package/dist/progressLine.js +293 -0
  449. package/dist/progressLine.js.map +1 -0
  450. package/dist/rateLimiter.d.ts +246 -0
  451. package/dist/rateLimiter.d.ts.map +1 -0
  452. package/dist/rateLimiter.js +555 -0
  453. package/dist/rateLimiter.js.map +1 -0
  454. package/dist/requests/answerRequest.d.ts +57 -0
  455. package/dist/requests/answerRequest.d.ts.map +1 -0
  456. package/dist/requests/answerRequest.js +96 -0
  457. package/dist/requests/answerRequest.js.map +1 -0
  458. package/dist/requests/answerTail.d.ts +83 -0
  459. package/dist/requests/answerTail.d.ts.map +1 -0
  460. package/dist/requests/answerTail.js +102 -0
  461. package/dist/requests/answerTail.js.map +1 -0
  462. package/dist/requests/limitWaitAnswer.d.ts +87 -0
  463. package/dist/requests/limitWaitAnswer.d.ts.map +1 -0
  464. package/dist/requests/limitWaitAnswer.js +83 -0
  465. package/dist/requests/limitWaitAnswer.js.map +1 -0
  466. package/dist/requests/requestGroup.d.ts +44 -0
  467. package/dist/requests/requestGroup.d.ts.map +1 -0
  468. package/dist/requests/requestGroup.js +63 -0
  469. package/dist/requests/requestGroup.js.map +1 -0
  470. package/dist/requests/requestHeader.d.ts +70 -0
  471. package/dist/requests/requestHeader.d.ts.map +1 -0
  472. package/dist/requests/requestHeader.js +83 -0
  473. package/dist/requests/requestHeader.js.map +1 -0
  474. package/dist/requests/requestLedger.d.ts +217 -0
  475. package/dist/requests/requestLedger.d.ts.map +1 -0
  476. package/dist/requests/requestLedger.js +468 -0
  477. package/dist/requests/requestLedger.js.map +1 -0
  478. package/dist/requests/sessionTurnProbe.d.ts +52 -0
  479. package/dist/requests/sessionTurnProbe.d.ts.map +1 -0
  480. package/dist/requests/sessionTurnProbe.js +29 -0
  481. package/dist/requests/sessionTurnProbe.js.map +1 -0
  482. package/dist/requests/types.d.ts +181 -0
  483. package/dist/requests/types.d.ts.map +1 -0
  484. package/dist/requests/types.js +9 -0
  485. package/dist/requests/types.js.map +1 -0
  486. package/dist/requests/wakeUpEngine.d.ts +160 -0
  487. package/dist/requests/wakeUpEngine.d.ts.map +1 -0
  488. package/dist/requests/wakeUpEngine.js +399 -0
  489. package/dist/requests/wakeUpEngine.js.map +1 -0
  490. package/dist/requests/wakeUpRules.d.ts +151 -0
  491. package/dist/requests/wakeUpRules.d.ts.map +1 -0
  492. package/dist/requests/wakeUpRules.js +167 -0
  493. package/dist/requests/wakeUpRules.js.map +1 -0
  494. package/dist/resumeContext.d.ts +87 -0
  495. package/dist/resumeContext.d.ts.map +1 -0
  496. package/dist/resumeContext.js +114 -0
  497. package/dist/resumeContext.js.map +1 -0
  498. package/dist/scheduler/checkRun.d.ts +95 -0
  499. package/dist/scheduler/checkRun.d.ts.map +1 -0
  500. package/dist/scheduler/checkRun.js +172 -0
  501. package/dist/scheduler/checkRun.js.map +1 -0
  502. package/dist/scheduler/delivery.d.ts +99 -0
  503. package/dist/scheduler/delivery.d.ts.map +1 -0
  504. package/dist/scheduler/delivery.js +212 -0
  505. package/dist/scheduler/delivery.js.map +1 -0
  506. package/dist/scheduler/deliveryKind.d.ts +29 -0
  507. package/dist/scheduler/deliveryKind.d.ts.map +1 -0
  508. package/dist/scheduler/deliveryKind.js +39 -0
  509. package/dist/scheduler/deliveryKind.js.map +1 -0
  510. package/dist/scheduler/directoryThreads.d.ts +19 -0
  511. package/dist/scheduler/directoryThreads.d.ts.map +1 -0
  512. package/dist/scheduler/directoryThreads.js +28 -0
  513. package/dist/scheduler/directoryThreads.js.map +1 -0
  514. package/dist/scheduler/engine.d.ts +77 -0
  515. package/dist/scheduler/engine.d.ts.map +1 -0
  516. package/dist/scheduler/engine.js +337 -0
  517. package/dist/scheduler/engine.js.map +1 -0
  518. package/dist/scheduler/injection.d.ts +102 -0
  519. package/dist/scheduler/injection.d.ts.map +1 -0
  520. package/dist/scheduler/injection.js +122 -0
  521. package/dist/scheduler/injection.js.map +1 -0
  522. package/dist/scheduler/mcpBoot.d.ts +65 -0
  523. package/dist/scheduler/mcpBoot.d.ts.map +1 -0
  524. package/dist/scheduler/mcpBoot.js +60 -0
  525. package/dist/scheduler/mcpBoot.js.map +1 -0
  526. package/dist/scheduler/mcpSurface.d.ts +355 -0
  527. package/dist/scheduler/mcpSurface.d.ts.map +1 -0
  528. package/dist/scheduler/mcpSurface.js +1257 -0
  529. package/dist/scheduler/mcpSurface.js.map +1 -0
  530. package/dist/scheduler/rebindResume.d.ts +27 -0
  531. package/dist/scheduler/rebindResume.d.ts.map +1 -0
  532. package/dist/scheduler/rebindResume.js +17 -0
  533. package/dist/scheduler/rebindResume.js.map +1 -0
  534. package/dist/scheduler/recurrence.d.ts +58 -0
  535. package/dist/scheduler/recurrence.d.ts.map +1 -0
  536. package/dist/scheduler/recurrence.js +210 -0
  537. package/dist/scheduler/recurrence.js.map +1 -0
  538. package/dist/scheduler/runLedger.d.ts +59 -0
  539. package/dist/scheduler/runLedger.d.ts.map +1 -0
  540. package/dist/scheduler/runLedger.js +54 -0
  541. package/dist/scheduler/runLedger.js.map +1 -0
  542. package/dist/scheduler/store.d.ts +97 -0
  543. package/dist/scheduler/store.d.ts.map +1 -0
  544. package/dist/scheduler/store.js +124 -0
  545. package/dist/scheduler/store.js.map +1 -0
  546. package/dist/scheduler/timezoneRecompute.d.ts +61 -0
  547. package/dist/scheduler/timezoneRecompute.d.ts.map +1 -0
  548. package/dist/scheduler/timezoneRecompute.js +46 -0
  549. package/dist/scheduler/timezoneRecompute.js.map +1 -0
  550. package/dist/scheduler/types.d.ts +139 -0
  551. package/dist/scheduler/types.d.ts.map +1 -0
  552. package/dist/scheduler/types.js +8 -0
  553. package/dist/scheduler/types.js.map +1 -0
  554. package/dist/sendErrorClassifier.d.ts +45 -0
  555. package/dist/sendErrorClassifier.d.ts.map +1 -0
  556. package/dist/sendErrorClassifier.js +65 -0
  557. package/dist/sendErrorClassifier.js.map +1 -0
  558. package/dist/sessionKey.d.ts +136 -0
  559. package/dist/sessionKey.d.ts.map +1 -0
  560. package/dist/sessionKey.js +153 -0
  561. package/dist/sessionKey.js.map +1 -0
  562. package/dist/sessionPick.d.ts +42 -0
  563. package/dist/sessionPick.d.ts.map +1 -0
  564. package/dist/sessionPick.js +39 -0
  565. package/dist/sessionPick.js.map +1 -0
  566. package/dist/shutdown.d.ts +126 -0
  567. package/dist/shutdown.d.ts.map +1 -0
  568. package/dist/shutdown.js +164 -0
  569. package/dist/shutdown.js.map +1 -0
  570. package/dist/startupPromptBuffer.d.ts +85 -0
  571. package/dist/startupPromptBuffer.d.ts.map +1 -0
  572. package/dist/startupPromptBuffer.js +123 -0
  573. package/dist/startupPromptBuffer.js.map +1 -0
  574. package/dist/state.d.ts +1245 -0
  575. package/dist/state.d.ts.map +1 -0
  576. package/dist/state.js +1830 -0
  577. package/dist/state.js.map +1 -0
  578. package/dist/threadContextPreamble.d.ts +89 -0
  579. package/dist/threadContextPreamble.d.ts.map +1 -0
  580. package/dist/threadContextPreamble.js +102 -0
  581. package/dist/threadContextPreamble.js.map +1 -0
  582. package/dist/threadRouting.d.ts +164 -0
  583. package/dist/threadRouting.d.ts.map +1 -0
  584. package/dist/threadRouting.js +182 -0
  585. package/dist/threadRouting.js.map +1 -0
  586. package/dist/types.d.ts +1015 -0
  587. package/dist/types.d.ts.map +1 -0
  588. package/dist/types.js +3 -0
  589. package/dist/types.js.map +1 -0
  590. package/dist/utils/abortableFifo.d.ts +10 -0
  591. package/dist/utils/abortableFifo.d.ts.map +1 -0
  592. package/dist/utils/abortableFifo.js +55 -0
  593. package/dist/utils/abortableFifo.js.map +1 -0
  594. package/dist/utils/adoptedToolList.d.ts +27 -0
  595. package/dist/utils/adoptedToolList.d.ts.map +1 -0
  596. package/dist/utils/adoptedToolList.js +22 -0
  597. package/dist/utils/adoptedToolList.js.map +1 -0
  598. package/dist/utils/agentEnvironment.d.ts +42 -0
  599. package/dist/utils/agentEnvironment.d.ts.map +1 -0
  600. package/dist/utils/agentEnvironment.js +122 -0
  601. package/dist/utils/agentEnvironment.js.map +1 -0
  602. package/dist/utils/ansiClean.d.ts +11 -0
  603. package/dist/utils/ansiClean.d.ts.map +1 -0
  604. package/dist/utils/ansiClean.js +139 -0
  605. package/dist/utils/ansiClean.js.map +1 -0
  606. package/dist/utils/autoContinueOnLimit.d.ts +49 -0
  607. package/dist/utils/autoContinueOnLimit.d.ts.map +1 -0
  608. package/dist/utils/autoContinueOnLimit.js +69 -0
  609. package/dist/utils/autoContinueOnLimit.js.map +1 -0
  610. package/dist/utils/canonicalPathContainment.d.ts +7 -0
  611. package/dist/utils/canonicalPathContainment.d.ts.map +1 -0
  612. package/dist/utils/canonicalPathContainment.js +48 -0
  613. package/dist/utils/canonicalPathContainment.js.map +1 -0
  614. package/dist/utils/claudeAuthLogin.d.ts +71 -0
  615. package/dist/utils/claudeAuthLogin.d.ts.map +1 -0
  616. package/dist/utils/claudeAuthLogin.js +93 -0
  617. package/dist/utils/claudeAuthLogin.js.map +1 -0
  618. package/dist/utils/claudeChunkClassifier.d.ts +97 -0
  619. package/dist/utils/claudeChunkClassifier.d.ts.map +1 -0
  620. package/dist/utils/claudeChunkClassifier.js +254 -0
  621. package/dist/utils/claudeChunkClassifier.js.map +1 -0
  622. package/dist/utils/claudeCodeVersion.d.ts +31 -0
  623. package/dist/utils/claudeCodeVersion.d.ts.map +1 -0
  624. package/dist/utils/claudeCodeVersion.js +62 -0
  625. package/dist/utils/claudeCodeVersion.js.map +1 -0
  626. package/dist/utils/claudeCompactHook.d.ts +64 -0
  627. package/dist/utils/claudeCompactHook.d.ts.map +1 -0
  628. package/dist/utils/claudeCompactHook.js +98 -0
  629. package/dist/utils/claudeCompactHook.js.map +1 -0
  630. package/dist/utils/claudeLivenessAction.d.ts +208 -0
  631. package/dist/utils/claudeLivenessAction.d.ts.map +1 -0
  632. package/dist/utils/claudeLivenessAction.js +135 -0
  633. package/dist/utils/claudeLivenessAction.js.map +1 -0
  634. package/dist/utils/claudeMcpHeal.d.ts +92 -0
  635. package/dist/utils/claudeMcpHeal.d.ts.map +1 -0
  636. package/dist/utils/claudeMcpHeal.js +91 -0
  637. package/dist/utils/claudeMcpHeal.js.map +1 -0
  638. package/dist/utils/claudeRelayRouting.d.ts +86 -0
  639. package/dist/utils/claudeRelayRouting.d.ts.map +1 -0
  640. package/dist/utils/claudeRelayRouting.js +159 -0
  641. package/dist/utils/claudeRelayRouting.js.map +1 -0
  642. package/dist/utils/claudeRuntimeInfo.d.ts +26 -0
  643. package/dist/utils/claudeRuntimeInfo.d.ts.map +1 -0
  644. package/dist/utils/claudeRuntimeInfo.js +151 -0
  645. package/dist/utils/claudeRuntimeInfo.js.map +1 -0
  646. package/dist/utils/claudeScrapeShapes.d.ts +256 -0
  647. package/dist/utils/claudeScrapeShapes.d.ts.map +1 -0
  648. package/dist/utils/claudeScrapeShapes.js +290 -0
  649. package/dist/utils/claudeScrapeShapes.js.map +1 -0
  650. package/dist/utils/claudeSessionTitle.d.ts +27 -0
  651. package/dist/utils/claudeSessionTitle.d.ts.map +1 -0
  652. package/dist/utils/claudeSessionTitle.js +90 -0
  653. package/dist/utils/claudeSessionTitle.js.map +1 -0
  654. package/dist/utils/claudeStreamJson.d.ts +202 -0
  655. package/dist/utils/claudeStreamJson.d.ts.map +1 -0
  656. package/dist/utils/claudeStreamJson.js +351 -0
  657. package/dist/utils/claudeStreamJson.js.map +1 -0
  658. package/dist/utils/claudeSubagentTail.d.ts +93 -0
  659. package/dist/utils/claudeSubagentTail.d.ts.map +1 -0
  660. package/dist/utils/claudeSubagentTail.js +136 -0
  661. package/dist/utils/claudeSubagentTail.js.map +1 -0
  662. package/dist/utils/clearThreadOutputQueues.d.ts +43 -0
  663. package/dist/utils/clearThreadOutputQueues.d.ts.map +1 -0
  664. package/dist/utils/clearThreadOutputQueues.js +39 -0
  665. package/dist/utils/clearThreadOutputQueues.js.map +1 -0
  666. package/dist/utils/compactCommandRoute.d.ts +45 -0
  667. package/dist/utils/compactCommandRoute.d.ts.map +1 -0
  668. package/dist/utils/compactCommandRoute.js +36 -0
  669. package/dist/utils/compactCommandRoute.js.map +1 -0
  670. package/dist/utils/compactOnIdle.d.ts +329 -0
  671. package/dist/utils/compactOnIdle.d.ts.map +1 -0
  672. package/dist/utils/compactOnIdle.js +379 -0
  673. package/dist/utils/compactOnIdle.js.map +1 -0
  674. package/dist/utils/consoleFileTap.d.ts +51 -0
  675. package/dist/utils/consoleFileTap.d.ts.map +1 -0
  676. package/dist/utils/consoleFileTap.js +86 -0
  677. package/dist/utils/consoleFileTap.js.map +1 -0
  678. package/dist/utils/displayVerbosity.d.ts +44 -0
  679. package/dist/utils/displayVerbosity.d.ts.map +1 -0
  680. package/dist/utils/displayVerbosity.js +75 -0
  681. package/dist/utils/displayVerbosity.js.map +1 -0
  682. package/dist/utils/download.d.ts +72 -0
  683. package/dist/utils/download.d.ts.map +1 -0
  684. package/dist/utils/download.js +216 -0
  685. package/dist/utils/download.js.map +1 -0
  686. package/dist/utils/draftFinalize.d.ts +110 -0
  687. package/dist/utils/draftFinalize.d.ts.map +1 -0
  688. package/dist/utils/draftFinalize.js +100 -0
  689. package/dist/utils/draftFinalize.js.map +1 -0
  690. package/dist/utils/draftId.d.ts +36 -0
  691. package/dist/utils/draftId.d.ts.map +1 -0
  692. package/dist/utils/draftId.js +46 -0
  693. package/dist/utils/draftId.js.map +1 -0
  694. package/dist/utils/draftPacer.d.ts +72 -0
  695. package/dist/utils/draftPacer.d.ts.map +1 -0
  696. package/dist/utils/draftPacer.js +53 -0
  697. package/dist/utils/draftPacer.js.map +1 -0
  698. package/dist/utils/effortStartupKeystroke.d.ts +20 -0
  699. package/dist/utils/effortStartupKeystroke.d.ts.map +1 -0
  700. package/dist/utils/effortStartupKeystroke.js +28 -0
  701. package/dist/utils/effortStartupKeystroke.js.map +1 -0
  702. package/dist/utils/ensureSessionPlan.d.ts +70 -0
  703. package/dist/utils/ensureSessionPlan.d.ts.map +1 -0
  704. package/dist/utils/ensureSessionPlan.js +54 -0
  705. package/dist/utils/ensureSessionPlan.js.map +1 -0
  706. package/dist/utils/fileSendPlan.d.ts +217 -0
  707. package/dist/utils/fileSendPlan.d.ts.map +1 -0
  708. package/dist/utils/fileSendPlan.js +291 -0
  709. package/dist/utils/fileSendPlan.js.map +1 -0
  710. package/dist/utils/fileSendService.d.ts +94 -0
  711. package/dist/utils/fileSendService.d.ts.map +1 -0
  712. package/dist/utils/fileSendService.js +387 -0
  713. package/dist/utils/fileSendService.js.map +1 -0
  714. package/dist/utils/groupFinalizePlan.d.ts +38 -0
  715. package/dist/utils/groupFinalizePlan.d.ts.map +1 -0
  716. package/dist/utils/groupFinalizePlan.js +35 -0
  717. package/dist/utils/groupFinalizePlan.js.map +1 -0
  718. package/dist/utils/identicalOutputGuard.d.ts +83 -0
  719. package/dist/utils/identicalOutputGuard.d.ts.map +1 -0
  720. package/dist/utils/identicalOutputGuard.js +99 -0
  721. package/dist/utils/identicalOutputGuard.js.map +1 -0
  722. package/dist/utils/isoTimestamp.d.ts +16 -0
  723. package/dist/utils/isoTimestamp.d.ts.map +1 -0
  724. package/dist/utils/isoTimestamp.js +34 -0
  725. package/dist/utils/isoTimestamp.js.map +1 -0
  726. package/dist/utils/jsonStreamBusyWatchdog.d.ts +130 -0
  727. package/dist/utils/jsonStreamBusyWatchdog.d.ts.map +1 -0
  728. package/dist/utils/jsonStreamBusyWatchdog.js +122 -0
  729. package/dist/utils/jsonStreamBusyWatchdog.js.map +1 -0
  730. package/dist/utils/jsonStreamHost.d.ts +227 -0
  731. package/dist/utils/jsonStreamHost.d.ts.map +1 -0
  732. package/dist/utils/jsonStreamHost.js +417 -0
  733. package/dist/utils/jsonStreamHost.js.map +1 -0
  734. package/dist/utils/keyedTransitionQueue.d.ts +18 -0
  735. package/dist/utils/keyedTransitionQueue.d.ts.map +1 -0
  736. package/dist/utils/keyedTransitionQueue.js +34 -0
  737. package/dist/utils/keyedTransitionQueue.js.map +1 -0
  738. package/dist/utils/limitEpisodeRecovery.d.ts +92 -0
  739. package/dist/utils/limitEpisodeRecovery.d.ts.map +1 -0
  740. package/dist/utils/limitEpisodeRecovery.js +133 -0
  741. package/dist/utils/limitEpisodeRecovery.js.map +1 -0
  742. package/dist/utils/limitHeldPromptQueue.d.ts +48 -0
  743. package/dist/utils/limitHeldPromptQueue.d.ts.map +1 -0
  744. package/dist/utils/limitHeldPromptQueue.js +59 -0
  745. package/dist/utils/limitHeldPromptQueue.js.map +1 -0
  746. package/dist/utils/limitHeldPrompts.d.ts +58 -0
  747. package/dist/utils/limitHeldPrompts.d.ts.map +1 -0
  748. package/dist/utils/limitHeldPrompts.js +65 -0
  749. package/dist/utils/limitHeldPrompts.js.map +1 -0
  750. package/dist/utils/linkPreviewSuppression.d.ts +18 -0
  751. package/dist/utils/linkPreviewSuppression.d.ts.map +1 -0
  752. package/dist/utils/linkPreviewSuppression.js +37 -0
  753. package/dist/utils/linkPreviewSuppression.js.map +1 -0
  754. package/dist/utils/localClock.d.ts +15 -0
  755. package/dist/utils/localClock.d.ts.map +1 -0
  756. package/dist/utils/localClock.js +29 -0
  757. package/dist/utils/localClock.js.map +1 -0
  758. package/dist/utils/markdownTableRender.d.ts +83 -0
  759. package/dist/utils/markdownTableRender.d.ts.map +1 -0
  760. package/dist/utils/markdownTableRender.js +298 -0
  761. package/dist/utils/markdownTableRender.js.map +1 -0
  762. package/dist/utils/mediaGroupCollector.d.ts +51 -0
  763. package/dist/utils/mediaGroupCollector.d.ts.map +1 -0
  764. package/dist/utils/mediaGroupCollector.js +90 -0
  765. package/dist/utils/mediaGroupCollector.js.map +1 -0
  766. package/dist/utils/messageSendService.d.ts +136 -0
  767. package/dist/utils/messageSendService.d.ts.map +1 -0
  768. package/dist/utils/messageSendService.js +165 -0
  769. package/dist/utils/messageSendService.js.map +1 -0
  770. package/dist/utils/minutesOverride.d.ts +7 -0
  771. package/dist/utils/minutesOverride.d.ts.map +1 -0
  772. package/dist/utils/minutesOverride.js +16 -0
  773. package/dist/utils/minutesOverride.js.map +1 -0
  774. package/dist/utils/modelPickerPlan.d.ts +119 -0
  775. package/dist/utils/modelPickerPlan.d.ts.map +1 -0
  776. package/dist/utils/modelPickerPlan.js +171 -0
  777. package/dist/utils/modelPickerPlan.js.map +1 -0
  778. package/dist/utils/modelSetReplyDecision.d.ts +67 -0
  779. package/dist/utils/modelSetReplyDecision.d.ts.map +1 -0
  780. package/dist/utils/modelSetReplyDecision.js +27 -0
  781. package/dist/utils/modelSetReplyDecision.js.map +1 -0
  782. package/dist/utils/openCodeAuthLogin.d.ts +181 -0
  783. package/dist/utils/openCodeAuthLogin.d.ts.map +1 -0
  784. package/dist/utils/openCodeAuthLogin.js +393 -0
  785. package/dist/utils/openCodeAuthLogin.js.map +1 -0
  786. package/dist/utils/openCodeCompactPlugin.d.ts +81 -0
  787. package/dist/utils/openCodeCompactPlugin.d.ts.map +1 -0
  788. package/dist/utils/openCodeCompactPlugin.js +146 -0
  789. package/dist/utils/openCodeCompactPlugin.js.map +1 -0
  790. package/dist/utils/openCodeTurnActivity.d.ts +67 -0
  791. package/dist/utils/openCodeTurnActivity.d.ts.map +1 -0
  792. package/dist/utils/openCodeTurnActivity.js +27 -0
  793. package/dist/utils/openCodeTurnActivity.js.map +1 -0
  794. package/dist/utils/outputDebounce.d.ts +24 -0
  795. package/dist/utils/outputDebounce.d.ts.map +1 -0
  796. package/dist/utils/outputDebounce.js +30 -0
  797. package/dist/utils/outputDebounce.js.map +1 -0
  798. package/dist/utils/outputFlushTiming.d.ts +54 -0
  799. package/dist/utils/outputFlushTiming.d.ts.map +1 -0
  800. package/dist/utils/outputFlushTiming.js +48 -0
  801. package/dist/utils/outputFlushTiming.js.map +1 -0
  802. package/dist/utils/paginateList.d.ts +25 -0
  803. package/dist/utils/paginateList.d.ts.map +1 -0
  804. package/dist/utils/paginateList.js +32 -0
  805. package/dist/utils/paginateList.js.map +1 -0
  806. package/dist/utils/paneDiff.d.ts +55 -0
  807. package/dist/utils/paneDiff.d.ts.map +1 -0
  808. package/dist/utils/paneDiff.js +88 -0
  809. package/dist/utils/paneDiff.js.map +1 -0
  810. package/dist/utils/paneResizeGuard.d.ts +69 -0
  811. package/dist/utils/paneResizeGuard.d.ts.map +1 -0
  812. package/dist/utils/paneResizeGuard.js +91 -0
  813. package/dist/utils/paneResizeGuard.js.map +1 -0
  814. package/dist/utils/persistAdapterSessionIds.d.ts +39 -0
  815. package/dist/utils/persistAdapterSessionIds.d.ts.map +1 -0
  816. package/dist/utils/persistAdapterSessionIds.js +28 -0
  817. package/dist/utils/persistAdapterSessionIds.js.map +1 -0
  818. package/dist/utils/pinnedBannerSkipDecision.d.ts +44 -0
  819. package/dist/utils/pinnedBannerSkipDecision.d.ts.map +1 -0
  820. package/dist/utils/pinnedBannerSkipDecision.js +20 -0
  821. package/dist/utils/pinnedBannerSkipDecision.js.map +1 -0
  822. package/dist/utils/pollBackoff.d.ts +45 -0
  823. package/dist/utils/pollBackoff.d.ts.map +1 -0
  824. package/dist/utils/pollBackoff.js +44 -0
  825. package/dist/utils/pollBackoff.js.map +1 -0
  826. package/dist/utils/promptDelivery.d.ts +46 -0
  827. package/dist/utils/promptDelivery.d.ts.map +1 -0
  828. package/dist/utils/promptDelivery.js +49 -0
  829. package/dist/utils/promptDelivery.js.map +1 -0
  830. package/dist/utils/providerDisconnectPlan.d.ts +82 -0
  831. package/dist/utils/providerDisconnectPlan.d.ts.map +1 -0
  832. package/dist/utils/providerDisconnectPlan.js +110 -0
  833. package/dist/utils/providerDisconnectPlan.js.map +1 -0
  834. package/dist/utils/rateLimitLog.d.ts +48 -0
  835. package/dist/utils/rateLimitLog.d.ts.map +1 -0
  836. package/dist/utils/rateLimitLog.js +43 -0
  837. package/dist/utils/rateLimitLog.js.map +1 -0
  838. package/dist/utils/recentRelayWindow.d.ts +165 -0
  839. package/dist/utils/recentRelayWindow.d.ts.map +1 -0
  840. package/dist/utils/recentRelayWindow.js +284 -0
  841. package/dist/utils/recentRelayWindow.js.map +1 -0
  842. package/dist/utils/recvPreviewRedaction.d.ts +23 -0
  843. package/dist/utils/recvPreviewRedaction.d.ts.map +1 -0
  844. package/dist/utils/recvPreviewRedaction.js +39 -0
  845. package/dist/utils/recvPreviewRedaction.js.map +1 -0
  846. package/dist/utils/reminderFlow.d.ts +148 -0
  847. package/dist/utils/reminderFlow.d.ts.map +1 -0
  848. package/dist/utils/reminderFlow.js +122 -0
  849. package/dist/utils/reminderFlow.js.map +1 -0
  850. package/dist/utils/reminderScheduleText.d.ts +87 -0
  851. package/dist/utils/reminderScheduleText.d.ts.map +1 -0
  852. package/dist/utils/reminderScheduleText.js +135 -0
  853. package/dist/utils/reminderScheduleText.js.map +1 -0
  854. package/dist/utils/reminderWizard.d.ts +710 -0
  855. package/dist/utils/reminderWizard.d.ts.map +1 -0
  856. package/dist/utils/reminderWizard.js +1164 -0
  857. package/dist/utils/reminderWizard.js.map +1 -0
  858. package/dist/utils/replyQuote.d.ts +111 -0
  859. package/dist/utils/replyQuote.d.ts.map +1 -0
  860. package/dist/utils/replyQuote.js +122 -0
  861. package/dist/utils/replyQuote.js.map +1 -0
  862. package/dist/utils/resolveBinary.d.ts +25 -0
  863. package/dist/utils/resolveBinary.d.ts.map +1 -0
  864. package/dist/utils/resolveBinary.js +99 -0
  865. package/dist/utils/resolveBinary.js.map +1 -0
  866. package/dist/utils/retryAfterHeader.d.ts +7 -0
  867. package/dist/utils/retryAfterHeader.d.ts.map +1 -0
  868. package/dist/utils/retryAfterHeader.js +17 -0
  869. package/dist/utils/retryAfterHeader.js.map +1 -0
  870. package/dist/utils/rotatingJsonlFile.d.ts +40 -0
  871. package/dist/utils/rotatingJsonlFile.d.ts.map +1 -0
  872. package/dist/utils/rotatingJsonlFile.js +106 -0
  873. package/dist/utils/rotatingJsonlFile.js.map +1 -0
  874. package/dist/utils/rotatingLogFile.d.ts +25 -0
  875. package/dist/utils/rotatingLogFile.d.ts.map +1 -0
  876. package/dist/utils/rotatingLogFile.js +74 -0
  877. package/dist/utils/rotatingLogFile.js.map +1 -0
  878. package/dist/utils/sendRateTracker.d.ts +59 -0
  879. package/dist/utils/sendRateTracker.d.ts.map +1 -0
  880. package/dist/utils/sendRateTracker.js +144 -0
  881. package/dist/utils/sendRateTracker.js.map +1 -0
  882. package/dist/utils/serialQueue.d.ts +5 -0
  883. package/dist/utils/serialQueue.d.ts.map +1 -0
  884. package/dist/utils/serialQueue.js +14 -0
  885. package/dist/utils/serialQueue.js.map +1 -0
  886. package/dist/utils/sseStreamLifecycle.d.ts +32 -0
  887. package/dist/utils/sseStreamLifecycle.d.ts.map +1 -0
  888. package/dist/utils/sseStreamLifecycle.js +41 -0
  889. package/dist/utils/sseStreamLifecycle.js.map +1 -0
  890. package/dist/utils/startupReadiness.d.ts +119 -0
  891. package/dist/utils/startupReadiness.d.ts.map +1 -0
  892. package/dist/utils/startupReadiness.js +148 -0
  893. package/dist/utils/startupReadiness.js.map +1 -0
  894. package/dist/utils/statusFlushDecision.d.ts +18 -0
  895. package/dist/utils/statusFlushDecision.d.ts.map +1 -0
  896. package/dist/utils/statusFlushDecision.js +53 -0
  897. package/dist/utils/statusFlushDecision.js.map +1 -0
  898. package/dist/utils/subagentRender.d.ts +52 -0
  899. package/dist/utils/subagentRender.d.ts.map +1 -0
  900. package/dist/utils/subagentRender.js +66 -0
  901. package/dist/utils/subagentRender.js.map +1 -0
  902. package/dist/utils/subagentStatusRender.d.ts +90 -0
  903. package/dist/utils/subagentStatusRender.d.ts.map +1 -0
  904. package/dist/utils/subagentStatusRender.js +124 -0
  905. package/dist/utils/subagentStatusRender.js.map +1 -0
  906. package/dist/utils/telegramError.d.ts +2 -0
  907. package/dist/utils/telegramError.d.ts.map +1 -0
  908. package/dist/utils/telegramError.js +16 -0
  909. package/dist/utils/telegramError.js.map +1 -0
  910. package/dist/utils/terminalEmitPlan.d.ts +74 -0
  911. package/dist/utils/terminalEmitPlan.d.ts.map +1 -0
  912. package/dist/utils/terminalEmitPlan.js +86 -0
  913. package/dist/utils/terminalEmitPlan.js.map +1 -0
  914. package/dist/utils/thinkingRender.d.ts +74 -0
  915. package/dist/utils/thinkingRender.d.ts.map +1 -0
  916. package/dist/utils/thinkingRender.js +85 -0
  917. package/dist/utils/thinkingRender.js.map +1 -0
  918. package/dist/utils/threadStatusReport.d.ts +55 -0
  919. package/dist/utils/threadStatusReport.d.ts.map +1 -0
  920. package/dist/utils/threadStatusReport.js +65 -0
  921. package/dist/utils/threadStatusReport.js.map +1 -0
  922. package/dist/utils/threadToggle.d.ts +29 -0
  923. package/dist/utils/threadToggle.d.ts.map +1 -0
  924. package/dist/utils/threadToggle.js +39 -0
  925. package/dist/utils/threadToggle.js.map +1 -0
  926. package/dist/utils/timezone.d.ts +125 -0
  927. package/dist/utils/timezone.d.ts.map +1 -0
  928. package/dist/utils/timezone.js +278 -0
  929. package/dist/utils/timezone.js.map +1 -0
  930. package/dist/utils/tmuxExec.d.ts +61 -0
  931. package/dist/utils/tmuxExec.d.ts.map +1 -0
  932. package/dist/utils/tmuxExec.js +109 -0
  933. package/dist/utils/tmuxExec.js.map +1 -0
  934. package/dist/utils/tmuxSessionName.d.ts +34 -0
  935. package/dist/utils/tmuxSessionName.d.ts.map +1 -0
  936. package/dist/utils/tmuxSessionName.js +51 -0
  937. package/dist/utils/tmuxSessionName.js.map +1 -0
  938. package/dist/utils/toolResultRender.d.ts +61 -0
  939. package/dist/utils/toolResultRender.d.ts.map +1 -0
  940. package/dist/utils/toolResultRender.js +62 -0
  941. package/dist/utils/toolResultRender.js.map +1 -0
  942. package/dist/utils/topicRequest.d.ts +61 -0
  943. package/dist/utils/topicRequest.d.ts.map +1 -0
  944. package/dist/utils/topicRequest.js +68 -0
  945. package/dist/utils/topicRequest.js.map +1 -0
  946. package/dist/utils/topicView.d.ts +55 -0
  947. package/dist/utils/topicView.d.ts.map +1 -0
  948. package/dist/utils/topicView.js +56 -0
  949. package/dist/utils/topicView.js.map +1 -0
  950. package/dist/utils/transcribeAudio.d.ts +89 -0
  951. package/dist/utils/transcribeAudio.d.ts.map +1 -0
  952. package/dist/utils/transcribeAudio.js +249 -0
  953. package/dist/utils/transcribeAudio.js.map +1 -0
  954. package/dist/utils/transientFrames.d.ts +52 -0
  955. package/dist/utils/transientFrames.d.ts.map +1 -0
  956. package/dist/utils/transientFrames.js +41 -0
  957. package/dist/utils/transientFrames.js.map +1 -0
  958. package/dist/utils/typingActive.d.ts +35 -0
  959. package/dist/utils/typingActive.d.ts.map +1 -0
  960. package/dist/utils/typingActive.js +22 -0
  961. package/dist/utils/typingActive.js.map +1 -0
  962. package/dist/utils/typingLoopBackstop.d.ts +64 -0
  963. package/dist/utils/typingLoopBackstop.d.ts.map +1 -0
  964. package/dist/utils/typingLoopBackstop.js +68 -0
  965. package/dist/utils/typingLoopBackstop.js.map +1 -0
  966. package/dist/utils/verbosityRender.d.ts +18 -0
  967. package/dist/utils/verbosityRender.d.ts.map +1 -0
  968. package/dist/utils/verbosityRender.js +17 -0
  969. package/dist/utils/verbosityRender.js.map +1 -0
  970. package/dist/utils/wedgeRecovery.d.ts +57 -0
  971. package/dist/utils/wedgeRecovery.d.ts.map +1 -0
  972. package/dist/utils/wedgeRecovery.js +24 -0
  973. package/dist/utils/wedgeRecovery.js.map +1 -0
  974. package/dist/utils.d.ts +30 -0
  975. package/dist/utils.d.ts.map +1 -0
  976. package/dist/utils.js +61 -0
  977. package/dist/utils.js.map +1 -0
  978. package/dist/validation.d.ts +113 -0
  979. package/dist/validation.d.ts.map +1 -0
  980. package/dist/validation.js +226 -0
  981. package/dist/validation.js.map +1 -0
  982. package/dist/voiceQueue.d.ts +7 -0
  983. package/dist/voiceQueue.d.ts.map +1 -0
  984. package/dist/voiceQueue.js +33 -0
  985. package/dist/voiceQueue.js.map +1 -0
  986. package/examples/.env.example +17 -0
  987. package/examples/docker-compose.yml +36 -0
  988. package/package.json +83 -4
package/README.md CHANGED
@@ -1,3 +1,1025 @@
1
- # Temporary Holding Version
1
+ <h1 align="center">Charness</h1>
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <p align="center"><em>formerly TelegramCode</em></p>
4
+
5
+ <h2 align="center">Telegram bot/group as a terminal for OpenCode, Claude Code — every thread like a terminal tab + scheduling, voice control</h2>
6
+
7
+ <p align="center">
8
+ <img src="docs/images/hero-v2.png" alt="Charness — all your agents in Telegram; chat, code, ship; powerful slash commands" width="900" />
9
+ </p>
10
+
11
+ Charness turns Telegram into a terminal for running agentic CLIs. Open as
12
+ many tabs as you need — even two on the same project for parallel work — and
13
+ drive the agents from your phone or tablet, voice messages included.
14
+
15
+ Think **OpenClaw for vibe coding**, driven entirely from Telegram: no complex
16
+ setup, no extra dashboards — direct access to your own **OpenCode** /
17
+ **Claude Code** running on your machine or server.
18
+
19
+ ### Features
20
+
21
+ - **Simple setup & management** — install the CLI, put your bot token in one `.env`, bind a topic to a folder, and run; everything after is managed from Telegram — no dashboards
22
+ - **Multi-thread / topic** — one topic per directory with your agent; two topics can share one folder for parallel work — run almost unlimited agents in parallel
23
+ - **Two chat surfaces** — forum-group topics and the owner's bot DM, both served at once by default, with tabs on either
24
+ - **Agent backends** — OpenCode (native server API over HTTP+SSE) and Claude Code (three backends — json-stream, per-turn and tmux)
25
+ - **Sleeping agents** — a Claude json-stream topic's process is stopped when idle (or after every turn in per-turn mode) and the next message resumes the same conversation by itself, prompt cache still warm
26
+ - **Raw terminal** — `/terminal` binds a topic to a real `$SHELL` in the project folder
27
+ - **Notifications** — when the agent asks a question, the bot pins that message so even a muted topic notifies you: the pin pierces mute, giving exactly one notification per question (unpinned once you answer)
28
+ - **Scheduled & self-driving runs** — `/schedule` arms cron / one-shot / N-times jobs per topic, and the agent can schedule *itself* via injected MCP tools: nightly reviews, recurring reports, a "finish this tomorrow at 9" hand-off. Each job fires as a fresh session with your prompt; restart-safe, with one catch-up for a run missed while the bot was down
29
+ - **Reminders** — `/reminders` sets a plain reminder with inline buttons (how often, which day, what time, then the text by typing or voice); at the due time the bot posts and pins it itself, so even a muted topic notifies you. No agent, no bound folder, no date parsing — works in any topic, General included
30
+ - **Inbound files** — photos / documents / video / audio sent to a topic are saved and announced to the agent; albums arrive as one prompt
31
+ - **Outbound files** — the agent sends files back to you — a generated chart, a screenshot, a log, a rendered PDF — single files or albums, straight into the topic
32
+ - **Voice input** — Whisper transcription via Groq (preferred) or OpenAI
33
+ - **Display verbosity and view** — `/verbosity` (plus `/thinking`, `/tool_results`, `/subagent`) per topic: detail `minimal|short|full`, and what the topic shows — `stream` (default), `stream_answers` or `answers` (only the agent's pinned answers, no stream)
34
+ - **Time-aware prompts** — `/timestamps on` prepends each forwarded prompt's send time (local-offset ISO), so a days-long session knows what "yesterday" or "2 days ago" means; agent-facing only, per topic
35
+ - **Reply context** — reply to a message in a topic and the quoted text is folded into the prompt the agent receives, so it sees what you point at without re-pasting; works for text, voice and files (a photo, a document, an album…), both agents; a reply TO a photo, file or voice note names it, with the path of the bot's saved copy when it has one
36
+ - **Your timezone** — `/timezone Europe/Moscow` once, and schedules fire at your wall-clock time while the agent is told what time it actually is; no server-clock guessing
37
+ - **Waits out usage limits** — when the agent hits a usage / session limit the bot reads the reset time out of the error (honouring the timezone the provider quotes), waits, then tells the agent to continue by itself and pins the "work resumed" message so a muted topic still notifies you; `/auto_continue_limits` turns it off per topic or skips a single resume
38
+
39
+ ## Two surfaces: group topics, bot DM, or both
40
+
41
+ The bot serves two Telegram surfaces:
42
+
43
+ - **Forum supergroup** — every topic is a tab. The most familiar UX: a visible
44
+ topic list, per-topic names and icons, quick switching between agents. The
45
+ trade-off is Telegram's group throughput cap (about 20 messages/min per
46
+ group), so the bot paces and coalesces heavy output streams.
47
+ - **The bot's own DM** (owner only) — the private
48
+ chat runs in topic mode too, so you get the same thread-per-agent tabs
49
+ there, and live output streams through Telegram's native draft "cursor":
50
+ the reply grows in place in one message instead of arriving as a flood of
51
+ separate messages.
52
+
53
+ ## Where to run the bot server
54
+
55
+ The bot runs anywhere the agent CLIs run:
56
+
57
+ - **Your own working machine** — the simplest start: launch `telegramcode`
58
+ from your projects parent folder and your laptop/desktop becomes the
59
+ backend. The catch: agents are only reachable while the machine is on and
60
+ awake.
61
+ - **A remote server (cloud VPS/VDS or bare metal)** — the more convenient
62
+ setup: organize your working environment there once (git + your repos,
63
+ Claude Code / OpenCode, `telegramcode`) and you get an always-on dev box you
64
+ drive from any device through Telegram — phone, tablet, voice. When picking
65
+ one, a bare-metal server without virtualization beats a virtualized VPS/VDS:
66
+ its NVMe drives are usually much faster, which matters once you also build
67
+ and run a dev server there, not just the agents. Classic terminal access to
68
+ the very same Claude sessions stays available over SSH — run plain `claude`
69
+ in the project folder: it reads the same `~/.claude/projects/<cwd-slug>/`
70
+ session store the bot uses, so a session started in a Telegram thread can
71
+ be picked up in the terminal and vice versa.
72
+
73
+ ## Quick Start
74
+
75
+ Five steps: install the CLI, create a bot, (optionally) set up a group, launch
76
+ from your projects folder, bind a topic to an agent.
77
+
78
+ > **[manual]** tags a step you must do by hand in the Telegram app — an agent
79
+ > running on the host can't do it for you. Everything unmarked is a host-side
80
+ > command (or file edit) that can be automated.
81
+
82
+ ### 1. Install the CLI
83
+
84
+ ```bash
85
+ npm install -g charness # needs Node ≥ 22.12
86
+ ```
87
+
88
+ This registers the `telegramcode` command (it keeps the project's former name;
89
+ `charness` works too). Coming from the old `telegramcode` npm package (2.x)?
90
+ Run `npm uninstall -g telegramcode` first: both install the same commands. The
91
+ config, `DATA_DIR` and sessions carry over as they are.
92
+
93
+ Prefer the whole bot in a container, isolated from the host? Use
94
+ [Run with Docker](#run-with-docker) instead — the bot and group steps are the
95
+ same. To hack on the bot itself, see [Run from source](#run-from-source).
96
+
97
+ ### 2. Create the bot
98
+
99
+ **[manual]** In Telegram, with @BotFather:
100
+
101
+ 1. Message [@BotFather](https://t.me/BotFather), send `/newbot`, follow prompts.
102
+ 2. Save the token (`123456789:ABCdef...`).
103
+ 3. **Disable privacy mode** — same chat: `/setprivacy` → pick this bot →
104
+ `Disable`. Without this the bot only sees `/commands`, not free-form text.
105
+
106
+ <p><img src="docs/images/01-botfather.png" width="360" alt="@BotFather: /newbot to get the token, then /setprivacy → this bot → Disable" /></p>
107
+
108
+ Put the token where the bot reads it — the global config `.env`:
109
+
110
+ ```bash
111
+ mkdir -p ~/.config/telegramcode
112
+ $EDITOR ~/.config/telegramcode/.env
113
+ ```
114
+
115
+ ```dotenv
116
+ # ~/.config/telegramcode/.env — the one place your keys live
117
+ TELEGRAM_BOT_TOKEN=123456789:ABCdef...
118
+ # GROQ_API_KEY=... # optional: free voice transcription
119
+ # OWNER_USER_ID=... # set to drive the bot from its DM (step 3)
120
+ ```
121
+
122
+ All keys the bot needs live in this one file — the bot token and the optional
123
+ `GROQ_API_KEY` for voice sit side by side, no separate stores. (Agent
124
+ provider auth is the exception, and lives inside the agents themselves:
125
+ `claude login` for Claude, `/connect` or OpenCode's own config for OpenCode —
126
+ see [Environment Variables](#environment-variables).) The wrapper loads env
127
+ from two locations, in order — a global base and an optional per-project
128
+ override:
129
+
130
+ 1. `~/.config/telegramcode/.env` — base, set once, used everywhere.
131
+ 2. `$PWD/.env` — per-project tweaks (last write wins).
132
+
133
+ The full annotated template is `.env.example` (shipped with the package and in
134
+ the repo).
135
+
136
+ ### 3. Set up a group (optional) or DM
137
+
138
+ The bot serves a **forum supergroup**, the **owner's private chat (DM)**, or
139
+ both at once (`CHAT_MODE`, default `both`). The group is the nicest UX — a
140
+ visible topic list, per-topic names/icons, quick agent switching — but it is
141
+ **optional**: with `OWNER_USER_ID` set you can run entirely from the bot's DM.
142
+
143
+ **To use a group (recommended)** — **[manual]**, all in the Telegram client:
144
+
145
+ 1. In a Telegram client: `New Group` → name it → add the bot.
146
+ 2. Open group settings → enable **Topics** (Forum mode).
147
+ 3. Promote the bot to admin with these rights:
148
+ - `Manage Topics` (required — bind/create threads)
149
+ - `Delete Messages` (for `/clear_messages`)
150
+ - `Pin Messages` (per-thread status banner + question alerts; `/doctor`
151
+ warns if missing, but the bot still runs without it)
152
+ 4. **Remove the bot from the group and add it again.** Telegram caches the
153
+ privacy-mode flag on join; without re-adding, the bot keeps private-mode and
154
+ ignores free-form messages (see
155
+ [Troubleshooting → Bot doesn't see text](#bot-doesnt-see-text)).
156
+
157
+ <p>
158
+ <img src="docs/images/02-enable-topics.png" width="360" alt="Group settings → Topics toggle enabled (Forum mode)" />
159
+ <img src="docs/images/03-promote-admin.png" width="360" alt="Bot admin rights: Manage Topics, Delete Messages, Pin Messages ticked" />
160
+ </p>
161
+
162
+ The group **id is automatic** — leave `ALLOWED_GROUP_ID` empty and the bot
163
+ auto-pairs with the first forum supergroup an admin talks to it from (the
164
+ `-100…` id is saved to `state.json`; re-point later with `/pair`). To pin a
165
+ specific id by hand, set `ALLOWED_GROUP_ID` to the numeric value (a group
166
+ **name** is not accepted) — that also disables auto-pairing. **Access control:**
167
+ whoever is a creator/administrator of that group may drive the bot — there is no
168
+ separate allow-list.
169
+
170
+ **To use the DM instead (or as well):** **[manual]** send `/start` to
171
+ [@userinfobot](https://t.me/userinfobot) for your numeric user id, then add it
172
+ (host-side) to the same `.env`:
173
+
174
+ ```dotenv
175
+ OWNER_USER_ID=987654321
176
+ ```
177
+
178
+ With the default `CHAT_MODE=both`, an unset `OWNER_USER_ID` just leaves the DM
179
+ surface inert (group-only); set it and the DM lights up alongside the group. Use
180
+ `CHAT_MODE=dm` to skip the group entirely.
181
+
182
+ ### 4. Launch from your projects folder
183
+
184
+ ```bash
185
+ cd ~/projects && telegramcode
186
+ ```
187
+
188
+ That `$PWD` — the parent folder holding your repositories — is the folder the
189
+ bot works in; each topic binds to a subfolder under it. There's nothing else to
190
+ configure for it. A single-instance lockfile (`$DATA_DIR/instance.lock`) stops a
191
+ second bot starting under the same user; stale locks (after `kill -9`) are
192
+ reclaimed automatically.
193
+
194
+ ### 5. Bind a topic and talk to an agent
195
+
196
+ This step is **[manual]** — it all happens inside the Telegram app.
197
+
198
+ **In the group:**
199
+
200
+ 1. Open the group; in the General topic send `/ls` — the bot lists subfolders
201
+ under the work root.
202
+ 2. Create a topic (the `+` button).
203
+ 3. In the new topic: `/bind <subdir>` (auto-bound if the topic name matches a
204
+ subdir).
205
+ 4. `/claude` or `/opencode` → talk to the agent (text or voice).
206
+
207
+ <p><img src="docs/images/04-bind-and-run.png" width="360" alt="A topic bound with /bind, /claude started, and the agent replying" /></p>
208
+
209
+ **In the DM:** the same commands, one thread per agent — `/ls`,
210
+ `/bind <subdir>`, then `/claude` or `/opencode`.
211
+
212
+ Then: `/quit` ends the session; bare `/bind` inspects/reconfigures the binding;
213
+ `/clear_messages` deletes the topic's bot messages.
214
+
215
+ > **Continue the same session from a terminal.** No wrapper needed: run
216
+ > `claude --dangerously-skip-permissions` in the project folder. It is the same
217
+ > binary and the same `~/.claude/projects/<cwd-slug>/` session store the bot
218
+ > uses, so a thread started in Telegram can be picked up with `claude --resume`
219
+ > and vice versa.
220
+
221
+ ### Run with Docker
222
+
223
+ The whole bot runs in one container: the bot itself, tmux, every agent it starts (Claude Code in any backend,
224
+ `/terminal`, OpenCode and its server) and the bot's MCP server. Everything works as on a host; the host only
225
+ lends the container the folders it mounts, so the bot and its agents see nothing else of the machine.
226
+
227
+ ```
228
+ host container telegramcode-<instance>
229
+ your instance env file (tokens) ── ro ──▶ the bot (hot mode) + a restart loop
230
+ DATA_DIR, WORK_ROOT ──────▶ tmux, the agents, OpenCode, the bot MCP
231
+ a clone of this repository ──────▶ /opt/telegramcode
232
+ the agents' home folder ──────▶ /home/telegramcode: their Claude / OpenCode
233
+ login, sessions and CLI installs
234
+ ```
235
+
236
+ 1. **Image and checkout.** Clone the repository for the container (a full clone of its own, not a worktree and
237
+ not the checkout a host instance runs from) and build the image from it:
238
+
239
+ ```bash
240
+ git clone https://github.com/olosegres/charness /srv/telegramcode/checkout
241
+ cd /srv/telegramcode/checkout && docker build -t telegramcode -f docker/Dockerfile .
242
+ ```
243
+
244
+ The image holds only system tools (tmux, git, ffmpeg, ripgrep, …) and the yarn the checkout's `package.json`
245
+ names (build from the checkout's root, which is how the build reads it; rebuild after a yarn bump so no
246
+ container has to download it). On first start the container installs the checkout's dependencies for the
247
+ image and builds it; the bot installs Claude Code and OpenCode into the agents' home.
248
+ 2. **Instance env file**, as for a host install (`TELEGRAM_BOT_TOKEN`, `DATA_DIR`, `WORK_ROOT`, …). It stays on
249
+ the host and is mounted read-only. Every host path it names (`DATA_DIR`, `WORK_ROOT`, `TMUX_TMPDIR`, …) is
250
+ mounted at the SAME path, so `state.json`, sessions and file paths stay valid inside and an instance can move
251
+ between host and container unchanged.
252
+ 3. **Run** (or use `examples/docker-compose.yml`):
253
+
254
+ ```bash
255
+ docker run -d --name telegramcode-main --hostname telegramcode-main \
256
+ --restart unless-stopped --stop-timeout 60 \
257
+ -v /srv/telegramcode/checkout:/opt/telegramcode \
258
+ -v /srv/telegramcode/home:/home/telegramcode \
259
+ -v /srv/telegramcode/data:/srv/telegramcode/data \
260
+ -v /srv/projects:/srv/projects \
261
+ -v /srv/telegramcode/instance.env:/srv/telegramcode/instance.env:ro \
262
+ -e ENV_FILE=/srv/telegramcode/instance.env \
263
+ telegramcode
264
+ ```
265
+
266
+ No port is published: Telegram is long polling, and the bot's MCP server and the OpenCode server listen on the
267
+ container's own loopback. Keep the `--hostname` fixed and unique per instance: the instance lock tells its own
268
+ restarted container from another machine by it.
269
+ 4. **Log the agents in once**, with the account the agents should use (a separate one is best):
270
+ `docker exec -it telegramcode-main claude`, then `/login` (or `/login` in a topic). OpenCode: `/connect` in a
271
+ topic, or `docker exec -it telegramcode-main opencode auth login`. Never copy your own `~/.claude` login in:
272
+ a second holder of its refresh token can log the first one out. For git, put a `.gitconfig` and an SSH key of
273
+ their own into the agents' home.
274
+
275
+ Running it:
276
+
277
+ - **Restart the bot only** (agents keep running and are adopted): `docker exec telegramcode-main
278
+ telegramcode-restart-bot`. A crashed bot is restarted by the container after 20 s.
279
+ - **Update the bot**: `docker exec telegramcode-main /opt/telegramcode/scripts/self-update.sh` (by hand or from
280
+ a timer on the host); hot mode picks the change up. A change to `src/cli.ts`, `src/cli/hot.ts` or `nodemon.json`
281
+ needs the bot restart above. **Update Claude Code / OpenCode**: they update themselves in the agents' home, or
282
+ `docker exec telegramcode-main npm install -g @anthropic-ai/claude-code` / `… opencode-ai`.
283
+ - **A container restart is a reboot**: every agent process ends; each conversation resumes with its next message.
284
+ - **Rootless Docker** (recommended): the container's root is your user, so everything written on the mounts is
285
+ yours. **Rootful Docker**: add `--user "$(id -u):$(id -g)"` and give that user the agents' home.
286
+ - **Moving a host instance in**: stop it first (the lock refuses a lock written on another host), mount its
287
+ folders at their paths, and copy its conversations' folders from `~/.claude/projects/` into the agents' home
288
+ `.claude/projects/` — without them each conversation starts a fresh session on its next message.
289
+ - **What the agents can read**: everything mounted, the instance env file with its tokens, `DATA_DIR` and the
290
+ checkout included. The container keeps the rest of the host out of reach; running the agents as a second user
291
+ inside the container would close that gap later.
292
+
293
+ ### Run from source
294
+
295
+ To hack on the bot itself:
296
+
297
+ ```bash
298
+ git clone https://github.com/olosegres/charness && cd charness
299
+ yarn install && yarn build
300
+ npm install -g . # registers the `telegramcode` (and `charness`) command
301
+ ```
302
+
303
+ Shortcut: `yarn install-link` runs `yarn install && yarn build && npm link` in
304
+ one shot — `npm link` symlinks the global `telegramcode` to this folder, so
305
+ later rebuilds (`yarn build`) are picked up without re-installing. Config and
306
+ launch are identical to steps 2–5 above.
307
+
308
+ ## Required files structure
309
+
310
+ Launch `telegramcode` from the parent folder that holds your project repos —
311
+ that `$PWD` is the work root, and every topic binds to a subfolder under it:
312
+
313
+ ```
314
+ $PWD=/home/user/src ← run the bot here ($PWD = work root)
315
+ ├── projectA/ ← Topic "projectA" (claude)
316
+ ├── projectB/ ← Topic "projB-frontend" (claude)
317
+ │ ← Topic "projB-backend" (opencode) ← one folder, two topics
318
+ │ ← Topic "projB-refactor" (claude)
319
+ └── projectC/ ← Topic "projectC" (opencode)
320
+
321
+ Telegram forum supergroup
322
+ ├── Bot is admin with can_manage_topics + can_delete_messages + can_pin_messages
323
+ └── Routes each topic by (chatId, threadId) → remembered in state.json
324
+ ```
325
+
326
+ **What the bot remembers (`state.json`).** So a restart is seamless, the bot
327
+ saves which topic is bound to which folder, each topic's live agent session (to
328
+ re-attach it after a reboot), your per-topic preferences (agent, model,
329
+ reasoning effort, language, display verbosity), scheduled prompts, and the
330
+ paired group id. This file lives in `DATA_DIR` (default `~/.telegramCode`) and
331
+ is written safely, so a crash can't corrupt it.
332
+
333
+ ## Commands
334
+
335
+ ### In any topic
336
+
337
+ Most commands work in any topic; a binding (`/bind`) is required only to
338
+ actually start an agent or terminal in the folder.
339
+
340
+ | Command | Description |
341
+ |---|---|
342
+ | `/claude`, `/opencode`, `/oc` | Start agent in this topic's bound folder |
343
+ | `/terminal` | Open a raw `$SHELL` in the bound folder — see [Raw terminal](#raw-terminal-terminal) |
344
+ | `/claude_mode` | Switch this topic's Claude backend (json-stream ⇄ per-turn ⇄ tmux-scrape); bare shows a picker — see [Claude Code backends](#claude-code-backends-claude_mode) |
345
+ | `/model` | Switch model through a two-level picker: providers, then that provider's models (10 per page). Each provider row has a 🙈 to hide it from the picker (bot-wide, persisted; 👁 brings it back) — `/model <provider/model>` still reaches a hidden provider. If OpenCode is waiting on an old provider's retry, the next prompt interrupts that wait and starts with the selected model instead of sitting queued behind it |
346
+ | `/connect [provider]` | Connect an OpenCode provider (OpenAI by default; for example `/connect openrouter`). Special OAuth methods are shown when available; ordinary catalog providers ask for an API key and delete the key message from Telegram |
347
+ | `/disconnect [provider]` | Remove an OpenCode provider's stored credentials; bare shows a picker of active providers. A provider OpenCode enables from an environment variable (e.g. `OPENROUTER_API_KEY`) stays active after this — the reply says so and points at hiding it in `/model` |
348
+ | `/effort` | Set reasoning effort (per-thread) via inline buttons. Claude: native `/effort` levels (`low…ultracode`). OpenCode: the current model's variants, applied per-prompt. No env configuration |
349
+ | `/verbosity` | Output-verbosity macro (`minimal\|short\|full`): sets the thinking, tool-results and sub-agent display prefs at once; `/thinking`, `/tool_results`, `/subagent` point-override afterwards. Mixed prefs show as "custom" in the picker. The picker's second row (or `/verbosity stream\|stream_answers\|answers`) sets what the topic SHOWS — "Stream only" (default), "Stream + answers" (the stream plus the agent's pinned answers) or "Answers only" (the pinned answers, no stream; an agent that keeps writing after its final answer is reminded once to send what matters); a switch applies from the next message, and `/status` names the view |
350
+ | `/thinking` | Chain-of-thought display: `full` keeps the reasoning, `short` collapses to "💭 thought for Ns", `minimal` keeps only the live working cue |
351
+ | `/tool_results` | Completed tool-call output: `full` whole body, `short` capped (15 lines / 1200 chars), `minimal` transient 🔧 status only |
352
+ | `/subagent` | Sub-agent transcript: status-only with a ticking elapsed counter (`minimal`/`short`), or streamed "🤖 ⤷" chunks (`full`) |
353
+ | `/sessions` | List & resume previous sessions in this folder |
354
+ | `/rename_session` | Rename the current live session (OpenCode; Claude transcripts have no title) |
355
+ | `/quit`, `/q` | End the session — Claude: graceful double Ctrl+C; OpenCode/terminal: `stopSession`. Releases the persisted session id, so a bot restart won't auto-reattach it (resume later via `/sessions`). A topic whose agent process is gone but whose session is kept (it "sleeps": the next message, file, voice note, `/compact`, `/model` or `/effort` resumes it by itself) is released the same way |
356
+ | `/new`, `/clear_session` | End the current session and start a fresh one (same adapter) |
357
+ | `/status` | This thread's status: agent, bound folder and resolved workdir; a live session also shows the model it is actually running, effort, start time, runtime version and context use (anything the backend cannot report renders as unknown) |
358
+ | `/output` | Last 500 lines of agent output (sent as at most 5 chunks; the overflow is reported as omitted) |
359
+ | `/c`, `/y`, `/n` | Ctrl+C / "y" / "n" |
360
+ | `/enter`, `/up`, `/down`, `/tab` | tmux key passthrough |
361
+ | `/esc`, `/escape` | Send a raw Escape — interrupt the current turn / dismiss a selector |
362
+ | `/schedule` | Schedule a prompt in free text — the agent parses the time and owns the job; see [Scheduler](#scheduler-schedule) |
363
+ | `/reminders` | Reminders set with buttons only — the bot posts and pins them itself, with no agent involved; works in any topic, bound or not. See [Reminders](#reminders-reminders) |
364
+ | `/clear_messages` | Delete bot messages in this topic (up to 48h, Telegram limit) |
365
+ | `/clear` | Forwarded to the agent (Claude wipes context; OpenCode plain text) — not a bot command anymore. Also purges the topic's file-intake dir |
366
+ | `/compact` | Compact the agent's context — a real, confirmed compaction on OpenCode and on the default (stream) Claude backend; the tmux Claude backend has the literal command forwarded (its TUI compacts); terminal: not supported |
367
+ | `/compact_on_idle` | Toggle auto-compaction after ~55 min idle (Enable/Disable picker; `AGENT_IDLE_MINUTES` sets the window). Per topic; run it in **General** to set the default for all topics. On by default. Each idle compaction posts one short notice; the picker's «Show summary» / «Hide summary» row (the `/compact_summary` switch) adds the agent's full summary after it. Fires at most once per active period (until you write again); if a question was pending it is re-asked with tappable buttons after the compaction. On the json-stream backends the idle mark also STOPS the agent process, compaction or not — the topic sleeps and your next message resumes it (`/status` says so) |
368
+ | `/compact_summary` | Toggle posting the agent's full summary after a compaction — `/compact`, the idle compaction and the agent's own `compact_conversation` (Enable/Disable picker). Per topic; run it in **General** to set the default for all topics. Off by default: a compaction ends in a short line. On OpenCode it also hides the summary OpenCode streams itself |
369
+ | `/auto_continue_limits` | Toggle waiting out a usage / session limit and resuming this topic by itself (Enable/Disable picker). Per topic; run it in **General** to set the default for all topics. On by default. While a resume is waiting the picker also offers «⏭ Skip once» — drop that one resume without turning the mode off |
370
+ | `/bind` | Bare: current binding + folder picker, with «leave current dir» (the old `/unbind`) and «create new folder» buttons |
371
+ | `/mcp` | List MCP servers active for this thread |
372
+
373
+ ### Info / ops (work anywhere)
374
+
375
+ | Command | Description |
376
+ |---|---|
377
+ | `/start` | Intro: work root + available agents |
378
+ | `/help` | Context-aware help |
379
+ | `/ls` | List the project subfolders of the folder the bot was launched from |
380
+ | `/list` | List existing topics and their bindings |
381
+ | `/status` | This topic's status; in General — a global view of all topics + active agents. A live non-General topic also reports the resolved workdir, the model actually running, runtime version and context use (unknown when the backend cannot report it) |
382
+ | `/doctor` | Self-diagnose: admin rights, privacy mode, paths, CLIs |
383
+ | `/version` | Versions: bot, claude, opencode, node, tmux |
384
+ | `/whoami` | Show userId, chatId, threadId, isAllowed, binding |
385
+ | `/trace` | Output-trace recorder (`on`/`off`, `on all`/`off all`; bare = status) — see [Observability](#observability) |
386
+ | `/timestamps` | Prepend the send time to prompts forwarded to the agent (`on`/`off`; bare = status) |
387
+ | `/language`, `/lang` | Set the bot UI language for this chat; bare opens an endonym picker (sorted A→Z by English name), `/language auto` returns to automatic per-chat detection |
388
+ | `/timezone` | Set the timezone every clock the bot touches speaks in — schedules, agent time context, time renders. `/timezone Europe/Moscow`, `/timezone +04:00`, `/timezone auto` (host zone); bare opens a region → zone picker. Instance-wide, not per chat |
389
+ | `/pair` | Bind this forum supergroup to the bot (re-point auto-pairing) — works from any topic of the target group; a numeric `ALLOWED_GROUP_ID` env locks pairing |
390
+
391
+ ### General-only
392
+
393
+ | Command | Description |
394
+ |---|---|
395
+ | `/quit-all`, `/quitall` | End every active agent in every bound topic (also releases their session ids) |
396
+
397
+ ### Natural language
398
+
399
+ In a bound topic you can also type:
400
+ - `claude fix the bug` / `opencode add tests`
401
+ - A plain message after `/claude` is already running → routed as input.
402
+
403
+ Voice messages are transcribed via Groq Whisper (free) or OpenAI Whisper
404
+ (fallback) and follow the same routing. If the provider times out or fails
405
+ temporarily, the bot says so in the topic and retries automatically (after 5 s,
406
+ then 15 s) — no need to re-record the voice note.
407
+
408
+ `/terminal` is never started from a natural-language phrase — only the
409
+ explicit command opens a shell.
410
+
411
+ ## Claude Code backends (`/claude_mode`)
412
+
413
+ "Claude Code" in a topic is one user-facing choice with three interchangeable
414
+ backends:
415
+
416
+ - **json-stream** (`/claude_mode json`) — `claude -p` in stream-json mode as
417
+ an external tmux-hosted process emitting structured events. Cleaner
418
+ output, and it survives bot restarts (the bot re-adopts the process and
419
+ replays what was produced during the downtime). After ~55 min idle
420
+ (`AGENT_IDLE_MINUTES`) the process is stopped — right after the idle
421
+ compaction when `/compact_on_idle` allows one. The **default**.
422
+ - **per-turn** (`/claude_mode per-turn`) — the same host, stopped right
423
+ after every answer. Nothing is lost: the conversation sleeps and your next
424
+ message resumes it. Needs Claude Code 2.1.287 or newer; the switch is
425
+ refused when the topic's last known version is older.
426
+ - **tmux-scrape** (`/claude_mode tmux`) — the classic TUI driven by
427
+ keystrokes inside tmux; output is scraped from the pane. Never stopped
428
+ automatically.
429
+
430
+ A stopped json-stream conversation **sleeps**: `/status` says so, and the next
431
+ message, file, voice note, `/compact`, `/model` or `/effort` resumes it by
432
+ itself — same session, prompt cache still warm, no "started" notice. A
433
+ process is never stopped while it works (a running turn, a background task,
434
+ a message not yet taken in). Only `/quit`, `/new` and leaving the folder end
435
+ the conversation.
436
+
437
+ `/login` works on all three backends. tmux-scrape hosts the sign-in inside its
438
+ TUI; the json-stream backends (which have no TUI) run it **out-of-band** — the bot spawns
439
+ `claude auth login` in a pty, posts the sign-in link into the topic, and takes
440
+ your pasted code back (the code message is deleted and a 🔐 confirmation is
441
+ posted). While the sign-in waits for the code, a message that is not a code
442
+ is answered with a waiting hint instead of being swallowed; `/esc` cancels
443
+ the pending sign-in (the link message says so).
444
+
445
+ All three drive the same `claude` CLI against the same on-disk
446
+ transcript, so `/claude_mode` switches a live topic seamlessly — the
447
+ conversation resumes on the other backend. The pick persists per topic
448
+ (▶️ Claude and `/claude` reopen it; on a sleeping topic they resume it).
449
+ Billing note: the json-stream backends strip `ANTHROPIC_API_KEY` from the
450
+ agent's env, so usage bills to your Claude subscription rather than an API key.
451
+
452
+ ## Scheduler (`/schedule`)
453
+
454
+ A topic can have scheduled prompts: at fire time the bot posts the prompt
455
+ into the topic, pins the announcement (pins accumulate as run history),
456
+ waits for a busy agent to go idle (up to 10 min, rather than interrupting
457
+ live work) and delivers it — reusing the active session or starting one
458
+ with the thread's last-used backend.
459
+
460
+ - `/schedule <free text>` is a thin wrapper — the bot owns no date parsing.
461
+ The agent interviews you (bare `/schedule`) or parses "every day at 9" /
462
+ "tomorrow 15:00" itself, then calls the bot-injected `schedule_create` /
463
+ `schedule_list` / `schedule_cancel` MCP tools (cron, one-shot, or
464
+ N-times; min interval 5 min; up to 30 agent jobs per topic — reminders are
465
+ counted separately, see below).
466
+ - Restart-safe: timers re-arm at boot; a run missed during downtime fires
467
+ one catch-up annotated with the missed time.
468
+ - Leaving a folder pauses the topic's scheduled prompts; `/bind` resumes
469
+ them (an expired one-shot is dropped). Reminders keep running — see
470
+ below. Run history: `DATA_DIR/scheduler-runs.jsonl`.
471
+
472
+ ## Reminders (`/reminders`)
473
+
474
+ A reminder is the bot's own notification, not a job for an agent: at the due
475
+ time the bot posts your text into the topic and pins it — and the pin is what
476
+ notifies you through a muted topic. Nothing is started and nothing is
477
+ interrupted, so reminders work in **any** topic, with or without a bound
478
+ folder and with or without a running agent.
479
+
480
+ `/reminders` opens a hub with «➕ Add», «📋 List (N)» and «✕ Close». Add walks
481
+ four steps, all in the same message:
482
+
483
+ 1. **How often** — once, every day, weekdays, weekly, monthly.
484
+ 2. **Which day** — a date (today / tomorrow / a date grid), a weekday, or a
485
+ day of the month. Skipped for "every day" and "weekdays".
486
+ 3. **What time** — four quick presets, or «🕐 Other time» → the hour, then
487
+ the minutes in 5-minute steps.
488
+ 4. **The text** — type it or send a voice note, up to 1000 characters.
489
+
490
+ Steps 1–3 are buttons only: the reminder text is the one thing you type.
491
+ `‹ Back` changes a pick, `✕ Cancel` drops the wizard, and any command cancels
492
+ it. Picking a time that has already passed says so instead of quietly moving
493
+ the reminder to tomorrow, and a text over the limit is refused with nothing
494
+ saved — send a shorter one and the reminder is created from that.
495
+
496
+ The finished reminder shows when it fires, its text and its next run. «📋
497
+ List» pages through the topic's reminders (8 per page); tapping one opens it
498
+ with a «🗑 Delete» button — the card shows exactly what you are deleting, so
499
+ there is no extra confirmation step. A topic holds up to 100 reminders — counted
500
+ separately from the agent's scheduled prompts, so neither takes slots from the
501
+ other — and they survive a restart, with one missed while the bot was down firing
502
+ a single catch-up.
503
+
504
+ Reminders are yours, not the agent's: the scheduling tools an agent can call
505
+ do not see them, so it can neither list nor delete them.
506
+
507
+ ## Raw terminal (`/terminal`)
508
+
509
+ `/terminal` binds the topic to a real interactive `$SHELL` (in tmux) in the
510
+ bound folder — a third backend alongside the agents, mutually exclusive
511
+ with them. Every plain message is typed in as a command; output streams
512
+ back as one rolling message per command. Raw keys reuse the TUI commands:
513
+ `/c` (Ctrl-C), `/up` `/down` (history), `/tab` (completion), `/enter`.
514
+ Restart-safe like the agents: the shell survives a bot restart and is
515
+ re-adopted. Known v1 limitation: full-screen TUIs (vim, htop, less) render
516
+ messy; normal commands, builds, and logs stream cleanly. The shell comes
517
+ from the `SHELL` env (fallback `/bin/bash`).
518
+
519
+ ## File intake
520
+
521
+ Send a file to a bound topic with an active agent and the bot hands it to
522
+ the agent:
523
+
524
+ - Six kinds: photo, document (incl. PDF), video, video note, audio,
525
+ animation. Voice is **not** intake — it stays on the transcription path.
526
+ - The file is downloaded to `DATA_DIR/files/<chatId>_<threadId>/`
527
+ (bot-owned, never inside your project folder) and announced to the agent
528
+ as `[Telegram file] … saved to: <path>` plus your caption.
529
+ - A media album (several files sent as one message) is batched into ONE
530
+ combined `[Telegram album]` prompt instead of N separate ones.
531
+ - Limits and cleanup: 20 MB per file (Telegram Bot API cap); forwarding a
532
+ bare `/clear` purges the topic's intake dir (the agent's context is gone,
533
+ so the files are useless); files older than 30 days are swept daily.
534
+
535
+ ## Observability
536
+
537
+ - **Output trace** — ON by default for every thread: each incoming update,
538
+ adapter emit, and outgoing Bot API call (with outcome, incl. 429 details)
539
+ is recorded into hourly buckets `DATA_DIR/output-trace-*.jsonl` (pruned
540
+ after 6h). `/trace off all` turns it off durably; bare `/trace` reports
541
+ status. This is the source of truth when debugging "a message never
542
+ reached the topic".
543
+ - **Console tee** — the bot's stdout/stderr is mirrored to
544
+ `DATA_DIR/bot-console-*.log` (same hourly buckets, same 6h prune), so
545
+ boot logs stay readable post-incident without the operator's terminal.
546
+ - **`/timestamps on`** — prepends each forwarded prompt's send time
547
+ (local-offset ISO) so a days-long session knows what "yesterday" means.
548
+ Agent-facing only, default off, persisted per topic.
549
+
550
+ ## Timezone
551
+
552
+ The bot runs on the host clock, which on a typical server is UTC. `/timezone`
553
+ declares yours ONCE, instance-wide, and every clock the bot touches follows:
554
+
555
+ - **Schedules** fire at your wall-clock time. Changing the zone PRESERVES the
556
+ wall clock — "9am" stays 9am rather than drifting by the offset — and every
557
+ job's next run is recomputed on the spot (an expired one-shot is dropped).
558
+ - **The agent** is told the current time and zone with each `/schedule` request,
559
+ and carries the zone in its thread-context preamble, so "remind me tomorrow at
560
+ 9" resolves against your clock instead of the model's assumption.
561
+ - **Time renders** — "missed at HH:MM", schedule descriptions, `/timestamps`
562
+ injection and the `schedule_list` next-run stamp all read in your zone.
563
+
564
+ ```
565
+ /timezone Europe/Moscow # IANA name
566
+ /timezone +04:00 # fixed offset (accepted; does not follow DST)
567
+ /timezone # region → zone picker
568
+ /timezone auto # back to the host zone
569
+ ```
570
+
571
+ The current zone and its wall clock also show in `/status`. Nothing is stored
572
+ until you set one, so an install that never runs `/timezone` behaves exactly as
573
+ it did before.
574
+
575
+ ## Environment Variables
576
+
577
+ ### Set Before Launch
578
+
579
+ | Variable | Description |
580
+ |---|---|
581
+ | `TELEGRAM_BOT_TOKEN` | Token from @BotFather |
582
+
583
+ Start from the parent folder containing your projects: `cd ~/projects && telegramcode`.
584
+ That `$PWD` is the folder the bot works in — nothing else to set for it.
585
+
586
+ **Access control.** There is no user allow-list. Whoever is a **creator or
587
+ administrator of the served forum group** may talk to the agent — read live from
588
+ Telegram (`getChatAdministrators`) and cached for 1h. Promote someone in the
589
+ group to grant access; demote/remove them to revoke it — the bot subscribes to
590
+ `chat_member` updates, so an admin change invalidates the cache and takes
591
+ effect immediately (the 1h TTL is only the fallback). The bot must be a group
592
+ admin itself (it already needs that to create topics and pin). Anonymous admins
593
+ can't be matched from their messages, so post non-anonymously. The DM surface
594
+ (when enabled) is gated separately: only the configured `OWNER_USER_ID` may
595
+ talk to the bot there.
596
+
597
+ > **Security model — group admin (or the DM owner) ⇒ shell on the host.** The agents run with
598
+ > permission checks disabled (`--dangerously-skip-permissions` for Claude,
599
+ > auto-approve for OpenCode) and `/terminal` opens a raw `$SHELL` in the bound
600
+ > folder, so anyone who can talk to the bot can execute arbitrary commands as
601
+ > the bot's OS user. Only promote people you would trust with SSH access to
602
+ > that machine, and keep the served group itself private.
603
+
604
+ ### External Optional
605
+
606
+ | Variable | Default | Description |
607
+ |---|---|---|
608
+ | `ALLOWED_GROUP_ID` | (auto-pair) | Numeric forum supergroup id (`-100…`). Leave empty to auto-pair with the first forum group a group **admin/creator** contacts the bot from (id is saved to `state.json`; re-point with `/pair`). A **name** is not accepted. A numeric value disables auto-pairing |
609
+ | `DATA_DIR` | `~/.telegramCode` | Per-instance state. **Mandatory** if you run two bots on the same host — otherwise both share `state.json` and `mcp.json` and corrupt each other |
610
+ | `CHAT_MODE` | `both` | Which surface(s) this instance serves: `group`, `dm`, or `both` — see [Two surfaces](#two-surfaces-group-topics-bot-dm-or-both) |
611
+ | `OWNER_USER_ID` | — | **Optional.** Numeric Telegram user id of the owner — leave it unset to run group-only (the DM surface stays inert; a group works fully without it). Set it only to enable the owner-DM surface; **required** solely for `CHAT_MODE=dm` |
612
+ | `GROQ_API_KEY` | — | Recommended for voice transcription. Without it, voice messages are not transcribed unless you intentionally configure the OpenAI fallback |
613
+
614
+ **Localized in 12 languages** — the bot UI (commands, buttons, notices) speaks
615
+ 中文, English, Français, ქართული, Deutsch, हिन्दी, 日本語, Português, Русский,
616
+ Español, Українська, Oʻzbekcha. The language is automatic per Telegram chat:
617
+ explicit `/language <locale>` override wins, then the sender's Telegram client
618
+ language, then the last supported Telegram locale seen for that chat, then
619
+ English.
620
+ Supported locales: `en`, `de`, `fr`, `es`, `pt`, `ru`, `zh`, `ja`, `hi`, `uz`,
621
+ `ka`, `uk`. Bare `/language` opens a single-message inline picker with each language
622
+ shown by its own name (endonym), sorted A→Z by English name, and a `🌐 Auto` button
623
+ (all 12 fit at once — no pagination). Tapping a language (or `🌐 Auto`) applies it, then the menu
624
+ disappears and a short confirmation stays in the chosen language;
625
+ `/language auto` returns a DM/group to automatic selection.
626
+
627
+ Agent provider/auth setup is normally done inside the agents themselves:
628
+ `claude login` for Claude CLI and `/connect <provider-id>` (for example,
629
+ `/connect openrouter`) or OpenCode's own config/plugins for OpenCode. No provider
630
+ API key env var is required by the bot for text sessions. For OpenCode, install
631
+ any third-party provider plugins or authentication resolvers before launch if
632
+ your chosen providers need them.
633
+
634
+ ### Advanced / Not Normally Needed
635
+
636
+ | Variable | Default | Set only when |
637
+ |---|---|---|
638
+ | `WORK_ROOT` | `$PWD` | You cannot control the process cwd; normal launch uses `cd <projects-parent> && telegramcode` |
639
+ | `OPENCODE_URL` | `http://localhost:4096` | You use a custom OpenCode port or external server; the port must differ per instance |
640
+ | `OPENCODE_USERNAME` | `opencode` | `OPENCODE_PASSWORD` is set for a protected OpenCode server |
641
+ | `OPENCODE_PASSWORD` | — | Connecting to a protected OpenCode server |
642
+ | `OPENCODE_ALLOW_REMOTE` | — | `OPENCODE_URL` intentionally points outside loopback |
643
+ | `OPENCODE_BIN` | (auto) | The `opencode` binary is not on PATH or you use a fork |
644
+ | `CLAUDE_BIN` | (auto) | The `claude` binary is not on PATH due to nvm/asdf/systemd PATH differences |
645
+ | `OPENAI_API_KEY` | — | You intentionally use OpenAI Whisper as the voice fallback instead of Groq |
646
+ | `ANTHROPIC_API_KEY` | — | A custom MCP/OpenCode plugin/auth resolver explicitly reads it; not needed for normal Claude CLI auth. The json-stream Claude backend strips it from the agent's env (subscription billing) |
647
+ | `SHELL` | `/bin/bash` | You want `/terminal` to open a different shell than your login shell (host env var, not set by the bot) |
648
+ | `SCHEDULER_MCP_PORT` | an OS-chosen free loopback port | You need a stable, explicitly chosen port; it must differ from this instance's `OPENCODE_URL` port |
649
+ | `CLAUDE_SCRAPE_DEBUG` | off | You are debugging Claude tmux scraping and need full RAW/FILTERED chunks |
650
+ | `AGENT_IDLE_MINUTES` | `55` | A json-stream Claude topic idle for this long is compacted (when `/compact_on_idle` allows) and its process stopped; the session sleeps and the next message resumes it — shorten it only for a test; a non-positive or non-numeric value keeps the default |
651
+ | `REQUEST_BACKSTOP_MINUTES` | `90` | A request (a Jira hand-over, a message in an answers-only topic) with nothing seen working on it for this long is re-posted to its session — shorten it only for a test; a non-positive or non-numeric value keeps the default |
652
+ | `TMUX_SOCKET_NAME` | — (the default tmux server) | The instance must run its agents on its OWN tmux server (`tmux -L <name>`; any plain name except `default`). Recommended for a second instance on one host: at boot an instance kills every agent session on its server that its own state does not know |
653
+ | `ENV_FILE` | — | Absolute path of the ONLY env file to read — no `~/.config/telegramcode/.env`, no `$PWD/.env`; its values win over the inherited environment. Set it in the launching environment, never inside an env file (the start refuses that). `scripts/run-isolated.sh <env-file>` starts an instance with a clean environment plus this variable |
654
+ | `CONNECTORS` | `telegram` | Experimental: the surfaces this instance serves, as a comma list — see [Jira connector](#jira-connector-experimental). An instance that lists `jira` refuses to start on Node below 22.12 and without `ENV_FILE`, `TMUX_SOCKET_NAME` and `DATA_DIR/jira.json`, and — with Telegram off — with a `TELEGRAM_BOT_TOKEN` or any `ATLASSIAN_*` variable in its environment. Any instance refuses to start on a `DATA_DIR` that holds conversations of a surface it does not serve |
655
+
656
+ > `WORK_DIR` (1.x) is retired. Use the wrapper from the desired parent folder
657
+ > instead of carrying the old env forward.
658
+
659
+ ## Jira connector (experimental)
660
+
661
+ The same core can serve **Jira Cloud issues** instead of (or next to) Telegram topics: an issue assigned to
662
+ a dedicated AI account becomes a conversation with an agent, and the agent answers in comments.
663
+
664
+ ```
665
+ a person assigns an issue to the AI account (in a trigger status)
666
+ → the instance polls Jira as the AI account and opens a request
667
+ → the agent works in the project's folder; its streamed output never reaches Jira
668
+ → it answers through the answer_request tool:
669
+ progress → a comment, the issue stays with the AI
670
+ question → a comment, the issue goes back to the person who handed it over
671
+ final → a comment, the issue goes back to that person
672
+ → an agent that stops answering is reminded; when the reminders give up, a
673
+ comment says a person has to look, and the issue goes back
674
+ ```
675
+
676
+ - **One issue = one conversation = one agent session**, bound to the folder its project maps to. The agent
677
+ reads the issue's text as information, never as instructions.
678
+ - **The agent gets the whole issue.** Every request's prompt carries the issue's fields, the full description,
679
+ its sub-tasks or an epic's children, links and remote links, an attachment list and ALL comments (oldest
680
+ first, restricted ones marked). A screenshot or video pasted into the description or a comment shows up as a
681
+ placeholder where it sat, naming its attachment. Nothing is cut: a comment over 10 000 characters, or any text
682
+ that would not fit the prompt, is written whole to a file in the conversation's folder under `DATA_DIR/files`
683
+ and the prompt says where. A later hand-over to the same conversation carries only what changed since the
684
+ agent's last prompt (new and edited comments, deleted ones named, the rest listed as unchanged); a fresh
685
+ session, or one whose context was compacted, gets the whole issue again.
686
+ - **Who gets the answer.** Each hand-over is a request from its sender. Two people handing one issue over in
687
+ turn get two answers, each their own comment; the first closing answer hands the issue back to its sender,
688
+ the second finds the issue no longer the AI's and leaves the assignee alone. The same person handing over
689
+ again replaces their earlier open request; the agent answers only what the new hand-over adds (if it
690
+ already finished the first, a short pointer to that answer).
691
+ - **Budget.** An issue handed over more than `runBudgetPer24h` times in 24 hours is handed back with a notice
692
+ and opens no request.
693
+
694
+ ### Setup
695
+
696
+ 1. Create a Jira account for the AI (its API token is the instance's only secret) and note its account id.
697
+ 2. An instance of its own: a `DATA_DIR`, a `WORK_ROOT` with one folder per project, a private tmux server, and
698
+ an env file that is the ONLY configuration it reads:
699
+
700
+ ```bash
701
+ # /path/to/jira-instance.env
702
+ CONNECTORS=jira
703
+ DATA_DIR=/path/to/jira-instance/data
704
+ WORK_ROOT=/path/to/jira-instance/work
705
+ TMUX_SOCKET_NAME=jira-instance
706
+ OPENCODE_URL=http://localhost:4097 # a port of its own — the default 4096 is refused (other instances use it)
707
+ JIRA_AI_API_TOKEN=... # referenced by jira.json below, never written there
708
+ ```
709
+
710
+ 3. `DATA_DIR/jira.json` — the allowlist and the credentials (`${VAR}` expands from the env file; an unknown
711
+ key is an error):
712
+
713
+ ```json
714
+ {
715
+ "site": "example.atlassian.net",
716
+ "email": "ai-account@example.com",
717
+ "apiToken": "${JIRA_AI_API_TOKEN}",
718
+ "accountId": "<the AI account's id>",
719
+ "projects": {
720
+ "PROJ": { "folder": "proj", "triggerStatuses": ["In Progress"], "extraFields": ["customfield_10042"] }
721
+ },
722
+ "pollIntervalSeconds": 90,
723
+ "runBudgetPer24h": 5,
724
+ "model": "opus",
725
+ "effort": "high"
726
+ }
727
+ ```
728
+
729
+ `projects` maps each served project key to its folder under `WORK_ROOT` and the status names in which an
730
+ assigned issue is a request; a folder with Claude memory (`CLAUDE.md`, `.claude/`, …) in it or above it is
731
+ refused, because the agent still loads project memory. `pollIntervalSeconds` is 10–600 (default 90);
732
+ `runBudgetPer24h` defaults to 5; `model` and `effort` set the sessions' Claude model and reasoning effort
733
+ (optional; absent → `opus` and `high`, either key overrides only its own default). `extraFields` (optional,
734
+ none by default) names fields by id (`customfield_…`, or a system one like `duedate`) the agent should see next to the standard
735
+ ones, shown under their site name; an id the site does not list is logged at start and left out. `agentBinaries` (optional, host runtime) maps a tool name to the absolute path of an executable — for
736
+ example `{ "ffmpeg": "/opt/tools/ffmpeg" }` — which is checked at start and put on the agents' PATH. `adapter` is `claude-json-stream` (the default — the agent process is stopped when an issue
737
+ idles) or `claude-per-turn` (stopped after every answer); either way the issue's conversation sleeps between
738
+ requests and the next request resumes it.
739
+
740
+ 4. Start it with a clean environment, so nothing of the calling shell reaches it:
741
+
742
+ ```bash
743
+ scripts/run-isolated.sh /path/to/jira-instance.env
744
+ ```
745
+
746
+ The start refuses a missing `ENV_FILE`, `TMUX_SOCKET_NAME` or `jira.json`, an `OPENCODE_URL` on the default
747
+ port, a `TELEGRAM_BOT_TOKEN` or any `ATLASSIAN_*` variable when Telegram is off, a token that belongs to
748
+ another account, an unknown project or status, and a `DATA_DIR` holding another surface's state. An unreachable Jira does not refuse the start —
749
+ polling backs off and resumes.
750
+
751
+ The agent sessions of a Jira instance run without the user-level Claude settings, hooks and skills, with the
752
+ bot's MCP server only, without the native question tool, and with an environment reduced to a short allowlist
753
+ — the Jira token is not in the agent's environment. The agent still runs as the instance's OS user and can read
754
+ the instance's env file and `DATA_DIR`, so run the instance under an OS user that holds nothing else, with an AI
755
+ account that sees only the allowlisted projects. **A production Jira account runs only in the Docker container**
756
+ ([Run with Docker](#run-with-docker)): there the agent can still read the instance's env file and `DATA_DIR`,
757
+ but nothing of the host outside the container's mounts. Module details: `src/connectors/jira/README.md`.
758
+
759
+ ## Bot-injected agent tools
760
+
761
+ The bot injects its own `telegramBot` MCP server into every bot-started session
762
+ — for Claude via a generated `--mcp-config`, for OpenCode via runtime
763
+ registration. It is loopback-only (`127.0.0.1`, on an OS-chosen free port unless
764
+ `SCHEDULER_MCP_PORT` explicitly pins one) and authenticated with per-session
765
+ HMAC bearer tokens scoped to the thread / directory. Every generated registration
766
+ also carries a unique client identity, so cancellation affects only the originating
767
+ agent even when two clients use the same JSON-RPC request id. It exposes:
768
+
769
+ - `schedule_create` / `schedule_list` / `schedule_cancel` — the agent-side
770
+ scheduling API behind `/schedule`;
771
+ - `jira_get_attachment` — Jira conversations only: saves the original of one attachment of the conversation's
772
+ own issue (any size) and returns its path, so the agent can read an image or run `ffmpeg` on a video;
773
+ - `compact_conversation` — lets the agent compact its OWN session when you ask it
774
+ to in plain words ("compact our conversation"). It arms the compaction and
775
+ returns immediately; the bot runs the real compaction the moment the current
776
+ turn finishes (never mid-turn);
777
+ - `send_file_to_user` — lets the agent push files from the bound folder into the
778
+ topic. A single MP4 uses Bot API `sendVideo`; eligible all-video and mixed
779
+ photo/video albums use `sendMediaGroup`, with each MP4 represented by an
780
+ `InputMediaVideo` entry. `as_file:true` explicitly forces documents. Secure
781
+ outbound file traversal currently requires Linux; on macOS this tool
782
+ fails closed until a native descriptor-relative bridge is available. Existing
783
+ path, count, caption, and size caps apply; canonical upload basenames have
784
+ control characters and quoted-string metacharacters replaced before they enter multipart headers. Cancelling
785
+ the MCP call removes queued work. While any upload stream is still being
786
+ consumed, cancellation destroys the streams and aborts Telegraf, then returns
787
+ `AbortError` after terminal cleanup. Once every request-body stream has ended,
788
+ cancellation is too late to abort safely: the operation awaits Telegram's
789
+ response, and any returned message IDs are durably recorded before success.
790
+ Directory-scoped
791
+ calls re-check the topic binding both before opening files and inside the
792
+ per-topic delivery queue immediately before dispatch, so a queued call cannot
793
+ continue after the topic is rebound elsewhere. Delivery and its durable
794
+ `/clear_messages` tracking update share one per-topic queue transaction; the
795
+ complete response-ID batch is on disk before success is reported. If Telegram
796
+ has accepted a delivery but local tracking/cleanup then fails, the tool returns
797
+ success with a warning and never retries. If the connection fails after an
798
+ upload starts and Telegram's response never arrives, the tool returns a
799
+ machine-readable, non-error `deliveryUnknown` outcome with `retryable:false`,
800
+ preventing accidental duplicates.
801
+ - `send_messages_to_user` — delivers a batch of DISCRETE messages into the topic,
802
+ each item posted as its OWN Telegram message in order (never merged) — for a
803
+ per-item digest and similar. Each item is either a plain text string OR an
804
+ object `{text?, path?, as_file?}` that may carry ONE attachment: a `path` item
805
+ reuses the SAME secure `send_file_to_user` pipeline (path relative to the bound
806
+ folder, supported photos inline, `.gif` as animation, `.mp4` as video, everything else a document,
807
+ `as_file` forces a document), with `text` used as the media caption (trimmed to
808
+ 1024 chars — send long prose as its own text-only item). Items are validated up
809
+ front (an empty object is rejected and nothing is sent); a total send failure
810
+ returns an error, a partial one stays successful but reports how many landed,
811
+ and an ambiguous attachment delivery surfaces as the same non-retryable
812
+ `deliveryUnknown` outcome as `send_file_to_user`. Capped at 50 items per call.
813
+
814
+ This server is bot-owned plumbing; if its port fails to bind, the bot still
815
+ boots — only these agent-facing tools go inert. (Your own MCP servers still work
816
+ independently: Claude auto-loads them from `~/.claude/settings.json` and a
817
+ project's `.mcp.json`; inspect what's active in a thread with `/mcp`.)
818
+
819
+ Third-party OpenCode MCP servers, including a separate `telegram-mcp`, belong to
820
+ the OpenCode configuration of the Linux user that runs the bot. They are not
821
+ injected by Charness; run `opencode mcp list` as that user to verify them.
822
+
823
+ ## Two instances on one host
824
+
825
+ Two host instances, or a host instance next to a container: each pair of variables below must differ to avoid
826
+ silent corruption. Two containers need only different tokens, groups, `DATA_DIR`s, container names and hostnames: their
827
+ ports and tmux servers are their own.
828
+
829
+ | What | Why it must differ |
830
+ |---|---|
831
+ | `TELEGRAM_BOT_TOKEN` | Telegram routes updates to a single long-poller per token; sharing → message loss |
832
+ | `ALLOWED_GROUP_ID` | The bot gates by group id; sharing → cross-instance leakage |
833
+ | `WORK_ROOT` | Each instance manages its subtree; sharing → tmux name collisions |
834
+ | `DATA_DIR` | `state.json` / `mcp.json` / `threads/` per instance; sharing → corrupted JSON |
835
+ | `OPENCODE_URL` port | OpenCode server binds the port; second start fails with `EADDRINUSE` and you'd silently share sessions |
836
+ | `SCHEDULER_MCP_PORT` | Optional stable scheduler-MCP port; if set, it must differ from that instance's `OPENCODE_URL` port |
837
+ | `TMUX_SOCKET_NAME` | Optional, recommended: each instance's boot kills the agent sessions on its tmux server that its own state does not know — on one shared server, the other instance's |
838
+
839
+ ## Updating a deployment (self-update, no root)
840
+
841
+ `scripts/self-update.sh` refreshes a checkout in place, running as the
842
+ checkout's owning user — no root, no cross-user copying. It is safe to run
843
+ blindly (e.g. from cron):
844
+
845
+ - fast-forward only: it skips silently when the tree has tracked changes, a
846
+ merge/rebase is in progress, or local history diverged from the upstream —
847
+ a dev clone that is ahead of origin is never touched;
848
+ - `yarn install --immutable` runs only when `yarn.lock` / `package.json`
849
+ changed;
850
+ - a hot-mode instance (`telegramcode hot`) rebuilds and restarts the worker
851
+ by itself (after a dependency install the script touches `tsconfig.json`
852
+ so `tsc -w` recompiles with the new modules); a non-hot instance gets
853
+ `yarn build` plus a restart notice — the script never kills processes;
854
+ - changes to the hot supervisor itself (`src/cli.ts`, `src/cli/hot.ts`,
855
+ `nodemon.json`) are flagged with a warning: those need a manual
856
+ `telegramcode hot` restart (nodemon reloads only the worker).
857
+
858
+ For unattended updates give the deploying user read access to the repo (e.g.
859
+ a read-only GitLab deploy key on a passphrase-less SSH key) and add a cron
860
+ entry:
861
+
862
+ ```cron
863
+ */10 * * * * /path/to/telegramcode/scripts/self-update.sh >> "$HOME/.local/state/telegramcode/self-update.log" 2>&1
864
+ ```
865
+
866
+ (create the log directory once: `mkdir -p ~/.local/state/telegramcode`).
867
+
868
+ ## Restart behaviour
869
+
870
+ Bot restarts are designed to be invisible: agents run in external
871
+ processes (`tmux`, `opencode serve`), so the bot re-adopts them instead
872
+ of killing them. In hot mode the long-lived supervisor starts the initial
873
+ `opencode serve` before `nodemon`, outside the replaceable worker's process
874
+ tree. If a worker later starts a replacement server after a crash, credential
875
+ reload, or late install, a one-shot host reparents that generation outside the
876
+ same tree before startup completes. An endpoint-bound process identity file
877
+ under `DATA_DIR` records a bot-started generation as `starting` before its host
878
+ releases it, then promotes it to `ready` after health succeeds. A successor can
879
+ therefore stop a pre-bind startup by its process-group identity, while `ready`
880
+ or adopted ownership is revalidated against the exact hostname and port before
881
+ a signal is sent, without trusting a reused PID or assuming an adopted listener
882
+ owns its process group.
883
+ A source rebuild therefore does not terminate an in-flight OpenCode turn.
884
+ The supervisor reads the same checkout `.env` as the worker before the initial
885
+ start. Hot mode supports Linux and macOS; it refuses to start on Windows because
886
+ nodemon cannot gracefully drain its worker tree there.
887
+ On boot the bot:
888
+
889
+ 1. Loads `state.json` (archives to `state.json.corrupted-<ts>` if parse
890
+ fails, then starts fresh and notifies in General) and classifies the
891
+ boot as a hot reload vs cold start from the persisted heartbeat gap.
892
+ 2. Re-adopts live sessions per backend:
893
+ - **Claude tmux-scrape** (`claude-…`) and **terminal** (`term-…`)
894
+ sessions still in `state.json` are re-attached; orphan bot-owned
895
+ tmux sessions are killed. The current pane seeds the baseline, so
896
+ nothing is re-flooded into the topic.
897
+ - **Claude json-stream** (`cjson-…`) runs as an external process
898
+ (stdin held on a FIFO, stdout appended to a file), so the restart
899
+ never kills it: the bot adopts it and replays the downtime tail
900
+ from the persisted offset — an in-flight turn is delivered
901
+ end-to-end, and a pending interactive question is restored from its
902
+ on-disk sidecar. A topic whose process is gone (stopped at the idle
903
+ mark, or after its turn in per-turn mode) is NOT restarted at boot:
904
+ it sleeps, and your next message resumes it. An adopted process
905
+ started under an earlier bot build keeps that build's bot tools, so it
906
+ is stopped once idle and resumed with the current ones.
907
+ - **OpenCode** threads re-resume their stored session ids over the
908
+ shared SSE stream. `opencode serve` itself is reconciled: a dead
909
+ server is auto-restarted and the sessions restored (the in-flight
910
+ reply is lost); a live server running an outdated binary is killed
911
+ and respawned on the current one.
912
+ 3. Re-arms pending API-error retries and scheduler timers; a run missed
913
+ during the downtime fires one catch-up annotated with the missed time.
914
+ A usage-limit wait that the previous process never recognised is also
915
+ picked up: for a Claude json-stream topic the bot re-reads the tail of
916
+ the session's output log and arms the resume itself (only for a limit
917
+ error younger than 12 hours, only when nothing is armed already, and
918
+ never the same one twice — a resume you skipped stays skipped).
919
+ 4. Stays silent on a quiet hot reload; a bounded recap of missed output
920
+ is posted only when the agent kept working while the bot was down.
921
+ 5. Schedules `setMyCommands` so the Telegram client picks up the menu.
922
+
923
+ Explicit `/quit`, `/quit-all`, and leaving a folder (the `/bind` «leave
924
+ current dir» button) release the persisted session ids — those sessions
925
+ are not auto-reattached on the next boot (they stay reachable via
926
+ `/sessions`).
927
+
928
+ Closed-but-not-deleted topics keep their binding; only `400: message
929
+ thread not found` from a send triggers binding cleanup. Closed topics
930
+ are detected by `TOPIC_CLOSED` errors and surface a friendly message
931
+ asking the user to reopen.
932
+
933
+ In the Docker container (see [Run with Docker](#run-with-docker)) the same holds for a bot restart
934
+ inside the container; a restart of the container itself ends every agent process, and each conversation
935
+ resumes with its next message.
936
+
937
+ If you run the bot under `systemd`/`systemd-run`, set `KillMode=process`.
938
+ The default `control-group` mode kills tmux and `opencode serve` children,
939
+ which defeats restart/reattach. Process restarts intentionally leave agent
940
+ sessions alive; use `/quit` or `/quit-all` when you want to terminate them.
941
+
942
+ ## Troubleshooting
943
+
944
+ ### Bot doesn't see text
945
+
946
+ You disabled privacy mode in @BotFather **after** adding the bot. The
947
+ flag is cached on join. Fix:
948
+
949
+ 1. Remove the bot from the group.
950
+ 2. (Re-confirm in @BotFather → `/setprivacy` → this bot → `Disable`.)
951
+ 3. Add the bot back.
952
+
953
+ Verify with `/doctor` — the privacy line should report it as disabled.
954
+
955
+ ### `/bind` says "folder not found"
956
+
957
+ The folder you launched from doesn't contain a subfolder with that name (or
958
+ it's a symlink pointing outside it). Run `/ls` to see what's available, or fix
959
+ the host path. Path-traversal attempts (`../`, absolute paths,
960
+ NUL bytes, NFC-vs-NFD mismatches) are rejected by design — see
961
+ `validateSubdir` in `src/validation.ts`.
962
+
963
+ ### "Missing right" / `/clear_messages` does nothing
964
+
965
+ The bot needs admin rights `can_delete_messages` and `can_pin_messages`
966
+ in addition to `can_manage_topics`. Open group settings → admins →
967
+ the bot → tick the missing boxes. `/doctor` shows the exact gap.
968
+
969
+ ### Two instances on the same machine fight each other
970
+
971
+ You forgot to set distinct `DATA_DIR` and `OPENCODE_URL`. The bot logs
972
+ `[startup] DATA_DIR=<path>` on boot — if both instances log the same
973
+ path, fix `.env` and `docker compose down && up -d`.
974
+
975
+ ### Claude opens a session picker instead of resuming
976
+
977
+ The 1.x adapter invoked `claude --resume` with no UUID and relied on the
978
+ interactive picker, which doesn't survive a non-interactive pty. 2.0
979
+ generates a UUID with `crypto.randomUUID()`, passes it via
980
+ `--session-id`, and resumes via `--resume <uuid>`. If you see a picker
981
+ anyway, your local claude CLI is older than 2.1.x — upgrade with
982
+ `npm i -g @anthropic-ai/claude-code` (the bot also auto-installs on
983
+ first use, see `src/installManager.ts`).
984
+
985
+ ### OpenCode "address already in use"
986
+
987
+ Two instances on the same host with the same `OPENCODE_URL`. Pick a
988
+ different port for the second instance (e.g. `4097`).
989
+
990
+ ## FAQ
991
+
992
+ **How do I run Claude Code from my phone?**
993
+ Install Charness on your own machine or VPS, create a Telegram bot, and bind a forum topic (or the bot's DM) to a project folder. Start a Claude Code session in that topic and you drive it entirely from Telegram: type prompts, send voice notes, or drop in files, and the agent's output streams back into the same topic. Your phone never runs the agent; it stays on your machine.
994
+
995
+ **Can I control OpenCode without opening any ports?**
996
+ Yes. The bot only makes outbound connections to Telegram, so nothing listens for inbound traffic and you never expose a port. OpenCode runs as a local HTTP server that the bot talks to on loopback, and you reach it from anywhere through Telegram.
997
+
998
+ **How do I run a coding agent on a VPS and drive it from Telegram?**
999
+ Set up your VPS once with git, your repos, the agent CLIs, and `telegramcode`, then launch the bot from your projects folder. You get an always-on dev box you drive from any device: phone, tablet, or voice. Because the agent lives on the server, it keeps working even when your laptop sleeps, and the same sessions are still reachable over plain SSH.
1000
+
1001
+ **Does my session survive a restart?**
1002
+ Yes, both a bot restart and an agent-server restart. State is persisted to disk; on restart the bot re-attaches to the running Claude session in tmux or re-connects to OpenCode, and an in-flight turn is delivered once it is back. A prompt sent during startup is buffered and replayed, never dropped.
1003
+
1004
+ **Does it run both Claude Code and OpenCode?**
1005
+ Yes. Each topic picks its backend independently: OpenCode over its native HTTP+SSE server, or Claude Code (tmux or json-stream). One topic is one project with its own isolated session, like terminal tabs, and two topics can point at the same folder for parallel work.
1006
+
1007
+ **Can I talk to the agent by voice?**
1008
+ Yes. Send a voice message to a topic and it is transcribed with Whisper (via Groq or OpenAI) and forwarded to the agent as a normal prompt, so you can vibe code hands-free from your phone.
1009
+
1010
+ ## Documentation
1011
+
1012
+ - **[DEVELOPMENT.md](DEVELOPMENT.md)** — architecture and local development (build, test, hot-reload, Docker dev loop).
1013
+ - **[MIGRATION_1_TO_2.md](MIGRATION_1_TO_2.md)** — upgrading a 1.x install to 2.0.
1014
+
1015
+ ## The story behind it
1016
+
1017
+ I started running Claude Code on my own machine, and I kept wanting to check on it after I stepped away from the desk. Back then there was no way to reach it from a phone, so I put together a small bot that launched the agent and let me talk to it over Telegram. It ran on my own computer, which had to stay on the whole time.
1018
+
1019
+ Then I moved the whole thing onto a server that never sleeps, added OpenCode next to Claude Code, and it grew into what it is now: voice control, file intake, scheduling, a raw terminal, and sessions that survive restarts. These days I run it every day and drive everything from my phone or tablet, no laptop required.
1020
+
1021
+ Full write-up: [Vibe-coding by voice, from my phone](https://dev.to/olosegres/vibe-coding-by-voice-from-my-phone-claude-code-and-opencode-over-telegram-j75).
1022
+
1023
+ ## License
1024
+
1025
+ MIT