chainlesschain 0.162.176 → 0.162.178

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 (260) hide show
  1. package/README.md +10 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BDLu5Elz.js → AIOps-DRftcSue.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DH5us-CK.js → ActionButton-BFvUaMKR.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BZ0u8IFG.js → Analytics-C0-Rru4P.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-bx-BCljj.js +9 -0
  8. package/src/assets/web-panel/assets/{Artifacts-Br2YBbh_.js → Artifacts-DAJ8B3Un.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-C3C6a482.js → Audit-hOk4L1lr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-CzHvebcj.js → BackgroundAgents-3pbDpM9z.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-DZNHYDFd.js → Backup-BtziGDn9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-VRiC4fIx.js → BaseInput-kmshphJ3.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-Cm08WWAD.js → Chat-d7tGSosA.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CGHYrugQ.js → ChatBubbleRenderer-p-_PBpX_.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-DtByk7d7.js → Checkbox-C4Eh4Tz5.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CphXXOfD.js → Codegen-r7gmhZyA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-Hz86TnLR.js → Col-U7mDaGlL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-HP03ltpD.js → Community-Bpu8aH1T.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-B_N1mqQz.js → Compact-CHb2XBJe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CVztRpi8.js → Compliance-Cn_r4dey.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-wUVOyZih.js → Cowork-CdMWfDcz.js} +4 -4
  22. package/src/assets/web-panel/assets/{Cron-DW5TLJKw.js → Cron-BtnREHWy.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-CrAFGhWH.js → Crosschain-BKI9TSzr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-pA-oD8ke.js → DID-pkNubzzJ.js} +2 -2
  25. package/src/assets/web-panel/assets/Dashboard-1rrz1snN.js +3 -0
  26. package/src/assets/web-panel/assets/{Dropdown-EweJcsek.js → Dropdown-DLyFVxDc.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-C9xVLPG4.js → EmailListRenderer-CtUWfYVq.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CDcEEqlu.js → FamilyGuardDashboard-B8eX2Qe4.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DtZda9A_.js → Federation-D8lsDGus.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-CiYdcVz3.js → FormItemContext-CRYt_aIw.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DA13uwA0.js → GenericCardRenderer-ClKzY8_N.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DzRhZD1O.js → Git-BXj5Dney.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-D5RMgOSb.js → Governance-Cm-I8RMz.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BgrPfSNk.js → Inference-CPvbhvgt.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-DzjnD571.js → KnowledgeGraph-sYNDZaoi.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BhWzVDtN.js → Logs-BZY_3CRZ.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-BDape9cx.js → MarkdownRenderer-up0BW6pv.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-DJtL5GV7.js → Marketplace-C5MEJ2Qe.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CZcQygI7.js → McpTools-ihdrn43I.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-DUWSDUUu.js → Memory-Br8bPMlh.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BTwq2WKY.js → MobileBridge-BNntgrYW.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-BdV915mw.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-BiGugEQm.js → Mtc-A3wDmlqL.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-DY7a6gUe.js → MtcAudit-CF-mBLWM.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BQbDu_w7.js → Multisig-Iy2k_MSo.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-CM2wXTLv.js → NLProgramming-kk0W7HUb.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-DI6gGs1v.js → Notes-B5u5t_4S.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-BsZOLWAB.js → NotificationSettings-BN0POf6_.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-Cds0_tXr.js → Organization-DwWQESuN.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-WunYMD87.js → Overflow-DG6klrcC.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-Bo6j4DDv.js → P2P-BwJcKkgZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bh2XPD5f.js → PdhVaultBrowser-BwOHG0in.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-EMvMMC8N.js → Permissions-CGMXe4TX.js} +4 -4
  55. package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +8 -0
  56. package/src/assets/web-panel/assets/{Pipeline-BOx8HOgL.js → Pipeline-BzJfeoGt.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-tBeTVQVM.js → Privacy-DLh3Bblx.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-CVYg1W4M.js → ProjectInit-USVWW36c.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Dvt5I7y1.js → ProjectSettings-CiiOn62h.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-Ct8xqY-E.js → Providers-CBPWWzpu.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-Cni8Ooja.js → QrScannerModal-QVwoRUq3.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BJVLKdxr.js → QuickAsk-CXnR-2Vn.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DPG8Rbbm.js → Recommend-ClaNzjmF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DsdR7dCt.js → RemoteSession-C3rGw8vJ.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Csg1YM-b.js → Reputation-Ci-OWAoP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-D9DoFoEf.js → Row-DRhAwXOS.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BsvKrXMS.js → RssFeed-BQLJQ2_y.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DFUStfgQ.js → Search-QQUpkfFd.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-ILg1zxdK.js → Security-DdXKzuKT.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-Dd41PCaY.js → Services-x2Zh62Qd.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-Bzo1cN80.js → Skeleton-GdY4KwRz.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-BDHh17P0.js → Skills-Bv4tY1Fd.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-jl7UTiRY.js → Sla-C3QnM-11.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BePa-RF6.js → SpeechSettings-Bl3ytvmI.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DiqqDKGZ.js → SyncSettings-Wp9IwTsP.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DYQ_6lxj.js → Tasks-CF3rIrDe.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-JiDeI06L.js → Templates-CGfvVVM9.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-hhgI6zwS.js → Tenant-CVXmwQ3U.js} +1 -1
  80. package/src/assets/web-panel/assets/Terminal-bvwrt03E.js +3 -0
  81. package/src/assets/web-panel/assets/{TimelineRenderer-B8uB5Llk.js → TimelineRenderer-D0g74KhH.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-C3b_6r2z.js → Tokens-BH4IwzlT.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-tIP7HKVe.js → Trigger-DJOpM533.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-vjoc11vJ.js → Trust-B43EPRLr.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DUPBnGuH.js → UkeySign-BmBFG-em.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CBvhUY5T.js → VideoEditing-n9oJwB8k.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DUaQ0uA5.js → Wallet-D5QeivHp.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DllSivjE.js → WebAuthn-DcNy7igO.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-xc8D3uO6.js → WorkflowEditor-BVjc7-5-.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-BVicU7oA.js → chat-Dy5Tfj3m.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-DJzLmhLm.js → colors-C4c2CNZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-BvYRfThE.js → compact-item-DPa3O5vN.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DGXxCBKR.js → createContext-TLcfNA6_.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-SBDJB1y4.js → hasIn-D41LVFkp.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-D7HimK0M.js → index-0oevYjfT.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DCdRAgjy.js → index-388pLGJ1.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BO1itZaY.js → index-B8nLBZbv.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-DvUTDYXX.js → index-BPTGAjee.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-hxTODbM1.js → index-BPtrOBmW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-wcCL-miV.js → index-BQiIxHBy.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CKyEivf8.js → index-BSM1cHVK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BJZf9DhV.js → index-BYPr9fkz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DHVLJ8q0.js → index-BsQ7Ev6i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cs2rmLFt.js → index-Btk9qLL2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-z1ZylsLq.js → index-BvmziZSG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Cf-O_g_8.js → index-BvopLC_T.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DMarmfrv.js → index-C7xhTVyP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CoJAO1Lm.js → index-CNk_ZooP.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-9gdI7uUW.js → index-CQrEHoOG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bf-ctERC.js → index-CSxex23J.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-MjC4G09d.js → index-CVjgm992.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-e4fXH_WO.js → index-CWK4zOPe.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-xfluRkCT.js → index-CX0VsoP4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DRfiXB_p.js → index-CXl0YPNG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-d5BVd-cL.js → index-Cip7LWml.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Dm_77jgR.js → index-Cq_hlWNt.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-YJkzy8ZF.js → index-CzZ49YZL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CtwqO5qo.js → index-D2Nkivlm.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BYqz4Wrr.js → index-DBUgRs4D.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DEjVkdzq.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CdGY9P8a.js → index-DNsBj2Ob.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DonCnfMr.js → index-Di4X8cfI.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-1SdDS6lR.js → index-DljFsVGI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DnjPdvR6.js → index-Korc3QIe.js} +1 -1
  126. package/src/assets/web-panel/assets/index-LMOc5xrS.js +1 -0
  127. package/src/assets/web-panel/assets/{index-VMfwIH5G.js → index-MKZDje0H.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DKrvvWm4.js → index-OHAfRu_3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-FxZzRyE1.js → index-WEJr8PL5.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Crk5NTb0.js → index-WinCp6du.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DCqRS6da.js → index-Yjr6cqSK.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BTrBGBJs.js → index-iQ6gtFih.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-lpaGoTH3.js → index-pt3s7yvV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CloCgBUR.js → index-tQ3igERQ.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-C1zO1KnY.js → initDefaultProps-BFZu87WZ.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-CWLtEGmC.js → motion-DJIo7qvt.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DzD_C3Vm.js → move-Cfr5qknL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-D880PYJl.js → mtc-parser-Bgc-OadH.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-CFR_z2ov.js → omit-X3WI-TNp.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-srIhgrSC.js → pickAttrs-pJhU9IxK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-C1rXpibP.js → placementArrow-BNg_Vj3T.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-C30gRcSS.js → responsiveObserve-TeSEGQ5-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CvtKiMfV.js → slide-D_t_P0bv.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-DNHO7ITG.js → statusUtils-DrrIMwAT.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Dt_Sl-g4.js → styleChecker-BjZCuFRm.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-Cjev42Gh.js → useFlexGapSupport-rTFdlqdQ.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-Baxglj2V.js → useFs-BidCpfLX.js} +1 -1
  148. package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +1 -0
  149. package/src/assets/web-panel/assets/{vnode-mIRkdT7o.js → vnode-BrbcRiHZ.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-HzI5DlH_.js → zoom-PISX7AkR.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/__tests__/hub-wechat.test.js +45 -0
  153. package/src/commands/agenda.js +22 -6
  154. package/src/commands/agent.js +3 -3
  155. package/src/commands/background-session.js +16 -6
  156. package/src/commands/batch.js +79 -27
  157. package/src/commands/changelog.js +15 -4
  158. package/src/commands/context.js +33 -1
  159. package/src/commands/eval.js +21 -6
  160. package/src/commands/hook.js +2 -2
  161. package/src/commands/hub.js +473 -39
  162. package/src/commands/init.js +212 -110
  163. package/src/commands/llm.js +22 -6
  164. package/src/commands/loop.js +9 -2
  165. package/src/commands/skill.js +13 -1
  166. package/src/commands/statusline.js +3 -3
  167. package/src/data/changelog.json +53 -5
  168. package/src/gateways/ws/personal-data-hub-protocol.js +22 -2
  169. package/src/harness/background-task-manager.js +18 -6
  170. package/src/harness/background-task-worker.js +11 -18
  171. package/src/harness/golden-transcript.js +3 -0
  172. package/src/harness/mcp-client.js +9 -15
  173. package/src/harness/worktree-isolator.js +18 -21
  174. package/src/index.js +18 -7
  175. package/src/lib/agent-core.js +5 -0
  176. package/src/lib/agent-ipc-bus.js +12 -3
  177. package/src/lib/agent-team/team-worktree.js +28 -6
  178. package/src/lib/agent-worktree.js +20 -19
  179. package/src/lib/approval-authority-store.js +891 -0
  180. package/src/lib/async-hook-supervisor.cjs +134 -24
  181. package/src/lib/async-hook-supervisor.js +47 -0
  182. package/src/lib/background-agent-supervisor.js +35 -12
  183. package/src/lib/background-interaction-resolver.js +399 -54
  184. package/src/lib/background-session-transport.js +3 -1
  185. package/src/lib/background-task-manager.js +5 -1
  186. package/src/lib/changelog.js +57 -0
  187. package/src/lib/chrome-connector.js +6 -2
  188. package/src/lib/claude-code-bridge.js +24 -8
  189. package/src/lib/cli-anything-bridge.js +82 -25
  190. package/src/lib/context-breakdown.js +82 -3
  191. package/src/lib/doctor-checkup.js +18 -6
  192. package/src/lib/eval/tasks.js +25 -31
  193. package/src/lib/event-runtime-host.js +225 -0
  194. package/src/lib/event-runtime-store.js +102 -21
  195. package/src/lib/event-runtime-worker.js +70 -2
  196. package/src/lib/hook-runner.cjs +71 -36
  197. package/src/lib/hook-runner.js +55 -0
  198. package/src/lib/hooks-v2-producers.js +33 -8
  199. package/src/lib/hooks-v2-runtime.js +360 -40
  200. package/src/lib/host-adb-bridge.js +18 -3
  201. package/src/lib/interaction-adapter.js +66 -0
  202. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  203. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  204. package/src/lib/lsp/benchmark.js +20 -4
  205. package/src/lib/lsp/lsp-client.js +6 -4
  206. package/src/lib/lsp/lsp-server-registry.js +0 -2
  207. package/src/lib/operation-fingerprint.js +107 -2
  208. package/src/lib/orchestrator.js +8 -2
  209. package/src/lib/packer/pack-update-applier.js +14 -2
  210. package/src/lib/packer/smoke-runner.js +18 -5
  211. package/src/lib/paths.js +1 -1
  212. package/src/lib/pdh-dedicated-batch-collectors.js +124 -0
  213. package/src/lib/personal-data-hub-wiring.js +327 -52
  214. package/src/lib/plugin-monitor-supervisor.js +20 -13
  215. package/src/lib/plugin-runtime/install.js +10 -3
  216. package/src/lib/process-execution-broker/credential-agent.js +954 -111
  217. package/src/lib/process-execution-broker/index.js +369 -85
  218. package/src/lib/process-execution-broker/platform-sandbox.js +320 -140
  219. package/src/lib/provider-http-error.js +94 -0
  220. package/src/lib/remote-approval-bridge.js +222 -26
  221. package/src/lib/repl-bang-memorize.js +10 -2
  222. package/src/lib/repl-goal.js +19 -2
  223. package/src/lib/settings-hook-events.cjs +13 -8
  224. package/src/lib/settings-hook-events.js +20 -0
  225. package/src/lib/side-effect-ledger-store.js +65 -14
  226. package/src/lib/skill-loader.js +215 -12
  227. package/src/lib/skill-packs/generator.js +18 -9
  228. package/src/lib/skill-process-broker.js +85 -0
  229. package/src/lib/slash-commands.js +10 -2
  230. package/src/lib/status-line.cjs +28 -7
  231. package/src/lib/status-line.js +33 -0
  232. package/src/lib/sub-agent-context.js +42 -0
  233. package/src/lib/turn-binding-store.js +64 -15
  234. package/src/lib/turn-binding.js +85 -16
  235. package/src/repl/agent-repl.js +88 -49
  236. package/src/repl/remote-approval.js +13 -13
  237. package/src/repl/repl-turn-binding.js +46 -16
  238. package/src/runtime/agent-core.js +419 -84
  239. package/src/runtime/coding-agent-contract-shared.cjs +16 -0
  240. package/src/runtime/headless-runner.js +172 -44
  241. package/src/runtime/headless-stream.js +221 -11
  242. package/src/runtime/session-slash-commands.js +506 -0
  243. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  244. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  245. package/src/skills/video-editing/media-process.js +20 -0
  246. package/src/skills/video-editing/render/audio-mix.js +13 -3
  247. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  248. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  249. package/src/workers/background-agent-worker.js +76 -40
  250. package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +0 -9
  251. package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +0 -3
  252. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +0 -1
  253. package/src/assets/web-panel/assets/OrderTableRenderer-C48mu09U.js +0 -1
  254. package/src/assets/web-panel/assets/PersonalDataHub-AX3ypUDV.js +0 -9
  255. package/src/assets/web-panel/assets/Projects-Cc7FC9-e.js +0 -1
  256. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +0 -3
  257. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +0 -1
  258. package/src/assets/web-panel/assets/index-B7nuIywP.js +0 -1
  259. package/src/assets/web-panel/assets/index-ycwAfC-i.js +0 -1
  260. package/src/assets/web-panel/assets/usePersonalDataHub-B4NkcLaP.js +0 -1
@@ -183,12 +183,12 @@ function persistRecentDenial(record, options = {}) {
183
183
  * user's attention (e.g. waiting on a permission/risk confirmation). A hook can
184
184
  * ring a bell / send a desktop notification. Best-effort, never blocks.
185
185
  */
186
- async function _fireNotification(message, type = "info") {
186
+ async function _fireNotification(message, type = "info", session = null) {
187
187
  // Fire settings.json observe hooks first (best-effort)
188
188
  if (_settingsHooks) {
189
189
  try {
190
190
  const { runObserveHooks } =
191
- await import("../lib/settings-hook-events.cjs");
191
+ await import("../lib/settings-hook-events.js");
192
192
  runObserveHooks(
193
193
  _settingsHooks,
194
194
  "Notification",
@@ -196,7 +196,7 @@ async function _fireNotification(message, type = "info") {
196
196
  message,
197
197
  type,
198
198
  cwd: process.cwd(),
199
- session_id: _session?.sessionId || null,
199
+ session_id: session?.sessionId || null,
200
200
  },
201
201
  { cwd: process.cwd() },
202
202
  );
@@ -205,10 +205,10 @@ async function _fireNotification(message, type = "info") {
205
205
  }
206
206
  }
207
207
  // Fire database-registered Notification hooks (interceptable/suppressible)
208
- if (_session) {
208
+ if (session) {
209
209
  try {
210
210
  const result = await fireNotification(_hookDb, message, {
211
- session: _session,
211
+ session,
212
212
  type,
213
213
  });
214
214
  if (result?.directive?.suppress) {
@@ -750,6 +750,8 @@ export async function startAgentRepl(options = {}) {
750
750
  }
751
751
  await _fireNotification(
752
752
  `Permission needed: ${riskLevel || "medium"}-risk ${tool || "run_shell"}${args?.command ? " — " + args.command : ""}`,
753
+ "info",
754
+ _sessionHandle,
753
755
  );
754
756
  const local = askGateApprovalLocally({ tool, args, riskLevel });
755
757
  if (!_remoteApproval?.bridge) return local.promise;
@@ -840,6 +842,8 @@ export async function startAgentRepl(options = {}) {
840
842
  }
841
843
  await _fireNotification(
842
844
  `Permission needed: ${tool}${rule ? " (" + rule + ")" : ""}`,
845
+ "info",
846
+ _sessionHandle,
843
847
  );
844
848
  const local = askPermissionLocally({ tool, args, rule, reason });
845
849
  if (!_remoteApproval?.bridge) return local.promise;
@@ -1119,8 +1123,10 @@ export async function startAgentRepl(options = {}) {
1119
1123
  // P1 explicit turn→checkpoint binding — the REPL as PRODUCER (mirrors the
1120
1124
  // headless runner's feed via the shared feeder core): one record per
1121
1125
  // interactive turn, persisted dirty-gated at each settle. Lazily created on
1122
- // the first prompt turn of a persistable (JSONL) session; advisory only.
1126
+ // the first prompt turn of a persistable (JSONL) session. This is Critical
1127
+ // recovery state: a failure blocks subsequent model/tool turns.
1123
1128
  let _turnBindingProducer = null;
1129
+ let _turnBindingCriticalError = null;
1124
1130
  // Apply --output-style or the settings.json `outputStyle` default at startup.
1125
1131
  try {
1126
1132
  const { resolveOutputStyle } = await import("../lib/output-styles.js");
@@ -1142,7 +1148,7 @@ export async function startAgentRepl(options = {}) {
1142
1148
  if (_settingsHooks && _loadedReplInstructions) {
1143
1149
  try {
1144
1150
  const { runInstructionsLoadedHooks } =
1145
- await import("../lib/settings-hook-events.cjs");
1151
+ await import("../lib/settings-hook-events.js");
1146
1152
  const ctx = runInstructionsLoadedHooks(_settingsHooks, {
1147
1153
  files: _loadedReplInstructions.files,
1148
1154
  cwd: process.cwd(),
@@ -1158,7 +1164,7 @@ export async function startAgentRepl(options = {}) {
1158
1164
  if (_settingsHooks) {
1159
1165
  try {
1160
1166
  const { runSessionStartHooks } =
1161
- await import("../lib/settings-hook-events.cjs");
1167
+ await import("../lib/settings-hook-events.js");
1162
1168
  const ctx = runSessionStartHooks(_settingsHooks, {
1163
1169
  source: "startup",
1164
1170
  cwd: process.cwd(),
@@ -1466,7 +1472,7 @@ export async function startAgentRepl(options = {}) {
1466
1472
  if (_settingsHooks && messages.some((m) => m.role !== "system")) {
1467
1473
  try {
1468
1474
  const { runObserveHooks } =
1469
- await import("../lib/settings-hook-events.cjs");
1475
+ await import("../lib/settings-hook-events.js");
1470
1476
  runObserveHooks(
1471
1477
  _settingsHooks,
1472
1478
  "SessionResume",
@@ -1764,31 +1770,34 @@ export async function startAgentRepl(options = {}) {
1764
1770
 
1765
1771
  // MCP elicitation in the interactive REPL uses the same readline surface as
1766
1772
  // approvals, while MCPClient retains the protocol-level response shape.
1767
- const mcpElicitationClients = [
1768
- _adhocMcp?.mcpClient,
1769
- _bundleMcpClient,
1770
- ].filter((client) => client?.setElicitationHandler);
1773
+ const mcpElicitationClients = [_adhocMcp?.mcpClient, _bundleMcpClient].filter(
1774
+ (client) => client?.setElicitationHandler,
1775
+ );
1771
1776
  for (const client of mcpElicitationClients) {
1772
1777
  client.setElicitationHandler(
1773
1778
  (request) =>
1774
1779
  new Promise((resolve) => {
1775
- const label = request.message || "MCP server requests additional input";
1776
- rl.question(chalk.yellow(`\n MCP [${request.server}] ${label}\n > `), (answer) => {
1777
- const trimmed = String(answer || "").trim();
1778
- if (!trimmed) {
1779
- resolve({ action: "cancel" });
1780
- } else {
1781
- let content = { value: trimmed };
1782
- try {
1783
- const parsed = JSON.parse(trimmed);
1784
- if (parsed && typeof parsed === "object") content = parsed;
1785
- } catch {
1786
- // Plain text remains a valid single-field elicitation answer.
1780
+ const label =
1781
+ request.message || "MCP server requests additional input";
1782
+ rl.question(
1783
+ chalk.yellow(`\n MCP [${request.server}] ${label}\n > `),
1784
+ (answer) => {
1785
+ const trimmed = String(answer || "").trim();
1786
+ if (!trimmed) {
1787
+ resolve({ action: "cancel" });
1788
+ } else {
1789
+ let content = { value: trimmed };
1790
+ try {
1791
+ const parsed = JSON.parse(trimmed);
1792
+ if (parsed && typeof parsed === "object") content = parsed;
1793
+ } catch {
1794
+ // Plain text remains a valid single-field elicitation answer.
1795
+ }
1796
+ resolve({ action: "accept", content });
1787
1797
  }
1788
- resolve({ action: "accept", content });
1789
- }
1790
- rl.prompt();
1791
- });
1798
+ rl.prompt();
1799
+ },
1800
+ );
1792
1801
  }),
1793
1802
  { timeoutMs: options.mcpElicitationTimeoutMs },
1794
1803
  );
@@ -1896,7 +1905,7 @@ export async function startAgentRepl(options = {}) {
1896
1905
  // turn — the session stays open, the current work is suspended. The
1897
1906
  // keypress handler can't await, so fire observe-only fire-and-forget.
1898
1907
  if (_settingsHooks) {
1899
- import("../lib/settings-hook-events.cjs")
1908
+ import("../lib/settings-hook-events.js")
1900
1909
  .then(({ runObserveHooks }) =>
1901
1910
  runObserveHooks(
1902
1911
  _settingsHooks,
@@ -2058,7 +2067,7 @@ export async function startAgentRepl(options = {}) {
2058
2067
  const _costStore = newCostStore(); // running token spend for `/cost`
2059
2068
  let _renderStatus = null;
2060
2069
  try {
2061
- const slm = await import("../lib/status-line.cjs");
2070
+ const slm = await import("../lib/status-line.js");
2062
2071
  const _sl = slm.default || slm;
2063
2072
  const _slCfg = _sl.loadStatusLineConfig({ cwd: process.cwd() });
2064
2073
  _customStatus = !!_slCfg;
@@ -3153,12 +3162,12 @@ export async function startAgentRepl(options = {}) {
3153
3162
  if (_settingsHooks && oldCwd !== newCwd) {
3154
3163
  try {
3155
3164
  const { runCwdChangedHooks, dispatchAsyncHooks } =
3156
- await import("../lib/settings-hook-events.cjs");
3165
+ await import("../lib/settings-hook-events.js");
3157
3166
  const payload = { oldCwd, newCwd, sessionId };
3158
3167
  runCwdChangedHooks(_settingsHooks, payload);
3159
3168
  if (!_asyncHookSupervisor) {
3160
3169
  const { AsyncHookSupervisor } =
3161
- await import("../lib/async-hook-supervisor.cjs");
3170
+ await import("../lib/async-hook-supervisor.js");
3162
3171
  _asyncHookSupervisor = new AsyncHookSupervisor({
3163
3172
  persistStats: true,
3164
3173
  });
@@ -3643,6 +3652,11 @@ export async function startAgentRepl(options = {}) {
3643
3652
  _sanitizeRolesNextTurn =
3644
3653
  messages[messages.length - 1]?.role === "user";
3645
3654
  sessionId = resumeId;
3655
+ // The producer owns one immutable session id. Recreate it lazily
3656
+ // on the next turn so bindings from the resumed conversation can
3657
+ // never be appended to the previously active session.
3658
+ _turnBindingProducer = null;
3659
+ _turnBindingCriticalError = null;
3646
3660
  // The prior session's checkpoint marks (turn-index → checkpoint id)
3647
3661
  // no longer map to this swapped-in history; keeping them would make
3648
3662
  // a later /rewind restore files from the WRONG session's snapshot.
@@ -3664,6 +3678,8 @@ export async function startAgentRepl(options = {}) {
3664
3678
  _sanitizeRolesNextTurn =
3665
3679
  messages[messages.length - 1]?.role === "user";
3666
3680
  sessionId = existing.id;
3681
+ _turnBindingProducer = null;
3682
+ _turnBindingCriticalError = null;
3667
3683
  // Drop the prior session's checkpoint marks (see JSONL branch).
3668
3684
  _checkpointMarks.length = 0;
3669
3685
  _clearedConversation = null; // stash belongs to the swapped-out session
@@ -3739,7 +3755,7 @@ export async function startAgentRepl(options = {}) {
3739
3755
  if (_settingsHooks) {
3740
3756
  try {
3741
3757
  const { runObserveHooks, dispatchAsyncHooks } =
3742
- await import("../lib/settings-hook-events.cjs");
3758
+ await import("../lib/settings-hook-events.js");
3743
3759
  const payload = {
3744
3760
  session_id: sessionId || null,
3745
3761
  source: "reload-plugins",
@@ -3747,7 +3763,7 @@ export async function startAgentRepl(options = {}) {
3747
3763
  runObserveHooks(_settingsHooks, "ConfigChange", payload, { cwd });
3748
3764
  if (!_asyncHookSupervisor) {
3749
3765
  const { AsyncHookSupervisor } =
3750
- await import("../lib/async-hook-supervisor.cjs");
3766
+ await import("../lib/async-hook-supervisor.js");
3751
3767
  _asyncHookSupervisor = new AsyncHookSupervisor({
3752
3768
  persistStats: true,
3753
3769
  });
@@ -5099,7 +5115,7 @@ export async function startAgentRepl(options = {}) {
5099
5115
  if (_settingsHooks) {
5100
5116
  try {
5101
5117
  const { runUserPromptSubmitHooks } =
5102
- await import("../lib/settings-hook-events.cjs");
5118
+ await import("../lib/settings-hook-events.js");
5103
5119
  const ups = runUserPromptSubmitHooks(_settingsHooks, {
5104
5120
  prompt: userContent,
5105
5121
  cwd: process.cwd(),
@@ -5122,10 +5138,10 @@ export async function startAgentRepl(options = {}) {
5122
5138
  // turn without blocking it; its result/rewake surfaces on a later turn
5123
5139
  // via the async-hook drain below. Lazily create the supervisor.
5124
5140
  const { dispatchAsyncHooks } =
5125
- await import("../lib/settings-hook-events.cjs");
5141
+ await import("../lib/settings-hook-events.js");
5126
5142
  if (!_asyncHookSupervisor) {
5127
5143
  const { AsyncHookSupervisor } =
5128
- await import("../lib/async-hook-supervisor.cjs");
5144
+ await import("../lib/async-hook-supervisor.js");
5129
5145
  _asyncHookSupervisor = new AsyncHookSupervisor({
5130
5146
  persistStats: true,
5131
5147
  });
@@ -5260,6 +5276,15 @@ export async function startAgentRepl(options = {}) {
5260
5276
  }
5261
5277
  }
5262
5278
 
5279
+ if (_turnBindingCriticalError) {
5280
+ logger.error(
5281
+ `[recovery] critical turn-binding state is unavailable: ${_turnBindingCriticalError.message}. ` +
5282
+ "Resume or start a healthy session before running another model/tool turn.",
5283
+ );
5284
+ prompt();
5285
+ return;
5286
+ }
5287
+
5263
5288
  // Add user message (keep the object ref so a /btw aside can be injected for
5264
5289
  // this turn's model call and then stripped before persistence).
5265
5290
  const _userMsg = { role: "user", content: _userMessageContent };
@@ -5276,11 +5301,26 @@ export async function startAgentRepl(options = {}) {
5276
5301
  if (!_turnBindingProducer) {
5277
5302
  const { createReplTurnBindingProducer } =
5278
5303
  await import("./repl-turn-binding.js");
5279
- _turnBindingProducer = createReplTurnBindingProducer({ sessionId });
5304
+ _turnBindingProducer = createReplTurnBindingProducer({
5305
+ sessionId,
5306
+ onError: (error, phase) => {
5307
+ _turnBindingCriticalError = error;
5308
+ logger.warn(
5309
+ `[recovery] turn binding ${phase} failed for ${sessionId}: ${error.message}`,
5310
+ );
5311
+ },
5312
+ });
5280
5313
  }
5281
5314
  _turnBindingProducer?.beginTurn(messages.length);
5282
- } catch {
5283
- /* advisory — never disturb the turn */
5315
+ } catch (error) {
5316
+ _turnBindingCriticalError =
5317
+ error instanceof Error ? error : new Error(String(error));
5318
+ if (messages[messages.length - 1] === _userMsg) messages.pop();
5319
+ logger.error(
5320
+ `[recovery] refusing the turn because binding state is unavailable: ${_turnBindingCriticalError.message}`,
5321
+ );
5322
+ prompt();
5323
+ return;
5284
5324
  }
5285
5325
  }
5286
5326
 
@@ -5433,7 +5473,7 @@ export async function startAgentRepl(options = {}) {
5433
5473
  // fire-and-forget; their results/rewakes drain into the next turn above.
5434
5474
  if (_settingsHooks && !_asyncHookSupervisor) {
5435
5475
  const { AsyncHookSupervisor } =
5436
- await import("../lib/async-hook-supervisor.cjs");
5476
+ await import("../lib/async-hook-supervisor.js");
5437
5477
  _asyncHookSupervisor = new AsyncHookSupervisor({ persistStats: true });
5438
5478
  }
5439
5479
  const {
@@ -5579,7 +5619,6 @@ export async function startAgentRepl(options = {}) {
5579
5619
  try {
5580
5620
  const rg = await import("../lib/repl-goal.js");
5581
5621
  const fsMod = await import("node:fs");
5582
- const cpMod = await import("node:child_process");
5583
5622
  const judge = async (cond, { finalText }) => {
5584
5623
  const { chatWithTools } = await import("../runtime/agent-core.js");
5585
5624
  const { firstBalancedJson } =
@@ -5621,7 +5660,6 @@ export async function startAgentRepl(options = {}) {
5621
5660
  response || "",
5622
5661
  {
5623
5662
  cwd: process.cwd(),
5624
- spawnSync: cpMod.spawnSync,
5625
5663
  existsSync: fsMod.existsSync,
5626
5664
  judge,
5627
5665
  },
@@ -5717,8 +5755,9 @@ export async function startAgentRepl(options = {}) {
5717
5755
  // Non-critical
5718
5756
  }
5719
5757
  }
5720
- // Persist the explicit turn-binding table for this settled turn
5721
- // (dirty-gated: a tool-free Q&A turn writes nothing; never throws).
5758
+ // Persist the explicit turn-binding table for this settled turn,
5759
+ // including tool-free turns. A failure is Critical: the producer throws,
5760
+ // this turn reports an error, and the guard above blocks later turns.
5722
5761
  _turnBindingProducer?.persistIfDirty();
5723
5762
  // Auto-compact when context grows too large
5724
5763
  if (
@@ -5772,7 +5811,7 @@ export async function startAgentRepl(options = {}) {
5772
5811
  if (_settingsHooks) {
5773
5812
  try {
5774
5813
  const { runObserveHooks, dispatchAsyncHooks } =
5775
- await import("../lib/settings-hook-events.cjs");
5814
+ await import("../lib/settings-hook-events.js");
5776
5815
  runObserveHooks(
5777
5816
  _settingsHooks,
5778
5817
  "Stop",
@@ -5781,7 +5820,7 @@ export async function startAgentRepl(options = {}) {
5781
5820
  );
5782
5821
  if (!_asyncHookSupervisor) {
5783
5822
  const { AsyncHookSupervisor } =
5784
- await import("../lib/async-hook-supervisor.cjs");
5823
+ await import("../lib/async-hook-supervisor.js");
5785
5824
  _asyncHookSupervisor = new AsyncHookSupervisor({
5786
5825
  persistStats: true,
5787
5826
  });
@@ -5879,7 +5918,7 @@ export async function startAgentRepl(options = {}) {
5879
5918
  if (_settingsHooks) {
5880
5919
  try {
5881
5920
  const { runObserveHooks } =
5882
- await import("../lib/settings-hook-events.cjs");
5921
+ await import("../lib/settings-hook-events.js");
5883
5922
  runObserveHooks(
5884
5923
  _settingsHooks,
5885
5924
  "SessionEnd",
@@ -8,10 +8,9 @@
8
8
  * publishes permission.resolved so the phone/web UI clears it)
9
9
  * - remote answer first → cancel the local readline prompt and print
10
10
  * the outcome
11
- * - remote leg times out or the bridge closes mid-ask → NOT a decision:
12
- * keep waiting on the terminal. A silent phone must never auto-deny a
13
- * user sitting at the keyboard — the fail-closed timeout is headless
14
- * semantics only.
11
+ * - remote leg times out or the bridge closes mid-ask → NOT a remote human
12
+ * decision, so keep waiting on the terminal. A later local answer still
13
+ * needs the bridge's durable CAS; an expired/closed card cannot be revived.
15
14
  *
16
15
  * Pure over an injected bridge + local prompt handle ({promise, cancel}),
17
16
  * so the race is unit-testable without a WS server or readline.
@@ -46,8 +45,9 @@ export function describeAskContext({
46
45
  };
47
46
  }
48
47
 
49
- // Remote outcomes that are NOT a decision for an interactive session: the
50
- // devices went silent (timeout) or the bridge was torn down mid-ask (closed).
48
+ // These outcomes are not a remote human decision. The terminal may still
49
+ // answer, but its result is accepted only if resolveLocally can durably settle
50
+ // the same live card.
51
51
  const NON_DECISIVE_REMOTE = new Set(["timeout", "closed"]);
52
52
 
53
53
  /**
@@ -90,14 +90,14 @@ export async function raceLocalAndRemote({
90
90
  winner = await localLeg;
91
91
  }
92
92
  if (winner.src === "local") {
93
- if (requestId) {
94
- try {
95
- bridge.resolveLocally(requestId, winner.approved);
96
- } catch {
97
- // clearing the device card is best-effort
98
- }
93
+ if (!requestId) return false;
94
+ try {
95
+ const persisted = bridge.resolveLocally(requestId, winner.approved);
96
+ return persisted === true && winner.approved === true;
97
+ } catch {
98
+ // A local "yes" without a durable CAS is not an approval.
99
+ return false;
99
100
  }
100
- return winner.approved;
101
101
  }
102
102
  try {
103
103
  local.cancel?.();
@@ -14,10 +14,9 @@
14
14
  * anchored at/after its own offset — under `pickPersistedTurn`'s
15
15
  * exact-offset matching a stale record from the discarded timeline would
16
16
  * otherwise shadow the new turn and could offer the wrong checkpoint.
17
- * 3. Per-settle persistence: dirty-gated `persistTurnBinding` after each turn
18
- * (a tool-free Q&A turn writes nothing), best-effort — the producer is
19
- * advisory and must NEVER disturb the turn itself, so every method
20
- * swallows its own failures.
17
+ * 3. Per-settle persistence: `persistTurnBinding` after each turn, including
18
+ * tool-free turns. Failures are reported through `onError` and fail closed
19
+ * by default; tests/diagnostics may explicitly request advisory mode.
21
20
  *
22
21
  * I/O goes through `_deps` so tests drive the producer hermetically.
23
22
  */
@@ -37,15 +36,35 @@ export const _deps = {
37
36
  };
38
37
 
39
38
  /**
40
- * @param {{sessionId?: string, nonce?: string}} [opts]
39
+ * @param {{sessionId?: string, nonce?: string, failClosed?:boolean,
40
+ * onError?:(error:Error, phase:string)=>void}} [opts]
41
41
  * @returns {object|null} null when the session cannot persist (no sessionId)
42
42
  */
43
- export function createReplTurnBindingProducer({ sessionId, nonce } = {}) {
43
+ export function createReplTurnBindingProducer({
44
+ sessionId,
45
+ nonce,
46
+ onError,
47
+ failClosed = true,
48
+ } = {}) {
44
49
  if (!sessionId) return null;
45
50
  // Per-process nonce (mirrors the headless runner's per-run nonce): restored
46
51
  // turn ids keep their original nonce, new ids can't collide across resumes.
47
52
  const runNonce = nonce || String(_deps.now());
48
53
  let feed = null;
54
+ let lastError = null;
55
+ const report = (error, phase) => {
56
+ lastError = error instanceof Error ? error : new Error(String(error));
57
+ try {
58
+ onError?.(lastError, phase);
59
+ } catch {
60
+ // Reporting must not replace the underlying recovery warning.
61
+ }
62
+ };
63
+ const rejectOr = (error, phase, fallback) => {
64
+ report(error, phase);
65
+ if (failClosed) throw lastError;
66
+ return fallback;
67
+ };
49
68
  return {
50
69
  /**
51
70
  * Anchor a new interactive turn. Call just AFTER the user message was
@@ -61,8 +80,8 @@ export function createReplTurnBindingProducer({ sessionId, nonce } = {}) {
61
80
  });
62
81
  }
63
82
  return feed.beginTurn(conversationOffset, { supersede: true });
64
- } catch {
65
- return null; // advisory — never disturb the turn
83
+ } catch (error) {
84
+ return rejectOr(error, "load", null);
66
85
  }
67
86
  },
68
87
  /** Fold one agent-loop event into the current turn (fed by agentLoop). */
@@ -70,21 +89,32 @@ export function createReplTurnBindingProducer({ sessionId, nonce } = {}) {
70
89
  if (!feed) return false;
71
90
  try {
72
91
  return feed.handleEvent(event);
73
- } catch {
74
- return false;
92
+ } catch (error) {
93
+ return rejectOr(error, "event", false);
75
94
  }
76
95
  },
77
- /** Persist the table after a settled turn (dirty-gated, best-effort). */
96
+ /** Persist the table after a settled turn (dirty-gated, fail-closed). */
78
97
  persistIfDirty() {
79
98
  if (!feed || !feed.isDirty()) return false;
99
+ let ok = false;
80
100
  try {
81
- const ok = _deps.persistTurnBinding(sessionId, feed.log);
82
- if (ok) feed.clearDirty();
83
- return ok;
84
- } catch {
85
- return false;
101
+ ok = _deps.persistTurnBinding(sessionId, feed.log);
102
+ } catch (error) {
103
+ return rejectOr(error, "persist", false);
104
+ }
105
+ if (!ok) {
106
+ return rejectOr(
107
+ new Error("turn-binding snapshot was not persisted"),
108
+ "persist",
109
+ false,
110
+ );
86
111
  }
112
+ feed.clearDirty();
113
+ lastError = null;
114
+ return true;
87
115
  },
116
+ lastError: () => lastError,
117
+ sessionId: () => sessionId,
88
118
  /** The live table (for tests / future in-process consumers). */
89
119
  log: () => (feed ? feed.log : null),
90
120
  };