nolo-cli 0.212.0-alpha.1 → 0.222.0-alpha.2

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 (263) hide show
  1. package/README.md +3 -2
  2. package/{_executeModel-KE6MBNUJ.js → _executeModel-4GGQQHOB.js} +111 -82
  3. package/{accountResolver-4LKIHEKL.js → accountResolver-EK2S4PWF.js} +6 -6
  4. package/{action-DF5UYSJF.js → action-CK64LS6S.js} +12 -11
  5. package/{addContentAction-N2YC5IL3.js → addContentAction-GDHDOF4U.js} +18 -17
  6. package/{addDialogAgentAction-3NKVQCEC.js → addDialogAgentAction-CAKRU3ZT.js} +21 -20
  7. package/{agentDeleteCommand-NWK7VU4N.js → agentDeleteCommand-BPV2XZL7.js} +128 -119
  8. package/{agentEmailCommands-UZ4DHEX2.js → agentEmailCommands-EJOS75YL.js} +132 -124
  9. package/{agentFavoriteCommands-EDK743YA.js → agentFavoriteCommands-2LKGKSUD.js} +7 -8
  10. package/{agentGrantCommands-RQWK2TEV.js → agentGrantCommands-QQV4N4JW.js} +5 -5
  11. package/{agentListCommands-HE7L2A6L.js → agentListCommands-HIZODBF3.js} +196 -124
  12. package/{agentMachineCommands-6TE4MBMP.js → agentMachineCommands-ZMQ2CY7Y.js} +62 -53
  13. package/{agentProviderCommands-Q5O33MED.js → agentProviderCommands-WKJOAWBP.js} +5 -5
  14. package/{agentPullCommand-6EM3URVK.js → agentPullCommand-XN6ZPBUW.js} +7 -7
  15. package/agentRecordCommands-OMXVDLDX.js +176 -0
  16. package/agentRunCommand-CB34YWMV.js +206 -0
  17. package/{agentRunControl-OI3LOENH.js → agentRunControl-JNONH3RP.js} +30 -15
  18. package/{agentRuntimeLocal-HL526DNQ.js → agentRuntimeLocal-QZPFWMLG.js} +52 -45
  19. package/{agentSlice-TMYP2UEA.js → agentSlice-RD2CDU7T.js} +20 -19
  20. package/{appDeleteCommands-ZERW4AXY.js → appDeleteCommands-QCCFDIEQ.js} +61 -52
  21. package/{appDeployCommands-DUWSMYHL.js → appDeployCommands-V2T7TNMQ.js} +61 -52
  22. package/{appDeployStatusCommands-N2KIE4BF.js → appDeployStatusCommands-6PGH7ZCG.js} +61 -52
  23. package/{appGetCommands-YVR4NPVY.js → appGetCommands-3L7WWJA6.js} +61 -52
  24. package/{appListCommands-JS6HOHZS.js → appListCommands-ENJCK4PO.js} +61 -52
  25. package/{authCommand-FE2C3ZUM.js → authCommand-6V47SYGK.js} +29 -28
  26. package/{authCommands-HV3EAU3V.js → authCommands-GAJ3NIXO.js} +14 -13
  27. package/{builtinSkillRegistry-3YOIGEF6.js → builtinSkillRegistry-2ECA42FC.js} +11 -13
  28. package/{chromeCommands-REEAH4SX.js → chromeCommands-G5UDKVSV.js} +3 -3
  29. package/{chromeOpsCommands-HEN5AX2J.js → chromeOpsCommands-YKX2QX3A.js} +20 -10
  30. package/{chunk-DM6UMOEL.js → chunk-27PCUMJI.js} +2 -2
  31. package/chunk-2CI3ZFWD.js +97 -0
  32. package/{chunk-HIRC32DV.js → chunk-2GJKRKI6.js} +543 -29
  33. package/{chunk-SM3NSZJV.js → chunk-2UCBGYO4.js} +20 -1
  34. package/{chunk-PYRUW7XN.js → chunk-2VUZSVAS.js} +136 -54
  35. package/chunk-2YGBI2XB.js +30 -0
  36. package/chunk-2ZKFCIJJ.js +13 -0
  37. package/{chunk-CJRONOYL.js → chunk-34J5K2YA.js} +19 -75
  38. package/{chunk-LOEO7LRC.js → chunk-3IK6ILVW.js} +4 -1
  39. package/{chunk-CTPXME7U.js → chunk-3LMUPOHG.js} +29 -6
  40. package/chunk-44X52WOJ.js +1621 -0
  41. package/{chunk-6VLJ6YA7.js → chunk-4DESLZTK.js} +9 -9
  42. package/{chunk-MYOH447Z.js → chunk-4JVNURX4.js} +1 -1
  43. package/chunk-5CHGAKRT.js +364 -0
  44. package/{chunk-YDH3FDFQ.js → chunk-5FUN7EG4.js} +1 -1
  45. package/{chunk-LKDF3ATR.js → chunk-5RKUPJYL.js} +1 -1
  46. package/{chunk-ZC3CMUKU.js → chunk-6OVNSQHN.js} +84 -2
  47. package/{chunk-IF45XG7K.js → chunk-6WU5572L.js} +40 -24
  48. package/{chunk-OHNELDTO.js → chunk-7EEZJ6BS.js} +11 -152
  49. package/{chunk-Y6ORVLSS.js → chunk-7ET2U67U.js} +1 -1
  50. package/{chunk-G7MLNNEX.js → chunk-7JLY4WYF.js} +5 -5
  51. package/{chunk-P5L7CXOC.js → chunk-7Z6EW7AH.js} +3 -2
  52. package/{chunk-IIQ6JGRI.js → chunk-AD5EWDFD.js} +2 -2
  53. package/{chunk-WXYLV624.js → chunk-AIHYNKGV.js} +4 -4
  54. package/{chunk-HPFCI4SK.js → chunk-AK5O76CC.js} +3 -3
  55. package/{chunk-Y63ZSOR6.js → chunk-AL3TYDIF.js} +1 -1
  56. package/{chunk-PJNFNEJE.js → chunk-AMEOV5VZ.js} +59 -13
  57. package/chunk-AN74ON4G.js +675 -0
  58. package/{chunk-HRMJUN5J.js → chunk-AQZ6BMRK.js} +1 -1
  59. package/{chunk-CQ46IRNN.js → chunk-B4GOW4MT.js} +445 -201
  60. package/{chunk-4KB6WQUL.js → chunk-BGP2EDFG.js} +1 -1
  61. package/{chunk-NBTY7YJF.js → chunk-BR3GT6MR.js} +1 -0
  62. package/{chunk-7RFNTILS.js → chunk-BUDVRYES.js} +2 -2
  63. package/{chunk-D2MDMY2M.js → chunk-BV3FLP2L.js} +47 -1
  64. package/{chunk-25JZEIXN.js → chunk-C4VAR65S.js} +92 -150
  65. package/{chunk-AVTVGMDY.js → chunk-C4WTX5TU.js} +2 -2
  66. package/{chunk-ZL5RAEEB.js → chunk-CIRB724Q.js} +1 -1
  67. package/{chunk-F6UUQOG6.js → chunk-CKT6QUE2.js} +4 -4
  68. package/{chunk-M6RL5JCD.js → chunk-CMZ3Z5PT.js} +14 -14
  69. package/{secretCommands-4QS3HCA2.js → chunk-D726I5AZ.js} +191 -16
  70. package/{chunk-INSBKSBB.js → chunk-DNKTQ3IN.js} +4 -4
  71. package/chunk-DVQH4BTR.js +1137 -0
  72. package/{chunk-SOHTHFV2.js → chunk-DWX2HEVC.js} +91 -132
  73. package/{chunk-NOMIGOST.js → chunk-EIAI3PPH.js} +116 -124
  74. package/chunk-EUCFKKDM.js +247 -0
  75. package/chunk-EWQ3HGNZ.js +90 -0
  76. package/{chunk-RPQXPZZF.js → chunk-F3GRJO4Z.js} +908 -13
  77. package/{chunk-GSQNH63I.js → chunk-FIYSMXHY.js} +3 -19
  78. package/{chunk-KSTEXKL6.js → chunk-FJ23FZXF.js} +76 -31
  79. package/{chunk-INN4LTAJ.js → chunk-FJK526XH.js} +2 -2
  80. package/chunk-FS2XP5D7.js +142 -0
  81. package/{chunk-QT6PKHYJ.js → chunk-FYF2TONJ.js} +5 -5
  82. package/{chunk-OXME3PEE.js → chunk-GBWGPTA7.js} +2 -1
  83. package/{chunk-RU4SD66C.js → chunk-GYVCJABP.js} +30 -254
  84. package/{chunk-YUXQAVRD.js → chunk-HOCHUZQ7.js} +257 -90
  85. package/{chunk-Y2WUYX4I.js → chunk-HXER7YJF.js} +4 -4
  86. package/chunk-HYIETXA6.js +99 -0
  87. package/{chunk-H2TJUNCH.js → chunk-I4EADJ4P.js} +6 -6
  88. package/{chunk-TCZHTTUU.js → chunk-I7WBYGBP.js} +8 -8
  89. package/{chunk-5RYBGBQH.js → chunk-IILMY4W4.js} +14 -9
  90. package/chunk-JA3ICVJY.js +271 -0
  91. package/{chunk-ZRSNONOB.js → chunk-JWXITTQB.js} +8 -3
  92. package/{chunk-JNKSDZFS.js → chunk-KEN3N2TN.js} +310 -66
  93. package/{chunk-LEC5ALIN.js → chunk-KEPOVMF4.js} +1 -1
  94. package/{chunk-UHJGXRJ4.js → chunk-KQSYAIZR.js} +3 -3
  95. package/{chunk-4VJAC32I.js → chunk-L5I543VB.js} +3 -2
  96. package/chunk-LKA64WH4.js +227 -0
  97. package/{chunk-6OQUPIQF.js → chunk-LVL6GFCM.js} +4 -4
  98. package/{chunk-UTV2AYEG.js → chunk-MAIITKBT.js} +2 -2
  99. package/chunk-MBG66ZBH.js +226 -0
  100. package/{chunk-YUA6OAH5.js → chunk-MDLS23JK.js} +2 -2
  101. package/{chunk-VL5KOY2C.js → chunk-MK2Q7ZJD.js} +67 -5
  102. package/{chunk-B7NJ3T4A.js → chunk-MS4NZNKI.js} +10 -10
  103. package/{chunk-3AKFVZ2I.js → chunk-MYHNBEXD.js} +3 -3
  104. package/{chunk-VZKUKCA6.js → chunk-MZLE5HQ3.js} +3673 -241
  105. package/{chunk-AZMYUSVF.js → chunk-N5Z2V73W.js} +1 -1
  106. package/chunk-NIZ2BXFD.js +526 -0
  107. package/{chunk-YPD3VJZW.js → chunk-NWP2OYOX.js} +20 -20
  108. package/{chunk-3FOQHGHN.js → chunk-O6J3OTOP.js} +4 -4
  109. package/{chunk-3RQVYR4B.js → chunk-OB6DVWPF.js} +197 -65
  110. package/{chunk-YWSEPJ7C.js → chunk-OBWT45ZU.js} +3 -3
  111. package/{chunk-5AQR3DGV.js → chunk-ODJ7BVSI.js} +80 -11
  112. package/{chunk-XJWWBO34.js → chunk-OJKUU3LP.js} +5 -7
  113. package/{chunk-GNDJKC4B.js → chunk-ON37YXGX.js} +29 -29
  114. package/{chunk-D5RCRX4A.js → chunk-P5ZJRTCH.js} +9415 -5169
  115. package/{chunk-6XIE5ZGI.js → chunk-PDCV7P5W.js} +1 -1
  116. package/{chunk-EZFQ6XRV.js → chunk-PJCIP64Z.js} +861 -59
  117. package/{chunk-GPO7FCEZ.js → chunk-PJVKMGPZ.js} +1222 -1719
  118. package/{chunk-CKAHAVMK.js → chunk-PPJYZRSQ.js} +40 -26
  119. package/chunk-QHEN7NZ7.js +46 -0
  120. package/{chunk-4KH4J3G5.js → chunk-QNWG52I4.js} +19 -7
  121. package/{chunk-YGILMZ7N.js → chunk-QVBXJNZ2.js} +1 -1
  122. package/chunk-RDH5CQ4T.js +1382 -0
  123. package/{chunk-C6KNNXWU.js → chunk-RGQ4UUWZ.js} +25 -2
  124. package/{chunk-OWYYDMGJ.js → chunk-S2NTVGA2.js} +4 -2
  125. package/{chunk-KC4XDTT3.js → chunk-SOF4OWDM.js} +6 -4
  126. package/{chunk-IAPCBNEE.js → chunk-SQYIK6HO.js} +1 -1
  127. package/{chunk-22CIEBYO.js → chunk-SVT62ULE.js} +50 -48
  128. package/{chunk-KIM6CLW5.js → chunk-SY3C4SQ7.js} +42 -15
  129. package/{chunk-JY5NGFFH.js → chunk-SYTHYBMI.js} +5 -5
  130. package/{chunk-6NSSLV67.js → chunk-T6DXNI6Z.js} +52 -50
  131. package/chunk-TLE77UJ5.js +230 -0
  132. package/{chunk-4VNS5WPM.js → chunk-U67V476Y.js} +1 -8
  133. package/{chunk-F7CI2ZC5.js → chunk-UAIHKQIA.js} +2 -1
  134. package/{chunk-MMNYUVLW.js → chunk-UB3D4TRQ.js} +11 -77
  135. package/{chunk-WPORQFYN.js → chunk-UVLYNWIE.js} +2 -2
  136. package/{chunk-YOIW77S2.js → chunk-UYOICCYG.js} +29 -19
  137. package/{chunk-4I7ERB7F.js → chunk-VREDDN25.js} +1 -1
  138. package/{chunk-JIT6FSXJ.js → chunk-WAE3GFCJ.js} +3 -3
  139. package/{chunk-IJXYFTV7.js → chunk-WI67URFP.js} +20 -20
  140. package/{chunk-7OCO4TDT.js → chunk-WJVYAO6P.js} +1 -1
  141. package/{chunk-T43B7LB2.js → chunk-WKGQSYTB.js} +1 -1
  142. package/{chunk-YJMRHHGY.js → chunk-WUUH4LWZ.js} +60 -30
  143. package/{chunk-PDGKOTO7.js → chunk-X7UF53VA.js} +33 -22
  144. package/{chunk-TY3TIUJK.js → chunk-XI2BWJM5.js} +65 -0
  145. package/{chunk-OOF4UPC5.js → chunk-XWVCYUUU.js} +16 -159
  146. package/{chunk-J5JTMQIA.js → chunk-YFLVRVYK.js} +1 -1
  147. package/{chunk-EYLGAEB4.js → chunk-YGMBIXCF.js} +132 -0
  148. package/chunk-YL3EFYL3.js +199 -0
  149. package/{chunk-MMA3LZ3W.js → chunk-YNIYE47Z.js} +1 -1
  150. package/{chunk-W4S457LU.js → chunk-YU4ZPWVF.js} +41 -5
  151. package/{chunk-NJR3WKVP.js → chunk-YWWDC3O7.js} +281 -159
  152. package/chunk-YZMNK53S.js +68 -0
  153. package/{chunk-FHU44LN3.js → chunk-Z2PITYZN.js} +2179 -552
  154. package/{chunk-FY7F4Q6X.js → chunk-Z4PCXPUE.js} +2 -2
  155. package/{cliAgentConfigHelpers-WJBZQTLW.js → cliAgentConfigHelpers-TKYL43MT.js} +15 -16
  156. package/{cliAuthorityBrokerClient-PM3U6Q6J.js → cliAuthorityBrokerClient-XHH7MH2P.js} +1 -1
  157. package/{cliAuthorityStoreDriver-TKLXNNTG.js → cliAuthorityStoreDriver-6FE3BJNB.js} +1 -1
  158. package/{cliEnvHelpers-BVYTMUPJ.js → cliEnvHelpers-VPSJ4XWC.js} +3 -3
  159. package/{cliExecutor-BWBF2MTI.js → cliExecutor-4MAX3PZL.js} +2 -2
  160. package/cliLocalAgentRecordReader-MHHD4T7R.js +90 -0
  161. package/{commandRegistry-VVMTPFAX.js → commandRegistry-5DA5UP7S.js} +106 -98
  162. package/{contentThunks-N52O67MI.js → contentThunks-5GUQ7FWJ.js} +47 -43
  163. package/{cooldownCommands-YXLWOXWR.js → cooldownCommands-X7P5M2V5.js} +5 -4
  164. package/{createAgentAutomationAction-ZC57YRSV.js → createAgentAutomationAction-UCLTHQRI.js} +51 -49
  165. package/{createDialogAction-VQW5WDYX.js → createDialogAction-UPN774AF.js} +46 -48
  166. package/{createPageAction-M5VDA7SC.js → createPageAction-JRQ2TA4O.js} +50 -48
  167. package/{cursorProvider-YGGNMVWX.js → cursorProvider-2RACMEMF.js} +1 -1
  168. package/{db-HUCI5R4G.js → db-CS4ZJQKV.js} +1 -1
  169. package/{dbSlice-HP7G4LX3.js → dbSlice-WNON7TOL.js} +17 -16
  170. package/{deleteFile-7VQGLK2V.js → deleteFile-Q3YDB4ML.js} +9 -9
  171. package/{desktopFolderProjects-SFY5EZS3.js → desktopFolderProjects-4POAMVJN.js} +2 -2
  172. package/{dialogCommands-JHMR2N2Y.js → dialogCommands-VRI7VCXH.js} +64 -57
  173. package/dialogNotifyCommand-6TMCX4D2.js +36 -0
  174. package/{dialogSlice-GRSVGDTF.js → dialogSlice-LFNVUSAN.js} +42 -40
  175. package/{dialogUsageCommand-7KCPYGHG.js → dialogUsageCommand-HTVPR2CX.js} +11 -10
  176. package/{docCreateCommands-F2K3UPSQ.js → docCreateCommands-AMPXMOX3.js} +61 -54
  177. package/{docDeleteCommands-XGXB5RFT.js → docDeleteCommands-I5WFAEHP.js} +63 -57
  178. package/{docReadCommands-7HWZZV6J.js → docReadCommands-X53ELNIE.js} +61 -54
  179. package/{docStore-KNZK6Y2G.js → docStore-2VAWVLWH.js} +21 -20
  180. package/{docUpdateCommands-OZZ7NQII.js → docUpdateCommands-6QPNDC7M.js} +64 -57
  181. package/{fetchMessages-M3QU7ZCQ.js → fetchMessages-TQEUZD7H.js} +8 -7
  182. package/{getPricing-2MNMXEVG.js → getPricing-MB2GBXOK.js} +6 -6
  183. package/{handleSendMessageAction-SHIH74GJ.js → handleSendMessageAction-AAJOHXOB.js} +193 -301
  184. package/{hostCredentialCreate-X64WK4JD.js → hostCredentialCreate-GLMJ22M6.js} +123 -114
  185. package/index.js +29 -29
  186. package/{keys-BEGDOTRL.js → keys-RWF7UCB4.js} +5 -4
  187. package/legacyMigration-TF4SYJCZ.js +19 -0
  188. package/{lib-5KKIBFIT.js → lib-OT5OTXRU.js} +7087 -6664
  189. package/{localDialogRead-XRLAH4CZ.js → localDialogRead-SVTPA5GY.js} +3 -3
  190. package/{localFirstLog-DHNOY2NA.js → localFirstLog-OIETGK66.js} +1 -1
  191. package/localRecall-2J6QPOK2.js +15 -0
  192. package/{localRuntimeAdapter-A7K3ABA6.js → localRuntimeAdapter-BMP57P42.js} +119 -110
  193. package/{localRuntimeAuthority-OYH4AE4G.js → localRuntimeAuthority-IBJAT62G.js} +1 -1
  194. package/{localRuntimeDb-VBIMCWUC.js → localRuntimeDb-N7KHDXS2.js} +2 -2
  195. package/{machineCommands-DE3MR7CJ.js → machineCommands-3MLIAK5Q.js} +128 -119
  196. package/{machineInfo-UKPTZO5Y.js → machineInfo-LWHTEBHJ.js} +1 -1
  197. package/machineWsRunDispatch-FKIGROQF.js +191 -0
  198. package/{memberThunks-7WJAY5V6.js → memberThunks-ZGLZ36PK.js} +29 -28
  199. package/{memoryCommands-ASCE5STX.js → memoryCommands-5WC3T66U.js} +4 -4
  200. package/models-ULVWLSQ6.js +9 -0
  201. package/{oauthTokenStore-OEERZFUC.js → oauthTokenStore-RUYNYALS.js} +1 -1
  202. package/{offlineMarxistsAgentCommand-2QTHJCHV.js → offlineMarxistsAgentCommand-4IC3Y4C6.js} +4 -4
  203. package/overlay-THLKYIWY.js +11 -0
  204. package/package.json +2 -2
  205. package/{pdf-U6KSUEZZ.js → pdf-KE3QI2V7.js} +1 -1
  206. package/{processSpawn-ZU5QFM43.js → processSpawn-6GEO77KV.js} +1 -1
  207. package/{profileConfig-KN3N2BLF.js → profileConfig-KOWK4HOG.js} +1 -1
  208. package/{projectArchive-GGL5HLMD.js → projectArchive-HK63GMF4.js} +52 -50
  209. package/{providerPresetApply-YXGV6NXB.js → providerPresetApply-OIFQ73LH.js} +19 -12
  210. package/{providerRegistry-S6UL6UNA.js → providerRegistry-KZ3SDZ3D.js} +10 -10
  211. package/{read-4VZQODSN.js → read-MRQHJMJ2.js} +10 -10
  212. package/{readDocTool-AVD32RUJ.js → readDocTool-4BGTXTXA.js} +2 -2
  213. package/{readlineWorkspace-XKYKNL2I.js → readlineWorkspace-AKAHSCNY.js} +6505 -4634
  214. package/{removeDialogAgentAction-FKL4BYMR.js → removeDialogAgentAction-B6H7DXFM.js} +23 -22
  215. package/{runAgentClientLoop-V7E2WBVM.js → runAgentClientLoop-UVAWGEON.js} +81 -79
  216. package/{runtimeDoctorCommands-QAI3MBKQ.js → runtimeDoctorCommands-FW6XKDUG.js} +12 -12
  217. package/{runtimeModeArgs-2Y7BALTO.js → runtimeModeArgs-X7DL7PUN.js} +1 -1
  218. package/secretCommands-LJXOOYA7.js +24 -0
  219. package/{setDialogExtraReferencesAction-TLIOLWWI.js → setDialogExtraReferencesAction-EVCY3TRW.js} +22 -21
  220. package/{setPrimaryDialogAgentAction-2OLWRFYR.js → setPrimaryDialogAgentAction-KPFVPHCJ.js} +23 -22
  221. package/{settingThunks-UGBPCNYF.js → settingThunks-ENOAK2WV.js} +97 -88
  222. package/{skillDiscovery-XKHROSH2.js → skillDiscovery-B5V23AXP.js} +4 -4
  223. package/{skillSummaryMarker-42HKSXYY.js → skillSummaryMarker-62UW7HI7.js} +1 -1
  224. package/{slateToRenderMarkdown-Y5IYN73U.js → slateToRenderMarkdown-R3X74MCX.js} +1 -1
  225. package/{slateUtils-NGV2NMKM.js → slateUtils-ESQAJ6RN.js} +1 -1
  226. package/{spaceCommands-4KSLKVNF.js → spaceCommands-DTTNAS2L.js} +61 -53
  227. package/store-JRVN62NM.js +46 -0
  228. package/{streamAgentChatTurn-7FR7X2NU.js → streamAgentChatTurn-R5UY25VB.js} +875 -618
  229. package/{switchDialogAgentAction-UZSAG2N5.js → switchDialogAgentAction-SZ44XDRY.js} +21 -20
  230. package/{tableCommands-MP6VFSVT.js → tableCommands-HITDSLHS.js} +10 -10
  231. package/tombstones-FQQMEDOW.js +32 -0
  232. package/{toolApiClient-HGNO53TC.js → toolApiClient-OGQEBQVX.js} +4 -4
  233. package/{tools-ZLKCC25W.js → tools-HWFG6GMJ.js} +75 -74
  234. package/tuiSummaryLlmCaller-HQQZAMYF.js +44 -0
  235. package/{updateCommands-4744PL3P.js → updateCommands-RJVCRSBP.js} +10 -8
  236. package/{updateDialogTitleAction-U53ZT7PJ.js → updateDialogTitleAction-KNPOAMVS.js} +48 -46
  237. package/{usageCommand-F4IPVMIZ.js → usageCommand-FWZCB7AM.js} +11 -10
  238. package/{usageRebuildCommand-HAW2AD5N.js → usageRebuildCommand-SP2BLNJU.js} +12 -11
  239. package/userDataMerge-HT2ZVHBJ.js +13 -0
  240. package/{windowsSelfUpdate-XSQVF2GK.js → windowsSelfUpdate-KERHIAB4.js} +3 -3
  241. package/{workloadLifecycle-4BEQHMSD.js → workloadLifecycle-DYNEYHV5.js} +7 -6
  242. package/agentRecordCommands-L3QQ7HZ4.js +0 -167
  243. package/agentRunCommand-2IFGQHCO.js +0 -196
  244. package/chunk-3CGG2PAL.js +0 -594
  245. package/chunk-5XBUWM6I.js +0 -89
  246. package/chunk-BFM34N54.js +0 -842
  247. package/chunk-CJEIOP3E.js +0 -0
  248. package/chunk-CZM5YTZ7.js +0 -977
  249. package/chunk-EYUBHHFB.js +0 -636
  250. package/chunk-JTDORD5P.js +0 -415
  251. package/chunk-NRFKLKRU.js +0 -339
  252. package/chunk-VPAZOH2D.js +0 -115
  253. package/chunk-WJZOQR2J.js +0 -151
  254. package/chunk-ZCWRMUWT.js +0 -89
  255. package/dist-M676ULYG.js +0 -7314
  256. package/machineWsRunDispatch-R26P5V36.js +0 -182
  257. package/models-PJBE6JDV.js +0 -9
  258. package/tuiSummaryLlmCaller-WC54MVGP.js +0 -160
  259. package/{chunk-WACI5I6V.js → chunk-2THCNKE4.js} +3 -3
  260. package/{chunk-2QHFH3DR.js → chunk-47ITIXO7.js} +6 -6
  261. package/{chunk-GOO6G4PY.js → chunk-CKG3PRZ7.js} +0 -0
  262. package/{chunk-OQHIVPG4.js → chunk-SNBZZPYH.js} +3 -3
  263. package/{chunk-AETZ7AD6.js → chunk-W6RFEDN4.js} +3 -3
@@ -1,20 +1,8 @@
1
- import {
2
- applyChatCompletionDelta,
3
- applyResponsesToolEvent,
4
- createResponsesToolAccumulator,
5
- finalizeResponsesToolCalls,
6
- flushChatCompletionStream,
7
- providerHttpFailure,
8
- throwIfChatCompletionStreamFailed
9
- } from "./chunk-4KH4J3G5.js";
10
1
  import {
11
2
  DEVIN_CONNECT_URL,
12
3
  parseSseDataLineJson,
13
4
  parseSseDataLineObject
14
- } from "./chunk-CTPXME7U.js";
15
- import {
16
- readSseFrames
17
- } from "./chunk-HNUWUO75.js";
5
+ } from "./chunk-3LMUPOHG.js";
18
6
  import {
19
7
  evaluateFileWritePolicy,
20
8
  summarizeToolArguments
@@ -25,11 +13,36 @@ import {
25
13
  import {
26
14
  formatImageSummary,
27
15
  readLocalImage
28
- } from "./chunk-Y63ZSOR6.js";
16
+ } from "./chunk-AL3TYDIF.js";
17
+ import {
18
+ applyChatCompletionDelta,
19
+ applyResponsesToolEvent,
20
+ createResponsesToolAccumulator,
21
+ finalizeResponsesToolCalls,
22
+ flushChatCompletionStream,
23
+ providerHttpFailure,
24
+ throwIfChatCompletionStreamFailed
25
+ } from "./chunk-QNWG52I4.js";
26
+ import {
27
+ readSseFrames
28
+ } from "./chunk-HNUWUO75.js";
29
29
  import {
30
+ resolveToolExecutionContract
31
+ } from "./chunk-LKA64WH4.js";
32
+ import {
33
+ evaluateRequestBudget,
30
34
  hasImageInRuntimeMessages,
31
35
  stripImagePartsFromMessages
32
- } from "./chunk-W4S457LU.js";
36
+ } from "./chunk-YU4ZPWVF.js";
37
+ import {
38
+ clipToolText,
39
+ resolveHistoricalToolContentCap,
40
+ resolveToolOutputProfile
41
+ } from "./chunk-2WZXXAQD.js";
42
+ import {
43
+ resolveAgentImageInputSupport,
44
+ resolveToolImagePolicy
45
+ } from "./chunk-OBWT45ZU.js";
33
46
  import {
34
47
  applyShapingCounts,
35
48
  attachProjectionFacts,
@@ -40,7 +53,7 @@ import {
40
53
  projectUserPartForModel,
41
54
  resolveOutboundDiagnosticSink,
42
55
  writeThinkingFacts
43
- } from "./chunk-4KB6WQUL.js";
56
+ } from "./chunk-BGP2EDFG.js";
44
57
  import {
45
58
  createThinkParserState,
46
59
  createToolCallAccumulator,
@@ -66,7 +79,25 @@ import {
66
79
  hashStablePrefixContent,
67
80
  resolveResponseGuidelines,
68
81
  resolveToolGuidedSections
69
- } from "./chunk-NOMIGOST.js";
82
+ } from "./chunk-EIAI3PPH.js";
83
+ import {
84
+ BRIDGE_STREAM_MAX_CHARS,
85
+ CLI_NOLO_WORKSPACE_TOOL_NAMES,
86
+ DEFAULT_READ_DIALOG_MAX_RESPONSE_CHARS,
87
+ NOLO_WORKSPACE_TOOL_NAMES,
88
+ buildDialogReadEnvelope,
89
+ buildNoloWorkspaceCommandArgs,
90
+ buildNoloWorkspaceOpenAiTools,
91
+ createStreamingDialogReadProjector,
92
+ noloPositiveIntegerString,
93
+ noloStringArg,
94
+ parseNoloWorkspaceToolArguments,
95
+ readTextStreamUpToChars,
96
+ resolveNoloDialogInput
97
+ } from "./chunk-C4VAR65S.js";
98
+ import {
99
+ applyToolSurfaceConstraints
100
+ } from "./chunk-SOF4OWDM.js";
70
101
  import {
71
102
  classifyUnparsableToolArgs,
72
103
  collectStablePromptPrefix,
@@ -90,7 +121,7 @@ import {
90
121
  toOpenAiCompatibleMessages,
91
122
  toResponsesTools,
92
123
  transferDowngradedToolTurnOrigin
93
- } from "./chunk-UTV2AYEG.js";
124
+ } from "./chunk-MAIITKBT.js";
94
125
  import {
95
126
  TOOL_HAD_IMAGES,
96
127
  TOOL_IMAGES,
@@ -105,36 +136,11 @@ import {
105
136
  withoutToolImages
106
137
  } from "./chunk-E2BESSAW.js";
107
138
  import {
108
- BRIDGE_STREAM_MAX_CHARS,
109
- CLI_NOLO_WORKSPACE_TOOL_NAMES,
110
- DEFAULT_READ_DIALOG_MAX_RESPONSE_CHARS,
111
- DEFAULT_SUBTASK_MAX_TOOL_CALLS,
112
- DEFAULT_SUBTASK_MAX_WALL_CLOCK_MS,
113
- NOLO_WORKSPACE_TOOL_NAMES,
114
- buildDialogReadEnvelope,
115
- buildNoloWorkspaceCommandArgs,
116
- buildNoloWorkspaceOpenAiTools,
117
- buildSubtaskBudgetExhaustedContent,
118
- createStreamingDialogReadProjector,
119
- createSubtaskBudgetTracker,
120
- noloPositiveIntegerString,
121
- noloStringArg,
122
- parseNoloWorkspaceToolArguments,
123
- readTextStreamUpToChars,
124
- resolveNoloDialogInput
125
- } from "./chunk-25JZEIXN.js";
126
- import {
127
- applyToolSurfaceConstraints
128
- } from "./chunk-KC4XDTT3.js";
129
- import {
130
- clipToolText,
131
- resolveHistoricalToolContentCap,
132
- resolveToolOutputProfile
133
- } from "./chunk-2WZXXAQD.js";
139
+ isQuotaExhaustedError
140
+ } from "./chunk-TLE77UJ5.js";
134
141
  import {
135
- resolveAgentImageInputSupport,
136
- resolveToolImagePolicy
137
- } from "./chunk-YWSEPJ7C.js";
142
+ isAgentSkillDisabled
143
+ } from "./chunk-FS2XP5D7.js";
138
144
  import {
139
145
  buildDialogFallbackTitleFromUserInput
140
146
  } from "./chunk-2MBTWINN.js";
