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
@@ -0,0 +1,853 @@
1
+ /**
2
+ * Versioned, machine-readable schema for ~/.chainlesschain/config.json.
3
+ *
4
+ * The descriptor table is the source of truth used by config set/validate,
5
+ * secret redaction, `config keys`, and the exported JSON Schema. Open maps are
6
+ * explicit: arbitrary top-level keys are rejected. Open maps such as
7
+ * `features.*` are declared as such; plugin keys remain closed until their
8
+ * owner calls registerPluginConfigSchema().
9
+ */
10
+ import { DEFAULT_CONFIG, LLM_PROVIDERS } from "../constants.js";
11
+
12
+ export const CONFIG_SCHEMA_VERSION = "1.0.0";
13
+ export const CONFIG_SCHEMA_ID =
14
+ "https://chainlesschain.com/schemas/cli-config/v1.json";
15
+
16
+ const descriptor = (key, type, options = {}) => ({
17
+ key,
18
+ type,
19
+ scope: "user",
20
+ managedLock: true,
21
+ ...options,
22
+ });
23
+
24
+ const DESCRIPTORS = [
25
+ descriptor("setupCompleted", "boolean", {
26
+ default: DEFAULT_CONFIG.setupCompleted,
27
+ description: "Whether initial CLI setup has completed",
28
+ }),
29
+ descriptor("completedAt", ["string", "null"], {
30
+ default: DEFAULT_CONFIG.completedAt,
31
+ description: "ISO timestamp at which setup completed",
32
+ }),
33
+ descriptor("edition", "string", {
34
+ enum: ["personal", "enterprise"],
35
+ default: DEFAULT_CONFIG.edition,
36
+ description: "ChainlessChain edition",
37
+ }),
38
+ descriptor("features", "object", {
39
+ default: DEFAULT_CONFIG.features,
40
+ open: true,
41
+ description: "Feature flag map",
42
+ }),
43
+ descriptor("paths.projectRoot", ["string", "null"], {
44
+ default: DEFAULT_CONFIG.paths.projectRoot,
45
+ description: "Override the detected project root",
46
+ }),
47
+ descriptor("paths.database", ["string", "null"], {
48
+ default: DEFAULT_CONFIG.paths.database,
49
+ description: "Override the SQLite database path",
50
+ }),
51
+ descriptor("llm.provider", "string", {
52
+ enum: [...Object.keys(LLM_PROVIDERS), "local", "llamacpp", "mediapipe"],
53
+ default: DEFAULT_CONFIG.llm.provider,
54
+ description: "LLM provider id",
55
+ }),
56
+ descriptor("llm.apiKey", ["string", "null"], {
57
+ default: DEFAULT_CONFIG.llm.apiKey,
58
+ secret: true,
59
+ description: "LLM provider API key",
60
+ }),
61
+ descriptor("llm.apiKeyHelper", ["string", "null"], {
62
+ description: "Command that prints an API key to stdout",
63
+ }),
64
+ descriptor("llm.baseUrl", "string", {
65
+ default: DEFAULT_CONFIG.llm.baseUrl,
66
+ description: "LLM provider API endpoint",
67
+ }),
68
+ descriptor("llm.model", "string", {
69
+ default: DEFAULT_CONFIG.llm.model,
70
+ description: "Default model id",
71
+ }),
72
+ descriptor("llm.visionModel", ["string", "null"], {
73
+ description: "Model used for image turns",
74
+ }),
75
+ descriptor("llm.fallbackModel", ["string", "null"], {
76
+ deprecated: true,
77
+ migration: "llm.fallbackModels",
78
+ description: "Legacy single fallback model",
79
+ }),
80
+ descriptor("llm.fallbackModels", ["array", "string"], {
81
+ description: "Ordered fallback model ids",
82
+ }),
83
+ descriptor("llm.preferAndroidLocal", "boolean", {
84
+ default: DEFAULT_CONFIG.llm.preferAndroidLocal,
85
+ description: "Prefer the Android local Ollama-compatible endpoint",
86
+ }),
87
+ descriptor("llm.pricing", "object", {
88
+ open: true,
89
+ description: "Per-model pricing overrides",
90
+ }),
91
+ descriptor("llm.streamStallTimeoutMs", "number", {
92
+ minimum: 0,
93
+ description: "Abort an inactive model stream after this many milliseconds",
94
+ }),
95
+ descriptor("llm.temperature", "number", {
96
+ minimum: 0,
97
+ maximum: 2,
98
+ description: "Default sampling temperature",
99
+ }),
100
+ descriptor("llm.maxTokens", "number", {
101
+ minimum: 1,
102
+ description: "Default maximum output tokens",
103
+ }),
104
+ descriptor("enterprise.serverUrl", ["string", "null"], {
105
+ default: DEFAULT_CONFIG.enterprise.serverUrl,
106
+ description: "Enterprise server URL",
107
+ }),
108
+ descriptor("enterprise.apiKey", ["string", "null"], {
109
+ default: DEFAULT_CONFIG.enterprise.apiKey,
110
+ secret: true,
111
+ description: "Enterprise API key",
112
+ }),
113
+ descriptor("enterprise.tenantId", ["string", "null"], {
114
+ default: DEFAULT_CONFIG.enterprise.tenantId,
115
+ description: "Enterprise tenant id",
116
+ }),
117
+ descriptor("services.autoStart", "boolean", {
118
+ default: DEFAULT_CONFIG.services.autoStart,
119
+ description: "Start backing services automatically",
120
+ }),
121
+ descriptor("services.dockerComposePath", ["string", "null"], {
122
+ default: DEFAULT_CONFIG.services.dockerComposePath,
123
+ description: "Path to a Docker Compose file",
124
+ }),
125
+ descriptor("update.channel", "string", {
126
+ enum: ["stable", "beta"],
127
+ default: DEFAULT_CONFIG.update.channel,
128
+ description: "CLI update channel",
129
+ }),
130
+ descriptor("update.autoCheck", "boolean", {
131
+ default: DEFAULT_CONFIG.update.autoCheck,
132
+ description: "Check for updates on startup",
133
+ }),
134
+ descriptor("cli.theme", "string", {
135
+ enum: ["auto", "dark", "light", "mono"],
136
+ description: "Interactive color theme",
137
+ }),
138
+ descriptor("cli.tuiMode", "string", {
139
+ enum: ["auto", "inline", "fullscreen"],
140
+ description: "Interactive TUI mode",
141
+ }),
142
+ descriptor("cli.fastMode", "boolean", {
143
+ description: "Prefer lower-latency model behavior",
144
+ }),
145
+ descriptor("cli.promptSuggestions", "boolean", {
146
+ default: DEFAULT_CONFIG.cli.promptSuggestions,
147
+ description: "Show background prompt suggestions in the interactive REPL",
148
+ }),
149
+ descriptor("cli.keybindings", "object", {
150
+ default: DEFAULT_CONFIG.cli.keybindings,
151
+ open: true,
152
+ description: "Interactive REPL action-to-key binding overrides",
153
+ }),
154
+ descriptor("voice", "object", {
155
+ default: DEFAULT_CONFIG.voice,
156
+ description: "Interactive voice-dictation settings",
157
+ }),
158
+ descriptor("voice.backends", "object", {
159
+ default: DEFAULT_CONFIG.voice.backends,
160
+ open: true,
161
+ description: "Available host speech-to-text backend capability map",
162
+ }),
163
+ descriptor("voice.allowCloud", "boolean", {
164
+ default: DEFAULT_CONFIG.voice.allowCloud,
165
+ description: "Allow cloud speech transcription after local-first probes",
166
+ }),
167
+ descriptor("context.autoPin", ["boolean", "object"], {
168
+ open: true,
169
+ description: "Keep the original task through context compaction",
170
+ }),
171
+ descriptor("permissions.askTimeoutMs", "number", {
172
+ minimum: 0,
173
+ description: "Idle timeout for interactive permission prompts",
174
+ }),
175
+ descriptor("webSearch", "object", {
176
+ description: "Web-search provider options",
177
+ }),
178
+ descriptor("webSearch.provider", "string", {
179
+ enum: [
180
+ "auto",
181
+ "tavily",
182
+ "brave",
183
+ "bocha",
184
+ "qianfan",
185
+ "duckduckgo",
186
+ "searxng",
187
+ "baidu",
188
+ ],
189
+ description: "Web-search provider",
190
+ }),
191
+ descriptor("webSearch.maxResults", "number", {
192
+ minimum: 1,
193
+ description: "Default maximum search result count",
194
+ }),
195
+ descriptor("webSearch.apiKey", ["string", "null"], {
196
+ secret: true,
197
+ description: "Web-search provider API key",
198
+ }),
199
+ ...["tavily", "brave", "bocha", "qianfan"].map((provider) =>
200
+ descriptor(`webSearch.${provider}ApiKey`, ["string", "null"], {
201
+ secret: true,
202
+ description: `${provider} web-search API key`,
203
+ }),
204
+ ),
205
+ descriptor("webSearch.instanceUrl", "string", {
206
+ description: "SearXNG instance URL",
207
+ }),
208
+ descriptor("webSearch.qianfanUrl", "string", {
209
+ description: "Qianfan search endpoint",
210
+ }),
211
+ descriptor("cloud", "object", {
212
+ open: true,
213
+ description: "Private cloud-runner options",
214
+ }),
215
+ descriptor("cloud.token", ["string", "null"], {
216
+ secret: true,
217
+ description: "Private cloud-runner bearer token",
218
+ }),
219
+ descriptor("channels", "object", {
220
+ open: true,
221
+ description: "Inbound channel configuration",
222
+ }),
223
+ descriptor("session", "object", {
224
+ open: true,
225
+ description: "Session persistence and mirror options",
226
+ }),
227
+ descriptor("plugins", "object", {
228
+ description: "Plugin-owned configuration namespace",
229
+ }),
230
+ descriptor("plugins.registryTokens", "object", {
231
+ open: true,
232
+ secretChildren: true,
233
+ description: "Per-registry authentication tokens",
234
+ }),
235
+ descriptor("hub", "object", {
236
+ open: true,
237
+ description: "Hub service configuration",
238
+ }),
239
+ descriptor("hub.llm", ["string", "object"], {
240
+ open: true,
241
+ description: "Hub LLM routing configuration",
242
+ }),
243
+ descriptor("hub.llm.apiKey", ["string", "null"], {
244
+ secret: true,
245
+ description: "Hub-specific LLM API key",
246
+ }),
247
+ descriptor("hub.llm.provider", "string", {
248
+ description: "Hub-specific LLM provider",
249
+ }),
250
+ descriptor("hub.llm.model", "string", {
251
+ description: "Hub-specific LLM model",
252
+ }),
253
+ descriptor("hub.llm.baseUrl", "string", {
254
+ description: "Hub-specific LLM endpoint",
255
+ }),
256
+ descriptor("hub.llm.apiKeyEnv", "string", {
257
+ description: "Environment variable containing the hub LLM API key",
258
+ }),
259
+ descriptor("remoteControl", "object", {
260
+ open: true,
261
+ description: "Remote-control connection settings",
262
+ }),
263
+ descriptor("credentialProxy", "object", {
264
+ description: "Credential proxy policy",
265
+ }),
266
+ descriptor("credentialProxy.enabled", "boolean", {
267
+ description: "Mask credentials inherited by child processes",
268
+ }),
269
+ descriptor("credentialProxy.mode", "string", {
270
+ enum: ["mask", "deny"],
271
+ description: "Mask or remove credential variables",
272
+ }),
273
+ descriptor("credentialProxy.allow", "array", {
274
+ description: "Credential environment variable allowlist",
275
+ }),
276
+ descriptor("credentialProxy.deny", "array", {
277
+ description: "Additional credential environment variables to mask",
278
+ }),
279
+ descriptor("advisor.enabled", "boolean", {
280
+ default: DEFAULT_CONFIG.advisor.enabled,
281
+ description: "Enable the independent advisor/critic pass",
282
+ }),
283
+ descriptor("advisor.provider", ["string", "null"], {
284
+ description: "Advisor-specific LLM provider",
285
+ }),
286
+ descriptor("advisor.model", ["string", "null"], {
287
+ description: "Advisor-specific model",
288
+ }),
289
+ descriptor("advisor.budgetUsd", "number", {
290
+ minimum: 0,
291
+ description: "Maximum advisor spend in USD per run",
292
+ }),
293
+ descriptor("advisor.repeatErrorThreshold", "integer", {
294
+ minimum: 2,
295
+ description: "Repeated-error count that triggers advisor review",
296
+ }),
297
+ descriptor("advisorEnabled", "boolean", {
298
+ deprecated: true,
299
+ migration: "advisor.enabled",
300
+ description: "Legacy advisor enabled alias",
301
+ }),
302
+ descriptor("advisorProvider", ["string", "null"], {
303
+ deprecated: true,
304
+ migration: "advisor.provider",
305
+ description: "Legacy advisor provider alias",
306
+ }),
307
+ descriptor("advisorModel", ["string", "null"], {
308
+ deprecated: true,
309
+ migration: "advisor.model",
310
+ description: "Legacy advisor model alias",
311
+ }),
312
+ descriptor("advisorBudgetUsd", "number", {
313
+ minimum: 0,
314
+ deprecated: true,
315
+ migration: "advisor.budgetUsd",
316
+ description: "Legacy advisor budget alias",
317
+ }),
318
+ descriptor("advisorRepeatErrorThreshold", "integer", {
319
+ minimum: 2,
320
+ deprecated: true,
321
+ migration: "advisor.repeatErrorThreshold",
322
+ description: "Legacy advisor repeated-error alias",
323
+ }),
324
+ descriptor("telemetry", "object", {
325
+ open: true,
326
+ description: "Telemetry and local observability settings",
327
+ }),
328
+ descriptor("ui", "object", {
329
+ open: true,
330
+ description: "Desktop UI compatibility settings",
331
+ }),
332
+ ];
333
+
334
+ const BY_KEY = new Map(DESCRIPTORS.map((entry) => [entry.key, entry]));
335
+ // Extension namespaces cannot publish plaintext just because their common
336
+ // credential field uses a compound spelling. This is a closed leaf-name
337
+ // vocabulary (not a broad substring guess); plugin schemas can additionally
338
+ // mark any custom field with `secret: true`.
339
+ const SECRET_LEAF_RE =
340
+ /^(?:api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|bearer[_-]?token|bot[_-]?token|client[_-]?secret|secret[_-]?access[_-]?key|private[_-]?key|secret|token|password|passwd|credential|credentials)$/i;
341
+ const FORBIDDEN_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
342
+
343
+ export function validateConfigKeySyntax(key) {
344
+ const text = String(key || "");
345
+ const parts = text.split(".");
346
+ if (
347
+ !text ||
348
+ parts.some(
349
+ (part) =>
350
+ !part || FORBIDDEN_SEGMENTS.has(part) || !/^[A-Za-z0-9_-]+$/.test(part),
351
+ )
352
+ ) {
353
+ const error = new Error(`Invalid configuration key: ${text || "(empty)"}`);
354
+ error.code = "CONFIG_KEY_INVALID";
355
+ throw error;
356
+ }
357
+ return text;
358
+ }
359
+
360
+ function parentDescriptors(key) {
361
+ const parents = [];
362
+ const parts = key.split(".");
363
+ for (let index = parts.length - 1; index > 0; index -= 1) {
364
+ const found = BY_KEY.get(parts.slice(0, index).join("."));
365
+ if (found) parents.push(found);
366
+ }
367
+ return parents;
368
+ }
369
+
370
+ function implicitContainerDescriptor(key) {
371
+ const prefix = `${key}.`;
372
+ const descendants = DESCRIPTORS.filter((entry) =>
373
+ entry.key.startsWith(prefix),
374
+ );
375
+ if (descendants.length === 0) return null;
376
+ return {
377
+ key,
378
+ type: "object",
379
+ scope: descendants.every((entry) => entry.scope === descendants[0].scope)
380
+ ? descendants[0].scope
381
+ : "user",
382
+ managedLock: descendants.some((entry) => entry.managedLock !== false),
383
+ implicit: true,
384
+ description: `Configuration group for ${key}`,
385
+ };
386
+ }
387
+
388
+ /** Return the exact descriptor, or a descriptor inherited from an open map. */
389
+ export function getConfigDescriptor(key) {
390
+ const normalized = validateConfigKeySyntax(key);
391
+ const exact = BY_KEY.get(normalized);
392
+ if (exact) return { ...exact };
393
+ const container = implicitContainerDescriptor(normalized);
394
+ if (container) return container;
395
+ const openParent = parentDescriptors(normalized).find((entry) => entry.open);
396
+ if (!openParent) return null;
397
+ const leaf = normalized.split(".").at(-1);
398
+ return {
399
+ key: normalized,
400
+ type: "any",
401
+ scope: openParent.scope,
402
+ managedLock: openParent.managedLock,
403
+ extension: true,
404
+ secret:
405
+ openParent.secretChildren === true || SECRET_LEAF_RE.test(String(leaf)),
406
+ description: `Extension value under ${openParent.key}`,
407
+ };
408
+ }
409
+
410
+ export function isKnownConfigKey(key) {
411
+ try {
412
+ return getConfigDescriptor(key) !== null;
413
+ } catch {
414
+ return false;
415
+ }
416
+ }
417
+
418
+ export function isSecretConfigKey(key) {
419
+ const leaf = String(key || "")
420
+ .split(".")
421
+ .at(-1);
422
+ try {
423
+ const entry = getConfigDescriptor(key);
424
+ if (entry?.secret === true) return true;
425
+ return SECRET_LEAF_RE.test(leaf || "");
426
+ } catch {
427
+ // Redaction is an independent security boundary and may receive a raw,
428
+ // invalid document before validation. Preserve the closed leaf-name
429
+ // fallback even when the full dotted path is deliberately rejected.
430
+ return SECRET_LEAF_RE.test(leaf || "");
431
+ }
432
+ }
433
+
434
+ export function getConfigDescriptors() {
435
+ return DESCRIPTORS.map((entry) => ({ ...entry }));
436
+ }
437
+
438
+ /**
439
+ * Register typed plugin-owned keys without opening arbitrary global keys.
440
+ * Entries use keys relative to `plugins.<pluginId>` and may declare `secret`.
441
+ */
442
+ export function registerPluginConfigSchema(pluginId, entries = []) {
443
+ const id = String(pluginId || "").trim();
444
+ validateConfigKeySyntax(`plugins.${id}`);
445
+ if (!id || id.includes(".")) {
446
+ throw new Error("Plugin config schema id must be one path segment");
447
+ }
448
+ const namespace = `plugins.${id}`;
449
+ if (
450
+ BY_KEY.has(namespace) ||
451
+ DESCRIPTORS.some((entry) => entry.key.startsWith(`${namespace}.`))
452
+ ) {
453
+ throw new Error(`Plugin config namespace is reserved: ${namespace}`);
454
+ }
455
+ const pending = [];
456
+ const pendingKeys = new Set();
457
+ for (const value of entries) {
458
+ const relative = validateConfigKeySyntax(value?.key);
459
+ const key = `${namespace}.${relative}`;
460
+ if (BY_KEY.has(key) || pendingKeys.has(key)) {
461
+ throw new Error(`Configuration key is already registered: ${key}`);
462
+ }
463
+ const next = descriptor(key, value.type || "any", {
464
+ ...value,
465
+ key,
466
+ scope: "plugin",
467
+ });
468
+ pending.push(next);
469
+ pendingKeys.add(key);
470
+ }
471
+ // Resolve inheritance only after every descriptor exists so callers are not
472
+ // forced to order a secretChildren parent before its children.
473
+ for (const next of pending) {
474
+ const inheritedSecret = [...DESCRIPTORS, ...pending].some(
475
+ (entry) =>
476
+ next.key.startsWith(`${entry.key}.`) &&
477
+ (entry.secret === true || entry.secretChildren === true),
478
+ );
479
+ if (inheritedSecret) next.secret = true;
480
+ }
481
+ for (const next of pending) {
482
+ DESCRIPTORS.push(next);
483
+ BY_KEY.set(next.key, next);
484
+ }
485
+ return pending.map((entry) => ({ ...entry }));
486
+ }
487
+
488
+ function typesFor(entry) {
489
+ return Array.isArray(entry?.type) ? entry.type : [entry?.type || "any"];
490
+ }
491
+
492
+ function actualType(value) {
493
+ if (value === null) return "null";
494
+ if (Array.isArray(value)) return "array";
495
+ return typeof value;
496
+ }
497
+
498
+ function valueIssue(key, code, message) {
499
+ return { key, code, message };
500
+ }
501
+
502
+ function isPersistedSecretRef(value) {
503
+ return Boolean(
504
+ value &&
505
+ typeof value === "object" &&
506
+ !Array.isArray(value) &&
507
+ Object.keys(value).length === 1 &&
508
+ typeof value.__cc_secret_ref === "string" &&
509
+ value.__cc_secret_ref.length > 0,
510
+ );
511
+ }
512
+
513
+ export function validateConfigValue(key, value, options = {}) {
514
+ let entry;
515
+ try {
516
+ entry = getConfigDescriptor(key);
517
+ } catch (error) {
518
+ return [valueIssue(String(key || ""), error.code, error.message)];
519
+ }
520
+ if (!entry) {
521
+ return options.allowUnknown
522
+ ? []
523
+ : [
524
+ valueIssue(
525
+ key,
526
+ "CONFIG_KEY_UNKNOWN",
527
+ `Unknown configuration key: ${key}`,
528
+ ),
529
+ ];
530
+ }
531
+ if (isPersistedSecretRef(value)) {
532
+ if (entry.secret === true) return [];
533
+ return [
534
+ valueIssue(
535
+ key,
536
+ "CONFIG_SECRET_REF_INVALID",
537
+ `Secret reference is not allowed at non-secret key: ${key}`,
538
+ ),
539
+ ];
540
+ }
541
+ const allowed = typesFor(entry);
542
+ const got = actualType(value);
543
+ const typeMatches =
544
+ allowed.includes("any") ||
545
+ allowed.includes(got) ||
546
+ (got === "number" &&
547
+ allowed.includes("integer") &&
548
+ Number.isInteger(value));
549
+ if (!typeMatches) {
550
+ return [
551
+ valueIssue(
552
+ key,
553
+ "CONFIG_TYPE_INVALID",
554
+ `${key} must be ${allowed.join(" | ")}; received ${got}`,
555
+ ),
556
+ ];
557
+ }
558
+ if (entry.enum && !entry.enum.includes(value)) {
559
+ return [
560
+ valueIssue(
561
+ key,
562
+ "CONFIG_ENUM_INVALID",
563
+ `${key} must be one of: ${entry.enum.join(", ")}`,
564
+ ),
565
+ ];
566
+ }
567
+ if (typeof value === "number") {
568
+ if (!Number.isFinite(value)) {
569
+ return [
570
+ valueIssue(key, "CONFIG_NUMBER_INVALID", `${key} must be finite`),
571
+ ];
572
+ }
573
+ if (entry.minimum != null && value < entry.minimum) {
574
+ return [
575
+ valueIssue(
576
+ key,
577
+ "CONFIG_NUMBER_RANGE",
578
+ `${key} must be >= ${entry.minimum}`,
579
+ ),
580
+ ];
581
+ }
582
+ if (entry.maximum != null && value > entry.maximum) {
583
+ return [
584
+ valueIssue(
585
+ key,
586
+ "CONFIG_NUMBER_RANGE",
587
+ `${key} must be <= ${entry.maximum}`,
588
+ ),
589
+ ];
590
+ }
591
+ }
592
+ return [];
593
+ }
594
+
595
+ export function coerceConfigValue(key, raw, options = {}) {
596
+ const entry = getConfigDescriptor(key);
597
+ if (!entry && !options.allowUnknown) {
598
+ const error = new Error(`Unknown configuration key: ${key}`);
599
+ error.code = "CONFIG_KEY_UNKNOWN";
600
+ throw error;
601
+ }
602
+ if (typeof raw !== "string") {
603
+ const issues = validateConfigValue(key, raw, options);
604
+ if (issues.length)
605
+ throw Object.assign(new Error(issues[0].message), issues[0]);
606
+ return raw;
607
+ }
608
+ const types = typesFor(entry);
609
+ let value = raw;
610
+ if (raw === "null" && types.includes("null")) {
611
+ value = null;
612
+ } else if (types.includes("boolean")) {
613
+ if (raw !== "true" && raw !== "false") {
614
+ const error = new Error(`${key} must be true or false`);
615
+ error.code = "CONFIG_TYPE_INVALID";
616
+ throw error;
617
+ }
618
+ value = raw === "true";
619
+ } else if (types.includes("number") || types.includes("integer")) {
620
+ value = Number(raw);
621
+ if (
622
+ !Number.isFinite(value) ||
623
+ (types.includes("integer") && !Number.isInteger(value))
624
+ ) {
625
+ const error = new Error(
626
+ `${key} must be a finite ${types.includes("integer") ? "integer" : "number"}`,
627
+ );
628
+ error.code = "CONFIG_TYPE_INVALID";
629
+ throw error;
630
+ }
631
+ } else if (
632
+ (types.includes("array") && raw.trim().startsWith("[")) ||
633
+ (types.includes("object") && raw.trim().startsWith("{")) ||
634
+ ((types.includes("array") || types.includes("object")) &&
635
+ !types.includes("string"))
636
+ ) {
637
+ try {
638
+ value = JSON.parse(raw);
639
+ } catch {
640
+ const error = new Error(`${key} must be valid JSON`);
641
+ error.code = "CONFIG_TYPE_INVALID";
642
+ throw error;
643
+ }
644
+ } else if (types.includes("any")) {
645
+ if (raw === "true") value = true;
646
+ else if (raw === "false") value = false;
647
+ else if (raw === "null") value = null;
648
+ else if (raw.trim() !== "" && Number.isFinite(Number(raw)))
649
+ value = Number(raw);
650
+ }
651
+ const issues = validateConfigValue(key, value, options);
652
+ if (issues.length)
653
+ throw Object.assign(new Error(issues[0].message), issues[0]);
654
+ return value;
655
+ }
656
+
657
+ function walkConfig(value, prefix, issues, options) {
658
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
659
+ for (const [name, child] of Object.entries(value)) {
660
+ const key = prefix ? `${prefix}.${name}` : name;
661
+ let entry;
662
+ try {
663
+ entry = getConfigDescriptor(key);
664
+ } catch (error) {
665
+ issues.push(valueIssue(key, error.code, error.message));
666
+ continue;
667
+ }
668
+ if (!entry) {
669
+ if (!options.allowUnknown) {
670
+ issues.push(
671
+ valueIssue(
672
+ key,
673
+ "CONFIG_KEY_UNKNOWN",
674
+ `Unknown configuration key: ${key}`,
675
+ ),
676
+ );
677
+ }
678
+ continue;
679
+ }
680
+ issues.push(...validateConfigValue(key, child, options));
681
+ if (
682
+ child &&
683
+ typeof child === "object" &&
684
+ !Array.isArray(child) &&
685
+ !isPersistedSecretRef(child)
686
+ ) {
687
+ walkConfig(child, key, issues, options);
688
+ }
689
+ }
690
+ }
691
+
692
+ export function validateConfigDocument(config, options = {}) {
693
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
694
+ return {
695
+ valid: false,
696
+ schemaVersion: CONFIG_SCHEMA_VERSION,
697
+ issues: [
698
+ valueIssue(
699
+ "",
700
+ "CONFIG_ROOT_INVALID",
701
+ "Configuration root must be an object",
702
+ ),
703
+ ],
704
+ };
705
+ }
706
+ const issues = [];
707
+ walkConfig(config, "", issues, options);
708
+ return {
709
+ valid: issues.length === 0,
710
+ schemaVersion: CONFIG_SCHEMA_VERSION,
711
+ issues,
712
+ };
713
+ }
714
+
715
+ export function migrateConfigDocument(config) {
716
+ const next = structuredClone(config || {});
717
+ const migrations = [];
718
+ const legacy = next?.llm?.fallbackModel;
719
+ if (legacy != null) {
720
+ if (next?.llm?.fallbackModels == null) {
721
+ next.llm.fallbackModels = Array.isArray(legacy) ? legacy : [legacy];
722
+ }
723
+ delete next.llm.fallbackModel;
724
+ migrations.push({ from: "llm.fallbackModel", to: "llm.fallbackModels" });
725
+ }
726
+ const advisorAliases = [
727
+ ["advisorEnabled", "enabled"],
728
+ ["advisorProvider", "provider"],
729
+ ["advisorModel", "model"],
730
+ ["advisorBudgetUsd", "budgetUsd"],
731
+ ["advisorRepeatErrorThreshold", "repeatErrorThreshold"],
732
+ ];
733
+ for (const [legacyKey, nestedKey] of advisorAliases) {
734
+ if (!Object.prototype.hasOwnProperty.call(next, legacyKey)) continue;
735
+ if (next.advisor == null) next.advisor = {};
736
+ if (typeof next.advisor !== "object" || Array.isArray(next.advisor)) {
737
+ // Preserve both invalid values so validation can report them; migration
738
+ // must never crash or silently replace a malformed nested document.
739
+ continue;
740
+ }
741
+ if (!Object.prototype.hasOwnProperty.call(next.advisor, nestedKey)) {
742
+ next.advisor[nestedKey] = next[legacyKey];
743
+ }
744
+ delete next[legacyKey];
745
+ migrations.push({ from: legacyKey, to: `advisor.${nestedKey}` });
746
+ }
747
+ return { config: next, migrations };
748
+ }
749
+
750
+ function schemaType(type) {
751
+ if (type === "any") return {};
752
+ return { type };
753
+ }
754
+
755
+ function objectPropertiesFor(schema) {
756
+ if (Array.isArray(schema.anyOf)) {
757
+ let objectBranch = schema.anyOf.find((entry) => entry?.type === "object");
758
+ if (!objectBranch) {
759
+ objectBranch = { type: "object" };
760
+ schema.anyOf.push(objectBranch);
761
+ }
762
+ objectBranch.properties ||= {};
763
+ objectBranch.additionalProperties ??= false;
764
+ return objectBranch.properties;
765
+ }
766
+ schema.type = "object";
767
+ schema.properties ||= {};
768
+ schema.additionalProperties ??= false;
769
+ return schema.properties;
770
+ }
771
+
772
+ function markObjectBranchesOpen(schema) {
773
+ if (!schema || typeof schema !== "object") return;
774
+ if (schema.type === "object") schema.additionalProperties = true;
775
+ if (Array.isArray(schema.anyOf)) {
776
+ for (const branch of schema.anyOf) markObjectBranchesOpen(branch);
777
+ }
778
+ }
779
+
780
+ function buildJsonSchema() {
781
+ const root = {
782
+ $schema: "https://json-schema.org/draft/2020-12/schema",
783
+ $id: CONFIG_SCHEMA_ID,
784
+ title: "ChainlessChain CLI configuration",
785
+ type: "object",
786
+ additionalProperties: false,
787
+ properties: {},
788
+ $defs: {
789
+ secretRef: {
790
+ type: "object",
791
+ additionalProperties: false,
792
+ required: ["__cc_secret_ref"],
793
+ properties: {
794
+ __cc_secret_ref: { type: "string", minLength: 1 },
795
+ },
796
+ },
797
+ },
798
+ "x-chainlesschain-schema-version": CONFIG_SCHEMA_VERSION,
799
+ };
800
+ const orderedDescriptors = [...DESCRIPTORS].sort(
801
+ (left, right) => left.key.split(".").length - right.key.split(".").length,
802
+ );
803
+ for (const entry of orderedDescriptors) {
804
+ const parts = entry.key.split(".");
805
+ let properties = root.properties;
806
+ for (let index = 0; index < parts.length; index += 1) {
807
+ const part = parts[index];
808
+ const leaf = index === parts.length - 1;
809
+ if (!properties[part]) {
810
+ properties[part] = leaf
811
+ ? {}
812
+ : { type: "object", additionalProperties: false, properties: {} };
813
+ }
814
+ if (!leaf) {
815
+ properties = objectPropertiesFor(properties[part]);
816
+ continue;
817
+ }
818
+ const types = typesFor(entry);
819
+ const declaredSchema =
820
+ types.length === 1
821
+ ? schemaType(types[0])
822
+ : { anyOf: types.map((type) => schemaType(type)) };
823
+ const leafSchema = entry.secret
824
+ ? { anyOf: [declaredSchema, { $ref: "#/$defs/secretRef" }] }
825
+ : declaredSchema;
826
+ if (entry.enum) leafSchema.enum = [...entry.enum];
827
+ if (entry.default !== undefined)
828
+ leafSchema.default = structuredClone(entry.default);
829
+ if (entry.minimum !== undefined) leafSchema.minimum = entry.minimum;
830
+ if (entry.maximum !== undefined) leafSchema.maximum = entry.maximum;
831
+ if (entry.description) leafSchema.description = entry.description;
832
+ if (entry.open) {
833
+ leafSchema.additionalProperties = true;
834
+ markObjectBranchesOpen(leafSchema);
835
+ }
836
+ if (entry.secret) leafSchema["x-secret"] = true;
837
+ if (entry.secretChildren) leafSchema["x-secret-children"] = true;
838
+ leafSchema["x-scope"] = entry.scope;
839
+ leafSchema["x-managed-lock"] = entry.managedLock;
840
+ if (entry.deprecated) leafSchema.deprecated = true;
841
+ if (entry.migration) leafSchema["x-migration"] = entry.migration;
842
+ properties[part] = { ...properties[part], ...leafSchema };
843
+ }
844
+ }
845
+ return root;
846
+ }
847
+
848
+ export const CONFIG_SCHEMA = Object.freeze(buildJsonSchema());
849
+
850
+ /** Include any plugin schemas registered in the current process. */
851
+ export function getConfigJsonSchema() {
852
+ return buildJsonSchema();
853
+ }