opensquid 0.5.441 → 0.5.447

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 (380) hide show
  1. package/README.md +1 -0
  2. package/dist/functions/arm_scope.d.ts +27 -0
  3. package/dist/functions/arm_scope.d.ts.map +1 -0
  4. package/dist/functions/arm_scope.js +52 -0
  5. package/dist/functions/arm_scope.js.map +1 -0
  6. package/dist/functions/index.d.ts +1 -0
  7. package/dist/functions/index.d.ts.map +1 -1
  8. package/dist/functions/index.js +1 -0
  9. package/dist/functions/index.js.map +1 -1
  10. package/dist/runtime/bootstrap.d.ts.map +1 -1
  11. package/dist/runtime/bootstrap.js +2 -0
  12. package/dist/runtime/bootstrap.js.map +1 -1
  13. package/dist/runtime/handoff/render.d.ts +5 -4
  14. package/dist/runtime/handoff/render.d.ts.map +1 -1
  15. package/dist/runtime/handoff/render.js +7 -7
  16. package/dist/runtime/handoff/render.js.map +1 -1
  17. package/dist/runtime/hooks/active_task_mirror.js +0 -0
  18. package/dist/runtime/hooks/apply_patch.js +0 -0
  19. package/dist/runtime/hooks/dispatch.js +0 -0
  20. package/dist/runtime/hooks/hook_output.js +0 -0
  21. package/dist/runtime/hooks/memory_reconcile.js +0 -0
  22. package/dist/runtime/hooks/new_project_detect.js +0 -0
  23. package/dist/runtime/hooks/profession_resolver.js +0 -0
  24. package/dist/runtime/hooks/scope_intent.js +0 -0
  25. package/dist/runtime/hooks/session_id.js +0 -0
  26. package/dist/runtime/hooks/session_liveness.js +0 -0
  27. package/dist/runtime/hooks/stop_drive.js +0 -0
  28. package/dist/runtime/hooks/stop_stream.js +0 -0
  29. package/dist/runtime/hooks/subagent_guard.js +0 -0
  30. package/dist/runtime/hooks/transcript.js +0 -0
  31. package/dist/runtime/hooks/transcript_tasks.js +0 -0
  32. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
  33. package/dist/runtime/ralph/orchestrator.js +2 -1
  34. package/dist/runtime/ralph/orchestrator.js.map +1 -1
  35. package/dist/setup/cli/limits_state.d.ts.map +1 -1
  36. package/dist/setup/cli/limits_state.js +6 -40
  37. package/dist/setup/cli/limits_state.js.map +1 -1
  38. package/dist/setup/cli/pack_walk.d.ts +32 -0
  39. package/dist/setup/cli/pack_walk.d.ts.map +1 -0
  40. package/dist/setup/cli/pack_walk.js +76 -0
  41. package/dist/setup/cli/pack_walk.js.map +1 -0
  42. package/dist/setup/cli/permissions_state.d.ts.map +1 -1
  43. package/dist/setup/cli/permissions_state.js +6 -37
  44. package/dist/setup/cli/permissions_state.js.map +1 -1
  45. package/dist/setup/cli/triggers_state.d.ts.map +1 -1
  46. package/dist/setup/cli/triggers_state.js +3 -29
  47. package/dist/setup/cli/triggers_state.js.map +1 -1
  48. package/dist/workgraph/events.d.ts.map +1 -1
  49. package/dist/workgraph/events.js +10 -0
  50. package/dist/workgraph/events.js.map +1 -1
  51. package/dist/workgraph/store.d.ts.map +1 -1
  52. package/dist/workgraph/store.js +5 -0
  53. package/dist/workgraph/store.js.map +1 -1
  54. package/dist/workgraph/types.d.ts +2 -1
  55. package/dist/workgraph/types.d.ts.map +1 -1
  56. package/docs/ARCHITECTURE.md +268 -0
  57. package/package.json +5 -3
  58. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
  59. package/dist/anti-drift/evaluator.d.ts +0 -88
  60. package/dist/anti-drift/evaluator.d.ts.map +0 -1
  61. package/dist/anti-drift/evaluator.js +0 -417
  62. package/dist/anti-drift/evaluator.js.map +0 -1
  63. package/dist/anti-drift/evaluator.test.js +0 -78
  64. package/dist/anti-drift/rules.d.ts +0 -80
  65. package/dist/anti-drift/rules.d.ts.map +0 -1
  66. package/dist/anti-drift/rules.js +0 -368
  67. package/dist/anti-drift/rules.js.map +0 -1
  68. package/dist/anti-drift/rules.test.js +0 -213
  69. package/dist/anti-drift/state.d.ts +0 -107
  70. package/dist/anti-drift/state.d.ts.map +0 -1
  71. package/dist/anti-drift/state.js +0 -177
  72. package/dist/anti-drift/state.js.map +0 -1
  73. package/dist/anti-drift/state.test.js +0 -120
  74. package/dist/chat/adapters/discord.d.ts +0 -41
  75. package/dist/chat/adapters/discord.d.ts.map +0 -1
  76. package/dist/chat/adapters/discord.js +0 -176
  77. package/dist/chat/adapters/discord.js.map +0 -1
  78. package/dist/chat/adapters/discord.test.js +0 -25
  79. package/dist/chat/adapters/slack.d.ts +0 -43
  80. package/dist/chat/adapters/slack.d.ts.map +0 -1
  81. package/dist/chat/adapters/slack.js +0 -172
  82. package/dist/chat/adapters/slack.js.map +0 -1
  83. package/dist/chat/adapters/slack.test.js +0 -30
  84. package/dist/chat/adapters/telegram.d.ts +0 -148
  85. package/dist/chat/adapters/telegram.d.ts.map +0 -1
  86. package/dist/chat/adapters/telegram.js +0 -498
  87. package/dist/chat/adapters/telegram.js.map +0 -1
  88. package/dist/chat/adapters/telegram.test.js +0 -94
  89. package/dist/chat/config.d.ts +0 -98
  90. package/dist/chat/config.d.ts.map +0 -1
  91. package/dist/chat/config.js +0 -185
  92. package/dist/chat/config.js.map +0 -1
  93. package/dist/chat/daemon/active-project.d.ts +0 -17
  94. package/dist/chat/daemon/active-project.d.ts.map +0 -1
  95. package/dist/chat/daemon/active-project.js +0 -23
  96. package/dist/chat/daemon/active-project.js.map +0 -1
  97. package/dist/chat/daemon/autospawn.d.ts +0 -40
  98. package/dist/chat/daemon/autospawn.d.ts.map +0 -1
  99. package/dist/chat/daemon/autospawn.js +0 -129
  100. package/dist/chat/daemon/autospawn.js.map +0 -1
  101. package/dist/chat/daemon/autospawn.test.js +0 -112
  102. package/dist/chat/daemon/cli.d.ts +0 -18
  103. package/dist/chat/daemon/cli.d.ts.map +0 -1
  104. package/dist/chat/daemon/cli.js +0 -71
  105. package/dist/chat/daemon/cli.js.map +0 -1
  106. package/dist/chat/daemon/collisions.js +0 -384
  107. package/dist/chat/daemon/health-check.d.ts +0 -69
  108. package/dist/chat/daemon/health-check.d.ts.map +0 -1
  109. package/dist/chat/daemon/health-check.js +0 -112
  110. package/dist/chat/daemon/health-check.js.map +0 -1
  111. package/dist/chat/daemon/inbox-read.d.ts +0 -35
  112. package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
  113. package/dist/chat/daemon/inbox-read.js +0 -75
  114. package/dist/chat/daemon/inbox-read.js.map +0 -1
  115. package/dist/chat/daemon/inbox-read.test.js +0 -97
  116. package/dist/chat/daemon/inbox.d.ts +0 -63
  117. package/dist/chat/daemon/inbox.d.ts.map +0 -1
  118. package/dist/chat/daemon/inbox.js +0 -56
  119. package/dist/chat/daemon/inbox.js.map +0 -1
  120. package/dist/chat/daemon/inbox.test.js +0 -110
  121. package/dist/chat/daemon/lifecycle.d.ts +0 -71
  122. package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
  123. package/dist/chat/daemon/lifecycle.js +0 -221
  124. package/dist/chat/daemon/lifecycle.js.map +0 -1
  125. package/dist/chat/daemon/lifecycle.test.js +0 -163
  126. package/dist/chat/daemon/protocol.d.ts +0 -107
  127. package/dist/chat/daemon/protocol.d.ts.map +0 -1
  128. package/dist/chat/daemon/protocol.js +0 -54
  129. package/dist/chat/daemon/protocol.js.map +0 -1
  130. package/dist/chat/daemon/routing.d.ts +0 -140
  131. package/dist/chat/daemon/routing.d.ts.map +0 -1
  132. package/dist/chat/daemon/routing.js +0 -198
  133. package/dist/chat/daemon/routing.js.map +0 -1
  134. package/dist/chat/daemon/routing.test.js +0 -259
  135. package/dist/chat/daemon/rpc-client.d.ts +0 -45
  136. package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
  137. package/dist/chat/daemon/rpc-client.js +0 -133
  138. package/dist/chat/daemon/rpc-client.js.map +0 -1
  139. package/dist/chat/daemon/rpc-server.d.ts +0 -39
  140. package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
  141. package/dist/chat/daemon/rpc-server.js +0 -385
  142. package/dist/chat/daemon/rpc-server.js.map +0 -1
  143. package/dist/chat/daemon/rpc.test.js +0 -177
  144. package/dist/chat/daemon/subscribers.js +0 -257
  145. package/dist/chat/daemon/worker.d.ts +0 -27
  146. package/dist/chat/daemon/worker.d.ts.map +0 -1
  147. package/dist/chat/daemon/worker.js +0 -313
  148. package/dist/chat/daemon/worker.js.map +0 -1
  149. package/dist/chat/daemon/workspace-topic.js +0 -324
  150. package/dist/chat/env-token.d.ts +0 -60
  151. package/dist/chat/env-token.d.ts.map +0 -1
  152. package/dist/chat/env-token.js +0 -137
  153. package/dist/chat/env-token.js.map +0 -1
  154. package/dist/chat/env-token.test.js +0 -160
  155. package/dist/chat/factory.d.ts +0 -30
  156. package/dist/chat/factory.d.ts.map +0 -1
  157. package/dist/chat/factory.js +0 -50
  158. package/dist/chat/factory.js.map +0 -1
  159. package/dist/chat/factory.test.js +0 -55
  160. package/dist/chat/gateway.d.ts +0 -176
  161. package/dist/chat/gateway.d.ts.map +0 -1
  162. package/dist/chat/gateway.js +0 -146
  163. package/dist/chat/gateway.js.map +0 -1
  164. package/dist/chat/gateway.test.js +0 -192
  165. package/dist/claude-md.d.ts +0 -39
  166. package/dist/claude-md.d.ts.map +0 -1
  167. package/dist/claude-md.js +0 -113
  168. package/dist/claude-md.js.map +0 -1
  169. package/dist/claude-md.test.js +0 -91
  170. package/dist/codex/activate.d.ts +0 -66
  171. package/dist/codex/activate.d.ts.map +0 -1
  172. package/dist/codex/activate.js +0 -329
  173. package/dist/codex/activate.js.map +0 -1
  174. package/dist/codex/activate.test.js +0 -229
  175. package/dist/codex/bundled-default/bundled-default.test.js +0 -161
  176. package/dist/codex/cli-publish.test.js +0 -133
  177. package/dist/codex/cli.d.ts +0 -35
  178. package/dist/codex/cli.d.ts.map +0 -1
  179. package/dist/codex/cli.js +0 -554
  180. package/dist/codex/cli.js.map +0 -1
  181. package/dist/codex/cli.test.js +0 -277
  182. package/dist/codex/import-skill-md.d.ts +0 -53
  183. package/dist/codex/import-skill-md.d.ts.map +0 -1
  184. package/dist/codex/import-skill-md.js +0 -236
  185. package/dist/codex/import-skill-md.js.map +0 -1
  186. package/dist/codex/import-skill-md.test.js +0 -225
  187. package/dist/codex/loader.d.ts +0 -27
  188. package/dist/codex/loader.d.ts.map +0 -1
  189. package/dist/codex/loader.js +0 -86
  190. package/dist/codex/loader.js.map +0 -1
  191. package/dist/codex/loader.test.js +0 -75
  192. package/dist/codex/parse.d.ts +0 -28
  193. package/dist/codex/parse.d.ts.map +0 -1
  194. package/dist/codex/parse.js +0 -309
  195. package/dist/codex/parse.js.map +0 -1
  196. package/dist/codex/parse.test.js +0 -241
  197. package/dist/codex/store.d.ts +0 -87
  198. package/dist/codex/store.d.ts.map +0 -1
  199. package/dist/codex/store.js +0 -205
  200. package/dist/codex/store.js.map +0 -1
  201. package/dist/codex/store.test.js +0 -242
  202. package/dist/codex/types.d.ts +0 -398
  203. package/dist/codex/types.d.ts.map +0 -1
  204. package/dist/codex/types.js +0 -21
  205. package/dist/codex/types.js.map +0 -1
  206. package/dist/config.d.ts +0 -53
  207. package/dist/config.d.ts.map +0 -1
  208. package/dist/config.js +0 -202
  209. package/dist/config.js.map +0 -1
  210. package/dist/config.test.js +0 -117
  211. package/dist/engine/cli.d.ts +0 -14
  212. package/dist/engine/cli.d.ts.map +0 -1
  213. package/dist/engine/cli.js +0 -171
  214. package/dist/engine/cli.js.map +0 -1
  215. package/dist/engine/client.d.ts +0 -219
  216. package/dist/engine/client.d.ts.map +0 -1
  217. package/dist/engine/client.js +0 -312
  218. package/dist/engine/client.js.map +0 -1
  219. package/dist/engine/config.d.ts +0 -62
  220. package/dist/engine/config.d.ts.map +0 -1
  221. package/dist/engine/config.js +0 -223
  222. package/dist/engine/config.js.map +0 -1
  223. package/dist/engine/index.d.ts +0 -17
  224. package/dist/engine/index.d.ts.map +0 -1
  225. package/dist/engine/index.js +0 -16
  226. package/dist/engine/index.js.map +0 -1
  227. package/dist/engine/resolver.d.ts +0 -62
  228. package/dist/engine/resolver.d.ts.map +0 -1
  229. package/dist/engine/resolver.js +0 -103
  230. package/dist/engine/resolver.js.map +0 -1
  231. package/dist/engine/singleton.d.ts +0 -95
  232. package/dist/engine/singleton.d.ts.map +0 -1
  233. package/dist/engine/singleton.js +0 -325
  234. package/dist/engine/singleton.js.map +0 -1
  235. package/dist/engine/types.d.ts +0 -402
  236. package/dist/engine/types.d.ts.map +0 -1
  237. package/dist/engine/types.js +0 -22
  238. package/dist/engine/types.js.map +0 -1
  239. package/dist/engine-binary-resolver.js +0 -110
  240. package/dist/engine-binary-resolver.test.js +0 -61
  241. package/dist/engine-cli.js +0 -60
  242. package/dist/engine-client.js +0 -301
  243. package/dist/engine-client.test.js +0 -118
  244. package/dist/functions/chain_state.d.ts +0 -51
  245. package/dist/functions/chain_state.d.ts.map +0 -1
  246. package/dist/functions/chain_state.js +0 -59
  247. package/dist/functions/chain_state.js.map +0 -1
  248. package/dist/hooks/drift-catalog.d.ts +0 -68
  249. package/dist/hooks/drift-catalog.d.ts.map +0 -1
  250. package/dist/hooks/drift-catalog.js +0 -184
  251. package/dist/hooks/drift-catalog.js.map +0 -1
  252. package/dist/hooks/drift-catalog.test.js +0 -154
  253. package/dist/hooks/drift-patterns.d.ts +0 -110
  254. package/dist/hooks/drift-patterns.d.ts.map +0 -1
  255. package/dist/hooks/drift-patterns.js +0 -289
  256. package/dist/hooks/drift-patterns.js.map +0 -1
  257. package/dist/hooks/drift-patterns.test.js +0 -325
  258. package/dist/hooks/engine-vocab-gate.d.ts +0 -108
  259. package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
  260. package/dist/hooks/engine-vocab-gate.js +0 -225
  261. package/dist/hooks/engine-vocab-gate.js.map +0 -1
  262. package/dist/hooks/engine-vocab-gate.test.js +0 -170
  263. package/dist/hooks/heartbeat.d.ts +0 -107
  264. package/dist/hooks/heartbeat.d.ts.map +0 -1
  265. package/dist/hooks/heartbeat.js +0 -316
  266. package/dist/hooks/heartbeat.js.map +0 -1
  267. package/dist/hooks/heartbeat.test.js +0 -393
  268. package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
  269. package/dist/hooks/honesty-ledger.d.ts +0 -123
  270. package/dist/hooks/honesty-ledger.d.ts.map +0 -1
  271. package/dist/hooks/honesty-ledger.js +0 -226
  272. package/dist/hooks/honesty-ledger.js.map +0 -1
  273. package/dist/hooks/honesty-ledger.test.js +0 -466
  274. package/dist/hooks/inline-report-check.d.ts +0 -63
  275. package/dist/hooks/inline-report-check.d.ts.map +0 -1
  276. package/dist/hooks/inline-report-check.js +0 -88
  277. package/dist/hooks/inline-report-check.js.map +0 -1
  278. package/dist/hooks/inline-report-check.test.js +0 -96
  279. package/dist/hooks/pre-tool-use.d.ts +0 -62
  280. package/dist/hooks/pre-tool-use.d.ts.map +0 -1
  281. package/dist/hooks/pre-tool-use.js +0 -342
  282. package/dist/hooks/pre-tool-use.js.map +0 -1
  283. package/dist/hooks/pre-tool-use.test.js +0 -134
  284. package/dist/hooks/session-end.d.ts +0 -15
  285. package/dist/hooks/session-end.d.ts.map +0 -1
  286. package/dist/hooks/session-end.js +0 -60
  287. package/dist/hooks/session-end.js.map +0 -1
  288. package/dist/hooks/session-end.test.js +0 -52
  289. package/dist/hooks/stop.d.ts +0 -35
  290. package/dist/hooks/stop.d.ts.map +0 -1
  291. package/dist/hooks/stop.js +0 -136
  292. package/dist/hooks/stop.js.map +0 -1
  293. package/dist/hooks/transcript-active-task.test.js +0 -342
  294. package/dist/hooks/transcript.d.ts +0 -26
  295. package/dist/hooks/transcript.d.ts.map +0 -1
  296. package/dist/hooks/transcript.js +0 -266
  297. package/dist/hooks/transcript.js.map +0 -1
  298. package/dist/hooks/transcript.test.js +0 -103
  299. package/dist/hooks/user-prompt-submit.d.ts +0 -74
  300. package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
  301. package/dist/hooks/user-prompt-submit.js +0 -256
  302. package/dist/hooks/user-prompt-submit.js.map +0 -1
  303. package/dist/hooks/user-prompt-submit.test.js +0 -118
  304. package/dist/hooks/versioning-gate.d.ts +0 -101
  305. package/dist/hooks/versioning-gate.d.ts.map +0 -1
  306. package/dist/hooks/versioning-gate.js +0 -245
  307. package/dist/hooks/versioning-gate.js.map +0 -1
  308. package/dist/hooks/versioning-gate.test.js +0 -368
  309. package/dist/hooks/workflow-gate.d.ts +0 -64
  310. package/dist/hooks/workflow-gate.d.ts.map +0 -1
  311. package/dist/hooks/workflow-gate.js +0 -152
  312. package/dist/hooks/workflow-gate.js.map +0 -1
  313. package/dist/hooks/workflow-gate.test.js +0 -197
  314. package/dist/hooks-cli.d.ts +0 -25
  315. package/dist/hooks-cli.d.ts.map +0 -1
  316. package/dist/hooks-cli.js +0 -286
  317. package/dist/hooks-cli.js.map +0 -1
  318. package/dist/hooks-cli.test.js +0 -148
  319. package/dist/origin.d.ts +0 -16
  320. package/dist/origin.d.ts.map +0 -1
  321. package/dist/origin.js +0 -92
  322. package/dist/origin.js.map +0 -1
  323. package/dist/packs/seed_lessons_ingest.d.ts +0 -30
  324. package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
  325. package/dist/packs/seed_lessons_ingest.js +0 -107
  326. package/dist/packs/seed_lessons_ingest.js.map +0 -1
  327. package/dist/project-cli.d.ts +0 -7
  328. package/dist/project-cli.d.ts.map +0 -1
  329. package/dist/project-cli.js +0 -145
  330. package/dist/project-cli.js.map +0 -1
  331. package/dist/project.d.ts +0 -127
  332. package/dist/project.d.ts.map +0 -1
  333. package/dist/project.js +0 -281
  334. package/dist/project.js.map +0 -1
  335. package/dist/project.test.js +0 -287
  336. package/dist/rag/backends/loop_engine.d.ts +0 -61
  337. package/dist/rag/backends/loop_engine.d.ts.map +0 -1
  338. package/dist/rag/backends/loop_engine.js +0 -160
  339. package/dist/rag/backends/loop_engine.js.map +0 -1
  340. package/dist/recall.d.ts +0 -82
  341. package/dist/recall.d.ts.map +0 -1
  342. package/dist/recall.js +0 -81
  343. package/dist/recall.js.map +0 -1
  344. package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
  345. package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
  346. package/dist/runtime/agent_bridge/autospawn.js +0 -251
  347. package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
  348. package/dist/runtime/chain_state.d.ts +0 -124
  349. package/dist/runtime/chain_state.d.ts.map +0 -1
  350. package/dist/runtime/chain_state.js +0 -189
  351. package/dist/runtime/chain_state.js.map +0 -1
  352. package/dist/runtime/hooks/permission_decision.d.ts +0 -34
  353. package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
  354. package/dist/runtime/hooks/permission_decision.js +0 -39
  355. package/dist/runtime/hooks/permission_decision.js.map +0 -1
  356. package/dist/runtime/workflow_fsm.d.ts +0 -21
  357. package/dist/runtime/workflow_fsm.d.ts.map +0 -1
  358. package/dist/runtime/workflow_fsm.js +0 -25
  359. package/dist/runtime/workflow_fsm.js.map +0 -1
  360. package/dist/runtime/workflow_map.d.ts +0 -26
  361. package/dist/runtime/workflow_map.d.ts.map +0 -1
  362. package/dist/runtime/workflow_map.js +0 -38
  363. package/dist/runtime/workflow_map.js.map +0 -1
  364. package/dist/scope.d.ts +0 -48
  365. package/dist/scope.d.ts.map +0 -1
  366. package/dist/scope.js +0 -111
  367. package/dist/scope.js.map +0 -1
  368. package/dist/setup/cli/topic_create_step.d.ts +0 -84
  369. package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
  370. package/dist/setup/cli/topic_create_step.js +0 -213
  371. package/dist/setup/cli/topic_create_step.js.map +0 -1
  372. package/dist/system-export.d.ts +0 -65
  373. package/dist/system-export.d.ts.map +0 -1
  374. package/dist/system-export.js +0 -194
  375. package/dist/system-export.js.map +0 -1
  376. package/dist/utterance/classifier.d.ts +0 -53
  377. package/dist/utterance/classifier.d.ts.map +0 -1
  378. package/dist/utterance/classifier.js +0 -184
  379. package/dist/utterance/classifier.js.map +0 -1
  380. package/dist/utterance/classifier.test.js +0 -147
