@qwen-code/qwen-code 0.19.9 → 0.19.10-nightly.20260716.506ce0a1a

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 (323) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +14 -6
  2. package/bundled/qc-helper/docs/features/approval-mode.md +2 -2
  3. package/bundled/qc-helper/docs/features/channels/dingtalk.md +33 -0
  4. package/bundled/qc-helper/docs/features/channels/overview.md +64 -20
  5. package/bundled/qc-helper/docs/features/channels/wecom.md +8 -7
  6. package/bundled/qc-helper/docs/features/code-review.md +88 -44
  7. package/bundled/qc-helper/docs/features/commands.md +6 -6
  8. package/bundled/qc-helper/docs/features/hooks.md +29 -0
  9. package/bundled/qc-helper/docs/features/sandbox.md +2 -0
  10. package/bundled/qc-helper/docs/features/sub-agents.md +21 -0
  11. package/bundled/qc-helper/docs/qwen-serve.md +49 -20
  12. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
  13. package/bundled/review/DESIGN.md +219 -33
  14. package/bundled/review/SKILL.md +429 -382
  15. package/chunks/{MaxSizedBox-6HYJ3ZPJ.js → MaxSizedBox-WOC3YDWA.js} +31 -32
  16. package/chunks/{StandaloneSessionPicker-WS4GXYOG.js → StandaloneSessionPicker-H5HZCAFD.js} +49 -48
  17. package/chunks/{acpAgent-5FWSOZL5.js → acpAgent-55YDJDNM.js} +922 -550
  18. package/chunks/{agent-HOLHB3RT.js → agent-H6VE5W3L.js} +26 -27
  19. package/chunks/{agent-headless-SXL6H56A.js → agent-headless-T4XJOCRU.js} +26 -27
  20. package/chunks/{anthropicContentGenerator-L73TO3DB.js → anthropicContentGenerator-HBRXJSP4.js} +78 -16
  21. package/chunks/{artifact-tool-U7IRTLIV.js → artifact-tool-MIZLXBLF.js} +2 -2
  22. package/chunks/{askUserQuestion-2DLUUZZG.js → askUserQuestion-XMJVPNWM.js} +2 -2
  23. package/chunks/{bridge-BCJQ7BBB.js → bridge-FTB6ZP3R.js} +31 -34
  24. package/chunks/{ca-UJ6HC4H2.js → ca-CODEN7TD.js} +9 -1
  25. package/chunks/channel-worker-group-K7QKH6IG.js +13 -0
  26. package/chunks/channel-worker-manager-CUDPU4CN.js +418 -0
  27. package/chunks/channel-worker-supervisor-4XTO2ZD3.js +20 -0
  28. package/chunks/{chunk-LXNEJD34.js → chunk-2FO5GFKZ.js} +1 -1
  29. package/chunks/{chunk-VA4KQY6V.js → chunk-2JNR4VP2.js} +3 -3
  30. package/chunks/{chunk-L36P3NBD.js → chunk-2W3OOD4W.js} +2 -0
  31. package/chunks/{chunk-7R3CZA3Q.js → chunk-2Z5UX4KI.js} +1 -1
  32. package/chunks/{chunk-CUMMSHH6.js → chunk-3DWIFU7L.js} +1 -1
  33. package/chunks/chunk-3MX7D6QN.js +516 -0
  34. package/chunks/{chunk-SJHDYNHF.js → chunk-4BJVSIS3.js} +3 -3
  35. package/chunks/{chunk-TO6QE22P.js → chunk-5CNYZ7TN.js} +26 -22
  36. package/chunks/{chunk-N3EMS75V.js → chunk-5DEJ4T6C.js} +1333 -1232
  37. package/chunks/chunk-5HBA2Z7V.js +28 -0
  38. package/chunks/{chunk-ZJE7NSL3.js → chunk-5M6IDOMF.js} +34 -97
  39. package/chunks/{chunk-SCGXUJBT.js → chunk-6LI3L22E.js} +3 -3
  40. package/chunks/{chunk-K434OPES.js → chunk-6WPJRYLZ.js} +3 -3
  41. package/chunks/{chunk-YAUFFXDS.js → chunk-7GM2RWCR.js} +12 -11
  42. package/chunks/{chunk-OQ4DRIOA.js → chunk-7HWE2W74.js} +2 -2
  43. package/chunks/{chunk-J4TSQ5EQ.js → chunk-7NFB7LBQ.js} +8 -1
  44. package/chunks/{chunk-MC23XKFE.js → chunk-A2HWMJVO.js} +37 -3
  45. package/chunks/{chunk-3G7CIHVV.js → chunk-ABNYOMDG.js} +4 -4
  46. package/chunks/{chunk-3AHXV67U.js → chunk-AGZLXLBY.js} +1 -1
  47. package/chunks/{chunk-RDJSGHZE.js → chunk-ALGGS7UH.js} +1 -38
  48. package/chunks/{chunk-Q4YQW46J.js → chunk-AMRZVQUY.js} +43 -40
  49. package/chunks/{chunk-W3JHSWRP.js → chunk-AN6C2U46.js} +4 -4
  50. package/chunks/{chunk-GWHYEOV2.js → chunk-ARKANCNX.js} +2 -1
  51. package/chunks/{chunk-5SFOMFG4.js → chunk-AVEOWVOM.js} +36 -2
  52. package/chunks/{chunk-34MW4KTI.js → chunk-BA5JGE5F.js} +2 -2
  53. package/chunks/{chunk-5HOMWMCL.js → chunk-BSJAQUFD.js} +5492 -1336
  54. package/chunks/{chunk-L6P4KPHR.js → chunk-BWJKL5DB.js} +58 -38
  55. package/chunks/{chunk-PVZM22CK.js → chunk-C32JFRGV.js} +5 -5
  56. package/chunks/{chunk-AKIVHSJR.js → chunk-CZIHPO7L.js} +163 -782
  57. package/chunks/chunk-DL7RCO6V.js +493 -0
  58. package/chunks/{chunk-KKNMOEZH.js → chunk-E2UVIFI2.js} +185 -398
  59. package/chunks/{chunk-5VXNZWGF.js → chunk-ENUQSKEY.js} +1 -1
  60. package/chunks/{chunk-PDZ7MBDG.js → chunk-EWILX3BM.js} +27 -6
  61. package/chunks/{chunk-BMGVRZN4.js → chunk-EZWX232B.js} +1 -1
  62. package/chunks/{chunk-4GORAUCN.js → chunk-F333XEAU.js} +1 -1
  63. package/chunks/{chunk-YLV3XXFB.js → chunk-FBU7WRZI.js} +17 -1
  64. package/chunks/chunk-FEOZBUMT.js +77 -0
  65. package/chunks/{chunk-5U27BZ72.js → chunk-FKEGVMPX.js} +269 -83
  66. package/chunks/chunk-FNX3LGAK.js +34 -0
  67. package/chunks/{chunk-LK7KUE55.js → chunk-FSA7ERJ2.js} +1 -1
  68. package/chunks/{chunk-PODM4SZ4.js → chunk-G3BIBPI7.js} +5 -5
  69. package/chunks/{chunk-A3XKDTSH.js → chunk-GA65O2TB.js} +4 -4
  70. package/chunks/{chunk-3BTRNWWD.js → chunk-GOXKNSDZ.js} +1 -64
  71. package/chunks/{channel-worker-supervisor-WSOAAYUO.js → chunk-GXGIM7QP.js} +67 -35
  72. package/chunks/{chunk-6PESIEKJ.js → chunk-GXUWGPYX.js} +68 -6
  73. package/chunks/chunk-GYTKQYDB.js +23 -0
  74. package/chunks/{chunk-GZRTYZ2R.js → chunk-H3XMJV7T.js} +218 -1
  75. package/chunks/{chunk-WQUQVGTF.js → chunk-HGRXW4HQ.js} +467 -106
  76. package/chunks/{chunk-YVEDCAUE.js → chunk-HGT6JR3U.js} +19 -0
  77. package/chunks/{chunk-ECNYL4AA.js → chunk-HUEWOHKV.js} +1 -1
  78. package/chunks/chunk-IDS7MSUP.js +74 -0
  79. package/chunks/{chunk-KHUQZZJ6.js → chunk-IHDRJLLR.js} +382 -36
  80. package/chunks/{chunk-CLFKOZEK.js → chunk-J4SEAF4X.js} +114 -4
  81. package/chunks/{chunk-B5ORX3FG.js → chunk-JGUT3LWZ.js} +6 -1
  82. package/chunks/{chunk-P67PFTUT.js → chunk-KEYEYCHZ.js} +4 -4
  83. package/chunks/{chunk-ORBGV7NJ.js → chunk-KKRTYONM.js} +6 -4
  84. package/chunks/{chunk-7PU4FYLG.js → chunk-KOXFAG6P.js} +4 -18
  85. package/chunks/{chunk-EGYZ275M.js → chunk-KWLVWNF5.js} +55 -7
  86. package/chunks/{chunk-XLA62MYB.js → chunk-KXECVZPD.js} +2 -2
  87. package/chunks/{chunk-IAVB3HM3.js → chunk-L4Y55KIS.js} +33 -1
  88. package/chunks/{chunk-4QLQE5WJ.js → chunk-L5U3ELZX.js} +1 -1
  89. package/chunks/{chunk-4ZVWQHEQ.js → chunk-LQIDP6K5.js} +1 -1
  90. package/chunks/{chunk-DC5Z3DU6.js → chunk-LXLTBIDP.js} +43 -6
  91. package/chunks/chunk-LXXMOYPL.js +22 -0
  92. package/chunks/{chunk-Q6I5QHMH.js → chunk-MFNKBXYH.js} +1 -1
  93. package/chunks/chunk-MR3PXB6E.js +48 -0
  94. package/chunks/{chunk-JZETEN3Z.js → chunk-N45S36AH.js} +3 -3
  95. package/chunks/{chunk-GWLSNBG4.js → chunk-NA3IG6XG.js} +748 -27
  96. package/chunks/{chunk-D5SKC4YG.js → chunk-O3M25JXE.js} +213 -29
  97. package/chunks/{chunk-EAXLGSFP.js → chunk-O6XGHDS5.js} +11 -11
  98. package/chunks/{chunk-WL4L7EZH.js → chunk-OA4JVLSA.js} +27 -1
  99. package/chunks/{chunk-X4NKFGFJ.js → chunk-OJAMDVF5.js} +0 -15
  100. package/chunks/{chunk-BZIFY5TF.js → chunk-OKAIGAYW.js} +3 -3
  101. package/chunks/{chunk-CNEIYWSA.js → chunk-OVIV236K.js} +5 -5
  102. package/chunks/{chunk-3SJSQXNS.js → chunk-PEJWA3QQ.js} +12 -8
  103. package/chunks/{chunk-E2IY465S.js → chunk-PWNOAWXB.js} +59 -12
  104. package/chunks/{chunk-B4G2WOFA.js → chunk-Q5MKHNIR.js} +28 -5
  105. package/chunks/{chunk-MXL5UP4X.js → chunk-Q6TUALBE.js} +1 -1
  106. package/chunks/{chunk-QYMQSECS.js → chunk-QAW7PIHT.js} +2 -2
  107. package/chunks/{chunk-HKMAENAO.js → chunk-QDTGDM2T.js} +14 -9
  108. package/chunks/{chunk-LUODI7WD.js → chunk-RCEEXIOO.js} +6 -4
  109. package/chunks/{chunk-W2UIXQDL.js → chunk-RTV5TUYB.js} +12 -4
  110. package/chunks/chunk-RW7DUKFO.js +91 -0
  111. package/chunks/{chunk-NVYTCB5Z.js → chunk-SA6QVLWI.js} +17325 -16259
  112. package/chunks/chunk-SJSI7PXU.js +59 -0
  113. package/chunks/{chunk-HVY356M2.js → chunk-SMGBWLNU.js} +3 -3
  114. package/chunks/{chunk-CEA3E3JB.js → chunk-SPJQO2CE.js} +2 -2
  115. package/chunks/{chunk-CC2ITGCF.js → chunk-STZQCNN3.js} +49 -6
  116. package/chunks/{chunk-HPDUL5MF.js → chunk-T2ZDAER2.js} +1 -1
  117. package/chunks/{chunk-XUOOWUZS.js → chunk-T3TQ3ACP.js} +1 -1
  118. package/chunks/{chunk-4C5BQ2D2.js → chunk-TIKDHS6V.js} +100 -17
  119. package/chunks/chunk-TV6K2FB4.js +224 -0
  120. package/chunks/{chunk-5XUCZNSY.js → chunk-TYLOFD2U.js} +1 -1
  121. package/chunks/{chunk-D2GFWEXB.js → chunk-UCUUH2JV.js} +6 -6
  122. package/chunks/{chunk-BAPGSC7U.js → chunk-UZP7K33Y.js} +23 -9
  123. package/chunks/{chunk-77XJ3SWN.js → chunk-VDAR6UO2.js} +8 -6
  124. package/chunks/{chunk-XYQSMABS.js → chunk-VKLVGA2D.js} +4 -4
  125. package/chunks/{chunk-JSG7ZSBC.js → chunk-VRR65QYW.js} +251 -3
  126. package/chunks/{chunk-U2NNEY4R.js → chunk-VT5AWJN4.js} +1 -1
  127. package/chunks/{chunk-ZLXQN2QS.js → chunk-WIEO4CWB.js} +1 -1
  128. package/chunks/{chunk-X6TJ3YZV.js → chunk-WKEO3GLZ.js} +160 -60
  129. package/chunks/{chunk-J3JA76CF.js → chunk-WTNZ6DXB.js} +1 -1
  130. package/chunks/{chunk-VWPVHAMO.js → chunk-XIWQ5QST.js} +8 -1
  131. package/chunks/{chunk-TDEXZKMT.js → chunk-Y66VOBRP.js} +2 -2
  132. package/chunks/{chunk-3VAGG5T5.js → chunk-YYBZUIPL.js} +1015 -100
  133. package/chunks/{chunk-CCDMCZPP.js → chunk-YZ3SBNP7.js} +1 -1
  134. package/chunks/{chunk-ATAKVRIF.js → chunk-Z3XA2TNG.js} +7 -7
  135. package/chunks/{chunk-EMDMPBJ4.js → chunk-Z7SVC7PJ.js} +10 -3
  136. package/chunks/{chunk-AURZZYMD.js → chunk-ZIVSSTUG.js} +4 -4
  137. package/chunks/{chunk-UOZWKTQG.js → chunk-ZPAHLFZU.js} +2 -2
  138. package/chunks/{chunk-34Z3U5RQ.js → chunk-ZQIYGQCG.js} +765 -168
  139. package/chunks/{computer-use-HM37RX6A.js → computer-use-NADT7ULB.js} +26 -27
  140. package/chunks/{config-utils-NV2SVGPB.js → config-utils-IQQT3GCI.js} +3 -2
  141. package/chunks/{contextCommand-OF5SFGH2.js → contextCommand-U7NEJYJ7.js} +28 -29
  142. package/chunks/{create-sub-session-INX7TXJI.js → create-sub-session-EAB2U5XW.js} +2 -2
  143. package/chunks/{create-sub-session-KPRT7ECJ.js → create-sub-session-SUN33INC.js} +26 -27
  144. package/chunks/{cron-create-AMBKGZX6.js → cron-create-GMVKSXZT.js} +4 -4
  145. package/chunks/{cron-delete-LRU4OJYG.js → cron-delete-VDZKUAVK.js} +4 -4
  146. package/chunks/{cron-list-WVP2JXER.js → cron-list-UG7C7RAR.js} +4 -4
  147. package/chunks/{daemon-ARHTSTXP.js → daemon-SJXEKP3H.js} +773 -126
  148. package/chunks/daemon-status-provider-WIIYLGZJ.js +74 -0
  149. package/chunks/{de-EBJN3OVN.js → de-NOXPCSYN.js} +9 -1
  150. package/chunks/{dist-XKU3ABM5.js → dist-4WCQEZIN.js} +4 -51
  151. package/chunks/{dist-QYCAEZIT.js → dist-DOPL5LSQ.js} +5 -1
  152. package/chunks/{dist-WKPOYU7O.js → dist-GXJVCOS7.js} +1 -1
  153. package/chunks/{dist-R7LN5AZE.js → dist-PX2ERZ6I.js} +2 -2
  154. package/chunks/{dist-PNVLTKTL.js → dist-V2YVLYOW.js} +215 -33
  155. package/chunks/{dist-VHV4EVHG.js → dist-VYWUKURL.js} +1 -1
  156. package/chunks/{earlyInputCapture-3VTHRC4C.js → earlyInputCapture-6FRM6UMU.js} +27 -28
  157. package/chunks/{edit-VNI4NJ73.js → edit-7GOIQTCZ.js} +27 -28
  158. package/chunks/{en-5A6LA7RK.js → en-JFVULHTB.js} +11 -1
  159. package/chunks/{enter-worktree-IYYBKPZT.js → enter-worktree-476E6WIK.js} +26 -27
  160. package/chunks/{enterPlanMode-GMC5FD5A.js → enterPlanMode-IRJM6W3C.js} +42 -28
  161. package/chunks/{environment-5HSSRMJ7.js → environment-ROMULS2X.js} +30 -30
  162. package/chunks/{errors-HMFCJJ7P.js → errors-OUIWPUDY.js} +28 -29
  163. package/chunks/{exit-worktree-6BHCMP5G.js → exit-worktree-PPSFK753.js} +26 -27
  164. package/chunks/{exitPlanMode-CJW7G74W.js → exitPlanMode-YKQMD4D7.js} +26 -27
  165. package/chunks/{fast-path-FRDMDVHG.js → fast-path-CNK2HHKH.js} +3 -3
  166. package/chunks/{fast-path-settings-5QZE64HR.js → fast-path-settings-DAOVFADC.js} +6 -4
  167. package/chunks/{fr-YXRABLYZ.js → fr-HVFNU42H.js} +9 -1
  168. package/chunks/{gemini-QYSEGHAX.js → gemini-WQNFT6TR.js} +216 -90
  169. package/chunks/{geminiContentGenerator-J7YNZDYP.js → geminiContentGenerator-4QJH2JDP.js} +4 -4
  170. package/chunks/{glob-BRCSGFOT.js → glob-36TVLBWW.js} +28 -28
  171. package/chunks/{grep-EE2DUOSY.js → grep-QTYLNO2M.js} +26 -27
  172. package/chunks/{handleAutoUpdate-5NWTLR67.js → handleAutoUpdate-36FQFODK.js} +30 -31
  173. package/chunks/{i18n-BASPENNQ.js → i18n-JUGYPVSP.js} +27 -28
  174. package/chunks/initializer-OBBJUS25.js +72 -0
  175. package/chunks/{installationInfo-W6V4DT7W.js → installationInfo-MCKTPX64.js} +27 -28
  176. package/chunks/{ja-7VQOANVG.js → ja-PY5AF544.js} +10 -2
  177. package/chunks/{keychain-token-storage-VKUBNCP4.js → keychain-token-storage-AU22CQI6.js} +2 -2
  178. package/chunks/list-VW5QY4VJ.js +75 -0
  179. package/chunks/loadedSettingsAdapter-IELOOXG6.js +69 -0
  180. package/chunks/{loop-wakeup-WX7BLWWP.js → loop-wakeup-QXWZSCYM.js} +5 -5
  181. package/chunks/{ls-ZB2BDP6T.js → ls-2OUTG3IZ.js} +4 -4
  182. package/chunks/{lsp-V5DN4YUJ.js → lsp-4VMNXZW6.js} +2 -2
  183. package/chunks/mcp-Q3EC37ZS.js +69 -0
  184. package/chunks/{monitor-X5Z3H4FY.js → monitor-DCZ27AFR.js} +26 -27
  185. package/chunks/nonInteractiveCli-KXD7PFO4.js +128 -0
  186. package/chunks/{notebook-edit-TRM2QIJK.js → notebook-edit-YU4XF5LI.js} +27 -28
  187. package/chunks/{openaiContentGenerator-AAYONPMF.js → openaiContentGenerator-KL5WER2D.js} +13 -13
  188. package/chunks/{pidfile-D4I4FTVA.js → pidfile-WBOD7EJV.js} +27 -28
  189. package/chunks/processUtils-AOV4R34M.js +29 -0
  190. package/chunks/{pt-2C6YCSHL.js → pt-A3PRQXMU.js} +9 -1
  191. package/chunks/{qwenContentGenerator-K3HZ2H76.js → qwenContentGenerator-YIGBHYE3.js} +28 -29
  192. package/chunks/{qwenOAuth2-PJQ27GSO.js → qwenOAuth2-LEYOU7RU.js} +5 -5
  193. package/chunks/{read-file-7SZSIZOL.js → read-file-WVL4Y76X.js} +9 -9
  194. package/chunks/{read-mcp-resource-SM5DA4RG.js → read-mcp-resource-JUFHCKSC.js} +2 -2
  195. package/chunks/{record-artifact-4XKZX3UW.js → record-artifact-DWSMBXYY.js} +2 -2
  196. package/chunks/{ripGrep-YNPFVCZP.js → ripGrep-6GSWJERP.js} +26 -27
  197. package/chunks/{ru-6BBHVZZV.js → ru-IBCWUTOC.js} +9 -1
  198. package/chunks/{run-qwen-serve-DMWV5N6L.js → run-qwen-serve-QYUQMEDT.js} +1233 -371
  199. package/chunks/{runtime-57WWRVHN.js → runtime-K2YZCNB3.js} +42 -38
  200. package/chunks/{scheduler-YBLGWSIL.js → scheduler-EQCSN7QV.js} +26 -27
  201. package/chunks/{send-message-F36YD6KQ.js → send-message-JZ752NWD.js} +3 -3
  202. package/chunks/{serve-R63NK3CV.js → serve-DX5UGZD4.js} +36 -35
  203. package/chunks/{server-Z22GCAXP.js → server-T6HJH3DR.js} +5869 -1398
  204. package/chunks/{session-HKAAI7BN.js → session-SNU6ACY6.js} +299 -100
  205. package/chunks/{settings-3EYN66GK.js → settings-AKHZZNEL.js} +34 -33
  206. package/chunks/{shell-6YXINTWJ.js → shell-EY74IEZK.js} +28 -27
  207. package/chunks/{skill-OO57P4X6.js → skill-DPU62O3H.js} +11 -11
  208. package/chunks/{spawnChannel-JD7G4VU6.js → spawnChannel-RQZRWJ2V.js} +28 -30
  209. package/chunks/{src-G5UPLZSZ.js → src-7FITDSVC.js} +164 -52
  210. package/chunks/{standalone-update-OB7F4JPZ.js → standalone-update-XO3RK6DO.js} +28 -29
  211. package/chunks/{startInteractiveUI-73EGLIIH.js → startInteractiveUI-DVWZRHYQ.js} +728 -382
  212. package/chunks/{syntheticOutput-34H7LGA7.js → syntheticOutput-LELY6HAI.js} +3 -3
  213. package/chunks/{task-create-FRGOEIKY.js → task-create-QY3BIMNM.js} +8 -7
  214. package/chunks/{task-list-OW766BXZ.js → task-list-D2U47WBJ.js} +6 -5
  215. package/chunks/{task-stop-SIR5SNBN.js → task-stop-N6E5IYLH.js} +2 -2
  216. package/chunks/{task-update-YRA532MT.js → task-update-HJCQILLB.js} +8 -7
  217. package/chunks/{team-create-PECAAPXK.js → team-create-FOSRT62K.js} +26 -27
  218. package/chunks/{team-delete-6OSECUZC.js → team-delete-RCWYDRF4.js} +6 -5
  219. package/chunks/{team-plan-approval-L6F434RY.js → team-plan-approval-6VUDV27J.js} +26 -27
  220. package/chunks/{theme-manager-ZG4RY6GU.js → theme-manager-6UT6IO5T.js} +27 -28
  221. package/chunks/{todoWrite-YDMFEV4G.js → todoWrite-FKCGATPJ.js} +4 -4
  222. package/chunks/{tool-search-THVIWAPB.js → tool-search-ASSFMMN5.js} +10 -10
  223. package/chunks/{total-session-admission-JOKNRIIA.js → total-session-admission-YYWUO23H.js} +32 -35
  224. package/chunks/tree-sitter-YJVE2ZUK.js +2980 -0
  225. package/chunks/{trustedFolders-EOGT3UVS.js → trustedFolders-4BWV2IBK.js} +29 -29
  226. package/chunks/types-ML3TRJQ5.js +16 -0
  227. package/chunks/update-relaunch-74DLGH73.js +87 -0
  228. package/chunks/{updateCheck-2KYEYVSB.js → updateCheck-CRFCGFO5.js} +31 -30
  229. package/chunks/{validateNonInterActiveAuth-EN6ZQSR2.js → validateNonInterActiveAuth-BTTEPBGQ.js} +76 -73
  230. package/chunks/{version-3UEA6ZIR.js → version-Y5JE5Q63.js} +1 -1
  231. package/chunks/{web-fetch-PAAEQKBW.js → web-fetch-CFCWTCAP.js} +5 -6
  232. package/chunks/{workflow-QCCCMYP3.js → workflow-6WC7VPQN.js} +27 -28
  233. package/chunks/workspace-providers-status-RVG2C5ZS.js +72 -0
  234. package/chunks/workspace-registration-store-SLEU3D3X.js +27 -0
  235. package/chunks/{workspace-registry-24QFLQYH.js → workspace-registry-QMWGVOTF.js} +32 -35
  236. package/chunks/workspace-service-G572JSEU.js +84 -0
  237. package/chunks/workspace-skills-status-VSSMSMNK.js +71 -0
  238. package/chunks/{write-file-I3MUDRIE.js → write-file-5XVDRORP.js} +27 -28
  239. package/chunks/{zh-RQE7I22T.js → zh-PAUNIMHQ.js} +12 -2
  240. package/chunks/{zh-TW-TBPQGHKH.js → zh-TW-GNEUIUFF.js} +12 -2
  241. package/cli-entry.js +92 -8
  242. package/cli.js +11 -11
  243. package/locales/ca.js +16 -0
  244. package/locales/de.js +16 -0
  245. package/locales/en.js +19 -0
  246. package/locales/fr.js +16 -0
  247. package/locales/ja.js +17 -1
  248. package/locales/pt.js +16 -0
  249. package/locales/ru.js +16 -0
  250. package/locales/zh-TW.js +20 -1
  251. package/locales/zh.js +20 -1
  252. package/package.json +3 -3
  253. package/web-shell/assets/{arc-C15hqnJe.js → arc-vx1tRwnX.js} +1 -1
  254. package/web-shell/assets/{architectureDiagram-3BPJPVTR-B-At37J_.js → architectureDiagram-3BPJPVTR-DBi6iUA1.js} +1 -1
  255. package/web-shell/assets/{blockDiagram-GPEHLZMM-B51wjcU7.js → blockDiagram-GPEHLZMM-Ga9JJ4Ak.js} +1 -1
  256. package/web-shell/assets/{c4Diagram-AAUBKEIU-xBK7BAwN.js → c4Diagram-AAUBKEIU-D8Vi5FYw.js} +1 -1
  257. package/web-shell/assets/channel-B29zQDTL.js +1 -0
  258. package/web-shell/assets/{chunk-2J33WTMH-xKmLg7Yr.js → chunk-2J33WTMH-CSV6yQpU.js} +1 -1
  259. package/web-shell/assets/{chunk-4BX2VUAB-BfkdKZCM.js → chunk-4BX2VUAB-toYlppdI.js} +1 -1
  260. package/web-shell/assets/{chunk-55IACEB6-DT713sZf.js → chunk-55IACEB6-DbjzuyAr.js} +1 -1
  261. package/web-shell/assets/{chunk-727SXJPM-CYEeAamv.js → chunk-727SXJPM-IkkSjSkk.js} +1 -1
  262. package/web-shell/assets/{chunk-AQP2D5EJ-DBN-5CFd.js → chunk-AQP2D5EJ-D5wUL6Zm.js} +1 -1
  263. package/web-shell/assets/{chunk-FMBD7UC4-CGZKk4PH.js → chunk-FMBD7UC4-C3o2DXG0.js} +1 -1
  264. package/web-shell/assets/{chunk-ND2GUHAM-qQbC2O-y.js → chunk-ND2GUHAM-CutIi14T.js} +1 -1
  265. package/web-shell/assets/{chunk-QZHKN3VN-B3zeXwsX.js → chunk-QZHKN3VN-DZ0NRHH-.js} +1 -1
  266. package/web-shell/assets/classDiagram-4FO5ZUOK-BNGI2SvC.js +1 -0
  267. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BNGI2SvC.js +1 -0
  268. package/web-shell/assets/{cose-bilkent-S5V4N54A-BKsl1Wvy.js → cose-bilkent-S5V4N54A-B3Dl88UJ.js} +1 -1
  269. package/web-shell/assets/{dagre-BM42HDAG-CuuuLiBK.js → dagre-BM42HDAG-DfTxfF6-.js} +1 -1
  270. package/web-shell/assets/{diagram-2AECGRRQ-CG4_eKiV.js → diagram-2AECGRRQ-CJyH98yO.js} +1 -1
  271. package/web-shell/assets/{diagram-5GNKFQAL-DSFTmZB1.js → diagram-5GNKFQAL-EbOiJNmW.js} +1 -1
  272. package/web-shell/assets/{diagram-KO2AKTUF-BxwlEHn8.js → diagram-KO2AKTUF-AYNqpqqA.js} +1 -1
  273. package/web-shell/assets/{diagram-LMA3HP47-BUAg-jMG.js → diagram-LMA3HP47-BY5VPa4H.js} +1 -1
  274. package/web-shell/assets/{diagram-OG6HWLK6-BFM2jlO5.js → diagram-OG6HWLK6-BhMYAKlk.js} +1 -1
  275. package/web-shell/assets/{erDiagram-TEJ5UH35-CerbFPa2.js → erDiagram-TEJ5UH35-Bp8S_F9l.js} +1 -1
  276. package/web-shell/assets/{flowDiagram-I6XJVG4X-BNeZz5Zz.js → flowDiagram-I6XJVG4X-az3TE9PU.js} +1 -1
  277. package/web-shell/assets/{ganttDiagram-6RSMTGT7-CTOogLaM.js → ganttDiagram-6RSMTGT7-CfkAEpCe.js} +3 -3
  278. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-yxSFkXPj.js → gitGraphDiagram-PVQCEYII-D1QmjMCy.js} +1 -1
  279. package/web-shell/assets/index-C8sDwQUv.js +1130 -0
  280. package/web-shell/assets/index-CaBoc5bb.js +3 -0
  281. package/web-shell/assets/index-Dpg5Wymg.css +5 -0
  282. package/web-shell/assets/{infoDiagram-5YYISTIA-DYdAUevm.js → infoDiagram-5YYISTIA-D3ompLrG.js} +1 -1
  283. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-CNuijZ90.js → ishikawaDiagram-YF4QCWOH-D3MRWp9I.js} +1 -1
  284. package/web-shell/assets/{journeyDiagram-JHISSGLW-BTyiuonf.js → journeyDiagram-JHISSGLW-DVkKCa44.js} +1 -1
  285. package/web-shell/assets/{kanban-definition-UN3LZRKU-DPJo0Uow.js → kanban-definition-UN3LZRKU-BLif81wF.js} +1 -1
  286. package/web-shell/assets/{linear-C8wKCbIC.js → linear-BDU96EGS.js} +1 -1
  287. package/web-shell/assets/{mermaid.core-BV5o7nW5.js → mermaid.core-B-CwC26B.js} +5 -5
  288. package/web-shell/assets/{mindmap-definition-RKZ34NQL-BtLcBmSt.js → mindmap-definition-RKZ34NQL-CS1WOJ4G.js} +1 -1
  289. package/web-shell/assets/{pieDiagram-4H26LBE5-C22J7A9Q.js → pieDiagram-4H26LBE5-BzgtNXVP.js} +1 -1
  290. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DSL10wiL.js → quadrantDiagram-W4KKPZXB-BRfGC8MA.js} +1 -1
  291. package/web-shell/assets/{requirementDiagram-4Y6WPE33-feqh0UpX.js → requirementDiagram-4Y6WPE33-CPBZkC7g.js} +1 -1
  292. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-Z4OVaWaZ.js → sankeyDiagram-5OEKKPKP-nqNvinIk.js} +1 -1
  293. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DIluIcb1.js → sequenceDiagram-3UESZ5HK-CM_Wdjh3.js} +1 -1
  294. package/web-shell/assets/{stateDiagram-AJRCARHV-hlhnuoYH.js → stateDiagram-AJRCARHV-L0y-48VE.js} +1 -1
  295. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BX1DdF8s.js +1 -0
  296. package/web-shell/assets/{timeline-definition-PNZ67QCA-Dqf_CqgL.js → timeline-definition-PNZ67QCA-B_n5rb3A.js} +1 -1
  297. package/web-shell/assets/{vennDiagram-CIIHVFJN-DD2QhaTX.js → vennDiagram-CIIHVFJN-C9NmLV5e.js} +1 -1
  298. package/web-shell/assets/{wardley-L42UT6IY-B2uI85U_.js → wardley-L42UT6IY-CrefK9gt.js} +1 -1
  299. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-U1aJX71g.js → wardleyDiagram-YWT4CUSO-BUccf-bC.js} +1 -1
  300. package/web-shell/assets/{xychartDiagram-2RQKCTM6-C0ODHUee.js → xychartDiagram-2RQKCTM6-DM6ef92l.js} +1 -1
  301. package/web-shell/index.html +2 -2
  302. package/chunks/chunk-524RQP4Q.js +0 -305
  303. package/chunks/chunk-CARU2RR2.js +0 -24
  304. package/chunks/chunk-CU3L64TP.js +0 -93
  305. package/chunks/chunk-OCPBI7J5.js +0 -18
  306. package/chunks/chunk-TEGEBB2I.js +0 -122
  307. package/chunks/chunk-VTU57BWZ.js +0 -30
  308. package/chunks/daemon-status-provider-B2UTZUJ2.js +0 -76
  309. package/chunks/initializer-UTQ7FB7G.js +0 -71
  310. package/chunks/list-DEICDL65.js +0 -74
  311. package/chunks/loadedSettingsAdapter-NOWEGDYJ.js +0 -68
  312. package/chunks/mcp-L5JXEEIA.js +0 -68
  313. package/chunks/nonInteractiveCli-LS2K4X6A.js +0 -124
  314. package/chunks/types-JNKGKUJT.js +0 -12
  315. package/chunks/workspace-providers-status-4KMGETOJ.js +0 -71
  316. package/chunks/workspace-service-RVLBMSMZ.js +0 -80
  317. package/chunks/workspace-skills-status-DS4ZVFTO.js +0 -70
  318. package/web-shell/assets/channel-CLiQD50f.js +0 -1
  319. package/web-shell/assets/classDiagram-4FO5ZUOK-D3sYqow5.js +0 -1
  320. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-D3sYqow5.js +0 -1
  321. package/web-shell/assets/index-D5IWFVk1.css +0 -5
  322. package/web-shell/assets/index-KFJRDhX7.js +0 -783
  323. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BhQMwgpl.js +0 -1
