chainlesschain 0.162.148 → 0.162.150

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 (322) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-CBLvrjeG.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-DYv2GtE0.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-CL6PZ-Ww.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-xuyHt4uA.js} +3 -3
  8. package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-DOvSVycq.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-DMhfTswr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-CQxDdm63.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-BEeNhK2a.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-8nul8eTq.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CL04O-ER.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-C50o-n7x.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-CH-bDkzs.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-DqAIMvSe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-C43fo_my.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-BERnMrdP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-Baw3w1gp.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-BEqAHF1-.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-CvnmKwK4.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-iMPxgVdt.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard--4SJX3YR.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BgLV6xgz.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-BbFfJ1Xi.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DeGq1B-q.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-L1PjW-lT.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-PK-X58to.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-BePWEH-d.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-BKkfk9hy.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-BNMqUDla.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-w4WPiyz2.js} +5 -5
  37. package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-DUKMVGNi.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-BWhiEyTJ.js} +1 -1
  39. package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-Ocf5JEkX.js} +1 -1
  40. package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-D2B6MMhU.js} +4 -4
  41. package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-DdYjCq1O.js} +4 -4
  42. package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-Cb1hfuUZ.js} +3 -3
  43. package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-CTeImGp7.js} +1 -1
  44. package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-BjYNc7eQ.js} +4 -4
  45. package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
  46. package/src/assets/web-panel/assets/{OrderTableRenderer-DIp8Xcbd.js → OrderTableRenderer-DVYYIizh.js} +1 -1
  47. package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-Be2Kmr5j.js} +4 -4
  48. package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow-Cu_-qRlR.js} +1 -1
  49. package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-DD2HoGzE.js} +2 -2
  50. package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
  51. package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-C9Srcs2M.js} +4 -4
  52. package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CUqcMgo1.js} +3 -3
  53. package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-HBrQGLXt.js} +1 -1
  54. package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-D-B0vjwh.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-eTeLIu9e.js} +2 -2
  56. package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-DrRqCsC6.js} +2 -2
  57. package/src/assets/web-panel/assets/{Projects-Dpid9CDg.js → Projects-1G9DNOhI.js} +1 -1
  58. package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-RkTZzpxP.js} +1 -1
  59. package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-BSCUgsgC.js} +1 -1
  60. package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CHgyM3Bz.js} +1 -1
  61. package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-0lry4OZq.js} +1 -1
  62. package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-Cn45UroZ.js} +2 -2
  63. package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-Q-Zjb707.js} +1 -1
  64. package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-BdM70oda.js} +1 -1
  65. package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-DmOAKKap.js} +2 -2
  66. package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-Dwavbm07.js} +1 -1
  67. package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DukXFCnk.js} +3 -3
  68. package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-BIlRnITu.js} +2 -2
  69. package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-YYVQdhhQ.js} +1 -1
  70. package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-B8_iYHz2.js} +1 -1
  71. package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-BdVrhT31.js} +1 -1
  72. package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-CD3ggRx7.js} +1 -1
  73. package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-Bp02VZFH.js} +2 -2
  74. package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-DEVmCEsr.js} +1 -1
  75. package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-B6czWc4N.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-BCJLv17r.js} +1 -1
  77. package/src/assets/web-panel/assets/Terminal-CD132d2Y.js +3 -0
  78. package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BnSY0S-s.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-VZw9Oy2w.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-pZq7Hjd2.js} +1 -1
  82. package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-DnQaqk4q.js} +1 -1
  83. package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-BEUfmvJV.js} +1 -1
  84. package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-CoZKMXJ2.js} +4 -4
  85. package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-CBOGVx1I.js} +5 -5
  86. package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-huSKn7TT.js} +1 -1
  87. package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-A_3w6FBO.js} +1 -1
  88. package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-D1Bem9Oy.js} +1 -1
  89. package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-CJYJj0oO.js} +1 -1
  90. package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-BkbFiKT4.js} +1 -1
  91. package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
  92. package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-CKvUafVY.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-AjrSPnvv.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-B8i0lViZ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-B9Svn0zc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-BHdHSX06.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BHdailyo.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-BJwlEnYo.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-BOoAQjJz.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-BSgWxAOG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-BdutMsne.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-BiDWxzbn.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-By5a0T_W.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-C-WIY-FQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-C37f6iey.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-C9bWmTcO.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-CBaosWRO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-CBlBVJ_m.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-CD62EEGo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-CUj-l7GM.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-CVS8Ymuq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-ChkXUj3f.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-Cj0OZ-37.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-Co3OFL0t.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-308gCGh7.js → index-Crp8CbRg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-D22zNXiS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-DTs_D1OL.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-DfgCdFrN.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-DjYrecNb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-DpW9cf3e.js} +1 -1
  121. package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-F--HuPG1.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-Fuk6t_3K.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-JEAGMii9.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-PF-mpv8K.js} +1 -1
  126. package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
  127. package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-b-sbVdjQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-pyvMVX8r.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DISWAYce.js → index-qNTtOVi_.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-tgO8iza6.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CahryTX0.js → index-ttDQVhZy.js} +1 -1
  132. package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-DtrnfsZH.js} +1 -1
  133. package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D4zqSaX3.js} +1 -1
  134. package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-CGFGOUQj.js} +1 -1
  135. package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DH2HvkJl.js} +1 -1
  136. package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-jJ2at5HX.js} +1 -1
  137. package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-CRyMT1Fv.js} +1 -1
  138. package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-DZ_CX0Pt.js} +1 -1
  139. package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-wruHk-h5.js} +1 -1
  140. package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BGgx8q67.js} +1 -1
  141. package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-Do5M7gKm.js} +1 -1
  142. package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-CRa-GZpX.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-v5O2ZcJm.js} +1 -1
  145. package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub--z2FU0Px.js} +1 -1
  146. package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-1emidVKd.js} +1 -1
  147. package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-CcJaRyHs.js} +1 -1
  148. package/src/assets/web-panel/index.html +1 -1
  149. package/src/assets/web-panel/remote-session-sw.js +43 -0
  150. package/src/command-manifest.json +22 -1
  151. package/src/commands/a2a.js +19 -4
  152. package/src/commands/activitypub.js +20 -3
  153. package/src/commands/agent.js +71 -10
  154. package/src/commands/audit.js +40 -24
  155. package/src/commands/codeintel.js +320 -0
  156. package/src/commands/collab.js +15 -2
  157. package/src/commands/compliance.js +5 -0
  158. package/src/commands/config.js +4 -1
  159. package/src/commands/dao.js +86 -14
  160. package/src/commands/dev.js +2 -0
  161. package/src/commands/did.js +7 -1
  162. package/src/commands/dlp.js +23 -1
  163. package/src/commands/economy.js +29 -3
  164. package/src/commands/eval.js +255 -0
  165. package/src/commands/evomap.js +26 -0
  166. package/src/commands/governance.js +17 -3
  167. package/src/commands/hardening.js +18 -0
  168. package/src/commands/incentive.js +5 -0
  169. package/src/commands/init.js +46 -2
  170. package/src/commands/kg.js +4 -0
  171. package/src/commands/loop.js +6 -1
  172. package/src/commands/lowcode.js +15 -2
  173. package/src/commands/marketplace.js +40 -6
  174. package/src/commands/matrix.js +20 -1
  175. package/src/commands/memory.js +4 -1
  176. package/src/commands/mtc.js +7 -1
  177. package/src/commands/nostr.js +31 -4
  178. package/src/commands/note.js +7 -4
  179. package/src/commands/plugin.js +474 -0
  180. package/src/commands/pqc.js +5 -0
  181. package/src/commands/reputation.js +10 -1
  182. package/src/commands/scim.js +6 -0
  183. package/src/commands/session.js +32 -10
  184. package/src/commands/siem.js +11 -0
  185. package/src/commands/sla.js +18 -3
  186. package/src/commands/social.js +38 -5
  187. package/src/commands/sso.js +10 -2
  188. package/src/commands/stress.js +23 -4
  189. package/src/commands/team.js +463 -0
  190. package/src/commands/tech.js +2 -0
  191. package/src/commands/tenant.js +10 -1
  192. package/src/commands/terraform.js +6 -0
  193. package/src/commands/update.js +1 -1
  194. package/src/commands/zkp.js +20 -0
  195. package/src/gateways/ws/message-dispatcher.js +48 -2
  196. package/src/gateways/ws/remote-session-protocol.js +140 -42
  197. package/src/gateways/ws/ws-server.js +1 -1
  198. package/src/harness/jsonl-session-store.js +12 -5
  199. package/src/harness/plugin-manager.js +16 -4
  200. package/src/harness/prompt-compressor.js +27 -1
  201. package/src/harness/remote-command-ledger.js +189 -0
  202. package/src/harness/remote-session-push-apns.js +246 -0
  203. package/src/harness/remote-session-push-errors.js +15 -0
  204. package/src/harness/remote-session-push-fcm.js +9 -25
  205. package/src/harness/remote-session-push-huawei.js +174 -0
  206. package/src/harness/remote-session-push-oppo.js +171 -0
  207. package/src/harness/remote-session-push-senders.js +42 -0
  208. package/src/harness/remote-session-push-vivo.js +179 -0
  209. package/src/harness/remote-session-push-web.js +299 -0
  210. package/src/harness/remote-session-push-xiaomi.js +99 -0
  211. package/src/harness/worktree-isolator.js +16 -6
  212. package/src/index.js +6 -0
  213. package/src/lib/__tests__/logger.test.js +5 -2
  214. package/src/lib/a2a-protocol.js +25 -1
  215. package/src/lib/activitypub-bridge.js +61 -0
  216. package/src/lib/agent-core.js +4 -0
  217. package/src/lib/agent-economy.js +262 -29
  218. package/src/lib/agent-network.js +7 -1
  219. package/src/lib/agent-sandbox.js +161 -5
  220. package/src/lib/agent-team/task-lease.js +434 -0
  221. package/src/lib/agent-team/team-budget.js +165 -0
  222. package/src/lib/agent-team/team-mailbox.js +106 -0
  223. package/src/lib/agent-team/team-runner.js +282 -0
  224. package/src/lib/agent-team/team-worktree.js +236 -0
  225. package/src/lib/agents.js +18 -0
  226. package/src/lib/async-hook-supervisor.cjs +391 -0
  227. package/src/lib/audit-logger.js +7 -6
  228. package/src/lib/autonomous-developer.js +60 -0
  229. package/src/lib/chat-core.js +17 -4
  230. package/src/lib/collaboration-governance.js +56 -0
  231. package/src/lib/community-governance.js +68 -0
  232. package/src/lib/compliance-manager.js +63 -0
  233. package/src/lib/cross-chain.js +5 -0
  234. package/src/lib/dao-governance.js +151 -2
  235. package/src/lib/did-manager.js +23 -10
  236. package/src/lib/dlp-engine.js +70 -0
  237. package/src/lib/eval/runner.js +251 -0
  238. package/src/lib/eval/tasks.js +626 -0
  239. package/src/lib/eval/trend.js +200 -0
  240. package/src/lib/evolution-system.js +92 -0
  241. package/src/lib/evomap-federation.js +55 -0
  242. package/src/lib/evomap-governance.js +94 -0
  243. package/src/lib/fatal-handler.js +17 -1
  244. package/src/lib/hardening-manager.js +73 -0
  245. package/src/lib/hierarchical-memory.js +14 -8
  246. package/src/lib/knowledge-exporter.js +8 -2
  247. package/src/lib/knowledge-graph.js +79 -0
  248. package/src/lib/llm-providers.js +23 -14
  249. package/src/lib/logger.js +4 -1
  250. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  251. package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
  252. package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
  253. package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
  254. package/src/lib/lsp/__tests__/lsp-client.test.js +269 -0
  255. package/src/lib/lsp/__tests__/lsp-manager.test.js +333 -0
  256. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
  257. package/src/lib/lsp/benchmark.js +257 -0
  258. package/src/lib/lsp/code-intelligence.js +356 -0
  259. package/src/lib/lsp/jsonrpc-stream.js +139 -0
  260. package/src/lib/lsp/lsp-client.js +339 -0
  261. package/src/lib/lsp/lsp-manager.js +375 -0
  262. package/src/lib/lsp/lsp-server-registry.js +196 -0
  263. package/src/lib/matrix-bridge.js +84 -0
  264. package/src/lib/memory-manager.js +5 -3
  265. package/src/lib/nostr-bridge.js +53 -0
  266. package/src/lib/permission-engine.js +22 -4
  267. package/src/lib/plugin-monitor-supervisor.js +238 -0
  268. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  269. package/src/lib/plugin-runtime/agents.js +51 -0
  270. package/src/lib/plugin-runtime/bin.js +100 -0
  271. package/src/lib/plugin-runtime/hooks.js +100 -0
  272. package/src/lib/plugin-runtime/install.js +396 -0
  273. package/src/lib/plugin-runtime/lsp.js +75 -0
  274. package/src/lib/plugin-runtime/manifest.js +458 -0
  275. package/src/lib/plugin-runtime/mcp.js +89 -0
  276. package/src/lib/plugin-runtime/monitors.js +100 -0
  277. package/src/lib/plugin-runtime/policy.js +152 -0
  278. package/src/lib/plugin-runtime/reload.js +79 -0
  279. package/src/lib/plugin-runtime/remote-source.js +240 -0
  280. package/src/lib/plugin-runtime/scopes.js +197 -0
  281. package/src/lib/plugin-runtime/settings.js +119 -0
  282. package/src/lib/plugin-runtime/signature.js +107 -0
  283. package/src/lib/plugin-runtime/skills.js +43 -0
  284. package/src/lib/plugin-runtime/trust.js +140 -0
  285. package/src/lib/pqc-manager.js +64 -0
  286. package/src/lib/reputation-optimizer.js +101 -0
  287. package/src/lib/sandbox-egress-proxy.js +262 -0
  288. package/src/lib/sandbox-fs-policy.js +112 -0
  289. package/src/lib/sandbox-network-policy.js +151 -0
  290. package/src/lib/sandbox-v2.js +24 -18
  291. package/src/lib/scim-manager.js +36 -0
  292. package/src/lib/session-manager.js +5 -2
  293. package/src/lib/settings-hook-events.cjs +78 -6
  294. package/src/lib/settings-hooks.cjs +30 -3
  295. package/src/lib/settings-loader.cjs +35 -1
  296. package/src/lib/siem-exporter.js +45 -0
  297. package/src/lib/skill-loader.js +38 -2
  298. package/src/lib/skill-marketplace.js +83 -0
  299. package/src/lib/sla-manager.js +88 -0
  300. package/src/lib/social-manager.js +74 -0
  301. package/src/lib/stress-tester.js +65 -0
  302. package/src/lib/tech-learning-engine.js +75 -0
  303. package/src/lib/telemetry/span-recorder.js +237 -0
  304. package/src/lib/tenant-saas.js +107 -0
  305. package/src/lib/terraform-manager.js +67 -0
  306. package/src/lib/token-incentive.js +180 -29
  307. package/src/lib/wallet-manager.js +81 -35
  308. package/src/lib/zkp-engine.js +87 -0
  309. package/src/repl/agent-repl.js +417 -6
  310. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  311. package/src/runtime/agent-core.js +771 -104
  312. package/src/runtime/auto-pin.js +117 -0
  313. package/src/runtime/coding-agent-contract-shared.cjs +82 -8
  314. package/src/runtime/coding-agent-policy.cjs +32 -7
  315. package/src/runtime/fallback-model.js +134 -7
  316. package/src/runtime/headless-runner.js +388 -108
  317. package/src/runtime/mcp-config.js +46 -0
  318. package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
  319. package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
  320. package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
  321. package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
  322. package/src/assets/web-panel/assets/index-BXiw9dQf.js +0 -1
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Auto-pin policy for compaction (OPT-IN).
3
+ *
4
+ * Phase 7.7 gave the compressor a deterministic pin mechanism: a message the
5
+ * predicate marks is pulled out before any strategy and re-inserted verbatim,
6
+ * so it survives compaction no matter what the (non-deterministic) LLM summary
7
+ * drops. What was deliberately left unbuilt is the *policy* — deciding WHICH
8
+ * messages to auto-pin — because auto-pinning reorders the hot agent loop's
9
+ * context and is a product-UX call.
10
+ *
11
+ * This module supplies that policy, but strictly opt-in: it only ever produces
12
+ * a predicate when the caller explicitly enables auto-pin. When disabled it
13
+ * returns null and the caller passes NO predicate, so the compressor falls back
14
+ * to its default (pin only messages already tagged `pinned:true`) and behaviour
15
+ * is byte-identical to before.
16
+ *
17
+ * Default policy when enabled: pin the ORIGINAL TASK — the first user turn —
18
+ * because that is the single fact most likely to scroll out of the recent
19
+ * window yet most important to keep (the coding agent's goal). Explicit
20
+ * `pinned:true` messages are always honored on top of it. A token cap keeps a
21
+ * pathologically large first turn from itself blowing the budget.
22
+ */
23
+
24
+ const DEFAULT_MAX_PIN_TOKENS = 2000;
25
+
26
+ /**
27
+ * Normalize the opt-in value into a config object, or null when auto-pin is off.
28
+ * Accepts: falsy (off), `true` (defaults on), or a config object
29
+ * `{ firstUserGoal?, maxPinTokens? }`.
30
+ */
31
+ export function resolveAutoPinConfig(opt) {
32
+ if (!opt) return null;
33
+ const cfg = typeof opt === "object" ? opt : {};
34
+ return {
35
+ firstUserGoal: cfg.firstUserGoal !== false, // default true when enabled
36
+ maxPinTokens: Number.isFinite(cfg.maxPinTokens)
37
+ ? cfg.maxPinTokens
38
+ : DEFAULT_MAX_PIN_TOKENS,
39
+ };
40
+ }
41
+
42
+ /** Coarse char/4 token estimate — matches the compressor's own heuristic. */
43
+ function estimateTokens(text) {
44
+ return Math.ceil(String(text || "").length / 4);
45
+ }
46
+
47
+ function contentString(m) {
48
+ if (!m) return "";
49
+ return typeof m.content === "string"
50
+ ? m.content
51
+ : JSON.stringify(m.content || "");
52
+ }
53
+
54
+ /**
55
+ * Build an `isPinned(message)` predicate for `compress({ isPinned })`, or null
56
+ * when auto-pin is disabled (caller then passes no predicate → default).
57
+ *
58
+ * The predicate combines explicit pins (`pinned:true`/`_pin`) with the policy's
59
+ * auto-selected messages, matched by identity against `messages` (compress
60
+ * filters the same array, so identity is stable).
61
+ */
62
+ export function buildAutoPinPredicate(messages, opt) {
63
+ const cfg = resolveAutoPinConfig(opt);
64
+ if (!cfg) return null;
65
+ if (!Array.isArray(messages)) {
66
+ return (m) => !!m && (m.pinned === true || m._pin === true);
67
+ }
68
+
69
+ const auto = new Set();
70
+ if (cfg.firstUserGoal) {
71
+ const firstUser = messages.find((m) => m && m.role === "user");
72
+ // Skip a first turn that is itself huge — pinning it would defeat the point
73
+ // (it would dominate the very budget compaction is trying to reclaim).
74
+ if (
75
+ firstUser &&
76
+ estimateTokens(contentString(firstUser)) <= cfg.maxPinTokens
77
+ ) {
78
+ auto.add(firstUser);
79
+ }
80
+ }
81
+
82
+ return (m) => !!m && (m.pinned === true || m._pin === true || auto.has(m));
83
+ }
84
+
85
+ /**
86
+ * Explain what the policy would pin for `messages` (for `--dry-run` / logging).
87
+ * Returns `{ enabled, reasons: [{ role, preview, why }] }`.
88
+ */
89
+ export function describeAutoPin(messages, opt) {
90
+ const cfg = resolveAutoPinConfig(opt);
91
+ if (!cfg) return { enabled: false, reasons: [] };
92
+ const reasons = [];
93
+ const list = Array.isArray(messages) ? messages : [];
94
+ for (const m of list) {
95
+ if (m && (m.pinned === true || m._pin === true)) {
96
+ reasons.push({ role: m.role, preview: preview(m), why: "explicit pin" });
97
+ }
98
+ }
99
+ if (cfg.firstUserGoal) {
100
+ const firstUser = list.find((m) => m && m.role === "user");
101
+ if (
102
+ firstUser &&
103
+ estimateTokens(contentString(firstUser)) <= cfg.maxPinTokens
104
+ ) {
105
+ reasons.push({
106
+ role: "user",
107
+ preview: preview(firstUser),
108
+ why: "original task (first user turn)",
109
+ });
110
+ }
111
+ }
112
+ return { enabled: true, reasons };
113
+ }
114
+
115
+ function preview(m) {
116
+ return contentString(m).replace(/\s+/g, " ").slice(0, 80);
117
+ }
@@ -353,6 +353,74 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
353
353
  tags: ["tool:search_files", "contract:coding-agent", "tier:mvp"],
354
354
  },
