charness 3.2.0 → 3.3.1

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 (313) hide show
  1. package/.env.example +3 -6
  2. package/DEVELOPMENT.md +15 -0
  3. package/README.md +111 -38
  4. package/SETUP.md +121 -16
  5. package/dist/adapters/adapterEventRouting.d.ts +2 -1
  6. package/dist/adapters/adapterEventRouting.d.ts.map +1 -1
  7. package/dist/adapters/adapterEventRouting.js +3 -0
  8. package/dist/adapters/adapterEventRouting.js.map +1 -1
  9. package/dist/adapters/adapterNames.d.ts +13 -0
  10. package/dist/adapters/adapterNames.d.ts.map +1 -1
  11. package/dist/adapters/adapterNames.js +16 -0
  12. package/dist/adapters/adapterNames.js.map +1 -1
  13. package/dist/adapters/backendUnavailableError.d.ts +11 -0
  14. package/dist/adapters/backendUnavailableError.d.ts.map +1 -0
  15. package/dist/adapters/backendUnavailableError.js +19 -0
  16. package/dist/adapters/backendUnavailableError.js.map +1 -0
  17. package/dist/adapters/claudeCliAdapter.d.ts +29 -6
  18. package/dist/adapters/claudeCliAdapter.d.ts.map +1 -1
  19. package/dist/adapters/claudeCliAdapter.js +70 -16
  20. package/dist/adapters/claudeCliAdapter.js.map +1 -1
  21. package/dist/adapters/claudeJsonStreamAdapter.d.ts +36 -3
  22. package/dist/adapters/claudeJsonStreamAdapter.d.ts.map +1 -1
  23. package/dist/adapters/claudeJsonStreamAdapter.js +93 -30
  24. package/dist/adapters/claudeJsonStreamAdapter.js.map +1 -1
  25. package/dist/adapters/createAdapter.d.ts +3 -14
  26. package/dist/adapters/createAdapter.d.ts.map +1 -1
  27. package/dist/adapters/createAdapter.js +9 -19
  28. package/dist/adapters/createAdapter.js.map +1 -1
  29. package/dist/adapters/openCodeAdapter.d.ts +140 -12
  30. package/dist/adapters/openCodeAdapter.d.ts.map +1 -1
  31. package/dist/adapters/openCodeAdapter.js +385 -119
  32. package/dist/adapters/openCodeAdapter.js.map +1 -1
  33. package/dist/apiErrorRetry.d.ts +70 -6
  34. package/dist/apiErrorRetry.d.ts.map +1 -1
  35. package/dist/apiErrorRetry.js +312 -34
  36. package/dist/apiErrorRetry.js.map +1 -1
  37. package/dist/apiRetryKick.d.ts +26 -1
  38. package/dist/apiRetryKick.d.ts.map +1 -1
  39. package/dist/apiRetryKick.js +41 -2
  40. package/dist/apiRetryKick.js.map +1 -1
  41. package/dist/bot.d.ts +2 -1
  42. package/dist/bot.d.ts.map +1 -1
  43. package/dist/bot.js +736 -184
  44. package/dist/bot.js.map +1 -1
  45. package/dist/cli/claudeProfiles.d.ts +1 -1
  46. package/dist/cli/claudeProfiles.js +1 -1
  47. package/dist/cli/hot.d.ts +7 -1
  48. package/dist/cli/hot.d.ts.map +1 -1
  49. package/dist/cli/hot.js +29 -3
  50. package/dist/cli/hot.js.map +1 -1
  51. package/dist/configDirFile.d.ts +73 -0
  52. package/dist/configDirFile.d.ts.map +1 -0
  53. package/dist/configDirFile.js +216 -0
  54. package/dist/configDirFile.js.map +1 -0
  55. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts +2 -2
  56. package/dist/connectors/telegram/commands/autoContinueLimits.d.ts.map +1 -1
  57. package/dist/connectors/telegram/commands/autoContinueLimits.js +4 -4
  58. package/dist/connectors/telegram/commands/autoContinueLimits.js.map +1 -1
  59. package/dist/connectors/telegram/commands/backendSwitch.d.ts +25 -0
  60. package/dist/connectors/telegram/commands/backendSwitch.d.ts.map +1 -0
  61. package/dist/connectors/telegram/commands/backendSwitch.js +104 -0
  62. package/dist/connectors/telegram/commands/backendSwitch.js.map +1 -0
  63. package/dist/connectors/telegram/commands/callbackMessage.d.ts +12 -0
  64. package/dist/connectors/telegram/commands/callbackMessage.d.ts.map +1 -1
  65. package/dist/connectors/telegram/commands/callbackMessage.js +24 -0
  66. package/dist/connectors/telegram/commands/callbackMessage.js.map +1 -1
  67. package/dist/connectors/telegram/commands/callbackToast.d.ts +36 -0
  68. package/dist/connectors/telegram/commands/callbackToast.d.ts.map +1 -0
  69. package/dist/connectors/telegram/commands/callbackToast.js +83 -0
  70. package/dist/connectors/telegram/commands/callbackToast.js.map +1 -0
  71. package/dist/connectors/telegram/commands/claudeAccounts.d.ts +18 -0
  72. package/dist/connectors/telegram/commands/claudeAccounts.d.ts.map +1 -1
  73. package/dist/connectors/telegram/commands/claudeAccounts.js +220 -66
  74. package/dist/connectors/telegram/commands/claudeAccounts.js.map +1 -1
  75. package/dist/connectors/telegram/commands/displayModes.d.ts.map +1 -1
  76. package/dist/connectors/telegram/commands/displayModes.js +4 -2
  77. package/dist/connectors/telegram/commands/displayModes.js.map +1 -1
  78. package/dist/connectors/telegram/commands/fallbackChain.d.ts +5 -8
  79. package/dist/connectors/telegram/commands/fallbackChain.d.ts.map +1 -1
  80. package/dist/connectors/telegram/commands/fallbackChain.js +183 -193
  81. package/dist/connectors/telegram/commands/fallbackChain.js.map +1 -1
  82. package/dist/connectors/telegram/commands/limitsBoard.d.ts +40 -0
  83. package/dist/connectors/telegram/commands/limitsBoard.d.ts.map +1 -0
  84. package/dist/connectors/telegram/commands/limitsBoard.js +281 -0
  85. package/dist/connectors/telegram/commands/limitsBoard.js.map +1 -0
  86. package/dist/connectors/telegram/commands/modelPage.d.ts +130 -0
  87. package/dist/connectors/telegram/commands/modelPage.d.ts.map +1 -0
  88. package/dist/connectors/telegram/commands/modelPage.js +120 -0
  89. package/dist/connectors/telegram/commands/modelPage.js.map +1 -0
  90. package/dist/connectors/telegram/commands/modelProviders.d.ts +9 -1
  91. package/dist/connectors/telegram/commands/modelProviders.d.ts.map +1 -1
  92. package/dist/connectors/telegram/commands/modelProviders.js +203 -111
  93. package/dist/connectors/telegram/commands/modelProviders.js.map +1 -1
  94. package/dist/connectors/telegram/commands/stt.d.ts +32 -0
  95. package/dist/connectors/telegram/commands/stt.d.ts.map +1 -0
  96. package/dist/connectors/telegram/commands/stt.js +460 -0
  97. package/dist/connectors/telegram/commands/stt.js.map +1 -0
  98. package/dist/effortLevels.d.ts +57 -7
  99. package/dist/effortLevels.d.ts.map +1 -1
  100. package/dist/effortLevels.js +65 -9
  101. package/dist/effortLevels.js.map +1 -1
  102. package/dist/i18n/de.d.ts.map +1 -1
  103. package/dist/i18n/de.js +160 -17
  104. package/dist/i18n/de.js.map +1 -1
  105. package/dist/i18n/en.d.ts.map +1 -1
  106. package/dist/i18n/en.js +160 -17
  107. package/dist/i18n/en.js.map +1 -1
  108. package/dist/i18n/es.d.ts.map +1 -1
  109. package/dist/i18n/es.js +160 -17
  110. package/dist/i18n/es.js.map +1 -1
  111. package/dist/i18n/fr.d.ts.map +1 -1
  112. package/dist/i18n/fr.js +160 -17
  113. package/dist/i18n/fr.js.map +1 -1
  114. package/dist/i18n/hi.d.ts.map +1 -1
  115. package/dist/i18n/hi.js +160 -17
  116. package/dist/i18n/hi.js.map +1 -1
  117. package/dist/i18n/ja.d.ts.map +1 -1
  118. package/dist/i18n/ja.js +160 -17
  119. package/dist/i18n/ja.js.map +1 -1
  120. package/dist/i18n/ka.d.ts.map +1 -1
  121. package/dist/i18n/ka.js +160 -17
  122. package/dist/i18n/ka.js.map +1 -1
  123. package/dist/i18n/pt.d.ts.map +1 -1
  124. package/dist/i18n/pt.js +160 -17
  125. package/dist/i18n/pt.js.map +1 -1
  126. package/dist/i18n/ru.d.ts.map +1 -1
  127. package/dist/i18n/ru.js +160 -17
  128. package/dist/i18n/ru.js.map +1 -1
  129. package/dist/i18n/uk.d.ts.map +1 -1
  130. package/dist/i18n/uk.js +160 -17
  131. package/dist/i18n/uk.js.map +1 -1
  132. package/dist/i18n/uz.d.ts.map +1 -1
  133. package/dist/i18n/uz.js +160 -17
  134. package/dist/i18n/uz.js.map +1 -1
  135. package/dist/i18n/zh.d.ts.map +1 -1
  136. package/dist/i18n/zh.js +160 -17
  137. package/dist/i18n/zh.js.map +1 -1
  138. package/dist/installManager.d.ts +20 -4
  139. package/dist/installManager.d.ts.map +1 -1
  140. package/dist/installManager.js +82 -14
  141. package/dist/installManager.js.map +1 -1
  142. package/dist/instanceSettings.d.ts +52 -0
  143. package/dist/instanceSettings.d.ts.map +1 -0
  144. package/dist/instanceSettings.js +136 -0
  145. package/dist/instanceSettings.js.map +1 -0
  146. package/dist/pinnedStatus.d.ts +7 -1
  147. package/dist/pinnedStatus.d.ts.map +1 -1
  148. package/dist/pinnedStatus.js +1 -1
  149. package/dist/pinnedStatus.js.map +1 -1
  150. package/dist/requests/requestLedger.d.ts +8 -0
  151. package/dist/requests/requestLedger.d.ts.map +1 -1
  152. package/dist/requests/requestLedger.js +19 -1
  153. package/dist/requests/requestLedger.js.map +1 -1
  154. package/dist/scheduler/mcpSurface.js +4 -4
  155. package/dist/scheduler/mcpSurface.js.map +1 -1
  156. package/dist/shutdown.d.ts +14 -0
  157. package/dist/shutdown.d.ts.map +1 -1
  158. package/dist/shutdown.js +16 -0
  159. package/dist/shutdown.js.map +1 -1
  160. package/dist/state.d.ts +54 -2
  161. package/dist/state.d.ts.map +1 -1
  162. package/dist/state.js +62 -1
  163. package/dist/state.js.map +1 -1
  164. package/dist/stt/sttAutoInstall.d.ts +39 -0
  165. package/dist/stt/sttAutoInstall.d.ts.map +1 -0
  166. package/dist/stt/sttAutoInstall.js +158 -0
  167. package/dist/stt/sttAutoInstall.js.map +1 -0
  168. package/dist/stt/sttChildEnv.d.ts +11 -0
  169. package/dist/stt/sttChildEnv.d.ts.map +1 -0
  170. package/dist/stt/sttChildEnv.js +30 -0
  171. package/dist/stt/sttChildEnv.js.map +1 -0
  172. package/dist/stt/sttConfigEdit.d.ts +71 -0
  173. package/dist/stt/sttConfigEdit.d.ts.map +1 -0
  174. package/dist/stt/sttConfigEdit.js +144 -0
  175. package/dist/stt/sttConfigEdit.js.map +1 -0
  176. package/dist/stt/sttInstall.d.ts +147 -0
  177. package/dist/stt/sttInstall.d.ts.map +1 -0
  178. package/dist/stt/sttInstall.js +491 -0
  179. package/dist/stt/sttInstall.js.map +1 -0
  180. package/dist/stt/sttInstallEntry.d.ts +2 -0
  181. package/dist/stt/sttInstallEntry.d.ts.map +1 -0
  182. package/dist/stt/sttInstallEntry.js +31 -0
  183. package/dist/stt/sttInstallEntry.js.map +1 -0
  184. package/dist/stt/sttInstallReporter.d.ts +45 -0
  185. package/dist/stt/sttInstallReporter.d.ts.map +1 -0
  186. package/dist/stt/sttInstallReporter.js +57 -0
  187. package/dist/stt/sttInstallReporter.js.map +1 -0
  188. package/dist/stt/sttRuntime.d.ts +35 -0
  189. package/dist/stt/sttRuntime.d.ts.map +1 -0
  190. package/dist/stt/sttRuntime.js +73 -0
  191. package/dist/stt/sttRuntime.js.map +1 -0
  192. package/dist/stt/sttScreenPlan.d.ts +193 -0
  193. package/dist/stt/sttScreenPlan.d.ts.map +1 -0
  194. package/dist/stt/sttScreenPlan.js +541 -0
  195. package/dist/stt/sttScreenPlan.js.map +1 -0
  196. package/dist/stt/sttSettings.d.ts +201 -0
  197. package/dist/stt/sttSettings.d.ts.map +1 -0
  198. package/dist/stt/sttSettings.js +387 -0
  199. package/dist/stt/sttSettings.js.map +1 -0
  200. package/dist/stt/sttSidecarOwner.d.ts +115 -0
  201. package/dist/stt/sttSidecarOwner.d.ts.map +1 -0
  202. package/dist/stt/sttSidecarOwner.js +475 -0
  203. package/dist/stt/sttSidecarOwner.js.map +1 -0
  204. package/dist/stt/transcribeChain.d.ts +36 -0
  205. package/dist/stt/transcribeChain.d.ts.map +1 -0
  206. package/dist/stt/transcribeChain.js +42 -0
  207. package/dist/stt/transcribeChain.js.map +1 -0
  208. package/dist/types.d.ts +77 -4
  209. package/dist/types.d.ts.map +1 -1
  210. package/dist/utils/adapterSwitchSessionIds.d.ts +32 -0
  211. package/dist/utils/adapterSwitchSessionIds.d.ts.map +1 -0
  212. package/dist/utils/adapterSwitchSessionIds.js +44 -0
  213. package/dist/utils/adapterSwitchSessionIds.js.map +1 -0
  214. package/dist/utils/backendSwitchPlan.d.ts +63 -0
  215. package/dist/utils/backendSwitchPlan.d.ts.map +1 -0
  216. package/dist/utils/backendSwitchPlan.js +73 -0
  217. package/dist/utils/backendSwitchPlan.js.map +1 -0
  218. package/dist/utils/claudeAccountPickerPlan.d.ts +12 -1
  219. package/dist/utils/claudeAccountPickerPlan.d.ts.map +1 -1
  220. package/dist/utils/claudeAccountPickerPlan.js +20 -1
  221. package/dist/utils/claudeAccountPickerPlan.js.map +1 -1
  222. package/dist/utils/claudeTranscriptContent.d.ts +24 -0
  223. package/dist/utils/claudeTranscriptContent.d.ts.map +1 -0
  224. package/dist/utils/claudeTranscriptContent.js +107 -0
  225. package/dist/utils/claudeTranscriptContent.js.map +1 -0
  226. package/dist/utils/claudeUsage.d.ts +47 -8
  227. package/dist/utils/claudeUsage.d.ts.map +1 -1
  228. package/dist/utils/claudeUsage.js +76 -14
  229. package/dist/utils/claudeUsage.js.map +1 -1
  230. package/dist/utils/claudeUsageRefresher.d.ts +8 -1
  231. package/dist/utils/claudeUsageRefresher.d.ts.map +1 -1
  232. package/dist/utils/claudeUsageRefresher.js +9 -4
  233. package/dist/utils/claudeUsageRefresher.js.map +1 -1
  234. package/dist/utils/effortPickerPlan.d.ts +54 -0
  235. package/dist/utils/effortPickerPlan.d.ts.map +1 -0
  236. package/dist/utils/effortPickerPlan.js +56 -0
  237. package/dist/utils/effortPickerPlan.js.map +1 -0
  238. package/dist/utils/ensureSessionPlan.d.ts +20 -4
  239. package/dist/utils/ensureSessionPlan.d.ts.map +1 -1
  240. package/dist/utils/ensureSessionPlan.js +17 -4
  241. package/dist/utils/ensureSessionPlan.js.map +1 -1
  242. package/dist/utils/fallbackBridge.d.ts +6 -2
  243. package/dist/utils/fallbackBridge.d.ts.map +1 -1
  244. package/dist/utils/fallbackBridge.js +10 -3
  245. package/dist/utils/fallbackBridge.js.map +1 -1
  246. package/dist/utils/fallbackChain.d.ts +36 -0
  247. package/dist/utils/fallbackChain.d.ts.map +1 -1
  248. package/dist/utils/fallbackChain.js +65 -1
  249. package/dist/utils/fallbackChain.js.map +1 -1
  250. package/dist/utils/fallbackSwitchNotice.d.ts +41 -0
  251. package/dist/utils/fallbackSwitchNotice.d.ts.map +1 -0
  252. package/dist/utils/fallbackSwitchNotice.js +75 -0
  253. package/dist/utils/fallbackSwitchNotice.js.map +1 -0
  254. package/dist/utils/fileSendService.d.ts +33 -10
  255. package/dist/utils/fileSendService.d.ts.map +1 -1
  256. package/dist/utils/fileSendService.js +78 -27
  257. package/dist/utils/fileSendService.js.map +1 -1
  258. package/dist/utils/jsonStreamHost.d.ts +39 -0
  259. package/dist/utils/jsonStreamHost.d.ts.map +1 -1
  260. package/dist/utils/jsonStreamHost.js +60 -1
  261. package/dist/utils/jsonStreamHost.js.map +1 -1
  262. package/dist/utils/limitEpisodeRecovery.d.ts +16 -2
  263. package/dist/utils/limitEpisodeRecovery.d.ts.map +1 -1
  264. package/dist/utils/limitEpisodeRecovery.js +44 -3
  265. package/dist/utils/limitEpisodeRecovery.js.map +1 -1
  266. package/dist/utils/limitSettings.d.ts +25 -0
  267. package/dist/utils/limitSettings.d.ts.map +1 -0
  268. package/dist/utils/limitSettings.js +31 -0
  269. package/dist/utils/limitSettings.js.map +1 -0
  270. package/dist/utils/limitsBoardPlan.d.ts +52 -0
  271. package/dist/utils/limitsBoardPlan.d.ts.map +1 -0
  272. package/dist/utils/limitsBoardPlan.js +93 -0
  273. package/dist/utils/limitsBoardPlan.js.map +1 -0
  274. package/dist/utils/openCodeUsage.d.ts +93 -0
  275. package/dist/utils/openCodeUsage.d.ts.map +1 -0
  276. package/dist/utils/openCodeUsage.js +248 -0
  277. package/dist/utils/openCodeUsage.js.map +1 -0
  278. package/dist/utils/operatorSwitchPlan.d.ts +61 -0
  279. package/dist/utils/operatorSwitchPlan.d.ts.map +1 -0
  280. package/dist/utils/operatorSwitchPlan.js +32 -0
  281. package/dist/utils/operatorSwitchPlan.js.map +1 -0
  282. package/dist/utils/promptDelivery.d.ts +40 -0
  283. package/dist/utils/promptDelivery.d.ts.map +1 -1
  284. package/dist/utils/promptDelivery.js +31 -0
  285. package/dist/utils/promptDelivery.js.map +1 -1
  286. package/dist/utils/recvPreviewRedaction.d.ts +14 -4
  287. package/dist/utils/recvPreviewRedaction.d.ts.map +1 -1
  288. package/dist/utils/recvPreviewRedaction.js +19 -7
  289. package/dist/utils/recvPreviewRedaction.js.map +1 -1
  290. package/dist/utils/startupReadiness.d.ts +3 -3
  291. package/dist/utils/startupReadiness.d.ts.map +1 -1
  292. package/dist/utils/startupReadiness.js +2 -2
  293. package/dist/utils/startupReadiness.js.map +1 -1
  294. package/dist/utils/threadStatusReport.d.ts +3 -1
  295. package/dist/utils/threadStatusReport.d.ts.map +1 -1
  296. package/dist/utils/threadStatusReport.js +4 -3
  297. package/dist/utils/threadStatusReport.js.map +1 -1
  298. package/dist/utils/timezone.d.ts +23 -0
  299. package/dist/utils/timezone.d.ts.map +1 -1
  300. package/dist/utils/timezone.js +38 -0
  301. package/dist/utils/timezone.js.map +1 -1
  302. package/dist/utils/transcribeAudio.d.ts +25 -12
  303. package/dist/utils/transcribeAudio.d.ts.map +1 -1
  304. package/dist/utils/transcribeAudio.js +39 -21
  305. package/dist/utils/transcribeAudio.js.map +1 -1
  306. package/dist/utils/usageRefreshBudget.d.ts +9 -0
  307. package/dist/utils/usageRefreshBudget.d.ts.map +1 -0
  308. package/dist/utils/usageRefreshBudget.js +27 -0
  309. package/dist/utils/usageRefreshBudget.js.map +1 -0
  310. package/examples/.env.example +7 -3
  311. package/examples/docker-compose.yml +9 -7
  312. package/package.json +5 -4
  313. package/stt/gigaam_sidecar.py +800 -0
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBackendSwitch = createBackendSwitch;
4
+ /**
5
+ * The question before a MANUAL switch of a topic between the two agent backends, Claude and OpenCode
6
+ * (`utils/backendSwitchPlan.ts` decides when it is asked): «Switch this topic from OpenCode to Claude «work»? The
7
+ * conversation context will be handed over in compacted form.» with ✅ Continue / ✖️ Cancel. It is a one-shot
8
+ * keyboard: a tap consumes it (the message is edited into the outcome, the keyboard dropped), and an old one — the
9
+ * topic changed backend meanwhile, a newer question replaced it, the bot restarted — only says it is out of date.
10
+ * The switch itself is a port (`bot.ts`: the cross-backend move of the usage-limit switch, with the context
11
+ * bridge as the first prompt). See README.md in this folder.
12
+ */
13
+ const telegraf_1 = require("telegraf");
14
+ const sessionKey_1 = require("../../../sessionKey");
15
+ const i18n_1 = require("../../../i18n");
16
+ const backendSwitchPlan_1 = require("../../../utils/backendSwitchPlan");
17
+ const callbackMessage_1 = require("./callbackMessage");
18
+ /** Questions whose snapshot is kept (the newest); an older keyboard is refused as out of date. */
19
+ const pendingQuestionLimit = 20;
20
+ /** @description Build the question and its buttons over their ports. */
21
+ function createBackendSwitch(ports) {
22
+ const { bot, replyToThread, editThreadMessage, authoriseContext, withThreadLocale, getCurrentFamily, performBackendSwitch } = ports;
23
+ /** Per MESSAGE, keyed by chat + message id (`buildMessageSnapshotKey`): a message id is unique only within one chat. */
24
+ const pendingByMessage = new Map();
25
+ /** The one open question per topic (its snapshot key): a newer question retires the older one's snapshot. */
26
+ const snapshotKeyByTopic = new Map();
27
+ function forget(snapshotKey) {
28
+ const pending = pendingByMessage.get(snapshotKey);
29
+ pendingByMessage.delete(snapshotKey);
30
+ if (pending && snapshotKeyByTopic.get(pending.topicKey) === snapshotKey)
31
+ snapshotKeyByTopic.delete(pending.topicKey);
32
+ }
33
+ /**
34
+ * @description Ask whether to switch `key` from `from` to `target`. Posts the question with its keyboard and
35
+ * remembers it; the switch waits for the answer. A send that failed leaves nothing to remember.
36
+ */
37
+ async function askBackendSwitch(key, request) {
38
+ const keyboard = telegraf_1.Markup.inlineKeyboard([[
39
+ telegraf_1.Markup.button.callback((0, i18n_1.t)('handover.continueButton'), backendSwitchPlan_1.backendSwitchContinueCallback),
40
+ telegraf_1.Markup.button.callback((0, i18n_1.t)('handover.cancelButton'), backendSwitchPlan_1.backendSwitchCancelCallback),
41
+ ]]);
42
+ const messageId = await replyToThread(key, (0, backendSwitchPlan_1.buildBackendSwitchQuestion)(request), keyboard);
43
+ if (messageId === null)
44
+ return;
45
+ const topicKey = (0, sessionKey_1.keyToString)(key);
46
+ const snapshotKey = (0, callbackMessage_1.buildMessageSnapshotKey)(key, messageId);
47
+ const older = snapshotKeyByTopic.get(topicKey);
48
+ if (older !== undefined)
49
+ pendingByMessage.delete(older);
50
+ pendingByMessage.set(snapshotKey, { topicKey, from: request.from, target: request.target, userMessage: request.userMessage });
51
+ snapshotKeyByTopic.set(topicKey, snapshotKey);
52
+ while (pendingByMessage.size > pendingQuestionLimit) {
53
+ const oldest = pendingByMessage.keys().next().value;
54
+ if (oldest === undefined)
55
+ break;
56
+ forget(oldest);
57
+ }
58
+ }
59
+ async function handleTap(ctx, run) {
60
+ const key = await authoriseContext(ctx);
61
+ if (!key) {
62
+ await ctx.answerCbQuery((0, i18n_1.t)('cb.access_denied'));
63
+ return;
64
+ }
65
+ await withThreadLocale(key, () => run(key, (0, callbackMessage_1.getCallbackMessageId)(ctx)));
66
+ }
67
+ function registerBackendSwitchCallbacks() {
68
+ bot.action(backendSwitchPlan_1.backendSwitchCallbackRe, (ctx) => handleTap(ctx, async (key, messageId) => {
69
+ // A button with no message to resolve a snapshot against (a very old message) is out of date by definition.
70
+ if (messageId === null) {
71
+ await ctx.answerCbQuery((0, i18n_1.t)('backendSwitch.stale'));
72
+ return;
73
+ }
74
+ const snapshotKey = (0, callbackMessage_1.buildMessageSnapshotKey)(key, messageId);
75
+ const pending = pendingByMessage.get(snapshotKey);
76
+ // Out of date: nothing remembered (an older question, a restart), another topic's, or the topic is no longer on
77
+ // the backend it was asked from. The keyboard is consumed all the same.
78
+ if (!pending || pending.topicKey !== (0, sessionKey_1.keyToString)(key) || getCurrentFamily(key) !== pending.from) {
79
+ await ctx.answerCbQuery((0, i18n_1.t)('backendSwitch.stale'));
80
+ forget(snapshotKey);
81
+ await editThreadMessage(key, messageId, (0, i18n_1.t)('backendSwitch.stale'));
82
+ return;
83
+ }
84
+ forget(snapshotKey);
85
+ await ctx.answerCbQuery();
86
+ const to = (0, backendSwitchPlan_1.formatBackendSwitchTarget)(pending.target);
87
+ if (ctx.match[0] === backendSwitchPlan_1.backendSwitchCancelCallback) {
88
+ await editThreadMessage(key, messageId, (0, i18n_1.t)('backendSwitch.cancelled', { from: (0, backendSwitchPlan_1.formatBackendFamily)(pending.from) }));
89
+ return;
90
+ }
91
+ await editThreadMessage(key, messageId, (0, i18n_1.t)('backendSwitch.switching', { to }));
92
+ try {
93
+ await performBackendSwitch(key, pending.target, { userMessage: pending.userMessage });
94
+ await editThreadMessage(key, messageId, (0, i18n_1.t)('backendSwitch.switched', { to }));
95
+ }
96
+ catch (e) {
97
+ console.error(`[backendSwitch] ${(0, sessionKey_1.keyToString)(key)}: the switch to ${to} failed:`, e instanceof Error ? e.message : e);
98
+ await editThreadMessage(key, messageId, (0, i18n_1.t)('backendSwitch.failed', { to, error: e instanceof Error ? e.message : 'unknown error' }));
99
+ }
100
+ }));
101
+ }
102
+ return { askBackendSwitch, registerBackendSwitchCallbacks };
103
+ }
104
+ //# sourceMappingURL=backendSwitch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backendSwitch.js","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/backendSwitch.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  * The message a callback button sits on.
3
3
  */
