chainlesschain 0.166.44 → 0.166.46

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 (195) hide show
  1. package/package.json +3 -3
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-HiHMDVK3.js → AIOps-Cp8KiMmv.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-H2RKuS8V.js → ActionButton-B4d9BGsm.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CBPFBB61.js → Analytics-Cf9yXTkv.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-Da-3Laze.js → AppLayout-40UkmxtP.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-DiRQ9yzD.js → Artifacts-Ckkb4oOA.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-C9LtWEBh.js → Audit-Dnf569k5.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-4rpi7fVg.js → BackgroundAgents-CA6bLem4.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-B8VQmHys.js → Backup-ChQU0I_Q.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-iCVpF2DK.js → BaseInput-BHeZXnNN.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BrQaXBwC.js → Chat-DcFVYB2E.js} +5 -5
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-HIyU-9c0.js → ChatBubbleRenderer-Bi2SY9E_.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CsV__0_R.js → Checkbox-2PDbejv2.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BvFeCIAE.js → Codegen-DJBn1RAu.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CagxV1O8.js → Col-CPRq7n6y.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-DAWr4ahG.js → Community-axvINFMi.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-Ba49Tdc2.js → Compact-_OX4Qbe0.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Brk-MSO4.js → Compliance-DWAkvzRL.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DeKH1v0o.js → Cowork-CUIJ25oj.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-AHB-T-VL.js → Cron-nR0m7H3_.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-Zbcrtimh.js → Crosschain-CgTLXzh_.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-ByFa1MWG.js → DID-BllzRB6Q.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-9jKovyuH.js → Dashboard-C9f0IIBN.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DY8TaXi5.js → Dropdown-BkahetiO.js} +1 -1
  26. package/src/assets/web-panel/assets/EmailListRenderer-rartVC-k.js +1 -0
  27. package/src/assets/web-panel/assets/EvolutionSettings-BqpG3XHV.js +1 -0
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-5ao8smvA.js → FamilyGuardDashboard-DgUdFvw1.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-RTpNXXHT.js → Federation-oFpIQU6g.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DWZvRMWv.js → FormItemContext-BUH5xlyP.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-CWi8SGvx.js → GenericCardRenderer-ilRa-ueg.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-CCLgDIqs.js → Git-DlHHSF8j.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-DUuyKrQl.js → Governance-yQiiXIWe.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-RkFcM2A0.js → Inference-BUDRVGOP.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-C-QTauZr.js → KnowledgeGraph-BaGyIoN1.js} +1 -1
  36. package/src/assets/web-panel/assets/Logs-C1cO6zE0.js +2 -0
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-CgiHBe0A.js → MarkdownRenderer-_z_Iaoes.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-CK1kSFDt.js → Marketplace-CM8cGL0K.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-BcaVwoY6.js → McpTools-CDqAMU0U.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-QEI4xRwF.js → Memory-CfB0-Ofv.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BqD-R0Ek.js → MobileBridge-BL18ytvB.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-CwFoXf1M.js → MobileProjects-PIyyxbuJ.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-CTZ6qICG.js → Mtc-BTTrFCol.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-Cn7y2BzO.js → MtcAudit-JHZn037c.js} +4 -4
  45. package/src/assets/web-panel/assets/{Multisig-BCjsdGiN.js → Multisig-CMf9NEdB.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-CupTVNtA.js → NLProgramming-Dhk3qaus.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-C8kO3mGt.js → Notes-6F8EarNN.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-Cn-MJ8sQ.js → NotificationSettings-gxdaVEit.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-BX4PrVfN.js → OrderTableRenderer-gVgXQWdc.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-G_RjOL7M.js → Organization-CzvHcDNO.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-BvX644ya.js → Overflow-Dclx30Vv.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-yWmLgYgQ.js → P2P-CM0Wm9vZ.js} +2 -2
  53. package/src/assets/web-panel/assets/PdhVaultBrowser-fYmQzmTA.js +7 -0
  54. package/src/assets/web-panel/assets/{Permissions-DtU8fhcZ.js → Permissions-B_oATenG.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-B17Vyk-x.js → PersonalDataHub-CptiVBh5.js} +5 -5
  56. package/src/assets/web-panel/assets/{Pipeline-BZUsTyhP.js → Pipeline-DkHzgyyI.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-BswdhlPL.js → Privacy-DzZvwcKh.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-sJxSlY0C.js → ProjectInit-CzkCs-5G.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BnbpdOke.js → ProjectSettings-BB8dcxRD.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-DUjauf-6.js → Projects-B7PW9CFY.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-DhF5I-UR.js → Providers-Tfx-ti1V.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-B3PeCAGo.js → QrScannerModal-EvWlNhfX.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-Cpaxrabg.js → QuickAsk-CUspgbNC.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DqXFF66-.js → Recommend-COY2g77J.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DtDQ76Ox.js → RemoteSession-BzMBU6E6.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-BgLLKwu2.js → Reputation-RtYRF4YP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-BuMLhd8M.js → Row-Bva3l9Lc.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BizxUzBA.js → RssFeed-BhjzLvW3.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-CYNrJAQY.js → Search-BYzT2PTR.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-CIC02soH.js → Security-Bcl4-KTv.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-DD3WxXxw.js → Services-BRRa4A0f.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-C-d9Mbos.js → Skeleton-DYQ016qy.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-CAUKL9JA.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-DgrqUhns.js → Sla-BTyUqfuD.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-CDXFwOHX.js → SpeechSettings-CUjMihrs.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-BBio_v8M.js → SyncSettings-CvfOSupr.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-EvjNXrWH.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-RAPdM85o.js → Templates-wPAp0EoM.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-2jI745PC.js → Tenant-CP0D-q9Q.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-ZCtYBMAe.js → Terminal-CQ0f2iFg.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-DfI36Yg6.js → TimelineRenderer-D3JrGSPb.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-NQ1VaO0W.js → Tokens-yXf2PPp9.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-DwFh5AMe.js → Trigger-DfmMOoZ2.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-Dr0v45uH.js → Trust-BTCyMgf1.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-i7Q1Bdqr.js → UkeySign-DKCwIBHi.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CXSkk4gl.js → VideoEditing-CQpDSzLO.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-8_cAjou4.js → Wallet-CyFrzC3K.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-jn_3Gma7.js → WebAuthn-D6Nr_1kd.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-BmvodVLl.js → WorkflowEditor-CgR9tNrB.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-DGi3bmwL.js → chat-CRG_IeyP.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-D8OSTOU0.js → colors-MdE3TStI.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-DfNygJU_.js → compact-item-B0LRuiNV.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-B0lT0dbj.js → createContext-B_V5fjcX.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BtFLt3qI.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-Xtzg_EDR.js → hasIn-DSL-c_hr.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BBegBTDb.js → index-0200QxC4.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BBR-_Pkv.js → index-B3iW-mhn.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BFRkiqtr.js → index-BD0w-888.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B4-hl9Rw.js → index-BHp1nSur.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BJ6nxb18.js → index-BIDCNwy6.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cx_EAQEh.js → index-BMm8tO2X.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BhS9-r8Z.js → index-BNhWoD6d.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CShl59Um.js → index-BQAsL19t.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DEH5ms1R.js → index-BbZZ7P_c.js} +1 -1
  105. package/src/assets/web-panel/assets/index-BhuWQHeG.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DVfE2S0f.js → index-BpwGsAA-.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B3ittRaJ.js → index-Bq8-2Ibv.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DWpRgQvO.js → index-BsAEd-Do.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B0kDqFPU.js → index-C-aKWR2v.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CyVOaMS7.js → index-C5dqieYX.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-De9jsBiC.js → index-C67dPW0L.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cp-yGWti.js → index-CEOAZm5j.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B-B7DDjX.js → index-CNytQuFq.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-ConAvib0.js → index-CPYshcic.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-EV-o-rTH.js → index-CS3bOhV7.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-4s8GZJ73.js → index-CVDi4rS9.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Byto86PA.js → index-CXMeisJK.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DXNWsfbw.js → index-CZICtYJ0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BpsIkWwQ.js → index-CdHMc9LQ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CIbYQLD-.js → index-CdplnAS9.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DVnowoxR.js → index-CkN53KiJ.js} +1 -1
  122. package/src/assets/web-panel/assets/index-D-4tFek3.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CLRMkKeD.js → index-D3pJBTvo.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BVOyO-ZV.js → index-DBwBZwio.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DFkgqRqx.js → index-DE7bSYmW.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-hDo9-qDw.js → index-DRRY44XP.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BbTx5wo1.js → index-DVgTf7CZ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BhYM_Rzp.js → index-Didyuq2Z.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DZKM2nm2.js → index-DtOTppk8.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-kdyFlPZw.js → index-KaSejHjf.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-svr-L6NS.js → index-OBhlHSMN.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-B2bgL5vL.js → index-SmQfEHjp.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Bujix_OR.js → index-Tg3yl8wc.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BhhS0xnV.js → index-c8OqO3rq.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-CPsOp_T6.js → initDefaultProps-P8SBN6Di.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-8Jakob3_.js → motion-BubbjUT_.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DAogsEBs.js → move-Chcykv0I.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CkA9zZB_.js → mtc-parser-DyiYuGDu.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-DMHNkosQ.js → omit-Rdwb_Neb.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-ByIvo_lk.js → pickAttrs-CCr0MYHl.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-CafMUHzz.js → placementArrow-B1ZXz3bb.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-CTehELUQ.js → responsiveObserve-BGEdwej6.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BQISQ1KV.js → slide-CpnsUMaT.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-Drm4jUoK.js → statusUtils-C7x-TVp_.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-DGniLRqn.js → styleChecker-CC5k_791.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-DYz_z9wn.js → useFlexGapSupport-BpZFKsJW.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-DRAPQMjC.js → useFs-CVq5HySy.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-CpgGLb8q.js → usePersonalDataHub-BGkdLryu.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-p6925VWJ.js → vnode-uLtOUEK-.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-ClpO8REK.js → zoom-BfVC9iUs.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/agent.js +14 -1
  154. package/src/commands/ask.js +13 -8
  155. package/src/commands/chat.js +8 -5
  156. package/src/commands/cowork.js +18 -9
  157. package/src/commands/eval.js +100 -45
  158. package/src/commands/evolution-deployment.js +58 -0
  159. package/src/commands/hub.js +13 -7
  160. package/src/commands/setup.js +31 -1
  161. package/src/data/changelog.json +18 -5
  162. package/src/gateways/ws/personal-data-hub-protocol.js +12 -5
  163. package/src/lazy-dispatch.js +42 -6
  164. package/src/lib/agent-router.js +25 -23
  165. package/src/lib/chat-intent-service.js +3 -13
  166. package/src/lib/context-memory-kernel/provider-context.js +19 -3
  167. package/src/lib/cowork-adapter.js +9 -4
  168. package/src/lib/doctor-checkup.js +42 -0
  169. package/src/lib/eval/evidence.js +203 -0
  170. package/src/lib/eval/runner.js +16 -2
  171. package/src/lib/eval/trend.js +4 -3
  172. package/src/lib/evolution/agent-evolution-ingress.js +60 -8
  173. package/src/lib/evolution/evolution-deployment-config.js +217 -10
  174. package/src/lib/evolution/evolution-deployment-descriptor-revocations.js +131 -0
  175. package/src/lib/evolution/evolution-deployment-loader.js +126 -11
  176. package/src/lib/evolution/evolution-deployment-profile.js +281 -37
  177. package/src/lib/evolution/evolution-deployment-root-rotation.js +154 -0
  178. package/src/lib/evolution/governed-hub-llm.js +18 -12
  179. package/src/lib/evolution/governed-hub-skill.js +4 -17
  180. package/src/lib/evolution/governed-model-turn.js +7 -8
  181. package/src/lib/orchestrator.js +14 -8
  182. package/src/lib/personal-data-hub-wiring.js +7 -1
  183. package/src/lib/sub-agent-context.js +5 -1
  184. package/src/repl/chat-repl.js +8 -4
  185. package/src/runtime/agent-core.js +14 -0
  186. package/src/runtime/headless-runner.js +16 -0
  187. package/src/assets/web-panel/assets/EmailListRenderer-rcaSveQ8.js +0 -1
  188. package/src/assets/web-panel/assets/EvolutionSettings-B3dcSq1H.js +0 -1
  189. package/src/assets/web-panel/assets/Logs-B-7Dz5Ui.js +0 -2
  190. package/src/assets/web-panel/assets/PdhVaultBrowser-yYL7-rno.js +0 -7
  191. package/src/assets/web-panel/assets/Skills-BBkg1Eqr.js +0 -1
  192. package/src/assets/web-panel/assets/Tasks-QQWSk-tc.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-BUooUkHH.js +0 -1
  194. package/src/assets/web-panel/assets/index-DVu4QCII.js +0 -1
  195. package/src/assets/web-panel/assets/index-XYnz-yJ_.js +0 -1