355
355
  },
356
+ {
357
+ name: "code_intelligence",
358
+ title: "Code Intelligence",
359
+ kind: "analysis",
360
+ tier: "extension",
361
+ description:
362
+ "Semantic code navigation via a Language Server (LSP): resolve a symbol's " +
363
+ "definition, find its references, get type/hover info, list document or " +
364
+ "workspace symbols, read diagnostics, or preview a rename — WITHOUT text " +
365
+ "guessing. Prefer this over search_files when you need exact symbol " +
366
+ "semantics. Positions are 1-based (line/col). Degrades gracefully to a " +
367
+ "'not available' result when no language server is installed for the file.",
368
+ inputSchema: {
369
+ type: "object",
370
+ properties: {
371
+ action: {
372
+ type: "string",
373
+ enum: [
374
+ "definition",
375
+ "references",
376
+ "hover",
377
+ "document_symbols",
378
+ "workspace_symbols",
379
+ "diagnostics",
380
+ "rename_preview",
381
+ ],
382
+ description: "Which semantic query to run",
383
+ },
384
+ file: {
385
+ type: "string",
386
+ description:
387
+ "File path (relative to cwd). Required for every action except workspace_symbols.",
388
+ },
389
+ line: {
390
+ type: "integer",
391
+ description:
392
+ "1-based line number of the symbol (definition/references/hover/rename_preview).",
393
+ },
394
+ col: {
395
+ type: "integer",
396
+ description:
397
+ "1-based column of the symbol (definition/references/hover/rename_preview).",
398
+ },
399
+ query: {
400
+ type: "string",
401
+ description: "Symbol name to search for (workspace_symbols).",
402
+ },
403
+ new_name: {
404
+ type: "string",
405
+ description: "Proposed new identifier (rename_preview).",
406
+ },
407
+ },
408
+ required: ["action"],
409
+ },
410
+ ...TOOL_POLICY_METADATA.code_intelligence,
411
+ permissions: {
412
+ level: "readonly",
413
+ scopes: ["filesystem:read", "process:spawn"],
414
+ },
415
+ telemetry: {
416
+ category: "analysis",
417
+ tags: [
418
+ "tool:code_intelligence",
419
+ "contract:coding-agent",
420
+ "tier:extension",
421
+ ],
422
+ },
423
+ },
356
424
  {
357
425
  name: "list_dir",
358
426
  title: "List Directory",
@@ -422,7 +490,8 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
422
490
  properties: {
423
491
  category: {
424
492
  type: "string",
425
- description: "Filter by category (e.g. development, automation, data)",
493
+ description:
494
+ "Filter by category (e.g. development, automation, data)",
426
495
  },
427
496
  query: {
428
497
  type: "string",
@@ -491,7 +560,7 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
491
560
  agent: {
492
561
  type: "string",
493
562
  description:
494
- 'Optional name of a pre-defined subagent from .chainlesschain/agents/ or .claude/agents/ (e.g. "review:security"). Loads that file\'s system prompt + tool allow-list. Run `cc agents list` to see them. Explicit `role`/`tools` override the agent\'s values.',
563
+ "Optional name of a pre-defined subagent from .chainlesschain/agents/ or .claude/agents/ (e.g. \"review:security\"). Loads that file's system prompt + tool allow-list. Run `cc agents list` to see them. Explicit `role`/`tools` override the agent's values.",
495
564
  },
496
565
  role: {
497
566
  type: "string",
@@ -588,7 +657,16 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
588
657
  },
589
658
  provider: {
590
659
  type: "string",
591
- enum: ["auto", "tavily", "brave", "bocha", "qianfan", "duckduckgo", "searxng", "baidu"],
660
+ enum: [
661
+ "auto",
662
+ "tavily",
663
+ "brave",
664
+ "bocha",
665
+ "qianfan",
666
+ "duckduckgo",
667
+ "searxng",
668
+ "baidu",
669
+ ],
592
670
  description:
593
671
  "Override the configured search backend for this call (default: from config / auto)",
594
672
  },
@@ -763,11 +841,7 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
763
841
  },
764
842
  telemetry: {
765
843
  category: "search",
766
- tags: [
767
- "tool:search_sessions",
768
- "contract:coding-agent",
769
- "tier:extension",
770
- ],
844
+ tags: ["tool:search_sessions", "contract:coding-agent", "tier:extension"],
771
845
  },
772
846
  },
773
847
  ]);
