chainlesschain 0.166.107 → 0.166.108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +13 -9
  2. package/package.json +1 -1
  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-DCx6L_HW.js → AIOps-B5HWUFaS.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-DYm1CN3Z.js → ActionButton-DCIKpxoQ.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-D5ItUS_y.js → Analytics-dsgvjCEQ.js} +2 -2
  8. package/src/assets/web-panel/assets/{AppLayout-Cb7XeRdH.js → AppLayout-DWDH5Qbm.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-DDDeqCDQ.js → Artifacts-BD8S8kzU.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-BRt3x13w.js → Audit-CDXv4Aoj.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-C_oNALMd.js → BackgroundAgents-BLAQ5Uy0.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-PSU2p6CR.js → Backup-moPac6tF.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-G3oL4hcX.js → BaseInput-C_6nzsLU.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-C2cLI10b.js → Chat-cYuslkFG.js} +4 -4
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-KX1OITf8.js → ChatBubbleRenderer-Ceh3MG6M.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-CHLnEgFD.js → Checkbox-Qjb-XOX_.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-DZm9g_Bc.js → Codegen-IYRWXlMA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-D5Z4zSay.js → Col-CsQ9jv8M.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-DHw69NSk.js → Community-BbNmfTwe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-B-vjYNWz.js → Compact-SShDx-nr.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-DaIYVZQY.js → Compliance-CLWhKIJp.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-CoA9U-gF.js → Cowork-D4tK9_s3.js} +2 -2
  23. package/src/assets/web-panel/assets/{Cron-DXqmxlRC.js → Cron-D7GnfQ1z.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-Cdkc3ue1.js → Crosschain-TG86SH3X.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-oKfYyNSR.js → DID-6pue0NCO.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-n_ycvgWu.js → Dashboard-BwkJ29du.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-D38rZ8WY.js → Dropdown-DEvpYDof.js} +1 -1
  28. package/src/assets/web-panel/assets/EmailListRenderer-CQj7UlKJ.js +1 -0
  29. package/src/assets/web-panel/assets/{EvolutionSettings-CTxA_SIq.js → EvolutionSettings-tpvPMM9W.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DmECNwV5.js → FamilyGuardDashboard-CWYFW75O.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-B2IaaDmQ.js → Federation-gRIi96rM.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DgcgqUIk.js → FormItemContext-B7Nr3YkV.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-B1-du6Qm.js → GenericCardRenderer-b1BgGgLT.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-XceM21Il.js → Git-Dis1jjO7.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-CV97iz3z.js → Governance-C4Yg99Am.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-CI7fj305.js → Inference-D4UQRbGu.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-B5mekWRy.js → KnowledgeGraph-Co_kLMAq.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BlxFA0xY.js → Logs-D4BRp7bg.js} +2 -2
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-rDE-XyMY.js → MarkdownRenderer-Dgy8arQI.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-DL1BVCiX.js → Marketplace-ys0j1vr9.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-BPtwrO3c.js → McpTools-B3ZvRZe_.js} +2 -2
  42. package/src/assets/web-panel/assets/{Memory-DuSNw4E-.js → Memory-BOa0jbr-.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-DTgY9ywI.js → MobileBridge-CWJRmUbj.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-7ty8vTpj.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-h4fCLjXw.js → Mtc-CCcOk5m5.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-fqMDY3-w.js → MtcAudit-vlrrScbQ.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-Do3FehBL.js → Multisig-C8K59S_8.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-CQVY0kQF.js → NLProgramming-oTpFYVme.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-4c3oNvWh.js → Notes-BhqAJWiz.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-BKr2JD4k.js → NotificationSettings-DCnV1CJM.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-Bq4jgzRt.js → OrderTableRenderer-CZGnsrVW.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-Dy3XidyA.js → Organization-DX1LuQlJ.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-DFF0qN5K.js → Overflow-CbfKrFdT.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-DAgYYykd.js → P2P-BEwYZuHL.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DgNHa8b1.js → PdhVaultBrowser-YNmWbwd7.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-Cev6uSBc.js → Permissions-DyZ18LiA.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-DJzbN06K.js → PersonalDataHub-B2J3aw2g.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-CoJWvbQ5.js → Pipeline-CXl2ZlhQ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-DZxV6etm.js → Privacy-I5SDmgAp.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BFW87JnM.js → ProjectInit-Crl9tXw3.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-LBrRT27p.js → ProjectSettings-DCFtFlSK.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-Do5BQq1i.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-CnRbjOYk.js → Providers-Cb7OWa3y.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-BVFnIS3r.js → QrScannerModal-CJmhAU-s.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-DrcHx5f1.js → QuickAsk-Cu6lEWou.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-CbRgrSJm.js → Recommend-Crip3jLB.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-B2Uz_ElB.js → RemoteSession-KxYY-2CB.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-DS0ljTxL.js → Reputation-D0vN-htI.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-ClLobFht.js → Row-wqzd_xCD.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-DxxcTD0x.js → RssFeed-8lsPfb0a.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-BbHN0Fik.js → Search-C-e8qAeU.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-BAVU_EcY.js → Security-CMM9WgFy.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-CtqRSVq_.js → Services-CZdxjN2B.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-IJ8ZKdSZ.js → Skeleton-BI3XNk35.js} +1 -1
  75. package/src/assets/web-panel/assets/Skills-DMlsh9It.js +1 -0
  76. package/src/assets/web-panel/assets/{Sla-fRkhnA3i.js → Sla-HQruznKY.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Bi8sYJ-I.js → SpeechSettings-Dl-MFq3w.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-CjeplUtS.js → SyncSettings-BU224vDm.js} +2 -2
  79. package/src/assets/web-panel/assets/Tasks-DESg3yCs.js +1 -0
  80. package/src/assets/web-panel/assets/{Templates-BwVugi8n.js → Templates-Jb3TPz4Q.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-CsPuu5xK.js → Tenant-CQ-GTrfa.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-DuaeOPjY.js → Terminal-BbMl3Rue.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-D-PMrFxO.js → TimelineRenderer-CS_vstKF.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-BXZuiQPZ.js → Tokens-BaopVl1f.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-DgpTaHL3.js → Trigger-DtnD4WUN.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-Bn7lEGRY.js → Trust-CsvG4Tmf.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-zz-8JAAQ.js → UkeySign-C1-A-cOV.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-DCzfQGTc.js → VideoEditing-CNgJPdSL.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-BiRHM_zB.js → Wallet-CdF41501.js} +2 -2
  90. package/src/assets/web-panel/assets/{WebAuthn-QmPo2JwU.js → WebAuthn-D-JmxD5g.js} +5 -5
  91. package/src/assets/web-panel/assets/{WorkflowEditor-gL7SsPnY.js → WorkflowEditor-C0XunR13.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-BysvK4hk.js → chat-BBAZpD_G.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-CQq20vqN.js → colors-BxSxO5BX.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CAHlOf1X.js → compact-item-BoxL8pg5.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BeF3ESj_.js → createContext-W5JAyZmY.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-BI5TZ7EX.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CiqnRf-U.js → hasIn-HpiKDPVC.js} +1 -1
  98. package/src/assets/web-panel/assets/index-9-JLDK5n.js +1 -0
  99. package/src/assets/web-panel/assets/{index-CCX0daFQ.js → index-9caMEwQ-.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B5941vyo.js → index-B05_YRHZ.js} +1 -1
  101. package/src/assets/web-panel/assets/index-B7m8ioaQ.js +1 -0
  102. package/src/assets/web-panel/assets/{index-jJhemX1R.js → index-B9VxE1C-.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BcVmt6u_.js → index-BA3WWQAT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-ZBuJ6O6g.js → index-BSJ0cAle.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-i9aJV9qk.js → index-BW4PP5GN.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Cp3IRTDK.js → index-BZAbaY64.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B2xbtl_c.js → index-Ba4usRFM.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-4a0Lmz_y.js → index-BgMViZIY.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CTp89vmg.js → index-BtFAEG08.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DTPUVm71.js → index-Bwn6k_qw.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-xwtRrgVY.js → index-C4uI7WCB.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CeIL3-nY.js → index-C5kqii-G.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D7q5VH2n.js → index-C7GIUWWF.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-qjaCk1Mj.js → index-CM_gdohF.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BfJabF_8.js → index-CRg3aOoj.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DHGtFwwR.js → index-CcyzZloJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DXT7MKkD.js → index-CehxfRUR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CowbNBJs.js → index-D6m6B4Wy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DBmGQHEz.js → index-DSyfxxQS.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-yFQWSRoW.js → index-DaJk9U9V.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DKYbru3i.js → index-DcmAiq_Y.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CImCEo8S.js → index-DdEcQ1rp.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CcccefJc.js → index-DinY7IQa.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-zNkw0IY7.js → index-DkSnlgK_.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-QALafVsT.js → index-DlPvZgv8.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Bvkbj3x-.js → index-DuM9I1If.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CgkUkmEJ.js → index-DwidpDEE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Br5tGVbC.js → index-IO2I7nzu.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BOiyW31p.js → index-JrMBRi62.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BHpHGGmB.js → index-QzlEbtRL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-TdVEaJfk.js → index-V6VIJe2N.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DXsXhR7p.js → index-_38IJqoP.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BsnLkZjz.js → index-_p4e3KfB.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BtWchYfW.js → index-gvfIRZxJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-B2AOvjgR.js → index-ooP1pQCt.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-Dqkls2_6.js → index-p6rNtYud.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-BQR2HnKb.js → initDefaultProps-Dfn6M05J.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-gAz6l-nZ.js → motion-BRRwFqGO.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-Cy5TN856.js → move-DpDPN6zR.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-BeT7VrGi.js → mtc-parser-BHlSE87F.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-BLN3mlgK.js → omit-CEOri_L5.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DtwZ8u2d.js → pickAttrs-DqICh-ad.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-ViMwQSa-.js → placementArrow-DQizFFiV.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-dEL3u4E7.js → responsiveObserve-DmFHPdvC.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-l05us-Gz.js → slide-s2s1XS3T.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-Bk-Qn2Og.js → statusUtils-IwEIbBan.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-Bn186qeY.js → styleChecker-CIFLlbXH.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-WP-ugR3H.js → useFlexGapSupport-CFkN2Cgh.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-1SvoeSCL.js → useFs-BSY6O48s.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-CfNU8N3z.js → usePersonalDataHub-iJl3VnU0.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-Bd2Ya101.js → vnode-vu8ReMs7.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-Def_MeZv.js → zoom-CeSrYAHp.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/data/changelog.json +16 -3
  155. package/src/harness/jsonl-session-store.js +128 -5
  156. package/src/harness/prompt-compressor.js +18 -10
  157. package/src/harness/structured-handoff.js +151 -13
  158. package/src/lib/agent-session-export.js +20 -0
  159. package/src/lib/background-agent-supervisor.js +54 -0
  160. package/src/lib/coding-task-guidance.js +2 -1
  161. package/src/lib/context-memory-kernel/live-compaction.js +75 -12
  162. package/src/lib/read-file-loop-guard.js +124 -8
  163. package/src/lib/session-host-snapshot.js +3 -0
  164. package/src/lib/session-message-provenance.js +24 -5
  165. package/src/lib/session-tool-round.js +39 -0
  166. package/src/lib/session-transcript-history.js +2 -0
  167. package/src/lib/session-transcript-page.js +4 -1
  168. package/src/lib/session-transcript-structure.js +4 -0
  169. package/src/runtime/__tests__/message-roles.test.js +29 -0
  170. package/src/runtime/agent-core.js +115 -43
  171. package/src/runtime/headless-runner.js +191 -34
  172. package/src/runtime/headless-stream.js +140 -42
  173. package/src/runtime/message-roles.js +43 -0
  174. package/src/workers/background-agent-worker.js +146 -47
  175. package/src/assets/web-panel/assets/EmailListRenderer-BjreeBQ1.js +0 -1
  176. package/src/assets/web-panel/assets/MobileProjects-CTpVHJMa.js +0 -1
  177. package/src/assets/web-panel/assets/Projects-O3uz76R1.js +0 -1
  178. package/src/assets/web-panel/assets/Skills-DXxcznnO.js +0 -1
  179. package/src/assets/web-panel/assets/Tasks-D4KB2Hbx.js +0 -1
  180. package/src/assets/web-panel/assets/devWarning-D-Iugqxv.js +0 -1
  181. package/src/assets/web-panel/assets/index-B0Oz2_rQ.js +0 -1
  182. package/src/assets/web-panel/assets/index-DwoycEQa.js +0 -1
@@ -27,6 +27,7 @@ import {
27
27
  sanitizePersistedMessages,
28
28
  } from "./session-message-provenance.js";
29
29
  import { createSessionTranscriptStructureProjection } from "./session-transcript-structure.js";
30
+ import { projectToolRoundMessages } from "./session-tool-round.js";
30
31
  import { normalizeSessionBudgetRootConfig } from "./session-budget-production-root.js";
31
32
  import {
32
33
  createDeferredQuestionEventReducer,
@@ -86,6 +87,8 @@ function replayMessagesFromVerifiedEvents(events) {
86
87
  isReplayableMessage(event.data)
87
88
  ) {
88
89
  suffix.push(event.data);
90
+ } else if (event?.type === "tool_round") {
91
+ suffix.push(...projectToolRoundMessages(event).toReversed());
89
92
  } else if (event?.type === "ws_turn") {
90
93
  const turn = projectWsTurnMessages(event);
91
94
  if (turn) suffix.push(turn.assistant, turn.user);
@@ -26,6 +26,7 @@ export const DURABLE_SYSTEM_MESSAGE_KINDS = Object.freeze({
26
26
 
27
27
  const DURABLE_KINDS = new Set(Object.values(DURABLE_SYSTEM_MESSAGE_KINDS));
28
28
  const RUNTIME_PROVENANCE = new WeakMap();
29
+ const RUNTIME_ONLY_MESSAGES = new WeakSet();
29
30
  const INVALID_DATA = Symbol("invalid-session-message-data");
30
31
  const MAX_DATA_DEPTH = 100;
31
32
  const MAX_DATA_NODES = 100_000;
@@ -227,11 +228,11 @@ function validWireTag(value) {
227
228
  const snapshot = plainDataSnapshot(value);
228
229
  return Boolean(
229
230
  snapshot &&
230
- snapshot.keys.length === 2 &&
231
- snapshot.keys.includes("schema") &&
232
- snapshot.keys.includes("kind") &&
233
- dataValue(snapshot, "schema") === SESSION_MESSAGE_PROVENANCE_SCHEMA &&
234
- DURABLE_KINDS.has(dataValue(snapshot, "kind")),
231
+ snapshot.keys.length === 2 &&
232
+ snapshot.keys.includes("schema") &&
233
+ snapshot.keys.includes("kind") &&
234
+ dataValue(snapshot, "schema") === SESSION_MESSAGE_PROVENANCE_SCHEMA &&
235
+ DURABLE_KINDS.has(dataValue(snapshot, "kind")),
235
236
  );
236
237
  }
237
238
 
@@ -248,6 +249,23 @@ export function markDurableSystemMessage(message, kind) {
248
249
  return attachRuntimeTag(message, canonicalTag(kind));
249
250
  }
250
251
 
252
+ // Only host-created retry/result-delivery messages can be excluded from replay.
253
+ // There is deliberately no wire tag: a provider field or a serialized copy
254
+ // cannot suppress a real user message from authority comparison.
255
+ export function markRuntimeOnlySessionMessage(message) {
256
+ const snapshot = plainDataSnapshot(message);
257
+ if (!snapshot || !["user", "assistant"].includes(dataValue(snapshot, "role")))
258
+ throw new TypeError(
259
+ "Runtime-only nudges must be user or assistant messages",
260
+ );
261
+ RUNTIME_ONLY_MESSAGES.add(message);
262
+ return message;
263
+ }
264
+
265
+ export function isRuntimeOnlySessionMessage(message) {
266
+ return RUNTIME_ONLY_MESSAGES.has(message);
267
+ }
268
+
251
269
  export function getDurableSystemMessageProvenance(message) {
252
270
  const snapshot = plainDataSnapshot(message);
253
271
  if (!snapshot || dataValue(snapshot, "role") !== "system") return null;
@@ -422,6 +440,7 @@ export function projectCanonicalResumeMessages(messages, options = {}) {
422
440
  invalid("Canonical resume message must contain JSON-safe data");
423
441
  continue;
424
442
  }
443
+ if (isRuntimeOnlySessionMessage(raw)) continue;
425
444
  if (role !== "system") {
426
445
  projected.push(clean);
427
446
  } else if (runtimeTag) {
@@ -0,0 +1,39 @@
1
+ /** Append-only, complete provider assistant/tool exchanges. No policy or user
2
+ * messages may enter durable replay through this event. */
3
+ export const TOOL_ROUND_EVENT = "tool_round";
4
+ export const TOOL_ROUND_SCHEMA = "chainlesschain.session-tool-round/v1";
5
+
6
+ export function assertCompleteToolRounds(messages) {
7
+ if (!Array.isArray(messages) || messages.length === 0)
8
+ throw new TypeError("Tool rounds require a non-empty message array");
9
+ const pending = new Set();
10
+ for (const message of messages) {
11
+ if (message?.role === "assistant" && pending.size === 0) {
12
+ if (!Array.isArray(message.tool_calls) || message.tool_calls.length === 0)
13
+ throw new TypeError("Tool round assistant must contain tool calls");
14
+ for (const call of message.tool_calls) {
15
+ if (typeof call?.id !== "string" || !call.id || pending.has(call.id))
16
+ throw new TypeError("Malformed or duplicate tool call");
17
+ pending.add(call.id);
18
+ }
19
+ } else if (
20
+ message?.role === "tool" &&
21
+ pending.delete(message.tool_call_id)
22
+ ) {
23
+ // Every result settles exactly one call in the immediately preceding round.
24
+ } else {
25
+ throw new TypeError(
26
+ "Tool round contains an orphan result or forbidden message",
27
+ );
28
+ }
29
+ }
30
+ if (pending.size) throw new TypeError("Tool round is incomplete");
31
+ return messages;
32
+ }
33
+
34
+ export function projectToolRoundMessages(event) {
35
+ if (event?.type !== TOOL_ROUND_EVENT) return null;
36
+ if (event.data?.schema !== TOOL_ROUND_SCHEMA)
37
+ throw new TypeError("Unsupported session tool round schema");
38
+ return assertCompleteToolRounds(event.data.messages);
39
+ }
@@ -11,6 +11,7 @@ import {
11
11
  } from "./session-transcript-page.js";
12
12
  import { createSessionHistoryOrigins } from "./session-history-origins.js";
13
13
  import { getDurableSystemMessageProvenance } from "./session-message-provenance.js";
14
+ import { projectToolRoundMessages } from "./session-tool-round.js";
14
15
  import {
15
16
  BRANCH_HISTORY_SCHEMA,
16
17
  BRANCH_HISTORY_SYSTEM_ORIGINS_SCHEMA,
@@ -29,6 +30,7 @@ const MAX_HISTORY_RANGES = 16384;
29
30
  // Enumerate all possible display sources, including replacement snapshots.
30
31
  // Their physical sequence never changes when the selected history rewinds.
31
32
  function eventMessages(event, imported = null) {
33
+ if (event.type === "tool_round") return projectToolRoundMessages(event);
32
34
  if (event.type === BRANCH_HISTORY_MESSAGE)
33
35
  return imported?.kind === "archive" ? [imported.message] : [];
34
36
  if (
@@ -1,3 +1,4 @@
1
+ import { projectToolRoundMessages } from "./session-tool-round.js";
1
2
  import { createHash } from "node:crypto";
2
3
  import {
3
4
  readVerifiedProjection,
@@ -120,7 +121,9 @@ export function createSessionTranscriptPageProjection(
120
121
  ["user_message", "assistant_message", "system"].includes(event.type)
121
122
  )
122
123
  append(event.data);
123
- else if (event.type === "ws_turn") {
124
+ else if (event.type === "tool_round") {
125
+ for (const message of projectToolRoundMessages(event)) append(message);
126
+ } else if (event.type === "ws_turn") {
124
127
  const pair = projectWsTurnMessages(event);
125
128
  if (pair) {
126
129
  append(pair.user);
@@ -1,3 +1,4 @@
1
+ import { projectToolRoundMessages } from "./session-tool-round.js";
1
2
  /**
2
3
  * Streaming structural authority for canonical session transcripts.
3
4
  *
@@ -72,6 +73,9 @@ export function createSessionTranscriptStructureProjection(
72
73
 
73
74
  return Object.freeze({
74
75
  accept(event) {
76
+ // Unknown versions and malformed/orphan results fail closed at every
77
+ // verified projection, even if that projection does not display tools.
78
+ if (event?.type === "tool_round") projectToolRoundMessages(event);
75
79
  const index = eventCount;
76
80
  eventCount += 1;
77
81
  if (event?.type === "session_start") {
@@ -2,9 +2,38 @@ import { describe, it, expect } from "vitest";
2
2
  import {
3
3
  mergeConsecutiveMessages,
4
4
  collapseConsecutiveMessagesInPlace,
5
+ createResumeRoleProjection,
5
6
  _internal,
6
7
  } from "../message-roles.js";
7
8
 
9
+ describe("createResumeRoleProjection", () => {
10
+ it("normalizes retained resume pairs across clones without folding later feedback", () => {
11
+ const initial = [
12
+ { role: "system", content: "policy" },
13
+ { role: "user", content: "unanswered" },
14
+ { role: "user", content: "resume" },
15
+ ];
16
+ const project = createResumeRoleProjection(initial);
17
+ const request = [
18
+ ...structuredClone(initial),
19
+ { role: "assistant", content: "reply" },
20
+ { role: "user", content: "feedback" },
21
+ { role: "user", content: "next input" },
22
+ ];
23
+ const before = structuredClone(request);
24
+ const projected = project(request);
25
+ expect(request).toEqual(before);
26
+ expect(initial).toHaveLength(3);
27
+ expect(
28
+ projected
29
+ .filter((message) => message.role === "user")
30
+ .map((message) => message.content),
31
+ ).toEqual(["unanswered\n\nresume", "feedback", "next input"]);
32
+ expect(projected.at(-1)).toBe(request.at(-1));
33
+ expect(project(request)).toEqual(projected);
34
+ });
35
+ });
36
+
8
37
  describe("mergeConsecutiveMessages", () => {
9
38
  it("is a no-op on a healthy alternating transcript", () => {
10
39
  const msgs = [
@@ -46,6 +46,7 @@ import {
46
46
  buildReadFileOutline,
47
47
  } from "../lib/read-file-page.js";
48
48
  import { ReadFileLoopGuard } from "../lib/read-file-loop-guard.js";
49
+ const READ_FILE_REQUEST_CONTEXT = Symbol("read-file-request-context");
49
50
  import { gitToolInputError } from "../lib/git-tool-input.js";
50
51
  import { shellOutputPage, shellOutputPreview } from "../lib/shell-output.js";
51
52
  import { DIAGNOSTIC_WORKFLOW_GUIDANCE } from "../lib/diagnostic-workflow.js";
@@ -232,7 +233,10 @@ import {
232
233
  formatStructuredHandoff,
233
234
  } from "../harness/structured-handoff.js";
234
235
  import { isMcpRpcError } from "../harness/mcp-client.js";
235
- import { projectCanonicalResumeMessages } from "../lib/session-message-provenance.js";
236
+ import {
237
+ projectCanonicalResumeMessages,
238
+ markRuntimeOnlySessionMessage,
239
+ } from "../lib/session-message-provenance.js";
236
240
  import { releaseOldLiveSessionResults } from "../lib/session-runtime-retention.js";
237
241
  import {
238
242
  pathMatchesOpenedFileIdentitySync,
@@ -4935,7 +4939,19 @@ export async function _withPostEditDiagnostics(
4935
4939
  // Parsing the bytes we just wrote is safe even when starting a language
4936
4940
  // server is forbidden. Feed definite local import mistakes back in the same
4937
4941
  // tool result; a successful write still means persistence, not test success.
4938
- let enriched = result;
4942
+ let enriched =
4943
+ result?.success === true && typeof writtenContent === "string"
4944
+ ? {
4945
+ ...result,
4946
+ validation: {
4947
+ persisted: true,
4948
+ testsExecuted: false,
4949
+ relativeImports: "not-checked",
4950
+ message:
4951
+ "File saved. This file operation did not run tests. Any import feedback checks explicit JavaScript relative references only and does not establish test or task acceptance.",
4952
+ },
4953
+ }
4954
+ : result;
4939
4955
  if (
4940
4956
  result?.success === true &&
4941
4957
  typeof writtenContent === "string" &&
@@ -4956,9 +4972,21 @@ export async function _withPostEditDiagnostics(
4956
4972
  content: writtenContent,
4957
4973
  workspaceRoot: root,
4958
4974
  });
4975
+ enriched = {
4976
+ ...enriched,
4977
+ validation: {
4978
+ ...enriched.validation,
4979
+ relativeImports: checked.status,
4980
+ checkedReferences: checked.checkedReferences,
4981
+ uncheckedReferences: checked.uncheckedReferences.length,
4982
+ truncated: checked.truncated,
4983
+ diagnosticsTruncated: checked.diagnostics.length > 8,
4984
+ ...(checked.reason ? { skipReason: checked.reason } : {}),
4985
+ },
4986
+ };
4959
4987
  if (checked.diagnostics.length) {
4960
4988
  enriched = {
4961
- ...result,
4989
+ ...enriched,
4962
4990
  newDiagnostics: [
4963
4991
  ...(result.newDiagnostics || []),
4964
4992
  ...checked.diagnostics.slice(0, 8),
@@ -12103,6 +12131,10 @@ export async function chatWithTools(rawMessages, options) {
12103
12131
  tools = projected.tools;
12104
12132
  }
12105
12133
 
12134
+ if (options[READ_FILE_REQUEST_CONTEXT]) {
12135
+ options[READ_FILE_REQUEST_CONTEXT].messages = messages;
12136
+ }
12137
+
12106
12138
  throwIfAborted(signal);
12107
12139
 
12108
12140
  // Host accounting starts only after request admission. A rejected model
@@ -14697,7 +14729,9 @@ export async function* agentLoop(messages, options) {
14697
14729
  // These tools are alternative ways to deliver content to the same target.
14698
14730
  // A successful explicit replacement must settle a deferred edit (and vice
14699
14731
  // versa); move/delete/notebook effects retain their distinct obligations.
14700
- const effect = ["write_file", "edit_file", "edit_file_hashed"].includes(tool)
14732
+ const effect = ["write_file", "edit_file", "edit_file_hashed"].includes(
14733
+ tool,
14734
+ )
14701
14735
  ? "file-content"
14702
14736
  : tool;
14703
14737
  const key = JSON.stringify([effect, targets]);
@@ -15850,10 +15884,12 @@ export async function* agentLoop(messages, options) {
15850
15884
  // SubagentStop hooks are best-effort
15851
15885
  }
15852
15886
  }
15853
- messages.push({
15854
- role: "user",
15855
- content: _backgroundSubAgentResultText(entry),
15856
- });
15887
+ messages.push(
15888
+ markRuntimeOnlySessionMessage({
15889
+ role: "user",
15890
+ content: _backgroundSubAgentResultText(entry),
15891
+ }),
15892
+ );
15857
15893
  taskProgressTracker.retainChildResult(
15858
15894
  entry.id,
15859
15895
  {
@@ -16002,6 +16038,11 @@ export async function* agentLoop(messages, options) {
16002
16038
  ];
16003
16039
  }
16004
16040
  }
16041
+ // Role normalization is request-only: durable history remains unchanged
16042
+ // for tool settlement and canonical compaction authority comparisons.
16043
+ if (typeof options.projectProviderMessages === "function") {
16044
+ callMessages = options.projectProviderMessages(callMessages);
16045
+ }
16005
16046
  const contextMemoryTrustedSystemIndexes = [];
16006
16047
  if (Number.isFinite(budget.limit)) {
16007
16048
  // consume() already charged this dispatch. The current request may still
@@ -16197,10 +16238,15 @@ export async function* agentLoop(messages, options) {
16197
16238
  const requestOptions = providerRequestId
16198
16239
  ? { ...plannedProviderOptions, providerRequestId }
16199
16240
  : plannedProviderOptions;
16200
- const providerCallOptions =
16201
- evolutionIngress !== null
16241
+ // Capture the custom adapter's input or the built-in adapter's final
16242
+ // projection. Commit visibility only after a successful model response.
16243
+ const readFileRequestContext = { messages: callMessages };
16244
+ const providerCallOptions = {
16245
+ [READ_FILE_REQUEST_CONTEXT]: readFileRequestContext,
16246
+ ...(evolutionIngress !== null
16202
16247
  ? { ...requestOptions, evolutionIngress }
16203
- : requestOptions;
16248
+ : requestOptions),
16249
+ };
16204
16250
  const modelUsageCall = {
16205
16251
  type: "model-usage-started",
16206
16252
  callId: _newModelUsageCallId("model"),
@@ -16302,6 +16348,7 @@ export async function* agentLoop(messages, options) {
16302
16348
  },
16303
16349
  "model_error",
16304
16350
  );
16351
+ readFileLoopGuard.observeVisibleMessages(readFileRequestContext.messages);
16305
16352
  providerReceipt = providerRequestId
16306
16353
  ? _snapshotProviderRequestReceipt(
16307
16354
  result?.providerReceipt,
@@ -16463,17 +16510,24 @@ export async function* agentLoop(messages, options) {
16463
16510
  );
16464
16511
  _throwBackgroundUsageFailureState(backgroundUsageFailureState);
16465
16512
  throwIfAborted(signal);
16466
- messages.push({ role: "assistant", content: msg.content || "" });
16513
+ messages.push(
16514
+ markRuntimeOnlySessionMessage({
16515
+ role: "assistant",
16516
+ content: msg.content || "",
16517
+ }),
16518
+ );
16467
16519
  for (const entry of _takeSettledBackgroundSubAgents(
16468
16520
  backgroundSubAgents,
16469
16521
  )) {
16470
16522
  _throwBackgroundSubAgentUsageFailure(entry);
16471
- messages.push({
16472
- role: "user",
16473
- content:
16474
- _backgroundSubAgentResultText(entry) +
16475
- "\n\nAll background sub-agents have finished. Incorporate their results and give your final answer.",
16476
- });
16523
+ messages.push(
16524
+ markRuntimeOnlySessionMessage({
16525
+ role: "user",
16526
+ content:
16527
+ _backgroundSubAgentResultText(entry) +
16528
+ "\n\nAll background sub-agents have finished. Incorporate their results and give your final answer.",
16529
+ }),
16530
+ );
16477
16531
  taskProgressTracker.retainChildResult(
16478
16532
  entry.id,
16479
16533
  {
@@ -16498,17 +16552,24 @@ export async function* agentLoop(messages, options) {
16498
16552
  const pending = [...pendingSubtreeMutations.values()];
16499
16553
  if (!subtreeMutationReprompted && budget.remaining() > 0) {
16500
16554
  subtreeMutationReprompted = true;
16501
- messages.push({ role: "assistant", content: msg.content || "" });
16502
- messages.push({
16503
- role: "user",
16504
- content:
16505
- "Runtime status: these mutations were deferred before any effect: " +
16506
- JSON.stringify(pending) +
16507
- ". Review the authoritative subtree instructions already returned. " +
16508
- "If still authorized and appropriate, explicitly issue the required tool call. " +
16509
- "Do not bypass rules or perform a prohibited write. Otherwise report the unfinished work and its blocker. " +
16510
- "A promise to retry does not execute the mutation.",
16511
- });
16555
+ messages.push(
16556
+ markRuntimeOnlySessionMessage({
16557
+ role: "assistant",
16558
+ content: msg.content || "",
16559
+ }),
16560
+ );
16561
+ messages.push(
16562
+ markRuntimeOnlySessionMessage({
16563
+ role: "user",
16564
+ content:
16565
+ "Runtime status: these mutations were deferred before any effect: " +
16566
+ JSON.stringify(pending) +
16567
+ ". Review the authoritative subtree instructions already returned. " +
16568
+ "If still authorized and appropriate, explicitly issue the required tool call. " +
16569
+ "Do not bypass rules or perform a prohibited write. Otherwise report the unfinished work and its blocker. " +
16570
+ "A promise to retry does not execute the mutation.",
16571
+ }),
16572
+ );
16512
16573
  yield {
16513
16574
  type: "iteration-warning",
16514
16575
  message:
@@ -16551,13 +16612,17 @@ export async function* agentLoop(messages, options) {
16551
16612
  const _contentEmpty = !String(msg.content || "").trim();
16552
16613
  if (_contentEmpty && _thinking && !emptyThinkingReprompted) {
16553
16614
  emptyThinkingReprompted = true;
16554
- messages.push({ role: "assistant", content: "" });
16555
- messages.push({
16556
- role: "user",
16557
- content:
16558
- "You ended your turn with only internal reasoning and no visible " +
16559
- "response. Please provide your actual answer now.",
16560
- });
16615
+ messages.push(
16616
+ markRuntimeOnlySessionMessage({ role: "assistant", content: "" }),
16617
+ );
16618
+ messages.push(
16619
+ markRuntimeOnlySessionMessage({
16620
+ role: "user",
16621
+ content:
16622
+ "You ended your turn with only internal reasoning and no visible " +
16623
+ "response. Please provide your actual answer now.",
16624
+ }),
16625
+ );
16561
16626
  yield { type: "empty-thinking-reprompt", runId };
16562
16627
  continue;
16563
16628
  }
@@ -16614,13 +16679,20 @@ export async function* agentLoop(messages, options) {
16614
16679
  }
16615
16680
  if (stopOutcome && stopOutcome.decision === "block") {
16616
16681
  stopHookActive = true;
16617
- messages.push({ role: "assistant", content: msg.content || "" });
16618
- messages.push({
16619
- role: "user",
16620
- content:
16621
- stopOutcome.reason ||
16622
- "A Stop hook requested that you keep working.",
16623
- });
16682
+ messages.push(
16683
+ markRuntimeOnlySessionMessage({
16684
+ role: "assistant",
16685
+ content: msg.content || "",
16686
+ }),
16687
+ );
16688
+ messages.push(
16689
+ markRuntimeOnlySessionMessage({
16690
+ role: "user",
16691
+ content:
16692
+ stopOutcome.reason ||
16693
+ "A Stop hook requested that you keep working.",
16694
+ }),
16695
+ );
16624
16696
  yield {
16625
16697
  type: "stop-hook-continue",
16626
16698
  runId,