chainlesschain 0.166.106 → 0.166.108

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 (225) hide show
  1. package/README.md +187 -8
  2. package/package.json +2 -2
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-CKwEku2e.js → AIOps-B5HWUFaS.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-BSUOuTpW.js → ActionButton-DCIKpxoQ.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-CGxvL_FI.js → Analytics-dsgvjCEQ.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-bCPwz-5w.js → AppLayout-DWDH5Qbm.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-2_BOWo6e.js → Artifacts-BD8S8kzU.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-CBOruzgW.js → Audit-CDXv4Aoj.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-lg0IU5HN.js → BackgroundAgents-BLAQ5Uy0.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-OspCi452.js → Backup-moPac6tF.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-BMkliqlz.js → BaseInput-C_6nzsLU.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-BfoiUZwp.js → Chat-cYuslkFG.js} +6 -6
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BjnfMwep.js → ChatBubbleRenderer-Ceh3MG6M.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-jTMlkNQZ.js → Checkbox-Qjb-XOX_.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-CycyJcnU.js → Codegen-IYRWXlMA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-BVWlReQj.js → Col-CsQ9jv8M.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CKRHl4xK.js → Community-BbNmfTwe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-BtpfMdhT.js → Compact-SShDx-nr.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-DtHB1Ct1.js → Compliance-CLWhKIJp.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-D_8X7-RK.js → Cowork-D4tK9_s3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Cron-DMCOyvEa.js → Cron-D7GnfQ1z.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-CeHdsu7m.js → Crosschain-TG86SH3X.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-CL6mR6zX.js → DID-6pue0NCO.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-oz9n6DC7.js → Dashboard-BwkJ29du.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-CbWm8mWu.js → Dropdown-DEvpYDof.js} +1 -1
  28. package/src/assets/web-panel/assets/EmailListRenderer-CQj7UlKJ.js +1 -0
  29. package/src/assets/web-panel/assets/{EvolutionSettings-DPwqgRYm.js → EvolutionSettings-tpvPMM9W.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DkV4YMFj.js → FamilyGuardDashboard-CWYFW75O.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-CpUKHIOH.js → Federation-gRIi96rM.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-C6KXRgjR.js → FormItemContext-B7Nr3YkV.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-C5Y4L5E3.js → GenericCardRenderer-b1BgGgLT.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CBwKdF8s.js → Git-Dis1jjO7.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-Dt-OR7Aa.js → Governance-C4Yg99Am.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Czkl-Lhl.js → Inference-D4UQRbGu.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-C7NE4949.js → KnowledgeGraph-Co_kLMAq.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-CoAuZiMG.js → Logs-D4BRp7bg.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-DTkjTwGI.js → MarkdownRenderer-Dgy8arQI.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-IzrV-7zH.js → Marketplace-ys0j1vr9.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-DYEea7aS.js → McpTools-B3ZvRZe_.js} +2 -2
  42. package/src/assets/web-panel/assets/{Memory-Uqe7ZrV6.js → Memory-BOa0jbr-.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-D092EGyM.js → MobileBridge-CWJRmUbj.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-7ty8vTpj.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-gg4jzfB-.js → Mtc-CCcOk5m5.js} +5 -5
  46. package/src/assets/web-panel/assets/{MtcAudit-BibGfs71.js → MtcAudit-vlrrScbQ.js} +4 -4
  47. package/src/assets/web-panel/assets/{Multisig-BmB5yRXP.js → Multisig-C8K59S_8.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-BBSlPDyY.js → NLProgramming-oTpFYVme.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-Bui3-KiC.js → Notes-BhqAJWiz.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-ovXtbHe5.js → NotificationSettings-DCnV1CJM.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-Bq-qD7eG.js → OrderTableRenderer-CZGnsrVW.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-DacIY9oJ.js → Organization-DX1LuQlJ.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-CX8osheZ.js → Overflow-CbfKrFdT.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-1lnikqVp.js → P2P-BEwYZuHL.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DCrU-V6A.js → PdhVaultBrowser-YNmWbwd7.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-Dfcb6kM2.js → Permissions-DyZ18LiA.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-dvgai4hf.js → PersonalDataHub-B2J3aw2g.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-nymAEtnK.js → Pipeline-CXl2ZlhQ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-D0Vd91oM.js → Privacy-I5SDmgAp.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BpUBcu7o.js → ProjectInit-Crl9tXw3.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-DIzwTT-M.js → ProjectSettings-DCFtFlSK.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-Den4RUw6.js → Projects-Do5BQq1i.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-Cq8p-M0d.js → Providers-Cb7OWa3y.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-DSmqOfYk.js → QrScannerModal-CJmhAU-s.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BDHCeSnA.js → QuickAsk-Cu6lEWou.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-B_9M8oF4.js → Recommend-Crip3jLB.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-5ot4leHy.js → RemoteSession-KxYY-2CB.js} +3 -3
  68. package/src/assets/web-panel/assets/{Reputation-B3F013PY.js → Reputation-D0vN-htI.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-BMyBJnZw.js → Row-wqzd_xCD.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-WmV0l52g.js → RssFeed-8lsPfb0a.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-Wl80yXnR.js → Search-C-e8qAeU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-gbDMKb5m.js → Security-CMM9WgFy.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-Bx2Bjhah.js → Services-CZdxjN2B.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-BvJNybjV.js → Skeleton-BI3XNk35.js} +1 -1
  75. package/src/assets/web-panel/assets/Skills-DMlsh9It.js +1 -0
  76. package/src/assets/web-panel/assets/{Sla-BM9gyZwc.js → Sla-HQruznKY.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Bu9FL9Tz.js → SpeechSettings-Dl-MFq3w.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-D8ICJsMt.js → SyncSettings-BU224vDm.js} +2 -2
  79. package/src/assets/web-panel/assets/Tasks-DESg3yCs.js +1 -0
  80. package/src/assets/web-panel/assets/{Templates-BJzpTD0W.js → Templates-Jb3TPz4Q.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-BN8eX4R2.js → Tenant-CQ-GTrfa.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-CjtP9bpq.js → Terminal-BbMl3Rue.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-CpNRa4XW.js → TimelineRenderer-CS_vstKF.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-2lNNvNAH.js → Tokens-BaopVl1f.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DxtxXyPg.js → Trigger-DtnD4WUN.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-CakzGeF9.js → Trust-CsvG4Tmf.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-CvJeRXwV.js → UkeySign-C1-A-cOV.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-D80pMzcO.js → VideoEditing-CNgJPdSL.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-BF-VOBmZ.js → Wallet-CdF41501.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DXyoAzE_.js → WebAuthn-D-JmxD5g.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-X6CDxzrF.js → WorkflowEditor-C0XunR13.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-BcUhqjeA.js → chat-BBAZpD_G.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-Dw4bKaCm.js → colors-BxSxO5BX.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-BWhu5Eri.js → compact-item-BoxL8pg5.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BLJ9JKoN.js → createContext-W5JAyZmY.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-BI5TZ7EX.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-DIkCiJbt.js → hasIn-HpiKDPVC.js} +1 -1
  98. package/src/assets/web-panel/assets/index-9-JLDK5n.js +1 -0
  99. package/src/assets/web-panel/assets/{index-BAwcMKaD.js → index-9caMEwQ-.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-0mHbawfQ.js → index-B05_YRHZ.js} +1 -1
  101. package/src/assets/web-panel/assets/index-B7m8ioaQ.js +1 -0
  102. package/src/assets/web-panel/assets/{index-D3wp_jgy.js → index-B9VxE1C-.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BotMuGS2.js → index-BA3WWQAT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-LOsZYbic.js → index-BSJ0cAle.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C5qms36i.js → index-BW4PP5GN.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CDfB6hWf.js → index-BZAbaY64.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BipG-tDs.js → index-Ba4usRFM.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CgsVg5Up.js → index-BgMViZIY.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CTPoBbIR.js → index-BtFAEG08.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DKzG3kBM.js → index-Bwn6k_qw.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-PRYboDFm.js → index-C4uI7WCB.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DQbWGvE4.js → index-C5kqii-G.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B1fufz1A.js → index-C7GIUWWF.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DjEP26Te.js → index-CM_gdohF.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BcTch6Fp.js → index-CRg3aOoj.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-04SKBH8l.js → index-CcyzZloJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DHLW27Je.js → index-CehxfRUR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-5vzPcr1g.js → index-D6m6B4Wy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DV4FSjmI.js → index-DSyfxxQS.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-D5vFJmmX.js → index-DaJk9U9V.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BxLDNxEQ.js → index-DcmAiq_Y.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D-Erv8A9.js → index-DdEcQ1rp.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CqGqoID5.js → index-DinY7IQa.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BGMzh2oj.js → index-DkSnlgK_.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BxfimJOH.js → index-DlPvZgv8.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DOjQi7oU.js → index-DuM9I1If.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-uMtx6e3D.js → index-DwidpDEE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CXJ8t1hK.js → index-IO2I7nzu.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Cmc7qH2C.js → index-JrMBRi62.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BaStonJm.js → index-QzlEbtRL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cr05twNa.js → index-V6VIJe2N.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BGFqk1-E.js → index-_38IJqoP.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-u0tqS0iq.js → index-_p4e3KfB.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D1r2R4a-.js → index-gvfIRZxJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CFGOV-YT.js → index-ooP1pQCt.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-BUC4TZWg.js → index-p6rNtYud.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-Bx6Hj_FG.js → initDefaultProps-Dfn6M05J.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-BSWivB70.js → motion-BRRwFqGO.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-Dv1Pg9R7.js → move-DpDPN6zR.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-Bqtl8fsk.js → mtc-parser-BHlSE87F.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-DpOBjOSQ.js → omit-CEOri_L5.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-D-M-dz48.js → pickAttrs-DqICh-ad.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DsTLAifk.js → placementArrow-DQizFFiV.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-BVTetMpc.js → responsiveObserve-DmFHPdvC.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-abEcz-Cv.js → slide-s2s1XS3T.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-CBhrdU2G.js → statusUtils-IwEIbBan.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-CFq8ZCKa.js → styleChecker-CIFLlbXH.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-o4eE7fDV.js → useFlexGapSupport-CFkN2Cgh.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-Hs9W8ihi.js → useFs-BSY6O48s.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-Bnet5Jho.js → usePersonalDataHub-iJl3VnU0.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-DJfN4kWs.js → vnode-vu8ReMs7.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-BzV-2e7X.js → zoom-CeSrYAHp.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +2 -2
  155. package/src/commands/agent.js +20 -20
  156. package/src/commands/goal.js +299 -1
  157. package/src/commands/plugin.js +129 -68
  158. package/src/constants.js +2 -2
  159. package/src/data/changelog.json +29 -3
  160. package/src/harness/jsonl-session-store.js +128 -5
  161. package/src/harness/prompt-compressor.js +18 -10
  162. package/src/harness/structured-handoff.js +151 -13
  163. package/src/lib/agent-session-export.js +20 -0
  164. package/src/lib/background-agent-supervisor.js +54 -0
  165. package/src/lib/cli-goal-catalog.js +129 -0
  166. package/src/lib/cli-goal-source.js +119 -0
  167. package/src/lib/coding-task-guidance.js +2 -1
  168. package/src/lib/config-schema.js +1 -1
  169. package/src/lib/context-memory-kernel/live-compaction.js +75 -12
  170. package/src/lib/eval/stream-terminal.js +27 -2
  171. package/src/lib/evolution/evolution-artifact-ports.js +22 -11
  172. package/src/lib/evolution/evolution-file-witness.js +86 -63
  173. package/src/lib/evolution/evolution-ledger-file-backend.js +22 -0
  174. package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +34 -35
  175. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +32 -39
  176. package/src/lib/evolution/evolution-ledger.js +367 -169
  177. package/src/lib/evolution/evolution-manifest-state-reader.js +58 -0
  178. package/src/lib/evolution/evolution-physical-state-reader.js +308 -0
  179. package/src/lib/evolution/evolution-witness-segments.js +12 -13
  180. package/src/lib/evolution/governed-wiki-pruning-ledger-adapter.js +82 -56
  181. package/src/lib/evolution/rrsi-observed-text-index.js +17 -1
  182. package/src/lib/evolution/rrsi-tenant-index-anchor-history.js +5 -0
  183. package/src/lib/execution-location-failure-site.js +20 -3
  184. package/src/lib/goal-store.js +8 -1
  185. package/src/lib/image-input.js +43 -14
  186. package/src/lib/llm-providers.js +1 -0
  187. package/src/lib/plugin-runtime/capabilities.js +10 -0
  188. package/src/lib/plugin-runtime/cli-goal-draft-request.js +138 -0
  189. package/src/lib/plugin-runtime/cli-goal-drafts.js +413 -0
  190. package/src/lib/plugin-runtime/cli-goal-extension-host.js +234 -0
  191. package/src/lib/plugin-runtime/examples/goal-description-drafter/plugin.json +17 -0
  192. package/src/lib/plugin-runtime/examples/goal-evidence-viewer/plugin.json +25 -0
  193. package/src/lib/plugin-runtime/examples/goal-progress-viewer/plugin.json +21 -0
  194. package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +38 -0
  195. package/src/lib/plugin-runtime/goal-extension-contract.js +190 -0
  196. package/src/lib/plugin-runtime/goal-extensions.js +277 -0
  197. package/src/lib/plugin-runtime/install.js +40 -40
  198. package/src/lib/plugin-runtime/manifest.js +12 -0
  199. package/src/lib/plugin-runtime/policy.js +1 -0
  200. package/src/lib/project-goal-connection.js +225 -0
  201. package/src/lib/project-goal-draft-connection.js +217 -0
  202. package/src/lib/read-file-loop-guard.js +124 -8
  203. package/src/lib/session-host-snapshot.js +3 -0
  204. package/src/lib/session-message-provenance.js +24 -5
  205. package/src/lib/session-tool-round.js +39 -0
  206. package/src/lib/session-transcript-history.js +2 -0
  207. package/src/lib/session-transcript-page.js +4 -1
  208. package/src/lib/session-transcript-structure.js +4 -0
  209. package/src/lib/task-model-selector.js +1 -1
  210. package/src/repl/agent-repl.js +14 -7
  211. package/src/repl/config-summary.js +1 -1
  212. package/src/runtime/__tests__/message-roles.test.js +29 -0
  213. package/src/runtime/agent-core.js +116 -44
  214. package/src/runtime/headless-runner.js +191 -34
  215. package/src/runtime/headless-stream.js +144 -48
  216. package/src/runtime/message-roles.js +43 -0
  217. package/src/runtime/policies/agent-policy.js +1 -0
  218. package/src/workers/background-agent-worker.js +146 -47
  219. package/src/assets/web-panel/assets/EmailListRenderer-DXlmbC45.js +0 -1
  220. package/src/assets/web-panel/assets/MobileProjects-CwA1VoVJ.js +0 -1
  221. package/src/assets/web-panel/assets/Skills-Tz4rQcDp.js +0 -1
  222. package/src/assets/web-panel/assets/Tasks-B6MowBDo.js +0 -1
  223. package/src/assets/web-panel/assets/devWarning-D_MJgDGJ.js +0 -1
  224. package/src/assets/web-panel/assets/index-BP8log78.js +0 -1
  225. package/src/assets/web-panel/assets/index-PexF9zYf.js +0 -1
@@ -13,6 +13,11 @@ import {
13
13
  import { JsonlSessionContextPort } from "./jsonl-session-context-port.js";
14
14
  import { createCliContextMemoryRuntime } from "./runtime.js";
15
15
  import { projectRuntimeTokenUsage } from "../runtime-usage-ledger.js";
16
+ import {
17
+ projectCanonicalResumeMessages,
18
+ getDurableSystemMessageProvenance,
19
+ isRuntimeOnlySessionMessage,
20
+ } from "../session-message-provenance.js";
16
21
 
17
22
  const rejectedCompactionUsage = new WeakMap();
18
23
 
@@ -138,7 +143,7 @@ class CallbackSessionContextPort extends InMemorySessionContextPort {
138
143
  }
139
144
  }
140
145
 
141
- function kernelBudget(compressor) {
146
+ function kernelBudget(compressor, ephemeralTokens = 0) {
142
147
  const inputBudget = Math.max(
143
148
  256,
144
149
  Math.trunc(Number(compressor?.maxTokens)) || 16_384,
@@ -150,7 +155,9 @@ function kernelBudget(compressor) {
150
155
  return {
151
156
  modelWindowTokens: inputBudget + reservedOutputTokens,
152
157
  reservedOutputTokens,
153
- safetyMarginTokens: 0,
158
+ // Live host policy/retry text still consumes the provider window even
159
+ // though it is deliberately absent from the durable authority partition.
160
+ safetyMarginTokens: ephemeralTokens,
154
161
  recoveryReserveTokens: Math.min(
155
162
  256,
156
163
  Math.max(1, Math.floor(inputBudget * 0.05)),
@@ -202,7 +209,7 @@ export async function compactLiveMessagesCanonical(
202
209
  messagesInput,
203
210
  options = {},
204
211
  ) {
205
- const messages = Array.isArray(messagesInput) ? [...messagesInput] : [];
212
+ const liveMessages = Array.isArray(messagesInput) ? [...messagesInput] : [];
206
213
  const sessionId = String(options.sessionId || `live-${randomUUID()}`);
207
214
  const operationId = String(options.operationId || `compact-${randomUUID()}`);
208
215
  const trigger = options.trigger || "auto";
@@ -224,6 +231,24 @@ export async function compactLiveMessagesCanonical(
224
231
  options.persist !== false &&
225
232
  Boolean(options.sessionId) &&
226
233
  sessionExists(sessionId);
234
+ // Host policy is live-only. Durable summary/lineage systems retain their
235
+ // private provenance and remain part of the exact authority comparison.
236
+ const messages = persistentSession
237
+ ? projectCanonicalResumeMessages(liveMessages, { strict: true })
238
+ : liveMessages;
239
+ const ephemeralMessages = [];
240
+ if (persistentSession) {
241
+ let sequence = 0;
242
+ for (const message of liveMessages) {
243
+ if (
244
+ isRuntimeOnlySessionMessage(message) ||
245
+ (message.role === "system" &&
246
+ !getDurableSystemMessageProvenance(message))
247
+ )
248
+ ephemeralMessages.push({ sequence, message });
249
+ else sequence += 1;
250
+ }
251
+ }
227
252
  if (typeof options.commit === "function") {
228
253
  const bootstrapRuntime = createCliContextMemoryRuntime({
229
254
  sessionId,
@@ -323,12 +348,29 @@ export async function compactLiveMessagesCanonical(
323
348
  const provider = options.provider || "provider.local";
324
349
  const model = options.model || "default";
325
350
  const memoryRevision = await runtime.memoryPort.getRevision();
351
+ let settledOutputItems = null;
352
+ if (ephemeralMessages.length) {
353
+ const appendCompaction = runtime.sessionPort.appendCompaction.bind(
354
+ runtime.sessionPort,
355
+ );
356
+ runtime.sessionPort.appendCompaction = async (event, expectedHead) => {
357
+ const result = await appendCompaction(event, expectedHead);
358
+ if (result?.ok === true) settledOutputItems = event.outputItems;
359
+ return result;
360
+ };
361
+ }
326
362
  let receipt;
327
363
  try {
328
364
  receipt = await runtime.kernel.compactContext({
329
365
  operationId,
330
366
  sessionId,
331
- ...kernelBudget(compressor),
367
+ ...kernelBudget(
368
+ compressor,
369
+ messagesToContextItems(
370
+ ephemeralMessages.map(({ message }) => message),
371
+ { sessionId },
372
+ ).reduce((total, item) => total + item.tokenEstimate, 0),
373
+ ),
332
374
  sink: provider,
333
375
  scopeAdmissions: [{ scope: "session", scopeId: sessionId }],
334
376
  policyVersion: "cli-context-policy-v1",
@@ -421,7 +463,7 @@ export async function compactLiveMessagesCanonical(
421
463
  }
422
464
  if (!["committed", "degraded"].includes(receipt.status)) {
423
465
  return {
424
- messages,
466
+ messages: liveMessages,
425
467
  stats: {
426
468
  ...(summaryStats || {}),
427
469
  strategy: "none",
@@ -433,20 +475,41 @@ export async function compactLiveMessagesCanonical(
433
475
  };
434
476
  }
435
477
  const snapshot = await runtime.sessionPort.readSnapshot(sessionId);
436
- const outputMessages = contextItemsToMessages(snapshot.items);
478
+ // Reattach host policy and private retry nudges at their durable sequence
479
+ // boundaries. They remain live, never enter the receipt, and do not move a
480
+ // retry instruction behind tool exchanges that happened after that retry.
481
+ const restoredMessages = [];
482
+ let ephemeralIndex = 0;
483
+ for (const item of [...(settledOutputItems || snapshot.items)].sort(
484
+ (left, right) =>
485
+ (left.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER) -
486
+ (right.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER) ||
487
+ left.itemId.localeCompare(right.itemId, "en"),
488
+ )) {
489
+ const sequence = item.sourceRef.eventSequence ?? Number.MAX_SAFE_INTEGER;
490
+ while (
491
+ ephemeralIndex < ephemeralMessages.length &&
492
+ ephemeralMessages[ephemeralIndex].sequence <= sequence
493
+ )
494
+ restoredMessages.push(ephemeralMessages[ephemeralIndex++].message);
495
+ restoredMessages.push(...contextItemsToMessages([item]));
496
+ }
497
+ restoredMessages.push(
498
+ ...ephemeralMessages.slice(ephemeralIndex).map(({ message }) => message),
499
+ );
437
500
  return {
438
- messages: outputMessages,
501
+ messages: restoredMessages,
439
502
  stats: {
440
503
  ...(summaryStats || {}),
441
504
  strategy:
442
505
  receipt.status === "degraded" ? "deterministic-fallback" : "canonical",
443
- originalMessages: messages.length,
444
- compressedMessages: outputMessages.length,
445
- originalTokens: estimatedTokens(messages),
446
- compressedTokens: estimatedTokens(outputMessages),
506
+ originalMessages: liveMessages.length,
507
+ compressedMessages: restoredMessages.length,
508
+ originalTokens: estimatedTokens(liveMessages),
509
+ compressedTokens: estimatedTokens(restoredMessages),
447
510
  saved: Math.max(
448
511
  0,
449
- estimatedTokens(messages) - estimatedTokens(outputMessages),
512
+ estimatedTokens(liveMessages) - estimatedTokens(restoredMessages),
450
513
  ),
451
514
  canonicalAlreadySettled: true,
452
515
  canonicalReceipt: receipt,
@@ -1,5 +1,5 @@
1
1
  /** Parse raw agent NDJSON. A declaration of success is never terminal evidence. */
2
- export function verifyAgentTerminal(output) {
2
+ export function verifyAgentTerminal(output, { allowSessionEnd = false } = {}) {
3
3
  try {
4
4
  const events = output
5
5
  .split(/\r?\n/u)
@@ -7,6 +7,31 @@ export function verifyAgentTerminal(output) {
7
7
  .map((line) => JSON.parse(line));
8
8
  const results = events.filter((event) => event?.type === "result");
9
9
  const terminal = results[0];
10
+ const last = events.at(-1);
11
+ const init = events.find(
12
+ (event) => event?.type === "system" && event.subtype === "init",
13
+ );
14
+ const sessionEnded =
15
+ allowSessionEnd &&
16
+ events.at(-2) === terminal &&
17
+ last?.type === "system" &&
18
+ last.subtype === "end" &&
19
+ typeof terminal?.session_id === "string" &&
20
+ terminal.session_id.length > 0 &&
21
+ last.session_id === terminal.session_id &&
22
+ init?.session_id === terminal.session_id &&
23
+ typeof terminal.trace_id === "string" &&
24
+ terminal.trace_id.length > 0 &&
25
+ last.trace_id === terminal.trace_id &&
26
+ Number.isSafeInteger(terminal.seq) &&
27
+ terminal.seq > 0 &&
28
+ last.seq === terminal.seq + 1 &&
29
+ Number.isSafeInteger(terminal.turn) &&
30
+ terminal.turn > 0 &&
31
+ last.turns === terminal.turn &&
32
+ events.filter(
33
+ (event) => event?.type === "system" && event.subtype === "end",
34
+ ).length === 1;
10
35
  const usage = terminal?.usage;
11
36
  const normalizedUsage =
12
37
  usage &&
@@ -26,7 +51,7 @@ export function verifyAgentTerminal(output) {
26
51
  return {
27
52
  terminalVerified:
28
53
  results.length === 1 &&
29
- events.at(-1) === terminal &&
54
+ (last === terminal || sessionEnded) &&
30
55
  terminal.subtype === "success" &&
31
56
  terminal.is_error === false &&
32
57
  !events.some((event) => event?.type === "error"),
@@ -23,6 +23,11 @@ import {
23
23
  } from "./evolution-ledger.js";
24
24
 
25
25
  const ARTIFACT_STORE_DIRECTORY_BOUNDARIES = new WeakMap();
26
+ // Capture repository function identity before binding instance methods. Only
27
+ // these exact defaults may skip legacy list/get I/O; overrides still execute
28
+ // and must match the independently authenticated descriptor snapshots.
29
+ const DEFAULT_STORE_LIST = ArtifactStore.prototype.list;
30
+ const DEFAULT_STORE_GET = ArtifactStore.prototype.get;
26
31
  // Only the shared identity helper's directory opens use this adapter. A POSIX
27
32
  // directory/FIFO swap must fail before reaching a potentially blocking open.
28
33
  const INDEX_DIRECTORY_FS = Object.freeze({
@@ -1014,7 +1019,7 @@ function normalizeKeyId(value, label) {
1014
1019
  return value;
1015
1020
  }
1016
1021
 
1017
- function captureMethod(port, method, label) {
1022
+ function captureDataMethod(port, method, label) {
1018
1023
  if (!port || (typeof port !== "object" && typeof port !== "function")) {
1019
1024
  throw new TypeError(`EvolutionArtifactPorts requires ${label}.${method}`);
1020
1025
  }
@@ -1029,13 +1034,17 @@ function captureMethod(port, method, label) {
1029
1034
  `EvolutionArtifactPorts requires ${label}.${method} as a data method`,
1030
1035
  );
1031
1036
  }
1032
- return Object.freeze(descriptor.value.bind(port));
1037
+ return descriptor.value;
1033
1038
  }
1034
1039
  cursor = Object.getPrototypeOf(cursor);
1035
1040
  }
1036
1041
  throw new TypeError(`EvolutionArtifactPorts requires ${label}.${method}`);
1037
1042
  }
1038
1043
 
1044
+ function captureMethod(port, method, label) {
1045
+ return Object.freeze(captureDataMethod(port, method, label).bind(port));
1046
+ }
1047
+
1039
1048
  function captureArtifactStore(store) {
1040
1049
  rejectProxy(store, "artifactStore");
1041
1050
  if (!hasStablePrototype(store, ArtifactStore.prototype, "artifactStore")) {
@@ -1049,19 +1058,21 @@ function captureArtifactStore(store) {
1049
1058
  }
1050
1059
  const resolvedDir = path.resolve(dir);
1051
1060
  const layout = establishStoreLayout(resolvedDir);
1061
+ const list = captureDataMethod(store, "list", "artifactStore");
1062
+ const get = captureDataMethod(store, "get", "artifactStore");
1052
1063
  const captured = Object.freeze({
1053
1064
  dir: resolvedDir,
1054
- get: captureMethod(store, "get", "artifactStore"),
1065
+ get: get === DEFAULT_STORE_GET ? null : Object.freeze(get.bind(store)),
1055
1066
  layout,
1056
- list: captureMethod(store, "list", "artifactStore"),
1067
+ list: list === DEFAULT_STORE_LIST ? null : Object.freeze(list.bind(store)),
1057
1068
  publishDataOnce: captureMethod(store, "publishDataOnce", "artifactStore"),
1058
1069
  storedPath: captureMethod(store, "storedPath", "artifactStore"),
1059
1070
  verifyIntegrity: captureMethod(store, "verifyIntegrity", "artifactStore"),
1060
1071
  });
1061
1072
  // ArtifactStore methods consult public instance fields such as `dir` on
1062
1073
  // every call. Freeze the trusted instance after capturing its methods so a
1063
- // later collaborator cannot redirect a bound get/list/integrity call to a
1064
- // different store root.
1074
+ // later collaborator cannot redirect a bound custom get/list/integrity call
1075
+ // to a different store root. Null index methods select trusted snapshots.
1065
1076
  Object.freeze(store);
1066
1077
  return captured;
1067
1078
  }
@@ -2164,7 +2175,7 @@ export class EvolutionArtifactPorts {
2164
2175
  let entries;
2165
2176
  try {
2166
2177
  entries = rejectPromise(
2167
- this.#store.list(),
2178
+ this.#store.list ? this.#store.list() : trustedBefore.entries,
2168
2179
  "ArtifactStore.list",
2169
2180
  EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
2170
2181
  );
@@ -2187,9 +2198,9 @@ export class EvolutionArtifactPorts {
2187
2198
  "ArtifactStore.list did not return an array",
2188
2199
  );
2189
2200
  }
2190
- // ArtifactStore currently materializes its whole JSONL index before this
2191
- // adapter can enforce a limit. Reject immediately after return; this is an
2192
- // explicit scalability boundary, not a claim of bounded store-side query.
2201
+ // Default entries already came from a bounded descriptor read. Custom
2202
+ // list hooks retain their original execution and exact cross-check; their
2203
+ // own I/O cannot be constrained by this adapter's snapshot byte limit.
2193
2204
  if (entries.length > EVOLUTION_ARTIFACT_MAX_INDEX_ENTRIES) {
2194
2205
  throw artifactError(
2195
2206
  EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
@@ -2280,7 +2291,7 @@ export class EvolutionArtifactPorts {
2280
2291
  let current;
2281
2292
  try {
2282
2293
  current = rejectPromise(
2283
- this.#store.get(artifactId),
2294
+ this.#store.get ? this.#store.get(artifactId) : listed[0],
2284
2295
  "ArtifactStore.get",
2285
2296
  EVOLUTION_ARTIFACT_STORE_FAILED_CODE,
2286
2297
  );
@@ -4,8 +4,7 @@ import path from "node:path";
4
4
  import { types } from "node:util";
5
5
  import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
6
6
  import { withFileLock } from "../with-file-lock.js";
7
- import { readBoundedDescriptor } from "./bounded-descriptor-read.js";
8
- import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
7
+ import { createEvolutionPhysicalStateReader } from "./evolution-physical-state-reader.js";
9
8
  import {
10
9
  createEvolutionWitnessSegments,
11
10
  EVOLUTION_SEGMENTED_WITNESS_SCHEMA,
@@ -17,6 +16,8 @@ import {
17
16
 
18
17
  export const EVOLUTION_FILE_WITNESS_STORE_SCHEMA =
19
18
  "chainlesschain.evolution-file-witness-store/v1";
19
+ export const EVOLUTION_WITNESS_STATE_READ_FAILED_CODE =
20
+ "CC_EVOLUTION_WITNESS_STATE_READ_FAILED";
20
21
 
21
22
  const WITNESS_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_SCHEMA}\0`;
22
23
  const ANCESTRY_DOMAIN = `${EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA}\0`;
@@ -247,6 +248,63 @@ export function createEvolutionFileWitness({
247
248
  }
248
249
  const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
249
250
  ensurePrivateDirectory(directory, secureOptions);
251
+ const readerOptions = {
252
+ fsImpl,
253
+ maximumBytes,
254
+ minimumBytes: 2,
255
+ directoryMode: process.platform === "win32" ? null : 0o700n,
256
+ sizeError: "store size is invalid",
257
+ label: "witness",
258
+ errorCode: EVOLUTION_WITNESS_STATE_READ_FAILED_CODE,
259
+ readFailureMessage: "witness bounded read failed",
260
+ };
261
+ const rootReader = createEvolutionPhysicalStateReader({
262
+ ...readerOptions,
263
+ directoryPath: directory,
264
+ });
265
+ const segmentDirectory = `${target}.segments-v2`;
266
+ let segmentReader = null;
267
+ const captureSegments = () => {
268
+ rootReader.recheck();
269
+ segmentReader = createEvolutionPhysicalStateReader({
270
+ ...readerOptions,
271
+ label: "witness segment",
272
+ readFailureMessage: "witness segment bounded read failed",
273
+ directoryPath: segmentDirectory,
274
+ });
275
+ rootReader.recheck();
276
+ return segmentReader;
277
+ };
278
+ // A constructor may secure existing local storage. Read paths never create
279
+ // or repair a missing segment directory; first publication retains that job.
280
+ let existingSegments = false;
281
+ try {
282
+ fsImpl.lstatSync(segmentDirectory, { bigint: true });
283
+ existingSegments = true;
284
+ } catch (cause) {
285
+ if (cause?.code !== "ENOENT") throw cause;
286
+ }
287
+ if (existingSegments) {
288
+ ensurePrivateDirectory(segmentDirectory, secureOptions);
289
+ captureSegments();
290
+ }
291
+ const checkSegments = () => {
292
+ const reader = segmentReader ?? captureSegments();
293
+ rootReader.recheck();
294
+ reader.recheck();
295
+ return reader;
296
+ };
297
+ const prepareSegments = () => {
298
+ rootReader.recheck();
299
+ if (segmentReader !== null) return segmentReader.recheck();
300
+ ensurePrivateDirectory(segmentDirectory, secureOptions);
301
+ captureSegments();
302
+ };
303
+ const recheckStorage = () => {
304
+ rootReader.recheck();
305
+ segmentReader?.recheck();
306
+ rootReader.recheck();
307
+ };
250
308
  // Cache immutable encodings, never an authority decision. Keep the earliest
251
309
  // entries when full so full-history scans cannot churn the bounded cache.
252
310
  const recordEncodings = new Map();
@@ -587,67 +645,25 @@ export function createEvolutionFileWitness({
587
645
  return store;
588
646
  };
589
647
 
590
- const readBytes = (file, byteLimit = maximumBytes) =>
591
- withEvolutionFileIdentity(fsImpl, file, (samePathHandle) =>
592
- readBytesWithIdentity(
593
- file,
594
- Math.min(maximumBytes, byteLimit),
595
- samePathHandle,
596
- ),
597
- );
598
-
599
- const readBytesWithIdentity = (file, byteLimit, samePathHandle) => {
600
- ensurePrivateFile(file, secureOptions);
601
- const stat = fsImpl.lstatSync(file);
602
- if (
603
- !stat.isFile() ||
604
- stat.isSymbolicLink() ||
605
- stat.nlink !== 1 ||
606
- stat.size < 2 ||
607
- stat.size > byteLimit
608
- ) {
609
- throw new Error("witness store size is invalid");
610
- }
611
- let descriptor;
612
- const sameFile = (observed) =>
613
- observed.isFile() &&
614
- observed.nlink === 1 &&
615
- observed.dev === stat.dev &&
616
- observed.ino === stat.ino &&
617
- observed.size === stat.size &&
618
- observed.mtimeMs === stat.mtimeMs &&
619
- observed.ctimeMs === stat.ctimeMs;
620
- try {
621
- descriptor = fsImpl.openSync(
622
- file,
623
- fsImpl.constants.O_RDONLY | (fsImpl.constants.O_NOFOLLOW || 0),
624
- );
625
- if (!samePathHandle(stat, fsImpl.fstatSync(descriptor))) {
626
- throw new Error("witness store changed while opening");
627
- }
628
- const bytes = readBoundedDescriptor(
629
- fsImpl,
630
- descriptor,
631
- stat.size,
632
- byteLimit,
633
- );
634
- const afterPath = fsImpl.lstatSync(file);
635
- if (
636
- !samePathHandle(stat, fsImpl.fstatSync(descriptor)) ||
637
- afterPath.isSymbolicLink() ||
638
- !sameFile(afterPath)
639
- ) {
640
- throw new Error("witness store changed while reading");
641
- }
642
- return bytes;
643
- } finally {
644
- if (descriptor !== undefined) fsImpl.closeSync(descriptor);
645
- }
648
+ const readBytes = (file, byteLimit = maximumBytes, allowAbsent = false) => {
649
+ recheckStorage();
650
+ const reader = file === target ? rootReader : checkSegments();
651
+ const bytes = reader.read(file, {
652
+ maximumBytes: byteLimit,
653
+ allowAbsent,
654
+ prepareFile: (name) => ensurePrivateFile(name, secureOptions),
655
+ });
656
+ recheckStorage();
657
+ return bytes;
646
658
  };
647
659
 
648
660
  const readStore = (options = {}) => {
649
- if (!fsImpl.existsSync(target)) return emptyStore();
650
- const bytes = readBytes(target, maximumHistoryBytes);
661
+ const bytes = readBytes(target, maximumHistoryBytes, true);
662
+ if (bytes === null) {
663
+ const store = emptyStore();
664
+ recheckStorage();
665
+ return store;
666
+ }
651
667
  const parsed = JSON.parse(
652
668
  new TextDecoder("utf-8", { fatal: true }).decode(bytes),
653
669
  );
@@ -660,7 +676,10 @@ export function createEvolutionFileWitness({
660
676
  headBytes: bytes.length,
661
677
  })
662
678
  : validateStore(parsed, trustEpoch);
663
- if (trustEpoch === currentTrustEpoch()) return store;
679
+ if (trustEpoch === currentTrustEpoch()) {
680
+ recheckStorage();
681
+ return store;
682
+ }
664
683
  }
665
684
  throw new Error("witness verifier trust epoch changed while reading");
666
685
  };
@@ -716,6 +735,8 @@ export function createEvolutionFileWitness({
716
735
  const segments = createEvolutionWitnessSegments({
717
736
  filePath: target,
718
737
  fsImpl,
738
+ checkDirectory: checkSegments,
739
+ prepareDirectory: prepareSegments,
719
740
  readBytes,
720
741
  writeBytes,
721
742
  validateStore,
@@ -760,8 +781,10 @@ export function createEvolutionFileWitness({
760
781
  return store.current;
761
782
  };
762
783
 
763
- const underLock = (operation) =>
764
- lock(target, operation, { failIfUnavailable: true, _fs: fsImpl });
784
+ const underLock = (operation) => {
785
+ recheckStorage();
786
+ return lock(target, operation, { failIfUnavailable: true, _fs: fsImpl });
787
+ };
765
788
  const discarded = (store, snapshot) =>
766
789
  segments.isDiscarded(store, snapshot) ||
767
790
  store.discardedAnchors.some(
@@ -4,6 +4,7 @@ import { createEvolutionFileWitness } from "./evolution-file-witness.js";
4
4
  import {
5
5
  EvolutionLedger,
6
6
  captureEvolutionLedgerMigrationSource,
7
+ captureEvolutionLedgerDirectoryBoundary,
7
8
  } from "./evolution-ledger.js";
8
9
  import { createEvolutionLedgerV2JournalFromFactory } from "./evolution-ledger-v2-journal.js";
9
10
 
@@ -12,6 +13,7 @@ export const EVOLUTION_LEDGER_FILE_BACKEND_SCHEMA =
12
13
 
13
14
  const BACKENDS = new WeakSet();
14
15
  const BACKEND_BINDINGS = new WeakMap();
16
+ const SOURCE_DIRECTORY_BOUNDARIES = new WeakMap();
15
17
 
16
18
  function requiredString(value, label) {
17
19
  if (typeof value !== "string" || value.trim() === "") {
@@ -205,7 +207,10 @@ export function createEvolutionLedgerFileBackend({
205
207
  ledger,
206
208
  witness,
207
209
  });
210
+ const sourceDirectoryBoundary =
211
+ captureEvolutionLedgerDirectoryBoundary(sourceLedger);
208
212
  BACKENDS.add(backend);
213
+ SOURCE_DIRECTORY_BOUNDARIES.set(backend, sourceDirectoryBoundary);
209
214
  BACKEND_BINDINGS.set(
210
215
  backend,
211
216
  Object.freeze({
@@ -234,3 +239,20 @@ export function captureEvolutionLedgerFileBackendBinding(value) {
234
239
  );
235
240
  return binding;
236
241
  }
242
+
243
+ /** Captured private v1 source even when the public journal is a v2 wrapper. */
244
+ export function captureEvolutionLedgerFileBackendSourceDirectoryBoundary(
245
+ value,
246
+ ) {
247
+ if (arguments.length !== 1)
248
+ throw new TypeError(
249
+ "source directory boundary capture takes exactly one original backend",
250
+ );
251
+ const boundary = SOURCE_DIRECTORY_BOUNDARIES.get(value);
252
+ if (!boundary)
253
+ throw new TypeError(
254
+ "a branded file backend source directory boundary is required",
255
+ );
256
+ boundary.recheck();
257
+ return boundary;
258
+ }
@@ -5,7 +5,10 @@ import { types as utilTypes } from "node:util";
5
5
 
6
6
  import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
7
7
  import { withFileLock } from "../with-file-lock.js";
8
- import { readBoundedDescriptor } from "./bounded-descriptor-read.js";
8
+ import {
9
+ createEvolutionManifestStateReader,
10
+ captureEvolutionManifestStateReaderDirectoryBoundary,
11
+ } from "./evolution-manifest-state-reader.js";
9
12
  import { EVOLUTION_LEDGER_MANIFEST_CATALOG_APPEND_RESULT_SCHEMA } from "./evolution-ledger-manifest-catalog.js";
10
13
 
11
14
  export const EVOLUTION_LEDGER_FILE_MANIFEST_CATALOG_SCHEMA =
@@ -15,6 +18,7 @@ const STATE_SCHEMA =
15
18
  "chainlesschain.evolution-ledger-file-manifest-catalog-state/v2";
16
19
  const STATE_KEYS = new Set(["manifests", "schema"]);
17
20
  const DEFAULT_MAXIMUM_BYTES = 64 * 1024 * 1024;
21
+ const DIRECTORY_BOUNDARIES = new WeakMap();
18
22
 
19
23
  function exactRecord(value, keys, label) {
20
24
  if (
@@ -75,39 +79,13 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
75
79
  const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
76
80
  ensurePrivateDirectory(directory, secureOptions);
77
81
 
78
- const readBytes = () => {
79
- if (!fsImpl.existsSync(file)) return null;
80
- const stat = fsImpl.lstatSync(file);
81
- if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
82
- throw new Error("manifest catalog file identity is invalid");
83
- }
84
- if (stat.size > maximumBytes)
85
- throw new Error("manifest catalog exceeds maximumBytes");
86
- let descriptor;
87
- try {
88
- descriptor = fsImpl.openSync(
89
- file,
90
- fsImpl.constants.O_RDONLY | (fsImpl.constants.O_NOFOLLOW || 0),
91
- );
92
- const opened = fsImpl.fstatSync(descriptor);
93
- if (!opened.isFile() || opened.nlink !== 1 || opened.size !== stat.size) {
94
- throw new Error("manifest catalog changed while opening");
95
- }
96
- const bytes = readBoundedDescriptor(
97
- fsImpl,
98
- descriptor,
99
- stat.size,
100
- maximumBytes,
101
- );
102
- const after = fsImpl.lstatSync(file);
103
- if (after.isSymbolicLink() || after.size !== stat.size) {
104
- throw new Error("manifest catalog changed while reading");
105
- }
106
- return bytes;
107
- } finally {
108
- if (descriptor !== undefined) fsImpl.closeSync(descriptor);
109
- }
110
- };
82
+ const stateReader = createEvolutionManifestStateReader({
83
+ file,
84
+ fsImpl,
85
+ maximumBytes,
86
+ label: "manifest catalog",
87
+ });
88
+ const readBytes = stateReader.read;
111
89
 
112
90
  const readState = () => {
113
91
  const bytes = readBytes();
@@ -173,13 +151,14 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
173
151
  }
174
152
  };
175
153
 
176
- return Object.freeze({
154
+ const backend = Object.freeze({
177
155
  descriptor: Object.freeze({
178
156
  file,
179
157
  localOnly: true,
180
158
  schema: EVOLUTION_LEDGER_FILE_MANIFEST_CATALOG_SCHEMA,
181
159
  }),
182
160
  compareAndAppend(request) {
161
+ stateReader.recheck();
183
162
  return lock(
184
163
  file,
185
164
  () => {
@@ -215,4 +194,24 @@ export function createEvolutionLedgerFileManifestCatalogBackend({
215
194
  return structuredClone(readState().manifests.at(-1) ?? null);
216
195
  },
217
196
  });
197
+ DIRECTORY_BOUNDARIES.set(
198
+ backend,
199
+ captureEvolutionManifestStateReaderDirectoryBoundary(stateReader),
200
+ );
201
+ return backend;
202
+ }
203
+
204
+ /** Original local catalog directory only; structural wrappers are not owners. */
205
+ export function captureEvolutionLedgerFileManifestCatalogDirectoryBoundary(
206
+ backend,
207
+ ) {
208
+ if (arguments.length !== 1)
209
+ throw new TypeError("catalog directory capture takes one original backend");
210
+ const boundary = DIRECTORY_BOUNDARIES.get(backend);
211
+ if (!boundary)
212
+ throw new TypeError(
213
+ "an original file manifest catalog backend is required",
214
+ );
215
+ boundary.recheck();
216
+ return boundary;
218
217
  }