4
4
  import type { Context } from 'telegraf';
5
+ import type { SessionKey } from '../../../sessionKey';
5
6
  /**
6
7
  * @description Id of the message a callback button sits on, or `null` when
7
8
  * Telegram did not attach one (a very old message). Callbacks that resolve an
@@ -9,4 +10,15 @@ import type { Context } from 'telegraf';
9
10
  * rather than resolve against another message's list.
10
11
  */
11
12
  export declare function getCallbackMessageId(ctx: Context): number | null;
13
+ /**
14
+ * @description The map key of a per-MESSAGE snapshot (the list a keyboard's indexes resolve against, the rung a
15
+ * question waits to add). A Telegram message id is unique only within ONE chat, so the chat (`key.space`) is part of
16
+ * the key: with several groups, or the DM and group transports side by side, two chats' messages never meet.
17
+ */
18
+ export declare function buildMessageSnapshotKey(key: SessionKey, messageId: number): string;
19
+ /**
20
+ * @description Store a per-MESSAGE snapshot, keeping only the newest `limit`: the oldest is dropped, so a map keyed by
21
+ * message never grows without bound and an older keyboard resolves to «out of date». Storing again refreshes the entry's age.
22
+ */
23
+ export declare function rememberBoundedSnapshot<TSnapshot>(snapshots: Map<string, TSnapshot>, snapshotKey: string, snapshot: TSnapshot, limit: number): void;
12
24
  //# sourceMappingURL=callbackMessage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"callbackMessage.d.ts","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackMessage.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEhE"}