@@ -142,32 +148,34 @@ import {
142
148
  EMPTY_ASSISTANT_REPAIR_PROMPT,
143
149
  extractReferenceKeysFromMessage,
144
150
  hasAssistantVisibleOutput,
145
- normalizeUsage,
146
- readCacheCreationInputTokens,
147
- readCacheReadInputTokens,
148
151
  resolveAgentContextWindow,
149
152
  resolveEmptyAssistantFallbackMessage,
150
153
  resolveEmptyAssistantOutcome,
151
154
  resolveTruncatedReasoningTailLog
152
- } from "./chunk-JTDORD5P.js";
155
+ } from "./chunk-NIZ2BXFD.js";
153
156
  import {
154
157
  COMPACTION_SUMMARY_SCHEMA_VERSION,
155
158
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
156
- TOOL_RESULT_TRUNCATE_CHARS,
159
+ buildBoundedCompactionRequest,
157
160
  buildCompactionMetricsFromPlan,
158
- buildCompactionUserContent,
159
- computeSummaryInputTokenCap,
161
+ classifySummaryError,
160
162
  computeSummarySourceMetadata,
163
+ createSummaryBudget,
161
164
  formatCompactionMetricsLog,
162
- formatFileOperationsFromMessages,
163
- formatMessagesForSummaryWithTruncation,
165
+ getMessageTokenCount,
164
166
  getModelContextWindow,
167
+ isTruncatedCompletion,
168
+ normalizeUsage,
165
169
  planCompression,
166
- planContextUsage,
170
+ readCacheCreationInputTokens,
171
+ readCacheReadInputTokens,
167
172
  resolveCompressionTriggerRatio,
173
+ resolveEffectiveCompactionBudgets,
174
+ runSummaryWithContract,
168
175
  sha256Hex,
176
+ summaryCompletionTokens,
169
177
  truncateToolOutputForContext
170
- } from "./chunk-CZM5YTZ7.js";
178
+ } from "./chunk-RDH5CQ4T.js";
171
179
  import {
172
180
  createProviderCallTimingTracker,
173
181
  finalizeProviderCallTiming,
@@ -178,25 +186,38 @@ import {
178
186
  import {
179
187
  clipCompactText
180
188
  } from "./chunk-5MMAK75Q.js";
181
- import {
182
- assertCredentialRef
183
- } from "./chunk-OQHIVPG4.js";
184
189
  import {
185
190
  asRecordOrEmpty
186
191
  } from "./chunk-KX6YBIHG.js";
192
+ import {
193
+ assertCredentialRef
194
+ } from "./chunk-SNBZZPYH.js";
195
+ import {
196
+ isLevelNotFoundError
197
+ } from "./chunk-OW765XBU.js";
187
198
  import {
188
199
  isAbortError
189
200
  } from "./chunk-7DPNBQSV.js";
190
201
  import {
191
- buildDialogSummaryLayer
192
- } from "./chunk-ZRSNONOB.js";
193
- import {
194
- buildUserResponseLanguageContext
195
- } from "./chunk-4VJAC32I.js";
202
+ WORKTREE_ISOLATION_PRELUDE
203
+ } from "./chunk-PJCIP64Z.js";
196
204
  import {
197
205
  canonicalizeToolName,
198
206
  canonicalizeToolNames
199
- } from "./chunk-ZCWRMUWT.js";
207
+ } from "./chunk-6OVNSQHN.js";
208
+ import {
209
+ dialogMessageKey,
210
+ dialogMessageRange
211
+ } from "./chunk-OJKUU3LP.js";
212
+ import {
213
+ asTrimmedNonEmptyStringArray
214
+ } from "./chunk-I42VC5KR.js";
215
+ import {
216
+ buildDialogSummaryLayer
217
+ } from "./chunk-JWXITTQB.js";
218
+ import {
219
+ buildUserResponseLanguageContext
220
+ } from "./chunk-L5I543VB.js";
200
221
  import {
201
222
  buildInteractiveCommandBlockedResult,
202
223
  buildProcessTaskResultCapsule,
@@ -211,56 +232,54 @@ import {
211
232
  formatToolOverflowMarker,
212
233
  getProcessRegistry,
213
234
  isProcessTaskTerminalStatus,
235
+ isSpilledToolOutput,
214
236
  reportedTaskStatus,
215
237
  resolveExecShellTimeoutMs,
216
238
  resolveExecutableOnPath,
217
239
  runWorkspaceCommand,
218
240
  spawnToWebStreams,
241
+ spillLargeToolOutput,
219
242
  spillToolOutput,
220
243
  toProcessTaskView,
221
244
  truncateToolOutput
222
- } from "./chunk-JNKSDZFS.js";
245
+ } from "./chunk-KEN3N2TN.js";
223
246
  import {
224
247
  estimateTokenCount
225
248
  } from "./chunk-PSCNXBYA.js";
226
249
  import {
227
250
  buildProviderAuthHeaders,
228
251
  buildProviderExecutionPlan
229
- } from "./chunk-TCZHTTUU.js";
252
+ } from "./chunk-I7WBYGBP.js";
230
253
  import {
231
254
  toErrorMessage
232
255
  } from "./chunk-HNMXYYJR.js";
256
+ import {
257
+ asTrimmedString
258
+ } from "./chunk-3EUW3WEO.js";
233
259
  import {
234
260
  isRecord
235
261
  } from "./chunk-KUG52IAP.js";
262
+ import {
263
+ PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL
264
+ } from "./chunk-CKG3PRZ7.js";
236
265
  import {
237
266
  asTrimmedLowercaseString
238
267
  } from "./chunk-QSJ3HFEF.js";
239
- import {
240
- normalizeServerOrigin
241
- } from "./chunk-CF7PE763.js";
242
- import {
243
- resolveNoloStateDir
244
- } from "./chunk-6UBGXX2W.js";
245
- import {
246
- dialogMessageKey,
247
- dialogMessageRange
248
- } from "./chunk-XJWWBO34.js";
249
- import {
250
- asTrimmedNonEmptyStringArray
251
- } from "./chunk-I42VC5KR.js";
252
268
  import {
253
269
  asOptionalPositiveFiniteNumber
254
270
  } from "./chunk-U6VC4MU4.js";
255
- import {
256
- asTrimmedString
257
- } from "./chunk-3EUW3WEO.js";
258
271
  import {
259
272
  asOptionalTrimmedString
260
273
  } from "./chunk-G3SUCT4W.js";
274
+ import {
275
+ normalizeServerOrigin
276
+ } from "./chunk-CF7PE763.js";
277
+ import {
278
+ resolveNoloStateDir
279
+ } from "./chunk-6UBGXX2W.js";
261
280
  import {
262
281
  __export
263
- } from "./chunk-4VNS5WPM.js";
282
+ } from "./chunk-U67V476Y.js";
264
283
 
265
284
  // packages/agent-runtime/actionGate.ts
