chainlesschain 0.166.43 → 0.166.45

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 (202) hide show
  1. package/README.md +75 -14
  2. package/package.json +3 -3
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BIl-Z62M.js → AIOps-CrMhYNeH.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BgPi_Te6.js → ActionButton-DLY5blZ3.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CaNIzlAG.js → Analytics-C0NhKQB0.js} +2 -2
  7. package/src/assets/web-panel/assets/{AppLayout-D9GSx2hl.js → AppLayout-DLyepgMq.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-Kq7kotzv.js → Artifacts-Depk01Sa.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-e1RTJLco.js → Audit-jKe6NQ3J.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BfyNT0BW.js → BackgroundAgents-CG4GKT9s.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-BEAq7i6S.js → Backup-DYWCkzwM.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-BHuP82DF.js → BaseInput-xm5vodmJ.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-BLPgCQsB.js → Chat-B20_PNd3.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-D4d-eh0F.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-DDJrOqEh.js → Checkbox-CfgwkV_r.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CxyKcM6S.js → Codegen-DbnoWI44.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-BYFh9rE1.js → Col-CfzzhgGw.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-D8JPppoh.js → Community-BU7mGM8p.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-B5VGSqZe.js → Compact-DpYim0an.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-C7KiFcUK.js → Compliance-BNJcO6ra.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-D-F2fgA_.js → Cowork-CecZ-pWj.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-B6TmPqRO.js → Cron-BG_C1fe3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BeGcBXhk.js → Crosschain-BAeILQLL.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-9QklLcmQ.js → DID-CYqOEkXi.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-DIadmsuS.js → Dashboard-BYU8wmL1.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-0I5gks3i.js → Dropdown-DYlJeTTa.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BdTAjT2q.js → EmailListRenderer-C0M6py3K.js} +1 -1
  28. package/src/assets/web-panel/assets/EvolutionSettings-_QnjPFZJ.js +1 -0
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DQUQWwUY.js → FamilyGuardDashboard-Du90iDch.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-Drgi--Zn.js → Federation-DkFp_hzB.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-BOf-9P8n.js → FormItemContext-BGgtGTfG.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-CBBnIDaW.js → GenericCardRenderer-BIDsSCei.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-BKimGd5P.js → Git--d3Te93J.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DoXCXppu.js → Governance-CZwD0vmO.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-DzOhGhOE.js → Inference-DiDcSFEW.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-DawgmU58.js → KnowledgeGraph-Bi0hSfzg.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-Co3EJL2Y.js → Logs-C8NHSc0K.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-VUfiDRZN.js → MarkdownRenderer-DfDuADqF.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-BqpMHnMW.js → Marketplace-D6kD0bNf.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-C7ZSokcR.js → McpTools-mTSamCr5.js} +2 -2
  41. package/src/assets/web-panel/assets/{Memory-DuJrpeDx.js → Memory-YQBXfLjU.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-CaM744NU.js → MobileBridge-D2awuupp.js} +1 -1
  43. package/src/assets/web-panel/assets/MobileProjects-Bl7lZc_-.js +1 -0
  44. package/src/assets/web-panel/assets/{Mtc-5oUB7lPh.js → Mtc-CBqjLK2Q.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-D9DNF6DB.js → MtcAudit-C4poRInZ.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-CfMpFQSr.js → Multisig-C_J0zXYZ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CuhPZQ7q.js → NLProgramming-Ci43sWOU.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-NtSeMoyM.js → Notes-DN7A-GFZ.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-CB4e7V83.js → NotificationSettings-D5jNm71V.js} +1 -1
  50. package/src/assets/web-panel/assets/OrderTableRenderer-C9UsPOG8.js +1 -0
  51. package/src/assets/web-panel/assets/{Organization-K_0bjm7y.js → Organization-BapYMJrP.js} +2 -2
  52. package/src/assets/web-panel/assets/{Overflow-D2vwxiPv.js → Overflow-BwxwjhFw.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-WQMHBu8q.js → P2P-TObXXOu3.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-CTZVBMZZ.js → PdhVaultBrowser-UwJvMbFO.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-DcLy9Nfp.js → Permissions-Damnxv-O.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-zc-cm-02.js → PersonalDataHub-d_7jlBb-.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-CCnI8peM.js → Pipeline-DA87VjLw.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-DO1Xor8L.js → Privacy-DzO_UvWn.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-CZqC-jyt.js → ProjectInit-CwqECDhs.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-DqJtyavX.js → ProjectSettings-CT5YdIl6.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-Dx14NAO3.js → Projects-D3KdZPCv.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-DH13a6ro.js → Providers-BjBIS8kk.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-C9bwzVfA.js → QrScannerModal-mMFqiKkI.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-Cbt-oIPf.js → QuickAsk-BwGi9ZU1.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-BYIimGcY.js → Recommend-BY65SR5s.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-C8OWTG1H.js → RemoteSession-BGsVH_XU.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-DpUGT_jD.js → Reputation-DpPbxSgI.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-DZ5Izi2A.js → Row-B5YBYoVz.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CAn94No1.js → RssFeed-DvM5jaoh.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-BqNaxcgj.js → Search-6RX39EPO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-Ck6vFTWn.js → Security-Beal-7cM.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-Y3RdvvDj.js → Services-D6rjVLe-.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-Cx9Iqcp7.js → Skeleton-Dur7R7vz.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-Oiqz7wLM.js → Skills-D1QkGwMk.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-DaKJQADm.js → Sla-3ObrX8Es.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-BWoOpPW4.js → SpeechSettings-DG_KAiH1.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-CgFle8GX.js → SyncSettings-BaHq0SJ2.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-C1tQNvWx.js → Tasks-BDpAkBAB.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-CvvycaWi.js → Templates-tYdoRd-c.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-D1FwaD12.js → Tenant-yGS5zIQ1.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-oorFjObO.js → Terminal-B3QbmVFa.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-BjMs48Sj.js → TimelineRenderer-B_7Pr5dS.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-BkRb95xG.js → Tokens-DPvpDa53.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-B94RYYAF.js → Trigger-DcSAD0GZ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-DnBVpOWt.js → Trust-BBzEq-uy.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-DouHO6Up.js → UkeySign-Dssav7-5.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-DDgRerWV.js → VideoEditing-CPOZf4yi.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CQN9Un7X.js → Wallet-BzBVRXEq.js} +3 -3
  89. package/src/assets/web-panel/assets/{WebAuthn-DyHl2G3J.js → WebAuthn-DdSGaCCh.js} +2 -2
  90. package/src/assets/web-panel/assets/{WorkflowEditor-BQlN77Ey.js → WorkflowEditor-CKjBFoSs.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-0jOqe7ZD.js → chat-B9aMGLg0.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-kp5TAaKx.js → colors-GFCMctSV.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DqF6Rml7.js → compact-item-BqSDFAYJ.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-lDyLBjy5.js → createContext-BwUm3Ffp.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-LxtRBsCm.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-CkiF91kH.js → hasIn-DN1kMJOT.js} +1 -1
  97. package/src/assets/web-panel/assets/index-36hxBH-o.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BvFFY-2V.js → index-7W1e9Vi4.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DQGk8bV8.js → index-B4MfxM5W.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-kenTha3h.js → index-B4dI_csl.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B6V9l2P-.js → index-B7Yu5wwV.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bq2s5obn.js → index-B9Pe-EyB.js} +3 -3
  103. package/src/assets/web-panel/assets/{index-aHKeys8c.js → index-BBMW8bQO.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPwtIqRo.js → index-BCnbC2z7.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-ByggSxTQ.js → index-BDL2MwxN.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BPcjLn2w.js → index-BHUxz4dy.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Bu14YcwM.js → index-BL_0ndQc.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DrVVi5jX.js → index-BPGlcDvs.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DzU_SfdF.js → index-BeycSy7n.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DnI__Fwu.js → index-BpiR6uzH.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CI-H0ljO.js → index-BwssFWI1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DPHmcQWh.js → index-By9GbJhu.js} +1 -1
  113. package/src/assets/web-panel/assets/index-C0b78zWZ.js +1 -0
  114. package/src/assets/web-panel/assets/{index-DX40v3aL.js → index-CEMl-Qte.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BRu6Pe98.js → index-CH2wK5dx.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-8Hxbv1SG.js → index-CRikhGgx.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DgGNW2ep.js → index-Ci0zIpSX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BUqE17wu.js → index-Cli0VREv.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BAtpuJQ7.js → index-CmEHzOsw.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DK47iA2d.js → index-Cwih67e8.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-mkwJZMBT.js → index-D8jcp9Rj.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DSDM-VNz.js → index-DAK28GY8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CNPCAYHk.js → index-DI90Wo_P.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLchRTsS.js → index-DO-IKERp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BKFL4DfP.js → index-DaFr7XB1.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DduPk1RS.js → index-DbDhKroq.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-X1l5-ive.js → index-DnJt9DaX.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-INxX1TMz.js → index-DvvJWwT8.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DHnFhMOL.js → index-PJt1Zrx2.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DHVNtkrR.js → index-PKwZkQDC.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DZU13W4o.js → index-THsr_UUJ.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C-CS_-jC.js → index-bwb4cg_D.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BSAeSPQe.js → index-rCTtYCGu.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BnU8hi3c.js → index-tMF_deZb.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DTA_B9gU.js → index-y7cfh9rP.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-CYYQ0SI1.js → initDefaultProps-B6e5CZ5p.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion--Tyo36te.js → motion-riMM1KfJ.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-D2BjgQv7.js → move-C1NSDMjc.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-dyIu5RU_.js → mtc-parser-Bibuny9B.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-CDEAhC5H.js → omit-Cmf6FoRn.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-DLKDlIQs.js → pickAttrs-BSsYBUCY.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BveC8fe2.js → placementArrow-gSB7-GAS.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-51urgYrW.js → responsiveObserve-qaqttvKa.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-CCHbhZg-.js → slide-RX2gxl3k.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-W4mtUXRP.js → statusUtils-DEJmkhqw.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-D4Drrz2a.js → styleChecker-CIWmJ78O.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-BBCd4kCM.js → useFlexGapSupport-D-UPSQBE.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-4Khxvw5Y.js → useFs-9QFtqADc.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-7c72tleZ.js → usePersonalDataHub-DM6kyift.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-DzZ7Y0LN.js → vnode-C-q6FNSJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-C5jhaET3.js → zoom-D4WJY2m8.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/agent.js +19 -2
  155. package/src/commands/ask.js +13 -8
  156. package/src/commands/chat.js +8 -5
  157. package/src/commands/cowork.js +18 -9
  158. package/src/commands/eval.js +100 -45
  159. package/src/commands/evolution-deployment.js +58 -0
  160. package/src/commands/hub.js +13 -7
  161. package/src/commands/setup.js +31 -1
  162. package/src/constants.js +2 -2
  163. package/src/data/changelog.json +188 -762
  164. package/src/gateways/ws/personal-data-hub-protocol.js +12 -5
  165. package/src/lazy-dispatch.js +42 -6
  166. package/src/lib/agent-router.js +25 -23
  167. package/src/lib/chat-intent-service.js +3 -13
  168. package/src/lib/context-memory-kernel/provider-context.js +19 -3
  169. package/src/lib/cowork-adapter.js +9 -4
  170. package/src/lib/doctor-checkup.js +42 -0
  171. package/src/lib/eval/evidence.js +203 -0
  172. package/src/lib/eval/runner.js +16 -2
  173. package/src/lib/eval/trend.js +4 -3
  174. package/src/lib/evolution/evolution-deployment-config.js +217 -10
  175. package/src/lib/evolution/evolution-deployment-descriptor-revocations.js +131 -0
  176. package/src/lib/evolution/evolution-deployment-loader.js +23 -2
  177. package/src/lib/evolution/evolution-deployment-profile.js +281 -37
  178. package/src/lib/evolution/evolution-deployment-root-rotation.js +154 -0
  179. package/src/lib/evolution/evolution-file-identity.js +42 -0
  180. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +242 -0
  181. package/src/lib/evolution/evolution-ledger-manifest-chain.js +74 -18
  182. package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +135 -20
  183. package/src/lib/evolution/evolution-ledger.js +353 -209
  184. package/src/lib/evolution/governed-hub-llm.js +18 -12
  185. package/src/lib/evolution/governed-hub-skill.js +4 -17
  186. package/src/lib/evolution/governed-model-turn.js +7 -8
  187. package/src/lib/llm-providers.js +1 -0
  188. package/src/lib/orchestrator.js +14 -8
  189. package/src/lib/personal-data-hub-wiring.js +7 -1
  190. package/src/lib/sub-agent-context.js +5 -1
  191. package/src/lib/task-model-selector.js +1 -1
  192. package/src/repl/agent-repl.js +1 -1
  193. package/src/repl/chat-repl.js +8 -4
  194. package/src/runtime/agent-core.js +36 -10
  195. package/src/runtime/headless-runner.js +16 -0
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-DWXPJh-J.js +0 -1
  197. package/src/assets/web-panel/assets/EvolutionSettings-C7_tq643.js +0 -1
  198. package/src/assets/web-panel/assets/MobileProjects-Bd78jDwm.js +0 -1
  199. package/src/assets/web-panel/assets/OrderTableRenderer-Bu8C6Qlj.js +0 -1
  200. package/src/assets/web-panel/assets/devWarning-IfrMTwCK.js +0 -1
  201. package/src/assets/web-panel/assets/index-C7nsIbMa.js +0 -1
  202. package/src/assets/web-panel/assets/index-DFXFLdKN.js +0 -1
@@ -192,6 +192,15 @@ async function withHub(fn, loadHub = getHub) {
192
192
  }
193
193
  }
