chainlesschain 0.162.175 → 0.162.177

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 (311) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-DherW0Rh.js → AIOps-DT-VPWuN.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CDGfqt2Y.js → ActionButton-DyOreOVN.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BYhkzdgD.js → Analytics-DslNsKux.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-cEKk1cpb.js → AppLayout-BNJJAYME.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-_aXR3WWc.js → Artifacts-DoaAtEcY.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CYAf-gY7.js → Audit-DPG3WdJ9.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D4Q20phR.js → BackgroundAgents-C_JO6R5G.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cq0Tyk58.js → Backup-Cv3BooyX.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CPj_CdAv.js → BaseInput-D2uQXWu-.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-BQ-Qq0es.js → Chat-B4Vczyf3.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-rke6l3yg.js → Checkbox-qTgk4VeP.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DiacrIPq.js → Codegen-DlLWcIzj.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-Cx3BY-Up.js → Col-DNO73qp7.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DfmWiQy8.js → Community-vRDNpc4j.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DdQnpxy5.js → Compact-CEWtIXni.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-lzliOH_r.js → Compliance-KgYC9Fxi.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CYx4Un5k.js → Cowork-zj5wvkpT.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-BGDcPFlo.js → Cron-BLDoCozU.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-0_X5g2ri.js → Crosschain-BYnff4VY.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-C0PEf3Eu.js → DID-C75VFsVm.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DULQjQ58.js → Dashboard-C4Gutni2.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DGRHRbW6.js → Dropdown-B5dbO4Kx.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-ChX8XPwB.js → EmailListRenderer-BDipZtHJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dyq6IBNu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-DH1NfAL2.js → Federation-D9QOS-uW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BN1I_uNi.js → FormItemContext-B47HRwAm.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Dq_2UYpe.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BAy1yvI2.js → Git-DrNXZf1C.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-Db6Q2iSy.js → Governance-BGd7JR98.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-0ub6yHcr.js → Inference-D-2he1ed.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DtLetM36.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Bcoy7CUd.js → Logs-DiBQO-jL.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BfEMFAXf.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-tt2rzwRR.js → Marketplace-VR59OdrC.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BhOgX-MU.js → McpTools-DrPzwSd3.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-BJUxeWBL.js → Memory-CBgKALwQ.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BwUY2G9w.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BLRfHhqi.js → MobileProjects-mASB6jyj.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-B2WMboOm.js → Mtc-DTMgE7HC.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-8pNEXO63.js → MtcAudit-CwTnCtiL.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-C1wm5a_1.js → Multisig-ngm6WU8f.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-C1QpKF7g.js → NLProgramming-4YO4Qww9.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CiQUcxFu.js → Notes-DP1k45mF.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-BblDPJ_x.js → NotificationSettings-r2H8yPKb.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DRjYcPtb.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dp3VKB0X.js → Organization-DrO7jRWS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Czx161m9.js → Overflow-Cnwu4i4t.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-ClFi6wOs.js → P2P-zi-cDwA_.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CRweW0lz.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-BzcRoqTn.js → Permissions-lqXK2C3x.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-ChGTpNMd.js → PersonalDataHub-b63T3HJM.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BMOOpKWA.js → Pipeline-Kep5KAGW.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-D0AZHObH.js → Privacy-CHdvOAh1.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-yYFlZ6Ja.js → ProjectInit-BhKfdwe9.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DJNVuMQs.js → ProjectSettings-Yavk9K8A.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-Br0rBWH1.js → Projects-CN8E_5Mq.js} +1 -1
  59. package/src/assets/web-panel/assets/Providers-Bfym6SQn.js +1 -0
  60. package/src/assets/web-panel/assets/{QrScannerModal-81NZOitV.js → QrScannerModal-CLSQcrlR.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Dic2lCbJ.js → QuickAsk-VPa2WKQN.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Dh9iwViE.js → Recommend-CGXR8jVU.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-Bq9VYEFy.js → RemoteSession-C_20YfMB.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-CxFZ-NbJ.js → Reputation-awZh68UV.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BVSLcaX2.js → Row-Ctb4oiYN.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BbC-5DOH.js → RssFeed-D3sdjvUV.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-BfjZ1GCr.js → Search-C-zfA4kT.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CVjhOV5r.js → Security-D9XlmHA3.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D45u-Aqe.js → Services-C3UKnr41.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-WkGoK4he.js → Skeleton-BQxagZfF.js} +1 -1
  71. package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +1 -0
  72. package/src/assets/web-panel/assets/{Sla-Bf4QTF3E.js → Sla-DveENvek.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DVpZdbMN.js → SpeechSettings-BFjXLNgF.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DEEojoMS.js → SyncSettings-BpHgbaP9.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-Bzl-9wY5.js → Templates-DEiaoyve.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-0khxUOB8.js → Tenant-ITg774nY.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-DWGYhAYU.js → Terminal-D3ZBcvnb.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-B0Y4LONc.js → TimelineRenderer-BsLU6JjU.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-DEE07SFh.js → Tokens-Cb5Pquc9.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-BM7xvA5z.js → Trigger-Dwfy-m9N.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-Dv3ijvvZ.js → Trust-CnlSRGG0.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B1-enmMt.js → UkeySign-Y4TlvKVo.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DYiIPByd.js → VideoEditing-BpKLtTTO.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D-y374BQ.js → Wallet-LLBzIq8x.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-HBDjcUhi.js → WebAuthn-BeSRiusP.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-sKn4qTID.js → WorkflowEditor-BK_l1tXO.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-TVYhaBXj.js → chat-DYd-X7Wg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DMADxY9v.js → colors-C9uip71j.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-OftcHpO_.js → compact-item-JBrfjUP6.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DW1zVUSN.js → createContext-D03fbuiq.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-CaUathNp.js → hasIn-DLQOR6l-.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Cmdv6Dev.js → index-5YN7F5bO.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-iDmgT2Dg.js → index-6GuLRpBQ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-77kgkUgY.js → index-B0X76LjX.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CM8W07sc.js → index-B46YcgsV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-C9JoaYSs.js → index-BNCdRmWf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CpDtN0dl.js → index-BYv-LWdy.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CZXYI5LU.js → index-Ba-Ps_AJ.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DWVZ3T0t.js → index-BcBZwlnG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--5UvwqH_.js → index-BdH9l8wl.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BBNg08cD.js → index-BfOs_bmg.js} +3 -3
  104. package/src/assets/web-panel/assets/index-Bh8BPtqR.js +1 -0
  105. package/src/assets/web-panel/assets/{index-s-EnkUCn.js → index-BnQgRjsm.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Lr0MO-GU.js → index-BpYKLrUT.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B-19mFhn.js → index-BuNFCZCx.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CeCIYYG7.js → index-ByHO3SqZ.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-hXq7QRA_.js → index-C2zmOsyD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-8YelceUU.js → index-CChQBM1d.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-B52Ztt8Q.js → index-CChaISMb.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DKjqFpYa.js → index-CILVyK3b.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DAfT3EMd.js → index-CL0ixCxk.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-sm1sGIxW.js → index-CS63r8h3.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DHi634g9.js → index-CVZWiUE_.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BEzLLVYd.js → index-Ce0Bf1YF.js} +1 -1
  117. package/src/assets/web-panel/assets/index-CeVrHLzZ.js +1 -0
  118. package/src/assets/web-panel/assets/{index-pSVCBsG4.js → index-Ck17i0OD.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-GUKz4Pv9.js → index-Cpn19c9k.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CKNdnJRo.js → index-CqWk1FLG.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bg-7FpaL.js → index-CwoDpKzW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DYwwUKDg.js → index-D-1SqEpQ.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZdT8nio.js → index-D4UY5Z60.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BvRw4Fna.js → index-D8KvuDz0.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-kdMl_0zL.js → index-DNMAAUVk.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CK-p7Hji.js → index-DYLZY4uh.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DXcqvWBp.js → index-DaokIHt3.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BFVbDpqg.js → index-Q7mmL6w4.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BJ1PjRY3.js → index-SO6lgMg0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BSp1Ofvx.js → index-iahNhZwm.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BupSWDcl.js → index-l08afIbV.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D63QVAiJ.js → index-rIxxfdTN.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-DdcKTlQR.js → initDefaultProps-CG9z1c6w.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CL-hTBQj.js → motion-B1Ky7Zff.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-qYSAkxNG.js → move-DME8X2P1.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-ZwRSZtRP.js → mtc-parser-cuJYSWB7.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-Bld-8ftI.js → omit-UI_EE0uZ.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DGHs-a9Z.js → pickAttrs-BIoE_Z_V.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-DRQPMyI5.js → placementArrow-B48-9HKN.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-C5MJG_Yo.js → responsiveObserve-D7x_GDgD.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-VZ-fpZCD.js → slide-DjzC5_7m.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9ocfCyHk.js → statusUtils-C6BIymhh.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B6QAwAN5.js → styleChecker-B4dGrdD9.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Bq1twor3.js → useFlexGapSupport-Bken4aNE.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DKZq96tz.js → useFs-BMlqECqv.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-D0RmRVXf.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-D6kEj_tt.js → vnode-CKJQmxa0.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-2yfasoQf.js → zoom-CaZunuau.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/auth/npm-auth.js +30 -5
  151. package/src/commands/agenda.js +83 -5
  152. package/src/commands/agent.js +42 -4
  153. package/src/commands/background-session.js +11 -4
  154. package/src/commands/batch.js +79 -27
  155. package/src/commands/changelog.js +15 -4
  156. package/src/commands/config.js +70 -5
  157. package/src/commands/context.js +36 -2
  158. package/src/commands/eval.js +21 -6
  159. package/src/commands/hook.js +2 -2
  160. package/src/commands/hub.js +19 -1
  161. package/src/commands/loop.js +9 -2
  162. package/src/commands/memory.js +21 -6
  163. package/src/commands/permissions.js +57 -1
  164. package/src/commands/plugin.js +2 -0
  165. package/src/commands/review.js +23 -11
  166. package/src/commands/routine.js +31 -8
  167. package/src/commands/session.js +11 -3
  168. package/src/commands/skill.js +2 -0
  169. package/src/commands/statusline.js +3 -3
  170. package/src/commands/team.js +20 -7
  171. package/src/commands/update.js +65 -10
  172. package/src/data/changelog.json +293 -51
  173. package/src/gateways/terminal/PtyManager.js +8 -1
  174. package/src/gateways/ws/message-dispatcher.js +3 -0
  175. package/src/gateways/ws/session-protocol.js +26 -0
  176. package/src/gateways/ws/ws-agent-handler.js +107 -69
  177. package/src/gateways/ws/ws-server.js +74 -2
  178. package/src/harness/background-task-manager.js +18 -6
  179. package/src/harness/background-task-worker.js +11 -18
  180. package/src/harness/jsonl-session-store.js +13 -6
  181. package/src/harness/mcp-client.js +195 -2
  182. package/src/harness/worktree-isolator.js +18 -21
  183. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  184. package/src/lib/agent-coordinator.js +4 -0
  185. package/src/lib/agent-core.js +5 -0
  186. package/src/lib/agent-ipc-bus.js +44 -5
  187. package/src/lib/agent-sandbox.js +16 -2
  188. package/src/lib/agent-schedule-store.js +55 -0
  189. package/src/lib/agent-team/team-worktree.js +28 -6
  190. package/src/lib/agent-tool-admission.js +104 -0
  191. package/src/lib/agent-worktree.js +20 -19
  192. package/src/lib/api-key-helper.js +10 -2
  193. package/src/lib/async-hook-supervisor.cjs +135 -15
  194. package/src/lib/async-hook-supervisor.js +47 -0
  195. package/src/lib/background-agent-supervisor.js +35 -12
  196. package/src/lib/background-task-manager.js +5 -1
  197. package/src/lib/changelog.js +76 -11
  198. package/src/lib/channels/channel-manager.js +6 -0
  199. package/src/lib/channels/telegram-channel.js +11 -1
  200. package/src/lib/channels/webhook-channel.js +15 -3
  201. package/src/lib/checkpoint-store.js +10 -2
  202. package/src/lib/chrome-connector.js +6 -2
  203. package/src/lib/claude-code-bridge.js +24 -8
  204. package/src/lib/cli-anything-bridge.js +82 -25
  205. package/src/lib/cloud/bundle.js +12 -2
  206. package/src/lib/code-review.js +32 -8
  207. package/src/lib/computer-use/control-backend.js +18 -3
  208. package/src/lib/context-breakdown.js +46 -0
  209. package/src/lib/context-source-ledger.js +43 -0
  210. package/src/lib/diff-review-follow-up.js +154 -0
  211. package/src/lib/doctor-checkup.js +18 -6
  212. package/src/lib/downloader.js +17 -3
  213. package/src/lib/ensure-utf8.js +11 -3
  214. package/src/lib/eval/tasks.js +25 -31
  215. package/src/lib/event-runtime-producer.js +58 -0
  216. package/src/lib/event-runtime-store.js +354 -0
  217. package/src/lib/event-runtime-worker.js +109 -0
  218. package/src/lib/execution-backend.js +72 -21
  219. package/src/lib/git-integration.js +19 -4
  220. package/src/lib/hook-manager.js +6 -15
  221. package/src/lib/hook-runner.cjs +88 -8
  222. package/src/lib/hook-runner.js +55 -0
  223. package/src/lib/hooks-v2-producers.js +15 -0
  224. package/src/lib/hooks-v2-runtime.js +70 -21
  225. package/src/lib/host-adb-bridge.js +129 -24
  226. package/src/lib/ide-context.js +128 -1
  227. package/src/lib/interaction-adapter.js +39 -0
  228. package/src/lib/json-schema-validate.js +190 -36
  229. package/src/lib/lan-pairing-preflight.js +13 -2
  230. package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
  231. package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
  232. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  233. package/src/lib/lsp/benchmark.js +20 -4
  234. package/src/lib/lsp/lsp-client.js +22 -3
  235. package/src/lib/lsp/lsp-manager.js +4 -0
  236. package/src/lib/lsp/lsp-server-registry.js +11 -2
  237. package/src/lib/mcp-oauth.js +8 -2
  238. package/src/lib/npm-invocation.js +29 -0
  239. package/src/lib/orchestrator.js +8 -2
  240. package/src/lib/packer/pack-update-applier.js +14 -2
  241. package/src/lib/packer/pkg-runner.js +13 -2
  242. package/src/lib/packer/precheck.js +33 -16
  243. package/src/lib/packer/smoke-runner.js +18 -5
  244. package/src/lib/packer/web-panel-builder.js +16 -2
  245. package/src/lib/paths.js +1 -1
  246. package/src/lib/permission-rules.cjs +5 -1
  247. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  248. package/src/lib/personal-data-hub-wiring.js +27 -3
  249. package/src/lib/plan-mode.js +204 -11
  250. package/src/lib/plugin-monitor-supervisor.js +48 -6
  251. package/src/lib/plugin-runtime/bin.js +45 -0
  252. package/src/lib/plugin-runtime/hooks.js +14 -1
  253. package/src/lib/plugin-runtime/install.js +11 -3
  254. package/src/lib/plugin-runtime/lsp.js +3 -0
  255. package/src/lib/plugin-runtime/mcp.js +8 -1
  256. package/src/lib/plugin-runtime/monitors.js +4 -0
  257. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  258. package/src/lib/plugin-runtime/signature.js +45 -0
  259. package/src/lib/pr-create.js +21 -8
  260. package/src/lib/pr-link-ledger.js +15 -3
  261. package/src/lib/process-execution-broker/credential-agent.js +23 -3
  262. package/src/lib/process-execution-broker/index.js +315 -38
  263. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  264. package/src/lib/process-manager.js +18 -3
  265. package/src/lib/publish-workspace.js +14 -6
  266. package/src/lib/repl-bang-memorize.js +10 -2
  267. package/src/lib/repl-goal.js +19 -2
  268. package/src/lib/secret-store.js +159 -0
  269. package/src/lib/service-manager.js +64 -29
  270. package/src/lib/settings-hook-events.cjs +74 -9
  271. package/src/lib/settings-hook-events.js +20 -0
  272. package/src/lib/settings-hooks.cjs +4 -0
  273. package/src/lib/side-effect-ledger.js +22 -0
  274. package/src/lib/skill-packs/generator.js +18 -9
  275. package/src/lib/skill-process-broker.js +71 -0
  276. package/src/lib/slash-commands.js +10 -2
  277. package/src/lib/status-line.cjs +28 -7
  278. package/src/lib/status-line.js +33 -0
  279. package/src/lib/sub-agent-context.js +5 -0
  280. package/src/lib/sub-agent-profiles.js +4 -0
  281. package/src/lib/turn-context.js +14 -8
  282. package/src/lib/unattended-action-policy.js +83 -0
  283. package/src/lib/update-notice.js +6 -2
  284. package/src/lib/with-file-lock.js +7 -0
  285. package/src/lib/ws-server.js +1 -0
  286. package/src/repl/agent-repl.js +60 -23
  287. package/src/repl/clipboard-copy.js +8 -2
  288. package/src/repl/pr-comments.js +30 -7
  289. package/src/runtime/agent-core.js +554 -118
  290. package/src/runtime/agent-runtime.js +1 -0
  291. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  292. package/src/runtime/coding-agent-policy.cjs +20 -0
  293. package/src/runtime/diagnostics.js +73 -8
  294. package/src/runtime/headless-runner.js +88 -10
  295. package/src/runtime/headless-stream.js +343 -21
  296. package/src/runtime/mcp-config.js +16 -1
  297. package/src/runtime/session-slash-commands.js +506 -0
  298. package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
  299. package/src/skills/video-editing/extractors/video-extractor.js +13 -3
  300. package/src/skills/video-editing/media-process.js +20 -0
  301. package/src/skills/video-editing/render/audio-mix.js +13 -3
  302. package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
  303. package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
  304. package/src/workers/background-agent-worker.js +6 -2
  305. package/src/assets/web-panel/assets/ChatBubbleRenderer-Ci97vNTs.js +0 -1
  306. package/src/assets/web-panel/assets/Providers-zZRhNtSu.js +0 -1
  307. package/src/assets/web-panel/assets/Skills-D_jvQZcj.js +0 -1
  308. package/src/assets/web-panel/assets/Tasks-DFwxpLIk.js +0 -1
  309. package/src/assets/web-panel/assets/devWarning-CLOk6rdE.js +0 -1
  310. package/src/assets/web-panel/assets/index-CSgUfMTM.js +0 -1
  311. package/src/assets/web-panel/assets/index-DliSZvAP.js +0 -1
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import crypto from "crypto";
7
- import { execSync } from "child_process";
8
7
  import broker from "./process-execution-broker/index.js";
9
8
 
10
9
  /**
@@ -275,8 +274,8 @@ export async function executeHook(hook, context = {}) {
275
274
  const type = hook.type || HookType.SYNC;
276
275
 
277
276
  if (type === HookType.COMMAND || type === HookType.SCRIPT) {
278
- // Command/script hooks execute a shell command (execSync hoisted to a
279
- // top-level import — this runs per hook on the tool-use hot path).
277
+ // Command/script hooks execute a shell command through the shared
278
+ // process broker on the tool-use hot path.
280
279
  const cmd = hook.handler || "";
281
280
  if (!cmd) {
282
281
  return {
@@ -291,18 +290,10 @@ export async function executeHook(hook, context = {}) {
291
290
  HOOK_EVENT: hook.event,
292
291
  HOOK_CONTEXT: JSON.stringify(context),
293
292
  };
294
- // M1: Record hook command execution in broker audit log
295
- const auditEntry = {
296
- pid: process.pid,
297
- origin: "hook",
298
- command: cmd,
299
- args: [],
300
- startedAt: Date.now(),
301
- sessionId: context?.sessionId,
302
- };
303
- if (typeof broker._recordAudit === "function")
304
- broker._recordAudit(auditEntry);
305
- const output = execSync(cmd, {
293
+ const output = broker.execSync(cmd, {
294
+ origin: "hook-manager:command",
295
+ scope: "hook",
296
+ policy: "allow",
306
297
  encoding: "utf-8",
307
298
  timeout: hook.timeout || 20000,
308
299
  // execSync defaults maxBuffer to 1 MB; a hook that prints more (a
@@ -17,10 +17,9 @@
17
17
  * agent — only an explicit block decision blocks).
18
18
  *
19
19
  * Returns a normalized `{ decision, reason, exitCode, stdout, stderr, ... }`.
20
- * `_deps.spawnSync` is injected for unit tests (no real process needed).
20
+ * Process runners are injected for unit tests and by the ESM Broker facade.
21
21
  */
22
22
 
23
- const cpDefault = require("node:child_process");
24
23
  const {
25
24
  normalizeShellKind,
26
25
  buildPowershellArgv,
@@ -28,7 +27,27 @@ const {
28
27
  } = require("./shell-selector.cjs");
29
28
  const { mergeHookDecisions } = require("./hook-event-bus.cjs");
30
29
 
31
- const _deps = { spawnSync: cpDefault.spawnSync, spawn: cpDefault.spawn };
30
+ const _deps = { runSync: null, run: null };
31
+ Object.defineProperties(_deps, {
32
+ spawnSync: {
33
+ enumerable: true,
34
+ get() {
35
+ return this.runSync;
36
+ },
37
+ set(value) {
38
+ this.runSync = value;
39
+ },
40
+ },
41
+ spawn: {
42
+ enumerable: true,
43
+ get() {
44
+ return this.run;
45
+ },
46
+ set(value) {
47
+ this.run = value;
48
+ },
49
+ },
50
+ });
32
51
 
33
52
  const HOOK_DECISIONS = Object.freeze({
34
53
  BLOCK: "block",
@@ -232,7 +251,30 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
232
251
  CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
233
252
  },
234
253
  };
235
- if (usePowershell) {
254
+ const processOptions = {
255
+ ...common,
256
+ origin: opts.origin || "hook",
257
+ policy: "allow",
258
+ scope: "hook",
259
+ pluginId: opts.pluginId || null,
260
+ pluginVersion: opts.pluginVersion || null,
261
+ pluginSource: opts.pluginSource || null,
262
+ };
263
+ const brokerRunSync =
264
+ opts.broker &&
265
+ opts.origin === "plugin:hook" &&
266
+ typeof opts.broker["spawnSync"] === "function"
267
+ ? opts.broker["spawnSync"].bind(opts.broker)
268
+ : null;
269
+ if (brokerRunSync) {
270
+ const invocation = usePowershell
271
+ ? buildPowershellArgv(command, shellKind, {
272
+ executionPolicy: loadShellConfig({ cwd: common.cwd })
273
+ .executionPolicy,
274
+ })
275
+ : { file: command, argv: [] };
276
+ res = brokerRunSync(invocation.file, invocation.argv, processOptions);
277
+ } else if (usePowershell) {
236
278
  // Per-hook `shell: powershell|pwsh` (settings hook entry): explicit argv
237
279
  // instead of the default shell; the enterprise ExecutionPolicy override
238
280
  // (settings shell.powershell.executionPolicy) applies here too.
@@ -240,9 +282,15 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
240
282
  cwd: common.cwd,
241
283
  });
242
284
  const inv = buildPowershellArgv(command, shellKind, { executionPolicy });
243
- res = _deps.spawnSync(inv.file, inv.argv, common);
285
+ if (typeof _deps.runSync !== "function") {
286
+ throw new Error("hook process runner unavailable");
287
+ }
288
+ res = _deps.runSync(inv.file, inv.argv, processOptions);
244
289
  } else {
245
- res = _deps.spawnSync(command, { ...common, shell: true });
290
+ if (typeof _deps.runSync !== "function") {
291
+ throw new Error("hook process runner unavailable");
292
+ }
293
+ res = _deps.runSync(command, { ...processOptions, shell: true });
246
294
  }
247
295
  } catch (err) {
248
296
  return {
@@ -346,15 +394,37 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
346
394
  ...process.env,
347
395
  CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
348
396
  };
397
+ const processOptions = {
398
+ cwd: wd,
399
+ env: spawnEnv,
400
+ origin: opts.origin || "hook",
401
+ policy: "allow",
402
+ scope: "hook",
403
+ pluginId: opts.pluginId || null,
404
+ pluginVersion: opts.pluginVersion || null,
405
+ pluginSource: opts.pluginSource || null,
406
+ };
349
407
 
350
408
  let child;
351
409
  try {
410
+ const brokerRun =
411
+ opts.broker &&
412
+ opts.origin === "plugin:hook" &&
413
+ typeof opts.broker["spawn"] === "function"
414
+ ? opts.broker["spawn"].bind(opts.broker)
415
+ : null;
416
+ const run = brokerRun || _deps.run;
417
+ if (typeof run !== "function") {
418
+ throw new Error("hook process runner unavailable");
419
+ }
352
420
  if (usePowershell) {
353
421
  const { executionPolicy } = loadShellConfig({ cwd: wd });
354
422
  const inv = buildPowershellArgv(command, shellKind, { executionPolicy });
355
- child = _deps.spawn(inv.file, inv.argv, { cwd: wd, env: spawnEnv });
423
+ child = run(inv.file, inv.argv, processOptions);
356
424
  } else {
357
- child = _deps.spawn(command, { cwd: wd, env: spawnEnv, shell: true });
425
+ child = brokerRun
426
+ ? run(command, [], { ...processOptions, shell: true })
427
+ : run(command, { ...processOptions, shell: true });
358
428
  }
359
429
  } catch (err) {
360
430
  return {
@@ -444,6 +514,11 @@ function runHooks(commandHooks, input = {}, opts = {}) {
444
514
  const runOne = (h) =>
445
515
  runCommandHook(h.command, input, {
446
516
  ...opts,
517
+ broker: opts.broker,
518
+ origin: h.origin,
519
+ pluginId: h.pluginId,
520
+ pluginVersion: h.pluginVersion,
521
+ pluginSource: h.pluginSource,
447
522
  timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
448
523
  // per-hook shell selection (P1 #8): `{ "type":"command", "command":…,
449
524
  // "shell":"powershell" }` in the settings hook entry
@@ -504,6 +579,11 @@ async function runHooksParallel(commandHooks, input = {}, opts = {}) {
504
579
  hooks.map((h) =>
505
580
  runCommandHookAsync(h.command, input, {
506
581
  ...opts,
582
+ broker: opts.broker,
583
+ origin: h.origin,
584
+ pluginId: h.pluginId,
585
+ pluginVersion: h.pluginVersion,
586
+ pluginSource: h.pluginSource,
507
587
  timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
508
588
  shell: h.shell != null ? h.shell : opts.shell,
509
589
  }).then((r) => ({ h, r })),
@@ -0,0 +1,55 @@
1
+ import runner from "./hook-runner.cjs";
2
+ import executionBroker from "./process-execution-broker/index.js";
3
+
4
+ const brokerRunner = executionBroker.spawn.bind(executionBroker);
5
+ const brokerSyncRunner = executionBroker.spawnSync.bind(executionBroker);
6
+
7
+ export const _processDeps = {
8
+ run: brokerRunner,
9
+ runSync: brokerSyncRunner,
10
+ };
11
+
12
+ function normalizeInvocation(argsOrOptions, maybeOptions) {
13
+ return Array.isArray(argsOrOptions)
14
+ ? { args: argsOrOptions, options: maybeOptions || {} }
15
+ : { args: [], options: argsOrOptions || {} };
16
+ }
17
+
18
+ function runHookProcess(file, argsOrOptions, maybeOptions) {
19
+ const { args, options } = normalizeInvocation(argsOrOptions, maybeOptions);
20
+ return _processDeps.run(file, args, {
21
+ ...options,
22
+ origin: options.origin || "hook",
23
+ policy: "allow",
24
+ scope: "hook",
25
+ });
26
+ }
27
+
28
+ function runHookProcessSync(file, argsOrOptions, maybeOptions) {
29
+ const { args, options } = normalizeInvocation(argsOrOptions, maybeOptions);
30
+ return _processDeps.runSync(file, args, {
31
+ ...options,
32
+ origin: options.origin || "hook",
33
+ policy: "allow",
34
+ scope: "hook",
35
+ });
36
+ }
37
+
38
+ runner._deps.run = runHookProcess;
39
+ runner._deps.runSync = runHookProcessSync;
40
+
41
+ export const {
42
+ runCommandHook,
43
+ runCommandHookAsync,
44
+ runHooks,
45
+ runHooksParallel,
46
+ interpretHookOutcome,
47
+ tryParseDecision,
48
+ HOOK_DECISIONS,
49
+ HOOK_PAYLOAD_SCHEMA_VERSION,
50
+ hookBreakerConfig,
51
+ _resetHookBreaker,
52
+ _deps,
53
+ } = runner;
54
+
55
+ export default runner;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Best-effort bridge from real runtime producers to Hooks v2.
3
+ * Hook failures never change the surrounding agent or interaction result.
4
+ */
5
+ export function emitHooksV2Event(eventName, context = {}, options = {}) {
6
+ void import("./hooks-v2-runtime.js")
7
+ .then(({ default: runtime }) => {
8
+ if (!runtime || typeof runtime.executeHooks !== "function") return;
9
+ return runtime.executeHooks(eventName, context, options);
10
+ })
11
+ .catch(() => {
12
+ // Hooks are an optional extension; producer delivery is best-effort.
13
+ });
14
+ }
15
+
@@ -24,10 +24,12 @@ import crypto from "node:crypto";
24
24
  import fs from "node:fs/promises";
25
25
  import path from "node:path";
26
26
  import broker from "./process-execution-broker/index.js";
27
+ import { EventRuntimeStore } from "./event-runtime-store.js";
27
28
 
28
29
  const VALID_HOOK_EVENTS = new Set([
29
30
  // Session
30
- "PreToolUse", "PostToolUse", "Notification", "Stop", "SubagentStop",
31
+ "PreToolUse", "PostToolUse", "Notification", "Stop", "SubagentStart", "SubagentStop",
32
+ "SessionResume", "SessionPause", "PostCompact",
31
33
  // Auth
32
34
  "PreCommit", "PostCommit",
33
35
  // Skill
@@ -35,24 +37,33 @@ const VALID_HOOK_EVENTS = new Set([
35
37
  // Model
36
38
  "PreCompact", "ModelSelection",
37
39
  // Config
38
- "ConfigChange", "PermissionAllow", "PermissionDeny",
40
+ "ConfigChange", "PermissionAllow", "PermissionDeny", "PermissionRequest", "PermissionDenied",
39
41
  // Timeline
40
42
  "TimelineEntry",
41
43
  // MCP
42
- "McpRequest", "McpResponse",
44
+ "McpRequest", "McpResponse", "MCPElicitation",
45
+ // Task / workspace lifecycle
46
+ "TaskCreated", "TaskCompleted", "InstructionsLoaded", "CwdChanged",
47
+ "WorktreeCreate", "WorktreeRemove",
43
48
  ]);
44
49
 
45
50
  const VALID_EXECUTOR_TYPES = new Set(["command", "http", "prompt", "agent", "js"]);
46
51
 
47
52
  class HooksV2Runtime extends EventEmitter {
48
- constructor(configDir) {
53
+ constructor(configDir, options = {}) {
49
54
  super();
50
55
  this.configDir = configDir;
56
+ this.durableStore = options.durableStore || null;
51
57
  this.hooks = new Map(); // eventName -> HookDefinition[]
52
58
  this.executionLog = [];
53
59
  this._loaded = false;
54
60
  }
55
61
 
62
+ setDurableStore(store) {
63
+ this.durableStore = store || null;
64
+ return this.durableStore;
65
+ }
66
+
56
67
  /**
57
68
  * @typedef {Object} HookDefinition
58
69
  * @property {string} id
@@ -132,17 +143,29 @@ class HooksV2Runtime extends EventEmitter {
132
143
  * @param {Object} context - event context (tool_name, tool_input, session_id, etc.)
133
144
  * @returns {Promise<{results: Array, blocked: boolean, blockingResult?: any}>}
134
145
  */
135
- async emitEvent(eventName, context = {}) {
146
+ async emitEvent(eventName, context = {}, options = {}) {
136
147
  if (!VALID_HOOK_EVENTS.has(eventName)) {
137
148
  throw new Error(`Invalid hook event: ${eventName}`);
138
149
  }
139
150
 
140
151
  const hooks = this.hooks.get(eventName) || [];
141
- const results = [];
142
- let blocked = false;
143
- let blockingResult = null;
144
-
152
+ const durableRecord = this.durableStore
153
+ ? this.durableStore.enqueueInbox(
154
+ { event: eventName, context },
155
+ { id: context.event_id || context.eventId || null },
156
+ )
157
+ : null;
158
+ // Hooks are parallel by default. De-duplicate by id so a reload or layered
159
+ // config cannot execute the same handler twice. `parallel:false` remains a
160
+ // deterministic compatibility mode for callers that require ordering.
161
+ const uniqueHooks = [];
162
+ const seen = new Set();
145
163
  for (const hook of hooks) {
164
+ if (seen.has(hook.id)) continue;
165
+ seen.add(hook.id);
166
+ uniqueHooks.push(hook);
167
+ }
168
+ const runOne = async (hook) => {
146
169
  const execId = crypto.randomUUID();
147
170
  const start = Date.now();
148
171
  const record = { execId, hookId: hook.id, event: eventName, type: hook.type, startedAt: new Date() };
@@ -170,28 +193,47 @@ class HooksV2Runtime extends EventEmitter {
170
193
  record.durationMs = Date.now() - start;
171
194
  record.status = "success";
172
195
  record.result = result;
173
- results.push(record);
174
-
175
- // Check for blocking decision
176
- if (hook.blocking && result && result.decision === "block") {
177
- blocked = true;
178
- blockingResult = result;
179
- record.blocked = true;
180
- }
196
+ if (hook.blocking && result && result.decision === "block") record.blocked = true;
181
197
 
182
198
  this.emit("hook:success", record);
183
199
  } catch (err) {
184
200
  record.durationMs = Date.now() - start;
185
201
  record.status = "error";
186
202
  record.error = err.message;
187
- results.push(record);
188
203
  this.emit("hook:error", record);
189
204
  }
190
205
 
191
206
  this.executionLog.push(record);
207
+ return record;
208
+ };
209
+ const results = options.parallel === false
210
+ ? []
211
+ : await Promise.all(uniqueHooks.map(runOne));
212
+ if (options.parallel === false) {
213
+ for (const hook of uniqueHooks) results.push(await runOne(hook));
192
214
  }
215
+ const blocking = results.filter((r) => r.blocked === true);
216
+ const blocked = blocking.length > 0;
217
+ 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
+ });
226
+ this.durableStore.enqueueOutbox(
227
+ { event: eventName, outcome },
228
+ { id: `${durableRecord.id}:result` },
229
+ );
230
+ }
231
+ return outcome;
232
+ }
193
233
 
194
- return { results, blocked, blockingResult };
234
+ /** Compatibility/public name used by runtime parity and SDK callers. */
235
+ async executeHooks(eventName, context = {}, options = {}) {
236
+ return this.emitEvent(eventName, context, options);
195
237
  }
196
238
 
197
239
  async _execCommand(hook, context) {
@@ -253,7 +295,9 @@ class HooksV2Runtime extends EventEmitter {
253
295
  }
254
296
 
255
297
  async _execJs(hook, context) {
256
- // In-process JS execution via vm (sandboxed) - stub for full vm2/isolated-vm integration
298
+ 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.
257
301
  return { decision: "noop", note: "JS executor stub - context received", contextKeys: Object.keys(context) };
258
302
  }
259
303
 
@@ -273,6 +317,11 @@ class HooksV2Runtime extends EventEmitter {
273
317
  }
274
318
 
275
319
  // Singleton instance
276
- const hooksRuntime = new HooksV2Runtime();
320
+ const hooksRuntime = new HooksV2Runtime(undefined, {
321
+ durableStore:
322
+ process.env.CC_EVENT_RUNTIME_DURABLE === "1"
323
+ ? new EventRuntimeStore()
324
+ : null,
325
+ });
277
326
  export default hooksRuntime;
278
327
  export { HooksV2Runtime, VALID_HOOK_EVENTS, VALID_EXECUTOR_TYPES };
@@ -17,8 +17,8 @@
17
17
  * still used for the V5/V6 IPC code path (separate hub singleton).
18
18
  *
19
19
  * Methods implemented (only what system-data-android consumes):
20
- * - `contacts.query({since?})` →
21
- * [{ lookupKey, displayName }, ...]
20
+ * - `contacts.query({since?})` → contacts with phones, emails,
21
+ * organization, job title, starred state, and photo URI
22
22
  * - `app.list({includeSystem?})` →
23
23
  * [{ packageName }, ...]
24
24
  *
@@ -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]);
@@ -120,27 +134,116 @@ function parseContentQueryRows(stdout) {
120
134
  return rows;
121
135
  }
122
136
 
123
- async function queryContacts(_params, opts) {
137
+ function mergeContactRows(contactRows, phoneRows, emailRows, organizationRows) {
138
+ const byId = new Map();
139
+ for (const row of contactRows) {
140
+ const contactId = row._id == null ? null : String(row._id);
141
+ const displayName = row.display_name || null;
142
+ if (!contactId || !displayName) continue;
143
+ byId.set(contactId, {
144
+ lookupKey: row.lookup || null,
145
+ displayName,
146
+ phones: [],
147
+ emails: [],
148
+ starred: row.starred === "1",
149
+ organization: null,
150
+ jobTitle: null,
151
+ photoUri: row.photo_uri || null,
152
+ });
153
+ }
154
+
155
+ const appendUnique = (rows, field, valueOf) => {
156
+ for (const row of rows) {
157
+ const contact = byId.get(String(row.contact_id));
158
+ const value = valueOf(row);
159
+ if (!contact || typeof value !== "string" || !value.trim()) continue;
160
+ const normalized = value.trim();
161
+ if (!contact[field].includes(normalized)) contact[field].push(normalized);
162
+ }
163
+ };
164
+ appendUnique(phoneRows, "phones", (row) => row.data1 || row.number);
165
+ appendUnique(emailRows, "emails", (row) => row.data1 || row.address);
166
+ for (const row of organizationRows) {
167
+ const contact = byId.get(String(row.contact_id));
168
+ if (!contact) continue;
169
+ const organization = row.data1 || row.company;
170
+ const jobTitle = row.data4 || row.title;
171
+ if (!contact.organization && organization && organization.trim()) {
172
+ contact.organization = organization.trim();
173
+ }
174
+ if (!contact.jobTitle && jobTitle && jobTitle.trim()) {
175
+ contact.jobTitle = jobTitle.trim();
176
+ }
177
+ }
178
+ return [...byId.values()];
179
+ }
180
+
181
+ async function queryContacts(params, opts) {
124
182
  const serial = await pickDevice(opts);
125
- const stdout = await adb(
126
- [
127
- "shell",
128
- "content",
129
- "query",
130
- "--uri",
131
- "content://com.android.contacts/contacts",
132
- "--projection",
133
- "lookup:display_name",
134
- ],
135
- { ...opts, serial },
183
+ const contactArgs = [
184
+ "shell",
185
+ "content",
186
+ "query",
187
+ "--uri",
188
+ "content://com.android.contacts/contacts",
189
+ "--projection",
190
+ "_id:lookup:display_name:starred:photo_uri",
191
+ ];
192
+ if (Number.isInteger(params?.since) && params.since > 0) {
193
+ contactArgs.push(
194
+ "--where",
195
+ `contact_last_updated_timestamp >= ${params.since}`,
196
+ );
197
+ }
198
+ const commandOpts = { ...opts, serial, timeoutMs: opts.timeoutMs || 120_000 };
199
+ const [contactsOut, phonesOut, emailsOut, organizationsOut] =
200
+ await Promise.all([
201
+ adb(contactArgs, commandOpts),
202
+ adb(
203
+ [
204
+ "shell",
205
+ "content",
206
+ "query",
207
+ "--uri",
208
+ "content://com.android.contacts/data/phones",
209
+ "--projection",
210
+ "contact_id:data1",
211
+ ],
212
+ commandOpts,
213
+ ),
214
+ adb(
215
+ [
216
+ "shell",
217
+ "content",
218
+ "query",
219
+ "--uri",
220
+ "content://com.android.contacts/data/emails",
221
+ "--projection",
222
+ "contact_id:data1",
223
+ ],
224
+ commandOpts,
225
+ ),
226
+ adb(
227
+ [
228
+ "shell",
229
+ "content",
230
+ "query",
231
+ "--uri",
232
+ "content://com.android.contacts/data",
233
+ "--projection",
234
+ "contact_id:data1:data4",
235
+ "--where",
236
+ "mimetype='vnd.android.cursor.item/organization'",
237
+ ],
238
+ commandOpts,
239
+ ),
240
+ ]);
241
+ return mergeContactRows(
242
+ parseContentQueryRows(contactsOut),
243
+ parseContentQueryRows(phonesOut),
244
+ parseContentQueryRows(emailsOut),
245
+ parseContentQueryRows(organizationsOut),
136
246
  );
137
- const rows = parseContentQueryRows(stdout);
138
- return rows
139
- .map((r) => ({
140
- lookupKey: r.lookup || null,
141
- displayName: r.display_name || null,
142
- }))
143
- .filter((c) => c.displayName);
144
247
  }
145
248
 
146
249
  async function listApps(params, opts) {
@@ -496,7 +599,9 @@ export function createHostAdbBridge(opts = {}) {
496
599
 
497
600
  // Exposed for unit testing without spawning real adb.
498
601
  export const _internals = {
602
+ adb,
499
603
  parseContentQueryRows,
604
+ mergeContactRows,
500
605
  listDevices,
501
606
  pickDevice,
502
607
  queryContacts,