1
+ {"version":3,"file":"callbackMessage.d.ts","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackMessage.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAElF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQnJ"}
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getCallbackMessageId = getCallbackMessageId;
4
+ exports.buildMessageSnapshotKey = buildMessageSnapshotKey;
5
+ exports.rememberBoundedSnapshot = rememberBoundedSnapshot;
4
6
  /**
5
7
  * @description Id of the message a callback button sits on, or `null` when
6
8
  * Telegram did not attach one (a very old message). Callbacks that resolve an
@@ -10,4 +12,26 @@ exports.getCallbackMessageId = getCallbackMessageId;
10
12
  function getCallbackMessageId(ctx) {
11
13
  return ctx.callbackQuery?.message?.message_id ?? null;
12
14
  }
15
+ /**
16
+ * @description The map key of a per-MESSAGE snapshot (the list a keyboard's indexes resolve against, the rung a
17
+ * question waits to add). A Telegram message id is unique only within ONE chat, so the chat (`key.space`) is part of
18
+ * the key: with several groups, or the DM and group transports side by side, two chats' messages never meet.
19
+ */
20
+ function buildMessageSnapshotKey(key, messageId) {
21
+ return `${key.platform}:${key.space}:${messageId}`;
22
+ }
23
+ /**
24
+ * @description Store a per-MESSAGE snapshot, keeping only the newest `limit`: the oldest is dropped, so a map keyed by
25
+ * message never grows without bound and an older keyboard resolves to «out of date». Storing again refreshes the entry's age.
26
+ */
27
+ function rememberBoundedSnapshot(snapshots, snapshotKey, snapshot, limit) {
28
+ snapshots.delete(snapshotKey);
29
+ snapshots.set(snapshotKey, snapshot);
30
+ while (snapshots.size > limit) {
31
+ const oldest = snapshots.keys().next().value;
32
+ if (oldest === undefined)
33
+ break;
34
+ snapshots.delete(oldest);
35
+ }
36
+ }
13
37
  //# sourceMappingURL=callbackMessage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"callbackMessage.js","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackMessage.ts"],"names":[],"mappings":";;AAWA,oDAEC;AARD;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC;AACxD,CAAC"}