@@ -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
  }
@@ -23,6 +23,14 @@ export const AGENT_EVOLUTION_INGRESS_SCHEMA =
23
23
  export const AGENT_EVOLUTION_INGRESS_FAILED_CODE =
24
24
  "CC_AGENT_EVOLUTION_INGRESS_FAILED";
25
25
 
26
+ const RESPONSE_CACHE_RECORD_SCHEMA =
27
+ "chainlesschain.model-response-cache/v2";
28
+ const LEGACY_RESPONSE_CACHE_RECORD_SCHEMA =
29
+ "chainlesschain.model-response-cache/v1";
30
+ const RESPONSE_CACHE_IDENTIFIER_BINDING_SCHEMA =
31
+ "chainlesschain.model-response-cache-identifier-binding/v1";
32
+ const HEX_ALPHA = "abcdefghijklmnop";
33
+
26
34
  const INGRESSES = new WeakSet();
27
35
  const CORE_EVENT_KINDS = new Map([
28
36
  ["tool-executing", "tool-requested"],
@@ -101,6 +109,22 @@ function digest(value) {
101
109
  return `sha256:${crypto.createHash("sha256").update(canonical(value)).digest("hex")}`;
102
110
  }
103
111
 
112
+ function cacheIdentifierBinding(kind, value) {
113
+ const input = requiredString(value, `Cache ${kind}`);
114
+ const bytes = crypto
115
+ .createHash("sha256")
116
+ .update(
117
+ `${RESPONSE_CACHE_IDENTIFIER_BINDING_SCHEMA}\0${kind}\0${input}`,
118
+ "utf8",
119
+ )
120
+ .digest();
121
+ let encoded = "";
122
+ for (const byte of bytes) {
123
+ encoded += HEX_ALPHA[byte >>> 4] + HEX_ALPHA[byte & 0x0f];
124
+ }
125
+ return encoded;
126
+ }
127
+
104
128
  function timestamp(value, label) {
105
129
  const date = new Date(value);
106
130
  if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) {
@@ -263,10 +287,21 @@ export function createAgentEvolutionIngress({
263
287
  throw new Error("Cache receipt requires a recorded model response");
264
288
  const payload = cacheRecord
265
289
  ? {
266
- schema: "chainlesschain.model-response-cache/v1",
267
- requestKey: evidence.requestKey,
268
- sourceRunId: descriptor.runId,
269
- sourceEventId: lastModelResponse.eventId,
290
+ schema: RESPONSE_CACHE_RECORD_SCHEMA,
291
+ identifierBindingSchema:
292
+ RESPONSE_CACHE_IDENTIFIER_BINDING_SCHEMA,
293
+ requestKeyBinding: cacheIdentifierBinding(
294
+ "request key",
295
+ evidence.requestKey,
296
+ ),
297
+ sourceRunIdBinding: cacheIdentifierBinding(
298
+ "source Run ID",
299
+ descriptor.runId,
300
+ ),
301
+ sourceEventIdBinding: cacheIdentifierBinding(
302
+ "source event ID",
303
+ lastModelResponse.eventId,
304
+ ),
270
305
  responseEvent: clone(lastModelResponse.event),
271
306
  }
272
307
  : (modelRequest ?? clone(evidence));
@@ -433,6 +468,18 @@ export function createAgentEvolutionIngress({
433
468
  );
434
469
  const projection = resolved.bundle.modelProjection;
435
470
  const record = projection.content;
471
+ const legacyRecord =
472
+ record?.schema === LEGACY_RESPONSE_CACHE_RECORD_SCHEMA;
473
+ const currentRecord =
474
+ record?.schema === RESPONSE_CACHE_RECORD_SCHEMA &&
475
+ record.identifierBindingSchema ===
476
+ RESPONSE_CACHE_IDENTIFIER_BINDING_SCHEMA &&
477
+ record.requestKeyBinding ===
478
+ cacheIdentifierBinding("request key", requestKey) &&
479
+ record.sourceRunIdBinding ===
480
+ cacheIdentifierBinding("source Run ID", receipt.runId) &&
481
+ typeof record.sourceEventIdBinding === "string" &&
482
+ /^[a-p]{64}$/u.test(record.sourceEventIdBinding);
436
483
  if (
437
484
  resolved.verification.verified !== true ||
438
485
  resolved.verification.tenantId !== descriptor.tenantId ||
@@ -440,16 +487,21 @@ export function createAgentEvolutionIngress({
440
487
  projection.visibility !== "model-visible" ||
441
488
  projection.truncated ||
442
489
  projection.injectionFindings.length !== 0 ||
443
- record?.schema !== "chainlesschain.model-response-cache/v1" ||
444
- record.requestKey !== requestKey ||
445
- record.sourceRunId !== receipt.runId ||
490
+ (!currentRecord &&
491
+ (!legacyRecord ||
492
+ record.requestKey !== requestKey ||
493
+ record.sourceRunId !== receipt.runId)) ||
446
494
  record.responseEvent?.type !== "response-complete"
447
495
  )
448
496
  throw new Error(
449
497
  "Cache response projection is not safe or request-bound",
450
498
  );
451
499
  const responseEvent = loaded.events.find(
452
- (item) => item.eventId === record.sourceEventId,
500
+ (item) =>
501
+ legacyRecord
502
+ ? item.eventId === record.sourceEventId
503
+ : cacheIdentifierBinding("source event ID", item.eventId) ===
504
+ record.sourceEventIdBinding,
453
505
  );
454
506
  if (
455
507
  responseEvent?.data?.evidenceKind !== "response-completed" ||