chainlesschain 0.162.188 → 0.162.193

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 (337) hide show
  1. package/README.md +61 -11
  2. package/bin/chainlesschain.js +7 -52
  3. package/completions/_cc +209 -0
  4. package/completions/cc.bash +18 -0
  5. package/completions/cc.fish +13 -0
  6. package/completions/cc.ps1 +226 -0
  7. package/package.json +10 -2
  8. package/src/assets/web-panel/assets/{AIOps-Brr1LVcE.js → AIOps-DBLkaTje.js} +1 -1
  9. package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-BAy-Qebo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-Dfb1Mlau.js} +3 -3
  11. package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-BbFkN8m0.js} +5 -5
  12. package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-kM4-cEEH.js} +1 -1
  13. package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-BSQfYWf3.js} +1 -1
  14. package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-CWDbI_fG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BxLPbq_Q.js} +1 -1
  16. package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-DuIbdrp6.js} +1 -1
  17. package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-NEmjGahU.js} +5 -5
  18. package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DfJEo-5_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CaoeG3Ns.js} +1 -1
  20. package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-BFf_g3xY.js} +1 -1
  21. package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CNOd8mdQ.js} +1 -1
  22. package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-BhpDqz-i.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-CQ42T7Z6.js} +1 -1
  24. package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-DI5PcLMN.js} +1 -1
  25. package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-wYYZe0wR.js} +3 -3
  26. package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-Dlq03DD_.js} +2 -2
  27. package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BzqS8PP3.js} +1 -1
  28. package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-It55Yem7.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-9HkH6oxn.js} +2 -2
  30. package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-00e2YWRB.js} +1 -1
  31. package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-By-ugRDL.js} +1 -1
  32. package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-C396yu5m.js} +1 -1
  33. package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-BME5sbZY.js} +1 -1
  34. package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-B4TEZCr-.js} +1 -1
  35. package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-_9CX9ieY.js} +1 -1
  36. package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-BrS6WVSr.js} +2 -2
  37. package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-BMdCWnKF.js} +1 -1
  38. package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-Bcp3vzcA.js} +1 -1
  39. package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-DlLuqGY5.js} +1 -1
  40. package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-D-W_6x09.js} +2 -2
  41. package/src/assets/web-panel/assets/MarkdownRenderer-DElGPxCZ.js +1 -0
  42. package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-Dzn2guvI.js} +1 -1
  43. package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-uPict-KL.js} +6 -6
  44. package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-BCw43kOb.js} +2 -2
  45. package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-DmV8Wv7F.js} +1 -1
  46. package/src/assets/web-panel/assets/{MobileProjects-COBbEOlj.js → MobileProjects-C-HaCmSM.js} +1 -1
  47. package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-C6jxJ34Y.js} +5 -5
  48. package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-Za8ExwhQ.js} +5 -5
  49. package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-KKDbuEgb.js} +3 -3
  50. package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-Dj97zbCk.js} +1 -1
  51. package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DBXvMraU.js} +4 -4
  52. package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-DOxITLYl.js} +1 -1
  53. package/src/assets/web-panel/assets/OrderTableRenderer-7DVjZwMM.js +1 -0
  54. package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D1i73mjb.js} +4 -4
  55. package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-kW6Q3rvg.js} +1 -1
  56. package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-C-v_8p2v.js} +2 -2
  57. package/src/assets/web-panel/assets/PdhVaultBrowser-tUbbY64W.js +7 -0
  58. package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-DkHxEZ58.js} +4 -4
  59. package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-DWuMPGrK.js} +5 -5
  60. package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-30IYXWWm.js} +1 -1
  61. package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-DZNQ4RrH.js} +1 -1
  62. package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-De2MCElf.js} +2 -2
  63. package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-5uHk9wsC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-XjDfrFLb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-Bgwj6gcE.js} +1 -1
  66. package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-gKql9umv.js} +1 -1
  67. package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-BWvmwkh7.js} +1 -1
  68. package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-Uok4hZPd.js} +1 -1
  69. package/src/assets/web-panel/assets/{RemoteSession-oOVIv9wq.js → RemoteSession-DLJzTk9a.js} +3 -3
  70. package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-ZVwIPMSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CN04HxWR.js} +1 -1
  72. package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BdSrEG4g.js} +3 -3
  73. package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-5PwVd0IR.js} +1 -1
  74. package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-DdBo4zjN.js} +3 -3
  75. package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-D3letU7M.js} +2 -2
  76. package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton--Jb83f1v.js} +1 -1
  77. package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-Bg-CulHX.js} +1 -1
  78. package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-7QOa7S-X.js} +1 -1
  79. package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-DQwppjRw.js} +1 -1
  80. package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-dNPQGL-s.js} +2 -2
  81. package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-Ggs-Z48q.js} +1 -1
  82. package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-78TYHgRO.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-xHwwv9vO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BJXSJfm4.js} +2 -2
  85. package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-fLbjRhJw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-oMSBSBmB.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-CfnY_wX_.js} +1 -1
  88. package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-AdyTgYKO.js} +1 -1
  89. package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-C5K_81dS.js} +1 -1
  90. package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-vcGVDqki.js} +1 -1
  91. package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-hhOc3hlY.js} +4 -4
  92. package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-CVloC11s.js} +3 -3
  93. package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-CYeE32R5.js} +1 -1
  94. package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-CNROR5bN.js} +1 -1
  95. package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-Cjx-cnwU.js} +1 -1
  96. package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-DOIfelvD.js} +1 -1
  97. package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-DfA7UZ8u.js} +1 -1
  98. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +1 -0
  99. package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-BMSCzcfM.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-5-HXgOX5.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-AWg-0M1S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-B4iYzpkL.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-B5Udeb_6.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-B7gf77CJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-BEMwBBHk.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-BM1y_9hM.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-BUonmKSE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-BbQ2eQ8H.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-BemJ2u_E.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-BolaOQxx.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-BxOk9vkG.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-By61ojUx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-C2K9SNVX.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-C3L5gdiD.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-C7xn0Nub.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COG61sRv.js → index-CAnvhwg6.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CEKMcOyU.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-CNC5onm4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CVL27kXC.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-CVm1_i3t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-CdYBye8B.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-ClNMrqEY.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-Cs6l7uRu.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-CshQbilz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-Cyweh0Hy.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-DBpWXFed.js} +3 -3
  127. package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-DHtpHZAQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-DLmn0BoK.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-DPAWBrMf.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-DjEsxO5h.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-FBc_CJBd.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Icdoqmzt.js} +1 -1
  133. package/src/assets/web-panel/assets/index-Q2IajL54.js +1 -0
  134. package/src/assets/web-panel/assets/index-bRkHgOMc.js +1 -0
  135. package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-dCd8cSUq.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-m2vG9gG_.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-CaslYch6.js → index-mTpHoEzr.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-ti_lnTWl.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-Bu8k7H1Y.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-s3iVK41k.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-B6B-E5Jz.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-BiYgTidA.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-pU1RdUDA.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-Cdoq_SeM.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-DIuvAn2K.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-8w16YR2D.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-VZ01cZe3.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-lh8YMN8Z.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-CmDyreec.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-hiJWMU5y.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-Thnu-Iaj.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-LBdOxjwY.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-Cg6lFY_X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-DVfsR1YO.js} +1 -1
  155. package/src/assets/web-panel/index.html +1 -1
  156. package/src/cli-help.js +181 -0
  157. package/src/command-help-index.json +181 -0
  158. package/src/command-manifest.json +1107 -175
  159. package/src/command-surface-policy.js +197 -0
  160. package/src/commands/agent.js +98 -33
  161. package/src/commands/artifacts.js +400 -2
  162. package/src/commands/background-session.js +41 -3
  163. package/src/commands/checkpoint-managed.js +1059 -0
  164. package/src/commands/checkpoint-restore-recovery.js +1476 -0
  165. package/src/commands/checkpoint.js +950 -22
  166. package/src/commands/compact.js +101 -25
  167. package/src/commands/config.js +506 -20
  168. package/src/commands/doctor.js +4 -1
  169. package/src/commands/hub.js +1 -1
  170. package/src/commands/mcp.js +688 -24
  171. package/src/commands/pack.js +16 -2
  172. package/src/commands/plugin.js +20 -12
  173. package/src/commands/session-mcp-recovery.js +218 -0
  174. package/src/commands/session.js +470 -110
  175. package/src/commands/setup.js +36 -10
  176. package/src/commands/skill.js +181 -110
  177. package/src/commands/status.js +21 -5
  178. package/src/commands/team-distributed.js +3324 -0
  179. package/src/commands/team.js +1130 -74
  180. package/src/commands/update.js +68 -6
  181. package/src/constants.js +11 -0
  182. package/src/data/changelog.json +26 -0
  183. package/src/gateways/ws/action-protocol.js +10 -0
  184. package/src/gateways/ws/session-protocol.js +555 -27
  185. package/src/gateways/ws/ws-agent-handler.js +541 -14
  186. package/src/gateways/ws/ws-session-gateway.js +129 -22
  187. package/src/gateways/ws/ws-session-state.js +431 -0
  188. package/src/harness/background-task-manager.js +233 -36
  189. package/src/harness/jsonl-session-store.js +2551 -167
  190. package/src/harness/mcp-client.js +802 -61
  191. package/src/harness/prompt-compressor.js +227 -27
  192. package/src/harness/session-index.js +89 -38
  193. package/src/harness/session-list-index.js +254 -0
  194. package/src/harness/structured-handoff.js +410 -0
  195. package/src/lazy-dispatch.js +747 -84
  196. package/src/lib/__tests__/logger.test.js +50 -2
  197. package/src/lib/advisor-runtime.js +1047 -0
  198. package/src/lib/agent-core.js +2 -0
  199. package/src/lib/agent-sandbox.js +120 -26
  200. package/src/lib/agent-session-export.js +20 -0
  201. package/src/lib/agent-team/task-lease.js +271 -3
  202. package/src/lib/agent-team/team-adjudication.js +1501 -0
  203. package/src/lib/agent-team/team-budget.js +4 -1
  204. package/src/lib/agent-team/team-control-store.js +582 -0
  205. package/src/lib/agent-team/team-distributed-queue.js +5246 -0
  206. package/src/lib/agent-team/team-process-checkpoint.js +211 -0
  207. package/src/lib/agent-team/team-runner.js +543 -24
  208. package/src/lib/agent-team/team-worktree.js +1403 -95
  209. package/src/lib/artifact-store.js +131 -8
  210. package/src/lib/background-agent-supervisor.js +185 -13
  211. package/src/lib/background-interaction-journal.js +37 -9
  212. package/src/lib/background-launch-profile.js +1058 -0
  213. package/src/lib/checkpoint-restore-already-completed-controller.js +902 -0
  214. package/src/lib/checkpoint-restore-orchestrator.js +987 -0
  215. package/src/lib/checkpoint-restore-partial-rollback-controller.js +1953 -0
  216. package/src/lib/checkpoint-restore-recovery-controller.js +689 -0
  217. package/src/lib/checkpoint-restore-recovery.js +884 -0
  218. package/src/lib/checkpoint-restore-retention.js +126 -0
  219. package/src/lib/checkpoint-restore-saga.js +5079 -0
  220. package/src/lib/checkpoint-restore-session-recovery.js +1178 -0
  221. package/src/lib/checkpoint-restore-workspace-target-verifier.js +270 -0
  222. package/src/lib/checkpoint-store.js +1688 -97
  223. package/src/lib/checkpoint-timeline-authority.js +522 -0
  224. package/src/lib/checkpoint-timeline.js +642 -0
  225. package/src/lib/config-keys.js +18 -119
  226. package/src/lib/config-manager.js +446 -27
  227. package/src/lib/config-redaction.js +57 -0
  228. package/src/lib/config-schema.js +853 -0
  229. package/src/lib/cowork-mcp-tools.js +5 -0
  230. package/src/lib/cowork-task-runner.js +458 -63
  231. package/src/lib/delivery-coordinator.js +1234 -0
  232. package/src/lib/delivery-evidence.js +450 -0
  233. package/src/lib/doctor-checkup.js +199 -14
  234. package/src/lib/executable-path.js +34 -0
  235. package/src/lib/fatal-handler.js +16 -0
  236. package/src/lib/file-checkpoint.js +4909 -186
  237. package/src/lib/file-lines.js +125 -0
  238. package/src/lib/headless-config-command.js +16 -5
  239. package/src/lib/hook-runner.cjs +139 -38
  240. package/src/lib/hooks-v2-workspace-context.js +10 -0
  241. package/src/lib/impacted-gate-selector.js +424 -0
  242. package/src/lib/interaction-adapter.js +62 -0
  243. package/src/lib/jsonl-session-store.js +1 -35
  244. package/src/lib/logger.js +66 -14
  245. package/src/lib/managed-checkpoint-render.js +60 -0
  246. package/src/lib/managed-tool-checkpoint.js +194 -0
  247. package/src/lib/mcp-call-ledger-store.js +1069 -0
  248. package/src/lib/mcp-call-ledger.js +747 -0
  249. package/src/lib/mcp-client.js +5 -0
  250. package/src/lib/mcp-conflict-scheduler.js +468 -0
  251. package/src/lib/mcp-effect-contract.js +102 -0
  252. package/src/lib/mcp-headers-helper-trust.js +145 -0
  253. package/src/lib/mcp-headers-helper.js +616 -0
  254. package/src/lib/mcp-host-recovery-runtime.js +131 -0
  255. package/src/lib/mcp-ledger-recovery-admission.js +1065 -0
  256. package/src/lib/mcp-recovery-adjudication.js +443 -0
  257. package/src/lib/output-context.js +86 -0
  258. package/src/lib/packer/file-identity.js +170 -0
  259. package/src/lib/packer/index.js +3 -0
  260. package/src/lib/packer/native-update-state.js +277 -0
  261. package/src/lib/packer/pack-update-applier.js +1983 -136
  262. package/src/lib/packer/pack-update-checker.js +34 -0
  263. package/src/lib/packer/pack-update-downloader.js +875 -86
  264. package/src/lib/packer/pack-update-signature.js +86 -0
  265. package/src/lib/packer/pkg-config-generator.js +6 -0
  266. package/src/lib/paths.js +65 -10
  267. package/src/lib/plan-mode.js +831 -14
  268. package/src/lib/plan-persistence.js +540 -0
  269. package/src/lib/plugin-runtime/hooks.js +227 -69
  270. package/src/lib/plugin-runtime/plugin-options.js +189 -44
  271. package/src/lib/pr-automation-policy.js +146 -8
  272. package/src/lib/process-execution-broker/credential-agent.js +7 -0
  273. package/src/lib/process-execution-broker/index.js +491 -13
  274. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
  275. package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
  276. package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
  277. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  278. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  279. package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
  280. package/src/lib/process-execution-broker/workspace-transaction.js +4459 -0
  281. package/src/lib/project-instructions.js +165 -29
  282. package/src/lib/project-mcp-trust.js +67 -0
  283. package/src/lib/public-ide-capability-manifest.js +272 -0
  284. package/src/lib/public-ide-capability-validation.js +351 -0
  285. package/src/lib/secret-store.js +267 -43
  286. package/src/lib/secure-file-identity.js +433 -0
  287. package/src/lib/secure-fs.js +1089 -0
  288. package/src/lib/security-audit.js +58 -0
  289. package/src/lib/service-manager.js +34 -13
  290. package/src/lib/session-budget-runtime.js +1452 -0
  291. package/src/lib/session-host-snapshot.js +388 -0
  292. package/src/lib/session-message-provenance.js +429 -0
  293. package/src/lib/session-projection.js +804 -0
  294. package/src/lib/session-resource-budget.js +1495 -0
  295. package/src/lib/settings-hooks.cjs +138 -22
  296. package/src/lib/settings-loader.cjs +25 -2
  297. package/src/lib/side-effect-ledger-store.js +45 -23
  298. package/src/lib/skill-execution-identity.js +217 -0
  299. package/src/lib/skill-loader.js +299 -18
  300. package/src/lib/sub-agent-context.js +236 -28
  301. package/src/lib/sub-agent-registry.js +5 -0
  302. package/src/lib/subagent-contract.js +94 -0
  303. package/src/lib/todo-manager.js +185 -18
  304. package/src/lib/todo-persistence.js +417 -0
  305. package/src/lib/turn-binding-store.js +41 -23
  306. package/src/lib/turn-binding.js +160 -2
  307. package/src/lib/with-file-lock.js +165 -24
  308. package/src/lib/ws-chat-handler.js +21 -0
  309. package/src/program-base.js +18 -0
  310. package/src/repl/advisor-command.js +103 -0
  311. package/src/repl/agent-repl.js +2175 -146
  312. package/src/repl/btw-command.js +93 -10
  313. package/src/repl/clipboard-image.js +119 -0
  314. package/src/repl/config-summary.js +64 -108
  315. package/src/repl/prompt-editor.js +233 -0
  316. package/src/repl/prompt-interactions.js +344 -0
  317. package/src/repl/prompt-stash.js +354 -0
  318. package/src/repl/prompt-suggestions.js +312 -0
  319. package/src/repl/repl-keybindings.js +227 -0
  320. package/src/repl/session-recap.js +217 -0
  321. package/src/repl/slash-command-registry.js +22 -0
  322. package/src/repl/terminal-layout.js +88 -0
  323. package/src/runtime/agent-core.js +1630 -447
  324. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  325. package/src/runtime/coding-agent-managed-tool-policy.cjs +7 -2
  326. package/src/runtime/diagnostics.js +32 -13
  327. package/src/runtime/headless-runner.js +310 -118
  328. package/src/runtime/headless-stream.js +356 -95
  329. package/src/runtime/mcp-config.js +148 -14
  330. package/src/runtime/policies/agent-policy.js +14 -0
  331. package/src/runtime/status-diagnostics-lite.js +137 -0
  332. package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
  333. package/src/assets/web-panel/assets/OrderTableRenderer-BU2Yqq5H.js +0 -1
  334. package/src/assets/web-panel/assets/PdhVaultBrowser-69ca_WeR.js +0 -7
  335. package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
  336. package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
  337. package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
