@qwen-code/qwen-code 0.21.2 → 0.21.3-nightly.20260803.e1e5b42ce

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 (352) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +53 -38
  2. package/bundled/qc-helper/docs/features/channels/github.md +16 -5
  3. package/bundled/qc-helper/docs/features/channels/gitlab.md +6 -0
  4. package/bundled/qc-helper/docs/features/code-review.md +92 -24
  5. package/bundled/qc-helper/docs/features/commands.md +46 -38
  6. package/bundled/qc-helper/docs/features/hooks.md +115 -18
  7. package/bundled/qc-helper/docs/features/memory.md +27 -0
  8. package/bundled/qc-helper/docs/features/skills.md +46 -1
  9. package/bundled/qc-helper/docs/features/sub-agents.md +42 -9
  10. package/bundled/qc-helper/docs/features/tool-use-summaries.md +7 -7
  11. package/bundled/qc-helper/docs/qwen-serve.md +46 -25
  12. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +62 -31
  13. package/bundled/qc-helper/docs/support/troubleshooting.md +6 -1
  14. package/bundled/review/DESIGN.md +167 -32
  15. package/bundled/review/SKILL.md +207 -38
  16. package/chunks/{MaxSizedBox-TQBQ247P.js → MaxSizedBox-FCEK5P5K.js} +41 -38
  17. package/chunks/{StandaloneSessionPicker-2HPTKCLX.js → StandaloneSessionPicker-AT77J3PM.js} +63 -59
  18. package/chunks/{acp-startup-profiler-2C4A5ZTJ.js → acp-startup-profiler-3RICG2YW.js} +2 -2
  19. package/chunks/{acpAgent-PS6EDGH3.js → acpAgent-PBMKZ5KH.js} +664 -560
  20. package/chunks/{agent-VDOUB35F.js → agent-55W5BPK3.js} +37 -34
  21. package/chunks/{agent-headless-EO53EFS2.js → agent-headless-I7VMHI22.js} +37 -34
  22. package/chunks/{anthropicContentGenerator-OFHSZJBF.js → anthropicContentGenerator-3CAHEZBM.js} +341 -29
  23. package/chunks/{artifact-tool-RDN2W7ND.js → artifact-tool-TBOYEKTY.js} +2 -2
  24. package/chunks/{askUserQuestion-OZERWH5R.js → askUserQuestion-23Y5WZFJ.js} +2 -2
  25. package/chunks/{bridge-W7AZSRZQ.js → bridge-XNAWEDAN.js} +43 -40
  26. package/chunks/{ca-6W3SK7OQ.js → ca-NYKNH222.js} +50 -5
  27. package/chunks/{channel-management-service-5S7YCWX5.js → channel-management-service-UHX56NBO.js} +7 -7
  28. package/chunks/{channel-settings-store-JFVRSNER.js → channel-settings-store-7P6RJSSF.js} +44 -41
  29. package/chunks/{channel-worker-group-OVWGYMZS.js → channel-worker-group-NDG4WOOZ.js} +8 -8
  30. package/chunks/{channel-worker-manager-H55GKOQA.js → channel-worker-manager-ZCW6UW34.js} +8 -8
  31. package/chunks/{channel-worker-supervisor-CABBXYHK.js → channel-worker-supervisor-ATGFTZRN.js} +4 -4
  32. package/chunks/{chunk-6MPDY6ZG.js → chunk-24ER7UV4.js} +1 -1
  33. package/chunks/{chunk-QDK4F2N5.js → chunk-2DNKEBXY.js} +1 -1
  34. package/chunks/{chunk-AUOULH6Q.js → chunk-2GF3IDEK.js} +1610 -2196
  35. package/chunks/{chunk-JYBU6YUE.js → chunk-2M5N3DHY.js} +1 -1
  36. package/chunks/{chunk-LHKCY7DY.js → chunk-2UISUFHQ.js} +3 -3
  37. package/chunks/{chunk-BBIQAX4V.js → chunk-2ZCYE3F2.js} +30 -18
  38. package/chunks/{chunk-GJ2GBPBD.js → chunk-332FMXSD.js} +5 -5
  39. package/chunks/{chunk-JXVC2PL3.js → chunk-33FNCQSY.js} +2 -2
  40. package/chunks/{chunk-P755N4CS.js → chunk-3GALLNII.js} +6 -6
  41. package/chunks/{chunk-YANIWTRZ.js → chunk-3ICGQSVO.js} +1 -1
  42. package/chunks/{chunk-NBD76HUW.js → chunk-3LGZEYOF.js} +2 -2
  43. package/chunks/{chunk-E2BHWIDT.js → chunk-3RXUYQJI.js} +6 -2
  44. package/chunks/{chunk-E5Z2AVNV.js → chunk-3SH53ANB.js} +0 -11
  45. package/chunks/{chunk-N4ACHWNH.js → chunk-432XCUHF.js} +1 -1
  46. package/chunks/{chunk-JLZ3RBQG.js → chunk-44EJDGMS.js} +144 -11
  47. package/chunks/{chunk-KR32NIAN.js → chunk-4JP34TZ6.js} +10 -10
  48. package/chunks/{chunk-L2E5GFAG.js → chunk-4LKVQFHM.js} +2 -2
  49. package/chunks/{chunk-YDLJGZHL.js → chunk-4NFY2S7N.js} +0 -33
  50. package/chunks/{chunk-NHFEMWAZ.js → chunk-5WY5QEM6.js} +1 -1
  51. package/chunks/{chunk-56Z7QEAQ.js → chunk-6BUPGI3J.js} +16 -16
  52. package/chunks/{chunk-H6I4PGRV.js → chunk-6DNHLK7F.js} +4 -4
  53. package/chunks/{chunk-XQK4XXTN.js → chunk-6O5DVH27.js} +1 -1
  54. package/chunks/{chunk-2RFKHEXL.js → chunk-6PLDPT2C.js} +1 -1
  55. package/chunks/{chunk-Q5WZTTWE.js → chunk-7UPJXZLD.js} +6 -8
  56. package/chunks/{chunk-CBBP4BMC.js → chunk-7UXI6P72.js} +82 -207
  57. package/chunks/{chunk-6CQWRWUC.js → chunk-7XLPYF6B.js} +3 -3
  58. package/chunks/{chunk-VEMNQMHA.js → chunk-7ZH55K26.js} +5 -13
  59. package/chunks/{chunk-JJPKFJBT.js → chunk-A2FOI762.js} +3 -3
  60. package/chunks/{chunk-LYI2PZDK.js → chunk-A5LNZGJB.js} +2 -2
  61. package/chunks/{chunk-3APUUXRF.js → chunk-A5YZ3FA2.js} +2 -1
  62. package/chunks/{chunk-SHN4KHDK.js → chunk-A6SX5TAH.js} +3 -3
  63. package/chunks/{chunk-QCXGC7QB.js → chunk-A7PONO5G.js} +3 -3
  64. package/chunks/{chunk-SZ3KHTDR.js → chunk-AKA24BGO.js} +1 -1
  65. package/chunks/{chunk-RSVPYAGK.js → chunk-B4HXOGF5.js} +5 -5
  66. package/chunks/{chunk-D6MDRSZ6.js → chunk-BBXSRVSN.js} +22 -11
  67. package/chunks/{chunk-Q3T5KA2V.js → chunk-BFHXDAR7.js} +1 -1
  68. package/chunks/{chunk-L55JWS76.js → chunk-BGXZBI5B.js} +1 -1
  69. package/chunks/{chunk-2KKCCLPT.js → chunk-C4IUDGPI.js} +1 -1
  70. package/chunks/{chunk-MM7TH7ZM.js → chunk-CFDR3GNL.js} +18 -3
  71. package/chunks/{chunk-3F2WDAV6.js → chunk-CFVFQWFB.js} +14 -14
  72. package/chunks/{chunk-E2FN6SM7.js → chunk-CIUUYB23.js} +5 -5
  73. package/chunks/{chunk-JCJ7LKNA.js → chunk-DIEFUBV6.js} +1644 -1570
  74. package/chunks/{chunk-KWHKHSIS.js → chunk-DTAPI453.js} +8 -8
  75. package/chunks/{chunk-WWCMSZII.js → chunk-DV2GAGR7.js} +71 -7
  76. package/chunks/{chunk-675H6PLP.js → chunk-DYA25Y7U.js} +1 -1
  77. package/chunks/{chunk-3I6DUGQD.js → chunk-E4WHKETF.js} +32 -4
  78. package/chunks/{chunk-QTERJVPZ.js → chunk-EKTCKZV5.js} +1 -0
  79. package/chunks/{chunk-HX6RLYUO.js → chunk-ENOHFX2M.js} +4 -4
  80. package/chunks/{chunk-VMX5RZQH.js → chunk-EZZJHMFK.js} +18 -9
  81. package/chunks/{chunk-KXDONCPU.js → chunk-FBDRTW3I.js} +4 -4
  82. package/chunks/{chunk-FCYYAB4S.js → chunk-FHPKHXHT.js} +3 -3
  83. package/chunks/{chunk-DQ2O5QOJ.js → chunk-FJZS2MXB.js} +1 -1
  84. package/chunks/{chunk-EQNUDTL6.js → chunk-FVLPZSKH.js} +3 -3
  85. package/chunks/{chunk-2LEOZTBT.js → chunk-FYMMZV5F.js} +111 -26
  86. package/chunks/{chunk-UV6KF7T3.js → chunk-GNZ7MX73.js} +3 -3
  87. package/chunks/chunk-GOFAQQZA.js +132 -0
  88. package/chunks/{chunk-YUFIEIWC.js → chunk-GTM6IHBB.js} +2 -2
  89. package/chunks/{chunk-W3UKBMSI.js → chunk-GU5B6IF3.js} +118 -9
  90. package/chunks/{chunk-PCK4B6LR.js → chunk-GU5XWZU2.js} +1 -1
  91. package/chunks/{chunk-DEEABGH2.js → chunk-H2EDCJIM.js} +5954 -1766
  92. package/chunks/{chunk-FJ5WUMUQ.js → chunk-H36SETQS.js} +1 -1
  93. package/chunks/{chunk-6FMJLI5M.js → chunk-HFVW54NL.js} +41 -6
  94. package/chunks/chunk-HGCQ5SE7.js +136 -0
  95. package/chunks/{chunk-OFZKFPYG.js → chunk-HR4CHEQS.js} +3 -3
  96. package/chunks/{chunk-CO2U743O.js → chunk-I3GX6LGS.js} +4 -4
  97. package/chunks/{chunk-5NNHICSA.js → chunk-I7JDGNG6.js} +3 -3
  98. package/chunks/{chunk-RDJOB6S3.js → chunk-IEBT6DXF.js} +1 -1
  99. package/chunks/{chunk-EU5TBFJI.js → chunk-IV7QPMC2.js} +2 -2
  100. package/chunks/{chunk-B4AJNNOL.js → chunk-J26HHFI5.js} +1 -1
  101. package/chunks/{chunk-CNTR4GR6.js → chunk-JELIWHXD.js} +1 -1
  102. package/chunks/{chunk-FNDXMN7I.js → chunk-JQFMW7BD.js} +936 -223
  103. package/chunks/{chunk-IGTHU3T4.js → chunk-KKN2IE7Q.js} +1 -1
  104. package/chunks/{chunk-TZTAV5RH.js → chunk-KVUBYJDM.js} +2 -2
  105. package/chunks/{chunk-DUNI4EBW.js → chunk-KZXSAQYV.js} +86 -52
  106. package/chunks/chunk-LBOVL47Y.js +29 -0
  107. package/chunks/{chunk-FS674JW4.js → chunk-LBWNSLJK.js} +3 -3
  108. package/chunks/{chunk-I5BT7ADR.js → chunk-LEZFUYPM.js} +4 -4
  109. package/chunks/chunk-LLMGP5IC.js +222 -0
  110. package/chunks/{chunk-44F4LBWZ.js → chunk-LVW7BHTW.js} +56 -18
  111. package/chunks/{chunk-YGE3IXJP.js → chunk-M744OFVE.js} +1 -1
  112. package/chunks/chunk-MISUEWB4.js +8749 -0
  113. package/chunks/{chunk-7SJN2IWT.js → chunk-MJ2SKNTY.js} +5 -5
  114. package/chunks/{chunk-JE2CEOBO.js → chunk-MNTXIO6T.js} +4 -4
  115. package/chunks/{chunk-TEGJSJDF.js → chunk-MOHAWIAW.js} +2 -2
  116. package/chunks/{chunk-54YCNSK5.js → chunk-MVMBE5EH.js} +4521 -10220
  117. package/chunks/{chunk-PZEXDV3H.js → chunk-NCY2CDDX.js} +71 -43
  118. package/chunks/{chunk-TTJZAIFP.js → chunk-NJELCSFL.js} +3 -3
  119. package/chunks/{chunk-A7Y5H4TX.js → chunk-NXDJEZCI.js} +7 -7
  120. package/chunks/{chunk-PQNHRFJC.js → chunk-NYX53SV4.js} +0 -2
  121. package/chunks/{chunk-QARJNL2V.js → chunk-O2ZYHG33.js} +5 -4
  122. package/chunks/{chunk-6PQPKTXP.js → chunk-OPMLL3B3.js} +1 -1
  123. package/chunks/{chunk-PVNQRENX.js → chunk-OUQGNGIU.js} +1 -1
  124. package/chunks/{chunk-V7RNNPGC.js → chunk-P5Y23G2L.js} +18 -1
  125. package/chunks/{chunk-GURGAJTX.js → chunk-PDDYAXL2.js} +4 -1
  126. package/chunks/chunk-PWCQRLM5.js +1170 -0
  127. package/chunks/{chunk-GL4JOZXU.js → chunk-Q43MIWDH.js} +4 -4
  128. package/chunks/{chunk-V4XNUDPE.js → chunk-QFIYH5KR.js} +28 -7
  129. package/chunks/chunk-QL4TN4HS.js +59 -0
  130. package/chunks/{chunk-E6CJ2SMM.js → chunk-QMBEW7EW.js} +2 -2
  131. package/chunks/{chunk-U4VTUMMK.js → chunk-QONSRAEV.js} +1 -1
  132. package/chunks/{chunk-LOY7RWQG.js → chunk-QYPT3QUD.js} +3 -3
  133. package/chunks/{chunk-RG64CDT2.js → chunk-RJAVNGJJ.js} +131 -48
  134. package/chunks/{chunk-Z3S5EMOW.js → chunk-RPYYTL7E.js} +60 -12
  135. package/chunks/{chunk-V25K5ID7.js → chunk-SWCEYR6T.js} +1 -1
  136. package/chunks/{chunk-77V3VIKL.js → chunk-SXGNNAMC.js} +9 -9
  137. package/chunks/{chunk-7KAXXBER.js → chunk-TNQ5BEEB.js} +3 -3
  138. package/chunks/{chunk-R2XWGR75.js → chunk-TYERNYH4.js} +2 -2
  139. package/chunks/{chunk-45IOTAUS.js → chunk-U25CMJYY.js} +1 -1
  140. package/chunks/{chunk-7ULQIS27.js → chunk-UEJESCS7.js} +1 -1
  141. package/chunks/{chunk-HC45LTEN.js → chunk-UH7Z7Y45.js} +1 -1
  142. package/chunks/{chunk-P5VC5USY.js → chunk-UIKCPMID.js} +1 -1
  143. package/chunks/{chunk-CPZAH673.js → chunk-UKZEXS6K.js} +2 -0
  144. package/chunks/{chunk-HIVKI6O6.js → chunk-UO7AWUZJ.js} +9 -7
  145. package/chunks/{chunk-XP4L6KS3.js → chunk-URN76TKR.js} +2 -2
  146. package/chunks/{chunk-XUGZYLDH.js → chunk-UWSXFI6E.js} +1 -1
  147. package/chunks/{chunk-AOHY3QJ3.js → chunk-VA5NLVPT.js} +3 -3
  148. package/chunks/{chunk-FANRZEAJ.js → chunk-VWOVHOTT.js} +1 -1
  149. package/chunks/{chunk-NTDH4IML.js → chunk-W2K4OEW4.js} +7 -7
  150. package/chunks/{chunk-L32VV3ZJ.js → chunk-WH7W24EN.js} +12 -12
  151. package/chunks/{chunk-QTHQ6Z5X.js → chunk-WK4TJUEA.js} +1 -1
  152. package/chunks/{chunk-ZPZ7ZJEE.js → chunk-WUC6ZFXO.js} +1 -1
  153. package/chunks/{chunk-TIRRPCFS.js → chunk-X6QNZOTC.js} +1 -1
  154. package/chunks/{chunk-IIUGIWWY.js → chunk-XC2VAFVZ.js} +1 -1
  155. package/chunks/{chunk-D4HTUMGX.js → chunk-XC656O42.js} +3 -3
  156. package/chunks/{chunk-ZMOJ7ISP.js → chunk-XXOVLDZQ.js} +1 -1
  157. package/chunks/{chunk-THVU3RNH.js → chunk-YAVY33G4.js} +1 -1
  158. package/chunks/{chunk-PJOHNFIY.js → chunk-YIAC2OXH.js} +5 -5
  159. package/chunks/{chunk-CU47KXB5.js → chunk-YTSN63PD.js} +6 -6
  160. package/chunks/{chunk-DSFHVTWD.js → chunk-YXEVDA66.js} +2 -2
  161. package/chunks/{chunk-MB32MS4U.js → chunk-ZLJQIGCH.js} +260 -175
  162. package/chunks/{chunk-6TAASXWL.js → chunk-ZX6RZZFB.js} +3 -3
  163. package/chunks/{computer-use-5QWCFNKD.js → computer-use-BG2EHNJE.js} +37 -34
  164. package/chunks/{config-utils-WADIDWGG.js → config-utils-E3ZQTHGM.js} +4 -4
  165. package/chunks/{contextCommand-4Z7T7TUM.js → contextCommand-JAPYNKFC.js} +41 -38
  166. package/chunks/{core-runtime-6N4EFC2Q.js → core-runtime-2VCZN5QF.js} +37 -34
  167. package/chunks/{create-sub-session-AEREHZBQ.js → create-sub-session-52OUA2VU.js} +52 -43
  168. package/chunks/{create-sub-session-62YZWVAY.js → create-sub-session-J32WC64U.js} +2 -2
  169. package/chunks/{cron-create-6E7GIQTG.js → cron-create-HMFSHIAR.js} +4 -4
  170. package/chunks/{cron-delete-JD26XY3W.js → cron-delete-ZJFHOX57.js} +4 -4
  171. package/chunks/{cron-list-CUWFB44G.js → cron-list-ZEX2NBMN.js} +4 -4
  172. package/chunks/{daemon-PCNCI5YU.js → daemon-CCIX4BMG.js} +47 -6
  173. package/chunks/{daemon-status-provider-DDF7C5M7.js → daemon-status-provider-F2YGDKHR.js} +48 -45
  174. package/chunks/{daemon-trust-policy-Q4KSOJTR.js → daemon-trust-policy-XCY7JETR.js} +43 -40
  175. package/chunks/{daemon-trust-policy-monitor-V6FTE3IY.js → daemon-trust-policy-monitor-FBHB5GMQ.js} +43 -40
  176. package/chunks/{de-SY6O76BF.js → de-VL76CPLV.js} +50 -5
  177. package/chunks/{deferred-core-runtime-GHJ5GQHK.js → deferred-core-runtime-BBLJ4JUM.js} +37 -34
  178. package/chunks/display-image-2WOG27DY.js +184 -0
  179. package/chunks/{dist-ZHXOBXDN.js → dist-3BMEUGTG.js} +1 -1
  180. package/chunks/{dist-OAOC5OV4.js → dist-7JGAAQSC.js} +2 -2
  181. package/chunks/{dist-SL2LUMML.js → dist-A7H2KKUC.js} +1 -1
  182. package/chunks/{dist-ULIG4M5H.js → dist-C5U4LJKW.js} +162 -15
  183. package/chunks/{dist-2PERFI23.js → dist-DPPHJAVL.js} +3 -3
  184. package/chunks/{dist-PVEAFLHY.js → dist-HVBMEYWK.js} +1 -1
  185. package/chunks/{dist-26BIMQT6.js → dist-IKGVITP3.js} +676 -37
  186. package/chunks/{dist-WGMNT3JQ.js → dist-SFQ34F4M.js} +24 -6
  187. package/chunks/{earlyInputCapture-TQXLLISD.js → earlyInputCapture-ALVSMKP7.js} +38 -35
  188. package/chunks/{edit-FJEUV6KZ.js → edit-POB3CIHQ.js} +37 -34
  189. package/chunks/{en-NBK3JKCE.js → en-IRJ2WN5T.js} +61 -6
  190. package/chunks/{enter-worktree-7GE3TZ4P.js → enter-worktree-Q2N7UZWC.js} +37 -34
  191. package/chunks/{enterPlanMode-KR5TMPCJ.js → enterPlanMode-QGAH2GMP.js} +37 -34
  192. package/chunks/{environment-SWL3CMB3.js → environment-FE5AX2U5.js} +40 -37
  193. package/chunks/{errors-XQE7BGZV.js → errors-SLMCFLSC.js} +40 -37
  194. package/chunks/{exit-worktree-CHKTYNCV.js → exit-worktree-7WO2LPLF.js} +37 -34
  195. package/chunks/{exitPlanMode-QYAG6LWX.js → exitPlanMode-3PINWUTY.js} +37 -34
  196. package/chunks/{fast-path-Z2K7W6DC.js → fast-path-SVFFEL6U.js} +17 -4
  197. package/chunks/{fr-4KG5V7UT.js → fr-GK3HQYVB.js} +50 -5
  198. package/chunks/{gemini-YU4XKEDG.js → gemini-QECJNKNJ.js} +151 -90
  199. package/chunks/{geminiContentGenerator-ZZX6NBO4.js → geminiContentGenerator-ZFAZKT6R.js} +6 -6
  200. package/chunks/{glob-VYF5CXSX.js → glob-GXUNQYNR.js} +37 -34
  201. package/chunks/{goal-tools-HGO6VXPB.js → goal-tools-SPQ5M6D3.js} +3 -3
  202. package/chunks/{grep-5DP5ZUTW.js → grep-LVU5KBCV.js} +37 -34
  203. package/chunks/{handleAutoUpdate-UVBJ56DL.js → handleAutoUpdate-MPDHQY72.js} +43 -40
  204. package/chunks/{i18n-UQNYHEUD.js → i18n-LEYRN6RV.js} +39 -36
  205. package/chunks/{image-gen-WHPIZNWF.js → image-gen-4T2D4PTP.js} +9 -9
  206. package/chunks/{initializer-GMIICF4W.js → initializer-PQV2THBS.js} +44 -41
  207. package/chunks/{installationInfo-4MGG4WVI.js → installationInfo-XC3HXLTX.js} +38 -35
  208. package/chunks/{ja-F3GSQXMF.js → ja-3FA46IKD.js} +50 -5
  209. package/chunks/{keychain-token-storage-IKO4G53C.js → keychain-token-storage-7BT4TQ2A.js} +2 -2
  210. package/chunks/{kittyProtocolDetector-MBRTDRBK.js → kittyProtocolDetector-BFLAKYXH.js} +1 -1
  211. package/chunks/{list-CPN2W334.js → list-AQBTPZRG.js} +46 -43
  212. package/chunks/{list-agents-PXJ7ZUFB.js → list-agents-TWEGRJWO.js} +2 -2
  213. package/chunks/{loadedSettingsAdapter-ZQAV5NDO.js → loadedSettingsAdapter-EZYPDX2J.js} +43 -40
  214. package/chunks/{loggingContentGenerator-5E7W4IEP.js → loggingContentGenerator-WD6YWU7Q.js} +52 -37
  215. package/chunks/{loop-wakeup-Y26NQZ5I.js → loop-wakeup-6LLEOQYV.js} +5 -5
  216. package/chunks/{ls-ONQTTMQR.js → ls-H5WX22VK.js} +4 -4
  217. package/chunks/{lsp-YGHHRGMD.js → lsp-G6FXYEIS.js} +2 -2
  218. package/chunks/{managed-npm-update-MKKAXYXA.js → managed-npm-update-BMM57CHD.js} +40 -37
  219. package/chunks/{mcp-V5NTJF6X.js → mcp-FMI6NCWA.js} +43 -40
  220. package/chunks/{monitor-CO3ASY6E.js → monitor-PZJOYAA2.js} +37 -34
  221. package/chunks/nonInteractiveCli-QPX6R4LL.js +152 -0
  222. package/chunks/{notebook-edit-Z7AEE6FM.js → notebook-edit-GZF2SKTQ.js} +37 -34
  223. package/chunks/{openaiContentGenerator-CA3MQOP3.js → openaiContentGenerator-ZPLCFAAG.js} +18 -18
  224. package/chunks/{pidfile-NL7MKDRU.js → pidfile-PCADVJQW.js} +38 -35
  225. package/chunks/{processUtils-PB5K2DYH.js → processUtils-FKUVQVEU.js} +2 -2
  226. package/chunks/{pt-EQ33KWEN.js → pt-3AOBKPIJ.js} +50 -5
  227. package/chunks/{qwenContentGenerator-JYFNDCOS.js → qwenContentGenerator-2Y4R6YLB.js} +42 -40
  228. package/chunks/{qwenOAuth2-W2ALKQDD.js → qwenOAuth2-UCEHY3I2.js} +8 -8
  229. package/chunks/{read-file-IEOR5H5W.js → read-file-DYHDFVAS.js} +10 -10
  230. package/chunks/{read-mcp-resource-U2IJN5HM.js → read-mcp-resource-DRZSI3HE.js} +2 -2
  231. package/chunks/{record-artifact-MGPJ7EG4.js → record-artifact-IPIRWMFY.js} +3 -3
  232. package/chunks/{resumeHistoryUtils-BNBTP5AS.js → resumeHistoryUtils-5JS6R3AJ.js} +45 -42
  233. package/chunks/{ripGrep-UMQODKEB.js → ripGrep-2QOR3ZQT.js} +37 -34
  234. package/chunks/{ru-7OZAUKGG.js → ru-VJRAMC4W.js} +50 -5
  235. package/chunks/{run-qwen-serve-RN2R6QQW.js → run-qwen-serve-MTVRAUBR.js} +271 -60
  236. package/chunks/{runtime-QFJ2MJF5.js → runtime-DQGE2DYR.js} +50 -45
  237. package/chunks/{scheduler-AMYUFOZI.js → scheduler-BXIZQ4OO.js} +37 -34
  238. package/chunks/{sdk-exporters-http-4BQANSJ6.js → sdk-exporters-http-IKNWQJNV.js} +2 -2
  239. package/chunks/{sdk-impl-Z4VXWVFX.js → sdk-impl-7CUUMQDB.js} +2 -2
  240. package/chunks/{send-message-PMP4ATVM.js → send-message-LHJLVWFD.js} +5 -4
  241. package/chunks/{serve-EIIYJVX3.js → serve-D4GYHFLL.js} +44 -40
  242. package/chunks/{server-EP2LLAPD.js → server-S22MLWTJ.js} +618 -387
  243. package/chunks/{session-GMPZBLOW.js → session-AHNM6YXP.js} +151 -87
  244. package/chunks/{settings-LN7RNKBS.js → settings-77MDDUSX.js} +42 -39
  245. package/chunks/{shell-EM2OWU5X.js → shell-DLD2VQTQ.js} +37 -34
  246. package/chunks/{skill-T5VPSK73.js → skill-3B2PAQE4.js} +27 -14
  247. package/chunks/{skill-settings-GMR3GMNY.js → skill-settings-STR5NF4B.js} +42 -39
  248. package/chunks/{spawnChannel-YPCU62DI.js → spawnChannel-TOWYOVDZ.js} +39 -36
  249. package/chunks/{standalone-update-JAKIM2NF.js → standalone-update-F6ZWABD7.js} +40 -37
  250. package/chunks/{startInteractiveUI-YETQWGYF.js → startInteractiveUI-QR7MHDX7.js} +1829 -1036
  251. package/chunks/{syntheticOutput-D7E6ZFKR.js → syntheticOutput-EYL7DWO7.js} +3 -3
  252. package/chunks/{task-create-NFCYD43L.js → task-create-4ALZMGP5.js} +10 -9
  253. package/chunks/{task-list-TAVMGEVD.js → task-list-6CVQPORX.js} +6 -6
  254. package/chunks/{task-stop-ZDF2UJRM.js → task-stop-Y5ZTFVRX.js} +2 -2
  255. package/chunks/{task-update-6L7LHGEX.js → task-update-IKCEEWJ6.js} +10 -9
  256. package/chunks/{team-create-LFJ6HGNY.js → team-create-LQVIAVRF.js} +37 -34
  257. package/chunks/{team-delete-DPGGRQBU.js → team-delete-CE7DC53R.js} +6 -6
  258. package/chunks/{team-plan-approval-C2KLSDGO.js → team-plan-approval-G5PUNI52.js} +37 -34
  259. package/chunks/terminal-image-renderer-33GSM55L.js +90 -0
  260. package/chunks/{theme-manager-JQ3ZGBP5.js → theme-manager-DBFM5QOG.js} +38 -35
  261. package/chunks/{todoWrite-2UO5G6G6.js → todoWrite-FTZWQRZH.js} +108 -36
  262. package/chunks/{tool-search-FAZBFRJZ.js → tool-search-RGG7BBC4.js} +13 -12
  263. package/chunks/{total-session-admission-5CALIVYO.js → total-session-admission-LY3RMXXI.js} +44 -41
  264. package/chunks/{trustedFolders-KOPSP44S.js → trustedFolders-3M4IQ6K6.js} +39 -36
  265. package/chunks/{update-relaunch-6QL2TICS.js → update-relaunch-G3CMSX3M.js} +5 -5
  266. package/chunks/{updateCheck-4Q6RIKFW.js → updateCheck-ZKFGBDTG.js} +42 -39
  267. package/chunks/{useAutoAcceptIndicator-Q5BMKZV3.js → useAutoAcceptIndicator-7SAFZK5H.js} +47 -44
  268. package/chunks/{validateNonInterActiveAuth-GKOC5F6Z.js → validateNonInterActiveAuth-F2K6UGE4.js} +88 -84
  269. package/chunks/{version-VSATY2LD.js → version-KM6MYV2D.js} +1 -1
  270. package/chunks/{web-fetch-JCLKL6NL.js → web-fetch-SM4LZHRS.js} +13 -13
  271. package/chunks/{web-search-DSQTFUBX.js → web-search-OGKZNDAC.js} +8 -8
  272. package/chunks/{workflow-M6QAVKVJ.js → workflow-HU365RFR.js} +419 -304
  273. package/chunks/{workspace-providers-status-YKLTLMJA.js → workspace-providers-status-QKKXQHL7.js} +46 -43
  274. package/chunks/{workspace-registration-store-5DMGKU64.js → workspace-registration-store-7J7KOXFD.js} +2 -2
  275. package/chunks/{workspace-registry-PTYUSYXX.js → workspace-registry-KC66O6DH.js} +44 -41
  276. package/chunks/{workspace-service-25ML3T47.js → workspace-service-TEV5Q2AA.js} +51 -48
  277. package/chunks/{workspace-skills-status-YKZWOYRX.js → workspace-skills-status-VRL55OMU.js} +45 -42
  278. package/chunks/{workspace-trust-reconciler-5FI2S3OS.js → workspace-trust-reconciler-COC5M7CY.js} +50 -47
  279. package/chunks/{write-file-AY7VLP5X.js → write-file-3SO2BMNO.js} +37 -34
  280. package/chunks/{zh-H3YDK5QW.js → zh-ARWDLDLR.js} +58 -6
  281. package/chunks/{zh-TW-JYCP7XUA.js → zh-TW-Y4RGI3P7.js} +58 -6
  282. package/chunks/{zoom-image-S4LJZMD5.js → zoom-image-O3RWTVKV.js} +10 -10
  283. package/cli.js +32 -48
  284. package/locales/ca.js +68 -4
  285. package/locales/de.js +70 -4
  286. package/locales/en.js +84 -6
  287. package/locales/fr.js +72 -4
  288. package/locales/ja.js +69 -4
  289. package/locales/pt.js +68 -4
  290. package/locales/ru.js +71 -4
  291. package/locales/zh-TW.js +74 -6
  292. package/locales/zh.js +74 -6
  293. package/package.json +3 -3
  294. package/web-shell/assets/{arc-xjArKHU2.js → arc-KuwiZ67W.js} +1 -1
  295. package/web-shell/assets/{architectureDiagram-3BPJPVTR-BgrQDNYG.js → architectureDiagram-3BPJPVTR-Cppop6HB.js} +1 -1
  296. package/web-shell/assets/{blockDiagram-GPEHLZMM-CXxWC-1_.js → blockDiagram-GPEHLZMM-DQ6HT72s.js} +1 -1
  297. package/web-shell/assets/{c4Diagram-AAUBKEIU-Dx1tLm3v.js → c4Diagram-AAUBKEIU-B8TX_Mh-.js} +1 -1
  298. package/web-shell/assets/channel-BZd6Ile0.js +1 -0
  299. package/web-shell/assets/{chunk-2J33WTMH-CBxjogVR.js → chunk-2J33WTMH-B2WBUTp8.js} +1 -1
  300. package/web-shell/assets/{chunk-4BX2VUAB-izb-rY5W.js → chunk-4BX2VUAB-DXUsWC6O.js} +1 -1
  301. package/web-shell/assets/{chunk-55IACEB6-NzLyvSb8.js → chunk-55IACEB6-DtB-6cyk.js} +1 -1
  302. package/web-shell/assets/{chunk-727SXJPM-DVyKb0dl.js → chunk-727SXJPM-B-PhPyC3.js} +1 -1
  303. package/web-shell/assets/{chunk-AQP2D5EJ-BtqL7NzB.js → chunk-AQP2D5EJ-DNaTFMSC.js} +1 -1
  304. package/web-shell/assets/{chunk-FMBD7UC4-iNpdKIO0.js → chunk-FMBD7UC4-DZ345tFf.js} +1 -1
  305. package/web-shell/assets/{chunk-ND2GUHAM-DI7cbFmX.js → chunk-ND2GUHAM-BR_EpgzJ.js} +1 -1
  306. package/web-shell/assets/{chunk-QZHKN3VN-3Y_-xFYs.js → chunk-QZHKN3VN-BTU-0gDT.js} +1 -1
  307. package/web-shell/assets/classDiagram-4FO5ZUOK-B7c-TqtK.js +1 -0
  308. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B7c-TqtK.js +1 -0
  309. package/web-shell/assets/{cose-bilkent-S5V4N54A--raRatZn.js → cose-bilkent-S5V4N54A-BmsKBCNt.js} +1 -1
  310. package/web-shell/assets/{dagre-BM42HDAG-B8SU70mO.js → dagre-BM42HDAG-Ba8YWdDC.js} +1 -1
  311. package/web-shell/assets/{diagram-2AECGRRQ-ewgoakXo.js → diagram-2AECGRRQ-CCt2SnI6.js} +1 -1
  312. package/web-shell/assets/{diagram-5GNKFQAL-Bm_JLTnS.js → diagram-5GNKFQAL-BVTLXEU7.js} +1 -1
  313. package/web-shell/assets/{diagram-KO2AKTUF-DEIUjUy9.js → diagram-KO2AKTUF-DpDAVCYV.js} +1 -1
  314. package/web-shell/assets/{diagram-LMA3HP47-C2Wmrpj-.js → diagram-LMA3HP47-8ljXg-TB.js} +1 -1
  315. package/web-shell/assets/{diagram-OG6HWLK6-CtYe6bFx.js → diagram-OG6HWLK6-JAXfQSmd.js} +1 -1
  316. package/web-shell/assets/{erDiagram-TEJ5UH35-DfpHcWLV.js → erDiagram-TEJ5UH35-CUeakG6k.js} +1 -1
  317. package/web-shell/assets/{flowDiagram-I6XJVG4X-CAiUhio5.js → flowDiagram-I6XJVG4X-Cd4Wrdwv.js} +1 -1
  318. package/web-shell/assets/{ganttDiagram-6RSMTGT7-CYc7ezr4.js → ganttDiagram-6RSMTGT7-C0nMFvH_.js} +1 -1
  319. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-70lbkcRS.js → gitGraphDiagram-PVQCEYII-uKk88n_L.js} +1 -1
  320. package/web-shell/assets/index-BXGtjHs5.js +1756 -0
  321. package/web-shell/assets/{index-Ww86nT9f.js → index-C1UWBJSX.js} +1 -1
  322. package/web-shell/assets/index-WxSvj2tI.css +5 -0
  323. package/web-shell/assets/{infoDiagram-5YYISTIA-C7e8DWPa.js → infoDiagram-5YYISTIA-GfPN17hR.js} +1 -1
  324. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BYLr2op-.js → ishikawaDiagram-YF4QCWOH-CDbWTcGa.js} +1 -1
  325. package/web-shell/assets/{journeyDiagram-JHISSGLW-hEibAHKX.js → journeyDiagram-JHISSGLW-J8TvvVFz.js} +1 -1
  326. package/web-shell/assets/{kanban-definition-UN3LZRKU-F2rt8Uhp.js → kanban-definition-UN3LZRKU-Ds_ndLrs.js} +1 -1
  327. package/web-shell/assets/{linear-DPhyNezd.js → linear-DuL9O4i3.js} +1 -1
  328. package/web-shell/assets/{mermaid.core-ZUtnug0n.js → mermaid.core-DtK4PsQB.js} +5 -5
  329. package/web-shell/assets/{mindmap-definition-RKZ34NQL-DDWcMoi8.js → mindmap-definition-RKZ34NQL-DD0r7MS3.js} +1 -1
  330. package/web-shell/assets/{pieDiagram-4H26LBE5-CTRKzw1_.js → pieDiagram-4H26LBE5-CxGPZ8Q-.js} +1 -1
  331. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-CAEwYG_z.js → quadrantDiagram-W4KKPZXB-Ds7Buxed.js} +1 -1
  332. package/web-shell/assets/{requirementDiagram-4Y6WPE33-CRjnH9D9.js → requirementDiagram-4Y6WPE33-DIx28ph5.js} +1 -1
  333. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-LPQT_sOd.js → sankeyDiagram-5OEKKPKP-AxdAYcVX.js} +1 -1
  334. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Ht2Iqvb0.js → sequenceDiagram-3UESZ5HK-CojoXAZ5.js} +1 -1
  335. package/web-shell/assets/{stateDiagram-AJRCARHV-CIUdqOXu.js → stateDiagram-AJRCARHV-Dy1WN3mi.js} +1 -1
  336. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BaTNMz02.js +1 -0
  337. package/web-shell/assets/{timeline-definition-PNZ67QCA-BcilneZF.js → timeline-definition-PNZ67QCA-DeWe_43M.js} +1 -1
  338. package/web-shell/assets/{vennDiagram-CIIHVFJN-BljwiiH1.js → vennDiagram-CIIHVFJN-BN8PVlFq.js} +1 -1
  339. package/web-shell/assets/{wardley-L42UT6IY-DeKjkYjq.js → wardley-L42UT6IY-CbjV_1hf.js} +1 -1
  340. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-L3U1H7KG.js → wardleyDiagram-YWT4CUSO-D2mw84_p.js} +1 -1
  341. package/web-shell/assets/{xychartDiagram-2RQKCTM6-IJnWsgMT.js → xychartDiagram-2RQKCTM6-er9CTQXi.js} +1 -1
  342. package/web-shell/index.html +2 -2
  343. package/chunks/chunk-4KP2F2TE.js +0 -82
  344. package/chunks/chunk-HGT6JR3U.js +0 -177
  345. package/chunks/nonInteractiveCli-ZJYEOO7U.js +0 -148
  346. package/web-shell/assets/channel-B1cyk94G.js +0 -1
  347. package/web-shell/assets/classDiagram-4FO5ZUOK-BpBYZoRE.js +0 -1
  348. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BpBYZoRE.js +0 -1
  349. package/web-shell/assets/index-C1k3PDuU.css +0 -5
  350. package/web-shell/assets/index-DZLIXILO.js +0 -1750
  351. package/web-shell/assets/insert-DmRbh9Rv.svg +0 -1
  352. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BmqqQZ6w.js +0 -1