194
194
 
195
+ function governedHubLoader(factory) {
196
+ if (typeof factory === "function") return () => getGovernedAnalysisHub(factory);
197
+ const error = new Error(
198
+ "Personal Data Hub model egress requires an authenticated evolution composition factory",
199
+ );
200
+ error.code = "CC_AGENT_EVOLUTION_INGRESS_FAILED";
201
+ throw error;
202
+ }
203
+
195
204
  export const PERSONAL_DATA_HUB_HANDLERS = {
196
205
  "personal-data-hub.ask": async (msg, context = {}) => {
197
206
  const factory = context.server?.evolutionCompositionFactory;
@@ -200,7 +209,7 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
200
209
  if (!hub.engine) throw new Error("Analysis engine unavailable");
201
210
  return await hub.engine.ask(msg.question, msg.options || {});
202
211
  },
203
- factory == null ? getHub : () => getGovernedAnalysisHub(factory),
212
+ governedHubLoader(factory),
204
213
  );
205
214
  },
206
215
 
@@ -614,9 +623,7 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
614
623
  withHub(async (hub) => {
615
624
  if (!hub.entityResolver) throw new Error("EntityResolver not wired");
616
625
  const factory = context.server?.evolutionCompositionFactory;
617
- if (factory != null)
618
- return hub.drainResolver({ limit: msg.limit || 50 }, factory);
619
- return await hub.entityResolver.drain({ limit: msg.limit || 50 });
626
+ return await hub.drainResolver({ limit: msg.limit || 50 }, factory);
620
627
  }),
