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
@@ -0,0 +1,327 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ const ACTION_CAPABILITIES = Object.freeze({
4
+ observe: "ui.observe",
5
+ assert: "ui.observe",
6
+ click: "ui.interact",
7
+ type: "ui.interact",
8
+ select: "ui.interact",
9
+ });
10
+ const DRIVER_CAPABILITIES = Object.freeze([
11
+ "ui.interact",
12
+ "ui.observe",
13
+ ]);
14
+ const DENIED_SCHEMES = new Set(["file:", "http:", "https:", "ws:", "wss:"]);
15
+
16
+ function driverError(code, message, details = {}) {
17
+ const error = new Error(message);
18
+ error.name = "RecordedSkillUiDriverError";
19
+ error.code = code;
20
+ Object.assign(error, details);
21
+ return error;
22
+ }
23
+
24
+ function digest(value, domain) {
25
+ const bytes = Buffer.isBuffer(value) ? value : Buffer.from(String(value));
26
+ return `sha256:${createHash("sha256")
27
+ .update(domain)
28
+ .update("\0")
29
+ .update(bytes)
30
+ .digest("hex")}`;
31
+ }
32
+
33
+ function boundedString(value, label, maxLength) {
34
+ if (typeof value !== "string" || !value || value.length > maxLength) {
35
+ throw driverError(
36
+ "CC_REPLAY_UI_ARGUMENT_INVALID",
37
+ `${label} must be a non-empty string no longer than ${maxLength} characters`,
38
+ );
39
+ }
40
+ return value;
41
+ }
42
+
43
+ function boundedViewport(viewport) {
44
+ const width = Number(viewport?.width);
45
+ const height = Number(viewport?.height);
46
+ if (
47
+ !Number.isInteger(width) ||
48
+ width < 320 ||
49
+ width > 3_840 ||
50
+ !Number.isInteger(height) ||
51
+ height < 240 ||
52
+ height > 2_160
53
+ ) {
54
+ throw driverError(
55
+ "CC_REPLAY_UI_ARGUMENT_INVALID",
56
+ "viewport must use bounded integer width and height",
57
+ );
58
+ }
59
+ return Object.freeze({ width, height });
60
+ }
61
+
62
+ function requestMetadata(request) {
63
+ let scheme = "invalid:";
64
+ try {
65
+ scheme = new URL(request.url()).protocol;
66
+ } catch {
67
+ // The raw URL is deliberately not retained in evidence or errors.
68
+ }
69
+ return Object.freeze({
70
+ scheme,
71
+ method: String(request.method?.() || "GET").toUpperCase().slice(0, 16),
72
+ });
73
+ }
74
+
75
+ async function locatorState(locator, timeoutMs, { allowDetached = false } = {}) {
76
+ const count = await locator.count();
77
+ if (count !== 1) {
78
+ if (allowDetached && count === 0) {
79
+ return Object.freeze({ detached: true });
80
+ }
81
+ throw driverError(
82
+ "CC_REPLAY_UI_TARGET_AMBIGUOUS",
83
+ "recorded UI selector must resolve to exactly one element",
84
+ { matchCount: count },
85
+ );
86
+ }
87
+ await locator.waitFor({ state: "visible", timeout: timeoutMs });
88
+ const [tagName, role, inputType, visible] = await Promise.all([
89
+ locator.evaluate((element) => element.tagName.toLowerCase()),
90
+ locator.getAttribute("role"),
91
+ locator.getAttribute("type"),
92
+ locator.isVisible(),
93
+ ]);
94
+ let semanticValue;
95
+ if (["input", "select", "textarea"].includes(tagName)) {
96
+ semanticValue = await locator.inputValue();
97
+ } else {
98
+ semanticValue = String((await locator.textContent()) || "").trim();
99
+ }
100
+ return Object.freeze({
101
+ detached: false,
102
+ tagName,
103
+ role: role || null,
104
+ inputType: inputType || null,
105
+ visible,
106
+ semanticValueDigest: digest(
107
+ semanticValue,
108
+ "cc.record-replay.ui-semantic-value/v1",
109
+ ),
110
+ });
111
+ }
112
+
113
+ async function assertAction(locator, action, timeoutMs) {
114
+ await locator.waitFor({ state: "visible", timeout: timeoutMs });
115
+ if (!("value" in action)) return;
116
+ const tagName = await locator.evaluate((element) =>
117
+ element.tagName.toLowerCase(),
118
+ );
119
+ const actual = ["input", "select", "textarea"].includes(tagName)
120
+ ? await locator.inputValue()
121
+ : String((await locator.textContent()) || "").trim();
122
+ if (String(actual) !== String(action.value)) {
123
+ throw driverError(
124
+ "CC_REPLAY_UI_ASSERTION_FAILED",
125
+ "recorded UI assertion did not match the reviewed value",
126
+ );
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Launch an ephemeral Chromium context that can execute the low-risk
132
+ * Record & Replay action vocabulary. The context owns its page and denies all
133
+ * filesystem and network requests. Returned evidence contains hashes and
134
+ * structural metadata only; selector, typed value, page text, and URLs are
135
+ * never copied into the replay report.
136
+ */
137
+ export async function launchPlaywrightRecordedSkillDriver({
138
+ html,
139
+ playwright,
140
+ timeoutMs = 5_000,
141
+ settleMs = 25,
142
+ viewport = { width: 960, height: 640 },
143
+ } = {}) {
144
+ const fixtureHtml = boundedString(html, "fixture html", 1_000_000);
145
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 30_000) {
146
+ throw driverError(
147
+ "CC_REPLAY_UI_ARGUMENT_INVALID",
148
+ "timeoutMs must be an integer between 100 and 30000",
149
+ );
150
+ }
151
+ if (!Number.isInteger(settleMs) || settleMs < 0 || settleMs > 1_000) {
152
+ throw driverError(
153
+ "CC_REPLAY_UI_ARGUMENT_INVALID",
154
+ "settleMs must be an integer between 0 and 1000",
155
+ );
156
+ }
157
+ const safeViewport = boundedViewport(viewport);
158
+ const runtime = playwright || (await import("playwright"));
159
+ if (typeof runtime?.chromium?.launch !== "function") {
160
+ throw driverError(
161
+ "CC_REPLAY_UI_DRIVER_UNAVAILABLE",
162
+ "Playwright Chromium is required for recorded UI replay",
163
+ );
164
+ }
165
+
166
+ const browser = await runtime.chromium.launch({ headless: true });
167
+ const context = await browser.newContext({
168
+ viewport: safeViewport,
169
+ acceptDownloads: false,
170
+ serviceWorkers: "block",
171
+ });
172
+ const deniedRequests = [];
173
+ const receipts = [];
174
+ let page;
175
+ try {
176
+ await context.route("**/*", async (route) => {
177
+ const metadata = requestMetadata(route.request());
178
+ if (DENIED_SCHEMES.has(metadata.scheme)) {
179
+ deniedRequests.push(metadata);
180
+ await route.abort("blockedbyclient");
181
+ return;
182
+ }
183
+ await route.continue();
184
+ });
185
+ await context.setOffline(true);
186
+ page = await context.newPage();
187
+ await page.setContent(fixtureHtml, {
188
+ waitUntil: "domcontentloaded",
189
+ timeout: timeoutMs,
190
+ });
191
+ } catch {
192
+ await browser.close();
193
+ throw driverError(
194
+ "CC_REPLAY_UI_DRIVER_SETUP_FAILED",
195
+ "isolated recorded UI driver setup failed",
196
+ );
197
+ }
198
+ if (deniedRequests.length) {
199
+ await browser.close();
200
+ throw driverError(
201
+ "CC_REPLAY_UI_FIXTURE_NETWORKED",
202
+ "recorded UI fixture attempted filesystem or network access while loading",
203
+ { deniedRequestCount: deniedRequests.length },
204
+ );
205
+ }
206
+
207
+ const executor = Object.freeze({
208
+ capabilities: DRIVER_CAPABILITIES,
209
+ async execute(action, { isolation, capability } = {}) {
210
+ if (isolation?.sandboxed !== true || isolation?.network !== "deny") {
211
+ throw driverError(
212
+ "CC_REPLAY_UI_ISOLATION_REQUIRED",
213
+ "UI replay driver requires an ephemeral network-denied isolation",
214
+ );
215
+ }
216
+ const kind = String(action?.kind || "");
217
+ const expectedCapability = ACTION_CAPABILITIES[kind];
218
+ if (!expectedCapability || capability !== expectedCapability) {
219
+ throw driverError(
220
+ "CC_REPLAY_UI_CAPABILITY_MISMATCH",
221
+ "recorded UI action is outside the granted driver capability",
222
+ );
223
+ }
224
+ try {
225
+ const selector = boundedString(action.target, "action target", 1_024);
226
+ const locator = page.locator(selector);
227
+ const deniedBefore = deniedRequests.length;
228
+ const before = await locatorState(locator, timeoutMs);
229
+
230
+ if (kind === "click") {
231
+ await locator.click({ timeout: timeoutMs });
232
+ } else if (kind === "type") {
233
+ const value = boundedString(action.value, "typed value", 8_192);
234
+ await locator.fill(value, { timeout: timeoutMs });
235
+ } else if (kind === "select") {
236
+ const value = boundedString(action.value, "selected value", 1_024);
237
+ await locator.selectOption(value, { timeout: timeoutMs });
238
+ } else if (kind === "assert") {
239
+ await assertAction(locator, action, timeoutMs);
240
+ } else {
241
+ await locator.waitFor({ state: "visible", timeout: timeoutMs });
242
+ }
243
+
244
+ if (settleMs) await page.waitForTimeout(settleMs);
245
+ const newDeniedRequests = deniedRequests.length - deniedBefore;
246
+ if (newDeniedRequests > 0) {
247
+ throw driverError(
248
+ "CC_REPLAY_UI_NETWORK_ATTEMPT",
249
+ "recorded UI action attempted filesystem or network access",
250
+ { deniedRequestCount: newDeniedRequests },
251
+ );
252
+ }
253
+ const after = await locatorState(locator, timeoutMs, {
254
+ allowDetached: kind === "click",
255
+ });
256
+ const screenshot = await page.screenshot({
257
+ animations: "disabled",
258
+ caret: "hide",
259
+ });
260
+ const evidence = Object.freeze({
261
+ schema: "chainlesschain.recorded-skill-ui-evidence/v1",
262
+ kind,
263
+ capability,
264
+ targetDigest: digest(selector, "cc.record-replay.ui-target/v1"),
265
+ beforeStateDigest: digest(
266
+ JSON.stringify(before),
267
+ "cc.record-replay.ui-state/v1",
268
+ ),
269
+ afterStateDigest: digest(
270
+ JSON.stringify(after),
271
+ "cc.record-replay.ui-state/v1",
272
+ ),
273
+ pageDigest: digest(
274
+ await page.content(),
275
+ "cc.record-replay.ui-page/v1",
276
+ ),
277
+ screenshotDigest: digest(
278
+ screenshot,
279
+ "cc.record-replay.ui-screenshot/v1",
280
+ ),
281
+ networkPolicy: "deny",
282
+ deniedRequestCount: 0,
283
+ });
284
+ receipts.push(evidence);
285
+ return Object.freeze({ ok: true, evidence });
286
+ } catch (error) {
287
+ if (error?.name === "RecordedSkillUiDriverError") throw error;
288
+ throw driverError(
289
+ "CC_REPLAY_UI_ACTION_FAILED",
290
+ "recorded UI action failed inside the isolated driver",
291
+ );
292
+ }
293
+ },
294
+ });
295
+
296
+ let closed = false;
297
+ return Object.freeze({
298
+ executor,
299
+ browserVersion: String(browser.version?.() || "unknown").slice(0, 128),
300
+ fixtureDigest: digest(fixtureHtml, "cc.record-replay.ui-fixture/v1"),
301
+ summary() {
302
+ return Object.freeze({
303
+ schema: "chainlesschain.recorded-skill-ui-driver-summary/v1",
304
+ closed,
305
+ actionCount: receipts.length,
306
+ deniedRequestCount: deniedRequests.length,
307
+ receiptDigests: Object.freeze(
308
+ receipts.map((receipt) =>
309
+ digest(
310
+ JSON.stringify(receipt),
311
+ "cc.record-replay.ui-driver-receipt/v1",
312
+ ),
313
+ ),
314
+ ),
315
+ });
316
+ },
317
+ async close() {
318
+ if (closed) return;
319
+ closed = true;
320
+ try {
321
+ await context.close();
322
+ } finally {
323
+ await browser.close();
324
+ }
325
+ },
326
+ });
327
+ }