1
+ {"version":3,"file":"callbackMessage.js","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackMessage.ts"],"names":[],"mappings":";;AAYA,oDAEC;AAOD,0DAEC;AAMD,0DAQC;AA/BD;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,UAAU,IAAI,IAAI,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CAAC,GAAe,EAAE,SAAiB;IACxE,OAAO,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,SAAgB,uBAAuB,CAAY,SAAiC,EAAE,WAAmB,EAAE,QAAmB,EAAE,KAAa;IAC3I,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC9B,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,SAAS,CAAC,IAAI,GAAG,KAAK,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC7C,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM;QAChC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The toast a button tap is answered with.
3
+ */
4
+ import type { Context } from 'telegraf';
5
+ import type { SessionKey } from '../../../sessionKey';
6
+ /** The most characters Telegram shows in the toast a button tap is answered with (`answerCallbackQuery`'s `text`). */
7
+ export declare const callbackToastMaxLength = 200;
8
+ /**
9
+ * @description Answer a button tap, surviving an EXPIRED query. A handler that works a long time before it answers (a
10
+ * default-account change re-spawns every live topic, ~30 s) answers after Telegram stopped waiting, and the Bot API
11
+ * refuses with «query is too old»: the toast is lost, but that must never change what the handler still has to do (the
12
+ * re-render of the screen). Resolves to whether the toast reached the user; any other failure is thrown.
13
+ */
14
+ export declare function answerCallbackQuerySafely(ctx: Pick<Context, 'answerCbQuery'>, text?: string): Promise<boolean>;
15
+ /**
16
+ * @description Answer a button tap with `text` as the toast: whole when it fits {@link callbackToastMaxLength}, else
17
+ * cut (with an ellipsis, on a grapheme boundary, within the limit counted in UTF-16 units). Resolves to whether it
18
+ * was cut. For a text the thread already carries in full (an answer the question message was just edited to show):
19
+ * the toast only confirms the tap, and a longer one must still be answered — Telegram refuses a toast over the
20
+ * limit and the tap would get no feedback at all. An expired query ({@link answerCallbackQuerySafely}) is no failure
21
+ * here: the thread carries the text.
22
+ */
23
+ export declare function answerCallbackWithFittedText(ctx: Pick<Context, 'answerCbQuery'>, text: string): Promise<boolean>;
24
+ /**
25
+ * @description Answer a button tap with `text` whole. A toast holds {@link callbackToastMaxLength} characters: a
26
+ * longer text is cut for the toast and handed to `postWhole` to be posted in full as a message — a notice or an error
27
+ * that says what to do must never reach the user half-sentence, and must never be dropped because it did not fit.
28
+ * The same holds for a toast that came too late (the query expired): the text is posted instead.
29
+ */
30
+ export declare function answerCallbackWithWholeText(ctx: Pick<Context, 'answerCbQuery'>, text: string, postWhole: (wholeText: string) => Promise<unknown>): Promise<void>;
31
+ /**
32
+ * @description {@link answerCallbackWithWholeText} for a command module: the topic of the tap is where a text too long
33
+ * for the toast is posted in full. A module builds its answerer once from its own `replyToThread` port.
34
+ */
35
+ export declare function createWholeTextAnswerer(replyToThread: (key: SessionKey, text: string) => Promise<unknown>): (ctx: Pick<Context, 'answerCbQuery'>, key: SessionKey, text: string) => Promise<void>;
36
+ //# sourceMappingURL=callbackToast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbackToast.d.ts","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackToast.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,sHAAsH;AACtH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAmB1C;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpH;AAQD;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAItH;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GACjD,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GACjE,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAEvF"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.callbackToastMaxLength = void 0;
4
+ exports.answerCallbackQuerySafely = answerCallbackQuerySafely;
5
+ exports.answerCallbackWithFittedText = answerCallbackWithFittedText;
6
+ exports.answerCallbackWithWholeText = answerCallbackWithWholeText;
7
+ exports.createWholeTextAnswerer = createWholeTextAnswerer;
8
+ const telegramError_1 = require("../../../utils/telegramError");
9
+ /** The most characters Telegram shows in the toast a button tap is answered with (`answerCallbackQuery`'s `text`). */
10
+ exports.callbackToastMaxLength = 200;
11
+ /** Splits text into the characters a reader sees (an emoji with a skin tone, a family, a flag is ONE). */
12
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
13
+ /**
14
+ * The longest start of `text` that holds at most `maxUnits` UTF-16 units and ends on a whole grapheme: a combined
15
+ * emoji (a family, a flag, a skin tone) or an accented letter built from several code points is never split into a
16
+ * broken-looking piece.
17
+ */
18
+ function cutToGraphemes(text, maxUnits) {
19
+ let cut = '';
20
+ for (const { segment } of graphemeSegmenter.segment(text)) {
21
+ if (cut.length + segment.length > maxUnits)
22
+ break;
23
+ cut += segment;
24
+ }
25
+ return cut;
26
+ }
27
+ /**
28
+ * @description Answer a button tap, surviving an EXPIRED query. A handler that works a long time before it answers (a
29
+ * default-account change re-spawns every live topic, ~30 s) answers after Telegram stopped waiting, and the Bot API
30
+ * refuses with «query is too old»: the toast is lost, but that must never change what the handler still has to do (the
31
+ * re-render of the screen). Resolves to whether the toast reached the user; any other failure is thrown.
32
+ */
33
+ async function answerCallbackQuerySafely(ctx, text) {
34
+ try {
35
+ await ctx.answerCbQuery(text);
36
+ return true;
37
+ }
38
+ catch (error) {
39
+ if (!(0, telegramError_1.checkIsStaleAnswerCallbackQueryError)(error))
40
+ throw error;
41
+ console.debug('[callback] the tap was answered after its query expired; the toast is lost');
42
+ return false;
43
+ }
44
+ }
45
+ /** The toast for `text`: the text whole when it fits {@link callbackToastMaxLength}, else its start with an ellipsis. */
46
+ function fitToastText(text) {
47
+ if (text.length <= exports.callbackToastMaxLength)
48
+ return { toast: text, isCut: false };
49
+ return { toast: `${cutToGraphemes(text, exports.callbackToastMaxLength - 1)}…`, isCut: true };
50
+ }
51
+ /**
52
+ * @description Answer a button tap with `text` as the toast: whole when it fits {@link callbackToastMaxLength}, else
53
+ * cut (with an ellipsis, on a grapheme boundary, within the limit counted in UTF-16 units). Resolves to whether it
54
+ * was cut. For a text the thread already carries in full (an answer the question message was just edited to show):
55
+ * the toast only confirms the tap, and a longer one must still be answered — Telegram refuses a toast over the
56
+ * limit and the tap would get no feedback at all. An expired query ({@link answerCallbackQuerySafely}) is no failure
57
+ * here: the thread carries the text.
58
+ */
59
+ async function answerCallbackWithFittedText(ctx, text) {
60
+ const { toast, isCut } = fitToastText(text);
61
+ await answerCallbackQuerySafely(ctx, toast);
62
+ return isCut;
63
+ }
64
+ /**
65
+ * @description Answer a button tap with `text` whole. A toast holds {@link callbackToastMaxLength} characters: a
66
+ * longer text is cut for the toast and handed to `postWhole` to be posted in full as a message — a notice or an error
67
+ * that says what to do must never reach the user half-sentence, and must never be dropped because it did not fit.
68
+ * The same holds for a toast that came too late (the query expired): the text is posted instead.
69
+ */
70
+ async function answerCallbackWithWholeText(ctx, text, postWhole) {
71
+ const { toast, isCut } = fitToastText(text);
72
+ const isToastShown = await answerCallbackQuerySafely(ctx, toast);
73
+ if (isCut || !isToastShown)
74
+ await postWhole(text);
75
+ }
76
+ /**
77
+ * @description {@link answerCallbackWithWholeText} for a command module: the topic of the tap is where a text too long
78
+ * for the toast is posted in full. A module builds its answerer once from its own `replyToThread` port.
79
+ */
80
+ function createWholeTextAnswerer(replyToThread) {
81
+ return (ctx, key, text) => answerCallbackWithWholeText(ctx, text, (wholeText) => replyToThread(key, wholeText));
82
+ }
83
+ //# sourceMappingURL=callbackToast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callbackToast.js","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/callbackToast.ts"],"names":[],"mappings":";;;AAiCA,8DASC;AAgBD,oEAIC;AAQD,kEAQC;AAMD,0DAIC;AAnFD,gEAAoF;AAEpF,sHAAsH;AACzG,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAE1C,0GAA0G;AAC1G,MAAM,iBAAiB,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;AAErF;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,QAAgB;IACpD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ;YAAE,MAAM;QAClD,GAAG,IAAI,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,yBAAyB,CAAC,GAAmC,EAAE,IAAa;IAChG,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,IAAA,oDAAoC,EAAC,KAAK,CAAC;YAAE,MAAM,KAAK,CAAC;QAC9D,OAAO,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;QAC5F,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,yHAAyH;AACzH,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,IAAI,CAAC,MAAM,IAAI,8BAAsB;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAChF,OAAO,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,EAAE,8BAAsB,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACxF,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,4BAA4B,CAAC,GAAmC,EAAE,IAAY;IAClG,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,yBAAyB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,2BAA2B,CAC/C,GAAmC,EACnC,IAAY,EACZ,SAAkD;IAElD,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,MAAM,yBAAyB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACjE,IAAI,KAAK,IAAI,CAAC,YAAY;QAAE,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,SAAgB,uBAAuB,CACrC,aAAkE;IAElE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,2BAA2B,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;AAClH,CAAC"}
@@ -1,6 +1,9 @@
1
1
  import type { SessionKey } from '../../../sessionKey';