621
628
 
622
629
  "personal-data-hub.resolver-stats": async () =>
@@ -641,7 +648,7 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
641
648
  const factory = context.server?.evolutionCompositionFactory;
642
649
  return withHub(
643
650
  async (hub) => await hub.runSkill(msg.name, msg.options || {}),
644
- factory == null ? getHub : () => getGovernedAnalysisHub(factory),
651
+ governedHubLoader(factory),
645
652
  );
646
653
  },
647
654
 
@@ -688,6 +688,24 @@ async function defaultLoadCommandDependencies(commandName) {
688
688
  return loadEvolutionDeploymentCommandDependencies(commandName);
689
689
  }
690
690
 
691
+ function isEvolutionDeploymentInvocation(argv, entry) {
692
+ if (entry.name !== "evolution") return false;
693
+ const location = findCommandTokenLocation(argv);
694
+ if (!location) return false;
695
+ for (let index = location.index + 1; index < argv.length;) {
696
+ const option = consumeGlobalOption(argv, index);
697
+ if (option) {
698
+ index = option.nextIndex;
699
+ continue;
700
+ }
701
+ const token = argv[index++];
702
+ if (token === "--") return argv[index] === "deployment";
703
+ if (token.startsWith("-")) continue;
704
+ return token === "deployment";
705
+ }
706
+ return false;
707
+ }
708
+
691
709
  /**
692
710
  * Load/register may fall back to the compatibility program. parseAsync is
693
711
  * intentionally outside that catch: once an action begins, any failure must
@@ -706,21 +724,39 @@ export async function dispatchManifestEntry(
706
724
  } = {},
707
725
  ) {
708
726
  let program;
709
- let allowCompatibilityFallback = true;
727
+ // Deployment inspection and repair must remain usable even when the signed
728
+ // host throws during import or dependency construction. Register only this
729
+ // configuration subtree, without executing the host or importing the eager
730
+ // program (which assembles every command's deployment dependencies).
731
+ const deploymentConfigurationOnly = isEvolutionDeploymentInvocation(
732
+ argv,
733
+ entry,
734
+ );
735
+ const registrationEntry = deploymentConfigurationOnly
736
+ ? {
737
+ ...entry,
738
+ module: "./commands/evolution-deployment.js",
739
+ register: "registerEvolutionDeploymentCommand",
740
+ }
741
+ : entry;
742
+ let allowCompatibilityFallback = !deploymentConfigurationOnly;
710
743
  try {
711
744
  program = await createBaseProgram();
712
- const mod = await loadCommandModule(entry);
713
- const registerFn = mod[entry.register];
745
+ const mod = await loadCommandModule(registrationEntry);
746
+ const registerFn = mod[registrationEntry.register];
714
747
  if (typeof registerFn !== "function") {
715
748
  throw new Error(
716
- `Register function '${entry.register}' not found in ${entry.module}`,
749
+ `Register function '${registrationEntry.register}' not found in ${registrationEntry.module}`,
717
750
  );
718
751
  }
719
752
  // Governed deployment loading may reconcile durable state. Do not hide a
720
753
  // failed trust check or retry that assembly through the eager program.
721
754
  allowCompatibilityFallback = false;
722
- const dependencies = await loadCommandDependencies(entry.name);
723
- allowCompatibilityFallback = dependencies == null;
755
+ const dependencies = deploymentConfigurationOnly
756
+ ? null
757
+ : await loadCommandDependencies(entry.name);
758
+ allowCompatibilityFallback =
759
+ !deploymentConfigurationOnly && dependencies == null;
724
760
  registerFn(program, dependencies ?? undefined);
725
761
  } catch (error) {
726
762
  if (!allowCompatibilityFallback) throw error;
@@ -22,7 +22,10 @@
22
22
  * ],
23
23
  * strategy: "round-robin",
24
24
  * });
25
- * const results = await router.dispatch(subtasks, { cwd: "/my/project" });
25
+ * const results = await router.dispatch(subtasks, {
26
+ * cwd: "/my/project",
27
+ * evolutionIngress,
28
+ * });
26
29
  */