@@ -1,498 +0,0 @@
1
- /**
2
- * Telegram adapter — long-polling via the official Bot API (v0.7a).
3
- *
4
- * Rebuild path when you edit this file: `pnpm build` does NOT recompile
5
- * src.legacy/ (rootDir=src per tsconfig.build.json), but the chat-daemon
6
- * worker loads `dist/chat/adapters/telegram.js` at runtime. After
7
- * editing, regenerate the dist file with:
8
- *
9
- * pnpm exec tsc src.legacy/chat/adapters/telegram.ts \
10
- * --outDir dist --rootDir src.legacy \
11
- * --module NodeNext --moduleResolution NodeNext --target ES2022 \
12
- * --esModuleInterop --skipLibCheck --noEmitOnError false
13
- *
14
- * Then `node ./dist/index.js chat-daemon restart` to load the new code.
15
- * Without the restart, sends keep using the old in-memory adapter.
16
- *
17
- * SDK: `grammy` (modern, TS-first, actively maintained — recommended
18
- * over `telegraf` which is frozen at v4.16 from Feb 2024 and over
19
- * `node-telegram-bot-api` which is untyped).
20
- *
21
- * Connection: long-polling via `bot.start()` — no public webhook URL
22
- * required. The bot opens an outbound HTTPS connection to
23
- * `api.telegram.org` and polls for updates. Works behind any NAT.
24
- *
25
- * Why `grammy` is loaded via dynamic import: opensquid declares all
26
- * three chat SDKs as optionalDependencies. Users with no Telegram
27
- * config (and possibly `npm --omit=optional`) shouldn't pay the install
28
- * cost. The dynamic import only runs in `start()` when this adapter
29
- * is actually being activated — and produces a clear "install grammy"
30
- * error if the dep is missing.
31
- *
32
- * Gotcha: only ONE polling consumer per token at a time. A second
33
- * opensquid process with the same token will collide with 409 Conflict.
34
- * Surface that error clearly on start.
35
- */
36
- import { ChatGatewayError, formatChannelId, } from "../gateway.js";
37
- export class TelegramAdapter {
38
- config;
39
- platform = "telegram";
40
- bot = null;
41
- handlers = [];
42
- botUsername = "";
43
- botId = "";
44
- startPromise = null;
45
- /**
46
- * 0.7.4 (#147): true when the long-poll lost to a 409 Conflict
47
- * (another consumer holds the token — typically the Claude Code
48
- * `plugin:telegram` bun bot). Outbound sendMessage still works via
49
- * HTTPS, only inbound is dead. A periodic retry attempts to reclaim.
50
- */
51
- outboundOnly = false;
52
- /** 0.7.4 (#147): handle for the periodic long-poll retry timer. */
53
- retryTimer = null;
54
- /** Retry cadence — long enough that flapping doesn't burn API quota. */
55
- static RETRY_INTERVAL_MS = 60_000;
56
- /**
57
- * 0.5.90 (TG.3): chat_ids for which we've already logged an allowlist
58
- * drop this process. The adapter silently drops messages from non-
59
- * allowlisted chats (correct policy — never echo policy decisions back
60
- * to the sender), but operators need a one-time-per-chat log line so
61
- * they can diagnose "why isn't my message routing?" without reading
62
- * source. Tracked as a Set keyed by chatIdStr; resets on restart.
63
- */
64
- allowlistDropLogged = new Set();
65
- constructor(config) {
66
- this.config = config;
67
- if (!config.bot_token?.trim()) {
68
- throw new ChatGatewayError("telegram adapter: bot_token is required", "set chat_connections.telegram.bot_token in ~/.opensquid/config.json");
69
- }
70
- }
71
- async start() {
72
- if (this.bot)
73
- return;
74
- let Bot;
75
- try {
76
- const grammy = (await import("grammy"));
77
- Bot = grammy.Bot;
78
- }
79
- catch (err) {
80
- // v0.7 audit fix (M5): distinguish "not installed" from "installed
81
- // but threw on load" — different remediation each case.
82
- const code = err?.code;
83
- if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
84
- throw new ChatGatewayError("telegram adapter: 'grammy' SDK not installed", "run `npm install grammy` (or reinstall opensquid without --omit=optional)");
85
- }
86
- throw new ChatGatewayError(`telegram adapter: 'grammy' SDK failed to load: ${err instanceof Error ? err.message : String(err)}`, "the SDK is installed but threw on import — check node version compatibility");
87
- }
88
- const bot = new Bot(this.config.bot_token);
89
- this.bot = bot;
90
- // Probe identity + token validity. Throws on bad token.
91
- const me = await bot.api.getMe();
92
- this.botUsername = me.username ?? "";
93
- this.botId = String(me.id);
94
- bot.on("message", async (ctx) => {
95
- const m = ctx.message;
96
- if (!m?.text)
97
- return; // ignore non-text in v0.7
98
- const chatIdStr = String(m.chat.id);
99
- // Allowlist enforcement.
100
- if (this.config.allowlist_chat_ids &&
101
- this.config.allowlist_chat_ids.length > 0 &&
102
- !this.config.allowlist_chat_ids.includes(chatIdStr)) {
103
- // Silently drop on the chat side (correct — never echo policy back
104
- // to the sender). But log once per chat_id per process lifetime
105
- // (0.5.90 / TG.3) so operators can diagnose "why isn't my message
106
- // routing?" without reading source. The first message from a
107
- // newly-talking chat surfaces the drop with a hint to fix.
108
- if (!this.allowlistDropLogged.has(chatIdStr)) {
109
- this.allowlistDropLogged.add(chatIdStr);
110
- const chatType = m.chat.type;
111
- const hint = "add this chat_id to chat_connections.telegram.allowlist_chat_ids in ~/.opensquid/config.json to enable inbound routing";
112
- // eslint-disable-next-line no-console
113
- console.error(`[telegram adapter] dropped inbound from non-allowlisted chat ${chatIdStr} (type=${chatType}); ${hint}`);
114
- }
115
- return;
116
- }
117
- const normalized = {
118
- id: String(m.message_id),
119
- threadId: m.message_thread_id !== undefined ? String(m.message_thread_id) : undefined,
120
- platform: "telegram",
121
- channel: formatChannelId("telegram", chatIdStr),
122
- sender: m.from?.username ?? m.from?.first_name ?? String(m.from?.id ?? "unknown"),
123
- senderId: String(m.from?.id ?? ""),
124
- text: m.text,
125
- receivedAt: new Date(m.date * 1000),
126
- mentionsBot: detectBotMention(m.text, m.entities, this.botUsername),
127
- };
128
- for (const h of this.handlers) {
129
- try {
130
- await h(normalized);
131
- }
132
- catch (err) {
133
- const msg = err instanceof Error ? err.message : String(err);
134
- // eslint-disable-next-line no-console
135
- console.error(`[telegram adapter] handler error: ${msg}`);
136
- }
137
- }
138
- });
139
- // `bot.start()` resolves only on bot.stop(). Fire-and-track via a
140
- // promise we keep around for shutdown, but don't await it here.
141
- // 0.7.4 (#147) fix: rejection handler delegates to
142
- // handleStartRejection() so tests can exercise the 409 path
143
- // without spinning up grammy.
144
- this.startPromise = bot.start().catch((err) => this.handleStartRejection(err));
145
- // Yield once so the polling loop has a tick to register before the
146
- // gateway moves on to dispatch outbound messages.
147
- await new Promise((r) => setImmediate(r));
148
- }
149
- /**
150
- * 0.7.4 (#147): handle a rejection from `bot.start()`. Extracted
151
- * from inline catch handler so tests can simulate 409 without
152
- * needing a live grammy + colliding bot. EXPORTED VIA PROTECTED for
153
- * test-only direct invocation; not part of the public adapter API.
154
- */
155
- handleStartRejection(err) {
156
- const msg = err instanceof Error ? err.message : String(err);
157
- const is409 = /409|Conflict/.test(msg);
158
- if (is409) {
159
- // eslint-disable-next-line no-console
160
- console.error(`[telegram adapter] long-poll lost to 409 Conflict — degrading to OUTBOUND-ONLY (outbound sendMessage still works); periodic retry every ${TelegramAdapter.RETRY_INTERVAL_MS / 1000}s`);
161
- this.outboundOnly = true;
162
- this.startPromise = null;
163
- this.scheduleRetry();
164
- }
165
- else {
166
- // eslint-disable-next-line no-console
167
- console.error(`[telegram adapter] long-poll loop errored: ${msg}`);
168
- this.bot = null;
169
- this.startPromise = null;
170
- }
171
- }
172
- /**
173
- * 0.7.4 (#147): test-only seed — install a fake bot reference + mark
174
- * outbound-only so tests can verify isOutboundOnly() + retry timer
175
- * without spinning up grammy. Must be called before any
176
- * handleStartRejection in test context.
177
- */
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
- _testSeed(fakeBot) {
180
- this.bot = fakeBot;
181
- }
182
- /**
183
- * 0.7.4 (#147): test-only — clear the retry timer so tests don't
184
- * leak intervals after a 409 simulation.
185
- */
186
- _testClearRetryTimer() {
187
- if (this.retryTimer) {
188
- clearInterval(this.retryTimer);
189
- this.retryTimer = null;
190
- }
191
- }
192
- /**
193
- * 0.7.4 (#147): periodically retry the long-poll while in outbound-
194
- * only mode. If the competing consumer disconnects, we reclaim
195
- * inbound transparently.
196
- */
197
- scheduleRetry() {
198
- if (this.retryTimer)
199
- return;
200
- this.retryTimer = setInterval(() => {
201
- void this.tryReclaim();
202
- }, TelegramAdapter.RETRY_INTERVAL_MS);
203
- }
204
- async tryReclaim() {
205
- if (!this.outboundOnly || !this.bot)
206
- return;
207
- const bot = this.bot;
208
- // Fire bot.start() again; same rejection handling as the initial
209
- // start. If 409, stay outbound-only. If success (no rejection
210
- // observable yet — start() resolves only on stop()), clear the
211
- // outboundOnly flag and stop the retry timer.
212
- const next = bot.start().catch((err) => {
213
- const msg = err instanceof Error ? err.message : String(err);
214
- const is409 = /409|Conflict/.test(msg);
215
- if (!is409) {
216
- // eslint-disable-next-line no-console
217
- console.error(`[telegram adapter] retry start() errored: ${msg}`);
218
- }
219
- // Stay outbound-only; retry timer keeps running.
220
- return;
221
- });
222
- // Yield to let the long-poll register if it's going to succeed.
223
- await new Promise((r) => setImmediate(r));
224
- // If the bot reference is still alive and no rejection fired yet,
225
- // assume the long-poll is back. Reclaim inbound.
226
- if (this.bot && this.outboundOnly) {
227
- // Heuristic check: getMe still succeeds (the bot can still talk
228
- // to the API). If 409 already rejected `next`, that handler will
229
- // have run by now. We can't directly observe "start succeeded"
230
- // because start() doesn't resolve on success — it stays pending.
231
- // So we look for the absence of a fresh 409.
232
- try {
233
- await bot.api.getMe();
234
- this.outboundOnly = false;
235
- this.startPromise = next;
236
- if (this.retryTimer) {
237
- clearInterval(this.retryTimer);
238
- this.retryTimer = null;
239
- }
240
- // eslint-disable-next-line no-console
241
- console.error(`[telegram adapter] long-poll RECLAIMED — inbound restored`);
242
- }
243
- catch {
244
- // getMe failed → keep outbound-only, retry on next tick
245
- }
246
- }
247
- }
248
- async shutdown() {
249
- if (!this.bot)
250
- return;
251
- // 0.7.4 (#147): stop the retry timer first so it doesn't fire
252
- // mid-shutdown and resurrect a dead adapter.
253
- if (this.retryTimer) {
254
- clearInterval(this.retryTimer);
255
- this.retryTimer = null;
256
- }
257
- try {
258
- await this.bot.stop();
259
- }
260
- catch {
261
- // best-effort — if grammy already torn down, nothing to do
262
- }
263
- if (this.startPromise) {
264
- // bot.start()'s promise should resolve after stop. Don't hang
265
- // forever though — race against a short timeout.
266
- await Promise.race([this.startPromise, new Promise((r) => setTimeout(r, 2000))]);
267
- }
268
- this.bot = null;
269
- this.startPromise = null;
270
- this.outboundOnly = false;
271
- }
272
- /**
273
- * 0.7.4 (#147): introspection accessor for tests + the
274
- * `chat_daemon_status` MCP tool to surface "outbound-only" state to
275
- * operators trying to diagnose "where did my message go?"
276
- */
277
- isOutboundOnly() {
278
- return this.outboundOnly;
279
- }
280
- onMessage(handler) {
281
- this.handlers.push(handler);
282
- }
283
- async send(message) {
284
- if (!this.bot) {
285
- throw new ChatGatewayError("telegram adapter: not started", "call gateway.start() before send()");
286
- }
287
- const { chatId, threadId: channelThreadId } = parseTelegramChannel(message.channel);
288
- const opts = {};
289
- if (message.replyTo) {
290
- const n = Number(message.replyTo);
291
- if (Number.isFinite(n))
292
- opts.reply_to_message_id = n;
293
- }
294
- // Explicit `message.threadId` from the caller wins over a thread id
295
- // embedded in the channel string. The embedded form exists so that
296
- // composite channel literals echoed from `chat_poll_inbox`
297
- // (e.g. `telegram:-1001234567890:15`) can be passed back to
298
- // `chat_send` verbatim without the caller having to split them.
299
- const effectiveThreadId = message.threadId ?? channelThreadId;
300
- if (effectiveThreadId !== undefined) {
301
- const n = Number(effectiveThreadId);
302
- if (Number.isFinite(n))
303
- opts.message_thread_id = n;
304
- }
305
- // TPS.7 (v0.5.130) — detect topic-gone failure and re-throw as a
306
- // typed error so the daemon's send-RPC handler can clear the stale
307
- // binding + notify the operator. Other Bot API failures propagate
308
- // unchanged (the caller's existing error path stays in charge).
309
- let sent;
310
- try {
311
- sent = await this.bot.api.sendMessage(chatId, message.text, opts);
312
- }
313
- catch (err) {
314
- if (isTopicGoneError(err) && opts.message_thread_id !== undefined) {
315
- throw new TopicGoneError(extractGrammyDescription(err) ?? "topic gone", chatId, opts.message_thread_id, err);
316
- }
317
- throw err;
318
- }
319
- return {
320
- platform: "telegram",
321
- messageId: String(sent.message_id),
322
- deliveredAt: new Date(sent.date * 1000),
323
- };
324
- }
325
- /**
326
- * v0.7.2 — Create a forum topic in a supergroup. Requires the bot
327
- * to be admin with "Manage Topics" permission and the supergroup to
328
- * have Topics enabled in settings. Returns the topic's
329
- * `message_thread_id` for storage in chat-routing.json.
330
- */
331
- async createTopic(chatId, name, options = {}) {
332
- if (!this.bot) {
333
- throw new ChatGatewayError("telegram adapter: not started", "call gateway.start() before createTopic()");
334
- }
335
- const apiOpts = {};
336
- if (options.iconColor !== undefined)
337
- apiOpts.icon_color = options.iconColor;
338
- if (options.iconCustomEmojiId !== undefined)
339
- apiOpts.icon_custom_emoji_id = options.iconCustomEmojiId;
340
- const res = await this.bot.api.createForumTopic(chatId, name, apiOpts);
341
- return { message_thread_id: res.message_thread_id, name: res.name };
342
- }
343
- async identity() {
344
- if (!this.bot) {
345
- throw new ChatGatewayError("telegram adapter: not started");
346
- }
347
- return { username: this.botUsername, nativeId: this.botId };
348
- }
349
- }
350
- // ---------------------------------------------------------------------
351
- // Helpers
352
- // ---------------------------------------------------------------------
353
- /**
354
- * Parse a Telegram channel id (with optional embedded forum-topic
355
- * thread id) into its native chat_id + message_thread_id parts.
356
- *
357
- * Wire format (canonical, mirrors `chat_poll_inbox` output):
358
- * - `telegram:<chat_id>` → general topic, no thread
359
- * - `telegram:<chat_id>:<thread_id>` → forum topic (supergroup with
360
- * Topics enabled)
361
- *
362
- * Examples:
363
- * - `telegram:-1001234567890` → `{ chatId: "-1001234567890" }`
364
- * - `telegram:-1001234567890:15` → `{ chatId: "-1001234567890",
365
- * threadId: "15" }`
366
- * - `telegram:8075471258` → `{ chatId: "8075471258" }` (DM)
367
- *
368
- * Why the parser lives in the adapter (not in `gateway.ts`): Slack uses
369
- * a different colon-in-native-id convention (`slack:C012345:1234.5678`
370
- * where the trailing segment is `thread_ts`, an opaque part of the
371
- * native id). Telegram's `<chat_id>:<thread_id>` semantic is
372
- * platform-specific and shouldn't leak into the cross-platform
373
- * `nativeIdFromChannel` helper.
374
- *
375
- * Exported for unit testing.
376
- */
377
- export function parseTelegramChannel(channel) {
378
- const colon = channel.indexOf(":");
379
- if (colon === -1) {
380
- throw new ChatGatewayError(`malformed channel id '${channel}'`, "telegram channel ids must be 'telegram:<chat_id>' or 'telegram:<chat_id>:<thread_id>'");
381
- }
382
- if (channel.slice(0, colon) !== "telegram") {
383
- throw new ChatGatewayError(`telegram adapter received non-telegram channel: '${channel}'`);
384
- }
385
- const rest = channel.slice(colon + 1);
386
- if (rest.length === 0) {
387
- throw new ChatGatewayError(`malformed channel id '${channel}': empty chat_id`);
388
- }
389
- // chat_id is always the first segment after `telegram:`. If a second
390
- // colon-segment is present, it's the forum-topic message_thread_id.
391
- const sep = rest.indexOf(":");
392
- if (sep === -1) {
393
- return { chatId: rest };
394
- }
395
- const chatId = rest.slice(0, sep);
396
- const threadId = rest.slice(sep + 1);
397
- if (chatId.length === 0) {
398
- throw new ChatGatewayError(`malformed channel id '${channel}': empty chat_id`);
399
- }
400
- if (threadId.length === 0) {
401
- throw new ChatGatewayError(`malformed channel id '${channel}': empty thread_id after second colon`);
402
- }
403
- if (!/^\d+$/.test(threadId)) {
404
- throw new ChatGatewayError(`malformed channel id '${channel}': thread_id must be all-digits, got '${threadId}'`);
405
- }
406
- return { chatId, threadId };
407
- }
408
- // ---------------------------------------------------------------------
409
- // TPS.7 (v0.5.130) — typed stale-topic error
410
- // ---------------------------------------------------------------------
411
- /**
412
- * Thrown by `TelegramAdapter.send` when the Telegram Bot API rejects an
413
- * outbound `sendMessage` because the target `message_thread_id` no
414
- * longer exists in the supergroup. The daemon's RPC `send` handler
415
- * catches this, clears the workspace's stale binding via
416
- * `clearBinding` (TPS.3), and surfaces the staleness through TPS.5's
417
- * collision channel.
418
- *
419
- * Carries the underlying `GrammyError` (as `underlying: unknown`) for
420
- * diagnostics — callers shouldn't peek into it but the daemon logs it.
421
- *
422
- * Why a typed error vs. introspecting the original GrammyError at the
423
- * RPC layer: keeps grammy out of `rpc-server.ts` (which doesn't import
424
- * the SDK) and makes the contract obvious at every call site.
425
- */
426
- export class TopicGoneError extends Error {
427
- chatId;
428
- threadId;
429
- underlying;
430
- platform = "telegram";
431
- constructor(message, chatId, threadId, underlying) {
432
- super(message);
433
- this.chatId = chatId;
434
- this.threadId = threadId;
435
- this.underlying = underlying;
436
- this.name = "TopicGoneError";
437
- }
438
- }
439
- /**
440
- * Match Bot API 400 responses whose description matches one of the
441
- * known "thread not found" shapes. Defensive against Telegram changing
442
- * the description string: matches "message thread not found" with
443
- * either casing, both underscores or spaces, and the legacy CAPS form.
444
- *
445
- * Exported for unit testing.
446
- */
447
- export function isTopicGoneError(err) {
448
- if (!(err instanceof Error))
449
- return false;
450
- const code = err.error_code;
451
- if (code !== 400)
452
- return false;
453
- const desc = extractGrammyDescription(err);
454
- if (desc === undefined)
455
- return false;
456
- return /message[_ ]thread[_ ]not[_ ]found/i.test(desc);
457
- }
458
- /**
459
- * Pull the human-readable description out of a GrammyError-shaped value,
460
- * falling back to the Error.message. Returns undefined if neither yields
461
- * a usable string.
462
- *
463
- * Exported for unit testing.
464
- */
465
- export function extractGrammyDescription(err) {
466
- if (typeof err !== "object" || err === null)
467
- return undefined;
468
- const desc = err.description;
469
- if (typeof desc === "string" && desc.length > 0)
470
- return desc;
471
- if (err instanceof Error && err.message.length > 0)
472
- return err.message;
473
- return undefined;
474
- }
475
- /**
476
- * Detect whether a message text contains an @-mention of this bot, or
477
- * uses one of telegram's bot_command entities pointing at the bot
478
- * (`/cmd@my_bot`).
479
- *
480
- * Exported for unit testing.
481
- */
482
- export function detectBotMention(text, entities, botUsername) {
483
- if (!botUsername)
484
- return false;
485
- const lower = `@${botUsername.toLowerCase()}`;
486
- if (text.toLowerCase().includes(lower))
487
- return true;
488
- if (!entities)
489
- return false;
490
- for (const ent of entities) {
491
- if (ent.type !== "mention" && ent.type !== "bot_command")
492
- continue;
493
- const slice = text.slice(ent.offset, ent.offset + ent.length).toLowerCase();
494
- if (slice.includes(lower))
495
- return true;
496
- }
497
- return false;
498
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../src.legacy/chat/adapters/telegram.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAEL,gBAAgB,EAKhB,eAAe,GAChB,MAAM,eAAe,CAAC;AA2CvB,MAAM,OAAO,eAAe;IA6BG;IA5BpB,QAAQ,GAAG,UAAmB,CAAC;IAEhC,GAAG,GAAqB,IAAI,CAAC;IAC7B,QAAQ,GAAqB,EAAE,CAAC;IAChC,WAAW,GAAG,EAAE,CAAC;IACjB,KAAK,GAAG,EAAE,CAAC;IACX,YAAY,GAAyB,IAAI,CAAC;IAClD;;;;;OAKG;IACK,YAAY,GAAG,KAAK,CAAC;IAC7B,mEAAmE;IAC3D,UAAU,GAA0B,IAAI,CAAC;IACjD,wEAAwE;IAChE,MAAM,CAAU,iBAAiB,GAAG,MAAM,CAAC;IACnD;;;;;;;OAOG;IACK,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhD,YAA6B,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;QACjD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,gBAAgB,CACxB,yCAAyC,EACzC,qEAAqE,CACtE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,GAAG;YAAE,OAAO;QACrB,IAAI,GAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAErC,CAAC;YACF,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mEAAmE;YACnE,wDAAwD;YACxD,MAAM,IAAI,GAAI,GAA6B,EAAE,IAAI,CAAC;YAClD,IAAI,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACnE,MAAM,IAAI,gBAAgB,CACxB,8CAA8C,EAC9C,2EAA2E,CAC5E,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,gBAAgB,CACxB,kDAAkD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACpG,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QAEf,wDAAwD;QACxD,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE3B,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;YACtB,IAAI,CAAC,CAAC,EAAE,IAAI;gBAAE,OAAO,CAAC,0BAA0B;YAChD,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpC,yBAAyB;YACzB,IACE,IAAI,CAAC,MAAM,CAAC,kBAAkB;gBAC9B,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC;gBACzC,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,SAAS,CAAC,EACnD,CAAC;gBACD,mEAAmE;gBACnE,gEAAgE;gBAChE,kEAAkE;gBAClE,6DAA6D;gBAC7D,2DAA2D;gBAC3D,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBACxC,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAC7B,MAAM,IAAI,GACR,wHAAwH,CAAC;oBAC3H,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CACX,gEAAgE,SAAS,UAAU,QAAQ,MAAM,IAAI,EAAE,CACxG,CAAC;gBACJ,CAAC;gBACD,OAAO;YACT,CAAC;YACD,MAAM,UAAU,GAAgB;gBAC9B,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;gBACxB,QAAQ,EAAE,CAAC,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS;gBACrF,QAAQ,EAAE,UAAU;gBACpB,OAAO,EAAE,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC;gBAC/C,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,SAAS,CAAC;gBACjF,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC;gBAClC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC;gBACnC,WAAW,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC;aACpE,CAAC;YACF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC9B,IAAI,CAAC;oBACH,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;gBACtB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC7D,sCAAsC;oBACtC,OAAO,CAAC,KAAK,CAAC,qCAAqC,GAAG,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,kEAAkE;QAClE,gEAAgE;QAChE,mDAAmD;QACnD,4DAA4D;QAC5D,8BAA8B;QAC9B,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/E,mEAAmE;QACnE,kDAAkD;QAClD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,GAAY;QAC/B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC;YACV,sCAAsC;YACtC,OAAO,CAAC,KAAK,CACX,2IAA2I,eAAe,CAAC,iBAAiB,GAAG,IAAI,GAAG,CACvL,CAAC;YACF,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,sCAAsC;YACtC,OAAO,CAAC,KAAK,CAAC,8CAA8C,GAAG,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,8DAA8D;IAC9D,SAAS,CAAC,OAAY;QACpB,IAAI,CAAC,GAAG,GAAG,OAAoB,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,oBAAoB;QAClB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,aAAa;QACnB,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;QACzB,CAAC,EAAE,eAAe,CAAC,iBAAiB,CAAC,CAAC;IACxC,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,OAAO;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,iEAAiE;QACjE,8DAA8D;QAC9D,+DAA+D;QAC/D,8CAA8C;QAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,6CAA6C,GAAG,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,iDAAiD;YACjD,OAAO;QACT,CAAC,CAAC,CAAC;QACH,gEAAgE;QAChE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,kEAAkE;QAClE,iDAAiD;QACjD,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,gEAAgE;YAChE,iEAAiE;YACjE,+DAA+D;YAC/D,iEAAiE;YACjE,6CAA6C;YAC7C,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC1B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACzB,CAAC;gBACD,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;YAC7E,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,OAAO;QACtB,8DAA8D;QAC9D,6CAA6C;QAC7C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,8DAA8D;YAC9D,iDAAiD;YACjD,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,cAAc;QACZ,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,OAAuB;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAwB;QACjC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,gBAAgB,CACxB,+BAA+B,EAC/B,oCAAoC,CACrC,CAAC;QACJ,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpF,MAAM,IAAI,GAAiE,EAAE,CAAC;QAC9E,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;QACvD,CAAC;QACD,oEAAoE;QACpE,mEAAmE;QACnE,2DAA2D;QAC3D,4DAA4D;QAC5D,gEAAgE;QAChE,MAAM,iBAAiB,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;QAC9D,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,CAAC,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;YACpC,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxE,OAAO;YACL,QAAQ,EAAE,UAAU;YACpB,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;YAClC,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SACxC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CACf,MAAc,EACd,IAAY,EACZ,UAA8D,EAAE;QAEhE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,gBAAgB,CACxB,+BAA+B,EAC/B,2CAA2C,CAC5C,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAA2D,EAAE,CAAC;QAC3E,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QAC5E,IAAI,OAAO,CAAC,iBAAiB,KAAK,SAAS;YACzC,OAAO,CAAC,oBAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QAC3D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACvE,OAAO,EAAE,iBAAiB,EAAE,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,gBAAgB,CAAC,+BAA+B,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC9D,CAAC;;AAGH,wEAAwE;AACxE,UAAU;AACV,wEAAwE;AAExE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAIlD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,yBAAyB,OAAO,GAAG,EACnC,uFAAuF,CACxF,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,gBAAgB,CAAC,oDAAoD,OAAO,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,gBAAgB,CAAC,yBAAyB,OAAO,kBAAkB,CAAC,CAAC;IACjF,CAAC;IACD,qEAAqE;IACrE,oEAAoE;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,gBAAgB,CAAC,yBAAyB,OAAO,kBAAkB,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,gBAAgB,CACxB,yBAAyB,OAAO,uCAAuC,CACxE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,gBAAgB,CACxB,yBAAyB,OAAO,yCAAyC,QAAQ,GAAG,CACrF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,QAA6E,EAC7E,WAAmB;IAEnB,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC;IAC9C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,aAAa;YAAE,SAAS;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5E,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,94 +0,0 @@
1
- import { afterEach, describe, expect, it } from "vitest";
2
- import { ChatGatewayError } from "../gateway.js";
3
- import { TelegramAdapter, detectBotMention } from "./telegram.js";
4
- describe("TelegramAdapter constructor", () => {
5
- it("rejects empty bot_token", () => {
6
- expect(() => new TelegramAdapter({ bot_token: "" })).toThrow(ChatGatewayError);
7
- });
8
- it("rejects whitespace-only bot_token", () => {
9
- expect(() => new TelegramAdapter({ bot_token: " " })).toThrow(ChatGatewayError);
10
- });
11
- it("accepts a real-shaped token", () => {
12
- // Don't actually call start() — that would try to load grammy.
13
- const a = new TelegramAdapter({ bot_token: "123:ABCDEF" });
14
- expect(a.platform).toBe("telegram");
15
- });
16
- });
17
- describe("detectBotMention", () => {
18
- it("returns false when botUsername is empty", () => {
19
- expect(detectBotMention("@somebot hello", [], "")).toBe(false);
20
- });
21
- it("detects plain @-mention", () => {
22
- expect(detectBotMention("hey @mybot do x", [], "mybot")).toBe(true);
23
- });
24
- it("is case-insensitive", () => {
25
- expect(detectBotMention("hey @MyBot", [], "mybot")).toBe(true);
26
- expect(detectBotMention("hey @mybot", [], "MyBot")).toBe(true);
27
- });
28
- it("returns false when text mentions someone else", () => {
29
- expect(detectBotMention("hey @otherbot", [], "mybot")).toBe(false);
30
- });
31
- it("detects /cmd@mybot entity (bot_command)", () => {
32
- const text = "/start@mybot please";
33
- const entities = [{ type: "bot_command", offset: 0, length: 14 }];
34
- expect(detectBotMention(text, entities, "mybot")).toBe(true);
35
- });
36
- it("ignores entities of unrelated types", () => {
37
- const text = "see https://mybot.example.com";
38
- const entities = [{ type: "url", offset: 4, length: 25 }];
39
- expect(detectBotMention(text, entities, "mybot")).toBe(false);
40
- });
41
- });
42
- describe("TelegramAdapter — 409 outbound-only fallback (#147)", () => {
43
- let adapter;
44
- afterEach(() => {
45
- if (adapter) {
46
- adapter._testClearRetryTimer();
47
- }
48
- });
49
- it("starts in long-poll mode (isOutboundOnly false on a fresh adapter)", () => {
50
- adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
51
- expect(adapter.isOutboundOnly()).toBe(false);
52
- });
53
- it("degrades to outbound-only on a 409 Conflict error (does NOT null the bot)", () => {
54
- adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
55
- // Seed a fake bot so handleStartRejection can verify the bot
56
- // reference is preserved through the 409 path.
57
- const fakeBot = { api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } };
58
- adapter._testSeed(fakeBot);
59
- adapter.handleStartRejection(new Error("Call to 'getUpdates' failed! (409: Conflict: terminated by other getUpdates)"));
60
- expect(adapter.isOutboundOnly()).toBe(true);
61
- // Outbound bot reference still alive (we can't directly check
62
- // this.bot from outside, but the absence of a "telegram adapter:
63
- // not started" error on a hypothetical send() is the contract;
64
- // verified indirectly via isOutboundOnly + clean shutdown below).
65
- });
66
- it("matches the 409 detection across both 'Conflict' and '409' substrings", () => {
67
- adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
68
- adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
69
- adapter.handleStartRejection(new Error("HTTP 409 Conflict"));
70
- expect(adapter.isOutboundOnly()).toBe(true);
71
- const a2 = new TelegramAdapter({ bot_token: "123:ABCDEF" });
72
- a2._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
73
- a2.handleStartRejection(new Error("Some random Conflict happened"));
74
- expect(a2.isOutboundOnly()).toBe(true);
75
- a2._testClearRetryTimer();
76
- });
77
- it("non-409 errors still tear down the bot (treats as genuine failure)", () => {
78
- adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
79
- adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
80
- adapter.handleStartRejection(new Error("ECONNREFUSED"));
81
- // outboundOnly stays false; the bot was nulled
82
- expect(adapter.isOutboundOnly()).toBe(false);
83
- });
84
- it("schedules a periodic retry timer when entering outbound-only mode", () => {
85
- adapter = new TelegramAdapter({ bot_token: "123:ABCDEF" });
86
- adapter._testSeed({ api: { sendMessage: () => Promise.resolve({ message_id: 1, date: 0 }) } });
87
- expect(adapter.isOutboundOnly()).toBe(false);
88
- adapter.handleStartRejection(new Error("409 Conflict"));
89
- expect(adapter.isOutboundOnly()).toBe(true);
90
- // Retry timer is private; we can't directly assert it exists, but
91
- // _testClearRetryTimer() should successfully clear it (verified by
92
- // the afterEach not throwing on subsequent runs).
93
- });
94
- });