chainlesschain 0.166.32 → 0.166.36

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 (174) hide show
  1. package/README.md +24 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-DKUOkYex.js → AIOps-Zs-zPwVG.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-B3luFrPA.js → ActionButton-HZlxM6H0.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-jbZ8R2S3.js → Analytics-CXyThRDb.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CFS2vzPS.js → AppLayout-DeyYh_EF.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-sS0Qpy5q.js → Artifacts-BQDtMwkx.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-CZ7kfjCG.js → Audit-DNH4UOux.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BpWc5SgP.js → BackgroundAgents-BkKdIxE6.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DpEYnvoy.js → Backup-DYy4aJom.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-kFzUGLnl.js → BaseInput-DjoyGVYm.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BP_8aAfJ.js → Chat-D86ZYgOD.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-yFeZ15D4.js → ChatBubbleRenderer-RxI_YYth.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-HR1HQ06d.js → Checkbox-C7p8UY99.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-DxAqDPjT.js → Codegen-DnapqgV5.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-BaCLih7J.js → Col-C29J2DBT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-ZX64uW8D.js → Community-Bf3KeLCr.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CLtMoXTt.js → Compact-DA83wB48.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BlyxBDrs.js → Compliance-CXXVgRFG.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-CtQZIYPh.js → Cowork-B28Ftuqv.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-BebN-ajI.js → Cron-CO-BcnDf.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-3gRU0-58.js → Crosschain-BmgPMFCs.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DSVTtLnb.js → DID-CYW9WslA.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CvnhOWqc.js → Dashboard-CWWhVWds.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BhdofmCJ.js → Dropdown-CjTuc1-n.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-Dhpua9Lo.js → EmailListRenderer-BrROAu4d.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DA1mLqQ4.js → FamilyGuardDashboard-Ci8Lmk8W.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-D6SUyKGb.js → Federation-CKzKVLE_.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BcMiwguB.js → FormItemContext-CC2CK5PQ.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-CGEB1WcH.js → GenericCardRenderer-BYa3ElyT.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DGCNNqR_.js → Git-Bmn3ucA4.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-D4HVyFdL.js → Governance-DqEOXitg.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-BOoEmvKl.js → Inference-BjfeExba.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-ECLI0C0_.js → KnowledgeGraph-CBTyIFlJ.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-Cf9GjHtX.js → Logs-KZfA_M9Q.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-C3J59f8v.js → MarkdownRenderer-BG7vi3V4.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-DeL4rOAJ.js → Marketplace-BFeK4he0.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-Dvn3KpLW.js → McpTools-Brg7uUoK.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-CSlLy-aw.js → Memory-Ce4AmyX3.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-yNIe8-74.js → MobileBridge-Dx1VBL_E.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-CsS8Lozw.js → MobileProjects-DPyRr2Xl.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-Q7iMzSds.js → Mtc-BzRmtDQU.js} +3 -3
  43. package/src/assets/web-panel/assets/{MtcAudit-CW71IQrK.js → MtcAudit-DvZ5kLvJ.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-pursAeeT.js → Multisig-D8uGUjX1.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DtNo1qqO.js → NLProgramming-igc-TQE5.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-B1xUFwUT.js → Notes-CTqDYlek.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-BgmQs_J8.js → NotificationSettings-CkcLa2rb.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DI9t8YZl.js → OrderTableRenderer-BfScYvnH.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BbrrT6gR.js → Organization-BjKK9SdB.js} +2 -2
  50. package/src/assets/web-panel/assets/{Overflow-BcPi4YUc.js → Overflow-BkcghTPE.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-DfRteW76.js → P2P-l9f34Xu8.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-De81lu5Q.js → PdhVaultBrowser-yVXWmwWe.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-DLCRaOe3.js → Permissions-DeSYs0VV.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-fuTL6z8u.js → PersonalDataHub-CmKmhy0q.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-D3ztsQDw.js → Pipeline-DBxS4R_r.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-4f-SQ4ie.js → Privacy-CKngdGw3.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-CC_ZFjrV.js → ProjectInit-0Pso0jXN.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C9FWHcne.js → ProjectSettings-BHrqWQ6r.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-CR3ETMQQ.js → Projects-B8wIh9PK.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-BWcbdgXx.js → Providers-CXFTOHRV.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BavxpdIK.js → QrScannerModal-DfN0c_BN.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-CZ7gupY4.js → QuickAsk-v1Z5hty_.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BQNpBYLL.js → Recommend-DOaVmZUi.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-2LU0Vr44.js → RemoteSession--EWcPBY1.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-oYWo7qyp.js → Reputation-DUWFSNvV.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-B8Yj-Csx.js → Row-0-bFnXWF.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-CbxKbHJr.js → RssFeed-B_uTfubW.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-DGe-u9s0.js → Search-CqxiTLfB.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-BwjfMAFI.js → Security-CGoHOz5B.js} +2 -2
  70. package/src/assets/web-panel/assets/{Services-BB5NQHwQ.js → Services-COhAbS45.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-BPSVUive.js → Skeleton-B97crp6v.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DU2D-IYQ.js → Skills-er_XdavV.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DLX0vcyj.js → Sla-RpkWkzOL.js} +1 -1
  74. package/src/assets/web-panel/assets/SpeechSettings-DDVXmT5y.js +1 -0
  75. package/src/assets/web-panel/assets/{SyncSettings-QuDslViv.js → SyncSettings-Bbiw6ohw.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C-jF6Kwk.js → Tasks-CRghr7lo.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-B6tY1zm-.js → Templates-BUQkfydG.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-BXx5FAY1.js → Tenant-CfUFOsZu.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CNk8KI4J.js → Terminal-BGY7gi4M.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-Bo2YVc2l.js → TimelineRenderer-B3Len0l7.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-Bs1Oq8t9.js → Tokens-BjeHn2zI.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-CtDhiQxG.js → Trigger-ctcZ2mcv.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-riAZiPHp.js → Trust-oncF3TpO.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-DZRzYaNL.js → UkeySign-BwKbVYCd.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-DTwOdg2x.js → VideoEditing-D1dgL-3U.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-ByuDECxb.js → Wallet-4hXAfjBL.js} +3 -3
  87. package/src/assets/web-panel/assets/{WebAuthn-CkSKfId0.js → WebAuthn-C8p3cotb.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BmyJJndu.js → WorkflowEditor-BnrjG77E.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-s1LiqLff.js → chat-B-YNK7EI.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CMKVIFUx.js → colors-B9HYvK-e.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-DQGdG4Y5.js → compact-item-BJr0quBA.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-Csln9jj1.js → createContext-CXfbQEWD.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DNpXMZaQ.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-D1RYDhMF.js → hasIn-B7Q02hdZ.js} +1 -1
  95. package/src/assets/web-panel/assets/index-2hcdmWpb.js +1 -0
  96. package/src/assets/web-panel/assets/{index-DmIeFdLq.js → index-4gZEpDAG.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CUVbDS_3.js → index-6wWd08X-.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CiB2ZOQm.js → index-B6h86qjh.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-Cew8V1UQ.js → index-BB4KIBso.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DIFiRztW.js → index-BClzZrmx.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Bqhj13Jf.js → index-BNL9lmWL.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-D1kWd1be.js → index-BU_adeUp.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-rHRlGEnX.js → index-Bd3nX6aj.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CTg3OrkW.js → index-Bf0OxvOh.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C1y8O1ZP.js → index-BleXwr2w.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CGBHLqy3.js → index-BmCD1_HC.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B39ap5Fq.js → index-BmvpVSmM.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D6eoC11O.js → index-BoNVnnAq.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CUlRsoXi.js → index-BtunVZuS.js} +1 -1
  110. package/src/assets/web-panel/assets/{index--G_4msyB.js → index-BvRqE3Un.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-B0vls3Cv.js → index-C3M8M9_U.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-ugL-2h6d.js → index-C7IZWdoB.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BxVtT9q8.js → index-CEujQqdl.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C8sTim5H.js → index-COdAugDt.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DHic4uvV.js → index-CQ7qYZS4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D3TPgm-r.js → index-CU7cBzr9.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BFHhLE91.js → index-CXgHZ_HG.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DauplA4h.js → index-CcyxVqzn.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D2v6xn0e.js → index-D0zuXz4s.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CHmRaTdN.js → index-D1GP2MQz.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-FLnF1aVs.js → index-D5g5_oua.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CmAjtQdv.js → index-DMoUa3dF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-8mlgjcck.js → index-DPUg6cVc.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BJsa5jwk.js → index-D_UneDq3.js} +1 -1
  125. package/src/assets/web-panel/assets/index-DbQCUG4D.js +1 -0
  126. package/src/assets/web-panel/assets/{index-BsmEoBNu.js → index-DsV1HA0Z.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Bl5BNEwF.js → index-DutYFyC0.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BvrkxTu0.js → index-G7Z_On_n.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BiT8auXA.js → index-Gjegl5Ig.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-jPM2-LMz.js → index-HzCaU25O.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DQ86qdvJ.js → index-cq5Q-RHa.js} +19 -19
  132. package/src/assets/web-panel/assets/{index-CJwH7xWo.js → index-iCKFl_jt.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DU23ertt.js → index-kAggAkrB.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DWJU65d6.js → initDefaultProps-DrzGM6fO.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-B0cCdvX2.js → motion-SE_xFnGL.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-CSWGW4YZ.js → move-CrPjKNiG.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-_P9unVrC.js → mtc-parser-CpcyFPK6.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-eUFaswv5.js → omit-BV0g7vf4.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-Hmk4wpqj.js → pickAttrs-lWpsc-k6.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-COIVVq2l.js → placementArrow-CiewvU4X.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-CkXZS7fs.js → responsiveObserve-DbF8pamO.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BGdyZ1Ue.js → slide-Bn82Ip9-.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-rbVNSZAN.js → statusUtils-J76gk_-G.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-CcdFI5tK.js → styleChecker-BL95J74a.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-C9yiJQMh.js → useFlexGapSupport-BUzhdQsv.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-B73ulQmg.js → useFs-B2Fppepb.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-DNN-hD_j.js → usePersonalDataHub-DjTASfYE.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BN1KizIr.js → vnode-BwrOgBqP.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-0KE4Sg53.js → zoom-B6MeT_Gw.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/llm.js +4 -3
  152. package/src/data/changelog.json +52 -0
  153. package/src/lib/evolution/evolution-artifact-ports.js +40 -3
  154. package/src/lib/evolution/evolution-file-identity.js +50 -0
  155. package/src/lib/evolution/evolution-file-witness.js +206 -91
  156. package/src/lib/evolution/evolution-ledger-file-backend.js +4 -0
  157. package/src/lib/evolution/evolution-ledger.js +37 -5
  158. package/src/lib/evolution/evolution-witness-segments.js +330 -0
  159. package/src/lib/evolution/progressive-canary-external-watchdog.js +6 -4
  160. package/src/lib/evolution/skill-candidate-registry.js +45 -11
  161. package/src/lib/evolution/skill-release-registry.js +46 -12
  162. package/src/lib/llm-connection-probe.js +21 -4
  163. package/src/lib/remote-read-loop-guard.js +72 -13
  164. package/src/lib/text-file-search-worker.js +147 -0
  165. package/src/lib/text-file-search.js +116 -0
  166. package/src/lib/web-fetch-snapshots.js +211 -0
  167. package/src/lib/web-fetch.js +192 -18
  168. package/src/lib/web-search.js +118 -10
  169. package/src/runtime/agent-core.js +44 -0
  170. package/src/runtime/coding-agent-contract-shared.cjs +98 -4
  171. package/src/assets/web-panel/assets/SpeechSettings-CsPt1rpa.js +0 -1
  172. package/src/assets/web-panel/assets/devWarning-C8YOOF96.js +0 -1
  173. package/src/assets/web-panel/assets/index-BUXHmygu.js +0 -1
  174. package/src/assets/web-panel/assets/index-CsNpDb_W.js +0 -1
@@ -0,0 +1,50 @@
1
+ import {
2
+ isAffectedWindowsZeroDeviceStatRuntime,
3
+ sameFileStatIdentity,
4
+ samePathHandleFileIdentity,
5
+ samePathHandleStableFileIdentity,
6
+ withTrustedFileParentSync,
7
+ } from "../secure-file-identity.js";
8
+
9
+ // Keep ordinary same-API comparisons exact. Only affected Windows runtimes
10
+ // bridge path/handle device projections, with a separately held directory
11
+ // device; neither file inode nor mutation metadata is relaxed.
12
+ export function withEvolutionFileIdentity(
13
+ fsImpl,
14
+ filePath,
15
+ operation,
16
+ runtime = undefined,
17
+ ) {
18
+ if (!isAffectedWindowsZeroDeviceStatRuntime(runtime)) {
19
+ return operation(sameFileStatIdentity, (pathStat, handleStat) =>
20
+ samePathHandleStableFileIdentity(
21
+ pathStat,
22
+ handleStat,
23
+ pathStat.dev,
24
+ runtime,
25
+ ),
26
+ );
27
+ }
28
+ return withTrustedFileParentSync(
29
+ fsImpl,
30
+ filePath,
31
+ ({ parentDevice }) =>
32
+ operation(
33
+ (pathStat, handleStat) =>
34
+ samePathHandleFileIdentity(
35
+ pathStat,
36
+ handleStat,
37
+ parentDevice,
38
+ runtime,
39
+ ),
40
+ (pathStat, handleStat) =>
41
+ samePathHandleStableFileIdentity(
42
+ pathStat,
43
+ handleStat,
44
+ parentDevice,
45
+ runtime,
46
+ ),
47
+ ),
48
+ { runtime },
49
+ );
50
+ }
@@ -1,9 +1,15 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { types } from "node:util";
4
5
  import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
5
6
  import { withFileLock } from "../with-file-lock.js";
6
7
  import { readBoundedDescriptor } from "./bounded-descriptor-read.js";
8
+ import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
9
+ import {
10
+ createEvolutionWitnessSegments,
11
+ EVOLUTION_SEGMENTED_WITNESS_SCHEMA,
12
+ } from "./evolution-witness-segments.js";
7
13
  import {
8
14
  EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
9
15
  EVOLUTION_LEDGER_WITNESS_SCHEMA,
@@ -137,16 +143,27 @@ function nullableIdentifier(value, label) {
137
143
  }
138
144
 
139
145
  function capture(owner, name) {
140
- if (typeof owner?.[name] !== "function")
141
- throw new TypeError(`${name} port is required`);
142
- return (...args) => Reflect.apply(owner[name], owner, args);
146
+ const method = captureOptional(owner, name);
147
+ if (method === null) throw new TypeError(`${name} port is required`);
148
+ return method;
143
149
  }
144
150
 
145
151
  function captureOptional(owner, name) {
146
- if (owner?.[name] === undefined) return null;
147
- if (typeof owner[name] !== "function")
148
- throw new TypeError(`${name} port must be a function`);
149
- return (...args) => Reflect.apply(owner[name], owner, args);
152
+ if (owner === null || owner === undefined) return null;
153
+ if (typeof owner !== "object" || types.isProxy(owner)) {
154
+ throw new TypeError(`${name} port owner must be an object without Proxy`);
155
+ }
156
+ const descriptor = Object.getOwnPropertyDescriptor(owner, name);
157
+ if (!descriptor) return null;
158
+ if (
159
+ !("value" in descriptor) ||
160
+ typeof descriptor.value !== "function" ||
161
+ types.isProxy(descriptor.value)
162
+ ) {
163
+ throw new TypeError(`${name} port must be an own data function`);
164
+ }
165
+ const method = descriptor.value;
166
+ return (...args) => Reflect.apply(method, owner, args);
150
167
  }
151
168
 
152
169
  function normalizeTrustEpoch(value) {
@@ -205,6 +222,7 @@ export function createEvolutionFileWitness({
205
222
  lock = withFileLock,
206
223
  random = () => crypto.randomBytes(16).toString("hex"),
207
224
  maximumBytes = DEFAULT_MAXIMUM_BYTES,
225
+ maximumHistoryBytes = maximumBytes,
208
226
  } = {}) {
209
227
  if (!IDENTIFIER.test(id || "")) throw new TypeError("witness id is invalid");
210
228
  const target = path.resolve(requiredString(filePath, "filePath"));
@@ -219,6 +237,14 @@ export function createEvolutionFileWitness({
219
237
  if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 4096) {
220
238
  throw new TypeError("maximumBytes must be a safe integer of at least 4096");
221
239
  }
240
+ if (
241
+ !Number.isSafeInteger(maximumHistoryBytes) ||
242
+ maximumHistoryBytes < 4096
243
+ ) {
244
+ throw new TypeError(
245
+ "maximumHistoryBytes must be a safe integer of at least 4096",
246
+ );
247
+ }
222
248
  const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
223
249
  ensurePrivateDirectory(directory, secureOptions);
224
250
  // Cache immutable encodings, never an authority decision. Keep the earliest
@@ -470,7 +496,7 @@ export function createEvolutionFileWitness({
470
496
  };
471
497
  };
472
498
 
473
- const validateStore = (store, trustEpoch = null) => {
499
+ const validateStore = (store, trustEpoch = null, priorRecord = null) => {
474
500
  assertExactKeys(store, STORE_KEYS, "witness store");
475
501
  if (
476
502
  store.schema !== EVOLUTION_FILE_WITNESS_STORE_SCHEMA ||
@@ -482,23 +508,24 @@ export function createEvolutionFileWitness({
482
508
  }
483
509
  const genesis = store.history[0];
484
510
  if (
485
- genesis.status !== "absent" ||
486
- genesis.generation !== 0 ||
487
- genesis.previousWitnessDigest !== null ||
488
- genesis.discardAccumulatorDigest !== emptyDiscardDigest ||
489
- [
490
- genesis.anchorDigest,
491
- genesis.epoch,
492
- genesis.headDigest,
493
- genesis.identityDigest,
494
- genesis.ledgerId,
495
- genesis.payloadDigest,
496
- genesis.segmentDigest,
497
- genesis.sequence,
498
- genesis.storeMarkerDigest,
499
- genesis.storeMarkerEntryDigest,
500
- genesis.storeMarkerId,
501
- ].some((entry) => entry !== null)
511
+ !priorRecord &&
512
+ (genesis.status !== "absent" ||
513
+ genesis.generation !== 0 ||
514
+ genesis.previousWitnessDigest !== null ||
515
+ genesis.discardAccumulatorDigest !== emptyDiscardDigest ||
516
+ [
517
+ genesis.anchorDigest,
518
+ genesis.epoch,
519
+ genesis.headDigest,
520
+ genesis.identityDigest,
521
+ genesis.ledgerId,
522
+ genesis.payloadDigest,
523
+ genesis.segmentDigest,
524
+ genesis.sequence,
525
+ genesis.storeMarkerDigest,
526
+ genesis.storeMarkerEntryDigest,
527
+ genesis.storeMarkerId,
528
+ ].some((entry) => entry !== null))
502
529
  ) {
503
530
  throw new Error("witness genesis is invalid");
504
531
  }
@@ -514,10 +541,13 @@ export function createEvolutionFileWitness({
514
541
  safeCounter(discard.sequence, "witness discard sequence");
515
542
  }
516
543
  let discardIndex = 0;
544
+ let previous = priorRecord;
517
545
  for (const [index, record] of store.history.entries()) {
518
546
  verifySigned(record, trustEpoch);
519
- if (index === 0) continue;
520
- const previous = store.history[index - 1];
547
+ if (index === 0 && !previous) {
548
+ previous = record;
549
+ continue;
550
+ }
521
551
  if (
522
552
  record.status !== "committed" ||
523
553
  record.previousWitnessDigest !== previous.witnessDigest ||
@@ -544,6 +574,7 @@ export function createEvolutionFileWitness({
544
574
  throw new Error("witness discard history is invalid");
545
575
  }
546
576
  }
577
+ previous = record;
547
578
  }
548
579
  if (
549
580
  discardIndex !== store.discardedAnchors.length ||
@@ -556,16 +587,24 @@ export function createEvolutionFileWitness({
556
587
  return store;
557
588
  };
558
589
 
559
- const readStore = () => {
560
- if (!fsImpl.existsSync(target)) return emptyStore();
561
- ensurePrivateFile(target, secureOptions);
562
- const stat = fsImpl.lstatSync(target);
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);
563
602
  if (
564
603
  !stat.isFile() ||
565
604
  stat.isSymbolicLink() ||
566
605
  stat.nlink !== 1 ||
567
606
  stat.size < 2 ||
568
- stat.size > maximumBytes
607
+ stat.size > byteLimit
569
608
  ) {
570
609
  throw new Error("witness store size is invalid");
571
610
  }
@@ -580,48 +619,56 @@ export function createEvolutionFileWitness({
580
619
  observed.ctimeMs === stat.ctimeMs;
581
620
  try {
582
621
  descriptor = fsImpl.openSync(
583
- target,
622
+ file,
584
623
  fsImpl.constants.O_RDONLY | (fsImpl.constants.O_NOFOLLOW || 0),
585
624
  );
586
- if (!sameFile(fsImpl.fstatSync(descriptor))) {
625
+ if (!samePathHandle(stat, fsImpl.fstatSync(descriptor))) {
587
626
  throw new Error("witness store changed while opening");
588
627
  }
589
628
  const bytes = readBoundedDescriptor(
590
629
  fsImpl,
591
630
  descriptor,
592
631
  stat.size,
593
- maximumBytes,
632
+ byteLimit,
594
633
  );
595
- const afterPath = fsImpl.lstatSync(target);
634
+ const afterPath = fsImpl.lstatSync(file);
596
635
  if (
597
- !sameFile(fsImpl.fstatSync(descriptor)) ||
636
+ !samePathHandle(stat, fsImpl.fstatSync(descriptor)) ||
598
637
  afterPath.isSymbolicLink() ||
599
638
  !sameFile(afterPath)
600
639
  ) {
601
640
  throw new Error("witness store changed while reading");
602
641
  }
603
- const parsed = JSON.parse(
604
- new TextDecoder("utf-8", { fatal: true }).decode(bytes),
605
- );
606
- for (
607
- let attempt = 0;
608
- attempt < MAXIMUM_TRUST_EPOCH_RETRIES;
609
- attempt += 1
610
- ) {
611
- const trustEpoch = currentTrustEpoch();
612
- const store = validateStore(parsed, trustEpoch);
613
- if (trustEpoch === currentTrustEpoch()) return store;
614
- }
615
- throw new Error("witness verifier trust epoch changed while reading");
642
+ return bytes;
616
643
  } finally {
617
644
  if (descriptor !== undefined) fsImpl.closeSync(descriptor);
618
645
  }
619
646
  };
620
647
 
621
- const syncDirectory = () => {
648
+ const readStore = (options = {}) => {
649
+ if (!fsImpl.existsSync(target)) return emptyStore();
650
+ const bytes = readBytes(target, maximumHistoryBytes);
651
+ const parsed = JSON.parse(
652
+ new TextDecoder("utf-8", { fatal: true }).decode(bytes),
653
+ );
654
+ for (let attempt = 0; attempt < MAXIMUM_TRUST_EPOCH_RETRIES; attempt += 1) {
655
+ const trustEpoch = currentTrustEpoch();
656
+ const store =
657
+ parsed.schema === EVOLUTION_SEGMENTED_WITNESS_SCHEMA
658
+ ? segments.load(parsed, trustEpoch, {
659
+ ...options,
660
+ headBytes: bytes.length,
661
+ })
662
+ : validateStore(parsed, trustEpoch);
663
+ if (trustEpoch === currentTrustEpoch()) return store;
664
+ }
665
+ throw new Error("witness verifier trust epoch changed while reading");
666
+ };
667
+
668
+ const syncDirectory = (directoryPath = directory) => {
622
669
  let descriptor;
623
670
  try {
624
- descriptor = fsImpl.openSync(directory, "r");
671
+ descriptor = fsImpl.openSync(directoryPath, "r");
625
672
  fsImpl.fsyncSync(descriptor);
626
673
  } catch (error) {
627
674
  if (
@@ -634,12 +681,8 @@ export function createEvolutionFileWitness({
634
681
  }
635
682
  };
636
683
 
637
- const publish = (store) => {
638
- const temporary = `${target}.${requiredString(random(), "random token")}.tmp`;
639
- // Preserve the canonical on-disk format without reserializing every
640
- // unchanged historical record. Uncached records take the normal path.
641
- const encoded = `{"current":${encodeRecord(store.current)},"discardedAnchors":${canonical(store.discardedAnchors)},"history":[${store.history.map(encodeRecord).join(",")}],"schema":${JSON.stringify(store.schema)}}`;
642
- const bytes = Buffer.from(`${encoded}\n`, "utf8");
684
+ const writeBytes = (file, bytes) => {
685
+ const temporary = `${file}.${requiredString(random(), "random token")}.tmp`;
643
686
  if (bytes.length > maximumBytes) {
644
687
  throw new Error("witness store exceeds its configured maximum size");
645
688
  }
@@ -650,14 +693,16 @@ export function createEvolutionFileWitness({
650
693
  fsImpl.fsyncSync(descriptor);
651
694
  fsImpl.closeSync(descriptor);
652
695
  descriptor = undefined;
653
- fsImpl.renameSync(temporary, target);
654
- ensurePrivateFile(target, secureOptions);
655
- descriptor = fsImpl.openSync(target, "r+");
696
+ fsImpl.renameSync(temporary, file);
697
+ ensurePrivateFile(file, secureOptions);
698
+ descriptor = fsImpl.openSync(file, "r+");
656
699
  fsImpl.fsyncSync(descriptor);
657
700
  fsImpl.closeSync(descriptor);
658
701
  descriptor = undefined;
659
- syncDirectory();
660
- return store.current;
702
+ syncDirectory(path.dirname(file));
703
+ if (!readBytes(file).equals(bytes)) {
704
+ throw new Error("witness published bytes failed durable readback");
705
+ }
661
706
  } finally {
662
707
  if (descriptor !== undefined) fsImpl.closeSync(descriptor);
663
708
  try {
@@ -668,9 +713,57 @@ export function createEvolutionFileWitness({
668
713
  }
669
714
  };
670
715
 
716
+ const segments = createEvolutionWitnessSegments({
717
+ filePath: target,
718
+ fsImpl,
719
+ readBytes,
720
+ writeBytes,
721
+ validateStore,
722
+ legacySchema: EVOLUTION_FILE_WITNESS_STORE_SCHEMA,
723
+ canonical,
724
+ hash,
725
+ encodeRecord,
726
+ maximumHistoryBytes,
727
+ confirmDurable(file, expectedBytes) {
728
+ let descriptor;
729
+ try {
730
+ descriptor = fsImpl.openSync(
731
+ file,
732
+ fsImpl.constants.O_RDWR | (fsImpl.constants.O_NOFOLLOW || 0),
733
+ );
734
+ const observed = fsImpl.fstatSync(descriptor);
735
+ if (!observed.isFile() || observed.nlink !== 1) {
736
+ throw new Error(
737
+ "witness segment identity changed before durability confirmation",
738
+ );
739
+ }
740
+ fsImpl.fsyncSync(descriptor);
741
+ } finally {
742
+ if (descriptor !== undefined) fsImpl.closeSync(descriptor);
743
+ }
744
+ syncDirectory(path.dirname(file));
745
+ if (!readBytes(file).equals(expectedBytes)) {
746
+ throw new Error(
747
+ "witness segment changed during durability confirmation",
748
+ );
749
+ }
750
+ },
751
+ });
752
+
753
+ const publish = (store) => {
754
+ if (segments.handles(store)) return segments.publish(store);
755
+ const encoded = `{"current":${encodeRecord(store.current)},"discardedAnchors":${canonical(store.discardedAnchors)},"history":[${store.history.map(encodeRecord).join(",")}],"schema":${JSON.stringify(store.schema)}}`;
756
+ const bytes = Buffer.from(`${encoded}\n`, "utf8");
757
+ if (bytes.length > maximumHistoryBytes)
758
+ throw new Error("witness history exceeds its configured maximum size");
759
+ writeBytes(target, bytes);
760
+ return store.current;
761
+ };
762
+
671
763
  const underLock = (operation) =>
672
764
  lock(target, operation, { failIfUnavailable: true, _fs: fsImpl });
673
765
  const discarded = (store, snapshot) =>
766
+ segments.isDiscarded(store, snapshot) ||
674
767
  store.discardedAnchors.some(
675
768
  (entry) =>
676
769
  entry.anchorDigest === snapshot.anchorDigest ||
@@ -681,8 +774,10 @@ export function createEvolutionFileWitness({
681
774
  const port = {
682
775
  id,
683
776
  read: () => readStore().current,
684
- initialize: ({ expected, snapshot }) =>
685
- underLock(() => {
777
+ initialize: ({ expected, snapshot }) => {
778
+ expected = clone(expected);
779
+ snapshot = clone(snapshot);
780
+ return underLock(() => {
686
781
  const store = readStore();
687
782
  if (store.current.witnessDigest !== expected.witnessDigest) {
688
783
  return store.current;
@@ -691,10 +786,14 @@ export function createEvolutionFileWitness({
691
786
  store.current = witnessRecord(snapshot, store.current);
692
787
  store.history.push(store.current);
693
788
  return publish(store);
694
- }),
695
- compareAndSwap: ({ discard = null, expected, next }) =>
696
- underLock(() => {
697
- const store = readStore();
789
+ });
790
+ },
791
+ compareAndSwap: ({ discard = null, expected, next }) => {
792
+ expected = clone(expected);
793
+ next = clone(next);
794
+ discard = clone(discard);
795
+ return underLock(() => {
796
+ const store = readStore({ discards: [next, discard] });
698
797
  if (store.current.witnessDigest !== expected.witnessDigest) {
699
798
  return store.current;
700
799
  }
@@ -715,9 +814,22 @@ export function createEvolutionFileWitness({
715
814
  store.current = witnessRecord(next, store.current, discard);
716
815
  store.history.push(store.current);
717
816
  return publish(store);
718
- }),
817
+ });
818
+ },
719
819
  proveAncestry: ({ ancestor, descendant }) => {
720
- const store = readStore();
820
+ ancestor = clone(ancestor);
821
+ descendant = clone(descendant);
822
+ const store = readStore({ records: [ancestor, descendant] });
823
+ if (segments.isSegmented(store)) {
824
+ const first = segments.lookup(store, ancestor);
825
+ const last = segments.lookup(store, descendant);
826
+ if (!first || !last || first.generation > last.generation) {
827
+ throw new Error(
828
+ "witness ancestry is absent or checkpoint is not exactly bound",
829
+ );
830
+ }
831
+ return ancestryProof(first, last);
832
+ }
721
833
  const first = store.history.findIndex(
722
834
  (entry) => entry.witnessDigest === ancestor.witnessDigest,
723
835
  );
@@ -744,27 +856,30 @@ export function createEvolutionFileWitness({
744
856
  throw new Error("witness ancestry is not contiguous");
745
857
  }
746
858
  }
747
- const core = {
748
- ...trust,
749
- ancestorDigest: persistedAncestor.witnessDigest,
750
- ancestorGeneration: persistedAncestor.generation,
751
- authenticated: true,
752
- descendantDigest: persistedDescendant.witnessDigest,
753
- descendantGeneration: persistedDescendant.generation,
754
- durable: true,
755
- epoch: persistedAncestor.epoch,
756
- included: true,
757
- ledgerId: persistedAncestor.ledgerId,
758
- schema: EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
759
- witnessId: id,
760
- };
761
- return signed(
762
- ANCESTRY_DOMAIN,
763
- core,
764
- "proofDigest",
765
- "evolution-ledger-witness-ancestry",
766
- );
859
+ return ancestryProof(persistedAncestor, persistedDescendant);
767
860
  },
768
861
  };
862
+ const ancestryProof = (persistedAncestor, persistedDescendant) => {
863
+ const core = {
864
+ ...trust,
865
+ ancestorDigest: persistedAncestor.witnessDigest,
866
+ ancestorGeneration: persistedAncestor.generation,
867
+ authenticated: true,
868
+ descendantDigest: persistedDescendant.witnessDigest,
869
+ descendantGeneration: persistedDescendant.generation,
870
+ durable: true,
871
+ epoch: persistedAncestor.epoch,
872
+ included: true,
873
+ ledgerId: persistedAncestor.ledgerId,
874
+ schema: EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
875
+ witnessId: id,
876
+ };
877
+ return signed(
878
+ ANCESTRY_DOMAIN,
879
+ core,
880
+ "proofDigest",
881
+ "evolution-ledger-witness-ancestry",
882
+ );
883
+ };
769
884
  return Object.freeze(port);
770
885
  }
@@ -109,6 +109,7 @@ export function createEvolutionLedgerFileBackend({
109
109
  lockTimeoutMs,
110
110
  crashHook = null,
111
111
  witnessMaximumBytes,
112
+ witnessMaximumHistoryBytes,
112
113
  } = {}) {
113
114
  if (typeof artifactResolver !== "function") {
114
115
  throw new TypeError("artifactResolver port is required");
@@ -145,6 +146,9 @@ export function createEvolutionLedgerFileBackend({
145
146
  ...(witnessMaximumBytes === undefined
146
147
  ? {}
147
148
  : { maximumBytes: witnessMaximumBytes }),
149
+ ...(witnessMaximumHistoryBytes === undefined
150
+ ? {}
151
+ : { maximumHistoryBytes: witnessMaximumHistoryBytes }),
148
152
  };
149
153
  const witness = createEvolutionFileWitness(witnessOptions);
150
154
  const ledger = new EvolutionLedger({
@@ -3,6 +3,8 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { types as utilTypes } from "node:util";
5
5
  import { readBoundedDescriptor } from "./bounded-descriptor-read.js";
6
+ import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
7
+ import { sameFileStatIdentity } from "../secure-file-identity.js";
6
8
  import { getHomeDir } from "../paths.js";
7
9
  import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
8
10
  import { withFileLock } from "../with-file-lock.js";
@@ -2768,6 +2770,24 @@ export class EvolutionLedger {
2768
2770
  }
2769
2771
 
2770
2772
  #readCanonicalFile(filePath, maximum, label, { allowMissing = false } = {}) {
2773
+ return withEvolutionFileIdentity(this.#fs, filePath, (samePathHandle) =>
2774
+ this.#readCanonicalFileWithIdentity(
2775
+ filePath,
2776
+ maximum,
2777
+ label,
2778
+ allowMissing,
2779
+ samePathHandle,
2780
+ ),
2781
+ );
2782
+ }
2783
+
2784
+ #readCanonicalFileWithIdentity(
2785
+ filePath,
2786
+ maximum,
2787
+ label,
2788
+ allowMissing,
2789
+ samePathHandle,
2790
+ ) {
2771
2791
  this.#assertBoundaries();
2772
2792
  let before;
2773
2793
  try {
@@ -2805,7 +2825,7 @@ export class EvolutionLedger {
2805
2825
  if (
2806
2826
  !opened.isFile() ||
2807
2827
  opened.nlink !== 1 ||
2808
- entryIdentity(opened) !== entryIdentity(before) ||
2828
+ !samePathHandle(before, opened) ||
2809
2829
  opened.size !== before.size
2810
2830
  ) {
2811
2831
  throw ledgerError(
@@ -2831,7 +2851,7 @@ export class EvolutionLedger {
2831
2851
  const after = this.#fs.fstatSync(descriptor);
2832
2852
  if (
2833
2853
  after.nlink !== 1 ||
2834
- entryIdentity(after) !== entryIdentity(opened) ||
2854
+ !sameFileStatIdentity(opened, after) ||
2835
2855
  after.size !== opened.size ||
2836
2856
  bytes.length !== opened.size
2837
2857
  ) {
@@ -2875,7 +2895,9 @@ export class EvolutionLedger {
2875
2895
  return {
2876
2896
  bytes,
2877
2897
  contentDigest: sha256(bytes),
2878
- fingerprint: fileFingerprint(after),
2898
+ // Cache fingerprints belong to the pathname API. On affected Windows
2899
+ // runtimes the handle API has a different device projection.
2900
+ fingerprint: fileFingerprint(before),
2879
2901
  record,
2880
2902
  };
2881
2903
  } finally {
@@ -3361,6 +3383,16 @@ export class EvolutionLedger {
3361
3383
  }
3362
3384
 
3363
3385
  #matchesCachedFile(filePath, expected) {
3386
+ try {
3387
+ return withEvolutionFileIdentity(this.#fs, filePath, (samePathHandle) =>
3388
+ this.#matchesCachedFileWithIdentity(filePath, expected, samePathHandle),
3389
+ );
3390
+ } catch {
3391
+ return false;
3392
+ }
3393
+ }
3394
+
3395
+ #matchesCachedFileWithIdentity(filePath, expected, samePathHandle) {
3364
3396
  let descriptor = null;
3365
3397
  try {
3366
3398
  const before = this.#fs.lstatSync(filePath);
@@ -3381,7 +3413,7 @@ export class EvolutionLedger {
3381
3413
  if (
3382
3414
  !opened.isFile() ||
3383
3415
  opened.nlink !== 1 ||
3384
- entryIdentity(opened) !== entryIdentity(before) ||
3416
+ !samePathHandle(before, opened) ||
3385
3417
  opened.size !== before.size
3386
3418
  ) {
3387
3419
  return false;
@@ -3396,7 +3428,7 @@ export class EvolutionLedger {
3396
3428
  return (
3397
3429
  after.nlink === 1 &&
3398
3430
  entryIdentity(after) === entryIdentity(opened) &&
3399
- fileFingerprint(after) === expected.fingerprint &&
3431
+ samePathHandle(before, after) &&
3400
3432
  bytes.length === opened.size &&
3401
3433
  sha256(bytes) === expected.contentDigest
3402
3434
  );