2
2
  import type { ClaudeConfigDirApplyResult } from '../../../adapters/claudeConfigDir';
3
3
  import type { ClaudeLoginProfile } from '../../../agentLogin/agentLogin';
4
+ import { type FallbackChainEntry } from '../../../utils/fallbackChain';
5
+ import type { BackendFamily } from '../../../utils/backendSwitchPlan';
6
+ import type { OperatorSwitchOutcome } from '../../../utils/operatorSwitchPlan';
4
7
  import { type ClaudeAuthStatus } from '../../../utils/claudeProfiles';
5
8
  import type { BotCore } from './botCore';
6
9
  /**
@@ -28,8 +31,22 @@ export interface ClaudeAccountsPorts extends Pick<BotCore, 'bot' | 'command' | '
28
31
  applyClaudeProfile: (key: SessionKey) => Promise<ClaudeProfileApplyOutcome>;
29
32
  /** Bring every served topic's live Claude session onto its resolved profile (after a General change / a removal). */
30
33
  applyClaudeProfileEverywhere: () => Promise<void>;
34
+ /**
35
+ * The operator's own pick of a rung on a topic (`bot.ts`, `utils/operatorSwitchPlan.ts`): an account of the backend
36
+ * the topic runs changes the account, an account picked on the other backend asks first when a conversation is to
37
+ * be handed over (`asked`: the question is already in the topic, nothing more to say), a pick while a limit wait is
38
+ * armed is the limit move. The outcome carries the line to show.
39
+ */
40
+ switchTopicByOperator: (key: SessionKey, to: FallbackChainEntry) => Promise<OperatorSwitchOutcome>;
31
41
  /** The OpenCode providers the server is authenticated for (its live catalog); `[]` when it cannot be read. */
