@asermax/tachikoma 2.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 (345) hide show
  1. package/README.md +64 -0
  2. package/dist/agent/adapter.d.ts +8 -0
  3. package/dist/agent/adapter.js +86 -0
  4. package/dist/agent/adapter.js.map +1 -0
  5. package/dist/agent/manager.d.ts +35 -0
  6. package/dist/agent/manager.js +76 -0
  7. package/dist/agent/manager.js.map +1 -0
  8. package/dist/agent/models.d.ts +46 -0
  9. package/dist/agent/models.js +96 -0
  10. package/dist/agent/models.js.map +1 -0
  11. package/dist/agent/side-run.d.ts +42 -0
  12. package/dist/agent/side-run.js +83 -0
  13. package/dist/agent/side-run.js.map +1 -0
  14. package/dist/app.d.ts +5 -0
  15. package/dist/app.js +79 -0
  16. package/dist/app.js.map +1 -0
  17. package/dist/channels/types.d.ts +37 -0
  18. package/dist/channels/types.js +5 -0
  19. package/dist/channels/types.js.map +1 -0
  20. package/dist/config/default-template.d.ts +1 -0
  21. package/dist/config/default-template.js +49 -0
  22. package/dist/config/default-template.js.map +1 -0
  23. package/dist/config/load.d.ts +8 -0
  24. package/dist/config/load.js +28 -0
  25. package/dist/config/load.js.map +1 -0
  26. package/dist/config/parse.d.ts +5 -0
  27. package/dist/config/parse.js +18 -0
  28. package/dist/config/parse.js.map +1 -0
  29. package/dist/config/schema.d.ts +29 -0
  30. package/dist/config/schema.js +35 -0
  31. package/dist/config/schema.js.map +1 -0
  32. package/dist/coordinator.d.ts +54 -0
  33. package/dist/coordinator.js +344 -0
  34. package/dist/coordinator.js.map +1 -0
  35. package/dist/db/core-schema.d.ts +250 -0
  36. package/dist/db/core-schema.js +19 -0
  37. package/dist/db/core-schema.js.map +1 -0
  38. package/dist/db/index.d.ts +8 -0
  39. package/dist/db/index.js +16 -0
  40. package/dist/db/index.js.map +1 -0
  41. package/dist/db/schema.d.ts +4 -0
  42. package/dist/db/schema.js +7 -0
  43. package/dist/db/schema.js.map +1 -0
  44. package/dist/db/state.d.ts +10 -0
  45. package/dist/db/state.js +36 -0
  46. package/dist/db/state.js.map +1 -0
  47. package/dist/domain/agent-events.d.ts +26 -0
  48. package/dist/domain/agent-events.js +2 -0
  49. package/dist/domain/agent-events.js.map +1 -0
  50. package/dist/domain/message.d.ts +25 -0
  51. package/dist/domain/message.js +17 -0
  52. package/dist/domain/message.js.map +1 -0
  53. package/dist/events.d.ts +9 -0
  54. package/dist/events.js +27 -0
  55. package/dist/events.js.map +1 -0
  56. package/dist/extensions/api.d.ts +118 -0
  57. package/dist/extensions/api.js +7 -0
  58. package/dist/extensions/api.js.map +1 -0
  59. package/dist/extensions/boundary/detector.d.ts +20 -0
  60. package/dist/extensions/boundary/detector.js +57 -0
  61. package/dist/extensions/boundary/detector.js.map +1 -0
  62. package/dist/extensions/boundary/idle.d.ts +10 -0
  63. package/dist/extensions/boundary/idle.js +28 -0
  64. package/dist/extensions/boundary/idle.js.map +1 -0
  65. package/dist/extensions/boundary/index.d.ts +12 -0
  66. package/dist/extensions/boundary/index.js +65 -0
  67. package/dist/extensions/boundary/index.js.map +1 -0
  68. package/dist/extensions/boundary/summary.d.ts +5 -0
  69. package/dist/extensions/boundary/summary.js +33 -0
  70. package/dist/extensions/boundary/summary.js.map +1 -0
  71. package/dist/extensions/commands/index.d.ts +7 -0
  72. package/dist/extensions/commands/index.js +21 -0
  73. package/dist/extensions/commands/index.js.map +1 -0
  74. package/dist/extensions/context/index.d.ts +7 -0
  75. package/dist/extensions/context/index.js +65 -0
  76. package/dist/extensions/context/index.js.map +1 -0
  77. package/dist/extensions/context/processor.d.ts +27 -0
  78. package/dist/extensions/context/processor.js +228 -0
  79. package/dist/extensions/context/processor.js.map +1 -0
  80. package/dist/extensions/detached-processes/index.d.ts +12 -0
  81. package/dist/extensions/detached-processes/index.js +51 -0
  82. package/dist/extensions/detached-processes/index.js.map +1 -0
  83. package/dist/extensions/detached-processes/limits.d.ts +27 -0
  84. package/dist/extensions/detached-processes/limits.js +55 -0
  85. package/dist/extensions/detached-processes/limits.js.map +1 -0
  86. package/dist/extensions/detached-processes/output.d.ts +2 -0
  87. package/dist/extensions/detached-processes/output.js +26 -0
  88. package/dist/extensions/detached-processes/output.js.map +1 -0
  89. package/dist/extensions/detached-processes/reconcile.d.ts +31 -0
  90. package/dist/extensions/detached-processes/reconcile.js +71 -0
  91. package/dist/extensions/detached-processes/reconcile.js.map +1 -0
  92. package/dist/extensions/detached-processes/repository.d.ts +33 -0
  93. package/dist/extensions/detached-processes/repository.js +62 -0
  94. package/dist/extensions/detached-processes/repository.js.map +1 -0
  95. package/dist/extensions/detached-processes/schema.d.ts +252 -0
  96. package/dist/extensions/detached-processes/schema.js +23 -0
  97. package/dist/extensions/detached-processes/schema.js.map +1 -0
  98. package/dist/extensions/detached-processes/spawn.d.ts +40 -0
  99. package/dist/extensions/detached-processes/spawn.js +137 -0
  100. package/dist/extensions/detached-processes/spawn.js.map +1 -0
  101. package/dist/extensions/detached-processes/tools.d.ts +41 -0
  102. package/dist/extensions/detached-processes/tools.js +243 -0
  103. package/dist/extensions/detached-processes/tools.js.map +1 -0
  104. package/dist/extensions/detached-processes/watcher.d.ts +7 -0
  105. package/dist/extensions/detached-processes/watcher.js +19 -0
  106. package/dist/extensions/detached-processes/watcher.js.map +1 -0
  107. package/dist/extensions/external/index.d.ts +11 -0
  108. package/dist/extensions/external/index.js +40 -0
  109. package/dist/extensions/external/index.js.map +1 -0
  110. package/dist/extensions/external/installs.d.ts +39 -0
  111. package/dist/extensions/external/installs.js +98 -0
  112. package/dist/extensions/external/installs.js.map +1 -0
  113. package/dist/extensions/external/loader.d.ts +19 -0
  114. package/dist/extensions/external/loader.js +70 -0
  115. package/dist/extensions/external/loader.js.map +1 -0
  116. package/dist/extensions/external/tools.d.ts +17 -0
  117. package/dist/extensions/external/tools.js +112 -0
  118. package/dist/extensions/external/tools.js.map +1 -0
  119. package/dist/extensions/git/commit.d.ts +19 -0
  120. package/dist/extensions/git/commit.js +44 -0
  121. package/dist/extensions/git/commit.js.map +1 -0
  122. package/dist/extensions/git/git.d.ts +11 -0
  123. package/dist/extensions/git/git.js +29 -0
  124. package/dist/extensions/git/git.js.map +1 -0
  125. package/dist/extensions/git/hooks.d.ts +10 -0
  126. package/dist/extensions/git/hooks.js +88 -0
  127. package/dist/extensions/git/hooks.js.map +1 -0
  128. package/dist/extensions/git/index.d.ts +11 -0
  129. package/dist/extensions/git/index.js +28 -0
  130. package/dist/extensions/git/index.js.map +1 -0
  131. package/dist/extensions/git/processor.d.ts +13 -0
  132. package/dist/extensions/git/processor.js +52 -0
  133. package/dist/extensions/git/processor.js.map +1 -0
  134. package/dist/extensions/git/sync.d.ts +44 -0
  135. package/dist/extensions/git/sync.js +189 -0
  136. package/dist/extensions/git/sync.js.map +1 -0
  137. package/dist/extensions/git/tools.d.ts +21 -0
  138. package/dist/extensions/git/tools.js +101 -0
  139. package/dist/extensions/git/tools.js.map +1 -0
  140. package/dist/extensions/host.d.ts +31 -0
  141. package/dist/extensions/host.js +75 -0
  142. package/dist/extensions/host.js.map +1 -0
  143. package/dist/extensions/index.d.ts +3 -0
  144. package/dist/extensions/index.js +32 -0
  145. package/dist/extensions/index.js.map +1 -0
  146. package/dist/extensions/memory/archive.d.ts +8 -0
  147. package/dist/extensions/memory/archive.js +46 -0
  148. package/dist/extensions/memory/archive.js.map +1 -0
  149. package/dist/extensions/memory/dates.d.ts +2 -0
  150. package/dist/extensions/memory/dates.js +7 -0
  151. package/dist/extensions/memory/dates.js.map +1 -0
  152. package/dist/extensions/memory/extraction.d.ts +17 -0
  153. package/dist/extensions/memory/extraction.js +218 -0
  154. package/dist/extensions/memory/extraction.js.map +1 -0
  155. package/dist/extensions/memory/index.d.ts +20 -0
  156. package/dist/extensions/memory/index.js +67 -0
  157. package/dist/extensions/memory/index.js.map +1 -0
  158. package/dist/extensions/memory/indexes.d.ts +14 -0
  159. package/dist/extensions/memory/indexes.js +64 -0
  160. package/dist/extensions/memory/indexes.js.map +1 -0
  161. package/dist/extensions/memory/layout.d.ts +20 -0
  162. package/dist/extensions/memory/layout.js +79 -0
  163. package/dist/extensions/memory/layout.js.map +1 -0
  164. package/dist/extensions/memory/maintenance.d.ts +21 -0
  165. package/dist/extensions/memory/maintenance.js +357 -0
  166. package/dist/extensions/memory/maintenance.js.map +1 -0
  167. package/dist/extensions/memory/prompts.d.ts +8 -0
  168. package/dist/extensions/memory/prompts.js +125 -0
  169. package/dist/extensions/memory/prompts.js.map +1 -0
  170. package/dist/extensions/memory/transcript.d.ts +18 -0
  171. package/dist/extensions/memory/transcript.js +79 -0
  172. package/dist/extensions/memory/transcript.js.map +1 -0
  173. package/dist/extensions/notifications/format.d.ts +5 -0
  174. package/dist/extensions/notifications/format.js +17 -0
  175. package/dist/extensions/notifications/format.js.map +1 -0
  176. package/dist/extensions/notifications/index.d.ts +13 -0
  177. package/dist/extensions/notifications/index.js +29 -0
  178. package/dist/extensions/notifications/index.js.map +1 -0
  179. package/dist/extensions/notifications/payload.d.ts +22 -0
  180. package/dist/extensions/notifications/payload.js +29 -0
  181. package/dist/extensions/notifications/payload.js.map +1 -0
  182. package/dist/extensions/notifications/router.d.ts +29 -0
  183. package/dist/extensions/notifications/router.js +55 -0
  184. package/dist/extensions/notifications/router.js.map +1 -0
  185. package/dist/extensions/notifications/tools.d.ts +12 -0
  186. package/dist/extensions/notifications/tools.js +41 -0
  187. package/dist/extensions/notifications/tools.js.map +1 -0
  188. package/dist/extensions/projects/context-provider.d.ts +9 -0
  189. package/dist/extensions/projects/context-provider.js +37 -0
  190. package/dist/extensions/projects/context-provider.js.map +1 -0
  191. package/dist/extensions/projects/git.d.ts +28 -0
  192. package/dist/extensions/projects/git.js +91 -0
  193. package/dist/extensions/projects/git.js.map +1 -0
  194. package/dist/extensions/projects/hooks.d.ts +7 -0
  195. package/dist/extensions/projects/hooks.js +42 -0
  196. package/dist/extensions/projects/hooks.js.map +1 -0
  197. package/dist/extensions/projects/index.d.ts +11 -0
  198. package/dist/extensions/projects/index.js +30 -0
  199. package/dist/extensions/projects/index.js.map +1 -0
  200. package/dist/extensions/projects/processor.d.ts +13 -0
  201. package/dist/extensions/projects/processor.js +63 -0
  202. package/dist/extensions/projects/processor.js.map +1 -0
  203. package/dist/extensions/projects/tools.d.ts +21 -0
  204. package/dist/extensions/projects/tools.js +118 -0
  205. package/dist/extensions/projects/tools.js.map +1 -0
  206. package/dist/extensions/registrations.d.ts +21 -0
  207. package/dist/extensions/registrations.js +12 -0
  208. package/dist/extensions/registrations.js.map +1 -0
  209. package/dist/extensions/repl/index.d.ts +2 -0
  210. package/dist/extensions/repl/index.js +85 -0
  211. package/dist/extensions/repl/index.js.map +1 -0
  212. package/dist/extensions/skills/agents.d.ts +17 -0
  213. package/dist/extensions/skills/agents.js +77 -0
  214. package/dist/extensions/skills/agents.js.map +1 -0
  215. package/dist/extensions/skills/delegate.d.ts +22 -0
  216. package/dist/extensions/skills/delegate.js +54 -0
  217. package/dist/extensions/skills/delegate.js.map +1 -0
  218. package/dist/extensions/skills/index.d.ts +11 -0
  219. package/dist/extensions/skills/index.js +43 -0
  220. package/dist/extensions/skills/index.js.map +1 -0
  221. package/dist/extensions/skills/reload.d.ts +8 -0
  222. package/dist/extensions/skills/reload.js +38 -0
  223. package/dist/extensions/skills/reload.js.map +1 -0
  224. package/dist/extensions/tasks/executor.d.ts +43 -0
  225. package/dist/extensions/tasks/executor.js +179 -0
  226. package/dist/extensions/tasks/executor.js.map +1 -0
  227. package/dist/extensions/tasks/expiration.d.ts +12 -0
  228. package/dist/extensions/tasks/expiration.js +17 -0
  229. package/dist/extensions/tasks/expiration.js.map +1 -0
  230. package/dist/extensions/tasks/generation.d.ts +14 -0
  231. package/dist/extensions/tasks/generation.js +70 -0
  232. package/dist/extensions/tasks/generation.js.map +1 -0
  233. package/dist/extensions/tasks/index.d.ts +14 -0
  234. package/dist/extensions/tasks/index.js +75 -0
  235. package/dist/extensions/tasks/index.js.map +1 -0
  236. package/dist/extensions/tasks/repository.d.ts +53 -0
  237. package/dist/extensions/tasks/repository.js +147 -0
  238. package/dist/extensions/tasks/repository.js.map +1 -0
  239. package/dist/extensions/tasks/schedule.d.ts +13 -0
  240. package/dist/extensions/tasks/schedule.js +32 -0
  241. package/dist/extensions/tasks/schedule.js.map +1 -0
  242. package/dist/extensions/tasks/schema.d.ts +423 -0
  243. package/dist/extensions/tasks/schema.js +45 -0
  244. package/dist/extensions/tasks/schema.js.map +1 -0
  245. package/dist/extensions/tasks/session-delivery.d.ts +18 -0
  246. package/dist/extensions/tasks/session-delivery.js +39 -0
  247. package/dist/extensions/tasks/session-delivery.js.map +1 -0
  248. package/dist/extensions/tasks/tools.d.ts +38 -0
  249. package/dist/extensions/tasks/tools.js +181 -0
  250. package/dist/extensions/tasks/tools.js.map +1 -0
  251. package/dist/extensions/telegram/buttons.d.ts +14 -0
  252. package/dist/extensions/telegram/buttons.js +49 -0
  253. package/dist/extensions/telegram/buttons.js.map +1 -0
  254. package/dist/extensions/telegram/channel.d.ts +39 -0
  255. package/dist/extensions/telegram/channel.js +201 -0
  256. package/dist/extensions/telegram/channel.js.map +1 -0
  257. package/dist/extensions/telegram/chunking.d.ts +7 -0
  258. package/dist/extensions/telegram/chunking.js +67 -0
  259. package/dist/extensions/telegram/chunking.js.map +1 -0
  260. package/dist/extensions/telegram/inbound.d.ts +7 -0
  261. package/dist/extensions/telegram/inbound.js +29 -0
  262. package/dist/extensions/telegram/inbound.js.map +1 -0
  263. package/dist/extensions/telegram/index.d.ts +13 -0
  264. package/dist/extensions/telegram/index.js +67 -0
  265. package/dist/extensions/telegram/index.js.map +1 -0
  266. package/dist/extensions/telegram/media.d.ts +39 -0
  267. package/dist/extensions/telegram/media.js +223 -0
  268. package/dist/extensions/telegram/media.js.map +1 -0
  269. package/dist/extensions/telegram/mutex.d.ts +9 -0
  270. package/dist/extensions/telegram/mutex.js +14 -0
  271. package/dist/extensions/telegram/mutex.js.map +1 -0
  272. package/dist/extensions/telegram/sending.d.ts +48 -0
  273. package/dist/extensions/telegram/sending.js +119 -0
  274. package/dist/extensions/telegram/sending.js.map +1 -0
  275. package/dist/extensions/telegram/streaming.d.ts +46 -0
  276. package/dist/extensions/telegram/streaming.js +140 -0
  277. package/dist/extensions/telegram/streaming.js.map +1 -0
  278. package/dist/extensions/telegram/tools.d.ts +80 -0
  279. package/dist/extensions/telegram/tools.js +232 -0
  280. package/dist/extensions/telegram/tools.js.map +1 -0
  281. package/dist/extensions/workflows/cleanup.d.ts +10 -0
  282. package/dist/extensions/workflows/cleanup.js +38 -0
  283. package/dist/extensions/workflows/cleanup.js.map +1 -0
  284. package/dist/extensions/workflows/index.d.ts +11 -0
  285. package/dist/extensions/workflows/index.js +42 -0
  286. package/dist/extensions/workflows/index.js.map +1 -0
  287. package/dist/extensions/workflows/loader.d.ts +27 -0
  288. package/dist/extensions/workflows/loader.js +90 -0
  289. package/dist/extensions/workflows/loader.js.map +1 -0
  290. package/dist/extensions/workflows/model.d.ts +19 -0
  291. package/dist/extensions/workflows/model.js +7 -0
  292. package/dist/extensions/workflows/model.js.map +1 -0
  293. package/dist/extensions/workflows/repository.d.ts +24 -0
  294. package/dist/extensions/workflows/repository.js +61 -0
  295. package/dist/extensions/workflows/repository.js.map +1 -0
  296. package/dist/extensions/workflows/schema.d.ts +193 -0
  297. package/dist/extensions/workflows/schema.js +20 -0
  298. package/dist/extensions/workflows/schema.js.map +1 -0
  299. package/dist/extensions/workflows/tools.d.ts +27 -0
  300. package/dist/extensions/workflows/tools.js +285 -0
  301. package/dist/extensions/workflows/tools.js.map +1 -0
  302. package/dist/log.d.ts +8 -0
  303. package/dist/log.js +15 -0
  304. package/dist/log.js.map +1 -0
  305. package/dist/main.d.ts +2 -0
  306. package/dist/main.js +27 -0
  307. package/dist/main.js.map +1 -0
  308. package/dist/migration/ask.d.ts +8 -0
  309. package/dist/migration/ask.js +24 -0
  310. package/dist/migration/ask.js.map +1 -0
  311. package/dist/migration/config.d.ts +10 -0
  312. package/dist/migration/config.js +122 -0
  313. package/dist/migration/config.js.map +1 -0
  314. package/dist/migration/context.d.ts +3 -0
  315. package/dist/migration/context.js +24 -0
  316. package/dist/migration/context.js.map +1 -0
  317. package/dist/migration/database.d.ts +8 -0
  318. package/dist/migration/database.js +51 -0
  319. package/dist/migration/database.js.map +1 -0
  320. package/dist/migration/fs.d.ts +1 -0
  321. package/dist/migration/fs.js +11 -0
  322. package/dist/migration/fs.js.map +1 -0
  323. package/dist/migration/index.d.ts +11 -0
  324. package/dist/migration/index.js +28 -0
  325. package/dist/migration/index.js.map +1 -0
  326. package/dist/migration/skills.d.ts +19 -0
  327. package/dist/migration/skills.js +77 -0
  328. package/dist/migration/skills.js.map +1 -0
  329. package/dist/scheduler.d.ts +17 -0
  330. package/dist/scheduler.js +53 -0
  331. package/dist/scheduler.js.map +1 -0
  332. package/dist/sessions/registry.d.ts +15 -0
  333. package/dist/sessions/registry.js +42 -0
  334. package/dist/sessions/registry.js.map +1 -0
  335. package/dist/workspace.d.ts +13 -0
  336. package/dist/workspace.js +32 -0
  337. package/dist/workspace.js.map +1 -0
  338. package/drizzle/0000_init.sql +19 -0
  339. package/drizzle/0001_extensions.sql +63 -0
  340. package/drizzle/meta/0000_snapshot.json +134 -0
  341. package/drizzle/meta/0001_snapshot.json +526 -0
  342. package/drizzle/meta/_journal.json +20 -0
  343. package/package.json +63 -0
  344. package/skills/skill-authoring/SKILL.md +168 -0
  345. package/skills/workflow-authoring/SKILL.md +251 -0
