chainlesschain 0.162.180 → 0.162.182

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 (263) hide show
  1. package/package.json +7 -3
  2. package/scripts/build-windows-sandbox-helper.ps1 +163 -0
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-DzZ5_xoj.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-DeLXddIg.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-rvDCmet1.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-Yt6QVDnp.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-BS112K5L.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-0wJuQrNu.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-B90PMgbn.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-TkgMglQQ.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-CQVS9p1l.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-YTjQHoC7.js} +6 -6
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-5bwaOabq.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-BFmNQ_Xe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-BT_u4Dzp.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-CJP52u5b.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BpzhJE9i.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-Cg7rfslp.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-JisxL6Yy.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-Bu7Ug_MC.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CvuqoLdj.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-C2u9G8Pu.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-BFt5Yv3d.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-DIaT9Wxq.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-D_A601Y4.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard--IyQ8I8w.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-BihWHxuY.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-BB8dOXc_.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-DQYhj1HH.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-B-2QZxGf.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-CusSb-nG.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BAjmvPPL.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-DoNYWKYq.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-B8fKWD-n.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-BlJm1J7E.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-CyOscbSL.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-Ex6Om5da.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CDcD8l8r.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-xfBqoFDg.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-BwObMMA2.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-CIWd36OV.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPX0ouuA.js} +4 -4
  45. package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-BDJsIUuW.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-DEkbuD3N.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-DJpHRhH1.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-C1UZ-Qd5.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-GlJtqLjT.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-Bbhj0fac.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-1H04x3kx.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CDpPe3et.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-MUX0Bpvw.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-A07-0Ci1.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-DhhDWxAZ.js} +5 -5
  56. package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-CyfbJfe-.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-CNFps3ED.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit--T-u3Dr5.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-DJmrK2fC.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-DLzzbOZd.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-CexOomKS.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-QklGi19_.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-Bb9kwSw8.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-DZuVNNN5.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-Dr5cLP0N.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-oCQHYv5t.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-IFDEykIR.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-CspVIxDc.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Bm66o_DH.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-DvanNqXl.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-CHjEe9_O.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-Cb8jWjMX.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-DxZm1491.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-DWxEEclC.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-FIvOFw5p.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-CaNjzV_t.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-DjlP6TmH.js} +1 -1
  80. package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +3 -0
  81. package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-DoZ8y7y8.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-390cg2TC.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-CLrtCj5_.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-DpESfSPn.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-CN6279O0.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DPtAUlSe.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-nKOMV_1n.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-HuXlSOc4.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-CUfFjARi.js} +1 -1
  90. package/src/assets/web-panel/assets/chat-6Eq0PcZJ.js +1 -0
  91. package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BP_Sf6Iy.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-BufMKIh5.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BBMXxWSW.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-fUK_tRO2.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-2A5ph8b2.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-3FuwBewR.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-53z1YUxX.js → index-6GmBa4Kg.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-B2vS66eI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-B7Etk0Te.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-B9WYSn4m.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-BHy6yNiF.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-BMLHv389.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-BWKoBU8R.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-Bkg4ZePE.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-C44atv4k.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-CE2DdTP2.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-CGjmILDm.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-CW_G-MXw.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-CZ6Yt17L.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-CeASi4fr.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-CgiECHu2.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Cp8vhyZy.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-CwSf5pnf.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-D4_MuGkd.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-D9vU25ij.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-DASluSqY.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-DLpIs_P_.js} +1 -1
  119. package/src/assets/web-panel/assets/index-DOZTSoS7.js +1 -0
  120. package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DPdUNk1i.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-DVsqvI4M.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DhpvjpqI.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-DmkMXwnR.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-DocdRcbd.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-Dyngbd29.js} +20 -20
  126. package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-GwdRUZ-x.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-LvCBfTLo.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DhXay17G.js → index-cpEli44X.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-fzaMtSJ8.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-pum7HYBA.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-t8N1tSeX.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D5IytTND.js → index-uCbEN7cY.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-wKTLbKyc.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-wV6iGdpY.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-V5Fo95FM.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-BYWU0hUT.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Cr1M-J2h.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-BysQKi2N.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-D1zHU8GC.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-CUArxtfi.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-ChoYEys0.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-BQRs3Mo7.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-B8zbsCkg.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-C2U8Gs6h.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-Dd8MgL_V.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-hknqY5NH.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-CLTAmcfw.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-ByegHbiB.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-DqlAy5DP.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-DiXiztQn.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/agenda.js +115 -3
  153. package/src/commands/agent.js +10 -1
  154. package/src/commands/background-session.js +2 -1
  155. package/src/commands/context.js +69 -2
  156. package/src/data/changelog.json +26 -0
  157. package/src/gateways/terminal/PtyManager.js +45 -4
  158. package/src/gateways/ws/background-agent-protocol.js +38 -0
  159. package/src/gateways/ws/message-dispatcher.js +1 -0
  160. package/src/gateways/ws/remote-session-protocol.js +65 -6
  161. package/src/gateways/ws/ws-server.js +18 -2
  162. package/src/harness/background-task-manager.js +70 -2
  163. package/src/harness/feature-flags.js +15 -8
  164. package/src/harness/mcp-client.js +618 -43
  165. package/src/harness/worktree-isolator.js +14 -3
  166. package/src/lazy-dispatch.js +30 -1
  167. package/src/lib/agent-authority.js +22 -0
  168. package/src/lib/agent-core.js +3 -0
  169. package/src/lib/agent-sandbox.js +161 -0
  170. package/src/lib/agent-schedule-store.js +45 -22
  171. package/src/lib/agent-team/team-runner.js +22 -0
  172. package/src/lib/artifact-store.js +35 -0
  173. package/src/lib/async-hook-supervisor.cjs +13 -2
  174. package/src/lib/audit-mtc.js +3 -2
  175. package/src/lib/background-agent-supervisor.js +104 -3
  176. package/src/lib/background-interaction-journal.js +407 -0
  177. package/src/lib/background-interaction-resolver.js +23 -34
  178. package/src/lib/background-session-transport.js +12 -2
  179. package/src/lib/capability-manifest.js +41 -3
  180. package/src/lib/capability-negotiation.js +1 -1
  181. package/src/lib/channels/channel-manager.js +33 -0
  182. package/src/lib/channels/telegram-channel.js +23 -5
  183. package/src/lib/channels/webhook-channel.js +38 -15
  184. package/src/lib/chrome-connector.js +169 -23
  185. package/src/lib/config-manager.js +23 -9
  186. package/src/lib/context-breakdown.js +31 -0
  187. package/src/lib/cowork-cron.js +274 -32
  188. package/src/lib/cross-chain-mtc.js +3 -2
  189. package/src/lib/durable-security-store.js +124 -0
  190. package/src/lib/event-runtime-host.js +67 -6
  191. package/src/lib/event-runtime-store.js +163 -0
  192. package/src/lib/goal-store.js +32 -12
  193. package/src/lib/headless-manifest.js +2 -1
  194. package/src/lib/hook-environment.cjs +58 -0
  195. package/src/lib/hook-manager.js +12 -5
  196. package/src/lib/hook-runner.cjs +122 -28
  197. package/src/lib/hook-runner.js +7 -2
  198. package/src/lib/hooks-v2-producers.js +36 -0
  199. package/src/lib/hooks-v2-runtime.js +329 -52
  200. package/src/lib/interaction-adapter.js +60 -19
  201. package/src/lib/interaction-binding.js +64 -0
  202. package/src/lib/json-schema-output.js +439 -14
  203. package/src/lib/json-schema-validate.js +514 -883
  204. package/src/lib/lan-pairing-tokens.js +115 -57
  205. package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
  206. package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
  207. package/src/lib/lsp/lsp-client.js +4 -0
  208. package/src/lib/lsp/lsp-manager.js +3 -0
  209. package/src/lib/lsp/lsp-server-registry.js +18 -0
  210. package/src/lib/mcp-client.js +1 -0
  211. package/src/lib/mcp-oauth.js +35 -21
  212. package/src/lib/packer/pkg-config-generator.js +3 -0
  213. package/src/lib/permission-decision.js +99 -0
  214. package/src/lib/plugin-monitor-supervisor.js +3 -0
  215. package/src/lib/plugin-runtime/bin.js +1148 -70
  216. package/src/lib/plugin-runtime/capabilities.js +50 -0
  217. package/src/lib/plugin-runtime/capability-consent.js +42 -35
  218. package/src/lib/plugin-runtime/hooks.js +36 -9
  219. package/src/lib/plugin-runtime/install.js +23 -7
  220. package/src/lib/plugin-runtime/lsp.js +4 -0
  221. package/src/lib/plugin-runtime/manifest.js +112 -8
  222. package/src/lib/plugin-runtime/mcp.js +30 -5
  223. package/src/lib/plugin-runtime/monitors.js +11 -0
  224. package/src/lib/plugin-runtime/plugin-options.js +94 -38
  225. package/src/lib/plugin-runtime/policy.js +29 -2
  226. package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
  227. package/src/lib/plugin-runtime/scopes.js +27 -13
  228. package/src/lib/plugin-runtime/trust.js +36 -31
  229. package/src/lib/process-execution-broker/credential-agent.js +114 -16
  230. package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
  231. package/src/lib/process-execution-broker/credential-transport.js +378 -0
  232. package/src/lib/process-execution-broker/index.js +765 -49
  233. package/src/lib/process-execution-broker/platform-sandbox.js +4233 -138
  234. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  235. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  236. package/src/lib/process-execution-broker/windows-sandbox.cs +3856 -0
  237. package/src/lib/project-mcp-trust.js +21 -26
  238. package/src/lib/protocol-replay.js +19 -12
  239. package/src/lib/repl-bang-memorize.js +67 -10
  240. package/src/lib/settings-hooks.cjs +13 -0
  241. package/src/lib/skill-loader.js +83 -10
  242. package/src/lib/slash-commands.js +27 -4
  243. package/src/lib/sync-credentials.js +5 -4
  244. package/src/lib/turn-binding.js +8 -6
  245. package/src/repl/agent-repl.js +117 -31
  246. package/src/repl/repl-turn-binding.js +5 -2
  247. package/src/runtime/agent-core.js +699 -70
  248. package/src/runtime/agent-runtime.js +13 -1
  249. package/src/runtime/coding-agent-contract-shared.cjs +3 -3
  250. package/src/runtime/headless-runner.js +105 -7
  251. package/src/runtime/headless-stream.js +209 -23
  252. package/src/runtime/mcp-config.js +29 -17
  253. package/src/runtime/policies/agent-policy.js +2 -0
  254. package/src/workers/background-agent-worker.js +128 -13
  255. package/src/assets/web-panel/assets/ChatBubbleRenderer-C7bofJuA.js +0 -1
  256. package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
  257. package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
  258. package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
  259. package/src/assets/web-panel/assets/Terminal-CsMLKIoV.js +0 -3
  260. package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
  261. package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
  262. package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
  263. package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