32
42
  detectOpenCodeProviders: () => Promise<string[]>;
43
+ /** The OpenCode catalog as `provider/model` labels (what a provider's model page lists); `[]` when it cannot be read. */
44
+ listOpenCodeModels: () => Promise<string[]>;
45
+ /** Where the topic runs: the backend family of its agent (`null`: no agent / a shell) and, when known, its rung. */
46
+ getTopicRun: (key: SessionKey) => {
47
+ family: BackendFamily | null;
48
+ rung: FallbackChainEntry | null;
49
+ };
33
50
  /** Drop a provider's stored credentials and post the outcome — the `/disconnect <provider>` flow, unchanged. */
34
51
  disconnectOpenCodeProvider: (key: SessionKey, providerId: string) => Promise<void>;
35
52
  /** Start the `/connect <provider>` flow (the method picker: OAuth or API key), unchanged. */
@@ -53,6 +70,7 @@ export declare function createClaudeAccounts(ports: ClaudeAccountsPorts): {
53
70
  checkIsAwaitingProfileName: (key: SessionKey) => boolean;
54
71
  cancelProfileNameCapture: (key: SessionKey) => boolean;
55
72
  submitProfileName: (key: SessionKey, text: string) => Promise<void>;
73
+ pickClaudeAccount: (key: SessionKey, isGeneral: boolean, name: string) => Promise<string | null>;
56
74
  onClaudeProfileAuthStatus: (_key: SessionKey, profile: ClaudeLoginProfile, status: ClaudeAuthStatus | null) => Promise<void>;
57
75
  };