@@ -0,0 +1,67 @@
1
+ // Telegram's hard limit, in UTF-16 code units — exactly what JS string length measures.
2
+ export const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;
3
+ /**
4
+ * Split text into chunks within Telegram's message length limit, preferring
5
+ * paragraph boundaries, then line boundaries, then a hard split that never
6
+ * cuts a surrogate pair in half.
7
+ */
8
+ export const splitMessage = (text, limit = TELEGRAM_MAX_MESSAGE_LENGTH) => {
9
+ if (text.length <= limit)
10
+ return [text];
11
+ const chunks = [];
12
+ let current = "";
13
+ for (const paragraph of text.split("\n\n")) {
14
+ for (const piece of splitParagraph(paragraph, limit)) {
15
+ const joined = current.length > 0 ? `${current}\n\n${piece}` : piece;
16
+ if (joined.length <= limit) {
17
+ current = joined;
18
+ }
19
+ else {
20
+ if (current.length > 0)
21
+ chunks.push(current);
22
+ current = piece;
23
+ }
24
+ }
25
+ }
26
+ if (current.length > 0)
27
+ chunks.push(current);
28
+ return chunks;
29
+ };
30
+ const splitParagraph = (paragraph, limit) => {
31
+ if (paragraph.length <= limit)
32
+ return [paragraph];
33
+ const pieces = [];
34
+ let current = "";
35
+ for (const line of paragraph.split("\n")) {
36
+ for (const fragment of hardSplit(line, limit)) {
37
+ const joined = current.length > 0 ? `${current}\n${fragment}` : fragment;
38
+ if (joined.length <= limit) {
39
+ current = joined;
40
+ }
41
+ else {
42
+ if (current.length > 0)
43
+ pieces.push(current);
44
+ current = fragment;
45
+ }
46
+ }
47
+ }
48
+ if (current.length > 0)
49
+ pieces.push(current);
50
+ return pieces;
51
+ };
52
+ const hardSplit = (line, limit) => {
53
+ if (line.length <= limit)
54
+ return [line];
55
+ const fragments = [];
56
+ let start = 0;
57
+ while (start < line.length) {
58
+ let end = Math.min(start + limit, line.length);
59
+ if (end < line.length && isLowSurrogate(line.charCodeAt(end)))
60
+ end -= 1;
61
+ fragments.push(line.slice(start, end));
62
+ start = end;
63
+ }
64
+ return fragments;
65
+ };
66
+ const isLowSurrogate = (code) => code >= 0xdc00 && code <= 0xdfff;
67
+ //# sourceMappingURL=chunking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.js","sourceRoot":"","sources":["../../../src/extensions/telegram/chunking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Message } from "grammy/types";
2
+ import type { InboundMessage, MediaAttachment } from "../../domain/message.ts";
3
+ export declare const CHANNEL_NAME = "telegram";
4
+ export declare const mapTextMessage: (message: Pick<Message, "text" | "message_id">) => InboundMessage | null;
5
+ export declare const mapMediaMessage: (message: Pick<Message, "caption" | "message_id">, attachment: MediaAttachment) => InboundMessage;
6
+ /** Frame a button tap so the agent can distinguish it from typed input. */
7
+ export declare const mapButtonTap: (value: string, messageId: number | null) => InboundMessage;
@@ -0,0 +1,29 @@
1
+ export const CHANNEL_NAME = "telegram";
2
+ export const mapTextMessage = (message) => {
3
+ const text = message.text?.trim() ?? "";
4
+ if (text.length === 0)
5
+ return null;
6
+ return {
7
+ text,
8
+ channel: CHANNEL_NAME,
9
+ receivedAt: new Date(),
10
+ media: [],
11
+ metadata: { messageId: message.message_id },
12
+ };
13
+ };
14
+ export const mapMediaMessage = (message, attachment) => ({
15
+ text: message.caption?.trim() ?? "",
16
+ channel: CHANNEL_NAME,
17
+ receivedAt: new Date(),
18
+ media: [attachment],
19
+ metadata: { messageId: message.message_id },
20
+ });
21
+ /** Frame a button tap so the agent can distinguish it from typed input. */
22
+ export const mapButtonTap = (value, messageId) => ({
23
+ text: `The user tapped the option \`${value}\` out of the options you displayed.`,
24
+ channel: CHANNEL_NAME,
25
+ receivedAt: new Date(),
26
+ media: [],
27
+ metadata: { buttonValue: value, ...(messageId != null ? { messageId } : {}) },
28
+ });
29
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/extensions/telegram/inbound.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC;AAEvC,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAA6C,EACtB,EAAE;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAExC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,YAAY;QACrB,UAAU,EAAE,IAAI,IAAI,EAAE;QACtB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE;KAC5C,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,OAAgD,EAChD,UAA2B,EACX,EAAE,CAAC,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;IACnC,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,IAAI,IAAI,EAAE;IACtB,KAAK,EAAE,CAAC,UAAU,CAAC;IACnB,QAAQ,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE;CAC5C,CAAC,CAAC;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,SAAwB,EAAkB,EAAE,CAAC,CAAC;IACxF,IAAI,EAAE,gCAAgC,KAAK,sCAAsC;IACjF,OAAO,EAAE,YAAY;IACrB,UAAU,EAAE,IAAI,IAAI,EAAE;IACtB,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;CAC9E,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Telegram channel: grammY long-polling bot restricted to a single chat, media
3
+ * download into the workspace data dir, and agent tools for sending files,
4
+ * reactions, pins, and inline-button prompts through the shared bot instance.
5
+ */
6
+ declare const _default: import("../api.ts").TachikomaExtension<{
7
+ chatId: number;
8
+ botToken: string;
9
+ allowMedia: boolean;
10
+ pushNotifications: boolean;
11
+ extraFileRoots: string[];
12
+ }>;
13
+ export default _default;
@@ -0,0 +1,67 @@
1
+ import { tmpdir } from "node:os";
2
+ import { join, resolve } from "node:path";
3
+ import { Bot } from "grammy";
4
+ import { Type } from "typebox";
5
+ import { expandHome } from "../../workspace.js";
6
+ import { defineExtension } from "../api.js";
7
+ import { TelegramChannel } from "./channel.js";
8
+ import { ensureMediaDir } from "./media.js";
9
+ import { registerTelegramTools } from "./tools.js";
10
+ const TelegramConfigSchema = Type.Object({
11
+ botToken: Type.String({ default: "", description: "Telegram bot token from @BotFather" }),
12
+ chatId: Type.Number({ default: 0, description: "Authorized Telegram chat ID" }),
13
+ allowMedia: Type.Boolean({ default: true, description: "Accept inbound media attachments" }),
14
+ pushNotifications: Type.Boolean({
15
+ default: true,
16
+ description: "Fire push notifications for background deliveries via copy+delete",
17
+ }),
18
+ extraFileRoots: Type.Array(Type.String(), {
19
+ default: [],
20
+ description: "Extra absolute roots send_telegram_file may read from",
21
+ }),
22
+ });
23
+ /**
24
+ * Telegram channel: grammY long-polling bot restricted to a single chat, media
25
+ * download into the workspace data dir, and agent tools for sending files,
26
+ * reactions, pins, and inline-button prompts through the shared bot instance.
27
+ */
28
+ export default defineExtension({
29
+ name: "telegram",
30
+ configSchema: TelegramConfigSchema,
31
+ setup(app) {
32
+ const { botToken, chatId, allowMedia, pushNotifications, extraFileRoots } = app.extensionConfig;
33
+ if (botToken === "" || chatId === 0) {
34
+ app.log.info("telegram channel disabled — set botToken and chatId under [extensions.telegram]");
35
+ return;
36
+ }
37
+ const bot = new Bot(botToken);
38
+ const mediaDir = join(app.workspace.dataDir, "media");
39
+ const channel = new TelegramChannel(bot, {
40
+ chatId,
41
+ allowMedia,
42
+ pushNotifications,
43
+ mediaDir,
44
+ stop: () => app.sessions.abortExchange(),
45
+ });
46
+ app.bootstrap("media-dir", () => ensureMediaDir(mediaDir, app.log));
47
+ app.channels.register(channel);
48
+ const allowedRoots = [
49
+ ...new Set([
50
+ app.workspace.root,
51
+ tmpdir(),
52
+ ...extraFileRoots.map((root) => resolve(expandHome(root))),
53
+ ]),
54
+ ];
55
+ app.agent.use((pi) => {
56
+ registerTelegramTools(pi, {
57
+ api: bot.api,
58
+ chatId,
59
+ workspaceRoot: app.workspace.root,
60
+ allowedRoots,
61
+ getLastInboundMessageId: () => channel.lastInboundMessageId,
62
+ getLastOutboundMessageId: () => channel.lastOutboundMessageId,
63
+ });
64
+ });
65
+ },
66
+ });
67
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/telegram/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,oBAAoB,GAAG,IAAI,CAAC,MAAM,CAAC;IACvC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC;IACzF,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IAC/E,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;IAC5F,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC;QAC9B,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,mEAAmE;KACjF,CAAC;IACF,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QACxC,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,uDAAuD;KACrE,CAAC;CACH,CAAC,CAAC;AAIH;;;;GAIG;AACH,eAAe,eAAe,CAAiB;IAC7C,IAAI,EAAE,UAAU;IAEhB,YAAY,EAAE,oBAAoB;IAElC,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,eAAe,CAAC;QAEhG,IAAI,QAAQ,KAAK,EAAE,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,GAAG,CAAC,GAAG,CAAC,IAAI,CACV,iFAAiF,CAClF,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE;YACvC,MAAM;YACN,UAAU;YACV,iBAAiB;YACjB,QAAQ;YACR,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,EAAE;SACzC,CAAC,CAAC;QAEH,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE/B,MAAM,YAAY,GAAG;YACnB,GAAG,IAAI,GAAG,CAAC;gBACT,GAAG,CAAC,SAAS,CAAC,IAAI;gBAClB,MAAM,EAAE;gBACR,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;aAC3D,CAAC;SACH,CAAC;QAEF,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;YACnB,qBAAqB,CAAC,EAAE,EAAE;gBACxB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM;gBACN,aAAa,EAAE,GAAG,CAAC,SAAS,CAAC,IAAI;gBACjC,YAAY;gBACZ,uBAAuB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,oBAAoB;gBAC3D,wBAAwB,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,qBAAqB;aAC9D,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { Message } from "grammy/types";
2
+ import type { MediaAttachment, MediaKind } from "../../domain/message.ts";
3
+ import type { Logger } from "../../log.ts";
4
+ export declare const TELEGRAM_MAX_DOWNLOAD_BYTES: number;
5
+ export type MediaMessage = Pick<Message, "animation" | "sticker" | "video_note" | "photo" | "voice" | "video" | "audio" | "document">;
6
+ export interface ResolvedMedia {
7
+ kind: MediaKind;
8
+ label: string;
9
+ fileId: string;
10
+ fileSize: number | null;
11
+ fileName: string | null;
12
+ extension: string;
13
+ mimeType: string | null;
14
+ metadata: Record<string, unknown>;
15
+ /** Human-readable facts for the attachment description, e.g. "800 × 600". */
16
+ summary: string[];
17
+ }
18
+ export declare class MediaTooLargeError extends Error {
19
+ readonly fileSize: number;
20
+ constructor(fileSize: number);
21
+ }
22
+ /**
23
+ * Resolve the first matching media payload on a message, in priority order:
24
+ * animation before document (animations set both fields), video_note before
25
+ * video (defensive), document last (most generic).
26
+ */
27
+ export declare const resolveMedia: (message: MediaMessage) => ResolvedMedia | null;
28
+ /** Unique filename: original names are preserved behind a random prefix. */
29
+ export declare const generateMediaFilename: (media: Pick<ResolvedMedia, "fileName" | "extension">) => string;
30
+ export declare const buildAttachment: (media: ResolvedMedia, path: string) => MediaAttachment;
31
+ export interface FileApi {
32
+ getFile(fileId: string): Promise<{
33
+ file_path?: string;
34
+ }>;
35
+ }
36
+ /** Download a Telegram file to disk, pre-checking the bot download size limit. */
37
+ export declare const downloadMedia: (api: FileApi, token: string, media: Pick<ResolvedMedia, "fileId" | "fileSize">, destPath: string) => Promise<void>;
38
+ /** Bootstrap: ensure the media directory exists and prune stale downloads. */
39
+ export declare const ensureMediaDir: (mediaDir: string, log: Logger) => Promise<void>;
@@ -0,0 +1,223 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { mkdir, readdir, stat, unlink, writeFile } from "node:fs/promises";
3
+ import { dirname, extname, join } from "node:path";
4
+ // Telegram bots can only download files up to 20 MB.
5
+ export const TELEGRAM_MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;
6
+ const MEDIA_RETENTION_DAYS = 30;
7
+ export class MediaTooLargeError extends Error {
8
+ fileSize;
9
+ constructor(fileSize) {
10
+ super(`File too large to download (${(fileSize / (1024 * 1024)).toFixed(1)} MB). ` +
11
+ "Telegram bots can only download files up to 20 MB.");
12
+ this.fileSize = fileSize;
13
+ }
14
+ }
15
+ /**
16
+ * Resolve the first matching media payload on a message, in priority order:
17
+ * animation before document (animations set both fields), video_note before
18
+ * video (defensive), document last (most generic).
19
+ */
20
+ export const resolveMedia = (message) => {
21
+ if (message.animation != null) {
22
+ const media = message.animation;
23
+ return {
24
+ kind: "animation",
25
+ label: "Animation",
26
+ fileId: media.file_id,
27
+ fileSize: media.file_size ?? null,
28
+ fileName: media.file_name ?? null,
29
+ extension: ".mp4",
30
+ mimeType: media.mime_type ?? "video/mp4",
31
+ metadata: { width: media.width, height: media.height, duration: media.duration },
32
+ summary: [`${media.duration} seconds`, `${media.width} × ${media.height}`],
33
+ };
34
+ }
35
+ if (message.sticker != null) {
36
+ const media = message.sticker;
37
+ const format = media.is_animated
38
+ ? { extension: ".tgs", mimeType: "application/x-tgsticker", description: "animated (.tgs)" }
39
+ : media.is_video
40
+ ? { extension: ".webm", mimeType: "video/webm", description: "video (.webm)" }
41
+ : { extension: ".webp", mimeType: "image/webp", description: "regular (.webp)" };
42
+ return {
43
+ kind: "sticker",
44
+ label: "Sticker",
45
+ fileId: media.file_id,
46
+ fileSize: media.file_size ?? null,
47
+ fileName: null,
48
+ extension: format.extension,
49
+ mimeType: format.mimeType,
50
+ metadata: {
51
+ ...(media.emoji != null ? { emoji: media.emoji } : {}),
52
+ format: format.description,
53
+ },
54
+ summary: [
55
+ ...(media.emoji != null ? [`Emoji: ${media.emoji}`] : []),
56
+ `Format: ${format.description}`,
57
+ ],
58
+ };
59
+ }
60
+ if (message.video_note != null) {
61
+ const media = message.video_note;
62
+ // The domain has no dedicated video-note kind — closest is video.
63
+ return {
64
+ kind: "video",
65
+ label: "Video note",
66
+ fileId: media.file_id,
67
+ fileSize: media.file_size ?? null,
68
+ fileName: null,
69
+ extension: ".mp4",
70
+ mimeType: "video/mp4",
71
+ metadata: { duration: media.duration, diameter: media.length },
72
+ summary: [`${media.duration} seconds`, `Diameter: ${media.length}px`],
73
+ };
74
+ }
75
+ const photo = message.photo?.at(-1);
76
+ if (photo != null) {
77
+ return {
78
+ kind: "photo",
79
+ label: "Photo",
80
+ fileId: photo.file_id,
81
+ fileSize: photo.file_size ?? null,
82
+ fileName: null,
83
+ extension: ".jpg",
84
+ mimeType: "image/jpeg",
85
+ metadata: { width: photo.width, height: photo.height },
86
+ summary: [`${photo.width} × ${photo.height}`, formatFileSize(photo.file_size ?? null)],
87
+ };
88
+ }
89
+ if (message.voice != null) {
90
+ const media = message.voice;
91
+ return {
92
+ kind: "voice",
93
+ label: "Voice message",
94
+ fileId: media.file_id,
95
+ fileSize: media.file_size ?? null,
96
+ fileName: null,
97
+ extension: ".ogg",
98
+ mimeType: media.mime_type ?? "audio/ogg",
99
+ metadata: { duration: media.duration },
100
+ summary: [`${media.duration} seconds`, ...(media.mime_type != null ? [media.mime_type] : [])],
101
+ };
102
+ }
103
+ if (message.video != null) {
104
+ const media = message.video;
105
+ return {
106
+ kind: "video",
107
+ label: "Video",
108
+ fileId: media.file_id,
109
+ fileSize: media.file_size ?? null,
110
+ fileName: media.file_name ?? null,
111
+ extension: extensionFromName(media.file_name, ".mp4"),
112
+ mimeType: media.mime_type ?? null,
113
+ metadata: { width: media.width, height: media.height, duration: media.duration },
114
+ summary: [`${media.duration} seconds`, `${media.width} × ${media.height}`],
115
+ };
116
+ }
117
+ if (message.audio != null) {
118
+ const media = message.audio;
119
+ return {
120
+ kind: "audio",
121
+ label: "Audio file",
122
+ fileId: media.file_id,
123
+ fileSize: media.file_size ?? null,
124
+ fileName: media.file_name ?? null,
125
+ extension: extensionFromName(media.file_name, ".mp3"),
126
+ mimeType: media.mime_type ?? null,
127
+ metadata: {
128
+ duration: media.duration,
129
+ ...(media.title != null ? { title: media.title } : {}),
130
+ ...(media.performer != null ? { performer: media.performer } : {}),
131
+ },
132
+ summary: [
133
+ ...(media.title != null ? [media.title] : []),
134
+ ...(media.performer != null ? [media.performer] : []),
135
+ `${media.duration} seconds`,
136
+ ],
137
+ };
138
+ }
139
+ if (message.document != null) {
140
+ const media = message.document;
141
+ return {
142
+ kind: "document",
143
+ label: "Document",
144
+ fileId: media.file_id,
145
+ fileSize: media.file_size ?? null,
146
+ fileName: media.file_name ?? null,
147
+ extension: extensionFromName(media.file_name, ""),
148
+ mimeType: media.mime_type ?? null,
149
+ metadata: { ...(media.file_name != null ? { fileName: media.file_name } : {}) },
150
+ summary: [
151
+ ...(media.file_name != null ? [media.file_name] : []),
152
+ ...(media.mime_type != null ? [media.mime_type] : []),
153
+ ...(media.file_size != null ? [formatFileSize(media.file_size)] : []),
154
+ ],
155
+ };
156
+ }
157
+ return null;
158
+ };
159
+ /** Unique filename: original names are preserved behind a random prefix. */
160
+ export const generateMediaFilename = (media) => {
161
+ const unique = randomBytes(6).toString("hex");
162
+ return media.fileName != null ? `${unique}-${media.fileName}` : `${unique}${media.extension}`;
163
+ };
164
+ export const buildAttachment = (media, path) => ({
165
+ kind: media.kind,
166
+ path,
167
+ ...(media.mimeType != null ? { mimeType: media.mimeType } : {}),
168
+ description: `${media.label} (${media.summary.join(", ")})`,
169
+ metadata: { ...media.metadata, ...(media.fileSize != null ? { fileSize: media.fileSize } : {}) },
170
+ });
171
+ /** Download a Telegram file to disk, pre-checking the bot download size limit. */
172
+ export const downloadMedia = async (api, token, media, destPath) => {
173
+ if (media.fileSize != null && media.fileSize > TELEGRAM_MAX_DOWNLOAD_BYTES) {
174
+ throw new MediaTooLargeError(media.fileSize);
175
+ }
176
+ const file = await api.getFile(media.fileId);
177
+ if (file.file_path == null)
178
+ throw new Error("Telegram returned no file path for download");
179
+ const response = await fetch(`https://api.telegram.org/file/bot${token}/${file.file_path}`);
180
+ if (!response.ok)
181
+ throw new Error(`File download failed with status ${response.status}`);
182
+ await mkdir(dirname(destPath), { recursive: true });
183
+ await writeFile(destPath, Buffer.from(await response.arrayBuffer()));
184
+ };
185
+ /** Bootstrap: ensure the media directory exists and prune stale downloads. */
186
+ export const ensureMediaDir = async (mediaDir, log) => {
187
+ await mkdir(mediaDir, { recursive: true });
188
+ const cutoff = Date.now() - MEDIA_RETENTION_DAYS * 24 * 60 * 60 * 1000;
189
+ let cleaned = 0;
190
+ for (const entry of await readdir(mediaDir, { withFileTypes: true })) {
191
+ if (!entry.isFile())
192
+ continue;
193
+ const path = join(mediaDir, entry.name);
194
+ try {
195
+ if ((await stat(path)).mtimeMs < cutoff) {
196
+ await unlink(path);
197
+ cleaned += 1;
198
+ }
199
+ }
200
+ catch (error) {
201
+ log.warn({ err: error, path }, "failed to clean old media file");
202
+ }
203
+ }
204
+ log.debug({ mediaDir, cleaned }, "media directory ready");
205
+ };
206
+ const extensionFromName = (fileName, fallback) => {
207
+ if (fileName != null) {
208
+ const extension = extname(fileName);
209
+ if (extension !== "")
210
+ return extension;
211
+ }
212
+ return fallback;
213
+ };
214
+ const formatFileSize = (size) => {
215
+ if (size == null)
216
+ return "size unknown";
217
+ if (size < 1024)
218
+ return `${size} B`;
219
+ if (size < 1024 * 1024)
220
+ return `${Math.round(size / 1024)} KB`;
221
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
222
+ };
223
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/extensions/telegram/media.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Promise-chain mutex serializing channel deliveries: callers queue FIFO
3
+ * behind the in-flight task, so two send sequences never interleave their
4
+ * Telegram API calls.
5
+ */
6
+ export declare class Mutex {
7
+ private tail;
8
+ run<T>(task: () => Promise<T>): Promise<T>;
9
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Promise-chain mutex serializing channel deliveries: callers queue FIFO
3
+ * behind the in-flight task, so two send sequences never interleave their
4
+ * Telegram API calls.
5
+ */
6
+ export class Mutex {
7
+ tail = Promise.resolve();
8
+ run(task) {
9
+ const result = this.tail.then(() => task());
10
+ this.tail = result.then(() => undefined, () => undefined);
11
+ return result;
12
+ }
13
+ }
14
+ //# sourceMappingURL=mutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../../src/extensions/telegram/mutex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,KAAK;IACR,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEhD,GAAG,CAAI,IAAsB;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QAE5C,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CACrB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,48 @@
1
+ import type { Logger } from "../../log.ts";
2
+ export interface SendMessageOptions {
3
+ parse_mode?: "Markdown";
4
+ disable_notification?: boolean;
5
+ }
6
+ export interface EditMessageOptions {
7
+ parse_mode?: "Markdown";
8
+ }
9
+ /** Narrow grammY API surface the channel sends through — fakeable in tests. */
10
+ export interface SendApi {
11
+ sendMessage(chatId: number, text: string, other?: SendMessageOptions): Promise<{
12
+ message_id: number;
13
+ }>;
14
+ sendChatAction(chatId: number, action: "typing"): Promise<unknown>;
15
+ copyMessage(chatId: number, fromChatId: number, messageId: number): Promise<{
16
+ message_id: number;
17
+ }>;
18
+ deleteMessage(chatId: number, messageId: number): Promise<unknown>;
19
+ editMessageText(chatId: number, messageId: number, text: string, other?: EditMessageOptions): Promise<unknown>;
20
+ }
21
+ export declare const isMarkdownParseError: (error: unknown) => boolean;
22
+ /** Telegram rejects edits whose content matches the current message — benign. */
23
+ export declare const isMessageNotModifiedError: (error: unknown) => boolean;
24
+ /** Try Markdown first; on a Telegram entity-parse rejection resend as plain text. */
25
+ export declare const sendWithMarkdownFallback: (api: Pick<SendApi, "sendMessage">, chatId: number, text: string, options?: {
26
+ silent?: boolean;
27
+ }) => Promise<number>;
28
+ /**
29
+ * Edit a message trying Markdown first, falling back to plain text on a parse
30
+ * rejection. "Message is not modified" rejections are swallowed — the visible
31
+ * content already matches.
32
+ */
33
+ export declare const editWithMarkdownFallback: (api: Pick<SendApi, "editMessageText">, chatId: number, messageId: number, text: string) => Promise<void>;
34
+ /** Send text as one or more messages, split at Telegram's length limit. */
35
+ export declare const sendChunked: (api: Pick<SendApi, "sendMessage">, chatId: number, text: string, options?: {
36
+ silent?: boolean;
37
+ }) => Promise<number[]>;
38
+ /**
39
+ * Copy+delete a silently-sent message so the copy fires a push notification.
40
+ * Copy-first ordering keeps the
41
+ * text safe: on copy failure the original is preserved and no delete runs; on
42
+ * delete failure the duplicate is accepted after retries.
43
+ */
44
+ export declare const notifyViaCopyDelete: (api: Pick<SendApi, "copyMessage" | "deleteMessage">, chatId: number, messageId: number, log: Logger, retryDelayMs?: number) => Promise<number | null>;
45
+ /** Full background-delivery flow: silent chunked send, then copy+delete to fire the push. */
46
+ export declare const deliverText: (api: SendApi, chatId: number, text: string, pushNotifications: boolean, log: Logger, retryDelayMs?: number) => Promise<number | null>;
47
+ /** Show a typing indicator until the returned stop function is called. */
48
+ export declare const startTyping: (api: Pick<SendApi, "sendChatAction">, chatId: number, log: Logger) => (() => void);