@@ -14,13 +14,140 @@
14
14
  * through the existing hook-runner (which applies the project-hook trust model).
15
15
  */
16
16
 
17
+ import crypto from "node:crypto";
17
18
  import fs from "fs";
19
+ import path from "node:path";
18
20
  import { discoverPlugins } from "./scopes.js";
19
21
  import { partitionByTrust, warnUntrustedOnce } from "./trust.js";
20
22
  import { componentCapabilityDenial } from "./capabilities.js";
21
23
  import { mergePluginSandboxPolicies } from "./sandbox-policy.js";
22
24
 
23
- export const _deps = { readFileSync: fs.readFileSync };
25
+ export const _deps = {
26
+ discoverPlugins,
27
+ readFileSync: fs.readFileSync,
28
+ };
29
+
30
+ function sha256Content(content) {
31
+ return crypto.createHash("sha256").update(String(content)).digest("hex");
32
+ }
33
+
34
+ function pluginAuthorityError({
35
+ code,
36
+ error,
37
+ plugin = null,
38
+ sourceFile = null,
39
+ digest = null,
40
+ stage,
41
+ }) {
42
+ return Object.freeze({
43
+ code,
44
+ kind: "plugin",
45
+ authorityBearing: true,
46
+ pluginId: plugin?.name || null,
47
+ pluginVersion: plugin?.version || null,
48
+ sourceFile:
49
+ typeof sourceFile === "string" && sourceFile
50
+ ? path.resolve(sourceFile)
51
+ : null,
52
+ digest: typeof digest === "string" && digest ? digest : null,
53
+ stage,
54
+ message: error?.message || String(error || code),
55
+ });
56
+ }
57
+
58
+ function normalizedAuthorityErrors(errors) {
59
+ return Object.freeze(
60
+ (Array.isArray(errors) ? errors : []).map((entry) =>
61
+ Object.freeze({ ...(entry || {}) }),
62
+ ),
63
+ );
64
+ }
65
+
66
+ function attachAuthorityErrors(target, errors) {
67
+ Object.defineProperty(target, "_authorityErrors", {
68
+ value: normalizedAuthorityErrors(errors),
69
+ enumerable: false,
70
+ writable: false,
71
+ configurable: false,
72
+ });
73
+ return target;
74
+ }
75
+
76
+ function attachPluginAuthoritySource(target, { sourceFile, digest }) {
77
+ Object.defineProperty(target, "authoritySource", {
78
+ value: Object.freeze({
79
+ kind: "plugin",
80
+ sourceFile: path.resolve(sourceFile),
81
+ digest,
82
+ }),
83
+ enumerable: true,
84
+ writable: false,
85
+ configurable: false,
86
+ });
87
+ return target;
88
+ }
89
+
90
+ function loadPluginHookSource(plugin, component, authorityErrors) {
91
+ const sourceFile = component.absPath
92
+ ? component.absPath
93
+ : component.inline
94
+ ? plugin.manifest?.manifestPath
95
+ : null;
96
+ if (!sourceFile) {
97
+ authorityErrors.push(
98
+ pluginAuthorityError({
99
+ code: "CC_PLUGIN_HOOK_READ_FAILED",
100
+ error: new Error("plugin hook source is missing"),
101
+ plugin,
102
+ sourceFile: plugin.manifest?.manifestPath || null,
103
+ stage: "read",
104
+ }),
105
+ );
106
+ return null;
107
+ }
108
+
109
+ let raw;
110
+ try {
111
+ raw = _deps.readFileSync(sourceFile, "utf8");
112
+ } catch (error) {
113
+ authorityErrors.push(
114
+ pluginAuthorityError({
115
+ code: "CC_PLUGIN_HOOK_READ_FAILED",
116
+ error,
117
+ plugin,
118
+ sourceFile,
119
+ stage: "read",
120
+ }),
121
+ );
122
+ return null;
123
+ }
124
+
125
+ const digest = sha256Content(raw);
126
+ try {
127
+ const parsed = JSON.parse(raw);
128
+ return {
129
+ parsed: component.inline
130
+ ? parsed && typeof parsed === "object"
131
+ ? parsed.hooks
132
+ : null
133
+ : parsed,
134
+ sourceFile,
135
+ digest,
136
+ };
137
+ } catch (error) {
138
+ authorityErrors.push(
139
+ pluginAuthorityError({
140
+ code: "CC_PLUGIN_HOOK_PARSE_FAILED",
141
+ error,
142
+ plugin,
143
+ sourceFile,
144
+ digest,
145
+ stage: "parse",
146
+ }),
147
+ );
148
+ return null;
149
+ }
150
+ }
24
151
 