@@ -23,7 +23,11 @@ const TOOL_DECISIONS = Object.freeze({
23
23
  REQUIRE_CONFIRMATION: "require_confirmation",
24
24
  });
25
25
 
26
- const PLAN_APPROVED_STATES = Object.freeze(["approved", "executing", "completed"]);
26
+ const PLAN_APPROVED_STATES = Object.freeze([
27
+ "approved",
28
+ "executing",
29
+ "completed",
30
+ ]);
27
31
 
28
32
  const READ_ONLY_GIT_SUBCOMMANDS = Object.freeze([
29
33
  "status",
@@ -64,6 +68,20 @@ const TOOL_POLICY_METADATA = Object.freeze({
64
68
  approvalFlow: "auto",
65
69
  isReadOnly: true,
66
70
  },
71
+ code_intelligence: {
72
+ // Read-only semantic navigation via LSP (definition/references/hover/
73
+ // symbols/diagnostics/rename-preview). It never mutates the workspace — a
74
+ // rename returns a preview, not an applied edit — so it is auto-approved and
75
+ // available in plan mode, like search_files.
76
+ riskLevel: RISK_LEVELS.LOW,
77
+ category: TOOL_CATEGORIES.ANALYZE,
78
+ availableInPlanMode: true,
79
+ planModeBehavior: "allow",
80
+ requiresPlanApproval: false,
81
+ requiresConfirmation: false,
82
+ approvalFlow: "auto",
83
+ isReadOnly: true,
84
+ },
67
85
  edit_file: {
68
86
  riskLevel: RISK_LEVELS.MEDIUM,
69
87
  category: TOOL_CATEGORIES.WRITE,
@@ -295,7 +313,9 @@ function isDangerousGitCommand(command) {
295
313
  case "checkout":
296
314
  // Discard working-tree changes: `checkout -- <path>`, `checkout .`,
297
315
  // or `-f`/`--force` (a plain branch checkout is not flagged).
298
- return lower.includes("--") || lower.includes(".") || has("-f", "--force");
316
+ return (
317
+ lower.includes("--") || lower.includes(".") || has("-f", "--force")
318
+ );
299
319
  case "restore":
300
320
  // Worktree restore discards changes; a pure `--staged` restore only
301
321
  // unstages and is recoverable.
@@ -321,9 +341,9 @@ function isDangerousGitCommand(command) {
321
341
  // or an explicit `--delete --force`.
322
342
  return rest.includes("-D") || (has("--delete") && has("-f", "--force"));
323
343
  case "stash":
324
- return ["drop", "clear"].includes((lower[0] || ""));
344
+ return ["drop", "clear"].includes(lower[0] || "");
325
345
  case "reflog":
326
- return ["expire", "delete"].includes((lower[0] || ""));
346
+ return ["expire", "delete"].includes(lower[0] || "");
327
347
  case "update-ref":
328
348
  return has("-d", "--delete");
329
349
  case "filter-branch":
@@ -333,9 +353,14 @@ function isDangerousGitCommand(command) {
333
353
  return lower.some((t) => t.startsWith("--prune"));
334
354
  case "rebase":
335
355
  // History rewrite; control sub-actions (--abort/--continue/etc.) are safe.
336
- return !["--abort", "--continue", "--skip", "--quit", "--edit-todo", "--show-current-patch"].includes(
337
- lower[0] || "",
338
- );
356
+ return ![
357
+ "--abort",
358
+ "--continue",
359
+ "--skip",
360
+ "--quit",
361
+ "--edit-todo",
362
+ "--show-current-patch",
363
+ ].includes(lower[0] || "");
339
364
  default:
340
365
  return false;
341
366
  }
@@ -13,11 +13,20 @@
13
13
  * 3 models) and 2.1.152 switches to the configured fallback when the primary
14
14
  * model is "not found".
15
15
  *
16
- * Same-provider only: each hop swaps `options.model`, keeping the configured
17
- * provider / baseUrl / apiKey. Cross-provider fallback is a larger feature.
16
+ * Cross-provider fallback (v2): a fallback entry may name a DIFFERENT provider
17
+ * as `provider:model` (e.g. `openai:gpt-4o`, `ollama:qwen2.5:7b`). A cross-
18
+ * provider hop swaps provider + baseUrl + apiKey too, resolving the target
19
+ * provider's connection from BUILT_IN_PROVIDERS + its `apiKeyEnv`. SECURITY: a
20
+ * cross-provider hop is used ONLY when the user EXPLICITLY named that provider in
21
+ * the fallback chain AND the target provider's API key is present in the env —
22
+ * we never reuse the primary provider's key against a different vendor (that
23
+ * would be the silent auth-substitution this project forbids). A cross-provider
24
+ * entry whose key is missing is SKIPPED with a clear reason, not attempted with
25
+ * the wrong credential.
18
26
  */
19
27
 
20
28
  import { chatWithTools } from "./agent-core.js";
29
+ import { BUILT_IN_PROVIDERS } from "../lib/llm-providers.js";
21
30
 
22
31
  // Claude Code caps the fallback chain at 3 backups; mirror that here so a
23
32
  // mis-configured comma list can't fan out into an unbounded retry storm.
@@ -142,6 +151,85 @@ export function normalizeFallbackModels(input) {
142
151
  return out;
143
152
  }
144
153
 
154
+ /**
155
+ * Parse a fallback entry into `{ provider, model }`. A `provider:model` prefix
156
+ * is recognized ONLY when the prefix is a KNOWN provider name — so an
157
+ * ollama-style model id that itself contains a colon (`qwen2.5:7b`) is NOT
158
+ * mis-split, while `ollama:qwen2.5:7b` correctly yields provider `ollama` +
159
+ * model `qwen2.5:7b`. A plain `model` (no known-provider prefix) means
160
+ * "same provider" (provider: null).
161
+ *
162
+ * @param {string} entry
163
+ * @param {Set<string>} [knownProviders] provider names to recognize as prefixes
164
+ * @returns {{ provider: string|null, model: string }}
165
+ */
166
+ export function parseFallbackEntry(entry, knownProviders) {
167
+ const known =
168
+ knownProviders || new Set(Object.keys(BUILT_IN_PROVIDERS || {}));
169
+ const s = String(entry || "").trim();
170
+ const idx = s.indexOf(":");
171
+ if (idx > 0) {
172
+ const head = s.slice(0, idx);
173
+ const rest = s.slice(idx + 1);
174
+ if (rest && known.has(head)) {
175
+ return { provider: head, model: rest };
176
+ }
177
+ }
178
+ return { provider: null, model: s };
179
+ }
180
+
181
+ /**
182
+ * Resolve a fallback entry into a concrete call target. For a SAME-provider hop
183
+ * (no known provider prefix), only the model changes. For a CROSS-provider hop,
184
+ * resolve the target provider's baseUrl + apiKey (from its `apiKeyEnv` in the
185
+ * environment); if the target needs a key and none is set, the hop is
186
+ * `unavailable` (skipped) — we never fall back onto a different vendor with the
187
+ * wrong/absent credential.
188
+ *
189
+ * @param {string} entry
190
+ * @param {object} [opts] { env, providers, knownProviders }
191
+ * @returns {{ model, provider?, baseUrl?, apiKey?, crossProvider:boolean } | { unavailable:true, reason:string, model:string, provider:string }}
192
+ */
193
+ export function resolveFallbackTarget(entry, opts = {}) {
194
+ const env = opts.env || process.env;
195
+ const providers = opts.providers || BUILT_IN_PROVIDERS || {};
196
+ const { provider, model } = parseFallbackEntry(
197
+ entry,
198
+ opts.knownProviders || new Set(Object.keys(providers)),
199
+ );
200
+ if (!provider) {
201
+ return { model, crossProvider: false };
202
+ }
203
+ const def = providers[provider];
204
+ if (!def) {
205
+ return {
206
+ unavailable: true,
207
+ reason: `unknown provider "${provider}"`,
208
+ provider,
209
+ model,
210
+ };
211
+ }
212
+ let apiKey = null;
213
+ if (def.apiKeyEnv) {
214
+ apiKey = env[def.apiKeyEnv] || null;
215
+ if (!apiKey) {
216
+ return {
217
+ unavailable: true,
218
+ reason: `${provider} fallback skipped: ${def.apiKeyEnv} not set`,
219
+ provider,
220
+ model,
221
+ };
222
+ }
223
+ }
224
+ return {
225
+ model,
226
+ provider,
227
+ baseUrl: def.baseUrl || null,
228
+ apiKey,
229
+ crossProvider: true,
230
+ };
231
+ }
232
+
145
233
  /**
146
234
  * Build a chatFn that walks an ordered fallback chain when the primary fails.
147
235
  *
@@ -168,6 +256,9 @@ export function makeFallbackChatFn(opts = {}) {
168
256
  const isModelNotFound = opts.isModelNotFound || isModelNotFoundError;
169
257
  const onFallback = opts.onFallback;
170
258
 
259
+ const env = opts.env || process.env;
260
+ const providers = opts.providers || BUILT_IN_PROVIDERS || {};
261
+
171
262
  return async function chatWithFallback(messages, options = {}) {
172
263
  try {
173
264
  return await baseChatFn(messages, options);
@@ -175,8 +266,30 @@ export function makeFallbackChatFn(opts = {}) {
175
266
  let err = firstErr;
176
267
  let lastTried = options.model;
177
268
  for (const candidate of models) {
178
- // Skip a no-op hop (fallback identical to the model just attempted).
179
- if (!candidate || candidate === lastTried) continue;
269
+ const target = resolveFallbackTarget(candidate, { env, providers });
270
+ // A cross-provider hop whose key is absent (or an unknown provider) is
271
+ // skipped — never attempted with the wrong credential. Surface why.
272
+ if (target.unavailable) {
273
+ if (typeof onFallback === "function") {
274
+ try {
275
+ onFallback({
276
+ from: lastTried,
277
+ to: candidate,
278
+ skipped: true,
279
+ reason: target.reason,
280
+ error: err?.message || String(err),
281
+ });
282
+ } catch {
283
+ /* best-effort */
284
+ }
285
+ }
286
+ continue;
287
+ }
288
+ // Skip a no-op hop (same provider + same model just attempted).
289
+ const sameModel = target.model === lastTried;
290
+ const sameProvider =
291
+ !target.crossProvider || target.provider === options.provider;
292
+ if (!target.model || (sameModel && sameProvider)) continue;
180
293
  // Only advance the chain for transient failures or a missing primary;
181
294
  // a real bad-request / auth error is surfaced immediately.
182
295
  if (!isRetryable(err) && !isModelNotFound(err)) throw err;
@@ -184,16 +297,30 @@ export function makeFallbackChatFn(opts = {}) {
184
297
  try {
185
298
  onFallback({
186
299
  from: lastTried,
187
- to: candidate,
300
+ to: target.crossProvider
301
+ ? `${target.provider}:${target.model}`
302
+ : target.model,
303
+ crossProvider: target.crossProvider,
188
304
  error: err?.message || String(err),
189
305
  });
190
306
  } catch {
191
307
  // Notification is best-effort — never mask the retry.
192
308
  }
193
309
  }
194
- lastTried = candidate;
310
+ lastTried = target.model;
311
+ // Build the per-hop options: same-provider swaps only the model; a
312
+ // cross-provider hop also swaps provider/baseUrl/apiKey.
313
+ const hopOptions = target.crossProvider
314
+ ? {
315
+ ...options,
316
+ model: target.model,
317
+ provider: target.provider,
318
+ baseUrl: target.baseUrl,
319
+ apiKey: target.apiKey,
320
+ }
321
+ : { ...options, model: target.model };
195
322
  try {
196
- return await baseChatFn(messages, { ...options, model: candidate });
323
+ return await baseChatFn(messages, hopOptions);
197
324
  } catch (nextErr) {
198
325
  err = nextErr;
199
326
  }