pi-harness-runtime 1.1.397 → 1.1.402

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 (624) hide show
  1. package/AGENTS.md +44 -2
  2. package/CHANGELOG.md +21 -0
  3. package/RULES.md +28 -0
  4. package/harness/agent-handoff.ts +2 -3
  5. package/harness/autonomous-review.ts +1 -1
  6. package/harness/blackboard.ts +2 -3
  7. package/harness/context-compact-orchestrator.ts +1 -1
  8. package/harness/context-window-manager.ts +2 -2
  9. package/harness/e2e/playwright-runner.ts +6 -4
  10. package/harness/herdr-agents.ts +1 -1
  11. package/harness/job-state-machine.ts +1 -1
  12. package/harness/langchain/daemon.ts +3 -3
  13. package/harness/langchain/graph.ts +2 -2
  14. package/harness/langchain/ping-pong-middleware.ts +2 -2
  15. package/harness/loop-code-agent.ts +6 -4
  16. package/harness/loop-controller.ts +1 -1
  17. package/harness/loop-review-agent.ts +1 -1
  18. package/harness/loop-types.ts +1 -1
  19. package/harness/master-planner.ts +1 -1
  20. package/harness/notification-events.ts +6 -6
  21. package/harness/project-detector/detector.ts +1 -2
  22. package/harness/repair-engine.ts +2 -4
  23. package/harness/service-diagnostics.ts +1 -1
  24. package/harness/skill-sync.ts +3 -3
  25. package/index.ts +276 -55
  26. package/mirror.ts +1 -1
  27. package/package.json +5 -3
  28. package/packages/a2a-adapter/dist/discovery.d.ts.map +1 -1
  29. package/packages/a2a-adapter/dist/discovery.js +0 -1
  30. package/packages/a2a-adapter/dist/discovery.js.map +1 -1
  31. package/packages/a2a-adapter/package.json +1 -1
  32. package/packages/a2a-adapter/src/discovery.ts +0 -1
  33. package/packages/agent-policy/package.json +1 -1
  34. package/packages/architecture-generator/package.json +1 -1
  35. package/packages/auth/dist/index.d.ts +9 -0
  36. package/packages/auth/dist/index.d.ts.map +1 -0
  37. package/packages/auth/dist/index.js +9 -0
  38. package/packages/auth/dist/index.js.map +1 -0
  39. package/packages/auth/package.json +1 -1
  40. package/packages/auth/src/index.ts +9 -0
  41. package/packages/autonomous-refactor/package.json +1 -1
  42. package/packages/autonomous-runtime/package.json +1 -1
  43. package/packages/cache-strategy/package.json +1 -1
  44. package/packages/capability-registry/package.json +1 -1
  45. package/packages/checkpoint/package.json +1 -1
  46. package/packages/cli-plugin-sdk/package.json +1 -1
  47. package/packages/clipboard/package.json +1 -1
  48. package/packages/clipboard-plugin/package.json +1 -1
  49. package/packages/code-generation/package.json +1 -1
  50. package/packages/code-review/package.json +1 -1
  51. package/packages/codex-adapter/package.json +1 -1
  52. package/packages/codex-watcher/package.json +1 -1
  53. package/packages/config-capture/dist/index.d.ts +52 -0
  54. package/packages/config-capture/dist/index.d.ts.map +1 -0
  55. package/packages/config-capture/dist/index.js +261 -0
  56. package/packages/config-capture/dist/index.js.map +1 -0
  57. package/packages/config-capture/package.json +1 -1
  58. package/packages/connection-runtime/package.json +1 -1
  59. package/packages/context-compiler/package.json +1 -1
  60. package/packages/context-discovery/package.json +1 -1
  61. package/packages/context-manager/package.json +1 -1
  62. package/packages/contracts/package.json +1 -1
  63. package/packages/cookie-sanitizer/package.json +1 -1
  64. package/packages/cost-optimizer/package.json +1 -1
  65. package/packages/dependency-analyzer/package.json +1 -1
  66. package/packages/django-plugin/package.json +1 -1
  67. package/packages/doc-generator/package.json +1 -1
  68. package/packages/durable-review/package.json +1 -1
  69. package/packages/e2e-tester/package.json +1 -1
  70. package/packages/evaluation-engine/package.json +1 -1
  71. package/packages/evaluation-runner/package.json +1 -1
  72. package/packages/event-bus/package.json +1 -1
  73. package/packages/event-sink/package.json +1 -1
  74. package/packages/event-store/package.json +1 -1
  75. package/packages/experience-replay/package.json +1 -1
  76. package/packages/feedback-collector/package.json +1 -1
  77. package/packages/file-copy-helper/dist/extension.d.ts +15 -0
  78. package/packages/file-copy-helper/dist/extension.d.ts.map +1 -0
  79. package/packages/file-copy-helper/dist/extension.debug.d.ts +15 -0
  80. package/packages/file-copy-helper/dist/extension.debug.d.ts.map +1 -0
  81. package/packages/file-copy-helper/dist/extension.debug.js +215 -0
  82. package/packages/file-copy-helper/dist/extension.debug.js.map +1 -0
  83. package/packages/file-copy-helper/dist/extension.js +222 -0
  84. package/packages/file-copy-helper/dist/extension.js.map +1 -0
  85. package/packages/file-copy-helper/dist/index.d.ts +3 -0
  86. package/packages/file-copy-helper/dist/index.d.ts.map +1 -0
  87. package/packages/file-copy-helper/dist/index.js +3 -0
  88. package/packages/file-copy-helper/dist/index.js.map +1 -0
  89. package/packages/file-copy-helper/dist/intent-detector.d.ts +21 -0
  90. package/packages/file-copy-helper/dist/intent-detector.d.ts.map +1 -0
  91. package/packages/file-copy-helper/dist/intent-detector.js +348 -0
  92. package/packages/file-copy-helper/dist/intent-detector.js.map +1 -0
  93. package/packages/file-copy-helper/package.json +1 -1
  94. package/packages/framework-detector/package.json +1 -1
  95. package/packages/framework-plugin-sdk/package.json +1 -1
  96. package/packages/frappe-plugin/package.json +1 -1
  97. package/packages/generic-web-plugin/package.json +1 -1
  98. package/packages/health-monitor/package.json +1 -1
  99. package/packages/intent-analyzer/package.json +1 -1
  100. package/packages/jev-judge/dist/auto-continue.d.ts +161 -2
  101. package/packages/jev-judge/dist/auto-continue.d.ts.map +1 -1
  102. package/packages/jev-judge/dist/auto-continue.js +267 -22
  103. package/packages/jev-judge/dist/auto-continue.js.map +1 -1
  104. package/packages/jev-judge/dist/index.d.ts +2 -0
  105. package/packages/jev-judge/dist/index.d.ts.map +1 -1
  106. package/packages/jev-judge/dist/index.js +2 -0
  107. package/packages/jev-judge/dist/index.js.map +1 -1
  108. package/packages/jev-judge/dist/question-risk.d.ts +50 -0
  109. package/packages/jev-judge/dist/question-risk.d.ts.map +1 -0
  110. package/packages/jev-judge/dist/question-risk.js +100 -0
  111. package/packages/jev-judge/dist/question-risk.js.map +1 -0
  112. package/packages/jev-judge/package.json +1 -1
  113. package/packages/jev-judge/src/auto-continue.ts +426 -29
  114. package/packages/jev-judge/src/index.ts +4 -0
  115. package/packages/jev-judge/src/question-risk.ts +151 -0
  116. package/packages/jev-judge/tests/auto-continue-context.test.ts +221 -0
  117. package/packages/jev-judge/tests/outcome-application.test.ts +132 -0
  118. package/packages/jev-judge/tests/question-risk.test.ts +116 -0
  119. package/packages/knowledge-graph/package.json +1 -1
  120. package/packages/knowledge-retrieval/package.json +1 -1
  121. package/packages/laravel-plugin/package.json +1 -1
  122. package/packages/learning-engine/package.json +1 -1
  123. package/packages/logging/README.md +36 -3
  124. package/packages/logging/dist/config.d.ts +7 -0
  125. package/packages/logging/dist/config.d.ts.map +1 -1
  126. package/packages/logging/dist/config.js +14 -0
  127. package/packages/logging/dist/config.js.map +1 -1
  128. package/packages/logging/dist/incidents/schema.d.ts +111 -0
  129. package/packages/logging/dist/incidents/schema.d.ts.map +1 -0
  130. package/packages/logging/dist/incidents/schema.js +0 -0
  131. package/packages/logging/dist/incidents/schema.js.map +1 -0
  132. package/packages/logging/dist/index.d.ts +2 -0
  133. package/packages/logging/dist/index.d.ts.map +1 -1
  134. package/packages/logging/dist/index.js +2 -0
  135. package/packages/logging/dist/index.js.map +1 -1
  136. package/packages/logging/dist/server/http.d.ts +4 -3
  137. package/packages/logging/dist/server/http.d.ts.map +1 -1
  138. package/packages/logging/dist/server/http.js +35 -3
  139. package/packages/logging/dist/server/http.js.map +1 -1
  140. package/packages/logging/dist/server/incident-qdrant.d.ts +83 -0
  141. package/packages/logging/dist/server/incident-qdrant.d.ts.map +1 -0
  142. package/packages/logging/dist/server/incident-qdrant.js +129 -0
  143. package/packages/logging/dist/server/incident-qdrant.js.map +1 -0
  144. package/packages/logging/dist/server/incident-store.d.ts +76 -0
  145. package/packages/logging/dist/server/incident-store.d.ts.map +1 -0
  146. package/packages/logging/dist/server/incident-store.js +157 -0
  147. package/packages/logging/dist/server/incident-store.js.map +1 -0
  148. package/packages/logging/dist/server/index.d.ts +4 -0
  149. package/packages/logging/dist/server/index.d.ts.map +1 -1
  150. package/packages/logging/dist/server/index.js +2 -0
  151. package/packages/logging/dist/server/index.js.map +1 -1
  152. package/packages/logging/dist/server/service.d.ts +33 -0
  153. package/packages/logging/dist/server/service.d.ts.map +1 -1
  154. package/packages/logging/dist/server/service.js +63 -0
  155. package/packages/logging/dist/server/service.js.map +1 -1
  156. package/packages/logging/dist/shipper.d.ts +74 -3
  157. package/packages/logging/dist/shipper.d.ts.map +1 -1
  158. package/packages/logging/dist/shipper.js +88 -5
  159. package/packages/logging/dist/shipper.js.map +1 -1
  160. package/packages/logging/dist/singleton.d.ts.map +1 -1
  161. package/packages/logging/dist/singleton.js +1 -0
  162. package/packages/logging/dist/singleton.js.map +1 -1
  163. package/packages/logging/package.json +1 -1
  164. package/packages/logging/src/config.ts +22 -0
  165. package/packages/logging/src/incidents/schema.ts +0 -0
  166. package/packages/logging/src/index.ts +17 -0
  167. package/packages/logging/src/server/http.ts +42 -3
  168. package/packages/logging/src/server/incident-qdrant.ts +177 -0
  169. package/packages/logging/src/server/incident-store.ts +192 -0
  170. package/packages/logging/src/server/index.ts +21 -0
  171. package/packages/logging/src/server/service.ts +79 -0
  172. package/packages/logging/src/shipper.ts +120 -5
  173. package/packages/logging/src/singleton.ts +1 -0
  174. package/packages/logging/test/fanout.test.ts +186 -0
  175. package/packages/logging/test/incidents.test.ts +404 -0
  176. package/packages/mcp-adapter/package.json +1 -1
  177. package/packages/memory-engine/package.json +1 -1
  178. package/packages/milestone-manager/package.json +1 -1
  179. package/packages/model-registry/package.json +1 -1
  180. package/packages/nextjs-plugin/package.json +1 -1
  181. package/packages/notification/dist/adapters/detail-format.d.ts +42 -0
  182. package/packages/notification/dist/adapters/detail-format.d.ts.map +1 -0
  183. package/packages/notification/dist/adapters/detail-format.js +94 -0
  184. package/packages/notification/dist/adapters/detail-format.js.map +1 -0
  185. package/packages/notification/dist/adapters/email-adapter.d.ts.map +1 -1
  186. package/packages/notification/dist/adapters/email-adapter.js +3 -2
  187. package/packages/notification/dist/adapters/email-adapter.js.map +1 -1
  188. package/packages/notification/dist/adapters/line-adapter.d.ts +33 -0
  189. package/packages/notification/dist/adapters/line-adapter.d.ts.map +1 -1
  190. package/packages/notification/dist/adapters/line-adapter.js +87 -9
  191. package/packages/notification/dist/adapters/line-adapter.js.map +1 -1
  192. package/packages/notification/dist/adapters/ntfy-adapter.d.ts.map +1 -1
  193. package/packages/notification/dist/adapters/ntfy-adapter.js +3 -2
  194. package/packages/notification/dist/adapters/ntfy-adapter.js.map +1 -1
  195. package/packages/notification/dist/adapters/telegram-adapter.d.ts +14 -0
  196. package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
  197. package/packages/notification/dist/adapters/telegram-adapter.js +89 -69
  198. package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
  199. package/packages/notification/dist/notification-center.d.ts.map +1 -1
  200. package/packages/notification/dist/notification-center.js +60 -7
  201. package/packages/notification/dist/notification-center.js.map +1 -1
  202. package/packages/notification/dist/pending-question.d.ts +69 -2
  203. package/packages/notification/dist/pending-question.d.ts.map +1 -1
  204. package/packages/notification/dist/pending-question.js +88 -2
  205. package/packages/notification/dist/pending-question.js.map +1 -1
  206. package/packages/notification/dist/types.d.ts +21 -0
  207. package/packages/notification/dist/types.d.ts.map +1 -1
  208. package/packages/notification/package.json +1 -1
  209. package/packages/notification/src/adapters/detail-format.ts +114 -0
  210. package/packages/notification/src/adapters/email-adapter.ts +3 -2
  211. package/packages/notification/src/adapters/line-adapter.ts +97 -9
  212. package/packages/notification/src/adapters/ntfy-adapter.ts +3 -2
  213. package/packages/notification/src/adapters/telegram-adapter.ts +98 -76
  214. package/packages/notification/src/notification-center.ts +66 -8
  215. package/packages/notification/src/pending-question.ts +160 -8
  216. package/packages/notification/src/types.ts +21 -0
  217. package/packages/notification/test/approval-payload.test.ts +269 -0
  218. package/packages/notification/test/detail-format.test.ts +106 -0
  219. package/packages/notification/test/pending-question-lifecycle.test.ts +195 -0
  220. package/packages/observability/package.json +1 -1
  221. package/packages/okf-indexer/dist/index.d.ts +9 -0
  222. package/packages/okf-indexer/dist/index.d.ts.map +1 -0
  223. package/packages/okf-indexer/dist/index.js +9 -0
  224. package/packages/okf-indexer/dist/index.js.map +1 -0
  225. package/packages/okf-indexer/package.json +1 -1
  226. package/packages/okf-indexer/src/index.ts +9 -0
  227. package/packages/paperclip-integration/package.json +1 -1
  228. package/packages/performance-optimizer/package.json +1 -1
  229. package/packages/permission-mode/package.json +1 -1
  230. package/packages/privilege-broker/package.json +1 -1
  231. package/packages/project-analyzer/package.json +1 -1
  232. package/packages/project-bootstrap/package.json +1 -1
  233. package/packages/projection-engine/package.json +1 -1
  234. package/packages/prompt-compiler/package.json +1 -1
  235. package/packages/prompt-versioning/package.json +1 -1
  236. package/packages/provider-adapter-sdk/package.json +1 -1
  237. package/packages/provider-router/dist/provider-router.d.ts.map +1 -1
  238. package/packages/provider-router/dist/types.d.ts +2 -2
  239. package/packages/provider-router/dist/types.d.ts.map +1 -1
  240. package/packages/provider-router/dist/types.js +1 -1
  241. package/packages/provider-router/package.json +1 -1
  242. package/packages/provider-router/src/types.ts +2 -2
  243. package/packages/provider-selector/package.json +1 -1
  244. package/packages/providers/package.json +1 -1
  245. package/packages/qdrant-skills/package.json +1 -1
  246. package/packages/quota-manager/package.json +1 -1
  247. package/packages/rate-limiter/package.json +1 -1
  248. package/packages/react-vite-plugin/package.json +1 -1
  249. package/packages/relay/package.json +1 -1
  250. package/packages/release-manager/package.json +1 -1
  251. package/packages/requirement-compiler/package.json +1 -1
  252. package/packages/runtime/package.json +1 -1
  253. package/packages/scheduler/dist/index.d.ts +9 -0
  254. package/packages/scheduler/dist/index.d.ts.map +1 -0
  255. package/packages/scheduler/dist/index.js +9 -0
  256. package/packages/scheduler/dist/index.js.map +1 -0
  257. package/packages/scheduler/dist/scheduler.d.ts +17 -0
  258. package/packages/scheduler/dist/scheduler.d.ts.map +1 -0
  259. package/packages/scheduler/dist/scheduler.js +41 -0
  260. package/packages/scheduler/dist/scheduler.js.map +1 -0
  261. package/packages/scheduler/package.json +1 -1
  262. package/packages/scheduler/src/index.ts +9 -0
  263. package/packages/scheduler-adapter/package.json +1 -1
  264. package/packages/session/package.json +1 -1
  265. package/packages/session-api/package.json +1 -1
  266. package/packages/session-export/package.json +1 -1
  267. package/packages/shared-context/dist/index.d.ts +7 -0
  268. package/packages/shared-context/dist/index.d.ts.map +1 -0
  269. package/packages/shared-context/dist/index.js +7 -0
  270. package/packages/shared-context/dist/index.js.map +1 -0
  271. package/packages/shared-context/dist/shared-context.d.ts +12 -0
  272. package/packages/shared-context/dist/shared-context.d.ts.map +1 -0
  273. package/packages/shared-context/dist/shared-context.js +39 -0
  274. package/packages/shared-context/dist/shared-context.js.map +1 -0
  275. package/packages/shared-context/package.json +1 -1
  276. package/packages/skill-mcp-client/dist/client.d.ts +56 -0
  277. package/packages/skill-mcp-client/dist/client.d.ts.map +1 -0
  278. package/packages/skill-mcp-client/dist/client.js +101 -0
  279. package/packages/skill-mcp-client/dist/client.js.map +1 -0
  280. package/packages/skill-mcp-client/dist/errors.d.ts +44 -0
  281. package/packages/skill-mcp-client/dist/errors.d.ts.map +1 -0
  282. package/packages/skill-mcp-client/dist/errors.js +68 -0
  283. package/packages/skill-mcp-client/dist/errors.js.map +1 -0
  284. package/packages/skill-mcp-client/dist/index.d.ts +9 -0
  285. package/packages/skill-mcp-client/dist/index.d.ts.map +1 -0
  286. package/packages/skill-mcp-client/dist/index.js +9 -0
  287. package/packages/skill-mcp-client/dist/index.js.map +1 -0
  288. package/packages/skill-mcp-client/dist/types.d.ts +106 -0
  289. package/packages/skill-mcp-client/dist/types.d.ts.map +1 -0
  290. package/packages/skill-mcp-client/dist/types.js +14 -0
  291. package/packages/skill-mcp-client/dist/types.js.map +1 -0
  292. package/packages/skill-mcp-client/package.json +1 -1
  293. package/packages/skill-registry/package.json +1 -1
  294. package/packages/skills/package.json +1 -5
  295. package/packages/sprint-planner/package.json +1 -1
  296. package/packages/subscription-engine/package.json +1 -1
  297. package/packages/task-compiler/package.json +1 -1
  298. package/packages/telegram/package.json +1 -1
  299. package/packages/telegram/src/agent-messages.d.ts +28 -0
  300. package/packages/telegram/src/agent-messages.js +90 -0
  301. package/packages/telegram/src/bindings.d.ts +296 -0
  302. package/packages/telegram/src/bindings.js +1086 -0
  303. package/packages/telegram/src/bus.d.ts +759 -0
  304. package/packages/telegram/src/bus.js +2732 -0
  305. package/packages/telegram/src/commands.d.ts +761 -0
  306. package/packages/telegram/src/commands.js +2622 -0
  307. package/packages/telegram/src/config.d.ts +222 -0
  308. package/packages/telegram/src/config.js +844 -0
  309. package/packages/telegram/src/delivery.d.ts +161 -0
  310. package/packages/telegram/src/delivery.js +550 -0
  311. package/packages/telegram/src/extension.d.ts +7 -0
  312. package/packages/telegram/src/extension.js +2444 -0
  313. package/packages/telegram/src/inbound.d.ts +94 -0
  314. package/packages/telegram/src/inbound.js +478 -0
  315. package/packages/telegram/src/index.d.ts +6 -0
  316. package/packages/telegram/src/index.js +6 -0
  317. package/packages/telegram/src/keyboard.d.ts +21 -0
  318. package/packages/telegram/src/keyboard.js +37 -0
  319. package/packages/telegram/src/lifecycle.d.ts +210 -0
  320. package/packages/telegram/src/lifecycle.js +538 -0
  321. package/packages/telegram/src/pi.d.ts +76 -0
  322. package/packages/telegram/src/pi.js +130 -0
  323. package/packages/telegram/src/polling.d.ts +332 -0
  324. package/packages/telegram/src/polling.js +1219 -0
  325. package/packages/telegram/src/replies.d.ts +210 -0
  326. package/packages/telegram/src/replies.js +752 -0
  327. package/packages/telegram/src/routing.d.ts +388 -0
  328. package/packages/telegram/src/routing.js +7886 -0
  329. package/packages/telegram/src/runtime.d.ts +159 -0
  330. package/packages/telegram/src/runtime.js +351 -0
  331. package/packages/telegram/src/telegram-api.d.ts +564 -0
  332. package/packages/telegram/src/telegram-api.js +1332 -0
  333. package/packages/telegram/src/turns.d.ts +108 -0
  334. package/packages/telegram/src/turns.js +482 -0
  335. package/packages/telegram/src/updates.d.ts +1545 -0
  336. package/packages/telegram/src/updates.js +6420 -0
  337. package/packages/tencentdb-memory/package.json +1 -1
  338. package/packages/tencentdb-sync/package.json +1 -1
  339. package/packages/test-data-generator/package.json +1 -1
  340. package/packages/test-generator/package.json +1 -1
  341. package/packages/todo-bd-sync/dist/detector.d.ts +54 -0
  342. package/packages/todo-bd-sync/dist/detector.d.ts.map +1 -0
  343. package/packages/todo-bd-sync/dist/detector.js +132 -0
  344. package/packages/todo-bd-sync/dist/detector.js.map +1 -0
  345. package/packages/todo-bd-sync/dist/extension.d.ts +30 -0
  346. package/packages/todo-bd-sync/dist/extension.d.ts.map +1 -0
  347. package/packages/todo-bd-sync/dist/extension.js +35 -0
  348. package/packages/todo-bd-sync/dist/extension.js.map +1 -0
  349. package/packages/todo-bd-sync/dist/index.d.ts +87 -0
  350. package/packages/todo-bd-sync/dist/index.d.ts.map +1 -0
  351. package/packages/todo-bd-sync/dist/index.js +296 -0
  352. package/packages/todo-bd-sync/dist/index.js.map +1 -0
  353. package/packages/todo-bd-sync/dist/sync.d.ts +72 -0
  354. package/packages/todo-bd-sync/dist/sync.d.ts.map +1 -0
  355. package/packages/todo-bd-sync/dist/sync.js +290 -0
  356. package/packages/todo-bd-sync/dist/sync.js.map +1 -0
  357. package/packages/todo-bd-sync/dist/task-analyzer.d.ts +49 -0
  358. package/packages/todo-bd-sync/dist/task-analyzer.d.ts.map +1 -0
  359. package/packages/todo-bd-sync/dist/task-analyzer.js +240 -0
  360. package/packages/todo-bd-sync/dist/task-analyzer.js.map +1 -0
  361. package/packages/todo-bd-sync/dist/todo-reminder.d.ts +126 -0
  362. package/packages/todo-bd-sync/dist/todo-reminder.d.ts.map +1 -0
  363. package/packages/todo-bd-sync/dist/todo-reminder.js +329 -0
  364. package/packages/todo-bd-sync/dist/todo-reminder.js.map +1 -0
  365. package/packages/todo-bd-sync/dist/types.d.ts +65 -0
  366. package/packages/todo-bd-sync/dist/types.d.ts.map +1 -0
  367. package/packages/todo-bd-sync/dist/types.js +67 -0
  368. package/packages/todo-bd-sync/dist/types.js.map +1 -0
  369. package/packages/todo-bd-sync/package.json +1 -1
  370. package/packages/token-estimation/dist/index.d.ts +94 -0
  371. package/packages/token-estimation/dist/index.d.ts.map +1 -0
  372. package/packages/token-estimation/dist/index.js +115 -0
  373. package/packages/token-estimation/dist/index.js.map +1 -0
  374. package/packages/token-estimation/package.json +1 -1
  375. package/packages/token-optimizer/package.json +1 -1
  376. package/packages/trajectory/package.json +1 -1
  377. package/packages/tui/package.json +1 -1
  378. package/packages/types/dist/ai-providers.d.ts +36 -0
  379. package/packages/types/dist/ai-providers.d.ts.map +1 -0
  380. package/packages/types/dist/ai-providers.js +66 -0
  381. package/packages/types/dist/ai-providers.js.map +1 -0
  382. package/packages/types/dist/index.d.ts +12 -0
  383. package/packages/types/dist/index.d.ts.map +1 -0
  384. package/packages/types/dist/index.js +12 -0
  385. package/packages/types/dist/index.js.map +1 -0
  386. package/packages/types/package.json +5 -5
  387. package/packages/types/src/index.ts +12 -0
  388. package/packages/voice-api/package.json +1 -1
  389. package/packages/webmcp-tester/package.json +1 -1
  390. package/packages/wiki/package.json +1 -1
  391. package/packages/workflow-events/package.json +1 -1
  392. package/packages/workspace-scanner/package.json +1 -1
  393. package/packages/worktree/package.json +1 -1
  394. package/packages/worktree/worktree.ts +1 -1
  395. package/packages/write-review/package.json +1 -1
  396. package/renderer.ts +1 -1
  397. package/scripts/build-packages.ts +162 -0
  398. package/scripts/relay-client.ts +2 -2
  399. package/scripts/typecheck-packages.mjs +3 -3
  400. package/scripts/unified-webhook-server.ts +22 -10
  401. package/scripts/verify-package-contents.ts +345 -0
  402. package/scripts/ws-server.ts +1 -1
  403. package/tracker.ts +1 -1
  404. package/harness/browser-scrapers/base-browser-scraper.d.ts +0 -91
  405. package/harness/browser-scrapers/chrome-profile-manager.d.ts +0 -80
  406. package/harness/browser-scrapers/codex-browser-scraper.d.ts +0 -60
  407. package/harness/browser-scrapers/index.d.ts +0 -29
  408. package/harness/browser-scrapers/zai-browser-scraper.d.ts +0 -79
  409. package/harness/e2e/browser-scrapers/base-browser-scraper.d.ts +0 -92
  410. package/harness/e2e/browser-scrapers/chrome-profile-manager.d.ts +0 -81
  411. package/harness/e2e/browser-scrapers/codex-browser-scraper.d.ts +0 -61
  412. package/harness/e2e/browser-scrapers/index.d.ts +0 -30
  413. package/harness/e2e/browser-scrapers/zai-browser-scraper.d.ts +0 -80
  414. package/harness/langchain/agents.d.ts +0 -143
  415. package/harness/langchain/agents.test.d.ts +0 -2
  416. package/harness/langchain/checkpointer.d.ts +0 -48
  417. package/harness/langchain/checkpointer.test.d.ts +0 -8
  418. package/harness/langchain/cron-cli.d.ts +0 -12
  419. package/harness/langchain/daemon.d.ts +0 -161
  420. package/harness/langchain/daemon.test.d.ts +0 -11
  421. package/harness/langchain/file-tools.d.ts +0 -97
  422. package/harness/langchain/graph.d.ts +0 -706
  423. package/harness/langchain/model-router.d.ts +0 -63
  424. package/harness/langchain/model-router.test.d.ts +0 -2
  425. package/harness/langchain/models.d.ts +0 -72
  426. package/harness/langchain/ping-pong-middleware.d.ts +0 -28
  427. package/harness/langchain/ping-pong-middleware.test.d.ts +0 -2
  428. package/harness/langchain/run.d.ts +0 -15
  429. package/harness/langchain/scoreboard.test.d.ts +0 -13
  430. package/harness/langchain/status-line.d.ts +0 -91
  431. package/harness/langchain/status-line.test.d.ts +0 -8
  432. package/harness/langchain/surge.d.ts +0 -111
  433. package/harness/langchain/surge.test.d.ts +0 -10
  434. package/harness/langchain/widget.d.ts +0 -105
  435. package/harness/langchain/widget.test.d.ts +0 -8
  436. package/packages/autonomous-runtime/dist/worker.d.ts +0 -63
  437. package/packages/autonomous-runtime/dist/worker.js +0 -133
  438. package/packages/capability-registry/src/types.d.ts +0 -47
  439. package/packages/clipboard/dist/clipboard.d.ts +0 -11
  440. package/packages/clipboard/dist/clipboard.js +0 -40
  441. package/packages/clipboard/dist/index.d.ts +0 -2
  442. package/packages/clipboard/dist/index.js +0 -2
  443. package/packages/clipboard/dist/types.d.ts +0 -13
  444. package/packages/clipboard/dist/types.js +0 -5
  445. package/packages/context-discovery/dist/discover.d.ts +0 -3
  446. package/packages/context-discovery/dist/discover.js +0 -42
  447. package/packages/context-discovery/dist/discovery.d.ts +0 -28
  448. package/packages/context-discovery/dist/discovery.js +0 -21
  449. package/packages/context-discovery/dist/index.d.ts +0 -3
  450. package/packages/context-discovery/dist/index.js +0 -2
  451. package/packages/context-discovery/dist/types.d.ts +0 -16
  452. package/packages/context-discovery/dist/types.js +0 -2
  453. package/packages/event-bus/dist/bus.d.ts +0 -15
  454. package/packages/event-bus/dist/bus.js +0 -90
  455. package/packages/event-bus/dist/index.d.ts +0 -4
  456. package/packages/event-bus/dist/index.js +0 -3
  457. package/packages/event-bus/dist/test/bus.test.js +0 -52
  458. package/packages/event-bus/dist/types.d.ts +0 -27
  459. package/packages/event-bus/dist/types.js +0 -10
  460. package/packages/event-store/dist/index.d.ts +0 -3
  461. package/packages/event-store/dist/index.js +0 -2
  462. package/packages/event-store/dist/store.d.ts +0 -15
  463. package/packages/event-store/dist/store.js +0 -131
  464. package/packages/event-store/dist/test/store.test.js +0 -57
  465. package/packages/event-store/dist/types.d.ts +0 -48
  466. package/packages/event-store/dist/types.js +0 -5
  467. package/packages/event-store/src/types.d.ts +0 -48
  468. package/packages/file-copy-helper/dist/src/extension.js +0 -186
  469. package/packages/model-registry/src/types.d.ts +0 -63
  470. package/packages/notification/dist/src/adapters/email-adapter.d.ts +0 -17
  471. package/packages/notification/dist/src/adapters/email-adapter.d.ts.map +0 -1
  472. package/packages/notification/dist/src/adapters/email-adapter.js +0 -89
  473. package/packages/notification/dist/src/adapters/email-adapter.js.map +0 -1
  474. package/packages/notification/dist/src/adapters/line-adapter.d.ts +0 -17
  475. package/packages/notification/dist/src/adapters/line-adapter.d.ts.map +0 -1
  476. package/packages/notification/dist/src/adapters/line-adapter.js +0 -99
  477. package/packages/notification/dist/src/adapters/line-adapter.js.map +0 -1
  478. package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts +0 -17
  479. package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts.map +0 -1
  480. package/packages/notification/dist/src/adapters/ntfy-adapter.js +0 -84
  481. package/packages/notification/dist/src/adapters/ntfy-adapter.js.map +0 -1
  482. package/packages/notification/dist/src/adapters/telegram-adapter.d.ts +0 -56
  483. package/packages/notification/dist/src/adapters/telegram-adapter.d.ts.map +0 -1
  484. package/packages/notification/dist/src/adapters/telegram-adapter.js +0 -251
  485. package/packages/notification/dist/src/adapters/telegram-adapter.js.map +0 -1
  486. package/packages/notification/dist/src/adapters/webhook-adapter.d.ts +0 -15
  487. package/packages/notification/dist/src/adapters/webhook-adapter.d.ts.map +0 -1
  488. package/packages/notification/dist/src/adapters/webhook-adapter.js +0 -64
  489. package/packages/notification/dist/src/adapters/webhook-adapter.js.map +0 -1
  490. package/packages/notification/dist/src/base-adapter.d.ts +0 -36
  491. package/packages/notification/dist/src/base-adapter.d.ts.map +0 -1
  492. package/packages/notification/dist/src/base-adapter.js +0 -39
  493. package/packages/notification/dist/src/base-adapter.js.map +0 -1
  494. package/packages/notification/dist/src/index.d.ts +0 -12
  495. package/packages/notification/dist/src/index.d.ts.map +0 -1
  496. package/packages/notification/dist/src/index.js +0 -12
  497. package/packages/notification/dist/src/index.js.map +0 -1
  498. package/packages/notification/dist/src/notification-center.d.ts +0 -89
  499. package/packages/notification/dist/src/notification-center.d.ts.map +0 -1
  500. package/packages/notification/dist/src/notification-center.js +0 -356
  501. package/packages/notification/dist/src/notification-center.js.map +0 -1
  502. package/packages/notification/dist/src/notification-policy.d.ts +0 -66
  503. package/packages/notification/dist/src/notification-policy.d.ts.map +0 -1
  504. package/packages/notification/dist/src/notification-policy.js +0 -153
  505. package/packages/notification/dist/src/notification-policy.js.map +0 -1
  506. package/packages/notification/dist/src/telegram-webhook-handler.d.ts +0 -86
  507. package/packages/notification/dist/src/telegram-webhook-handler.d.ts.map +0 -1
  508. package/packages/notification/dist/src/telegram-webhook-handler.js +0 -149
  509. package/packages/notification/dist/src/telegram-webhook-handler.js.map +0 -1
  510. package/packages/notification/dist/src/types.d.ts +0 -106
  511. package/packages/notification/dist/src/types.d.ts.map +0 -1
  512. package/packages/notification/dist/src/types.js +0 -7
  513. package/packages/notification/dist/src/types.js.map +0 -1
  514. package/packages/projection-engine/dist/engine.d.ts +0 -61
  515. package/packages/projection-engine/dist/engine.js +0 -76
  516. package/packages/projection-engine/dist/index.d.ts +0 -2
  517. package/packages/projection-engine/dist/index.js +0 -2
  518. package/packages/projection-engine/dist/project.d.ts +0 -3
  519. package/packages/projection-engine/dist/project.js +0 -24
  520. package/packages/projection-engine/dist/types.d.ts +0 -55
  521. package/packages/projection-engine/dist/types.js +0 -5
  522. package/packages/projection-engine/src/types.d.ts +0 -55
  523. package/packages/provider-adapter-sdk/dist/src/builder.d.ts +0 -140
  524. package/packages/provider-adapter-sdk/dist/src/builder.d.ts.map +0 -1
  525. package/packages/provider-adapter-sdk/dist/src/builder.js +0 -332
  526. package/packages/provider-adapter-sdk/dist/src/builder.js.map +0 -1
  527. package/packages/provider-adapter-sdk/dist/src/errors.d.ts +0 -71
  528. package/packages/provider-adapter-sdk/dist/src/errors.d.ts.map +0 -1
  529. package/packages/provider-adapter-sdk/dist/src/errors.js +0 -111
  530. package/packages/provider-adapter-sdk/dist/src/errors.js.map +0 -1
  531. package/packages/provider-adapter-sdk/dist/src/index.d.ts +0 -15
  532. package/packages/provider-adapter-sdk/dist/src/index.d.ts.map +0 -1
  533. package/packages/provider-adapter-sdk/dist/src/index.js +0 -19
  534. package/packages/provider-adapter-sdk/dist/src/index.js.map +0 -1
  535. package/packages/provider-adapter-sdk/dist/src/registry.d.ts +0 -108
  536. package/packages/provider-adapter-sdk/dist/src/registry.d.ts.map +0 -1
  537. package/packages/provider-adapter-sdk/dist/src/registry.js +0 -301
  538. package/packages/provider-adapter-sdk/dist/src/registry.js.map +0 -1
  539. package/packages/provider-adapter-sdk/dist/src/tester.d.ts +0 -71
  540. package/packages/provider-adapter-sdk/dist/src/tester.d.ts.map +0 -1
  541. package/packages/provider-adapter-sdk/dist/src/tester.js +0 -392
  542. package/packages/provider-adapter-sdk/dist/src/tester.js.map +0 -1
  543. package/packages/provider-adapter-sdk/dist/src/types.d.ts +0 -182
  544. package/packages/provider-adapter-sdk/dist/src/types.d.ts.map +0 -1
  545. package/packages/provider-adapter-sdk/dist/src/types.js +0 -11
  546. package/packages/provider-adapter-sdk/dist/src/types.js.map +0 -1
  547. package/packages/provider-router/dist/capability-registry/src/types.d.ts +0 -47
  548. package/packages/provider-router/dist/capability-registry/src/types.d.ts.map +0 -1
  549. package/packages/provider-router/dist/capability-registry/src/types.js +0 -10
  550. package/packages/provider-router/dist/capability-registry/src/types.js.map +0 -1
  551. package/packages/provider-router/dist/provider-router/src/index.d.ts +0 -23
  552. package/packages/provider-router/dist/provider-router/src/index.d.ts.map +0 -1
  553. package/packages/provider-router/dist/provider-router/src/index.js +0 -25
  554. package/packages/provider-router/dist/provider-router/src/index.js.map +0 -1
  555. package/packages/provider-router/dist/provider-router/src/policy.d.ts +0 -10
  556. package/packages/provider-router/dist/provider-router/src/policy.d.ts.map +0 -1
  557. package/packages/provider-router/dist/provider-router/src/policy.js +0 -45
  558. package/packages/provider-router/dist/provider-router/src/policy.js.map +0 -1
  559. package/packages/provider-router/dist/provider-router/src/provider-router.d.ts +0 -56
  560. package/packages/provider-router/dist/provider-router/src/provider-router.d.ts.map +0 -1
  561. package/packages/provider-router/dist/provider-router/src/provider-router.js +0 -222
  562. package/packages/provider-router/dist/provider-router/src/provider-router.js.map +0 -1
  563. package/packages/provider-router/dist/provider-router/src/strategies.d.ts +0 -19
  564. package/packages/provider-router/dist/provider-router/src/strategies.d.ts.map +0 -1
  565. package/packages/provider-router/dist/provider-router/src/strategies.js +0 -110
  566. package/packages/provider-router/dist/provider-router/src/strategies.js.map +0 -1
  567. package/packages/provider-router/dist/provider-router/src/types.d.ts +0 -91
  568. package/packages/provider-router/dist/provider-router/src/types.d.ts.map +0 -1
  569. package/packages/provider-router/dist/provider-router/src/types.js +0 -10
  570. package/packages/provider-router/dist/provider-router/src/types.js.map +0 -1
  571. package/packages/provider-router/dist/src/index.d.ts +0 -23
  572. package/packages/provider-router/dist/src/index.d.ts.map +0 -1
  573. package/packages/provider-router/dist/src/index.js +0 -25
  574. package/packages/provider-router/dist/src/index.js.map +0 -1
  575. package/packages/provider-router/dist/src/policy.d.ts +0 -10
  576. package/packages/provider-router/dist/src/policy.d.ts.map +0 -1
  577. package/packages/provider-router/dist/src/policy.js +0 -45
  578. package/packages/provider-router/dist/src/policy.js.map +0 -1
  579. package/packages/provider-router/dist/src/provider-router.d.ts +0 -56
  580. package/packages/provider-router/dist/src/provider-router.d.ts.map +0 -1
  581. package/packages/provider-router/dist/src/provider-router.js +0 -222
  582. package/packages/provider-router/dist/src/provider-router.js.map +0 -1
  583. package/packages/provider-router/dist/src/strategies.d.ts +0 -19
  584. package/packages/provider-router/dist/src/strategies.d.ts.map +0 -1
  585. package/packages/provider-router/dist/src/strategies.js +0 -110
  586. package/packages/provider-router/dist/src/strategies.js.map +0 -1
  587. package/packages/provider-router/dist/src/types.d.ts +0 -91
  588. package/packages/provider-router/dist/src/types.d.ts.map +0 -1
  589. package/packages/provider-router/dist/src/types.js +0 -10
  590. package/packages/provider-router/dist/src/types.js.map +0 -1
  591. package/packages/provider-router/dist/types/src/runtime-types.d.ts +0 -412
  592. package/packages/provider-router/dist/types/src/runtime-types.d.ts.map +0 -1
  593. package/packages/provider-router/dist/types/src/runtime-types.js +0 -2
  594. package/packages/provider-router/dist/types/src/runtime-types.js.map +0 -1
  595. package/packages/session-api/dist/api.d.ts +0 -83
  596. package/packages/session-api/dist/api.js +0 -35
  597. package/packages/session-api/dist/index.d.ts +0 -2
  598. package/packages/session-api/dist/index.js +0 -2
  599. package/packages/session-api/dist/types.d.ts +0 -22
  600. package/packages/session-api/dist/types.js +0 -5
  601. package/packages/session-export/dist/exporter.d.ts +0 -28
  602. package/packages/session-export/dist/exporter.js +0 -38
  603. package/packages/session-export/dist/index.d.ts +0 -2
  604. package/packages/session-export/dist/index.js +0 -2
  605. package/packages/session-export/dist/types.d.ts +0 -20
  606. package/packages/session-export/dist/types.js +0 -5
  607. package/packages/subscription-engine/dist/engine.d.ts +0 -13
  608. package/packages/subscription-engine/dist/engine.js +0 -40
  609. package/packages/subscription-engine/dist/index.d.ts +0 -2
  610. package/packages/subscription-engine/dist/index.js +0 -2
  611. package/packages/subscription-engine/dist/subscriptions.d.ts +0 -19
  612. package/packages/subscription-engine/dist/subscriptions.js +0 -32
  613. package/packages/subscription-engine/dist/types.d.ts +0 -24
  614. package/packages/subscription-engine/dist/types.js +0 -5
  615. package/packages/trajectory/test/store.test.d.ts +0 -2
  616. package/packages/workflow-events/dist/events.d.ts +0 -38
  617. package/packages/workflow-events/dist/events.js +0 -46
  618. package/packages/workflow-events/dist/index.d.ts +0 -2
  619. package/packages/workflow-events/dist/index.js +0 -2
  620. package/packages/workflow-events/dist/pipeline.d.ts +0 -21
  621. package/packages/workflow-events/dist/pipeline.js +0 -27
  622. package/packages/workflow-events/dist/types.d.ts +0 -110
  623. package/packages/workflow-events/dist/types.js +0 -5
  624. package/packages/workflow-events/src/types.d.ts +0 -110