27
30
 
28
31
  import { EventEmitter } from "events";
@@ -253,24 +256,30 @@ export class AgentRouter extends EventEmitter {
253
256
  * @param {Array<{id, description, context?, type?}>} subtasks
254
257
  * @param {object} options
255
258
  * @param {string} options.cwd
259
+ * @param {object} options.evolutionIngress authenticated model ingress
256
260
  * @returns {Promise<Array<{taskId, agentId, backendType, success, output, duration}>>}
257
261
  */
258
262
  async dispatch(subtasks, options = {}) {
259
263
  const { cwd = process.cwd() } = options;
264
+ if (this._backends.length === 0) {
265
+ throw new Error(
266
+ "No agent backends available. Install Claude Code: npm i -g @anthropic-ai/claude-code",
267
+ );
268
+ }
260
269
  const evolutionIngress =
261
270
  options.evolutionIngress == null
262
271
  ? null
263
272
  : captureAgentEvolutionIngress(options.evolutionIngress);
264
- const backends =
265
- evolutionIngress === null
266
- ? this._backends
267
- : this._backends.filter((backend) => !backend.isCLI);
268
-
269
- if (this._backends.length === 0) {
270
- throw new Error(
271
- "No agent backends available. Install Claude Code: npm i -g @anthropic-ai/claude-code",
273
+ if (evolutionIngress === null) {
274
+ const error = new Error(
275
+ "AgentRouter model dispatch requires an authenticated evolution composition",
272
276
  );
277
+ error.code = "CC_AGENT_EVOLUTION_INGRESS_FAILED";
278
+ throw error;
273
279
  }
280
+ const backends =
281
+ this._backends.filter((backend) => !backend.isCLI);
282
+
274
283
  if (backends.length === 0) {
275
284
  const error = new Error(
276
285
  "No backend can attest per-request evolution ingress; external agent CLIs are opaque",
@@ -422,20 +431,13 @@ export class AgentRouter extends EventEmitter {
422
431
  // tool, or model calls crossed this Run's durable ingress. Until an
423
432
  // authenticated child protocol can bind every such call and return
424
433
  // evidence, admitting the black box would create an unobservable model
425
- // bypass. Keep unmanaged legacy dispatch available, but fail closed for
426
- // governed evolution Runs before the process can be spawned.
427
- if (evolutionIngress !== null) {
428
- const error = new Error(
429
- `External ${backend.type} CLI does not attest per-request evolution ingress`,
430
- );
431
- error.code = AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED;
432
- throw error;
433
- }
434
-
435
- // Use ClaudeCodePool for unmanaged CLI-based backends.
436
- const pool = backend._pool;
437
- const [r] = await pool.dispatch([task], { cwd });
438
- result = r;
434
+ // bypass. This is deliberately unconditional so a future internal caller
435
+ // cannot bypass dispatch()'s candidate filter by calling this helper.
436
+ const error = new Error(
437
+ `External ${backend.type} CLI does not attest per-request evolution ingress`,
438
+ );
439
+ error.code = AGENT_ROUTER_ERROR.EXTERNAL_MODEL_INGRESS_UNATTESTED;
440
+ throw error;
439
441
  } else {
440
442
  // Use LLM API for API-based backends
441
443
  result = await executeViaAPI(task, {
@@ -17,7 +17,7 @@
17
17
  * JSON-output prompt as V5.
18
18
  */
19
19
 
20
- import { chatWithStreaming, chatStream } from "./chat-core.js";
20
+ import { chatStream } from "./chat-core.js";
21
21
  import { firstBalancedJson } from "./json-schema-output.js";
22
22
  import { prepareGovernedModelTurn } from "./evolution/governed-model-turn.js";
23
23
 
@@ -41,10 +41,6 @@ async function waitForIntentOperation(operation, signal) {
41
41
  }
42
42
 
43
43
  async function* intentModelStream(messages, options, factory, mode) {
44
- if (factory === null) {
45
- yield* chatStream(messages, options);
46
- return;
47
- }
48
44
  const controller = new AbortController();
49
45
  const timeoutMs = resolveIntentTimeout(options);
50
46
  const timer =
@@ -92,12 +88,6 @@ async function* intentModelStream(messages, options, factory, mode) {
92
88
  }
93
89
 
94
90
  async function intentModelText(messages, options, factory, mode) {
95
- if (factory === null) {
96
- return withIntentTimeout(
97
- chatWithStreaming(messages, options),
98
- resolveIntentTimeout(options),
99
- );
100
- }
101
91
  let content = "";
102
92
  for await (const event of intentModelStream(
103
93
  messages,
@@ -330,8 +320,8 @@ export async function* understandIntentStream({
330
320
  );
331
321
  let buffer = "";
332
322
  try {
333
- // Governed streams use an abortable per-call intent deadline. The legacy
334
- // unconfigured stream retains chat-core's silence-based stall guard.
323
+ // Every model-backed intent stream uses an abortable per-call deadline and
324
+ // authenticated composition; the rule-only path above stays local.
335
325
  for await (const event of intentModelStream(
336
326
  [
337
327
  { role: "system", content: systemPrompt },
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  canonicalDigest,
3
3
  canonicalJson,
4
+ isProtected,
4
5
  normalizeContextItem,
5
6
  } from "@chainlesschain/context-memory-kernel";
6
7
  import { getContextWindow } from "../model-context-window.js";
@@ -145,10 +146,20 @@ export async function prepareCanonicalProviderContext(
145
146
  );
146
147
  const inputBudget =
147
148
  modelWindowTokens - reservedOutputTokens - safetyMarginTokens;
148
- const recoveryReserveTokens = Math.min(
149
+ const desiredRecoveryReserveTokens = Math.min(
149
150
  Math.max(32, Math.floor(inputBudget * 0.05)),
150
151
  Math.max(0, inputBudget - 1),
151
152
  );
153
+ // The recovery reserve is a soft allocation. It must yield to protected
154
+ // current-turn state, which is already part of this provider request.
155
+ const protectedTokens = items.reduce(
156
+ (total, item) => total + (isProtected(item) ? item.tokenEstimate : 0),
157
+ 0,
158
+ );
159
+ const recoveryReserveTokens = Math.min(
160
+ desiredRecoveryReserveTokens,
161
+ Math.max(0, inputBudget - protectedTokens),
162
+ );
152
163
  const sessionHead =
153
164
  options.contextMemorySessionHead ||
154
165
  canonicalDigest(
@@ -171,8 +182,13 @@ export async function prepareCanonicalProviderContext(
171
182
  "tool-evidence": inputBudget,
172
183
  "memory-and-rules": Math.max(1, Math.floor(inputBudget * 0.12)),
173
184
  },
174
- policyVersion: String(options.contextMemoryPolicyVersion || "cli-provider-v1"),
175
- modelProfile: `${provider}:${String(options.model || "default")}`.slice(0, 256),
185
+ policyVersion: String(
186
+ options.contextMemoryPolicyVersion || "cli-provider-v1",
187
+ ),
188
+ modelProfile: `${provider}:${String(options.model || "default")}`.slice(
189
+ 0,
190
+ 256,
191
+ ),
176
192
  sessionHead,
177
193
  memoryRevision: recall.memoryRevision,
178
194
  });
@@ -121,6 +121,13 @@ export function createChatFn(options = {}) {
121
121
  options.evolutionIngress == null
122
122
  ? null
123
123
  : captureAgentEvolutionIngress(options.evolutionIngress);
124
+ if (evolutionIngress === null) {
125
+ const error = new Error(
126
+ "Cowork model egress requires an authenticated evolution ingress",
127
+ );
128
+ error.code = "CC_AGENT_EVOLUTION_INGRESS_FAILED";
129
+ throw error;
130
+ }
124
131
  // Fill provider/model/baseUrl/apiKey from config.llm only when the caller
125
132
  // gave no explicit provider AND no LLM_PROVIDER env override (both still win).
126
133
  // Fail-open: a config read must never break chat construction.
@@ -157,10 +164,8 @@ export function createChatFn(options = {}) {
157
164
  // Every invocation (including concurrent reviewers) gets fresh durable
158
165
  // admission; the original conversation is never replaced in place.
159
166
  const prepareMessages = async () =>
160
- evolutionIngress === null
161
- ? messages
162
- : (await evolutionIngress.prepareModelRequest({ messages, tools: [] }))
163
- .messages;
167
+ (await evolutionIngress.prepareModelRequest({ messages, tools: [] }))
168
+ .messages;
164
169
 
165
170
  if (provider === "ollama") {
166
171
  const envelope = await invokeChatCall(
@@ -387,6 +387,47 @@ async function providerSection(_opts, _deps2) {
387
387
  return { id: "provider", title: "Provider auth", checks };
388
388
  }
389
389
 
390
+ // ── governed model execution ───────────────────────────────────────────────
391
+ async function evolutionDeploymentSection(opts) {
392
+ const checks = [];
393
+ try {
394
+ const { getEvolutionDeploymentStatus, assessEvolutionDeploymentReadiness } =
395
+ await import("./evolution/evolution-deployment-config.js");
396
+ const readiness = assessEvolutionDeploymentReadiness(
397
+ await getEvolutionDeploymentStatus(opts.evolutionDeploymentOptions),
398
+ );
399
+ checks.push(
400
+ check(
401
+ "evolution-model-ingress",
402
+ "Model deployment admission (ask, agent)",
403
+ readiness.ready ? CHECK_LEVELS.OK : CHECK_LEVELS.ERR,
404
+ readiness.detail,
405
+ readiness.ready
406
+ ? null
407
+ : {
408
+ id: "configure-evolution-model-ingress",
409
+ safe: false,
410
+ description: readiness.remediation,
411
+ command: "cc evolution deployment status --json",
412
+ },
413
+ ),
414
+ );
415
+ } catch (err) {
416
+ checks.push(
417
+ failedCheck(
418
+ "evolution-model-ingress",
419
+ "Model deployment admission (ask, agent)",
420
+ err,
421
+ ),
422
+ );
423
+ }
424
+ return {
425
+ id: "evolution-deployment",
426
+ title: "Model deployment prerequisites (not an execution probe)",
427
+ checks,
428
+ };
429
+ }
430
+
390
431
  // ── MCP config ─────────────────────────────────────────────────────────────
391
432
  async function mcpSection(opts, deps) {
392
433
  const checks = [];
@@ -1274,6 +1315,7 @@ export async function collectCheckupSections(opts = {}) {
1274
1315
  const builders = [
1275
1316
  configSection,
1276
1317
  providerSection,
1318
+ evolutionDeploymentSection,
1277
1319
  mcpSection,
1278
1320
  ideSection,
1279
1321
  pluginSection,
@@ -0,0 +1,203 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import { computeTrend } from "./trend.js";
4
+ import { redactSecrets } from "../secret-scan.js";
5
+
6
+ export const EVAL_HISTORY_SCHEMA = "chainlesschain.eval-history/v1";
7
+ export const EVAL_CONTEXT_SCHEMA = "chainlesschain.eval-comparison-context/v1";
8
+ export const EVAL_COMPARISON_SCHEMA = "chainlesschain.eval-comparison/v1";
9
+ export const EVAL_EXECUTION_PROTOCOL = "cc-agent-stream-json/v1";
10
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
11
+ const MAX_HISTORY_BYTES = 16 * 1024 * 1024;
12
+ const CONTEXT_KEYS = [
13
+ "schema", "provider", "model", "modelRevision", "environmentDigest",
14
+ "permissionDigest", "inferenceDigest",
15
+ ];
16
+
17
+ function canonical(value) {
18
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
19
+ if (value && typeof value === "object") {
20
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
21
+ }
22
+ return JSON.stringify(value);
23
+ }
24
+
25
+ export function evalDigest(value) {
26
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
27
+ }
28
+
29
+ function nonempty(value) {
30
+ return typeof value === "string" && value.trim().length > 0 && value.length <= 1024;
31
+ }
32
+
33
+ /** These are operator declarations, not observed provider state or PKI proofs. */
34
+ export function validateComparisonContext(value, { provider, model } = {}) {
35
+ if (!value || typeof value !== "object" || Array.isArray(value) ||
36
+ Object.keys(value).length !== CONTEXT_KEYS.length ||
37
+ Object.keys(value).some((key) => !CONTEXT_KEYS.includes(key)) ||
38
+ value.schema !== EVAL_CONTEXT_SCHEMA ||
39
+ ![value.provider, value.model, value.modelRevision].every(nonempty) ||
40
+ ![value.environmentDigest, value.permissionDigest, value.inferenceDigest].every((v) => DIGEST.test(v || ""))) {
41
+ throw new Error("comparison context requires its v1 schema, provider, model, modelRevision and environment/permission/inference sha256 digests");
42
+ }
43
+ if ((provider !== undefined && value.provider !== provider) ||
44
+ (model !== undefined && value.model !== model)) {
45
+ throw new Error("comparison context must match explicit --provider and --model");
46
+ }
47
+ return { ...value };
48
+ }
49
+
50
+ export function readComparisonContext(file, selection) {
51
+ const bytes = fs.readFileSync(file);
52
+ if (bytes.length === 0 || bytes.length > 16 * 1024) {
53
+ throw new Error("comparison context exceeds its size limit");
54
+ }
55
+ return validateComparisonContext(JSON.parse(bytes.toString("utf8")), selection);
56
+ }
57
+
58
+ export function createEvalComparison({ suite, corpusDigest, provider, model, context,
59
+ platform = process.platform, arch = process.arch, node = process.version } = {}) {
60
+ if (!context) return null;
61
+ if (!nonempty(provider) || !nonempty(model)) {
62
+ throw new Error("--comparison-context requires explicit --provider and --model");
63
+ }
64
+ if (!nonempty(suite) || !DIGEST.test(corpusDigest || "")) throw new Error("eval corpus identity is required");
65
+ const declared = validateComparisonContext(context, { provider, model });
66
+ return {
67
+ schema: EVAL_COMPARISON_SCHEMA,
68
+ basis: "local-runtime-and-operator-declarations",
69
+ suite, corpusDigest, provider, model,
70
+ runtime: { platform, arch, node },
71
+ permissionMode: "acceptEdits",
72
+ executionProtocol: EVAL_EXECUTION_PROTOCOL,
73
+ declared,
74
+ };
75
+ }
76
+
77
+ export function createEvalHistoryRecord(summary, {
78
+ comparison = null, dryRun = false, label = null,
79
+ ranAt = new Date().toISOString(), runId = randomUUID(),
80
+ } = {}) {
81
+ return {
82
+ schema: EVAL_HISTORY_SCHEMA, runId, ranAt, label, dryRun, comparison,
83
+ passed: summary.passed, failed: summary.failed, total: summary.total,
84
+ passRate: summary.passRate, unrelatedChangeRate: summary.unrelatedChangeRate,
85
+ results: summary.results.map((result) => ({
86
+ id: result.id, pass: result.pass,
87
+ artifactCheckPassed: result.artifactCheckPassed,
88
+ executionSucceeded: result.executionSucceeded,
89
+ agentOk: result.agentOk,
90
+ executionEvidence: result.executionEvidence,
91
+ error: result.error == null ? null : redactSecrets(String(result.error)).slice(0, 2048),
92
+ detail: redactSecrets(String(result.detail || "")).slice(0, 2048),
93
+ ms: result.ms,
94
+ changedFiles: result.changedFiles,
95
+ unrelatedChanges: result.unrelatedChanges,
96
+ })),
97
+ };
98
+ }
99
+
100
+ /** Diagnostic callers may inspect valid rows, but must keep parse issues. */
101
+ export function parseEvalHistory(text) {
102
+ const runs = [], issues = [];
103
+ if (Buffer.byteLength(text, "utf8") > MAX_HISTORY_BYTES) {
104
+ return { runs, issues: [{ code: "history_too_large" }] };
105
+ }
106
+ text.split(/\r?\n/u).forEach((line, index) => {
107
+ if (!line.trim()) return;
108
+ try {
109
+ const value = JSON.parse(line);
110
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("not a record");
111
+ runs.push(value);
112
+ } catch {
113
+ issues.push({ code: "invalid_history_record", line: index + 1 });
114
+ }
115
+ });
116
+ return { runs, issues };
117
+ }
118
+
119
+ export function readEvalHistory(file) {
120
+ if (!fs.existsSync(file)) return { runs: [], issues: [] };
121
+ if (fs.statSync(file).size > MAX_HISTORY_BYTES) {
122
+ return { runs: [], issues: [{ code: "history_too_large" }] };
123
+ }
124
+ return parseEvalHistory(fs.readFileSync(file, "utf8"));
125
+ }
126
+
127
+ function validateComparison(value) {
128
+ try {
129
+ const rebuilt = createEvalComparison({
130
+ suite: value?.suite, corpusDigest: value?.corpusDigest,
131
+ provider: value?.provider, model: value?.model, context: value?.declared,
132
+ platform: value?.runtime?.platform, arch: value?.runtime?.arch, node: value?.runtime?.node,
133
+ });
134
+ return rebuilt && Object.values(value.runtime).every(nonempty) && canonical(rebuilt) === canonical(value);
135
+ } catch { return false; }
136
+ }
137
+
138
+ function recordIssues(record, now, maxAgeMs) {
139
+ if (!record || typeof record !== "object" || Array.isArray(record)) return ["invalid_history_record"];
140
+ const issues = [];
141
+ if (record.schema !== EVAL_HISTORY_SCHEMA || !nonempty(record.runId)) issues.push("legacy_or_missing_run_identity");
142
+ if (record.dryRun !== false) issues.push("dry_run_or_missing_execution_mode");
143
+ const epoch = Date.parse(record.ranAt);
144
+ if (typeof record.ranAt !== "string" || !Number.isFinite(epoch) ||
145
+ new Date(epoch).toISOString() !== record.ranAt || epoch > now || now - epoch > maxAgeMs) issues.push("invalid_or_expired_evidence");
146
+ if (!validateComparison(record.comparison)) issues.push("missing_comparison_identity");
147
+ const results = record.results;
148
+ if (!Array.isArray(results) || results.length === 0) return [...issues, "missing_task_results"];
149
+ const ids = new Set();
150
+ let passed = 0;
151
+ for (const result of results) {
152
+ if (!result || !nonempty(result.id) || ids.has(result.id)) {
153
+ issues.push("invalid_or_duplicate_task");
154
+ continue;
155
+ }
156
+ ids.add(result.id);
157
+ const execution = result.executionEvidence;
158
+ if (typeof result.pass !== "boolean" || typeof result.artifactCheckPassed !== "boolean" ||
159
+ typeof result.executionSucceeded !== "boolean" || result.agentOk !== result.executionSucceeded ||
160
+ result.pass !== (result.artifactCheckPassed && result.executionSucceeded)) issues.push("inconsistent_task_verdict");
161
+ if (!result.executionSucceeded || result.error !== null ||
162
+ execution?.protocol !== EVAL_EXECUTION_PROTOCOL || execution.exitCode !== 0 ||
163
+ execution.signal !== null || execution.terminalVerified !== true) issues.push("execution_not_successful");
164
+ if (result.pass === true) passed += 1;
165
+ }
166
+ if (record.total !== results.length || record.passed !== passed ||
167
+ record.failed !== results.length - passed || record.passRate !== passed / results.length) issues.push("inconsistent_task_totals");
168
+ return issues;
169
+ }
170
+
171
+ /**
172
+ * A local comparison gate. It does not attest model checkpoints, configuration
173
+ * declarations, edited JSONL, hidden holdouts, or production release authority.
174
+ * Append order selects the pair; timestamps cannot silently select older rows.
175
+ */
176
+ export function evaluateStrictEvalGate({ runs = [], issues = [] } = {}, {
177
+ now = Date.now(), maxAgeMs = 7 * 24 * 60 * 60 * 1000, regressionThreshold = 0,
178
+ } = {}) {
179
+ const reasons = issues.map((issue) => issue.code);
180
+ if (!Number.isFinite(now) || !Number.isFinite(maxAgeMs) || maxAgeMs <= 0 ||
181
+ !Number.isFinite(regressionThreshold) || regressionThreshold < 0 || regressionThreshold > 1) reasons.push("invalid_gate_options");
182
+ if (!Array.isArray(runs) || runs.length < 2) reasons.push("missing_baseline");
183
+ const pair = Array.isArray(runs) ? runs.slice(-2) : [];
184
+ for (const record of pair) reasons.push(...recordIssues(record, now, maxAgeMs));
185
+ if (pair.length === 2) {
186
+ const [baseline, candidate] = pair;
187
+ if (baseline?.runId === candidate?.runId || Date.parse(baseline?.ranAt) >= Date.parse(candidate?.ranAt)) reasons.push("duplicate_or_unordered_runs");
188
+ if (canonical(baseline?.comparison) !== canonical(candidate?.comparison)) reasons.push("comparison_identity_changed");
189
+ const ids = (record) => (Array.isArray(record?.results) ? record.results : []).map((r) => r?.id).sort();
190
+ if (canonical(ids(baseline)) !== canonical(ids(candidate))) reasons.push("task_population_changed");
191
+ }
192
+ const uniqueReasons = [...new Set(reasons)];
193
+ const base = {
194
+ scope: "local-eval-comparison", productionAttested: false,
195
+ baselineRunId: pair.length === 2 ? pair[0]?.runId || null : null,
196
+ candidateRunId: pair.at(-1)?.runId || null,
197
+ };
198
+ if (uniqueReasons.length) return { ...base, status: "INSUFFICIENT_EVIDENCE", passed: false, reasons: uniqueReasons };
199
+ const trend = computeTrend(pair, { regressionThreshold });
200
+ const failed = trend.regressed || pair[1].passed !== pair[1].total;
201
+ return { ...base, status: failed ? "FAIL" : "PASS", passed: !failed,
202
+ reasons: failed ? [trend.regressed ? "task_regression" : "candidate_tasks_failed"] : [], trend };
203
+ }
@@ -104,6 +104,10 @@ export async function runEvalSuite(tasks, opts = {}) {
104
104
  id: task?.id || "(unnamed)",
105
105
  description: task?.description || "",
106
106
  pass: false,
107
+ artifactCheckPassed: false,
108
+ executionSucceeded: false,
109
+ agentOk: false,
110
+ executionEvidence: null,
107
111
  ms: 0,
108
112
  detail: "",
109
113
  error: null,
@@ -142,7 +146,12 @@ export async function runEvalSuite(tasks, opts = {}) {
142
146
  // and still run check() (the workspace may be partially done).
143
147
  rec.error = `agent error: ${agentErr.message}`;
144
148
  }
145
- rec.agentOk = agentResult ? agentResult.ok !== false : false;
149
+ rec.executionSucceeded = agentResult?.ok === true && !rec.error;
150
+ rec.agentOk = rec.executionSucceeded;
151
+ rec.executionEvidence = agentResult?.executionEvidence || null;
152
+ if (!rec.executionSucceeded && !rec.error) {
153
+ rec.error = `agent error: ${agentResult?.error || "execution did not report success"}`;
154
+ }
146
155
  // Edit locality — snapshot NOW, before check() (some checks write to the
147
156
  // workspace themselves). `unrelatedChanges` = files touched outside the
148
157
  // task's declared legitimate surface; null when the task doesn't declare
@@ -158,7 +167,10 @@ export async function runEvalSuite(tasks, opts = {}) {
158
167
  rec.unrelatedChanges = null; // not measured
159
168
  }
160
169
  const verdict = await task.check(dir, agentResult);
161
- rec.pass = verdict?.pass === true;
170
+ rec.artifactCheckPassed = verdict?.pass === true;
171
+ // A useful artifact survives a provider/transport failure, but it is not
172
+ // evidence that the Agent completed the task successfully.
173
+ rec.pass = rec.artifactCheckPassed && rec.executionSucceeded;
162
174
  rec.detail = verdict?.detail || "";
163
175
  } catch (err) {
164
176
  // A harness-level error (bad task def / setup threw) — mark failed.
@@ -216,6 +228,8 @@ export async function runEvalSuite(tasks, opts = {}) {
216
228
  failed: total - passed,
217
229
  total,
218
230
  passRate: total > 0 ? passed / total : 0,
231
+ artifactChecksPassed: results.filter((r) => r.artifactCheckPassed).length,
232
+ executionsSucceeded: results.filter((r) => r.executionSucceeded).length,
219
233
  tasksWithUnrelatedChanges,
220
234
  unrelatedChangeRate: measured.length
221
235
  ? tasksWithUnrelatedChanges / measured.length
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Eval trend analysis (Phase 7) — turn a history of `cc eval` runs into a
3
- * release-gate signal: is the task-success rate trending down, and did any task
3
+ * diagnostic signal: is the task-success rate trending down, and did any task
4
4
  * that used to pass start failing (a regression)?
5
5
  *
6
6
  * The eval RUNS themselves need a real model (a dry-run is always 0%), but the
7
7
  * REPORTING is pure: given the recorded per-run summaries (pass-rate + which
8
8
  * tasks passed), computing the delta, per-task regressions, and the gate verdict
9
9
  * is deterministic and unit-testable with fixtures — which is what a release
10
- * pipeline actually wires up. This module is that consumer.
10
+ * pipeline can consume. Formal completeness/comparability is checked separately
11
+ * by evaluateStrictEvalGate in evidence.js; this module preserves legacy history.
11
12
  *
12
13
  * A run record is the JSON `cc eval --json` emits, optionally tagged with
13
14
  * `ranAt` / `label` (version or commit). `perTask` is derived from `results`.
@@ -200,7 +201,7 @@ export function formatTrend(trend) {
200
201
  lines.push(
201
202
  trend.regressed
202
203
  ? " RESULT: REGRESSED (gate fails)"
203
- : " RESULT: ok (no regression)",
204
+ : " RESULT: ok (no regression; diagnostic only, use --strict for a comparison gate)",
204
205
  );
205
206
  return lines.join("\n");
206
207
  }