chainlesschain 0.166.7 → 0.166.9

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 (187) hide show
  1. package/README.md +21 -0
  2. package/package.json +3 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BlrmQxa2.js → AIOps-DJvpifGx.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-zyRyLITg.js → ActionButton-D2UhuF_A.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DwGz7OGN.js → Analytics-C9ho9obu.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-Dc-nj7Zd.js → AppLayout-BvRCpFK3.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-7Hu3O73p.js → Artifacts-jxrySvpk.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-Bnoe3-fR.js → Audit-ovEw0ZYl.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BqYFrDKZ.js → BackgroundAgents-D7H5z6zS.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-B3hwW1nz.js → Backup-BuXob8za.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-XFVxLA4w.js → BaseInput-CtnszuQ2.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-CWRDNw7e.js → Chat-B_mYE8fJ.js} +6 -6
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Bf30VM8G.js → ChatBubbleRenderer-DaiA6CsX.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-kFeeBgac.js → Checkbox-ierdtcJD.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CCxp8xoI.js → Codegen-3VKPJD_B.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-BLKZalle.js → Col-1EdMyf5P.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DD0eWCez.js → Community-D6H7UA8K.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-CHqHWdy0.js → Compact-BBAZvYIm.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-Bw54uPiK.js → Compliance-CgsTrAoj.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DZ13zKNJ.js → Cowork-tnZgLoTE.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-Dlj5Jevg.js → Cron-CPpZw2Nh.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-Nvz0aR0j.js → Crosschain-BymBcp5z.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-Ct89eUyY.js → DID-DKPxS_K1.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-BHUH5IAe.js → Dashboard-DvJhEmH9.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-BhZG-PO7.js → Dropdown-b8iQyQIZ.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-ae4RVwwJ.js → EmailListRenderer--0-A-zKb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-AOcxLfLr.js → FamilyGuardDashboard-C12iDN06.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BK2PJQOF.js → Federation-BRhaE2Z8.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-C2n3xO2_.js → FormItemContext-C4dnq7yf.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DcyUVFZq.js → GenericCardRenderer-CGAdbR5n.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-tBae9Lt8.js → Git-4eA2fYzL.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-DmNR3AP4.js → Governance-BJuY9Y-9.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CoseEigi.js → Inference-DZ5Dy4up.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Bdjd1sxr.js → KnowledgeGraph-BMTjRs4A.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-CtlkKIcn.js → Logs-BKEeQEfJ.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-BTYb7nEx.js → Marketplace-Dhho4fEg.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-D0iUdW3P.js → McpTools-BW6xxSlU.js} +2 -2
  40. package/src/assets/web-panel/assets/{Memory--gwI7w4Y.js → Memory-mPqlpSkq.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-JmFBi24T.js → MobileBridge-Cb9N_DQI.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-BK01cQRx.js → MobileProjects-BNs8tah_.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-u7avzyoO.js → Mtc-CaxBt8tn.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-gT3z5fo_.js → MtcAudit-DRPQbp3t.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BIqZTz5n.js → Multisig-CRm-d8bK.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-DdhVfXYq.js → NLProgramming-BCCk3LPs.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-CBlyUmY3.js → Notes-Cv8SN8gN.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-DSyI26N3.js → NotificationSettings-CggIvTh5.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-B7IEuNz4.js → OrderTableRenderer-BW3_bV12.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-ozhi4kGS.js → Organization-BuRE-VCO.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CBy57RVM.js → Overflow-DBrm1gju.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CA-ZRv77.js → P2P-CWVnDYgS.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-DOHc9zm8.js → PdhVaultBrowser-8RKUIpte.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-DiP632GP.js → Permissions-tDUAxogW.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-AnU8O-SS.js → PersonalDataHub-Dv2-CFis.js} +5 -5
  56. package/src/assets/web-panel/assets/{Pipeline-DDibOUaP.js → Pipeline-DM6kvFni.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-D421t36T.js → Privacy-DzK-yL0d.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-DR879ZPJ.js → ProjectInit-B5UrNpkj.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Ded8o6aY.js → ProjectSettings-CxJD43oW.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-Boivuu-E.js → Providers-CYl4jRYK.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-xKD_ntKp.js → QrScannerModal-DeH2T0fH.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-D3y_A6FV.js → QuickAsk-Bw9k_kTk.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-BjKM-YC2.js → Recommend-Bl_jGwoi.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-d6u01Kq0.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-Ci2GjH5p.js → Reputation-Ds_Dfr-k.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-B0-T2BSh.js → Row-BpQUQTNj.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-Bet-ur-r.js → RssFeed-BatpZBGt.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-DWtRAS2R.js → Search-D7hOObzi.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-BDfu2Qn6.js → Security-dXQ4FKNt.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-CvJmvSMM.js → Services-D8wInGPa.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-CEbrKpdC.js → Skeleton-DXQCoYpM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-CjVeiETf.js → Skills-D3oLRIVn.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-Dibypxum.js → Sla-BmP4-LnJ.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-XflYXp7a.js → SpeechSettings-DLj-xzro.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-ro1Ivx1E.js → SyncSettings-8KKaaRMr.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-CHq1qFbo.js → Tasks-GyK42I7C.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-CRnUZxtZ.js → Templates-n1y_8UBG.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-jUX72JCA.js → Tenant-B39nBph8.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-BBhAs7pt.js → Terminal-DCQoOKXe.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-CD5M3RGp.js → TimelineRenderer-BkhbDfXk.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-sah3v0gu.js → Tokens-BeHIIU8D.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BNT3Uz3s.js → Trigger-BHvWXVOb.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-BytMGPMo.js → Trust-y2WaOSto.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-BkYVMRQs.js → UkeySign-DBFeJBzt.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-psvb5Q7Y.js → VideoEditing-BEOWitC-.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-PFQTW0MH.js → Wallet-BITedTe6.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DgO-OwRT.js → WebAuthn-DMwlJVin.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-DL2l9VNl.js → WorkflowEditor-CKfGFEu1.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-C84akEzN.js → chat-DR6gnBGF.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-DzTWuQKP.js → colors-C_Hw5NCn.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-CR9LJQFv.js → compact-item-B8IPwsEu.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-CTTov5Kn.js → createContext-D-u4JVnJ.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DALj523o.js → hasIn-CZubdS4D.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Be6Os5S8.js → index-11ASqx-A.js} +1 -1
  97. package/src/assets/web-panel/assets/index-BQ3213fz.js +1 -0
  98. package/src/assets/web-panel/assets/{index-CXaCRXN4.js → index-BXS6wYJZ.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-7ux_XohH.js → index-Bi21Etg4.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CXTEpfGe.js → index-Bizm87cX.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DH6OG-ci.js → index-Bv2132PO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CZhhFQi9.js → index-BzoFanEQ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B6QOJbSn.js → index-C25rOz7O.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CunGDV0g.js → index-C2VmtNaQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CmELknPe.js → index-Cbm8F0M2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DZER6Lj3.js → index-CfniPP5p.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B2qpjU-D.js → index-Cg2bCeol.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-QW0EXx14.js → index-CiXkhOkY.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-C5eeMl_2.js → index-Cl5RpreD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-s4CFyn-n.js → index-CqmLw8cs.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-eJcFleIV.js → index-Ctje8Vl3.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-3-XJoYgX.js → index-D1w96gg9.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-_8vetPbU.js → index-D47jt8ET.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cef0n4k_.js → index-DJ6pG79m.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-AXnERsyV.js → index-DTosx2Wu.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DtW6Zr_i.js → index-DY6MHo9D.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CNmTDXmf.js → index-DcFjRT-K.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dah8M4l2.js → index-DfnzrUxU.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D0MqUnKO.js → index-DjRrqooR.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CqUgXYIQ.js → index-DmRcgiYT.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-D9ScRFxz.js → index-DpjHpRoq.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-DjE_ew7U.js → index-Dt-w3xQ7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-W9axN_Ce.js → index-DuOU5apg.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BDyt57zL.js → index-LvjAKDSl.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-hzzTITR9.js → index-_nL79NT8.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BNaQt49r.js → index-auyNYjLo.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DBIPnoTj.js → index-cVqbas5C.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CnxjXIsK.js → index-ffe7bvqP.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BHRXKiU1.js → index-idZKyADQ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D_Bg0LdK.js → index-k1Xa5iw1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BKNIzw0U.js → index-mYZJxGH6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DxdzSljS.js → index-rYBHSDcJ.js} +1 -1
  133. package/src/assets/web-panel/assets/index-vJagfET2.js +1 -0
  134. package/src/assets/web-panel/assets/{index-CjkPDSX4.js → index-xoNssgz_.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-JuW8mFj7.js → initDefaultProps-lcU_dT2r.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-Cwb0HBve.js → motion-Dc7UNXZ1.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DHokzMgu.js → move-Cll8ZXaL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-DX31y6QS.js → mtc-parser-CAb_yLIM.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-BCIUYDA6.js → omit-DurYMyTe.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-DMCj4EoZ.js → pickAttrs-BITQyOlw.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-Dy5K-u1s.js → placementArrow-Dg1rH91i.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-DkGeZu54.js → responsiveObserve-D_EWIeac.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-Dc5dyJdg.js → slide-lK4d3zsh.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-DIUBjOaC.js → statusUtils-Bflq-Git.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-DmyEOxm5.js → styleChecker-Ccp0I-wt.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-FRhakk1e.js → useFlexGapSupport-CYZbByYA.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-clXyV1WQ.js → useFs-B_x0nxFD.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-UF6DG41z.js → usePersonalDataHub-DW55rL85.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-CoCt95G7.js → vnode-Ca6MH3W8.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-yVWt-ENm.js → zoom-NdHelOog.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/orchestrate.js +61 -9
  154. package/src/data/changelog.json +34 -1
  155. package/src/gateways/ws/message-dispatcher.js +4 -0
  156. package/src/gateways/ws/session-protocol.js +89 -11
  157. package/src/gateways/ws/ws-agent-handler.js +65 -9
  158. package/src/gateways/ws/ws-approval-gate.js +181 -16
  159. package/src/gateways/ws/ws-server.js +16 -6
  160. package/src/generated/cc-agent-protocol.schema.json +148 -2
  161. package/src/lib/agent-team/task-lease.js +172 -3
  162. package/src/lib/agent-team/team-runner.js +60 -1
  163. package/src/lib/app-server/graph-runtime.js +326 -2
  164. package/src/lib/app-server/server.js +60 -0
  165. package/src/lib/approval-grant-ledger.js +69 -0
  166. package/src/lib/codex-app-server-adapter.js +11 -20
  167. package/src/lib/graph-kernel/compiler.js +66 -13
  168. package/src/lib/graph-kernel/cutover-authority-resolver.js +26 -0
  169. package/src/lib/graph-kernel/cutover-ledger.js +53 -11
  170. package/src/lib/graph-kernel/retirement-evidence.js +567 -0
  171. package/src/lib/graph-kernel/runtime.js +76 -14
  172. package/src/lib/interaction-adapter.js +61 -9
  173. package/src/lib/policy-decision-event.js +149 -0
  174. package/src/lib/process-execution-broker/platform-sandbox.js +15 -1
  175. package/src/lib/record-replay/index.js +1 -0
  176. package/src/lib/record-replay/playwright-ui-driver.js +327 -0
  177. package/src/lib/webhook-security.js +484 -36
  178. package/src/runtime/coding-agent-events.cjs +13 -11
  179. package/src/runtime/headless-runner.js +17 -1
  180. package/src/runtime/headless-stream.js +66 -3
  181. package/src/runtime/session-slash-commands.js +57 -6
  182. package/src/assets/web-panel/assets/MarkdownRenderer-CZsTBEQE.js +0 -1
  183. package/src/assets/web-panel/assets/Projects-BHPxiucB.js +0 -1
  184. package/src/assets/web-panel/assets/RemoteSession-CgqKMXG4.js +0 -5
  185. package/src/assets/web-panel/assets/devWarning-BOhvmlpc.js +0 -1
  186. package/src/assets/web-panel/assets/index-DDyMJexx.js +0 -1
  187. package/src/assets/web-panel/assets/index-btiojFYn.js +0 -1
@@ -606,9 +606,11 @@ async function ensureSessionHandler(
606
606
  if (budgetConfig.enabled) {
607
607
  const priceTable =
608
608
  budgetConfig.limits.maxUsd != null
609
- ? wsBudgetDependency(server, "mergePricing", mergePricing)(
610
- server.sessionManager.config?.llm?.pricing,
611
- )
609
+ ? wsBudgetDependency(
610
+ server,
611
+ "mergePricing",
612
+ mergePricing,
613
+ )(server.sessionManager.config?.llm?.pricing)
612
614
  : undefined;
613
615
  sessionBudgetRoot = wsBudgetDependency(
614
616
  server,
@@ -627,9 +629,7 @@ async function ensureSessionHandler(
627
629
  sessionBudgetRoot.budget ||
628
630
  typeof sessionBudgetRoot.close !== "function"
629
631
  ) {
630
- const error = new Error(
631
- "WebSocket session budget root is invalid",
632
- );
632
+ const error = new Error("WebSocket session budget root is invalid");
633
633
  error.code = "CC_SESSION_BUDGET_ROOT_INVALID";
634
634
  throw error;
635
635
  }
@@ -746,9 +746,7 @@ export async function handleSessionCreate(server, id, ws, message) {
746
746
  worktreeIsolation,
747
747
  systemPromptExtension,
748
748
  shellPolicyOverrides,
749
- sessionBudgetRoot: sessionBudgetRoot.enabled
750
- ? sessionBudgetRoot
751
- : null,
749
+ sessionBudgetRoot: sessionBudgetRoot.enabled ? sessionBudgetRoot : null,
752
750
  requireDurable: sessionBudgetRoot.enabled,
753
751
  });
754
752
  createdSessionId = sessionId;
@@ -1487,16 +1485,96 @@ export function handleSessionAnswer(server, id, ws, message) {
1487
1485
  }
1488
1486
 
1489
1487
  const session = server.sessionManager.getSession(sessionId);
1488
+ let settlement = null;
1489
+ let settlementAttempted = false;
1490
1490
  if (session && session.interaction && session.interaction.resolveAnswer) {
1491
- session.interaction.resolveAnswer(requestId, answer, binding);
1491
+ settlementAttempted = true;
1492
+ settlement = session.interaction.resolveAnswer(requestId, answer, binding);
1492
1493
  }
1493
1494
 
1495
+ // New adapters return an explicit settlement result. Legacy/custom
1496
+ // interactions return undefined and preserve the historical success ACK.
1497
+ const settled = settlementAttempted && settlement?.settled !== false;
1498
+
1494
1499
  server._send(
1495
1500
  ws,
1496
1501
  envelopeResponse(
1497
1502
  CODING_AGENT_EVENT_TYPES.COMMAND_RESPONSE,
1498
1503
  id,
1499
- { success: true },
1504
+ {
1505
+ success: settled,
1506
+ settled,
1507
+ ...(settlement?.reason ? { reason: settlement.reason } : {}),
1508
+ },
1509
+ sessionId,
1510
+ ),
1511
+ );
1512
+ }
1513
+
1514
+ export async function handleApprovalGrantsList(server, id, ws, message) {
1515
+ const sessionId = message.sessionId;
1516
+ const session = server.sessionManager?.getSession?.(sessionId) || null;
1517
+ const handler = server.sessionHandlers?.get?.(sessionId) || null;
1518
+ if (!session || !handler?.listApprovalGrants) {
1519
+ server._send(
1520
+ ws,
1521
+ envelopeError(
1522
+ id,
1523
+ "APPROVAL_GRANT_SESSION_UNAVAILABLE",
1524
+ "Approval grant session is not active",
1525
+ sessionId,
1526
+ ),
1527
+ );
1528
+ return;
1529
+ }
1530
+ const grants = await handler.listApprovalGrants();
1531
+ server._send(
1532
+ ws,
1533
+ envelopeResponse(
1534
+ CODING_AGENT_EVENT_TYPES.COMMAND_RESPONSE,
1535
+ id,
1536
+ { success: true, grants },
1537
+ sessionId,
1538
+ ),
1539
+ );
1540
+ }
1541
+
1542
+ export async function handleApprovalGrantRevoke(server, id, ws, message) {
1543
+ const sessionId = message.sessionId;
1544
+ const grantId = String(message.grantId || "");
1545
+ const handler = server.sessionHandlers?.get?.(sessionId) || null;
1546
+ if (!grantId || !handler?.revokeApprovalGrant) {
1547
+ server._send(
1548
+ ws,
1549
+ envelopeError(
1550
+ id,
1551
+ "APPROVAL_GRANT_REVOKE_INVALID",
1552
+ "An active approval grant and grantId are required",
1553
+ sessionId,
1554
+ ),
1555
+ );
1556
+ return;
1557
+ }
1558
+ const result = await handler.revokeApprovalGrant(grantId);
1559
+ if (!result?.success) {
1560
+ server._send(
1561
+ ws,
1562
+ envelopeError(
1563
+ id,
1564
+ "APPROVAL_GRANT_REVOKE_FAILED",
1565
+ result?.error || "Approval grant could not be revoked",
1566
+ sessionId,
1567
+ ),
1568
+ );
1569
+ return;
1570
+ }
1571
+ const grants = await handler.listApprovalGrants();
1572
+ server._send(
1573
+ ws,
1574
+ envelopeResponse(
1575
+ CODING_AGENT_EVENT_TYPES.COMMAND_RESPONSE,
1576
+ id,
1577
+ { success: true, revoked: result.grant, grants },
1500
1578
  sessionId,
1501
1579
  ),
1502
1580
  );
@@ -29,6 +29,11 @@ import { runnableTaskModel } from "../../lib/runnable-provider.js";
29
29
  import { PlanState } from "../../lib/plan-mode.js";
30
30
  import { CLISlotFiller } from "../../lib/slot-filler.js";
31
31
  import { createAbortError, isAbortError } from "../../lib/abort-utils.js";
32
+ import { addHooksV2EventObserver } from "../../lib/hooks-v2-producers.js";
33
+ import {
34
+ projectHookPolicyDecision,
35
+ projectToolPolicyDecision,
36
+ } from "../../lib/policy-decision-event.js";
32
37
  import {
33
38
  resolveRegisteredHostHooksV2Workspace,
34
39
  runWithHostHooksV2Workspace,
@@ -1081,6 +1086,7 @@ export class WSAgentHandler {
1081
1086
  this._approvalGate = await createWsApprovalGate({
1082
1087
  sessionId: this.session.id,
1083
1088
  interaction: this.interaction,
1089
+ cwd: this.session.projectRoot,
1084
1090
  });
1085
1091
  }
1086
1092
  return this._approvalGate;
@@ -1234,7 +1240,11 @@ export class WSAgentHandler {
1234
1240
  });
1235
1241
  const mcpRecoveryRuntime = this._ensureMcpRecoveryRuntime();
1236
1242
 
1237
- // Run agent loop
1243
+ // Run agent loop. Turn-scoped approval grants are reset before the
1244
+ // model/tool boundary for every user message; session grants remain in
1245
+ // the verified authority ledger owned by the session-scoped gate.
1246
+ const approvalGate = await this._ensureApprovalGate();
1247
+ approvalGate?.beginTurn?.(requestId || `turn-${Date.now()}`);
1238
1248
  const loopOptions = {
1239
1249
  provider: session.provider,
1240
1250
  model: activeModel,
@@ -1265,7 +1275,7 @@ export class WSAgentHandler {
1265
1275
  sessionBudget: this._sessionBudget,
1266
1276
  // P0 authority: null unless opted in (byte-identical default — agent
1267
1277
  // core already defaults `options.approvalGate || null`).
1268
- approvalGate: await this._ensureApprovalGate(),
1278
+ approvalGate,
1269
1279
  // Canonical WS history is compacted before ws_turn_prepare. Compaction
1270
1280
  // inside agent-core would include the staged pending user, which is not
1271
1281
  // part of the canonical message projection until turn settlement.
@@ -1435,7 +1445,23 @@ export class WSAgentHandler {
1435
1445
  tool: event.tool,
1436
1446
  result: event.result,
1437
1447
  error: event.error,
1448
+ permission_decision_id: event.permission_decision_id || null,
1449
+ permission_decision: event.permission_decision || null,
1438
1450
  });
1451
+ {
1452
+ const policyEvent = projectToolPolicyDecision(event, {
1453
+ sessionId: session.id,
1454
+ turnId: event.turn_id,
1455
+ toolUseId: event.tool_use_id,
1456
+ });
1457
+ if (policyEvent) {
1458
+ const { type: _type, ...payload } = policyEvent;
1459
+ this.interaction.emit("policy-decision", {
1460
+ requestId,
1461
+ ...payload,
1462
+ });
1463
+ }
1464
+ }
1439
1465
  if (
1440
1466
  event.tool === "todo_write" &&
1441
1467
  currentTodoWrite &&
@@ -1583,13 +1609,29 @@ export class WSAgentHandler {
1583
1609
  )
1584
1610
  : executeAgentTurn();
1585
1611
 
1586
- if (session.mcpClient?.withElicitationContext) {
1587
- await session.mcpClient.withElicitationContext(
1588
- session.id,
1589
- runAgentTurn,
1590
- );
1591
- } else {
1592
- await runAgentTurn();
1612
+ const removeHookPolicyObserver = addHooksV2EventObserver(
1613
+ session.id,
1614
+ (event) => {
1615
+ const policyEvent = projectHookPolicyDecision(event);
1616
+ if (!policyEvent) return;
1617
+ const { type: _type, ...payload } = policyEvent;
1618
+ this.interaction.emit("policy-decision", {
1619
+ requestId,
1620
+ ...payload,
1621
+ });
1622
+ },
1623
+ );
1624
+ try {
1625
+ if (session.mcpClient?.withElicitationContext) {
1626
+ await session.mcpClient.withElicitationContext(
1627
+ session.id,
1628
+ runAgentTurn,
1629
+ );
1630
+ } else {
1631
+ await runAgentTurn();
1632
+ }
1633
+ } finally {
1634
+ removeHookPolicyObserver();
1593
1635
  }
1594
1636
  if (session.canonicalJsonlSession === true && !canonicalTurn?.settled) {
1595
1637
  const error = new Error(
@@ -1714,6 +1756,20 @@ export class WSAgentHandler {
1714
1756
  }
1715
1757
  }
1716
1758
 
1759
+ async listApprovalGrants() {
1760
+ const gate = await this._ensureApprovalGate();
1761
+ if (!gate?.listGrants) return [];
1762
+ return gate.listGrants();
1763
+ }
1764
+
1765
+ async revokeApprovalGrant(grantId) {
1766
+ const gate = await this._ensureApprovalGate();
1767
+ if (!gate?.revokeGrant) {
1768
+ return { success: false, error: "Approval grant authority unavailable" };
1769
+ }
1770
+ return gate.revokeGrant(grantId);
1771
+ }
1772
+
1717
1773
  async interrupt() {
1718
1774
  const wasProcessing = this._processing;
1719
1775
  const interruptedRequestId = this._activeRequestId || null;
@@ -26,6 +26,32 @@
26
26
  */
27
27
 
28
28
  import { approvalBindingDigest } from "../../lib/agent-authority.js";
29
+ import {
30
+ APPROVAL_GRANTS_EVENT,
31
+ ApprovalGrantLedger,
32
+ approvalPermissionForContext,
33
+ } from "../../lib/approval-grant-ledger.js";
34
+
35
+ function approvalDecision(value) {
36
+ const candidate =
37
+ value?.decision && typeof value.decision === "object"
38
+ ? value.decision
39
+ : value;
40
+ if (
41
+ candidate &&
42
+ [
43
+ "acceptOnce",
44
+ "acceptForTurn",
45
+ "acceptForSession",
46
+ "decline",
47
+ "cancel",
48
+ ].includes(candidate.kind)
49
+ ) {
50
+ return candidate;
51
+ }
52
+ if (value === true) return { kind: "acceptOnce" };
53
+ return { kind: "decline" };
54
+ }
29
55
 
30
56
  /**
31
57
  * Build the binding-attaching confirmer for one WS session.
@@ -35,9 +61,19 @@ import { approvalBindingDigest } from "../../lib/agent-authority.js";
35
61
  * @param {string} opts.sessionId
36
62
  * @returns {(ctx?: object) => Promise<boolean>}
37
63
  */
38
- export function buildWsApprovalConfirmer({ interaction, sessionId }) {
64
+ export function buildWsApprovalConfirmer({
65
+ interaction,
66
+ sessionId,
67
+ cwd = null,
68
+ ledgerState = { ledger: new ApprovalGrantLedger({ sessionId }) },
69
+ persistApprovalGrants = () => false,
70
+ }) {
39
71
  let approvalSeq = 0;
40
72
  return async (ctx = {}) => {
73
+ const requiredPermission = approvalPermissionForContext(ctx, {
74
+ cwd: cwd || ctx.cwd || null,
75
+ });
76
+ if (ledgerState.ledger.allows(requiredPermission)) return true;
41
77
  const command = ctx.command ?? ctx.args?.command ?? null;
42
78
  const risk = ctx.riskLevel || ctx.risk || "unknown";
43
79
  // Bind this approval to the exact call it authorizes (same recipe as the
@@ -51,22 +87,66 @@ export function buildWsApprovalConfirmer({ interaction, sessionId }) {
51
87
  const what = ctx.tool || ctx.toolName || "tool";
52
88
  const detail = command != null ? `: ${String(command).slice(0, 200)}` : "";
53
89
  try {
54
- const ok = await interaction.askConfirm(
55
- `Approve ${what}${detail}? (risk: ${risk})`,
56
- {
57
- default: false,
58
- binding,
59
- // Structured context so UIs can render a proper approval card
60
- // instead of parsing the question text.
61
- approval: {
62
- tool: what,
63
- command,
64
- risk,
65
- rule: ctx.rule || null,
66
- },
90
+ const options = {
91
+ default: false,
92
+ binding,
93
+ // Structured context so UIs can render a proper approval card
94
+ // instead of parsing the question text. Reusable permissions are
95
+ // host-issued and exact; the renderer may choose a lifetime but may
96
+ // never widen this set.
97
+ approval: {
98
+ tool: what,
99
+ command,
100
+ risk,
101
+ rule: ctx.rule || null,
102
+ requestedPermissions: [requiredPermission],
67
103
  },
104
+ };
105
+ const rawDecision = interaction.askApproval
106
+ ? await interaction.askApproval(
107
+ `Approve ${what}${detail}? (risk: ${risk})`,
108
+ options,
109
+ )
110
+ : await interaction.askConfirm(
111
+ `Approve ${what}${detail}? (risk: ${risk})`,
112
+ options,
113
+ );
114
+ let decision = approvalDecision(rawDecision);
115
+ const approved = [
116
+ "acceptOnce",
117
+ "acceptForTurn",
118
+ "acceptForSession",
119
+ ].includes(decision.kind);
120
+ if (
121
+ approved &&
122
+ (decision.kind === "acceptForTurn" ||
123
+ decision.kind === "acceptForSession")
124
+ ) {
125
+ const priorSessionGrants = new Map(ledgerState.ledger.sessionGrants);
126
+ const priorRevision = ledgerState.ledger.revision;
127
+ const applied = ledgerState.ledger.applyDecision(
128
+ decision,
129
+ requiredPermission,
130
+ binding,
131
+ );
132
+ decision = applied.decision;
133
+ if (applied.granted.length === 0) return false;
134
+ if (applied.persistedScope && !persistApprovalGrants()) {
135
+ ledgerState.ledger.sessionGrants = priorSessionGrants;
136
+ ledgerState.ledger.revision = priorRevision;
137
+ // Match the stream-json contract: persistence failure never creates
138
+ // a phantom session grant, but the exact operation may proceed once.
139
+ decision = { kind: "acceptOnce" };
140
+ }
141
+ }
142
+ interaction.recordApprovalDecision?.({
143
+ binding,
144
+ decision,
145
+ requiredPermission,
146
+ });
147
+ return ["acceptOnce", "acceptForTurn", "acceptForSession"].includes(
148
+ decision.kind,
68
149
  );
69
- return ok === true;
70
150
  } catch {
71
151
  // timeout / disconnect / interrupt → fail closed, never approve
72
152
  return false;
@@ -91,6 +171,7 @@ export function buildWsApprovalConfirmer({ interaction, sessionId }) {
91
171
  export async function createWsApprovalGate({
92
172
  sessionId,
93
173
  interaction,
174
+ cwd = null,
94
175
  deps = {},
95
176
  }) {
96
177
  try {
@@ -104,8 +185,59 @@ export async function createWsApprovalGate({
104
185
  loadSessionCore(),
105
186
  ]);
106
187
  const inner = await singletons.getApprovalGate();
188
+ const store =
189
+ deps.sessionStore ||
190
+ (await import("../../harness/jsonl-session-store.js"));
191
+ const ledgerState = {
192
+ ledger: new ApprovalGrantLedger({ sessionId }),
193
+ };
194
+ let approvalGrantPersistenceError = null;
195
+ try {
196
+ const event = store.findLatestEvent(
197
+ sessionId,
198
+ APPROVAL_GRANTS_EVENT,
199
+ (candidate) => candidate?.data && typeof candidate.data === "object",
200
+ );
201
+ if (event) {
202
+ ledgerState.ledger = ApprovalGrantLedger.fromJSON(event.data, {
203
+ sessionId,
204
+ });
205
+ }
206
+ } catch (error) {
207
+ approvalGrantPersistenceError = error;
208
+ ledgerState.ledger = new ApprovalGrantLedger({ sessionId });
209
+ interaction.emit?.("recovery-degraded", {
210
+ component: "approval_grants",
211
+ error:
212
+ "Persisted approval grants could not be verified; all grants were discarded",
213
+ });
214
+ }
215
+ const persistApprovalGrants = () => {
216
+ if (approvalGrantPersistenceError) return false;
217
+ try {
218
+ const persisted = store.appendAuthorityEvent(
219
+ sessionId,
220
+ APPROVAL_GRANTS_EVENT,
221
+ ledgerState.ledger.toJSON(),
222
+ );
223
+ if (persisted === false) {
224
+ throw new Error("session store rejected approval grants");
225
+ }
226
+ return true;
227
+ } catch (error) {
228
+ approvalGrantPersistenceError = error;
229
+ return false;
230
+ }
231
+ };
107
232
  const confirmer =
108
- deps.confirmer || buildWsApprovalConfirmer({ interaction, sessionId });
233
+ deps.confirmer ||
234
+ buildWsApprovalConfirmer({
235
+ interaction,
236
+ sessionId,
237
+ cwd,
238
+ ledgerState,
239
+ persistApprovalGrants,
240
+ });
109
241
  const gate = new core.ApprovalGate({
110
242
  defaultPolicy: core.APPROVAL_POLICY?.STRICT || "strict",
111
243
  confirm: confirmer,
@@ -129,6 +261,39 @@ export async function createWsApprovalGate({
129
261
  getSessionPolicy: (sid) => inner.getSessionPolicy(sid || sessionId),
130
262
  setSessionPolicy: (sid, policy) =>
131
263
  inner.setSessionPolicy(sid || sessionId, policy),
264
+ beginTurn: (turnId) => ledgerState.ledger.beginTurn(turnId),
265
+ listGrants: () => ledgerState.ledger.listGrants(),
266
+ revokeGrant: (grantId) => {
267
+ const existing = ledgerState.ledger
268
+ .listGrants()
269
+ .find((grant) => grant.grantId === String(grantId || ""));
270
+ if (!existing) {
271
+ return { success: false, error: "Approval grant was not found" };
272
+ }
273
+ if (existing.lifetime === "turn") {
274
+ const result = ledgerState.ledger.revoke(grantId);
275
+ return { success: result.revoked, grant: result.grant };
276
+ }
277
+
278
+ const candidate = ApprovalGrantLedger.fromJSON(
279
+ ledgerState.ledger.toJSON(),
280
+ { sessionId },
281
+ );
282
+ const result = candidate.revoke(grantId);
283
+ if (!result.revoked) {
284
+ return { success: false, error: "Approval grant was not found" };
285
+ }
286
+ const previous = ledgerState.ledger;
287
+ ledgerState.ledger = candidate;
288
+ if (!persistApprovalGrants()) {
289
+ ledgerState.ledger = previous;
290
+ return {
291
+ success: false,
292
+ error: "Approval grant revocation could not be persisted",
293
+ };
294
+ }
295
+ return { success: true, grant: result.grant };
296
+ },
132
297
  };
133
298
  } catch {
134
299
  return null; // degrade to the legacy no-gate path
@@ -53,6 +53,8 @@ import {
53
53
  handleSessionClose,
54
54
  handleSessionInterrupt,
55
55
  handleSessionAnswer,
56
+ handleApprovalGrantsList,
57
+ handleApprovalGrantRevoke,
56
58
  handleHostToolResult,
57
59
  handleSubAgentList,
58
60
  handleSubAgentGet,
@@ -746,9 +748,8 @@ export class ChainlessChainWSServer extends EventEmitter {
746
748
  /** Read the same static + scoped authority used by the Agent Core. */
747
749
  async _handlePermissionRulesGet(id, ws) {
748
750
  try {
749
- const { loadPermissionAuthority } = await import(
750
- "../../lib/permission-authority.js"
751
- );
751
+ const { loadPermissionAuthority } =
752
+ await import("../../lib/permission-authority.js");
752
753
  const loaded = loadPermissionAuthority({ cwd: this.projectRoot });
753
754
  this._send(ws, {
754
755
  id,
@@ -1416,6 +1417,16 @@ export class ChainlessChainWSServer extends EventEmitter {
1416
1417
  return handleSessionAnswer(this, id, ws, message);
1417
1418
  }
1418
1419
 
1420
+ /** @private */
1421
+ _handleApprovalGrantsList(id, ws, message) {
1422
+ return handleApprovalGrantsList(this, id, ws, message);
1423
+ }
1424
+
1425
+ /** @private */
1426
+ _handleApprovalGrantRevoke(id, ws, message) {
1427
+ return handleApprovalGrantRevoke(this, id, ws, message);
1428
+ }
1429
+
1419
1430
  _handleHostToolResult(id, ws, message) {
1420
1431
  return handleHostToolResult(this, id, ws, message);
1421
1432
  }
@@ -1498,9 +1509,8 @@ export class ChainlessChainWSServer extends EventEmitter {
1498
1509
  /** @private — ping/pong heartbeat to detect dead connections */
1499
1510
  async _ensureTaskManager() {
1500
1511
  if (this._taskManager) return this._taskManager;
1501
- const { BackgroundTaskManager } = await import(
1502
- "../../harness/background-task-manager.js"
1503
- );
1512
+ const { BackgroundTaskManager } =
1513
+ await import("../../harness/background-task-manager.js");
1504
1514
  this._taskManager = new BackgroundTaskManager({
1505
1515
  recoverOnStart: true,
1506
1516
  policyCwd: this.projectRoot,