58
76
  //# sourceMappingURL=claudeAccounts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claudeAccounts.d.ts","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/claudeAccounts.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EASL,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AAiCvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,GAAG,iBAAiB,CAAC;AAEvF;;;GAGG;AACH,MAAM,WAAW,mBACf,SAAQ,IAAI,CACV,OAAO,EACP,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,eAAe,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,gBAAgB,CACpI;IACD,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,uEAAuE;IACvE,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,wGAAwG;IACxG,kBAAkB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,yGAAyG;IACzG,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACrF,2GAA2G;IAC3G,uBAAuB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,iFAAiF;IACjF,kBAAkB,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAC5E,qHAAqH;IACrH,4BAA4B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,8GAA8G;IAC9G,uBAAuB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,gHAAgH;IAChH,0BAA0B,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,6FAA6F;IAC7F,oBAAoB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;OAGG;IACH,eAAe,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5F,uHAAuH;IACvH,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAC;CACjC;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB;yCAkXnB,IAAI;0CAgEH,IAAI;sCAtGN,UAAU,KAAG,OAAO;oCAKtB,UAAU,KAAG,OAAO;6BAgBrB,UAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;sCA5Q/B,UAAU,WAAW,kBAAkB,UAAU,gBAAgB,GAAG,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;EA4dxI"}
