chainlesschain 0.166.117 → 0.166.121

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/README.md +36 -4
  2. package/package.json +3 -3
  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/.build-hash +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-C9tSCImB.js → AIOps-DYEi6MCC.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-CXKExmT_.js → ActionButton-kLXp42k3.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DE8WBWHo.js → Analytics-BOgKma-R.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-BdpwNM08.js → AppLayout-D8z-uRpv.js} +5 -5
  10. package/src/assets/web-panel/assets/{Artifacts-94TQpe38.js → Artifacts-TFX0t24T.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-DdDEv5Ej.js → Audit-GjRCAv0V.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-TU9nlV7j.js → BackgroundAgents-CpQcMxi1.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-C0GPoUUq.js → Backup-BuQNWrhT.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-iyrVn2I6.js → BaseInput-QuOtSaFy.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-Dqo8spwp.js → Chat-BC0W1L_3.js} +5 -5
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CDqHSzc4.js → ChatBubbleRenderer-B6SagRW5.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-BF_XJvQi.js → Checkbox-BsdQ1Ku0.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-5tUWoOa9.js → Codegen-CPsAAoqV.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-BQxSeRvG.js → Col-C9LgCMN0.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-B2Ra7w-J.js → Community-Xj0vGvD5.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-GjxJwlr0.js → Compact-CXoqTgrf.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-BTUP6RTA.js → Compliance-CeshjrVy.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-DiwfL16s.js → Cowork-DjXRRrg1.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-1ceOo5ux.js → Cron-vGoU419C.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-aHYxLu0u.js → Crosschain-DZaChZ5s.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-eeTjzOLV.js → DID-BojMQqQM.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-CT3fpkvX.js → Dashboard-pKAYt4UL.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-B3UpOy-N.js → Dropdown-CqJa8N05.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-Ba2Rvoru.js → EmailListRenderer-fZ4_KAvw.js} +1 -1
  30. package/src/assets/web-panel/assets/{EvolutionSettings-CGtn93on.js → EvolutionSettings-C3kenWes.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C0IUdHQt.js → FamilyGuardDashboard-B3tqj1Hu.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-1QIDwemn.js → Federation-CS1M1oJN.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-NGbSsdje.js → FormItemContext-C5q-KYPu.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-DzVZc2Ba.js → GenericCardRenderer-Bl43BLxD.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-B_1TDbSr.js → Git-BiN2Uo-z.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BDl_ySyI.js → Governance-weglJWG4.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-CAp-wcCK.js → Inference-B4qBxVSK.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-DRjRcxul.js → KnowledgeGraph-iePl1vLv.js} +1 -1
  39. package/src/assets/web-panel/assets/Logs-t2lKo4HX.js +2 -0
  40. package/src/assets/web-panel/assets/MarkdownRenderer-CxSrBHYC.js +1 -0
  41. package/src/assets/web-panel/assets/{Marketplace-DfiXL6UO.js → Marketplace-D0Y_OmXs.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-D9rIvFjN.js → McpTools-B7zxPsFE.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-D-p7yDzj.js → Memory-DDpmVHpl.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-D0XT5B6m.js → MobileBridge-BLXNdKH0.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-CL9cHVIk.js → MobileProjects-CF6DqNfk.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-CME4kDmC.js → Mtc-7OFckDUe.js} +4 -4
  47. package/src/assets/web-panel/assets/{MtcAudit-BjGOXFnn.js → MtcAudit-2QFWPXVR.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-CSzc53e0.js → Multisig-D9opE0iP.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-BYjZnaH8.js → NLProgramming-jOD2E7Rs.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-BUPOYE0C.js → Notes-CgtysN6L.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-BK8YADru.js → NotificationSettings-Itz3X5DM.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-C2ylWgSI.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-B0WNlhMd.js → Organization-CKgpbSoy.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-CED6ZO87.js → Overflow-C0Pi9LvB.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-CBdc8fkP.js → P2P-DdIDorai.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-DuauYi03.js → PdhVaultBrowser-DVJz6cGr.js} +4 -4
  57. package/src/assets/web-panel/assets/{Permissions-kfgY0ukN.js → Permissions-D19dtcL0.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-CDY3I1Do.js → PersonalDataHub-A-QMQUJW.js} +5 -5
  59. package/src/assets/web-panel/assets/{Pipeline-B9x0wKSK.js → Pipeline-7YF1yhkH.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-Cqhcr_Qv.js → Privacy-CcsoAU7d.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-CFhoyo57.js → ProjectInit-CMU0Z5in.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-DCBjV2Hm.js → ProjectSettings-D3l0RXK6.js} +2 -2
  63. package/src/assets/web-panel/assets/Projects-BCcHN_fQ.js +1 -0
  64. package/src/assets/web-panel/assets/{Providers-BuCIWZY1.js → Providers-DB0pw3DM.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-CEXNL5CB.js → QrScannerModal-SVvLX_VH.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-DgK8aLAU.js → QuickAsk-B4dJgwL4.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-BxEYQRAs.js → Recommend-9GxSlYt5.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-PRsxNsYU.js → RemoteSession-C5vzfpTM.js} +2 -2
  69. package/src/assets/web-panel/assets/{Reputation-S-tQH70r.js → Reputation-7AeSkJz1.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-U1Ws3CJl.js → Row-Dc3RwqWR.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BftCyeii.js → RssFeed-Y8iSgp9o.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-o4gK0cPy.js → Search-bxkz-UtW.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-U2ZRpsbA.js → Security-cs5yf-AE.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-D0tRjWl_.js → Services-2va9CJFi.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-DzZ8TFsB.js → Skeleton-n8HOSPcE.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-B1cC7rju.js → Skills-BcV5aWYc.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-CtIHOR0J.js → Sla-B73zPlDG.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-CD8ZlQpW.js → SpeechSettings-OhhEZsxi.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-BilFQWQk.js → SyncSettings-4HT8618-.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-Bmg2qLXl.js → Tasks-Dw7nEG94.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-D6yofyC1.js → Templates-BX7zHI9M.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-BzttD-Gp.js → Tenant-SrFN1ZTF.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-DyRXeYuH.js → Terminal-B-ohas9S.js} +3 -3
  84. package/src/assets/web-panel/assets/{TimelineRenderer-Bdmkx-Tr.js → TimelineRenderer-exEpcIMD.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-10RcVAVJ.js → Tokens-Fucr0-Ek.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-CKV-9DdV.js → Trigger-CPlH4qBU.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-BEc-sW1q.js → Trust-DCNisGxI.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-BSql8piX.js → UkeySign-Crf4bea0.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-C4OH2f4s.js → VideoEditing-JAq5jURW.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-BSSvWKmv.js → Wallet-BaOfcyqF.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-LxIzXo9d.js → WebAuthn-DMtCTRsk.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-DWwdfcte.js → WorkflowEditor-BAKiA5p0.js} +1 -1
  93. package/src/assets/web-panel/assets/{addon-fit-CK6X9sAG.js → addon-fit-CbkjSUJl.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-DWxYYLD5.js → chat-D3MA12Z1.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-BxEcQqtg.js → colors-C7xYOAG-.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-DNbezFOJ.js → compact-item-ClybpIV-.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-DaJXc5nx.js → createContext-Dyp-8LSs.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-CTazgK0z.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BMrw3sg3.js → hasIn-D4xsaFD1.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Cm9DYzhC.js → index-B-eU0wCl.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BaByEdBj.js → index-B7jiI-31.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-T4T84G6X.js → index-BKyEhMUl.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-LRb0QX6S.js → index-BMybVxE3.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dwg38xTG.js → index-BagQvwmy.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bum_aSyd.js → index-BcmJBS7G.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BCCd8Rj6.js → index-BfZW-iaH.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B9b6UaZl.js → index-BmBqpU1f.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-6W0V5G2t.js → index-Bn8O_DC1.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BR4nWOmR.js → index-BpLWtYzy.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-2c0bL3SE.js → index-CHJUN9dR.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DpSp97J8.js → index-CNBhQp0_.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DEvdSKT4.js → index-CS7E_bsQ.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BNZDGV2q.js → index-CSOgUWox.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-VD-dTYQE.js → index-CiRxc4Y0.js} +1 -1
  115. package/src/assets/web-panel/assets/index-CrZlBv0O.js +1 -0
  116. package/src/assets/web-panel/assets/{index-CpwgF7vh.js → index-CvsWZZzU.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BcuytRTa.js → index-Cvwa0445.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B_b9_6wQ.js → index-CxSVA3yj.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CaxzeSIM.js → index-D5aA2hc_.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-cCqCEa9X.js → index-D6ETHrUX.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-DzZHBzCb.js → index-D6GFzqYv.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-hPy_f08G.js → index-DDDP6-gb.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-NEW5EN2w.js → index-DJVzi25x.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Dr18agDl.js → index-DMlcfkHz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DarVV5yG.js → index-DNeHM7F3.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-7zzLqfvI.js → index-DT57ltH_.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BAV07GDK.js → index-DWHngGhj.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-cKBpcInt.js → index-DuGdqMTo.js} +1 -1
  129. package/src/assets/web-panel/assets/index-DyNp52Uj.js +1 -0
  130. package/src/assets/web-panel/assets/{index-CL-36X3A.js → index-KvhXXaLi.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CiEdfbB9.js → index-Rk6e-ajq.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-rFaCnjXi.js → index-ZmHm5rHI.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-IX0g0s2t.js → index-_9loRV63.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-C25ig6EV.js → index-avRJM_PM.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-Bs2QyMPQ.js → index-ciHSB-CC.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-pzYLf5qu.js → index-faSaFsUp.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-DL0k35f0.js → index-nV4PAkOC.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-Ca2hwPMo.js → index-v5sQDXJx.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-BWVLgWhF.js → initDefaultProps-xrojGNIo.js} +1 -1
  140. package/src/assets/web-panel/assets/{markdown-Yo_c6Krv.js → markdown-Bq2x0XsR.js} +1 -1
  141. package/src/assets/web-panel/assets/markdown-DUKxbhE4.js +51 -0
  142. package/src/assets/web-panel/assets/{motion-KJZ1xfDE.js → motion-DzhD1BmE.js} +1 -1
  143. package/src/assets/web-panel/assets/{move-DGqU6H7M.js → move-BAq6xac8.js} +1 -1
  144. package/src/assets/web-panel/assets/{mtc-parser-Dg0xkI5J.js → mtc-parser-CbbkHqsY.js} +1 -1
  145. package/src/assets/web-panel/assets/{omit-B6RnMJAQ.js → omit-DvK7nt_t.js} +1 -1
  146. package/src/assets/web-panel/assets/{pickAttrs-Ctw2i3jb.js → pickAttrs-B8N30ZEF.js} +1 -1
  147. package/src/assets/web-panel/assets/{placementArrow-D3F3x8Jm.js → placementArrow-cUxaRoU7.js} +1 -1
  148. package/src/assets/web-panel/assets/{responsiveObserve-W6eHE0nM.js → responsiveObserve-CxMioios.js} +1 -1
  149. package/src/assets/web-panel/assets/{slide-BlFf8sY5.js → slide-CQx7R0mc.js} +1 -1
  150. package/src/assets/web-panel/assets/{statusUtils-Cd6LMwmy.js → statusUtils-kUphM3-c.js} +1 -1
  151. package/src/assets/web-panel/assets/{styleChecker-Bg5-U4Y1.js → styleChecker-BrGZ1xWq.js} +1 -1
  152. package/src/assets/web-panel/assets/{useFlexGapSupport-DTkLS3tn.js → useFlexGapSupport-Sb5Vj_ag.js} +1 -1
  153. package/src/assets/web-panel/assets/{useFs-CwYGzoBt.js → useFs-DrMnDCO5.js} +1 -1
  154. package/src/assets/web-panel/assets/{usePersonalDataHub-PCgFQXZP.js → usePersonalDataHub-C48ZIkOr.js} +1 -1
  155. package/src/assets/web-panel/assets/{vnode-CJM7MkrB.js → vnode-BQP6PZY_.js} +1 -1
  156. package/src/assets/web-panel/assets/{xterm-BZcWGsqw.js → xterm-zbskxxGA.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-CP0mgaFi.js → zoom-DOJny9bH.js} +1 -1
  158. package/src/assets/web-panel/index.html +1 -1
  159. package/src/data/changelog.json +28 -2
  160. package/src/harness/jsonl-session-store.js +2 -1
  161. package/src/lib/artifact-store.js +129 -20
  162. package/src/lib/bounded-artifact-file-read.js +128 -46
  163. package/src/lib/evolution/agent-evolution-ingress.js +41 -7
  164. package/src/lib/evolution/agent-evolution-runtime-composition.js +65 -27
  165. package/src/lib/evolution/evidence-json-text.js +124 -8
  166. package/src/lib/evolution/evolution-artifact-ports.js +1005 -83
  167. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +59 -15
  168. package/src/lib/evolution/evolution-evidence-projector.js +59 -3
  169. package/src/lib/evolution/evolution-evidence-storage-layout.js +115 -0
  170. package/src/lib/evolution/evolution-file-witness.js +7 -3
  171. package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +6 -2
  172. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +6 -2
  173. package/src/lib/evolution/evolution-ledger.js +226 -55
  174. package/src/lib/evolution/evolution-private-storage.js +20 -0
  175. package/src/lib/evolution/evolution-run-ledger-adapter.js +298 -11
  176. package/src/lib/evolution/evolution-windows-directory-durability.js +215 -32
  177. package/src/lib/evolution/local-ui-evolution-runtime.js +3 -38
  178. package/src/lib/evolution/local-ui-signing-authority.js +69 -0
  179. package/src/lib/ollama-provider-fetch.js +33 -0
  180. package/src/lib/provider-stream.js +14 -13
  181. package/src/lib/session-history-origins.js +71 -32
  182. package/src/lib/session-message-provenance.js +26 -2
  183. package/src/lib/session-runtime-retention.js +10 -1
  184. package/src/lib/session-transcript-migration.js +67 -12
  185. package/src/lib/storage-failure-cause-codes.js +449 -0
  186. package/src/runtime/agent-core.js +54 -33
  187. package/src/runtime/headless-stream.js +89 -10
  188. package/src/assets/web-panel/assets/Logs-qSuwMwk7.js +0 -2
  189. package/src/assets/web-panel/assets/MarkdownRenderer-BgnYTegK.js +0 -1
  190. package/src/assets/web-panel/assets/OrderTableRenderer-B5F8bnWt.js +0 -1
  191. package/src/assets/web-panel/assets/Projects-C51w0LnM.js +0 -1
  192. package/src/assets/web-panel/assets/devWarning-CRyXoGo9.js +0 -1
  193. package/src/assets/web-panel/assets/index-C-W0Ntk_.js +0 -1
  194. package/src/assets/web-panel/assets/index-CCk_qhBx.js +0 -1
  195. package/src/assets/web-panel/assets/markdown-CsiA8-E5.js +0 -50
