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
@@ -1,21 +1,41 @@
1
1
  /**
2
- * Credential Agent P0-1 requirement
2
+ * In-process credential reference agent.
3
3
  *
4
- * Secrets are never passed raw to child processes. Instead:
5
- * - Environment variables are filtered (API keys, tokens stripped)
6
- * - Credentials are injected via a proxy/agent at runtime
7
- * - Audit log records credential usage without revealing values
8
- * - Child processes request credentials through IPC channel
4
+ * The process execution broker calls this module after a command is approved.
5
+ * Long-lived credential values stay in the broker process while the child only
6
+ * receives short-lived opaque references. Resolving a reference requires an
7
+ * attested agent id and the exact process/host target recorded at issuance.
9
8
  *
10
- * References: Claude Code CredentialAgent pattern
9
+ * This module deliberately does not claim to provide an OS IPC transport. A
10
+ * future broker-owned IPC implementation can call resolveCredentialRef() after
11
+ * it has authenticated the requesting process and destination host.
11
12
  */
12
13
 
13
- import os from "node:os";
14
- import path from "node:path";
15
- import fs from "node:fs";
16
14
  import crypto from "node:crypto";
17
15
 
18
- // Patterns for environment variables that contain secrets
16
+ const REDACTED_VALUE = "***REDACTED***";
17
+ const DEFAULT_TTL_MS = 60_000;
18
+ const MAX_TTL_MS = 5 * 60_000;
19
+ const DEFAULT_MAX_USES = 1;
20
+ const MAX_USES_LIMIT = 16;
21
+ const DEFAULT_MAX_CREDENTIALS = 4096;
22
+ const DEFAULT_MAX_AUDIT_ENTRIES = 10_000;
23
+
24
+ const CREDENTIAL_ERROR_CODES = Object.freeze({
25
+ DISABLED: "CC_CREDENTIAL_AGENT_DISABLED",
26
+ APPROVAL_REQUIRED: "CC_CREDENTIAL_APPROVAL_REQUIRED",
27
+ TARGET_REQUIRED: "CC_CREDENTIAL_TARGET_REQUIRED",
28
+ INVALID_REQUEST: "CC_CREDENTIAL_INVALID_REQUEST",
29
+ STORE_FULL: "CC_CREDENTIAL_STORE_FULL",
30
+ NOT_FOUND: "CC_CREDENTIAL_REF_NOT_FOUND",
31
+ EXPIRED: "CC_CREDENTIAL_REF_EXPIRED",
32
+ EXHAUSTED: "CC_CREDENTIAL_REF_EXHAUSTED",
33
+ REVOKED: "CC_CREDENTIAL_REF_REVOKED",
34
+ AGENT_MISMATCH: "CC_CREDENTIAL_AGENT_MISMATCH",
35
+ TARGET_MISMATCH: "CC_CREDENTIAL_TARGET_MISMATCH",
36
+ });
37
+
38
+ // Patterns for environment variables that contain secrets.
19
39
  const SENSITIVE_ENV_PATTERNS = [
20
40
  /API[_-]?KEY/i,
21
41
  /SECRET/i,
@@ -47,7 +67,7 @@ const SENSITIVE_ENV_PATTERNS = [
47
67
  /GITLAB_TOKEN/i,
48
68
  ];
49
69
 
50
- // Keys that are always safe to pass through (non-sensitive)
70
+ // Keys that are always safe to pass through (non-sensitive).
51
71
  const ENV_ALLOWLIST = new Set([
52
72
  "PATH",
53
73
  "HOME",
@@ -65,9 +85,7 @@ const ENV_ALLOWLIST = new Set([
65
85
  "VISUAL",
66
86
  "NODE_ENV",
67
87
  "NODE_OPTIONS",
68
- // Correlation identifiers required by ChainlessChain/Claude-compatible
69
- // child processes. These identify a run; they are not authentication
70
- // credentials. Other SESSION-named variables remain filtered.
88
+ // These identify a run; they are not authentication credentials.
71
89
  "CC_SESSION_ID",
72
90
  "CLAUDE_CODE_SESSION_ID",
73
91
  "npm_config_registry",
@@ -104,147 +122,956 @@ const ENV_ALLOWLIST = new Set([
104
122
  "CHAINLESSCHAIN_CONFIG_DIR",
105
123
  ]);
106
124
 
125
+ const RESERVED_AGENT_ENV_KEYS = new Set([
126
+ "CC_CREDENTIAL_AGENT_ID",
127
+ "CC_CREDENTIAL_PROXY",
128
+ "CC_CREDENTIAL_TRANSPORT",
129
+ ]);
130
+
131
+ function credentialError(code, message) {
132
+ const error = new Error(message);
133
+ error.code = code;
134
+ return error;
135
+ }
136
+
137
+ function isTruthyEnvironmentFlag(value) {
138
+ return ["1", "true", "yes", "on"].includes(
139
+ String(value ?? "")
140
+ .trim()
141
+ .toLowerCase(),
142
+ );
143
+ }
144
+
145
+ function positiveInteger(value, fallback, maximum) {
146
+ const parsed = Number(value);
147
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
148
+ const integer = Math.floor(parsed);
149
+ if (integer <= 0) return fallback;
150
+ return Math.min(integer, maximum);
151
+ }
152
+
153
+ function requestedPositiveInteger(value, fallback, maximum, fieldName) {
154
+ if (value === undefined || value === null) {
155
+ return Math.min(fallback, maximum);
156
+ }
157
+ const parsed = Number(value);
158
+ if (!Number.isFinite(parsed) || parsed <= 0) {
159
+ throw credentialError(
160
+ CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
161
+ `${fieldName} must be a positive integer`,
162
+ );
163
+ }
164
+ const integer = Math.floor(parsed);
165
+ if (integer <= 0) {
166
+ throw credentialError(
167
+ CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
168
+ `${fieldName} must be a positive integer`,
169
+ );
170
+ }
171
+ return Math.min(integer, maximum);
172
+ }
173
+
174
+ function normalizeProcessTarget(value) {
175
+ const normalized = String(value ?? "").trim();
176
+ if (!normalized) return null;
177
+ return process.platform === "win32"
178
+ ? normalized.replaceAll("/", "\\").toLowerCase()
179
+ : normalized;
180
+ }
181
+
182
+ function normalizeHostTarget(value) {
183
+ const candidate = String(value ?? "").trim();
184
+ if (!candidate) return null;
185
+
186
+ try {
187
+ const parsed = candidate.includes("://")
188
+ ? new URL(candidate)
189
+ : new URL(`https://${candidate}`);
190
+ return parsed.hostname.toLowerCase().replace(/\.$/, "");
191
+ } catch {
192
+ return candidate.toLowerCase().replace(/\.$/, "");
193
+ }
194
+ }
195
+
196
+ function cloneAuditEntry(entry) {
197
+ return {
198
+ ...entry,
199
+ target: entry.target ? { ...entry.target } : undefined,
200
+ envRedacted: entry.envRedacted ? [...entry.envRedacted] : undefined,
201
+ argRedacted: entry.argRedacted ? [...entry.argRedacted] : undefined,
202
+ reservedEnvRemoved: entry.reservedEnvRemoved
203
+ ? [...entry.reservedEnvRemoved]
204
+ : undefined,
205
+ };
206
+ }
207
+
107
208
  class CredentialAgent {
108
- constructor() {
209
+ constructor(options = {}) {
109
210
  this._credentialStore = new Map();
110
211
  this._accessLog = [];
111
- this._agentId = crypto.randomUUID();
112
- this._credentialProxyPath = null;
113
- this._setupProxy();
212
+ this._environment = options.env ?? process.env;
213
+ this._now = typeof options.now === "function" ? options.now : Date.now;
214
+ this._randomBytes =
215
+ typeof options.randomBytes === "function"
216
+ ? options.randomBytes
217
+ : crypto.randomBytes;
218
+ this._agentId = options.agentId || crypto.randomUUID();
219
+ this._enabledOverride =
220
+ typeof options.enabled === "boolean" ? options.enabled : undefined;
221
+ this._maxTtlMs = positiveInteger(
222
+ options.maxTtlMs,
223
+ MAX_TTL_MS,
224
+ Number.MAX_SAFE_INTEGER,
225
+ );
226
+ this._defaultTtlMs = Math.min(
227
+ positiveInteger(options.defaultTtlMs, DEFAULT_TTL_MS, this._maxTtlMs),
228
+ this._maxTtlMs,
229
+ );
230
+ this._maxUsesLimit = positiveInteger(
231
+ options.maxUsesLimit,
232
+ MAX_USES_LIMIT,
233
+ Number.MAX_SAFE_INTEGER,
234
+ );
235
+ this._defaultMaxUses = Math.min(
236
+ positiveInteger(
237
+ options.defaultMaxUses,
238
+ DEFAULT_MAX_USES,
239
+ this._maxUsesLimit,
240
+ ),
241
+ this._maxUsesLimit,
242
+ );
243
+ this._maxCredentials = positiveInteger(
244
+ options.maxCredentials,
245
+ DEFAULT_MAX_CREDENTIALS,
246
+ Number.MAX_SAFE_INTEGER,
247
+ );
248
+ this._maxAuditEntries = positiveInteger(
249
+ options.maxAuditEntries,
250
+ DEFAULT_MAX_AUDIT_ENTRIES,
251
+ Number.MAX_SAFE_INTEGER,
252
+ );
253
+
254
+ // "broker-api" is an in-process primitive, not a socket/pipe endpoint.
255
+ this._transport = "broker-api";
256
+ this._proxyReady = false;
257
+ this._lastEnabledState = this._computeEnabled();
258
+ }
259
+
260
+ _computeEnabled() {
261
+ if (this._enabledOverride !== undefined) {
262
+ return this._enabledOverride;
263
+ }
264
+ return !isTruthyEnvironmentFlag(this._environment?.CC_CRED_AGENT_DISABLE);
114
265
  }
115
266
 
116
- _setupProxy() {
117
- // In a full implementation, we'd set up an IPC/UDS proxy
118
- // For now, we track and filter
119
- this._proxyReady = true;
267
+ _syncEnabledState() {
268
+ const enabled = this._computeEnabled();
269
+ if (enabled === this._lastEnabledState) return enabled;
270
+
271
+ if (!enabled) {
272
+ let revoked = 0;
273
+ for (const record of this._credentialStore.values()) {
274
+ if (record.status === "active") {
275
+ record.value = null;
276
+ record.status = "disabled";
277
+ revoked += 1;
278
+ }
279
+ }
280
+ this._recordAudit({
281
+ event: "credential_agent_disabled",
282
+ outcome: "allowed",
283
+ credentialsRevoked: revoked,
284
+ });
285
+ } else {
286
+ this._recordAudit({
287
+ event: "credential_agent_enabled",
288
+ outcome: "allowed",
289
+ });
290
+ }
291
+
292
+ this._lastEnabledState = enabled;
293
+ return enabled;
294
+ }
295
+
296
+ /**
297
+ * CC_CRED_AGENT_DISABLE accepts 1/true/yes/on (case-insensitive).
298
+ *
299
+ * Disabled means references cannot be issued or resolved. Environment and
300
+ * argv filtering remains mandatory so disabling the agent can never restore
301
+ * plaintext credential inheritance.
302
+ */
303
+ isEnabled() {
304
+ return this._syncEnabledState();
305
+ }
306
+
307
+ /**
308
+ * Build the explicit context the ProcessExecutionBroker should pass to
309
+ * applyWithReport(). The caller remains responsible for attesting that the
310
+ * approval and target describe the process it will actually launch.
311
+ */
312
+ createBrokerContext({
313
+ approvalId,
314
+ process: processTarget,
315
+ host = null,
316
+ ttlMs,
317
+ maxUses,
318
+ } = {}) {
319
+ const normalizedProcess = normalizeProcessTarget(processTarget);
320
+ if (!approvalId) {
321
+ throw credentialError(
322
+ CREDENTIAL_ERROR_CODES.APPROVAL_REQUIRED,
323
+ "Credential approval id is required",
324
+ );
325
+ }
326
+ if (!normalizedProcess) {
327
+ throw credentialError(
328
+ CREDENTIAL_ERROR_CODES.TARGET_REQUIRED,
329
+ "Credential process target is required",
330
+ );
331
+ }
332
+
333
+ return Object.freeze({
334
+ approved: true,
335
+ approvalId: String(approvalId),
336
+ target: Object.freeze({
337
+ process: normalizedProcess,
338
+ host: normalizeHostTarget(host),
339
+ }),
340
+ ttlMs,
341
+ maxUses,
342
+ });
120
343
  }
121
344
 
122
345
  /**
123
- * Check if an env var key looks sensitive
346
+ * Check if an env var key looks sensitive.
124
347
  */
125
348
  isSensitiveKey(key) {
126
349
  if (ENV_ALLOWLIST.has(key)) return false;
127
350
  return SENSITIVE_ENV_PATTERNS.some((pattern) => pattern.test(key));
128
351
  }
129
352
 
353
+ isReservedAgentKey(key) {
354
+ return (
355
+ RESERVED_AGENT_ENV_KEYS.has(key) ||
356
+ key.startsWith("CC_CRED_REF_") ||
357
+ key.startsWith("CC_CRED_ARG_REF_")
358
+ );
359
+ }
360
+
361
+ _recordAudit(entry) {
362
+ this._accessLog.push({
363
+ timestamp: this._now(),
364
+ ...entry,
365
+ });
366
+ while (this._accessLog.length > this._maxAuditEntries) {
367
+ this._accessLog.shift();
368
+ }
369
+ }
370
+
371
+ _fingerprint(value) {
372
+ if (!value) return null;
373
+ return crypto
374
+ .createHash("sha256")
375
+ .update(String(value))
376
+ .digest("hex")
377
+ .slice(0, 16);
378
+ }
379
+
380
+ _expireStaleCredentials() {
381
+ const now = this._now();
382
+ for (const record of this._credentialStore.values()) {
383
+ if (record.status === "active" && now >= record.expiresAt) {
384
+ record.value = null;
385
+ record.status = "expired";
386
+ }
387
+ }
388
+ }
389
+
390
+ _ensureStoreCapacity() {
391
+ this._expireStaleCredentials();
392
+ if (this._credentialStore.size < this._maxCredentials) return;
393
+
394
+ for (const [refId, record] of this._credentialStore) {
395
+ if (record.status !== "active") {
396
+ this._credentialStore.delete(refId);
397
+ if (this._credentialStore.size < this._maxCredentials) return;
398
+ }
399
+ }
400
+
401
+ throw credentialError(
402
+ CREDENTIAL_ERROR_CODES.STORE_FULL,
403
+ "Credential reference store is full",
404
+ );
405
+ }
406
+
407
+ _createReferenceId() {
408
+ for (let attempt = 0; attempt < 32; attempt += 1) {
409
+ const refId = `cc-cred-${this._randomBytes(16).toString("hex")}`;
410
+ if (!this._credentialStore.has(refId)) return refId;
411
+ }
412
+ throw credentialError(
413
+ CREDENTIAL_ERROR_CODES.STORE_FULL,
414
+ "Unable to allocate a unique credential reference",
415
+ );
416
+ }
417
+
418
+ _normalizeIssueRequest(request = {}) {
419
+ const approval = request.approval || {};
420
+ const target = request.target || {};
421
+ const processTarget = normalizeProcessTarget(
422
+ target.process ?? request.process,
423
+ );
424
+ const hostTarget = normalizeHostTarget(target.host ?? request.host);
425
+ const approvalId = request.approvalId ?? approval.id;
426
+ const approved = request.approved ?? approval.approved;
427
+
428
+ return {
429
+ approved,
430
+ approvalId: approvalId ? String(approvalId) : null,
431
+ key: String(request.key ?? "credential"),
432
+ value: request.value,
433
+ target: {
434
+ process: processTarget,
435
+ host: hostTarget,
436
+ },
437
+ ttlMs: requestedPositiveInteger(
438
+ request.ttlMs,
439
+ this._defaultTtlMs,
440
+ this._maxTtlMs,
441
+ "Credential TTL",
442
+ ),
443
+ maxUses: requestedPositiveInteger(
444
+ request.maxUses,
445
+ this._defaultMaxUses,
446
+ this._maxUsesLimit,
447
+ "Credential maxUses",
448
+ ),
449
+ origin: String(request.origin ?? "unknown"),
450
+ };
451
+ }
452
+
130
453
  /**
131
- * Strip sensitive environment variables from spawn options
132
- * Returns filtered env and a list of redacted keys
454
+ * Issue a short-lived, target-bound reference.
455
+ *
456
+ * This is a parent/Broker API. A child must never be allowed to call it with
457
+ * self-asserted approval metadata.
133
458
  */
134
- filterEnvironment(env = process.env) {
135
- const safe = {};
136
- const redacted = [];
459
+ issueCredentialRef(request = {}) {
460
+ if (!this.isEnabled()) {
461
+ this._recordAudit({
462
+ event: "credential_ref_issue",
463
+ outcome: "denied",
464
+ reason: CREDENTIAL_ERROR_CODES.DISABLED,
465
+ key:
466
+ request && typeof request === "object"
467
+ ? String(request.key ?? "credential")
468
+ : "credential",
469
+ });
470
+ throw credentialError(
471
+ CREDENTIAL_ERROR_CODES.DISABLED,
472
+ "Credential reference issuance is disabled",
473
+ );
474
+ }
137
475
 
138
- for (const [key, value] of Object.entries(env)) {
139
- if (this.isSensitiveKey(key)) {
140
- // Store a masked reference instead of passing through
141
- const refId = `cc-cred-${crypto.randomBytes(8).toString("hex")}`;
142
- this._credentialStore.set(refId, { key, value, createdAt: Date.now() });
143
- redacted.push({ key, refId });
144
- // Pass the reference ID, child can request via proxy
145
- safe[`CC_CRED_REF_${key}`] = refId;
146
- } else {
147
- safe[key] = value;
476
+ let normalized;
477
+ try {
478
+ if (!request || typeof request !== "object") {
479
+ throw credentialError(
480
+ CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
481
+ "Credential issuance request is required",
482
+ );
148
483
  }
484
+ normalized = this._normalizeIssueRequest(request);
485
+ } catch (error) {
486
+ this._recordAudit({
487
+ event: "credential_ref_issue",
488
+ outcome: "denied",
489
+ reason: error.code || CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
490
+ });
491
+ throw error;
492
+ }
493
+
494
+ if (normalized.approved !== true || !normalized.approvalId) {
495
+ this._recordAudit({
496
+ event: "credential_ref_issue",
497
+ outcome: "denied",
498
+ reason: CREDENTIAL_ERROR_CODES.APPROVAL_REQUIRED,
499
+ key: normalized.key,
500
+ target: normalized.target,
501
+ });
502
+ throw credentialError(
503
+ CREDENTIAL_ERROR_CODES.APPROVAL_REQUIRED,
504
+ "Credential reference requires an approved broker decision",
505
+ );
506
+ }
507
+ if (!normalized.target.process) {
508
+ this._recordAudit({
509
+ event: "credential_ref_issue",
510
+ outcome: "denied",
511
+ reason: CREDENTIAL_ERROR_CODES.TARGET_REQUIRED,
512
+ key: normalized.key,
513
+ });
514
+ throw credentialError(
515
+ CREDENTIAL_ERROR_CODES.TARGET_REQUIRED,
516
+ "Credential process target is required",
517
+ );
518
+ }
519
+ if (normalized.value === undefined || normalized.value === null) {
520
+ this._recordAudit({
521
+ event: "credential_ref_issue",
522
+ outcome: "denied",
523
+ reason: CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
524
+ key: normalized.key,
525
+ target: normalized.target,
526
+ });
527
+ throw credentialError(
528
+ CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
529
+ "Credential value is required",
530
+ );
531
+ }
532
+
533
+ let refId;
534
+ try {
535
+ this._ensureStoreCapacity();
536
+ refId = this._createReferenceId();
537
+ } catch (error) {
538
+ this._recordAudit({
539
+ event: "credential_ref_issue",
540
+ outcome: "denied",
541
+ reason: error.code || CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
542
+ key: normalized.key,
543
+ target: normalized.target,
544
+ });
545
+ throw error;
546
+ }
547
+ const createdAt = this._now();
548
+ this._credentialStore.set(refId, {
549
+ key: normalized.key,
550
+ value: normalized.value,
551
+ createdAt,
552
+ expiresAt: createdAt + normalized.ttlMs,
553
+ maxUses: normalized.maxUses,
554
+ useCount: 0,
555
+ status: "active",
556
+ target: normalized.target,
557
+ approvalFingerprint: this._fingerprint(normalized.approvalId),
558
+ });
559
+ this._recordAudit({
560
+ event: "credential_ref_issue",
561
+ outcome: "allowed",
562
+ key: normalized.key,
563
+ target: normalized.target,
564
+ refFingerprint: this._fingerprint(refId),
565
+ approvalFingerprint: this._fingerprint(normalized.approvalId),
566
+ ttlMs: normalized.ttlMs,
567
+ maxUses: normalized.maxUses,
568
+ origin: normalized.origin,
569
+ });
570
+ return refId;
571
+ }
572
+
573
+ _assertResolutionTarget(record, request) {
574
+ if (request.agentId !== this._agentId) {
575
+ throw credentialError(
576
+ CREDENTIAL_ERROR_CODES.AGENT_MISMATCH,
577
+ "Credential agent identity does not match",
578
+ );
149
579
  }
150
580
 
151
- // Set credential proxy endpoint for the child process
152
- safe.CC_CREDENTIAL_AGENT_ID = this._agentId;
153
- safe.CC_CREDENTIAL_PROXY = "builtin";
581
+ const requestTarget = {
582
+ process: normalizeProcessTarget(
583
+ request.target?.process ?? request.process,
584
+ ),
585
+ host: normalizeHostTarget(request.target?.host ?? request.host),
586
+ };
587
+ if (
588
+ !requestTarget.process ||
589
+ requestTarget.process !== record.target.process ||
590
+ requestTarget.host !== record.target.host
591
+ ) {
592
+ throw credentialError(
593
+ CREDENTIAL_ERROR_CODES.TARGET_MISMATCH,
594
+ "Credential resolution target does not match",
595
+ );
596
+ }
597
+ }
154
598
 
155
- return { safeEnv: safe, redacted };
599
+ _throwStoredCredentialState(record) {
600
+ if (record.status === "expired") {
601
+ throw credentialError(
602
+ CREDENTIAL_ERROR_CODES.EXPIRED,
603
+ "Credential reference has expired",
604
+ );
605
+ }
606
+ if (record.status === "exhausted") {
607
+ throw credentialError(
608
+ CREDENTIAL_ERROR_CODES.EXHAUSTED,
609
+ "Credential reference usage limit has been reached",
610
+ );
611
+ }
612
+ throw credentialError(
613
+ CREDENTIAL_ERROR_CODES.REVOKED,
614
+ "Credential reference has been revoked",
615
+ );
156
616
  }
157
617
 
158
618
  /**
159
- * Sanitize command line arguments that might contain secrets
160
- * (e.g., curl -H "Authorization: Bearer sk-xxx")
619
+ * Resolve a reference for a Broker-attested request.
620
+ *
621
+ * The raw value is returned only to the trusted caller. An eventual IPC
622
+ * adapter must authenticate the OS process and host before constructing this
623
+ * request object; child-provided claims are not sufficient attestation.
161
624
  */
162
- sanitizeArgs(args = []) {
163
- const sanitized = [];
625
+ resolveCredentialRef(refId, request = {}) {
626
+ const refFingerprint = this._fingerprint(refId);
627
+ if (!this.isEnabled()) {
628
+ this._recordAudit({
629
+ event: "credential_ref_resolve",
630
+ outcome: "denied",
631
+ reason: CREDENTIAL_ERROR_CODES.DISABLED,
632
+ refFingerprint,
633
+ });
634
+ throw credentialError(
635
+ CREDENTIAL_ERROR_CODES.DISABLED,
636
+ "Credential reference resolution is disabled",
637
+ );
638
+ }
639
+
640
+ this._expireStaleCredentials();
641
+ const record =
642
+ typeof refId === "string" ? this._credentialStore.get(refId) : null;
643
+ if (!record) {
644
+ this._recordAudit({
645
+ event: "credential_ref_resolve",
646
+ outcome: "denied",
647
+ reason: CREDENTIAL_ERROR_CODES.NOT_FOUND,
648
+ refFingerprint,
649
+ });
650
+ throw credentialError(
651
+ CREDENTIAL_ERROR_CODES.NOT_FOUND,
652
+ "Credential reference was not found",
653
+ );
654
+ }
655
+
656
+ if (record.status !== "active") {
657
+ const code =
658
+ record.status === "expired"
659
+ ? CREDENTIAL_ERROR_CODES.EXPIRED
660
+ : record.status === "exhausted"
661
+ ? CREDENTIAL_ERROR_CODES.EXHAUSTED
662
+ : CREDENTIAL_ERROR_CODES.REVOKED;
663
+ this._recordAudit({
664
+ event: "credential_ref_resolve",
665
+ outcome: "denied",
666
+ reason: code,
667
+ key: record.key,
668
+ target: record.target,
669
+ refFingerprint,
670
+ });
671
+ this._throwStoredCredentialState(record);
672
+ }
673
+
674
+ try {
675
+ this._assertResolutionTarget(
676
+ record,
677
+ request && typeof request === "object" ? request : {},
678
+ );
679
+ } catch (error) {
680
+ this._recordAudit({
681
+ event: "credential_ref_resolve",
682
+ outcome: "denied",
683
+ reason: error.code || CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
684
+ key: record.key,
685
+ target: record.target,
686
+ refFingerprint,
687
+ });
688
+ throw error;
689
+ }
690
+
691
+ const value = record.value;
692
+ record.useCount += 1;
693
+ const remainingUses = record.maxUses - record.useCount;
694
+ if (remainingUses <= 0) {
695
+ record.value = null;
696
+ record.status = "exhausted";
697
+ }
698
+ this._recordAudit({
699
+ event: "credential_ref_resolve",
700
+ outcome: "allowed",
701
+ key: record.key,
702
+ target: record.target,
703
+ refFingerprint,
704
+ useCount: record.useCount,
705
+ remainingUses: Math.max(remainingUses, 0),
706
+ });
707
+ return value;
708
+ }
709
+
710
+ /** Strict backward-compatible name for callers migrating to the new API. */
711
+ resolveCredential(refId, request = {}) {
712
+ return this.resolveCredentialRef(refId, request);
713
+ }
714
+
715
+ revokeCredentialRef(refId, reason = "broker-revoked") {
716
+ const record = this._credentialStore.get(refId);
717
+ if (!record) return false;
718
+ record.value = null;
719
+ record.status = "revoked";
720
+ this._recordAudit({
721
+ event: "credential_ref_revoke",
722
+ outcome: "allowed",
723
+ reason: "broker-revoked",
724
+ reasonFingerprint: this._fingerprint(reason),
725
+ key: record.key,
726
+ target: record.target,
727
+ refFingerprint: this._fingerprint(refId),
728
+ });
729
+ return true;
730
+ }
731
+
732
+ _contextForSpawn(spawnOptions) {
733
+ if (
734
+ Object.prototype.hasOwnProperty.call(spawnOptions, "credentialContext")
735
+ ) {
736
+ const supplied = spawnOptions.credentialContext || {};
737
+ return {
738
+ approved: supplied.approved === true,
739
+ approvalId: supplied.approvalId,
740
+ target: {
741
+ process:
742
+ supplied.target?.process ??
743
+ supplied.process ??
744
+ spawnOptions.file ??
745
+ spawnOptions.command,
746
+ host: supplied.target?.host ?? supplied.host ?? null,
747
+ },
748
+ ttlMs: supplied.ttlMs,
749
+ maxUses: supplied.maxUses,
750
+ origin: spawnOptions.origin,
751
+ };
752
+ }
753
+
754
+ const processTarget = spawnOptions.file ?? spawnOptions.command;
755
+ if (!normalizeProcessTarget(processTarget)) return null;
756
+
757
+ /*
758
+ * Compatibility bridge for the current ProcessExecutionBroker: it invokes
759
+ * applyWithReport only after an allow/elevated decision but does not yet
760
+ * forward the decision id. The intended integration is to pass the result
761
+ * of createBrokerContext() as credentialContext.
762
+ */
763
+ return {
764
+ approved: spawnOptions.credentialApproved !== false,
765
+ approvalId:
766
+ spawnOptions.credentialApprovalId ||
767
+ `broker-boundary:${spawnOptions.origin || "unknown"}`,
768
+ target: {
769
+ process: processTarget,
770
+ host:
771
+ spawnOptions.credentialTarget?.host ??
772
+ spawnOptions.credentialTargetHost ??
773
+ null,
774
+ },
775
+ ttlMs: spawnOptions.credentialTtlMs,
776
+ maxUses: spawnOptions.credentialMaxUses,
777
+ origin: spawnOptions.origin,
778
+ };
779
+ }
780
+
781
+ _canIssueForContext(context) {
782
+ return Boolean(
783
+ context?.approved === true &&
784
+ context.approvalId &&
785
+ normalizeProcessTarget(context.target?.process ?? context.process),
786
+ );
787
+ }
788
+
789
+ _setReferenceMarkers(safeEnv) {
790
+ safeEnv.CC_CREDENTIAL_AGENT_ID = this._agentId;
791
+ safeEnv.CC_CREDENTIAL_TRANSPORT = this._transport;
792
+ }
793
+
794
+ /**
795
+ * Strip sensitive and reserved environment variables from spawn options.
796
+ */
797
+ filterEnvironment(env = process.env, context = null) {
798
+ const safeEnv = {};
164
799
  const redacted = [];
800
+ const reservedRemoved = [];
801
+ const enabled = this.isEnabled();
802
+ let refsIssued = 0;
165
803
 
166
- for (let i = 0; i < args.length; i++) {
167
- const arg = String(args[i]);
804
+ for (const [key, value] of Object.entries(env || {})) {
805
+ if (this.isReservedAgentKey(key)) {
806
+ reservedRemoved.push(key);
807
+ continue;
808
+ }
809
+ if (!this.isSensitiveKey(key)) {
810
+ safeEnv[key] = value;
811
+ continue;
812
+ }
813
+
814
+ const entry = { key, refIssued: false };
815
+ if (
816
+ enabled &&
817
+ this._canIssueForContext(context) &&
818
+ value !== undefined &&
819
+ value !== null
820
+ ) {
821
+ const refId = this.issueCredentialRef({
822
+ ...context,
823
+ key,
824
+ value,
825
+ });
826
+ safeEnv[`CC_CRED_REF_${key}`] = refId;
827
+ entry.refIssued = true;
828
+ refsIssued += 1;
829
+ }
830
+ redacted.push(entry);
831
+ }
832
+
833
+ if (refsIssued > 0) this._setReferenceMarkers(safeEnv);
834
+ return {
835
+ safeEnv,
836
+ redacted,
837
+ reservedRemoved,
838
+ refsIssued,
839
+ };
840
+ }
841
+
842
+ _sanitizeArgsWithSecrets(args = []) {
843
+ const sanitizedArgs = [];
844
+ const redacted = [];
845
+ const inputArgs = Array.isArray(args) ? args : [];
846
+ const secretOption =
847
+ /^--[^=]*(?:token|api[-_]?key|secret|password|passwd|credential)[^=]*$/i;
848
+
849
+ for (let index = 0; index < inputArgs.length; index += 1) {
850
+ const arg = String(inputArgs[index]);
851
+ const equalsIndex = arg.indexOf("=");
852
+ const optionName = equalsIndex >= 0 ? arg.slice(0, equalsIndex) : arg;
168
853
 
169
- // Check for --token=xxx or --api-key xxx patterns
170
- if (/^--[a-z-]*([Tt]oken|[Kk]ey|[Ss]ecret|[Pp]assword)=/i.test(arg)) {
171
- sanitized.push(arg.replace(/=.*/, "=***REDACTED***"));
172
- redacted.push({ index: i, pattern: "arg-with-secret" });
854
+ if (equalsIndex >= 0 && secretOption.test(optionName)) {
855
+ sanitizedArgs.push(`${optionName}=${REDACTED_VALUE}`);
856
+ redacted.push({
857
+ index,
858
+ pattern: "arg-with-secret",
859
+ secrets: [
860
+ {
861
+ key: optionName.replace(/^--/, "") || `argv-${index}`,
862
+ value: arg.slice(equalsIndex + 1),
863
+ },
864
+ ],
865
+ });
866
+ continue;
867
+ }
868
+
869
+ if (secretOption.test(arg) && index + 1 < inputArgs.length) {
870
+ sanitizedArgs.push(arg, REDACTED_VALUE);
871
+ redacted.push({
872
+ index: index + 1,
873
+ pattern: "arg-after-secret-option",
874
+ secrets: [
875
+ {
876
+ key: arg.replace(/^--/, "") || `argv-${index + 1}`,
877
+ value: String(inputArgs[index + 1]),
878
+ },
879
+ ],
880
+ });
881
+ index += 1;
173
882
  continue;
174
883
  }
175
884
 
176
- // Check for -H "Authorization: Bearer xxx"
177
885
  if (arg === "-H" || /^--header$/i.test(arg)) {
178
- sanitized.push(arg);
179
- if (i + 1 < args.length) {
180
- const nextArg = String(args[i + 1]);
181
- if (/^(Authorization|X-API-Key|Cookie|Set-Cookie):/i.test(nextArg)) {
182
- sanitized.push(`${nextArg.split(":")[0]}: ***REDACTED***`);
183
- redacted.push({ index: i + 1, pattern: "header-with-secret" });
184
- i++; // skip next
185
- continue;
886
+ sanitizedArgs.push(arg);
887
+ if (index + 1 < inputArgs.length) {
888
+ const headerArg = String(inputArgs[index + 1]);
889
+ const headerMatch = headerArg.match(
890
+ /^(Authorization|Proxy-Authorization|X-API-Key|Cookie|Set-Cookie)\s*:\s*(.*)$/i,
891
+ );
892
+ if (headerMatch) {
893
+ sanitizedArgs.push(`${headerMatch[1]}: ${REDACTED_VALUE}`);
894
+ redacted.push({
895
+ index: index + 1,
896
+ pattern: "header-with-secret",
897
+ secrets: [
898
+ {
899
+ key: `header-${headerMatch[1].toLowerCase()}`,
900
+ value: headerMatch[2],
901
+ },
902
+ ],
903
+ });
904
+ index += 1;
186
905
  }
187
906
  }
188
907
  continue;
189
908
  }
190
909
 
191
- // Check for inline Bearer tokens
192
- if (/Bearer\s+[A-Za-z0-9._-]{20,}/.test(arg)) {
193
- sanitized.push(
194
- arg.replace(/Bearer\s+[A-Za-z0-9._-]+/g, "Bearer ***REDACTED***"),
195
- );
196
- redacted.push({ index: i, pattern: "bearer-token-inline" });
910
+ const directHeaderMatch = arg.match(
911
+ /^(Authorization|Proxy-Authorization|X-API-Key|Cookie|Set-Cookie)\s*:\s*(.*)$/i,
912
+ );
913
+ if (directHeaderMatch) {
914
+ sanitizedArgs.push(`${directHeaderMatch[1]}: ${REDACTED_VALUE}`);
915
+ redacted.push({
916
+ index,
917
+ pattern: "header-with-secret",
918
+ secrets: [
919
+ {
920
+ key: `header-${directHeaderMatch[1].toLowerCase()}`,
921
+ value: directHeaderMatch[2],
922
+ },
923
+ ],
924
+ });
197
925
  continue;
198
926
  }
199
927
 
200
- // Check for sk- prefixed keys (OpenAI style)
201
- if (/\bsk-[A-Za-z0-9]{20,}\b/.test(arg)) {
202
- sanitized.push(
203
- arg.replace(/\bsk-[A-Za-z0-9]{20,}\b/g, "sk-***REDACTED***"),
204
- );
205
- redacted.push({ index: i, pattern: "sk-key-inline" });
206
- continue;
928
+ const inlineSecrets = [];
929
+ let sanitized = arg.replace(
930
+ /Bearer\s+([A-Za-z0-9._-]{20,})/g,
931
+ (_match, value) => {
932
+ inlineSecrets.push({
933
+ key: `argv-${index}-bearer`,
934
+ value,
935
+ });
936
+ return `Bearer ${REDACTED_VALUE}`;
937
+ },
938
+ );
939
+ sanitized = sanitized.replace(
940
+ /\bsk-([A-Za-z0-9]{20,})\b/g,
941
+ (_match, value) => {
942
+ inlineSecrets.push({
943
+ key: `argv-${index}-sk-key`,
944
+ value: `sk-${value}`,
945
+ });
946
+ return `sk-${REDACTED_VALUE}`;
947
+ },
948
+ );
949
+ sanitizedArgs.push(sanitized);
950
+ if (inlineSecrets.length > 0) {
951
+ redacted.push({
952
+ index,
953
+ pattern: "inline-secret",
954
+ secrets: inlineSecrets,
955
+ });
207
956
  }
208
-
209
- sanitized.push(arg);
210
957
  }
211
958
 
212
- return { sanitizedArgs: sanitized, redacted };
959
+ return { sanitizedArgs, redacted };
213
960
  }
214
961
 
215
962
  /**
216
- * Apply credential filtering and return explicit audit metadata. Credential
217
- * values and proxy reference ids are intentionally kept out of the report.
963
+ * Sanitize command line arguments without exposing captured values.
964
+ */
965
+ sanitizeArgs(args = []) {
966
+ const { sanitizedArgs, redacted } = this._sanitizeArgsWithSecrets(args);
967
+ return {
968
+ sanitizedArgs,
969
+ redacted: redacted.map(({ index, pattern }) => ({
970
+ index,
971
+ pattern,
972
+ })),
973
+ };
974
+ }
975
+
976
+ /**
977
+ * Apply filtering and issue references for a broker-approved spawn.
218
978
  */
219
979
  applyWithReport(spawnOptions) {
980
+ if (!spawnOptions || typeof spawnOptions !== "object") {
981
+ throw credentialError(
982
+ CREDENTIAL_ERROR_CODES.INVALID_REQUEST,
983
+ "Spawn options are required",
984
+ );
985
+ }
986
+
987
+ const context = this._contextForSpawn(spawnOptions);
220
988
  const env = spawnOptions.env || process.env;
221
- const { safeEnv, redacted: envRedacted } = this.filterEnvironment(env);
222
- spawnOptions.env = safeEnv;
989
+ const {
990
+ safeEnv,
991
+ redacted: envRedacted,
992
+ reservedRemoved,
993
+ refsIssued: envRefsIssued,
994
+ } = this.filterEnvironment(env, context);
223
995
 
224
996
  const args = spawnOptions.args || [];
225
- const { sanitizedArgs, redacted: argRedacted } = this.sanitizeArgs(args);
997
+ const { sanitizedArgs, redacted: internalArgRedacted } =
998
+ this._sanitizeArgsWithSecrets(args);
999
+
1000
+ let argRefsIssued = 0;
1001
+ if (this.isEnabled() && this._canIssueForContext(context)) {
1002
+ for (const entry of internalArgRedacted) {
1003
+ for (
1004
+ let secretIndex = 0;
1005
+ secretIndex < entry.secrets.length;
1006
+ secretIndex += 1
1007
+ ) {
1008
+ const secret = entry.secrets[secretIndex];
1009
+ const refId = this.issueCredentialRef({
1010
+ ...context,
1011
+ key: secret.key,
1012
+ value: secret.value,
1013
+ });
1014
+ const suffix = secretIndex === 0 ? "" : `_${secretIndex}`;
1015
+ safeEnv[`CC_CRED_ARG_REF_${entry.index}${suffix}`] = refId;
1016
+ argRefsIssued += 1;
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+ const refsIssued = envRefsIssued + argRefsIssued;
1022
+ if (refsIssued > 0) this._setReferenceMarkers(safeEnv);
1023
+ spawnOptions.env = safeEnv;
226
1024
  spawnOptions.args = sanitizedArgs;
227
1025
 
228
- // Record credential access audit
229
- if (envRedacted.length > 0 || argRedacted.length > 0) {
230
- this._accessLog.push({
231
- timestamp: Date.now(),
232
- origin: spawnOptions.origin || "unknown",
233
- envRedacted: envRedacted.map((r) => r.key),
234
- argRedacted: argRedacted.length,
235
- command: spawnOptions.file || spawnOptions.command,
1026
+ const envKeys = envRedacted.map((entry) => entry.key);
1027
+ const argIndexes = internalArgRedacted.map((entry) => entry.index);
1028
+ const filtered =
1029
+ envRedacted.length > 0 ||
1030
+ internalArgRedacted.length > 0 ||
1031
+ reservedRemoved.length > 0;
1032
+ const enabled = this.isEnabled();
1033
+
1034
+ if (filtered) {
1035
+ this._recordAudit({
1036
+ event: "credential_boundary_apply",
1037
+ outcome: "allowed",
1038
+ origin: String(spawnOptions.origin || "unknown"),
1039
+ target: context
1040
+ ? {
1041
+ process: normalizeProcessTarget(
1042
+ context.target?.process ?? context.process,
1043
+ ),
1044
+ host: normalizeHostTarget(context.target?.host ?? context.host),
1045
+ }
1046
+ : null,
1047
+ envRedacted: envKeys,
1048
+ argRedacted: argIndexes,
1049
+ reservedEnvRemoved: reservedRemoved,
1050
+ refsIssued,
1051
+ mode: enabled
1052
+ ? refsIssued > 0
1053
+ ? "broker-reference"
1054
+ : "redact-only"
1055
+ : "redact-only-disabled",
236
1056
  });
237
- if (this._accessLog.length > 10000) this._accessLog.shift();
238
1057
  }
239
1058
 
240
1059
  return {
241
1060
  spawnOptions,
242
1061
  report: {
243
- envKeys: envRedacted.map((entry) => entry.key),
244
- argIndexes: argRedacted.map((entry) => entry.index),
1062
+ envKeys,
1063
+ argIndexes,
245
1064
  envCount: envRedacted.length,
246
- argCount: argRedacted.length,
247
- filtered: envRedacted.length > 0 || argRedacted.length > 0,
1065
+ argCount: internalArgRedacted.length,
1066
+ filtered,
1067
+ refsIssued,
1068
+ agentDisabled: !enabled,
1069
+ mode: enabled
1070
+ ? refsIssued > 0
1071
+ ? "broker-reference"
1072
+ : "redact-only"
1073
+ : "redact-only-disabled",
1074
+ targetBound: refsIssued > 0,
248
1075
  },
249
1076
  };
250
1077
  }
@@ -254,41 +1081,57 @@ class CredentialAgent {
254
1081
  return this.applyWithReport(spawnOptions).spawnOptions;
255
1082
  }
256
1083
 
257
- /**
258
- * Resolve a credential reference (called via IPC from child processes)
259
- */
260
- resolveCredential(refId) {
261
- const cred = this._credentialStore.get(refId);
262
- if (!cred) return null;
263
- // Log access
264
- this._accessLog.push({
265
- timestamp: Date.now(),
266
- type: "resolve",
267
- refId,
268
- key: cred.key,
269
- });
270
- return cred.value;
1084
+ getAuditLog(limit = this._accessLog.length) {
1085
+ const safeLimit = Math.max(
1086
+ 0,
1087
+ Math.min(Number(limit) || 0, this._accessLog.length),
1088
+ );
1089
+ if (safeLimit === 0) return [];
1090
+ return this._accessLog.slice(-safeLimit).map(cloneAuditEntry);
1091
+ }
1092
+
1093
+ clearAuditLog() {
1094
+ this._accessLog = [];
271
1095
  }
272
1096
 
273
1097
  /**
274
- * Get credential agent info for status reporting
1098
+ * Get credential agent info for status reporting. No raw references or
1099
+ * credential values are included.
275
1100
  */
276
1101
  getInfo() {
1102
+ this.isEnabled();
1103
+ this._expireStaleCredentials();
1104
+ const statusCounts = {};
1105
+ for (const record of this._credentialStore.values()) {
1106
+ statusCounts[record.status] = (statusCounts[record.status] || 0) + 1;
1107
+ }
277
1108
  return {
278
1109
  agentId: this._agentId,
1110
+ enabled: this._lastEnabledState,
1111
+ mode: this._lastEnabledState
1112
+ ? "broker-reference"
1113
+ : "redact-only-disabled",
1114
+ transport: this._transport,
1115
+ proxyReady: this._proxyReady,
1116
+ ipcAvailable: false,
279
1117
  credentialsTracked: this._credentialStore.size,
1118
+ activeCredentials: statusCounts.active || 0,
280
1119
  accessCount: this._accessLog.length,
281
1120
  sensitivePatterns: SENSITIVE_ENV_PATTERNS.length,
282
1121
  allowlistSize: ENV_ALLOWLIST.size,
1122
+ defaultTtlMs: this._defaultTtlMs,
1123
+ defaultMaxUses: this._defaultMaxUses,
283
1124
  defaultOn: true,
284
1125
  };
285
1126
  }
286
1127
  }
287
1128
 
288
1129
  const credentialAgent = new CredentialAgent();
1130
+
289
1131
  export {
290
1132
  credentialAgent,
291
1133
  CredentialAgent,
1134
+ CREDENTIAL_ERROR_CODES,
292
1135
  SENSITIVE_ENV_PATTERNS,
293
1136
  ENV_ALLOWLIST,
294
1137
  };