1
+ {"version":3,"file":"claudeAccounts.d.ts","sourceRoot":"","sources":["../../../../src/connectors/telegram/commands/claudeAccounts.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAoF,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACzJ,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EASL,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AAuCvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,GAAG,iBAAiB,CAAC;AAEvF;;;GAGG;AACH,MAAM,WAAW,mBACf,SAAQ,IAAI,CACV,OAAO,EACP,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,eAAe,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,gBAAgB,CACpI;IACD,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,uEAAuE;IACvE,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,wGAAwG;IACxG,kBAAkB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,yGAAyG;IACzG,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACrF,2GAA2G;IAC3G,uBAAuB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,iFAAiF;IACjF,kBAAkB,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAC5E,qHAAqH;IACrH,4BAA4B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD;;;;;OAKG;IACH,qBAAqB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,kBAAkB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACnG,8GAA8G;IAC9G,uBAAuB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,yHAAyH;IACzH,kBAAkB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,oHAAoH;IACpH,WAAW,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK;QAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAA;KAAE,CAAC;IACpG,gHAAgH;IAChH,0BAA0B,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,6FAA6F;IAC7F,oBAAoB,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;OAGG;IACH,eAAe,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5F,uHAAuH;IACvH,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAC;CACjC;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB;yCA8cnB,IAAI;0CA2DH,IAAI;sCAvIN,UAAU,KAAG,OAAO;oCAKtB,UAAU,KAAG,OAAO;6BAgBrB,UAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;6BA9IhD,UAAU,aAAa,OAAO,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;sCA9JpD,UAAU,WAAW,kBAAkB,UAAU,gBAAgB,GAAG,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;EA4kBxI"}