chainlesschain 0.166.114 → 0.166.117

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 (183) hide show
  1. package/README.md +23 -3
  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/assets/{AIOps-CVac3rIN.js → AIOps-C9tSCImB.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-ChM9GxMl.js → ActionButton-CXKExmT_.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-CV5399Js.js → Analytics-DE8WBWHo.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-CuonVXhg.js → AppLayout-BdpwNM08.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-D9qKfgb6.js → Artifacts-94TQpe38.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-DUvLysQc.js → Audit-DdDEv5Ej.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-DfKohQeT.js → BackgroundAgents-TU9nlV7j.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CohoEI8R.js → Backup-C0GPoUUq.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-BwdoA3xv.js → BaseInput-iyrVn2I6.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-DRkuz1d2.js → Chat-Dqo8spwp.js} +5 -5
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-v3pzeHNC.js → ChatBubbleRenderer-CDqHSzc4.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-B-GNGpKv.js → Checkbox-BF_XJvQi.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-Bs2lAsoZ.js → Codegen-5tUWoOa9.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-Bk1mJKDZ.js → Col-BQxSeRvG.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-B_FDSCOo.js → Community-B2Ra7w-J.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-No1qSLLH.js → Compact-GjxJwlr0.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-CcrG43-d.js → Compliance-BTUP6RTA.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-DLkj2ywK.js → Cowork-DiwfL16s.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-sQHELVEz.js → Cron-1ceOo5ux.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-CRTZ3GZg.js → Crosschain-aHYxLu0u.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Ds2bjvxE.js → DID-eeTjzOLV.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-DOONG2NX.js → Dashboard-CT3fpkvX.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-Br3PNA2L.js → Dropdown-B3UpOy-N.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-DT6UUqjZ.js → EmailListRenderer-Ba2Rvoru.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-BSLuEjlT.js → EvolutionSettings-CGtn93on.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-2aEM9avE.js → FamilyGuardDashboard-C0IUdHQt.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-D3B0tlQU.js → Federation-1QIDwemn.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DTVkVVQP.js → FormItemContext-NGbSsdje.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-BgFEgP4y.js → GenericCardRenderer-DzVZc2Ba.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-3t71m6Xd.js → Git-B_1TDbSr.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-DS7WvlaX.js → Governance-BDl_ySyI.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-CeT-xWrI.js → Inference-CAp-wcCK.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-BQzMncp5.js → KnowledgeGraph-DRjRcxul.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-CC5DOKhV.js → Logs-qSuwMwk7.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-BgnYTegK.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-CpYDyy3o.js → Marketplace-DfiXL6UO.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-DRdAZPMr.js → McpTools-D9rIvFjN.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-CBLRr-l8.js → Memory-D-p7yDzj.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-Bnj9veNN.js → MobileBridge-D0XT5B6m.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-lYFPD24p.js → MobileProjects-CL9cHVIk.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-BMU_C9vD.js → Mtc-CME4kDmC.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-DQV-zumU.js → MtcAudit-BjGOXFnn.js} +4 -4
  47. package/src/assets/web-panel/assets/{Multisig-LpqpqV1d.js → Multisig-CSzc53e0.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-C4OMklSO.js → NLProgramming-BYjZnaH8.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-Cg0QCinP.js → Notes-BUPOYE0C.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-amoZoMna.js → NotificationSettings-BK8YADru.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-CxNvhf5n.js → OrderTableRenderer-B5F8bnWt.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-BetJmyX5.js → Organization-B0WNlhMd.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-DbeoPtVD.js → Overflow-CED6ZO87.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-B5sitQWT.js → P2P-CBdc8fkP.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-AP9SG2ic.js → PdhVaultBrowser-DuauYi03.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-Bc7fgNKx.js → Permissions-kfgY0ukN.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-BwdGMcWZ.js → PersonalDataHub-CDY3I1Do.js} +3 -3
  58. package/src/assets/web-panel/assets/{Pipeline-DVuJA9BA.js → Pipeline-B9x0wKSK.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-BGzwOYRG.js → Privacy-Cqhcr_Qv.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BpHcC5br.js → ProjectInit-CFhoyo57.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-BI5G5gsc.js → ProjectSettings-DCBjV2Hm.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-C51w0LnM.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-CRHCcLFS.js → Providers-BuCIWZY1.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-qSeg5xag.js → QrScannerModal-CEXNL5CB.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-CA8oSceR.js → QuickAsk-DgK8aLAU.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-DMvPodNo.js → Recommend-BxEYQRAs.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-BWsapEyN.js → RemoteSession-PRsxNsYU.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-BZJETVmC.js → Reputation-S-tQH70r.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-CUE1OSMp.js → Row-U1Ws3CJl.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-DvMazcUD.js → RssFeed-BftCyeii.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-e1c-NXWv.js → Search-o4gK0cPy.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-Dc9GZsx2.js → Security-U2ZRpsbA.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-AFz6ke-4.js → Services-D0tRjWl_.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-gow6MYcY.js → Skeleton-DzZ8TFsB.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-h6AxYjUP.js → Skills-B1cC7rju.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-Cr4CkJ0O.js → Sla-CtIHOR0J.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Bd5yuBtH.js → SpeechSettings-CD8ZlQpW.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-1t4QZMLW.js → SyncSettings-BilFQWQk.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-u-DrjLRM.js → Tasks-Bmg2qLXl.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-C0yLUC9b.js → Templates-D6yofyC1.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-ds1YT5iS.js → Tenant-BzttD-Gp.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-BoIskW2m.js → Terminal-DyRXeYuH.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-BkdVK24X.js → TimelineRenderer-Bdmkx-Tr.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-djyDgmfk.js → Tokens-10RcVAVJ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-CYefDS3H.js → Trigger-CKV-9DdV.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-CE_dbep_.js → Trust-BEc-sW1q.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-DwVs_fey.js → UkeySign-BSql8piX.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-BTUqMHAV.js → VideoEditing-C4OH2f4s.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-Bu_E7Rrc.js → Wallet-BSSvWKmv.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DTbre5AB.js → WebAuthn-LxIzXo9d.js} +5 -5
  91. package/src/assets/web-panel/assets/{WorkflowEditor-BsBzd3gt.js → WorkflowEditor-DWwdfcte.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-tdifQQvu.js → chat-DWxYYLD5.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-D3SNW2Vx.js → colors-BxEcQqtg.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-UBlCjrAW.js → compact-item-DNbezFOJ.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-DKsnEnFt.js → createContext-DaJXc5nx.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-CRyXoGo9.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-BkeySXWe.js → hasIn-BMrw3sg3.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CXkn77ZN.js → index-2c0bL3SE.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DtxUSZSS.js → index-6W0V5G2t.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DFMrIEIt.js → index-7zzLqfvI.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-D9m1HQzl.js → index-B9b6UaZl.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-bOIcWW8F.js → index-BAV07GDK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BV2zQteq.js → index-BCCd8Rj6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlO6HdD9.js → index-BNZDGV2q.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DfrW-iVs.js → index-BR4nWOmR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-PWScrdlX.js → index-B_b9_6wQ.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-SpQ26gD6.js → index-BaByEdBj.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D5Umd_Z-.js → index-BcuytRTa.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dg8-Duze.js → index-Bs2QyMPQ.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DlEIp1gJ.js → index-Bum_aSyd.js} +1 -1
  111. package/src/assets/web-panel/assets/index-C-W0Ntk_.js +1 -0
  112. package/src/assets/web-panel/assets/{index-D6es7MSG.js → index-C25ig6EV.js} +1 -1
  113. package/src/assets/web-panel/assets/index-CCk_qhBx.js +1 -0
  114. package/src/assets/web-panel/assets/{index-Bw0k3VgD.js → index-CL-36X3A.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DYFjOyTH.js → index-Ca2hwPMo.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BVvz3ntc.js → index-CaxzeSIM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-D9um0YVl.js → index-CiEdfbB9.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DugZRCiI.js → index-Cm9DYzhC.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-pgGKNvmm.js → index-CpwgF7vh.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-P-ailFQY.js → index-DEvdSKT4.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C9ImNw5O.js → index-DL0k35f0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DSCiZJXb.js → index-DarVV5yG.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BIeFLaMV.js → index-DpSp97J8.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-C-KTHSUE.js → index-Dr18agDl.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DiDgwRty.js → index-Dwg38xTG.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D93iSDUw.js → index-DzZHBzCb.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-6Nvzgytx.js → index-IX0g0s2t.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CG_b1xyX.js → index-LRb0QX6S.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Cuz2QCFT.js → index-NEW5EN2w.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CUjyL0ZS.js → index-T4T84G6X.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DdyTv6yc.js → index-VD-dTYQE.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BEiHKah0.js → index-cCqCEa9X.js} +3 -3
  133. package/src/assets/web-panel/assets/{index-DD6dD3LV.js → index-cKBpcInt.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-TLBpgN30.js → index-hPy_f08G.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DSe_7MSy.js → index-pzYLf5qu.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CZ6JUooI.js → index-rFaCnjXi.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-CVCXG2Hv.js → initDefaultProps-BWVLgWhF.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-Dv00jw87.js → motion-KJZ1xfDE.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-BFz51iHf.js → move-DGqU6H7M.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-BK2woIDE.js → mtc-parser-Dg0xkI5J.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-D2X_Gwoo.js → omit-B6RnMJAQ.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-CLmFyJ1q.js → pickAttrs-Ctw2i3jb.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-CCzPddQm.js → placementArrow-D3F3x8Jm.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-hcm_Zlxd.js → responsiveObserve-W6eHE0nM.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-BxfZEFVp.js → slide-BlFf8sY5.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-kzB9pT-t.js → statusUtils-Cd6LMwmy.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-CDFd7YCI.js → styleChecker-Bg5-U4Y1.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-Czl7NNhS.js → useFlexGapSupport-DTkLS3tn.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-y0SAzirG.js → useFs-CwYGzoBt.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BvdPznrA.js → usePersonalDataHub-PCgFQXZP.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-D8mNbyZ7.js → vnode-CJM7MkrB.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-_hun6w9W.js → zoom-CP0mgaFi.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/commands/compact.js +7 -2
  155. package/src/data/changelog.json +28 -2
  156. package/src/harness/jsonl-session-store.js +78 -73
  157. package/src/harness/prompt-compressor.js +57 -3
  158. package/src/lib/context-memory-kernel/context-image-transport.js +248 -0
  159. package/src/lib/context-memory-kernel/jsonl-session-context-port.js +4 -1
  160. package/src/lib/context-memory-kernel/live-compaction.js +40 -10
  161. package/src/lib/context-memory-kernel/message-adapter.js +80 -10
  162. package/src/lib/context-memory-kernel/message-bundle-store.js +1140 -0
  163. package/src/lib/context-memory-kernel/provider-context.js +23 -8
  164. package/src/lib/evolution/agent-evolution-ingress.js +62 -12
  165. package/src/lib/evolution/agent-model-projection.js +90 -18
  166. package/src/lib/evolution/evidence-json-text.js +279 -42
  167. package/src/lib/evolution/evolution-evidence-projector.js +169 -26
  168. package/src/lib/evolution/governed-model-turn.js +2 -2
  169. package/src/lib/evolution/local-ui-evolution-deployment.js +2 -2
  170. package/src/lib/evolution/local-ui-evolution-runtime.js +4 -4
  171. package/src/lib/evolution/opaque-hex-id.js +8 -0
  172. package/src/lib/image-token-estimate.js +59 -0
  173. package/src/lib/read-file-loop-guard.js +9 -6
  174. package/src/lib/session-history-origins.js +25 -5
  175. package/src/lib/session-transcript-history.js +5 -1
  176. package/src/lib/tool-result-preview.js +276 -0
  177. package/src/runtime/agent-core.js +85 -78
  178. package/src/runtime/headless-stream.js +32 -17
  179. package/src/assets/web-panel/assets/MarkdownRenderer-5jYM4kYG.js +0 -1
  180. package/src/assets/web-panel/assets/Projects-Ce92-l1V.js +0 -1
  181. package/src/assets/web-panel/assets/devWarning-BBnVfJHh.js +0 -1
  182. package/src/assets/web-panel/assets/index-B2w_3mtS.js +0 -1
  183. package/src/assets/web-panel/assets/index-BoHurydC.js +0 -1
@@ -11,12 +11,15 @@
11
11
 
12
12
  import { createHash, randomBytes } from "node:crypto";
13
13
  import {
14
+ AGENT_MODEL_REQUEST_LIMITS,
15
+ AGENT_MODEL_IMAGE_LIMITS,
14
16
  projectAgentOpaqueTransportBlock,
15
17
  snapshotAgentModelRequest,
16
18
  } from "./agent-model-projection.js";
17
19
  import { snapshotSkillDecisionPayload } from "./skill-decision-projection.js";
18
20
  import {
19
21
  createEvidenceJsonTextBoundary,
22
+ AGENT_CODE_TEXT_RESULT_TOOLS,
20
23
  EVIDENCE_JSON_POLICY,
21
24
  isAgentToolArgumentsPath,
22
25
  } from "./evidence-json-text.js";
@@ -93,6 +96,8 @@ const TRUSTED_PAYLOAD_COMMITMENT_PURPOSE =
93
96
  const LEGACY_LOCAL_UI_TENANT_ID_PATTERN = /^local-ui-[a-f0-9]{32}$/u;
94
97
  const FORMATTED_LOCAL_UI_TENANT_ID_PATTERN =
95
98
  /^local-ui-(?:[a-f0-9]{8}-){3}[a-f0-9]{8}$/u;
99
+ const ENCODED_LOCAL_UI_TENANT_ID_PATTERN =
100
+ /^local-ui-(?:[a-p]{8}-){3}[a-p]{8}$/u;
96
101
 
97
102
  const INPUT_KEYS = new Set(["sourceEnvelope", "payload"]);
98
103
  const SOURCE_VERIFICATION_KEYS = new Set([
@@ -513,13 +518,41 @@ export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V2_DIGEST = `sha256:${crea
513
518
  "utf8",
514
519
  )
515
520
  .digest("hex")}`;
516
- const AGENT_MODEL_RULESET = Object.freeze({
521
+ const AGENT_MODEL_RULESET_V3 = Object.freeze({
517
522
  ...AGENT_MODEL_RULESET_V2,
518
523
  schema: "chainlesschain.evolution-agent-model-projection-rules/v3",
519
524
  version: 3,
520
525
  opaqueTransportPolicy:
521
526
  "digest-bound-base64-image-and-signed-thinking-no-plaintext-projection-v1",
522
527
  });
528
+ export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V3_DIGEST = `sha256:${createHash(
529
+ "sha256",
530
+ )
531
+ .update(
532
+ `${AGENT_MODEL_RULESET_V3.schema}\0${JSON.stringify(AGENT_MODEL_RULESET_V3)}`,
533
+ "utf8",
534
+ )
535
+ .digest("hex")}`;
536
+ const AGENT_MODEL_RULESET = Object.freeze({
537
+ ...AGENT_MODEL_RULESET_V3,
538
+ schema: "chainlesschain.evolution-agent-model-projection-rules/v4",
539
+ version: 4,
540
+ codeTextPolicy: "paired-code-source-and-result-literal-scan-v1",
541
+ codeTextResultTools: AGENT_CODE_TEXT_RESULT_TOOLS,
542
+ // ArtifactPorts caps the complete durable record at 1 MiB. Reserve room for
543
+ // provenance/findings and the record envelope; no storage limit is relaxed.
544
+ maxModelBytes: AGENT_MODEL_REQUEST_LIMITS.maxBytes - 128 * 1024,
545
+ maxStringChars: AGENT_MODEL_REQUEST_LIMITS.maxBytes - 128 * 1024,
546
+ maxNodes: AGENT_MODEL_REQUEST_LIMITS.maxNodes,
547
+ maxDepth: AGENT_MODEL_REQUEST_LIMITS.maxDepth,
548
+ decodedDepthPolicy: "independent-bounded-json-depth-v1",
549
+ imageBudget: AGENT_MODEL_IMAGE_LIMITS,
550
+ jsonTextPolicy: Object.freeze({
551
+ ...EVIDENCE_JSON_POLICY,
552
+ version: 2,
553
+ maxNodes: AGENT_MODEL_REQUEST_LIMITS.maxNodes,
554
+ }),
555
+ });
523
556
  export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST = `sha256:${createHash(
524
557
  "sha256",
525
558
  )
@@ -550,6 +583,7 @@ const SUPPORTED_RULESETS = new Map([
550
583
  [RAW_RULESET_V3_DIGEST, RAW_RULESET_V3],
551
584
  [AGENT_MODEL_RULESET_V1_DIGEST, AGENT_MODEL_RULESET_V1],
552
585
  [EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V2_DIGEST, AGENT_MODEL_RULESET_V2],
586
+ [EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_V3_DIGEST, AGENT_MODEL_RULESET_V3],
553
587
  [EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST, AGENT_MODEL_RULESET],
554
588
  ]);
555
589
 
@@ -625,6 +659,7 @@ const AGENT_VALUE_PATTERNS = Object.freeze([
625
659
  function valuePatterns(ruleset) {
626
660
  return ruleset === SKILL_DECISION_RULESET ||
627
661
  ruleset === AGENT_MODEL_RULESET ||
662
+ ruleset === AGENT_MODEL_RULESET_V3 ||
628
663
  ruleset === AGENT_MODEL_RULESET_V2 ||
629
664
  ruleset === AGENT_MODEL_RULESET_V1
630
665
  ? AGENT_VALUE_PATTERNS
@@ -688,7 +723,12 @@ function assertExactRecord(
688
723
  }
689
724
  }
690
725
 
691
- function canonicalJson(value, seen = new Set(), depth = 0) {
726
+ function canonicalJson(
727
+ value,
728
+ seen = new Set(),
729
+ depth = 0,
730
+ maximumBytes = 2 * 1024 * 1024,
731
+ ) {
692
732
  if (depth > 32) {
693
733
  throw projectionError(
694
734
  EVOLUTION_PROJECTION_INVALID_CODE,
@@ -710,10 +750,12 @@ function canonicalJson(value, seen = new Set(), depth = 0) {
710
750
  output = JSON.stringify(value);
711
751
  }
712
752
  if (output !== undefined) {
713
- if (Buffer.byteLength(output, "utf8") > 2 * 1024 * 1024) {
753
+ if (Buffer.byteLength(output, "utf8") > maximumBytes) {
714
754
  throw projectionError(
715
755
  EVOLUTION_PROJECTION_INVALID_CODE,
716
- "projection exceeds the 2 MiB admission limit",
756
+ maximumBytes === 2 * 1024 * 1024
757
+ ? "projection exceeds the 2 MiB admission limit"
758
+ : "Agent image transport exceeds its canonical byte budget",
717
759
  );
718
760
  }
719
761
  return output;
@@ -755,7 +797,9 @@ function canonicalJson(value, seen = new Set(), depth = 0) {
755
797
  "projection arrays must use enumerable own data entries",
756
798
  );
757
799
  }
758
- entries.push(canonicalJson(descriptor.value, seen, depth + 1));
800
+ entries.push(
801
+ canonicalJson(descriptor.value, seen, depth + 1, maximumBytes),
802
+ );
759
803
  }
760
804
  output = `[${entries.join(",")}]`;
761
805
  } else {
@@ -782,24 +826,29 @@ function canonicalJson(value, seen = new Set(), depth = 0) {
782
826
  );
783
827
  }
784
828
  fields.push(
785
- `${JSON.stringify(key)}:${canonicalJson(descriptor.value, seen, depth + 1)}`,
829
+ `${JSON.stringify(key)}:${canonicalJson(descriptor.value, seen, depth + 1, maximumBytes)}`,
786
830
  );
787
831
  }
788
832
  output = `{${fields.join(",")}}`;
789
833
  }
790
834
  seen.delete(value);
791
- if (Buffer.byteLength(output, "utf8") > 2 * 1024 * 1024) {
835
+ if (Buffer.byteLength(output, "utf8") > maximumBytes) {
792
836
  throw projectionError(
793
837
  EVOLUTION_PROJECTION_INVALID_CODE,
794
- "projection exceeds the 2 MiB admission limit",
838
+ maximumBytes === 2 * 1024 * 1024
839
+ ? "projection exceeds the 2 MiB admission limit"
840
+ : "Agent image transport exceeds its canonical byte budget",
795
841
  );
796
842
  }
797
843
  return output;
798
844
  }
799
845
 
800
- function digest(value, domain) {
846
+ function digest(value, domain, maximumBytes) {
801
847
  return `sha256:${createHash("sha256")
802
- .update(`${domain}\0${canonicalJson(value)}`, "utf8")
848
+ .update(
849
+ `${domain}\0${canonicalJson(value, new Set(), 0, maximumBytes)}`,
850
+ "utf8",
851
+ )
803
852
  .digest("hex")}`;
804
853
  }
805
854
 
@@ -877,17 +926,32 @@ function normalizeId(value, label, maximum = 256) {
877
926
  return value;
878
927
  }
879
928
 
880
- function normalizeTenantId(value, label) {
881
- if (
929
+ function isLocalUiTenantId(value) {
930
+ return (
882
931
  typeof value === "string" &&
883
932
  (LEGACY_LOCAL_UI_TENANT_ID_PATTERN.test(value) ||
884
- FORMATTED_LOCAL_UI_TENANT_ID_PATTERN.test(value))
885
- ) {
886
- return value;
887
- }
933
+ FORMATTED_LOCAL_UI_TENANT_ID_PATTERN.test(value) ||
934
+ ENCODED_LOCAL_UI_TENANT_ID_PATTERN.test(value))
935
+ );
936
+ }
937
+
938
+ function normalizeTenantId(value, label) {
939
+ if (isLocalUiTenantId(value)) return value;
888
940
  return normalizeId(value, label);
889
941
  }
890
942
 
943
+ function normalizeLocalAttestationId(value, label, tenantId, suffix) {
944
+ // A bound local identity is already admitted as tenant metadata. Inspect its
945
+ // fixed reference structure without treating its random digits as content;
946
+ // preserve the original signed bytes and scan every other identifier fully.
947
+ const scanValue =
948
+ isLocalUiTenantId(tenantId) && value === `local-ui-${tenantId}-${suffix}`
949
+ ? `local-ui-local-identity-${suffix}`
950
+ : value;
951
+ normalizeId(scanValue, label);
952
+ return value;
953
+ }
954
+
891
955
  function normalizeDigest(
892
956
  value,
893
957
  label,
@@ -946,7 +1010,6 @@ function normalizeOpaqueUri(value, { label, schemes, tenantId }) {
946
1010
  `${label} is invalid`,
947
1011
  );
948
1012
  }
949
- assertNoKnownSecrets(value);
950
1013
  if (
951
1014
  /[?#@\\%\s]/u.test(value) ||
952
1015
  hasUriControlOrWhitespace(value) ||
@@ -985,7 +1048,11 @@ function normalizeOpaqueUri(value, { label, schemes, tenantId }) {
985
1048
  parsed.port ||
986
1049
  parsed.search ||
987
1050
  parsed.hash ||
988
- parsed.hostname !== formatLocalUiTenantReferenceId(tenantId) ||
1051
+ (parsed.hostname !== formatLocalUiTenantReferenceId(tenantId) &&
1052
+ !(
1053
+ LEGACY_LOCAL_UI_TENANT_ID_PATTERN.test(tenantId) &&
1054
+ parsed.hostname === tenantId
1055
+ )) ||
989
1056
  !decodedPath.startsWith("/") ||
990
1057
  decodedPath
991
1058
  .split("/")
@@ -996,6 +1063,14 @@ function normalizeOpaqueUri(value, { label, schemes, tenantId }) {
996
1063
  `${label} is outside the tenant-scoped URI policy`,
997
1064
  );
998
1065
  }
1066
+ // Only the exact, bound local host is identity metadata. Keep the complete
1067
+ // original path in the scan, and never rewrite persisted URI/digest bytes.
1068
+ const localPrefix = `${scheme}://${parsed.hostname}`;
1069
+ const scanValue =
1070
+ isLocalUiTenantId(tenantId) && value.startsWith(`${localPrefix}/`)
1071
+ ? `${scheme}://local-ui-identity${value.slice(localPrefix.length)}`
1072
+ : value;
1073
+ assertNoKnownSecrets(scanValue);
999
1074
  return value;
1000
1075
  }
1001
1076
 
@@ -1268,6 +1343,43 @@ function sanitizeString(value, path, findings, injections, ruleset, patterns) {
1268
1343
  return output;
1269
1344
  }
1270
1345
 
1346
+ function isAgentCodeTextPath(payload, path, resultToolNames) {
1347
+ if (
1348
+ path[0] !== "messages" ||
1349
+ !/^\d+$/u.test(path[1] ?? "") ||
1350
+ !Array.isArray(payload?.messages)
1351
+ ) {
1352
+ return false;
1353
+ }
1354
+ const message = payload.messages[Number(path[1])];
1355
+ if (!message || typeof message !== "object") return false;
1356
+ if (
1357
+ path[2] === "tool_calls" &&
1358
+ /^\d+$/u.test(path[3] ?? "") &&
1359
+ path[4] === "function" &&
1360
+ path[5] === "arguments"
1361
+ ) {
1362
+ const call = message.tool_calls?.[Number(path[3])];
1363
+ const fields = path.slice(6);
1364
+ if (fields[0] === "$json") fields.shift();
1365
+ return call?.function?.name === "run_code" && fields[0] === "code";
1366
+ }
1367
+ if (path[2] === "content" && message.role === "tool") {
1368
+ const callId = message.tool_call_id;
1369
+ return payload.messages.some(
1370
+ (candidate) =>
1371
+ candidate?.role === "assistant" &&
1372
+ Array.isArray(candidate.tool_calls) &&
1373
+ candidate.tool_calls.some(
1374
+ (call) =>
1375
+ call?.id === callId &&
1376
+ resultToolNames.includes(call?.function?.name),
1377
+ ),
1378
+ );
1379
+ }
1380
+ return false;
1381
+ }
1382
+
1271
1383
  function sanitizePayload(
1272
1384
  payload,
1273
1385
  ruleset = RULESET,
@@ -1278,8 +1390,10 @@ function sanitizePayload(
1278
1390
  let nodes = 0;
1279
1391
  let truncated = false;
1280
1392
  const jsonText = ruleset.jsonTextPolicy
1281
- ? createEvidenceJsonTextBoundary((message) =>
1282
- projectionError(EVOLUTION_PROJECTION_INVALID_CODE, message),
1393
+ ? createEvidenceJsonTextBoundary(
1394
+ (message) =>
1395
+ projectionError(EVOLUTION_PROJECTION_INVALID_CODE, message),
1396
+ ruleset.jsonTextPolicy,
1283
1397
  )
1284
1398
  : null;
1285
1399
  const visit = (value, path, depth, layer = 0) => {
@@ -1295,7 +1409,7 @@ function sanitizePayload(
1295
1409
  }
1296
1410
  const toolArguments = jsonText && isAgentToolArgumentsPath(path);
1297
1411
  const opaqueTransportBlock =
1298
- ruleset === AGENT_MODEL_RULESET
1412
+ ruleset === AGENT_MODEL_RULESET || ruleset === AGENT_MODEL_RULESET_V3
1299
1413
  ? projectAgentOpaqueTransportBlock(value, path.join("."))
1300
1414
  : null;
1301
1415
  if (opaqueTransportBlock !== null) return opaqueTransportBlock;
@@ -1309,11 +1423,25 @@ function sanitizePayload(
1309
1423
  const plain = (text) =>
1310
1424
  sanitizeString(text, path, findings, injections, ruleset, patterns);
1311
1425
  if (!jsonText) return plain(value);
1426
+ const codeText =
1427
+ ruleset.codeTextPolicy ===
1428
+ "paired-code-source-and-result-literal-scan-v1" &&
1429
+ isAgentCodeTextPath(payload, path, ruleset.codeTextResultTools);
1312
1430
  const output = jsonText(value, {
1313
1431
  layer,
1314
1432
  objectOnly: toolArguments,
1433
+ codeText,
1315
1434
  visit: (decoded, nextLayer) =>
1316
- visit(decoded, [...path, "$json"], depth + 1, nextLayer),
1435
+ visit(
1436
+ decoded,
1437
+ [...path, "$json"],
1438
+ // Serialized JSON has its own unchanged depth/layer limits. Its
1439
+ // traversal must not charge protocol nesting as decoded depth.
1440
+ ruleset.decodedDepthPolicy === "independent-bounded-json-depth-v1"
1441
+ ? 0
1442
+ : depth + 1,
1443
+ nextLayer,
1444
+ ),
1317
1445
  plain,
1318
1446
  equal: (left, right) => canonicalJson(left) === canonicalJson(right),
1319
1447
  });
@@ -2291,8 +2419,18 @@ function validateAttestation(value, receipt) {
2291
2419
  "projection attestation is invalid or unbound",
2292
2420
  );
2293
2421
  }
2294
- normalizeId(value.keyId, "attestation keyId");
2295
- normalizeId(value.issuer, "attestation issuer");
2422
+ normalizeLocalAttestationId(
2423
+ value.keyId,
2424
+ "attestation keyId",
2425
+ receipt.tenantId,
2426
+ "attestation",
2427
+ );
2428
+ normalizeLocalAttestationId(
2429
+ value.issuer,
2430
+ "attestation issuer",
2431
+ receipt.tenantId,
2432
+ "evolution-projector",
2433
+ );
2296
2434
  return deepFreeze(cloneCanonical(value));
2297
2435
  }
2298
2436
 
@@ -3007,7 +3145,7 @@ function validateBundleIntegrity(bundle) {
3007
3145
  validateRedactionSummary(model.redactionSummary);
3008
3146
  if (
3009
3147
  !Array.isArray(model.injectionFindings) ||
3010
- model.injectionFindings.length > RULESET.maxNodes * 2
3148
+ model.injectionFindings.length > ruleset.maxNodes * 2
3011
3149
  ) {
3012
3150
  throw projectionError(
3013
3151
  EVOLUTION_PROJECTION_INVALID_CODE,
@@ -3149,12 +3287,14 @@ function validateBundleIntegrity(bundle) {
3149
3287
  }
3150
3288
  if (
3151
3289
  ruleset === AGENT_MODEL_RULESET ||
3290
+ ruleset === AGENT_MODEL_RULESET_V3 ||
3152
3291
  ruleset === AGENT_MODEL_RULESET_V2 ||
3153
3292
  ruleset === AGENT_MODEL_RULESET_V1
3154
3293
  ) {
3155
3294
  snapshotAgentModelRequest(model.content, {
3156
3295
  allowStructuredArgumentText: ruleset !== AGENT_MODEL_RULESET_V1,
3157
- allowOpaqueTransportBlocks: ruleset === AGENT_MODEL_RULESET,
3296
+ allowOpaqueTransportBlocks:
3297
+ ruleset === AGENT_MODEL_RULESET || ruleset === AGENT_MODEL_RULESET_V3,
3158
3298
  });
3159
3299
  if (
3160
3300
  model.truncated ||
@@ -3439,6 +3579,9 @@ export class EvolutionEvidenceProjector {
3439
3579
  const sourceInputDigest = digest(
3440
3580
  payload,
3441
3581
  "chainlesschain.evolution-raw-plaintext/v2",
3582
+ ruleset === AGENT_MODEL_RULESET
3583
+ ? AGENT_MODEL_IMAGE_LIMITS.maxCanonicalRequestBytes
3584
+ : undefined,
3442
3585
  );
3443
3586
  const sourceEnvelopeDigest = digest(
3444
3587
  input.sourceEnvelope,
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { types } from "node:util";
3
3
  import { captureAgentEvolutionRuntimeComposition } from "./agent-evolution-runtime-composition-brand.js";
4
4
  import { captureAgentEvolutionIngress } from "./agent-evolution-ingress.js";
5
- import { formatOpaqueHexId } from "./opaque-hex-id.js";
5
+ import { encodeOpaqueHexId } from "./opaque-hex-id.js";
6
6
 
7
7
  export function readEvolutionCompositionFactory(dependencies = {}) {
8
8
  if (
@@ -44,7 +44,7 @@ export async function prepareGovernedModelTurn(
44
44
  }
45
45
  if (typeof factory !== "function")
46
46
  throw new TypeError("Invalid evolution composition factory");
47
- const runId = `${mode}-${formatOpaqueHexId(
47
+ const runId = `${mode}-${encodeOpaqueHexId(
48
48
  randomUUID().replaceAll("-", ""),
49
49
  )}`;
50
50
  const composition = captureAgentEvolutionRuntimeComposition(
@@ -21,7 +21,7 @@ import {
21
21
  flushWindowsLocalNtfsDirectory,
22
22
  } from "./evolution-windows-directory-durability.js";
23
23
  import { withFileLockAsync } from "../with-file-lock.js";
24
- import { formatOpaqueHexId } from "./opaque-hex-id.js";
24
+ import { encodeOpaqueHexId } from "./opaque-hex-id.js";
25
25
  import {
26
26
  configureEvolutionDeployment,
27
27
  getEvolutionDeploymentStatus,
@@ -660,7 +660,7 @@ export async function initializeDefaultLocalUiEvolutionDeployment(
660
660
  removeIncompleteStage(stagePath, parent);
661
661
  fs.mkdirSync(stagePath, { mode: 0o700 });
662
662
  privateDirectory(stagePath);
663
- const tenantId = `local-ui-${formatOpaqueHexId(
663
+ const tenantId = `local-ui-${encodeOpaqueHexId(
664
664
  randomUUID().replaceAll("-", ""),
665
665
  )}`;
666
666
  const paths = writeDeployment(stagePath, destination, tenantId);
@@ -25,7 +25,7 @@ import { loadLocalUiEvolutionDeployment } from "./local-ui-evolution-deployment.
25
25
  import { createLocalUiEvolutionFileSystem } from "./evolution-windows-directory-durability.js";
26
26
  import {
27
27
  formatLocalUiTenantReferenceId,
28
- formatOpaqueHexId,
28
+ encodeOpaqueHexId,
29
29
  } from "./opaque-hex-id.js";
30
30
 
31
31
  const SOURCE_ENVELOPE_SCHEMA = "chainlesschain.local-ui-source-envelope/v1";
@@ -84,7 +84,7 @@ function sha256(value) {
84
84
  }
85
85
 
86
86
  function sourceRefSegment(value, purpose) {
87
- return formatOpaqueHexId(
87
+ return encodeOpaqueHexId(
88
88
  createHash("sha256")
89
89
  .update(
90
90
  `chainlesschain.local-ui-source-ref/v1\0${purpose}\0${value}`,
@@ -615,9 +615,9 @@ export async function createLocalUiEvolutionComposition({
615
615
  witness,
616
616
  },
617
617
  evidenceIdGenerator: async () =>
618
- `evidence-${formatOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
618
+ `evidence-${encodeOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
619
619
  ingressIdGenerator: () =>
620
- `ingress-${formatOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
620
+ `ingress-${encodeOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
621
621
  secure: true,
622
622
  fsImpl,
623
623
  });
@@ -18,6 +18,14 @@ export function formatOpaqueHexId(value) {
18
18
  return groups.join("-");
19
19
  }
20
20
 
21
+ // Keep every hexadecimal nibble while preventing generated identifiers from
22
+ // resembling payment-card or phone numbers across their group separators.
23
+ export function encodeOpaqueHexId(value) {
24
+ return formatOpaqueHexId(value).replace(/[0-9]/gu, (digit) =>
25
+ String.fromCharCode(103 + Number(digit)),
26
+ );
27
+ }
28
+
21
29
  export function formatLocalUiTenantReferenceId(value) {
22
30
  const prefix = "local-ui-";
23
31
  if (typeof value !== "string" || !value.startsWith(prefix)) return value;
@@ -0,0 +1,59 @@
1
+ // Context planning estimates vision work separately from base64 transport.
2
+ // Providers and models meter images differently. These are planning reserves;
3
+ // provider usage receipts remain authoritative for token and cost settlement.
4
+ export const IMAGE_TOKEN_RESERVE = 8192;
5
+ export const LOW_DETAIL_IMAGE_TOKEN_RESERVE = 1024;
6
+ const BASE64_ALPHABET =
7
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
8
+
9
+ export function inspectCanonicalImageDataUrl(url) {
10
+ if (typeof url !== "string") return null;
11
+ const match =
12
+ /^data:image\/(?:png|jpeg|gif|webp);base64,([A-Za-z0-9+/]+={0,2})$/u.exec(
13
+ url,
14
+ );
15
+ if (!match || match[1].length % 4 !== 0) return null;
16
+ const data = match[1];
17
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
18
+ const finalValue = BASE64_ALPHABET.indexOf(data[data.length - padding - 1]);
19
+ if (
20
+ (padding === 2 && finalValue % 16 !== 0) ||
21
+ (padding === 1 && finalValue % 4 !== 0)
22
+ )
23
+ return null;
24
+ return {
25
+ decodedBytes: (data.length / 4) * 3 - padding,
26
+ encodedBytes: data.length,
27
+ };
28
+ }
29
+
30
+ export function imageTransportEstimate(messages) {
31
+ let bytes = 0;
32
+ let tokens = 0;
33
+ for (const message of messages) {
34
+ if (message?.role !== "user" || !Array.isArray(message.content)) continue;
35
+ for (const block of message.content) {
36
+ if (
37
+ block?.type !== "image_url" ||
38
+ Object.keys(block).sort().join() !== "image_url,type" ||
39
+ !block.image_url ||
40
+ Object.keys(block.image_url).some(
41
+ (key) => !["url", "detail"].includes(key),
42
+ )
43
+ )
44
+ continue;
45
+ const { url, detail } = block.image_url;
46
+ if (
47
+ typeof url !== "string" ||
48
+ (detail !== undefined && !["auto", "low", "high"].includes(detail)) ||
49
+ !inspectCanonicalImageDataUrl(url)
50
+ )
51
+ continue;
52
+ // Canonical base64 data URLs are ASCII and need no JSON escaping.
53
+ bytes += url.length;
54
+ tokens +=
55
+ detail === "low" ? LOW_DETAIL_IMAGE_TOKEN_RESERVE : IMAGE_TOKEN_RESERVE;
56
+ }
57
+ }
58
+ return { bytes, tokens };
59
+ }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { EXPLORATION_TOOLS } from "./task-progress-tracker.js";
3
3
  import { isToolRecoveryPaused } from "./tool-recovery-result.js";
4
+ import { hostToolOutputIdentity } from "./tool-result-preview.js";
4
5
  const MAX_FILES = 12;
5
6
 
6
7
  function keyFor(filePath, args) {
@@ -250,7 +251,7 @@ export class ReadFileLoopGuard {
250
251
  if (page.error || !page.readSpan) return page;
251
252
  page.readRecovery = {
252
253
  action: "continued",
253
- requestedRange,
254
+ ...(requestedRange !== undefined ? { requestedRange } : {}),
254
255
  resumedRange: page.range,
255
256
  };
256
257
  newContent = true;
@@ -258,7 +259,7 @@ export class ReadFileLoopGuard {
258
259
  return {
259
260
  path: filePath,
260
261
  fileVersion,
261
- range: page.range,
262
+ ...(page.range !== undefined ? { range: page.range } : {}),
262
263
  hashed: args.hashed === true,
263
264
  alreadyRead: true,
264
265
  contentOmitted: true,
@@ -353,14 +354,16 @@ export class ReadFileLoopGuard {
353
354
  } else if (
354
355
  (tool === "run_code" || tool === "run_shell") &&
355
356
  typeof result?.output === "string" &&
356
- result.output.length >= 8000
357
+ (result.output.length >= 8000 ||
358
+ hostToolOutputIdentity(result)?.chars >= 8000)
357
359
  ) {
358
360
  // Arbitrary code may just dump the same file again. Compare returned
359
361
  // evidence, not code spelling or timings. Never cache/skip execution:
360
362
  // identical stdout does not imply identical filesystem side effects.
361
- const fingerprint = createHash("sha256")
362
- .update(result.output)
363
- .digest("hex");
363
+ const identity = hostToolOutputIdentity(result);
364
+ const fingerprint = identity
365
+ ? `${identity.chars}:${identity.digest}`
366
+ : createHash("sha256").update(result.output).digest("hex");
364
367
  if (this.largeOutputs.has(fingerprint)) this.batch.duplicates++;
365
368
  else {
366
369
  this.largeOutputs.add(fingerprint);
@@ -1,4 +1,7 @@
1
- import { canonicalDigest } from "@chainlesschain/context-memory-kernel";
1
+ import {
2
+ canonicalDigest,
3
+ canonicalJson,
4
+ } from "@chainlesschain/context-memory-kernel";
2
5
  import {
3
6
  contextItemsToMessages,
4
7
  messagesToContextItems,
@@ -33,6 +36,19 @@ const historyBearing = (entry) =>
33
36
  );
34
37
  const fingerprint = (messages) =>
35
38
  canonicalDigest(messages.map(encodePersistedMessage));
39
+ const sameContextItemContent = (left, right) => {
40
+ if (typeof left?.content === "string" || typeof right?.content === "string") {
41
+ return (
42
+ typeof left?.content === "string" &&
43
+ typeof right?.content === "string" &&
44
+ left.content === right.content
45
+ );
46
+ }
47
+ if (left?.contentRef === undefined || right?.contentRef === undefined) {
48
+ return false;
49
+ }
50
+ return canonicalJson(left.contentRef) === canonicalJson(right.contentRef);
51
+ };
36
52
 
37
53
  /**
38
54
  * Origins belong to positions in the current display history, never to equal
@@ -128,7 +144,7 @@ export function createSessionHistoryOrigins() {
128
144
  if (context) {
129
145
  const originals = messagesToContextItems(
130
146
  context.map((entry) => entry.message),
131
- { sessionId: canonical.sessionId },
147
+ { sessionId: canonical.sessionId, allowExternalization: true },
132
148
  );
133
149
  const byDigest = new Map(
134
150
  originals.map((item) => [item.digest, item]),
@@ -140,7 +156,9 @@ export function createSessionHistoryOrigins() {
140
156
  context.slice(
141
157
  item.sourceRef.eventSequence,
142
158
  item.sourceRef.eventSequence +
143
- contextItemsToMessages([item]).length,
159
+ contextItemsToMessages([item], {
160
+ sessionId: canonical.sessionId,
161
+ }).length,
144
162
  );
145
163
  entries = [...canonical.outputItems]
146
164
  .sort(
@@ -150,12 +168,14 @@ export function createSessionHistoryOrigins() {
150
168
  left.itemId.localeCompare(right.itemId, "en"),
151
169
  )
152
170
  .flatMap((item) => {
153
- const messages = contextItemsToMessages([item]);
171
+ const messages = contextItemsToMessages([item], {
172
+ sessionId: canonical.sessionId,
173
+ });
154
174
  const source = bySequence.get(item.sourceRef.eventSequence);
155
175
  if (
156
176
  item.sourceRef.store === "cli-jsonl-session" &&
157
177
  source?.sourceRef.digest === item.sourceRef.digest &&
158
- source.content === item.content
178
+ sameContextItemContent(source, item)
159
179
  ) {
160
180
  return itemEntries(source);
161
181
  }
@@ -77,7 +77,11 @@ function isContextCompaction(data) {
77
77
  }));
78
78
  return (
79
79
  canonicalDigest(
80
- content(contextItemsToMessages(canonical.outputItems)),
80
+ content(
81
+ contextItemsToMessages(canonical.outputItems, {
82
+ sessionId: canonical.sessionId,
83
+ }),
84
+ ),
81
85
  ) === canonicalDigest(content(data.messages))
82
86
  );
83
87
  } catch {