266
285
  function readCommandActionGatePayload(payload) {
@@ -304,6 +323,42 @@ function readTitleParams(value) {
304
323
  return Object.fromEntries(entries);
305
324
  }
306
325
 
326
+ // packages/agent-runtime/contextRefusal.ts
327
+ var machineIdentifier = (value) => value && /^[A-Z][A-Z0-9_]{0,63}$/.test(value) ? value : void 0;
328
+ function classifyContextRefusal(args2) {
329
+ const { outcome, historyTokens, budget, droppedCount } = args2;
330
+ const largest = args2.largestMessageTokens;
331
+ const numbersKnown = Number.isFinite(historyTokens) && historyTokens >= 0 && Number.isFinite(budget) && budget >= 0 && largest !== void 0 && Number.isFinite(largest) && largest >= 0;
332
+ const base = { historyTokens, budget, droppedCount, largestMessageTokens: largest ?? -1 };
333
+ const result4 = (reason, detail) => ({ ...base, reason, detail });
334
+ if (!numbersKnown) return result4("unknown", "input-estimate-unavailable");
335
+ if (largest >= budget) return result4("single-message-too-large", "largest-message-at-or-above-budget");
336
+ const compacted = outcome?.result;
337
+ const decision = compacted?.decision;
338
+ const failure = outcome?.error ?? compacted?.failure;
339
+ if (failure || compacted?.failureReason || decision?.outcome === "failed") {
340
+ const code = machineIdentifier(failure?.code);
341
+ const errorType = failure?.errorType;
342
+ const status = failure?.status;
343
+ if (code === "PROVIDER_UNAVAILABLE" || code === "SUMMARY_PROVIDER_UNAVAILABLE") {
344
+ return result4("no-compactable-content", `code=${code}`);
345
+ }
346
+ const busy = status === 503 || status === 429 || !!code && /BUSY|RATE_LIMIT|CAPACITY|OVERLOAD|RESOURCE_EXHAUSTED/.test(code) || !!errorType && /^(?:capacity|overloaded|rate_limit)(?:_error)?$/.test(errorType);
347
+ const detail = [
348
+ status !== void 0 && Number.isInteger(status) && status >= 100 && status <= 599 ? `status=${status}` : "",
349
+ code ? `code=${code}` : "",
350
+ failure ? `category=${failure.category}` : `failure=${compacted?.failureReason ?? (decision?.outcome === "failed" ? decision.reason : "provider-error")}`
351
+ ].filter(Boolean).join(" ");
352
+ return result4(busy ? "summary-provider-busy" : "summary-failed", detail);
353
+ }
354
+ if (decision?.outcome === "skipped") {
355
+ if (decision.skipReason === "summary-empty") return result4("summary-failed", "summary-empty");
356
+ const bounded3 = decision.skipReason === "bounded-request-blocked" ? /\b(no-budget|invalid-window|empty-input|no-safe-group|group-too-large)\b/.exec(decision.detail ?? "")?.[1] : void 0;
357
+ return result4("no-compactable-content", `skip=${decision.skipReason}${bounded3 ? ` detail=${bounded3}` : ""}`);
358
+ }
359
+ return result4("unknown", decision ? "no-refusal-decision" : "compaction-not-evaluated");
360
+ }
361
+
307
362
  // node_modules/effect/dist/Pipeable.js
308
363
  var pipeArguments = (self, args2) => {
309
364
  switch (args2.length) {
@@ -13127,6 +13182,30 @@ function createLoopTiming() {
13127
13182
  return { mark, flush: flush2 };
13128
13183
  }
13129
13184
 
13185
+ // packages/agent-runtime/toolExecutionFailure.ts
13186
+ function isToolExecutionFailure(args2) {
13187
+ const metadata = args2.metadata;
13188
+ if (metadata) {
13189
+ if (metadata.error) return true;
13190
+ const exitCode = metadata.exitCode;
13191
+ if (typeof exitCode === "number" && Number.isFinite(exitCode) && exitCode !== 0) return true;
13192
+ if (typeof exitCode === "string" && exitCode.trim() !== "") {
13193
+ const parsedExitCode = Number(exitCode);
13194
+ if (Number.isFinite(parsedExitCode) && parsedExitCode !== 0) return true;
13195
+ }
13196
+ if (metadata.timedOut === true) return true;
13197
+ }
13198
+ if (typeof args2.content !== "string" || !args2.content.trim()) return false;
13199
+ try {
13200
+ const parsed = JSON.parse(args2.content);
13201
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
13202
+ const record2 = parsed;
13203
+ return Boolean(record2.error) || record2.ok === false || record2.success === false || record2.status === "error";
13204
+ } catch {
13205
+ return false;
13206
+ }
13207
+ }
13208
+
13130
13209
  // packages/agent-runtime/toolCallPairing.ts
13131
13210
  function readToolCallId(message) {
13132
13211
  if (typeof message.tool_call_id === "string" && message.tool_call_id) {
@@ -13485,7 +13564,7 @@ var OBSERVATION_ONLY_METADATA_KEYS = /* @__PURE__ */ new Set([
13485
13564
  ]);
13486
13565
  function formatToolMessageContent(args2) {
13487
13566
  const sanitizedContent = sanitizeToolOutputText(args2.content ?? "");
13488
- const scrubbedContent = scrubSecrets(sanitizedContent).cleaned;
13567
+ const scrubbedContent = spillLargeToolOutput(scrubSecrets(sanitizedContent).cleaned, args2.toolName, args2.workspaceRoot);
13489
13568
  if (args2.toolName !== "globFiles" && args2.toolName !== "codeSearch" && args2.toolName !== "readFile" || !args2.metadata || Object.keys(args2.metadata).length === 0) {
13490
13569
  return scrubbedContent;
13491
13570
  }
@@ -13501,7 +13580,7 @@ function formatToolMessageContent(args2) {
13501
13580
  [tool metadata]
13502
13581
  ${JSON.stringify(visible)}`;
13503
13582
  }
13504
- function blocksToOpenAiMessages(blocks) {
13583
+ function blocksToOpenAiMessages(blocks, workspaceRoot) {
13505
13584
  const out = [];
13506
13585
  let text2 = "";
13507
13586
  let reasoning = "";
@@ -13522,6 +13601,7 @@ function blocksToOpenAiMessages(blocks) {
13522
13601
  role: "tool",
13523
13602
  content: formatToolMessageContent({
13524
13603
  toolName: tc.function.name,
13604
+ workspaceRoot,
13525
13605
  content: res?.content ?? "",
13526
13606
  ...res?.metadata ? { metadata: res.metadata } : {}
13527
13607
  }),
@@ -13588,42 +13668,6 @@ var TOOL_METADATA_KEYS = [
13588
13668
  "pasteId",
13589
13669
  "source"
13590
13670
  ];
13591
- function trimHistoryToContextBudget(history, model, contextWindowOverride) {
13592
- if (history.length === 0) return { history, droppedCount: 0 };
13593
- const { rawMessageBudget } = planContextUsage({
13594
- contextWindow: contextWindowOverride ?? getModelContextWindow(model ?? ""),
13595
- summaryTokens: 0,
13596
- // localLoop 没有 web 端的负载分档器;medium 是中性默认值,
13597
- // 不为了省几个 token 在这里复制一份分类逻辑。
13598
- recentLoad: "medium"
13599
- });
13600
- const messageTokens = (message) => {
13601
- const toolCalls = message.tool_calls;
13602
- return estimateTokenCount(contentAsText(message.content)) + (Array.isArray(toolCalls) ? estimateTokenCount(JSON.stringify(toolCalls)) : 0);
13603
- };
13604
- let used = 0;
13605
- let start = history.length;
13606
- for (let i = history.length - 1; i >= 0; i -= 1) {
13607
- const cost = messageTokens(history[i]);
13608
- if (used + cost > rawMessageBudget && start < history.length) break;
13609
- used += cost;
13610
- start = i;
13611
- }
13612
- if (start === 0) return { history, droppedCount: 0 };
13613
- return {
13614
- history: sanitizeToolCallPairing(history.slice(start)),
13615
- droppedCount: start
13616
- };
13617
- }
13618
- function contentAsText(content) {
13619
- if (typeof content === "string") return content;
13620
- if (!Array.isArray(content)) return "";
13621
- return content.map((part) => {
13622
- if (part?.type === "text") return part.text;
13623
- if (part?.type === "image_url") return part.image_url.url;
13624
- return "";
13625
- }).join("\n");
13626
- }
13627
13671
  function contentCharCount(content) {
13628
13672
  if (typeof content === "string") return content.length;
13629
13673
  if (!Array.isArray(content)) return 0;
@@ -13657,6 +13701,7 @@ function projectToolContentForProvider(args2) {
13657
13701
  const rawContent = args2.content;
13658
13702
  const content = typeof rawContent === "string" ? sanitizeToolOutputText(rawContent) : rawContent;
13659
13703
  if (typeof content !== "string") return content;
13704
+ if (isSpilledToolOutput(content)) return content;
13660
13705
  const metadataText = compactToolMetadata(args2.metadata);
13661
13706
  const embeddedMetadataIndex = metadataText ? content.indexOf("\n\n[tool metadata]\n") : -1;
13662
13707
  const contentForProjection = embeddedMetadataIndex >= 0 ? content.slice(0, embeddedMetadataIndex) : content;
@@ -13688,7 +13733,7 @@ ${metadataText}` : "";
13688
13733
  content: contentForProjection,
13689
13734
  toolName: args2.toolName
13690
13735
  });
13691
- spillNote = `; spillFile=${spill.displayPath}; totalLines=${spill.totalLines}; hint=read full output via readFile`;
13736
+ spillNote = `; spillFile=${spill.displayPath}; totalLines=${spill.totalLines}; hint=read a range, not the whole spill (readFile lines=A-B or sed -n 'A,Bp'); a full read re-injects ${spill.totalChars} chars`;
13692
13737
  } catch {
13693
13738
  }
13694
13739
  }
@@ -13898,6 +13943,29 @@ ${args2.input}` : [
13898
13943
  } : {}
13899
13944
  };
13900
13945
  }
13946
+ function estimateLocalRequestInput(messages, toolSchemaTokens = 0) {
13947
+ return messages.reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0) + toolSchemaTokens;
13948
+ }
13949
+ function evaluateLocalRequestBudget(args2) {
13950
+ const estimate = estimateLocalRequestInput(args2.messages, args2.descriptor?.toolSchemaTokens ?? 0);
13951
+ const ratio = Math.max(1, args2.calibrationRatio ?? 1);
13952
+ const output = args2.descriptor?.output ?? { semantics: "unknown", fallbackReserveTokens: 8192 };
13953
+ const boundedOutput = output.semantics === "unknown" ? { ...output, fallbackReserveTokens: Math.min(
13954
+ output.fallbackReserveTokens,
13955
+ Math.min(8192, Math.max(1, Math.floor(args2.contextWindow / 4)))
13956
+ ) } : output;
13957
+ return evaluateRequestBudget({
13958
+ contextWindow: args2.contextWindow,
13959
+ messageTokens: Math.ceil(estimate * ratio),
13960
+ toolSchemaTokens: 0,
13961
+ // already included in the calibrated full-input estimate
13962
+ protocolReserveTokens: 0,
13963
+ output: boundedOutput
13964
+ });
13965
+ }
13966
+ function updateLocalRequestCalibration(previous, actual, estimated) {
13967
+ return Number.isFinite(actual) && actual > 0 && Number.isFinite(estimated) && estimated > 0 ? Math.max(1, actual / estimated) : previous;
13968
+ }
13901
13969
 
13902
13970
  // packages/agent-runtime/toolArgsTruncationPolicy.ts
13903
13971
  import { createHash } from "node:crypto";
@@ -14738,12 +14806,13 @@ function detectExecShellSynchronousWarnings(command, shell) {
14738
14806
  }
14739
14807
 
14740
14808
  // packages/agent-runtime/capabilities/execShellCapability.ts
14809
+ var WINDOWS_SHELL_NOTE = process.platform === "win32" ? ` On Windows the runtime prefers pwsh (PowerShell 7+): it is resolved first (PATH, then the WindowsApps alias), and powershell.exe 5.1 is only a fallback when pwsh is absent. Read metadata.shellKind from the result \u2014 "pwsh" supports '&&'/'||' and $PSStyle, "powershell5" does not (join with ';' or split calls). Match the syntax to the kind you actually got; never assume the version.` : "";
14741
14810
  function buildExecShellToolDefinition(toolName = "execShell") {
14742
14811
  return {
14743
14812
  type: "function",
14744
14813
  function: {
14745
14814
  name: toolName,
14746
- description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax. LIFECYCLE CONTRACT (misreading this contract silenced a real dialog for 10 hours): (1) A normal command waits synchronously for at most 120 seconds; if it ends inside that window the call IS finished \u2014 a result containing exitCode means the command has already completed, never "still running"; parse it immediately (no later event will remind you). (2) Only a result with metadata.detached === true AND a taskId represents a tracked background task that may trigger completion wake-up (terminal completion turn carrying a bounded result capsule); that handle produces a completion notification when finished; to read its output without waiting for the wake-up, read the result file for that taskId with readFile (a launchProcess taskId only yields a notice on your next real turn, never a new turn). Note that sleep commands exceeding the detach threshold will detach and trigger a completion wake-up upon exit \u2014 do not run standalone sleep commands purely to pause execution. (3) Text the command itself prints (LAUNCHED, STARTED, BACKGROUND, ...) is plain stdout with zero lifecycle meaning \u2014 never read it as a platform receipt. (4) Shell-level backgrounding (\`&\`, nohup, disown, setsid, self-daemonizing scripts, PowerShell Start-Process) is NOT tracked: those descendants produce no completion notification, and the outer shell finishing tells you nothing about them. Use background: true to turn a finite job into a tracked background task immediately. When a synchronous result's core output was redirected to a file, read that file in the SAME turn ("read it later" means it is lost) \u2014 never end the turn promising a later automatic report without a taskId.`,
14815
+ description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess.${WINDOWS_SHELL_NOTE} LIFECYCLE CONTRACT: (1) A normal command waits synchronously for at most 120 seconds; if it ends inside that window the call IS finished \u2014 a result carrying exitCode means the command has already completed: parse it immediately, never read it as "still running" (no later event will remind you). (2) Only a result with metadata.detached === true AND a taskId can deliver an automatic completion turn; that taskId is an opaque handle, NOT a file path and NOT a result file \u2014 never search for, guess or readFile a path from it, and never rerun the command just to obtain its output. That terminal turn delivers the result inline as a bounded capsule; when the capsule was truncated it also carries a real full-output path in its overflow marker \u2014 read that actual path with readFile only when it is present. While waiting for that terminal notification, continue independent work on other parts of the task. A launchProcess taskId is a tracked task too, but it only yields a notice on your next real turn. (3) Text the command itself prints (LAUNCHED, STARTED, ...) is plain stdout with zero lifecycle meaning \u2014 never read it as a platform receipt. (4) Shell-level backgrounding (&, nohup, disown, setsid, self-daemonizing scripts, PowerShell Start-Process) is NOT tracked: those descendants produce no completion notification, and the outer shell finishing tells you nothing about them. Use background: true to turn a finite job into a tracked background task. Do not run standalone sleep commands purely to pause execution. When a synchronous result's core output was redirected to a file, read that file in the SAME turn ("read it later" means it is lost) \u2014 never end the turn promising a later automatic report without a taskId.`,
14747
14816
  parameters: {
14748
14817
  type: "object",
14749
14818
  properties: {
@@ -14761,7 +14830,7 @@ function buildExecShellToolDefinition(toolName = "execShell") {
14761
14830
  },
14762
14831
  label: {
14763
14832
  type: "string",
14764
- description: "Optional human-readable label for the resulting background task, shown in the task list and completion notices. Defaults to the command's last token, which is unhelpful for pipelines (e.g. '| tail -25' yields '-25')."
14833
+ description: "Optional human-readable label for the resulting background task, shown in task lists and completion notices. Defaults to the last token."
14765
14834
  }
14766
14835
  }
14767
14836
  }
@@ -15037,14 +15106,14 @@ var configuredConcurrency = Number(process.env.NOLO_VISUAL_RENDERER_CONCURRENCY
15037
15106
  var renderSlots = new VisualRenderSemaphore(Number.isFinite(configuredConcurrency) ? Math.max(1, Math.min(2, Math.floor(configuredConcurrency))) : 2);
15038
15107
  var require2 = createRequire(import.meta.url);
15039
15108
  function visualRendererCapability() {
15040
- if (process.env.NOLO_VISUAL_RENDERER_DISABLED === "1") return { available: false, reason: "Platform renderer disabled by NOLO_VISUAL_RENDERER_DISABLED=1 on this runtime host." };
15109
+ if (process.env.NOLO_VISUAL_RENDERER_DISABLED === "1") return { available: false, reasonCode: "disabled", reason: "Platform renderer disabled by NOLO_VISUAL_RENDERER_DISABLED=1 on this runtime host." };
15041
15110
  try {
15042
15111
  const { chromium } = require2("playwright");
15043
15112
  const executablePath = [chromium.executablePath(), resolveExecutableOnPath("google-chrome"), resolveExecutableOnPath("chromium"), resolveExecutableOnPath("chromium-browser")].find((path) => !!path && existsSync(path));
15044
- if (!executablePath) return { available: false, reason: "No Chromium renderer installed. Install Playwright Chromium (playwright install chromium) or system Chromium/Google Chrome on the runtime host." };
15113
+ if (!executablePath) return { available: false, reasonCode: "missing-browser", reason: "No Chromium renderer installed. Install Playwright Chromium (playwright install chromium) or system Chromium/Google Chrome on the runtime host." };
15045
15114
  return { available: true, chromium, executablePath };
15046
15115
  } catch {
15047
- return { available: false, reason: "Platform renderer unavailable: install the platform Playwright dependency and Chromium on the runtime host (not a workspace npm script)." };
15116
+ return { available: false, reasonCode: "dependency-unavailable", reason: "Platform renderer unavailable: install the platform Playwright dependency and Chromium on the runtime host (not a workspace npm script)." };
15048
15117
  }
15049
15118
  }
15050
15119
  async function renderVisualPage(args2) {
@@ -15319,6 +15388,52 @@ import { homedir } from "node:os";
15319
15388
  import { dirname as dirname2, relative as relative4, resolve as resolve4, sep as sep2 } from "node:path";
15320
15389
  import { spawn as spawnChildProcess2 } from "node:child_process";
15321
15390
 
15391
+ // packages/agent-runtime/runResultReadProjection.ts
15392
+ import { basename } from "node:path";
15393
+ function projectRunResultRead(path, content, raw = false) {
15394
+ const unchanged = { content, projected: false };
15395
+ if (raw || !basename(path).endsWith(".result.md") || !content.startsWith("---\n")) return unchanged;
15396
+ const end3 = content.indexOf("\n---\n", 4);
15397
+ if (end3 < 0) return unchanged;
15398
+ const header = content.slice(0, end3).split("\n");
15399
+ const runLine = header.find((line) => line.startsWith("runId: "));
15400
+ const index = header.findIndex((line) => line.startsWith("acceptance: "));
15401
+ if (!runLine || index < 0) return unchanged;
15402
+ try {
15403
+ const runId = JSON.parse(runLine.slice(7));
15404
+ if (typeof runId !== "string" || basename(path) !== `${runId}.result.md`) return unchanged;
15405
+ const encoded = JSON.parse(header[index].slice(12));
15406
+ if (typeof encoded !== "string") return unchanged;
15407
+ const acceptance = JSON.parse(encoded);
15408
+ if (!isRecord(acceptance) || acceptance.schemaVersion !== 1 || acceptance.source !== "local-runtime" || !isRecord(acceptance.checks) || !Array.isArray(acceptance.checks.observations)) return unchanged;
15409
+ const { observations, ...checks } = acceptance.checks;
15410
+ let exitZero = 0;
15411
+ let exitNonzero = 0;
15412
+ let exitUnknown = 0;
15413
+ for (const observation of observations) {
15414
+ const code = isRecord(observation) ? observation.exitCode : void 0;
15415
+ if (code === 0) exitZero++;
15416
+ else if (typeof code === "number" && Number.isInteger(code)) exitNonzero++;
15417
+ else exitUnknown++;
15418
+ }
15419
+ const projected = { ...acceptance, checks: { ...checks, observationSummary: {
15420
+ count: observations.length,
15421
+ exitZero,
15422
+ exitNonzero,
15423
+ exitUnknown,
15424
+ evidence: "#/acceptance/checks/observations",
15425
+ readOriginal: "readFile with raw:true (force:true if already read)",
15426
+ limitation: "Exit counts are observations, not version-bound test or review approval."
15427
+ } } };
15428
+ const line = `acceptance: ${JSON.stringify(JSON.stringify(projected))}`;
15429
+ if (line.length >= header[index].length) return unchanged;
15430
+ header[index] = line;
15431
+ return { content: header.join("\n") + content.slice(end3), projected: true };
15432
+ } catch {
15433
+ return unchanged;
15434
+ }
15435
+ }
15436
+
15322
15437
  // packages/core/readRangeLedger.ts
15323
15438
  function filterRetainedLedgerRecords(records, retainedCap) {
15324
15439
  return records.filter((record2) => record2.chars <= retainedCap);
@@ -15353,15 +15468,27 @@ function isLineRangeCovered(request3, covered) {
15353
15468
 
15354
15469
  // packages/agent-runtime/fileTextSearch.ts
15355
15470
  import { readdir, readFile as readFile3, stat as stat3 } from "node:fs/promises";
15356
- import { basename, relative as relative2, resolve as resolve2 } from "node:path";
15471
+ import { basename as basename2, relative as relative2, resolve as resolve2 } from "node:path";
15357
15472
  import { spawn as spawnChildProcess } from "node:child_process";
15358
15473
 
15359
15474
  // packages/agent-runtime/gitignore.ts
15360
15475
  import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
15361
15476
  import { resolve } from "node:path";
15477
+ function isIgnorableFsError(err) {
15478
+ if (err && typeof err === "object" && "code" in err) {
15479
+ const code = err.code;
15480
+ return code === "ENOENT" || code === "ENOTDIR";
15481
+ }
15482
+ return false;
15483
+ }
15362
15484
  async function readRootGitignorePatterns(workspaceRoot) {
15363
- const content = await readFile2(resolve(workspaceRoot, ".gitignore"), "utf8").catch(() => null);
15364
- if (content === null) return [];
15485
+ let content;
15486
+ try {
15487
+ content = await readFile2(resolve(workspaceRoot, ".gitignore"), "utf8");
15488
+ } catch (err) {
15489
+ if (isIgnorableFsError(err)) return [];
15490
+ throw err;
15491
+ }
15365
15492
  return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).flatMap((line) => {
15366
15493
  const unanchored = line.replace(/^\//, "");
15367
15494
  if (unanchored.endsWith("/")) return [`${unanchored}**`];
@@ -15373,14 +15500,15 @@ async function hasRootGitignore(workspaceRoot) {
15373
15500
  try {
15374
15501
  await stat2(resolve(workspaceRoot, ".gitignore"));
15375
15502
  return true;
15376
- } catch {
15377
- return false;
15503
+ } catch (err) {
15504
+ if (isIgnorableFsError(err)) return false;
15505
+ throw err;
15378
15506
  }
15379
15507
  }
15380
15508
 
15381
15509
  // packages/agent-runtime/fileTextSearch.ts
15382
15510
  var normalizePath = (value) => value.replaceAll("\\", "/");
15383
- var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative2(root, file) || basename(file));
15511
+ var pathFor = (root, file, absolute = false) => absolute ? normalizePath(file) : normalizePath(relative2(root, file) || basename2(file));
15384
15512
  var COMMAND_LIMIT_HEADROOM = 1;
15385
15513
  async function runCommand(binary, args2, cwd, maxResults) {
15386
15514
  const recordLimit = maxResults === void 0 ? 0 : Math.max(0, maxResults) + COMMAND_LIMIT_HEADROOM;
@@ -15406,6 +15534,17 @@ async function runCommand(binary, args2, cwd, maxResults) {
15406
15534
  }
15407
15535
  });
15408
15536
  }
15537
+ function extractGrepExcludeDir(pattern) {
15538
+ const p = pattern.replaceAll("\\", "/");
15539
+ if (!p) return null;
15540
+ if (!/\/(\*{1,2})?$/.test(p)) return null;
15541
+ const clean = p.replace(/\/(\*{1,2})?$/, "");
15542
+ if (!clean || clean.includes("/") || /[*?{}[\]]/.test(clean)) return null;
15543
+ return clean;
15544
+ }
15545
+ function isGrepExpressibleExclude(pattern) {
15546
+ return extractGrepExcludeDir(pattern) !== null;
15547
+ }
15409
15548
  function commandArgs(o, binary, target) {
15410
15549
  const args2 = binary === "rg" ? ["--line-number", "--no-heading", "--color", "never", "--null"] : ["-RIn", "--binary-files=without-match", "--null"];
15411
15550
  if (binary === "rg") args2.push("--no-require-git");
@@ -15418,8 +15557,8 @@ function commandArgs(o, binary, target) {
15418
15557
  if (o.include) args2.push("--glob", o.include);
15419
15558
  } else {
15420
15559
  for (const x of o.exclude ?? []) {
15421
- const clean = x.replace(/\/\*\*$/, "");
15422
- if (clean && !clean.includes("/")) args2.push("--exclude-dir", clean);
15560
+ const clean = extractGrepExcludeDir(x);
15561
+ if (clean) args2.push("--exclude-dir", clean);
15423
15562
  }
15424
15563
  if (o.include) args2.push("--include", o.include);
15425
15564
  }
@@ -15467,7 +15606,7 @@ function globMatch(pattern, value) {
15467
15606
  else source += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
15468
15607
  }
15469
15608
  const r = new RegExp(`^${source}$`);
15470
- return r.test(value) || r.test(basename(value));
15609
+ return r.test(value) || r.test(basename2(value));
15471
15610
  }
15472
15611
  var defaultReadTextFile = (filePath) => readFile3(filePath, "utf8").catch(() => "");
15473
15612
  async function jsSearch(o, targetPath) {
@@ -15515,6 +15654,23 @@ async function jsSearch(o, targetPath) {
15515
15654
  }
15516
15655
  return { matches: out, totalMatches: out.length, truncated: false, engine: "js" };
15517
15656
  }
15657
+ async function canGrepFulfillOptions(o) {
15658
+ if (!o.includeIgnored) {
15659
+ const gitignorePatterns = await readRootGitignorePatterns(o.root);
15660
+ if (gitignorePatterns.length > 0) return false;
15661
+ }
15662
+ if (o.include) {
15663
+ if (o.include.includes("/") || o.include.includes("\\") || o.include.includes("**") || /[{}[\]]/.test(o.include)) {
15664
+ return false;
15665
+ }
15666
+ }
15667
+ if (o.exclude && o.exclude.length > 0) {
15668
+ for (const pattern of o.exclude) {
15669
+ if (!isGrepExpressibleExclude(pattern)) return false;
15670
+ }
15671
+ }
15672
+ return true;
15673
+ }
15518
15674
  var COMMAND_ENGINES = [
15519
15675
  { engine: "ripgrep", binaryOption: "resolveRipgrepBinary", binaryName: "rg", argsKey: "rg" },
15520
15676
  { engine: "grep", binaryOption: "resolveGrepBinary", binaryName: "grep", argsKey: "grep" }
@@ -15526,6 +15682,7 @@ async function searchFileTextMatches(o) {
15526
15682
  if (targetStat.isFile()) return jsSearch(o, targetPath);
15527
15683
  const run2 = o.runCommand ?? runCommand, max4 = o.maxResults;
15528
15684
  for (const entry of COMMAND_ENGINES) {
15685
+ if (entry.engine === "grep" && !await canGrepFulfillOptions(o)) continue;
15529
15686
  const injectedResolver = o[entry.binaryOption];
15530
15687
  const binary = injectedResolver ? injectedResolver() : resolveExecutableOnPath(entry.binaryName);
15531
15688
  if (!binary) continue;
@@ -15557,8 +15714,11 @@ function readProcessOwner(runtimeContext) {
15557
15714
  if (!dialogId) return null;
15558
15715
  const turnId = asNonEmptyString(runtimeContext.turnId);
15559
15716
  const ownerId = asNonEmptyString(runtimeContext.ownerId);
15717
+ const getDepth = runtimeContext.getProcessCausalDepth;
15718
+ const depth = typeof getDepth === "function" ? getDepth() : runtimeContext.processCausalDepth;
15560
15719
  return {
15561
15720
  dialogId,
15721
+ ...typeof depth === "number" && Number.isSafeInteger(depth) && depth >= 0 ? { processCausalDepth: depth } : {},
15562
15722
  ...turnId ? { turnId } : {},
15563
15723
  ...ownerId ? { ownerId } : {}
15564
15724
  };
@@ -15707,13 +15867,15 @@ function runTaskLogs(args2) {
15707
15867
  const allEvents = registry.getTaskEvents(taskId, 0);
15708
15868
  const terminal = lastTerminalEvent(allEvents);
15709
15869
  const status = terminal ? reportedTaskStatus(terminal.type, terminal.exitCode) : record2 ? statusOf(record2) : "running";
15870
+ const hint = events.length === 0 && !isProcessTaskTerminalStatus(status) ? `no new events since cursor ${cursor}; the task is still running and there is nothing more to read right now. Don't re-read the same cursor immediately: later events are returned once they exist. Where the host reports completions, wait for that signal; otherwise use taskWait with an explicit budget.` : void 0;
15710
15871
  return {
15711
15872
  outcome: "ok",
15712
15873
  taskId,
15713
15874
  cursor: registry.latestTaskSeq(taskId),
15714
15875
  events,
15715
15876
  status,
15716
- terminal: isProcessTaskTerminalStatus(status)
15877
+ terminal: isProcessTaskTerminalStatus(status),
15878
+ ...hint ? { hint } : {}
15717
15879
  };
15718
15880
  }
15719
15881
  function formatTaskLogsContent(result4, options) {
@@ -15875,7 +16037,11 @@ function buildReadWorkspaceParameters() {
15875
16037
  properties: {
15876
16038
  path,
15877
16039
  lines,
15878
- force
16040
+ force,
16041
+ raw: {
16042
+ type: "boolean",
16043
+ description: "Read original bytes including full runtime acceptance observations in *.result.md. Default compacts only machine observations; report text and physical line numbers are unchanged."
16044
+ }
15879
16045
  },
15880
16046
  required: ["path"]
15881
16047
  };
@@ -16200,7 +16366,7 @@ function filterDeclaredWorkspaceToolNames(args2) {
16200
16366
  // packages/agent-runtime/workspacePathGuard.ts
16201
16367
  import { constants as fsConstants } from "node:fs";
16202
16368
  import { lstat, mkdir, open, readlink, realpath as realpath2 } from "node:fs/promises";
16203
- import { basename as basename2, dirname, isAbsolute as isAbsolute2, join as join2, relative as relative3, resolve as resolve3, sep } from "node:path";
16369
+ import { basename as basename3, dirname, isAbsolute as isAbsolute2, join as join2, relative as relative3, resolve as resolve3, sep } from "node:path";
16204
16370
  function isPathInsideRoot(root, targetPath) {
16205
16371
  const rel = relative3(resolve3(root), resolve3(targetPath));
16206
16372
  return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute2(rel);
@@ -16223,7 +16389,7 @@ async function realpathAllowMissing(targetPath) {
16223
16389
  if (!isNotFoundError(error)) return null;
16224
16390
  const parent = dirname(current);
16225
16391
  if (parent === current) return null;
16226
- tail.push(basename2(current));
16392
+ tail.push(basename3(current));
16227
16393
  current = parent;
16228
16394
  }
16229
16395
  }
@@ -16417,7 +16583,7 @@ async function createMissingDirectoryChain(guard) {
16417
16583
  }
16418
16584
  try {
16419
16585
  for (const target of missing.slice().reverse()) {
16420
- const name = basename2(target);
16586
+ const name = basename3(target);
16421
16587
  if (!name || name === "." || name === "..") {
16422
16588
  throw workspacePathGuardError({
16423
16589
  ok: false,
@@ -16661,6 +16827,9 @@ function describeRejectedArgValue(value) {
16661
16827
  if (Array.isArray(value)) return "an array";
16662
16828
  return `a value of type ${typeof value}`;
16663
16829
  }
16830
+ function clipMarkerValue(text2, max4 = 60) {
16831
+ return text2.length <= max4 ? text2 : `${text2.slice(0, max4)}\u2026(${text2.length} chars)`;
16832
+ }
16664
16833
  function readIntegerArg(args2) {
16665
16834
  if (isAbsentArg(args2.value)) return {};
16666
16835
  const min4 = args2.min ?? 1;
@@ -16719,16 +16888,16 @@ function parseLinesArg(value) {
16719
16888
  }
16720
16889
  function readFileSliceArgs(args2) {
16721
16890
  const warnings = [];
16722
- const withFallback = (slice2) => {
16891
+ const withFallback = (slice2, invalidLines) => {
16723
16892
  warnings.push(
16724
16893
  `No usable line range remained; returned the first ${READ_FILE_SLICE_FALLBACK_LINES} lines instead of the whole file.`
16725
16894
  );
16726
- return { ...slice2, maxLines: READ_FILE_SLICE_FALLBACK_LINES, warnings };
16895
+ return { ...slice2, maxLines: READ_FILE_SLICE_FALLBACK_LINES, warnings, invalidLines };
16727
16896
  };
16728
- if (isAbsentArg(args2.lines)) return { warnings };
16897
+ if (isAbsentArg(args2.lines)) return { warnings, invalidLines: void 0 };
16729
16898
  const { slice, warning } = parseLinesArg(args2.lines);
16730
16899
  if (warning) warnings.push(warning);
16731
- return slice ? { ...slice, warnings } : withFallback({});
16900
+ return slice ? { ...slice, warnings, invalidLines: void 0 } : withFallback({}, describeRejectedArgValue(args2.lines));
16732
16901
  }
16733
16902
  function buildReadFileSliceHint(totalLines) {
16734
16903
  return `File has ${totalLines} lines. Use lines: ${LINES_ARG_SYNTAX}, or omit it for the whole file.`;
@@ -16997,7 +17166,7 @@ var READ_FILE_LEDGER_MAX_KEYS = 256;
16997
17166
  async function readFileTool(args2) {
16998
17167
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
16999
17168
  const requestedPath = requireWorkspaceToolPath(parsed);
17000
- const { warnings, ...sliceArgs } = readFileSliceArgs(parsed);
17169
+ const { warnings, invalidLines, ...sliceArgs } = readFileSliceArgs(parsed);
17001
17170
  const approval = await resolveLocalWorkspaceToolApproval({
17002
17171
  workspaceRoot: args2.workspaceRoot,
17003
17172
  requestedPath,
@@ -17005,7 +17174,9 @@ async function readFileTool(args2) {
17005
17174
  ...args2.confirmExternalFileAccess ? { confirmExternalFileAccess: args2.confirmExternalFileAccess } : {}
17006
17175
  });
17007
17176
  const absolutePath = approval.path;
17008
- const content = await readGuardApprovedFile(approval);
17177
+ const originalContent = await readGuardApprovedFile(approval);
17178
+ const projection = projectRunResultRead(absolutePath, originalContent, parsed.raw === true);
17179
+ const content = projection.content;
17009
17180
  const activity = extractActivity(parsed);
17010
17181
  const sliced = sliceReadFileContent({
17011
17182
  content,
@@ -17015,7 +17186,16 @@ async function readFileTool(args2) {
17015
17186
  content: sliced.content,
17016
17187
  totalLines: sliced.totalLines
17017
17188
  });
17018
- const isTruncated = sliced.truncated || truncatedRead.truncatedByCharLimit;
17189
+ const invalidLinesMarker = invalidLines !== void 0 ? `[readFile] lines=${clipMarkerValue(invalidLines)} was rejected (only ONE range: ${LINES_ARG_SYNTAX}); returned lines ${sliced.startLine}-${sliced.endLine} instead. Fix the argument before relying on this window.
17190
+ ` : "";
17191
+ const finalRead = invalidLinesMarker.length > 0 ? defensivelyTruncateReadFile({
17192
+ content: truncatedRead.content,
17193
+ totalLines: sliced.totalLines,
17194
+ limit: Math.max(1, READ_FILE_MAX_CHAR_LIMIT - invalidLinesMarker.length)
17195
+ }) : truncatedRead;
17196
+ const finalContent = invalidLinesMarker + finalRead.content;
17197
+ const truncatedByCharLimit = truncatedRead.truncatedByCharLimit || finalRead.truncatedByCharLimit;
17198
+ const isTruncated = sliced.truncated || truncatedByCharLimit;
17019
17199
  const relativePath = normalizeWorkspaceRelativePath({
17020
17200
  workspaceRoot: resolve4(args2.workspaceRoot),
17021
17201
  targetPath: absolutePath
@@ -17023,7 +17203,7 @@ async function readFileTool(args2) {
17023
17203
  const force = parsed.force === true;
17024
17204
  const ledger = args2.readLedger;
17025
17205
  const requestRange = { startLine: sliced.startLine, endLine: sliced.endLine };
17026
- const fingerprint = createHash3("sha1").update(content).digest("hex").slice(0, 20);
17206
+ const fingerprint = createHash3("sha1").update(originalContent).update(parsed.raw === true ? "\0raw" : "\0default").digest("hex").slice(0, 20);
17027
17207
  let entry = ledger?.get(relativePath);
17028
17208
  if (entry && entry.fingerprint !== fingerprint) {
17029
17209
  entry = void 0;
@@ -17051,20 +17231,25 @@ async function readFileTool(args2) {
17051
17231
  }
17052
17232
  const metadata = {
17053
17233
  path: relativePath,
17054
- bytes: Buffer.byteLength(truncatedRead.content),
17055
- totalBytes: Buffer.byteLength(content),
17234
+ bytes: Buffer.byteLength(finalContent),
17235
+ totalBytes: Buffer.byteLength(originalContent),
17236
+ ...projection.projected ? {
17237
+ projection: "run-result-observations",
17238
+ projectedTotalBytes: Buffer.byteLength(content),
17239
+ rawReadHint: "readFile raw:true reads original evidence; line numbers are unchanged."
17240
+ } : {},
17056
17241
  startLine: sliced.startLine,
17057
17242
  endLine: sliced.endLine,
17058
17243
  totalLines: sliced.totalLines,
17059
17244
  truncated: isTruncated,
17060
- ...truncatedRead.truncatedByCharLimit ? { truncatedByByteLimit: true, truncatedByCharLimit: true } : {},
17245
+ ...truncatedByCharLimit ? { truncatedByByteLimit: true, truncatedByCharLimit: true } : {},
17061
17246
  ...sliceArgs.maxLines ? { maxLines: sliceArgs.maxLines } : {},
17062
17247
  ...sliceArgs.tailLines ? { tailLines: sliceArgs.tailLines } : {},
17063
17248
  ...warnings.length ? { warnings: [...warnings, buildReadFileSliceHint(sliced.totalLines)] } : {},
17064
17249
  ...activity ? { activity } : {}
17065
17250
  };
17066
17251
  if (!isTruncated && ledger) {
17067
- const persistedChars = truncatedRead.content.length + "\n\n[tool metadata]\n".length + JSON.stringify(metadata).length;
17252
+ const persistedChars = finalContent.length + "\n\n[tool metadata]\n".length + JSON.stringify(metadata).length;
17068
17253
  const records = entry?.records ?? [];
17069
17254
  records.push({ ...requestRange, chars: persistedChars });
17070
17255
  ledger.delete(relativePath);
@@ -17078,7 +17263,7 @@ async function readFileTool(args2) {
17078
17263
  ledger.delete(oldestKey);
17079
17264
  }
17080
17265
  }
17081
- return { content: truncatedRead.content, metadata };
17266
+ return { content: finalContent, metadata };
17082
17267
  }
17083
17268
  async function ensureGuardedParentDirectory(args2) {
17084
17269
  const directoryGuard = await resolveWorkspaceSafePath({
@@ -17977,7 +18162,8 @@ function createLocalWorkspaceToolExecutors(args2) {
17977
18162
  if (!Array.isArray(paths) || paths.length === 0 || paths.some((path) => typeof path !== "string" || !path.trim())) {
17978
18163
  throw new Error("read_image: paths must contain 1 to 4 non-empty string paths (maximum 4 images)");
17979
18164
  }
17980
- const results2 = await Promise.all(paths.map(async (path) => {
18165
+ const imagePaths = paths.filter((path) => typeof path === "string");
18166
+ const results2 = await Promise.all(imagePaths.map(async (path) => {
17981
18167
  try {
17982
18168
  const absolutePath = await resolveLocalWorkspaceToolPath({
17983
18169
  workspaceRoot: args2.workspaceRoot,
@@ -18224,6 +18410,13 @@ function getAgentRuntimeToolParametersIndex() {
18224
18410
  }
18225
18411
 
18226
18412
  // packages/agent-runtime/localToolPolicy.ts
18413
+ var shellLifecycles = /* @__PURE__ */ new WeakMap();
18414
+ function registerLocalShellLifecycle(executor, lifecycle) {
18415
+ shellLifecycles.set(executor, lifecycle);
18416
+ }
18417
+ function getLocalShellLifecycle(executor) {
18418
+ return executor ? shellLifecycles.get(executor) : void 0;
18419
+ }
18227
18420
  var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
18228
18421
  "deleteSpaces",
18229
18422
  "updateAgent",
@@ -18337,12 +18530,13 @@ async function runExecShellGates(args2) {
18337
18530
  return { kind: "pass", opts };
18338
18531
  }
18339
18532
  if (args2.execShellApproval) {
18340
- const result4 = await invokeCapability("execShell", args2.callArguments, {
18533
+ const execute = () => invokeCapability("execShell", args2.callArguments, {
18341
18534
  ...opts,
18342
18535
  workspaceRoot: args2.workspaceRoot,
18343
18536
  commandOutputLimit: args2.commandOutputLimit,
18344
18537
  execShellApproval: args2.execShellApproval
18345
18538
  });
18539
+ const result4 = await (args2.shellLifecycle ? args2.shellLifecycle(execute, args2.abortSignal) : execute());
18346
18540
  return { kind: "handled", result: result4 };
18347
18541
  }
18348
18542
  let parsedInput = args2.callArguments;
@@ -18386,6 +18580,7 @@ async function executeLocalToolWithPolicy(args2) {
18386
18580
  const gate = await runExecShellGates({
18387
18581
  toolName: decision.toolName,
18388
18582
  callArguments: args2.call.arguments,
18583
+ shellLifecycle: getLocalShellLifecycle(executor),
18389
18584
  execShellApproval: args2.execShellApproval,
18390
18585
  workspaceRoot: args2.workspaceRoot,
18391
18586
  commandOutputLimit: args2.commandOutputLimit,
@@ -18406,7 +18601,41 @@ async function executeLocalToolWithPolicy(args2) {
18406
18601
  );
18407
18602
  }
18408
18603
 
18604
+ // packages/agent-runtime/credentialSinkPolicy.ts
18605
+ var CREDENTIAL_REF_ALLOWED_SINKS = /* @__PURE__ */ new Set();
18606
+ function classifyCredentialRefSink(toolName, allowlist = CREDENTIAL_REF_ALLOWED_SINKS) {
18607
+ if (isCommandExecutingTool(toolName)) return "command";
18608
+ if (allowlist.has(toolName)) return "allow";
18609
+ return "confirm";
18610
+ }
18611
+ function buildCredentialSinkPermissionRequest(toolName) {
18612
+ return {
18613
+ id: `permission-credential-sink-${toolName}`,
18614
+ tool: toolName,
18615
+ action: "credential_unwrap_tool_use",
18616
+ title: "\u786E\u8BA4\u5728\u5DE5\u5177\u53C2\u6570\u4E2D\u4F7F\u7528\u51ED\u636E",
18617
+ body: `\u8BE5\u5DE5\u5177\uFF08${toolName}\uFF09\u7684\u53C2\u6570\u8981\u643A\u5E26\u51ED\u636E\uFF0C\u786E\u8BA4\u540E\u4EC5\u672C\u6B21\u8C03\u7528\u89E3\u5305\uFF1B\u660E\u6587\u4E0D\u4F1A\u5199\u5165\u5386\u53F2\u6216\u8BB0\u5F55\u3002`,
18618
+ suggestedRule: {
18619
+ scope: "once",
18620
+ pattern: { capability: "credential_use", target: "tool_arguments" }
18621
+ }
18622
+ };
18623
+ }
18624
+
18409
18625
  // packages/agent-runtime/toolCallTransaction.ts
18626
+ function buildCredentialSinkRefusal(toolName, gateId) {
18627
+ return {
18628
+ content: `\u9700\u8981\u7528\u6237\u786E\u8BA4\u624D\u80FD\u628A\u51ED\u636E\u7528\u4E8E ${toolName}\u3002\u53EF\u6539\u8D70\u53D7\u63A7\u901A\u9053\uFF1Anolo secret run --with <name> -- <cmd>\u3002`,
18629
+ metadata: {
18630
+ cancelled: true,
18631
+ actionGateResult: {
18632
+ status: "cancelled",
18633
+ ...gateId ? { gateId } : {}
18634
+ }
18635
+ }
18636
+ };
18637
+ }
18638
+ var credentialGateQueues = /* @__PURE__ */ new WeakMap();
18410
18639
  var LOCAL_TURN_ABORTED_CODE = "LOCAL_TURN_ABORTED";
18411
18640
  function buildAbortedError() {
18412
18641
  const error = new Error("local agent turn aborted by user");
@@ -18655,32 +18884,72 @@ async function executeToolCall(args2) {
18655
18884
  const rawArguments = toolCall.function.arguments;
18656
18885
  let unwrappedArguments = rawArguments;
18657
18886
  if (typeof rawArguments === "string" && rawArguments.includes("$nolo_cred:") && input.adapter.credentialBroker) {
18658
- if (isCommandExecutingTool(toolName)) {
18887
+ const sink = classifyCredentialRefSink(toolName);
18888
+ if (sink === "command") {
18659
18889
  throw new Error(
18660
18890
  `[security] \u62D2\u7EDD\u5728 ${toolName} \u547D\u4EE4\u884C\u4E2D\u76F4\u63A5\u89E3\u5305\u51ED\u8BC1\u5F15\u7528 ($nolo_cred:)\uFF0C\u4EE5\u9632\u5FA1\u63D0\u793A\u8BCD\u6CE8\u5165\u5916\u6CC4\u98CE\u9669\u3002\u8BF7\u4F7F\u7528\u53D7\u63A7\u8BA4\u8BC1\u901A\u9053\u3002`
18661
18891
  );
18662
18892
  }
18663
- unwrappedArguments = await unwrapToolArguments(rawArguments, input.adapter.credentialBroker);
18664
- }
18665
- const executePromise = input.adapter.executeTool({
18666
- id: toolCall.id,
18667
- name: toolName,
18668
- arguments: unwrappedArguments,
18669
- ...userInputText ? { userInput: userInputText } : {},
18670
- ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {},
18671
- ...args2.runToolNames ? { runToolNames: args2.runToolNames } : {},
18672
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
18673
- // 定档信号:来源只由 turn 输入推导,不让模型/参数层参与。
18674
- // 三级判据见 plan §10——后台运行优先于子 run(后台派发的子 run 仍是
18675
- // 无人值守的,按 automation 报更保守)。
18676
- origin: input.background ? "automation" : input.runKind === "subtask" ? "agent" : "chat"
18677
- }, {
18678
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
18679
- ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {}
18680
- });
18681
- const execStartedAtMs = Date.now();
18682
- toolResult = await runAbortableToolTask(input, executePromise, toolName);
18683
- toolExecMs = Date.now() - execStartedAtMs;
18893
+ if (sink === "confirm") {
18894
+ if (input.onActionGate) {
18895
+ const permissionRequest = buildCredentialSinkPermissionRequest(toolName);
18896
+ const gate = {
18897
+ ...permissionRequest,
18898
+ // [M1] id 带上 toolCall.id,避免同一轮同一工具两次请求 id 碰撞(对齐既有 ask 分支约定)。
18899
+ id: `${permissionRequest.id}-${toolCall.id}`,
18900
+ kind: "confirm",
18901
+ toolName,
18902
+ toolCallId: toolCall.id
18903
+ };
18904
+ const onActionGate = input.onActionGate;
18905
+ const prev = credentialGateQueues.get(input) ?? Promise.resolve();
18906
+ const next = prev.catch(() => void 0).then(
18907
+ () => runAbortableToolTask(
18908
+ input,
18909
+ onActionGate(gate),
18910
+ `${toolName} credential confirmation`
18911
+ )
18912
+ );
18913
+ credentialGateQueues.set(input, next);
18914
+ const replacement = await next.finally(() => {
18915
+ if (credentialGateQueues.get(input) === next) {
18916
+ credentialGateQueues.delete(input);
18917
+ }
18918
+ });
18919
+ if (replacement !== void 0 && isCompletedActionGateResult(replacement?.metadata?.actionGateResult)) {
18920
+ unwrappedArguments = await unwrapToolArguments(rawArguments, input.adapter.credentialBroker);
18921
+ } else {
18922
+ toolResult = buildCredentialSinkRefusal(toolName, gate.id);
18923
+ }
18924
+ } else {
18925
+ toolResult = buildCredentialSinkRefusal(toolName);
18926
+ }
18927
+ }
18928
+ if (sink === "allow") {
18929
+ unwrappedArguments = await unwrapToolArguments(rawArguments, input.adapter.credentialBroker);
18930
+ }
18931
+ }
18932
+ if (!toolResult) {
18933
+ const executePromise = input.adapter.executeTool({
18934
+ id: toolCall.id,
18935
+ name: toolName,
18936
+ arguments: unwrappedArguments,
18937
+ ...userInputText ? { userInput: userInputText } : {},
18938
+ ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {},
18939
+ ...args2.runToolNames ? { runToolNames: args2.runToolNames } : {},
18940
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
18941
+ // 定档信号:来源只由 turn 输入推导,不让模型/参数层参与。
18942
+ // 三级判据见 plan §10——后台运行优先于子 run(后台派发的子 run 仍是
18943
+ // 无人值守的,按 automation 报更保守)。
18944
+ origin: input.background ? "automation" : input.runKind === "subtask" ? "agent" : "chat"
18945
+ }, {
18946
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {},
18947
+ ...input.runtimeContext ? { runtimeContext: input.runtimeContext } : {}
18948
+ });
18949
+ const execStartedAtMs = Date.now();
18950
+ toolResult = await runAbortableToolTask(input, executePromise, toolName);
18951
+ toolExecMs = Date.now() - execStartedAtMs;
18952
+ }
18684
18953
  }
18685
18954
  if (tailDropNotice) {
18686
18955
  toolResult = {
@@ -18695,6 +18964,15 @@ ${toolResult.content}` : toolResult.content,
18695
18964
  toolCallId: toolCall.id,
18696
18965
  metadata: toolResult.metadata
18697
18966
  });
18967
+ if (actionGate && !input.onActionGate && actionGate.kind === "input") {
18968
+ toolResult = {
18969
+ content: "\u5F53\u524D\u8FD0\u884C\u73AF\u5883\u6CA1\u6709\u4EA4\u4E92\u8F93\u5165\u9762\u677F\uFF0C\u65E0\u6CD5\u5F39\u51FA\u51ED\u636E\u586B\u5199\u754C\u9762\uFF0C\u8BE5\u8BF7\u6C42\u5DF2\u88AB\u53D6\u6D88\u3002\u8BF7\u8BA9\u7528\u6237\u5728\u684C\u9762\u7AEF\u6216 Web \u7AEF\u754C\u9762\u63D0\u4F9B\u51ED\u636E\uFF0C\u6216\u76F4\u63A5\u5728\u5BF9\u8BDD\u4E2D\u53D1\u9001\u51ED\u636E\u5185\u5BB9\uFF1B\u51ED\u636E\u4FDD\u5B58\u540E\u7528 nolo secret run --with <NAME> -- <cmd> \u6D88\u8D39\u3002",
18970
+ metadata: {
18971
+ cancelled: true,
18972
+ actionGateResult: { gateId: actionGate.id, status: "cancelled" }
18973
+ }
18974
+ };
18975
+ }
18698
18976
  if (actionGate && input.onActionGate) {
18699
18977
  const replacement = await runAbortableToolTask(input, input.onActionGate(actionGate), `${toolName} action gate`);
18700
18978
  if (replacement) {
@@ -18788,10 +19066,15 @@ function applyPersistedTurnInput(messages, persistedInput, persistedInputReferen
18788
19066
  }
18789
19067
  function createTurnTranscript(args2) {
18790
19068
  let working = args2.initialWorking;
18791
- const raw = args2.initialWorking.slice(
19069
+ let raw = args2.initialWorking.slice(
18792
19070
  args2.prefixCount + args2.historyCount
18793
19071
  );
18794
19072
  const cap = args2.capToolMessage ?? ((msg) => msg);
19073
+ function retract(callback4) {
19074
+ const predicate = callback4;
19075
+ working = working.filter((msg) => !predicate(msg));
19076
+ raw = raw.filter((msg) => !predicate(msg));
19077
+ }
18795
19078
  return {
18796
19079
  working: () => working,
18797
19080
  replaceWorkingHistory: (nextWorkingView) => {
@@ -18810,7 +19093,13 @@ function createTurnTranscript(args2) {
18810
19093
  raw.pop();
18811
19094
  return working.pop();
18812
19095
  },
18813
- persisted: (persistedInput, persistedInputReference) => applyPersistedTurnInput(raw.map(withoutToolImages), persistedInput, persistedInputReference)
19096
+ retract,
19097
+ persisted: (persistedInput, persistedInputReference) => {
19098
+ const firstUser = raw.find(
19099
+ (message) => message.role === "user"
19100
+ );
19101
+ return firstUser?.cliMessageId ? raw.map(withoutToolImages) : applyPersistedTurnInput(raw.map(withoutToolImages), persistedInput, persistedInputReference);
19102
+ }
18814
19103
  };
18815
19104
  }
18816
19105
 
@@ -18862,10 +19151,10 @@ function createTurnUsageLedger(args2) {
18862
19151
  recordProviderCall: ({ usage, model, provider, telemetry }) => {
18863
19152
  turnUsage = mergeTurnUsage(turnUsage, usage);
18864
19153
  lastContextUsage = usage;
18865
- if (usage && Object.keys(usage).length > 0) {
19154
+ if (usage && Object.keys(usage).length > 0 || telemetry?.requestState) {
18866
19155
  providerRecords.push({
18867
- callId: resolveRecordCallId(usage),
18868
- usage,
19156
+ callId: resolveRecordCallId(usage ?? {}),
19157
+ usage: usage ?? {},
18869
19158
  model: model || args2.model || "unknown",
18870
19159
  ...provider || args2.provider ? { provider: provider || args2.provider } : {},
18871
19160
  ...telemetry && Object.keys(telemetry).length > 0 ? { telemetry } : {},
@@ -18896,7 +19185,42 @@ function createTurnUsageLedger(args2) {
18896
19185
  }
18897
19186
 
18898
19187
  // packages/agent-runtime/leafFinalHandoff.ts
18899
- var LEAF_FINAL_HANDOFF_INSTRUCTIONS = `Leaf subtask final: return only the result, blockers/findings, and minimal evidence the parent needs. Do not narrate your process or restate the task. Keep success minimal (a one-line result is enough); on failure include the blocker, severity when relevant, and an evidence reference (file:line, test command, commit, artifact, or run/dialog). If the task explicitly requires a long-form deliverable (document, translation, dataset, code, or other artifact), provide that deliverable in full and keep only the handoff around it concise. During execution, follow the brief as given: report only results incremental to the brief, new evidence, overturned assumptions, and remaining questions; only when new evidence would materially change the goal, risk, irreversibility, or DoD should you surface it as a blocker/finding \u2014 do not silently expand scope or re-litigate the settled direction. Keep success concise, but include evidence for required DoD items.`;
19188
+ var LEAF_FINAL_HANDOFF_INSTRUCTIONS = `Leaf subtask final: return only the result, blockers/findings, and minimal evidence the parent needs. Do not narrate your process or restate the task. Keep success minimal (a one-line result is enough); on failure include the blocker, severity when relevant, and an evidence reference (file:line, test command, commit, artifact, or run/dialog). If the task explicitly requires a long-form deliverable (document, translation, dataset, code, or other artifact), provide that deliverable in full and keep only the handoff around it concise. During execution, follow the brief as given: report only results incremental to the brief, new evidence, overturned assumptions, and remaining questions; only when new evidence would materially change the goal, risk, irreversibility, or DoD should you surface it as a blocker/finding \u2014 do not silently expand scope or re-litigate the settled direction. Own investigation, implementation, related checks and failure fixes within the brief\u2019s scope and budget; leave goals and critical decisions to the parent. For required DoD checks, include the worktree, checked version (including uncommitted change scope), command, check scope, pass/fail and original-output reference; explicitly mark checks not run or evidence unavailable. Reuse valid same-version evidence; additional checks are warranted for version changes, missing/conflicting evidence or integration risk. Do not invent version metadata or treat a completion summary as independent review approval. Keep success concise without omitting required evidence.`;
19189
+
19190
+ // packages/agent-runtime/taskExecutionContract.ts
19191
+ var present = (value) => typeof value === "string" && value.trim().length > 0;
19192
+ function evaluateTaskExecutionContract(contract, outcome = {}) {
19193
+ if (!contract) return void 0;
19194
+ const result4 = (status, ...reasons) => ({ taskId: contract.taskId, status, reasons });
19195
+ if (outcome.stopped) return result4("blocked", "stopped");
19196
+ if (!present(contract.taskId) || !["owner", "implementation-leaf"].includes(contract.role) || !["active", "paused", "revoked"].includes(contract.authorization) || !["pending", "waived", "accepted", "unknown"].includes(contract.acceptance) || !["complete", "incomplete", "unknown"].includes(contract.implementation) || !["changed", "unchanged", "unknown"].includes(contract.changes)) {
19197
+ return result4("blocked", "invalid_contract");
19198
+ }
19199
+ if (contract.authorization === "revoked") return result4("blocked", "authorization_revoked");
19200
+ if (contract.authorization === "paused") return result4("blocked", "authorization_paused");
19201
+ if (outcome.executionFailed) return result4("blocked", "execution_failed");
19202
+ if (outcome.budgetExhausted) return result4("incomplete", "budget_exhausted");
19203
+ if (contract.implementation !== "complete") return result4("incomplete", "implementation_incomplete");
19204
+ if (contract.acceptance === "pending") return result4("waiting", "acceptance_pending");
19205
+ if (contract.acceptance === "unknown") return result4("blocked", "acceptance_unknown");
19206
+ if (contract.changes === "unknown") return result4("incomplete", "changes_unknown");
19207
+ if (contract.role === "implementation-leaf" || contract.changes === "unchanged") return result4("completed");
19208
+ if (!present(contract.revision)) return result4("incomplete", "revision_missing");
19209
+ const review = contract.review;
19210
+ if (!review || !present(review.evidenceRef)) return result4("incomplete", "review_missing");
19211
+ if (review.revision !== contract.revision) return result4("incomplete", "review_stale");
19212
+ if (!present(contract.authorInstanceId) || !present(review.reviewerInstanceId) || review.reviewerInstanceId === contract.authorInstanceId) return result4("blocked", "review_not_independent");
19213
+ if (review.verdict !== "APPROVE" || review.hasCriticalOrHigh !== false) return result4("blocked", "review_rejected");
19214
+ const commit = contract.commit;
19215
+ if (!commit || !present(commit.commitId) || !present(commit.evidenceRef)) return result4("incomplete", "commit_missing");
19216
+ if (commit.revision !== contract.revision) return result4("incomplete", "commit_stale");
19217
+ const merge4 = contract.localMerge;
19218
+ if (!merge4 || !present(merge4.targetBranch) || !present(merge4.mergeCommitId) || !present(merge4.evidenceRef)) {
19219
+ return result4("incomplete", "local_merge_missing");
19220
+ }
19221
+ if (merge4.sourceCommitId !== commit.commitId) return result4("incomplete", "local_merge_mismatch");
19222
+ return result4("completed");
19223
+ }
18900
19224
 
18901
19225
  // packages/agent-runtime/contextBlockScope.ts
18902
19226
  var isContextBlockScope = (value) => {
@@ -18912,6 +19236,34 @@ var normalizeContextBlockScopes = (legacyBlocks = void 0, scopes = void 0) => {
18912
19236
  return (Array.isArray(legacyBlocks) ? legacyBlocks : []).filter((block) => typeof block === "string").map((block) => block.trim()).filter((block) => block.length > 0).map((block) => ({ content: block, cacheScope: "turn" }));
18913
19237
  };
18914
19238
 
19239
+ // packages/agent-runtime/providerInputItemLimits.ts
19240
+ var PROVIDER_INPUT_ITEM_LIMITS = [
19241
+ {
19242
+ provider: "opendesign",
19243
+ endpointHost: "open-design.ai",
19244
+ // Upstream HTTP 400: "[code=context_length_exceeded] Input is too long:
19245
+ // conversation history exceeds the limit on input items (1003 > 1000 items)."
19246
+ // type=invalid_request_error, code=context_length_exceeded, param=messages.
19247
+ maxInputItems: 1e3
19248
+ }
19249
+ ];
19250
+ function resolveProviderInputItemLimit(args2) {
19251
+ const byProvider = PROVIDER_INPUT_ITEM_LIMITS.find(
19252
+ (entry) => entry.provider === args2.provider?.toLowerCase()
19253
+ );
19254
+ if (byProvider) return byProvider.maxInputItems;
19255
+ if (!args2.endpoint) return void 0;
19256
+ let host;
19257
+ try {
19258
+ host = new URL(args2.endpoint.includes("://") ? args2.endpoint : `https://${args2.endpoint}`).hostname.toLowerCase();
19259
+ } catch {
19260
+ return void 0;
19261
+ }
19262
+ return PROVIDER_INPUT_ITEM_LIMITS.find(
19263
+ (entry) => host === entry.endpointHost || host.endsWith(`.${entry.endpointHost}`)
19264
+ )?.maxInputItems;
19265
+ }
19266
+
18915
19267
  // packages/agent-runtime/localAutoCompaction.ts
18916
19268
  import { randomUUID } from "node:crypto";
18917
19269
  function toPlanCompressionMessages(history) {
@@ -18919,6 +19271,7 @@ function toPlanCompressionMessages(history) {
18919
19271
  id: typeof message.id === "string" && message.id.length > 0 ? message.id : `local-${index}`,
18920
19272
  role: message.role,
18921
19273
  content: message.content ?? "",
19274
+ ...typeof message.tool_call_id === "string" ? { tool_call_id: message.tool_call_id } : {},
18922
19275
  ...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {}
18923
19276
  }));
18924
19277
  }
@@ -18936,9 +19289,10 @@ function hashSummarySourceSlice(history, summarizedBeforeId) {
18936
19289
  const found = findSummaryAnchorIndex(bridged, summarizedBeforeId);
18937
19290
  if (found === -1) return void 0;
18938
19291
  const slice = bridged.slice(0, found + 1).map((message, index) => ({
18939
- ...message,
18940
19292
  id: `local-${index}`,
18941
- content: message.content ?? ""
19293
+ role: message.role,
19294
+ content: message.content ?? "",
19295
+ ...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {}
18942
19296
  }));
18943
19297
  return sha256Hex(JSON.stringify(slice));
18944
19298
  }
@@ -18976,37 +19330,87 @@ function projectHistoryWithSummary(args2) {
18976
19330
  if (found !== -1) startIndex = found + 1;
18977
19331
  }
18978
19332
  const projected = args2.history.slice(startIndex);
18979
- const summaryMsg = args2.summary.trim().length > 0 ? [buildLocalSummaryHistoryMessage(args2.summary)] : [];
18980
- return [...summaryMsg, ...projected];
18981
- }
18982
- var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
18983
- function isColdResume(history, nowMs) {
18984
- for (let i = history.length - 1; i >= 0; i -= 1) {
18985
- const at = history[i]?.createdAt;
18986
- if (typeof at === "number" && Number.isFinite(at)) {
18987
- return nowMs - at > COLD_RESUME_IDLE_MS;
19333
+ let latestUser = -1;
19334
+ for (let i = args2.history.length - 1; i >= 0; i--) {
19335
+ if (args2.history[i].role === "user") {
19336
+ latestUser = i;
19337
+ break;
18988
19338
  }
18989
19339
  }
18990
- return false;
19340
+ const preservedUser = latestUser >= 0 && latestUser < startIndex ? [args2.history[latestUser]] : [];
19341
+ const summaryMsg = args2.summary.trim().length > 0 ? [buildLocalSummaryHistoryMessage(args2.summary)] : [];
19342
+ return [...summaryMsg, ...preservedUser, ...projected];
18991
19343
  }
18992
- async function runSummaryFallback(fallback, content, options) {
19344
+ var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
19345
+ async function completeRoutedCompactionSummary(args2) {
19346
+ const budget = createSummaryBudget({ mode: "automatic", signal: args2.options.signal, deadlineMs: args2.options.deadlineMs });
18993
19347
  try {
18994
- const text2 = await fallback(content, options);
18995
- if (typeof text2 !== "string") return { summary: null, error: "non-string output" };
18996
- const trimmed = text2.trim();
18997
- return trimmed.length > 0 ? { summary: trimmed } : { summary: null, error: "empty summary" };
18998
- } catch (error) {
18999
- console.warn("[localLoop] auto-compaction summary fallback failed", {
19000
- attemptId: options.attemptId,
19001
- stage: "fallback"
19348
+ const options = { ...args2.options, signal: budget.signal, deadlineMs: budget.deadlineMs };
19349
+ const eligible = await budget.run(async () => {
19350
+ try {
19351
+ return await args2.fallback.preferPlatform?.(options) === true;
19352
+ } catch (error) {
19353
+ if (budget.signal.aborted || isAbortError(error) || error?.name === "TimeoutError") throw error;
19354
+ return false;
19355
+ }
19356
+ });
19357
+ let currentResult;
19358
+ const result4 = await runSummaryWithContract({
19359
+ budget,
19360
+ eligibility: { userId: "authenticated-account", authenticated: eligible, platformAvailable: eligible, credits: eligible ? 1 : void 0 },
19361
+ platform: {
19362
+ model: PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL,
19363
+ provider: "nolo",
19364
+ attemptId: options.attemptId,
19365
+ run: async () => args2.fallback.complete ? args2.fallback.complete(args2.content, options) : { text: await args2.fallback(args2.content, options) ?? "" }
19366
+ },
19367
+ current: { attemptId: options.attemptId, run: async () => {
19368
+ currentResult = await args2.current();
19369
+ return {
19370
+ text: currentResult.content ?? "",
19371
+ finishReason: currentResult.finish_reason,
19372
+ maxOutputTokens: options.maxOutputTokens,
19373
+ completionTokens: summaryCompletionTokens(currentResult.usage)
19374
+ };
19375
+ } }
19002
19376
  });
19003
- const message = error instanceof Error ? error.message : String(error);
19004
- return { summary: null, error: message };
19377
+ if (result4.kind === "error" || result4.kind === "cancelled") {
19378
+ throw Object.assign(new Error(result4.error.message), result4.error, {
19379
+ type: result4.error.errorType,
19380
+ name: result4.kind === "cancelled" ? "AbortError" : result4.error.category === "timeout" ? "TimeoutError" : "SummaryError"
19381
+ });
19382
+ }
19383
+ if (result4.kind === "truncated") throw {
19384
+ code: "SUMMARY_TRUNCATED",
19385
+ stage: "decode",
19386
+ finishReason: result4.completion?.finishReason,
19387
+ maxOutputTokens: result4.completion?.maxOutputTokens ?? options.maxOutputTokens,
19388
+ completionTokens: result4.completion?.completionTokens,
19389
+ outputChars: result4.completion?.text.length,
19390
+ responseStatus: result4.completion?.responseStatus,
19391
+ incompleteReason: result4.completion?.incompleteReason,
19392
+ model: result4.route === "platform" ? PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL : currentResult?.model,
19393
+ provider: result4.route === "platform" ? "nolo" : currentResult?.provider
19394
+ };
19395
+ return result4.route === "current" && currentResult ? { ...currentResult, summaryCompletion: result4.completion } : {
19396
+ content: result4.kind === "success" ? result4.text : "",
19397
+ model: PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL,
19398
+ provider: "nolo",
19399
+ summaryCompletion: result4.completion
19400
+ };
19401
+ } finally {
19402
+ budget.dispose();
19005
19403
  }
19006
19404
  }
19007
19405
  var COMPACTION_RETRY_BACKOFF_MS = 6e4;
19008
19406
  var COMPACTION_RETRY_BACKOFF_MAX_ENTRIES = 256;
19009
19407
  var compactionRetryBackoff = /* @__PURE__ */ new Map();
19408
+ var COMPACTION_RETRY_DELAYS_MS = [3e3, 8e3];
19409
+ var compactionRetryDelaysOverride;
19410
+ function compactionRetryDelayMs(retryCount) {
19411
+ const table = compactionRetryDelaysOverride ?? COMPACTION_RETRY_DELAYS_MS;
19412
+ return table[Math.min(retryCount, table.length - 1)];
19413
+ }
19010
19414
  function isCompactionBackedOff(dialogId, now) {
19011
19415
  for (const [key, until2] of compactionRetryBackoff) {
19012
19416
  if (until2 <= now) compactionRetryBackoff.delete(key);
@@ -19076,7 +19480,19 @@ async function maybeAutoCompactLocalHistory(args2) {
19076
19480
  reason = "hash-mismatch";
19077
19481
  }
19078
19482
  console.warn(
19079
- `[localLoop] invalidated dialog summary for ${dialogId} (${reason}); discarding and re-compressing`
19483
+ `[localLoop] invalidated dialog summary for ${dialogId} (${reason}); discarding and re-compressing`,
19484
+ // 诊断分项(**仅加日志,不改任何哈希/校验/重锚语义**):区分「历史真被改写」
19485
+ // 与「保存/重算口径不一致」。只记长度、条数、消息 id 与哈希前 8 位;
19486
+ // 消息正文与摘要正文一律不进日志。重算哈希只发生在失效分支内,
19487
+ // 健康路径零额外开销。
19488
+ {
19489
+ reason,
19490
+ historyLength: history.length,
19491
+ storedSourceCount: typeof invalidStored?.sourceCount === "number" ? invalidStored.sourceCount : null,
19492
+ summarizedBeforeId: typeof invalidStored?.summarizedBeforeId === "string" ? invalidStored.summarizedBeforeId : null,
19493
+ recomputedHashPrefix: hashSummarySourceSlice(history, invalidStored?.summarizedBeforeId)?.slice(0, 8) ?? null,
19494
+ storedHashPrefix: typeof invalidStored?.sourceHash === "string" && invalidStored.sourceHash ? invalidStored.sourceHash.slice(0, 8) : null
19495
+ }
19080
19496
  );
19081
19497
  stored = null;
19082
19498
  }
@@ -19085,19 +19501,34 @@ async function maybeAutoCompactLocalHistory(args2) {
19085
19501
  const legacyAnchorIndex = summarizedBeforeId ? findSummaryAnchorIndex(bridged, summarizedBeforeId) : -1;
19086
19502
  const planAnchorId = summarizedBeforeId && /^local-(0|[1-9]\d*)$/.test(summarizedBeforeId) && legacyAnchorIndex >= 0 ? bridged[legacyAnchorIndex].id : summarizedBeforeId;
19087
19503
  const contextWindow = typeof args2.contextWindow === "number" && Number.isFinite(args2.contextWindow) && args2.contextWindow > 0 ? args2.contextWindow : getModelContextWindow(args2.model ?? "");
19088
- const allMsgs = toPlanCompressionMessages(history);
19089
- const coldResume = isColdResume(history, args2.now?.() ?? Date.now());
19504
+ const allMsgs = toPlanCompressionMessages(prepareHistoryForNextTurn(history, args2.contextReferenceResolver));
19505
+ const maxInputItems = resolveProviderInputItemLimit({ provider: args2.provider, endpoint: args2.providerEndpoint });
19506
+ const force = args2.force === true || args2.requestOverBudget?.(
19507
+ existingSummary ? projectHistoryWithSummary({ history, summary: existingSummary, summarizedBeforeId }) : history
19508
+ ) === true;
19090
19509
  const plan = planCompression({
19091
19510
  allMsgs,
19092
19511
  summarizedBeforeId: planAnchorId,
19093
19512
  summary: existingSummary,
19094
19513
  contextWindow,
19095
- ...coldResume ? { coldResume: true, reason: "cold_resume" } : {},
19514
+ // 失败路径补救:预算裁剪即将丢消息时由 localLoop 传入 force,交由 planCompression
19515
+ // 的既有 force 语义产出待压缩切片(仍受最小条数/安全前缀约束)。
19516
+ force,
19517
+ ...force ? { reason: "budget_rescue" } : {},
19518
+ availableHistoryTokens: args2.availableHistoryTokens,
19519
+ preserveLatestUserByProjection: true,
19520
+ maxInputItems,
19096
19521
  ...args2.realContextUsagePercent !== void 0 ? { realContextUsagePercent: args2.realContextUsagePercent } : {}
19097
19522
  });
19523
+ const itemDiagnostics = maxInputItems !== void 0 ? {
19524
+ itemCount: plan.diagnostics?.itemCount,
19525
+ maxInputItems,
19526
+ triggeredByItemLimit: plan.diagnostics?.triggeredByItemLimit
19527
+ } : {};
19098
19528
  const projectExisting = () => {
19099
19529
  const diag = plan.diagnostics;
19100
19530
  const diagSnapshot = diag ? {
19531
+ ...itemDiagnostics,
19101
19532
  estimatedTokens: diag.totalUsed,
19102
19533
  historyBudget: diag.historyBudget,
19103
19534
  triggerRatio: diag.triggerRatio,
@@ -19105,7 +19536,7 @@ async function maybeAutoCompactLocalHistory(args2) {
19105
19536
  } : {};
19106
19537
  const skippedDecision = {
19107
19538
  outcome: "skipped",
19108
- skipReason: diag?.emptyReason === "no-pending" ? "no-pending" : diag?.emptyReason === "below-min-compress-count" ? "below-min-compress-count" : "below-trigger",
19539
+ skipReason: diag?.emptyReason === "no-pending" ? "no-pending" : diag?.emptyReason === "below-min-compress-count" ? "below-min-compress-count" : diag?.emptyReason === "no-safe-prefix" ? "no-safe-prefix" : "below-trigger",
19109
19540
  ...diagSnapshot,
19110
19541
  ...diag ? { pendingMsgCount: diag.pendingMsgCount } : {},
19111
19542
  ...diag?.compressCount !== void 0 ? { compressCount: diag.compressCount } : {}
@@ -19119,6 +19550,13 @@ async function maybeAutoCompactLocalHistory(args2) {
19119
19550
  }),
19120
19551
  compressed: true,
19121
19552
  summaryGenerated: false,
19553
+ selectedSummary: {
19554
+ outcome: "loaded",
19555
+ hash: sha256Hex(existingSummary),
19556
+ ...summarizedBeforeId ? { anchorId: summarizedBeforeId } : {},
19557
+ ...stored?.sourceHash ? { sourceHash: stored.sourceHash } : {},
19558
+ ...stored?.sourceCount !== void 0 ? { sourceCount: stored.sourceCount } : {}
19559
+ },
19122
19560
  decision: {
19123
19561
  outcome: "compressed",
19124
19562
  trigger: "existing-summary",
@@ -19139,6 +19577,7 @@ async function maybeAutoCompactLocalHistory(args2) {
19139
19577
  outcome: "skipped",
19140
19578
  skipReason: "retry-backoff",
19141
19579
  ...diag ? {
19580
+ ...itemDiagnostics,
19142
19581
  estimatedTokens: diag.totalUsed,
19143
19582
  historyBudget: diag.historyBudget,
19144
19583
  triggerRatio: diag.triggerRatio,
@@ -19147,17 +19586,48 @@ async function maybeAutoCompactLocalHistory(args2) {
19147
19586
  }
19148
19587
  };
19149
19588
  }
19150
- emitPhase({ kind: "compaction-start" });
19151
- const compactionAttemptId = randomUUID();
19152
- let compactionT0 = Date.now();
19153
- let compactionInputTokens;
19154
- let compactionPrevSummaryTokens;
19155
- let compactionInputSource;
19156
- let compactionPrevSummarySource;
19157
- let primaryElapsedMs;
19589
+ const msgsToCompress = plan.msgsToCompress;
19590
+ const bounded3 = buildBoundedCompactionRequest({
19591
+ msgsToCompress,
19592
+ previousSummary: existingSummary,
19593
+ contextWindow,
19594
+ canonicalizeToolName
19595
+ });
19596
+ if (bounded3.status === "blocked") {
19597
+ const detail = `bounded compaction blocked: ${bounded3.reason}`;
19598
+ console.warn(
19599
+ `[localLoop] ${detail}`,
19600
+ ...bounded3.diagnostics ? [bounded3.diagnostics] : []
19601
+ );
19602
+ return {
19603
+ ...projectExisting(),
19604
+ decision: { outcome: "skipped", skipReason: "bounded-request-blocked", detail }
19605
+ };
19606
+ }
19607
+ emitPhase({ kind: "compaction-start" });
19608
+ let compactionAttemptId = randomUUID();
19609
+ const maxAttempts = 3;
19610
+ let attempt = 0;
19611
+ let retryCount = 0;
19612
+ let attemptRoute = "current";
19613
+ let attemptTimingLogged = false;
19614
+ let compactionT0 = Date.now();
19615
+ let compactionInputTokens;
19616
+ let compactionPrevSummaryTokens;
19617
+ let compactionInputSource;
19618
+ let compactionPrevSummarySource;
19619
+ let primaryElapsedMs;
19158
19620
  let fallbackElapsedMs;
19159
19621
  let saveElapsedMs;
19622
+ let summaryDeadlineMs = Infinity;
19623
+ let summaryModel = args2.model;
19624
+ let summaryProvider = args2.provider;
19625
+ let summaryDiagnostics = {
19626
+ maxOutputTokens: bounded3.outputTokenBudget
19627
+ };
19160
19628
  const logCompactionTiming = (outcome, fields) => {
19629
+ if (attemptTimingLogged) return;
19630
+ attemptTimingLogged = true;
19161
19631
  if (compactionInputTokens === void 0 && compactionInputSource !== void 0) {
19162
19632
  compactionInputTokens = estimateTokenCount(compactionInputSource);
19163
19633
  }
@@ -19167,10 +19637,20 @@ async function maybeAutoCompactLocalHistory(args2) {
19167
19637
  console.log(
19168
19638
  `[compaction-timing] ${JSON.stringify({
19169
19639
  attemptId: compactionAttemptId,
19170
- model: fields.model ?? args2.model ?? null,
19640
+ attempt,
19641
+ maxAttempts,
19642
+ physicalAttempts: attempt,
19643
+ route: attemptRoute,
19644
+ model: fields.model ?? summaryModel ?? null,
19645
+ provider: summaryProvider ?? null,
19171
19646
  inputTokens: compactionInputTokens ?? null,
19172
19647
  prevSummaryTokens: compactionPrevSummaryTokens ?? null,
19173
- outputChars: fields.outputChars ?? null,
19648
+ outputChars: summaryDiagnostics.outputChars ?? fields.outputChars ?? null,
19649
+ finishReason: summaryDiagnostics.finishReason ?? null,
19650
+ responseStatus: summaryDiagnostics.responseStatus ?? null,
19651
+ incompleteReason: summaryDiagnostics.incompleteReason ?? null,
19652
+ maxOutputTokens: summaryDiagnostics.maxOutputTokens ?? null,
19653
+ completionTokens: summaryDiagnostics.completionTokens ?? null,
19174
19654
  elapsedMs: Date.now() - compactionT0,
19175
19655
  primaryElapsedMs: primaryElapsedMs ?? null,
19176
19656
  fallbackElapsedMs: fallbackElapsedMs ?? null,
@@ -19180,57 +19660,153 @@ async function maybeAutoCompactLocalHistory(args2) {
19180
19660
  );
19181
19661
  };
19182
19662
  try {
19663
+ let reportAttemptUsage2 = function(value) {
19664
+ const usage = value?.usage;
19665
+ if (usage && typeof usage === "object") args2.onAttemptUsage?.(usage);
19666
+ };
19667
+ var reportAttemptUsage = reportAttemptUsage2;
19183
19668
  if (args2.abortSignal?.aborted) {
19184
19669
  throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
19185
19670
  }
19186
- const provider = await args2.resolveProvider();
19187
- if (args2.abortSignal?.aborted) {
19188
- throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
19189
- }
19190
- const msgsToCompress = plan.msgsToCompress;
19191
- const messagesText = formatMessagesForSummaryWithTruncation(
19192
- msgsToCompress,
19193
- TOOL_RESULT_TRUNCATE_CHARS,
19194
- computeSummaryInputTokenCap(contextWindow)
19195
- );
19196
- const fileOpsText = formatFileOperationsFromMessages(
19197
- msgsToCompress,
19198
- canonicalizeToolName
19199
- );
19200
- const promptContent = buildCompactionUserContent({
19201
- previousSummary: existingSummary,
19202
- messagesText,
19203
- fileOpsText
19204
- });
19671
+ const promptContent = bounded3.promptContent;
19205
19672
  emitPhase({ kind: "waiting-provider" });
19206
- compactionInputSource = messagesText;
19673
+ compactionInputSource = promptContent;
19207
19674
  compactionPrevSummarySource = existingSummary;
19208
19675
  compactionT0 = Date.now();
19209
- const timeoutMs = typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : void 0;
19676
+ const timeoutMs = Math.min(6e4, typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : 6e4);
19677
+ const requestDeadlineMs = Date.now() + timeoutMs;
19678
+ summaryDeadlineMs = requestDeadlineMs;
19210
19679
  const requestController = new AbortController();
19211
19680
  let timeoutHandle;
19212
19681
  let abortListener;
19213
19682
  if (args2.abortSignal?.aborted) requestController.abort(args2.abortSignal.reason);
19214
- const request3 = provider.complete(
19215
- [{ role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT }, { role: "user", content: promptContent }],
19683
+ let currentOnly = false;
19684
+ const physicalRequest = async (route, operation) => {
19685
+ if (attempt >= maxAttempts) throw { code: "SUMMARY_ATTEMPT_LIMIT" };
19686
+ if (requestController.signal.aborted) throw requestController.signal.reason;
19687
+ attempt++;
19688
+ attemptRoute = route;
19689
+ attemptTimingLogged = false;
19690
+ compactionAttemptId = randomUUID();
19691
+ compactionT0 = Date.now();
19692
+ primaryElapsedMs = void 0;
19693
+ summaryDiagnostics = { maxOutputTokens: bounded3.outputTokenBudget };
19694
+ try {
19695
+ const value = await operation();
19696
+ reportAttemptUsage2(value);
19697
+ return value;
19698
+ } catch (error) {
19699
+ reportAttemptUsage2(error);
19700
+ const safe = classifySummaryError(error);
19701
+ summaryDiagnostics = {
19702
+ ...summaryDiagnostics,
19703
+ finishReason: safe.finishReason,
19704
+ responseStatus: safe.responseStatus,
19705
+ incompleteReason: safe.incompleteReason,
19706
+ completionTokens: safe.completionTokens,
19707
+ outputChars: safe.outputChars,
19708
+ maxOutputTokens: safe.maxOutputTokens ?? bounded3.outputTokenBudget
19709
+ };
19710
+ primaryElapsedMs = Date.now() - compactionT0;
19711
+ logCompactionTiming(safe.category === "cancelled" ? "aborted" : safe.category === "timeout" ? "timeout" : "provider-error", {});
19712
+ throw error;
19713
+ }
19714
+ };
19715
+ const currentRequest = async () => {
19716
+ currentOnly = true;
19717
+ const provider = await args2.resolveProvider();
19718
+ summaryModel = provider.model ?? args2.model;
19719
+ summaryProvider = args2.provider;
19720
+ if (requestController.signal.aborted) throw requestController.signal.reason ?? new DOMException("Summary cancelled", "AbortError");
19721
+ try {
19722
+ return await physicalRequest("current", () => provider.complete(
19723
+ [{ role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT }, { role: "user", content: promptContent }],
19724
+ {
19725
+ signal: requestController.signal,
19726
+ timeoutMs: Math.max(1, requestDeadlineMs - Date.now()),
19727
+ // 仅日志用的 attempt id:让 compaction 埋点与 transport 诊断日志 /
19728
+ // 服务端 x-request-id 可对上,用于关联请求级证据。不进 prompt/摘要/持久化。
19729
+ requestId: compactionAttemptId,
19730
+ // Room for factual handoff, exact commands and file operations.
19731
+ max_tokens: bounded3.outputTokenBudget,
19732
+ reasoning_effort: "low"
19733
+ }
19734
+ ));
19735
+ } catch (cause) {
19736
+ const safe = classifySummaryError(cause, { model: provider.model, provider: args2.provider ?? void 0, attemptId: compactionAttemptId });
19737
+ throw Object.assign(new Error(safe.message), safe, {
19738
+ type: safe.errorType,
19739
+ name: safe.category === "cancelled" ? "AbortError" : safe.category === "timeout" ? "TimeoutError" : "SummaryError"
19740
+ });
19741
+ }
19742
+ };
19743
+ const fallback = args2.summaryFallback?.preferPlatform ? args2.summaryFallback : void 0;
19744
+ let eligibility;
19745
+ const routedFallback = fallback ? Object.assign(
19746
+ async (content, options) => {
19747
+ summaryModel = PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL;
19748
+ summaryProvider = "nolo";
19749
+ return physicalRequest("platform", () => fallback(content, { ...options, attemptId: compactionAttemptId }));
19750
+ },
19216
19751
  {
19752
+ preferPlatform: (options) => eligibility ??= fallback.preferPlatform(options),
19753
+ ...fallback.complete ? { complete: async (content, options) => {
19754
+ summaryModel = PLATFORM_HOSTED_DEEPSEEK_FLASH_MODEL;
19755
+ summaryProvider = "nolo";
19756
+ return physicalRequest("platform", () => fallback.complete(content, { ...options, attemptId: compactionAttemptId }));
19757
+ } } : {}
19758
+ }
19759
+ ) : void 0;
19760
+ const requestOnce = () => routedFallback && !currentOnly ? completeRoutedCompactionSummary({
19761
+ fallback: routedFallback,
19762
+ content: promptContent,
19763
+ options: {
19217
19764
  signal: requestController.signal,
19218
- ...timeoutMs ? { timeoutMs } : {},
19219
- // 仅日志用的 attempt id:让 compaction 埋点与 transport 诊断日志 /
19220
- // 服务端 x-request-id 可对上,用于关联请求级证据。不进 prompt/摘要/持久化。
19221
- requestId: compactionAttemptId,
19222
- // Room for factual handoff, exact commands and file operations.
19223
- max_tokens: 8192,
19224
- reasoning_effort: "low"
19225
- }
19226
- ).then(
19765
+ deadlineMs: requestDeadlineMs,
19766
+ attemptId: compactionAttemptId,
19767
+ maxOutputTokens: bounded3.outputTokenBudget
19768
+ },
19769
+ current: currentRequest
19770
+ }) : currentRequest();
19771
+ const request3 = (async () => {
19772
+ for (; ; ) {
19773
+ try {
19774
+ return await requestOnce();
19775
+ } catch (error) {
19776
+ const safe = classifySummaryError(error);
19777
+ const terminal = isQuotaExhaustedError(safe.status ?? 0, safe.code, safe.errorType) || safe.category === "cancelled" || safe.category === "timeout" || ["PROVIDER_UNAVAILABLE", "SUMMARY_TRUNCATED", "INSUFFICIENT_BALANCE"].includes(safe.code ?? "") || safe.errorType === "insufficient_balance_error";
19778
+ const busy = !terminal && (safe.category === "http" && (safe.status === 503 || safe.status === 429) || /^(capacity|overload(?:ed)?|rate[_-]?limit(?:ed)?)(?:[_-](?:error|exceeded))?$/i.test(safe.errorType ?? "") || ["PLATFORM_LLM_BUSY", "RATE_LIMIT", "CAPACITY", "OVERLOAD", "RESOURCE_EXHAUSTED"].includes(safe.code ?? ""));
19779
+ const delayMs = compactionRetryDelayMs(retryCount);
19780
+ if (!busy || attempt >= maxAttempts || requestController.signal.aborted || requestDeadlineMs - Date.now() < delayMs + 5e3) throw error;
19781
+ primaryElapsedMs = Date.now() - compactionT0;
19782
+ logCompactionTiming("provider-error", {});
19783
+ emitPhase({ kind: "compaction-retry", attempt: attempt + 1, maxAttempts });
19784
+ await new Promise((resolve6, reject) => {
19785
+ const signal = requestController.signal;
19786
+ const cancel = () => {
19787
+ clearTimeout(timer);
19788
+ signal.removeEventListener("abort", cancel);
19789
+ reject(signal.reason);
19790
+ };
19791
+ const timer = setTimeout(() => {
19792
+ signal.removeEventListener("abort", cancel);
19793
+ resolve6();
19794
+ }, delayMs);
19795
+ signal.addEventListener("abort", cancel, { once: true });
19796
+ if (signal.aborted) cancel();
19797
+ });
19798
+ if (requestController.signal.aborted || requestDeadlineMs - Date.now() < 5e3) throw error;
19799
+ retryCount++;
19800
+ }
19801
+ }
19802
+ })().then(
19227
19803
  (value) => ({ kind: "done", value }),
19228
19804
  (error) => ({ kind: "provider-error", error })
19229
19805
  );
19230
19806
  const guards = [];
19231
19807
  if (timeoutMs) guards.push(new Promise((resolve6) => {
19232
19808
  timeoutHandle = setTimeout(() => {
19233
- requestController.abort();
19809
+ requestController.abort(new DOMException("Summary deadline exceeded", "TimeoutError"));
19234
19810
  resolve6({ kind: "timeout" });
19235
19811
  }, timeoutMs);
19236
19812
  }));
@@ -19247,80 +19823,123 @@ async function maybeAutoCompactLocalHistory(args2) {
19247
19823
  if (timeoutHandle) clearTimeout(timeoutHandle);
19248
19824
  if (abortListener) args2.abortSignal?.removeEventListener("abort", abortListener);
19249
19825
  const result4 = outcome.kind === "done" ? outcome.value : void 0;
19250
- let newSummary = result4?.finish_reason !== "length" && typeof result4?.content === "string" ? result4.content.trim() : "";
19251
- const primaryFailure = outcome.kind === "aborted" || args2.abortSignal?.aborted ? "aborted" : newSummary ? null : outcome.kind === "done" ? "provider-error" : outcome.kind;
19252
- if (primaryFailure === "aborted") {
19253
- throw Object.assign(new Error("auto-compaction aborted"), {
19254
- compactionReason: "aborted"
19255
- });
19256
- }
19257
- const summaryFallback = args2.summaryFallback;
19258
- let fallbackFailureDetail;
19259
- if (primaryFailure && summaryFallback) {
19260
- const fallbackT0 = Date.now();
19261
- const fallbackResult = await runSummaryFallback(
19262
- summaryFallback,
19263
- promptContent,
19264
- {
19265
- signal: args2.abortSignal,
19266
- deadlineMs: Date.now() + 3e4,
19267
- attemptId: compactionAttemptId
19268
- }
19269
- );
19270
- fallbackElapsedMs = Date.now() - fallbackT0;
19271
- if (args2.abortSignal?.aborted) {
19272
- throw Object.assign(new Error("auto-compaction aborted"), {
19273
- compactionReason: "aborted"
19274
- });
19275
- }
19276
- if (fallbackResult.summary) {
19277
- newSummary = fallbackResult.summary;
19278
- } else {
19279
- fallbackFailureDetail = fallbackResult.error ?? "no summary";
19280
- }
19826
+ if (result4?.model) summaryModel = result4.model;
19827
+ if (result4?.provider) summaryProvider = result4.provider;
19828
+ if (outcome.kind === "aborted") throw Object.assign(new Error("Summary cancelled"), { name: "AbortError", compactionReason: "aborted" });
19829
+ if (outcome.kind === "timeout") throw Object.assign(new Error("Summary deadline exceeded"), { compactionReason: "timeout" });
19830
+ if (outcome.kind === "provider-error") throw outcome.error;
19831
+ const completion = result4?.summaryCompletion;
19832
+ const diagnostic = classifySummaryError({
19833
+ finishReason: completion?.finishReason ?? result4?.finish_reason,
19834
+ responseStatus: completion?.responseStatus,
19835
+ incompleteReason: completion?.incompleteReason,
19836
+ maxOutputTokens: completion?.maxOutputTokens ?? bounded3.outputTokenBudget,
19837
+ completionTokens: completion?.completionTokens ?? summaryCompletionTokens(result4?.usage),
19838
+ outputChars: completion?.text.length ?? result4?.content?.length
19839
+ });
19840
+ summaryDiagnostics = {
19841
+ finishReason: diagnostic.finishReason,
19842
+ responseStatus: diagnostic.responseStatus,
19843
+ incompleteReason: diagnostic.incompleteReason,
19844
+ maxOutputTokens: diagnostic.maxOutputTokens,
19845
+ completionTokens: diagnostic.completionTokens,
19846
+ outputChars: diagnostic.outputChars
19847
+ };
19848
+ if (isTruncatedCompletion({ ...completion, finishReason: completion?.finishReason ?? result4?.finish_reason })) {
19849
+ throw { code: "SUMMARY_TRUNCATED", stage: "decode", ...summaryDiagnostics };
19281
19850
  }
19851
+ const newSummary = result4?.content?.trim() ?? "";
19282
19852
  if (!newSummary) {
19283
- const failureNow = args2.now?.() ?? Date.now();
19284
- recordCompactionFailure(dialogId, failureNow);
19285
- const primaryDetail = outcome.kind === "provider-error" ? outcome.error instanceof Error ? outcome.error.message : String(outcome.error) : outcome.kind === "done" ? "summary model returned empty content" : `auto-compaction timed out after ${timeoutMs ?? primaryElapsedMs}ms`;
19286
- const primaryDiagnostic = `primary failed (${primaryElapsedMs ?? 0}ms): ${primaryDetail}`;
19287
- const fallbackDiagnostic = fallbackElapsedMs !== void 0 ? `; fallback failed (${fallbackElapsedMs}ms): ${fallbackFailureDetail ?? "no summary"}` : "";
19288
- const combinedDetail = `${primaryDiagnostic}${fallbackDiagnostic}`;
19289
- if (outcome.kind === "done") {
19290
- console.warn(
19291
- "[localLoop] auto-compaction produced empty summary; keeping prior projection"
19292
- );
19293
- logCompactionTiming("empty", {});
19294
- const failureReason = "provider-error";
19295
- emitPhase({ kind: "compaction-failed", reason: failureReason });
19296
- const failureMessage = combinedDetail.slice(0, 200);
19297
- return {
19298
- ...projectExisting(),
19299
- failureMessage,
19300
- failureReason,
19301
- decision: {
19302
- outcome: "failed",
19303
- reason: failureReason,
19304
- detail: failureMessage
19305
- }
19306
- };
19853
+ recordCompactionFailure(dialogId, args2.now?.() ?? Date.now());
19854
+ logCompactionTiming("empty", { model: result4?.model });
19855
+ emitPhase({ kind: "compaction-end" });
19856
+ return { ...projectExisting(), failureMessage: "Summary empty", failureReason: "provider-error", decision: { outcome: "failed", reason: "provider-error", detail: "Summary empty", ...itemDiagnostics } };
19857
+ }
19858
+ const metricsReason = invalidSummary ? "invalid_summary" : "context_budget";
19859
+ const metrics = buildCompactionMetricsFromPlan({
19860
+ reason: metricsReason,
19861
+ previousSummary: existingSummary,
19862
+ plan: {
19863
+ msgsToCompress: bounded3.coveredMessages,
19864
+ msgsToKeep: [...msgsToCompress.slice(bounded3.coveredMessages.length), ...plan.msgsToKeep]
19865
+ },
19866
+ newSummary,
19867
+ summaryUsage: result4?.usage,
19868
+ summaryInput: {
19869
+ inputTokens: bounded3.inputTokens,
19870
+ capTokens: bounded3.capTokens,
19871
+ expandedMessages: bounded3.expandedMessages,
19872
+ omittedMessages: bounded3.omittedMessages
19873
+ },
19874
+ planCounts: {
19875
+ pendingMessageCount: plan.diagnostics?.pendingMsgCount,
19876
+ plannedCompressCount: plan.diagnostics?.compressCount
19307
19877
  }
19308
- const reason = primaryFailure ?? "provider-error";
19309
- throw Object.assign(new Error(combinedDetail), {
19310
- compactionReason: reason,
19311
- primaryElapsedMs,
19312
- fallbackElapsedMs
19878
+ });
19879
+ const projectedHistory = projectHistoryWithSummary({
19880
+ history,
19881
+ summary: newSummary,
19882
+ summarizedBeforeId: bounded3.summarizedBeforeId
19883
+ });
19884
+ const projectedMessages = toPlanCompressionMessages(prepareHistoryForNextTurn(projectedHistory, args2.contextReferenceResolver));
19885
+ const projectedTokens = projectedMessages.reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19886
+ const projectedRetained = projectedMessages.slice(1);
19887
+ metrics.retainedTokens = projectedRetained.reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19888
+ metrics.retainedCount = projectedRetained.length;
19889
+ const { effectiveRawMessageBudget } = resolveEffectiveCompactionBudgets({
19890
+ contextWindow,
19891
+ triggerRatio: plan.diagnostics.triggerRatio,
19892
+ historyBudget: contextWindow,
19893
+ rawMessageBudget: contextWindow,
19894
+ summaryTokens: 0,
19895
+ newSummaryTokenReserve: estimateTokenCount(newSummary)
19896
+ });
19897
+ const effectiveHistoryBudget = effectiveRawMessageBudget + estimateTokenCount(newSummary);
19898
+ const projectedItemCount = projectedHistory.length + 1;
19899
+ const withinSafetyBudget = projectedTokens <= effectiveHistoryBudget && (maxInputItems === void 0 || projectedItemCount <= Math.floor(maxInputItems * 0.6));
19900
+ if (!withinSafetyBudget) {
19901
+ console.warn("[localLoop] compaction projection remains above safety budget", {
19902
+ projectedTokens,
19903
+ effectiveHistoryBudget,
19904
+ ...maxInputItems !== void 0 ? { projectedItemCount, maxInputItems } : {}
19313
19905
  });
19314
19906
  }
19907
+ const oldProjection = projectExisting();
19908
+ const beforeProjectedTokens = toPlanCompressionMessages(prepareHistoryForNextTurn(oldProjection.history, args2.contextReferenceResolver)).reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19909
+ const historyBudget = Math.max(0, contextWindow - Math.min(8192, Math.max(1, Math.floor(contextWindow / 4))));
19910
+ const candidateOverBudget = args2.requestOverBudget ? args2.requestOverBudget(projectedHistory) : projectedTokens > historyBudget;
19911
+ if (projectedTokens >= beforeProjectedTokens || candidateOverBudget) {
19912
+ const failureMessage = `compaction candidate rejected: ${projectedTokens >= beforeProjectedTokens ? "no token reduction" : "above history budget"} (before=${beforeProjectedTokens}, after=${projectedTokens}, budget=${historyBudget})`;
19913
+ console.warn(`[localLoop] ${failureMessage}`);
19914
+ recordCompactionFailure(dialogId, args2.now?.() ?? Date.now());
19915
+ logCompactionTiming("empty", {});
19916
+ emitPhase({ kind: "compaction-end" });
19917
+ return {
19918
+ ...oldProjection,
19919
+ failureMessage,
19920
+ ...result4?.usage ? { usage: result4.usage } : {},
19921
+ decision: {
19922
+ outcome: "skipped",
19923
+ skipReason: "unsafe-projection",
19924
+ detail: failureMessage,
19925
+ ...itemDiagnostics,
19926
+ ...maxInputItems !== void 0 ? { projectedItemCount } : {}
19927
+ }
19928
+ };
19929
+ }
19315
19930
  const sourceMeta = await computeSummarySourceMetadata(
19316
19931
  toPlanCompressionMessages(history),
19317
- plan.newSummarizedBeforeId
19932
+ bounded3.summarizedBeforeId
19318
19933
  );
19934
+ if (Date.now() >= summaryDeadlineMs) throw Object.assign(new Error("Summary deadline exceeded"), { compactionReason: "timeout" });
19935
+ if (args2.abortSignal?.aborted) {
19936
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
19937
+ }
19319
19938
  const saveT0 = Date.now();
19320
19939
  await adapter.saveDialogSummary({
19321
19940
  dialogId,
19322
19941
  summary: newSummary,
19323
- summarizedBeforeId: plan.newSummarizedBeforeId,
19942
+ summarizedBeforeId: bounded3.summarizedBeforeId,
19324
19943
  // 内容寻址失效检测:存锚点切片哈希与长度,供下轮载入校验。
19325
19944
  sourceHash: sourceMeta?.sourceHash,
19326
19945
  sourceCount: sourceMeta?.sourceCount ?? 0,
@@ -19332,14 +19951,6 @@ async function maybeAutoCompactLocalHistory(args2) {
19332
19951
  )
19333
19952
  });
19334
19953
  saveElapsedMs = Date.now() - saveT0;
19335
- const metricsReason = invalidSummary ? "invalid_summary" : coldResume ? "cold_resume" : "context_budget";
19336
- const metrics = buildCompactionMetricsFromPlan({
19337
- reason: metricsReason,
19338
- previousSummary: existingSummary,
19339
- plan,
19340
- newSummary,
19341
- summaryUsage: result4?.usage
19342
- });
19343
19954
  console.log(formatCompactionMetricsLog(metrics));
19344
19955
  logCompactionTiming("success", {
19345
19956
  model: result4?.model ?? args2.model ?? null,
@@ -19347,21 +19958,27 @@ async function maybeAutoCompactLocalHistory(args2) {
19347
19958
  });
19348
19959
  emitPhase({ kind: "compaction-end" });
19349
19960
  return {
19350
- history: projectHistoryWithSummary({
19351
- history,
19352
- summary: newSummary,
19353
- summarizedBeforeId: plan.newSummarizedBeforeId
19354
- }),
19961
+ history: projectedHistory,
19355
19962
  compressed: true,
19356
19963
  summaryGenerated: true,
19964
+ selectedSummary: {
19965
+ outcome: "generated",
19966
+ hash: sha256Hex(newSummary),
19967
+ anchorId: bounded3.summarizedBeforeId,
19968
+ ...sourceMeta?.sourceHash ? { sourceHash: sourceMeta.sourceHash } : {},
19969
+ ...sourceMeta?.sourceCount !== void 0 ? { sourceCount: sourceMeta.sourceCount } : {}
19970
+ },
19357
19971
  reason: metricsReason,
19358
- beforeTokens: metrics.previousSummaryTokens + metrics.compressedTokens,
19359
- afterTokens: metrics.newSummaryTokens + metrics.retainedTokens,
19972
+ beforeTokens: beforeProjectedTokens,
19973
+ afterTokens: projectedTokens,
19974
+ withinSafetyBudget,
19360
19975
  ...result4?.usage ? { usage: result4.usage } : {},
19361
19976
  metrics,
19362
19977
  decision: {
19363
19978
  outcome: "compressed",
19364
- trigger: invalidSummary ? "invalid-summary" : coldResume ? "cold-resume" : plan.diagnostics?.triggeredByRealUsage ? "real-usage" : "estimate",
19979
+ trigger: invalidSummary ? "invalid-summary" : plan.diagnostics?.triggeredByItemLimit ? "item-limit" : plan.diagnostics?.triggeredByRealUsage ? "real-usage" : "estimate",
19980
+ ...itemDiagnostics,
19981
+ ...maxInputItems !== void 0 ? { projectedItemCount } : {},
19365
19982
  ...plan.diagnostics ? {
19366
19983
  estimatedTokens: plan.diagnostics.totalUsed,
19367
19984
  historyBudget: plan.diagnostics.historyBudget,
@@ -19371,18 +19988,39 @@ async function maybeAutoCompactLocalHistory(args2) {
19371
19988
  }
19372
19989
  };
19373
19990
  } catch (error) {
19374
- console.warn("[localLoop] auto-compaction failed:", error);
19375
- const failureMessage = error instanceof Error && error.message ? error.message.slice(0, 200) : String(error).slice(0, 200);
19376
- const failureReason = error instanceof Error && "compactionReason" in error ? error.compactionReason : "provider-error";
19991
+ const safe = classifySummaryError(error, { model: summaryModel ?? void 0, provider: summaryProvider ?? void 0, attemptId: compactionAttemptId });
19992
+ summaryModel = safe.model ?? summaryModel;
19993
+ summaryProvider = safe.provider ?? summaryProvider;
19994
+ const failureReason = args2.abortSignal?.aborted || safe.category === "cancelled" || error?.compactionReason === "aborted" ? "aborted" : safe.category === "timeout" || error?.compactionReason === "timeout" ? "timeout" : "provider-error";
19995
+ const failureMessage = [safe.message, safe.code, safe.errorType, safe.balanceSource].filter(Boolean).join(" ");
19996
+ if (failureReason !== "aborted") console.warn("[localLoop] auto-compaction failed:", safe);
19997
+ summaryDiagnostics = {
19998
+ ...summaryDiagnostics,
19999
+ ...safe.finishReason === void 0 ? {} : { finishReason: safe.finishReason },
20000
+ ...safe.responseStatus === void 0 ? {} : { responseStatus: safe.responseStatus },
20001
+ ...safe.incompleteReason === void 0 ? {} : { incompleteReason: safe.incompleteReason },
20002
+ ...safe.maxOutputTokens === void 0 ? {} : { maxOutputTokens: safe.maxOutputTokens },
20003
+ ...safe.completionTokens === void 0 ? {} : { completionTokens: safe.completionTokens },
20004
+ ...safe.outputChars === void 0 ? {} : { outputChars: safe.outputChars }
20005
+ };
19377
20006
  logCompactionTiming(failureReason, {});
19378
20007
  const failureNow = args2.now?.() ?? Date.now();
19379
20008
  if (failureReason !== "aborted") recordCompactionFailure(dialogId, failureNow);
19380
- emitPhase({ kind: "compaction-failed", reason: failureReason });
20009
+ if (failureReason === "aborted") emitPhase({ kind: "compaction-end" });
20010
+ else emitPhase({ kind: "compaction-failed", reason: failureReason });
19381
20011
  return {
19382
20012
  ...projectExisting(),
19383
20013
  failureMessage,
19384
20014
  failureReason,
19385
- decision: { outcome: "failed", reason: failureReason, detail: failureMessage }
20015
+ failure: safe,
20016
+ decision: {
20017
+ outcome: "failed",
20018
+ reason: failureReason,
20019
+ detail: failureMessage,
20020
+ ...itemDiagnostics,
20021
+ estimatedTokens: plan.diagnostics?.totalUsed,
20022
+ historyBudget: plan.diagnostics?.historyBudget
20023
+ }
19386
20024
  };
19387
20025
  }
19388
20026
  }
@@ -19393,13 +20031,14 @@ function emitCompactionPhaseEvent(boundary, phase, scope3) {
19393
20031
  emitLoopEvent(boundary, { kind: "turn-phase", phase: "compacting", atMs, compactionScope: scope3 });
19394
20032
  emitLoopEvent(boundary, { kind: "compaction-start", atMs, scope: scope3 });
19395
20033
  } else if (phase.kind === "waiting-provider") emitLoopEvent(boundary, { kind: "turn-phase", phase: "waiting-provider", atMs, compactionScope: scope3 });
20034
+ else if (phase.kind === "compaction-retry") emitLoopEvent(boundary, { kind: "compaction-retry", atMs, scope: scope3, attempt: phase.attempt, maxAttempts: phase.maxAttempts });
19396
20035
  else if (phase.kind === "compaction-end") emitLoopEvent(boundary, { kind: "compaction-end", atMs, scope: scope3 });
19397
20036
  else emitLoopEvent(boundary, { kind: "compaction-failed", atMs, scope: scope3, reason: phase.reason });
19398
20037
  }
19399
20038
  function emitCompactionObservationEvent(boundary, compacted) {
19400
20039
  const decision = compacted.decision;
19401
20040
  if (decision?.outcome === "skipped") {
19402
- const isProtectionGap = decision.skipReason === "adapter-missing-summary-methods" || decision.skipReason === "load-summary-failed";
20041
+ const isProtectionGap = decision.skipReason === "bounded-request-blocked" || decision.skipReason === "unsafe-projection" || decision.skipReason === "no-safe-prefix" || decision.skipReason === "adapter-missing-summary-methods" || decision.skipReason === "load-summary-failed";
19403
20042
  const approachingBudget = decision.skipReason === "below-trigger" && typeof decision.estimatedTokens === "number" && typeof decision.historyBudget === "number" && decision.historyBudget > 0 && decision.estimatedTokens >= decision.historyBudget * 0.5;
19404
20043
  if (!isProtectionGap && !approachingBudget) return;
19405
20044
  }
@@ -19407,15 +20046,22 @@ function emitCompactionObservationEvent(boundary, compacted) {
19407
20046
  kind: "compaction",
19408
20047
  atMs: Date.now(),
19409
20048
  ...compacted.reason ? { reason: compacted.reason } : {},
20049
+ ...compacted.withinSafetyBudget === false ? { detail: "Projected history remains above the token or input-item safety budget" } : {},
19410
20050
  summaryGenerated: compacted.summaryGenerated,
19411
20051
  compressed: compacted.compressed,
19412
20052
  ...compacted.failureMessage ? { failed: true, detail: compacted.failureMessage } : {},
19413
- ...decision?.outcome === "skipped" ? { skipped: true, skipReason: decision.skipReason } : {},
20053
+ ...decision?.outcome === "skipped" ? { skipped: true, skipReason: decision.skipReason, ...decision.detail ? { detail: decision.detail } : {} } : {},
19414
20054
  ...decision && "trigger" in decision && decision.trigger ? { trigger: decision.trigger } : {},
19415
20055
  ...decision && "estimatedTokens" in decision && decision.estimatedTokens !== void 0 ? { estimatedTokens: decision.estimatedTokens } : {},
19416
20056
  ...decision && "historyBudget" in decision && decision.historyBudget !== void 0 ? { historyBudget: decision.historyBudget } : {},
19417
20057
  ...decision && "realUsageRatio" in decision && decision.realUsageRatio !== void 0 ? { realUsageRatio: decision.realUsageRatio } : {},
19418
20058
  ...decision && "triggerRatio" in decision && decision.triggerRatio !== void 0 ? { triggerRatio: decision.triggerRatio } : {},
20059
+ ...decision?.maxInputItems !== void 0 ? {
20060
+ itemCount: decision.itemCount,
20061
+ maxInputItems: decision.maxInputItems,
20062
+ triggeredByItemLimit: decision.triggeredByItemLimit,
20063
+ ...decision.projectedItemCount !== void 0 ? { projectedItemCount: decision.projectedItemCount } : {}
20064
+ } : {},
19419
20065
  ...compacted.beforeTokens !== void 0 ? { beforeTokens: compacted.beforeTokens } : {},
19420
20066
  ...compacted.afterTokens !== void 0 ? { afterTokens: compacted.afterTokens } : {},
19421
20067
  ...compacted.savedTokens !== void 0 ? { savedTokens: compacted.savedTokens } : {}
@@ -19434,7 +20080,7 @@ function createTurnCompactionController(args2) {
19434
20080
  }
19435
20081
  };
19436
20082
  const compactionDialogId = args2.continueDialogId ?? ephemeralDialogId;
19437
- const runInitial = async (history) => {
20083
+ const runCompaction = async (history, force) => {
19438
20084
  let realContextUsagePercent;
19439
20085
  if (args2.continueDialogId && typeof args2.adapter.loadLastContextUsage === "function") {
19440
20086
  try {
@@ -19457,15 +20103,21 @@ function createTurnCompactionController(args2) {
19457
20103
  adapter: args2.adapter,
19458
20104
  dialogId: args2.continueDialogId,
19459
20105
  history,
20106
+ contextReferenceResolver: args2.contextReferenceResolver,
19460
20107
  model: args2.model,
20108
+ provider: args2.provider,
20109
+ providerEndpoint: args2.providerEndpoint,
19461
20110
  resolveProvider: args2.resolveProvider,
19462
20111
  contextWindow: args2.contextWindow,
19463
20112
  realContextUsagePercent,
20113
+ force,
19464
20114
  abortSignal: args2.abortSignal,
19465
20115
  timeoutMs: args2.timeoutMs,
19466
20116
  summaryFallback: args2.summaryFallback,
20117
+ onAttemptUsage: args2.onUsage,
19467
20118
  onPhase: (phase) => emitPhase(phase, "initial")
19468
20119
  });
20120
+ args2.onSelectedSummary?.(compacted.selectedSummary ?? { outcome: "absent" });
19469
20121
  emitObservation(compacted);
19470
20122
  return { history: compacted.history, usage: compacted.usage };
19471
20123
  } catch (error) {
@@ -19473,11 +20125,25 @@ function createTurnCompactionController(args2) {
19473
20125
  return { history };
19474
20126
  }
19475
20127
  };
19476
- const maybeCompactInLoop = async () => {
20128
+ const runInitial = async (history) => runCompaction(history, false);
20129
+ const forceCompact = async (history) => {
20130
+ const result4 = await runCompaction(history, true);
20131
+ return { history: result4.history, applied: result4.history !== history, usage: result4.usage };
20132
+ };
20133
+ let lastCompactionOutcome;
20134
+ const prepareRequest = async (requestOverBudget, initial = false, availableHistoryTokens) => {
20135
+ lastCompactionOutcome = void 0;
19477
20136
  const contextUsage = args2.getContextUsage();
19478
- const inputTokens = contextUsage ? normalizeUsage(contextUsage).input_tokens : 0;
20137
+ let inputTokens = contextUsage ? normalizeUsage(contextUsage).input_tokens : 0;
20138
+ if (initial && args2.continueDialogId && args2.adapter.loadLastContextUsage) {
20139
+ try {
20140
+ inputTokens = (await args2.adapter.loadLastContextUsage(args2.continueDialogId))?.inputTokens ?? 0;
20141
+ } catch (error) {
20142
+ console.warn("[localLoop] loadLastContextUsage failed:", error);
20143
+ }
20144
+ }
19479
20145
  const ratio = inputTokens > 0 ? Math.min(1, inputTokens / args2.contextWindow) : void 0;
19480
- if (ratio !== void 0 && ratio < args2.compressionTriggerRatio * 0.85) {
20146
+ if (!requestOverBudget && ratio !== void 0 && ratio < args2.compressionTriggerRatio * 0.85 && resolveProviderInputItemLimit({ provider: args2.provider, endpoint: args2.providerEndpoint }) === void 0) {
19481
20147
  return;
19482
20148
  }
19483
20149
  try {
@@ -19490,18 +20156,22 @@ function createTurnCompactionController(args2) {
19490
20156
  // 锚点与 sourceHash 都必须按持久化形态计算——否则下轮从 store
19491
20157
  // 重载后重算 hash 必不匹配,摘要被判无效、白付一次摘要调用。
19492
20158
  history: args2.getCanonicalCompactionHistory(),
20159
+ requestOverBudget,
20160
+ availableHistoryTokens: availableHistoryTokens?.(),
20161
+ contextReferenceResolver: args2.contextReferenceResolver,
19493
20162
  model: args2.model,
20163
+ provider: args2.provider,
20164
+ providerEndpoint: args2.providerEndpoint,
19494
20165
  resolveProvider: args2.resolveProvider,
19495
20166
  contextWindow: args2.contextWindow,
19496
20167
  ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {},
19497
20168
  abortSignal: args2.abortSignal,
19498
20169
  timeoutMs: args2.timeoutMs,
19499
20170
  summaryFallback: args2.summaryFallback,
19500
- onPhase: (phase) => emitPhase(phase, "in-loop")
20171
+ onAttemptUsage: args2.onUsage,
20172
+ onPhase: (phase) => emitPhase(phase, initial ? "initial" : "in-loop")
19501
20173
  });
19502
- if (compacted.usage) {
19503
- args2.onUsage(compacted.usage);
19504
- }
20174
+ lastCompactionOutcome = { result: compacted };
19505
20175
  if (compacted.compressed) {
19506
20176
  const rebuilt = composeProviderMessages({
19507
20177
  prefix: args2.getWorkingPrefix(),
@@ -19516,16 +20186,65 @@ function createTurnCompactionController(args2) {
19516
20186
  }
19517
20187
  args2.replaceWorkingView(rebuilt.messages);
19518
20188
  }
20189
+ if (compacted.compressed || initial) {
20190
+ args2.onSelectedSummary?.(compacted.selectedSummary ?? { outcome: initial ? "absent" : "unknown" });
20191
+ }
19519
20192
  emitObservation(compacted);
19520
20193
  } catch (error) {
20194
+ lastCompactionOutcome = { ...lastCompactionOutcome, error: classifySummaryError(error) };
19521
20195
  console.warn("[localLoop] in-loop auto-compaction failed:", error);
19522
20196
  }
19523
20197
  };
19524
- return { runInitial, maybeCompactInLoop };
20198
+ return { getLastCompactionOutcome: () => lastCompactionOutcome, runInitial, maybeCompactInLoop: () => prepareRequest(), forceCompact, prepareRequest };
19525
20199
  }
19526
20200
 
19527
20201
  // packages/agent-runtime/providerCallTelemetry.ts
19528
20202
  import { createHash as createHash4 } from "node:crypto";
20203
+ var isCount = (v) => Number.isSafeInteger(v) && v >= 0;
20204
+ var isDigest = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
20205
+ var isMessageId = (v) => typeof v === "string" && /^(?:[0-9]{13}-[0-9]{3,6}|[0-9A-HJKMNP-TV-Z]{26}|local-[0-9]{1,12})$/.test(v);
20206
+ var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
20207
+ function projectContextProvenance(value) {
20208
+ if (!isRecord2(value) || value.schemaVersion !== 1) return void 0;
20209
+ const transports = ["unknown", "local-cli", "direct-openai-compatible", "codex-oauth", "codex-responses", "owner-oauth-proxy", "server-proxy"];
20210
+ const result4 = { schemaVersion: 1, transport: transports.includes(value.transport) ? value.transport : "unknown" };
20211
+ if (Array.isArray(value.phases)) {
20212
+ result4.phases = [];
20213
+ for (const item of value.phases.slice(0, 8)) {
20214
+ if (!isRecord2(item) || !["history-loaded", "summary-projected", "budget-trim", "final-request"].includes(item.phase)) continue;
20215
+ const phase = { phase: item.phase };
20216
+ if (["local", "remote", "partial", "none", "unknown"].includes(item.source)) phase.source = item.source;
20217
+ for (const key of ["count", "droppedCount"]) if (isCount(item[key])) phase[key] = item[key];
20218
+ for (const key of ["hash", "idsHash"]) if (isDigest(item[key])) phase[key] = item[key];
20219
+ for (const key of ["firstId", "lastId"]) if (isMessageId(item[key])) phase[key] = item[key];
20220
+ result4.phases.push(phase);
20221
+ }
20222
+ }
20223
+ if (isRecord2(value.summary)) {
20224
+ const input = value.summary;
20225
+ const outcomes = ["absent", "loaded", "generated", "invalid", "skipped", "failed", "unknown"];
20226
+ const summary = { outcome: outcomes.includes(input.outcome) ? input.outcome : "unknown" };
20227
+ for (const key of ["summaryId", "hash", "sourceHash"]) if (isDigest(input[key])) summary[key] = input[key];
20228
+ if (isMessageId(input.anchorId)) summary.anchorId = input.anchorId;
20229
+ if (isCount(input.sourceCount)) summary.sourceCount = input.sourceCount;
20230
+ result4.summary = summary;
20231
+ }
20232
+ return result4;
20233
+ }
20234
+ function contextPhase(phase, messages, options = {}) {
20235
+ const digest = (value) => createHash4("sha256").update(JSON.stringify(value) ?? "null").digest("hex");
20236
+ const ids = messages.map((m) => isRecord2(m) && isMessageId(m.cliMessageId) ? m.cliMessageId : null);
20237
+ return {
20238
+ phase,
20239
+ count: messages.length,
20240
+ hash: digest(messages),
20241
+ idsHash: digest(ids),
20242
+ ...ids[0] ? { firstId: ids[0] } : {},
20243
+ ...ids.at(-1) ? { lastId: ids.at(-1) } : {},
20244
+ ...options.source ? { source: options.source } : {},
20245
+ ...isCount(options.droppedCount) ? { droppedCount: options.droppedCount } : {}
20246
+ };
20247
+ }
19529
20248
  var shortHash = (value) => createHash4("sha1").update(value).digest("hex").slice(0, 12);
19530
20249
  var hashMessage = (message) => {
19531
20250
  let text2;
@@ -19565,6 +20284,7 @@ function createDivergeTracker(max4 = DIVERGE_TRACKER_MAX_DIALOGS) {
19565
20284
  var processDivergeTracker = createDivergeTracker();
19566
20285
  function buildRequestTelemetry(args2) {
19567
20286
  try {
20287
+ const provenance = projectContextProvenance(args2.provenance);
19568
20288
  const hashes = args2.messages.map(hashMessage);
19569
20289
  let toolResultChars = 0;
19570
20290
  let toolResultCount = 0;
@@ -19580,6 +20300,7 @@ function buildRequestTelemetry(args2) {
19580
20300
  const diverge = (args2.tracker ?? processDivergeTracker).observe(args2.dialogKey, hashes);
19581
20301
  return {
19582
20302
  ...args2.toolsHash ? { toolsHash: args2.toolsHash } : {},
20303
+ ...provenance ? { provenance } : {},
19583
20304
  msgCount: args2.messages.length,
19584
20305
  contentChars,
19585
20306
  toolResultChars,
@@ -19599,13 +20320,23 @@ function hashToolDefinitions(tools) {
19599
20320
  }
19600
20321
 
19601
20322
  // packages/agent-runtime/localLoop.ts
20323
+ var LocalContextCoverageError = class extends Error {
20324
+ constructor(droppedCount, compactionUsage, diagnostics) {
20325
+ super("Context protection: history cannot fit without losing messages not represented in the sending context. This pending provider request was not sent; canonical history and pending input are retained. Restore summary access/provider availability and retry this same input, or select a larger context window. An invalid summary must be rebuilt before retrying.");
20326
+ this.droppedCount = droppedCount;
20327
+ this.compactionUsage = compactionUsage;
20328
+ this.diagnostics = diagnostics;
20329
+ this.name = "LocalContextCoverageError";
20330
+ }
20331
+ code = "LOCAL_CONTEXT_COVERAGE_REQUIRED";
20332
+ };
19602
20333
  var LOCAL_AGENT_CONFIG_MISSING_CODE = "LOCAL_AGENT_CONFIG_MISSING";
19603
20334
  var LLM_REQUEST_TIMEOUT = "LLM_REQUEST_TIMEOUT";
19604
20335
  var DEFAULT_LLM_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
19605
20336
  var DEFAULT_COMPACTION_TIMEOUT_MS = 60 * 1e3;
19606
20337
  var DEFAULT_ROUND_BUDGET_WARNING_THRESHOLD = 25;
19607
20338
  function buildRoundBudgetWarningPrompt(threshold) {
19608
- return `[\u7CFB\u7EDF\u63D0\u793A] \u5F53\u524D\u5355\u6B21\u4EFB\u52A1\u7684\u8FDE\u7EED\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE ${threshold} \u8F6E\uFF0C\u8BF7\u8FDB\u884C\u8FDB\u5C55\u4E0E\u6210\u672C\u68C0\u67E5\uFF1A\u82E5\u5DF2\u6388\u6743\u76EE\u6807\u672A\u5B8C\u6210\u4E14\u6301\u7EED\u6709\u8FDB\u5C55\uFF0C\u5E94\u7EE7\u7EED\u63A8\u8FDB\uFF0C\u4E0D\u5F97\u4EC5\u56E0\u8F6E\u6570\u4EA4\u63A5\uFF1B\u4EC5\u5728\u76EE\u6807\u5DF2\u5B8C\u6210\u3001\u9047\u5230\u771F\u5B9E\u963B\u585E\u6216\u89E6\u53CA\u786C\u9884\u7B97\u65F6\u624D\u6536\u5C3E\uFF1B\u82E5\u8FDB\u5C55\u505C\u6EDE\uFF0C\u8BF7\u53CA\u65F6\u66F4\u6362\u7B56\u7565\u3002`;
20339
+ return `[\u7CFB\u7EDF\u63D0\u793A] \u5F53\u524D\u5355\u6B21\u4EFB\u52A1\u7684\u8FDE\u7EED\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE ${threshold} \u8F6E\uFF0C\u8BF7\u8FDB\u884C\u8FDB\u5C55\u4E0E\u6210\u672C\u68C0\u67E5\uFF1A\u82E5\u5DF2\u6388\u6743\u76EE\u6807\u672A\u5B8C\u6210\u4E14\u6301\u7EED\u6709\u8FDB\u5C55\uFF0C\u5E94\u7EE7\u7EED\u63A8\u8FDB\uFF0C\u4E0D\u5F97\u4EC5\u56E0\u8F6E\u6570\u4EA4\u63A5\uFF1B\u4EC5\u5728\u76EE\u6807\u5DF2\u5B8C\u6210\u3001\u9047\u5230\u771F\u5B9E\u963B\u585E\u6216\u7528\u6237\u660E\u786E\u505C\u6B62\u65F6\u624D\u6536\u5C3E\uFF1B\u82E5\u8FDB\u5C55\u505C\u6EDE\uFF0C\u8BF7\u53CA\u65F6\u66F4\u6362\u7B56\u7565\u3002`;
19609
20340
  }
19610
20341
  var ROUND_BUDGET_WARNING_PROMPT = buildRoundBudgetWarningPrompt(
19611
20342
  DEFAULT_ROUND_BUDGET_WARNING_THRESHOLD
@@ -19635,23 +20366,24 @@ var CONCURRENT_READONLY_TOOLS = /* @__PURE__ */ new Set([
19635
20366
  ]);
19636
20367
  function partitionToolCallsIntoBatches(toolCalls) {
19637
20368
  const batches = [];
19638
- let currentReadBatch = [];
19639
- for (let i = 0; i < toolCalls.length; i += 1) {
19640
- const toolCall = toolCalls[i];
19641
- const isReadOnly = CONCURRENT_READONLY_TOOLS.has(toolCall.function.name);
19642
- if (isReadOnly) {
19643
- currentReadBatch.push({ index: i, toolCall });
19644
- } else {
19645
- if (currentReadBatch.length > 0) {
19646
- batches.push({ isConcurrent: true, calls: currentReadBatch });
19647
- currentReadBatch = [];
19648
- }
19649
- batches.push({ isConcurrent: false, calls: [{ index: i, toolCall }] });
20369
+ let currentBatch = [];
20370
+ const flush2 = () => {
20371
+ if (currentBatch.length) batches.push({ isConcurrent: true, calls: currentBatch });
20372
+ currentBatch = [];
20373
+ };
20374
+ for (let index = 0; index < toolCalls.length; index += 1) {
20375
+ const toolCall = toolCalls[index];
20376
+ const contract = resolveToolExecutionContract(toolCall.function.name);
20377
+ const isReadOnly = contract.interaction === "none" && contract.concurrency === "safe";
20378
+ const legacyRead = contract.concurrency === "exclusive" && contract.interaction === "unknown" && CONCURRENT_READONLY_TOOLS.has(toolCall.function.name);
20379
+ if (toolCall.function.name === "startAgentRun" || !isReadOnly && !legacyRead) {
20380
+ flush2();
20381
+ batches.push({ isConcurrent: false, calls: [{ index, toolCall }] });
20382
+ continue;
19650
20383
  }
20384
+ currentBatch.push({ index, toolCall });
19651
20385
  }
19652
- if (currentReadBatch.length > 0) {
19653
- batches.push({ isConcurrent: true, calls: currentReadBatch });
19654
- }
20386
+ flush2();
19655
20387
  return batches;
19656
20388
  }
19657
20389
  function resolveLlmRequestTimeoutMs(input) {
@@ -19660,7 +20392,7 @@ function resolveLlmRequestTimeoutMs(input) {
19660
20392
  }
19661
20393
  function resolveCompactionTimeoutMs(input) {
19662
20394
  const raw = input.compactionTimeoutMs;
19663
- return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TIMEOUT_MS;
20395
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? Math.min(raw, DEFAULT_COMPACTION_TIMEOUT_MS) : DEFAULT_COMPACTION_TIMEOUT_MS;
19664
20396
  }
19665
20397
  function readHostCompactionSummaryFallback(adapter) {
19666
20398
  const value = adapter.compactionSummaryFallback;
@@ -19785,6 +20517,10 @@ function attachUsageRecordsToError(error, usageRecords) {
19785
20517
  }
19786
20518
  async function persistFailedLocalTurn(args2) {
19787
20519
  const errorMessage = toErrorMessage(args2.error);
20520
+ const businessCompletion = evaluateTaskExecutionContract(args2.input.taskExecutionContract, {
20521
+ stopped: args2.input.abortSignal?.aborted || args2.error instanceof Error && args2.error.code === LOCAL_TURN_ABORTED_CODE,
20522
+ executionFailed: true
20523
+ });
19788
20524
  try {
19789
20525
  const saved = await args2.adapter.saveTurn({
19790
20526
  agentKey: args2.agentKey,
@@ -19795,6 +20531,7 @@ async function persistFailedLocalTurn(args2) {
19795
20531
  toolCallCount: args2.toolCallCount ?? 0,
19796
20532
  error: true,
19797
20533
  errorMessage,
20534
+ ...businessCompletion ? { businessCompletion } : {},
19798
20535
  ...args2.usage ? { usage: args2.usage } : {}
19799
20536
  },
19800
20537
  ...args2.accountingUsage ? { accountingUsage: args2.accountingUsage } : {},
@@ -19828,6 +20565,18 @@ async function loadAdapterDialogHistory(adapter, dialogId, options) {
19828
20565
  }
19829
20566
  return { messages: await adapter.loadDialogHistory(dialogId) };
19830
20567
  }
20568
+ function combineRollbackError(primary, rollbackError) {
20569
+ const primaryError = primary instanceof Error ? primary : new Error(String(primary));
20570
+ const rollbackErr = rollbackError instanceof Error ? rollbackError : new Error(String(rollbackError));
20571
+ const combined = new AggregateError(
20572
+ [primaryError, rollbackErr],
20573
+ `${primaryError.message} (additionally, injection rollback failed: ${rollbackErr.message})`
20574
+ );
20575
+ if (primary && typeof primary === "object") {
20576
+ Object.assign(combined, primary);
20577
+ }
20578
+ return combined;
20579
+ }
19831
20580
  async function runLocalAgentTurn(input) {
19832
20581
  const legacyCallbacks = {
19833
20582
  onLoopEvent: input.onLoopEvent,
@@ -19841,17 +20590,6 @@ async function runLocalAgentTurn(input) {
19841
20590
  observationBoundary.attachCallbacks(legacyCallbacks);
19842
20591
  }
19843
20592
  const loopTiming = createLoopTiming();
19844
- const subtaskBudgetClock = input.subtaskBudgetNow ?? (() => Date.now());
19845
- const subtaskBudgetLimits = {
19846
- maxToolCalls: input.subtaskMaxToolCalls ?? input.subtaskBudgetLimits?.maxToolCalls ?? DEFAULT_SUBTASK_MAX_TOOL_CALLS,
19847
- maxWallClockMs: input.subtaskMaxWallClockMs ?? input.subtaskBudgetLimits?.maxWallClockMs ?? DEFAULT_SUBTASK_MAX_WALL_CLOCK_MS
19848
- };
19849
- const subtaskBudgetTracker = input.runKind === "subtask" ? createSubtaskBudgetTracker({
19850
- limits: subtaskBudgetLimits,
19851
- now: subtaskBudgetClock,
19852
- // 墙钟从 run 开始计(含 provider 初始化与首轮前的准备)。
19853
- startedAtMs: subtaskBudgetClock()
19854
- }) : null;
19855
20593
  try {
19856
20594
  loopTiming.mark("turnStart", 0);
19857
20595
  let agentConfig = await input.adapter.loadAgentConfig(input.agentRef);
@@ -19939,7 +20677,8 @@ async function runLocalAgentTurn(input) {
19939
20677
  if (input.strictContinueHistory && (loaded.source === "none" || loaded.source === "partial")) {
19940
20678
  throw new Error(`Strict continuation history unavailable: ${loaded.source}`);
19941
20679
  }
19942
- history = loaded.messages;
20680
+ const currentIds = new Set(input.userMessages?.map((message) => message.cliMessageId));
20681
+ history = loaded.messages.filter((message) => !currentIds.has(message.cliMessageId));
19943
20682
  dialogHistorySource = loaded.source;
19944
20683
  }
19945
20684
  } catch (error) {
@@ -20025,6 +20764,9 @@ async function runLocalAgentTurn(input) {
20025
20764
  // 平台风控纪律:无条件系统层块,沿用 session 占位约定。
20026
20765
  { content: PLATFORM_RISK_CONTROL_RULE, cacheScope: "session" },
20027
20766
  { content: buildConsultationModeBlock(), cacheScope: "session" },
20767
+ // Git 任务隔离:系统层固定前置,四条装配线同序(请教模式之后、身份信息之前;
20768
+ // skill 配置 disabled 也注入)。
20769
+ { content: WORKTREE_ISOLATION_PRELUDE, cacheScope: "session" },
20028
20770
  { content: identityBlock, cacheScope: "session" },
20029
20771
  ...guidanceScopes,
20030
20772
  // 回复行为准则:local 线(CLI 非交互 / 桌面 / TUI)的唯一注入边界。
@@ -20041,13 +20783,33 @@ async function runLocalAgentTurn(input) {
20041
20783
  }
20042
20784
  return resolvedProvider;
20043
20785
  };
20786
+ let resolvedSummaryProvider;
20787
+ const resolveSummaryProviderOnce = async () => {
20788
+ if (!resolvedSummaryProvider) {
20789
+ const provider = await input.adapter.resolveProvider(agentConfig, {
20790
+ textOnly: true,
20791
+ toolNames: [],
20792
+ prompt: ""
20793
+ });
20794
+ if (provider.textOnly !== true) {
20795
+ throw new Error("auto-compaction requires an isolated text-only provider");
20796
+ }
20797
+ resolvedSummaryProvider = provider;
20798
+ }
20799
+ return resolvedSummaryProvider;
20800
+ };
20044
20801
  const contextWindow = resolveAgentContextWindow(agentConfig);
20045
20802
  const compressionTriggerRatio = resolveCompressionTriggerRatio(contextWindow);
20803
+ let selectedSummary = { outcome: "unknown" };
20804
+ let summaryProjectedPhase = contextPhase("summary-projected", history);
20805
+ const historyLoadedPhase = contextPhase("history-loaded", history, { source: dialogHistorySource ?? "unknown" });
20046
20806
  const compaction = createTurnCompactionController({
20047
20807
  adapter: input.adapter,
20048
20808
  continueDialogId: input.continueDialogId,
20049
20809
  model: agentConfig.model,
20050
- resolveProvider: resolveProviderOnce,
20810
+ provider: agentConfig.provider,
20811
+ providerEndpoint: agentConfig.customProviderUrl,
20812
+ resolveProvider: resolveSummaryProviderOnce,
20051
20813
  contextWindow,
20052
20814
  compressionTriggerRatio,
20053
20815
  abortSignal: input.abortSignal,
@@ -20067,19 +20829,18 @@ async function runLocalAgentTurn(input) {
20067
20829
  )
20068
20830
  ],
20069
20831
  getWorkingPrefix: () => transcript.working().slice(0, prefixCount),
20070
- replaceWorkingView: (next) => transcript.replaceWorkingHistory(next),
20832
+ onSelectedSummary: (summary) => {
20833
+ selectedSummary = summary;
20834
+ },
20835
+ replaceWorkingView: (next) => {
20836
+ transcript.replaceWorkingHistory(next);
20837
+ summaryProjectedPhase = contextPhase("summary-projected", next);
20838
+ },
20071
20839
  contextReferenceResolver: input.adapter.host === "cli" ? input.contextReferenceResolver : void 0,
20072
20840
  onUsage: (usage) => usageLedger.addCompaction(usage)
20073
20841
  });
20074
- const initialCompaction = await compaction.runInitial(history);
20075
- history = initialCompaction.history;
20076
- const initialCompactionUsage = initialCompaction.usage;
20077
- loopTiming.mark("maybeAutoCompactLocalHistory", 0);
20078
- const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model, contextWindow);
20079
- if (trimmedHistory.droppedCount > 0) {
20080
- history = trimmedHistory.history;
20081
- }
20082
- loopTiming.mark("trimHistoryToContextBudget", 0);
20842
+ throwIfAborted(input);
20843
+ let budgetTrimPhase = contextPhase("budget-trim", history, { droppedCount: 0 });
20083
20844
  const builtMessages = buildMessages({
20084
20845
  prompt: agentConfig.prompt,
20085
20846
  contextBlockScopes: mergedContextBlockScopes,
@@ -20111,6 +20872,14 @@ async function runLocalAgentTurn(input) {
20111
20872
  ...msg,
20112
20873
  content: truncateToolOutputForContext(msg.content, contextWindow)
20113
20874
  } : msg;
20875
+ if (input.userMessages?.length) {
20876
+ const currentInputStart = builtMessages.prefixCount + history.length;
20877
+ const sanitizedUsers = await Promise.all(input.userMessages.map(async (message) => ({
20878
+ ...message,
20879
+ content: await sanitizeInbound(message.content)
20880
+ })));
20881
+ messages.splice(currentInputStart, messages.length - currentInputStart, ...sanitizedUsers);
20882
+ }
20114
20883
  const acceptToolImages = createToolImageLedger();
20115
20884
  const transcript = createTurnTranscript({
20116
20885
  initialWorking: messages,
@@ -20120,7 +20889,9 @@ async function runLocalAgentTurn(input) {
20120
20889
  });
20121
20890
  const userInputText = extractUserInputText(input.input);
20122
20891
  let toolCallCount = 0;
20892
+ let requestCalibrationRatio = 1;
20123
20893
  let result4 = { content: "" };
20894
+ let stopReason;
20124
20895
  const usageLedger = createTurnUsageLedger({
20125
20896
  model: agentConfig.model,
20126
20897
  provider: agentConfig.provider,
@@ -20129,59 +20900,78 @@ async function runLocalAgentTurn(input) {
20129
20900
  stablePrefixEstimatedTokens: builtMessages.stablePrefixEstimatedTokens
20130
20901
  } : {}
20131
20902
  });
20132
- if (initialCompactionUsage) usageLedger.addCompaction(initialCompactionUsage);
20133
20903
  let loopError;
20904
+ let consumedInjectedMessages = false;
20134
20905
  let round = 0;
20135
- let budgetExhaustion = null;
20136
- const executedToolNameLog = [];
20137
- const markBudgetExhausted = () => {
20138
- if (!subtaskBudgetTracker || budgetExhaustion) return;
20139
- const exhaustion = subtaskBudgetTracker.exhaustion();
20140
- if (!exhaustion) return;
20141
- budgetExhaustion = exhaustion;
20142
- emitLoopEvent(observationBoundary, {
20143
- kind: "budget-exhausted",
20144
- limit: exhaustion.limit,
20145
- atMs: exhaustion.atMs,
20146
- maxToolCalls: exhaustion.limits.maxToolCalls,
20147
- maxWallClockMs: exhaustion.limits.maxWallClockMs,
20148
- toolCallsExecuted: exhaustion.toolCallsExecuted,
20149
- elapsedMs: exhaustion.elapsedMs,
20150
- round
20151
- });
20152
- };
20153
20906
  let emptyAssistantRepairPending = false;
20154
20907
  let emptyAssistantRepairUsed = false;
20155
20908
  let reasoningEmptyRepairCount = 0;
20156
20909
  let skipFinalAppend = false;
20157
20910
  let partialContent = "";
20158
20911
  const progressGuard = createLocalLoopProgressGuard(input.progressGuardConfig);
20159
- const budgetAdmissionGate = () => {
20160
- if (!subtaskBudgetTracker) return true;
20161
- if (!subtaskBudgetTracker.isExhausted()) return true;
20162
- markBudgetExhausted();
20163
- return false;
20912
+ let pendingInjectionCommits = [];
20913
+ let inFlightTransactions = [];
20914
+ let rollbackInjectionCommits = async () => {
20164
20915
  };
20165
20916
  try {
20166
20917
  const provider = await resolveProviderOnce();
20167
- const applyPendingInjections = () => {
20918
+ const applyPendingInjections = async () => {
20168
20919
  if (!input.drainInjections) return false;
20169
- let pending = [];
20170
- try {
20171
- pending = input.drainInjections() ?? [];
20172
- } catch (error) {
20173
- console.warn("[localLoop] drainInjections failed:", error);
20174
- return false;
20175
- }
20920
+ const pending = await input.drainInjections() ?? [];
20176
20921
  let injected = false;
20177
- for (const text2 of pending) {
20178
- if (typeof text2 !== "string" || !text2.trim()) continue;
20179
- const safeText = scrubSecrets(text2).cleaned;
20180
- transcript.push({ role: "user", content: safeText });
20922
+ for (const entry of pending) {
20923
+ if (typeof entry === "string") {
20924
+ if (!entry.trim()) continue;
20925
+ transcript.push({ role: "user", content: scrubSecrets(entry).cleaned });
20926
+ } else if (entry && typeof entry === "object" && "messages" in entry) {
20927
+ for (const message of entry.messages) {
20928
+ const content = typeof message.content === "string" ? scrubSecrets(message.content).cleaned : Array.isArray(message.content) ? message.content.map((part) => part?.type === "text" && typeof part.text === "string" ? { ...part, text: scrubSecrets(part.text).cleaned } : part) : message.content;
20929
+ transcript.push({
20930
+ role: "user",
20931
+ content,
20932
+ ...message.cliMessageId ? { cliMessageId: message.cliMessageId } : {}
20933
+ });
20934
+ }
20935
+ pendingInjectionCommits.push({ commit: entry.commit, rollback: entry.rollback, messages: entry.messages });
20936
+ } else if (entry && typeof entry === "object" && "content" in entry) {
20937
+ const content = typeof entry.content === "string" ? scrubSecrets(entry.content).cleaned : Array.isArray(entry.content) ? entry.content.map((part) => part?.type === "text" && typeof part.text === "string" ? { ...part, text: scrubSecrets(part.text).cleaned } : part) : entry.content;
20938
+ transcript.push({
20939
+ role: "user",
20940
+ content,
20941
+ ...entry.cliMessageId ? { cliMessageId: entry.cliMessageId } : {}
20942
+ });
20943
+ } else {
20944
+ continue;
20945
+ }
20946
+ consumedInjectedMessages = true;
20181
20947
  injected = true;
20182
20948
  }
20183
20949
  return injected;
20184
20950
  };
20951
+ rollbackInjectionCommits = async (transactions) => {
20952
+ const errors = [];
20953
+ for (const transaction of transactions.slice().reverse()) {
20954
+ try {
20955
+ await transaction.rollback();
20956
+ } catch (rollbackError) {
20957
+ errors.push(rollbackError);
20958
+ }
20959
+ const ids = new Set(
20960
+ transaction.messages.map((message) => message.cliMessageId).filter((id) => Boolean(id))
20961
+ );
20962
+ if (ids.size > 0) {
20963
+ transcript.retract((msg) => {
20964
+ const id = msg.cliMessageId;
20965
+ return typeof id === "string" && ids.has(id);
20966
+ });
20967
+ }
20968
+ }
20969
+ if (errors.length > 0) {
20970
+ throw new Error(
20971
+ `injection rollback failed: ${errors.map((error) => error instanceof Error ? error.message : String(error)).join("; ")}`
20972
+ );
20973
+ }
20974
+ };
20185
20975
  const newDialogTelemetryKey = `new:${crypto.randomUUID()}`;
20186
20976
  let roundBudgetWarningInjected = false;
20187
20977
  const roundWarningThreshold = input.roundBudgetWarningThreshold ?? DEFAULT_ROUND_BUDGET_WARNING_THRESHOLD;
@@ -20189,12 +20979,47 @@ async function runLocalAgentTurn(input) {
20189
20979
  partialContent = "";
20190
20980
  throwIfAborted(input);
20191
20981
  loopTiming.mark("roundStart", round);
20192
- applyPendingInjections();
20982
+ await applyPendingInjections();
20193
20983
  if (roundWarningThreshold > 0 && round >= roundWarningThreshold && !roundBudgetWarningInjected) {
20194
20984
  const prompt = buildRoundBudgetWarningPrompt(roundWarningThreshold);
20195
20985
  transcript.push({ role: "user", content: prompt });
20196
20986
  roundBudgetWarningInjected = true;
20197
20987
  }
20988
+ const projectRequest = (source) => {
20989
+ const prepared = prepareMessagesForProviderCall(source);
20990
+ const base = filterImagePartsFromMessages(prepared.messages, supportsImages);
20991
+ return emptyAssistantRepairPending ? [...base, { role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }] : base;
20992
+ };
20993
+ const admissionProvider = await resolveProviderOnce();
20994
+ throwIfAborted(input);
20995
+ const requestOverBudget = (messages2) => !evaluateLocalRequestBudget({
20996
+ messages: messages2,
20997
+ contextWindow,
20998
+ descriptor: admissionProvider.requestBudget,
20999
+ calibrationRatio: requestCalibrationRatio
21000
+ }).fitsEstimate;
21001
+ const availableHistoryTokens = () => {
21002
+ const decision = evaluateLocalRequestBudget({
21003
+ messages: [],
21004
+ contextWindow,
21005
+ descriptor: admissionProvider.requestBudget,
21006
+ calibrationRatio: requestCalibrationRatio
21007
+ });
21008
+ const prefixTokens = estimateLocalRequestInput(projectRequest(transcript.working().slice(0, prefixCount)));
21009
+ return Math.max(0, Math.floor(decision.availableInputTokens / requestCalibrationRatio) - prefixTokens - (admissionProvider.requestBudget?.toolSchemaTokens ?? 0));
21010
+ };
21011
+ const historyTokensBeforeCompaction = Math.ceil(estimateLocalRequestInput(
21012
+ projectRequest(transcript.working()).slice(prefixCount)
21013
+ ) * requestCalibrationRatio);
21014
+ await compaction.prepareRequest((source) => {
21015
+ const projected = composeProviderMessages({
21016
+ prefix: transcript.working().slice(0, prefixCount),
21017
+ history: source,
21018
+ contextReferenceResolver: input.adapter.host === "cli" ? input.contextReferenceResolver : void 0
21019
+ });
21020
+ return requestOverBudget(projectRequest(projected.messages));
21021
+ }, round === 0, availableHistoryTokens);
21022
+ throwIfAborted(input);
20198
21023
  const preparedMessages = prepareMessagesForProviderCall(transcript.working());
20199
21024
  if (!supportsImages && hasImageInRuntimeMessages(preparedMessages.messages) && !imageDowngradeEmitted) {
20200
21025
  emitLoopEvent(observationBoundary, {
@@ -20204,11 +21029,27 @@ async function runLocalAgentTurn(input) {
20204
21029
  });
20205
21030
  imageDowngradeEmitted = true;
20206
21031
  }
20207
- const baseRequestMessages = filterImagePartsFromMessages(
20208
- preparedMessages.messages,
20209
- supportsImages
20210
- );
20211
- const requestMessages = emptyAssistantRepairPending ? [...baseRequestMessages, { role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }] : baseRequestMessages;
21032
+ const requestMessages = projectRequest(transcript.working());
21033
+ if (requestOverBudget(requestMessages)) {
21034
+ const budget = evaluateLocalRequestBudget({
21035
+ messages: requestMessages,
21036
+ contextWindow,
21037
+ descriptor: admissionProvider.requestBudget,
21038
+ calibrationRatio: requestCalibrationRatio
21039
+ });
21040
+ const largestMessageTokens = requestMessages.reduce((largest, message) => Math.max(
21041
+ largest,
21042
+ Math.ceil(estimateLocalRequestInput([message]) * requestCalibrationRatio)
21043
+ ), 0);
21044
+ throw new LocalContextCoverageError(0, void 0, classifyContextRefusal({
21045
+ outcome: compaction.getLastCompactionOutcome(),
21046
+ historyTokens: historyTokensBeforeCompaction,
21047
+ budget: budget.availableInputTokens,
21048
+ droppedCount: 0,
21049
+ largestMessageTokens
21050
+ }));
21051
+ }
21052
+ budgetTrimPhase = contextPhase("budget-trim", requestMessages, { droppedCount: 0 });
20212
21053
  const contextMetrics = {
20213
21054
  ...preparedMessages.metrics,
20214
21055
  messageCount: requestMessages.length,
@@ -20236,63 +21077,109 @@ async function runLocalAgentTurn(input) {
20236
21077
  // 无 dialogId 的新对话用本 turn 唯一键,避免不同新对话共用一个槽误报漂移。
20237
21078
  dialogKey: input.continueDialogId || newDialogTelemetryKey,
20238
21079
  messages: requestMessages,
21080
+ provenance: {
21081
+ schemaVersion: 1,
21082
+ transport: provider.transport ?? "unknown",
21083
+ phases: [historyLoadedPhase, summaryProjectedPhase, budgetTrimPhase, contextPhase("final-request", requestMessages)],
21084
+ summary: selectedSummary
21085
+ },
20239
21086
  toolsHash: safeToolNamesHash(agentTools)
20240
21087
  });
20241
- if (!budgetAdmissionGate()) {
20242
- break;
20243
- }
20244
21088
  resetToolImageDelivery(requestMessages.flatMap(toolImages));
20245
- result4 = await runCompleteWithTimeout({
20246
- provider,
20247
- messages: requestMessages,
20248
- options: {
21089
+ if (pendingInjectionCommits.length > 0) {
21090
+ inFlightTransactions = pendingInjectionCommits.splice(0);
21091
+ try {
21092
+ for (const transaction of inFlightTransactions) await transaction.commit();
21093
+ if (input.abortSignal?.aborted) throw buildAbortedError();
21094
+ } catch (error) {
21095
+ const toRollback = inFlightTransactions;
21096
+ inFlightTransactions = [];
21097
+ try {
21098
+ await rollbackInjectionCommits(toRollback);
21099
+ } catch (rbErr) {
21100
+ throw combineRollbackError(error, rbErr);
21101
+ }
21102
+ throw error;
21103
+ }
21104
+ }
21105
+ let providerInvoked = false;
21106
+ try {
21107
+ result4 = await runCompleteWithTimeout({
21108
+ provider: { complete: (messages2, options) => {
21109
+ providerInvoked = true;
21110
+ inFlightTransactions = [];
21111
+ return provider.complete(messages2, options);
21112
+ } },
21113
+ messages: requestMessages,
21114
+ options: {
21115
+ timeoutMs: resolveLlmRequestTimeoutMs(input),
21116
+ // 用户取消信号穿进 provider options:providerStreamRetry 据此在
21117
+ // 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
21118
+ // 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
21119
+ ...input.abortSignal ? { signal: input.abortSignal } : {},
21120
+ // 计费归因:续聊轮次带上 dialogId,platform proxy 才能把 token 记录
21121
+ // 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
21122
+ ...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
21123
+ ...shouldStreamDeltas ? { onTextDelta: (chunk) => {
21124
+ llmStreamActivity += 1;
21125
+ partialContent += chunk;
21126
+ observationBoundary.emit({
21127
+ kind: "text-delta",
21128
+ chunk,
21129
+ round,
21130
+ atMs: Date.now()
21131
+ });
21132
+ } } : {},
21133
+ ...shouldStreamReasoning ? { onReasoningDelta: (chunk) => {
21134
+ llmStreamActivity += 1;
21135
+ observationBoundary.emit({
21136
+ kind: "reasoning-delta",
21137
+ chunk,
21138
+ round,
21139
+ atMs: Date.now()
21140
+ });
21141
+ } } : {},
21142
+ ...shouldPassToolEvents ? { onToolEvent: (event) => {
21143
+ llmStreamActivity += 1;
21144
+ observationBoundary.emit({
21145
+ kind: "tool-event",
21146
+ event,
21147
+ round,
21148
+ atMs: Date.now()
21149
+ });
21150
+ } } : {},
21151
+ ...shouldPassToolEvents ? { toolEventRound: round } : {}
21152
+ },
20249
21153
  timeoutMs: resolveLlmRequestTimeoutMs(input),
20250
- // 用户取消信号穿进 provider options:providerStreamRetry 据此在
20251
- // 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
20252
- // 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
20253
- ...input.abortSignal ? { signal: input.abortSignal } : {},
20254
- // 计费归因:续聊轮次带上 dialogId,platform proxy 才能把 token 记录
20255
- // 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
20256
- ...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
20257
- ...shouldStreamDeltas ? { onTextDelta: (chunk) => {
20258
- llmStreamActivity += 1;
20259
- partialContent += chunk;
20260
- observationBoundary.emit({
20261
- kind: "text-delta",
20262
- chunk,
20263
- round,
20264
- atMs: Date.now()
20265
- });
20266
- } } : {},
20267
- ...shouldStreamReasoning ? { onReasoningDelta: (chunk) => {
20268
- llmStreamActivity += 1;
20269
- observationBoundary.emit({
20270
- kind: "reasoning-delta",
20271
- chunk,
20272
- round,
20273
- atMs: Date.now()
20274
- });
20275
- } } : {},
20276
- ...shouldPassToolEvents ? { onToolEvent: (event) => {
20277
- llmStreamActivity += 1;
20278
- observationBoundary.emit({
20279
- kind: "tool-event",
20280
- event,
20281
- round,
20282
- atMs: Date.now()
20283
- });
20284
- } } : {},
20285
- ...shouldPassToolEvents ? { toolEventRound: round } : {}
20286
- },
20287
- timeoutMs: resolveLlmRequestTimeoutMs(input),
20288
- activityVersion: () => llmStreamActivity,
20289
- round,
20290
- input,
20291
- boundary: observationBoundary,
20292
- context: contextMetrics,
20293
- providerName: agentConfig.provider,
20294
- model: provider.model
20295
- });
21154
+ activityVersion: () => llmStreamActivity,
21155
+ round,
21156
+ input,
21157
+ boundary: observationBoundary,
21158
+ context: contextMetrics,
21159
+ providerName: agentConfig.provider,
21160
+ model: provider.model
21161
+ });
21162
+ } catch (error) {
21163
+ if (!providerInvoked && inFlightTransactions.length > 0) {
21164
+ const toRollback = inFlightTransactions;
21165
+ inFlightTransactions = [];
21166
+ try {
21167
+ await rollbackInjectionCommits(toRollback);
21168
+ } catch (rbErr) {
21169
+ error = combineRollbackError(error, rbErr);
21170
+ }
21171
+ }
21172
+ if (providerInvoked) usageLedger.recordProviderCall({
21173
+ model: agentConfig.model,
21174
+ telemetry: {
21175
+ ...requestTelemetry,
21176
+ round,
21177
+ endTs: Date.now(),
21178
+ requestState: input.abortSignal?.aborted ? "cancelled" : "failed"
21179
+ }
21180
+ });
21181
+ throw error;
21182
+ }
20296
21183
  acceptToolImages.discard(requestMessages.flatMap(toolImages).filter((image) => toolImageDelivery(image) === "impossible"));
20297
21184
  acceptToolImages.commit(
20298
21185
  requestMessages.flatMap(toolImages).filter((image) => toolImageDelivery(image) === "delivered"),
@@ -20302,13 +21189,25 @@ async function runLocalAgentTurn(input) {
20302
21189
  const pending = toolImages(message).filter((image) => supportsImages && !toolImageDelivery(image));
20303
21190
  return pending.length ? { ...message, [TOOL_IMAGES]: pending } : stripToolImages(message);
20304
21191
  }));
21192
+ const actualInput = normalizeUsage(result4.usage).input_tokens;
21193
+ const estimatedInput = estimateLocalRequestInput(
21194
+ requestMessages,
21195
+ admissionProvider.requestBudget?.toolSchemaTokens ?? 0
21196
+ );
21197
+ requestCalibrationRatio = updateLocalRequestCalibration(requestCalibrationRatio, actualInput, estimatedInput);
20305
21198
  loopTiming.mark("llmCall", round);
20306
21199
  usageLedger.recordProviderCall({
20307
21200
  usage: result4.usage,
20308
21201
  model: result4.model,
20309
21202
  provider: result4.provider,
20310
- telemetry: finalizeCallTelemetry(requestTelemetry, result4, round, callStartedAt)
21203
+ telemetry: { ...finalizeCallTelemetry(requestTelemetry, result4, round, callStartedAt), requestState: "completed" }
20311
21204
  });
21205
+ if (!result4.content && result4.output?.some((block) => block.type === "text") && !result4.output.some((block) => block.type === "toolCall")) {
21206
+ result4 = {
21207
+ ...result4,
21208
+ content: result4.output.filter((block) => block.type === "text").map((block) => block.text).join("")
21209
+ };
21210
+ }
20312
21211
  const assistantGuardVerdict = progressGuard.observeAssistantResponse(result4);
20313
21212
  if (assistantGuardVerdict.action === "stall") {
20314
21213
  emitLoopEvent(observationBoundary, {
@@ -20326,11 +21225,11 @@ async function runLocalAgentTurn(input) {
20326
21225
  };
20327
21226
  break;
20328
21227
  }
20329
- if (result4.runtimeProviderFailure) {
21228
+ if (result4.error || result4.runtimeProviderFailure) {
20330
21229
  result4 = {
20331
21230
  ...result4,
20332
21231
  error: true,
20333
- errorMessage: result4.runtimeProviderFailure.message
21232
+ errorMessage: result4.runtimeProviderFailure?.message ?? result4.errorMessage
20334
21233
  };
20335
21234
  break;
20336
21235
  }
@@ -20393,13 +21292,14 @@ ${reasoningTailLog}
20393
21292
  ...result4.reasoning_content ? { reasoning_content: result4.reasoning_content } : {}
20394
21293
  };
20395
21294
  transcript.push(assistantMessage);
20396
- if (applyPendingInjections()) {
21295
+ if (await applyPendingInjections()) {
20397
21296
  loopTiming.mark("roundEnd", round);
20398
21297
  round += 1;
20399
21298
  continue;
20400
21299
  }
20401
21300
  transcript.popLast();
20402
21301
  }
21302
+ stopReason = "done";
20403
21303
  break;
20404
21304
  }
20405
21305
  const outputBlocks = result4.output ?? [];
@@ -20418,9 +21318,11 @@ ${reasoningTailLog}
20418
21318
  }
20419
21319
  }
20420
21320
  if (hasInlineExecutedTools) {
20421
- for (const blockMsg of blocksToOpenAiMessages(outputBlocks)) {
21321
+ for (const blockMsg of blocksToOpenAiMessages(outputBlocks, input.adapter.workspaceRoot)) {
20422
21322
  transcript.pushTool(blockMsg);
20423
21323
  }
21324
+ const inlineResults = outputBlocks.flatMap((block) => block.type === "toolCall" && block.result ? [block.result] : []);
21325
+ stopReason = inlineResults.some(isToolExecutionFailure) ? "error" : inlineResults.some(({ metadata }) => metadata?.uiAskChoice === true && metadata.resolved !== true) ? "pending" : "done";
20424
21326
  skipFinalAppend = true;
20425
21327
  break;
20426
21328
  }
@@ -20437,15 +21339,10 @@ ${reasoningTailLog}
20437
21339
  const executedToolResults = [];
20438
21340
  loopTiming.mark("toolLoopStart", round);
20439
21341
  const slotResults = new Array(toolCalls.length);
20440
- const runSingleTool = async ({ index, toolCall }) => {
20441
- throwIfAborted(input);
20442
- if (!budgetAdmissionGate()) {
20443
- skippedToolCalls.push(toolCall);
20444
- return;
20445
- }
20446
- subtaskBudgetTracker?.recordToolCallExecuted();
21342
+ const runSingleTool = async ({ index, toolCall }, executionInput = input, isClosed = () => false) => {
21343
+ throwIfAborted(executionInput);
20447
21344
  const { toolResult, toolExecMs } = await executeToolCall({
20448
- input,
21345
+ input: executionInput,
20449
21346
  toolCall,
20450
21347
  round,
20451
21348
  boundary: observationBoundary,
@@ -20458,12 +21355,12 @@ ${reasoningTailLog}
20458
21355
  ...result4.finish_reason ? { finishReason: result4.finish_reason } : {}
20459
21356
  }
20460
21357
  });
21358
+ if (isClosed()) return;
20461
21359
  slotResults[index] = {
20462
21360
  toolName: toolCall.function.name,
20463
21361
  toolResult,
20464
21362
  toolExecMs
20465
21363
  };
20466
- executedToolNameLog.push(toolCall.function.name);
20467
21364
  };
20468
21365
  const commitSlot = (i) => {
20469
21366
  const toolCall = toolCalls[i];
@@ -20482,7 +21379,8 @@ ${reasoningTailLog}
20482
21379
  content: formatToolMessageContent({
20483
21380
  toolName,
20484
21381
  content: toolResult.content,
20485
- metadata: observedMetadata
21382
+ metadata: observedMetadata,
21383
+ workspaceRoot: input.adapter.workspaceRoot
20486
21384
  }),
20487
21385
  tool_call_id: toolCall.id,
20488
21386
  toolName,
@@ -20494,14 +21392,36 @@ ${reasoningTailLog}
20494
21392
  } } : {}
20495
21393
  });
20496
21394
  };
20497
- const skippedToolCalls = [];
20498
21395
  const batches = partitionToolCallsIntoBatches(toolCalls);
20499
21396
  for (const batch of batches) {
20500
21397
  throwIfAborted(input);
20501
21398
  let batchError;
20502
21399
  try {
20503
21400
  if (batch.isConcurrent && batch.calls.length > 1) {
20504
- await Promise.all(batch.calls.map(runSingleTool));
21401
+ const controller = new AbortController();
21402
+ const batchInput = {
21403
+ ...input,
21404
+ abortSignal: input.abortSignal ? AbortSignal.any([input.abortSignal, controller.signal]) : controller.signal
21405
+ };
21406
+ let closed = false;
21407
+ let failed = false;
21408
+ let firstError;
21409
+ const tasks = batch.calls.map((call) => runAbortableToolTask(
21410
+ batchInput,
21411
+ runSingleTool(call, batchInput, () => closed),
21412
+ call.toolCall.function.name
21413
+ ).catch((error) => {
21414
+ if (!failed) {
21415
+ failed = true;
21416
+ firstError = error;
21417
+ closed = true;
21418
+ controller.abort();
21419
+ }
21420
+ throw error;
21421
+ }));
21422
+ await Promise.allSettled(tasks);
21423
+ closed = true;
21424
+ if (failed) throw firstError;
20505
21425
  } else {
20506
21426
  for (const call of batch.calls) {
20507
21427
  await runSingleTool(call);
@@ -20518,15 +21438,20 @@ ${reasoningTailLog}
20518
21438
  }
20519
21439
  if (batchError) throw batchError;
20520
21440
  }
20521
- for (const skipped of skippedToolCalls) {
20522
- transcript.pushTool({
20523
- role: "tool",
20524
- content: "[\u5B50\u4EFB\u52A1\u9884\u7B97\u4E0A\u9650\u5DF2\u89E6\u53D1] \u8BE5\u5DE5\u5177\u8C03\u7528\u672A\u6267\u884C\uFF1A\u5DF2\u8FBE\u5B50\u4EFB\u52A1\u9884\u7B97\u4E0A\u9650\uFF0C\u672C\u8F6E\u4E0D\u518D\u53D1\u8D77\u65B0\u7684\u5DE5\u5177\u8C03\u7528\u3002",
20525
- tool_call_id: skipped.id,
20526
- toolName: skipped.function.name
20527
- });
21441
+ const handoff = executedToolResults.map(({ content }) => {
21442
+ try {
21443
+ return typeof content === "string" ? JSON.parse(content) : null;
21444
+ } catch {
21445
+ return null;
21446
+ }
21447
+ }).find((value) => value?.handoff && typeof value.content === "string" && value.content.trim());
21448
+ if (handoff) {
21449
+ result4 = { ...result4, content: handoff.content };
21450
+ stopReason = "handoff";
21451
+ break;
20528
21452
  }
20529
- if (budgetExhaustion) {
21453
+ if (executedToolResults.some(({ metadata }) => metadata?.uiAskChoice === true && metadata.resolved !== true)) {
21454
+ stopReason = "pending";
20530
21455
  break;
20531
21456
  }
20532
21457
  const toolExecutionGuardVerdict = progressGuard.observeToolExecution(
@@ -20549,31 +21474,28 @@ ${reasoningTailLog}
20549
21474
  };
20550
21475
  break;
20551
21476
  }
20552
- await compaction.maybeCompactInLoop();
20553
- if (!budgetAdmissionGate()) {
20554
- break;
20555
- }
20556
21477
  loopTiming.mark("roundEnd", round);
20557
21478
  round += 1;
20558
21479
  }
20559
21480
  } catch (error) {
20560
- loopError = error;
20561
- }
20562
- if (budgetExhaustion) {
20563
- const previousContent = typeof result4?.content === "string" ? result4.content.trim() : "";
20564
- result4 = {
20565
- ...result4,
20566
- content: [
20567
- previousContent,
20568
- buildSubtaskBudgetExhaustedContent({
20569
- exhaustion: budgetExhaustion,
20570
- roundsCompleted: round,
20571
- recentToolNames: executedToolNameLog
20572
- })
20573
- ].filter((part) => part.length > 0).join("\n\n")
20574
- };
21481
+ const toRollback = [...pendingInjectionCommits.splice(0), ...inFlightTransactions];
21482
+ inFlightTransactions = [];
21483
+ let rollbackErr;
21484
+ try {
21485
+ await rollbackInjectionCommits(toRollback);
21486
+ } catch (rbErr) {
21487
+ rollbackErr = rbErr;
21488
+ }
21489
+ loopError = rollbackErr ? combineRollbackError(error, rollbackErr) : error;
20575
21490
  }
21491
+ if (!loopError && input.abortSignal?.aborted) loopError = buildAbortedError();
20576
21492
  if (loopError) {
21493
+ if (loopError instanceof LocalContextCoverageError && round === 0 && !consumedInjectedMessages) {
21494
+ if (input.continueDialogId) attachDialogIdToError(loopError, input.continueDialogId);
21495
+ attachUsageRecordsToError(loopError, usageLedger.records());
21496
+ await loopTiming.flush();
21497
+ throw loopError;
21498
+ }
20577
21499
  const turnMessages2 = transcript.persisted(
20578
21500
  sanitizedPersistedInput,
20579
21501
  input.persistedInputReference
@@ -20599,6 +21521,16 @@ ${reasoningTailLog}
20599
21521
  throw loopError;
20600
21522
  }
20601
21523
  result4 = result4;
21524
+ result4 = {
21525
+ ...result4,
21526
+ stopReason: input.abortSignal?.aborted ? "aborted" : result4.error || result4.runtimeProviderFailure || result4.emptyAssistantFallbackReason ? "error" : stopReason
21527
+ };
21528
+ delete result4.businessCompletion;
21529
+ const businessCompletion = evaluateTaskExecutionContract(input.taskExecutionContract, {
21530
+ stopped: input.abortSignal?.aborted,
21531
+ executionFailed: result4.error === true || !!result4.runtimeProviderFailure || !!result4.emptyAssistantFallbackReason
21532
+ });
21533
+ if (businessCompletion) result4.businessCompletion = businessCompletion;
20602
21534
  if (!skipFinalAppend) {
20603
21535
  const truncatedReasoningTailLog = resolveTruncatedReasoningTailLog(
20604
21536
  result4.emptyAssistantFallbackReason,
@@ -20685,6 +21617,231 @@ function buildResponsesRequestBody(body, model, options) {
20685
21617
  };
20686
21618
  }
20687
21619
 
21620
+ // packages/agent-runtime/responsesProtocolLeakSanitizer.ts
21621
+ var LEAKED_PROTOCOL_NOTICE = "\n\n[\u63D0\u793A\uFF1A\u5DF2\u62E6\u622A\u6A21\u578B\u751F\u6210\u7684\u672A\u6267\u884C\u5DE5\u5177\u534F\u8BAE\u7247\u6BB5]";
21622
+ var INLINE_MARKER_REGEXES = [
21623
+ /\u3011\u3010\u3002\s*assistant\s+to\s*=\s*functions\./i,
21624
+ /\u3011\u3010\u3002\s*functions\./i,
21625
+ /<\|(?:im_start|start_header_id)\|>\s*assistant\s+to\s*=\s*functions\./i,
21626
+ /<\|(?:im_start|start_header_id)\|>\s*functions\./i
21627
+ ];
21628
+ var INLINE_MARKER_PREFIXES = [
21629
+ "\u3011\u3010\u3002assistant to=functions.",
21630
+ "\u3011\u3010\u3002functions.",
21631
+ "<|im_start|>assistant to=functions.",
21632
+ "<|im_start|>functions.",
21633
+ "<|start_header_id|>assistant to=functions.",
21634
+ "<|start_header_id|>functions."
21635
+ ];
21636
+ var TAB_D_PREFIX = " Dfunctions.".toLowerCase();
21637
+ function findLeakedMarker(text2, wasAtLineStart) {
21638
+ let earliestIndex = -1;
21639
+ let earliestLength = 0;
21640
+ for (const regex of INLINE_MARKER_REGEXES) {
21641
+ const match7 = regex.exec(text2);
21642
+ if (match7 && match7.index !== void 0) {
21643
+ if (earliestIndex === -1 || match7.index < earliestIndex) {
21644
+ earliestIndex = match7.index;
21645
+ earliestLength = match7[0].length;
21646
+ }
21647
+ }
21648
+ }
21649
+ const lineMarkerRegex = /(?:^|[\r\n])[ \t]*\tDfunctions\./gi;
21650
+ let m;
21651
+ while ((m = lineMarkerRegex.exec(text2)) !== null) {
21652
+ const isNewline = m[0].startsWith("\n") || m[0].startsWith("\r");
21653
+ const actualIndex = m.index + (isNewline ? 1 : 0);
21654
+ if (actualIndex === 0 && !wasAtLineStart) {
21655
+ continue;
21656
+ }
21657
+ if (earliestIndex === -1 || actualIndex < earliestIndex) {
21658
+ earliestIndex = actualIndex;
21659
+ earliestLength = m[0].length - (isNewline ? 1 : 0);
21660
+ }
21661
+ break;
21662
+ }
21663
+ return earliestIndex >= 0 ? { index: earliestIndex, length: earliestLength } : null;
21664
+ }
21665
+ function calculateHoldLength(buffer3, wasAtLineStart) {
21666
+ let hold = 0;
21667
+ const lower = buffer3.toLowerCase();
21668
+ for (const prefix of INLINE_MARKER_PREFIXES) {
21669
+ const pLower = prefix.toLowerCase();
21670
+ const limit = Math.min(lower.length, pLower.length);
21671
+ for (let len = limit; len > 0; len--) {
21672
+ if (pLower.startsWith(lower.slice(-len))) {
21673
+ if (len > hold) hold = len;
21674
+ break;
21675
+ }
21676
+ }
21677
+ }
21678
+ const trailingLineMatch = /(?:^|[\r\n])[ \t]*$/.exec(buffer3);
21679
+ if (trailingLineMatch) {
21680
+ const isNewline = trailingLineMatch[0].startsWith("\n") || trailingLineMatch[0].startsWith("\r");
21681
+ const len = trailingLineMatch[0].length - (isNewline ? 1 : 0);
21682
+ if (len > 0 && (isNewline || wasAtLineStart)) {
21683
+ if (len > hold) hold = len;
21684
+ }
21685
+ }
21686
+ const trailingTabMatch = /(?:^|[\r\n])[ \t]*(\t[a-zA-Z0-9_\.]*)$/i.exec(lower);
21687
+ if (trailingTabMatch) {
21688
+ const candidate = trailingTabMatch[1];
21689
+ if (TAB_D_PREFIX.startsWith(candidate)) {
21690
+ const isNewline = trailingTabMatch[0].startsWith("\n") || trailingTabMatch[0].startsWith("\r");
21691
+ const len = trailingTabMatch[0].length - (isNewline ? 1 : 0);
21692
+ if (isNewline || wasAtLineStart) {
21693
+ if (len > hold) hold = len;
21694
+ }
21695
+ }
21696
+ }
21697
+ const trailingFenceMatch = /(?:^|[\r\n])[ ]{0,3}(?:`{1,2}|~{1,2})$/.exec(buffer3);
21698
+ if (trailingFenceMatch) {
21699
+ const isNewline = trailingFenceMatch[0].startsWith("\n") || trailingFenceMatch[0].startsWith("\r");
21700
+ const len = trailingFenceMatch[0].length - (isNewline ? 1 : 0);
21701
+ if (isNewline || wasAtLineStart) {
21702
+ if (len > hold) hold = len;
21703
+ }
21704
+ }
21705
+ return hold;
21706
+ }
21707
+ function createResponsesProtocolLeakSanitizer(onTextDelta) {
21708
+ let safeContent = "";
21709
+ let buffer3 = "";
21710
+ let inCodeFence = false;
21711
+ let fenceChar = "";
21712
+ let fenceLength = 0;
21713
+ let intercepted = false;
21714
+ let wasAtLineStart = true;
21715
+ const emit = (text2) => {
21716
+ safeContent += text2;
21717
+ onTextDelta?.(text2);
21718
+ if (text2.length > 0) {
21719
+ const last = text2[text2.length - 1];
21720
+ wasAtLineStart = last === "\n" || last === "\r";
21721
+ }
21722
+ };
21723
+ const processBuffer = (isFinal) => {
21724
+ if (intercepted) return;
21725
+ while (buffer3.length > 0) {
21726
+ if (!inCodeFence) {
21727
+ let fenceIndex = -1;
21728
+ let fenceMarkerStr = "";
21729
+ let lineStartOffset = 0;
21730
+ const fenceRegex = /(?:^|[\r\n])[ ]{0,3}(`{3,}|~{3,})[^\r\n]*(?:\r?\n|$)/g;
21731
+ let m;
21732
+ while ((m = fenceRegex.exec(buffer3)) !== null) {
21733
+ const isNewlinePreceded = m[0].startsWith("\n") || m[0].startsWith("\r");
21734
+ const actualStart = m.index + (isNewlinePreceded ? 1 : 0);
21735
+ if (actualStart === 0 && !wasAtLineStart) {
21736
+ continue;
21737
+ }
21738
+ fenceIndex = actualStart;
21739
+ fenceMarkerStr = m[1];
21740
+ lineStartOffset = m[0].length - (isNewlinePreceded ? 1 : 0);
21741
+ break;
21742
+ }
21743
+ const markerMatch = findLeakedMarker(buffer3, wasAtLineStart);
21744
+ const markerIndex = markerMatch ? markerMatch.index : -1;
21745
+ if (markerIndex >= 0 && (fenceIndex === -1 || markerIndex < fenceIndex)) {
21746
+ const normalPart = buffer3.slice(0, markerIndex);
21747
+ emit(normalPart);
21748
+ emit(LEAKED_PROTOCOL_NOTICE);
21749
+ intercepted = true;
21750
+ buffer3 = "";
21751
+ return;
21752
+ }
21753
+ if (fenceIndex >= 0) {
21754
+ if (!isFinal && !buffer3.slice(fenceIndex).includes("\n") && !buffer3.slice(fenceIndex).includes("\r")) {
21755
+ const beforeFence2 = buffer3.slice(0, fenceIndex);
21756
+ emit(beforeFence2);
21757
+ buffer3 = buffer3.slice(fenceIndex);
21758
+ return;
21759
+ }
21760
+ const beforeFence = buffer3.slice(0, fenceIndex);
21761
+ emit(beforeFence);
21762
+ inCodeFence = true;
21763
+ fenceChar = fenceMarkerStr[0];
21764
+ fenceLength = fenceMarkerStr.length;
21765
+ emit(buffer3.slice(fenceIndex, fenceIndex + lineStartOffset));
21766
+ buffer3 = buffer3.slice(fenceIndex + lineStartOffset);
21767
+ continue;
21768
+ }
21769
+ if (isFinal) {
21770
+ emit(buffer3);
21771
+ buffer3 = "";
21772
+ return;
21773
+ }
21774
+ const hold = calculateHoldLength(buffer3, wasAtLineStart);
21775
+ if (hold === 0) {
21776
+ emit(buffer3);
21777
+ buffer3 = "";
21778
+ } else if (hold < buffer3.length) {
21779
+ emit(buffer3.slice(0, buffer3.length - hold));
21780
+ buffer3 = buffer3.slice(buffer3.length - hold);
21781
+ }
21782
+ return;
21783
+ } else {
21784
+ const closeRegex = new RegExp(`(?:^|[\\r\\n])[ ]{0,3}${fenceChar}{${fenceLength},}[ ]*(?:\\r?\\n|$)`, "g");
21785
+ const closeMatch = closeRegex.exec(buffer3);
21786
+ if (closeMatch) {
21787
+ const isNewlinePreceded = closeMatch[0].startsWith("\n") || closeMatch[0].startsWith("\r");
21788
+ const actualStart = closeMatch.index + (isNewlinePreceded ? 1 : 0);
21789
+ if (actualStart === 0 && !wasAtLineStart) {
21790
+ emit(buffer3.slice(0, closeMatch.index + 1));
21791
+ buffer3 = buffer3.slice(closeMatch.index + 1);
21792
+ continue;
21793
+ }
21794
+ const closeEnd = closeMatch.index + closeMatch[0].length;
21795
+ emit(buffer3.slice(0, closeEnd));
21796
+ buffer3 = buffer3.slice(closeEnd);
21797
+ inCodeFence = false;
21798
+ fenceChar = "";
21799
+ fenceLength = 0;
21800
+ continue;
21801
+ }
21802
+ if (isFinal) {
21803
+ emit(buffer3);
21804
+ buffer3 = "";
21805
+ return;
21806
+ }
21807
+ const trailingCloseMatch = new RegExp(`(?:^|[\\r\\n])[ ]{0,3}${fenceChar}{1,${fenceLength}}$`).exec(buffer3);
21808
+ const hold = trailingCloseMatch ? trailingCloseMatch[0].length : 0;
21809
+ if (hold === 0) {
21810
+ emit(buffer3);
21811
+ buffer3 = "";
21812
+ } else if (hold < buffer3.length) {
21813
+ emit(buffer3.slice(0, buffer3.length - hold));
21814
+ buffer3 = buffer3.slice(buffer3.length - hold);
21815
+ }
21816
+ return;
21817
+ }
21818
+ }
21819
+ };
21820
+ return {
21821
+ push(chunk) {
21822
+ if (intercepted) return;
21823
+ if (chunk.length === 0) {
21824
+ emit("");
21825
+ return;
21826
+ }
21827
+ buffer3 += chunk;
21828
+ processBuffer(false);
21829
+ },
21830
+ finalize() {
21831
+ processBuffer(true);
21832
+ return {
21833
+ content: safeContent,
21834
+ intercepted
21835
+ };
21836
+ }
21837
+ };
21838
+ }
21839
+ function sanitizeResponsesProtocolLeakText(text2) {
21840
+ const sanitizer = createResponsesProtocolLeakSanitizer();
21841
+ sanitizer.push(text2);
21842
+ return sanitizer.finalize();
21843
+ }
21844
+
20688
21845
  // packages/agent-runtime/responsesSseStream.ts
20689
21846
  function toResponsesStreamFailure(raw, fallbackPrefix = "Responses") {
20690
21847
  if (!raw || typeof raw !== "object") return void 0;
@@ -20721,6 +21878,8 @@ function hasNumericTokenCount(u) {
20721
21878
  function createResponsesStreamCollector(callbacks) {
20722
21879
  let content = "";
20723
21880
  let reasoning = "";
21881
+ const textSanitizer = createResponsesProtocolLeakSanitizer(callbacks?.onTextDelta);
21882
+ let sawNonEmptyOutputTextDelta = false;
20724
21883
  const toolCallsAcc = createResponsesToolAccumulator();
20725
21884
  let usage;
20726
21885
  let failure;
@@ -20772,8 +21931,8 @@ function createResponsesStreamCollector(callbacks) {
20772
21931
  return;
20773
21932
  }
20774
21933
  if (type === "response.output_text.delta" && typeof ev.delta === "string") {
20775
- content += ev.delta;
20776
- callbacks?.onTextDelta?.(ev.delta);
21934
+ if (ev.delta.length > 0) sawNonEmptyOutputTextDelta = true;
21935
+ textSanitizer.push(ev.delta);
20777
21936
  return;
20778
21937
  }
20779
21938
  if ((type === "response.reasoning_text.delta" || type === "response.reasoning.delta") && typeof ev.delta === "string") {
@@ -20783,11 +21942,11 @@ function createResponsesStreamCollector(callbacks) {
20783
21942
  }
20784
21943
  if (type === "response.output_item.added" || type === "response.output_item.done") {
20785
21944
  const item = ev.item;
20786
- if (item?.type === "message" && !content) {
21945
+ if (item?.type === "message" && !sawNonEmptyOutputTextDelta) {
20787
21946
  const itemText = extractOutputTextFromItem(item);
20788
21947
  if (itemText) {
20789
- content = itemText;
20790
- callbacks?.onTextDelta?.(itemText);
21948
+ sawNonEmptyOutputTextDelta = true;
21949
+ textSanitizer.push(itemText);
20791
21950
  }
20792
21951
  }
20793
21952
  applyResponsesToolEvent(toolCallsAcc, ev);
@@ -20886,8 +22045,16 @@ function createResponsesStreamCollector(callbacks) {
20886
22045
  if (authoritativeCompletedTools && failure) {
20887
22046
  finalToolCalls = [];
20888
22047
  }
22048
+ const sanitized = textSanitizer.finalize();
22049
+ content = sanitized.content;
22050
+ let intercepted = sanitized.intercepted;
20889
22051
  if (!content && completedResponse) {
20890
- content = extractTextFromResponseOutput(completedResponse);
22052
+ const extracted = extractTextFromResponseOutput(completedResponse);
22053
+ if (extracted) {
22054
+ const sanitizedExtracted = sanitizeResponsesProtocolLeakText(extracted);
22055
+ content = sanitizedExtracted.content;
22056
+ if (sanitizedExtracted.intercepted) intercepted = true;
22057
+ }
20891
22058
  }
20892
22059
  if (!reasoning && completedResponse) {
20893
22060
  reasoning = extractReasoningFromResponseOutput(completedResponse);
@@ -20902,6 +22069,7 @@ function createResponsesStreamCollector(callbacks) {
20902
22069
  ...streamComplete ? { finish_reason: finalToolCalls.length > 0 ? "tool_calls" : "stop" } : {},
20903
22070
  ...streamComplete ? { stream_complete: true } : {},
20904
22071
  ...streamTruncated ? { stream_truncated: true } : {},
22072
+ ...intercepted ? { leaked_protocol_intercepted: true } : {},
20905
22073
  ...completedResponse ? { completed_response: completedResponse } : {},
20906
22074
  ...completedResponse && typeof completedResponse.id === "string" ? { response_id: completedResponse.id } : {},
20907
22075
  ...failure ? { failure } : {}
@@ -21210,7 +22378,7 @@ async function executeOpenAiCompatibleChatCompletion(args2) {
21210
22378
  terminalIfEnabled(isAbortError(error) ? "aborted" : "network-error");
21211
22379
  throw error;
21212
22380
  }
21213
- if (res.status === 401 && args2.resolveApiKey) {
22381
+ if (res.status === 401 && args2.resolveApiKey && !args2.noRetries) {
21214
22382
  const refreshed = await args2.resolveApiKey({ force: true });
21215
22383
  if (refreshed && refreshed !== apiKey) {
21216
22384
  diagnosticCall?.httpResult(diagnosticAttempt, {
@@ -21283,6 +22451,7 @@ async function executeOpenAiCompatibleChatCompletion(args2) {
21283
22451
  ...streamed2.usage ? { usage: streamed2.usage } : {},
21284
22452
  ...streamed2.stream_complete ? { stream_complete: true } : {},
21285
22453
  ...streamed2.finish_reason ? { finish_reason: streamed2.finish_reason } : {},
22454
+ ...streamed2.leaked_protocol_intercepted ? { leaked_protocol_intercepted: true } : {},
21286
22455
  trace: args2.messages
21287
22456
  });
21288
22457
  }
@@ -21330,6 +22499,16 @@ async function executeOpenAiCompatibleChatCompletion(args2) {
21330
22499
  throw error;
21331
22500
  }
21332
22501
  }
22502
+ function describeOpenAiCompatibleRequestBudget(args2) {
22503
+ const request3 = buildOpenAiCompatibleChatCompletionRequest({ ...args2, messages: [] });
22504
+ const body = JSON.parse(request3.init.body);
22505
+ const cap = body.max_completion_tokens ?? body.max_output_tokens ?? body.max_tokens;
22506
+ return {
22507
+ toolSchemaTokens: estimateTokenCount(JSON.stringify(body.tools ?? [])),
22508
+ // Explicit wire allowance is conservatively reserved. Omitted provider defaults are unknown.
22509
+ output: typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? { semantics: "shared-window", finalWireCapTokens: cap } : { semantics: "unknown", fallbackReserveTokens: 8192 }
22510
+ };
22511
+ }
21333
22512
 
21334
22513
  // packages/agent-runtime/openAiCompatibleProviderConfig.ts
21335
22514
  async function resolveOpenAiCompatibleProviderConfig(args2) {
@@ -22435,9 +23614,23 @@ async function fetchHybridRemoteRecord(args2) {
22435
23614
  ...args2.signal ? { signal: args2.signal } : {}
22436
23615
  }
22437
23616
  );
22438
- if (!response.ok) return null;
22439
- const payload = await response.json().catch(() => null);
22440
- return normalizeHybridRecord(args2.dbKey, payload?.data ?? payload, args2.server);
23617
+ if (!response.ok) {
23618
+ if (response.status !== 404) args2.onUnavailable?.();
23619
+ return null;
23620
+ }
23621
+ let payload;
23622
+ try {
23623
+ payload = await response.json();
23624
+ } catch {
23625
+ args2.onUnavailable?.();
23626
+ return null;
23627
+ }
23628
+ const record2 = payload && typeof payload === "object" && "data" in payload ? payload.data : payload;
23629
+ if (record2 !== null && (!record2 || typeof record2 !== "object" || Array.isArray(record2))) {
23630
+ args2.onUnavailable?.();
23631
+ return null;
23632
+ }
23633
+ return normalizeHybridRecord(args2.dbKey, record2, args2.server);
22441
23634
  }
22442
23635
  function shouldCacheHybridRemoteRecord(remoteRecord, localRecord) {
22443
23636
  if (!remoteRecord || typeof remoteRecord !== "object") return false;
@@ -22459,14 +23652,18 @@ function createHybridRecordStore(deps) {
22459
23652
  const normalizedLocalRecord = normalizeHybridRecord(dbKey, localRecord);
22460
23653
  if (normalizedLocalRecord && options?.remote !== true) return normalizedLocalRecord;
22461
23654
  if (options?.remote === false) return null;
22462
- const fallbackDeadlineMs = requestTimeoutMs > 0 ? Date.now() + requestTimeoutMs : null;
23655
+ const effectiveTimeoutMs = typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : requestTimeoutMs;
23656
+ const fallbackDeadlineMs = effectiveTimeoutMs > 0 ? Date.now() + effectiveTimeoutMs : null;
22463
23657
  for (const server of resolveHybridServers(
22464
23658
  deps.defaultServer,
22465
23659
  options?.preferredServerOrigin,
22466
23660
  deps.fallbackServers
22467
23661
  )) {
22468
23662
  const remainingMs = fallbackDeadlineMs === null ? null : fallbackDeadlineMs - Date.now();
22469
- if (remainingMs !== null && remainingMs <= 0) break;
23663
+ if (remainingMs !== null && remainingMs <= 0) {
23664
+ options?.onRemoteUnavailable?.();
23665
+ break;
23666
+ }
22470
23667
  let remoteRecord = null;
22471
23668
  try {
22472
23669
  remoteRecord = await fetchHybridRemoteRecord({
@@ -22474,9 +23671,11 @@ function createHybridRecordStore(deps) {
22474
23671
  server,
22475
23672
  authToken: deps.authToken,
22476
23673
  fetchImpl,
23674
+ onUnavailable: options?.onRemoteUnavailable,
22477
23675
  ...remainingMs !== null ? { signal: AbortSignal.timeout(remainingMs) } : {}
22478
23676
  });
22479
23677
  } catch {
23678
+ options?.onRemoteUnavailable?.();
22480
23679
  continue;
22481
23680
  }
22482
23681
  if (!remoteRecord) continue;
@@ -22505,6 +23704,267 @@ function createHybridRecordStore(deps) {
22505
23704
  var DEFAULT_SANDBOX_TMPFS_SIZE_BYTES = 128 * 1024 * 1024;
22506
23705
  var BWRAP_PROBE_TTL_MS = 5 * 6e4;
22507
23706
 
23707
+ // node_modules/async-mutex/index.mjs
23708
+ var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
23709
+ var E_ALREADY_LOCKED = new Error("mutex already locked");
23710
+ var E_CANCELED = new Error("request for lock canceled");
23711
+ var __awaiter$2 = function(thisArg, _arguments, P, generator) {
23712
+ function adopt(value) {
23713
+ return value instanceof P ? value : new P(function(resolve6) {
23714
+ resolve6(value);
23715
+ });
23716
+ }
23717
+ return new (P || (P = Promise))(function(resolve6, reject) {
23718
+ function fulfilled(value) {
23719
+ try {
23720
+ step(generator.next(value));
23721
+ } catch (e) {
23722
+ reject(e);
23723
+ }
23724
+ }
23725
+ function rejected(value) {
23726
+ try {
23727
+ step(generator["throw"](value));
23728
+ } catch (e) {
23729
+ reject(e);
23730
+ }
23731
+ }
23732
+ function step(result4) {
23733
+ result4.done ? resolve6(result4.value) : adopt(result4.value).then(fulfilled, rejected);
23734
+ }
23735
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23736
+ });
23737
+ };
23738
+ var Semaphore = class {
23739
+ constructor(_value, _cancelError = E_CANCELED) {
23740
+ this._value = _value;
23741
+ this._cancelError = _cancelError;
23742
+ this._queue = [];
23743
+ this._weightedWaiters = [];
23744
+ }
23745
+ acquire(weight = 1, priority = 0) {
23746
+ if (weight <= 0)
23747
+ throw new Error(`invalid weight ${weight}: must be positive`);
23748
+ return new Promise((resolve6, reject) => {
23749
+ const task = { resolve: resolve6, reject, weight, priority };
23750
+ const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
23751
+ if (i === -1 && weight <= this._value) {
23752
+ this._dispatchItem(task);
23753
+ } else {
23754
+ this._queue.splice(i + 1, 0, task);
23755
+ }
23756
+ });
23757
+ }
23758
+ runExclusive(callback_1) {
23759
+ return __awaiter$2(this, arguments, void 0, function* (callback4, weight = 1, priority = 0) {
23760
+ const [value, release3] = yield this.acquire(weight, priority);
23761
+ try {
23762
+ return yield callback4(value);
23763
+ } finally {
23764
+ release3();
23765
+ }
23766
+ });
23767
+ }
23768
+ waitForUnlock(weight = 1, priority = 0) {
23769
+ if (weight <= 0)
23770
+ throw new Error(`invalid weight ${weight}: must be positive`);
23771
+ if (this._couldLockImmediately(weight, priority)) {
23772
+ return Promise.resolve();
23773
+ } else {
23774
+ return new Promise((resolve6) => {
23775
+ if (!this._weightedWaiters[weight - 1])
23776
+ this._weightedWaiters[weight - 1] = [];
23777
+ insertSorted(this._weightedWaiters[weight - 1], { resolve: resolve6, priority });
23778
+ });
23779
+ }
23780
+ }
23781
+ isLocked() {
23782
+ return this._value <= 0;
23783
+ }
23784
+ getValue() {
23785
+ return this._value;
23786
+ }
23787
+ setValue(value) {
23788
+ this._value = value;
23789
+ this._dispatchQueue();
23790
+ }
23791
+ release(weight = 1) {
23792
+ if (weight <= 0)
23793
+ throw new Error(`invalid weight ${weight}: must be positive`);
23794
+ this._value += weight;
23795
+ this._dispatchQueue();
23796
+ }
23797
+ cancel() {
23798
+ this._queue.forEach((entry) => entry.reject(this._cancelError));
23799
+ this._queue = [];
23800
+ }
23801
+ _dispatchQueue() {
23802
+ this._drainUnlockWaiters();
23803
+ while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
23804
+ this._dispatchItem(this._queue.shift());
23805
+ this._drainUnlockWaiters();
23806
+ }
23807
+ }
23808
+ _dispatchItem(item) {
23809
+ const previousValue = this._value;
23810
+ this._value -= item.weight;
23811
+ item.resolve([previousValue, this._newReleaser(item.weight)]);
23812
+ }
23813
+ _newReleaser(weight) {
23814
+ let called = false;
23815
+ return () => {
23816
+ if (called)
23817
+ return;
23818
+ called = true;
23819
+ this.release(weight);
23820
+ };
23821
+ }
23822
+ _drainUnlockWaiters() {
23823
+ if (this._queue.length === 0) {
23824
+ for (let weight = this._value; weight > 0; weight--) {
23825
+ const waiters = this._weightedWaiters[weight - 1];
23826
+ if (!waiters)
23827
+ continue;
23828
+ waiters.forEach((waiter) => waiter.resolve());
23829
+ this._weightedWaiters[weight - 1] = [];
23830
+ }
23831
+ } else {
23832
+ const queuedPriority = this._queue[0].priority;
23833
+ for (let weight = this._value; weight > 0; weight--) {
23834
+ const waiters = this._weightedWaiters[weight - 1];
23835
+ if (!waiters)
23836
+ continue;
23837
+ const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
23838
+ (i === -1 ? waiters : waiters.splice(0, i)).forEach((waiter) => waiter.resolve());
23839
+ }
23840
+ }
23841
+ }
23842
+ _couldLockImmediately(weight, priority) {
23843
+ return (this._queue.length === 0 || this._queue[0].priority < priority) && weight <= this._value;
23844
+ }
23845
+ };
23846
+ function insertSorted(a, v) {
23847
+ const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
23848
+ a.splice(i + 1, 0, v);
23849
+ }
23850
+ function findIndexFromEnd(a, predicate) {
23851
+ for (let i = a.length - 1; i >= 0; i--) {
23852
+ if (predicate(a[i])) {
23853
+ return i;
23854
+ }
23855
+ }
23856
+ return -1;
23857
+ }
23858
+ var __awaiter$1 = function(thisArg, _arguments, P, generator) {
23859
+ function adopt(value) {
23860
+ return value instanceof P ? value : new P(function(resolve6) {
23861
+ resolve6(value);
23862
+ });
23863
+ }
23864
+ return new (P || (P = Promise))(function(resolve6, reject) {
23865
+ function fulfilled(value) {
23866
+ try {
23867
+ step(generator.next(value));
23868
+ } catch (e) {
23869
+ reject(e);
23870
+ }
23871
+ }
23872
+ function rejected(value) {
23873
+ try {
23874
+ step(generator["throw"](value));
23875
+ } catch (e) {
23876
+ reject(e);
23877
+ }
23878
+ }
23879
+ function step(result4) {
23880
+ result4.done ? resolve6(result4.value) : adopt(result4.value).then(fulfilled, rejected);
23881
+ }
23882
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23883
+ });
23884
+ };
23885
+ var Mutex = class {
23886
+ constructor(cancelError) {
23887
+ this._semaphore = new Semaphore(1, cancelError);
23888
+ }
23889
+ acquire() {
23890
+ return __awaiter$1(this, arguments, void 0, function* (priority = 0) {
23891
+ const [, releaser] = yield this._semaphore.acquire(1, priority);
23892
+ return releaser;
23893
+ });
23894
+ }
23895
+ runExclusive(callback4, priority = 0) {
23896
+ return this._semaphore.runExclusive(() => callback4(), 1, priority);
23897
+ }
23898
+ isLocked() {
23899
+ return this._semaphore.isLocked();
23900
+ }
23901
+ waitForUnlock(priority = 0) {
23902
+ return this._semaphore.waitForUnlock(1, priority);
23903
+ }
23904
+ release() {
23905
+ if (this._semaphore.isLocked())
23906
+ this._semaphore.release();
23907
+ }
23908
+ cancel() {
23909
+ return this._semaphore.cancel();
23910
+ }
23911
+ };
23912
+
23913
+ // packages/database-engine/dialogOwner.ts
23914
+ var dialogOwnerKey = (dialogId) => `dialogown-${dialogId}`;
23915
+ var locks = /* @__PURE__ */ new Map();
23916
+ async function registerDialogOwner(args2) {
23917
+ const emit = (event) => {
23918
+ try {
23919
+ (args2.log ?? ((event2) => console.warn(event2)))(event);
23920
+ } catch {
23921
+ }
23922
+ };
23923
+ const key = dialogOwnerKey(args2.dialogId);
23924
+ let mutex = locks.get(key);
23925
+ if (!mutex) {
23926
+ mutex = new Mutex();
23927
+ locks.set(key, mutex);
23928
+ }
23929
+ try {
23930
+ await mutex.runExclusive(async () => {
23931
+ let existing;
23932
+ try {
23933
+ existing = await args2.store.get(key);
23934
+ } catch (error) {
23935
+ if (!isLevelNotFoundError(error)) throw error;
23936
+ existing = null;
23937
+ }
23938
+ if (existing != null) {
23939
+ if (existing.ownerId !== args2.ownerId) emit({
23940
+ event: "dialog_owner_conflict",
23941
+ dialogId: args2.dialogId,
23942
+ existingOwnerId: existing.ownerId,
23943
+ attemptedOwnerId: args2.ownerId,
23944
+ source: args2.source
23945
+ });
23946
+ return;
23947
+ }
23948
+ await args2.store.put(key, {
23949
+ dialogId: args2.dialogId,
23950
+ ownerId: args2.ownerId,
23951
+ ...args2.spaceId !== void 0 ? { spaceId: args2.spaceId } : {},
23952
+ source: args2.source,
23953
+ registeredAt: (args2.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))()
23954
+ });
23955
+ });
23956
+ } catch (error) {
23957
+ emit({
23958
+ event: "dialog_owner_registration_failed",
23959
+ dialogId: args2.dialogId,
23960
+ source: args2.source,
23961
+ error: error instanceof Error ? error.message : String(error)
23962
+ });
23963
+ } finally {
23964
+ if (!mutex.isLocked() && locks.get(key) === mutex) locks.delete(key);
23965
+ }
23966
+ }
23967
+
22508
23968
  // packages/cli/client/localDialogRecords.ts
22509
23969
  var localDialogMessageRecordToRuntimeMessage = dialogMessageRecordToAgentRuntimeMessage;
22510
23970
  function buildLocalDialogWritePlan(args2) {
@@ -22521,6 +23981,138 @@ function buildLocalDialogWritePlan(args2) {
22521
23981
  ...args2.titleOverride ? { titleOverride: args2.titleOverride } : {}
22522
23982
  });
22523
23983
  }
23984
+ async function saveCliPendingUserMessages(args2) {
23985
+ const dialogKey = `dialog-${args2.userId}-${args2.dialogId}`;
23986
+ const existing = await args2.store.read(dialogKey, { remote: false });
23987
+ const now = (/* @__PURE__ */ new Date()).toISOString();
23988
+ const ops = [];
23989
+ if (!existing) ops.push({ type: "put", key: dialogKey, value: {
23990
+ id: args2.dialogId,
23991
+ dbKey: dialogKey,
23992
+ type: "dialog",
23993
+ userId: args2.userId,
23994
+ primaryAgentKey: args2.agentKey,
23995
+ cybots: [args2.agentKey],
23996
+ createdAt: now,
23997
+ updatedAt: now
23998
+ } });
23999
+ for (const request3 of args2.requests) {
24000
+ if (!request3.messageId) throw new Error("Pending user message requires a stable messageId");
24001
+ const key = `dialog-${args2.dialogId}-msg-${request3.messageId}`;
24002
+ if (await args2.store.read(key, { remote: false })) continue;
24003
+ ops.push({ type: "put", key, value: {
24004
+ id: request3.messageId,
24005
+ dbKey: key,
24006
+ type: "message",
24007
+ dialogId: args2.dialogId,
24008
+ userId: args2.userId,
24009
+ role: "user",
24010
+ content: request3.text,
24011
+ createdAt: now,
24012
+ metadata: {
24013
+ queueState: "pending",
24014
+ source: request3.source ?? "tui",
24015
+ imagePaths: request3.imagePaths,
24016
+ mediaPaths: request3.mediaPaths
24017
+ }
24018
+ } });
24019
+ }
24020
+ await registerDialogOwner({
24021
+ store: { get: (key) => args2.store.read(key, { remote: false }), put: (key, value) => args2.store.batch([{ type: "put", key, value }]) },
24022
+ dialogId: args2.dialogId,
24023
+ ownerId: args2.userId,
24024
+ source: "cli-pending-user"
24025
+ });
24026
+ if (ops.length) await args2.store.batch(ops);
24027
+ }
24028
+ async function readCliPendingUserMessages(store, dialogId) {
24029
+ const { readDialogMessageRecords } = await import("./cliLocalAgentRecordReader-MHHD4T7R.js");
24030
+ const rows = await readDialogMessageRecords({ store, dialogId });
24031
+ const uncertain = rows.filter(({ record: record2 }) => record2.metadata?.cliBatchPhase === "requested" && !record2.metadata?.cliBatchResolution);
24032
+ const recovery = uncertain.length ? {
24033
+ status: "unknown",
24034
+ messageIds: uncertain.map((row) => row.messageId),
24035
+ // Attribute each unknown batch to its own members via the tail receipt.
24036
+ // `acceptedUserMessageIds` is written by `markCliUserBatchPhase`; older
24037
+ // records carry the same field from the legacy admission path. Records with
24038
+ // neither fall back to every accepted row (never attribute a completed
24039
+ // batch's members to an unknown one when the receipt is available).
24040
+ batches: uncertain.map((row) => {
24041
+ const receiptIds = row.record.metadata?.acceptedUserMessageIds;
24042
+ const memberIds = Array.isArray(receiptIds) && receiptIds.length ? receiptIds : rows.filter((message) => message.record.metadata?.queueState === "accepted").map((message) => message.messageId);
24043
+ const memberSet = new Set(memberIds);
24044
+ return { batchId: row.messageId, messages: rows.filter((message) => memberSet.has(message.messageId)).map((message) => ({ messageId: message.messageId, text: String(message.record.content ?? "") })) };
24045
+ }),
24046
+ message: `Batch provider outcome unknown for ${uncertain.map((row) => row.messageId).join(", ")}; pending history retained; resubmission paused to avoid duplicate billing`
24047
+ } : { status: "ready", messageIds: [] };
24048
+ const pending = rows.filter(({ record: record2 }) => record2.role === "user" && record2.metadata?.queueState === "pending").map(({ record: record2, messageId }) => ({
24049
+ messageId,
24050
+ source: record2.metadata?.source,
24051
+ text: String(record2.content ?? ""),
24052
+ event: { kind: "user", text: String(record2.content ?? "") },
24053
+ imagePaths: record2.metadata?.imagePaths,
24054
+ mediaPaths: record2.metadata?.mediaPaths
24055
+ }));
24056
+ return { pending, recovery };
24057
+ }
24058
+ async function markCliUserBatchPhase(store, dialogId, messageId, phase, messageIds, preparedMessages) {
24059
+ const key = `dialog-${dialogId}-msg-${messageId}`;
24060
+ const record2 = await store.read(key, { remote: false });
24061
+ if (!record2) throw new Error("Missing durable batch receipt");
24062
+ const current = record2.metadata?.cliBatchPhase;
24063
+ if (phase === "requested" && (current === "requested" || current === "completed")) {
24064
+ throw new Error(`Batch ${messageId} already requested; resubmission blocked`);
24065
+ }
24066
+ const ops = [];
24067
+ for (const id of messageIds) {
24068
+ const messageKey = `dialog-${dialogId}-msg-${id}`;
24069
+ const message = await store.read(messageKey, { remote: false });
24070
+ if (!message) throw new Error(`Missing batch user message ${id}`);
24071
+ const prepared = preparedMessages?.find((item) => item.cliMessageId === id);
24072
+ ops.push({ type: "put", key: messageKey, value: {
24073
+ ...message,
24074
+ ...prepared ? { content: prepared.content } : {},
24075
+ metadata: {
24076
+ ...message.metadata,
24077
+ queueState: "accepted",
24078
+ ...id === messageId ? { cliBatchPhase: phase, acceptedUserMessageIds: [...messageIds] } : {}
24079
+ }
24080
+ } });
24081
+ }
24082
+ await store.batch(ops);
24083
+ }
24084
+ async function rollbackCliUserBatchPhase(store, dialogId, batchId, messageIds) {
24085
+ const ops = [];
24086
+ for (const id of messageIds) {
24087
+ const key = `dialog-${dialogId}-msg-${id}`;
24088
+ const record2 = await store.read(key, { remote: false });
24089
+ if (!record2) continue;
24090
+ const metadata = { ...record2.metadata };
24091
+ delete metadata.cliBatchPhase;
24092
+ metadata.queueState = "pending";
24093
+ ops.push({ type: "put", key, value: { ...record2, metadata } });
24094
+ }
24095
+ if (ops.length) await store.batch(ops);
24096
+ }
24097
+ async function buildCliBatchCompletionOp(store, dialogId, batchId) {
24098
+ const key = `dialog-${dialogId}-msg-${batchId}`;
24099
+ const record2 = await store.read(key, { remote: false });
24100
+ if (!record2) throw new Error(`Missing batch user message ${batchId}`);
24101
+ return { type: "put", key, value: {
24102
+ ...record2,
24103
+ metadata: { ...record2.metadata, cliBatchPhase: "completed" }
24104
+ } };
24105
+ }
24106
+ async function resolveCliUnknownBatchWithoutResend(store, dialogId, batchId) {
24107
+ const key = `dialog-${dialogId}-msg-${batchId}`;
24108
+ const record2 = await store.read(key, { remote: false });
24109
+ if (!record2 || record2.metadata?.cliBatchPhase !== "requested") throw new Error(`Unknown requested batch not found: ${batchId}`);
24110
+ if (record2.metadata.cliBatchResolution) return;
24111
+ await store.batch([{ type: "put", key, value: { ...record2, metadata: {
24112
+ ...record2.metadata,
24113
+ cliBatchResolution: { decision: "continue-without-resend", decidedBy: "user", decidedAt: (/* @__PURE__ */ new Date()).toISOString() }
24114
+ } } }]);
24115
+ }
22524
24116
 
22525
24117
  // packages/agent-runtime/noloWorkspaceTools.node.ts
22526
24118
  async function readNoloProcessStream(readable) {
@@ -22636,7 +24228,7 @@ function buildLoadSkillExecutor(args2) {
22636
24228
  const parsed = parseNoloWorkspaceToolArguments(call.arguments);
22637
24229
  const referenceKeys = ["source", "slug", "projectBinding", "relativePath", "dbKey", "id", "skillId"];
22638
24230
  if (referenceKeys.some((key) => Object.hasOwn(parsed, key))) {
22639
- return loadPreciseSkill(args2.cwd, parsed);
24231
+ return loadPreciseSkill(args2.cwd, parsed, args2.agentSkillSource, args2.resolveAgentSkillSource);
22640
24232
  }
22641
24233
  const name = noloStringArg(parsed.name ?? parsed.skillName ?? parsed.skill);
22642
24234
  if (!name) {
@@ -22645,12 +24237,14 @@ function buildLoadSkillExecutor(args2) {
22645
24237
  metadata: { loadSkill: true, resolved: false }
22646
24238
  };
22647
24239
  }
22648
- const { resolveSkillByName } = await import("./skillDiscovery-XKHROSH2.js");
24240
+ const { resolveSkillByName } = await import("./skillDiscovery-B5V23AXP.js");
22649
24241
  const resolved = resolveSkillByName(args2.cwd, name);
22650
24242
  if (!resolved) {
22651
- const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-3YOIGEF6.js");
24243
+ const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-2ECA42FC.js");
22652
24244
  const builtin = resolveBuiltinSkillByName(name);
22653
24245
  if (builtin) {
24246
+ const denied = await rejectDisabledBuiltin(args2.agentSkillSource, builtin.slug, name, args2.resolveAgentSkillSource);
24247
+ if (denied) return denied;
22654
24248
  return {
22655
24249
  content: `Skill "${name}" loaded inline. Follow its instructions.
22656
24250
 
@@ -22690,7 +24284,25 @@ ${body}`,
22690
24284
  }
22691
24285
  };
22692
24286
  }
22693
- async function loadPreciseSkill(cwd, parsed) {
24287
+ async function rejectDisabledBuiltin(source, slug, requestedName, resolveSource) {
24288
+ if (resolveSource) {
24289
+ try {
24290
+ source = await resolveSource();
24291
+ } catch {
24292
+ source = null;
24293
+ }
24294
+ if (!source) return {
24295
+ content: `Skill "${requestedName}" cannot be loaded: current agent policy is unavailable.`,
24296
+ metadata: { loadSkill: true, resolved: false, builtin: true, policyUnavailable: true, slug, name: requestedName, requestedName }
24297
+ };
24298
+ }
24299
+ if (!isAgentSkillDisabled(source, slug)) return null;
24300
+ return {
24301
+ content: `Skill "${requestedName}" \u5DF2\u88AB\u7981\u7528\uFF1A\u8BE5\u80FD\u529B\u5728\u6B64 agent \u4E0A\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u52A0\u8F7D\u3002`,
24302
+ metadata: { loadSkill: true, resolved: false, builtin: true, disabled: true, slug, name: requestedName, requestedName }
24303
+ };
24304
+ }
24305
+ async function loadPreciseSkill(cwd, parsed, agentSkillSource, resolveAgentSkillSource) {
22694
24306
  const source = parsed.source;
22695
24307
  const has2 = (key) => Object.hasOwn(parsed, key);
22696
24308
  const reject = (reason) => ({
@@ -22707,9 +24319,11 @@ async function loadPreciseSkill(cwd, parsed) {
22707
24319
  if (!nonEmpty(parsed.slug) || has2("projectBinding") || has2("relativePath")) {
22708
24320
  return reject("builtin requires only an exact slug.");
22709
24321
  }
22710
- const { listBuiltinSkills } = await import("./builtinSkillRegistry-3YOIGEF6.js");
24322
+ const { listBuiltinSkills } = await import("./builtinSkillRegistry-2ECA42FC.js");
22711
24323
  const builtin = listBuiltinSkills().find((entry) => entry.slug === parsed.slug);
22712
24324
  if (!builtin) return reject("builtin slug not found.");
24325
+ const denied = await rejectDisabledBuiltin(agentSkillSource, builtin.slug, builtin.slug, resolveAgentSkillSource);
24326
+ if (denied) return { ...denied, metadata: { ...denied.metadata, source } };
22713
24327
  return {
22714
24328
  content: `Skill "${builtin.slug}" loaded inline. Follow its instructions.
22715
24329
 
@@ -22731,7 +24345,7 @@ ${builtin.content}`,
22731
24345
  }
22732
24346
  try {
22733
24347
  const { realpathSync, statSync, readFileSync } = await import("node:fs");
22734
- const { resolve: resolve6, relative: relative6, isAbsolute: isAbsolute5, basename: basename3, sep: sep3 } = await import("node:path");
24348
+ const { resolve: resolve6, relative: relative6, isAbsolute: isAbsolute5, basename: basename4, sep: sep3 } = await import("node:path");
22735
24349
  const inside = (root, target) => {
22736
24350
  const rel = relative6(root, target);
22737
24351
  return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep3}`) && !isAbsolute5(rel);
@@ -22740,7 +24354,7 @@ ${builtin.content}`,
22740
24354
  if (!isAbsolute5(parsed.projectBinding) || realpathSync(parsed.projectBinding) !== realCwd) {
22741
24355
  return reject("projectBinding does not match the authorized cwd.");
22742
24356
  }
22743
- if (isAbsolute5(parsed.relativePath) || basename3(parsed.relativePath) !== "SKILL.md") {
24357
+ if (isAbsolute5(parsed.relativePath) || basename4(parsed.relativePath) !== "SKILL.md") {
22744
24358
  return reject("relativePath must reference a project SKILL.md.");
22745
24359
  }
22746
24360
  const skillsRoot = resolve6(realCwd, ".agents", "skills");
@@ -22748,7 +24362,7 @@ ${builtin.content}`,
22748
24362
  if (!inside(skillsRoot, candidate)) return reject("path is outside cwd/.agents/skills/.");
22749
24363
  const realRoot = realpathSync(skillsRoot);
22750
24364
  const realPath = realpathSync(candidate);
22751
- if (realRoot !== skillsRoot || !inside(realRoot, realPath) || basename3(realPath) !== "SKILL.md") {
24365
+ if (realRoot !== skillsRoot || !inside(realRoot, realPath) || basename4(realPath) !== "SKILL.md") {
22752
24366
  return reject("skill path escapes cwd/.agents/skills/ or is not SKILL.md.");
22753
24367
  }
22754
24368
  if (!statSync(realPath).isFile()) return reject("SKILL.md is not a file.");
@@ -22771,7 +24385,7 @@ ${body}`,
22771
24385
  }
22772
24386
  }
22773
24387
  async function formatUnknownSkillMessage(cwd, requestedName) {
22774
- const { discoverSkills } = await import("./skillDiscovery-XKHROSH2.js");
24388
+ const { discoverSkills } = await import("./skillDiscovery-B5V23AXP.js");
22775
24389
  let available = [];
22776
24390
  try {
22777
24391
  available = discoverSkills(cwd).map((s) => s.name);
@@ -23423,6 +25037,7 @@ export {
23423
25037
  evaluateShellCommandPolicy,
23424
25038
  filterRetainedLedgerRecords,
23425
25039
  isLineRangeCovered,
25040
+ runTaskStop,
23426
25041
  WORKSPACE_TOOL_NAMES,
23427
25042
  buildLocalWorkspaceToolset,
23428
25043
  buildLocalWorkspacePolicyToolNames,
@@ -23435,12 +25050,16 @@ export {
23435
25050
  createLocalWorkspaceToolExecutors,
23436
25051
  buildToolParametersIndex,
23437
25052
  getAgentRuntimeToolParametersIndex,
25053
+ registerLocalShellLifecycle,
25054
+ getLocalShellLifecycle,
23438
25055
  normalizeLocalToolName,
23439
25056
  isCommandExecutingTool,
23440
25057
  resolveLocalToolPolicy,
23441
25058
  runExecShellGates,
23442
25059
  executeLocalToolWithPolicy,
23443
25060
  LOCAL_TURN_ABORTED_CODE,
25061
+ evaluateTaskExecutionContract,
25062
+ projectContextProvenance,
23444
25063
  hashToolDefinitions,
23445
25064
  LOCAL_AGENT_CONFIG_MISSING_CODE,
23446
25065
  runLocalAgentTurn,
@@ -23452,6 +25071,7 @@ export {
23452
25071
  parseOpenAiCompatibleChatCompletionResponse,
23453
25072
  readOpenAiCompatibleSseCompletion,
23454
25073
  executeOpenAiCompatibleChatCompletion,
25074
+ describeOpenAiCompatibleRequestBudget,
23455
25075
  resolveOpenAiCompatibleProviderConfig,
23456
25076
  dialogMessageRecordToAgentRuntimeMessage,
23457
25077
  buildAgentRuntimeDialogWritePlan,
@@ -23459,8 +25079,15 @@ export {
23459
25079
  parseSyncServersEnv,
23460
25080
  DEFAULT_HYBRID_READ_TIMEOUT_MS,
23461
25081
  createHybridRecordStore,
25082
+ registerDialogOwner,
23462
25083
  localDialogMessageRecordToRuntimeMessage,
23463
25084
  buildLocalDialogWritePlan,
25085
+ saveCliPendingUserMessages,
25086
+ readCliPendingUserMessages,
25087
+ markCliUserBatchPhase,
25088
+ rollbackCliUserBatchPhase,
25089
+ buildCliBatchCompletionOp,
25090
+ resolveCliUnknownBatchWithoutResend,
23464
25091
  buildNoloWorkspaceCliToolExecutors,
23465
25092
  buildLoadSkillExecutor,
23466
25093
  isStableSessionIdentityEnabled,