25
152
  // One-time stderr notice when a plugin's hooks are refused at the COMPONENT
26
153
  // level because the plugin opted into the capability model but did not declare
@@ -71,11 +198,20 @@ function normalizeHookMap(parsed) {
71
198
  * @returns {Record<string, Array>} event name → concatenated hook entries
72
199
  */
73
200
  export function collectPluginHooks(opts = {}) {
201
+ const merged = {};
202
+ const authorityErrors = [];
74
203
  let plugins = [];
75
204
  try {
76
- plugins = discoverPlugins({ cwd: opts.cwd, scopes: opts.scopes });
77
- } catch {
78
- return {};
205
+ plugins = _deps.discoverPlugins({ cwd: opts.cwd, scopes: opts.scopes });
206
+ } catch (error) {
207
+ authorityErrors.push(
208
+ pluginAuthorityError({
209
+ code: "CC_PLUGIN_HOOK_DISCOVERY_FAILED",
210
+ error,
211
+ stage: "discover",
212
+ }),
213
+ );
214
+ return attachAuthorityErrors(merged, authorityErrors);
79
215
  }
80
216
  // A hook runs a shell command — gate it behind trust so a cloned repo's
81
217
  // project plugin can't run commands the moment the agent starts.
@@ -84,7 +220,6 @@ export function collectPluginHooks(opts = {}) {
84
220
  skipped.filter((p) => p.manifest?.components?.hooks).map((p) => p.name),
85
221
  "hooks",
86
222
  );
87
- const merged = {};
88
223
  const denied = [];
89
224
  for (const p of trusted) {
90
225
  if (!p.manifest || p.manifest.ok !== true) continue;
@@ -98,75 +233,92 @@ export function collectPluginHooks(opts = {}) {
98
233
  denied.push({ name: p.name, reason: denial.reason });
99
234
  continue;
100
235
  }
101
- let parsed;
102
- if (h.absPath) {
103
- try {
104
- parsed = JSON.parse(_deps.readFileSync(h.absPath, "utf8"));
105
- } catch {
106
- continue;
107
- }
108
- } else if (h.inline) {
109
- // Hooks declared inline in plugin.json — the normalized component keeps
110
- // only counts, so re-read the raw manifest for the actual entries (same
111
- // approach as the MCP collector). Without this, inline hooks never fire.
112
- try {
113
- const raw = JSON.parse(
114
- _deps.readFileSync(p.manifest.manifestPath, "utf8"),
115
- );
116
- parsed = raw && typeof raw === "object" ? raw.hooks : null;
117
- } catch {
118
- continue;
119
- }
120
- } else {
121
- continue;
122
- }
123
- const map = normalizeHookMap(parsed);
236
+ // Inline declarations are re-read from the manifest because discovery's
237
+ // normalized component retains only counts. Both source forms bind every
238
+ // executable hook to the exact bytes that authorized it.
239
+ const source = loadPluginHookSource(p, h, authorityErrors);
240
+ if (!source) continue;
241
+ const map = normalizeHookMap(source.parsed);
124
242
  for (const [event, entries] of Object.entries(map)) {
125
243
  if (!Array.isArray(entries)) continue;
126
- const tagged = entries.map((group) => {
127
- if (!group || typeof group !== "object" || !Array.isArray(group.hooks)) return group;
128
- let groupSandboxPolicy;
129
- try {
130
- groupSandboxPolicy = mergePluginSandboxPolicies(
131
- p.manifest.sandboxPolicy,
132
- group.sandboxPolicy,
133
- );
134
- } catch {
135
- return null;
136
- }
137
- const { sandboxPolicy: _rawGroupPolicy, ...groupConfig } = group;
138
- return {
139
- ...groupConfig,
140
- hooks: group.hooks
141
- .map((hook) => {
142
- if (!hook || typeof hook !== "object") return hook;
143
- let sandboxPolicy;
144
- try {
145
- sandboxPolicy = mergePluginSandboxPolicies(
146
- groupSandboxPolicy,
147
- hook.sandboxPolicy,
244
+ const tagged = entries
245
+ .map((group) => {
246
+ if (
247
+ !group ||
248
+ typeof group !== "object" ||
249
+ !Array.isArray(group.hooks)
250
+ ) {
251
+ return group;
252
+ }
253
+ let groupSandboxPolicy;
254
+ try {
255
+ groupSandboxPolicy = mergePluginSandboxPolicies(
256
+ p.manifest.sandboxPolicy,
257
+ group.sandboxPolicy,
258
+ );
259
+ } catch (error) {
260
+ authorityErrors.push(
261
+ pluginAuthorityError({
262
+ code: "CC_PLUGIN_HOOK_SANDBOX_INVALID",
263
+ error,
264
+ plugin: p,
265
+ sourceFile: source.sourceFile,
266
+ digest: source.digest,
267
+ stage: "sandbox-policy",
268
+ }),
269
+ );
270
+ return null;
271
+ }
272
+ const groupConfig = { ...group };
273
+ delete groupConfig.sandboxPolicy;
274
+ return {
275
+ ...groupConfig,
276
+ hooks: group.hooks
277
+ .map((hook) => {
278
+ if (!hook || typeof hook !== "object") return hook;
279
+ let sandboxPolicy;
280
+ try {
281
+ sandboxPolicy = mergePluginSandboxPolicies(
282
+ groupSandboxPolicy,
283
+ hook.sandboxPolicy,
284
+ );
285
+ } catch (error) {
286
+ authorityErrors.push(
287
+ pluginAuthorityError({
288
+ code: "CC_PLUGIN_HOOK_SANDBOX_INVALID",
289
+ error,
290
+ plugin: p,
291
+ sourceFile: source.sourceFile,
292
+ digest: source.digest,
293
+ stage: "sandbox-policy",
294
+ }),
295
+ );
296
+ return null;
297
+ }
298
+ const hookConfig = { ...hook };
299
+ delete hookConfig.sandboxPolicy;
300
+ delete hookConfig.authoritySource;
301
+ return attachPluginAuthoritySource(
302
+ {
303
+ ...hookConfig,
304
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
305
+ origin: "plugin:hook",
306
+ pluginId: p.name,
307
+ pluginVersion: p.version || null,
308
+ pluginSource: p.manifest?.manifestPath || null,
309
+ },
310
+ source,
148
311
  );
149
- } catch {
150
- return null;
151
- }
152
- const { sandboxPolicy: _rawHookPolicy, ...hookConfig } = hook;
153
- return {
154
- ...hookConfig,
155
- ...(sandboxPolicy ? { sandboxPolicy } : {}),
156
- origin: "plugin:hook",
157
- pluginId: p.name,
158
- pluginVersion: p.version || null,
159
- pluginSource: p.manifest?.manifestPath || null,
160
- };
161
- })
162
- .filter(Boolean),
163
- };
164
- }).filter(Boolean);
312
+ })
313
+ .filter(Boolean),
314
+ };
315
+ })
316
+ .filter(Boolean);
165
317
  merged[event] = (merged[event] || []).concat(tagged);
166
318
  }
167
319
  }
168
320
  warnHookCapabilityDeniedOnce(denied);
169
- return merged;
321
+ return attachAuthorityErrors(merged, authorityErrors);
170
322
  }
171
323
 
172
324
  /**
@@ -179,7 +331,10 @@ export function collectPluginHooks(opts = {}) {
179
331
  export function mergePluginHooks(settingsHooks, opts = {}) {
180
332
  const plugin = collectPluginHooks(opts);
181
333
  const events = Object.keys(plugin);
182
- if (events.length === 0) return settingsHooks;
334
+ const pluginAuthorityErrors = plugin._authorityErrors || [];
335
+ if (events.length === 0 && pluginAuthorityErrors.length === 0) {
336
+ return settingsHooks;
337
+ }
183
338
  const out =
184
339
  settingsHooks && typeof settingsHooks === "object"
185
340
  ? { ...settingsHooks }
@@ -189,5 +344,8 @@ export function mergePluginHooks(settingsHooks, opts = {}) {
189
344
  plugin[event],
190
345
  );
191
346
  }
192
- return out;
347
+ return attachAuthorityErrors(out, [
348
+ ...(settingsHooks?._authorityErrors || []),
349
+ ...pluginAuthorityErrors,
350
+ ]);
193
351
  }
@@ -27,11 +27,7 @@ import {
27
27
  validateOptions,
28
28
  redactSensitiveOptions,
29
29
  } from "./capabilities.js";
30
- import {
31
- createSecretStore,
32
- isSecretRef,
33
- secretRef,
34
- } from "../secret-store.js";
30
+ import { createSecretStore, isSecretRef, secretRef } from "../secret-store.js";
35
31
  import { withFileLock } from "../with-file-lock.js";
36
32
 
37
33
  export const _deps = {
@@ -91,6 +87,69 @@ function withOptionStoreLock(p, body) {
91
87
  return _deps.withFileLock(p, body, { failIfUnavailable: true });
92
88
  }
93
89
 
90
+ function hashSecretNamespace(value, length = 64) {
91
+ return crypto
92
+ .createHash("sha256")
93
+ .update(String(value))
94
+ .digest("hex")
95
+ .slice(0, length);
96
+ }
97
+
98
+ function normalizedStoreIdentity(storePath) {
99
+ const resolved = path.resolve(String(storePath));
100
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
101
+ }
102
+
103
+ function pluginSecretRefPrefix(storePath, name, key) {
104
+ const storeNamespace = hashSecretNamespace(
105
+ normalizedStoreIdentity(storePath),
106
+ 24,
107
+ );
108
+ const optionNamespace = hashSecretNamespace(
109
+ JSON.stringify([String(name), String(key)]),
110
+ );
111
+ return `plugin-options/${storeNamespace}/${optionNamespace}/`;
112
+ }
113
+
114
+ function createPluginSecretRef(storePath, name, key) {
115
+ return `${pluginSecretRefPrefix(storePath, name, key)}${crypto.randomUUID()}`;
116
+ }
117
+
118
+ function isOwnedPluginSecretRef(ref, storePath, name, key) {
119
+ if (typeof ref !== "string") return false;
120
+ return (
121
+ ref === `${name}/${key}` ||
122
+ ref.startsWith(pluginSecretRefPrefix(storePath, name, key))
123
+ );
124
+ }
125
+
126
+ function collectSecretRefs(value, refs = new Set()) {
127
+ if (isSecretRef(value)) {
128
+ refs.add(value.__cc_secret_ref);
129
+ return refs;
130
+ }
131
+ if (Array.isArray(value)) {
132
+ for (const entry of value) collectSecretRefs(entry, refs);
133
+ return refs;
134
+ }
135
+ if (value && typeof value === "object") {
136
+ for (const child of Object.values(value)) collectSecretRefs(child, refs);
137
+ }
138
+ return refs;
139
+ }
140
+
141
+ function deleteSecretRefsBestEffort(store, refs) {
142
+ if (!store || typeof store.delete !== "function") return;
143
+ for (const ref of refs) {
144
+ try {
145
+ store.delete(ref);
146
+ } catch {
147
+ // Metadata is authoritative. A stale keychain entry is preferable to
148
+ // rolling back a successfully committed plugin option document.
149
+ }
150
+ }
151
+ }
152
+
94
153
  /** Load a plugin's option VALUES at the given scope (`user` | `project`). */
95
154
  export function loadPluginOptionValues(
96
155
  name,
@@ -121,66 +180,152 @@ export function loadPluginOptionValues(
121
180
  return out;
122
181
  }
123
182
 
124
- /** Persist a plugin's option VALUES at a scope (replaces the plugin's entry). */
125
- export function setPluginOptionValues(
183
+ function persistPluginOptionValuesLocked(
184
+ p,
126
185
  name,
127
186
  values,
128
187
  scope,
129
- { cwd, schema, secretStore } = {},
188
+ { store, schema, secretStore, touchedKeys = null },
130
189
  ) {
131
- const p =
132
- scope === "project" ? _deps.projectStorePath(cwd) : _deps.userStorePath();
133
- return withOptionStoreLock(p, () => {
134
- const store = readJsonObject(p, { failIfUnavailable: true });
135
- const input = values && typeof values === "object" ? values : {};
136
- const persisted = { ...input };
137
- const normalized = schema || {};
138
- let secrets = null;
139
- const getSecrets = () => {
140
- if (!secrets) secrets = (secretStore || _deps.secretStore)();
141
- return secrets;
142
- };
143
- const rejectedSensitive = [];
190
+ const previous =
191
+ store[name] &&
192
+ typeof store[name] === "object" &&
193
+ !Array.isArray(store[name])
194
+ ? store[name]
195
+ : {};
196
+ const input = values && typeof values === "object" ? values : {};
197
+ const persisted = { ...input };
198
+ const normalized = schema || {};
199
+ let secrets = null;
200
+ const getSecrets = () => {
201
+ if (!secrets) secrets = (secretStore || _deps.secretStore)();
202
+ return secrets;
203
+ };
204
+ const rejectedSensitive = [];
205
+ const stagedSecretRefs = [];
206
+ const replacedSecretRefs = new Set();
207
+ try {
144
208
  for (const [key, desc] of Object.entries(normalized)) {
145
- if (
146
- !desc?.sensitive ||
147
- !Object.prototype.hasOwnProperty.call(input, key)
148
- ) {
149
- continue;
150
- }
151
- // Project files are shareable and must never receive a sensitive value.
209
+ if (!desc?.sensitive) continue;
210
+ const hasInput = Object.prototype.hasOwnProperty.call(input, key);
211
+ const previousRef = isSecretRef(previous[key])
212
+ ? previous[key].__cc_secret_ref
213
+ : null;
214
+
215
+ // Project files are shareable and must never retain a sensitive value.
216
+ // This gate also sanitizes an existing invalid value during an unrelated
217
+ // patch, so patching cannot accidentally preserve project-scope secrets.
152
218
  if (scope === "project") {
153
- delete persisted[key];
154
- rejectedSensitive.push(key);
219
+ if (hasInput) {
220
+ delete persisted[key];
221
+ rejectedSensitive.push(key);
222
+ }
155
223
  continue;
156
224
  }
157
- if (input[key] == null || input[key] === "") {
225
+
226
+ // A patch leaves untouched secret references byte-for-byte. Only keys in
227
+ // the patch participate in the secret-store transaction.
228
+ if (touchedKeys && !touchedKeys.has(key)) continue;
229
+
230
+ if (!hasInput || input[key] == null || input[key] === "") {
158
231
  delete persisted[key];
159
- try {
160
- getSecrets().delete(`${name}/${key}`);
161
- } catch {
162
- // Missing secrets already behave as absent on read.
163
- }
164
- continue;
232
+ } else {
233
+ // Every write gets a new immutable reference. Namespaces are hashes of
234
+ // the option-store path and the unambiguous [plugin, key] tuple, so
235
+ // names containing '/' cannot collide with another plugin/key pair.
236
+ const ref = createPluginSecretRef(p, name, key);
237
+ stagedSecretRefs.push(ref);
238
+ getSecrets().set(ref, input[key]);
239
+ persisted[key] = secretRef(ref);
240
+ }
241
+
242
+ if (previousRef && isOwnedPluginSecretRef(previousRef, p, name, key)) {
243
+ replacedSecretRefs.add(previousRef);
165
244
  }
166
- getSecrets().set(`${name}/${key}`, input[key]);
167
- persisted[key] = secretRef(`${name}/${key}`);
168
245
  }
169
246
  if (rejectedSensitive.length > 0) {
170
247
  persisted.__cc_rejected_sensitive = [
171
248
  ...new Set(rejectedSensitive),
172
249
  ].sort();
173
250
  } else if (
174
- Object.prototype.hasOwnProperty.call(
175
- persisted,
176
- "__cc_rejected_sensitive",
177
- )
251
+ Object.prototype.hasOwnProperty.call(persisted, "__cc_rejected_sensitive")
178
252
  ) {
179
253
  delete persisted.__cc_rejected_sensitive;
180
254
  }
181
255
  store[name] = persisted;
182
256
  writeJsonObject(p, store);
183
- return { name, scope, path: p };
257
+ } catch (error) {
258
+ // The old metadata is still authoritative. Remove every newly staged
259
+ // value so a failed JSON save cannot leak an orphaned credential.
260
+ deleteSecretRefsBestEffort(secrets, stagedSecretRefs);
261
+ throw error;
262
+ }
263
+
264
+ const activeSecretRefs = collectSecretRefs(store);
265
+ const staleSecretRefs = [...replacedSecretRefs].filter(
266
+ (ref) => !activeSecretRefs.has(ref),
267
+ );
268
+ if (staleSecretRefs.length > 0) {
269
+ try {
270
+ deleteSecretRefsBestEffort(getSecrets(), staleSecretRefs);
271
+ } catch {
272
+ // The JSON commit succeeded; unavailable cleanup must not undo it.
273
+ }
274
+ }
275
+ return { name, scope, path: p };
276
+ }
277
+
278
+ /** Persist a plugin's option VALUES at a scope (replaces the plugin's entry). */
279
+ export function setPluginOptionValues(
280
+ name,
281
+ values,
282
+ scope,
283
+ { cwd, schema, secretStore } = {},
284
+ ) {
285
+ const p =
286
+ scope === "project" ? _deps.projectStorePath(cwd) : _deps.userStorePath();
287
+ return withOptionStoreLock(p, () => {
288
+ const store = readJsonObject(p, { failIfUnavailable: true });
289
+ return persistPluginOptionValuesLocked(p, name, values, scope, {
290
+ store,
291
+ schema,
292
+ secretStore,
293
+ });
294
+ });
295
+ }
296
+
297
+ /**
298
+ * Merge option updates under the same lock used for the durable JSON commit.
299
+ * This is the command-safe API: callers never perform a lock-free read/modify/
300
+ * write cycle that could overwrite a concurrent writer's unrelated keys.
301
+ */
302
+ export function patchPluginOptionValues(
303
+ name,
304
+ updates,
305
+ scope,
306
+ { cwd, schema, secretStore } = {},
307
+ ) {
308
+ const p =
309
+ scope === "project" ? _deps.projectStorePath(cwd) : _deps.userStorePath();
310
+ return withOptionStoreLock(p, () => {
311
+ const store = readJsonObject(p, { failIfUnavailable: true });
312
+ const previous =
313
+ store[name] &&
314
+ typeof store[name] === "object" &&
315
+ !Array.isArray(store[name])
316
+ ? store[name]
317
+ : {};
318
+ const patch =
319
+ updates && typeof updates === "object" && !Array.isArray(updates)
320
+ ? updates
321
+ : {};
322
+ const values = { ...previous, ...patch };
323
+ return persistPluginOptionValuesLocked(p, name, values, scope, {
324
+ store,
325
+ schema,
326
+ secretStore,
327
+ touchedKeys: new Set(Object.keys(patch)),
328
+ });
184
329
  });
185
330
  }
186
331