@@ -3,8 +3,9 @@
3
3
  import {
4
4
  PollingChannelBase,
5
5
  getGlobalQwenDir,
6
+ getWorkspaceScopeDirName,
6
7
  sanitizeLogText
7
- } from "./chunk-JCJ7LKNA.js";
8
+ } from "./chunk-DIEFUBV6.js";
8
9
  import "./chunk-UDXSXLER.js";
9
10
  import "./chunk-H6XPXXMH.js";
10
11
  import {
@@ -125,9 +126,9 @@ init_esbuild_shims();
125
126
 
126
127
  // packages/channels/github/dist/GithubAdapter.js
127
128
  init_esbuild_shims();
128
- import { createHash } from "node:crypto";
129
- import { appendFileSync, chmodSync, mkdirSync } from "node:fs";
130
- import { join } from "node:path";
129
+ import { createHash, randomUUID } from "node:crypto";
130
+ import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
131
+ import { dirname, join } from "node:path";
131
132
  import process2 from "node:process";
132
133
 
133
134
  // node_modules/@octokit/rest/dist-src/index.js
@@ -3840,6 +3841,27 @@ function normalizeReasonFilter(config, channelName) {
3840
3841
  return reasons.length > 0 ? new Set(reasons) : null;
3841
3842
  }
3842
3843
  __name(normalizeReasonFilter, "normalizeReasonFilter");
3844
+ var MAX_INBOUND_TASK_ATTEMPTS = 3;
3845
+ function isOptionalStringArray(value) {
3846
+ return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
3847
+ }
3848
+ __name(isOptionalStringArray, "isOptionalStringArray");
3849
+ function isInboundEnvelope(value) {
3850
+ if (value === void 0)
3851
+ return true;
3852
+ if (value === null || typeof value !== "object")
3853
+ return false;
3854
+ const envelope = value;
3855
+ return typeof envelope.channelName === "string" && typeof envelope.senderId === "string" && typeof envelope.senderName === "string" && typeof envelope.chatId === "string" && typeof envelope.text === "string" && typeof envelope.isGroup === "boolean" && typeof envelope.isMentioned === "boolean" && typeof envelope.isReplyToBot === "boolean" && (envelope.chatName === void 0 || typeof envelope.chatName === "string") && (envelope.threadId === void 0 || typeof envelope.threadId === "string") && (envelope.messageId === void 0 || typeof envelope.messageId === "string") && (envelope.referencedText === void 0 || typeof envelope.referencedText === "string") && (envelope.imageBase64 === void 0 || typeof envelope.imageBase64 === "string") && (envelope.imageMimeType === void 0 || typeof envelope.imageMimeType === "string") && (envelope.attachments === void 0 || Array.isArray(envelope.attachments)) && (envelope.metadata === void 0 || typeof envelope.metadata === "string") && (envelope.alreadyPrefixed === void 0 || envelope.alreadyPrefixed === true);
3856
+ }
3857
+ __name(isInboundEnvelope, "isInboundEnvelope");
3858
+ function isInboundTaskRecord(value) {
3859
+ if (value === null || typeof value !== "object")
3860
+ return false;
3861
+ const record = value;
3862
+ return record.version === 1 && typeof record.id === "string" && typeof record.createdAt === "string" && typeof record.updatedAt === "string" && ["accepted", "running", "reply_pending", "failed", "cancelled"].includes(record.state) && Number.isSafeInteger(record.issueNumber) && record.source !== null && typeof record.source === "object" && typeof record.source.chatId === "string" && (record.source.threadId === void 0 || typeof record.source.threadId === "string") && (record.source.messageId === void 0 || typeof record.source.messageId === "string") && isInboundEnvelope(record.envelope) && record.dedupe !== null && typeof record.dedupe === "object" && isOptionalStringArray(record.dedupe.dispatchedBodies) && isOptionalStringArray(record.dedupe.dispatchedComments) && isOptionalStringArray(record.dedupe.dispatchedEvents) && (record.attempts === void 0 || Number.isSafeInteger(record.attempts) && record.attempts >= 0) && (record.errorCommentPosted === void 0 || typeof record.errorCommentPosted === "boolean") && (record.error === void 0 || typeof record.error === "string");
3863
+ }
3864
+ __name(isInboundTaskRecord, "isInboundTaskRecord");
3843
3865
  var FinalPublicationError = class extends Error {
3844
3866
  static {
3845
3867
  __name(this, "FinalPublicationError");
@@ -3863,8 +3885,7 @@ function isNoReplySentinel(text) {
3863
3885
  __name(isNoReplySentinel, "isNoReplySentinel");
3864
3886
  function isDefiniteNoWriteGithubError(err) {
3865
3887
  const e = err;
3866
- const remaining = Number(e.response?.headers?.["x-ratelimit-remaining"]);
3867
- return e.status === 429 || e.status === 403 && remaining === 0;
3888
+ return (e.status === 403 || e.status === 429) && Number(e.response?.headers?.["x-ratelimit-remaining"]) === 0;
3868
3889
  }
3869
3890
  __name(isDefiniteNoWriteGithubError, "isDefiniteNoWriteGithubError");
3870
3891
  function parseTriggerKind(metadata) {
@@ -3881,6 +3902,11 @@ function buildTriggerGuidance(reason) {
3881
3902
  return `For ${reason}, output exactly ${NO_REPLY_SENTINEL} when a public reply is unnecessary.`;
3882
3903
  }
3883
3904
  __name(buildTriggerGuidance, "buildTriggerGuidance");
3905
+ function isPendingFinalDelivery(value) {
3906
+ const item = value;
3907
+ return item !== null && typeof item === "object" && typeof item.id === "string" && typeof item.createdAt === "string" && typeof item.chatId === "string" && typeof item.threadId === "string" && typeof item.fullText === "string" && typeof item.sessionId === "string";
3908
+ }
3909
+ __name(isPendingFinalDelivery, "isPendingFinalDelivery");
3884
3910
  var GithubChannel = class extends PollingChannelBase {
3885
3911
  static {
3886
3912
  __name(this, "GithubChannel");
@@ -3890,7 +3916,17 @@ var GithubChannel = class extends PollingChannelBase {
3890
3916
  webOrigin = "https://github.com";
3891
3917
  activeReactions = /* @__PURE__ */ new Map();
3892
3918
  reactionsPendingRemoval = /* @__PURE__ */ new Set();
3919
+ pendingFinalDeliveryRetry;
3920
+ pendingFinalDeliveryRetryAbort;
3921
+ pendingFinalDeliveryRequestsActive = 0;
3922
+ pendingFinalDeliveryRetryStopRequested = false;
3893
3923
  reasonFilter = null;
3924
+ inboundRecoveryPending = true;
3925
+ recoverableInboundTasks = 0;
3926
+ activeInboundTaskIdsByMessage = /* @__PURE__ */ new Map();
3927
+ cancelledInboundTaskIds = /* @__PURE__ */ new Set();
3928
+ pendingCursorUpdatedAt;
3929
+ inboundPersistenceBlocked = false;
3894
3930
  constructor(name, config, bridge, options) {
3895
3931
  config.blockStreaming = "off";
3896
3932
  config.instructions = [
@@ -3949,10 +3985,40 @@ var GithubChannel = class extends PollingChannelBase {
3949
3985
  `);
3950
3986
  }
3951
3987
  this.gate.replaceAllowedUsers(allowed);
3988
+ this.migrateLegacyPublicationState();
3989
+ this.inboundPersistenceBlocked = false;
3990
+ this.inboundRecoveryPending = true;
3991
+ try {
3992
+ this.recoverableInboundTasks = this.readInboundTasks().filter((record) => this.isRecoverableInboundTask(record)).length;
3993
+ } catch {
3994
+ this.recoverableInboundTasks = 0;
3995
+ }
3996
+ this.pendingFinalDeliveryRetryStopRequested = false;
3952
3997
  this.startPollLoop();
3998
+ if (this.pendingFinalDeliveryRetry) {
3999
+ return;
4000
+ }
4001
+ const retryAbort = new AbortController();
4002
+ const retry = this.retryPendingFinalDeliveries(retryAbort.signal).catch((err) => {
4003
+ if (!retryAbort.signal.aborted) {
4004
+ process2.stderr.write(`[Channel:${this.name}] pending GitHub delivery retry failed: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4005
+ `);
4006
+ }
4007
+ }).finally(() => {
4008
+ if (this.pendingFinalDeliveryRetry === retry) {
4009
+ this.pendingFinalDeliveryRetry = void 0;
4010
+ this.pendingFinalDeliveryRetryAbort = void 0;
4011
+ }
4012
+ });
4013
+ this.pendingFinalDeliveryRetryAbort = retryAbort;
4014
+ this.pendingFinalDeliveryRetry = retry;
3953
4015
  }
3954
4016
  disconnect() {
3955
4017
  this.stopPollLoop();
4018
+ this.pendingFinalDeliveryRetryStopRequested = true;
4019
+ if (this.pendingFinalDeliveryRequestsActive === 0) {
4020
+ this.pendingFinalDeliveryRetryAbort?.abort();
4021
+ }
3956
4022
  }
3957
4023
  async sendMessage(_chatId, _text) {
3958
4024
  throw new Error(`[Channel:${this.name}] sendMessage requires a threadId; use sendThreadMessage`);
@@ -3966,18 +4032,15 @@ var GithubChannel = class extends PollingChannelBase {
3966
4032
  async publishFinalResponse(chatId, threadId, fullText, sessionId) {
3967
4033
  const threadMatch = threadId?.match(/^(issue|pr):(\d+)$/);
3968
4034
  const metadata = this.getResponseMetadata(sessionId);
3969
- const auditBase = {
3970
- channel: this.name,
3971
- triggerKind: parseTriggerKind(metadata),
3972
- repository: chatId,
3973
- number: threadMatch ? Number(threadMatch[2]) : void 0,
4035
+ const auditBase = this.buildPublicationAuditBase({
4036
+ chatId,
4037
+ threadId,
4038
+ fullText,
3974
4039
  sessionId,
3975
4040
  sourceMessageId: this.getResponseMessageId(sessionId),
3976
4041
  actor: this.getResponseSenderId(sessionId),
3977
- threadId,
3978
- bodySha256: createHash("sha256").update(fullText).digest("hex"),
3979
- bodyChars: Array.from(fullText).length
3980
- };
4042
+ metadata
4043
+ });
3981
4044
  if (isNoReplySentinel(fullText)) {
3982
4045
  this.recordPublicationAudit({
3983
4046
  ...auditBase,
@@ -3993,6 +4056,12 @@ var GithubChannel = class extends PollingChannelBase {
3993
4056
  if (!threadMatch) {
3994
4057
  throw new Error(`[Channel:${this.name}] invalid threadId format: ${threadId}`);
3995
4058
  }
4059
+ this.recordPublicationAudit({
4060
+ ...auditBase,
4061
+ at: (/* @__PURE__ */ new Date()).toISOString(),
4062
+ type: "github_publication",
4063
+ outcome: "posting"
4064
+ });
3996
4065
  try {
3997
4066
  const comment = await this.createIssueComment(chatId, threadId, fullText, 3, isDefiniteNoWriteGithubError);
3998
4067
  this.recordPublicationAudit({
@@ -4012,10 +4081,196 @@ var GithubChannel = class extends PollingChannelBase {
4012
4081
  failurePhase: "delivery",
4013
4082
  failureError: sanitizeLogText(err instanceof Error ? err.message : String(err), 200)
4014
4083
  });
4084
+ if (threadId && isDefiniteNoWriteGithubError(err)) {
4085
+ try {
4086
+ this.enqueuePendingFinalDelivery({
4087
+ ...auditBase,
4088
+ chatId,
4089
+ threadId,
4090
+ fullText
4091
+ });
4092
+ } catch (persistError) {
4093
+ throw new Error(`[Channel:${this.name}] failed to persist pending GitHub delivery: ${sanitizeLogText(persistError instanceof Error ? persistError.message : String(persistError), 200)}`, { cause: persistError });
4094
+ }
4095
+ }
4015
4096
  throw new FinalPublicationError(err instanceof Error ? err.message : String(err), { cause: err });
4016
4097
  }
4017
4098
  }
4018
- async createIssueComment(chatId, threadId, text, retries = 3, shouldRetry = () => true) {
4099
+ buildPublicationAuditBase(input) {
4100
+ const threadMatch = input.threadId?.match(/^(issue|pr):(\d+)$/);
4101
+ return {
4102
+ channel: this.name,
4103
+ triggerKind: parseTriggerKind(input.metadata),
4104
+ repository: input.chatId,
4105
+ number: threadMatch ? Number(threadMatch[2]) : void 0,
4106
+ sessionId: input.sessionId,
4107
+ sourceMessageId: input.sourceMessageId,
4108
+ actor: input.actor,
4109
+ threadId: input.threadId,
4110
+ bodySha256: createHash("sha256").update(input.fullText).digest("hex"),
4111
+ bodyChars: Array.from(input.fullText).length
4112
+ };
4113
+ }
4114
+ enqueuePendingFinalDelivery(input) {
4115
+ const record = {
4116
+ id: createHash("sha256").update(JSON.stringify([
4117
+ input.chatId,
4118
+ input.threadId,
4119
+ input.sessionId,
4120
+ input.sourceMessageId ?? randomUUID(),
4121
+ input.bodySha256
4122
+ ])).digest("hex"),
4123
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
4124
+ chatId: input.chatId,
4125
+ threadId: input.threadId,
4126
+ fullText: input.fullText,
4127
+ sessionId: input.sessionId,
4128
+ sourceMessageId: input.sourceMessageId,
4129
+ actor: input.actor,
4130
+ triggerKind: input.triggerKind
4131
+ };
4132
+ const pending = this.readPendingFinalDeliveries(true).filter((item) => item.id !== record.id);
4133
+ this.writePendingFinalDeliveries([...pending, record]);
4134
+ }
4135
+ async retryPendingFinalDeliveries(signal) {
4136
+ const pending = this.readPendingFinalDeliveries();
4137
+ for (const record of pending) {
4138
+ if (signal?.aborted)
4139
+ return;
4140
+ const auditBase = this.buildPublicationAuditBase({
4141
+ chatId: record.chatId,
4142
+ threadId: record.threadId,
4143
+ fullText: record.fullText,
4144
+ sessionId: record.sessionId,
4145
+ sourceMessageId: record.sourceMessageId,
4146
+ actor: record.actor,
4147
+ metadata: record.triggerKind ? `Trigger: ${record.triggerKind}.` : void 0
4148
+ });
4149
+ if (this.hasPostedPublicationAudit(record.id)) {
4150
+ process2.stderr.write(`[Channel:${this.name}] dropping pending GitHub delivery already recorded as posted: ${sanitizeLogText(record.id, 80)}
4151
+ `);
4152
+ if (this.updatePendingFinalDeliveries((current) => current.filter((item) => item.id !== record.id))) {
4153
+ this.removeReplyPendingInboundTask(record);
4154
+ }
4155
+ continue;
4156
+ }
4157
+ try {
4158
+ const comment = await this.createIssueComment(record.chatId, record.threadId, record.fullText, 3, isDefiniteNoWriteGithubError, signal);
4159
+ this.recordPublicationAudit({
4160
+ ...auditBase,
4161
+ at: (/* @__PURE__ */ new Date()).toISOString(),
4162
+ type: "github_publication",
4163
+ outcome: "posted",
4164
+ pendingId: record.id,
4165
+ commentId: comment.id,
4166
+ commentUrl: comment.html_url
4167
+ });
4168
+ if (!this.updatePendingFinalDeliveries((current) => current.filter((item) => item.id !== record.id))) {
4169
+ continue;
4170
+ }
4171
+ this.removeReplyPendingInboundTask(record);
4172
+ } catch (err) {
4173
+ if (signal?.aborted)
4174
+ return;
4175
+ if (isDefiniteNoWriteGithubError(err)) {
4176
+ continue;
4177
+ }
4178
+ this.recordPublicationAudit({
4179
+ ...auditBase,
4180
+ at: (/* @__PURE__ */ new Date()).toISOString(),
4181
+ type: "github_publication",
4182
+ outcome: "failed",
4183
+ failurePhase: "delivery",
4184
+ failureError: sanitizeLogText(err instanceof Error ? err.message : String(err), 200)
4185
+ });
4186
+ if (!this.updatePendingFinalDeliveries((current) => current.filter((item) => item.id !== record.id))) {
4187
+ continue;
4188
+ }
4189
+ this.removeReplyPendingInboundTask(record);
4190
+ }
4191
+ }
4192
+ }
4193
+ hasPostedPublicationAudit(pendingId) {
4194
+ try {
4195
+ return readFileSync(this.channelFilePath("github-audit.jsonl"), "utf-8").split("\n").some((line) => {
4196
+ if (!line)
4197
+ return false;
4198
+ let record;
4199
+ try {
4200
+ record = JSON.parse(line);
4201
+ } catch {
4202
+ return false;
4203
+ }
4204
+ return record.outcome === "posted" && record.pendingId === pendingId;
4205
+ });
4206
+ } catch {
4207
+ return false;
4208
+ }
4209
+ }
4210
+ updatePendingFinalDeliveries(update) {
4211
+ try {
4212
+ this.writePendingFinalDeliveries(update(this.readPendingFinalDeliveries(true)));
4213
+ return true;
4214
+ } catch (err) {
4215
+ process2.stderr.write(`[Channel:${this.name}] failed to update pending GitHub deliveries: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4216
+ `);
4217
+ return false;
4218
+ }
4219
+ }
4220
+ removeReplyPendingInboundTask(delivery) {
4221
+ try {
4222
+ const tasks = this.readInboundTasks();
4223
+ const matching = tasks.filter((task) => task.state === "reply_pending" && task.source.chatId === delivery.chatId && task.source.threadId === delivery.threadId && task.source.messageId === delivery.sourceMessageId);
4224
+ for (const task of matching) {
4225
+ this.removeInboundTask(task.id);
4226
+ }
4227
+ } catch (cleanupErr) {
4228
+ process2.stderr.write(`[Channel:${this.name}] failed to clean up inbound task: ${sanitizeLogText(cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr), 200)}
4229
+ `);
4230
+ }
4231
+ }
4232
+ pendingFinalDeliveriesPath() {
4233
+ return this.channelFilePath("github-pending-deliveries.json");
4234
+ }
4235
+ readPendingFinalDeliveries(strict = false) {
4236
+ try {
4237
+ const parsed = JSON.parse(readFileSync(this.pendingFinalDeliveriesPath(), "utf-8"));
4238
+ return Array.isArray(parsed) ? parsed.filter(isPendingFinalDelivery) : [];
4239
+ } catch (err) {
4240
+ if (err.code !== "ENOENT") {
4241
+ process2.stderr.write(`[Channel:${this.name}] failed to read pending GitHub deliveries: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4242
+ `);
4243
+ if (strict)
4244
+ throw err;
4245
+ }
4246
+ return [];
4247
+ }
4248
+ }
4249
+ writePendingFinalDeliveries(records) {
4250
+ const path = this.pendingFinalDeliveriesPath();
4251
+ if (records.length === 0) {
4252
+ try {
4253
+ unlinkSync(path);
4254
+ } catch (err) {
4255
+ if (err.code !== "ENOENT")
4256
+ throw err;
4257
+ }
4258
+ return;
4259
+ }
4260
+ const dir = join(getGlobalQwenDir(), "channels", getWorkspaceScopeDirName(this.config.cwd));
4261
+ mkdirSync(dir, { recursive: true, mode: 448 });
4262
+ chmodSync(dir, 448);
4263
+ const tmpPath = `${path}.${process2.pid}.tmp`;
4264
+ writeFileSync(tmpPath, `${JSON.stringify(records)}
4265
+ `, {
4266
+ encoding: "utf-8",
4267
+ mode: 384
4268
+ });
4269
+ chmodSync(tmpPath, 384);
4270
+ renameSync(tmpPath, path);
4271
+ chmodSync(path, 384);
4272
+ }
4273
+ async createIssueComment(chatId, threadId, text, retries = 3, shouldRetry = () => true, signal) {
4019
4274
  if (!threadId) {
4020
4275
  throw new Error(`[Channel:${this.name}] createIssueComment requires a threadId`);
4021
4276
  }
@@ -4025,12 +4280,23 @@ var GithubChannel = class extends PollingChannelBase {
4025
4280
  }
4026
4281
  const issueNumber = Number(match[1]);
4027
4282
  const [owner, repo] = chatId.split("/");
4028
- const response = await this.githubApi(() => this.octokit.rest.issues.createComment({
4029
- owner,
4030
- repo,
4031
- issue_number: issueNumber,
4032
- body: text
4033
- }), `createComment(${threadId})`, retries, shouldRetry);
4283
+ const response = await this.githubApi(async () => {
4284
+ this.pendingFinalDeliveryRequestsActive += 1;
4285
+ try {
4286
+ return await this.octokit.rest.issues.createComment({
4287
+ owner,
4288
+ repo,
4289
+ issue_number: issueNumber,
4290
+ body: text,
4291
+ ...signal ? { request: { signal } } : {}
4292
+ });
4293
+ } finally {
4294
+ this.pendingFinalDeliveryRequestsActive -= 1;
4295
+ if (this.pendingFinalDeliveryRequestsActive === 0 && this.pendingFinalDeliveryRetryStopRequested) {
4296
+ this.pendingFinalDeliveryRetryAbort?.abort();
4297
+ }
4298
+ }
4299
+ }, `createComment(${threadId})`, retries, shouldRetry, signal);
4034
4300
  return response.data;
4035
4301
  }
4036
4302
  /**
@@ -4099,15 +4365,25 @@ var GithubChannel = class extends PollingChannelBase {
4099
4365
  });
4100
4366
  }
4101
4367
  async pollOnce() {
4368
+ this.inboundPersistenceBlocked = false;
4369
+ if (this.inboundRecoveryPending) {
4370
+ try {
4371
+ await this.recoverInboundTasks();
4372
+ } catch (err) {
4373
+ process2.stderr.write(`[Channel:${this.name}] inbound task recovery failed, will retry next poll: ${err}
4374
+ `);
4375
+ } finally {
4376
+ this.inboundRecoveryPending = false;
4377
+ }
4378
+ }
4102
4379
  this.cursor.metaFloor ??= this.cursor.lastProcessedAt;
4103
4380
  const since = new Date(new Date(this.cursor.lastProcessedAt).getTime() - 1e3).toISOString();
4104
4381
  const notifications = await this.githubApi(() => this.octokit.paginate(this.octokit.rest.activity.listNotificationsForAuthenticatedUser, { since, per_page: 100 }), "listNotifications");
4105
4382
  notifications.sort((a, b) => a.updated_at.localeCompare(b.updated_at));
4106
4383
  const maxUpdatedAt = notifications.length > 0 ? notifications[notifications.length - 1].updated_at : this.cursor.lastProcessedAt;
4107
4384
  const windowSince = this.cursor.lastProcessedAt;
4108
- await this.markNotificationsAsRead(maxUpdatedAt);
4109
4385
  if (maxUpdatedAt > this.cursor.lastProcessedAt) {
4110
- this.cursor.lastProcessedAt = maxUpdatedAt;
4386
+ this.pendingCursorUpdatedAt = !this.pendingCursorUpdatedAt || maxUpdatedAt > this.pendingCursorUpdatedAt ? maxUpdatedAt : this.pendingCursorUpdatedAt;
4111
4387
  }
4112
4388
  for (const notification of notifications) {
4113
4389
  if (!notification.subject.url)
@@ -4171,6 +4447,17 @@ var GithubChannel = class extends PollingChannelBase {
4171
4447
  continue;
4172
4448
  }
4173
4449
  }
4450
+ if (this.hasRecoverableInboundTasks()) {
4451
+ this.inboundRecoveryPending = true;
4452
+ }
4453
+ if (!this.inboundPersistenceBlocked && !this.hasRecoverableInboundTasks() && this.pendingCursorUpdatedAt) {
4454
+ const committedAt = this.pendingCursorUpdatedAt;
4455
+ await this.markNotificationsAsRead(committedAt);
4456
+ if (committedAt > this.cursor.lastProcessedAt) {
4457
+ this.cursor.lastProcessedAt = committedAt;
4458
+ }
4459
+ this.pendingCursorUpdatedAt = void 0;
4460
+ }
4174
4461
  }
4175
4462
  async processCommentLane(ctx, onlyMentioned, directed = false) {
4176
4463
  const comments = await this.fetchNewComments(ctx);
@@ -4198,7 +4485,9 @@ var GithubChannel = class extends PollingChannelBase {
4198
4485
  isReplyToBot: false,
4199
4486
  metadata: this.buildRouteMetadata(ctx)
4200
4487
  };
4201
- if (!await this.dispatchEnvelope(envelope, ctx.issueNumber)) {
4488
+ if (!await this.dispatchEnvelope(envelope, ctx.issueNumber, {
4489
+ dispatchedComments: [key]
4490
+ })) {
4202
4491
  dispatched = true;
4203
4492
  continue;
4204
4493
  }
@@ -4238,7 +4527,9 @@ Trigger: ${reason}.
4238
4527
  ${buildTriggerGuidance(reason)}
4239
4528
  ${details}`
4240
4529
  };
4241
- await this.dispatchEnvelope(envelope, ctx.issueNumber);
4530
+ await this.dispatchEnvelope(envelope, ctx.issueNumber, {
4531
+ dispatchedEvents: [trigger.key]
4532
+ });
4242
4533
  this.recordDispatched("dispatchedEvents", trigger.key);
4243
4534
  }
4244
4535
  async processAggregateLane(ctx) {
@@ -4273,7 +4564,9 @@ ${summary}`,
4273
4564
  isReplyToBot: false,
4274
4565
  metadata: this.buildRouteMetadata(ctx)
4275
4566
  };
4276
- await this.dispatchEnvelope(envelope, ctx.issueNumber);
4567
+ await this.dispatchEnvelope(envelope, ctx.issueNumber, {
4568
+ dispatchedComments: allComments.map((comment) => comment.node_id || String(comment.id))
4569
+ });
4277
4570
  }
4278
4571
  async findDirectTrigger(ctx, reason) {
4279
4572
  const [owner, repo] = ctx.chatId.split("/");
@@ -4355,7 +4648,9 @@ ${summary}`,
4355
4648
  isReplyToBot: false,
4356
4649
  metadata: this.buildRouteMetadata(ctx)
4357
4650
  };
4358
- await this.dispatchEnvelope(envelope, issueNumber);
4651
+ await this.dispatchEnvelope(envelope, issueNumber, {
4652
+ dispatchedBodies: [bodyKey]
4653
+ });
4359
4654
  this.recordDispatchedBody(bodyKey);
4360
4655
  } catch (err) {
4361
4656
  process2.stderr.write(`[Channel:${this.name}] failed to fetch issue for first contact: ${err}
@@ -4374,24 +4669,235 @@ ${summary}`,
4374
4669
  list.push(key);
4375
4670
  this.cursor[field] = list.slice(-MAX_DISPATCHED);
4376
4671
  }
4377
- async dispatchEnvelope(envelope, issueNumber) {
4672
+ async dispatchEnvelope(envelope, issueNumber, dedupe = {}) {
4673
+ const task = this.claimInboundTask(envelope, issueNumber, dedupe);
4674
+ return this.runInboundTask(task);
4675
+ }
4676
+ claimInboundTask(envelope, issueNumber, dedupe) {
4677
+ const existing = this.readInboundTasks().find((record) => record.source.chatId === envelope.chatId && record.source.threadId === envelope.threadId && record.source.messageId === envelope.messageId);
4678
+ if (existing) {
4679
+ this.applyTaskDedupe(existing);
4680
+ return existing;
4681
+ }
4682
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4683
+ const task = {
4684
+ version: 1,
4685
+ id: randomUUID(),
4686
+ createdAt: now,
4687
+ updatedAt: now,
4688
+ state: "accepted",
4689
+ issueNumber,
4690
+ source: {
4691
+ chatId: envelope.chatId,
4692
+ threadId: envelope.threadId,
4693
+ messageId: envelope.messageId
4694
+ },
4695
+ envelope,
4696
+ dedupe,
4697
+ attempts: 0
4698
+ };
4699
+ this.updateInboundTasks((records) => [...records, task]);
4700
+ this.applyTaskDedupe(task);
4701
+ return task;
4702
+ }
4703
+ async runInboundTask(task) {
4704
+ if (!this.isRecoverableInboundTask(task))
4705
+ return true;
4706
+ const envelope = task.envelope;
4707
+ if (!envelope)
4708
+ return true;
4709
+ const attempts = (task.attempts ?? 0) + 1;
4710
+ this.activeInboundTaskIdsByMessage.set(this.inboundMessageKey(envelope.chatId, envelope.messageId), task.id);
4711
+ this.transitionInboundTask(task.id, "running", { attempts });
4712
+ let cancelled = false;
4378
4713
  try {
4379
4714
  await this.handleInbound(envelope);
4380
- return true;
4715
+ cancelled = this.cancelledInboundTaskIds.has(task.id);
4381
4716
  } catch (err) {
4717
+ const error = sanitizeLogText(err instanceof Error ? err.message : String(err), 200);
4382
4718
  process2.stderr.write(`[Channel:${this.name}] handleInbound failed for ${envelope.messageId}: ${err}
4383
4719
  `);
4384
- if (!(err instanceof FinalPublicationError)) {
4385
- await this.postErrorComment(envelope.chatId, issueNumber);
4720
+ if (err instanceof FinalPublicationError) {
4721
+ if (this.cancelledInboundTaskIds.has(task.id)) {
4722
+ } else if (this.hasPendingFinalDeliveryForTask(task)) {
4723
+ this.transitionInboundTask(task.id, "reply_pending", {
4724
+ envelope: void 0,
4725
+ error
4726
+ });
4727
+ } else {
4728
+ this.removeInboundTask(task.id);
4729
+ }
4730
+ } else if (!this.cancelledInboundTaskIds.has(task.id)) {
4731
+ let posted = task.errorCommentPosted === true;
4732
+ if (!posted) {
4733
+ posted = await this.postErrorComment(envelope.chatId, task.issueNumber);
4734
+ }
4735
+ this.transitionInboundTask(task.id, "failed", {
4736
+ error,
4737
+ attempts,
4738
+ errorCommentPosted: posted
4739
+ });
4386
4740
  }
4387
4741
  return false;
4742
+ } finally {
4743
+ this.activeInboundTaskIdsByMessage.delete(this.inboundMessageKey(envelope.chatId, envelope.messageId));
4744
+ this.cancelledInboundTaskIds.delete(task.id);
4745
+ }
4746
+ if (cancelled)
4747
+ return true;
4748
+ if (this.hasPendingFinalDeliveryForTask(task)) {
4749
+ this.transitionInboundTask(task.id, "reply_pending", {
4750
+ envelope: void 0
4751
+ });
4752
+ } else {
4753
+ this.removeInboundTask(task.id);
4754
+ }
4755
+ return true;
4756
+ }
4757
+ async recoverInboundTasks() {
4758
+ const tasks = this.readInboundTasks().filter((task) => this.isRecoverableInboundTask(task));
4759
+ const pendingDeliveries = tasks.length ? this.readPendingFinalDeliveries(true) : [];
4760
+ const publicationAuditKeys = tasks.length ? this.readPublicationAuditKeys(true) : /* @__PURE__ */ new Set();
4761
+ for (const task of tasks) {
4762
+ if (!task.envelope)
4763
+ continue;
4764
+ this.applyTaskDedupe(task);
4765
+ if (pendingDeliveries.some((record) => record.chatId === task.source.chatId && record.threadId === task.source.threadId && record.sourceMessageId === task.source.messageId)) {
4766
+ this.transitionInboundTask(task.id, "reply_pending", {
4767
+ envelope: void 0
4768
+ });
4769
+ continue;
4770
+ }
4771
+ if (publicationAuditKeys.has(this.inboundTaskSourceKey(task))) {
4772
+ this.removeInboundTask(task.id);
4773
+ continue;
4774
+ }
4775
+ await this.runInboundTask(task);
4776
+ }
4777
+ }
4778
+ isRecoverableInboundTask(task) {
4779
+ return task.state === "accepted" || task.state === "running" || task.state === "failed" && (task.attempts ?? 0) < MAX_INBOUND_TASK_ATTEMPTS;
4780
+ }
4781
+ hasRecoverableInboundTasks() {
4782
+ return this.recoverableInboundTasks > 0;
4783
+ }
4784
+ hasPendingFinalDeliveryForTask(task) {
4785
+ return this.readPendingFinalDeliveries(true).some((record) => record.chatId === task.source.chatId && record.threadId === task.source.threadId && record.sourceMessageId === task.source.messageId);
4786
+ }
4787
+ inboundTaskSourceKey(task) {
4788
+ return `${task.source.chatId}|${task.source.threadId ?? ""}|${task.source.messageId ?? ""}`;
4789
+ }
4790
+ readPublicationAuditKeys(strict = false) {
4791
+ try {
4792
+ const keys = /* @__PURE__ */ new Set();
4793
+ for (const line of readFileSync(this.channelFilePath("github-audit.jsonl"), "utf-8").split("\n")) {
4794
+ if (!line)
4795
+ continue;
4796
+ try {
4797
+ const record = JSON.parse(line);
4798
+ if (record.outcome === "posted" || record.outcome === "suppressed" || record.outcome === "posting") {
4799
+ keys.add(`${record.repository}|${record.threadId ?? ""}|${record.sourceMessageId ?? ""}`);
4800
+ }
4801
+ } catch {
4802
+ continue;
4803
+ }
4804
+ }
4805
+ return keys;
4806
+ } catch (err) {
4807
+ if (err.code === "ENOENT")
4808
+ return /* @__PURE__ */ new Set();
4809
+ if (strict)
4810
+ throw err;
4811
+ return /* @__PURE__ */ new Set();
4812
+ }
4813
+ }
4814
+ applyTaskDedupe(task) {
4815
+ for (const [field, keys] of Object.entries(task.dedupe)) {
4816
+ for (const key of keys ?? []) {
4817
+ this.recordDispatched(field, key);
4818
+ }
4819
+ }
4820
+ }
4821
+ transitionInboundTask(taskId, state, updates = {}) {
4822
+ this.updateInboundTasks((records) => records.map((record) => record.id === taskId ? {
4823
+ ...record,
4824
+ ...updates,
4825
+ state,
4826
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4827
+ } : record));
4828
+ }
4829
+ removeInboundTask(taskId) {
4830
+ this.updateInboundTasks((records) => records.filter((record) => record.id !== taskId));
4831
+ }
4832
+ updateInboundTasks(update) {
4833
+ try {
4834
+ const records = update(this.readInboundTasks());
4835
+ this.writeInboundTasks(records);
4836
+ this.recoverableInboundTasks = records.filter((record) => this.isRecoverableInboundTask(record)).length;
4837
+ } catch (err) {
4838
+ this.inboundPersistenceBlocked = true;
4839
+ throw err;
4840
+ }
4841
+ }
4842
+ inboundTasksPath() {
4843
+ return this.channelFilePath("github-inbound-tasks.json");
4844
+ }
4845
+ readInboundTasks() {
4846
+ try {
4847
+ const parsed = JSON.parse(readFileSync(this.inboundTasksPath(), "utf-8"));
4848
+ if (!Array.isArray(parsed) || !parsed.every(isInboundTaskRecord)) {
4849
+ throw new Error("invalid GitHub inbound task state");
4850
+ }
4851
+ return parsed;
4852
+ } catch (err) {
4853
+ if (err.code === "ENOENT")
4854
+ return [];
4855
+ this.inboundPersistenceBlocked = true;
4856
+ process2.stderr.write(`[Channel:${this.name}] failed to read GitHub inbound tasks: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4857
+ `);
4858
+ throw err;
4859
+ }
4860
+ }
4861
+ writeInboundTasks(records) {
4862
+ const path = this.inboundTasksPath();
4863
+ if (records.length === 0) {
4864
+ try {
4865
+ unlinkSync(path);
4866
+ } catch (err) {
4867
+ if (err.code !== "ENOENT")
4868
+ throw err;
4869
+ }
4870
+ return;
4871
+ }
4872
+ const dir = dirname(path);
4873
+ mkdirSync(dir, { recursive: true, mode: 448 });
4874
+ chmodSync(dir, 448);
4875
+ const tmpPath = `${path}.${process2.pid}.${randomUUID()}.tmp`;
4876
+ writeFileSync(tmpPath, `${JSON.stringify(records)}
4877
+ `, {
4878
+ encoding: "utf-8",
4879
+ mode: 384
4880
+ });
4881
+ chmodSync(tmpPath, 384);
4882
+ renameSync(tmpPath, path);
4883
+ chmodSync(path, 384);
4884
+ }
4885
+ inboundMessageKey(chatId, messageId) {
4886
+ return `${chatId}|${messageId ?? ""}`;
4887
+ }
4888
+ onTaskLifecycle(event) {
4889
+ const taskId = this.activeInboundTaskIdsByMessage.get(this.inboundMessageKey(event.chatId, event.messageId));
4890
+ if (!taskId)
4891
+ return;
4892
+ if (event.type === "cancelled") {
4893
+ this.cancelledInboundTaskIds.add(taskId);
4894
+ this.transitionInboundTask(taskId, "cancelled");
4388
4895
  }
4389
4896
  }
4390
4897
  recordPublicationAudit(record) {
4391
4898
  try {
4392
- const encodedName = this.name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 200);
4393
- const dir = join(getGlobalQwenDir(), "channels");
4394
- const path = join(dir, `${encodedName}-github-audit.jsonl`);
4899
+ const dir = join(getGlobalQwenDir(), "channels", getWorkspaceScopeDirName(this.config.cwd));
4900
+ const path = this.channelFilePath("github-audit.jsonl");
4395
4901
  mkdirSync(dir, { recursive: true, mode: 448 });
4396
4902
  chmodSync(dir, 448);
4397
4903
  appendFileSync(path, `${JSON.stringify(record)}
@@ -4405,6 +4911,35 @@ ${summary}`,
4405
4911
  `);
4406
4912
  }
4407
4913
  }
4914
+ migrateLegacyPublicationState() {
4915
+ try {
4916
+ const channelsRoot = join(getGlobalQwenDir(), "channels");
4917
+ const encodedName = this.name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 200);
4918
+ const sentinel = this.channelFilePath("github-state-migrated");
4919
+ if (existsSync(sentinel))
4920
+ return;
4921
+ mkdirSync(dirname(sentinel), { recursive: true, mode: 448 });
4922
+ for (const suffix of [
4923
+ "github-pending-deliveries.json",
4924
+ "github-audit.jsonl"
4925
+ ]) {
4926
+ const legacyPath = join(channelsRoot, `${encodedName}-${suffix}`);
4927
+ const scopedPath = this.channelFilePath(suffix);
4928
+ if (!existsSync(scopedPath) && existsSync(legacyPath)) {
4929
+ renameSync(legacyPath, scopedPath);
4930
+ }
4931
+ }
4932
+ writeFileSync(sentinel, "", { mode: 384 });
4933
+ } catch (err) {
4934
+ process2.stderr.write(`[Channel:${this.name}] legacy GitHub state migration failed: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4935
+ `);
4936
+ }
4937
+ }
4938
+ channelFilePath(suffix) {
4939
+ const encodedName = this.name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 200);
4940
+ const nameHash = createHash("sha256").update(this.name).digest("hex").slice(0, 16);
4941
+ return join(getGlobalQwenDir(), "channels", getWorkspaceScopeDirName(this.config.cwd), `${encodedName}-${nameHash}-${suffix}`);
4942
+ }
4408
4943
  extractFromSubjectUrl(url) {
4409
4944
  const match = url.match(/\/repos\/([^/]+\/[^/]+)\/(issues|pulls)\/(\d+)/);
4410
4945
  if (!match)
@@ -4426,11 +4961,15 @@ ${GITHUB_PUBLICATION_INSTRUCTIONS}
4426
4961
  Trigger: ${ctx.reason}.
4427
4962
  ${buildTriggerGuidance(ctx.reason)}`;
4428
4963
  }
4429
- async githubApi(fn, label, retries = 3, shouldRetry = () => true) {
4964
+ async githubApi(fn, label, retries = 3, shouldRetry = () => true, signal) {
4430
4965
  for (let attempt = 1; attempt <= retries; attempt++) {
4966
+ if (signal?.aborted)
4967
+ throw new Error(`${label} aborted`);
4431
4968
  try {
4432
4969
  return await fn();
4433
4970
  } catch (err) {
4971
+ if (signal?.aborted)
4972
+ throw err;
4434
4973
  if (attempt === retries || !shouldRetry(err))
4435
4974
  throw err;
4436
4975
  const e = err;
@@ -4446,11 +4985,30 @@ ${buildTriggerGuidance(ctx.reason)}`;
4446
4985
  }
4447
4986
  process2.stderr.write(`[Channel:${this.name}] ${label} failed (attempt ${attempt}/${retries}, status=${e.status}), retrying in ${cooldown}ms: ${e.message}
4448
4987
  `);
4449
- await this.abortableSleep(cooldown);
4988
+ await this.sleepForRetry(cooldown, signal);
4450
4989
  }
4451
4990
  }
4452
4991
  throw new Error("unreachable");
4453
4992
  }
4993
+ sleepForRetry(ms, signal) {
4994
+ if (!signal)
4995
+ return this.abortableSleep(ms);
4996
+ return new Promise((resolve) => {
4997
+ if (signal.aborted) {
4998
+ resolve();
4999
+ return;
5000
+ }
5001
+ const timer = setTimeout(() => {
5002
+ signal.removeEventListener("abort", onAbort);
5003
+ resolve();
5004
+ }, ms);
5005
+ const onAbort = /* @__PURE__ */ __name(() => {
5006
+ clearTimeout(timer);
5007
+ resolve();
5008
+ }, "onAbort");
5009
+ signal.addEventListener("abort", onAbort, { once: true });
5010
+ });
5011
+ }
4454
5012
  async markNotificationsAsRead(lastReadAt) {
4455
5013
  await this.githubApi(() => this.octokit.rest.activity.markNotificationsAsRead({
4456
5014
  last_read_at: lastReadAt,
@@ -4465,9 +5023,11 @@ ${buildTriggerGuidance(ctx.reason)}`;
4465
5023
  issue_number: issueNumber,
4466
5024
  body: "\u26A0\uFE0F Failed to process this request. Please re-mention the bot to retry."
4467
5025
  }), `postErrorComment(${chatId}#${issueNumber})`);
5026
+ return true;
4468
5027
  } catch (err) {
4469
5028
  process2.stderr.write(`[Channel:${this.name}] postErrorComment also failed for ${chatId}#${issueNumber}, user must re-mention manually: ${err}
4470
5029
  `);
5030
+ return false;
4471
5031
  }
4472
5032
  }
4473
5033
  };
@@ -4479,6 +5039,85 @@ var plugin = {
4479
5039
  requiredConfigFields: ["token"],
4480
5040
  envResolvableConfigFields: ["baseUrl"],
4481
5041
  defaultSessionScope: "chat_thread",
5042
+ management: {
5043
+ fields: [
5044
+ {
5045
+ key: "token",
5046
+ label: "Personal Access Token",
5047
+ kind: "secret",
5048
+ required: true,
5049
+ envResolvable: true,
5050
+ description: 'Classic PAT with "notifications" scope'
5051
+ },
5052
+ {
5053
+ key: "baseUrl",
5054
+ label: "Base URL",
5055
+ kind: "string",
5056
+ envResolvable: true,
5057
+ description: "GitHub Enterprise API root (e.g. https://ghe.example.com/api/v3). Leave empty for github.com"
5058
+ },
5059
+ {
5060
+ key: "groupPolicy",
5061
+ label: "Group Policy",
5062
+ kind: "enum",
5063
+ required: true,
5064
+ description: 'Must be "Open" for notifications to flow',
5065
+ default: "open",
5066
+ options: [
5067
+ { value: "open", label: "Open" },
5068
+ { value: "allowlist", label: "Allowlist" },
5069
+ { value: "disabled", label: "Disabled" }
5070
+ ]
5071
+ },
5072
+ {
5073
+ key: "senderPolicy",
5074
+ label: "Sender Policy",
5075
+ kind: "enum",
5076
+ required: true,
5077
+ description: 'Use "Allowlist" with allowed users on public repos',
5078
+ options: [
5079
+ { value: "allowlist", label: "Allowlist" },
5080
+ { value: "pairing", label: "Pairing" },
5081
+ { value: "open", label: "Open" }
5082
+ ]
5083
+ },
5084
+ {
5085
+ key: "allowedUsers",
5086
+ label: "Allowed Users",
5087
+ kind: "string-list",
5088
+ description: "GitHub usernames, used by Allowlist and Pairing policies"
5089
+ },
5090
+ {
5091
+ key: "reasonFilter",
5092
+ label: "Reason Filter",
5093
+ kind: "string-list",
5094
+ description: "Optional. Comma-separated notification reasons to process. Leave empty to process all.",
5095
+ options: [
5096
+ { value: "mention", label: "mention" },
5097
+ { value: "review_requested", label: "review_requested" },
5098
+ { value: "assign", label: "assign" },
5099
+ { value: "author", label: "author" },
5100
+ { value: "comment", label: "comment" },
5101
+ { value: "ci_activity", label: "ci_activity" },
5102
+ { value: "manual", label: "manual" },
5103
+ { value: "state_change", label: "state_change" },
5104
+ { value: "subscribed", label: "subscribed" },
5105
+ { value: "team_mention", label: "team_mention" },
5106
+ { value: "security_alert", label: "security_alert" },
5107
+ { value: "approval_requested", label: "approval_requested" },
5108
+ { value: "invitation", label: "invitation" },
5109
+ {
5110
+ value: "member_feature_requested",
5111
+ label: "member_feature_requested"
5112
+ },
5113
+ {
5114
+ value: "security_advisory_credit",
5115
+ label: "security_advisory_credit"
5116
+ }
5117
+ ]
5118
+ }
5119
+ ]
5120
+ },
4482
5121
  createChannel: /* @__PURE__ */ __name((name, config, bridge, options) => new GithubChannel(name, config, bridge, options), "createChannel")
4483
5122
  };
4484
5123
  export {