chainlesschain 0.166.81 → 0.166.82

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 (166) hide show
  1. package/package.json +1 -1
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BZPs-bVl.js → AIOps-DMzLSUWK.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BP1iLSwn.js → ActionButton-CgnA23oA.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-WxwtPqcd.js → Analytics-CtXmm8Qe.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-DvcgeNkP.js → AppLayout-C5RIh5jA.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CtYGOtnT.js → Artifacts-DM525zx0.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BBzFYWMk.js → Audit-ChBx6f2l.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-Dl9wCt0Y.js → BackgroundAgents-BBHDxCda.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-CAL13Eak.js → Backup-B-qzCAoM.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-f_lncpU_.js → BaseInput-C5yoIWAD.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-DLEbkiqI.js → Chat-NmTrci9a.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1tCmIvv1.js → ChatBubbleRenderer-pPuUDG2V.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-SNvgd2UG.js → Checkbox-KdFSU3vd.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BG0D-INl.js → Codegen-L__BOv_4.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DUBBZz0e.js → Col-Dfuw4IQV.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-8LfuBDPs.js → Community-BRlEhSbx.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-GWs7Ka6o.js → Compact-D7dRkv8n.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CXRhB-KC.js → Compliance-CdlFYNR3.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-OpW8dLKg.js → Cowork-Ey7ksHq4.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-6e47Syc_.js → Cron-DBH__wGJ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-D-vGH4nC.js → Crosschain-D3nu9dw7.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-zEIa2jNx.js → DID-DS6vZBNv.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-BfUbZxsl.js → Dashboard-DwFRk2T0.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BeCD8gf0.js → Dropdown-D7b73q3I.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-apOBM2r2.js → EmailListRenderer-DPNaVH36.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-C2dK36po.js → EvolutionSettings-BEsOtdpf.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DPMLWPTR.js → FamilyGuardDashboard-ijSqsSsF.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-CivI2pzm.js → Federation-C0BOzPDh.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-B3zrwBDw.js → FormItemContext-DkF5NoAg.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-jGgdcPck.js → GenericCardRenderer-DQsMcyJj.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-Cpfkrasd.js → Git-DQRt88PU.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DG8_7YS3.js → Governance-DIjPmDN8.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-TQgUqPXx.js → Inference-BWMFtT7i.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-By657fHZ.js → KnowledgeGraph-M7TfkUVl.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-CFJ_70-E.js → Logs-BZv9Z6BY.js} +2 -2
  38. package/src/assets/web-panel/assets/MarkdownRenderer-D5NZGdg3.js +1 -0
  39. package/src/assets/web-panel/assets/{Marketplace-BWC3xler.js → Marketplace-Crd9MYHg.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-B0NkUzeJ.js → McpTools-C37yOQV0.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-DBnXwjWD.js → Memory-BBsijJCd.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-BNWDrEJ4.js → MobileBridge-CiXIQCju.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-BaE94B_N.js → MobileProjects-C0KSvQm5.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-Bp3OAEYb.js → Mtc-C0XaQsOY.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-_1T24zO3.js → MtcAudit-DPqggKhw.js} +4 -4
  46. package/src/assets/web-panel/assets/{Multisig-C2i6Yi0B.js → Multisig-Bie0ZVHe.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CBq3ujGE.js → NLProgramming-BbuJlQ5b.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-BxRmci_W.js → Notes-CgqrJV8w.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-CqBLgOdv.js → NotificationSettings-CusdZEQl.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-cODPlpK7.js → OrderTableRenderer-B7RrCvCQ.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-DlTnQBlp.js → Organization-BnFpz3bb.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-Df2jS2Ep.js → Overflow-C_4_HyyZ.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-BwieT0aA.js → P2P-D0KpHxeg.js} +2 -2
  54. package/src/assets/web-panel/assets/PdhVaultBrowser-Cfd_C9nc.js +7 -0
  55. package/src/assets/web-panel/assets/{Permissions-BhVYLcqT.js → Permissions-D2LxL66N.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-CPbNKPCA.js → PersonalDataHub-CvSNIxt0.js} +3 -3
  57. package/src/assets/web-panel/assets/{Pipeline-B1BuhoY3.js → Pipeline-rUl-SM7V.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-BWeikVbU.js → Privacy-BD8cWSNd.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-BVXcimHE.js → ProjectInit-RM7J4X2F.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-CX87x3rT.js → ProjectSettings-BIH38RsB.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-3Qo2Mvxb.js → Projects-CTgx3Fxc.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-CGm0Wuer.js → Providers-B9Kja2RK.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-DJxv7J64.js → QrScannerModal-ByDKowTD.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-DOXBReRk.js → QuickAsk-DqEN1QOE.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend--MiLwmLM.js → Recommend-CLrYwNiv.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-D6LoXH94.js → RemoteSession-DyDltVPC.js} +3 -3
  67. package/src/assets/web-panel/assets/{Reputation-G2jToiqh.js → Reputation-BfP8rJ1-.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-B79ZV_lZ.js → Row-BpnP6r1H.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CHQN68w_.js → RssFeed-CBNI1lCN.js} +2 -2
  70. package/src/assets/web-panel/assets/{Search-j7v5S2Fa.js → Search-CLjrM-xj.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-B2ykFZJH.js → Security-dRXtg-Mq.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-H8BK8uX0.js → Services-DAHCtOHL.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-XEUUrOTK.js → Skeleton-D11Jgtur.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-B387UJPk.js → Skills-DchvTO4k.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-CWWBujpe.js → Sla-BFOc2SbL.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-Q2AOv9DV.js → SpeechSettings-Dud1xDd2.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-OSNyOUr8.js → SyncSettings-CILfsA2S.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-DfUDrno5.js → Tasks-C6Qrah_5.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-CSTkzDRS.js → Templates--puiHyCR.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-BR__gCTG.js → Tenant-cz5vITz_.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-Bv4DM5OI.js → Terminal-YRs75Gz8.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-DUnZuAtn.js → TimelineRenderer-C5myb_QZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-Dm0sczRV.js → Tokens-Bk60iMIj.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-DsSOp0Ob.js → Trigger-BAb-Fd4z.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-C0HJtFwV.js → Trust-BlqwmpEB.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-BbqIZiX-.js → UkeySign-C6jvuF-7.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-D7dDTt3D.js → VideoEditing-7zbsdQUj.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-DJGu0KWq.js → Wallet-BAouk_kp.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-x8OoogZU.js → WebAuthn-BQapv504.js} +5 -5
  90. package/src/assets/web-panel/assets/{WorkflowEditor-CC3jIUqx.js → WorkflowEditor-ChUXM-Qx.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-Bqgv5oe8.js → chat-7B6U8zgJ.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-CbAMqLFr.js → colors-C8UqaLpe.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-B-zIDFg3.js → compact-item-x47giTCj.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-D_s1ZLtZ.js → createContext-D6ZwjgaZ.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-BFfV5AtN.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-Cj_J9Eb9.js → hasIn-Cv3d9-Bu.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CVPI_iIh.js → index-B7Dorc7r.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bi1E5h9V.js → index-BGxo1YH0.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-JdSkHz64.js → index-BM-NYjej.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Cu3FtpNc.js → index-BMPk5D1m.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-0je3TLmx.js → index-BTBkdyLk.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B6qoyCkx.js → index-BTisPSqa.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BLwUCYDf.js → index-BUzw-kDo.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C6lO24YD.js → index-C1ElqYDD.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CsHTqPTg.js → index-CQ0-ny-o.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BFNO6ycz.js → index-CUUgXcPh.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-J4JLWUMK.js → index-CUtcWTSe.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-ab6FUgkq.js → index-CXG6yiAf.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D_igPAXH.js → index-CXIUyx4U.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cy3XOp7S.js → index-CaPZ_8kP.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CSzWODfj.js → index-Cbf4RqOd.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C-Z13SCI.js → index-Cc2bhSuI.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-nEWJobH4.js → index-Cc6jr0Yb.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DuqVIsHL.js → index-Ce666S9v.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DtplHZ5o.js → index-CiVEatgH.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BPdH-XTd.js → index-CrGQsbpb.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cg0oYTy5.js → index-D4ZHX6wo.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DVjFV2mg.js → index-D9lFBkS4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C6bAvixK.js → index-DD-V-N_m.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-BN5sO3kL.js → index-DLHnYXVa.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DUz_bv5i.js +1 -0
  122. package/src/assets/web-panel/assets/{index-BtJzqXCQ.js → index-DVBG0Jqz.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-dg0JqQDA.js → index-Dc0hePXV.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Cz0blpuN.js → index-DtBUgc0G.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-3o8DjHHt.js → index-Dv5E07PZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CD_A-N7c.js → index-DvoMGeL9.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-ggeC-_dR.js → index-EDWA9JL1.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BGxI_Qxg.js → index-IVelXaiQ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Bw88Mk2t.js → index-N9hNOl1i.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BivQyKes.js → index-WhoNHp50.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BjfpW5s3.js → index-dbM4QvQZ.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-KY7udrs-.js → index-jgN2e0Oy.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D1t2IqNU.js → index-nogjrcQE.js} +1 -1
  134. package/src/assets/web-panel/assets/index-rQ59l7GO.js +1 -0
  135. package/src/assets/web-panel/assets/{index-D9CqHOgd.js → index-yYdCdtY1.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-DDIGXQ08.js → initDefaultProps-DkKS5xtR.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-97SXSYE4.js → motion-CU-zBUIg.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-CfDQMFRe.js → move-zBE0VEd4.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-qLYCGwY8.js → mtc-parser-B9WyO1je.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-xVqy1mCI.js → omit-6sCoBNTL.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DCGlFyfv.js → pickAttrs-DDRv_sFc.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-DYgPuMk3.js → placementArrow-Cy_CBDw5.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-7zUIdAWH.js → responsiveObserve-CIczLq9K.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-TyaiZlSh.js → slide-DFfZCvnG.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BPd13Fae.js → statusUtils-DW-0HUl-.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-A44XGgQ0.js → styleChecker-BPYaBorP.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-ZX5EUa8B.js → useFlexGapSupport-DYPoOaZc.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-Sh_fBPqz.js → useFs-ODUEWR17.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-DhR6Izei.js → usePersonalDataHub-CstUt15X.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DC8g6oRh.js → vnode-D2GW8xCL.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-DRjJmOwj.js → zoom-Bz6GoDwq.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/data/changelog.json +13 -0
  154. package/src/lib/auto-mode-config.js +129 -45
  155. package/src/lib/context-memory-kernel/task-worklog-port.js +73 -24
  156. package/src/lib/diagnostic-excerpt.js +91 -10
  157. package/src/lib/diagnostic-workflow.js +2 -0
  158. package/src/lib/git-tool-input.js +7 -0
  159. package/src/lib/remote-read-loop-guard.js +59 -14
  160. package/src/lib/task-progress-tracker.js +112 -12
  161. package/src/repl/agent-repl.js +15 -14
  162. package/src/assets/web-panel/assets/MarkdownRenderer-BpRWpN1E.js +0 -1
  163. package/src/assets/web-panel/assets/PdhVaultBrowser-Btw816xR.js +0 -7
  164. package/src/assets/web-panel/assets/devWarning-QMSC9xgn.js +0 -1
  165. package/src/assets/web-panel/assets/index-DVq7UuW0.js +0 -1
  166. package/src/assets/web-panel/assets/index-DYVepsIV.js +0 -1
@@ -1,12 +1,41 @@
1
+ // Prefer test-runner verdicts over arbitrary "Error:" lines printed by tests.
2
+ // A verdict is evidence to retain, not a claim about which defect caused it.
3
+ const TEST_FAILURE =
4
+ /\bFAIL(?:ED)?\s+[^\r\n]*\.(?:[cm]?[jt]sx?|py|go|rs|java)(?=[:\s]|$)|(?:^|\s)not ok \d+\s+-|\bTest Files\s+\d+ failed/;
5
+ export function hasTestFailureVerdict(value) {
6
+ return (
7
+ typeof value === "string" &&
8
+ TEST_FAILURE.test(stripVTControlCharacters(value))
9
+ );
10
+ }
11
+
1
12
  /** Retain source context for several errors; an error line is not a root cause. */
2
13
  export function diagnosticExcerpt(value, limit = 1600) {
3
- const text = typeof value === "string" ? value : JSON.stringify(value ?? "");
14
+ const text = stripVTControlCharacters(
15
+ typeof value === "string" ? value : JSON.stringify(value ?? ""),
16
+ );
4
17
  if (text.length <= limit) return text;
18
+ if (limit < 256) return text.slice(0, Math.max(0, limit));
5
19
  const marker = "\n[... omitted ...]\n";
6
- const lines = text.split("\n");
20
+ // gh prefixes every log line with job/step/timestamp. Keep the original
21
+ // head/tail, but spend diagnostic windows on the actual failure and stack.
22
+ const lines = text
23
+ .split("\n")
24
+ .map((line) =>
25
+ line.replace(/^[^\t\r\n]+\t[^\t\r\n]+\t\d{4}-\d\d-\d\dT[\d:.]+Z\s?/, ""),
26
+ );
7
27
  const errors = [];
8
28
  const workers = [];
29
+ const failures = [];
30
+ const summaries = [];
9
31
  for (let i = 0; i < lines.length; i++) {
32
+ if (TEST_FAILURE.test(lines[i])) failures.push(i);
33
+ if (
34
+ /\b(?:Test Files|Tests)\s+\d+ (?:failed|passed)|\b[\w -]+ tests (?:PASSED|FAILED)\b|\b(?:PASS|ok \d+ -)\b.*(?:negative|spawn|gate)/i.test(
35
+ lines[i],
36
+ )
37
+ )
38
+ summaries.push(i);
10
39
  if (
11
40
  /\b\w*Error:|Exception in thread|Caused by:|\bpanic:|panicked at|\bFAIL\b|error TS\d+|##\[error\]/.test(
12
41
  lines[i],
@@ -20,11 +49,21 @@ export function diagnosticExcerpt(value, limit = 1600) {
20
49
  )
21
50
  workers.push(i);
22
51
  }
23
- // Keep both an early diagnostic (possibly expected negative-test output)
24
- // and later runner failures. Never infer causality from their ordering.
52
+ // A middle failed-test block used to disappear between an expected early
53
+ // error and the final gate error. Keep its location and adjacent assertion,
54
+ // alongside process summaries; never infer causality from their ordering.
55
+ const lastError = errors.at(-1);
56
+ const finalError =
57
+ lastError !== undefined &&
58
+ /Process completed with exit code/.test(lines[lastError]) &&
59
+ errors.at(-2) >= lastError - 2
60
+ ? errors.at(-2)
61
+ : lastError;
25
62
  const indices = [
26
63
  ...new Set(
27
- [workers[0], errors[0], errors.at(-1)].filter((i) => i !== undefined),
64
+ [failures[0], workers[0], errors[0], finalError].filter(
65
+ (i) => i !== undefined,
66
+ ),
28
67
  ),
29
68
  ].sort((a, b) => a - b);
30
69
  if (!indices.length) {
@@ -33,19 +72,61 @@ export function diagnosticExcerpt(value, limit = 1600) {
33
72
  text.slice(0, head) + marker + text.slice(-(limit - marker.length - head))
34
73
  );
35
74
  }
36
- const available = limit - marker.length * (indices.length + 1);
75
+ const failedSuites = summaries.filter((index) =>
76
+ /tests FAILED\b/.test(lines[index]),
77
+ );
78
+ const verdictIndices = failedSuites.length
79
+ ? [
80
+ ...new Set(
81
+ [
82
+ ...failedSuites,
83
+ summaries.find((index) => /tests PASSED\b/.test(lines[index])),
84
+ ...failedSuites.map((index) => {
85
+ const suite = /([\w-]+ tests) FAILED\b/.exec(lines[index])?.[1];
86
+ return suite
87
+ ? summaries.findLast((next) =>
88
+ lines[next].includes(`${suite} PASSED`),
89
+ )
90
+ : undefined;
91
+ }),
92
+ ].filter((index) => index !== undefined),
93
+ ),
94
+ ]
95
+ : summaries.slice(-5);
96
+ const verdicts = verdictIndices
97
+ .map((index) => lines[index])
98
+ .join("\n")
99
+ .slice(0, Math.floor(limit * 0.2));
100
+ const available =
101
+ limit -
102
+ marker.length * (indices.length + 1 + (verdicts ? 1 : 0)) -
103
+ verdicts.length;
37
104
  const head = Math.floor(available * 0.1);
38
105
  const tail = Math.floor(available * 0.15);
39
- const perWindow = Math.floor((available - head - tail) / indices.length);
106
+ const windowBudget = available - head - tail;
107
+ const failureBudget = failures.length
108
+ ? Math.floor(windowBudget * (indices.length > 1 ? 0.55 : 1))
109
+ : 0;
110
+ const perWindow = Math.floor(
111
+ (windowBudget - failureBudget) /
112
+ Math.max(1, indices.length - (failures.length ? 1 : 0)),
113
+ );
40
114
  const windows = indices.map((index) => {
115
+ const size = index === failures[0] ? failureBudget : perWindow;
41
116
  const before = lines
42
117
  .slice(Math.max(0, index - 2), index)
43
118
  .join("\n")
44
- .slice(-Math.floor(perWindow * 0.2));
119
+ .slice(-Math.floor(size * 0.1));
45
120
  return (before + "\n" + lines.slice(index, index + 10).join("\n")).slice(
46
121
  0,
47
- perWindow,
122
+ size,
48
123
  );
49
124
  });
50
- return [text.slice(0, head), ...windows, text.slice(-tail)].join(marker);
125
+ return [
126
+ text.slice(0, head),
127
+ ...windows,
128
+ ...(verdicts ? [verdicts] : []),
129
+ text.slice(-tail),
130
+ ].join(marker);
51
131
  }
132
+ import { stripVTControlCharacters } from "node:util";
@@ -4,6 +4,8 @@ export const DIAGNOSTIC_WORKFLOW_GUIDANCE =
4
4
  "For debugging, retain the original failure signature (error, source location, stack, command, environment and revision when verified). " +
5
5
  "Label each observation by its source and reproduction scope. A passing subset is not a successful full reproduction; a different error is a separate finding until evidence connects it to the original failure. " +
6
6
  "Expected negative-test output, dependency/setup failures, failed assertions and runner/infrastructure failures are different possibilities. Inspect surrounding output and process outcomes before selecting one. Timing alone does not identify the cause, and argument order is not execution order. " +
7
+ "Keep confirmed facts separate from hypotheses and ruled-out signals, with their source. Preserve an established failure and excluded negative-test errors across compaction; a later command syntax error does not invalidate an earlier successful observation. Reopen a ruled-out hypothesis only when new evidence contradicts it. " +
8
+ "An issue, its linked PR, the PR head, a synthetic merge ref, the merged commit, and each workflow run/attempt are separate identities. Keep their numbers and revisions distinct. A green diagnostic retry does not replace the primary failure or prove that a flaky test was fixed. A different release workflow failing on that commit is a separate finding until connected to this task. " +
7
9
  "State one hypothesis and the smallest check that can disprove it; after that check, update the hypothesis from the observed result instead of restarting discovery. " +
8
10
  "Do not change dependencies while a reproduction using them is running. Poll background tasks by task_id and drain has_more_output after exit. " +
9
11
  "Bind reports to verified paths, runs and attempts; do not substitute a local or stale report for the target run. A green report alone does not override a nonzero exit, unhandled errors or incomplete output. " +
@@ -1,6 +1,13 @@
1
1
  // The git tool accepts argv text, not a shell program. Diagnose shell syntax
2
2
  // before spawning git while preserving literal operators in quoted arguments.
3
3
  export function gitToolInputError(command) {
4
+ if (/^\s*(?:git\s+)?gh(?:\.exe)?(?:\s|$)/i.test(command))
5
+ return {
6
+ success: false,
7
+ code: "CC_GIT_WRONG_TOOL",
8
+ error: "gh is the GitHub CLI, not a Git subcommand.",
9
+ hint: "Use run_shell for the original gh command, subject to its normal permissions. Use the git tool only for Git arguments such as status or log. No command was executed.",
10
+ };
4
11
  let quote = null;
5
12
  for (let i = 0; i < command.length; i++) {
6
13
  const char = command[i];
@@ -18,6 +18,19 @@ function pullRequestTarget(repo, id = "list") {
18
18
  return { ...githubTarget(repo, "pulls", id), pullRequest: true };
19
19
  }
20
20
 
21
+ function issueTarget(repo, id = "list") {
22
+ return { ...githubTarget(repo, "issues", id), issue: true };
23
+ }
24
+
25
+ const ISSUE_WORKFLOW_GUIDANCE =
26
+ "Issue investigation: retain the issue, linked PR and workflow run as separate targets, with verified revisions and observed test outcomes. " +
27
+ "Use gh issue view for an issue and gh pr view for its linked PR; their numbers need not match. " +
28
+ "Use gh run view <run-id> for one run; gh run list accepts filters, not a positional run ID. " +
29
+ "For unknown JSON fields, inspect stderr/help and request supported fields without jq or shell pipelines first. " +
30
+ "A completed metadata read remains evidence after an unrelated command fails; do not restart the same investigation. " +
31
+ "Once the failing test and source are known, state a falsifiable hypothesis, run the focused reproduction, then make and validate the authorized fix. " +
32
+ "A merged PR or a passing diagnostic retry alone does not resolve the reported failure. Perform issue closure only when authorized and supported by the requested resolution evidence. ";
33
+
21
34
  function commandFlag(command, names) {
22
35
  return new RegExp(
23
36
  `(?:^|\\s)(?:${names})(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|([^\\s;&|]+))`,
@@ -27,13 +40,16 @@ function commandFlag(command, names) {
27
40
  .find((value) => value !== undefined);
28
41
  }
29
42
 
30
- function pullRequestCommandTarget(command) {
43
+ function discussionCommandTarget(command) {
31
44
  const match =
32
- /(?:^|[\s;&|])gh(?:\.exe)?\s+pr\s+(list|view|diff)\b([^;&|\r\n]*)/i.exec(
45
+ /(?:^|[\s;&|])gh(?:\.exe)?\s+(pr|issue)\s+(list|view|diff)\b([^;&|\r\n]*)/i.exec(
33
46
  command,
34
47
  );
35
48
  if (!match) return null;
36
- const fields = commandFlag(match[2], "--json")?.split(",");
49
+ const target =
50
+ match[1].toLowerCase() === "issue" ? issueTarget : pullRequestTarget;
51
+ const tail = match[3];
52
+ const fields = commandFlag(tail, "--json")?.split(",");
37
53
  // Lightweight status polling is intentional waiting, not repeated review.
38
54
  if (
39
55
  fields?.length &&
@@ -44,18 +60,20 @@ function pullRequestCommandTarget(command) {
44
60
  )
45
61
  )
46
62
  return null;
47
- const repo = commandFlag(match[2], "--repo|-R") || "current-repo";
48
- if (match[1].toLowerCase() === "list") return pullRequestTarget(repo);
63
+ const repo = commandFlag(tail, "--repo|-R") || "current-repo";
64
+ if (match[2].toLowerCase() === "list") return target(repo);
49
65
  const prUrl =
50
- /https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/pull\/(\d+)\b/i.exec(match[2]);
51
- if (prUrl) return pullRequestTarget(prUrl[1], prUrl[2]);
66
+ /https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/(?:pull|issues)\/(\d+)\b/i.exec(
67
+ tail,
68
+ );
69
+ if (prUrl) return target(prUrl[1], prUrl[2]);
52
70
  // Remove flags that consume values before finding the positional PR number.
53
- const positional = match[2].replace(
71
+ const positional = tail.replace(
54
72
  /(?:^|\s)(?:--repo|-R|--json|--jq|-q|--template|-t|--color)(?:=|\s+)(?:"[^"]*"|'[^']*'|[^\s]+)/g,
55
73
  " ",
56
74
  );
57
75
  const number = /(?:^|\s)["']?(\d+)["']?(?=\s|$)/.exec(positional)?.[1];
58
- return number ? pullRequestTarget(repo, number) : null;
76
+ return number ? target(repo, number) : null;
59
77
  }
60
78
 
61
79
  function urlTarget(value) {
@@ -69,6 +87,10 @@ function urlTarget(value) {
69
87
  };
70
88
  }
71
89
  if (url.hostname === "github.com") {
90
+ const issue = /^\/([^/]+\/[^/]+)\/issues(?:\/(\d+))?\/?$/.exec(
91
+ url.pathname,
92
+ );
93
+ if (issue) return issueTarget(issue[1], issue[2]);
72
94
  const pr =
73
95
  /^\/([^/]+\/[^/]+)\/(?:pulls\/?|pull\/(\d+)(?:\/(?:files|commits|checks))?\/?|pull\/(\d+)\.(?:diff|patch))$/.exec(
74
96
  url.pathname,
@@ -87,6 +109,11 @@ function urlTarget(value) {
87
109
  );
88
110
  }
89
111
  if (url.hostname === "api.github.com") {
112
+ const issue =
113
+ /^\/repos\/([^/]+\/[^/]+)\/issues(?:\/(\d+)(?:\/(?:comments|events|timeline))?)?\/?$/.exec(
114
+ url.pathname,
115
+ );
116
+ if (issue) return issueTarget(issue[1], issue[2]);
90
117
  const pr =
91
118
  /^\/repos\/([^/]+\/[^/]+)\/pulls(?:\/(\d+)(?:\/(?:files|commits|reviews|comments))?)?\/?$/.exec(
92
119
  url.pathname,
@@ -144,8 +171,8 @@ export function remoteReadTarget(tool, args = {}) {
144
171
  }
145
172
  if (tool !== "run_shell") return null;
146
173
  const command = typeof args.command === "string" ? args.command : "";
147
- const pr = pullRequestCommandTarget(command);
148
- if (pr) return pr;
174
+ const discussion = discussionCommandTarget(command);
175
+ if (discussion) return discussion;
149
176
  const view = /(?:^|[\s;&|])gh(?:\.exe)?\s+run\s+view\s+([^;&|\r\n]+)/i.exec(
150
177
  command,
151
178
  );
@@ -172,6 +199,11 @@ export function remoteReadTarget(tool, args = {}) {
172
199
  /(?:^|\s)(?:(?:--field|--raw-field|--input)(?:=|\s)|-[fF])/.test(command)
173
200
  )
174
201
  return null;
202
+ const issueApi =
203
+ /(?:https:\/\/api\.github\.com\/)?\/?repos\/([\w.-]+\/[\w.-]+)\/issues(?:\/(\d+)(?:\/(?:comments|events|timeline))?)?(?=[?\s"']|$)/.exec(
204
+ command,
205
+ );
206
+ if (issueApi) return issueTarget(issueApi[1], issueApi[2]);
175
207
  const prApi =
176
208
  /(?:https:\/\/api\.github\.com\/)?\/?repos\/([\w.-]+\/[\w.-]+)\/pulls(?:\/(\d+)(?:\/(?:files|commits|reviews|comments))?)?(?=[?\s"']|$)/.exec(
177
209
  command,
@@ -369,6 +401,14 @@ export class RemoteReadLoopGuard {
369
401
  get recoveryHint() {
370
402
  if (!(this.targets.get(this.activeKey)?.repeats >= RECOVERY_AFTER))
371
403
  return null;
404
+ if (this.targets.get(this.activeKey)?.issue) {
405
+ return (
406
+ "Remote-read loop recovery: repeated issue reads returned failures or already-seen evidence. " +
407
+ "Use the retained observations; identify the exact missing fact before a new focused read. " +
408
+ ISSUE_WORKFLOW_GUIDANCE +
409
+ DIAGNOSTIC_WORKFLOW_GUIDANCE
410
+ );
411
+ }
372
412
  if (this.targets.get(this.activeKey)?.shellPolicy) {
373
413
  return (
374
414
  "Tool-policy loop recovery: the same execution policy keeps rejecting commands, even when their arguments change. " +
@@ -449,6 +489,8 @@ export class RemoteReadLoopGuard {
449
489
  }
450
490
 
451
491
  get workflowHint() {
492
+ if (this.targets.get(this.activeKey)?.issue)
493
+ return ISSUE_WORKFLOW_GUIDANCE + DIAGNOSTIC_WORKFLOW_GUIDANCE;
452
494
  const active = this.targets.get(this.activeKey);
453
495
  if (active?.github && !active.pullRequest) {
454
496
  return (
@@ -505,6 +547,7 @@ export class RemoteReadLoopGuard {
505
547
  page,
506
548
  localLog,
507
549
  pullRequest,
550
+ issue,
508
551
  shellPolicy,
509
552
  gitRepository,
510
553
  }) => ({
@@ -515,9 +558,11 @@ export class RemoteReadLoopGuard {
515
558
  : github
516
559
  ? pullRequest
517
560
  ? "GitHub pull requests"
518
- : localLog
519
- ? "saved GitHub Actions log"
520
- : "GitHub Actions logs"
561
+ : issue
562
+ ? "GitHub issues"
563
+ : localLog
564
+ ? "saved GitHub Actions log"
565
+ : "GitHub Actions logs"
521
566
  : tool === "web_search"
522
567
  ? "web_search"
523
568
  : "web_fetch",
@@ -1,6 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { remoteReadTarget } from "./remote-read-loop-guard.js";
3
- import { diagnosticExcerpt } from "./diagnostic-excerpt.js";
3
+ import {
4
+ diagnosticExcerpt,
5
+ hasTestFailureVerdict,
6
+ } from "./diagnostic-excerpt.js";
4
7
  import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "./diagnostic-workflow.js";
5
8
 
6
9
  // Discovery and bookkeeping are useful, but are not evidence that the task
@@ -63,21 +66,36 @@ function isGitInspection(result, args) {
63
66
  function isRemoteInspectionCommand(command) {
64
67
  if (typeof command !== "string") return false;
65
68
  if (
66
- /(?:^|[\s;&|])gh(?:\.exe)?\s+(?:run\s+(?:view|list|watch)|pr\s+(?:view|list|diff|checks)|workflow\s+(?:view|list)|auth\s+status)\b/i.test(
69
+ /(?:^|[\s;&|])gh(?:\.exe)?\s+(?:run\s+(?:view|list|watch|download)|(?:pr|issue)\s+(?:view|list|diff|checks)|workflow\s+(?:view|list)|release\s+(?:view|list|download)|repo\s+view|search\s+\w+|auth\s+status|(?:\w+\s+)*(?:--help|-h))\b/i.test(
67
70
  command,
68
71
  )
69
72
  )
70
73
  return true;
71
74
  return (
72
75
  /(?:^|[\s;&|])gh(?:\.exe)?\s+api\s/i.test(command) &&
73
- /(?:https:\/\/api\.github\.com\/)?\/?repos\/[\w.-]+\/[\w.-]+\/(?:actions|pulls|compare|releases|commits|check-runs|check-suites)\b/i.test(
74
- command,
75
- ) &&
76
76
  !/(?:^|\s)(?:--method|-X)(?:=|\s*)["']?(?!GET\b)\w+/i.test(command) &&
77
77
  !/(?:^|\s)(?:(?:--field|--raw-field|--input)(?:=|\s)|-[fF])/.test(command)
78
78
  );
79
79
  }
80
80
 
81
+ // Shell equivalents of read_file/search_files/list_dir are discovery too.
82
+ // This is advisory accounting, never an execution parser or permission grant.
83
+ function isLocalInspectionCommand(command) {
84
+ if (typeof command !== "string") return false;
85
+ if (
86
+ /\b(?:npm(?:\.cmd)?\s+(?:test|run|publish)|node(?:\.exe)?\s+--test|(?:go|mvn(?:\.cmd)?)\s+test|pytest|git\s+(?:commit|merge|cherry-pick|push)|gh\s+(?:(?:pr|issue)\s+(?:create|merge|close|edit|comment)|run\s+rerun))\b/i.test(
87
+ command,
88
+ ) ||
89
+ /\b(?:Set-Content|Add-Content|Remove-Item|Move-Item|Copy-Item|New-Item)\b/i.test(
90
+ command,
91
+ )
92
+ )
93
+ return false;
94
+ return /(?:^|[;&|\r\n]|-Command\s+["']?|=\s*)\s*(?:Get-Content|Get-ChildItem|Select-String|Select-Object|Test-Path|cat|head|tail|grep|rg|findstr(?:\.exe)?|dir|type|more|jq)(?=\s|$)/i.test(
95
+ command,
96
+ );
97
+ }
98
+
81
99
  // Advisory classification, never execution parsing or permission approval.
82
100
  // A retry wrapper or JSON filter is still investigation, even when each call
83
101
  // prints a different small result. Unknown computations remain available.
@@ -85,10 +103,8 @@ function isInspectionScript(code) {
85
103
  if (typeof code !== "string") return false;
86
104
  // Keep actual changes and focused verification eligible to advance the task.
87
105
  if (
88
- /\b(?:writeFile(?:Sync)?|appendFile(?:Sync)?|unlink(?:Sync)?|rename(?:Sync)?|mkdir(?:Sync)?|rmSync|rmdirSync|assert|pytest|vitest|unittest)\b/.test(
89
- code,
90
- ) ||
91
- /\b(?:npm\s+(?:test|run|publish)|git\s+(?:commit|merge|cherry-pick|push)|gh\s+(?:pr\s+(?:create|merge|close)|run\s+rerun))\b/i.test(
106
+ /\b(?:assert|pytest|vitest|unittest)\b/.test(code) ||
107
+ /\b(?:npm\s+(?:test|run|publish)|git\s+(?:commit|merge|cherry-pick|push)|gh\s+(?:(?:pr|issue)\s+(?:create|merge|close|edit|comment)|run\s+rerun))\b/i.test(
92
108
  code,
93
109
  )
94
110
  )
@@ -104,6 +120,15 @@ function isInspectionScript(code) {
104
120
  literals.some((value) => isRemoteInspectionCommand(`gh ${value}`))
105
121
  )
106
122
  return true;
123
+ // Saving fetched metadata is still inspection. Other file changes can be
124
+ // implementation work and remain eligible to advance the task.
125
+ if (
126
+ /\b(?:writeFile(?:Sync)?|appendFile(?:Sync)?|unlink(?:Sync)?|rename(?:Sync)?|mkdir(?:Sync)?|rmSync|rmdirSync)\b/.test(
127
+ code,
128
+ )
129
+ )
130
+ return false;
131
+ if (literals.some((value) => isLocalInspectionCommand(value))) return true;
107
132
  // Parsing previously saved logs/metadata is evidence collection too.
108
133
  return (
109
134
  /\b(?:readFileSync|readFile|readdirSync|readdir|read_text|read_bytes|json\.load)\s*\(/.test(
@@ -141,6 +166,7 @@ export class TaskProgressTracker {
141
166
  this.remoteInspections = [];
142
167
  this.localFindings = [];
143
168
  this.diagnostics = [];
169
+ this.failureEvidence = new Map();
144
170
  }
145
171
 
146
172
  retainChildResult(id, result, owner = "root") {
@@ -180,6 +206,51 @@ export class TaskProgressTracker {
180
206
  typeof args.old_string === "string" &&
181
207
  args.old_string === args.new_string);
182
208
 
209
+ // Preserve observed test verdicts separately from the rolling last-four
210
+ // queries. Repeated metadata reads must not evict the original failure.
211
+ // These are source excerpts, never an inferred root cause or fix verdict.
212
+ if (result?.code !== "CC_TOOL_RECOVERY_PAUSED") {
213
+ const evidence = [
214
+ result?.stdout_diagnostics,
215
+ result?.stderr_diagnostics,
216
+ result?.output,
217
+ result?.stdout,
218
+ result?.stderr,
219
+ result?.content,
220
+ result?.matches,
221
+ ]
222
+ .filter(Boolean)
223
+ .map((value) =>
224
+ typeof value === "string" ? value : JSON.stringify(value),
225
+ )
226
+ .join("\n");
227
+ if (hasTestFailureVerdict(evidence)) {
228
+ const excerpt = diagnosticExcerpt(evidence, 1600);
229
+ const digest = createHash("sha256").update(excerpt).digest("hex");
230
+ const entries = this.failureEvidence.get(owner) || [];
231
+ if (!entries.some((entry) => entry.digest === digest)) {
232
+ const entry = {
233
+ digest,
234
+ tool,
235
+ observedAt: this.now(),
236
+ source: boundedText(
237
+ args.path || args.command || args.code || result?.path,
238
+ 400,
239
+ ),
240
+ excerpt,
241
+ excerptTruncated: evidence.length > excerpt.length,
242
+ };
243
+ // Keep the first failure plus the latest distinct observation.
244
+ remember(
245
+ this.failureEvidence,
246
+ owner,
247
+ entries.length ? [entries[0], entry] : [entry],
248
+ MAX_CHECKPOINT_OWNERS,
249
+ );
250
+ }
251
+ }
252
+ }
253
+
183
254
  // Preserve observed reproduction results through compaction, including
184
255
  // failed runs and completed background tasks. They do not establish that
185
256
  // the original CI failure was reproduced or fixed.
@@ -219,7 +290,8 @@ export class TaskProgressTracker {
219
290
  ? "git-inspection"
220
291
  : remoteTarget?.github || isRemoteInspectionCommand(command)
221
292
  ? "remote-inspection"
222
- : tool === "run_code" && isInspectionScript(command)
293
+ : (tool === "run_code" && isInspectionScript(command)) ||
294
+ (tool === "run_shell" && isLocalInspectionCommand(command))
223
295
  ? "script-inspection"
224
296
  : "local-execution"),
225
297
  invocation: boundedText(command || previous?.invocation, 1000),
@@ -334,6 +406,7 @@ export class TaskProgressTracker {
334
406
  exploration =
335
407
  !!remoteReadTarget(tool, args) ||
336
408
  remoteInspection ||
409
+ (tool === "run_shell" && isLocalInspectionCommand(args.command)) ||
337
410
  (tool === "run_code" && isInspectionScript(args.code)) ||
338
411
  output.length >= 8000 ||
339
412
  this.commandOutputs.has(digest);
@@ -413,11 +486,21 @@ export class TaskProgressTracker {
413
486
  !this.lastActions.length &&
414
487
  !this.remoteInspections.length &&
415
488
  !this.diagnostics.length &&
416
- !this.localFindings.length
489
+ !this.localFindings.length &&
490
+ !this.failureEvidence.size
417
491
  )
418
492
  return null;
419
493
  const checkpoint = {
420
494
  bounded: true,
495
+ observedTestFailures: (this.failureEvidence.get(owner) || []).map(
496
+ ({ tool, observedAt, source, excerpt, excerptTruncated }) => ({
497
+ tool,
498
+ observedAt,
499
+ source,
500
+ excerpt,
501
+ excerptTruncated,
502
+ }),
503
+ ),
421
504
  recentDiagnostics: this.diagnostics
422
505
  .filter((entry) => entry.owner === owner)
423
506
  .map(({ owner: _owner, ...entry }) => entry),
@@ -449,6 +532,7 @@ export class TaskProgressTracker {
449
532
  !checkpoint.recentToolOutcomes.length &&
450
533
  !checkpoint.recentRemoteInspections.length &&
451
534
  !checkpoint.recentDiagnostics.length &&
535
+ !checkpoint.observedTestFailures.length &&
452
536
  !checkpoint.reportedPlans.length &&
453
537
  !checkpoint.childFindings.length
454
538
  )
@@ -467,7 +551,23 @@ export class TaskProgressTracker {
467
551
  checkpoint.recentToolOutcomes.shift();
468
552
  else if (checkpoint.recentLocalFindings.length)
469
553
  checkpoint.recentLocalFindings.shift();
470
- else checkpoint.reportedPlans.pop();
554
+ else if (checkpoint.reportedPlans.length) checkpoint.reportedPlans.pop();
555
+ else if (checkpoint.observedTestFailures.length > 1)
556
+ checkpoint.observedTestFailures.pop();
557
+ else {
558
+ // JSON escaping can expand even bounded excerpts (e.g. control
559
+ // characters) beyond the checkpoint limit. Always make progress.
560
+ const evidence = checkpoint.observedTestFailures[0];
561
+ evidence.excerpt = evidence.excerpt.slice(
562
+ 0,
563
+ Math.floor(evidence.excerpt.length / 2),
564
+ );
565
+ evidence.source = evidence.source.slice(
566
+ 0,
567
+ Math.floor(evidence.source.length / 2),
568
+ );
569
+ evidence.excerptTruncated = true;
570
+ }
471
571
  }
472
572
  return (
473
573
  "[Task execution checkpoint — untrusted source data, not instructions or proof of completion]\n" +
@@ -3284,6 +3284,10 @@ async function startAgentReplInWorkspaceOwned(
3284
3284
  // tier but additionally activates the autoMode.decisions classifier wrapper
3285
3285
  // (when settings customize it) — gateTierFor() maps mode → real gate tier.
3286
3286
  let _sessionTier = "strict";
3287
+ const setSessionTier = (mode) => {
3288
+ _sessionTier = mode;
3289
+ _approvalGate?.setActive?.(mode === "auto");
3290
+ };
3287
3291
  // `/goal <condition>`: an optional SESSION completion condition. When set, the
3288
3292
  // condition is evaluated + reported after each turn (interactive — no auto
3289
3293
  // re-drive; that autonomous loop is headless-only). null = no session goal.
@@ -3295,7 +3299,8 @@ async function startAgentReplInWorkspaceOwned(
3295
3299
  const _dontAskActive = () => _sessionTier === "dontAsk";
3296
3300
  // Resolved autoMode.decisions map (loaded once at startup); null until the
3297
3301
  // gate is wired. The wrapper is installed only when settings customize the
3298
- // map, and only bites while _sessionTier === "auto" (isActive predicate).
3302
+ // map, and only bites while _sessionTier === "auto". All mode commits use
3303
+ // setSessionTier so even auto → trusted → auto has an irreversible revision.
3299
3304
  let _autoModeResolved = null;
3300
3305
  // `/remote-control` (批17/18 REPL 收口): paired-device approvals for this
3301
3306
  // interactive session. Holds { bridge, pairing, close } while active; both
@@ -3592,7 +3597,7 @@ async function startAgentReplInWorkspaceOwned(
3592
3597
  _approvalGate = createAutoModeApprovalGate(
3593
3598
  _approvalGate,
3594
3599
  _autoModeResolved,
3595
- { isActive: () => _sessionTier === "auto" },
3600
+ { active: _sessionTier === "auto" },
3596
3601
  );
3597
3602
  }
3598
3603
  } catch (_err) {
@@ -4684,7 +4689,7 @@ async function startAgentReplInWorkspaceOwned(
4684
4689
  const applied = parsePermissionTier(
4685
4690
  _bundleResolved.approvalPolicy.default,
4686
4691
  );
4687
- if (applied) _sessionTier = applied;
4692
+ if (applied) setSessionTier(applied);
4688
4693
  } catch (error) {
4689
4694
  logger.warn(`Bundle approval policy was not persisted: ${error.message}`);
4690
4695
  }
@@ -4701,11 +4706,9 @@ async function startAgentReplInWorkspaceOwned(
4701
4706
  try {
4702
4707
  await _approvalGate.setSessionPolicy(sessionId, parsed.tier);
4703
4708
  await _approvalGate.awaitPersistence?.();
4704
- _sessionTier = parsed.auto
4705
- ? "auto"
4706
- : parsed.dontAsk
4707
- ? "dontAsk"
4708
- : parsed.tier;
4709
+ setSessionTier(
4710
+ parsed.auto ? "auto" : parsed.dontAsk ? "dontAsk" : parsed.tier,
4711
+ );
4709
4712
  } catch (error) {
4710
4713
  logger.warn(`Approval policy was not persisted: ${error.message}`);
4711
4714
  }
@@ -5418,7 +5421,7 @@ async function startAgentReplInWorkspaceOwned(
5418
5421
  try {
5419
5422
  await _approvalGate.setSessionPolicy(sessionId, next);
5420
5423
  await _approvalGate.awaitPersistence?.();
5421
- _sessionTier = next;
5424
+ setSessionTier(next);
5422
5425
  process.stdout.write(
5423
5426
  "\n" +
5424
5427
  chalk.cyan(`⇥ approval: ${next}`) +
@@ -8850,11 +8853,9 @@ async function startAgentReplInWorkspaceOwned(
8850
8853
  try {
8851
8854
  await _approvalGate.setSessionPolicy(sessionId, parsed.tier);
8852
8855
  await _approvalGate.awaitPersistence?.();
8853
- _sessionTier = parsed.auto
8854
- ? "auto"
8855
- : parsed.dontAsk
8856
- ? "dontAsk"
8857
- : parsed.tier;
8856
+ setSessionTier(
8857
+ parsed.auto ? "auto" : parsed.dontAsk ? "dontAsk" : parsed.tier,
8858
+ );
8858
8859
  logger.info(
8859
8860
  `Approval policy → ${chalk.cyan(_sessionTier)} ${chalk.gray(`(${describeTier(_sessionTier)})`)}`,
8860
8861
  );
@@ -1 +0,0 @@
1
- import{P as c,J as p,b as s}from"./vendor-Bn1JzzAn.js";import{r as a}from"./markdown-Yo_c6Krv.js";import{_ as d}from"./index-C6bAvixK.js";const _={__name:"MarkdownRenderer",props:{content:{type:String,default:""}},setup(t,{expose:o}){o();const e=t,r=s(()=>a(e.content||"")),n={props:e,rendered:r,computed:s,get renderMarkdown(){return a}};return Object.defineProperty(n,"__isScriptSetup",{enumerable:!1,value:!0}),n}},i=["innerHTML"];function m(t,o,e,r,n,u){return c(),p("div",{class:"markdown-content",innerHTML:r.rendered},null,8,i)}const w=d(_,[["render",m],["__scopeId","data-v-0b0b9f63"],["__file","/tmp/cc-web-panel-2Nh58T/repo/packages/web-panel/src/components/MarkdownRenderer.vue"]]);export{w as M};