chainlesschain 0.166.41 → 0.166.43

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 (176) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-CK9AMSXc.js → AIOps-BIl-Z62M.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-B_PARoec.js → ActionButton-BgPi_Te6.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-kB2GmWlI.js → Analytics-CaNIzlAG.js} +3 -3
  6. package/src/assets/web-panel/assets/AppLayout-D9GSx2hl.js +9 -0
  7. package/src/assets/web-panel/assets/{Artifacts-C30LufT9.js → Artifacts-Kq7kotzv.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-Dx477PoD.js → Audit-e1RTJLco.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-ClvsCYCT.js → BackgroundAgents-BfyNT0BW.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-Bx9uCsgt.js → Backup-BEAq7i6S.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-Br_ecv7G.js → BaseInput-BHuP82DF.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-Dzr-d6NV.js → Chat-BLPgCQsB.js} +6 -6
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-DWXPJh-J.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-C9O-E90A.js → Checkbox-DDJrOqEh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CTRBkb29.js → Codegen-CxyKcM6S.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col--50unLLP.js → Col-BYFh9rE1.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-CFtrKGE_.js → Community-D8JPppoh.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DFoxxofh.js → Compact-B5VGSqZe.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CTpxqxpc.js → Compliance-C7KiFcUK.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-Dr-56gcV.js → Cowork-D-F2fgA_.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-B4xZgO5G.js → Cron-B6TmPqRO.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-Da7BQ7f3.js → Crosschain-BeGcBXhk.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DRsk74D5.js → DID-9QklLcmQ.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-DqtAeCxK.js → Dashboard-DIadmsuS.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-CvWZp7Kk.js → Dropdown-0I5gks3i.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-D2gWVD3O.js → EmailListRenderer-BdTAjT2q.js} +1 -1
  27. package/src/assets/web-panel/assets/EvolutionSettings-Bdw8ApoT.css +1 -0
  28. package/src/assets/web-panel/assets/EvolutionSettings-C7_tq643.js +1 -0
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BFrROP-5.js → FamilyGuardDashboard-DQUQWwUY.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-CNMTv4_n.js → Federation-Drgi--Zn.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-Ct_saU8n.js → FormItemContext-BOf-9P8n.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-CrBn2s3P.js → GenericCardRenderer-CBBnIDaW.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-CufnJCrr.js → Git-BKimGd5P.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DKS1J5P5.js → Governance-DoXCXppu.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-CxB7n-T4.js → Inference-DzOhGhOE.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-uaO_yOid.js → KnowledgeGraph-DawgmU58.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-DnR-CXn3.js → Logs-Co3EJL2Y.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-DdkDiGUd.js → MarkdownRenderer-VUfiDRZN.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-ePcCe7m1.js → Marketplace-BqpMHnMW.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-CAR6PCH2.js → McpTools-C7ZSokcR.js} +2 -2
  41. package/src/assets/web-panel/assets/{Memory-Bl_H1aqs.js → Memory-DuJrpeDx.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-BjMD_o0K.js → MobileBridge-CaM744NU.js} +1 -1
  43. package/src/assets/web-panel/assets/MobileProjects-Bd78jDwm.js +1 -0
  44. package/src/assets/web-panel/assets/{Mtc-DxN4oBCy.js → Mtc-5oUB7lPh.js} +6 -6
  45. package/src/assets/web-panel/assets/{MtcAudit-CZg_CONT.js → MtcAudit-D9DNF6DB.js} +4 -4
  46. package/src/assets/web-panel/assets/{Multisig-D3nrsJvN.js → Multisig-CfMpFQSr.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-DfffM-fH.js → NLProgramming-CuhPZQ7q.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-QE3VQe9j.js → Notes-NtSeMoyM.js} +4 -4
  49. package/src/assets/web-panel/assets/{NotificationSettings-CYGciq5y.js → NotificationSettings-CB4e7V83.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-Cx5IwZLp.js → OrderTableRenderer-Bu8C6Qlj.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-C8RQZUBu.js → Organization-K_0bjm7y.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-Dg6cVFza.js → Overflow-D2vwxiPv.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-DjRkujm6.js → P2P-WQMHBu8q.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-CMpEtOjE.js → PdhVaultBrowser-CTZVBMZZ.js} +5 -5
  55. package/src/assets/web-panel/assets/{Permissions-jOoYkoQU.js → Permissions-DcLy9Nfp.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-CfArs3Fo.js → PersonalDataHub-zc-cm-02.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-DBaHyX1b.js → Pipeline-CCnI8peM.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-Wv2VD4nl.js → Privacy-DO1Xor8L.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-CiJPc1_A.js → ProjectInit-CZqC-jyt.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-Dw1cWjEG.js → ProjectSettings-DqJtyavX.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-CWtB5sqm.js → Projects-Dx14NAO3.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-DSn6SDyz.js → Providers-DH13a6ro.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-ft3b2Qpr.js → QrScannerModal-C9bwzVfA.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-GE6AQSq7.js → QuickAsk-Cbt-oIPf.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-DMSEq8uY.js → Recommend-BYIimGcY.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-Uu-HSEai.js → RemoteSession-C8OWTG1H.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-CIcpi3_V.js → Reputation-DpUGT_jD.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-UVMeD3Wm.js → Row-DZ5Izi2A.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CD4MNI1l.js → RssFeed-CAn94No1.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-AlxcFL5q.js → Search-BqNaxcgj.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-Du5J0dDR.js → Security-Ck6vFTWn.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-CGDVJAoN.js → Services-Y3RdvvDj.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-CCQDNIXk.js → Skeleton-Cx9Iqcp7.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-4BRwqGQe.js → Skills-Oiqz7wLM.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-CgG0JuJh.js → Sla-DaKJQADm.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-qgkTz2pO.js → SpeechSettings-BWoOpPW4.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-DRTuKcuZ.js → SyncSettings-CgFle8GX.js} +2 -2
  78. package/src/assets/web-panel/assets/Tasks-C1tQNvWx.js +1 -0
  79. package/src/assets/web-panel/assets/{Templates-d78d-BZI.js → Templates-CvvycaWi.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-XwKmfX8F.js → Tenant-D1FwaD12.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-Wqr2Kcev.js → Terminal-oorFjObO.js} +2 -2
  82. package/src/assets/web-panel/assets/TimelineRenderer-BjMs48Sj.js +1 -0
  83. package/src/assets/web-panel/assets/{Tokens-DWRgtMpW.js → Tokens-BkRb95xG.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-BCDWY1I7.js → Trigger-B94RYYAF.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-CI47nsTZ.js → Trust-DnBVpOWt.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-BxxUVNJf.js → UkeySign-DouHO6Up.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-K6atXnE3.js → VideoEditing-DDgRerWV.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-DALnM69L.js → Wallet-CQN9Un7X.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-DiDAUHrG.js → WebAuthn-DyHl2G3J.js} +4 -4
  90. package/src/assets/web-panel/assets/{WorkflowEditor-CCYTnfUH.js → WorkflowEditor-BQlN77Ey.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-0oQNEtRw.js → chat-0jOqe7ZD.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-C1A1ekPG.js → colors-kp5TAaKx.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-Bm5w8Q--.js → compact-item-DqF6Rml7.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-VzW0CVyb.js → createContext-lDyLBjy5.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-IfrMTwCK.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-B8u-fCyP.js → hasIn-CkiF91kH.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-5dKHibv1.js → index-8Hxbv1SG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DOrjt98B.js → index-B6V9l2P-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CflXFLrN.js → index-BAtpuJQ7.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Dj0yOalO.js → index-BKFL4DfP.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-NzJz9-49.js → index-BPcjLn2w.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BicVNOi5.js → index-BPwtIqRo.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-C4_ABRMS.js → index-BRu6Pe98.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C5LAh2pw.js → index-BSAeSPQe.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CtMiuJK-.js → index-BUqE17wu.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-k5exH2qO.js → index-BnU8hi3c.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-KCmGGWAT.js → index-Bq2s5obn.js} +5 -5
  108. package/src/assets/web-panel/assets/{index-DBE3blUA.js → index-Bu14YcwM.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BIYC0OR5.js → index-BvFFY-2V.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Dxx4WU96.js → index-ByggSxTQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BEQNF8dD.js → index-C-CS_-jC.js} +1 -1
  112. package/src/assets/web-panel/assets/index-C7nsIbMa.js +1 -0
  113. package/src/assets/web-panel/assets/{index-lE4Q1Goz.js → index-CI-H0ljO.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Dy85cPQr.js → index-CLchRTsS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CXEOVGeZ.js → index-CNPCAYHk.js} +1 -1
  116. package/src/assets/web-panel/assets/index-DFXFLdKN.js +1 -0
  117. package/src/assets/web-panel/assets/{index-BVcRk2yF.js → index-DHVNtkrR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CX4GAKgB.js → index-DHnFhMOL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BdhglNFh.js → index-DK47iA2d.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D7q2TsaJ.js → index-DPHmcQWh.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DcLZlcMA.js → index-DQGk8bV8.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-SmhdgbaR.js → index-DSDM-VNz.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C0m5PpMV.js → index-DTA_B9gU.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DV_Pfu-b.js → index-DX40v3aL.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DcmUqRug.js → index-DZU13W4o.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CzTQseVr.js → index-DduPk1RS.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DW-aGHSh.js → index-DgGNW2ep.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BbFi-5C6.js → index-DnI__Fwu.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-jMjP2tFO.js → index-DrVVi5jX.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DjMG0Vw9.js → index-DzU_SfdF.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CpZdwtCH.js → index-INxX1TMz.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BfKBL8PY.js → index-X1l5-ive.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Bg-LBQME.js → index-aHKeys8c.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CicT_guC.js → index-kenTha3h.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-D6ou7QIj.js → index-mkwJZMBT.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-DbZ3dIR7.js → initDefaultProps-CYYQ0SI1.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-ATVJwqzT.js → motion--Tyo36te.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-Bxj0ZywY.js → move-D2BjgQv7.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-0j7iOj1h.js → mtc-parser-dyIu5RU_.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-DsAzx9jL.js → omit-CDEAhC5H.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-CIyAoQIB.js → pickAttrs-DLKDlIQs.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-KU51oLwW.js → placementArrow-BveC8fe2.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-CCmx1m3K.js → responsiveObserve-51urgYrW.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-CoxSb_UP.js → slide-CCHbhZg-.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-B-3-3uWm.js → statusUtils-W4mtUXRP.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-BT1IFFpw.js → styleChecker-D4Drrz2a.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-CEKYnxGt.js → useFlexGapSupport-BBCd4kCM.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-B_UyzZK-.js → useFs-4Khxvw5Y.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-B7fGu-O1.js → usePersonalDataHub-7c72tleZ.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-BLOmOgkK.js → vnode-DzZ7Y0LN.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-8yzckhp-.js → zoom-C5jhaET3.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +1 -1
  154. package/src/commands/evolution-deployment.js +95 -0
  155. package/src/commands/evolution.js +2 -0
  156. package/src/data/changelog.json +26 -0
  157. package/src/gateways/ws/evolution-deployment-topic-handlers.js +53 -0
  158. package/src/lib/evolution/evolution-deployment-config.js +81 -0
  159. package/src/lib/evolution/evolution-deployment-loader.js +62 -13
  160. package/src/lib/evolution/evolution-deployment-profile.js +114 -0
  161. package/src/lib/pr-investigation-policy.js +42 -0
  162. package/src/lib/public-ide-capability-manifest.js +2 -0
  163. package/src/lib/read-file-loop-guard.js +36 -3
  164. package/src/lib/remote-read-loop-guard.js +65 -6
  165. package/src/lib/task-progress-tracker.js +97 -3
  166. package/src/lib/text-file-search.js +11 -0
  167. package/src/runtime/agent-core.js +130 -23
  168. package/src/runtime/agent-runtime.js +2 -0
  169. package/src/assets/web-panel/assets/AppLayout-SnniLCL6.js +0 -9
  170. package/src/assets/web-panel/assets/ChatBubbleRenderer-BQ7pUK2c.js +0 -1
  171. package/src/assets/web-panel/assets/MobileProjects--CKxaWJC.js +0 -1
  172. package/src/assets/web-panel/assets/Tasks-BfOq9EqX.js +0 -1
  173. package/src/assets/web-panel/assets/TimelineRenderer-BjzPNKDr.js +0 -1
  174. package/src/assets/web-panel/assets/devWarning-BuusI785.js +0 -1
  175. package/src/assets/web-panel/assets/index-BiH_a1Cv.js +0 -1
  176. package/src/assets/web-panel/assets/index-t3MTPSt6.js +0 -1
@@ -0,0 +1,95 @@
1
+ import chalk from "chalk";
2
+ import { logger } from "../lib/logger.js";
3
+ import {
4
+ configureEvolutionDeployment,
5
+ getEvolutionDeploymentStatus,
6
+ setEvolutionDeploymentEnabled,
7
+ } from "../lib/evolution/evolution-deployment-config.js";
8
+
9
+ function printStatus(status) {
10
+ logger.log(chalk.bold("Governed Skill evolution deployment"));
11
+ logger.log(
12
+ ` Effective: ${status.effectiveEnabled ? "enabled" : "disabled"}`,
13
+ );
14
+ logger.log(` Source: ${status.source}`);
15
+ logger.log(
16
+ ` Signature: ${status.verified ? "verified" : "not verified"}`,
17
+ );
18
+ logger.log(` Auto promotion: HOLD (manual review required)`);
19
+ logger.log(` Profile: ${status.profilePath}`);
20
+ if (status.descriptorPath)
21
+ logger.log(` Descriptor: ${status.descriptorPath}`);
22
+ if (status.trustRootPath)
23
+ logger.log(` Trust root: ${status.trustRootPath}`);
24
+ if (status.commands?.length)
25
+ logger.log(` Commands: ${status.commands.join(", ")}`);
26
+ if (status.error) logger.log(chalk.red(` Error: ${status.error}`));
27
+ }
28
+
29
+ function output(status, json) {
30
+ if (json) console.log(JSON.stringify(status, null, 2));
31
+ else printStatus(status);
32
+ }
33
+
34
+ function fail(error, json) {
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ if (json) console.log(JSON.stringify({ ok: false, error: message }, null, 2));
37
+ else logger.error(`Failed: ${message}`);
38
+ process.exitCode = 1;
39
+ }
40
+
41
+ export function registerEvolutionDeploymentCommands(parent) {
42
+ const deployment = parent
43
+ .command("deployment")
44
+ .description("Configure the signed governed Skill evolution deployment");
45
+
46
+ deployment
47
+ .command("status")
48
+ .description("Show the effective deployment and signature status")
49
+ .option("--json", "Output as JSON")
50
+ .action(async (options) => {
51
+ try {
52
+ output(await getEvolutionDeploymentStatus(), options.json);
53
+ } catch (error) {
54
+ fail(error, options.json);
55
+ }
56
+ });
57
+
58
+ deployment
59
+ .command("configure")
60
+ .description("Verify, save, and enable a signed deployment")
61
+ .requiredOption("--descriptor <path>", "Absolute signed descriptor path")
62
+ .requiredOption("--trust-root <path>", "Absolute Ed25519 public key path")
63
+ .option("--disabled", "Save without enabling")
64
+ .option("--json", "Output as JSON")
65
+ .action(async (options) => {
66
+ try {
67
+ output(
68
+ await configureEvolutionDeployment({
69
+ descriptorPath: options.descriptor,
70
+ trustRootPath: options.trustRoot,
71
+ enabled: options.disabled !== true,
72
+ }),
73
+ options.json,
74
+ );
75
+ } catch (error) {
76
+ fail(error, options.json);
77
+ }
78
+ });
79
+
80
+ for (const enabled of [true, false]) {
81
+ deployment
82
+ .command(enabled ? "enable" : "disable")
83
+ .description(
84
+ `${enabled ? "Enable" : "Disable"} the saved deployment profile`,
85
+ )
86
+ .option("--json", "Output as JSON")
87
+ .action(async (options) => {
88
+ try {
89
+ output(await setEvolutionDeploymentEnabled(enabled), options.json);
90
+ } catch (error) {
91
+ fail(error, options.json);
92
+ }
93
+ });
94
+ }
95
+ }
@@ -14,6 +14,7 @@ import { registerEvolutionWorkbenchCommands } from "./evolution-workbench.js";
14
14
  import { registerEvolutionAttestorTrustCommands } from "./evolution-attestor-trust.js";
15
15
  import { registerGovernedKnowledgeCommands } from "./evolution-knowledge.js";
16
16
  import { registerWikiSkillBenchmarkCommands } from "./evolution-benchmark.js";
17
+ import { registerEvolutionDeploymentCommands } from "./evolution-deployment.js";
17
18
  import {
18
19
  assessCapability,
19
20
  recordIncrementalModelMetrics,
@@ -56,6 +57,7 @@ export function registerEvolutionCommand(program, dependencies = {}) {
56
57
  registerEvolutionAttestorTrustCommands(evolution, dependencies);
57
58
  registerGovernedKnowledgeCommands(evolution, dependencies);
58
59
  registerWikiSkillBenchmarkCommands(evolution, dependencies);
60
+ registerEvolutionDeploymentCommands(evolution);
59
61
 
60
62
  // evolution assess <name> <score>
61
63
  evolution
@@ -1,6 +1,32 @@
1
1
  {
2
2
  "source": "CHANGELOG.md",
3
3
  "releases": [
4
+ {
5
+ "productVersion": null,
6
+ "cliVersion": "0.166.43",
7
+ "cliVersions": ["0.166.43"],
8
+ "date": null,
9
+ "title": "",
10
+ "sections": [
11
+ {
12
+ "heading": "Added - cc CLI 0.166.43: configure governed Skill evolution",
13
+ "body": "- Add a persistent, fail-closed deployment profile for signed governed-evolution\n descriptors and Ed25519 trust roots. Environment overrides remain available\n for centrally managed installations.\n- Add `cc evolution deployment status/configure/enable/disable` so administrators\n can verify and control the deployment without maintaining process environment\n variables.\n- Expose the same bounded configuration through authenticated App Server topics\n for `cc ui`; automatic promotion remains `HOLD` and review/release authority\n stays in the signed deployment host.\n- Document defaults, triggers, review flow, storage locations, and the visual\n configuration entry points used by both IDE plugins and `cc ui`."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "productVersion": null,
19
+ "cliVersion": "0.166.42",
20
+ "cliVersions": ["0.166.42"],
21
+ "date": null,
22
+ "title": "",
23
+ "sections": [
24
+ {
25
+ "heading": "Fixed - cc CLI 0.166.42: converge stalled investigations",
26
+ "body": "- Treat repeated web searches, fetches and read-only GitHub inspection as one\n bounded investigation, retaining remote evidence across compaction without\n letting cosmetic query changes reset convergence.\n- Keep recovery active until the agent takes a concrete mutation or verification\n step, and direct cancelled Actions investigations toward actionable reruns or\n fixes without weakening release gates.\n- Prevent cancellation propagation from being rewritten as a new aggregate gate\n failure while preserving fail-closed aggregation for completed matrix failures.\n- Stabilize Windows release evidence under hosted-runner load with bounded,\n commit-safe retries and test-only startup/capacity budgets; production lock,\n access-control and message-capacity limits remain unchanged."
27
+ }
28
+ ]
29
+ },
4
30
  {
5
31
  "productVersion": null,
6
32
  "cliVersion": "0.166.41",
@@ -0,0 +1,53 @@
1
+ import {
2
+ configureEvolutionDeployment,
3
+ getEvolutionDeploymentStatus,
4
+ setEvolutionDeploymentEnabled,
5
+ } from "../../lib/evolution/evolution-deployment-config.js";
6
+
7
+ function assertSecureControl(context) {
8
+ const server = context?.server;
9
+ const loopback = new Set(["127.0.0.1", "localhost", "::1"]);
10
+ if (!server?.token && !loopback.has(server?.host)) {
11
+ throw new Error(
12
+ "Skill evolution configuration requires a token-protected or loopback-only cc ui",
13
+ );
14
+ }
15
+ }
16
+
17
+ function requiredPath(value, name) {
18
+ if (
19
+ typeof value !== "string" ||
20
+ value.length === 0 ||
21
+ value.length > 4096 ||
22
+ /[\r\n\0]/u.test(value)
23
+ ) {
24
+ throw new Error(`${name} is invalid`);
25
+ }
26
+ return value;
27
+ }
28
+
29
+ export function createEvolutionDeploymentTopicHandlers(options = {}) {
30
+ const getStatus = options.getStatus || getEvolutionDeploymentStatus;
31
+ const configure = options.configure || configureEvolutionDeployment;
32
+ const setEnabled = options.setEnabled || setEvolutionDeploymentEnabled;
33
+ return {
34
+ "evolution.deployment.status": async (_frame, context) => {
35
+ assertSecureControl(context);
36
+ return getStatus();
37
+ },
38
+ "evolution.deployment.configure": async (frame, context) => {
39
+ assertSecureControl(context);
40
+ return configure({
41
+ descriptorPath: requiredPath(frame?.descriptorPath, "descriptorPath"),
42
+ trustRootPath: requiredPath(frame?.trustRootPath, "trustRootPath"),
43
+ enabled: true,
44
+ });
45
+ },
46
+ "evolution.deployment.set-enabled": async (frame, context) => {
47
+ assertSecureControl(context);
48
+ if (typeof frame?.enabled !== "boolean")
49
+ throw new Error("enabled must be a boolean");
50
+ return setEnabled(frame.enabled);
51
+ },
52
+ };
53
+ }
@@ -0,0 +1,81 @@
1
+ import {
2
+ readEvolutionDeploymentProfile,
3
+ setEvolutionDeploymentProfileEnabled,
4
+ writeEvolutionDeploymentProfile,
5
+ } from "./evolution-deployment-profile.js";
6
+ import { verifyEvolutionDeployment } from "./evolution-deployment-loader.js";
7
+
8
+ function environmentSelection(env) {
9
+ const descriptorPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_DESCRIPTOR;
10
+ const trustRootPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_TRUST_ROOT;
11
+ if (!descriptorPath && !trustRootPath) return null;
12
+ return { descriptorPath, trustRootPath };
13
+ }
14
+
15
+ export async function getEvolutionDeploymentStatus(options = {}) {
16
+ const env = options.env || process.env;
17
+ const saved = await readEvolutionDeploymentProfile(options);
18
+ const fromEnvironment = environmentSelection(env);
19
+ const source = fromEnvironment
20
+ ? "environment"
21
+ : saved.profile?.enabled
22
+ ? "profile"
23
+ : "none";
24
+ const selected =
25
+ fromEnvironment || (saved.profile?.enabled ? saved.profile : null);
26
+ const base = {
27
+ source,
28
+ effectiveEnabled: source !== "none",
29
+ profileEnabled: saved.profile?.enabled === true,
30
+ profilePath: saved.filePath,
31
+ descriptorPath:
32
+ selected?.descriptorPath || saved.profile?.descriptorPath || null,
33
+ trustRootPath:
34
+ selected?.trustRootPath || saved.profile?.trustRootPath || null,
35
+ verified: false,
36
+ error: saved.error,
37
+ autoPromotion: "hold",
38
+ };
39
+ if (!selected || saved.error) return Object.freeze(base);
40
+ try {
41
+ const verified = await verifyEvolutionDeployment(selected, options);
42
+ return Object.freeze({
43
+ ...base,
44
+ verified: true,
45
+ error: null,
46
+ revision: verified.descriptor.revision,
47
+ modulePath: verified.modulePath,
48
+ moduleDigest: verified.descriptor.moduleDigest,
49
+ commands: verified.descriptor.commands,
50
+ });
51
+ } catch (error) {
52
+ return Object.freeze({
53
+ ...base,
54
+ error: error instanceof Error ? error.message : String(error),
55
+ });
56
+ }
57
+ }
58
+
59
+ export async function configureEvolutionDeployment(
60
+ { descriptorPath, trustRootPath, enabled = true },
61
+ options = {},
62
+ ) {
63
+ const verified = await verifyEvolutionDeployment(
64
+ { descriptorPath, trustRootPath },
65
+ options,
66
+ );
67
+ await writeEvolutionDeploymentProfile(
68
+ {
69
+ enabled: enabled === true,
70
+ descriptorPath: verified.descriptorPath,
71
+ trustRootPath: verified.trustRootPath,
72
+ },
73
+ options,
74
+ );
75
+ return getEvolutionDeploymentStatus(options);
76
+ }
77
+
78
+ export async function setEvolutionDeploymentEnabled(enabled, options = {}) {
79
+ await setEvolutionDeploymentProfileEnabled(enabled, options);
80
+ return getEvolutionDeploymentStatus(options);
81
+ }
@@ -2,6 +2,7 @@ import { createHash, createPublicKey, verify } from "node:crypto";
2
2
  import { readFile, realpath } from "node:fs/promises";
3
3
  import { isAbsolute } from "node:path";
4
4
  import { types as utilTypes } from "node:util";
5
+ import { readEvolutionDeploymentProfile } from "./evolution-deployment-profile.js";
5
6
 
6
7
  export const EVOLUTION_DEPLOYMENT_DESCRIPTOR_SCHEMA =
7
8
  "chainlesschain.evolution-deployment-descriptor/v1";
@@ -223,7 +224,9 @@ async function loadBuiltInFactories(commandName) {
223
224
  import("./evolution-eval-process-supervisor.js"),
224
225
  import("./governed-skill-synthesis-attestor-trust-approval-client.js"),
225
226
  import("./governed-skill-synthesis-attestor-trust-operations-client.js"),
226
- import("./governed-skill-synthesis-attestor-trust-operations-cli-host.js"),
227
+ import(
228
+ "./governed-skill-synthesis-attestor-trust-operations-cli-host.js"
229
+ ),
227
230
  ]);
228
231
  factories.createEvolutionWorkbenchCliHost = createEvolutionWorkbenchCliHost;
229
232
  factories.createEvolutionWorkbenchReviewRuntime =
@@ -350,8 +353,9 @@ async function loadBuiltInFactories(commandName) {
350
353
  commandName === "stream" ||
351
354
  commandName === "ui"
352
355
  ) {
353
- const { createAgentEvolutionRuntimeComposition } =
354
- await import("./agent-evolution-runtime-composition.js");
356
+ const { createAgentEvolutionRuntimeComposition } = await import(
357
+ "./agent-evolution-runtime-composition.js"
358
+ );
355
359
  factories.createAgentEvolutionRuntimeComposition =
356
360
  createAgentEvolutionRuntimeComposition;
357
361
  }
@@ -414,20 +418,14 @@ function bindFactoriesToModule(factories, moduleDigest) {
414
418
  return Object.freeze(result);
415
419
  }
416
420
 
417
- export async function loadEvolutionDeploymentCommandDependencies(
418
- commandName,
421
+ export async function verifyEvolutionDeployment(
422
+ { descriptorPath, trustRootPath },
419
423
  {
420
- env = process.env,
421
424
  read = readFile,
422
425
  resolveRealPath = realpath,
423
- importModule = (url) => import(url),
424
- additionalFactories = {},
426
+ includeModuleBytes = false,
425
427
  } = {},
426
428
  ) {
427
- if (!SUPPORTED_COMMANDS.has(commandName)) return null;
428
- const descriptorPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_DESCRIPTOR;
429
- const trustRootPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_TRUST_ROOT;
430
- if (!descriptorPath && !trustRootPath) return null;
431
429
  if (!descriptorPath || !trustRootPath)
432
430
  throw new Error(
433
431
  "evolution deployment requires both descriptor and trust root",
@@ -458,7 +456,6 @@ export async function loadEvolutionDeploymentCommandDependencies(
458
456
  )
459
457
  )
460
458
  throw new Error("evolution deployment descriptor signature rejected");
461
- if (!descriptor.commands.includes(commandName)) return null;
462
459
 
463
460
  const modulePath = await resolveRealPath(descriptor.modulePath);
464
461
  const moduleBytes = await read(modulePath);
@@ -466,6 +463,58 @@ export async function loadEvolutionDeploymentCommandDependencies(
466
463
  throw new Error("evolution deployment module size is invalid");
467
464
  if (sha256(moduleBytes) !== descriptor.moduleDigest)
468
465
  throw new Error("evolution deployment module digest mismatch");
466
+ return Object.freeze({
467
+ descriptor,
468
+ descriptorPath: await resolveRealPath(descriptorPath),
469
+ trustRootPath: await resolveRealPath(trustRootPath),
470
+ modulePath,
471
+ ...(includeModuleBytes ? { moduleBytes } : {}),
472
+ });
473
+ }
474
+
475
+ export async function loadEvolutionDeploymentCommandDependencies(
476
+ commandName,
477
+ {
478
+ env = process.env,
479
+ read = readFile,
480
+ resolveRealPath = realpath,
481
+ importModule = (url) => import(url),
482
+ additionalFactories = {},
483
+ } = {},
484
+ ) {
485
+ if (!SUPPORTED_COMMANDS.has(commandName)) return null;
486
+ let descriptorPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_DESCRIPTOR;
487
+ let trustRootPath = env.CHAINLESSCHAIN_EVOLUTION_DEPLOYMENT_TRUST_ROOT;
488
+ let fromSavedProfile = false;
489
+ if (!descriptorPath && !trustRootPath) {
490
+ const saved = await readEvolutionDeploymentProfile({ env });
491
+ if (!saved.error && saved.profile?.enabled) {
492
+ descriptorPath = saved.profile.descriptorPath;
493
+ trustRootPath = saved.profile.trustRootPath;
494
+ fromSavedProfile = true;
495
+ }
496
+ }
497
+ if (!descriptorPath && !trustRootPath) return null;
498
+ if (!descriptorPath || !trustRootPath)
499
+ throw new Error(
500
+ "evolution deployment requires both descriptor and trust root",
501
+ );
502
+ let verified;
503
+ try {
504
+ verified = await verifyEvolutionDeployment(
505
+ { descriptorPath, trustRootPath },
506
+ { read, resolveRealPath, includeModuleBytes: true },
507
+ );
508
+ } catch (error) {
509
+ // A saved profile may become stale after files are moved or rotated. Keep
510
+ // governed capabilities fail-closed while allowing `deployment status`,
511
+ // `configure`, and `disable` to start so the operator can recover. Explicit
512
+ // environment overrides remain strict and surface the error immediately.
513
+ if (fromSavedProfile) return null;
514
+ throw error;
515
+ }
516
+ const { descriptor, moduleBytes } = verified;
517
+ if (!descriptor.commands.includes(commandName)) return null;
469
518
  // Import the exact bytes that were authenticated. Importing modulePath here
470
519
  // would reopen a pathname-replacement window between hashing and execution.
471
520
  // Deployment entrypoints are therefore single-file ESM bundles; any external
@@ -0,0 +1,114 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFile, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { dirname, isAbsolute, join } from "node:path";
4
+ import { resolveConfigDataRoot } from "../paths.js";
5
+ import {
6
+ PRIVATE_FILE_MODE,
7
+ ensurePrivateDirectory,
8
+ ensurePrivateFile,
9
+ } from "../secure-fs.js";
10
+
11
+ export const EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA =
12
+ "chainlesschain.evolution-deployment-profile/v1";
13
+
14
+ export function getEvolutionDeploymentProfilePath({
15
+ env = process.env,
16
+ cwd = process.cwd(),
17
+ } = {}) {
18
+ return join(
19
+ resolveConfigDataRoot({ env, cwd }).path,
20
+ "evolution",
21
+ "deployment-profile.json",
22
+ );
23
+ }
24
+
25
+ function normalizeProfile(value) {
26
+ if (!value || typeof value !== "object" || Array.isArray(value))
27
+ throw new TypeError("evolution deployment profile must be an object");
28
+ const keys = Object.keys(value).sort();
29
+ const expected = ["descriptorPath", "enabled", "schema", "trustRootPath"];
30
+ if (JSON.stringify(keys) !== JSON.stringify(expected.sort()))
31
+ throw new TypeError("evolution deployment profile has unexpected fields");
32
+ if (value.schema !== EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA)
33
+ throw new TypeError("evolution deployment profile schema is invalid");
34
+ if (typeof value.enabled !== "boolean")
35
+ throw new TypeError("evolution deployment profile enabled is invalid");
36
+ for (const [name, path] of [
37
+ ["descriptorPath", value.descriptorPath],
38
+ ["trustRootPath", value.trustRootPath],
39
+ ]) {
40
+ if (typeof path !== "string" || !isAbsolute(path))
41
+ throw new TypeError(
42
+ `evolution deployment profile ${name} must be absolute`,
43
+ );
44
+ }
45
+ return Object.freeze({
46
+ schema: value.schema,
47
+ enabled: value.enabled,
48
+ descriptorPath: value.descriptorPath,
49
+ trustRootPath: value.trustRootPath,
50
+ });
51
+ }
52
+
53
+ export async function readEvolutionDeploymentProfile(options = {}) {
54
+ const filePath =
55
+ options.filePath || getEvolutionDeploymentProfilePath(options);
56
+ try {
57
+ const profile = normalizeProfile(
58
+ JSON.parse(await readFile(filePath, "utf8")),
59
+ );
60
+ return Object.freeze({ filePath, profile, error: null });
61
+ } catch (error) {
62
+ if (error?.code === "ENOENT")
63
+ return Object.freeze({ filePath, profile: null, error: null });
64
+ return Object.freeze({
65
+ filePath,
66
+ profile: null,
67
+ error: error instanceof Error ? error.message : String(error),
68
+ });
69
+ }
70
+ }
71
+
72
+ export async function writeEvolutionDeploymentProfile(input, options = {}) {
73
+ const profile = normalizeProfile({
74
+ schema: EVOLUTION_DEPLOYMENT_PROFILE_SCHEMA,
75
+ enabled: input?.enabled === true,
76
+ descriptorPath: input?.descriptorPath,
77
+ trustRootPath: input?.trustRootPath,
78
+ });
79
+ const filePath =
80
+ options.filePath || getEvolutionDeploymentProfilePath(options);
81
+ ensurePrivateDirectory(dirname(filePath));
82
+ const temporaryPath = join(
83
+ dirname(filePath),
84
+ `.deployment-profile.${randomUUID()}.tmp`,
85
+ );
86
+ try {
87
+ await writeFile(temporaryPath, `${JSON.stringify(profile, null, 2)}\n`, {
88
+ encoding: "utf8",
89
+ mode: PRIVATE_FILE_MODE,
90
+ flag: "wx",
91
+ });
92
+ ensurePrivateFile(temporaryPath);
93
+ await rename(temporaryPath, filePath);
94
+ ensurePrivateFile(filePath);
95
+ } catch (error) {
96
+ await unlink(temporaryPath).catch(() => {});
97
+ throw error;
98
+ }
99
+ return Object.freeze({ filePath, profile });
100
+ }
101
+
102
+ export async function setEvolutionDeploymentProfileEnabled(
103
+ enabled,
104
+ options = {},
105
+ ) {
106
+ const current = await readEvolutionDeploymentProfile(options);
107
+ if (current.error) throw new Error(current.error);
108
+ if (!current.profile)
109
+ throw new Error("evolution deployment profile is not configured");
110
+ return writeEvolutionDeploymentProfile(
111
+ { ...current.profile, enabled: enabled === true },
112
+ { ...options, filePath: current.filePath },
113
+ );
114
+ }
@@ -0,0 +1,42 @@
1
+ // This selects a progress budget, never permission to close/merge a PR. Capture
2
+ // the user's request before compaction; tool output cannot select this policy.
3
+ export function isPrActionRequest(messages) {
4
+ const latest = messages?.findLast((message) => message.role === "user");
5
+ const text = (
6
+ typeof latest?.content === "string"
7
+ ? latest.content
8
+ : Array.isArray(latest?.content)
9
+ ? latest.content
10
+ .filter((part) => part?.type === "text")
11
+ .map((part) => part.text || "")
12
+ .join("\n")
13
+ : ""
14
+ ).replace(/<ide-context\b[^>]*>[\s\S]*?<\/ide-context>/gi, "");
15
+ return (
16
+ /\bPRs?\b|pull[ /_-]requests?|github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+|拉取请求|合并请求/iu.test(
17
+ text,
18
+ ) &&
19
+ /\b(?:handle|fix|resolve|close|merge)\b|处理|修复|解决|关闭|合并/iu.test(
20
+ text,
21
+ ) &&
22
+ !/\b(?:watch|monitor|wait|review|explain)\b|等待|监控|观察|审查|评审|解释/iu.test(
23
+ text,
24
+ )
25
+ );
26
+ }
27
+
28
+ export const PR_INVESTIGATION_LIMIT = 36;
29
+
30
+ export const PR_ACTION_GUIDANCE =
31
+ "PR action workflow: retain the target, head/base commits, current checks and concrete next action. " +
32
+ "Read metadata once, then inspect only the exact missing file patch or failed job. " +
33
+ "gh pr checks exit code 1 can mean failed checks: inspect the returned check results, not just the exit code. " +
34
+ "Do not rotate between gh api, gh pr and PR web pages to rediscover the same facts. " +
35
+ "Use the correct repository cwd and actual remote names. Fix and validate when the evidence supports an authorized change; otherwise report the specific missing prerequisite. " +
36
+ "A general request to handle a PR does not by itself authorize closing or merging it. Never make dummy edits to reset recovery.";
37
+
38
+ export const PR_INVESTIGATION_EXIT_GUIDANCE =
39
+ "PR investigation budget exhausted without an actionable outcome. This is the final synthesis turn; no tools may execute. " +
40
+ "Use the retained evidence to report what was verified, the concrete blocker or missing fact, and the smallest next step. " +
41
+ "Explicitly say the task is incomplete. Do not claim a PR was fixed, closed or merged without successful tool evidence. " +
42
+ "Do not promise another retry or restart the investigation.";
@@ -61,6 +61,7 @@ const VSCODE_COMMANDS = [
61
61
  "chainlesschain.plugins.manage",
62
62
  "chainlesschain.skills.retrieve",
63
63
  "chainlesschain.evolution.workbench",
64
+ "chainlesschain.evolution.configure",
64
65
  "chainlesschain.worktree.tasks",
65
66
  "chainlesschain.chrome.connector",
66
67
  "chainlesschain.artifacts.show",
@@ -118,6 +119,7 @@ const JETBRAINS_ACTIONS = [
118
119
  "chainlesschain.plugins.Manage",
119
120
  "chainlesschain.skills.Retrieve",
120
121
  "chainlesschain.evolution.Workbench",
122
+ "chainlesschain.evolution.Configure",
121
123
  "chainlesschain.worktree.Tasks",
122
124
  "chainlesschain.chrome.Connector",
123
125
  "chainlesschain.workspace.ScanAutoExec",
@@ -75,7 +75,7 @@ export class ReadFileLoopGuard {
75
75
  if (
76
76
  targeted &&
77
77
  !entry.rereads.has(fingerprint) &&
78
- entry.rereads.size < 128
78
+ entry.rereads.size < 3
79
79
  ) {
80
80
  entry.rereads.add(fingerprint);
81
81
  page.readRecovery = { action: "targeted-review" };
@@ -142,6 +142,14 @@ export class ReadFileLoopGuard {
142
142
  }
143
143
 
144
144
  record(tool, result, actionable = true) {
145
+ if (
146
+ this.batch &&
147
+ tool === "read_file" &&
148
+ result?.code === "CC_TOOL_RECOVERY_PAUSED"
149
+ ) {
150
+ this.batch.duplicates++;
151
+ return;
152
+ }
145
153
  if (
146
154
  !this.batch ||
147
155
  result?.error ||
@@ -153,8 +161,6 @@ export class ReadFileLoopGuard {
153
161
  return;
154
162
  if (tool === "read_file") {
155
163
  if (result?.readProgress?.newContent === true) this.batch.advanced = true;
156
- if (result?.readRecovery?.action === "targeted-review")
157
- this.batch.advanced = true;
158
164
  if (result?.readProgress?.newContent === false) this.batch.duplicates++;
159
165
  } else if (
160
166
  (tool === "run_code" || tool === "run_shell") &&
@@ -193,6 +199,33 @@ export class ReadFileLoopGuard {
193
199
  this.batch = null;
194
200
  }
195
201
 
202
+ get hasUnreadPages() {
203
+ return [...this.progress.values()].some(
204
+ (entry) => entry.summary?.reachedEnd === false,
205
+ );
206
+ }
207
+
208
+ get hasRecoveryReads() {
209
+ return (
210
+ this.hasUnreadPages ||
211
+ [...this.progress.values()].some((entry) => entry.rereads.size < 3)
212
+ );
213
+ }
214
+
215
+ canContinue(filePath, args = {}) {
216
+ const entry = this.progress.get(keyFor(filePath, args));
217
+ return (
218
+ entry?.summary?.reachedEnd === false ||
219
+ (!!entry &&
220
+ entry.rereads.size < 3 &&
221
+ Number.isSafeInteger(args.offset) &&
222
+ args.offset > 0 &&
223
+ Number.isSafeInteger(args.limit) &&
224
+ args.limit > 0 &&
225
+ args.limit <= 80)
226
+ );
227
+ }
228
+
196
229
  get recoveryHint() {
197
230
  return this.repeatedBatches >= 1
198
231
  ? "Repeated file reads or identical large command outputs detected. Do not switch to run_code/run_shell to dump the same file again. Filter/count locally and print only relevant rows, totals and source line numbers, or use a focused search. The runtime continues unfinished read_file scans automatically and returns use-findings after EOF. Use the retained outline to perform the user's task. Do not restart a whole-file scan after context compaction; answer once the requested evidence is sufficient."