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
@@ -2,35 +2,214 @@ import {
2
2
  readFileSync,
3
3
  writeFileSync,
4
4
  existsSync,
5
- mkdirSync,
6
5
  renameSync,
7
6
  unlinkSync,
8
7
  copyFileSync,
8
+ lstatSync,
9
+ constants as fsConstants,
9
10
  } from "node:fs";
11
+ import { createHash, randomUUID } from "node:crypto";
10
12
  import { dirname } from "node:path";
11
13
  import { getConfigPath } from "./paths.js";
12
14
  import { DEFAULT_CONFIG } from "../constants.js";
13
15
  import { withFileLock } from "./with-file-lock.js";
16
+ import {
17
+ coerceConfigValue,
18
+ isSecretConfigKey,
19
+ validateConfigValue,
20
+ } from "./config-schema.js";
21
+ import { createSecretStore, isSecretRef, secretRef } from "./secret-store.js";
22
+ import {
23
+ ensurePrivateDirectory,
24
+ ensurePrivateFile,
25
+ PRIVATE_FILE_MODE,
26
+ } from "./secure-fs.js";
14
27
 
15
28
  // Warn at most once per config path per process. Injectable seam: the default
16
29
  // stays silent under vitest so test output isn't polluted; tests override
17
30
  // `_deps.warn` to assert it fired.
18
31
  const _warnedConfigPaths = new Set();
32
+ const _loadedSecretRefs = new Map();
19
33
  export const _deps = {
34
+ createSecretStore: () => createSecretStore(),
20
35
  warn: (msg) => {
21
36
  if (!process.env.VITEST) process.stderr.write(msg);
22
37
  },
23
38
  };
24
39
 