@@ -3,7 +3,10 @@
3
3
  import {
4
4
  formatHistoryGapNotice,
5
5
  indexGapsByChild
6
- } from "./chunk-U2NNEY4R.js";
6
+ } from "./chunk-VT5AWJN4.js";
7
+ import {
8
+ sanitizeTerminalText
9
+ } from "./chunk-MFNKBXYH.js";
7
10
  import {
8
11
  require_undici
9
12
  } from "./chunk-L573Z2QZ.js";
@@ -12,13 +15,18 @@ import {
12
15
  } from "./chunk-YUZI3WAC.js";
13
16
  import {
14
17
  getActiveGoal
15
- } from "./chunk-NVYTCB5Z.js";
18
+ } from "./chunk-SA6QVLWI.js";
19
+ import {
20
+ apiActivityTracker,
21
+ formatVisionBridgeNoticeDisplay,
22
+ isVisionBridgeNoticeDisplay
23
+ } from "./chunk-YYBZUIPL.js";
16
24
  import {
17
25
  ToolNames
18
- } from "./chunk-B5ORX3FG.js";
26
+ } from "./chunk-JGUT3LWZ.js";
19
27
  import {
20
28
  createDebugLogger
21
- } from "./chunk-J4TSQ5EQ.js";
29
+ } from "./chunk-7NFB7LBQ.js";
22
30
  import {
23
31
  init_esbuild_shims
24
32
  } from "./chunk-5O2XNYP6.js";
@@ -27,1435 +35,1516 @@ import {
27
35
  __toESM
28
36
  } from "./chunk-J2S4EL5Y.js";
29
37
 
30
- // packages/cli/src/acp-integration/session/HistoryReplayer.ts
31
- init_esbuild_shims();
32
-
33
- // packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
38
+ // packages/cli/src/services/setup-github.ts
34
39
  init_esbuild_shims();
40
+ var import_undici2 = __toESM(require_undici(), 1);
41
+ import { promises as fsp } from "node:fs";
42
+ import * as path from "node:path";
35
43
 
36
- // packages/cli/src/acp-integration/session/emitters/BaseEmitter.ts
44
+ // packages/cli/src/utils/gitUtils.ts
37
45
  init_esbuild_shims();