@@ -2,7 +2,10 @@
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { readBoundedDescriptor } from "./evolution/bounded-descriptor-read.js";
5
- import { withEvolutionFileIdentity } from "./evolution/evolution-file-identity.js";
5
+ import {
6
+ withEvolutionDirectoryFileIdentity,
7
+ withEvolutionFileIdentity,
8
+ } from "./evolution/evolution-file-identity.js";
6
9
  import { artifactPhysicalIdentity } from "./evolution/evolution-artifact-identity.js";
7
10
 
8
11
  export const ARTIFACT_BOUNDED_READ_FAILED_CODE =
@@ -67,10 +70,10 @@ function sameStat(left, right) {
67
70
  );
68
71
  }
69
72
 
70
- /** Read one stable window only. Callers retain hash/signature and root checks. */
71
- export function readBoundedArtifactFile(target, expectedSize, maximumBytes) {
73
+ function assertReadArguments(args) {
74
+ const [target, expectedSize, maximumBytes] = args;
72
75
  if (
73
- arguments.length !== 3 ||
76
+ args.length !== 3 ||
74
77
  typeof target !== "string" ||
75
78
  !path.isAbsolute(target) ||
76
79
  path.resolve(target) !== target ||
@@ -83,49 +86,128 @@ export function readBoundedArtifactFile(target, expectedSize, maximumBytes) {
83
86
  throw new TypeError(
84
87
  "bounded artifact read requires a canonical path and finite size bounds",
85
88
  );
89
+ }
90
+
91
+ function readWithIdentity(target, expectedSize, maximumBytes, samePathHandle) {
92
+ const before = fs.lstatSync(target, { bigint: true });
93
+ safeStat(before, expectedSize, maximumBytes, "artifact path");
94
+ if (canonical(fs.realpathSync.native(target)) !== canonical(target))
95
+ fail("artifact path is not physically canonical");
96
+ const fd = fs.openSync(
97
+ target,
98
+ fs.constants.O_RDONLY |
99
+ (fs.constants.O_NOFOLLOW || 0) |
100
+ (fs.constants.O_NONBLOCK || 0),
101
+ );
86
102
  try {
87
- return withEvolutionFileIdentity(DIRECTORY_FS, target, (samePathHandle) => {
88
- const before = fs.lstatSync(target, { bigint: true });
89
- safeStat(before, expectedSize, maximumBytes, "artifact path");
90
- if (canonical(fs.realpathSync.native(target)) !== canonical(target))
91
- fail("artifact path is not physically canonical");
92
- const fd = fs.openSync(
93
- target,
94
- fs.constants.O_RDONLY |
95
- (fs.constants.O_NOFOLLOW || 0) |
96
- (fs.constants.O_NONBLOCK || 0),
97
- );
98
- try {
99
- const opened = fs.fstatSync(fd, { bigint: true });
100
- safeStat(opened, expectedSize, maximumBytes, "artifact descriptor");
101
- if (!samePathHandle(before, opened) || !sameMetadata(before, opened))
102
- fail("artifact path and descriptor identities differ");
103
- const bytes = readBoundedDescriptor(
104
- fs,
105
- fd,
106
- Number(opened.size),
107
- maximumBytes,
108
- );
109
- const closed = fs.fstatSync(fd, { bigint: true });
110
- const after = fs.lstatSync(target, { bigint: true });
111
- safeStat(closed, expectedSize, maximumBytes, "artifact descriptor");
112
- safeStat(after, expectedSize, maximumBytes, "artifact path");
113
- if (!sameStat(opened, closed))
114
- fail("artifact descriptor changed during bounded read");
115
- if (
116
- !sameStat(before, after) ||
117
- !samePathHandle(after, closed) ||
118
- !sameMetadata(after, closed) ||
119
- canonical(fs.realpathSync.native(target)) !== canonical(target)
120
- )
121
- fail("artifact pathname changed during bounded read");
122
- return bytes;
123
- } finally {
124
- fs.closeSync(fd);
125
- }
126
- });
103
+ const opened = fs.fstatSync(fd, { bigint: true });
104
+ safeStat(opened, expectedSize, maximumBytes, "artifact descriptor");
105
+ if (!samePathHandle(before, opened) || !sameMetadata(before, opened))
106
+ fail("artifact path and descriptor identities differ");
107
+ const bytes = readBoundedDescriptor(
108
+ fs,
109
+ fd,
110
+ Number(opened.size),
111
+ maximumBytes,
112
+ );
113
+ const closed = fs.fstatSync(fd, { bigint: true });
114
+ const after = fs.lstatSync(target, { bigint: true });
115
+ safeStat(closed, expectedSize, maximumBytes, "artifact descriptor");
116
+ safeStat(after, expectedSize, maximumBytes, "artifact path");
117
+ if (!sameStat(opened, closed))
118
+ fail("artifact descriptor changed during bounded read");
119
+ if (
120
+ !sameStat(before, after) ||
121
+ !samePathHandle(after, closed) ||
122
+ !sameMetadata(after, closed) ||
123
+ canonical(fs.realpathSync.native(target)) !== canonical(target)
124
+ )
125
+ fail("artifact pathname changed during bounded read");
126
+ return bytes;
127
+ } finally {
128
+ fs.closeSync(fd);
129
+ }
130
+ }
131
+
132
+ function wrapReadFailure(cause) {
133
+ if (cause?.code === ARTIFACT_BOUNDED_READ_FAILED_CODE) throw cause;
134
+ fail("artifact bounded descriptor read failed", cause);
135
+ }
136
+
137
+ /** Read one stable window only. Callers retain hash/signature and root checks. */
138
+ export function readBoundedArtifactFile(target, expectedSize, maximumBytes) {
139
+ assertReadArguments(arguments);
140
+ try {
141
+ return withEvolutionFileIdentity(DIRECTORY_FS, target, (samePathHandle) =>
142
+ readWithIdentity(target, expectedSize, maximumBytes, samePathHandle),
143
+ );
144
+ } catch (cause) {
145
+ wrapReadFailure(cause);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * A synchronous batch shares one authenticated parent handle, never file
151
+ * contents or authority. Each child keeps the complete independent read
152
+ * window above. The parent is reattested before the batch can return.
153
+ */
154
+ export function withBoundedArtifactDirectoryRead(directory, callback) {
155
+ if (
156
+ arguments.length !== 2 ||
157
+ typeof directory !== "string" ||
158
+ !path.isAbsolute(directory) ||
159
+ path.resolve(directory) !== directory ||
160
+ typeof callback !== "function" ||
161
+ callback.constructor?.name === "AsyncFunction"
162
+ )
163
+ throw new TypeError(
164
+ "bounded artifact directory read requires a canonical directory and synchronous callback",
165
+ );
166
+ let callbackFailure = null;
167
+ try {
168
+ return withEvolutionDirectoryFileIdentity(
169
+ DIRECTORY_FS,
170
+ directory,
171
+ (samePathHandle, _sameStableHandle, { parentPath }) => {
172
+ if (canonical(parentPath) !== canonical(directory))
173
+ fail("artifact batch parent is not physically canonical");
174
+ let active = true;
175
+ const read = Object.freeze((...args) => {
176
+ if (!active) fail("artifact batch read window is closed");
177
+ assertReadArguments(args);
178
+ const [target, expectedSize, maximumBytes] = args;
179
+ if (canonical(path.dirname(target)) !== canonical(parentPath))
180
+ fail(
181
+ "artifact batch read must name a direct child of its captured parent",
182
+ );
183
+ try {
184
+ return readWithIdentity(
185
+ target,
186
+ expectedSize,
187
+ maximumBytes,
188
+ samePathHandle,
189
+ );
190
+ } catch (cause) {
191
+ wrapReadFailure(cause);
192
+ }
193
+ });
194
+ try {
195
+ const result = callback(read);
196
+ if (result && typeof result.then === "function")
197
+ throw new TypeError(
198
+ "artifact batch callback must not return a promise",
199
+ );
200
+ return result;
201
+ } catch (cause) {
202
+ callbackFailure = cause;
203
+ throw cause;
204
+ } finally {
205
+ active = false;
206
+ }
207
+ },
208
+ );
127
209
  } catch (cause) {
128
- if (cause?.code === ARTIFACT_BOUNDED_READ_FAILED_CODE) throw cause;
129
- fail("artifact bounded descriptor read failed", cause);
210
+ if (cause === callbackFailure) throw cause;
211
+ wrapReadFailure(cause);
130
212
  }
131
213
  }
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import { types } from "node:util";
3
3
  import evolutionRun from "@chainlesschain/session-core/evolution-run";
4
+ import { storageFailureCauseSuffix } from "../storage-failure-cause-codes.js";
4
5
 
5
6
  import { EvolutionEvidenceArtifactAdapter } from "./evolution-evidence-artifact-adapter.js";
6
7
  import {
@@ -8,7 +9,14 @@ import {
8
9
  EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST,
9
10
  EVOLUTION_PROJECTION_RULESET_DIGEST,
10
11
  } from "./evolution-evidence-projector.js";
11
- import { EvolutionRunLedgerAdapter } from "./evolution-run-ledger-adapter.js";
12
+ import {
13
+ EvolutionRunLedgerAdapter,
14
+ captureEvolutionRunAppendNext,
15
+ } from "./evolution-run-ledger-adapter.js";
16
+ import {
17
+ evidenceStorageLayoutForRun,
18
+ validateEvidenceStorageLayout,
19
+ } from "./evolution-evidence-storage-layout.js";
12
20
  import { captureEvolutionRunWikiMaintenanceProducer } from "./evolution-run-wiki-maintenance-source.js";
13
21
  import { WIKI_MAINTENANCE_TRIGGER_KIND } from "./wiki-maintenance-trigger-ledger-adapter.js";
14
22
  import { captureEvolutionReleaseTrain } from "./evolution-release-train.js";
@@ -152,6 +160,7 @@ function ingressFailure(cause) {
152
160
  `Agent evolution ingress failed: ${cause?.message || String(cause)}`,
153
161
  { cause },
154
162
  );
163
+ error.message += storageFailureCauseSuffix(cause);
155
164
  error.code = AGENT_EVOLUTION_INGRESS_FAILED_CODE;
156
165
  return error;
157
166
  }
@@ -165,6 +174,7 @@ function guardIngress(operation) {
165
174
  export function createAgentEvolutionIngress({
166
175
  evidenceAdapter,
167
176
  runAdapter,
177
+ evidenceStorageLayout = "legacy",
168
178
  sourceEnvelopeAuthority,
169
179
  openCacheSourceRun = null,
170
180
  wikiMaintenanceProducer = null,
@@ -173,6 +183,7 @@ export function createAgentEvolutionIngress({
173
183
  now = () => new Date(),
174
184
  idGenerator = () => crypto.randomUUID(),
175
185
  } = {}) {
186
+ validateEvidenceStorageLayout(evidenceStorageLayout);
176
187
  if (!(evidenceAdapter instanceof EvolutionEvidenceArtifactAdapter)) {
177
188
  throw new TypeError("an EvolutionEvidenceArtifactAdapter is required");
178
189
  }
@@ -245,10 +256,24 @@ export function createAgentEvolutionIngress({
245
256
  return timestamp(iso, "ingress timestamp");
246
257
  };
247
258
 
259
+ const assertLoadedLayout = (loaded) => {
260
+ if (
261
+ evidenceStorageLayoutForRun(loaded.events, evidenceStorageLayout) !==
262
+ evidenceStorageLayout
263
+ )
264
+ throw new Error("Authenticated Run Evidence storage layout changed");
265
+ };
266
+
248
267
  const appendStarted = () => {
249
268
  const loaded = runAdapter.load();
269
+ assertLoadedLayout(loaded);
250
270
  if (loaded.events.length > 0) return loaded.projection;
251
271
  const occurredAt = currentTimestamp();
272
+ const data = {
273
+ occurredAt,
274
+ runtime: "agent",
275
+ ...(evidenceStorageLayout === "legacy" ? {} : { evidenceStorageLayout }),
276
+ };
252
277
  return runAdapter.appendEvent({
253
278
  schema: EVOLUTION_RUN_EVENT_SCHEMA,
254
279
  tenantId: descriptor.tenantId,
@@ -257,10 +282,10 @@ export function createAgentEvolutionIngress({
257
282
  sequence: 1,
258
283
  type: EVENT_TYPES.RUN_STARTED,
259
284
  subjectId: descriptor.runId,
260
- payloadDigest: digest({ occurredAt, runtime: "agent" }),
285
+ payloadDigest: digest(data),
261
286
  artifactRef: null,
262
287
  keyRef: null,
263
- data: { occurredAt, runtime: "agent" },
288
+ data,
264
289
  }).projection;
265
290
  };
266
291
 
@@ -347,13 +372,14 @@ export function createAgentEvolutionIngress({
347
372
  "Agent evidence persistence returned an unbound result",
348
373
  );
349
374
  }
350
- const current = runAdapter.load();
351
- const appended = runAdapter.appendEvent({
375
+ const appendNext = captureEvolutionRunAppendNext(runAdapter);
376
+ // Custom ports keep their established load-before-append phase.
377
+ const current = appendNext === null ? runAdapter.load() : null;
378
+ const event = {
352
379
  schema: EVOLUTION_RUN_EVENT_SCHEMA,
353
380
  tenantId: descriptor.tenantId,
354
381
  runId: descriptor.runId,
355
382
  eventId,
356
- sequence: current.events.length + 1,
357
383
  type: EVENT_TYPES.RAW_EVENT_REFERENCED,
358
384
  subjectId: persisted.evidenceId,
359
385
  payloadDigest: persisted.manifest.digest,
@@ -371,7 +397,14 @@ export function createAgentEvolutionIngress({
371
397
  candidateSetDigest: skillDecisionRequest.candidateSetDigest,
372
398
  }),
373
399
  },
374
- });
400
+ };
401
+ const appended =
402
+ appendNext !== null
403
+ ? appendNext(event)
404
+ : runAdapter.appendEvent({
405
+ ...event,
406
+ sequence: current.events.length + 1,
407
+ });
375
408
  if (kind === "model-input") {
376
409
  lastModelResponse = null;
377
410
  modelAdmissionPending = true;
@@ -677,6 +710,7 @@ export function createAgentEvolutionIngress({
677
710
  serialize(async () => {
678
711
  if (admissionFailure !== null) throw admissionFailure;
679
712
  const loaded = runAdapter.load();
713
+ assertLoadedLayout(loaded);
680
714
  let projection;
681
715
  if (loaded.projection?.status === "completed") {
682
716
  projection = loaded.projection;
@@ -14,6 +14,11 @@ import {
14
14
  EvolutionEvidenceReader,
15
15
  } from "./evolution-evidence-projector.js";
16
16
  import { EvolutionArtifactPorts } from "./evolution-artifact-ports.js";
17
+ import {
18
+ createTypedEvidenceArtifactPorts,
19
+ evidenceStorageLayoutForRun,
20
+ validateEvidenceStorageLayout,
21
+ } from "./evolution-evidence-storage-layout.js";
17
22
  import {
18
23
  captureEvolutionLedgerFileBackend,
19
24
  createEvolutionLedgerFileBackend,
@@ -172,16 +177,14 @@ function layout(stateRootDir, tenantId, runId, initialize = true, fsImpl = fs) {
172
177
  const result = Object.freeze({
173
178
  rootDir,
174
179
  artifactDir: path.join(scope, "artifacts"),
180
+ evidenceArtifactDir: path.join(scope, "evidence-artifacts"),
175
181
  rawDir: path.join(scope, "raw"),
176
182
  ledgerRootDir: path.join(scope, "ledger-events"),
177
183
  ledgerAuthorityRootDir: path.join(scope, "ledger-authority"),
178
184
  witnessFilePath: path.join(scope, "witness", "checkpoint.json"),
179
185
  });
180
186
  if (initialize) {
181
- ensureDirectoryTreeDurable(
182
- fsImpl,
183
- path.dirname(result.witnessFilePath),
184
- );
187
+ ensureDirectoryTreeDurable(fsImpl, path.dirname(result.witnessFilePath));
185
188
  }
186
189
  return result;
187
190
  }
@@ -224,6 +227,7 @@ export function createAgentEvolutionRuntimeComposition({
224
227
  stateRootDir,
225
228
  authorities: authorityInput,
226
229
  evidenceTtlMs,
230
+ evidenceStorageLayout = "legacy",
227
231
  witnessId: witnessIdInput,
228
232
  clock: clockInput = Date.now,
229
233
  evidenceIdGenerator,
@@ -240,6 +244,7 @@ export function createAgentEvolutionRuntimeComposition({
240
244
  witnessMaximumBytes,
241
245
  ledgerV2: ledgerV2Input = null,
242
246
  } = {}) {
247
+ validateEvidenceStorageLayout(evidenceStorageLayout);
243
248
  const ledgerV2 =
244
249
  ledgerV2Input === null
245
250
  ? null
@@ -445,14 +450,6 @@ export function createAgentEvolutionRuntimeComposition({
445
450
  attestationVerifier: ports.attestationVerifier,
446
451
  now: () => new Date(clock()),
447
452
  });
448
- const evidenceAdapter = new EvolutionEvidenceArtifactAdapter({
449
- tenantId,
450
- audience,
451
- projector,
452
- bundleVerifier,
453
- artifactPorts,
454
- ...(evidenceTtlMs === undefined ? {} : { ttlMs: evidenceTtlMs }),
455
- });
456
453
  const ledgerArtifactResolver =
457
454
  artifactPorts.createEvolutionLedgerArtifactResolver({
458
455
  purpose: "evolution-ledger",
@@ -516,6 +513,46 @@ export function createAgentEvolutionRuntimeComposition({
516
513
  ledgerArtifactResolver,
517
514
  now: clock,
518
515
  });
516
+ const createEvidenceAdapter = (run, runStorage, mainPorts, emptyLayout) => {
517
+ const loaded = run.load();
518
+ const selectedLayout = evidenceStorageLayoutForRun(
519
+ loaded.events,
520
+ emptyLayout,
521
+ );
522
+ return {
523
+ selectedLayout,
524
+ adapter: new EvolutionEvidenceArtifactAdapter({
525
+ tenantId,
526
+ audience,
527
+ projector,
528
+ bundleVerifier,
529
+ ...(selectedLayout === "legacy"
530
+ ? { artifactPorts: mainPorts }
531
+ : {
532
+ typedArtifactPorts: createTypedEvidenceArtifactPorts({
533
+ rootDir: runStorage.evidenceArtifactDir,
534
+ tenantId,
535
+ audience,
536
+ artifactAuthority: {
537
+ envelopeSigner: ports.artifactEnvelopeSigner,
538
+ envelopeVerifier: ports.artifactEnvelopeVerifier,
539
+ currentAuthorityResolver:
540
+ ports.artifactCurrentAuthorityResolver,
541
+ },
542
+ now: clock,
543
+ existingRun: loaded.events.length > 0,
544
+ }),
545
+ }),
546
+ ...(evidenceTtlMs === undefined ? {} : { ttlMs: evidenceTtlMs }),
547
+ }),
548
+ };
549
+ };
550
+ const { adapter: evidenceAdapter, selectedLayout } = createEvidenceAdapter(
551
+ runAdapter,
552
+ storage,
553
+ artifactPorts,
554
+ evidenceStorageLayout,
555
+ );
519
556
  let wikiMaintenance = null;
520
557
  let wikiMaintainer = null;
521
558
  if (wikiConfiguration !== null) {
@@ -707,6 +744,7 @@ export function createAgentEvolutionRuntimeComposition({
707
744
  const evolutionIngress = createAgentEvolutionIngress({
708
745
  evidenceAdapter,
709
746
  runAdapter,
747
+ evidenceStorageLayout: selectedLayout,
710
748
  openCacheSourceRun: (input) => {
711
749
  const sourceRunId = identifier(input, "cache source runId");
712
750
  const sourceStorage = layout(
@@ -758,22 +796,22 @@ export function createAgentEvolutionRuntimeComposition({
758
796
  ...(lockTimeoutMs === undefined ? {} : { lockTimeoutMs }),
759
797
  ...(witnessMaximumBytes === undefined ? {} : { witnessMaximumBytes }),
760
798
  });
799
+ const sourceRunAdapter = new EvolutionRunLedgerAdapter({
800
+ descriptor: { ...runAdapter.descriptor, runId: sourceRunId },
801
+ artifactPorts: sourcePorts,
802
+ ledger: sourceBackend.ledger,
803
+ ledgerArtifactResolver: sourceResolver,
804
+ now: clock,
805
+ });
806
+ const sourceEvidence = createEvidenceAdapter(
807
+ sourceRunAdapter,
808
+ sourceStorage,
809
+ sourcePorts,
810
+ "legacy",
811
+ );
761
812
  return {
762
- runAdapter: new EvolutionRunLedgerAdapter({
763
- descriptor: { ...runAdapter.descriptor, runId: sourceRunId },
764
- artifactPorts: sourcePorts,
765
- ledger: sourceBackend.ledger,
766
- ledgerArtifactResolver: sourceResolver,
767
- now: clock,
768
- }),
769
- evidenceAdapter: new EvolutionEvidenceArtifactAdapter({
770
- tenantId,
771
- audience,
772
- projector,
773
- bundleVerifier,
774
- artifactPorts: sourcePorts,
775
- ...(evidenceTtlMs === undefined ? {} : { ttlMs: evidenceTtlMs }),
776
- }),
813
+ runAdapter: sourceRunAdapter,
814
+ evidenceAdapter: sourceEvidence.adapter,
777
815
  };
778
816
  },
779
817
  sourceEnvelopeAuthority: ports.sourceEnvelope,
@@ -21,6 +21,43 @@ export const AGENT_CODE_TEXT_RESULT_TOOLS = Object.freeze([
21
21
  "search_files",
22
22
  ]);
23
23
 
24
+ export const AGENT_FENCED_CODE_LANGUAGES = Object.freeze([
25
+ "javascript",
26
+ "js",
27
+ "typescript",
28
+ "ts",
29
+ "jsx",
30
+ "tsx",
31
+ "python",
32
+ "py",
33
+ ]);
34
+
35
+ function fencedCodeRanges(text, languages) {
36
+ const ranges = [];
37
+ const fences = /^ {0,3}(`{3,}|~{3,})([^\r\n]*)\r?$/gmu;
38
+ let opening = null;
39
+ for (const match of text.matchAll(fences)) {
40
+ const marker = match[1];
41
+ const info = match[2].trim().toLowerCase();
42
+ if (opening === null) {
43
+ opening = {
44
+ marker,
45
+ supported: languages.includes(info),
46
+ start: match.index + match[0].length,
47
+ };
48
+ } else if (
49
+ marker[0] === opening.marker[0] &&
50
+ marker.length >= opening.marker.length &&
51
+ info === ""
52
+ ) {
53
+ if (opening.supported) ranges.push([opening.start, match.index]);
54
+ opening = null;
55
+ }
56
+ }
57
+ // Unclosed/unknown fences never acquire code semantics.
58
+ return ranges;
59
+ }
60
+
24
61
  /* eslint-disable no-control-regex -- Quoted literals exclude unescaped control bytes. */
25
62
  const JSON_STRING_PREFIX =
26
63
  /^"(?:[^"\\\x00-\x1f]|\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4}))*"/u;
@@ -28,6 +65,28 @@ const CODE_DOUBLE_QUOTED_PREFIX = /^"(?:[^"\\\x00-\x1f]|\\[\s\S])*"/u;
28
65
  const CODE_SINGLE_QUOTED_PREFIX = /^'(?:[^'\\\x00-\x1f]|\\[\s\S])*'/u;
29
66
  /* eslint-enable no-control-regex */
30
67
 
68
+ // Only closed, bounded display formats qualify. Never accept quotes, escapes,
69
+ // commas or nested containers here. Exponent-shaped hashes remain JSON numbers
70
+ // so non-finite/unsafe numeric values cannot evade the existing budget checks.
71
+ const DISPLAY_BRACKET_MARKER = new RegExp(
72
+ "^\\[(?:" +
73
+ "\\d+[ \\t]+\\d+(?:[ \\t]+\\d+)*|" +
74
+ "\\d+[ \\t]*\\/[ \\t]*\\d+(?:[ \\t]+\\d{1,3}%)?|" +
75
+ "\\d+[ \\t]+of[ \\t]+\\d+|" +
76
+ "\\d{1,3}(?:\\.\\d+)?%|" +
77
+ "\\d{4}-\\d{2}-\\d{2}(?:[ T]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})?)?|" +
78
+ "\\d{1,2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?|" +
79
+ "v?\\d+\\.\\d+\\.\\d+|" +
80
+ "[0-9]-[0-9]|" +
81
+ "\\[[A-Za-z][A-Za-z0-9_./ #+-]*\\]|" +
82
+ "[ \\t]+\\d+[ \\t]+-(?:eq|ne|lt|le|gt|ge)[ \\t]+\\d+[ \\t]+|" +
83
+ "\\[[ \\t]+-[defrswx][ \\t]+[A-Za-z0-9_./-]+[ \\t]+\\]|" +
84
+ "(?!\\d+[eE]\\d+\\])(?=[0-9a-f]{7,64}\\])(?=[0-9a-f]*[a-f])[0-9][0-9a-f]+|" +
85
+ "[0-9a-f]{7,64}\\.\\.[0-9a-f]{7,64}" +
86
+ ")\\]",
87
+ "u",
88
+ );
89
+
31
90
  export function isAgentToolArgumentsPath(path) {
32
91
  return (
33
92
  path.length === 6 &&
@@ -255,6 +314,14 @@ export function createEvidenceJsonTextBoundary(
255
314
  );
256
315
  }
257
316
  index += token.length;
317
+ // A primitive prefix is not a complete source expression. In particular,
318
+ // redacting only `true` from trueSecretValue would expose the suffix of
319
+ // a sensitive quoted assignment. Historical policies retain their parser.
320
+ if (
321
+ policy.proseQuotedPairPolicy &&
322
+ /[\p{ID_Continue}$\\]/u.test(text[index] ?? "")
323
+ )
324
+ return parserReject("code-expression");
258
325
  const parsed = JSON.parse(token);
259
326
  if (
260
327
  typeof parsed === "number" &&
@@ -271,10 +338,35 @@ export function createEvidenceJsonTextBoundary(
271
338
  };
272
339
  };
273
340
 
274
- return (
341
+ return function scanText(
275
342
  text,
276
- { layer = 0, objectOnly = false, codeText = false, visit, plain, equal },
277
- ) => {
343
+ {
344
+ layer = 0,
345
+ objectOnly = false,
346
+ codeText = false,
347
+ fencedCodeLanguages = null,
348
+ visit,
349
+ plain,
350
+ equal,
351
+ },
352
+ ) {
353
+ if (!objectOnly && !codeText && fencedCodeLanguages) {
354
+ const ranges = fencedCodeRanges(text, fencedCodeLanguages);
355
+ if (ranges.length > 0) {
356
+ let output = "";
357
+ let offset = 0;
358
+ const options = { layer, visit, plain, equal };
359
+ for (const [start, end] of ranges) {
360
+ output += scanText(text.slice(offset, start), options);
361
+ output += scanText(text.slice(start, end), {
362
+ ...options,
363
+ codeText: true,
364
+ });
365
+ offset = end;
366
+ }
367
+ return output + scanText(text.slice(offset), options);
368
+ }
369
+ }
278
370
  bytes += Buffer.byteLength(text, "utf8");
279
371
  if (bytes > policy.maxDecodedBytes)
280
372
  reject("Agent model JSON text exceeds its budget");
@@ -314,6 +406,26 @@ export function createEvidenceJsonTextBoundary(
314
406
  char === "{" || char === "["
315
407
  ? text.slice(index + 1).match(/^\s*(.)/u)?.[1]
316
408
  : undefined;
409
+ // Human-readable status markers are not JSON arrays. Only complete,
410
+ // bounded numeric formats qualify; quotes, escapes, commas and nested
411
+ // containers must still go through strict parsing. Leave these spans in
412
+ // plain text so the ordinary credential/PII sanitizer still sees them.
413
+ if (
414
+ char === "[" &&
415
+ policy.bracketMarkerPolicy === "bounded-display-markers-v1"
416
+ ) {
417
+ // ANSI CSI controls in terminal output start with ESC followed by [.
418
+ // Limit recognition to numeric parameters and a final control letter.
419
+ const marker =
420
+ (text[index - 1] === "\x1b"
421
+ ? /^\[[0-9;:?]{0,64}[A-Za-z]/u.exec(text.slice(index, index + 128))
422
+ : null) ??
423
+ DISPLAY_BRACKET_MARKER.exec(text.slice(index, index + 128));
424
+ if (marker) {
425
+ index += marker[0].length;
426
+ continue;
427
+ }
428
+ }
317
429
  const container =
318
430
  (char === "{" &&
319
431
  (next === '"' || next === "}" || next === "'" || next === "\\")) ||
@@ -405,7 +517,7 @@ export function createEvidenceJsonTextBoundary(
405
517
  const entryEnd = rhs.end();
406
518
  rhs.whitespace();
407
519
  if (
408
- codeText &&
520
+ (codeText || policy.proseQuotedPairPolicy) &&
409
521
  /^[+\-*/%?.([<>&|^=!"']/u.test(text[rhs.end()] ?? "")
410
522
  )
411
523
  requireSafeExpressionKey();
@@ -417,10 +529,14 @@ export function createEvidenceJsonTextBoundary(
417
529
  } catch (error) {
418
530
  const kind = error?.[PARSE_FAILURE];
419
531
  if (kind === undefined) throw error;
420
- if (!codeText || kind !== "code-expression")
532
+ if (
533
+ (!codeText && !policy.proseQuotedPairPolicy) ||
534
+ kind !== "code-expression"
535
+ )
421
536
  rejectParseFailure(error);
422
- // In a run_code source/result string, a quoted key followed by a
423
- // Python/JavaScript expression is code, not a broken JSON pair.
537
+ // A standalone prose label or a source key followed by an
538
+ // expression is not a broken JSON container. Sensitive decoded
539
+ // keys still fail closed before scanning resumes.
424
540
  // Resume the scan at the next literal so any string value still
425
541
  // receives key-aware or ordinary secret scanning.
426
542
  requireSafeExpressionKey();
@@ -447,7 +563,7 @@ export function createEvidenceJsonTextBoundary(
447
563
  }
448
564
  const safe = transform(parsed);
449
565
  if (
450
- codeText &&
566
+ (codeText || policy.proseQuotedPairPolicy) &&
451
567
  assignmentKey &&
452
568
  !equal(parsed, safe) &&
453
569
  /^\s*[+\-*/%?.([<>&|^=!"']/u.test(text.slice(end))