25
- export function loadConfig({ failIfUnavailable = false } = {}) {
40
+ const FORBIDDEN_CONFIG_SEGMENTS = new Set([
41
+ "__proto__",
42
+ "prototype",
43
+ "constructor",
44
+ ]);
45
+
46
+ function assertSafeConfigObject(value, prefix = "") {
47
+ if (!value || typeof value !== "object") return;
48
+ if (Array.isArray(value)) {
49
+ value.forEach((entry, index) =>
50
+ assertSafeConfigObject(entry, `${prefix}.${index}`),
51
+ );
52
+ return;
53
+ }
54
+ for (const [key, child] of Object.entries(value)) {
55
+ const path = prefix ? `${prefix}.${key}` : key;
56
+ if (FORBIDDEN_CONFIG_SEGMENTS.has(key)) {
57
+ const error = new Error(`Invalid configuration key: ${path}`);
58
+ error.code = "CONFIG_KEY_INVALID";
59
+ throw error;
60
+ }
61
+ assertSafeConfigObject(child, path);
62
+ }
63
+ }
64
+
65
+ function assertNotSymbolicLink(target, label = "configuration path") {
66
+ const stat = lstatSync(target);
67
+ if (stat.isSymbolicLink()) {
68
+ const error = new Error(`Refusing symbolic link for ${label}: ${target}`);
69
+ error.code = "CONFIG_PATH_SYMLINK";
70
+ throw error;
71
+ }
72
+ return stat;
73
+ }
74
+
75
+ function readConfigDocument({ failIfUnavailable = false } = {}) {
76
+ const configPath = getConfigPath();
77
+ if (!existsSync(configPath)) return {};
78
+ try {
79
+ assertNotSymbolicLink(configPath, "config.json");
80
+ const parsed = JSON.parse(readFileSync(configPath, "utf-8"));
81
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
82
+ throw new Error("configuration root must be an object");
83
+ }
84
+ assertSafeConfigObject(parsed);
85
+ return parsed;
86
+ } catch (err) {
87
+ if (failIfUnavailable) {
88
+ const error = new Error(
89
+ `Could not read durable config at ${configPath}: ${err.message}`,
90
+ { cause: err },
91
+ );
92
+ error.code = "CONFIG_STORE_UNAVAILABLE";
93
+ throw error;
94
+ }
95
+ throw err;
96
+ }
97
+ }
98
+
99
+ export function loadUserConfig(options = {}) {
100
+ return readConfigDocument(options);
101
+ }
102
+
103
+ function secretRefCacheKey(configPath, key) {
104
+ return `${configPath}\0${key}`;
105
+ }
106
+
107
+ function createSecretStorageRef(configPath, key) {
108
+ const namespace = createHash("sha256")
109
+ .update(String(configPath))
110
+ .digest("hex")
111
+ .slice(0, 24);
112
+ const encodedKey = Buffer.from(String(key), "utf8").toString("base64url");
113
+ return `config/${namespace}/${encodedKey}/${randomUUID()}`;
114
+ }
115
+
116
+ function deleteSecretRefBestEffort(store, ref, context) {
117
+ if (!store || !ref || typeof store.delete !== "function") return;
118
+ try {
119
+ if (store.delete(ref) === false) {
120
+ _deps.warn(
121
+ `Warning: could not remove stale OS secret reference (${context}).\n`,
122
+ );
123
+ }
124
+ } catch (error) {
125
+ _deps.warn(
126
+ `Warning: could not remove stale OS secret reference (${context}: ${error.message}).\n`,
127
+ );
128
+ }
129
+ }
130
+
131
+ function resolveSecretReferences(value, prefix = "", state = {}) {
132
+ if (isSecretRef(value)) {
133
+ if (!isSecretConfigKey(prefix)) {
134
+ const error = new Error(
135
+ `Secret reference is not allowed at non-secret key: ${prefix || "(root)"}`,
136
+ );
137
+ error.code = "CONFIG_SECRET_REF_INVALID";
138
+ throw error;
139
+ }
140
+ const ref = value.__cc_secret_ref;
141
+ try {
142
+ state.store ||= _deps.createSecretStore();
143
+ const resolved = state.store.get(ref);
144
+ if (resolved == null && !state.warned) {
145
+ state.warned = true;
146
+ _deps.warn(
147
+ `Warning: an OS-backed configuration secret is missing (${prefix}).\n`,
148
+ );
149
+ }
150
+ _loadedSecretRefs.set(secretRefCacheKey(state.configPath, prefix), {
151
+ ref,
152
+ resolved,
153
+ });
154
+ return resolved;
155
+ } catch (error) {
156
+ _loadedSecretRefs.set(secretRefCacheKey(state.configPath, prefix), {
157
+ ref,
158
+ resolved: null,
159
+ });
160
+ if (!state.warned) {
161
+ state.warned = true;
162
+ _deps.warn(
163
+ `Warning: an OS-backed configuration secret could not be read (${error.message}).\n`,
164
+ );
165
+ }
166
+ return null;
167
+ }
168
+ }
169
+ if (Array.isArray(value)) {
170
+ return value.map((entry, index) =>
171
+ resolveSecretReferences(entry, `${prefix}.${index}`, state),
172
+ );
173
+ }
174
+ if (value && typeof value === "object") {
175
+ const out = {};
176
+ for (const [key, child] of Object.entries(value)) {
177
+ const childKey = prefix ? `${prefix}.${key}` : key;
178
+ out[key] = resolveSecretReferences(child, childKey, state);
179
+ }
180
+ return out;
181
+ }
182
+ return value;
183
+ }
184
+
185
+ function restoreSecretReferences(config) {
186
+ const out = structuredClone(config);
187
+ const prefix = `${getConfigPath()}\0`;
188
+ for (const [cacheKey, saved] of _loadedSecretRefs) {
189
+ if (!cacheKey.startsWith(prefix)) continue;
190
+ const key = cacheKey.slice(prefix.length);
191
+ const current = getNestedValue(out, key);
192
+ if (current === saved.resolved || isSecretRef(current)) {
193
+ setNestedValue(out, key, secretRef(saved.ref));
194
+ }
195
+ }
196
+ return out;
197
+ }
198
+
199
+ export function loadConfig({
200
+ failIfUnavailable = false,
201
+ resolveSecrets = true,
202
+ } = {}) {
26
203
  const configPath = getConfigPath();
27
204
  if (!existsSync(configPath)) {
28
205
  return { ...structuredClone(DEFAULT_CONFIG) };
29
206
  }
30
207
  try {
31
- const raw = readFileSync(configPath, "utf-8");
32
- const parsed = JSON.parse(raw);
33
- return deepMerge(structuredClone(DEFAULT_CONFIG), parsed);
208
+ const parsed = readConfigDocument({ failIfUnavailable });
209
+ const effective = deepMerge(structuredClone(DEFAULT_CONFIG), parsed);
210
+ return resolveSecrets
211
+ ? resolveSecretReferences(effective, "", { configPath })
212
+ : effective;
34
213
  } catch (err) {
35
214
  if (failIfUnavailable) {
36
215
  const error = new Error(
@@ -55,8 +234,18 @@ export function loadConfig({ failIfUnavailable = false } = {}) {
55
234
  // Fixed sibling name (no timestamp) so repeated runs don't accumulate.
56
235
  let backupNote = "";
57
236
  try {
58
- copyFileSync(configPath, `${configPath}.corrupted`);
59
- backupNote = ` A copy was saved to ${configPath}.corrupted.`;
237
+ assertNotSymbolicLink(configPath, "corrupted config source");
238
+ const backupPath = `${configPath}.corrupted`;
239
+ if (existsSync(backupPath)) {
240
+ assertNotSymbolicLink(backupPath, "corrupted config backup");
241
+ } else {
242
+ copyFileSync(configPath, backupPath, fsConstants.COPYFILE_EXCL);
243
+ }
244
+ ensurePrivateFile(backupPath, {
245
+ applyWindowsAcl: true,
246
+ failIfUnavailable: true,
247
+ });
248
+ backupNote = ` A copy is available at ${backupPath}.`;
60
249
  } catch {
61
250
  /* backup is best-effort — the warning still fires without it */
62
251
  }
@@ -70,9 +259,12 @@ export function loadConfig({ failIfUnavailable = false } = {}) {
70
259
  }
71
260
  }
72
261
 
73
- export function saveConfig(config) {
262
+ export function saveConfig(config, options = {}) {
74
263
  const configPath = getConfigPath();
75
- mkdirSync(dirname(configPath), { recursive: true });
264
+ ensurePrivateDirectory(dirname(configPath), {
265
+ applyWindowsAcl: true,
266
+ failIfUnavailable: true,
267
+ });
76
268
  // Atomic write: config.json holds API keys + LLM settings, so a crash (or two
77
269
  // concurrent `cc config set`) mid-write must never leave a truncated/corrupt
78
270
  // file — that would break every cc command and could lose credentials. Write a
@@ -81,8 +273,26 @@ export function saveConfig(config) {
81
273
  // one, never a half-written one.
82
274
  const tmp = `${configPath}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
83
275
  try {
84
- writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n", "utf-8");
276
+ const persisted =
277
+ options.preserveSecretRefs === false
278
+ ? structuredClone(config)
279
+ : restoreSecretReferences(config);
280
+ assertSafeConfigObject(persisted);
281
+ writeFileSync(tmp, JSON.stringify(persisted, null, 2) + "\n", {
282
+ encoding: "utf-8",
283
+ mode: PRIVATE_FILE_MODE,
284
+ });
85
285
  renameWithRetry(tmp, configPath);
286
+ // POSIX needs an explicit 0600 chmod. On Windows the temp file was created
287
+ // after the parent DACL was repaired above, so it inherits that owner-only
288
+ // ACL through the atomic rename without a second PowerShell round-trip.
289
+ ensurePrivateFile(configPath);
290
+ if (options.preserveSecretRefs === false) {
291
+ const prefix = `${configPath}\0`;
292
+ for (const cacheKey of _loadedSecretRefs.keys()) {
293
+ if (cacheKey.startsWith(prefix)) _loadedSecretRefs.delete(cacheKey);
294
+ }
295
+ }
86
296
  } catch (err) {
87
297
  try {
88
298
  if (existsSync(tmp)) unlinkSync(tmp);
@@ -93,6 +303,32 @@ export function saveConfig(config) {
93
303
  }
94
304
  }
95
305
 
306
+ /**
307
+ * Create a stable, owner-only snapshot before an in-place schema migration.
308
+ * The first snapshot wins so a repeated `validate --fix` cannot overwrite the
309
+ * last known pre-migration document with an already-migrated copy.
310
+ */
311
+ export function backupConfigForMigration(
312
+ suffix = `.before-schema-migration`,
313
+ options = {},
314
+ ) {
315
+ const configPath = getConfigPath();
316
+ if (!existsSync(configPath)) return null;
317
+ assertNotSymbolicLink(configPath, "config.json migration source");
318
+ const backupPath = `${configPath}${suffix}`;
319
+ if (existsSync(backupPath)) {
320
+ assertNotSymbolicLink(backupPath, "config migration backup");
321
+ } else {
322
+ copyFileSync(configPath, backupPath, fsConstants.COPYFILE_EXCL);
323
+ }
324
+ ensurePrivateFile(backupPath, {
325
+ applyWindowsAcl: options.applyWindowsAcl !== false,
326
+ failIfUnavailable: options.failIfUnavailable !== false,
327
+ ...(options.secureOptions || {}),
328
+ });
329
+ return backupPath;
330
+ }
331
+
96
332
  // On Windows, `rename` over a target that another process currently has open
97
333
  // (e.g. a concurrent `loadConfig` reader) fails with EPERM/EACCES/EBUSY instead
98
334
  // of POSIX's silent atomic replace. Under bursts of concurrent `cc config`/`cc
@@ -134,23 +370,36 @@ function sleepSync(ms) {
134
370
  }
135
371
  }
136
372
 
137
- export function getConfigValue(key) {
138
- const config = loadConfig();
373
+ export function getConfigValue(key, options = {}) {
374
+ const config = loadConfig(options);
139
375
  return getNestedValue(config, key);
140
376
  }
141
377
 
142
- export function setConfigValue(key, value) {
378
+ export function setConfigValue(key, value, options = {}) {
379
+ if (isSecretConfigKey(key) && options.allowSecret !== true) {
380
+ const error = new Error(
381
+ `Refusing a secret on the command line for ${key}; use "cc config set-secret ${key}"`,
382
+ );
383
+ error.code = "CONFIG_SECRET_REQUIRES_SECURE_INPUT";
384
+ throw error;
385
+ }
386
+ const parsedValue = coerceConfigValue(key, value, {
387
+ allowUnknown: options.allowUnknown === true,
388
+ });
143
389
  // Serialize the read-modify-write across processes: two concurrent
144
390
  // `cc config set` invocations would otherwise each load, mutate, and write
145
391
  // back, silently losing one update. Configuration can carry credentials and
146
392
  // permission policy, so bounded lock failure must never write unlocked.
147
393
  const configPath = getConfigPath();
148
- mkdirSync(dirname(configPath), { recursive: true });
394
+ ensurePrivateDirectory(dirname(configPath), {
395
+ applyWindowsAcl: true,
396
+ failIfUnavailable: true,
397
+ });
149
398
  return withFileLock(
150
399
  configPath,
151
400
  () => {
152
401
  const config = loadConfig({ failIfUnavailable: true });
153
- setNestedValue(config, key, parseValue(value));
402
+ setNestedValue(config, key, parsedValue);
154
403
  saveConfig(config);
155
404
  return config;
156
405
  },
@@ -158,10 +407,189 @@ export function setConfigValue(key, value) {
158
407
  );
159
408
  }
160
409
 
410
+ /** Run a synchronous operation while holding the cross-process config lock. */
411
+ export function withConfigLock(callback) {
412
+ if (typeof callback !== "function") {
413
+ throw new TypeError("Config lock callback must be a function");
414
+ }
415
+ const configPath = getConfigPath();
416
+ ensurePrivateDirectory(dirname(configPath), {
417
+ applyWindowsAcl: true,
418
+ failIfUnavailable: true,
419
+ });
420
+ return withFileLock(configPath, callback, { failIfUnavailable: true });
421
+ }
422
+
423
+ /** Serialize an arbitrary in-process config mutation under the config lock. */
424
+ export function updateConfigAtomically(mutator) {
425
+ if (typeof mutator !== "function") {
426
+ throw new TypeError("Config mutator must be a function");
427
+ }
428
+ return withConfigLock(() => {
429
+ const config = loadConfig({ failIfUnavailable: true });
430
+ mutator(config);
431
+ saveConfig(config);
432
+ return config;
433
+ });
434
+ }
435
+
436
+ /**
437
+ * Persist a secret without ever accepting it as a positional CLI argument.
438
+ * `auto` prefers the OS store and falls back to the owner-only config file.
439
+ */
440
+ export function setSecretConfigValue(key, value, options = {}) {
441
+ if (!isSecretConfigKey(key)) {
442
+ const error = new Error(
443
+ `${key} is not declared as a secret configuration key`,
444
+ );
445
+ error.code = "CONFIG_KEY_NOT_SECRET";
446
+ throw error;
447
+ }
448
+ const issues = validateConfigValue(key, value);
449
+ if (issues.length)
450
+ throw Object.assign(new Error(issues[0].message), issues[0]);
451
+ const storage = options.storage || "auto";
452
+ if (!new Set(["auto", "keychain", "file"]).has(storage)) {
453
+ throw new Error("Secret storage must be auto, keychain, or file");
454
+ }
455
+ const configPath = getConfigPath();
456
+ ensurePrivateDirectory(dirname(configPath), {
457
+ applyWindowsAcl: true,
458
+ failIfUnavailable: true,
459
+ });
460
+ return withFileLock(
461
+ configPath,
462
+ () => {
463
+ const config = loadConfig({
464
+ failIfUnavailable: true,
465
+ resolveSecrets: false,
466
+ });
467
+ const previousValue = getNestedValue(config, key);
468
+ const previousRef = isSecretRef(previousValue)
469
+ ? previousValue.__cc_secret_ref
470
+ : null;
471
+ if (typeof options.configMutator === "function") {
472
+ options.configMutator(config);
473
+ }
474
+ if (storage !== "file") {
475
+ // A new immutable reference makes the keychain write + config rename a
476
+ // small transaction: if persistence fails, the still-active old config
477
+ // can only resolve its old value, never the just-written one.
478
+ const ref = createSecretStorageRef(configPath, key);
479
+ let store;
480
+ try {
481
+ store = options.secretStore || _deps.createSecretStore();
482
+ store.set(ref, String(value));
483
+ } catch (error) {
484
+ if (storage === "keychain") {
485
+ const wrapped = new Error(
486
+ `OS secret store is unavailable for ${key}: ${error.message}`,
487
+ { cause: error },
488
+ );
489
+ wrapped.code = "CONFIG_SECRET_STORE_UNAVAILABLE";
490
+ throw wrapped;
491
+ }
492
+ store = null;
493
+ }
494
+ if (store) {
495
+ // Persistence failures are not evidence that the OS store is
496
+ // unavailable. Keep them outside the fallback catch so a disk or
497
+ // atomic-rename failure can never silently downgrade the just-stored
498
+ // secret to plaintext configuration.
499
+ setNestedValue(config, key, secretRef(ref));
500
+ try {
501
+ // This load kept every secret as its persisted reference, so bypass
502
+ // the resolved-value restoration cache and persist this exact raw
503
+ // document.
504
+ saveConfig(config, { preserveSecretRefs: false });
505
+ } catch (error) {
506
+ deleteSecretRefBestEffort(
507
+ store,
508
+ ref,
509
+ "rollback after config write",
510
+ );
511
+ throw error;
512
+ }
513
+ _loadedSecretRefs.set(secretRefCacheKey(configPath, key), {
514
+ ref,
515
+ resolved: String(value),
516
+ });
517
+ if (previousRef && previousRef !== ref) {
518
+ deleteSecretRefBestEffort(
519
+ store,
520
+ previousRef,
521
+ "replaced config secret",
522
+ );
523
+ }
524
+ return { key, storage: "keychain", backend: store.name };
525
+ }
526
+ }
527
+ setNestedValue(config, key, String(value));
528
+ saveConfig(config, { preserveSecretRefs: false });
529
+ if (previousRef) {
530
+ let previousStore = options.secretStore || null;
531
+ if (!previousStore) {
532
+ try {
533
+ previousStore = _deps.createSecretStore();
534
+ } catch {
535
+ previousStore = null;
536
+ }
537
+ }
538
+ deleteSecretRefBestEffort(
539
+ previousStore,
540
+ previousRef,
541
+ "moved config secret to file storage",
542
+ );
543
+ }
544
+ return { key, storage: "file", backend: null };
545
+ },
546
+ { failIfUnavailable: true },
547
+ );
548
+ }
549
+
161
550
  export function resetConfig() {
162
- const config = structuredClone(DEFAULT_CONFIG);
163
- saveConfig(config);
164
- return config;
551
+ const configPath = getConfigPath();
552
+ ensurePrivateDirectory(dirname(configPath), {
553
+ applyWindowsAcl: true,
554
+ failIfUnavailable: true,
555
+ });
556
+ return withFileLock(
557
+ configPath,
558
+ () => {
559
+ const current = loadConfig({
560
+ failIfUnavailable: true,
561
+ resolveSecrets: false,
562
+ });
563
+ const refs = [];
564
+ const collectRefs = (value) => {
565
+ if (isSecretRef(value)) {
566
+ refs.push(value.__cc_secret_ref);
567
+ return;
568
+ }
569
+ if (!value || typeof value !== "object") return;
570
+ for (const child of Object.values(value)) collectRefs(child);
571
+ };
572
+ collectRefs(current);
573
+
574
+ const config = structuredClone(DEFAULT_CONFIG);
575
+ saveConfig(config, { preserveSecretRefs: false });
576
+ if (refs.length > 0) {
577
+ let store = null;
578
+ try {
579
+ store = _deps.createSecretStore();
580
+ } catch (error) {
581
+ _deps.warn(
582
+ `Warning: config reset could not open the OS secret store (${error.message}).\n`,
583
+ );
584
+ }
585
+ for (const ref of new Set(refs)) {
586
+ deleteSecretRefBestEffort(store, ref, "config reset");
587
+ }
588
+ }
589
+ return config;
590
+ },
591
+ { failIfUnavailable: true },
592
+ );
165
593
  }
166
594
 
167
595
  export function listConfig() {
@@ -190,15 +618,6 @@ function setNestedValue(obj, key, value) {
190
618
  current[parts[parts.length - 1]] = value;
191
619
  }
192
620
 
193
- function parseValue(value) {
194
- if (value === "true") return true;
195
- if (value === "false") return false;
196
- if (value === "null") return null;
197
- const num = Number(value);
198
- if (!isNaN(num) && value.trim() !== "") return num;
199
- return value;
200
- }
201
-
202
621
  function deepMerge(target, source) {
203
622
  const result = { ...target };
204
623
  for (const key of Object.keys(source)) {
@@ -0,0 +1,57 @@
1
+ /** One redaction boundary for every configuration output surface. */
2
+ import { isSecretConfigKey } from "./config-schema.js";
3
+ import { redactSecrets } from "./secret-scan.js";
4
+ import { isSecretRef } from "./secret-store.js";
5
+
6
+ export const CONFIG_REDACTED = "[REDACTED]";
7
+ export const CONFIG_CONFIGURED = "<configured>";
8
+
9
+ export function redactConfigValue(key, value) {
10
+ const leaf = String(key || "")
11
+ .split(".")
12
+ .at(-1);
13
+ if (/^headersHelper$/i.test(leaf || "")) {
14
+ return value == null || value === "" ? value : CONFIG_CONFIGURED;
15
+ }
16
+ if (isSecretConfigKey(key) || isSecretRef(value)) {
17
+ return value == null || value === "" ? value : CONFIG_REDACTED;
18
+ }
19
+ if (typeof value === "string") return redactSecrets(value);
20
+ if (Array.isArray(value)) {
21
+ return value.map((entry, index) =>
22
+ redactConfigValue(`${key}.${index}`, entry),
23
+ );
24
+ }
25
+ if (value && typeof value === "object") {
26
+ return redactConfigObject(value, key);
27
+ }
28
+ return value;
29
+ }
30
+
31
+ export function redactConfigObject(config, prefix = "") {
32
+ if (!config || typeof config !== "object") return config;
33
+ if (Array.isArray(config)) {
34
+ return config.map((entry, index) =>
35
+ redactConfigValue(`${prefix}.${index}`, entry),
36
+ );
37
+ }
38
+ const out = {};
39
+ for (const [name, value] of Object.entries(config)) {
40
+ const key = prefix ? `${prefix}.${name}` : name;
41
+ // Defining an own data property avoids the legacy Object.prototype
42
+ // __proto__ setter while retaining a normal serializable object.
43
+ Object.defineProperty(out, name, {
44
+ value: redactConfigValue(key, value),
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ });
49
+ }
50
+ return out;
51
+ }
52
+
53
+ export function stringifyRedactedConfig(config, space = 2) {
54
+ return JSON.stringify(redactConfigObject(config), null, space);
55
+ }
56
+
57
+ export { isSecretConfigKey } from "./config-schema.js";