38
- var BaseEmitter = class {
39
- constructor(ctx) {
40
- this.ctx = ctx;
46
+ var import_undici = __toESM(require_undici(), 1);
47
+ import * as childProcess from "node:child_process";
48
+ var debugLogger = createDebugLogger("GIT");
49
+ async function runGit(args, opts = {}) {
50
+ return await new Promise((resolve2, reject) => {
51
+ childProcess.execFile(
52
+ "git",
53
+ args,
54
+ {
55
+ encoding: "utf-8",
56
+ ...opts.cwd ? { cwd: opts.cwd } : {}
57
+ },
58
+ (err, stdout) => {
59
+ if (err) {
60
+ reject(err);
61
+ return;
62
+ }
63
+ resolve2(String(stdout ?? "").trim());
64
+ }
65
+ );
66
+ });
67
+ }
68
+ __name(runGit, "runGit");
69
+ var isGitHubRepositoryAsync = /* @__PURE__ */ __name(async (opts = {}) => {
70
+ try {
71
+ const remotes = await runGit(["remote", "-v"], opts);
72
+ return remotes.split("\n").some((line) => {
73
+ const remoteUrl = line.trim().split(/\s+/)[1];
74
+ return remoteUrl ? isGitHubRemoteUrl(remoteUrl) : false;
75
+ });
76
+ } catch (_error) {
77
+ debugLogger.debug(`Failed to get git remote:`, _error);
78
+ return false;
41
79
  }
42
- static {
43
- __name(this, "BaseEmitter");
80
+ }, "isGitHubRepositoryAsync");
81
+ function isGitHubRemoteUrl(remoteUrl) {
82
+ if (remoteUrl.startsWith("git@github.com:")) {
83
+ return true;
44
84
  }
45
- /**
46
- * Converts an ISO timestamp string or epoch ms to epoch ms number.
47
- * Returns undefined if the input is not a valid timestamp.
48
- */
49
- static toEpochMs(ts) {
50
- if (typeof ts === "number") {
51
- return Number.isFinite(ts) ? ts : void 0;
85
+ if (remoteUrl.startsWith("git@")) {
86
+ return false;
87
+ }
88
+ try {
89
+ return new URL(remoteUrl).hostname === "github.com";
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+ __name(isGitHubRemoteUrl, "isGitHubRemoteUrl");
95
+ var getGitRepoRootAsync = /* @__PURE__ */ __name(async (opts = {}) => {
96
+ const gitRepoRoot = await runGit(["rev-parse", "--show-toplevel"], opts);
97
+ if (!gitRepoRoot) {
98
+ throw new Error(`Git repo returned empty value`);
99
+ }
100
+ return gitRepoRoot;
101
+ }, "getGitRepoRootAsync");
102
+ var getLatestGitHubRelease = /* @__PURE__ */ __name(async (proxy) => {
103
+ try {
104
+ const controller = new AbortController();
105
+ const endpoint = `https://api.github.com/repos/QwenLM/qwen-code-action/releases/latest`;
106
+ const response = await fetch(endpoint, {
107
+ method: "GET",
108
+ headers: {
109
+ Accept: "application/vnd.github+json",
110
+ "Content-Type": "application/json",
111
+ "X-GitHub-Api-Version": "2022-11-28"
112
+ },
113
+ dispatcher: proxy ? new import_undici.ProxyAgent(proxy) : void 0,
114
+ signal: AbortSignal.any([AbortSignal.timeout(3e4), controller.signal])
115
+ });
116
+ if (!response.ok) {
117
+ throw new Error(
118
+ `Invalid response code: ${response.status} - ${response.statusText}`
119
+ );
52
120
  }
53
- if (typeof ts === "string") {
54
- const ms = new Date(ts).getTime();
55
- return Number.isFinite(ms) ? ms : void 0;
121
+ const releaseTag = (await response.json()).tag_name;
122
+ if (!releaseTag) {
123
+ throw new Error(`Response did not include tag_name field`);
56
124
  }
57
- return void 0;
125
+ return releaseTag;
126
+ } catch (_error) {
127
+ debugLogger.debug(
128
+ `Failed to determine latest qwen-code-action release:`,
129
+ _error
130
+ );
131
+ throw new Error(
132
+ `Unable to determine the latest qwen-code-action release on GitHub.`
133
+ );
58
134
  }
59
- /**
60
- * Sends a session update to the ACP client.
61
- * If a message rewriter is configured, updates pass through it first
62
- * (original messages are sent as-is, rewritten versions are appended).
63
- */
64
- async sendUpdate(update) {
65
- if (this.ctx.messageRewriter) {
66
- return this.ctx.messageRewriter.interceptUpdate(update);
67
- }
68
- return this.ctx.sendUpdate(update);
135
+ }, "getLatestGitHubRelease");
136
+ async function getGitHubRepoInfoAsync(opts = {}) {
137
+ return parseGitHubRepoInfo(
138
+ await runGit(["remote", "get-url", "origin"], opts)
139
+ );
140
+ }
141
+ __name(getGitHubRepoInfoAsync, "getGitHubRepoInfoAsync");
142
+ function parseGitHubRepoInfo(remoteUrl) {
143
+ let urlToParse = remoteUrl;
144
+ if (remoteUrl.startsWith("git@github.com:")) {
145
+ urlToParse = remoteUrl.replace("git@github.com:", "");
146
+ } else if (remoteUrl.startsWith("git@")) {
147
+ throw new Error(
148
+ `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
149
+ );
69
150
  }
70
- /**
71
- * Gets the session configuration.
72
- */
73
- get config() {
74
- return this.ctx.config;
151
+ let parsedUrl;
152
+ try {
153
+ parsedUrl = new URL(urlToParse, "https://github.com");
154
+ } catch {
155
+ throw new Error(
156
+ `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
157
+ );
75
158
  }
76
- /**
77
- * Gets the session ID.
78
- */
79
- get sessionId() {
80
- return this.ctx.sessionId;
159
+ if (parsedUrl.hostname !== "github.com") {
160
+ throw new Error(
161
+ `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
162
+ );
81
163
  }
82
- };
164
+ const parts = parsedUrl.pathname.split("/").filter((part) => part !== "");
165
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
166
+ throw new Error(
167
+ `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
168
+ );
169
+ }
170
+ return { owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
171
+ }
172
+ __name(parseGitHubRepoInfo, "parseGitHubRepoInfo");
83
173
 
84
- // packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
85
- var MessageEmitter = class extends BaseEmitter {
174
+ // packages/cli/src/services/setup-github.ts
175
+ var debugLogger2 = createDebugLogger("SETUP_GITHUB");
176
+ var GITHUB_WORKFLOW_PATHS = [
177
+ "qwen-dispatch/qwen-dispatch.yml",
178
+ "qwen-assistant/qwen-invoke.yml",
179
+ "issue-triage/qwen-triage.yml",
180
+ "issue-triage/qwen-scheduled-triage.yml",
181
+ "pr-review/qwen-review.yml"
182
+ ];
183
+ var GITIGNORE_ENTRIES = [".qwen/", "gha-creds-*.json"];
184
+ var MAX_WORKFLOW_DOWNLOAD_BYTES = 5 * 1024 * 1024;
185
+ var SetupGithubError = class extends Error {
86
186
  static {
87
- __name(this, "MessageEmitter");
187
+ __name(this, "SetupGithubError");
88
188
  }
89
- /**
90
- * Emits a StopHookLoop event when Stop hooks create a loop.
91
- * This informs the client that Stop hooks have been executed multiple times.
92
- *
93
- * @param iterationCount - The current iteration count
94
- * @param reasons - Array of reasons from each Stop hook execution
95
- * @param stopHookCount - Number of Stop hooks that were executed
96
- */
97
- async emitStopHookLoop(iterationCount, reasons, stopHookCount) {
98
- const activeGoal = getActiveGoal(this.sessionId);
99
- await this.sendUpdate({
100
- sessionUpdate: "agent_message_chunk",
101
- content: { type: "text", text: "" },
102
- _meta: {
103
- stopHookLoop: {
104
- iterationCount,
105
- reasons,
106
- stopHookCount,
107
- ...activeGoal ? {
108
- goal: {
109
- condition: activeGoal.condition,
110
- iterations: activeGoal.iterations,
111
- setAt: activeGoal.setAt,
112
- lastReason: activeGoal.lastReason
113
- }
114
- } : {}
115
- }
116
- }
117
- });
189
+ code;
190
+ status;
191
+ partial;
192
+ partialResult;
193
+ constructor(code, message, status, partialResult) {
194
+ super(message);
195
+ this.name = "SetupGithubError";
196
+ this.code = code;
197
+ this.status = status;
198
+ this.partial = partialResult !== void 0;
199
+ this.partialResult = partialResult;
118
200
  }
119
- async emitGoalTerminal(event) {
120
- await this.sendUpdate({
121
- sessionUpdate: "agent_message_chunk",
122
- content: { type: "text", text: "" },
123
- _meta: {
124
- goalTerminal: event
125
- }
201
+ };
202
+ var nodeFileOps = {
203
+ assertCanWrite() {
204
+ },
205
+ async ensureWorkflowDirectory(gitRepoRoot) {
206
+ await fsp.mkdir(path.join(gitRepoRoot, ".github", "workflows"), {
207
+ recursive: true
126
208
  });
127
- }
128
- async emitGoalStatus(status) {
129
- await this.sendUpdate({
130
- sessionUpdate: "agent_message_chunk",
131
- content: { type: "text", text: "" },
132
- _meta: {
133
- goalStatus: status
209
+ },
210
+ async writeTextFile(gitRepoRoot, relativePath, content) {
211
+ const target = path.join(gitRepoRoot, relativePath);
212
+ await fsp.writeFile(target, content, { mode: 420 });
213
+ return { sizeBytes: Buffer.byteLength(content, "utf8") };
214
+ },
215
+ async readTextFile(gitRepoRoot, relativePath) {
216
+ try {
217
+ return await fsp.readFile(path.join(gitRepoRoot, relativePath), "utf8");
218
+ } catch (error) {
219
+ if (error.code === "ENOENT") {
220
+ return void 0;
134
221
  }
135
- });
222
+ throw error;
223
+ }
136
224
  }
137
- /**
138
- * Emits a user message chunk.
139
- *
140
- * @param text - The user message text content
141
- * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
142
- */
143
- async emitUserMessage(text, timestamp, options = {}) {
144
- const epochMs = BaseEmitter.toEpochMs(timestamp);
145
- const _meta = {
146
- ...epochMs != null ? { timestamp: epochMs } : {},
147
- ...options.source ? { source: options.source } : {}
148
- };
149
- await this.sendUpdate({
150
- sessionUpdate: "user_message_chunk",
151
- content: { type: "text", text },
152
- ...Object.keys(_meta).length > 0 ? { _meta } : {}
153
- });
154
- }
155
- /**
156
- * Emits an agent thought chunk.
157
- *
158
- * @param text - The thought text content
159
- * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
160
- */
161
- async emitAgentThought(text, timestamp, subagentMeta) {
162
- const _meta = this.buildChunkMeta(
163
- BaseEmitter.toEpochMs(timestamp),
164
- subagentMeta
225
+ };
226
+ async function setupGithub(options = {}) {
227
+ const cwd = options.cwd ?? process.cwd();
228
+ const fileOps = options.fileOps ?? nodeFileOps;
229
+ if (!await isGitHubRepositoryAsync({ cwd })) {
230
+ throw new SetupGithubError(
231
+ "github_repository_not_found",
232
+ "Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
233
+ 400
165
234
  );
166
- await this.sendUpdate({
167
- sessionUpdate: "agent_thought_chunk",
168
- content: { type: "text", text },
169
- ..._meta ? { _meta } : {}
170
- });
171
235
  }
172
- /**
173
- * Emits an agent message chunk.
174
- *
175
- * @param text - The agent message text content
176
- * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
177
- */
178
- async emitAgentMessage(text, timestamp, subagentMeta) {
179
- const _meta = this.buildChunkMeta(
180
- BaseEmitter.toEpochMs(timestamp),
181
- subagentMeta
236
+ let gitRepoRoot;
237
+ try {
238
+ gitRepoRoot = await getGitRepoRootAsync({ cwd });
239
+ } catch (error) {
240
+ debugLogger2.debug("Failed to get git repo root:", error);
241
+ throw new SetupGithubError(
242
+ "github_repository_not_found",
243
+ "Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
244
+ 400
182
245
  );
183
- await this.sendUpdate({
184
- sessionUpdate: "agent_message_chunk",
185
- content: { type: "text", text },
186
- ..._meta ? { _meta } : {}
187
- });
188
246
  }
189
- /**
190
- * Emits usage metadata.
191
- */
192
- async emitUsageMetadata(usageMetadata, text = "", durationMs, subagentMeta) {
193
- const usage = {
194
- inputTokens: usageMetadata.promptTokenCount ?? 0,
195
- outputTokens: usageMetadata.candidatesTokenCount ?? 0,
196
- totalTokens: usageMetadata.totalTokenCount ?? 0,
197
- thoughtTokens: usageMetadata.thoughtsTokenCount,
198
- cachedReadTokens: usageMetadata.cachedContentTokenCount
199
- };
200
- const cumulative = this.ctx.cumulativeUsage;
201
- if (cumulative) {
202
- const addFinite = /* @__PURE__ */ __name((total, value) => typeof value === "number" && Number.isFinite(value) ? total + value : total, "addFinite");
203
- cumulative.promptTokens = addFinite(
204
- cumulative.promptTokens,
205
- usage.inputTokens
247
+ if (options.workspaceRoot) {
248
+ const [gitRootReal, workspaceReal] = await Promise.all([
249
+ realpathOrResolve(gitRepoRoot),
250
+ realpathOrResolve(options.workspaceRoot)
251
+ ]);
252
+ if (gitRootReal !== workspaceReal) {
253
+ throw new SetupGithubError(
254
+ "github_git_root_mismatch",
255
+ "The Git repository root must match the daemon workspace root.",
256
+ 400
206
257
  );
207
- cumulative.candidateTokens = addFinite(
208
- cumulative.candidateTokens,
209
- usage.outputTokens
258
+ }
259
+ }
260
+ fileOps.assertCanWrite?.();
261
+ let releaseTag;
262
+ try {
263
+ releaseTag = await getLatestGitHubRelease(options.proxy);
264
+ } catch (error) {
265
+ writeStderrLine(
266
+ `qwen setup-github: failed to determine latest qwen-code-action release: ${error instanceof Error ? error.message : String(error)}`
267
+ );
268
+ debugLogger2.debug(
269
+ "Failed to determine latest qwen-code-action release:",
270
+ error
271
+ );
272
+ throw new SetupGithubError(
273
+ "github_release_lookup_failed",
274
+ "Unable to determine the latest qwen-code-action release on GitHub.",
275
+ 502
276
+ );
277
+ }
278
+ const readmeUrl = `https://github.com/QwenLM/qwen-code-action/blob/${releaseTag}/README.md#quick-start`;
279
+ const secretsUrl = await resolveSecretsUrl(cwd);
280
+ const downloads = await downloadWorkflows({
281
+ releaseTag,
282
+ proxy: options.proxy,
283
+ abortSignal: options.abortSignal,
284
+ fetchImpl: options.fetchImpl ?? fetch
285
+ });
286
+ const result = {
287
+ kind: "github_setup",
288
+ workspaceCwd: options.workspaceRoot ?? gitRepoRoot,
289
+ gitRepoRoot,
290
+ releaseTag,
291
+ readmeUrl,
292
+ ...secretsUrl ? { secretsUrl } : {},
293
+ workflows: [],
294
+ gitignore: { path: ".gitignore", status: "skipped" },
295
+ warnings: []
296
+ };
297
+ result.gitignore = await updateGitignore(gitRepoRoot, fileOps);
298
+ if (result.gitignore.status === "failed") {
299
+ result.warnings.push("Failed to update .gitignore.");
300
+ }
301
+ try {
302
+ await fileOps.ensureWorkflowDirectory(gitRepoRoot);
303
+ for (const workflow of downloads) {
304
+ const relativePath = path.posix.join(
305
+ ".github",
306
+ "workflows",
307
+ path.posix.basename(workflow.sourcePath)
210
308
  );
211
- cumulative.cachedTokens = addFinite(
212
- cumulative.cachedTokens,
213
- usage.cachedReadTokens
309
+ try {
310
+ const write = await fileOps.writeTextFile(
311
+ gitRepoRoot,
312
+ relativePath,
313
+ workflow.content
314
+ );
315
+ result.workflows.push({
316
+ sourcePath: workflow.sourcePath,
317
+ path: relativePath,
318
+ status: "written",
319
+ sizeBytes: write.sizeBytes
320
+ });
321
+ } catch (error) {
322
+ result.partial = true;
323
+ result.workflows.push({
324
+ sourcePath: workflow.sourcePath,
325
+ path: relativePath,
326
+ status: "failed",
327
+ error: error instanceof Error ? error.message : String(error)
328
+ });
329
+ throw new SetupGithubError(
330
+ "github_workflow_write_failed",
331
+ `Unable to write ${relativePath}.`,
332
+ 500,
333
+ result
334
+ );
335
+ }
336
+ }
337
+ } catch (error) {
338
+ if (error instanceof SetupGithubError) throw error;
339
+ throw new SetupGithubError(
340
+ "github_workflow_write_failed",
341
+ "Unable to create .github/workflows.",
342
+ 500,
343
+ result
344
+ );
345
+ }
346
+ return result;
347
+ }
348
+ __name(setupGithub, "setupGithub");
349
+ async function updateGitignore(gitRepoRoot, fileOps = nodeFileOps) {
350
+ try {
351
+ const existingContent = await fileOps.readTextFile(
352
+ gitRepoRoot,
353
+ ".gitignore"
354
+ );
355
+ if (existingContent === void 0) {
356
+ const content = GITIGNORE_ENTRIES.join("\n") + "\n";
357
+ await fileOps.writeTextFile(gitRepoRoot, ".gitignore", content);
358
+ return {
359
+ path: ".gitignore",
360
+ status: "created",
361
+ added: [...GITIGNORE_ENTRIES]
362
+ };
363
+ }
364
+ const missingEntries = GITIGNORE_ENTRIES.filter(
365
+ (entry) => !existingContent.split(/\r?\n/).some((line) => line.split("#")[0].trim() === entry)
366
+ );
367
+ if (missingEntries.length === 0) {
368
+ return { path: ".gitignore", status: "unchanged" };
369
+ }
370
+ const nextContent = existingContent + "\n" + missingEntries.join("\n") + "\n";
371
+ await fileOps.writeTextFile(gitRepoRoot, ".gitignore", nextContent);
372
+ return {
373
+ path: ".gitignore",
374
+ status: "updated",
375
+ added: missingEntries
376
+ };
377
+ } catch (error) {
378
+ debugLogger2.debug("Failed to update .gitignore:", error);
379
+ return {
380
+ path: ".gitignore",
381
+ status: "failed",
382
+ error: error instanceof Error ? error.message : String(error)
383
+ };
384
+ }
385
+ }
386
+ __name(updateGitignore, "updateGitignore");
387
+ async function downloadWorkflows(options) {
388
+ const internalAbort = new AbortController();
389
+ try {
390
+ const dispatcher = options.proxy ? new import_undici2.ProxyAgent(options.proxy) : void 0;
391
+ return await Promise.all(
392
+ GITHUB_WORKFLOW_PATHS.map(async (workflow) => {
393
+ const endpoint = `https://raw.githubusercontent.com/QwenLM/qwen-code-action/refs/tags/${options.releaseTag}/examples/workflows/${workflow}`;
394
+ const response = await options.fetchImpl(endpoint, {
395
+ method: "GET",
396
+ dispatcher,
397
+ signal: AbortSignal.any([
398
+ AbortSignal.timeout(3e4),
399
+ internalAbort.signal,
400
+ ...options.abortSignal ? [options.abortSignal] : []
401
+ ])
402
+ });
403
+ if (!response.ok) {
404
+ throw new Error(
405
+ `Invalid response code downloading ${endpoint}: ${response.status} - ${response.statusText}`
406
+ );
407
+ }
408
+ return {
409
+ sourcePath: workflow,
410
+ content: await readResponseTextWithLimit(response, workflow)
411
+ };
412
+ })
413
+ );
414
+ } catch (error) {
415
+ internalAbort.abort();
416
+ const message = error instanceof Error ? error.message : String(error);
417
+ debugLogger2.debug("Failed to download qwen-code-action workflows:", error);
418
+ throw new SetupGithubError(
419
+ "github_workflow_download_failed",
420
+ `Unable to download qwen-code-action workflows from GitHub. ${message}`,
421
+ 502
422
+ );
423
+ }
424
+ }
425
+ __name(downloadWorkflows, "downloadWorkflows");
426
+ async function readResponseTextWithLimit(response, sourcePath) {
427
+ const contentLength = response.headers.get("content-length");
428
+ if (contentLength !== null) {
429
+ const parsedLength = Number(contentLength);
430
+ if (Number.isFinite(parsedLength) && parsedLength > MAX_WORKFLOW_DOWNLOAD_BYTES) {
431
+ throw new Error(
432
+ `${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
214
433
  );
215
- cumulative.apiTimeMs = addFinite(cumulative.apiTimeMs, durationMs);
216
434
  }
217
- const meta = typeof durationMs === "number" ? { usage, durationMs, ...subagentMeta } : { usage, ...subagentMeta };
218
- await this.sendUpdate({
219
- sessionUpdate: "agent_message_chunk",
220
- content: { type: "text", text },
221
- _meta: meta
222
- });
223
435
  }
224
- /**
225
- * Emits a message chunk based on role and thought flag.
226
- * This is the unified method that handles all message types.
227
- *
228
- * @param text - The message text content
229
- * @param role - Whether this is a user or assistant message
230
- * @param isThought - Whether this is an assistant thought (only applies to assistant role)
231
- * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
232
- */
233
- async emitMessage(text, role, isThought = false, timestamp, subagentMeta) {
234
- if (role === "user") {
235
- return this.emitUserMessage(text, timestamp);
436
+ if (!response.body) return "";
437
+ const reader = response.body.getReader();
438
+ const chunks = [];
439
+ let totalBytes = 0;
440
+ try {
441
+ while (true) {
442
+ const { done, value } = await reader.read();
443
+ if (done) break;
444
+ if (!value) continue;
445
+ totalBytes += value.byteLength;
446
+ if (totalBytes > MAX_WORKFLOW_DOWNLOAD_BYTES) {
447
+ await reader.cancel().catch(() => {
448
+ });
449
+ throw new Error(
450
+ `${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
451
+ );
452
+ }
453
+ chunks.push(value);
236
454
  }
237
- return isThought ? this.emitAgentThought(text, timestamp, subagentMeta) : this.emitAgentMessage(text, timestamp, subagentMeta);
455
+ } finally {
456
+ reader.releaseLock();
238
457
  }
239
- buildChunkMeta(epochMs, subagentMeta) {
240
- const meta = {
241
- ...subagentMeta?.parentToolCallId ? { parentToolCallId: subagentMeta.parentToolCallId } : {},
242
- ...subagentMeta?.subagentType ? { subagentType: subagentMeta.subagentType } : {},
243
- ...epochMs != null ? { timestamp: epochMs } : {}
244
- };
245
- return Object.keys(meta).length > 0 ? meta : void 0;
458
+ const body = new Uint8Array(totalBytes);
459
+ let offset = 0;
460
+ for (const chunk of chunks) {
461
+ body.set(chunk, offset);
462
+ offset += chunk.byteLength;
246
463
  }
247
- };
464
+ return new TextDecoder().decode(body);
465
+ }
466
+ __name(readResponseTextWithLimit, "readResponseTextWithLimit");
467
+ async function resolveSecretsUrl(cwd) {
468
+ try {
469
+ const repoInfo = await getGitHubRepoInfoAsync({ cwd });
470
+ return `https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`;
471
+ } catch {
472
+ return void 0;
473
+ }
474
+ }
475
+ __name(resolveSecretsUrl, "resolveSecretsUrl");
476
+ async function realpathOrResolve(input) {
477
+ try {
478
+ return await fsp.realpath(input);
479
+ } catch {
480
+ return path.resolve(input);
481
+ }
482
+ }
483
+ __name(realpathOrResolve, "realpathOrResolve");
248
484
 
249
- // packages/cli/src/acp-integration/session/emitters/ToolCallEmitter.ts
485
+ // packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
250
486
  init_esbuild_shims();
251
487
 
252
- // packages/cli/src/acp-integration/session/emitters/PlanEmitter.ts
488
+ // packages/cli/src/acp-integration/session/emitters/base-emitter.ts
253
489
  init_esbuild_shims();
254
- var PlanEmitter = class extends BaseEmitter {
255
- static {
256
- __name(this, "PlanEmitter");
490
+ var BaseEmitter = class {
491
+ constructor(ctx) {
492
+ this.ctx = ctx;
257
493
  }
258
- /**
259
- * Emits a plan update with the given todo items.
260
- *
261
- * @param todos - Array of todo items to send as plan entries
262
- */
263
- async emitPlan(todos) {
264
- const entries = todos.map((todo) => ({
265
- content: todo.content,
266
- priority: "medium",
267
- // Default priority since todos don't have priority
268
- status: todo.status
269
- }));
270
- const cumulative = this.ctx.cumulativeUsage;
271
- await this.sendUpdate({
272
- sessionUpdate: "plan",
273
- entries,
274
- ...cumulative ? { _meta: { stats: { ...cumulative } } } : {}
275
- });
494
+ static {
495
+ __name(this, "BaseEmitter");
276
496
  }
277
497
  /**
278
- * Extracts todos from tool result display or args.
279
- * Tries multiple sources in priority order:
280
- * 1. Result display object with type 'todo_list'
281
- * 2. Result display as JSON string
282
- * 3. Args with 'todos' array
283
- *
284
- * @param resultDisplay - The tool result display (object, string, or undefined)
285
- * @param args - The tool call arguments (fallback source)
286
- * @returns Array of todos if found, null otherwise
498
+ * Converts an ISO timestamp string or epoch ms to epoch ms number.
499
+ * Returns undefined if the input is not a valid timestamp.
287
500
  */
288
- extractTodos(resultDisplay, args) {
289
- const fromDisplay = this.extractFromResultDisplay(resultDisplay);
290
- if (fromDisplay) return fromDisplay;
291
- if (args && Array.isArray(args["todos"])) {
292
- return args["todos"];
501
+ static toEpochMs(ts) {
502
+ if (typeof ts === "number") {
503
+ return Number.isFinite(ts) ? ts : void 0;
293
504
  }
294
- return null;
505
+ if (typeof ts === "string") {
506
+ const ms = new Date(ts).getTime();
507
+ return Number.isFinite(ms) ? ms : void 0;
508
+ }
509
+ return void 0;
295
510
  }
296
511
  /**
297
- * Extracts todos from a result display value.
298
- * Handles both object and JSON string formats.
512
+ * Sends a session update to the ACP client.
513
+ * If a message rewriter is configured, updates pass through it first
514
+ * (original messages are sent as-is, rewritten versions are appended).
299
515
  */
300
- extractFromResultDisplay(resultDisplay) {
301
- if (!resultDisplay) return null;
302
- if (typeof resultDisplay === "object") {
303
- const obj = resultDisplay;
304
- if (obj["type"] === "todo_list" && Array.isArray(obj["todos"])) {
305
- return obj["todos"];
306
- }
307
- }
308
- if (typeof resultDisplay === "string") {
309
- try {
310
- const parsed = JSON.parse(resultDisplay);
311
- if (parsed?.["type"] === "todo_list" && Array.isArray(parsed["todos"])) {
312
- return parsed["todos"];
313
- }
314
- } catch {
315
- }
516
+ async sendUpdate(update) {
517
+ if (this.ctx.messageRewriter) {
518
+ return this.ctx.messageRewriter.interceptUpdate(update);
316
519
  }
317
- return null;
520
+ return this.ctx.sendUpdate(update);
318
521
  }
319
- };
320
-
321
- // packages/cli/src/utils/truncatedDiffPreview.ts
322
- init_esbuild_shims();
323
- function buildTruncatedDiffPreviewText(display) {
324
- const fileName = typeof display["fileName"] === "string" ? display["fileName"] : "the edited file";
325
- const fileDiffLength = typeof display["fileDiffLength"] === "number" ? ` Original fileDiff length: ${display["fileDiffLength"]} chars.` : "";
326
- if (display["fileDiffTruncated"] === true) {
327
- return `Full diff omitted from saved session history for ${fileName}.${fileDiffLength}`;
522
+ /**
523
+ * Gets the session ID.
524
+ */
525
+ get sessionId() {
526
+ return this.ctx.sessionId;
328
527
  }
329
- return `Saved session preview only for ${fileName}; full original and new file contents are unavailable.`;
330
- }
331
- __name(buildTruncatedDiffPreviewText, "buildTruncatedDiffPreviewText");
332
-
333
- // packages/cli/src/acp-integration/session/emitters/ToolCallEmitter.ts
334
- var KIND_MAP = {
335
- ["read" /* Read */]: "read",
336
- ["edit" /* Edit */]: "edit",
337
- ["delete" /* Delete */]: "delete",
338
- ["move" /* Move */]: "move",
339
- ["search" /* Search */]: "search",
340
- ["execute" /* Execute */]: "execute",
341
- ["think" /* Think */]: "think",
342
- ["fetch" /* Fetch */]: "fetch",
343
- // ACP defines no 'agent' ToolKind (verified through @agentclientprotocol/sdk
344
- // 0.25.1). The daemon's ClientSideConnection Zod-validates every session/update
345
- // and session/request_permission from the `qwen --acp` child before fanning out
346
- // to SSE clients, so emitting 'agent' is rejected at that hop and the frame is
347
- // dropped. Map the internal Kind.Agent to 'other' on the wire to stay
348
- // protocol-valid; dedicated agent UI is delivered out-of-band (via _meta.toolName)
349
- // in a follow-up rather than via a kind the protocol can't carry.
350
- ["agent" /* Agent */]: "other",
351
- ["other" /* Other */]: "other"
352
528
  };
353
- var ToolCallEmitter = class _ToolCallEmitter extends BaseEmitter {
529
+
530
+ // packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
531
+ var MessageEmitter = class extends BaseEmitter {
354
532
  static {
355
- __name(this, "ToolCallEmitter");
356
- }
357
- planEmitter;
358
- constructor(ctx) {
359
- super(ctx);
360
- this.planEmitter = new PlanEmitter(ctx);
533
+ __name(this, "MessageEmitter");
361
534
  }
362
535
  /**
363
- * Emits a tool call start event.
536
+ * Emits a StopHookLoop event when Stop hooks create a loop.
537
+ * This informs the client that Stop hooks have been executed multiple times.
364
538
  *
365
- * @param params - Tool call start parameters
366
- * @returns true if event was emitted, false if skipped (e.g., TodoWriteTool)
539
+ * @param iterationCount - The current iteration count
540
+ * @param reasons - Array of reasons from each Stop hook execution
541
+ * @param stopHookCount - Number of Stop hooks that were executed
367
542
  */
368
- async emitStart(params) {
369
- if (this.isTodoWriteTool(params.toolName)) {
370
- return false;
371
- }
372
- const { title, locations, kind } = this.resolveToolMetadata(
373
- params.toolName,
374
- params.args
375
- );
376
- const provenance = _ToolCallEmitter.resolveToolProvenance(
377
- params.toolName,
378
- params.subagentMeta
379
- );
543
+ async emitStopHookLoop(iterationCount, reasons, stopHookCount) {
544
+ const activeGoal = getActiveGoal(this.sessionId);
380
545
  await this.sendUpdate({
381
- sessionUpdate: "tool_call",
382
- toolCallId: params.callId,
383
- status: params.status || "pending",
384
- title,
385
- content: [],
386
- locations,
387
- kind,
388
- rawInput: params.args ?? {},
546
+ sessionUpdate: "agent_message_chunk",
547
+ content: { type: "text", text: "" },
389
548
  _meta: {
390
- toolName: params.toolName,
391
- ...params.subagentMeta,
392
- provenance: provenance.provenance,
393
- ...provenance.serverId ? { serverId: provenance.serverId } : {},
394
- ...BaseEmitter.toEpochMs(params.timestamp) != null && {
395
- timestamp: BaseEmitter.toEpochMs(params.timestamp)
549
+ stopHookLoop: {
550
+ iterationCount,
551
+ reasons,
552
+ stopHookCount,
553
+ ...activeGoal ? {
554
+ goal: {
555
+ condition: activeGoal.condition,
556
+ iterations: activeGoal.iterations,
557
+ setAt: activeGoal.setAt,
558
+ lastReason: activeGoal.lastReason
559
+ }
560
+ } : {}
396
561
  }
397
562
  }
398
563
  });
399
- return true;
400
564
  }
401
- /**
402
- * Emits a tool call result event.
403
- * Handles TodoWriteTool specially by routing to plan updates.
404
- *
405
- * @param params - Tool call result parameters
406
- */
407
- async emitResult(params) {
408
- if (this.isTodoWriteTool(params.toolName)) {
409
- const todos = this.planEmitter.extractTodos(
410
- params.resultDisplay,
411
- params.args
412
- );
413
- if (todos && todos.length > 0) {
414
- await this.planEmitter.emitPlan(todos);
415
- } else if (params.args && Array.isArray(params.args["todos"])) {
416
- await this.planEmitter.emitPlan([]);
565
+ async emitGoalTerminal(event) {
566
+ await this.sendUpdate({
567
+ sessionUpdate: "agent_message_chunk",
568
+ content: { type: "text", text: "" },
569
+ _meta: {
570
+ goalTerminal: event
417
571
  }
418
- return;
419
- }
420
- let contentArray = [];
421
- const diffContent = this.extractDiffContent(params.resultDisplay);
422
- if (diffContent) {
423
- contentArray = [diffContent];
424
- } else if (params.error) {
425
- contentArray = [
426
- {
427
- type: "content",
428
- content: { type: "text", text: params.error.message }
429
- }
430
- ];
431
- } else {
432
- contentArray = this.transformPartsToToolCallContent(params.message);
433
- }
434
- const provenance = _ToolCallEmitter.resolveToolProvenance(
435
- params.toolName,
436
- params.subagentMeta
437
- );
438
- const update = {
439
- sessionUpdate: "tool_call_update",
440
- toolCallId: params.callId,
441
- status: params.success ? "completed" : "failed",
442
- content: contentArray,
572
+ });
573
+ }
574
+ async emitGoalStatus(status) {
575
+ await this.sendUpdate({
576
+ sessionUpdate: "agent_message_chunk",
577
+ content: { type: "text", text: "" },
443
578
  _meta: {
444
- toolName: params.toolName,
445
- ...params.subagentMeta,
446
- provenance: provenance.provenance,
447
- ...provenance.serverId ? { serverId: provenance.serverId } : {},
448
- ...BaseEmitter.toEpochMs(params.timestamp) != null && {
449
- timestamp: BaseEmitter.toEpochMs(params.timestamp)
450
- },
451
- ...params.artifacts && params.artifacts.length > 0 ? { artifacts: params.artifacts } : {}
579
+ goalStatus: status
452
580
  }
453
- };
454
- if (params.resultDisplay !== void 0 && !this.isTruncatedSessionDiffDisplay(params.resultDisplay)) {
455
- update["rawOutput"] = params.resultDisplay;
456
- }
457
- await this.sendUpdate(update);
581
+ });
458
582
  }
459
583
  /**
460
- * Emits a tool call error event.
461
- * Use this for explicit error handling when not using emitResult.
584
+ * Emits a user message chunk.
462
585
  *
463
- * @param callId - The tool call ID
464
- * @param toolName - The tool name
465
- * @param error - The error that occurred
466
- * @param subagentMeta - Optional subagent metadata
586
+ * @param text - The user message text content
587
+ * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
467
588
  */
468
- async emitError(callId, toolName, error, subagentMeta) {
469
- const provenance = _ToolCallEmitter.resolveToolProvenance(
470
- toolName,
471
- subagentMeta
472
- );
589
+ async emitUserMessage(text, timestamp, options = {}) {
590
+ const epochMs = BaseEmitter.toEpochMs(timestamp);
591
+ const _meta = {
592
+ ...epochMs != null ? { timestamp: epochMs } : {},
593
+ ...options.source ? { source: options.source } : {}
594
+ };
473
595
  await this.sendUpdate({
474
- sessionUpdate: "tool_call_update",
475
- toolCallId: callId,
476
- status: "failed",
477
- content: [
478
- { type: "content", content: { type: "text", text: error.message } }
479
- ],
480
- _meta: {
481
- toolName,
482
- ...subagentMeta,
483
- provenance: provenance.provenance,
484
- ...provenance.serverId ? { serverId: provenance.serverId } : {}
485
- }
596
+ sessionUpdate: "user_message_chunk",
597
+ content: { type: "text", text },
598
+ ...Object.keys(_meta).length > 0 ? { _meta } : {}
486
599
  });
487
600
  }
488
601
  /**
489
- * Resolve a tool's provenance for UI dispatch on tool_call events.
490
- * The SDK reads `_meta.
491
- * provenance` + `_meta.serverId` to render builtin / MCP-server-badge /
492
- * subagent-block differently. Without this stamping, the SDK falls
493
- * back to string-matching the toolName which can't reliably
494
- * distinguish builtin from subagent.
495
- *
496
- * Resolution rules:
497
- * - `subagentMeta` present → `'subagent'` (a Task tool / Codex
498
- * subagent / etc. wrapping its own tool calls)
499
- * - toolName matches `mcp__<server>__<tool>` → `'mcp'` with
500
- * `serverId: <server>`. Naming convention from
501
- * `packages/core/src/tools/mcp-tool.ts` in the
502
- * `@qwen-code/qwen-code-core` package — mirrors the SDK's same
503
- * heuristic fallback so SDK consumers stay consistent with
504
- * daemon classification.
505
- * - everything else → `'builtin'`
602
+ * Emits an agent thought chunk.
506
603
  *
507
- * Static + pure so it can be unit-tested without an emitter
508
- * instance. Exported via `ToolCallEmitter.resolveToolProvenance`.
604
+ * @param text - The thought text content
605
+ * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
509
606
  */
510
- static resolveToolProvenance(toolName, subagentMeta) {
511
- if (subagentMeta !== void 0) {
512
- return { provenance: "subagent" };
513
- }
514
- if (toolName.startsWith("mcp__")) {
515
- const parts = toolName.split("__");
516
- if (parts.length >= 3 && parts[1] && parts[1].length > 0) {
517
- return { provenance: "mcp", serverId: parts[1] };
518
- }
519
- }
520
- return { provenance: "builtin" };
607
+ async emitAgentThought(text, timestamp, subagentMeta) {
608
+ const _meta = this.buildChunkMeta(
609
+ BaseEmitter.toEpochMs(timestamp),
610
+ subagentMeta
611
+ );
612
+ await this.sendUpdate({
613
+ sessionUpdate: "agent_thought_chunk",
614
+ content: { type: "text", text },
615
+ ..._meta ? { _meta } : {}
616
+ });
521
617
  }
522
- // ==================== Public Utilities ====================
523
618
  /**
524
- * Checks if a tool name is the TodoWriteTool.
525
- * Exposed for external use in components that need to check this.
619
+ * Emits an agent message chunk.
620
+ *
621
+ * @param text - The agent message text content
622
+ * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
526
623
  */
527
- isTodoWriteTool(toolName) {
528
- return toolName === ToolNames.TODO_WRITE;
624
+ async emitAgentMessage(text, timestamp, subagentMeta) {
625
+ const _meta = this.buildChunkMeta(
626
+ BaseEmitter.toEpochMs(timestamp),
627
+ subagentMeta
628
+ );
629
+ await this.sendUpdate({
630
+ sessionUpdate: "agent_message_chunk",
631
+ content: { type: "text", text },
632
+ ..._meta ? { _meta } : {}
633
+ });
529
634
  }
530
- /**
531
- * Checks if a tool name is the ExitPlanModeTool.
532
- */
533
- isExitPlanModeTool(toolName) {
534
- return toolName === ToolNames.EXIT_PLAN_MODE;
635
+ async emitSlashCommandOutput(text, timestamp) {
636
+ const epochMs = BaseEmitter.toEpochMs(timestamp);
637
+ await this.sendUpdate({
638
+ sessionUpdate: "agent_message_chunk",
639
+ content: { type: "text", text },
640
+ _meta: {
641
+ source: "slash_command",
642
+ ...epochMs != null ? { timestamp: epochMs } : {}
643
+ }
644
+ });
535
645
  }
536
646
  /**
537
- * Checks if a tool name is the EnterPlanModeTool.
647
+ * Emits usage metadata.
538
648
  */
539
- isEnterPlanModeTool(toolName) {
540
- return toolName === ToolNames.ENTER_PLAN_MODE;
649
+ async emitUsageMetadata(usageMetadata, text = "", durationMs, subagentMeta) {
650
+ const usage = {
651
+ inputTokens: usageMetadata.promptTokenCount ?? 0,
652
+ outputTokens: usageMetadata.candidatesTokenCount ?? 0,
653
+ totalTokens: usageMetadata.totalTokenCount ?? 0,
654
+ thoughtTokens: usageMetadata.thoughtsTokenCount,
655
+ cachedReadTokens: usageMetadata.cachedContentTokenCount
656
+ };
657
+ const cumulative = this.ctx.cumulativeUsage;
658
+ if (cumulative) {
659
+ const addFinite = /* @__PURE__ */ __name((total, value) => typeof value === "number" && Number.isFinite(value) ? total + value : total, "addFinite");
660
+ cumulative.promptTokens = addFinite(
661
+ cumulative.promptTokens,
662
+ usage.inputTokens
663
+ );
664
+ cumulative.candidateTokens = addFinite(
665
+ cumulative.candidateTokens,
666
+ usage.outputTokens
667
+ );
668
+ cumulative.cachedTokens = addFinite(
669
+ cumulative.cachedTokens,
670
+ usage.cachedReadTokens
671
+ );
672
+ cumulative.apiTimeMs = addFinite(cumulative.apiTimeMs, durationMs);
673
+ }
674
+ let meta;
675
+ if (typeof durationMs === "number") {
676
+ const activity = apiActivityTracker.drain();
677
+ meta = {
678
+ usage,
679
+ durationMs,
680
+ ...activity.errors > 0 ? { apiErrors: activity.errors } : {},
681
+ ...activity.retries > 0 ? { apiRetries: activity.retries } : {},
682
+ ...subagentMeta
683
+ };
684
+ } else {
685
+ meta = { usage, ...subagentMeta };
686
+ }
687
+ await this.sendUpdate({
688
+ sessionUpdate: "agent_message_chunk",
689
+ content: { type: "text", text },
690
+ _meta: meta
691
+ });
541
692
  }
542
693
  /**
543
- * Resolves tool metadata from the registry.
544
- * Falls back to defaults if tool not found or build fails.
694
+ * Emits a message chunk based on role and thought flag.
695
+ * This is the unified method that handles all message types.
545
696
  *
546
- * @param toolName - Name of the tool
547
- * @param args - Tool call arguments (used to build invocation)
697
+ * @param text - The message text content
698
+ * @param role - Whether this is a user or assistant message
699
+ * @param isThought - Whether this is an assistant thought (only applies to assistant role)
700
+ * @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
548
701
  */
549
- resolveToolMetadata(toolName, args) {
550
- const toolRegistry = this.config.getToolRegistry();
551
- const tool = toolRegistry.getTool(toolName);
552
- let title = tool?.displayName ?? toolName;
553
- let locations = [];
554
- let kind = "other";
555
- if (tool && args) {
556
- try {
557
- const invocation = tool.build(args);
558
- title = `${title}: ${invocation.getDescription()}`;
559
- locations = invocation.toolLocations().map((loc) => ({
560
- path: loc.path,
561
- line: loc.line ?? null
562
- }));
563
- kind = this.mapToolKind(tool.kind, toolName);
564
- } catch {
565
- if (typeof args["description"] === "string") {
566
- title = `${title}: ${args["description"]}`;
567
- }
568
- if (tool.kind) {
569
- kind = this.mapToolKind(tool.kind, toolName);
570
- }
571
- }
702
+ async emitMessage(text, role, isThought = false, timestamp, subagentMeta) {
703
+ if (role === "user") {
704
+ return this.emitUserMessage(text, timestamp);
572
705
  }
573
- return { title, locations, kind };
706
+ return isThought ? this.emitAgentThought(text, timestamp, subagentMeta) : this.emitAgentMessage(text, timestamp, subagentMeta);
707
+ }
708
+ buildChunkMeta(epochMs, subagentMeta) {
709
+ const meta = {
710
+ ...subagentMeta?.parentToolCallId ? { parentToolCallId: subagentMeta.parentToolCallId } : {},
711
+ ...subagentMeta?.subagentType ? { subagentType: subagentMeta.subagentType } : {},
712
+ ...epochMs != null ? { timestamp: epochMs } : {}
713
+ };
714
+ return Object.keys(meta).length > 0 ? meta : void 0;
715
+ }
716
+ };
717
+
718
+ // packages/cli/src/acp-integration/session/emitters/PlanEmitter.ts
719
+ init_esbuild_shims();
720
+ var PlanEmitter = class extends BaseEmitter {
721
+ static {
722
+ __name(this, "PlanEmitter");
574
723
  }
575
724
  /**
576
- * Maps core Tool Kind enum to ACP ToolKind string literals.
725
+ * Emits a plan update with the given todo items.
577
726
  *
578
- * @param kind - The core Kind enum value
579
- * @param toolName - Optional tool name to handle special cases like exit_plan_mode
727
+ * @param todos - Array of todo items to send as plan entries
580
728
  */
581
- mapToolKind(kind, toolName) {
582
- if (toolName && (this.isExitPlanModeTool(toolName) || this.isEnterPlanModeTool(toolName))) {
583
- return "switch_mode";
584
- }
585
- return KIND_MAP[kind] ?? "other";
729
+ async emitPlan(todos) {
730
+ const entries = todos.map((todo) => ({
731
+ content: todo.content,
732
+ priority: "medium",
733
+ // Default priority since todos don't have priority
734
+ status: todo.status
735
+ }));
736
+ const cumulative = this.ctx.cumulativeUsage;
737
+ await this.sendUpdate({
738
+ sessionUpdate: "plan",
739
+ entries,
740
+ ...cumulative ? { _meta: { stats: { ...cumulative } } } : {}
741
+ });
586
742
  }
587
- // ==================== Private Helpers ====================
588
743
  /**
589
- * Extracts diff content from resultDisplay if it's a diff type (edit tool result).
590
- * Returns null if not a diff.
744
+ * Extracts todos from tool result display or args.
745
+ * Tries multiple sources in priority order:
746
+ * 1. Result display object with type 'todo_list'
747
+ * 2. Result display as JSON string
748
+ * 3. Args with 'todos' array
749
+ *
750
+ * @param resultDisplay - The tool result display (object, string, or undefined)
751
+ * @param args - The tool call arguments (fallback source)
752
+ * @returns Array of todos if found, null otherwise
591
753
  */
592
- extractDiffContent(resultDisplay) {
593
- if (!resultDisplay || typeof resultDisplay !== "object") return null;
594
- const obj = resultDisplay;
595
- if ("fileName" in obj && "newContent" in obj) {
596
- if (this.isTruncatedSessionDiffDisplay(resultDisplay)) {
597
- return {
598
- type: "content",
599
- content: {
600
- type: "text",
601
- text: buildTruncatedDiffPreviewText(obj)
602
- }
603
- };
604
- }
605
- return {
606
- type: "diff",
607
- path: obj["fileName"],
608
- oldText: obj["originalContent"] ?? "",
609
- newText: obj["newContent"]
610
- };
754
+ extractTodos(resultDisplay, args) {
755
+ const fromDisplay = this.extractFromResultDisplay(resultDisplay);
756
+ if (fromDisplay) return fromDisplay;
757
+ if (args && Array.isArray(args["todos"])) {
758
+ return args["todos"];
611
759
  }
612
760
  return null;
613
761
  }
614
- isTruncatedSessionDiffDisplay(resultDisplay) {
615
- if (!resultDisplay || typeof resultDisplay !== "object") return false;
616
- const obj = resultDisplay;
617
- return obj["truncatedForSession"] === true && "fileName" in obj && "newContent" in obj;
618
- }
619
762
  /**
620
- * Transforms Part[] to ToolCallContent[].
621
- * Extracts text from functionResponse parts and text parts.
763
+ * Extracts todos from a result display value.
764
+ * Handles both object and JSON string formats.
622
765
  */
623
- transformPartsToToolCallContent(parts) {
624
- const result = [];
625
- for (const part of parts) {
626
- if ("text" in part && part.text) {
627
- result.push({
628
- type: "content",
629
- content: { type: "text", text: part.text }
630
- });
766
+ extractFromResultDisplay(resultDisplay) {
767
+ if (!resultDisplay) return null;
768
+ if (typeof resultDisplay === "object") {
769
+ const obj = resultDisplay;
770
+ if (obj["type"] === "todo_list" && Array.isArray(obj["todos"])) {
771
+ return obj["todos"];
631
772
  }
632
- if ("functionResponse" in part && part.functionResponse) {
633
- try {
634
- const resp = part.functionResponse.response;
635
- const outputField = resp["output"];
636
- const errorField = resp["error"];
637
- const responseText = typeof outputField === "string" ? outputField : typeof errorField === "string" ? errorField : JSON.stringify(resp);
638
- result.push({
639
- type: "content",
640
- content: { type: "text", text: responseText }
641
- });
642
- } catch {
773
+ }
774
+ if (typeof resultDisplay === "string") {
775
+ try {
776
+ const parsed = JSON.parse(resultDisplay);
777
+ if (parsed?.["type"] === "todo_list" && Array.isArray(parsed["todos"])) {
778
+ return parsed["todos"];
643
779
  }
780
+ } catch {
644
781
  }
645
782
  }
646
- return result;
783
+ return null;
647
784
  }
648
785
  };
649
786
 
650
- // packages/cli/src/utils/chat-record-tool-call-id.ts
787
+ // packages/cli/src/acp-integration/session/emitters/tool-call-emitter.ts
651
788
  init_esbuild_shims();
652
- function getToolResultCallId(record) {
653
- return getExplicitToolResultCallId(record) ?? record.uuid;
654
- }
655
- __name(getToolResultCallId, "getToolResultCallId");
656
- function getExplicitToolResultCallId(record) {
657
- const resultCallId = record.toolCallResult?.callId;
658
- if (typeof resultCallId === "string" && resultCallId.length > 0) {
659
- return resultCallId;
660
- }
661
- return extractFunctionResponseId(record);
789
+
790
+ // packages/cli/src/acp-integration/session/types.ts
791
+ init_esbuild_shims();
792
+ function hasFullSessionContext(context) {
793
+ return "config" in context;
662
794
  }
663
- __name(getExplicitToolResultCallId, "getExplicitToolResultCallId");
664
- function extractFunctionResponseId(record) {
665
- if (!record.message?.parts) {
666
- return void 0;
667
- }
668
- for (const part of record.message.parts) {
669
- const id = "functionResponse" in part ? part.functionResponse?.id : void 0;
670
- if (typeof id === "string" && id.length > 0) {
671
- return id;
672
- }
795
+ __name(hasFullSessionContext, "hasFullSessionContext");
796
+
797
+ // packages/cli/src/utils/truncatedDiffPreview.ts
798
+ init_esbuild_shims();
799
+ function buildTruncatedDiffPreviewText(display) {
800
+ const fileName = typeof display["fileName"] === "string" ? display["fileName"] : "the edited file";
801
+ const fileDiffLength = typeof display["fileDiffLength"] === "number" ? ` Original fileDiff length: ${display["fileDiffLength"]} chars.` : "";
802
+ if (display["fileDiffTruncated"] === true) {
803
+ return `Full diff omitted from saved session history for ${fileName}.${fileDiffLength}`;
673
804
  }
674
- return void 0;
805
+ return `Saved session preview only for ${fileName}; full original and new file contents are unavailable.`;
675
806
  }
676
- __name(extractFunctionResponseId, "extractFunctionResponseId");
807
+ __name(buildTruncatedDiffPreviewText, "buildTruncatedDiffPreviewText");
677
808
 
678
- // packages/cli/src/acp-integration/session/HistoryReplayer.ts
679
- var MISSING_TOOL_RESULT_MESSAGE = "Tool result missing from saved history; the previous run likely ended before this tool completed.";
680
- var HistoryReplayer = class {
809
+ // packages/cli/src/acp-integration/session/emitters/tool-call-emitter.ts
810
+ var KIND_MAP = {
811
+ ["read" /* Read */]: "read",
812
+ ["edit" /* Edit */]: "edit",
813
+ ["delete" /* Delete */]: "delete",
814
+ ["move" /* Move */]: "move",
815
+ ["search" /* Search */]: "search",
816
+ ["execute" /* Execute */]: "execute",
817
+ ["think" /* Think */]: "think",
818
+ ["fetch" /* Fetch */]: "fetch",
819
+ // ACP defines no 'agent' ToolKind (verified through @agentclientprotocol/sdk
820
+ // 0.25.1). The daemon's ClientSideConnection Zod-validates every session/update
821
+ // and session/request_permission from the `qwen --acp` child before fanning out
822
+ // to SSE clients, so emitting 'agent' is rejected at that hop and the frame is
823
+ // dropped. Map the internal Kind.Agent to 'other' on the wire to stay
824
+ // protocol-valid; dedicated agent UI is delivered out-of-band (via _meta.toolName)
825
+ // in a follow-up rather than via a kind the protocol can't carry.
826
+ ["agent" /* Agent */]: "other",
827
+ ["other" /* Other */]: "other"
828
+ };
829
+ var ToolCallEmitter = class _ToolCallEmitter extends BaseEmitter {
681
830
  static {
682
- __name(this, "HistoryReplayer");
831
+ __name(this, "ToolCallEmitter");
683
832
  }
684
- ctx;
685
- messageEmitter;
686
- toolCallEmitter;
687
- pendingReplayToolCalls = /* @__PURE__ */ new Map();
833
+ planEmitter;
834
+ preparedCallIds = /* @__PURE__ */ new Set();
688
835
  constructor(ctx) {
689
- this.ctx = ctx;
690
- this.messageEmitter = new MessageEmitter(ctx);
691
- this.toolCallEmitter = new ToolCallEmitter(ctx);
836
+ super(ctx);
837
+ this.planEmitter = new PlanEmitter(ctx);
692
838
  }
693
839
  /**
694
- * Replays all chat records from a loaded session.
840
+ * Emits a tool call start event.
695
841
  *
696
- * @param records - Array of chat records to replay
697
- * @param gaps - Optional detected history gaps; a visible notice is emitted
698
- * immediately before each gap's child record so the user sees that an
699
- * earlier segment was lost rather than assuming the halves are contiguous.
842
+ * @param params - Tool call start parameters
843
+ * @returns true if event was emitted, false if skipped (e.g., TodoWriteTool)
700
844
  */
701
- async replay(records, gaps) {
702
- try {
703
- await this.replayPage(records, { finalizeDangling: true, gaps });
704
- } finally {
705
- this.pendingReplayToolCalls.clear();
706
- this.setActiveRecordId(null);
845
+ async emitStart(params) {
846
+ if (this.isTodoWriteTool(params.toolName)) {
847
+ return false;
707
848
  }
708
- }
709
- async replayPage(records, options = {}) {
710
- this.pendingReplayToolCalls.clear();
711
- for (const pending of options.pendingToolCalls ?? []) {
712
- this.pendingReplayToolCalls.set(pending.callId, pending);
849
+ if (params.phase === "preparing" && this.preparedCallIds.has(params.callId)) {
850
+ return false;
713
851
  }
714
- const gapByChildUuid = indexGapsByChild(options.gaps);
715
- let replayError;
716
- try {
717
- for (const record of records) {
718
- const gap = gapByChildUuid.get(record.uuid);
719
- if (gap) {
720
- await this.emitHistoryGapNotice(gap, record.timestamp);
852
+ const { title, locations, kind } = this.resolveToolMetadata(
853
+ params.toolName,
854
+ params.args
855
+ );
856
+ const provenance = _ToolCallEmitter.resolveToolProvenance(
857
+ params.toolName,
858
+ params.subagentMeta
859
+ );
860
+ const updatesPreparedCall = params.phase !== "preparing" && this.preparedCallIds.delete(params.callId);
861
+ const update = {
862
+ toolCallId: params.callId,
863
+ status: params.status || "pending",
864
+ title,
865
+ content: [],
866
+ locations,
867
+ kind,
868
+ rawInput: params.args ?? {},
869
+ _meta: {
870
+ toolName: params.toolName,
871
+ ...params.phase ? { phase: params.phase } : {},
872
+ ...params.subagentMeta,
873
+ provenance: provenance.provenance,
874
+ ...provenance.serverId ? { serverId: provenance.serverId } : {},
875
+ ...BaseEmitter.toEpochMs(params.timestamp) != null && {
876
+ timestamp: BaseEmitter.toEpochMs(params.timestamp)
721
877
  }
722
- await this.replayRecord(record);
723
- }
724
- } catch (error) {
725
- replayError = error;
726
- }
727
- let danglingError;
728
- if (options.finalizeDangling === true) {
729
- try {
730
- await this.failDanglingToolCalls();
731
- } catch (error) {
732
- danglingError = error;
733
878
  }
734
- }
735
- const state = {
736
- pendingToolCalls: options.finalizeDangling === true ? [] : Array.from(this.pendingReplayToolCalls.values())
737
879
  };
738
- this.setActiveRecordId(null);
739
- if (replayError && danglingError) {
740
- throw new AggregateError(
741
- [replayError, danglingError],
742
- "Replay and dangling-cleanup both failed"
743
- );
744
- }
745
- if (replayError) {
746
- throw replayError;
747
- }
748
- if (danglingError) {
749
- throw danglingError;
880
+ await this.sendUpdate(
881
+ updatesPreparedCall ? { sessionUpdate: "tool_call_update", ...update } : { sessionUpdate: "tool_call", ...update }
882
+ );
883
+ if (params.phase === "preparing") {
884
+ this.preparedCallIds.add(params.callId);
750
885
  }
751
- return state;
886
+ return true;
752
887
  }
753
- getPendingToolCalls() {
754
- return Array.from(this.pendingReplayToolCalls.values());
888
+ /**
889
+ * Emits a terminal frame when a prepared tool call is discarded before
890
+ * execution. TodoWrite remains represented exclusively by plan updates.
891
+ *
892
+ * @param callId - ID of the prepared tool call
893
+ * @param toolName - Name of the prepared tool
894
+ */
895
+ async emitPreparationDiscarded(callId, toolName) {
896
+ if (this.isTodoWriteTool(toolName)) return;
897
+ this.preparedCallIds.delete(callId);
898
+ const provenance = _ToolCallEmitter.resolveToolProvenance(toolName);
899
+ await this.sendUpdate({
900
+ sessionUpdate: "tool_call_update",
901
+ toolCallId: callId,
902
+ status: "failed",
903
+ content: [],
904
+ _meta: {
905
+ toolName,
906
+ phase: "preparing",
907
+ preparationDiscarded: true,
908
+ provenance: provenance.provenance,
909
+ ...provenance.serverId ? { serverId: provenance.serverId } : {}
910
+ }
911
+ });
755
912
  }
756
913
  /**
757
- * Replays a single chat record.
914
+ * Emits a tool call result event.
915
+ * Handles TodoWriteTool specially by routing to plan updates.
916
+ *
917
+ * @param params - Tool call result parameters
758
918
  */
759
- async replayRecord(record) {
760
- this.setActiveRecordId(record.uuid, record.timestamp);
761
- try {
762
- switch (record.type) {
763
- case "user":
764
- if (record.subtype === "notification" || record.subtype === "cron") {
765
- const displayText = record.systemPayload?.displayText;
766
- if (displayText) {
767
- await this.messageEmitter.emitUserMessage(
768
- displayText,
769
- record.timestamp,
770
- record.subtype === "cron" ? { source: "cron" } : void 0
771
- );
772
- }
773
- break;
774
- }
775
- if (record.subtype === "mid_turn_user_message") {
776
- const displayText = record.systemPayload?.displayText;
777
- if (displayText) {
778
- await this.messageEmitter.emitUserMessage(
779
- displayText,
780
- record.timestamp
781
- );
782
- } else if (record.message) {
783
- await this.replayContent(
784
- record.message,
785
- "user",
786
- record.timestamp,
787
- record.uuid
788
- );
789
- }
790
- break;
791
- }
792
- if (record.message) {
793
- await this.replayContent(
794
- record.message,
795
- "user",
796
- record.timestamp,
797
- record.uuid
798
- );
799
- }
800
- break;
801
- case "assistant":
802
- if (record.message) {
803
- await this.replayContent(
804
- record.message,
805
- "assistant",
806
- record.timestamp,
807
- record.uuid
808
- );
809
- }
810
- if (record.usageMetadata) {
811
- await this.replayUsageMetadata(record.usageMetadata);
812
- }
813
- break;
814
- case "tool_result":
815
- await this.replayToolResult(record);
816
- break;
817
- case "system":
818
- if (record.subtype === "slash_command") {
819
- await this.replaySlashCommandResult(record);
820
- }
821
- break;
822
- default:
823
- break;
919
+ async emitResult(params) {
920
+ if (this.isTodoWriteTool(params.toolName)) {
921
+ const todos = this.planEmitter.extractTodos(
922
+ params.resultDisplay,
923
+ params.args
924
+ );
925
+ if (todos && todos.length > 0) {
926
+ await this.planEmitter.emitPlan(todos);
927
+ } else if (params.args && Array.isArray(params.args["todos"])) {
928
+ await this.planEmitter.emitPlan([]);
824
929
  }
825
- } finally {
826
- this.setActiveRecordId(null);
930
+ return;
931
+ }
932
+ this.preparedCallIds.delete(params.callId);
933
+ let contentArray = [];
934
+ const diffContent = this.extractDiffContent(params.resultDisplay);
935
+ if (diffContent) {
936
+ contentArray = [diffContent];
937
+ } else if (params.error) {
938
+ contentArray = [
939
+ {
940
+ type: "content",
941
+ content: { type: "text", text: params.error.message }
942
+ }
943
+ ];
944
+ } else {
945
+ contentArray = this.transformPartsToToolCallContent(params.message);
946
+ }
947
+ if (isVisionBridgeNoticeDisplay(params.resultDisplay)) {
948
+ contentArray.unshift({
949
+ type: "content",
950
+ content: {
951
+ type: "text",
952
+ text: sanitizeTerminalText(
953
+ formatVisionBridgeNoticeDisplay(params.resultDisplay)
954
+ )
955
+ }
956
+ });
957
+ }
958
+ const provenance = _ToolCallEmitter.resolveToolProvenance(
959
+ params.toolName,
960
+ params.subagentMeta
961
+ );
962
+ const update = {
963
+ sessionUpdate: "tool_call_update",
964
+ toolCallId: params.callId,
965
+ status: params.success ? "completed" : "failed",
966
+ content: contentArray,
967
+ _meta: {
968
+ toolName: params.toolName,
969
+ ...params.subagentMeta,
970
+ provenance: provenance.provenance,
971
+ ...provenance.serverId ? { serverId: provenance.serverId } : {},
972
+ ...BaseEmitter.toEpochMs(params.timestamp) != null && {
973
+ timestamp: BaseEmitter.toEpochMs(params.timestamp)
974
+ },
975
+ ...params.artifacts && params.artifacts.length > 0 ? { artifacts: params.artifacts } : {}
976
+ }
977
+ };
978
+ if (params.resultDisplay !== void 0 && !this.isTruncatedSessionDiffDisplay(params.resultDisplay)) {
979
+ update["rawOutput"] = params.resultDisplay;
827
980
  }
981
+ await this.sendUpdate(update);
828
982
  }
829
983
  /**
830
- * Emits a visible notice marking a break in the persisted history chain: an
831
- * earlier segment was physically lost (storage interruption) and could not be
832
- * recovered, so the surviving turns below must not be read as contiguous with
833
- * whatever came before the gap. Uses the agent message channel — the same one
834
- * used for other system notices (see MessageEmitter.emitStopHookLoop) — so no
835
- * new session-update kind is needed.
984
+ * Emits a tool call error event.
985
+ * Use this for explicit error handling when not using emitResult.
986
+ *
987
+ * @param callId - The tool call ID
988
+ * @param toolName - The tool name
989
+ * @param error - The error that occurred
990
+ * @param subagentMeta - Optional subagent metadata
836
991
  */
837
- async emitHistoryGapNotice(gap, timestamp) {
838
- await this.messageEmitter.emitAgentMessage(
839
- formatHistoryGapNotice(gap),
840
- timestamp
992
+ async emitError(callId, toolName, error, subagentMeta) {
993
+ this.preparedCallIds.delete(callId);
994
+ const provenance = _ToolCallEmitter.resolveToolProvenance(
995
+ toolName,
996
+ subagentMeta
841
997
  );
998
+ await this.sendUpdate({
999
+ sessionUpdate: "tool_call_update",
1000
+ toolCallId: callId,
1001
+ status: "failed",
1002
+ content: [
1003
+ { type: "content", content: { type: "text", text: error.message } }
1004
+ ],
1005
+ _meta: {
1006
+ toolName,
1007
+ ...subagentMeta,
1008
+ provenance: provenance.provenance,
1009
+ ...provenance.serverId ? { serverId: provenance.serverId } : {}
1010
+ }
1011
+ });
842
1012
  }
843
1013
  /**
844
- * Replays content from a message (user or assistant).
845
- * Handles text parts, thought parts, and function calls.
1014
+ * Resolve a tool's provenance for UI dispatch on tool_call events.
1015
+ * The SDK reads `_meta.
1016
+ * provenance` + `_meta.serverId` to render builtin / MCP-server-badge /
1017
+ * subagent-block differently. Without this stamping, the SDK falls
1018
+ * back to string-matching the toolName which can't reliably
1019
+ * distinguish builtin from subagent.
846
1020
  *
847
- * @param content - The content to replay
848
- * @param role - The role (user or assistant)
849
- * @param timestamp - Optional server-side timestamp from the JSONL record
1021
+ * Resolution rules:
1022
+ * - `subagentMeta` present `'subagent'` (a Task tool / Codex
1023
+ * subagent / etc. wrapping its own tool calls)
1024
+ * - toolName matches `mcp__<server>__<tool>` → `'mcp'` with
1025
+ * `serverId: <server>`. Naming convention from
1026
+ * `packages/core/src/tools/mcp-tool.ts` in the
1027
+ * `@qwen-code/qwen-code-core` package — mirrors the SDK's same
1028
+ * heuristic fallback so SDK consumers stay consistent with
1029
+ * daemon classification.
1030
+ * - everything else → `'builtin'`
1031
+ *
1032
+ * Static + pure so it can be unit-tested without an emitter
1033
+ * instance. Exported via `ToolCallEmitter.resolveToolProvenance`.
850
1034
  */
851
- async replayContent(content, role, timestamp, recordId) {
852
- for (const part of content.parts ?? []) {
853
- if ("text" in part && part.text) {
854
- const isThought = part.thought ?? false;
855
- await this.messageEmitter.emitMessage(
856
- part.text,
857
- role,
858
- isThought,
859
- timestamp
860
- );
861
- }
862
- if ("functionCall" in part && part.functionCall) {
863
- const functionName = part.functionCall.name ?? "";
864
- const sourceCallId = part.functionCall.id;
865
- const callId = sourceCallId ?? `${functionName}-${Date.now()}`;
866
- const emitted = await this.toolCallEmitter.emitStart({
867
- toolName: functionName,
868
- callId,
869
- args: part.functionCall.args,
870
- status: "in_progress",
871
- timestamp
872
- });
873
- if (emitted && role === "assistant" && recordId && sourceCallId) {
874
- this.pendingReplayToolCalls.set(callId, {
875
- callId,
876
- toolName: functionName,
877
- timestamp,
878
- recordId
879
- });
880
- }
1035
+ static resolveToolProvenance(toolName, subagentMeta) {
1036
+ if (subagentMeta !== void 0) {
1037
+ return { provenance: "subagent" };
1038
+ }
1039
+ if (toolName.startsWith("mcp__")) {
1040
+ const parts = toolName.split("__");
1041
+ if (parts.length >= 3 && parts[1] && parts[1].length > 0) {
1042
+ return { provenance: "mcp", serverId: parts[1] };
881
1043
  }
882
1044
  }
1045
+ return { provenance: "builtin" };
883
1046
  }
1047
+ // ==================== Public Utilities ====================
884
1048
  /**
885
- * Replays usage metadata.
886
- * @param usageMetadata - The usage metadata to replay
1049
+ * Checks if a tool name is the TodoWriteTool.
1050
+ * Exposed for external use in components that need to check this.
887
1051
  */
888
- async replayUsageMetadata(usageMetadata) {
889
- await this.messageEmitter.emitUsageMetadata(usageMetadata);
1052
+ isTodoWriteTool(toolName) {
1053
+ return toolName === ToolNames.TODO_WRITE;
890
1054
  }
891
1055
  /**
892
- * Replays a tool result record.
1056
+ * Checks if a tool name is the ExitPlanModeTool.
893
1057
  */
894
- async replayToolResult(record) {
895
- if (!record.message?.parts) {
896
- return;
897
- }
898
- const result = record.toolCallResult;
899
- const callId = getToolResultCallId(record);
900
- this.pendingReplayToolCalls.delete(callId);
901
- const toolName = this.extractToolNameFromRecord(record);
902
- await this.toolCallEmitter.emitResult({
903
- toolName,
904
- callId,
905
- success: !result?.error,
906
- message: record.message.parts,
907
- resultDisplay: result?.resultDisplay,
908
- artifacts: result?.artifacts,
909
- // For TodoWriteTool fallback, try to extract args from the record
910
- // Note: args aren't stored in tool_result records by default
911
- args: void 0,
912
- timestamp: record.timestamp
913
- });
914
- const { resultDisplay } = result ?? {};
915
- if (!!resultDisplay && typeof resultDisplay === "object" && "type" in resultDisplay && resultDisplay.type === "task_execution") {
916
- await this.emitTaskUsageFromResultDisplay(
917
- resultDisplay
918
- );
919
- }
1058
+ isExitPlanModeTool(toolName) {
1059
+ return toolName === ToolNames.EXIT_PLAN_MODE;
920
1060
  }
921
- async failDanglingToolCalls() {
922
- let firstError;
923
- for (const pending of this.pendingReplayToolCalls.values()) {
924
- this.setActiveRecordId(pending.recordId, pending.timestamp);
1061
+ /**
1062
+ * Checks if a tool name is the EnterPlanModeTool.
1063
+ */
1064
+ isEnterPlanModeTool(toolName) {
1065
+ return toolName === ToolNames.ENTER_PLAN_MODE;
1066
+ }
1067
+ /**
1068
+ * Resolves tool metadata from the registry.
1069
+ * Falls back to defaults if tool not found or build fails.
1070
+ *
1071
+ * @param toolName - Name of the tool
1072
+ * @param args - Tool call arguments (used to build invocation)
1073
+ */
1074
+ resolveToolMetadata(toolName, args) {
1075
+ if (!hasFullSessionContext(this.ctx)) {
1076
+ const description = typeof args?.["description"] === "string" ? args["description"].trim() : "";
1077
+ return {
1078
+ title: description ? `${toolName}: ${description}` : toolName,
1079
+ locations: [],
1080
+ kind: "other"
1081
+ };
1082
+ }
1083
+ const toolRegistry = this.ctx.config.getToolRegistry();
1084
+ const tool = toolRegistry.getTool(toolName);
1085
+ let title = tool?.displayName ?? toolName;
1086
+ let locations = [];
1087
+ let kind = "other";
1088
+ if (tool && args) {
925
1089
  try {
926
- await this.toolCallEmitter.emitResult({
927
- toolName: pending.toolName,
928
- callId: pending.callId,
929
- success: false,
930
- message: [],
931
- error: new Error(MISSING_TOOL_RESULT_MESSAGE),
932
- timestamp: pending.timestamp
933
- });
934
- } catch (error) {
935
- firstError ??= error;
936
- } finally {
937
- this.setActiveRecordId(null);
1090
+ const invocation = tool.build(args);
1091
+ title = `${title}: ${invocation.getDescription()}`;
1092
+ locations = invocation.toolLocations().map((loc) => ({
1093
+ path: loc.path,
1094
+ line: loc.line ?? null
1095
+ }));
1096
+ kind = this.mapToolKind(tool.kind, toolName);
1097
+ } catch {
1098
+ if (typeof args["description"] === "string") {
1099
+ title = `${title}: ${args["description"]}`;
1100
+ }
1101
+ if (tool.kind) {
1102
+ kind = this.mapToolKind(tool.kind, toolName);
1103
+ }
938
1104
  }
939
1105
  }
940
- if (firstError) {
941
- throw firstError;
942
- }
1106
+ return { title, locations, kind };
943
1107
  }
944
1108
  /**
945
- * Emits token usage from a AgentResultDisplay execution summary, if present.
1109
+ * Maps core Tool Kind enum to ACP ToolKind string literals.
1110
+ *
1111
+ * @param kind - The core Kind enum value
1112
+ * @param toolName - Optional tool name to handle special cases like exit_plan_mode
946
1113
  */
947
- async emitTaskUsageFromResultDisplay(resultDisplay) {
948
- const summary = resultDisplay.executionSummary;
949
- if (!summary) {
950
- return;
951
- }
952
- const usageMetadata = {};
953
- if (Number.isFinite(summary.inputTokens)) {
954
- usageMetadata.promptTokenCount = summary.inputTokens;
955
- }
956
- if (Number.isFinite(summary.outputTokens)) {
957
- usageMetadata.candidatesTokenCount = summary.outputTokens;
958
- }
959
- if (Number.isFinite(summary.thoughtTokens)) {
960
- usageMetadata.thoughtsTokenCount = summary.thoughtTokens;
961
- }
962
- if (Number.isFinite(summary.cachedTokens)) {
963
- usageMetadata.cachedContentTokenCount = summary.cachedTokens;
964
- }
965
- if (Number.isFinite(summary.totalTokens)) {
966
- usageMetadata.totalTokenCount = summary.totalTokens;
967
- }
968
- if (Object.keys(usageMetadata).length > 0) {
969
- await this.messageEmitter.emitUsageMetadata(usageMetadata);
1114
+ mapToolKind(kind, toolName) {
1115
+ if (toolName && (this.isExitPlanModeTool(toolName) || this.isEnterPlanModeTool(toolName))) {
1116
+ return "switch_mode";
970
1117
  }
1118
+ return KIND_MAP[kind] ?? "other";
971
1119
  }
1120
+ // ==================== Private Helpers ====================
972
1121
  /**
973
- * Replays a slash_command system record by re-emitting its output as an
974
- * agent message chunk. This allows Zed to reconstruct the correct turn
975
- * structure (user → agent) on session resume without polluting model context.
1122
+ * Extracts diff content from resultDisplay if it's a diff type (edit tool result).
1123
+ * Returns null if not a diff.
976
1124
  */
977
- async replaySlashCommandResult(record) {
978
- const payload = record.systemPayload;
979
- if (payload?.phase !== "result" || !payload.outputHistoryItems?.length) {
980
- return;
981
- }
982
- for (const item of payload.outputHistoryItems) {
983
- const text = typeof item["text"] === "string" ? item["text"] : "";
984
- if (text) {
985
- await this.messageEmitter.emitAgentMessage(
986
- text.replace(/\n/g, " \n"),
987
- record.timestamp
988
- );
1125
+ extractDiffContent(resultDisplay) {
1126
+ if (!resultDisplay || typeof resultDisplay !== "object") return null;
1127
+ const obj = resultDisplay;
1128
+ if ("fileName" in obj && "newContent" in obj) {
1129
+ if (this.isTruncatedSessionDiffDisplay(resultDisplay)) {
1130
+ return {
1131
+ type: "content",
1132
+ content: {
1133
+ type: "text",
1134
+ text: buildTruncatedDiffPreviewText(obj)
1135
+ }
1136
+ };
989
1137
  }
1138
+ return {
1139
+ type: "diff",
1140
+ path: obj["fileName"],
1141
+ oldText: obj["originalContent"] ?? "",
1142
+ newText: obj["newContent"]
1143
+ };
990
1144
  }
1145
+ return null;
1146
+ }
1147
+ isTruncatedSessionDiffDisplay(resultDisplay) {
1148
+ if (!resultDisplay || typeof resultDisplay !== "object") return false;
1149
+ const obj = resultDisplay;
1150
+ return obj["truncatedForSession"] === true && "fileName" in obj && "newContent" in obj;
991
1151
  }
992
1152
  /**
993
- * Extracts tool name from a chat record's function response.
1153
+ * Transforms Part[] to ToolCallContent[].
1154
+ * Extracts text from functionResponse parts and text parts.
994
1155
  */
995
- extractToolNameFromRecord(record) {
996
- if (record.message?.parts) {
997
- for (const part of record.message.parts) {
998
- if ("functionResponse" in part && part.functionResponse?.name) {
999
- return part.functionResponse.name;
1156
+ transformPartsToToolCallContent(parts) {
1157
+ const result = [];
1158
+ for (const part of parts) {
1159
+ if ("text" in part && part.text) {
1160
+ result.push({
1161
+ type: "content",
1162
+ content: { type: "text", text: part.text }
1163
+ });
1164
+ }
1165
+ if ("functionResponse" in part && part.functionResponse) {
1166
+ try {
1167
+ const resp = part.functionResponse.response;
1168
+ const outputField = resp["output"];
1169
+ const errorField = resp["error"];
1170
+ const responseText = typeof outputField === "string" ? outputField : typeof errorField === "string" ? errorField : JSON.stringify(resp);
1171
+ result.push({
1172
+ type: "content",
1173
+ content: { type: "text", text: responseText }
1174
+ });
1175
+ } catch {
1000
1176
  }
1001
1177
  }
1002
1178
  }
1003
- return "";
1004
- }
1005
- setActiveRecordId(recordId, timestamp) {
1006
- const context = this.ctx;
1007
- if (typeof context.setActiveRecordId === "function") {
1008
- context.setActiveRecordId(recordId, timestamp);
1009
- }
1179
+ return result;
1010
1180
  }
1011
1181
  };
1012
1182
 
1013
- // packages/cli/src/services/setup-github.ts
1183
+ // packages/cli/src/acp-integration/session/history-replayer.ts
1014
1184
  init_esbuild_shims();
1015
- var import_undici2 = __toESM(require_undici(), 1);
1016
- import { promises as fsp } from "node:fs";
1017
- import * as path from "node:path";
1018
1185
 
1019
- // packages/cli/src/utils/gitUtils.ts
1186
+ // packages/cli/src/utils/chat-record-tool-call-id.ts
1020
1187
  init_esbuild_shims();
1021
- var import_undici = __toESM(require_undici(), 1);
1022
- import * as childProcess from "node:child_process";
1023
- var debugLogger = createDebugLogger("GIT");
1024
- async function runGit(args, opts = {}) {
1025
- return await new Promise((resolve2, reject) => {
1026
- childProcess.execFile(
1027
- "git",
1028
- args,
1029
- {
1030
- encoding: "utf-8",
1031
- ...opts.cwd ? { cwd: opts.cwd } : {}
1032
- },
1033
- (err, stdout) => {
1034
- if (err) {
1035
- reject(err);
1036
- return;
1037
- }
1038
- resolve2(String(stdout ?? "").trim());
1039
- }
1040
- );
1041
- });
1188
+ function getToolResultCallId(record) {
1189
+ return getExplicitToolResultCallId(record) ?? record.uuid;
1042
1190
  }
1043
- __name(runGit, "runGit");
1044
- var isGitHubRepositoryAsync = /* @__PURE__ */ __name(async (opts = {}) => {
1045
- try {
1046
- const remotes = await runGit(["remote", "-v"], opts);
1047
- return remotes.split("\n").some((line) => {
1048
- const remoteUrl = line.trim().split(/\s+/)[1];
1049
- return remoteUrl ? isGitHubRemoteUrl(remoteUrl) : false;
1050
- });
1051
- } catch (_error) {
1052
- debugLogger.debug(`Failed to get git remote:`, _error);
1053
- return false;
1054
- }
1055
- }, "isGitHubRepositoryAsync");
1056
- function isGitHubRemoteUrl(remoteUrl) {
1057
- if (remoteUrl.startsWith("git@github.com:")) {
1058
- return true;
1059
- }
1060
- if (remoteUrl.startsWith("git@")) {
1061
- return false;
1062
- }
1063
- try {
1064
- return new URL(remoteUrl).hostname === "github.com";
1065
- } catch {
1066
- return false;
1191
+ __name(getToolResultCallId, "getToolResultCallId");
1192
+ function getExplicitToolResultCallId(record) {
1193
+ const resultCallId = record.toolCallResult?.callId;
1194
+ if (typeof resultCallId === "string" && resultCallId.length > 0) {
1195
+ return resultCallId;
1067
1196
  }
1197
+ return extractFunctionResponseId(record);
1068
1198
  }
1069
- __name(isGitHubRemoteUrl, "isGitHubRemoteUrl");
1070
- var getGitRepoRootAsync = /* @__PURE__ */ __name(async (opts = {}) => {
1071
- const gitRepoRoot = await runGit(["rev-parse", "--show-toplevel"], opts);
1072
- if (!gitRepoRoot) {
1073
- throw new Error(`Git repo returned empty value`);
1199
+ __name(getExplicitToolResultCallId, "getExplicitToolResultCallId");
1200
+ function extractFunctionResponseId(record) {
1201
+ if (!record.message?.parts) {
1202
+ return void 0;
1074
1203
  }
1075
- return gitRepoRoot;
1076
- }, "getGitRepoRootAsync");
1077
- var getLatestGitHubRelease = /* @__PURE__ */ __name(async (proxy) => {
1078
- try {
1079
- const controller = new AbortController();
1080
- const endpoint = `https://api.github.com/repos/QwenLM/qwen-code-action/releases/latest`;
1081
- const response = await fetch(endpoint, {
1082
- method: "GET",
1083
- headers: {
1084
- Accept: "application/vnd.github+json",
1085
- "Content-Type": "application/json",
1086
- "X-GitHub-Api-Version": "2022-11-28"
1087
- },
1088
- dispatcher: proxy ? new import_undici.ProxyAgent(proxy) : void 0,
1089
- signal: AbortSignal.any([AbortSignal.timeout(3e4), controller.signal])
1090
- });
1091
- if (!response.ok) {
1092
- throw new Error(
1093
- `Invalid response code: ${response.status} - ${response.statusText}`
1094
- );
1095
- }
1096
- const releaseTag = (await response.json()).tag_name;
1097
- if (!releaseTag) {
1098
- throw new Error(`Response did not include tag_name field`);
1204
+ for (const part of record.message.parts) {
1205
+ const id = "functionResponse" in part ? part.functionResponse?.id : void 0;
1206
+ if (typeof id === "string" && id.length > 0) {
1207
+ return id;
1099
1208
  }
1100
- return releaseTag;
1101
- } catch (_error) {
1102
- debugLogger.debug(
1103
- `Failed to determine latest qwen-code-action release:`,
1104
- _error
1105
- );
1106
- throw new Error(
1107
- `Unable to determine the latest qwen-code-action release on GitHub.`
1108
- );
1109
- }
1110
- }, "getLatestGitHubRelease");
1111
- async function getGitHubRepoInfoAsync(opts = {}) {
1112
- return parseGitHubRepoInfo(
1113
- await runGit(["remote", "get-url", "origin"], opts)
1114
- );
1115
- }
1116
- __name(getGitHubRepoInfoAsync, "getGitHubRepoInfoAsync");
1117
- function parseGitHubRepoInfo(remoteUrl) {
1118
- let urlToParse = remoteUrl;
1119
- if (remoteUrl.startsWith("git@github.com:")) {
1120
- urlToParse = remoteUrl.replace("git@github.com:", "");
1121
- } else if (remoteUrl.startsWith("git@")) {
1122
- throw new Error(
1123
- `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
1124
- );
1125
1209
  }
1126
- let parsedUrl;
1127
- try {
1128
- parsedUrl = new URL(urlToParse, "https://github.com");
1129
- } catch {
1130
- throw new Error(
1131
- `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
1132
- );
1133
- }
1134
- if (parsedUrl.hostname !== "github.com") {
1135
- throw new Error(
1136
- `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
1137
- );
1138
- }
1139
- const parts = parsedUrl.pathname.split("/").filter((part) => part !== "");
1140
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
1141
- throw new Error(
1142
- `Owner & repo could not be extracted from remote URL: ${remoteUrl}`
1143
- );
1144
- }
1145
- return { owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
1210
+ return void 0;
1146
1211
  }
1147
- __name(parseGitHubRepoInfo, "parseGitHubRepoInfo");
1212
+ __name(extractFunctionResponseId, "extractFunctionResponseId");
1148
1213
 
1149
- // packages/cli/src/services/setup-github.ts
1150
- var debugLogger2 = createDebugLogger("SETUP_GITHUB");
1151
- var GITHUB_WORKFLOW_PATHS = [
1152
- "qwen-dispatch/qwen-dispatch.yml",
1153
- "qwen-assistant/qwen-invoke.yml",
1154
- "issue-triage/qwen-triage.yml",
1155
- "issue-triage/qwen-scheduled-triage.yml",
1156
- "pr-review/qwen-review.yml"
1157
- ];
1158
- var GITIGNORE_ENTRIES = [".qwen/", "gha-creds-*.json"];
1159
- var MAX_WORKFLOW_DOWNLOAD_BYTES = 5 * 1024 * 1024;
1160
- var SetupGithubError = class extends Error {
1214
+ // packages/cli/src/acp-integration/session/history-replayer.ts
1215
+ var MISSING_TOOL_RESULT_MESSAGE = "Tool result missing from saved history; the previous run likely ended before this tool completed.";
1216
+ var HistoryReplayer = class {
1161
1217
  static {
1162
- __name(this, "SetupGithubError");
1218
+ __name(this, "HistoryReplayer");
1163
1219
  }
1164
- code;
1165
- status;
1166
- partial;
1167
- partialResult;
1168
- constructor(code, message, status, partialResult) {
1169
- super(message);
1170
- this.name = "SetupGithubError";
1171
- this.code = code;
1172
- this.status = status;
1173
- this.partial = partialResult !== void 0;
1174
- this.partialResult = partialResult;
1220
+ ctx;
1221
+ messageEmitter;
1222
+ toolCallEmitter;
1223
+ pendingReplayToolCalls = /* @__PURE__ */ new Map();
1224
+ constructor(ctx) {
1225
+ this.ctx = ctx;
1226
+ this.messageEmitter = new MessageEmitter(ctx);
1227
+ this.toolCallEmitter = new ToolCallEmitter(ctx);
1175
1228
  }
1176
- };
1177
- var nodeFileOps = {
1178
- assertCanWrite() {
1179
- },
1180
- async ensureWorkflowDirectory(gitRepoRoot) {
1181
- await fsp.mkdir(path.join(gitRepoRoot, ".github", "workflows"), {
1182
- recursive: true
1183
- });
1184
- },
1185
- async writeTextFile(gitRepoRoot, relativePath, content) {
1186
- const target = path.join(gitRepoRoot, relativePath);
1187
- await fsp.writeFile(target, content, { mode: 420 });
1188
- return { sizeBytes: Buffer.byteLength(content, "utf8") };
1189
- },
1190
- async readTextFile(gitRepoRoot, relativePath) {
1229
+ /**
1230
+ * Replays all chat records from a loaded session.
1231
+ *
1232
+ * @param records - Array of chat records to replay
1233
+ * @param gaps - Optional detected history gaps; a visible notice is emitted
1234
+ * immediately before each gap's child record so the user sees that an
1235
+ * earlier segment was lost rather than assuming the halves are contiguous.
1236
+ */
1237
+ async replay(records, gaps) {
1238
+ try {
1239
+ await this.replayPage(records, { finalizeDangling: true, gaps });
1240
+ } finally {
1241
+ this.pendingReplayToolCalls.clear();
1242
+ this.setActiveRecordId(null);
1243
+ }
1244
+ }
1245
+ async replayPage(records, options = {}) {
1246
+ this.pendingReplayToolCalls.clear();
1247
+ for (const pending of options.pendingToolCalls ?? []) {
1248
+ this.pendingReplayToolCalls.set(pending.callId, pending);
1249
+ }
1250
+ const gapByChildUuid = indexGapsByChild(options.gaps);
1251
+ let replayError;
1191
1252
  try {
1192
- return await fsp.readFile(path.join(gitRepoRoot, relativePath), "utf8");
1253
+ for (const record of records) {
1254
+ const gap = gapByChildUuid.get(record.uuid);
1255
+ if (gap) {
1256
+ await this.emitHistoryGapNotice(gap, record.timestamp);
1257
+ }
1258
+ await this.replayRecord(record);
1259
+ }
1193
1260
  } catch (error) {
1194
- if (error.code === "ENOENT") {
1195
- return void 0;
1261
+ replayError = error;
1262
+ }
1263
+ let danglingError;
1264
+ if (options.finalizeDangling === true) {
1265
+ try {
1266
+ await this.failDanglingToolCalls();
1267
+ } catch (error) {
1268
+ danglingError = error;
1196
1269
  }
1197
- throw error;
1198
1270
  }
1271
+ const state = {
1272
+ pendingToolCalls: options.finalizeDangling === true ? [] : Array.from(this.pendingReplayToolCalls.values())
1273
+ };
1274
+ this.setActiveRecordId(null);
1275
+ if (replayError && danglingError) {
1276
+ throw new AggregateError(
1277
+ [replayError, danglingError],
1278
+ "Replay and dangling-cleanup both failed"
1279
+ );
1280
+ }
1281
+ if (replayError) {
1282
+ throw replayError;
1283
+ }
1284
+ if (danglingError) {
1285
+ throw danglingError;
1286
+ }
1287
+ return state;
1199
1288
  }
1200
- };
1201
- async function setupGithub(options = {}) {
1202
- const cwd = options.cwd ?? process.cwd();
1203
- const fileOps = options.fileOps ?? nodeFileOps;
1204
- if (!await isGitHubRepositoryAsync({ cwd })) {
1205
- throw new SetupGithubError(
1206
- "github_repository_not_found",
1207
- "Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
1208
- 400
1209
- );
1210
- }
1211
- let gitRepoRoot;
1212
- try {
1213
- gitRepoRoot = await getGitRepoRootAsync({ cwd });
1214
- } catch (error) {
1215
- debugLogger2.debug("Failed to get git repo root:", error);
1216
- throw new SetupGithubError(
1217
- "github_repository_not_found",
1218
- "Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
1219
- 400
1220
- );
1289
+ getPendingToolCalls() {
1290
+ return Array.from(this.pendingReplayToolCalls.values());
1221
1291
  }
1222
- if (options.workspaceRoot) {
1223
- const [gitRootReal, workspaceReal] = await Promise.all([
1224
- realpathOrResolve(gitRepoRoot),
1225
- realpathOrResolve(options.workspaceRoot)
1226
- ]);
1227
- if (gitRootReal !== workspaceReal) {
1228
- throw new SetupGithubError(
1229
- "github_git_root_mismatch",
1230
- "The Git repository root must match the daemon workspace root.",
1231
- 400
1232
- );
1292
+ /**
1293
+ * Replays a single chat record.
1294
+ */
1295
+ async replayRecord(record) {
1296
+ this.setActiveRecordId(record.uuid, record.timestamp);
1297
+ try {
1298
+ switch (record.type) {
1299
+ case "user":
1300
+ if (record.subtype === "notification" || record.subtype === "cron") {
1301
+ const displayText = record.systemPayload?.displayText;
1302
+ if (displayText) {
1303
+ await this.messageEmitter.emitUserMessage(
1304
+ displayText,
1305
+ record.timestamp,
1306
+ record.subtype === "cron" ? { source: "cron" } : void 0
1307
+ );
1308
+ }
1309
+ break;
1310
+ }
1311
+ if (record.subtype === "mid_turn_user_message") {
1312
+ const displayText = record.systemPayload?.displayText;
1313
+ if (displayText) {
1314
+ await this.messageEmitter.emitUserMessage(
1315
+ displayText,
1316
+ record.timestamp
1317
+ );
1318
+ } else if (record.message) {
1319
+ await this.replayContent(
1320
+ record.message,
1321
+ "user",
1322
+ record.timestamp,
1323
+ record.uuid
1324
+ );
1325
+ }
1326
+ break;
1327
+ }
1328
+ if (record.message) {
1329
+ await this.replayContent(
1330
+ record.message,
1331
+ "user",
1332
+ record.timestamp,
1333
+ record.uuid
1334
+ );
1335
+ }
1336
+ break;
1337
+ case "assistant":
1338
+ if (record.message) {
1339
+ await this.replayContent(
1340
+ record.message,
1341
+ "assistant",
1342
+ record.timestamp,
1343
+ record.uuid
1344
+ );
1345
+ }
1346
+ if (record.usageMetadata) {
1347
+ await this.replayUsageMetadata(record.usageMetadata);
1348
+ }
1349
+ break;
1350
+ case "tool_result":
1351
+ await this.replayToolResult(record);
1352
+ break;
1353
+ case "system":
1354
+ if (record.subtype === "slash_command") {
1355
+ await this.replaySlashCommandResult(record);
1356
+ }
1357
+ break;
1358
+ default:
1359
+ break;
1360
+ }
1361
+ } finally {
1362
+ this.setActiveRecordId(null);
1233
1363
  }
1234
1364
  }
1235
- fileOps.assertCanWrite?.();
1236
- let releaseTag;
1237
- try {
1238
- releaseTag = await getLatestGitHubRelease(options.proxy);
1239
- } catch (error) {
1240
- writeStderrLine(
1241
- `qwen setup-github: failed to determine latest qwen-code-action release: ${error instanceof Error ? error.message : String(error)}`
1242
- );
1243
- debugLogger2.debug(
1244
- "Failed to determine latest qwen-code-action release:",
1245
- error
1246
- );
1247
- throw new SetupGithubError(
1248
- "github_release_lookup_failed",
1249
- "Unable to determine the latest qwen-code-action release on GitHub.",
1250
- 502
1365
+ /**
1366
+ * Emits a visible notice marking a break in the persisted history chain: an
1367
+ * earlier segment was physically lost (storage interruption) and could not be
1368
+ * recovered, so the surviving turns below must not be read as contiguous with
1369
+ * whatever came before the gap. Uses the agent message channel — the same one
1370
+ * used for other system notices (see MessageEmitter.emitStopHookLoop) — so no
1371
+ * new session-update kind is needed.
1372
+ */
1373
+ async emitHistoryGapNotice(gap, timestamp) {
1374
+ await this.messageEmitter.emitAgentMessage(
1375
+ formatHistoryGapNotice(gap),
1376
+ timestamp
1251
1377
  );
1252
1378
  }
1253
- const readmeUrl = `https://github.com/QwenLM/qwen-code-action/blob/${releaseTag}/README.md#quick-start`;
1254
- const secretsUrl = await resolveSecretsUrl(cwd);
1255
- const downloads = await downloadWorkflows({
1256
- releaseTag,
1257
- proxy: options.proxy,
1258
- abortSignal: options.abortSignal,
1259
- fetchImpl: options.fetchImpl ?? fetch
1260
- });
1261
- const result = {
1262
- kind: "github_setup",
1263
- workspaceCwd: options.workspaceRoot ?? gitRepoRoot,
1264
- gitRepoRoot,
1265
- releaseTag,
1266
- readmeUrl,
1267
- ...secretsUrl ? { secretsUrl } : {},
1268
- workflows: [],
1269
- gitignore: { path: ".gitignore", status: "skipped" },
1270
- warnings: []
1271
- };
1272
- result.gitignore = await updateGitignore(gitRepoRoot, fileOps);
1273
- if (result.gitignore.status === "failed") {
1274
- result.warnings.push("Failed to update .gitignore.");
1379
+ /**
1380
+ * Replays content from a message (user or assistant).
1381
+ * Handles text parts, thought parts, and function calls.
1382
+ *
1383
+ * @param content - The content to replay
1384
+ * @param role - The role (user or assistant)
1385
+ * @param timestamp - Optional server-side timestamp from the JSONL record
1386
+ */
1387
+ async replayContent(content, role, timestamp, recordId) {
1388
+ for (const part of content.parts ?? []) {
1389
+ if ("text" in part && part.text) {
1390
+ const isThought = part.thought ?? false;
1391
+ await this.messageEmitter.emitMessage(
1392
+ part.text,
1393
+ role,
1394
+ isThought,
1395
+ timestamp
1396
+ );
1397
+ }
1398
+ if ("functionCall" in part && part.functionCall) {
1399
+ const functionName = part.functionCall.name ?? "";
1400
+ const sourceCallId = part.functionCall.id;
1401
+ const callId = sourceCallId ?? `${functionName}-${Date.now()}`;
1402
+ const emitted = await this.toolCallEmitter.emitStart({
1403
+ toolName: functionName,
1404
+ callId,
1405
+ args: part.functionCall.args,
1406
+ status: "in_progress",
1407
+ timestamp
1408
+ });
1409
+ if (emitted && role === "assistant" && recordId && sourceCallId) {
1410
+ this.pendingReplayToolCalls.set(callId, {
1411
+ callId,
1412
+ toolName: functionName,
1413
+ timestamp,
1414
+ recordId
1415
+ });
1416
+ }
1417
+ }
1418
+ }
1275
1419
  }
1276
- try {
1277
- await fileOps.ensureWorkflowDirectory(gitRepoRoot);
1278
- for (const workflow of downloads) {
1279
- const relativePath = path.posix.join(
1280
- ".github",
1281
- "workflows",
1282
- path.posix.basename(workflow.sourcePath)
1420
+ /**
1421
+ * Replays usage metadata.
1422
+ * @param usageMetadata - The usage metadata to replay
1423
+ */
1424
+ async replayUsageMetadata(usageMetadata) {
1425
+ await this.messageEmitter.emitUsageMetadata(usageMetadata);
1426
+ }
1427
+ /**
1428
+ * Replays a tool result record.
1429
+ */
1430
+ async replayToolResult(record) {
1431
+ if (!record.message?.parts) {
1432
+ return;
1433
+ }
1434
+ const result = record.toolCallResult;
1435
+ const callId = getToolResultCallId(record);
1436
+ this.pendingReplayToolCalls.delete(callId);
1437
+ const toolName = this.extractToolNameFromRecord(record);
1438
+ await this.toolCallEmitter.emitResult({
1439
+ toolName,
1440
+ callId,
1441
+ success: !result?.error,
1442
+ message: record.message.parts,
1443
+ resultDisplay: result?.resultDisplay,
1444
+ artifacts: result?.artifacts,
1445
+ // For TodoWriteTool fallback, try to extract args from the record
1446
+ // Note: args aren't stored in tool_result records by default
1447
+ args: void 0,
1448
+ timestamp: record.timestamp
1449
+ });
1450
+ const { resultDisplay } = result ?? {};
1451
+ if (!!resultDisplay && typeof resultDisplay === "object" && "type" in resultDisplay && resultDisplay.type === "task_execution") {
1452
+ await this.emitTaskUsageFromResultDisplay(
1453
+ resultDisplay
1283
1454
  );
1455
+ }
1456
+ }
1457
+ async failDanglingToolCalls() {
1458
+ let firstError;
1459
+ for (const pending of this.pendingReplayToolCalls.values()) {
1460
+ this.setActiveRecordId(pending.recordId, pending.timestamp);
1284
1461
  try {
1285
- const write = await fileOps.writeTextFile(
1286
- gitRepoRoot,
1287
- relativePath,
1288
- workflow.content
1289
- );
1290
- result.workflows.push({
1291
- sourcePath: workflow.sourcePath,
1292
- path: relativePath,
1293
- status: "written",
1294
- sizeBytes: write.sizeBytes
1462
+ await this.toolCallEmitter.emitResult({
1463
+ toolName: pending.toolName,
1464
+ callId: pending.callId,
1465
+ success: false,
1466
+ message: [],
1467
+ error: new Error(MISSING_TOOL_RESULT_MESSAGE),
1468
+ timestamp: pending.timestamp
1295
1469
  });
1296
1470
  } catch (error) {
1297
- result.partial = true;
1298
- result.workflows.push({
1299
- sourcePath: workflow.sourcePath,
1300
- path: relativePath,
1301
- status: "failed",
1302
- error: error instanceof Error ? error.message : String(error)
1303
- });
1304
- throw new SetupGithubError(
1305
- "github_workflow_write_failed",
1306
- `Unable to write ${relativePath}.`,
1307
- 500,
1308
- result
1309
- );
1471
+ firstError ??= error;
1472
+ } finally {
1473
+ this.setActiveRecordId(null);
1310
1474
  }
1311
1475
  }
1312
- } catch (error) {
1313
- if (error instanceof SetupGithubError) throw error;
1314
- throw new SetupGithubError(
1315
- "github_workflow_write_failed",
1316
- "Unable to create .github/workflows.",
1317
- 500,
1318
- result
1319
- );
1476
+ if (firstError) {
1477
+ throw firstError;
1478
+ }
1320
1479
  }
1321
- return result;
1322
- }
1323
- __name(setupGithub, "setupGithub");
1324
- async function updateGitignore(gitRepoRoot, fileOps = nodeFileOps) {
1325
- try {
1326
- const existingContent = await fileOps.readTextFile(
1327
- gitRepoRoot,
1328
- ".gitignore"
1329
- );
1330
- if (existingContent === void 0) {
1331
- const content = GITIGNORE_ENTRIES.join("\n") + "\n";
1332
- await fileOps.writeTextFile(gitRepoRoot, ".gitignore", content);
1333
- return {
1334
- path: ".gitignore",
1335
- status: "created",
1336
- added: [...GITIGNORE_ENTRIES]
1337
- };
1480
+ /**
1481
+ * Emits token usage from a AgentResultDisplay execution summary, if present.
1482
+ */
1483
+ async emitTaskUsageFromResultDisplay(resultDisplay) {
1484
+ const summary = resultDisplay.executionSummary;
1485
+ if (!summary) {
1486
+ return;
1338
1487
  }
1339
- const missingEntries = GITIGNORE_ENTRIES.filter(
1340
- (entry) => !existingContent.split(/\r?\n/).some((line) => line.split("#")[0].trim() === entry)
1341
- );
1342
- if (missingEntries.length === 0) {
1343
- return { path: ".gitignore", status: "unchanged" };
1488
+ const usageMetadata = {};
1489
+ if (Number.isFinite(summary.inputTokens)) {
1490
+ usageMetadata.promptTokenCount = summary.inputTokens;
1344
1491
  }
1345
- const nextContent = existingContent + "\n" + missingEntries.join("\n") + "\n";
1346
- await fileOps.writeTextFile(gitRepoRoot, ".gitignore", nextContent);
1347
- return {
1348
- path: ".gitignore",
1349
- status: "updated",
1350
- added: missingEntries
1351
- };
1352
- } catch (error) {
1353
- debugLogger2.debug("Failed to update .gitignore:", error);
1354
- return {
1355
- path: ".gitignore",
1356
- status: "failed",
1357
- error: error instanceof Error ? error.message : String(error)
1358
- };
1359
- }
1360
- }
1361
- __name(updateGitignore, "updateGitignore");
1362
- async function downloadWorkflows(options) {
1363
- const internalAbort = new AbortController();
1364
- try {
1365
- const dispatcher = options.proxy ? new import_undici2.ProxyAgent(options.proxy) : void 0;
1366
- return await Promise.all(
1367
- GITHUB_WORKFLOW_PATHS.map(async (workflow) => {
1368
- const endpoint = `https://raw.githubusercontent.com/QwenLM/qwen-code-action/refs/tags/${options.releaseTag}/examples/workflows/${workflow}`;
1369
- const response = await options.fetchImpl(endpoint, {
1370
- method: "GET",
1371
- dispatcher,
1372
- signal: AbortSignal.any([
1373
- AbortSignal.timeout(3e4),
1374
- internalAbort.signal,
1375
- ...options.abortSignal ? [options.abortSignal] : []
1376
- ])
1377
- });
1378
- if (!response.ok) {
1379
- throw new Error(
1380
- `Invalid response code downloading ${endpoint}: ${response.status} - ${response.statusText}`
1381
- );
1382
- }
1383
- return {
1384
- sourcePath: workflow,
1385
- content: await readResponseTextWithLimit(response, workflow)
1386
- };
1387
- })
1388
- );
1389
- } catch (error) {
1390
- internalAbort.abort();
1391
- const message = error instanceof Error ? error.message : String(error);
1392
- debugLogger2.debug("Failed to download qwen-code-action workflows:", error);
1393
- throw new SetupGithubError(
1394
- "github_workflow_download_failed",
1395
- `Unable to download qwen-code-action workflows from GitHub. ${message}`,
1396
- 502
1397
- );
1398
- }
1399
- }
1400
- __name(downloadWorkflows, "downloadWorkflows");
1401
- async function readResponseTextWithLimit(response, sourcePath) {
1402
- const contentLength = response.headers.get("content-length");
1403
- if (contentLength !== null) {
1404
- const parsedLength = Number(contentLength);
1405
- if (Number.isFinite(parsedLength) && parsedLength > MAX_WORKFLOW_DOWNLOAD_BYTES) {
1406
- throw new Error(
1407
- `${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
1408
- );
1492
+ if (Number.isFinite(summary.outputTokens)) {
1493
+ usageMetadata.candidatesTokenCount = summary.outputTokens;
1494
+ }
1495
+ if (Number.isFinite(summary.thoughtTokens)) {
1496
+ usageMetadata.thoughtsTokenCount = summary.thoughtTokens;
1497
+ }
1498
+ if (Number.isFinite(summary.cachedTokens)) {
1499
+ usageMetadata.cachedContentTokenCount = summary.cachedTokens;
1500
+ }
1501
+ if (Number.isFinite(summary.totalTokens)) {
1502
+ usageMetadata.totalTokenCount = summary.totalTokens;
1503
+ }
1504
+ if (Object.keys(usageMetadata).length > 0) {
1505
+ await this.messageEmitter.emitUsageMetadata(usageMetadata);
1409
1506
  }
1410
1507
  }
1411
- if (!response.body) return "";
1412
- const reader = response.body.getReader();
1413
- const chunks = [];
1414
- let totalBytes = 0;
1415
- try {
1416
- while (true) {
1417
- const { done, value } = await reader.read();
1418
- if (done) break;
1419
- if (!value) continue;
1420
- totalBytes += value.byteLength;
1421
- if (totalBytes > MAX_WORKFLOW_DOWNLOAD_BYTES) {
1422
- await reader.cancel().catch(() => {
1423
- });
1424
- throw new Error(
1425
- `${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
1508
+ /**
1509
+ * Replays a slash_command system record by re-emitting its output as an
1510
+ * agent message chunk. This allows Zed to reconstruct the correct turn
1511
+ * structure (user → agent) on session resume without polluting model context.
1512
+ */
1513
+ async replaySlashCommandResult(record) {
1514
+ const payload = record.systemPayload;
1515
+ if (payload?.phase !== "result" || !payload.outputHistoryItems?.length) {
1516
+ return;
1517
+ }
1518
+ for (const item of payload.outputHistoryItems) {
1519
+ const text = typeof item["text"] === "string" ? item["text"] : "";
1520
+ if (text) {
1521
+ await this.messageEmitter.emitSlashCommandOutput(
1522
+ text.replace(/\n/g, " \n"),
1523
+ record.timestamp
1426
1524
  );
1427
1525
  }
1428
- chunks.push(value);
1429
1526
  }
1430
- } finally {
1431
- reader.releaseLock();
1432
- }
1433
- const body = new Uint8Array(totalBytes);
1434
- let offset = 0;
1435
- for (const chunk of chunks) {
1436
- body.set(chunk, offset);
1437
- offset += chunk.byteLength;
1438
1527
  }
1439
- return new TextDecoder().decode(body);
1440
- }
1441
- __name(readResponseTextWithLimit, "readResponseTextWithLimit");
1442
- async function resolveSecretsUrl(cwd) {
1443
- try {
1444
- const repoInfo = await getGitHubRepoInfoAsync({ cwd });
1445
- return `https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`;
1446
- } catch {
1447
- return void 0;
1528
+ /**
1529
+ * Extracts tool name from a chat record's function response.
1530
+ */
1531
+ extractToolNameFromRecord(record) {
1532
+ if (record.message?.parts) {
1533
+ for (const part of record.message.parts) {
1534
+ if ("functionResponse" in part && part.functionResponse?.name) {
1535
+ return part.functionResponse.name;
1536
+ }
1537
+ }
1538
+ }
1539
+ return "";
1448
1540
  }
1449
- }
1450
- __name(resolveSecretsUrl, "resolveSecretsUrl");
1451
- async function realpathOrResolve(input) {
1452
- try {
1453
- return await fsp.realpath(input);
1454
- } catch {
1455
- return path.resolve(input);
1541
+ setActiveRecordId(recordId, timestamp) {
1542
+ const context = this.ctx;
1543
+ if (typeof context.setActiveRecordId === "function") {
1544
+ context.setActiveRecordId(recordId, timestamp);
1545
+ }
1456
1546
  }
1457
- }
1458
- __name(realpathOrResolve, "realpathOrResolve");
1547
+ };
1459
1548
 
1460
1549
  // packages/cli/src/ui/utils/export/collect.ts
1461
1550
  init_esbuild_shims();
@@ -1646,7 +1735,7 @@ async function extractMetadata(conversation, config) {
1646
1735
  const gitBranch = firstRecord?.gitBranch;
1647
1736
  let gitRepo;
1648
1737
  if (cwd) {
1649
- const { getGitRepoName } = await import("./src-G5UPLZSZ.js");
1738
+ const { getGitRepoName } = await import("./src-7FITDSVC.js");
1650
1739
  gitRepo = getGitRepoName(cwd);
1651
1740
  }
1652
1741
  let model;
@@ -1961,7 +2050,19 @@ function buildToolCallMessageFromResult(record, config) {
1961
2050
  const rawInput = normalizeRawInput(
1962
2051
  functionCallArgs ?? toolCallResult?.args
1963
2052
  );
1964
- const content = extractDiffContent(toolCallResult?.resultDisplay) ?? transformPartsToToolCallContent(record.message?.parts ?? []);
2053
+ const resultContent = extractDiffContent(toolCallResult?.resultDisplay) ?? transformPartsToToolCallContent(record.message?.parts ?? []);
2054
+ const content = isVisionBridgeNoticeDisplay(toolCallResult?.resultDisplay) ? [
2055
+ {
2056
+ type: "content",
2057
+ content: {
2058
+ type: "text",
2059
+ text: sanitizeTerminalText(
2060
+ formatVisionBridgeNoticeDisplay(toolCallResult.resultDisplay)
2061
+ )
2062
+ }
2063
+ },
2064
+ ...resultContent
2065
+ ] : resultContent;
1965
2066
  return {
1966
2067
  uuid: record.uuid,
1967
2068
  parentUuid: record.parentUuid,
@@ -2491,7 +2592,7 @@ export {
2491
2592
  };
2492
2593
  /**
2493
2594
  * @license
2494
- * Copyright 2025 Qwen
2595
+ * Copyright 2025 Google LLC
2495
2596
  * SPDX-License-Identifier: Apache-2.0
2496
2597
  */
2497
2598
  /**
@@ -2501,7 +2602,7 @@ export {
2501
2602
  */
2502
2603
  /**
2503
2604
  * @license
2504
- * Copyright 2025 Google LLC
2605
+ * Copyright 2025 Qwen
2505
2606
  * SPDX-License-Identifier: Apache-2.0
2506
2607
  */
2507
2608
  /**