@@ -0,0 +1,1332 @@
1
+ /**
2
+ * Telegram API transport helpers
3
+ * Zones: telegram transport, filesystem, runtime diagnostics
4
+ *
5
+ * Wraps bot API calls, file uploads/downloads (including voice messages),
6
+ * multipart sending, runtime transport binding, and Telegram temp-file lifecycle.
7
+ */
8
+ import { randomUUID } from "node:crypto";
9
+ import { createWriteStream, openAsBlob } from "node:fs";
10
+ import { mkdir, readdir, rename, stat, unlink, writeFile, } from "node:fs/promises";
11
+ import { request as requestHttps } from "node:https";
12
+ import { join } from "node:path";
13
+ import { Readable, Transform } from "node:stream";
14
+ import { pipeline } from "node:stream/promises";
15
+ import { resolveTelegramAttachmentsDir } from "./paths.js";
16
+ const TELEGRAM_API_BASE = "https://api.telegram.org";
17
+ export const TELEGRAM_FILE_MAX_BYTES = 50 * 1024 * 1024;
18
+ /** First positive safe-integer byte limit among ordered env names, else the caller's default. */
19
+ export function getTelegramByteLimitFromEnv(env, names, defaultValue) {
20
+ for (const name of names) {
21
+ const rawValue = env[name]?.trim();
22
+ if (!rawValue)
23
+ continue;
24
+ const parsed = Number(rawValue);
25
+ if (Number.isSafeInteger(parsed) && parsed > 0)
26
+ return parsed;
27
+ }
28
+ return defaultValue;
29
+ }
30
+ function getTelegramApiTempDir() {
31
+ return resolveTelegramAttachmentsDir();
32
+ }
33
+ const TELEGRAM_TEMP_FILE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
34
+ const TELEGRAM_DOWNLOAD_RENAME_MAX_ATTEMPTS = 6;
35
+ const activeTelegramApiWorkspaceAdmissionOperationIds = new Set();
36
+ const TELEGRAM_INBOUND_FILE_MAX_BYTES = getTelegramByteLimitFromEnv(process.env, ["PI_TELEGRAM_INBOUND_FILE_MAX_BYTES", "TELEGRAM_MAX_FILE_SIZE_BYTES"], TELEGRAM_FILE_MAX_BYTES);
37
+ const TELEGRAM_NETWORK_FAMILY_ENV = "PI_TELEGRAM_NETWORK_FAMILY";
38
+ const TELEGRAM_NETWORK_FAMILY_VALUES = new Set([
39
+ "auto",
40
+ "ipv4",
41
+ "ipv6",
42
+ "ipv4-fallback",
43
+ ]);
44
+ export class TelegramApiAuthorityError extends Error {
45
+ requestIssued;
46
+ constructor(requestIssued) {
47
+ super("Telegram API call authority is unavailable.");
48
+ this.name = "TelegramApiAuthorityError";
49
+ this.requestIssued = requestIssued;
50
+ }
51
+ }
52
+ /** Recheck caller authority, classifying refusal by whether a Bot API request was already issued. */
53
+ export function assertTelegramApiCallAuthority(assertAuthority, requestIssued) {
54
+ try {
55
+ assertAuthority?.();
56
+ }
57
+ catch {
58
+ throw new TelegramApiAuthorityError(requestIssued);
59
+ }
60
+ }
61
+ function parseTelegramApiTargetInteger(value) {
62
+ const parsed = typeof value === "number"
63
+ ? value
64
+ : typeof value === "string" && /^-?\d+$/u.test(value)
65
+ ? Number(value)
66
+ : undefined;
67
+ return parsed !== undefined && Number.isSafeInteger(parsed)
68
+ ? parsed
69
+ : undefined;
70
+ }
71
+ export function createTelegramApiTargetActivityRuntime() {
72
+ const pending = new Map();
73
+ const pendingChats = new Map();
74
+ const messageScopedMethods = new Set([
75
+ "deleteMessage",
76
+ "editMessageCaption",
77
+ "editMessageReplyMarkup",
78
+ "editMessageText",
79
+ ]);
80
+ return {
81
+ begin(method, body) {
82
+ const chatId = parseTelegramApiTargetInteger(body.chat_id);
83
+ const threadId = parseTelegramApiTargetInteger(body.message_thread_id);
84
+ const messageId = parseTelegramApiTargetInteger(body.message_id);
85
+ const chatScoped = chatId !== undefined &&
86
+ threadId === undefined &&
87
+ messageId !== undefined &&
88
+ messageScopedMethods.has(method);
89
+ if (chatId === undefined || (threadId === undefined && !chatScoped)) {
90
+ return () => undefined;
91
+ }
92
+ const key = threadId === undefined ? undefined : `${chatId}:${threadId}`;
93
+ if (key) {
94
+ const existing = pending.get(key);
95
+ if (existing)
96
+ existing.count += 1;
97
+ else
98
+ pending.set(key, {
99
+ target: { chatId, threadId: threadId },
100
+ count: 1,
101
+ });
102
+ }
103
+ else {
104
+ pendingChats.set(chatId, (pendingChats.get(chatId) ?? 0) + 1);
105
+ }
106
+ let completed = false;
107
+ return () => {
108
+ if (completed)
109
+ return;
110
+ completed = true;
111
+ if (!key) {
112
+ const count = pendingChats.get(chatId);
113
+ if (!count || count <= 1)
114
+ pendingChats.delete(chatId);
115
+ else
116
+ pendingChats.set(chatId, count - 1);
117
+ return;
118
+ }
119
+ const current = pending.get(key);
120
+ if (!current || current.count <= 1)
121
+ pending.delete(key);
122
+ else
123
+ current.count -= 1;
124
+ };
125
+ },
126
+ hasPendingTarget(target) {
127
+ return (pendingChats.has(target.chatId) ||
128
+ (target.threadId !== undefined &&
129
+ pending.has(`${target.chatId}:${target.threadId}`)));
130
+ },
131
+ listPendingTargets() {
132
+ return Array.from(pending.values(), ({ target }) => ({ ...target }));
133
+ },
134
+ listPendingChats() {
135
+ return Array.from(pendingChats.keys());
136
+ },
137
+ };
138
+ }
139
+ export function createTelegramApiTargetTrackingClient(client, activity) {
140
+ const track = async (method, body, operation) => {
141
+ const end = activity.begin(method, body);
142
+ try {
143
+ return await operation();
144
+ }
145
+ finally {
146
+ end();
147
+ }
148
+ };
149
+ return {
150
+ call: (method, body, options) => track(method, body, () => client.call(method, body, options)),
151
+ callMultipart: (method, fields, fileField, filePath, fileName, options) => track(method, fields, () => client.callMultipart(method, fields, fileField, filePath, fileName, options)),
152
+ downloadFile: client.downloadFile,
153
+ answerCallbackQuery: client.answerCallbackQuery,
154
+ ...(client.answerGuestQuery
155
+ ? { answerGuestQuery: client.answerGuestQuery }
156
+ : {}),
157
+ };
158
+ }
159
+ export class TelegramApiWorkspaceAdmissionError extends Error {
160
+ code;
161
+ constructor(code, message) {
162
+ super(message);
163
+ this.name = "TelegramApiWorkspaceAdmissionError";
164
+ this.code = code;
165
+ }
166
+ }
167
+ export function getTelegramApiWorkspaceAdmissionScope(body) {
168
+ if (!("chat_id" in body))
169
+ return undefined;
170
+ const chatId = parseTelegramApiTargetInteger(body.chat_id);
171
+ if (chatId === undefined || chatId === 0)
172
+ return { kind: "profile" };
173
+ if (!("message_thread_id" in body) || body.message_thread_id === undefined) {
174
+ return { kind: "chat", chatId };
175
+ }
176
+ const threadId = parseTelegramApiTargetInteger(body.message_thread_id);
177
+ if (threadId === undefined || threadId <= 0)
178
+ return { kind: "profile" };
179
+ return { kind: "target", target: { chatId, threadId } };
180
+ }
181
+ export function createTelegramApiWorkspaceAdmissionClient(client, admission, options = {}) {
182
+ const admit = async (method, body, operation) => {
183
+ const scope = getTelegramApiWorkspaceAdmissionScope(body);
184
+ if (!scope)
185
+ return operation();
186
+ const currentAdmission = typeof admission === "function" ? admission() : admission;
187
+ if (!currentAdmission) {
188
+ throw new TelegramApiWorkspaceAdmissionError("unavailable", "Telegram API Workspace admission authority is unavailable.");
189
+ }
190
+ const operationId = `api:${(options.createOperationId ?? randomUUID)()}`;
191
+ if (activeTelegramApiWorkspaceAdmissionOperationIds.has(operationId)) {
192
+ throw new TelegramApiWorkspaceAdmissionError("duplicate-operation", "Telegram API Workspace admission operation is already active.");
193
+ }
194
+ activeTelegramApiWorkspaceAdmissionOperationIds.add(operationId);
195
+ try {
196
+ const acquired = currentAdmission.acquireAdmission({
197
+ operationId,
198
+ operationKind: `api.${method}`,
199
+ scope,
200
+ });
201
+ if (acquired.kind === "blocked") {
202
+ throw new TelegramApiWorkspaceAdmissionError("blocked", "Telegram API target is temporarily unavailable during Workspace retirement.");
203
+ }
204
+ try {
205
+ return await operation();
206
+ }
207
+ finally {
208
+ try {
209
+ if (!currentAdmission.releaseAdmission(acquired.lease)) {
210
+ throw new TelegramApiWorkspaceAdmissionError("release-lost", "Telegram API Workspace admission lease disappeared before release.");
211
+ }
212
+ }
213
+ catch (error) {
214
+ try {
215
+ options.onReleaseError?.(error, method);
216
+ }
217
+ catch {
218
+ // Diagnostics cannot convert an already-settled API request into replay.
219
+ }
220
+ }
221
+ }
222
+ }
223
+ finally {
224
+ activeTelegramApiWorkspaceAdmissionOperationIds.delete(operationId);
225
+ }
226
+ };
227
+ return {
228
+ call: (method, body, callOptions) => admit(method, body, () => client.call(method, body, callOptions)),
229
+ callMultipart: (method, fields, fileField, filePath, fileName, callOptions) => admit(method, fields, () => client.callMultipart(method, fields, fileField, filePath, fileName, callOptions)),
230
+ downloadFile: client.downloadFile,
231
+ answerCallbackQuery: client.answerCallbackQuery,
232
+ ...(client.answerGuestQuery
233
+ ? { answerGuestQuery: client.answerGuestQuery }
234
+ : {}),
235
+ };
236
+ }
237
+ function sanitizeFileName(name) {
238
+ return name.replace(/[^a-zA-Z0-9._-]+/g, "_");
239
+ }
240
+ /** Names follow `<kind>-<scope>-<messageId>[-<index>][-<name>|.<ext>]`. */
241
+ /**
242
+ * `scope` is the bot `@username` (id fallback) in a private chat and the public chat `@username` (id fallback) elsewhere.
243
+ * A negative chat id loses its sign, so the name never contains two adjacent dashes.
244
+ */
245
+ export function createTelegramAttachmentFileName(source, generatedName, botScope) {
246
+ const chat = source.chat;
247
+ const clean = (value) => sanitizeFileName(value.replace(/^@/u, "")).replace(/^[-.]+/u, "");
248
+ const scope = source.scope
249
+ ? clean(source.scope)
250
+ : chat?.type === "private" || !chat
251
+ ? botScope
252
+ ? clean(botScope)
253
+ : chat
254
+ ? String(Math.abs(chat.id))
255
+ : ""
256
+ : chat.username
257
+ ? clean(chat.username)
258
+ : String(Math.abs(chat.id));
259
+ const dot = generatedName.lastIndexOf(".");
260
+ const extension = dot > 0 ? sanitizeFileName(generatedName.slice(dot)) : "";
261
+ const tail = source.userFileName
262
+ ? `-${clean(source.userFileName)}`
263
+ : extension;
264
+ return ([sanitizeFileName(source.kind), scope, String(source.messageId)]
265
+ .filter(Boolean)
266
+ .join("-") +
267
+ (source.index !== undefined ? `-${source.index}` : "") +
268
+ tail);
269
+ }
270
+ class TelegramApiMalformedSuccessError extends Error {
271
+ constructor(method, detail) {
272
+ super(`Telegram API ${method} ${detail}`);
273
+ this.name = "TelegramApiMalformedSuccessError";
274
+ }
275
+ }
276
+ export class TelegramApiCommitUnknownError extends Error {
277
+ kind = "commit-unknown";
278
+ method;
279
+ cause;
280
+ constructor(method, cause) {
281
+ super(`Telegram API ${method} may have committed before transport failed.`);
282
+ this.name = "TelegramApiCommitUnknownError";
283
+ this.method = method;
284
+ this.cause = cause;
285
+ }
286
+ }
287
+ export function isTelegramApiCommitUnknownError(error) {
288
+ return error instanceof TelegramApiCommitUnknownError;
289
+ }
290
+ class TelegramApiHttpError extends Error {
291
+ status;
292
+ retryAfterSeconds;
293
+ rejectedRequestMethod;
294
+ requestTarget;
295
+ constructor(message, status, retryAfterSeconds, rejectedRequestMethod) {
296
+ super(message);
297
+ this.status = status;
298
+ this.retryAfterSeconds = retryAfterSeconds;
299
+ this.rejectedRequestMethod = rejectedRequestMethod;
300
+ }
301
+ }
302
+ function attachTelegramApiRequestTarget(error, body) {
303
+ if (!(error instanceof TelegramApiHttpError))
304
+ return;
305
+ const chatId = Number(body.chat_id);
306
+ const threadId = Number(body.message_thread_id);
307
+ if (!Number.isSafeInteger(chatId) || !Number.isSafeInteger(threadId))
308
+ return;
309
+ error.requestTarget = { chatId, threadId };
310
+ }
311
+ export class TelegramApiStaleTargetError extends Error {
312
+ requestTarget;
313
+ constructor(message, requestTarget) {
314
+ super(message);
315
+ this.name = "TelegramApiStaleTargetError";
316
+ this.requestTarget = { ...requestTarget };
317
+ }
318
+ }
319
+ export function getTelegramApiErrorRequestTarget(error) {
320
+ const target = error instanceof TelegramApiHttpError ||
321
+ error instanceof TelegramApiStaleTargetError
322
+ ? error.requestTarget
323
+ : undefined;
324
+ return target ? { ...target } : undefined;
325
+ }
326
+ export function isTelegramStaleTargetHttpError(error) {
327
+ if (!(error instanceof TelegramApiHttpError) || error.status !== 400)
328
+ return false;
329
+ return /^Telegram API \w+ failed: HTTP 400: Bad Request: (message thread not found|thread not found|topic not found|topic deleted|topic closed|thread closed|forum topic closed|message thread closed|topic_id_invalid|topic_closed)$/i.test(error.message);
330
+ }
331
+ /** Only a parsed Telegram rejection of this method proves a request had no effect. */
332
+ export function isTelegramApiRequestRejected(error, method) {
333
+ return (error instanceof TelegramApiHttpError &&
334
+ error.rejectedRequestMethod !== undefined &&
335
+ error.rejectedRequestMethod === method);
336
+ }
337
+ export function isTelegramMessageNotModifiedError(error) {
338
+ return (error instanceof Error && error.message.includes("message is not modified"));
339
+ }
340
+ const TELEGRAM_RETRY_SAFE_METHODS = new Set([
341
+ "answerCallbackQuery",
342
+ "closeForumTopic",
343
+ "deleteForumTopic",
344
+ "deleteMessage",
345
+ "deleteWebhook",
346
+ "editForumTopic",
347
+ "editMessageCaption",
348
+ "editMessageReplyMarkup",
349
+ "editMessageText",
350
+ "getChat",
351
+ "getFile",
352
+ "getMe",
353
+ "getUpdates",
354
+ "sendChatAction",
355
+ "sendMessageDraft",
356
+ "sendRichMessageDraft",
357
+ "setMyCommands",
358
+ ]);
359
+ export function isTelegramApiMethodRetrySafe(method) {
360
+ return TELEGRAM_RETRY_SAFE_METHODS.has(method);
361
+ }
362
+ export function isRetryableTelegramApiError(error) {
363
+ return (error instanceof TelegramApiHttpError &&
364
+ (error.status === 429 ||
365
+ (error.status !== undefined && error.status >= 500)));
366
+ }
367
+ export function getTelegramApiRetryAfterMs(error) {
368
+ return error instanceof TelegramApiHttpError &&
369
+ error.retryAfterSeconds !== undefined
370
+ ? Math.max(0, error.retryAfterSeconds * 1000)
371
+ : undefined;
372
+ }
373
+ export function isTelegramMessageUnavailableError(error) {
374
+ return (error instanceof TelegramApiHttpError &&
375
+ error.status === 400 &&
376
+ /Bad Request: (message to edit not found|message not found|message_id_invalid)/iu.test(error.message));
377
+ }
378
+ function getTelegramRetryDelayMs(error, attempt, baseDelayMs) {
379
+ if (error instanceof TelegramApiHttpError &&
380
+ error.retryAfterSeconds !== undefined) {
381
+ return Math.max(0, error.retryAfterSeconds * 1000);
382
+ }
383
+ return Math.max(0, baseDelayMs * 2 ** attempt);
384
+ }
385
+ function getTelegramApiAbortReason(signal) {
386
+ return signal.reason ?? new DOMException("Aborted", "AbortError");
387
+ }
388
+ function throwIfTelegramApiCallAborted(signal) {
389
+ if (signal?.aborted)
390
+ throw getTelegramApiAbortReason(signal);
391
+ }
392
+ function sleepTelegramRetry(ms, signal) {
393
+ if (signal?.aborted)
394
+ return Promise.reject(getTelegramApiAbortReason(signal));
395
+ return new Promise((resolve, reject) => {
396
+ const timeout = setTimeout(() => {
397
+ signal?.removeEventListener("abort", onAbort);
398
+ resolve();
399
+ }, ms);
400
+ const onAbort = () => {
401
+ clearTimeout(timeout);
402
+ reject(getTelegramApiAbortReason(signal));
403
+ };
404
+ signal?.addEventListener("abort", onAbort, { once: true });
405
+ if (signal?.aborted)
406
+ onAbort();
407
+ });
408
+ }
409
+ function assertTelegramFileSizeWithinLimit(size, maxFileSizeBytes) {
410
+ if (size === undefined || maxFileSizeBytes === undefined)
411
+ return;
412
+ if (size <= maxFileSizeBytes)
413
+ return;
414
+ throw new Error(`Telegram file exceeds size limit (${size} bytes > ${maxFileSizeBytes} bytes)`);
415
+ }
416
+ function createTelegramDownloadLimitTransform(maxFileSizeBytes) {
417
+ let downloadedBytes = 0;
418
+ return new Transform({
419
+ transform(chunk, _encoding, callback) {
420
+ downloadedBytes += chunk.byteLength;
421
+ try {
422
+ assertTelegramFileSizeWithinLimit(downloadedBytes, maxFileSizeBytes);
423
+ callback(undefined, chunk);
424
+ }
425
+ catch (error) {
426
+ callback(error instanceof Error ? error : new Error(String(error)));
427
+ }
428
+ },
429
+ });
430
+ }
431
+ async function writeTelegramDownloadResponse(response, targetPath, maxFileSizeBytes) {
432
+ if (!response.body) {
433
+ const buffer = Buffer.from(await response.arrayBuffer());
434
+ assertTelegramFileSizeWithinLimit(buffer.byteLength, maxFileSizeBytes);
435
+ await writeFile(targetPath, buffer, { mode: 0o600 });
436
+ return;
437
+ }
438
+ await pipeline(Readable.from(response.body, { objectMode: false }), createTelegramDownloadLimitTransform(maxFileSizeBytes), createWriteStream(targetPath, { mode: 0o600 }));
439
+ }
440
+ async function publishTelegramDownload(partPath, targetPath, signal) {
441
+ for (let attempt = 0;; attempt++) {
442
+ throwIfTelegramApiCallAborted(signal);
443
+ try {
444
+ await rename(partPath, targetPath);
445
+ return;
446
+ }
447
+ catch (error) {
448
+ const code = error?.code;
449
+ // Windows may briefly deny replacement while another publisher/scanner holds the file.
450
+ if (process.platform !== "win32" ||
451
+ (code !== "EPERM" && code !== "EACCES") ||
452
+ attempt + 1 >= TELEGRAM_DOWNLOAD_RENAME_MAX_ATTEMPTS)
453
+ throw error;
454
+ await sleepTelegramRetry(50 * 2 ** attempt, signal);
455
+ }
456
+ }
457
+ }
458
+ async function removeTelegramPartialDownload(path) {
459
+ try {
460
+ await unlink(path);
461
+ }
462
+ catch {
463
+ // ignore
464
+ }
465
+ }
466
+ async function parseTelegramApiResponse(response, method) {
467
+ let data;
468
+ try {
469
+ if (typeof response.text === "function") {
470
+ const text = await response.text();
471
+ data = text
472
+ ? JSON.parse(text)
473
+ : undefined;
474
+ }
475
+ else {
476
+ data = (await response.json());
477
+ }
478
+ }
479
+ catch {
480
+ data = undefined;
481
+ }
482
+ if (response.ok === false) {
483
+ const status = `HTTP ${response.status}`;
484
+ const description = data?.description ? `: ${data.description}` : "";
485
+ const retryAfterHeader = response.headers?.get("retry-after");
486
+ const retryAfterSeconds = data?.parameters?.retry_after ??
487
+ (retryAfterHeader ? Number.parseInt(retryAfterHeader, 10) : undefined);
488
+ throw new TelegramApiHttpError(`Telegram API ${method} failed: ${status}${description}`, response.status, Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : undefined, data?.ok === false &&
489
+ data.error_code === response.status &&
490
+ [400, 401, 403, 404, 429].includes(response.status)
491
+ ? method
492
+ : undefined);
493
+ }
494
+ if (!data) {
495
+ throw new TelegramApiMalformedSuccessError(method, "returned invalid JSON");
496
+ }
497
+ return data;
498
+ }
499
+ function unwrapTelegramApiResult(method, data) {
500
+ if (data.ok && data.result === undefined) {
501
+ throw new TelegramApiMalformedSuccessError(method, "returned no result");
502
+ }
503
+ if (!data.ok) {
504
+ throw new Error(data.description || `Telegram API ${method} failed`);
505
+ }
506
+ return data.result;
507
+ }
508
+ function getTelegramNetworkFamilyPolicy(env = process.env) {
509
+ const value = env[TELEGRAM_NETWORK_FAMILY_ENV]?.trim().toLowerCase();
510
+ if (TELEGRAM_NETWORK_FAMILY_VALUES.has(value)) {
511
+ return value;
512
+ }
513
+ return "ipv4-fallback";
514
+ }
515
+ function getTelegramNetworkFamily(policy) {
516
+ if (policy === "ipv4")
517
+ return 4;
518
+ if (policy === "ipv6")
519
+ return 6;
520
+ return undefined;
521
+ }
522
+ function isTelegramTransportFailure(error) {
523
+ if (!(error instanceof Error))
524
+ return false;
525
+ if (error.name === "AbortError")
526
+ return false;
527
+ if (error instanceof TypeError && /fetch failed/i.test(error.message)) {
528
+ return true;
529
+ }
530
+ if (error instanceof AggregateError)
531
+ return true;
532
+ const code = getErrorCode(error);
533
+ if (code === "ECONNREFUSED" ||
534
+ code === "ETIMEDOUT" ||
535
+ code === "ENETUNREACH" ||
536
+ code === "EHOSTUNREACH" ||
537
+ code === "ECONNRESET" ||
538
+ code === "EAI_AGAIN") {
539
+ return true;
540
+ }
541
+ return isTelegramTransportFailure(error.cause);
542
+ }
543
+ function getTelegramRequestBodyBuffer(body) {
544
+ if (body === undefined || body === null)
545
+ return undefined;
546
+ if (typeof body === "string")
547
+ return Buffer.from(body);
548
+ if (body instanceof Uint8Array)
549
+ return Buffer.from(body);
550
+ throw new Error("Unsupported Telegram HTTPS request body");
551
+ }
552
+ async function buildTelegramMultipartBody(fields, fileField, fileBlob, fileName) {
553
+ const boundary = `pi-telegram-${randomUUID()}`;
554
+ const chunks = [];
555
+ for (const [key, value] of Object.entries(fields)) {
556
+ chunks.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`));
557
+ }
558
+ chunks.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${fileField}"; filename="${fileName}"\r\nContent-Type: ${fileBlob.type || "application/octet-stream"}\r\n\r\n`), Buffer.from(await fileBlob.arrayBuffer()), Buffer.from(`\r\n--${boundary}--\r\n`));
559
+ return {
560
+ body: Buffer.concat(chunks),
561
+ contentType: `multipart/form-data; boundary=${boundary}`,
562
+ };
563
+ }
564
+ async function telegramHttpsFetch(input, init, family) {
565
+ const url = new URL(typeof input === "string" || input instanceof URL ? input : input.url);
566
+ const body = getTelegramRequestBodyBuffer(init.body);
567
+ const headers = new Headers(init.headers);
568
+ if (body && !headers.has("content-length")) {
569
+ headers.set("content-length", String(body.byteLength));
570
+ }
571
+ return new Promise((resolve, reject) => {
572
+ const req = requestHttps(url, {
573
+ method: init.method ?? "GET",
574
+ family,
575
+ headers: Object.fromEntries(headers.entries()),
576
+ }, (res) => {
577
+ const responseHeaders = new Headers();
578
+ for (const [key, value] of Object.entries(res.headers)) {
579
+ if (Array.isArray(value))
580
+ responseHeaders.set(key, value.join(", "));
581
+ else if (value !== undefined)
582
+ responseHeaders.set(key, String(value));
583
+ }
584
+ resolve(new Response(Readable.toWeb(res), {
585
+ status: res.statusCode ?? 200,
586
+ statusText: res.statusMessage,
587
+ headers: responseHeaders,
588
+ }));
589
+ });
590
+ req.on("error", reject);
591
+ if (init.signal) {
592
+ if (init.signal.aborted)
593
+ req.destroy(new DOMException("Aborted", "AbortError"));
594
+ else {
595
+ init.signal.addEventListener("abort", () => req.destroy(new DOMException("Aborted", "AbortError")), { once: true });
596
+ }
597
+ }
598
+ req.end(body);
599
+ });
600
+ }
601
+ let telegramHttpsFetchForTesting;
602
+ export function setTelegramApiHttpsFetchForTesting(fetchImpl) {
603
+ const previous = telegramHttpsFetchForTesting;
604
+ telegramHttpsFetchForTesting = fetchImpl;
605
+ return () => {
606
+ telegramHttpsFetchForTesting = previous;
607
+ };
608
+ }
609
+ async function telegramFetch(input, init = {}, family) {
610
+ if (!family)
611
+ return fetch(input, init);
612
+ return (telegramHttpsFetchForTesting ?? telegramHttpsFetch)(input, init, family);
613
+ }
614
+ async function callTelegramTransportRequest(request, allowFallback = true) {
615
+ const policy = getTelegramNetworkFamilyPolicy();
616
+ if (policy === "auto")
617
+ return request();
618
+ const family = getTelegramNetworkFamily(policy);
619
+ if (family)
620
+ return request(family);
621
+ if (!allowFallback)
622
+ return request();
623
+ try {
624
+ return await request();
625
+ }
626
+ catch (error) {
627
+ if (!isTelegramTransportFailure(error))
628
+ throw error;
629
+ return request(4);
630
+ }
631
+ }
632
+ function getErrorCode(error) {
633
+ const maybeCode = error.code;
634
+ return typeof maybeCode === "string" ? maybeCode : undefined;
635
+ }
636
+ function getErrorAddress(error) {
637
+ const maybeAddress = error.address;
638
+ return typeof maybeAddress === "string" ? maybeAddress : undefined;
639
+ }
640
+ function getErrorPort(error) {
641
+ const maybePort = error.port;
642
+ return typeof maybePort === "number" ? maybePort : undefined;
643
+ }
644
+ function getErrorFamily(error) {
645
+ const maybeFamily = error.family;
646
+ if (typeof maybeFamily === "number" || typeof maybeFamily === "string") {
647
+ return maybeFamily;
648
+ }
649
+ return undefined;
650
+ }
651
+ function describeTelegramErrorSummary(error) {
652
+ return {
653
+ name: error.name,
654
+ message: error.message,
655
+ ...(getErrorCode(error) ? { code: getErrorCode(error) } : {}),
656
+ };
657
+ }
658
+ function describeTelegramTransportAttempt(error) {
659
+ return {
660
+ name: error.name,
661
+ ...(getErrorCode(error) ? { code: getErrorCode(error) } : {}),
662
+ ...(getErrorAddress(error) ? { address: getErrorAddress(error) } : {}),
663
+ ...(getErrorPort(error) ? { port: getErrorPort(error) } : {}),
664
+ ...(getErrorFamily(error) ? { family: getErrorFamily(error) } : {}),
665
+ };
666
+ }
667
+ function describeTelegramTransportError(error) {
668
+ if (!isTelegramTransportFailure(error) || !(error instanceof Error)) {
669
+ return undefined;
670
+ }
671
+ const cause = error.cause instanceof Error ? error.cause : undefined;
672
+ const aggregate = error instanceof AggregateError
673
+ ? error
674
+ : cause instanceof AggregateError
675
+ ? cause
676
+ : undefined;
677
+ const attempts = aggregate?.errors
678
+ .filter((attempt) => attempt instanceof Error)
679
+ .map(describeTelegramTransportAttempt);
680
+ return {
681
+ error: describeTelegramErrorSummary(error),
682
+ ...(cause ? { cause: describeTelegramErrorSummary(cause) } : {}),
683
+ ...(attempts && attempts.length > 0 ? { attempts } : {}),
684
+ };
685
+ }
686
+ function withTelegramTransportDiagnostics(error, details) {
687
+ const transport = describeTelegramTransportError(error);
688
+ return transport ? { ...details, transport } : details;
689
+ }
690
+ async function callTelegramWithRetry(method, request, options) {
691
+ const assertAuthority = options?.assertAuthority;
692
+ let requestIssued = false;
693
+ const beforeRequest = () => {
694
+ assertTelegramApiCallAuthority(assertAuthority, requestIssued);
695
+ requestIssued = true;
696
+ };
697
+ const retrySafe = options?.retrySafety === "safe" ||
698
+ (options?.retrySafety !== "non-idempotent" &&
699
+ isTelegramApiMethodRetrySafe(method));
700
+ const maxAttempts = Math.max(1, options?.maxAttempts ?? 3);
701
+ const retryBaseDelayMs = options?.retryBaseDelayMs ?? 500;
702
+ const waitBeforeRetry = async (error, attempt) => {
703
+ const ms = getTelegramRetryDelayMs(error, attempt, retryBaseDelayMs);
704
+ if (!options?.signal?.aborted &&
705
+ error instanceof TelegramApiHttpError &&
706
+ error.status === 429) {
707
+ options?.onRetryWait?.({
708
+ method,
709
+ delayMs: ms,
710
+ attempt,
711
+ ...(error.retryAfterSeconds !== undefined
712
+ ? { retryAfterSeconds: error.retryAfterSeconds }
713
+ : {}),
714
+ });
715
+ }
716
+ if (options?.sleep)
717
+ await options.sleep(ms);
718
+ else
719
+ await sleepTelegramRetry(ms, options?.signal);
720
+ throwIfTelegramApiCallAborted(options?.signal);
721
+ };
722
+ for (let attempt = 0;; attempt += 1) {
723
+ throwIfTelegramApiCallAborted(options?.signal);
724
+ assertTelegramApiCallAuthority(assertAuthority, requestIssued);
725
+ try {
726
+ const result = unwrapTelegramApiResult(method, await parseTelegramApiResponse(await callTelegramTransportRequest((family) => request(family, beforeRequest), retrySafe), method));
727
+ assertTelegramApiCallAuthority(assertAuthority, requestIssued);
728
+ return result;
729
+ }
730
+ catch (error) {
731
+ // Authority refusal is never a transport failure, retry grant or acknowledged success.
732
+ if (error instanceof TelegramApiAuthorityError)
733
+ throw error;
734
+ assertTelegramApiCallAuthority(assertAuthority, requestIssued);
735
+ const retryable = isRetryableTelegramApiError(error) &&
736
+ !(options?.retryRateLimit === false &&
737
+ error instanceof TelegramApiHttpError &&
738
+ error.status === 429);
739
+ if (!retrySafe) {
740
+ if (error instanceof TelegramApiHttpError && error.status === 429) {
741
+ if (attempt >= maxAttempts - 1)
742
+ throw error;
743
+ await waitBeforeRetry(error, attempt);
744
+ continue;
745
+ }
746
+ if (error instanceof TelegramApiMalformedSuccessError ||
747
+ isTelegramTransportFailure(error) ||
748
+ (error instanceof TelegramApiHttpError &&
749
+ error.status !== undefined &&
750
+ error.status >= 500)) {
751
+ throw new TelegramApiCommitUnknownError(method, error);
752
+ }
753
+ throw error;
754
+ }
755
+ if (attempt >= maxAttempts - 1 || !retryable)
756
+ throw error;
757
+ await waitBeforeRetry(error, attempt);
758
+ }
759
+ }
760
+ }
761
+ export async function cleanupTelegramTempFiles(tempDir, maxAgeMs, now = Date.now()) {
762
+ let removedCount = 0;
763
+ let entries;
764
+ try {
765
+ entries = await readdir(tempDir, { withFileTypes: true });
766
+ }
767
+ catch {
768
+ return 0;
769
+ }
770
+ for (const entry of entries) {
771
+ // The directory holds only attachment scratch files, so age alone decides.
772
+ if (!entry.isFile())
773
+ continue;
774
+ const path = join(tempDir, entry.name);
775
+ try {
776
+ const stats = await stat(path);
777
+ if (now - stats.mtimeMs <= maxAgeMs)
778
+ continue;
779
+ await unlink(path);
780
+ removedCount += 1;
781
+ }
782
+ catch {
783
+ // ignore
784
+ }
785
+ }
786
+ return removedCount;
787
+ }
788
+ export async function prepareTelegramTempDir(tempDir, maxAgeMs) {
789
+ await mkdir(tempDir, { recursive: true, mode: 0o700 });
790
+ return cleanupTelegramTempFiles(tempDir, maxAgeMs);
791
+ }
792
+ function assertTelegramBotTokenConfigured(botToken) {
793
+ if (!botToken)
794
+ throw new Error("Telegram bot token is not configured");
795
+ return botToken;
796
+ }
797
+ export async function callTelegram(botToken, method, body, options) {
798
+ const configuredBotToken = assertTelegramBotTokenConfigured(botToken);
799
+ try {
800
+ return await callTelegramWithRetry(method, async (family, beforeRequest) => {
801
+ const encodedBody = JSON.stringify(body);
802
+ beforeRequest();
803
+ return telegramFetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
804
+ method: "POST",
805
+ headers: { "content-type": "application/json" },
806
+ body: encodedBody,
807
+ signal: options?.signal,
808
+ }, family);
809
+ }, options);
810
+ }
811
+ catch (error) {
812
+ attachTelegramApiRequestTarget(error, body);
813
+ throw error;
814
+ }
815
+ }
816
+ export async function fetchTelegramBotIdentity(botToken, fetchImpl = fetch) {
817
+ const url = `${TELEGRAM_API_BASE}/bot${botToken}/getMe`;
818
+ const response = await callTelegramTransportRequest((family) => fetchImpl === fetch ? telegramFetch(url, {}, family) : fetchImpl(url));
819
+ return response.json();
820
+ }
821
+ /**
822
+ * Low-level helper to send a multipart/form-data request to the Telegram Bot API.
823
+ * This is the core implementation used for uploading voice messages, photos,
824
+ * documents, animations, etc. It handles FormData construction, retry logic
825
+ * (via callTelegramWithRetry), and error recording under the "multipart" category.
826
+ */
827
+ export async function callTelegramMultipart(botToken, method, fields, fileField, filePath, fileName, options) {
828
+ const configuredBotToken = assertTelegramBotTokenConfigured(botToken);
829
+ options = options && { ...options };
830
+ assertTelegramApiCallAuthority(options?.assertAuthority, false);
831
+ const fileBlob = await openAsBlob(filePath);
832
+ try {
833
+ return await callTelegramWithRetry(method, async (family, beforeRequest) => {
834
+ if (family) {
835
+ const multipart = await buildTelegramMultipartBody(fields, fileField, fileBlob, fileName);
836
+ beforeRequest();
837
+ return telegramFetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
838
+ method: "POST",
839
+ headers: { "content-type": multipart.contentType },
840
+ body: multipart.body,
841
+ signal: options?.signal,
842
+ }, family);
843
+ }
844
+ const form = new FormData();
845
+ for (const [key, value] of Object.entries(fields)) {
846
+ form.set(key, value);
847
+ }
848
+ form.set(fileField, fileBlob, fileName);
849
+ beforeRequest();
850
+ return telegramFetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
851
+ method: "POST",
852
+ body: form,
853
+ signal: options?.signal,
854
+ });
855
+ }, options);
856
+ }
857
+ catch (error) {
858
+ attachTelegramApiRequestTarget(error, fields);
859
+ throw error;
860
+ }
861
+ }
862
+ export async function downloadTelegramFile(botToken, fileId, suggestedName, tempDir, options) {
863
+ const configuredBotToken = assertTelegramBotTokenConfigured(botToken);
864
+ const file = await callTelegram(configuredBotToken, "getFile", { file_id: fileId }, { signal: options?.signal });
865
+ assertTelegramFileSizeWithinLimit(file.file_size, options?.maxFileSizeBytes);
866
+ await mkdir(tempDir, { recursive: true, mode: 0o700 });
867
+ // Names carry scope and message id, so the same message maps to the same path; publish by rename.
868
+ const targetPath = join(tempDir, sanitizeFileName(suggestedName));
869
+ const partPath = `${targetPath}.${randomUUID()}.part`;
870
+ const response = await callTelegramTransportRequest((family) => telegramFetch(`${TELEGRAM_API_BASE}/file/bot${configuredBotToken}/${file.file_path}`, { signal: options?.signal }, family));
871
+ if (!response.ok) {
872
+ throw new Error(`Failed to download Telegram file: ${response.status}`);
873
+ }
874
+ const contentLength = response.headers?.get("content-length");
875
+ assertTelegramFileSizeWithinLimit(contentLength ? Number.parseInt(contentLength, 10) : undefined, options?.maxFileSizeBytes);
876
+ try {
877
+ await writeTelegramDownloadResponse(response, partPath, options?.maxFileSizeBytes);
878
+ await publishTelegramDownload(partPath, targetPath, options?.signal);
879
+ }
880
+ catch (error) {
881
+ await removeTelegramPartialDownload(partPath);
882
+ throw error;
883
+ }
884
+ return targetPath;
885
+ }
886
+ export async function answerTelegramCallbackQuery(botToken, callbackQueryId, text, options = {}) {
887
+ try {
888
+ await callTelegram(botToken, "answerCallbackQuery", text
889
+ ? { callback_query_id: callbackQueryId, text }
890
+ : { callback_query_id: callbackQueryId }, options.assertAuthority
891
+ ? { assertAuthority: options.assertAuthority }
892
+ : undefined);
893
+ }
894
+ catch (error) {
895
+ options.recordRuntimeEvent?.("api", error, withTelegramTransportDiagnostics(error, {
896
+ method: "answerCallbackQuery",
897
+ }));
898
+ if (error instanceof TelegramApiAuthorityError)
899
+ throw error;
900
+ }
901
+ }
902
+ export function createTelegramChatActionSender(sendChatAction, action) {
903
+ return (chatId, options) => sendChatAction(chatId, action, options);
904
+ }
905
+ export function createTelegramNativeMarkdownDraftSender(deps) {
906
+ return (chatId, draftId, text, options) => {
907
+ if (text === undefined) {
908
+ return deps.sendMessageDraft(chatId, draftId, text, options);
909
+ }
910
+ return deps.sendRichMessageDraft({
911
+ chat_id: chatId,
912
+ draft_id: draftId,
913
+ rich_message: { markdown: text },
914
+ ...(options?.message_thread_id !== undefined
915
+ ? { message_thread_id: options.message_thread_id }
916
+ : {}),
917
+ });
918
+ };
919
+ }
920
+ export function createTelegramAssistantDraftSender(deps) {
921
+ const sendNativeDraft = createTelegramNativeMarkdownDraftSender(deps);
922
+ return (chatId, draftId, text, options) => {
923
+ if (text === undefined || deps.getAssistantRenderingMode() === "rich") {
924
+ return sendNativeDraft(chatId, draftId, text, options);
925
+ }
926
+ return deps.sendMessageDraft(chatId, draftId, deps.renderMarkdownToHtmlDraft(text), {
927
+ ...options,
928
+ parse_mode: "HTML",
929
+ });
930
+ };
931
+ }
932
+ export function buildTelegramAnswerGuestQueryBody(guestQueryId, text, options) {
933
+ const body = { guest_query_id: guestQueryId };
934
+ if (options?.result) {
935
+ body.result = options.result;
936
+ }
937
+ else if (text !== undefined || options?.richMessage) {
938
+ const inputContent = options?.richMessage
939
+ ? { rich_message: options.richMessage }
940
+ : { message_text: text };
941
+ if (!options?.richMessage && options?.parseMode) {
942
+ inputContent.parse_mode = options.parseMode;
943
+ }
944
+ body.result = {
945
+ type: "article",
946
+ id: "1",
947
+ title: "Response",
948
+ input_message_content: inputContent,
949
+ };
950
+ }
951
+ return body;
952
+ }
953
+ export function createDefaultTelegramBridgeApiRuntime(deps) {
954
+ const client = createTelegramApiClient(deps.getBotToken, {
955
+ recordRuntimeEvent: deps.recordRuntimeEvent,
956
+ });
957
+ const admittedClient = deps.workspaceAdmission
958
+ ? createTelegramApiWorkspaceAdmissionClient(client, deps.workspaceAdmission, {
959
+ onReleaseError(error, method) {
960
+ deps.recordRuntimeEvent("api", error, {
961
+ phase: "workspace-admission-release",
962
+ method,
963
+ });
964
+ },
965
+ })
966
+ : client;
967
+ const runtime = createTelegramBridgeApiRuntime({
968
+ client: deps.targetActivity
969
+ ? createTelegramApiTargetTrackingClient(admittedClient, deps.targetActivity)
970
+ : admittedClient,
971
+ tempDir: getTelegramApiTempDir(),
972
+ maxFileSizeBytes: TELEGRAM_INBOUND_FILE_MAX_BYTES,
973
+ tempFileMaxAgeMs: TELEGRAM_TEMP_FILE_MAX_AGE_MS,
974
+ ...(deps.getBotScope ? { getBotScope: deps.getBotScope } : {}),
975
+ recordRuntimeEvent: deps.recordRuntimeEvent,
976
+ captureRequestErrorHandler: deps.captureRequestErrorHandler,
977
+ });
978
+ return {
979
+ ...runtime,
980
+ async deleteWorkspaceThread(authorize) {
981
+ // The exclusive deletion permit replaces ordinary target admission.
982
+ const target = authorize();
983
+ const deleted = await client.call("deleteForumTopic", {
984
+ chat_id: target.chatId,
985
+ message_thread_id: target.threadId,
986
+ }, { maxAttempts: 1, retrySafety: "non-idempotent" });
987
+ if (deleted !== true)
988
+ throw new Error("Telegram Workspace Thread deletion was not confirmed.");
989
+ },
990
+ };
991
+ }
992
+ export function createTelegramBridgeApiRuntime(deps) {
993
+ const recoverRequestError = async (handler, error) => {
994
+ try {
995
+ await handler?.(error);
996
+ }
997
+ catch (recoveryError) {
998
+ deps.recordRuntimeEvent("api", recoveryError, {
999
+ phase: "stale-target-recovery",
1000
+ });
1001
+ }
1002
+ };
1003
+ const now = deps.now ?? Date.now;
1004
+ const chatActionMinIntervalMs = Math.max(0, deps.chatActionMinIntervalMs ?? 2_000);
1005
+ const chatActionMaxGates = Math.max(1, deps.chatActionMaxGates ?? 256);
1006
+ const chatActionGates = new Map();
1007
+ const chatActionChatGates = new Map();
1008
+ const getChatActionKeys = (method, body) => {
1009
+ if (method !== "sendChatAction")
1010
+ return undefined;
1011
+ const chatId = body.chat_id;
1012
+ const action = body.action;
1013
+ if ((typeof chatId !== "number" && typeof chatId !== "string") ||
1014
+ typeof action !== "string") {
1015
+ return undefined;
1016
+ }
1017
+ const chat = String(chatId);
1018
+ const threadId = body.message_thread_id;
1019
+ return {
1020
+ action: `${chat}:${typeof threadId === "number" || typeof threadId === "string"
1021
+ ? String(threadId)
1022
+ : "all"}:${action}`,
1023
+ chat,
1024
+ };
1025
+ };
1026
+ const callRecorded = async (method, body, options) => {
1027
+ const recoverError = deps.captureRequestErrorHandler?.(body);
1028
+ const chatActionKeys = getChatActionKeys(method, body);
1029
+ if (chatActionKeys) {
1030
+ const nowMs = now();
1031
+ for (const [key, candidate] of chatActionGates) {
1032
+ if (!candidate.inFlight && nowMs >= candidate.notBeforeMs) {
1033
+ chatActionGates.delete(key);
1034
+ }
1035
+ }
1036
+ for (const [key, candidate] of chatActionChatGates) {
1037
+ if (!candidate.inFlight && nowMs >= candidate.notBeforeMs) {
1038
+ chatActionChatGates.delete(key);
1039
+ }
1040
+ }
1041
+ let gate = chatActionGates.get(chatActionKeys.action);
1042
+ if (!gate) {
1043
+ if (chatActionGates.size >= chatActionMaxGates)
1044
+ return true;
1045
+ gate = { notBeforeMs: 0 };
1046
+ chatActionGates.set(chatActionKeys.action, gate);
1047
+ }
1048
+ let chatGate = chatActionChatGates.get(chatActionKeys.chat);
1049
+ if (!chatGate) {
1050
+ if (chatActionChatGates.size >= chatActionMaxGates) {
1051
+ return true;
1052
+ }
1053
+ chatGate = { notBeforeMs: 0 };
1054
+ chatActionChatGates.set(chatActionKeys.chat, chatGate);
1055
+ }
1056
+ if (gate.inFlight)
1057
+ return (await gate.inFlight);
1058
+ if (chatGate.inFlight)
1059
+ return true;
1060
+ if (nowMs < gate.notBeforeMs || nowMs < chatGate.notBeforeMs) {
1061
+ return true;
1062
+ }
1063
+ let request;
1064
+ request = Promise.resolve()
1065
+ .then(() => deps.client.call(method, body, {
1066
+ ...options,
1067
+ retryRateLimit: false,
1068
+ }))
1069
+ .then((result) => {
1070
+ gate.notBeforeMs = now() + chatActionMinIntervalMs;
1071
+ return result;
1072
+ })
1073
+ .catch(async (error) => {
1074
+ await recoverRequestError(recoverError, error);
1075
+ if (error instanceof TelegramApiHttpError && error.status === 429) {
1076
+ const retryAfterMs = Math.max(chatActionMinIntervalMs, (error.retryAfterSeconds ?? 0) * 1_000);
1077
+ const notBeforeMs = now() + retryAfterMs;
1078
+ gate.notBeforeMs = notBeforeMs;
1079
+ chatGate.notBeforeMs = Math.max(chatGate.notBeforeMs, notBeforeMs);
1080
+ deps.recordRuntimeEvent("api", error, withTelegramTransportDiagnostics(error, {
1081
+ method,
1082
+ rateLimited: true,
1083
+ retryAfterMs,
1084
+ }));
1085
+ return true;
1086
+ }
1087
+ deps.recordRuntimeEvent("api", error, withTelegramTransportDiagnostics(error, { method }));
1088
+ throw error;
1089
+ })
1090
+ .finally(() => {
1091
+ if (gate.inFlight === request)
1092
+ gate.inFlight = undefined;
1093
+ if (chatGate.inFlight === request)
1094
+ chatGate.inFlight = undefined;
1095
+ });
1096
+ gate.inFlight = request;
1097
+ chatGate.inFlight = request;
1098
+ return request;
1099
+ }
1100
+ try {
1101
+ return await deps.client.call(method, body, options);
1102
+ }
1103
+ catch (error) {
1104
+ await recoverRequestError(recoverError, error);
1105
+ if (method === "deleteMessage" &&
1106
+ error instanceof TelegramApiHttpError &&
1107
+ error.status === 400 &&
1108
+ error.message ===
1109
+ "Telegram API deleteMessage failed: HTTP 400: Bad Request: message to delete not found") {
1110
+ return true;
1111
+ }
1112
+ deps.recordRuntimeEvent("api", error, withTelegramTransportDiagnostics(error, { method }));
1113
+ throw error;
1114
+ }
1115
+ };
1116
+ return {
1117
+ call: callRecorded,
1118
+ /**
1119
+ * Sends a multipart/form-data request (used for sending voice messages,
1120
+ * photos, documents, animations, etc.).
1121
+ * Errors are recorded under the "multipart" category for diagnostics.
1122
+ */
1123
+ callMultipart: async (method, fields, fileField, filePath, fileName, options) => {
1124
+ const recoverError = deps.captureRequestErrorHandler?.(fields);
1125
+ try {
1126
+ return await deps.client.callMultipart(method, fields, fileField, filePath, fileName, options);
1127
+ }
1128
+ catch (error) {
1129
+ await recoverRequestError(recoverError, error);
1130
+ deps.recordRuntimeEvent("multipart", error, withTelegramTransportDiagnostics(error, { method, fileName }));
1131
+ throw error;
1132
+ }
1133
+ },
1134
+ /**
1135
+ * Downloads a file from the Telegram servers into the local temp directory.
1136
+ * Used for inbound voice messages, photos, documents, etc.
1137
+ */
1138
+ downloadFile: async (fileId, suggestedName, source) => {
1139
+ try {
1140
+ return await deps.client.downloadFile(fileId, source
1141
+ ? createTelegramAttachmentFileName(source, suggestedName, deps.getBotScope?.())
1142
+ : suggestedName, deps.tempDir, {
1143
+ maxFileSizeBytes: deps.maxFileSizeBytes,
1144
+ });
1145
+ }
1146
+ catch (error) {
1147
+ deps.recordRuntimeEvent("download", error, withTelegramTransportDiagnostics(error, { suggestedName }));
1148
+ throw error;
1149
+ }
1150
+ },
1151
+ deleteWebhook: (signal) => callRecorded("deleteWebhook", { drop_pending_updates: false }, { signal }),
1152
+ getUpdates: (body, signal) => callRecorded("getUpdates", body, { signal }),
1153
+ setMyCommands: (commands, options) => callRecorded("setMyCommands", { commands }, options),
1154
+ sendChatAction: (chatId, action, options) => callRecorded("sendChatAction", {
1155
+ chat_id: chatId,
1156
+ action,
1157
+ ...(options?.message_thread_id !== undefined
1158
+ ? { message_thread_id: options.message_thread_id }
1159
+ : {}),
1160
+ }),
1161
+ sendTypingAction: createTelegramChatActionSender((chatId, action, options) => callRecorded("sendChatAction", {
1162
+ chat_id: chatId,
1163
+ action,
1164
+ ...(options?.message_thread_id !== undefined
1165
+ ? { message_thread_id: options.message_thread_id }
1166
+ : {}),
1167
+ }), "typing"),
1168
+ sendRecordVoiceAction: createTelegramChatActionSender((chatId, action, options) => callRecorded("sendChatAction", {
1169
+ chat_id: chatId,
1170
+ action,
1171
+ ...(options?.message_thread_id !== undefined
1172
+ ? { message_thread_id: options.message_thread_id }
1173
+ : {}),
1174
+ }), "record_voice"),
1175
+ sendMessageDraft: (chatId, draftId, text, options) => {
1176
+ const body = {
1177
+ chat_id: chatId,
1178
+ draft_id: draftId,
1179
+ };
1180
+ if (text !== undefined)
1181
+ body.text = text;
1182
+ if (options?.parse_mode !== undefined)
1183
+ body.parse_mode = options.parse_mode;
1184
+ if (options?.entities !== undefined)
1185
+ body.entities = options.entities;
1186
+ if (options?.message_thread_id !== undefined)
1187
+ body.message_thread_id = options.message_thread_id;
1188
+ return callRecorded("sendMessageDraft", body);
1189
+ },
1190
+ sendMessage: (body, options) => callRecorded("sendMessage", body, options),
1191
+ sendRichMessage: (body, options) => callRecorded("sendRichMessage", body, options),
1192
+ sendRichMessageDraft: (body) => callRecorded("sendRichMessageDraft", body),
1193
+ editMessageText: async (body, options) => {
1194
+ const recoverError = deps.captureRequestErrorHandler?.(body);
1195
+ try {
1196
+ await deps.client.call("editMessageText", body, options);
1197
+ return "edited";
1198
+ }
1199
+ catch (error) {
1200
+ if (isTelegramMessageNotModifiedError(error))
1201
+ return "unchanged";
1202
+ await recoverRequestError(recoverError, error);
1203
+ deps.recordRuntimeEvent("api", error, withTelegramTransportDiagnostics(error, {
1204
+ method: "editMessageText",
1205
+ }));
1206
+ throw error;
1207
+ }
1208
+ },
1209
+ editMessageReplyMarkup: async (chatId, messageId, replyMarkup) => {
1210
+ await callRecorded("editMessageReplyMarkup", {
1211
+ chat_id: chatId,
1212
+ message_id: messageId,
1213
+ reply_markup: replyMarkup,
1214
+ });
1215
+ },
1216
+ answerCallbackQuery: async (callbackQueryId, text, options) => {
1217
+ if (options?.assertAuthority) {
1218
+ try {
1219
+ await callRecorded("answerCallbackQuery", text
1220
+ ? { callback_query_id: callbackQueryId, text }
1221
+ : { callback_query_id: callbackQueryId }, { assertAuthority: options.assertAuthority });
1222
+ }
1223
+ catch (error) {
1224
+ if (error instanceof TelegramApiAuthorityError)
1225
+ throw error;
1226
+ }
1227
+ return;
1228
+ }
1229
+ try {
1230
+ await deps.client.answerCallbackQuery(callbackQueryId, text);
1231
+ }
1232
+ catch (error) {
1233
+ deps.recordRuntimeEvent("api", error, withTelegramTransportDiagnostics(error, {
1234
+ method: "answerCallbackQuery",
1235
+ }));
1236
+ if (error instanceof TelegramApiAuthorityError)
1237
+ throw error;
1238
+ }
1239
+ },
1240
+ answerGuestQuery: (guestQueryId, text, options) => callRecorded("answerGuestQuery", buildTelegramAnswerGuestQueryBody(guestQueryId, text, options)),
1241
+ answerGuestQueryForInlineMessage: async (guestQueryId, text, options) => {
1242
+ const sent = await callRecorded("answerGuestQuery", buildTelegramAnswerGuestQueryBody(guestQueryId, text, options));
1243
+ return sent?.inline_message_id;
1244
+ },
1245
+ editGuestInlineMessage: async (inlineMessageId, content) => {
1246
+ await callRecorded("editMessageText", {
1247
+ inline_message_id: inlineMessageId,
1248
+ ...(content.richMessage
1249
+ ? { rich_message: content.richMessage }
1250
+ : { text: content.text }),
1251
+ ...(content.parseMode ? { parse_mode: content.parseMode } : {}),
1252
+ });
1253
+ },
1254
+ prepareTempDir: () => prepareTelegramTempDir(deps.tempDir, deps.tempFileMaxAgeMs),
1255
+ deleteMessage: (chatId, messageId) => callRecorded("deleteMessage", {
1256
+ chat_id: chatId,
1257
+ message_id: messageId,
1258
+ }).then(() => { }),
1259
+ };
1260
+ }
1261
+ /**
1262
+ * Creates a low-level Telegram Bot API client.
1263
+ * This is the main entry point for all direct Bot API communication
1264
+ * (both JSON calls and multipart uploads for files/voice).
1265
+ */
1266
+ export function createTelegramApiClient(getBotToken, options = {}) {
1267
+ const now = options.now ?? Date.now;
1268
+ const recordRuntimeEvent = options.recordRuntimeEvent;
1269
+ const draftRetryNotBeforeByTarget = new Map();
1270
+ return {
1271
+ call: async (method, body, options) => {
1272
+ const token = getBotToken();
1273
+ // Cooldown keys retain only the public bot-id prefix, not the credential.
1274
+ const botId = token?.match(/^(\d+):/)?.[1];
1275
+ const isDraft = method === "sendMessageDraft" || method === "sendRichMessageDraft";
1276
+ const draftKey = isDraft && botId
1277
+ ? `${botId}:${String(body.chat_id)}:${String(body.message_thread_id ?? "all")}`
1278
+ : undefined;
1279
+ if (draftKey) {
1280
+ const nowMs = now();
1281
+ for (const [key, deadline] of draftRetryNotBeforeByTarget) {
1282
+ if (nowMs >= deadline)
1283
+ draftRetryNotBeforeByTarget.delete(key);
1284
+ }
1285
+ if (draftRetryNotBeforeByTarget.has(draftKey))
1286
+ return false;
1287
+ }
1288
+ try {
1289
+ // A draft is a replaceable snapshot, not a body to replay after backoff.
1290
+ const retryWaitOptions = recordRuntimeEvent
1291
+ ? {
1292
+ onRetryWait: (wait) => {
1293
+ recordRuntimeEvent("api", new Error(`Telegram API rate limit: waiting ${wait.delayMs} ms before retrying ${wait.method}`), {
1294
+ phase: "retry-wait",
1295
+ method: wait.method,
1296
+ waitMs: wait.delayMs,
1297
+ attempt: wait.attempt,
1298
+ ...(wait.retryAfterSeconds !== undefined
1299
+ ? { retryAfterSeconds: wait.retryAfterSeconds }
1300
+ : {}),
1301
+ });
1302
+ },
1303
+ }
1304
+ : {};
1305
+ return await callTelegram(token, method, body, {
1306
+ ...(isDraft ? { ...options, maxAttempts: 1 } : options),
1307
+ ...retryWaitOptions,
1308
+ });
1309
+ }
1310
+ catch (error) {
1311
+ if (draftKey && isRetryableTelegramApiError(error)) {
1312
+ draftRetryNotBeforeByTarget.set(draftKey, Math.max(draftRetryNotBeforeByTarget.get(draftKey) ?? 0, now() +
1313
+ getTelegramRetryDelayMs(error, 0, options?.retryBaseDelayMs ?? 500)));
1314
+ }
1315
+ throw error;
1316
+ }
1317
+ },
1318
+ callMultipart: async (method, fields, fileField, filePath, fileName, options) => {
1319
+ return callTelegramMultipart(getBotToken(), method, fields, fileField, filePath, fileName, options);
1320
+ },
1321
+ downloadFile: async (fileId, suggestedName, tempDir, options) => {
1322
+ return downloadTelegramFile(getBotToken(), fileId, suggestedName, tempDir, options);
1323
+ },
1324
+ answerCallbackQuery: async (callbackQueryId, text, authorityOptions) => {
1325
+ const assertAuthority = authorityOptions?.assertAuthority;
1326
+ const callbackOptions = assertAuthority
1327
+ ? { ...options, assertAuthority }
1328
+ : options;
1329
+ await answerTelegramCallbackQuery(getBotToken(), callbackQueryId, text, callbackOptions);
1330
+ },
1331
+ };
1332
+ }