opensquid 0.5.441 → 0.5.449

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 (391) 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/functions/recall_pre_inject.d.ts.map +1 -1
  11. package/dist/functions/recall_pre_inject.js +12 -0
  12. package/dist/functions/recall_pre_inject.js.map +1 -1
  13. package/dist/rag/store_git.d.ts +23 -0
  14. package/dist/rag/store_git.d.ts.map +1 -0
  15. package/dist/rag/store_git.js +57 -0
  16. package/dist/rag/store_git.js.map +1 -0
  17. package/dist/runtime/bootstrap.d.ts.map +1 -1
  18. package/dist/runtime/bootstrap.js +2 -0
  19. package/dist/runtime/bootstrap.js.map +1 -1
  20. package/dist/runtime/handoff/render.d.ts +5 -4
  21. package/dist/runtime/handoff/render.d.ts.map +1 -1
  22. package/dist/runtime/handoff/render.js +7 -7
  23. package/dist/runtime/handoff/render.js.map +1 -1
  24. package/dist/runtime/hooks/active_task_mirror.js +0 -0
  25. package/dist/runtime/hooks/apply_patch.js +0 -0
  26. package/dist/runtime/hooks/dispatch.js +0 -0
  27. package/dist/runtime/hooks/hook_output.js +0 -0
  28. package/dist/runtime/hooks/memory_reconcile.js +0 -0
  29. package/dist/runtime/hooks/new_project_detect.js +0 -0
  30. package/dist/runtime/hooks/profession_resolver.js +0 -0
  31. package/dist/runtime/hooks/scope_intent.js +0 -0
  32. package/dist/runtime/hooks/session-end.js +11 -0
  33. package/dist/runtime/hooks/session-end.js.map +1 -1
  34. package/dist/runtime/hooks/session_id.js +0 -0
  35. package/dist/runtime/hooks/session_liveness.js +0 -0
  36. package/dist/runtime/hooks/stop_drive.js +0 -0
  37. package/dist/runtime/hooks/stop_stream.js +0 -0
  38. package/dist/runtime/hooks/subagent_guard.js +0 -0
  39. package/dist/runtime/hooks/transcript.js +0 -0
  40. package/dist/runtime/hooks/transcript_tasks.js +0 -0
  41. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
  42. package/dist/runtime/ralph/orchestrator.js +2 -1
  43. package/dist/runtime/ralph/orchestrator.js.map +1 -1
  44. package/dist/setup/cli/limits_state.d.ts.map +1 -1
  45. package/dist/setup/cli/limits_state.js +6 -40
  46. package/dist/setup/cli/limits_state.js.map +1 -1
  47. package/dist/setup/cli/pack_walk.d.ts +32 -0
  48. package/dist/setup/cli/pack_walk.d.ts.map +1 -0
  49. package/dist/setup/cli/pack_walk.js +76 -0
  50. package/dist/setup/cli/pack_walk.js.map +1 -0
  51. package/dist/setup/cli/permissions_state.d.ts.map +1 -1
  52. package/dist/setup/cli/permissions_state.js +6 -37
  53. package/dist/setup/cli/permissions_state.js.map +1 -1
  54. package/dist/setup/cli/triggers_state.d.ts.map +1 -1
  55. package/dist/setup/cli/triggers_state.js +3 -29
  56. package/dist/setup/cli/triggers_state.js.map +1 -1
  57. package/dist/workgraph/events.d.ts.map +1 -1
  58. package/dist/workgraph/events.js +10 -0
  59. package/dist/workgraph/events.js.map +1 -1
  60. package/dist/workgraph/store.d.ts.map +1 -1
  61. package/dist/workgraph/store.js +5 -0
  62. package/dist/workgraph/store.js.map +1 -1
  63. package/dist/workgraph/types.d.ts +2 -1
  64. package/dist/workgraph/types.d.ts.map +1 -1
  65. package/docs/ARCHITECTURE.md +268 -0
  66. package/docs/pack-runtime.md +15 -12
  67. package/docs/skill-grammar-guide.md +4 -4
  68. package/package.json +5 -3
  69. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
  70. package/dist/anti-drift/evaluator.d.ts +0 -88
  71. package/dist/anti-drift/evaluator.d.ts.map +0 -1
  72. package/dist/anti-drift/evaluator.js +0 -417
  73. package/dist/anti-drift/evaluator.js.map +0 -1
  74. package/dist/anti-drift/evaluator.test.js +0 -78
  75. package/dist/anti-drift/rules.d.ts +0 -80
  76. package/dist/anti-drift/rules.d.ts.map +0 -1
  77. package/dist/anti-drift/rules.js +0 -368
  78. package/dist/anti-drift/rules.js.map +0 -1
  79. package/dist/anti-drift/rules.test.js +0 -213
  80. package/dist/anti-drift/state.d.ts +0 -107
  81. package/dist/anti-drift/state.d.ts.map +0 -1
  82. package/dist/anti-drift/state.js +0 -177
  83. package/dist/anti-drift/state.js.map +0 -1
  84. package/dist/anti-drift/state.test.js +0 -120
  85. package/dist/chat/adapters/discord.d.ts +0 -41
  86. package/dist/chat/adapters/discord.d.ts.map +0 -1
  87. package/dist/chat/adapters/discord.js +0 -176
  88. package/dist/chat/adapters/discord.js.map +0 -1
  89. package/dist/chat/adapters/discord.test.js +0 -25
  90. package/dist/chat/adapters/slack.d.ts +0 -43
  91. package/dist/chat/adapters/slack.d.ts.map +0 -1
  92. package/dist/chat/adapters/slack.js +0 -172
  93. package/dist/chat/adapters/slack.js.map +0 -1
  94. package/dist/chat/adapters/slack.test.js +0 -30
  95. package/dist/chat/adapters/telegram.d.ts +0 -148
  96. package/dist/chat/adapters/telegram.d.ts.map +0 -1
  97. package/dist/chat/adapters/telegram.js +0 -498
  98. package/dist/chat/adapters/telegram.js.map +0 -1
  99. package/dist/chat/adapters/telegram.test.js +0 -94
  100. package/dist/chat/config.d.ts +0 -98
  101. package/dist/chat/config.d.ts.map +0 -1
  102. package/dist/chat/config.js +0 -185
  103. package/dist/chat/config.js.map +0 -1
  104. package/dist/chat/daemon/active-project.d.ts +0 -17
  105. package/dist/chat/daemon/active-project.d.ts.map +0 -1
  106. package/dist/chat/daemon/active-project.js +0 -23
  107. package/dist/chat/daemon/active-project.js.map +0 -1
  108. package/dist/chat/daemon/autospawn.d.ts +0 -40
  109. package/dist/chat/daemon/autospawn.d.ts.map +0 -1
  110. package/dist/chat/daemon/autospawn.js +0 -129
  111. package/dist/chat/daemon/autospawn.js.map +0 -1
  112. package/dist/chat/daemon/autospawn.test.js +0 -112
  113. package/dist/chat/daemon/cli.d.ts +0 -18
  114. package/dist/chat/daemon/cli.d.ts.map +0 -1
  115. package/dist/chat/daemon/cli.js +0 -71
  116. package/dist/chat/daemon/cli.js.map +0 -1
  117. package/dist/chat/daemon/collisions.js +0 -384
  118. package/dist/chat/daemon/health-check.d.ts +0 -69
  119. package/dist/chat/daemon/health-check.d.ts.map +0 -1
  120. package/dist/chat/daemon/health-check.js +0 -112
  121. package/dist/chat/daemon/health-check.js.map +0 -1
  122. package/dist/chat/daemon/inbox-read.d.ts +0 -35
  123. package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
  124. package/dist/chat/daemon/inbox-read.js +0 -75
  125. package/dist/chat/daemon/inbox-read.js.map +0 -1
  126. package/dist/chat/daemon/inbox-read.test.js +0 -97
  127. package/dist/chat/daemon/inbox.d.ts +0 -63
  128. package/dist/chat/daemon/inbox.d.ts.map +0 -1
  129. package/dist/chat/daemon/inbox.js +0 -56
  130. package/dist/chat/daemon/inbox.js.map +0 -1
  131. package/dist/chat/daemon/inbox.test.js +0 -110
  132. package/dist/chat/daemon/lifecycle.d.ts +0 -71
  133. package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
  134. package/dist/chat/daemon/lifecycle.js +0 -221
  135. package/dist/chat/daemon/lifecycle.js.map +0 -1
  136. package/dist/chat/daemon/lifecycle.test.js +0 -163
  137. package/dist/chat/daemon/protocol.d.ts +0 -107
  138. package/dist/chat/daemon/protocol.d.ts.map +0 -1
  139. package/dist/chat/daemon/protocol.js +0 -54
  140. package/dist/chat/daemon/protocol.js.map +0 -1
  141. package/dist/chat/daemon/routing.d.ts +0 -140
  142. package/dist/chat/daemon/routing.d.ts.map +0 -1
  143. package/dist/chat/daemon/routing.js +0 -198
  144. package/dist/chat/daemon/routing.js.map +0 -1
  145. package/dist/chat/daemon/routing.test.js +0 -259
  146. package/dist/chat/daemon/rpc-client.d.ts +0 -45
  147. package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
  148. package/dist/chat/daemon/rpc-client.js +0 -133
  149. package/dist/chat/daemon/rpc-client.js.map +0 -1
  150. package/dist/chat/daemon/rpc-server.d.ts +0 -39
  151. package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
  152. package/dist/chat/daemon/rpc-server.js +0 -385
  153. package/dist/chat/daemon/rpc-server.js.map +0 -1
  154. package/dist/chat/daemon/rpc.test.js +0 -177
  155. package/dist/chat/daemon/subscribers.js +0 -257
  156. package/dist/chat/daemon/worker.d.ts +0 -27
  157. package/dist/chat/daemon/worker.d.ts.map +0 -1
  158. package/dist/chat/daemon/worker.js +0 -313
  159. package/dist/chat/daemon/worker.js.map +0 -1
  160. package/dist/chat/daemon/workspace-topic.js +0 -324
  161. package/dist/chat/env-token.d.ts +0 -60
  162. package/dist/chat/env-token.d.ts.map +0 -1
  163. package/dist/chat/env-token.js +0 -137
  164. package/dist/chat/env-token.js.map +0 -1
  165. package/dist/chat/env-token.test.js +0 -160
  166. package/dist/chat/factory.d.ts +0 -30
  167. package/dist/chat/factory.d.ts.map +0 -1
  168. package/dist/chat/factory.js +0 -50
  169. package/dist/chat/factory.js.map +0 -1
  170. package/dist/chat/factory.test.js +0 -55
  171. package/dist/chat/gateway.d.ts +0 -176
  172. package/dist/chat/gateway.d.ts.map +0 -1
  173. package/dist/chat/gateway.js +0 -146
  174. package/dist/chat/gateway.js.map +0 -1
  175. package/dist/chat/gateway.test.js +0 -192
  176. package/dist/claude-md.d.ts +0 -39
  177. package/dist/claude-md.d.ts.map +0 -1
  178. package/dist/claude-md.js +0 -113
  179. package/dist/claude-md.js.map +0 -1
  180. package/dist/claude-md.test.js +0 -91
  181. package/dist/codex/activate.d.ts +0 -66
  182. package/dist/codex/activate.d.ts.map +0 -1
  183. package/dist/codex/activate.js +0 -329
  184. package/dist/codex/activate.js.map +0 -1
  185. package/dist/codex/activate.test.js +0 -229
  186. package/dist/codex/bundled-default/bundled-default.test.js +0 -161
  187. package/dist/codex/cli-publish.test.js +0 -133
  188. package/dist/codex/cli.d.ts +0 -35
  189. package/dist/codex/cli.d.ts.map +0 -1
  190. package/dist/codex/cli.js +0 -554
  191. package/dist/codex/cli.js.map +0 -1
  192. package/dist/codex/cli.test.js +0 -277
  193. package/dist/codex/import-skill-md.d.ts +0 -53
  194. package/dist/codex/import-skill-md.d.ts.map +0 -1
  195. package/dist/codex/import-skill-md.js +0 -236
  196. package/dist/codex/import-skill-md.js.map +0 -1
  197. package/dist/codex/import-skill-md.test.js +0 -225
  198. package/dist/codex/loader.d.ts +0 -27
  199. package/dist/codex/loader.d.ts.map +0 -1
  200. package/dist/codex/loader.js +0 -86
  201. package/dist/codex/loader.js.map +0 -1
  202. package/dist/codex/loader.test.js +0 -75
  203. package/dist/codex/parse.d.ts +0 -28
  204. package/dist/codex/parse.d.ts.map +0 -1
  205. package/dist/codex/parse.js +0 -309
  206. package/dist/codex/parse.js.map +0 -1
  207. package/dist/codex/parse.test.js +0 -241
  208. package/dist/codex/store.d.ts +0 -87
  209. package/dist/codex/store.d.ts.map +0 -1
  210. package/dist/codex/store.js +0 -205
  211. package/dist/codex/store.js.map +0 -1
  212. package/dist/codex/store.test.js +0 -242
  213. package/dist/codex/types.d.ts +0 -398
  214. package/dist/codex/types.d.ts.map +0 -1
  215. package/dist/codex/types.js +0 -21
  216. package/dist/codex/types.js.map +0 -1
  217. package/dist/config.d.ts +0 -53
  218. package/dist/config.d.ts.map +0 -1
  219. package/dist/config.js +0 -202
  220. package/dist/config.js.map +0 -1
  221. package/dist/config.test.js +0 -117
  222. package/dist/engine/cli.d.ts +0 -14
  223. package/dist/engine/cli.d.ts.map +0 -1
  224. package/dist/engine/cli.js +0 -171
  225. package/dist/engine/cli.js.map +0 -1
  226. package/dist/engine/client.d.ts +0 -219
  227. package/dist/engine/client.d.ts.map +0 -1
  228. package/dist/engine/client.js +0 -312
  229. package/dist/engine/client.js.map +0 -1
  230. package/dist/engine/config.d.ts +0 -62
  231. package/dist/engine/config.d.ts.map +0 -1
  232. package/dist/engine/config.js +0 -223
  233. package/dist/engine/config.js.map +0 -1
  234. package/dist/engine/index.d.ts +0 -17
  235. package/dist/engine/index.d.ts.map +0 -1
  236. package/dist/engine/index.js +0 -16
  237. package/dist/engine/index.js.map +0 -1
  238. package/dist/engine/resolver.d.ts +0 -62
  239. package/dist/engine/resolver.d.ts.map +0 -1
  240. package/dist/engine/resolver.js +0 -103
  241. package/dist/engine/resolver.js.map +0 -1
  242. package/dist/engine/singleton.d.ts +0 -95
  243. package/dist/engine/singleton.d.ts.map +0 -1
  244. package/dist/engine/singleton.js +0 -325
  245. package/dist/engine/singleton.js.map +0 -1
  246. package/dist/engine/types.d.ts +0 -402
  247. package/dist/engine/types.d.ts.map +0 -1
  248. package/dist/engine/types.js +0 -22
  249. package/dist/engine/types.js.map +0 -1
  250. package/dist/engine-binary-resolver.js +0 -110
  251. package/dist/engine-binary-resolver.test.js +0 -61
  252. package/dist/engine-cli.js +0 -60
  253. package/dist/engine-client.js +0 -301
  254. package/dist/engine-client.test.js +0 -118
  255. package/dist/functions/chain_state.d.ts +0 -51
  256. package/dist/functions/chain_state.d.ts.map +0 -1
  257. package/dist/functions/chain_state.js +0 -59
  258. package/dist/functions/chain_state.js.map +0 -1
  259. package/dist/hooks/drift-catalog.d.ts +0 -68
  260. package/dist/hooks/drift-catalog.d.ts.map +0 -1
  261. package/dist/hooks/drift-catalog.js +0 -184
  262. package/dist/hooks/drift-catalog.js.map +0 -1
  263. package/dist/hooks/drift-catalog.test.js +0 -154
  264. package/dist/hooks/drift-patterns.d.ts +0 -110
  265. package/dist/hooks/drift-patterns.d.ts.map +0 -1
  266. package/dist/hooks/drift-patterns.js +0 -289
  267. package/dist/hooks/drift-patterns.js.map +0 -1
  268. package/dist/hooks/drift-patterns.test.js +0 -325
  269. package/dist/hooks/engine-vocab-gate.d.ts +0 -108
  270. package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
  271. package/dist/hooks/engine-vocab-gate.js +0 -225
  272. package/dist/hooks/engine-vocab-gate.js.map +0 -1
  273. package/dist/hooks/engine-vocab-gate.test.js +0 -170
  274. package/dist/hooks/heartbeat.d.ts +0 -107
  275. package/dist/hooks/heartbeat.d.ts.map +0 -1
  276. package/dist/hooks/heartbeat.js +0 -316
  277. package/dist/hooks/heartbeat.js.map +0 -1
  278. package/dist/hooks/heartbeat.test.js +0 -393
  279. package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
  280. package/dist/hooks/honesty-ledger.d.ts +0 -123
  281. package/dist/hooks/honesty-ledger.d.ts.map +0 -1
  282. package/dist/hooks/honesty-ledger.js +0 -226
  283. package/dist/hooks/honesty-ledger.js.map +0 -1
  284. package/dist/hooks/honesty-ledger.test.js +0 -466
  285. package/dist/hooks/inline-report-check.d.ts +0 -63
  286. package/dist/hooks/inline-report-check.d.ts.map +0 -1
  287. package/dist/hooks/inline-report-check.js +0 -88
  288. package/dist/hooks/inline-report-check.js.map +0 -1
  289. package/dist/hooks/inline-report-check.test.js +0 -96
  290. package/dist/hooks/pre-tool-use.d.ts +0 -62
  291. package/dist/hooks/pre-tool-use.d.ts.map +0 -1
  292. package/dist/hooks/pre-tool-use.js +0 -342
  293. package/dist/hooks/pre-tool-use.js.map +0 -1
  294. package/dist/hooks/pre-tool-use.test.js +0 -134
  295. package/dist/hooks/session-end.d.ts +0 -15
  296. package/dist/hooks/session-end.d.ts.map +0 -1
  297. package/dist/hooks/session-end.js +0 -60
  298. package/dist/hooks/session-end.js.map +0 -1
  299. package/dist/hooks/session-end.test.js +0 -52
  300. package/dist/hooks/stop.d.ts +0 -35
  301. package/dist/hooks/stop.d.ts.map +0 -1
  302. package/dist/hooks/stop.js +0 -136
  303. package/dist/hooks/stop.js.map +0 -1
  304. package/dist/hooks/transcript-active-task.test.js +0 -342
  305. package/dist/hooks/transcript.d.ts +0 -26
  306. package/dist/hooks/transcript.d.ts.map +0 -1
  307. package/dist/hooks/transcript.js +0 -266
  308. package/dist/hooks/transcript.js.map +0 -1
  309. package/dist/hooks/transcript.test.js +0 -103
  310. package/dist/hooks/user-prompt-submit.d.ts +0 -74
  311. package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
  312. package/dist/hooks/user-prompt-submit.js +0 -256
  313. package/dist/hooks/user-prompt-submit.js.map +0 -1
  314. package/dist/hooks/user-prompt-submit.test.js +0 -118
  315. package/dist/hooks/versioning-gate.d.ts +0 -101
  316. package/dist/hooks/versioning-gate.d.ts.map +0 -1
  317. package/dist/hooks/versioning-gate.js +0 -245
  318. package/dist/hooks/versioning-gate.js.map +0 -1
  319. package/dist/hooks/versioning-gate.test.js +0 -368
  320. package/dist/hooks/workflow-gate.d.ts +0 -64
  321. package/dist/hooks/workflow-gate.d.ts.map +0 -1
  322. package/dist/hooks/workflow-gate.js +0 -152
  323. package/dist/hooks/workflow-gate.js.map +0 -1
  324. package/dist/hooks/workflow-gate.test.js +0 -197
  325. package/dist/hooks-cli.d.ts +0 -25
  326. package/dist/hooks-cli.d.ts.map +0 -1
  327. package/dist/hooks-cli.js +0 -286
  328. package/dist/hooks-cli.js.map +0 -1
  329. package/dist/hooks-cli.test.js +0 -148
  330. package/dist/origin.d.ts +0 -16
  331. package/dist/origin.d.ts.map +0 -1
  332. package/dist/origin.js +0 -92
  333. package/dist/origin.js.map +0 -1
  334. package/dist/packs/seed_lessons_ingest.d.ts +0 -30
  335. package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
  336. package/dist/packs/seed_lessons_ingest.js +0 -107
  337. package/dist/packs/seed_lessons_ingest.js.map +0 -1
  338. package/dist/project-cli.d.ts +0 -7
  339. package/dist/project-cli.d.ts.map +0 -1
  340. package/dist/project-cli.js +0 -145
  341. package/dist/project-cli.js.map +0 -1
  342. package/dist/project.d.ts +0 -127
  343. package/dist/project.d.ts.map +0 -1
  344. package/dist/project.js +0 -281
  345. package/dist/project.js.map +0 -1
  346. package/dist/project.test.js +0 -287
  347. package/dist/rag/backends/loop_engine.d.ts +0 -61
  348. package/dist/rag/backends/loop_engine.d.ts.map +0 -1
  349. package/dist/rag/backends/loop_engine.js +0 -160
  350. package/dist/rag/backends/loop_engine.js.map +0 -1
  351. package/dist/recall.d.ts +0 -82
  352. package/dist/recall.d.ts.map +0 -1
  353. package/dist/recall.js +0 -81
  354. package/dist/recall.js.map +0 -1
  355. package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
  356. package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
  357. package/dist/runtime/agent_bridge/autospawn.js +0 -251
  358. package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
  359. package/dist/runtime/chain_state.d.ts +0 -124
  360. package/dist/runtime/chain_state.d.ts.map +0 -1
  361. package/dist/runtime/chain_state.js +0 -189
  362. package/dist/runtime/chain_state.js.map +0 -1
  363. package/dist/runtime/hooks/permission_decision.d.ts +0 -34
  364. package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
  365. package/dist/runtime/hooks/permission_decision.js +0 -39
  366. package/dist/runtime/hooks/permission_decision.js.map +0 -1
  367. package/dist/runtime/workflow_fsm.d.ts +0 -21
  368. package/dist/runtime/workflow_fsm.d.ts.map +0 -1
  369. package/dist/runtime/workflow_fsm.js +0 -25
  370. package/dist/runtime/workflow_fsm.js.map +0 -1
  371. package/dist/runtime/workflow_map.d.ts +0 -26
  372. package/dist/runtime/workflow_map.d.ts.map +0 -1
  373. package/dist/runtime/workflow_map.js +0 -38
  374. package/dist/runtime/workflow_map.js.map +0 -1
  375. package/dist/scope.d.ts +0 -48
  376. package/dist/scope.d.ts.map +0 -1
  377. package/dist/scope.js +0 -111
  378. package/dist/scope.js.map +0 -1
  379. package/dist/setup/cli/topic_create_step.d.ts +0 -84
  380. package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
  381. package/dist/setup/cli/topic_create_step.js +0 -213
  382. package/dist/setup/cli/topic_create_step.js.map +0 -1
  383. package/dist/system-export.d.ts +0 -65
  384. package/dist/system-export.d.ts.map +0 -1
  385. package/dist/system-export.js +0 -194
  386. package/dist/system-export.js.map +0 -1
  387. package/dist/utterance/classifier.d.ts +0 -53
  388. package/dist/utterance/classifier.d.ts.map +0 -1
  389. package/dist/utterance/classifier.js +0 -184
  390. package/dist/utterance/classifier.js.map +0 -1
  391. 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":"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"}
@@ -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
- });