@@ -20,6 +20,7 @@ import {
20
20
  finishAgentWorktree,
21
21
  validateAgentWorktree,
22
22
  } from "./agent-worktree.js";
23
+ import { rejectPendingBackgroundInteractions } from "./background-interaction-journal.js";
23
24
  import executionBroker from "./process-execution-broker/index.js";
24
25
 
25
26
  export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
@@ -464,6 +465,67 @@ export function effectiveBackgroundAgentState(state, options = {}) {
464
465
  /* best-effort */
465
466
  }
466
467
  }
468
+ if (
469
+ options.persist !== false &&
470
+ state.sessionId &&
471
+ !isSameProcess(state.pid, state.startedAt)
472
+ ) {
473
+ const pending = state.pendingQuestion;
474
+ const requestId = pending?.requestId || pending?.intId;
475
+ try {
476
+ const recovery = rejectPendingBackgroundInteractions(
477
+ state.sessionId,
478
+ state.id,
479
+ {
480
+ fallbackRequest: requestId
481
+ ? {
482
+ requestId,
483
+ binding: pending.binding,
484
+ payload: {
485
+ kind: "question",
486
+ question: pending.question || pending.prompt || "",
487
+ options: pending.options || null,
488
+ multiSelect: pending.multiSelect === true,
489
+ timeoutMs: pending.timeoutMs,
490
+ },
491
+ createdAt: pending.askedAt,
492
+ }
493
+ : null,
494
+ code: "INTERACTION_WORKER_LOST",
495
+ message:
496
+ "The background worker was lost before the interaction settled",
497
+ },
498
+ );
499
+ const recoveredRecord = requestId
500
+ ? recovery.journal.get(requestId)
501
+ : null;
502
+ if (
503
+ recovery.changed ||
504
+ (recoveredRecord && recoveredRecord.status !== "pending")
505
+ ) {
506
+ next = {
507
+ ...next,
508
+ phase: null,
509
+ pendingQuestion: null,
510
+ interactionRecovery: {
511
+ status: "rejected",
512
+ requestIds: recovery.rejected.map((record) => record.requestId),
513
+ recoveredAt: t,
514
+ },
515
+ };
516
+ }
517
+ } catch (error) {
518
+ next = {
519
+ ...next,
520
+ interactionRecovery: {
521
+ status: "failed",
522
+ code: error?.code || "INTERACTION_RECOVERY_FAILED",
523
+ message: error?.message || String(error),
524
+ recoveredAt: t,
525
+ },
526
+ };
527
+ }
528
+ }
467
529
  if (options.persist !== false) {
468
530
  try {
469
531
  writeBackgroundAgentState(next);
@@ -838,11 +900,17 @@ export function launchBackgroundAgent({
838
900
  };
839
901
  writeBackgroundAgentState(state);
840
902
  let child;
903
+ let workerLogFd;
841
904
  try {
905
+ // Preserve loader/bootstrap failures that happen before the worker can
906
+ // open its own append handle. This also gives detached Windows targets
907
+ // concrete inheritable stdout/stderr handles instead of relying on the
908
+ // hosted process' possibly absent standard handles.
909
+ workerLogFd = openSync(job.logFile, "a");
842
910
  child = _deps.spawn(process.execPath, [worker, jobFile], {
843
911
  cwd,
844
912
  detached: true,
845
- stdio: "ignore",
913
+ stdio: ["ignore", workerLogFd, workerLogFd],
846
914
  windowsHide: true,
847
915
  origin: "background-agent:worker",
848
916
  policy: "allow",
@@ -853,6 +921,10 @@ export function launchBackgroundAgent({
853
921
  rmSync(jobFile, { force: true });
854
922
  rmSync(statePath(id), { force: true });
855
923
  throw error;
924
+ } finally {
925
+ if (Number.isInteger(workerLogFd)) {
926
+ closeSync(workerLogFd);
927
+ }
856
928
  }
857
929
  // Async spawn failures (EPERM, cwd raced away between the check and the
858
930
  // spawn, …) arrive as an 'error' event on the detached child. Reap them
@@ -875,13 +947,42 @@ export function launchBackgroundAgent({
875
947
  /* best-effort */
876
948
  }
877
949
  });
950
+ child.on("exit", (code, signal) => {
951
+ try {
952
+ rmSync(jobFile, { force: true });
953
+ const current = readBackgroundAgentState(id);
954
+ // A healthy worker persists completed/failed before exiting. If the
955
+ // process (or its platform wrapper) exits while the state still says
956
+ // running, bootstrap or finalization failed and the session must not
957
+ // remain a phantom live task until heartbeat reconciliation.
958
+ if (current && current.status === "running") {
959
+ writeBackgroundAgentState({
960
+ ...current,
961
+ status: "failed",
962
+ endedAt: Date.now(),
963
+ exitCode: Number.isInteger(code) ? code : null,
964
+ signal: signal || null,
965
+ lostReason: `worker-exited-before-finalize: exit=${
966
+ Number.isInteger(code) ? code : "null"
967
+ } signal=${signal || "none"}`,
968
+ });
969
+ }
970
+ } catch {
971
+ /* best-effort */
972
+ }
973
+ });
878
974
  }
879
975
  child.unref();
880
976
  const current = readBackgroundAgentState(id) || state;
881
977
  const withPid = {
882
978
  ...current,
883
- pid: child.pid,
884
- workerPid: child.pid,
979
+ // A platform wrapper (notably the Windows Job Object adapter) can return
980
+ // from spawn after the real worker has already claimed the state file.
981
+ // Preserve that authoritative process.pid instead of racing it back to
982
+ // the wrapper pid. With an ordinary direct spawn both fields are still
983
+ // null here and are initialized from child.pid as before.
984
+ pid: current.pid ?? child.pid,
985
+ workerPid: current.workerPid ?? child.pid,
885
986
  };
886
987
  writeBackgroundAgentState(withPid);
887
988
  return withPid;
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Durable pending/settlement journal for background human interactions.
3
+ *
4
+ * Each mutation appends a full snapshot to the session's tamper-evident JSONL
5
+ * transcript. The worker persists a terminal settlement before delivering it
6
+ * to the suspended child, so retries can be classified as the same idempotent
7
+ * answer or a conflicting second settlement even after a process restart.
8
+ */
9
+
10
+ import { createHash } from "node:crypto";
11
+ import {
12
+ appendEvent as storeAppendEvent,
13
+ readEvents as storeReadEvents,
14
+ } from "../harness/jsonl-session-store.js";
15
+ import {
16
+ normalizeInteractionBinding,
17
+ sameInteractionBinding,
18
+ } from "./interaction-binding.js";
19
+
20
+ export const BACKGROUND_INTERACTION_JOURNAL_EVENT =
21
+ "background_interaction_journal";
22
+ export const BACKGROUND_INTERACTION_JOURNAL_VERSION = 1;
23
+ export const MAX_BACKGROUND_INTERACTION_RECORDS = 256;
24
+
25
+ const TERMINAL_STATUSES = new Set(["resolved", "rejected", "cancelled"]);
26
+
27
+ export const _deps = {
28
+ appendEvent: storeAppendEvent,
29
+ readEvents: storeReadEvents,
30
+ };
31
+
32
+ function journalError(message, code, details = {}) {
33
+ const error = new Error(message);
34
+ error.code = code;
35
+ Object.assign(error, details);
36
+ return error;
37
+ }
38
+
39
+ function cloneJson(value) {
40
+ if (value === undefined) return null;
41
+ return JSON.parse(JSON.stringify(value));
42
+ }
43
+
44
+ function canonicalize(value) {
45
+ if (value === undefined) return "__undefined__";
46
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
47
+ if (Array.isArray(value)) {
48
+ return `[${value.map((entry) => canonicalize(entry)).join(",")}]`;
49
+ }
50
+ return `{${Object.keys(value)
51
+ .sort()
52
+ .map((key) => `${JSON.stringify(key)}:${canonicalize(value[key])}`)
53
+ .join(",")}}`;
54
+ }
55
+
56
+ function fingerprint(value) {
57
+ return createHash("sha256").update(canonicalize(value)).digest("hex");
58
+ }
59
+
60
+ function normalizeError(error, fallbackCode = "INTERACTION_REJECTED") {
61
+ if (!error) return null;
62
+ if (typeof error === "string") {
63
+ return { code: fallbackCode, message: error };
64
+ }
65
+ return {
66
+ code: String(error.code || fallbackCode),
67
+ message: String(error.message || error),
68
+ };
69
+ }
70
+
71
+ function normalizeSettlement(outcome = {}) {
72
+ const status = String(outcome.status || "");
73
+ if (!TERMINAL_STATUSES.has(status)) {
74
+ throw journalError(
75
+ `Invalid background interaction settlement status: ${status || "<missing>"}`,
76
+ "INTERACTION_SETTLEMENT_INVALID",
77
+ );
78
+ }
79
+ return {
80
+ status,
81
+ answer: status === "resolved" ? cloneJson(outcome.answer) : null,
82
+ error:
83
+ status === "resolved"
84
+ ? null
85
+ : normalizeError(
86
+ outcome.error,
87
+ status === "cancelled"
88
+ ? "INTERACTION_CANCELLED"
89
+ : "INTERACTION_REJECTED",
90
+ ),
91
+ };
92
+ }
93
+
94
+ export class BackgroundInteractionJournal {
95
+ constructor(options = {}) {
96
+ this.backgroundAgentId = String(options.backgroundAgentId || "");
97
+ if (!this.backgroundAgentId) {
98
+ throw journalError(
99
+ "backgroundAgentId is required for the interaction journal",
100
+ "INTERACTION_JOURNAL_INVALID",
101
+ );
102
+ }
103
+ this._now = options.now || Date.now;
104
+ this.records = [];
105
+ }
106
+
107
+ static fromJSON(snapshot, options = {}) {
108
+ if (
109
+ !snapshot ||
110
+ snapshot.version !== BACKGROUND_INTERACTION_JOURNAL_VERSION ||
111
+ typeof snapshot.backgroundAgentId !== "string" ||
112
+ !Array.isArray(snapshot.records)
113
+ ) {
114
+ throw journalError(
115
+ "Invalid background interaction journal snapshot",
116
+ "INTERACTION_JOURNAL_CORRUPT",
117
+ );
118
+ }
119
+ const journal = new BackgroundInteractionJournal({
120
+ backgroundAgentId: snapshot.backgroundAgentId,
121
+ now: options.now,
122
+ });
123
+ journal.records = snapshot.records.map((record) => ({
124
+ requestId: String(record.requestId || ""),
125
+ binding: normalizeInteractionBinding(record.binding),
126
+ payload: cloneJson(record.payload || {}),
127
+ payloadFingerprint: String(record.payloadFingerprint || ""),
128
+ status: String(record.status || "pending"),
129
+ createdAt: Number(record.createdAt) || 0,
130
+ settledAt: record.settledAt == null ? null : Number(record.settledAt),
131
+ settlement: record.settlement ? cloneJson(record.settlement) : null,
132
+ settlementFingerprint: record.settlementFingerprint
133
+ ? String(record.settlementFingerprint)
134
+ : null,
135
+ }));
136
+ return journal;
137
+ }
138
+
139
+ toJSON() {
140
+ return {
141
+ version: BACKGROUND_INTERACTION_JOURNAL_VERSION,
142
+ backgroundAgentId: this.backgroundAgentId,
143
+ records: cloneJson(this.records),
144
+ };
145
+ }
146
+
147
+ get(requestId) {
148
+ return (
149
+ this.records.find((record) => record.requestId === String(requestId)) ||
150
+ null
151
+ );
152
+ }
153
+
154
+ pending() {
155
+ return this.records.filter((record) => record.status === "pending");
156
+ }
157
+
158
+ recordPending({ requestId, binding, payload = {}, createdAt } = {}) {
159
+ const id = String(requestId || "");
160
+ const normalizedBinding = normalizeInteractionBinding(binding);
161
+ if (!id || normalizedBinding.sequence === null) {
162
+ throw journalError(
163
+ "A requestId and complete interaction binding are required",
164
+ "INTERACTION_REQUEST_INVALID",
165
+ );
166
+ }
167
+ const safePayload = cloneJson(payload);
168
+ const payloadFingerprint = fingerprint(safePayload);
169
+ const existing = this.get(id);
170
+ if (existing) {
171
+ if (
172
+ sameInteractionBinding(existing.binding, normalizedBinding) &&
173
+ existing.payloadFingerprint === payloadFingerprint
174
+ ) {
175
+ return { applied: false, record: cloneJson(existing) };
176
+ }
177
+ throw journalError(
178
+ `Interaction request ${id} was replayed with different binding or payload`,
179
+ "INTERACTION_REQUEST_CONFLICT",
180
+ { requestId: id },
181
+ );
182
+ }
183
+
184
+ const record = {
185
+ requestId: id,
186
+ binding: normalizedBinding,
187
+ payload: safePayload,
188
+ payloadFingerprint,
189
+ status: "pending",
190
+ createdAt: Number(createdAt) || this._now(),
191
+ settledAt: null,
192
+ settlement: null,
193
+ settlementFingerprint: null,
194
+ };
195
+ this.records.push(record);
196
+ this._prune();
197
+ return { applied: true, record: cloneJson(record) };
198
+ }
199
+
200
+ settle(requestId, binding, outcome = {}) {
201
+ const id = String(requestId || "");
202
+ const record = this.get(id);
203
+ if (!record) {
204
+ throw journalError(
205
+ `Unknown background interaction request: ${id || "<missing>"}`,
206
+ "INTERACTION_REQUEST_UNKNOWN",
207
+ { requestId: id },
208
+ );
209
+ }
210
+ if (!sameInteractionBinding(record.binding, binding)) {
211
+ throw journalError(
212
+ `Interaction response binding does not match request ${id}`,
213
+ "INTERACTION_BINDING_MISMATCH",
214
+ { requestId: id },
215
+ );
216
+ }
217
+ const settlement = normalizeSettlement(outcome);
218
+ const settlementFingerprint = fingerprint(settlement);
219
+ if (record.status !== "pending") {
220
+ if (record.settlementFingerprint === settlementFingerprint) {
221
+ return { applied: false, record: cloneJson(record) };
222
+ }
223
+ throw journalError(
224
+ `Interaction request ${id} is already settled`,
225
+ "INTERACTION_ALREADY_SETTLED",
226
+ { requestId: id, status: record.status },
227
+ );
228
+ }
229
+
230
+ record.status = settlement.status;
231
+ record.settledAt = this._now();
232
+ record.settlement = settlement;
233
+ record.settlementFingerprint = settlementFingerprint;
234
+ return { applied: true, record: cloneJson(record) };
235
+ }
236
+
237
+ rejectPending(error = {}) {
238
+ const rejected = [];
239
+ for (const record of this.pending()) {
240
+ const result = this.settle(record.requestId, record.binding, {
241
+ status: "rejected",
242
+ error,
243
+ });
244
+ rejected.push(result.record);
245
+ }
246
+ return rejected;
247
+ }
248
+
249
+ _prune() {
250
+ while (this.records.length > MAX_BACKGROUND_INTERACTION_RECORDS) {
251
+ const terminalIndex = this.records.findIndex((record) =>
252
+ TERMINAL_STATUSES.has(record.status),
253
+ );
254
+ if (terminalIndex === -1) {
255
+ throw journalError(
256
+ `Background interaction journal exceeded ${MAX_BACKGROUND_INTERACTION_RECORDS} pending requests`,
257
+ "INTERACTION_PENDING_LIMIT",
258
+ );
259
+ }
260
+ this.records.splice(terminalIndex, 1);
261
+ }
262
+ }
263
+ }
264
+
265
+ export class BackgroundInteractionPersistenceError extends Error {
266
+ constructor(operation, sessionId, cause) {
267
+ const detail =
268
+ cause?.message || String(cause || "unknown persistence error");
269
+ super(
270
+ `Background interaction journal ${operation} failed for ${sessionId}: ${detail}`,
271
+ { cause },
272
+ );
273
+ this.name = "BackgroundInteractionPersistenceError";
274
+ this.code =
275
+ operation === "read"
276
+ ? "INTERACTION_JOURNAL_READ_FAILED"
277
+ : "INTERACTION_JOURNAL_PERSIST_FAILED";
278
+ this.operation = operation;
279
+ this.sessionId = sessionId;
280
+ }
281
+ }
282
+
283
+ export function persistBackgroundInteractionJournal(
284
+ sessionId,
285
+ journal,
286
+ options = {},
287
+ ) {
288
+ const failIfUnavailable = options.failIfUnavailable !== false;
289
+ try {
290
+ if (!sessionId || !(journal instanceof BackgroundInteractionJournal)) {
291
+ throw new TypeError("sessionId and interaction journal are required");
292
+ }
293
+ _deps.appendEvent(
294
+ sessionId,
295
+ BACKGROUND_INTERACTION_JOURNAL_EVENT,
296
+ journal.toJSON(),
297
+ );
298
+ return true;
299
+ } catch (error) {
300
+ if (!failIfUnavailable) return false;
301
+ throw new BackgroundInteractionPersistenceError(
302
+ "write",
303
+ sessionId || "<missing-session>",
304
+ error,
305
+ );
306
+ }
307
+ }
308
+
309
+ export function loadBackgroundInteractionJournal(
310
+ sessionId,
311
+ backgroundAgentId,
312
+ options = {},
313
+ ) {
314
+ const failIfUnavailable = options.failIfUnavailable !== false;
315
+ try {
316
+ if (!sessionId || !backgroundAgentId) {
317
+ throw new TypeError("sessionId and backgroundAgentId are required");
318
+ }
319
+ const events = _deps.readEvents(sessionId) || [];
320
+ for (let index = events.length - 1; index >= 0; index--) {
321
+ const event = events[index];
322
+ if (
323
+ event?.type === BACKGROUND_INTERACTION_JOURNAL_EVENT &&
324
+ event.data?.backgroundAgentId === backgroundAgentId
325
+ ) {
326
+ return BackgroundInteractionJournal.fromJSON(event.data, options);
327
+ }
328
+ }
329
+ return new BackgroundInteractionJournal({
330
+ backgroundAgentId,
331
+ now: options.now,
332
+ });
333
+ } catch (error) {
334
+ if (!failIfUnavailable) {
335
+ return new BackgroundInteractionJournal({
336
+ backgroundAgentId,
337
+ now: options.now,
338
+ });
339
+ }
340
+ throw new BackgroundInteractionPersistenceError(
341
+ "read",
342
+ sessionId || "<missing-session>",
343
+ error,
344
+ );
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Clone-mutate-persist transaction. The live journal is not changed when the
350
+ * append fails, so a caller may safely retry the same settlement.
351
+ */
352
+ export function updateBackgroundInteractionJournal(
353
+ sessionId,
354
+ journal,
355
+ mutate,
356
+ options = {},
357
+ ) {
358
+ const draft = BackgroundInteractionJournal.fromJSON(journal.toJSON(), {
359
+ now: options.now || journal._now,
360
+ });
361
+ const result = mutate(draft);
362
+ if (!options.persistIf || options.persistIf(result) !== false) {
363
+ persistBackgroundInteractionJournal(sessionId, draft, options);
364
+ }
365
+ return { journal: draft, result };
366
+ }
367
+
368
+ /**
369
+ * Recovery entry point used when a worker is observed lost or starts again.
370
+ * An optional state-file fallback lets pre-journal pendingQuestion records be
371
+ * imported once and deterministically rejected.
372
+ */
373
+ export function rejectPendingBackgroundInteractions(
374
+ sessionId,
375
+ backgroundAgentId,
376
+ options = {},
377
+ ) {
378
+ let journal = loadBackgroundInteractionJournal(
379
+ sessionId,
380
+ backgroundAgentId,
381
+ options,
382
+ );
383
+ const fallback = options.fallbackRequest;
384
+ const hasFallback =
385
+ fallback?.requestId &&
386
+ normalizeInteractionBinding(fallback.binding).sequence !== null &&
387
+ !journal.get(fallback.requestId);
388
+ if (!journal.pending().length && !hasFallback) {
389
+ return { journal, rejected: [], changed: false };
390
+ }
391
+ const mutation = updateBackgroundInteractionJournal(
392
+ sessionId,
393
+ journal,
394
+ (draft) => {
395
+ if (hasFallback) draft.recordPending(fallback);
396
+ return draft.rejectPending({
397
+ code: options.code || "INTERACTION_WORKER_LOST",
398
+ message:
399
+ options.message ||
400
+ "The background worker exited before the interaction settled",
401
+ });
402
+ },
403
+ options,
404
+ );
405
+ journal = mutation.journal;
406
+ return { journal, rejected: mutation.result, changed: true };
407
+ }
@@ -13,6 +13,16 @@
13
13
  */
14
14
 
15
15
  import { randomUUID } from "node:crypto";
16
+ import {
17
+ hasCompleteInteractionBinding,
18
+ normalizeInteractionBinding,
19
+ sameInteractionBinding,
20
+ } from "./interaction-binding.js";
21
+
22
+ export {
23
+ normalizeInteractionBinding,
24
+ sameInteractionBinding,
25
+ } from "./interaction-binding.js";
16
26
 
17
27
  export const BACKGROUND_INTERACTION_PROTOCOL_VERSION = 1;
18
28
  export const DEFAULT_BACKGROUND_INTERACTION_TIMEOUT_MS = 60_000;
@@ -30,31 +40,6 @@ function asNullableString(value) {
30
40
  return String(value);
31
41
  }
32
42
 
33
- function normalizeBinding(binding = {}) {
34
- return {
35
- backgroundAgentId: asNullableString(binding.backgroundAgentId),
36
- sessionId: asNullableString(binding.sessionId),
37
- turnId: asNullableString(binding.turnId),
38
- toolUseId: asNullableString(binding.toolUseId),
39
- sequence:
40
- Number.isSafeInteger(binding.sequence) && binding.sequence > 0
41
- ? binding.sequence
42
- : null,
43
- };
44
- }
45
-
46
- function sameBinding(left, right) {
47
- const a = normalizeBinding(left);
48
- const b = normalizeBinding(right);
49
- return (
50
- a.backgroundAgentId === b.backgroundAgentId &&
51
- a.sessionId === b.sessionId &&
52
- a.turnId === b.turnId &&
53
- a.toolUseId === b.toolUseId &&
54
- a.sequence === b.sequence
55
- );
56
- }
57
-
58
43
  function validRequest(message) {
59
44
  return (
60
45
  message &&
@@ -65,12 +50,16 @@ function validRequest(message) {
65
50
  message.requestId.length <= 256 &&
66
51
  message.payload &&
67
52
  typeof message.payload === "object" &&
68
- normalizeBinding(message.binding).sequence !== null
53
+ hasCompleteInteractionBinding(message.binding)
69
54
  );
70
55
  }
71
56
 
72
57
  function sendIpc(target, message, onError = () => {}) {
73
- if (!target || typeof target.send !== "function" || target.connected === false)
58
+ if (
59
+ !target ||
60
+ typeof target.send !== "function" ||
61
+ target.connected === false
62
+ )
74
63
  return false;
75
64
  try {
76
65
  target.send(message, (error) => {
@@ -138,7 +127,8 @@ export function createBackgroundInteractionClient(options = {}) {
138
127
  return;
139
128
  }
140
129
  const entry = pending.get(message.requestId);
141
- if (!entry || !sameBinding(entry.binding, message.binding)) return;
130
+ if (!entry || !sameInteractionBinding(entry.binding, message.binding))
131
+ return;
142
132
 
143
133
  if (message.status === "resolved") {
144
134
  settle(entry, entry.resolve, message.answer);
@@ -170,7 +160,7 @@ export function createBackgroundInteractionClient(options = {}) {
170
160
 
171
161
  sequence += 1;
172
162
  const requestId = `${backgroundAgentId}:${idFactory()}`;
173
- const binding = normalizeBinding({
163
+ const binding = normalizeInteractionBinding({
174
164
  backgroundAgentId,
175
165
  sessionId: payload.sessionId ?? sessionId,
176
166
  turnId: payload.turnId ?? turnId,
@@ -301,7 +291,7 @@ export function attachInteractionRequestHandler(
301
291
  protocolVersion: BACKGROUND_INTERACTION_PROTOCOL_VERSION,
302
292
  requestId:
303
293
  typeof message?.requestId === "string" ? message.requestId : "",
304
- binding: normalizeBinding(message?.binding),
294
+ binding: normalizeInteractionBinding(message?.binding),
305
295
  status: "rejected",
306
296
  error: { code, message: detail },
307
297
  };
@@ -315,7 +305,7 @@ export function attachInteractionRequestHandler(
315
305
  message.protocolVersion === BACKGROUND_INTERACTION_PROTOCOL_VERSION
316
306
  ) {
317
307
  const active = inflight.get(message.requestId);
318
- if (active && sameBinding(active.binding, message.binding)) {
308
+ if (active && sameInteractionBinding(active.binding, message.binding)) {
319
309
  inflight.delete(message.requestId);
320
310
  active.controller.abort(message.reason || "cancelled");
321
311
  }
@@ -331,12 +321,11 @@ export function attachInteractionRequestHandler(
331
321
  return;
332
322
  }
333
323
 
334
- const binding = normalizeBinding(message.binding);
324
+ const binding = normalizeInteractionBinding(message.binding);
335
325
  const expectedAgentId = asNullableString(options.backgroundAgentId);
336
326
  const expectedSessionId = asNullableString(options.sessionId);
337
327
  if (
338
- (expectedAgentId &&
339
- binding.backgroundAgentId !== expectedAgentId) ||
328
+ (expectedAgentId && binding.backgroundAgentId !== expectedAgentId) ||
340
329
  (expectedSessionId && binding.sessionId !== expectedSessionId)
341
330
  ) {
342
331
  rejectRequest(
@@ -198,7 +198,8 @@ export function startBackgroundSessionServer(opts) {
198
198
  case "interaction_response": {
199
199
  try {
200
200
  // Support both field names: requestId (client) and intId (wire legacy)
201
- const { requestId, intId, answer, result, error } = message;
201
+ const { requestId, intId, binding, answer, result, error } =
202
+ message;
202
203
  const resolvedId = requestId || intId;
203
204
  if (!resolvedId) {
204
205
  writeMessage(socket, {
@@ -208,14 +209,23 @@ export function startBackgroundSessionServer(opts) {
208
209
  return;
209
210
  }
210
211
  // Single-object callback matching worker expectation: { requestId, answer, error }
211
- onInteractionResponse?.({
212
+ const settlement = onInteractionResponse?.({
212
213
  requestId: resolvedId,
214
+ binding,
213
215
  answer: answer ?? result,
214
216
  error,
215
217
  });
216
218
  writeMessage(socket, {
217
219
  type: "received",
218
220
  requestId: resolvedId,
221
+ accepted:
222
+ typeof settlement === "object"
223
+ ? settlement.accepted === true
224
+ : settlement === true,
225
+ duplicate:
226
+ typeof settlement === "object"
227
+ ? settlement.duplicate === true
228
+ : false,
219
229
  });
220
230
  } catch (err) {
221
231
  writeMessage(socket, {