chainlesschain 0.162.176 → 0.162.178

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/README.md +10 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BDLu5Elz.js → AIOps-DRftcSue.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DH5us-CK.js → ActionButton-BFvUaMKR.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BZ0u8IFG.js → Analytics-C0-Rru4P.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-bx-BCljj.js +9 -0
  8. package/src/assets/web-panel/assets/{Artifacts-Br2YBbh_.js → Artifacts-DAJ8B3Un.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-C3C6a482.js → Audit-hOk4L1lr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-CzHvebcj.js → BackgroundAgents-3pbDpM9z.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-DZNHYDFd.js → Backup-BtziGDn9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-VRiC4fIx.js → BaseInput-kmshphJ3.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-Cm08WWAD.js → Chat-d7tGSosA.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CGHYrugQ.js → ChatBubbleRenderer-p-_PBpX_.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-DtByk7d7.js → Checkbox-C4Eh4Tz5.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CphXXOfD.js → Codegen-r7gmhZyA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-Hz86TnLR.js → Col-U7mDaGlL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-HP03ltpD.js → Community-Bpu8aH1T.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-B_N1mqQz.js → Compact-CHb2XBJe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CVztRpi8.js → Compliance-Cn_r4dey.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-wUVOyZih.js → Cowork-CdMWfDcz.js} +4 -4
  22. package/src/assets/web-panel/assets/{Cron-DW5TLJKw.js → Cron-BtnREHWy.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-CrAFGhWH.js → Crosschain-BKI9TSzr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-pA-oD8ke.js → DID-pkNubzzJ.js} +2 -2
  25. package/src/assets/web-panel/assets/Dashboard-1rrz1snN.js +3 -0
  26. package/src/assets/web-panel/assets/{Dropdown-EweJcsek.js → Dropdown-DLyFVxDc.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-C9xVLPG4.js → EmailListRenderer-CtUWfYVq.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CDcEEqlu.js → FamilyGuardDashboard-B8eX2Qe4.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DtZda9A_.js → Federation-D8lsDGus.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-CiYdcVz3.js → FormItemContext-CRYt_aIw.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DA13uwA0.js → GenericCardRenderer-ClKzY8_N.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DzRhZD1O.js → Git-BXj5Dney.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-D5RMgOSb.js → Governance-Cm-I8RMz.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BgrPfSNk.js → Inference-CPvbhvgt.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-DzjnD571.js → KnowledgeGraph-sYNDZaoi.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BhWzVDtN.js → Logs-BZY_3CRZ.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-BDape9cx.js → MarkdownRenderer-up0BW6pv.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-DJtL5GV7.js → Marketplace-C5MEJ2Qe.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CZcQygI7.js → McpTools-ihdrn43I.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-DUWSDUUu.js → Memory-Br8bPMlh.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BTwq2WKY.js → MobileBridge-BNntgrYW.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-BdV915mw.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-BiGugEQm.js → Mtc-A3wDmlqL.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-DY7a6gUe.js → MtcAudit-CF-mBLWM.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BQbDu_w7.js → Multisig-Iy2k_MSo.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-CM2wXTLv.js → NLProgramming-kk0W7HUb.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-DI6gGs1v.js → Notes-B5u5t_4S.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-BsZOLWAB.js → NotificationSettings-BN0POf6_.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-Cds0_tXr.js → Organization-DwWQESuN.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-WunYMD87.js → Overflow-DG6klrcC.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-Bo6j4DDv.js → P2P-BwJcKkgZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-Bh2XPD5f.js → PdhVaultBrowser-BwOHG0in.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-EMvMMC8N.js → Permissions-CGMXe4TX.js} +4 -4
  55. package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +8 -0
  56. package/src/assets/web-panel/assets/{Pipeline-BOx8HOgL.js → Pipeline-BzJfeoGt.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-tBeTVQVM.js → Privacy-DLh3Bblx.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-CVYg1W4M.js → ProjectInit-USVWW36c.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Dvt5I7y1.js → ProjectSettings-CiiOn62h.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-Ct8xqY-E.js → Providers-CBPWWzpu.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-Cni8Ooja.js → QrScannerModal-QVwoRUq3.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BJVLKdxr.js → QuickAsk-CXnR-2Vn.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DPG8Rbbm.js → Recommend-ClaNzjmF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DsdR7dCt.js → RemoteSession-C3rGw8vJ.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Csg1YM-b.js → Reputation-Ci-OWAoP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-D9DoFoEf.js → Row-DRhAwXOS.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BsvKrXMS.js → RssFeed-BQLJQ2_y.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DFUStfgQ.js → Search-QQUpkfFd.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-ILg1zxdK.js → Security-DdXKzuKT.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-Dd41PCaY.js → Services-x2Zh62Qd.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-Bzo1cN80.js → Skeleton-GdY4KwRz.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-BDHh17P0.js → Skills-Bv4tY1Fd.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-jl7UTiRY.js → Sla-C3QnM-11.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BePa-RF6.js → SpeechSettings-Bl3ytvmI.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DiqqDKGZ.js → SyncSettings-Wp9IwTsP.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DYQ_6lxj.js → Tasks-CF3rIrDe.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-JiDeI06L.js → Templates-CGfvVVM9.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-hhgI6zwS.js → Tenant-CVXmwQ3U.js} +1 -1
  80. package/src/assets/web-panel/assets/Terminal-bvwrt03E.js +3 -0
  81. package/src/assets/web-panel/assets/{TimelineRenderer-B8uB5Llk.js → TimelineRenderer-D0g74KhH.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-C3b_6r2z.js → Tokens-BH4IwzlT.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-tIP7HKVe.js → Trigger-DJOpM533.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-vjoc11vJ.js → Trust-B43EPRLr.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DUPBnGuH.js → UkeySign-BmBFG-em.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CBvhUY5T.js → VideoEditing-n9oJwB8k.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DUaQ0uA5.js → Wallet-D5QeivHp.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DllSivjE.js → WebAuthn-DcNy7igO.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-xc8D3uO6.js → WorkflowEditor-BVjc7-5-.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-BVicU7oA.js → chat-Dy5Tfj3m.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-DJzLmhLm.js → colors-C4c2CNZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-BvYRfThE.js → compact-item-DPa3O5vN.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DGXxCBKR.js → createContext-TLcfNA6_.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-SBDJB1y4.js → hasIn-D41LVFkp.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-D7HimK0M.js → index-0oevYjfT.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DCdRAgjy.js → index-388pLGJ1.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BO1itZaY.js → index-B8nLBZbv.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-DvUTDYXX.js → index-BPTGAjee.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-hxTODbM1.js → index-BPtrOBmW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-wcCL-miV.js → index-BQiIxHBy.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CKyEivf8.js → index-BSM1cHVK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BJZf9DhV.js → index-BYPr9fkz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DHVLJ8q0.js → index-BsQ7Ev6i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cs2rmLFt.js → index-Btk9qLL2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-z1ZylsLq.js → index-BvmziZSG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Cf-O_g_8.js → index-BvopLC_T.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DMarmfrv.js → index-C7xhTVyP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CoJAO1Lm.js → index-CNk_ZooP.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-9gdI7uUW.js → index-CQrEHoOG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bf-ctERC.js → index-CSxex23J.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-MjC4G09d.js → index-CVjgm992.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-e4fXH_WO.js → index-CWK4zOPe.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-xfluRkCT.js → index-CX0VsoP4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DRfiXB_p.js → index-CXl0YPNG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-d5BVd-cL.js → index-Cip7LWml.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Dm_77jgR.js → index-Cq_hlWNt.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-YJkzy8ZF.js → index-CzZ49YZL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CtwqO5qo.js → index-D2Nkivlm.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BYqz4Wrr.js → index-DBUgRs4D.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DEjVkdzq.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CdGY9P8a.js → index-DNsBj2Ob.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DonCnfMr.js → index-Di4X8cfI.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-1SdDS6lR.js → index-DljFsVGI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DnjPdvR6.js → index-Korc3QIe.js} +1 -1
  126. package/src/assets/web-panel/assets/index-LMOc5xrS.js +1 -0
  127. package/src/assets/web-panel/assets/{index-VMfwIH5G.js → index-MKZDje0H.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DKrvvWm4.js → index-OHAfRu_3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-FxZzRyE1.js → index-WEJr8PL5.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Crk5NTb0.js → index-WinCp6du.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DCqRS6da.js → index-Yjr6cqSK.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BTrBGBJs.js → index-iQ6gtFih.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-lpaGoTH3.js → index-pt3s7yvV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CloCgBUR.js → index-tQ3igERQ.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-C1zO1KnY.js → initDefaultProps-BFZu87WZ.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-CWLtEGmC.js → motion-DJIo7qvt.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DzD_C3Vm.js → move-Cfr5qknL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-D880PYJl.js → mtc-parser-Bgc-OadH.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-CFR_z2ov.js → omit-X3WI-TNp.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-srIhgrSC.js → pickAttrs-pJhU9IxK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-C1rXpibP.js → placementArrow-BNg_Vj3T.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-C30gRcSS.js → responsiveObserve-TeSEGQ5-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CvtKiMfV.js → slide-D_t_P0bv.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-DNHO7ITG.js → statusUtils-DrrIMwAT.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Dt_Sl-g4.js → styleChecker-BjZCuFRm.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-Cjev42Gh.js → useFlexGapSupport-rTFdlqdQ.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-Baxglj2V.js → useFs-BidCpfLX.js} +1 -1
  148. package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +1 -0
  149. package/src/assets/web-panel/assets/{vnode-mIRkdT7o.js → vnode-BrbcRiHZ.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-HzI5DlH_.js → zoom-PISX7AkR.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/__tests__/hub-wechat.test.js +45 -0
  153. package/src/commands/agenda.js +22 -6
  154. package/src/commands/agent.js +3 -3
  155. package/src/commands/background-session.js +16 -6
  156. package/src/commands/batch.js +79 -27
  157. package/src/commands/changelog.js +15 -4
  158. package/src/commands/context.js +33 -1
  159. package/src/commands/eval.js +21 -6
  160. package/src/commands/hook.js +2 -2
  161. package/src/commands/hub.js +473 -39
  162. package/src/commands/init.js +212 -110
  163. package/src/commands/llm.js +22 -6
  164. package/src/commands/loop.js +9 -2
  165. package/src/commands/skill.js +13 -1
  166. package/src/commands/statusline.js +3 -3
  167. package/src/data/changelog.json +53 -5
  168. package/src/gateways/ws/personal-data-hub-protocol.js +22 -2
  169. package/src/harness/background-task-manager.js +18 -6
  170. package/src/harness/background-task-worker.js +11 -18
  171. package/src/harness/golden-transcript.js +3 -0
  172. package/src/harness/mcp-client.js +9 -15
  173. package/src/harness/worktree-isolator.js +18 -21
  174. package/src/index.js +18 -7
  175. package/src/lib/agent-core.js +5 -0
  176. package/src/lib/agent-ipc-bus.js +12 -3
  177. package/src/lib/agent-team/team-worktree.js +28 -6
  178. package/src/lib/agent-worktree.js +20 -19
  179. package/src/lib/approval-authority-store.js +891 -0
  180. package/src/lib/async-hook-supervisor.cjs +134 -24
  181. package/src/lib/async-hook-supervisor.js +47 -0
  182. package/src/lib/background-agent-supervisor.js +35 -12
  183. package/src/lib/background-interaction-resolver.js +399 -54
  184. package/src/lib/background-session-transport.js +3 -1
  185. package/src/lib/background-task-manager.js +5 -1
  186. package/src/lib/changelog.js +57 -0
  187. package/src/lib/chrome-connector.js +6 -2
  188. package/src/lib/claude-code-bridge.js +24 -8
  189. package/src/lib/cli-anything-bridge.js +82 -25
  190. package/src/lib/context-breakdown.js +82 -3
  191. package/src/lib/doctor-checkup.js +18 -6
  192. package/src/lib/eval/tasks.js +25 -31
  193. package/src/lib/event-runtime-host.js +225 -0
  194. package/src/lib/event-runtime-store.js +102 -21
  195. package/src/lib/event-runtime-worker.js +70 -2
  196. package/src/lib/hook-runner.cjs +71 -36
  197. package/src/lib/hook-runner.js +55 -0
  198. package/src/lib/hooks-v2-producers.js +33 -8
  199. package/src/lib/hooks-v2-runtime.js +360 -40
  200. package/src/lib/host-adb-bridge.js +18 -3
  201. package/src/lib/interaction-adapter.js +66 -0
  202. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  203. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  204. package/src/lib/lsp/benchmark.js +20 -4
  205. package/src/lib/lsp/lsp-client.js +6 -4
  206. package/src/lib/lsp/lsp-server-registry.js +0 -2
  207. package/src/lib/operation-fingerprint.js +107 -2
  208. package/src/lib/orchestrator.js +8 -2
  209. package/src/lib/packer/pack-update-applier.js +14 -2
  210. package/src/lib/packer/smoke-runner.js +18 -5
  211. package/src/lib/paths.js +1 -1
  212. package/src/lib/pdh-dedicated-batch-collectors.js +124 -0
  213. package/src/lib/personal-data-hub-wiring.js +327 -52
  214. package/src/lib/plugin-monitor-supervisor.js +20 -13
  215. package/src/lib/plugin-runtime/install.js +10 -3
  216. package/src/lib/process-execution-broker/credential-agent.js +954 -111
  217. package/src/lib/process-execution-broker/index.js +369 -85
  218. package/src/lib/process-execution-broker/platform-sandbox.js +320 -140
  219. package/src/lib/provider-http-error.js +94 -0
  220. package/src/lib/remote-approval-bridge.js +222 -26
  221. package/src/lib/repl-bang-memorize.js +10 -2
  222. package/src/lib/repl-goal.js +19 -2
  223. package/src/lib/settings-hook-events.cjs +13 -8
  224. package/src/lib/settings-hook-events.js +20 -0
  225. package/src/lib/side-effect-ledger-store.js +65 -14
  226. package/src/lib/skill-loader.js +215 -12
  227. package/src/lib/skill-packs/generator.js +18 -9
  228. package/src/lib/skill-process-broker.js +85 -0
  229. package/src/lib/slash-commands.js +10 -2
  230. package/src/lib/status-line.cjs +28 -7
  231. package/src/lib/status-line.js +33 -0
  232. package/src/lib/sub-agent-context.js +42 -0
  233. package/src/lib/turn-binding-store.js +64 -15
  234. package/src/lib/turn-binding.js +85 -16
  235. package/src/repl/agent-repl.js +88 -49
  236. package/src/repl/remote-approval.js +13 -13
  237. package/src/repl/repl-turn-binding.js +46 -16
  238. package/src/runtime/agent-core.js +419 -84
  239. package/src/runtime/coding-agent-contract-shared.cjs +16 -0
  240. package/src/runtime/headless-runner.js +172 -44
  241. package/src/runtime/headless-stream.js +221 -11
  242. package/src/runtime/session-slash-commands.js +506 -0
  243. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  244. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  245. package/src/skills/video-editing/media-process.js +20 -0
  246. package/src/skills/video-editing/render/audio-mix.js +13 -3
  247. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  248. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  249. package/src/workers/background-agent-worker.js +76 -40
  250. package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +0 -9
  251. package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +0 -3
  252. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +0 -1
  253. package/src/assets/web-panel/assets/OrderTableRenderer-C48mu09U.js +0 -1
  254. package/src/assets/web-panel/assets/PersonalDataHub-AX3ypUDV.js +0 -9
  255. package/src/assets/web-panel/assets/Projects-Cc7FC9-e.js +0 -1
  256. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +0 -3
  257. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +0 -1
  258. package/src/assets/web-panel/assets/index-B7nuIywP.js +0 -1
  259. package/src/assets/web-panel/assets/index-ycwAfC-i.js +0 -1
  260. package/src/assets/web-panel/assets/usePersonalDataHub-B4NkcLaP.js +0 -1
@@ -25,15 +25,17 @@ import fs from "node:fs/promises";
25
25
  import path from "node:path";
26
26
  import broker from "./process-execution-broker/index.js";
27
27
  import { EventRuntimeStore } from "./event-runtime-store.js";
28
+ import { getDefaultEventRuntimeHost } from "./event-runtime-host.js";
28
29
 
29
30
  const VALID_HOOK_EVENTS = new Set([
31
+ "Setup",
30
32
  // Session
31
33
  "PreToolUse", "PostToolUse", "Notification", "Stop", "SubagentStart", "SubagentStop",
32
- "SessionResume", "SessionPause", "PostCompact",
34
+ "SessionResume", "SessionPause", "PostCompact", "StopFailure",
33
35
  // Auth
34
36
  "PreCommit", "PostCommit",
35
37
  // Skill
36
- "UserPromptSubmit", "SessionStart", "SessionEnd",
38
+ "UserPromptSubmit", "UserPromptExpansion", "SessionStart", "SessionEnd",
37
39
  // Model
38
40
  "PreCompact", "ModelSelection",
39
41
  // Config
@@ -41,19 +43,178 @@ const VALID_HOOK_EVENTS = new Set([
41
43
  // Timeline
42
44
  "TimelineEntry",
43
45
  // MCP
44
- "McpRequest", "McpResponse", "MCPElicitation",
46
+ "McpRequest", "McpResponse", "MCPElicitation", "Elicitation", "ElicitationResult",
45
47
  // Task / workspace lifecycle
46
48
  "TaskCreated", "TaskCompleted", "InstructionsLoaded", "CwdChanged",
47
- "WorktreeCreate", "WorktreeRemove",
49
+ "WorktreeCreate", "WorktreeRemove", "TeammateIdle",
50
+ // Tool aggregate / failure / filesystem lifecycle
51
+ "PostToolUseFailure", "PostToolBatch", "FileChanged",
48
52
  ]);
49
53
 
50
- const VALID_EXECUTOR_TYPES = new Set(["command", "http", "prompt", "agent", "js"]);
54
+ const VALID_EXECUTOR_TYPES = new Set([
55
+ "command",
56
+ "http",
57
+ "mcp_tool",
58
+ "prompt",
59
+ "agent",
60
+ // Trusted programmatic compatibility executor. Config-loaded source text is
61
+ // not executed in-process.
62
+ "js",
63
+ ]);
64
+
65
+ export const HOOK_EVENT_SCHEMA_VERSION = 1;
66
+ const DECISION_EVENTS = new Set([
67
+ "Setup",
68
+ "PreToolUse",
69
+ "UserPromptSubmit",
70
+ "UserPromptExpansion",
71
+ "PermissionRequest",
72
+ "ModelSelection",
73
+ "PreCompact",
74
+ ]);
75
+ const CONTEXT_EVENTS = new Set([
76
+ "Setup",
77
+ "UserPromptSubmit",
78
+ "UserPromptExpansion",
79
+ "SessionStart",
80
+ "PostCompact",
81
+ ]);
82
+ const DEFAULT_ALLOWED_EXECUTORS = Object.freeze([
83
+ "command",
84
+ "http",
85
+ "mcp_tool",
86
+ "prompt",
87
+ "agent",
88
+ "js",
89
+ ]);
90
+
91
+ export const HOOK_EVENT_CONTRACTS = Object.freeze(
92
+ Object.fromEntries(
93
+ [...VALID_HOOK_EVENTS].map((event) => [
94
+ event,
95
+ Object.freeze({
96
+ schemaVersion: HOOK_EVENT_SCHEMA_VERSION,
97
+ event,
98
+ allowedExecutors: DEFAULT_ALLOWED_EXECUTORS,
99
+ decisionCapable: DECISION_EVENTS.has(event),
100
+ contextCapable: CONTEXT_EVENTS.has(event),
101
+ blockingSemantics: DECISION_EVENTS.has(event)
102
+ ? "strictest:block>ask>allow>continue"
103
+ : "observe-only",
104
+ }),
105
+ ]),
106
+ ),
107
+ );
108
+
109
+ const SAFE_ENV_KEYS = Object.freeze([
110
+ "PATH",
111
+ "PATHEXT",
112
+ "SystemRoot",
113
+ "WINDIR",
114
+ "COMSPEC",
115
+ "TMP",
116
+ "TEMP",
117
+ "TMPDIR",
118
+ "LANG",
119
+ "LC_ALL",
120
+ "HOME",
121
+ "USERPROFILE",
122
+ ]);
123
+ const DECISION_RANK = Object.freeze({
124
+ continue: 0,
125
+ allow: 1,
126
+ ask: 2,
127
+ block: 3,
128
+ });
129
+
130
+ function normalizeDecision(value) {
131
+ const decision = String(value || "continue").toLowerCase();
132
+ return Object.hasOwn(DECISION_RANK, decision) ? decision : "continue";
133
+ }
134
+
135
+ function strictestDecision(results) {
136
+ let decision = "continue";
137
+ for (const record of results) {
138
+ const candidate = normalizeDecision(record?.decision);
139
+ if (DECISION_RANK[candidate] > DECISION_RANK[decision]) {
140
+ decision = candidate;
141
+ }
142
+ }
143
+ return decision;
144
+ }
145
+
146
+ function hostnameMatches(hostname, pattern) {
147
+ const host = String(hostname || "").toLowerCase();
148
+ const rule = String(pattern || "").trim().toLowerCase();
149
+ if (!host || !rule) return false;
150
+ if (rule.startsWith("*.")) {
151
+ const suffix = rule.slice(1);
152
+ return host.endsWith(suffix) && host.length > suffix.length;
153
+ }
154
+ return host === rule;
155
+ }
156
+
157
+ function buildHookEnvironment(hook, policy) {
158
+ const env = {};
159
+ for (const key of SAFE_ENV_KEYS) {
160
+ if (process.env[key] != null) env[key] = process.env[key];
161
+ }
162
+ const managed = new Set(
163
+ (policy.environmentAllowlist || []).map((key) => String(key)),
164
+ );
165
+ const requested = new Set(
166
+ (hook.environmentAllowlist || hook.envAllowlist || []).map((key) =>
167
+ String(key),
168
+ ),
169
+ );
170
+ for (const key of requested) {
171
+ if (!managed.has(key) || process.env[key] == null) continue;
172
+ env[key] = process.env[key];
173
+ }
174
+ env.CC_HOOK_EVENT = hook.event;
175
+ env.CC_HOOK_SCHEMA_VERSION = String(HOOK_EVENT_SCHEMA_VERSION);
176
+ return env;
177
+ }
178
+
179
+ function hookBudget(hook) {
180
+ return Object.freeze({
181
+ maxTurns: Math.min(10, Math.max(1, Number(hook.maxTurns) || 1)),
182
+ maxTokens: Math.min(
183
+ 32768,
184
+ Math.max(1, Number(hook.maxTokens || hook.tokenBudget) || 4096),
185
+ ),
186
+ timeoutMs: Math.min(
187
+ 10 * 60 * 1000,
188
+ Math.max(1, Number(hook.timeoutMs) || 30000),
189
+ ),
190
+ });
191
+ }
51
192
 
52
193
  class HooksV2Runtime extends EventEmitter {
53
194
  constructor(configDir, options = {}) {
54
195
  super();
55
196
  this.configDir = configDir;
56
197
  this.durableStore = options.durableStore || null;
198
+ this.durableOwner =
199
+ options.durableOwner ||
200
+ `hooks-inline:${process.pid}:${crypto.randomUUID()}`;
201
+ this.durableRecoveryBufferMs = Math.max(
202
+ 1000,
203
+ Number(options.durableRecoveryBufferMs) || 5000,
204
+ );
205
+ this.executionBroker = options.broker || broker;
206
+ this.fetchImpl = options.fetch || globalThis.fetch;
207
+ this.executors = {
208
+ ...(options.executors || {}),
209
+ ...(options.mcpExecutor ? { mcp_tool: options.mcpExecutor } : {}),
210
+ ...(options.promptExecutor ? { prompt: options.promptExecutor } : {}),
211
+ ...(options.agentExecutor ? { agent: options.agentExecutor } : {}),
212
+ };
213
+ this.managedPolicy = {
214
+ httpAllowlist: [],
215
+ environmentAllowlist: [],
216
+ ...(options.managedPolicy || {}),
217
+ };
57
218
  this.hooks = new Map(); // eventName -> HookDefinition[]
58
219
  this.executionLog = [];
59
220
  this._loaded = false;
@@ -119,6 +280,12 @@ class HooksV2Runtime extends EventEmitter {
119
280
  registerHook(def) {
120
281
  if (!VALID_HOOK_EVENTS.has(def.event)) throw new Error(`Invalid event: ${def.event}`);
121
282
  if (!VALID_EXECUTOR_TYPES.has(def.type)) throw new Error(`Invalid executor type: ${def.type}`);
283
+ const contract = HOOK_EVENT_CONTRACTS[def.event];
284
+ if (!contract.allowedExecutors.includes(def.type)) {
285
+ throw new Error(
286
+ `Executor ${def.type} is not allowed for event ${def.event}`,
287
+ );
288
+ }
122
289
  const id = def.id || crypto.randomUUID();
123
290
  const full = { id, blocking: false, timeoutMs: 30000, ...def };
124
291
  if (!this.hooks.has(def.event)) this.hooks.set(def.event, []);
@@ -149,12 +316,45 @@ class HooksV2Runtime extends EventEmitter {
149
316
  }
150
317
 
151
318
  const hooks = this.hooks.get(eventName) || [];
152
- const durableRecord = this.durableStore
319
+ // Reserve the durable record for the inline producer before executing any
320
+ // hook. A process-level EventRuntimeHost may observe the same store, but it
321
+ // cannot reclaim this record until the longest declared hook timeout plus
322
+ // a recovery buffer has elapsed. If this process dies, the expired lease
323
+ // becomes claimable and the host replays the event exactly once.
324
+ const longestHookTimeout = hooks.reduce(
325
+ (max, hook) => Math.max(max, Number(hook?.timeoutMs) || 30000),
326
+ 0,
327
+ );
328
+ const durableRecord =
329
+ this.durableStore && options.skipDurable !== true
153
330
  ? this.durableStore.enqueueInbox(
154
- { event: eventName, context },
155
- { id: context.event_id || context.eventId || null },
331
+ {
332
+ runtime_type: "hooks.v2",
333
+ requiresHandler: true,
334
+ event: eventName,
335
+ context,
336
+ },
337
+ {
338
+ id: context.event_id || context.eventId || null,
339
+ claimOwner: this.durableOwner,
340
+ leaseMs: longestHookTimeout + this.durableRecoveryBufferMs,
341
+ },
156
342
  )
157
343
  : null;
344
+ if (durableRecord?.duplicate) {
345
+ if (durableRecord.status === "done" && durableRecord.result) {
346
+ return { ...durableRecord.result, duplicate: true };
347
+ }
348
+ return {
349
+ success: false,
350
+ results: [],
351
+ blocked: true,
352
+ blockingResult: null,
353
+ decision: "block",
354
+ duplicate: true,
355
+ pending: true,
356
+ };
357
+ }
158
358
  // Hooks are parallel by default. De-duplicate by id so a reload or layered
159
359
  // config cannot execute the same handler twice. `parallel:false` remains a
160
360
  // deterministic compatibility mode for callers that require ordering.
@@ -179,6 +379,9 @@ class HooksV2Runtime extends EventEmitter {
179
379
  case "http":
180
380
  result = await this._execHttp(hook, context);
181
381
  break;
382
+ case "mcp_tool":
383
+ result = await this._execMcpTool(hook, context);
384
+ break;
182
385
  case "prompt":
183
386
  result = await this._execPrompt(hook, context);
184
387
  break;
@@ -193,13 +396,18 @@ class HooksV2Runtime extends EventEmitter {
193
396
  record.durationMs = Date.now() - start;
194
397
  record.status = "success";
195
398
  record.result = result;
196
- if (hook.blocking && result && result.decision === "block") record.blocked = true;
399
+ record.decision = normalizeDecision(result?.decision);
197
400
 
198
401
  this.emit("hook:success", record);
199
402
  } catch (err) {
200
403
  record.durationMs = Date.now() - start;
201
404
  record.status = "error";
202
405
  record.error = err.message;
406
+ record.decision =
407
+ HOOK_EVENT_CONTRACTS[eventName].decisionCapable &&
408
+ hook.failureMode !== "ignore"
409
+ ? "block"
410
+ : "continue";
203
411
  this.emit("hook:error", record);
204
412
  }
205
413
 
@@ -212,17 +420,42 @@ class HooksV2Runtime extends EventEmitter {
212
420
  if (options.parallel === false) {
213
421
  for (const hook of uniqueHooks) results.push(await runOne(hook));
214
422
  }
215
- const blocking = results.filter((r) => r.blocked === true);
216
- const blocked = blocking.length > 0;
423
+ const contract = HOOK_EVENT_CONTRACTS[eventName];
424
+ const decision = contract.decisionCapable
425
+ ? strictestDecision(results)
426
+ : "continue";
427
+ const blocking = results.filter((record) => record.decision === "block");
428
+ const blocked = decision === "block";
217
429
  const blockingResult = blocking[0]?.result || null;
218
-
219
- const outcome = { success: !blocked, results, blocked, blockingResult };
220
- if (durableRecord && !durableRecord.duplicate) {
221
- this.durableStore.acknowledgeInbox(durableRecord.id, {
222
- event: eventName,
223
- blocked,
224
- resultCount: results.length,
225
- });
430
+ const outcome = {
431
+ success:
432
+ !blocked && results.every((record) => record.status !== "error"),
433
+ results,
434
+ blocked,
435
+ requiresApproval: decision === "ask",
436
+ decision,
437
+ blockingResult,
438
+ schemaVersion: HOOK_EVENT_SCHEMA_VERSION,
439
+ };
440
+ if (durableRecord) {
441
+ const settled = this.durableStore.acknowledgeInbox(
442
+ durableRecord.id,
443
+ outcome,
444
+ {
445
+ owner: durableRecord.lease?.owner,
446
+ fence: durableRecord.lease?.fence,
447
+ },
448
+ );
449
+ if (settled == null) {
450
+ return {
451
+ success: false,
452
+ results,
453
+ blocked: true,
454
+ blockingResult: null,
455
+ decision: "block",
456
+ leaseLost: true,
457
+ };
458
+ }
226
459
  this.durableStore.enqueueOutbox(
227
460
  { event: eventName, outcome },
228
461
  { id: `${durableRecord.id}:result` },
@@ -237,19 +470,36 @@ class HooksV2Runtime extends EventEmitter {
237
470
  }
238
471
 
239
472
  async _execCommand(hook, context) {
240
- const child = await broker.spawn(
241
- hook.command, hook.args || [],
473
+ if (!hook.command || typeof hook.command !== "string") {
474
+ throw new Error("command hook requires a command");
475
+ }
476
+ const budget = hookBudget(hook);
477
+ const child = await this.executionBroker.spawn(
478
+ hook.command,
479
+ hook.args || [],
242
480
  {
243
- env: { ...process.env, CC_HOOK_EVENT: hook.event, CC_HOOK_CONTEXT: JSON.stringify(context) },
244
- stdio: "pipe",
245
- timeout: hook.timeoutMs,
481
+ cwd: hook.cwd || this.configDir || process.cwd(),
482
+ env: buildHookEnvironment(hook, this.managedPolicy),
483
+ stdio: ["pipe", "pipe", "pipe"],
484
+ timeout: budget.timeoutMs,
485
+ shell: hook.shell === true,
486
+ origin: "hook",
487
+ scope: "hook",
488
+ policy: "allow",
489
+ hookName: hook.id,
246
490
  },
247
- { origin: "hook", hookName: hook.id, timeout: hook.timeoutMs },
248
491
  );
492
+ const payload = JSON.stringify({
493
+ schema_version: HOOK_EVENT_SCHEMA_VERSION,
494
+ hook_event_name: hook.event,
495
+ context,
496
+ });
497
+ child.stdin?.end(payload);
249
498
  return new Promise((resolve, reject) => {
250
499
  let stdout = "", stderr = "";
251
- child.stdout.on("data", d => stdout += d);
252
- child.stderr.on("data", d => stderr += d);
500
+ child.stdout?.on("data", d => stdout += d);
501
+ child.stderr?.on("data", d => stderr += d);
502
+ child.on("error", reject);
253
503
  child.on("exit", (code) => {
254
504
  if (code === 0) {
255
505
  let parsed = {};
@@ -263,14 +513,36 @@ class HooksV2Runtime extends EventEmitter {
263
513
  }
264
514
 
265
515
  async _execHttp(hook, context) {
266
- // Simple HTTP webhook executor
516
+ if (typeof this.fetchImpl !== "function") {
517
+ throw new Error("HTTP hook executor is unavailable");
518
+ }
519
+ const target = new URL(hook.url);
520
+ const allowlist = this.managedPolicy.httpAllowlist || [];
521
+ if (
522
+ target.protocol !== "https:" ||
523
+ !allowlist.some((pattern) => hostnameMatches(target.hostname, pattern))
524
+ ) {
525
+ const error = new Error(
526
+ `HTTP hook target is outside the managed HTTPS allowlist: ${target.hostname}`,
527
+ );
528
+ error.code = "CC_HOOK_HTTP_TARGET_DENIED";
529
+ throw error;
530
+ }
267
531
  const method = hook.method || "POST";
268
532
  const controller = new AbortController();
269
- const timer = setTimeout(() => controller.abort(), hook.timeoutMs || 30000);
533
+ const timer = setTimeout(
534
+ () => controller.abort(),
535
+ hookBudget(hook).timeoutMs,
536
+ );
537
+ const headers = { "Content-Type": "application/json" };
538
+ for (const [key, value] of Object.entries(hook.headers || {})) {
539
+ if (/^(authorization|cookie|proxy-authorization)$/i.test(key)) continue;
540
+ headers[key] = String(value);
541
+ }
270
542
  try {
271
- const res = await fetch(hook.url, {
543
+ const res = await this.fetchImpl(target, {
272
544
  method,
273
- headers: { "Content-Type": "application/json", ...(hook.headers || {}) },
545
+ headers,
274
546
  body: method !== "GET" ? JSON.stringify({ event: hook.event, context }) : undefined,
275
547
  signal: controller.signal,
276
548
  });
@@ -282,23 +554,58 @@ class HooksV2Runtime extends EventEmitter {
282
554
  }
283
555
 
284
556
  async _execPrompt(hook, context) {
285
- // Prompt template execution - returns expanded prompt for agent prepend
286
557
  const vars = { ...context };
287
558
  let expanded = hook.template || "";
288
559
  expanded = expanded.replace(/\$\{(\w+)\}/g, (_, k) => vars[k] ?? "");
289
- return { decision: "augment", prompt: expanded };
560
+ const executor = this.executors.prompt;
561
+ if (typeof executor !== "function") {
562
+ throw new Error("prompt hook executor is not configured");
563
+ }
564
+ return executor({
565
+ hook,
566
+ prompt: expanded,
567
+ context,
568
+ budget: hookBudget(hook),
569
+ });
290
570
  }
291
571
 
292
572
  async _execAgent(hook, context) {
293
- // Dispatch to agent/skill - stub for full integration
294
- return { decision: "delegate", agentName: hook.agentName, skillName: hook.skillName, context };
573
+ const executor = this.executors.agent;
574
+ if (typeof executor !== "function") {
575
+ throw new Error("agent hook executor is not configured");
576
+ }
577
+ return executor({
578
+ hook,
579
+ agentName: hook.agentName || null,
580
+ skillName: hook.skillName || null,
581
+ context,
582
+ budget: hookBudget(hook),
583
+ });
584
+ }
585
+
586
+ async _execMcpTool(hook, context) {
587
+ const executor = this.executors.mcp_tool;
588
+ if (typeof executor !== "function") {
589
+ throw new Error("MCP hook executor is not configured");
590
+ }
591
+ if (!hook.server || !hook.tool) {
592
+ throw new Error("mcp_tool hook requires server and tool");
593
+ }
594
+ return executor({
595
+ server: hook.server,
596
+ tool: hook.tool,
597
+ arguments: hook.arguments || context,
598
+ context,
599
+ hook,
600
+ budget: hookBudget(hook),
601
+ });
295
602
  }
296
603
 
297
604
  async _execJs(hook, context) {
298
605
  if (typeof hook.handler === "function") return hook.handler(context);
299
- // In-process JS execution via vm (sandboxed) - config-loaded code remains
300
- // disabled until the dedicated VM executor is enabled.
301
- return { decision: "noop", note: "JS executor stub - context received", contextKeys: Object.keys(context) };
606
+ throw new Error(
607
+ "Config-loaded JavaScript is disabled; register a trusted function handler",
608
+ );
302
609
  }
303
610
 
304
611
  /** Get all registered hooks */
@@ -317,11 +624,24 @@ class HooksV2Runtime extends EventEmitter {
317
624
  }
318
625
 
319
626
  // Singleton instance
627
+ const defaultEventRuntimeHost =
628
+ process.env.CC_EVENT_RUNTIME_DURABLE === "1"
629
+ ? getDefaultEventRuntimeHost()
630
+ : null;
320
631
  const hooksRuntime = new HooksV2Runtime(undefined, {
321
632
  durableStore:
322
- process.env.CC_EVENT_RUNTIME_DURABLE === "1"
633
+ defaultEventRuntimeHost?.store ||
634
+ (process.env.CC_EVENT_RUNTIME_DURABLE === "1"
323
635
  ? new EventRuntimeStore()
324
- : null,
636
+ : null),
325
637
  });
638
+ defaultEventRuntimeHost?.registerHandler(
639
+ (event) =>
640
+ hooksRuntime.executeHooks(event.event, event.context || {}, {
641
+ skipDurable: true,
642
+ recovered: true,
643
+ }),
644
+ { queue: "inbox", type: "hooks.v2" },
645
+ );
326
646
  export default hooksRuntime;
327
647
  export { HooksV2Runtime, VALID_HOOK_EVENTS, VALID_EXECUTOR_TYPES };
@@ -31,10 +31,20 @@
31
31
  * `\n` / EOS — every parse routine strips `\r+$` first.
32
32
  */
33
33
 
34
- import { execFile } from "node:child_process";
35
- import { promisify } from "node:util";
34
+ import executionBroker from "./process-execution-broker/index.js";
36
35
 
37
- const execFileP = promisify(execFile);
36
+ export const _deps = {
37
+ execFile: (...args) => executionBroker.execFile(...args),
38
+ };
39
+
40
+ function execFileP(file, args, options) {
41
+ return new Promise((resolve, reject) => {
42
+ _deps.execFile(file, args, options, (error, stdout, stderr) => {
43
+ if (error) reject(error);
44
+ else resolve({ stdout, stderr });
45
+ });
46
+ });
47
+ }
38
48
 
39
49
  export class HostAdbBridgeUnavailableError extends Error {
40
50
  constructor(reason) {
@@ -52,6 +62,10 @@ async function adb(args, opts = {}) {
52
62
  timeout: opts.timeoutMs || 30_000,
53
63
  maxBuffer: 32 * 1024 * 1024,
54
64
  encoding: "utf8",
65
+ origin: "host-adb:command",
66
+ policy: "allow",
67
+ scope: "host-adb",
68
+ shell: false,
55
69
  });
56
70
  if (stderr && /error:|failed:|protocol fault/i.test(stderr)) {
57
71
  throw new HostAdbBridgeUnavailableError(stderr.trim().split("\n")[0]);
@@ -585,6 +599,7 @@ export function createHostAdbBridge(opts = {}) {
585
599
 
586
600
  // Exposed for unit testing without spawning real adb.
587
601
  export const _internals = {
602
+ adb,
588
603
  parseContentQueryRows,
589
604
  mergeContactRows,
590
605
  listDevices,
@@ -179,6 +179,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
179
179
 
180
180
  this._sendWs({
181
181
  ...message,
182
+ ...(message.type === "question_request" ? { id: requestId } : {}),
182
183
  sessionId: this.sessionId,
183
184
  requestId,
184
185
  // Ride the binding out so the remote UI/bridge can echo it back on the
@@ -238,6 +239,71 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
238
239
  return answer === "true" || answer === "yes" || answer === "y";
239
240
  }
240
241
 
242
+ /**
243
+ * Agent-core AskUserQuestion transport for Desktop/WebSocket consumers.
244
+ * It uses the same pending map and `session-answer` resolver as MCP
245
+ * elicitation, while exposing the stream protocol's `question_request`
246
+ * shape so capable UIs can render options natively.
247
+ */
248
+ async askUser({
249
+ question,
250
+ options = null,
251
+ multiSelect = false,
252
+ timeoutMs,
253
+ defaultValue,
254
+ onTimeout,
255
+ onReject,
256
+ turnId,
257
+ toolUseId,
258
+ metadata = null,
259
+ } = {}) {
260
+ const normalizedTurnId =
261
+ turnId === undefined || turnId === null ? null : String(turnId);
262
+ const normalizedToolUseId =
263
+ toolUseId === undefined || toolUseId === null ? null : String(toolUseId);
264
+ const questionMetadata = {
265
+ ...(metadata && typeof metadata === "object" ? metadata : {}),
266
+ kind: "ask_user_question",
267
+ ...(normalizedTurnId
268
+ ? { turnId: normalizedTurnId, turn_id: normalizedTurnId }
269
+ : {}),
270
+ ...(normalizedToolUseId
271
+ ? {
272
+ toolUseId: normalizedToolUseId,
273
+ tool_use_id: normalizedToolUseId,
274
+ }
275
+ : {}),
276
+ };
277
+
278
+ return this._request(
279
+ {
280
+ type: "question_request",
281
+ questionType: "ask_user_question",
282
+ question: typeof question === "string" ? question : "",
283
+ options: Array.isArray(options) ? options : null,
284
+ multiSelect: multiSelect === true,
285
+ ...(defaultValue !== undefined ? { defaultValue } : {}),
286
+ ...(onTimeout ? { onTimeout } : {}),
287
+ ...(onReject ? { onReject } : {}),
288
+ ...(Number(timeoutMs) > 0 ? { timeoutMs: Number(timeoutMs) } : {}),
289
+ ...(normalizedTurnId
290
+ ? { turnId: normalizedTurnId, turn_id: normalizedTurnId }
291
+ : {}),
292
+ ...(normalizedToolUseId
293
+ ? {
294
+ toolUseId: normalizedToolUseId,
295
+ tool_use_id: normalizedToolUseId,
296
+ }
297
+ : {}),
298
+ metadata: questionMetadata,
299
+ },
300
+ {
301
+ kind: "question",
302
+ ...(Number(timeoutMs) > 0 ? { timeoutMs: Number(timeoutMs) } : {}),
303
+ },
304
+ );
305
+ }
306
+
241
307
  /**
242
308
  * Called by ws-server when a session-answer message arrives.
243
309
  * Resolves the corresponding pending promise.