chainlesschain 0.162.149 → 0.162.151

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 (285) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -2
  3. package/src/assets/web-panel/assets/{AIOps-BJif14yR.js → AIOps-DuEOzebi.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXek6gJY.js → ActionButton-C6GiJa5H.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BgQhdAJi.js → Analytics-BBIiL_pO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DTYl5NtR.js → AppLayout-CuI2gZRy.js} +3 -3
  7. package/src/assets/web-panel/assets/{Audit-BzATQAeR.js → Audit-CbUjfr-K.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-BzdPEQhL.js → Backup-CsSW9ljf.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-BM0KVh8E.js → BaseInput-kM3rLe7F.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-D7fHXHkz.js → Chat-kfE51YN3.js} +4 -4
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Wm34RR-b.js → ChatBubbleRenderer-Bg2FTW6A.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-CAiSQ9vO.js → Checkbox-jYb1uj-M.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-Df40CrEe.js → Codegen-X9XiD5Lj.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DIT2fNFU.js → Col-BfbHu1xI.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CSTIbW2k.js → Community-c4oJMFS-.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-DtqXZZUi.js → Compact-BFmnZpH7.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CUtzw6o7.js → Compliance-s9shVHBS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-CezmlnmU.js → Cowork-C5da--TC.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-P4BITarg.js → Cron-9kNbyNE4.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-BRRiYwvq.js → Crosschain-C1k3NJD4.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-L5ijB9i4.js → DID-DkheEvNK.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-CFxro4ob.js → Dashboard-C3FuSGmJ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-BmAFCQ71.js → Dropdown-CAV6FMFD.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-DJBCEuon.js → EmailListRenderer-Cc-Ulc4l.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-z4oLq6eT.js → FamilyGuardDashboard-8CitHEtv.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-DsVCpHMF.js → Federation-DFSZ5KDt.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-DP3bP5th.js → FormItemContext-CEWCtnG2.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-BAXP2Gc6.js → GenericCardRenderer-CwRzNzmX.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-I3g_bAw9.js → Git-0rgDM7cJ.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-jHS5-ioS.js → Governance-CfBKtK9F.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-CPL7sfx9.js → Inference-CzcUVvud.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-Jf236h4C.js → KnowledgeGraph-Dwc4YL4A.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-Cm2tcSKg.js → Logs-DgFPwR2F.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CZK82rhi.js → Marketplace-DnpPTbGU.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-CZCeji7a.js → McpTools-DRxxINyW.js} +4 -4
  36. package/src/assets/web-panel/assets/{Memory-3l2KVS9k.js → Memory-OhmW_6Z3.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-VYdpoLh6.js → MobileBridge-DkTW-RK5.js} +1 -1
  38. package/src/assets/web-panel/assets/{MobileProjects-CiB9cmm1.js → MobileProjects-DpCGV_lI.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-wCFZbBuL.js → Mtc--vmkL0AS.js} +2 -2
  40. package/src/assets/web-panel/assets/{MtcAudit-1a3THIyG.js → MtcAudit-DGoFWjD4.js} +4 -4
  41. package/src/assets/web-panel/assets/{Multisig-DMzLB2hG.js → Multisig-B4kWgq95.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-D9wZbf6l.js → NLProgramming-YQwvommE.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-hrFe2ZM-.js → Notes-CgjtUADJ.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-CHGX5Jwm.js → NotificationSettings-6JDrpBG5.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-PM6IlxjO.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-By7FkhtY.js → Organization-8McOT_OF.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow--khGSzJn.js → Overflow-DAZpSCGc.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-CAG9dH35.js → P2P-B7l2ZWLy.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-B3SQZmK9.js → PdhVaultBrowser-W1Ud-NAL.js} +3 -3
  50. package/src/assets/web-panel/assets/{Permissions-D35ec6JA.js → Permissions-Czm_w9u_.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-CxBF7hW_.js → PersonalDataHub-B4F0_ZVM.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-s6EtOO1s.js → Pipeline-B37aFiWv.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-BbJYmWmO.js → Privacy-94KBnRrD.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-VGQq1jMT.js → ProjectInit-B5h8_dlh.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-B1ew3Teh.js → ProjectSettings-lmeI3Lpm.js} +2 -2
  56. package/src/assets/web-panel/assets/Projects-D5AYOasl.js +1 -0
  57. package/src/assets/web-panel/assets/{Providers-mrO47FIK.js → Providers-B3cNWwJg.js} +1 -1
  58. package/src/assets/web-panel/assets/{QrScannerModal-IJF2mHyw.js → QrScannerModal-CHRvOw1R.js} +1 -1
  59. package/src/assets/web-panel/assets/{QuickAsk-CU0wN_Z0.js → QuickAsk-D1JMgFEd.js} +1 -1
  60. package/src/assets/web-panel/assets/{Recommend-DhN37R6G.js → Recommend-EYWGR79p.js} +1 -1
  61. package/src/assets/web-panel/assets/{RemoteSession-D_xOX_hu.js → RemoteSession-BAMC2pJt.js} +2 -2
  62. package/src/assets/web-panel/assets/{Reputation-D_Ssv7uH.js → Reputation-X2Hk1XG_.js} +1 -1
  63. package/src/assets/web-panel/assets/{Row-Kx5dKxX7.js → Row-Eq98LRtU.js} +1 -1
  64. package/src/assets/web-panel/assets/{RssFeed-BlGwqZkY.js → RssFeed-i76s6z_o.js} +2 -2
  65. package/src/assets/web-panel/assets/{Search-hLFQzxpb.js → Search-D_Es7CAI.js} +1 -1
  66. package/src/assets/web-panel/assets/{Security-DMlvsVt4.js → Security-kayD2e94.js} +3 -3
  67. package/src/assets/web-panel/assets/{Services-Bd0Qsj2m.js → Services-Ctsm6ul8.js} +2 -2
  68. package/src/assets/web-panel/assets/{Skeleton-DFclq9X3.js → Skeleton-CRNYlfdf.js} +1 -1
  69. package/src/assets/web-panel/assets/{Skills-BNtln2qY.js → Skills-DkZYpF4-.js} +1 -1
  70. package/src/assets/web-panel/assets/{Sla-Dch5H19G.js → Sla-DXkoBcOP.js} +1 -1
  71. package/src/assets/web-panel/assets/{SpeechSettings-2UfQcU1g.js → SpeechSettings-DXi_GI1d.js} +1 -1
  72. package/src/assets/web-panel/assets/{SyncSettings-CD9YQr4i.js → SyncSettings-rNKpfomn.js} +2 -2
  73. package/src/assets/web-panel/assets/{Tasks-BNu-7MzI.js → Tasks-XF4uPtG-.js} +1 -1
  74. package/src/assets/web-panel/assets/{Templates-D1n9CaIR.js → Templates-i0Hzfeyu.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tenant-DenL3iBW.js → Tenant-sMpHAi27.js} +1 -1
  76. package/src/assets/web-panel/assets/Terminal-BoZvaYAl.js +3 -0
  77. package/src/assets/web-panel/assets/{TimelineRenderer-D87IfukO.js → TimelineRenderer-yuC6K7Rf.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tokens-BkiZc8E3.js → Tokens-B8O8i0s6.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trigger-BNRSytGN.js → Trigger-Bv6yrlCr.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trust-Bwbd4X2m.js → Trust-C4mW95Ev.js} +1 -1
  81. package/src/assets/web-panel/assets/{UkeySign-B8O2bs0o.js → UkeySign-DV6yKaHn.js} +1 -1
  82. package/src/assets/web-panel/assets/{VideoEditing-C2fL8zmH.js → VideoEditing-UI479-sD.js} +1 -1
  83. package/src/assets/web-panel/assets/{Wallet-BORYDdE1.js → Wallet-M5oV9EVP.js} +4 -4
  84. package/src/assets/web-panel/assets/{WebAuthn-C0V5S2FM.js → WebAuthn-DDyDHPDR.js} +4 -4
  85. package/src/assets/web-panel/assets/{WorkflowEditor-2dR0fcHL.js → WorkflowEditor-RoBv4rqk.js} +1 -1
  86. package/src/assets/web-panel/assets/{chat-BmuAFAn8.js → chat-Cw1mE5LS.js} +1 -1
  87. package/src/assets/web-panel/assets/{colors-CN3smMcK.js → colors-BFP62Gwf.js} +1 -1
  88. package/src/assets/web-panel/assets/{compact-item-DCnxb4kW.js → compact-item-17HL6pyC.js} +1 -1
  89. package/src/assets/web-panel/assets/{createContext-vlR43pHn.js → createContext-D55X4fZX.js} +1 -1
  90. package/src/assets/web-panel/assets/devWarning-CqbHYuRc.js +1 -0
  91. package/src/assets/web-panel/assets/{hasIn-D04tHYFd.js → hasIn-D3E5mNsP.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-DbVsQBOH.js → index--44J50mc.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-TslMTwNy.js → index-ASPYTzMK.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CTOCnIQ7.js → index-BC6zxOlU.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-ljuBiQW9.js → index-BEa1RW4c.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-jzR7Ujuw.js → index-BQmZg5si.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BudvKQfG.js → index-BY_xZ2jy.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CP9m9Ggr.js → index-BgN5G5vF.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-Cr-A0FYJ.js → index-BnGpft-Z.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D5LZVZ0L.js → index-Bq83UBDr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-ToUh2b1-.js → index-Bzpp2z2S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DbADHwhr.js → index-CFYGn88j.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-ClhAHDK3.js → index-CGyTos4p.js} +1 -1
  104. package/src/assets/web-panel/assets/index-CKBI1U5K.js +1 -0
  105. package/src/assets/web-panel/assets/{index-C-6NO5il.js → index-CLEzsZ8A.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D8vwp4qp.js → index-CR9rNUvi.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-_FIkN3jd.js → index-CW6LLoJO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D65_XseV.js → index-C_MfcrLf.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CnXebZLL.js → index-Cdvk2_2q.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D_n8_vfI.js → index-CjCwi7we.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CkLp6DTO.js +1 -0
  112. package/src/assets/web-panel/assets/{index-DDwLgQY9.js → index-CvCFKojj.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-430S68MN.js → index-D4LHSde4.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BXRg8GFQ.js → index-DCLu84x-.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DTtRscUa.js → index-DD_qGBrt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BasvsWDM.js → index-DIRvdwBX.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BeGDEXFs.js → index-DOjA8bvj.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CsAsCPJ6.js → index-Da27u6j0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D28DeAAB.js → index-DkBVYlE4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CngCC8hC.js → index-DtAnPBWP.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-B78xL3s2.js → index-DzG7mkgW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIpY0qM5.js → index-H9_X1Cg_.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C5Sxb1sE.js → index-ITeEHb8L.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BTPEZTk1.js → index-KtLJXn4s.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-hpvvtAZ3.js → index-RYZgFIwo.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-HhNjnCfe.js → index-gAVxDiTZ.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DTWg-18U.js → index-paVubXzn.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BqhASEL4.js → index-tUH_6bdJ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DYAtmqiv.js → index-tVRPgYGr.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-0DnaZGkC.js → index-uSsiwgZ2.js} +1 -1
  131. package/src/assets/web-panel/assets/{initDefaultProps-C3wUb4je.js → initDefaultProps-Ckp7xVZS.js} +1 -1
  132. package/src/assets/web-panel/assets/{motion-D-jYFUNA.js → motion-DUZgCpn6.js} +1 -1
  133. package/src/assets/web-panel/assets/{move-tqb8nwJ2.js → move-B2rttglH.js} +1 -1
  134. package/src/assets/web-panel/assets/{mtc-parser-DSOjMJMz.js → mtc-parser-BYWcLTuN.js} +1 -1
  135. package/src/assets/web-panel/assets/{omit-CJ7VimIW.js → omit-DhHqKeFx.js} +1 -1
  136. package/src/assets/web-panel/assets/{pickAttrs-qXiG1iT3.js → pickAttrs-ANpJaWMO.js} +1 -1
  137. package/src/assets/web-panel/assets/{placementArrow-CkGBcy1Z.js → placementArrow-CfZWe7CA.js} +1 -1
  138. package/src/assets/web-panel/assets/{responsiveObserve-D3WvTlLO.js → responsiveObserve-D7Xz2y-R.js} +1 -1
  139. package/src/assets/web-panel/assets/{slide-BEE2ftge.js → slide-DekqCBY9.js} +1 -1
  140. package/src/assets/web-panel/assets/{statusUtils-CsfBpxiG.js → statusUtils-BET6XU7h.js} +1 -1
  141. package/src/assets/web-panel/assets/{styleChecker-xPrIiJPI.js → styleChecker-qHmJjV62.js} +1 -1
  142. package/src/assets/web-panel/assets/{useFlexGapSupport-C5Yd-SCR.js → useFlexGapSupport-BoMCTNyu.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFs-D_MDS8HI.js → useFs-TdW-NgFC.js} +1 -1
  144. package/src/assets/web-panel/assets/{usePersonalDataHub-BqJh-usA.js → usePersonalDataHub-Dyox7wCu.js} +1 -1
  145. package/src/assets/web-panel/assets/{vnode-chm_p-gz.js → vnode-XUn5S8CP.js} +1 -1
  146. package/src/assets/web-panel/assets/{zoom-3-YDNz0Z.js → zoom-Ls77i8hn.js} +1 -1
  147. package/src/assets/web-panel/index.html +1 -1
  148. package/src/command-manifest.json +8 -1
  149. package/src/commands/a2a.js +19 -4
  150. package/src/commands/activitypub.js +20 -3
  151. package/src/commands/agent.js +27 -5
  152. package/src/commands/audit.js +40 -24
  153. package/src/commands/changelog.js +148 -0
  154. package/src/commands/codeintel.js +68 -0
  155. package/src/commands/collab.js +15 -2
  156. package/src/commands/compliance.js +5 -0
  157. package/src/commands/config.js +4 -1
  158. package/src/commands/dao.js +86 -14
  159. package/src/commands/dev.js +2 -0
  160. package/src/commands/did.js +7 -1
  161. package/src/commands/dlp.js +23 -1
  162. package/src/commands/economy.js +29 -3
  163. package/src/commands/eval.js +7 -0
  164. package/src/commands/evomap.js +26 -0
  165. package/src/commands/governance.js +17 -3
  166. package/src/commands/hardening.js +18 -0
  167. package/src/commands/incentive.js +5 -0
  168. package/src/commands/init.js +46 -2
  169. package/src/commands/kg.js +4 -0
  170. package/src/commands/loop.js +6 -1
  171. package/src/commands/lowcode.js +15 -2
  172. package/src/commands/marketplace.js +40 -6
  173. package/src/commands/matrix.js +20 -1
  174. package/src/commands/memory.js +4 -1
  175. package/src/commands/mtc.js +7 -1
  176. package/src/commands/nostr.js +31 -4
  177. package/src/commands/note.js +7 -4
  178. package/src/commands/plugin.js +125 -5
  179. package/src/commands/pqc.js +5 -0
  180. package/src/commands/reputation.js +10 -1
  181. package/src/commands/scim.js +6 -0
  182. package/src/commands/session.js +32 -10
  183. package/src/commands/siem.js +11 -0
  184. package/src/commands/sla.js +18 -3
  185. package/src/commands/social.js +38 -5
  186. package/src/commands/sso.js +10 -2
  187. package/src/commands/stress.js +23 -4
  188. package/src/commands/team.js +9 -2
  189. package/src/commands/tech.js +2 -0
  190. package/src/commands/tenant.js +10 -1
  191. package/src/commands/terraform.js +6 -0
  192. package/src/commands/update.js +1 -1
  193. package/src/commands/zkp.js +20 -0
  194. package/src/data/changelog.json +189 -0
  195. package/src/gateways/ws/message-dispatcher.js +5 -2
  196. package/src/gateways/ws/remote-session-protocol.js +110 -42
  197. package/src/harness/jsonl-session-store.js +12 -5
  198. package/src/harness/plugin-manager.js +16 -4
  199. package/src/harness/remote-command-ledger.js +46 -15
  200. package/src/harness/worktree-isolator.js +16 -6
  201. package/src/index.js +2 -0
  202. package/src/lib/__tests__/logger.test.js +5 -2
  203. package/src/lib/a2a-protocol.js +25 -1
  204. package/src/lib/activitypub-bridge.js +61 -0
  205. package/src/lib/agent-core.js +2 -0
  206. package/src/lib/agent-economy.js +262 -29
  207. package/src/lib/agent-network.js +7 -1
  208. package/src/lib/agent-team/team-runner.js +25 -9
  209. package/src/lib/agent-team/team-worktree.js +36 -4
  210. package/src/lib/async-hook-supervisor.cjs +102 -16
  211. package/src/lib/audit-logger.js +7 -6
  212. package/src/lib/autonomous-developer.js +60 -0
  213. package/src/lib/changelog.js +252 -0
  214. package/src/lib/chat-core.js +17 -4
  215. package/src/lib/collaboration-governance.js +56 -0
  216. package/src/lib/community-governance.js +68 -0
  217. package/src/lib/compliance-manager.js +63 -0
  218. package/src/lib/cross-chain.js +5 -0
  219. package/src/lib/dao-governance.js +151 -2
  220. package/src/lib/did-manager.js +23 -10
  221. package/src/lib/dlp-engine.js +70 -0
  222. package/src/lib/eval/runner.js +89 -1
  223. package/src/lib/eval/tasks.js +170 -0
  224. package/src/lib/eval/trend.js +16 -1
  225. package/src/lib/evolution-system.js +92 -0
  226. package/src/lib/evomap-federation.js +55 -0
  227. package/src/lib/evomap-governance.js +94 -0
  228. package/src/lib/fatal-handler.js +17 -1
  229. package/src/lib/hardening-manager.js +73 -0
  230. package/src/lib/hierarchical-memory.js +14 -8
  231. package/src/lib/knowledge-exporter.js +8 -2
  232. package/src/lib/knowledge-graph.js +79 -0
  233. package/src/lib/llm-providers.js +23 -14
  234. package/src/lib/logger.js +4 -1
  235. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  236. package/src/lib/lsp/__tests__/lsp-client.test.js +11 -0
  237. package/src/lib/lsp/__tests__/lsp-manager.test.js +69 -0
  238. package/src/lib/lsp/benchmark.js +257 -0
  239. package/src/lib/lsp/lsp-client.js +30 -9
  240. package/src/lib/lsp/lsp-manager.js +54 -2
  241. package/src/lib/lsp/lsp-server-registry.js +15 -4
  242. package/src/lib/matrix-bridge.js +84 -0
  243. package/src/lib/memory-manager.js +5 -3
  244. package/src/lib/nostr-bridge.js +53 -0
  245. package/src/lib/permission-engine.js +22 -4
  246. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  247. package/src/lib/plugin-runtime/hooks.js +20 -4
  248. package/src/lib/plugin-runtime/install.js +9 -1
  249. package/src/lib/plugin-runtime/monitors.js +20 -4
  250. package/src/lib/plugin-runtime/policy.js +9 -1
  251. package/src/lib/plugin-runtime/remote-source.js +240 -0
  252. package/src/lib/plugin-runtime/signature.js +16 -0
  253. package/src/lib/pqc-manager.js +64 -0
  254. package/src/lib/reputation-optimizer.js +101 -0
  255. package/src/lib/sandbox-egress-proxy.js +195 -59
  256. package/src/lib/sandbox-fs-policy.js +112 -0
  257. package/src/lib/sandbox-network-policy.js +44 -5
  258. package/src/lib/sandbox-v2.js +14 -14
  259. package/src/lib/scim-manager.js +36 -0
  260. package/src/lib/session-manager.js +5 -2
  261. package/src/lib/settings-hooks.cjs +1 -0
  262. package/src/lib/siem-exporter.js +45 -0
  263. package/src/lib/skill-loader.js +20 -2
  264. package/src/lib/skill-marketplace.js +83 -0
  265. package/src/lib/sla-manager.js +88 -0
  266. package/src/lib/social-manager.js +74 -0
  267. package/src/lib/stress-tester.js +65 -0
  268. package/src/lib/tech-learning-engine.js +75 -0
  269. package/src/lib/telemetry/span-recorder.js +12 -1
  270. package/src/lib/tenant-saas.js +107 -0
  271. package/src/lib/terraform-manager.js +67 -0
  272. package/src/lib/token-incentive.js +180 -29
  273. package/src/lib/wallet-manager.js +81 -35
  274. package/src/lib/zkp-engine.js +87 -0
  275. package/src/repl/agent-repl.js +17 -2
  276. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  277. package/src/runtime/agent-core.js +241 -68
  278. package/src/runtime/auto-pin.js +117 -0
  279. package/src/runtime/headless-runner.js +35 -0
  280. package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +0 -1
  281. package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +0 -1
  282. package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +0 -3
  283. package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +0 -1
  284. package/src/assets/web-panel/assets/index-Cg2ldRfU.js +0 -1
  285. package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +0 -1
@@ -64,6 +64,7 @@ import {
64
64
  PromptCompressor,
65
65
  getContextWindow,
66
66
  } from "../harness/prompt-compressor.js";
67
+ import { buildAutoPinPredicate } from "../runtime/auto-pin.js";
67
68
  import { feature } from "../lib/feature-flags.js";
68
69
  import { recordCompressionMetric } from "../lib/compression-telemetry.js";
69
70
  import {
@@ -416,10 +417,15 @@ export async function startAgentRepl(options = {}) {
416
417
  // Unset → agent-core's 180s default (matches cc chat/ask). Set to 0 to
417
418
  // disable. Left undefined here means "use the default".
418
419
  let _streamStallTimeoutMs;
420
+ // Auto-pin (OPT-IN, off by default): when set, compaction pins the original
421
+ // task. Resolved from config `context.autoPin` or CC_AUTO_PIN=1; falsy → the
422
+ // compress calls below pass no pin predicate (byte-identical default).
423
+ let _autoPinOpt;
419
424
  try {
420
425
  const { loadConfig } = await import("../lib/config-manager.js");
421
426
  const _cfg = loadConfig();
422
427
  _visionModel = _cfg?.llm?.visionModel || undefined;
428
+ _autoPinOpt = _cfg?.context?.autoPin;
423
429
  const raw = _cfg?.llm?.streamStallTimeoutMs;
424
430
  const t = Number(raw);
425
431
  // Accept 0 (explicit disable) — only ignore absent/invalid values.
@@ -477,6 +483,15 @@ export async function startAgentRepl(options = {}) {
477
483
  if (feature("PROMPT_COMPRESSOR")) {
478
484
  _compressor = new PromptCompressor({ model, provider });
479
485
  }
486
+ if (process.env.CC_AUTO_PIN === "1") _autoPinOpt = _autoPinOpt || true;
487
+ // Compaction options shared by /compact + auto-compact. Adds the opt-in
488
+ // pin predicate only when auto-pin is enabled; otherwise byte-identical.
489
+ const _compactOpts = (msgs) => {
490
+ const base = { preserveToolPairs: true };
491
+ if (!_autoPinOpt) return base;
492
+ const isPinned = buildAutoPinPredicate(msgs, _autoPinOpt);
493
+ return isPinned ? { ...base, isPinned } : base;
494
+ };
480
495
 
481
496
  // Initialize permanent memory
482
497
  let permanentMemory = null;
@@ -2533,7 +2548,7 @@ export async function startAgentRepl(options = {}) {
2533
2548
  if (_compressor && messages.length > 3) {
2534
2549
  const { messages: compacted, stats } = await _compressor.compress(
2535
2550
  messages,
2536
- { preserveToolPairs: true },
2551
+ _compactOpts(messages),
2537
2552
  );
2538
2553
  messages.length = 0;
2539
2554
  messages.push(...compacted);
@@ -4460,7 +4475,7 @@ export async function startAgentRepl(options = {}) {
4460
4475
  try {
4461
4476
  const { messages: compacted, stats } = await _compressor.compress(
4462
4477
  messages,
4463
- { preserveToolPairs: true },
4478
+ _compactOpts(messages),
4464
4479
  );
4465
4480
  messages.length = 0;
4466
4481
  messages.push(...compacted);
@@ -0,0 +1,104 @@
1
+ /**
2
+ * auto-pin policy tests — the OPT-IN compaction pin policy. Verifies it is off
3
+ * by default (returns null → caller passes no predicate → byte-identical), and
4
+ * when enabled pins the original task (first user turn) plus explicit pins,
5
+ * honoring the token cap.
6
+ */
7
+
8
+ import { describe, it, expect } from "vitest";
9
+ import {
10
+ resolveAutoPinConfig,
11
+ buildAutoPinPredicate,
12
+ describeAutoPin,
13
+ } from "../auto-pin.js";
14
+
15
+ const MSGS = [
16
+ { role: "system", content: "you are a coding agent" },
17
+ { role: "user", content: "Refactor the auth module to use async/await" },
18
+ { role: "assistant", content: "ok, reading files" },
19
+ { role: "user", content: "also add tests" },
20
+ ];
21
+
22
+ describe("resolveAutoPinConfig", () => {
23
+ it("returns null when off (falsy)", () => {
24
+ expect(resolveAutoPinConfig(undefined)).toBeNull();
25
+ expect(resolveAutoPinConfig(false)).toBeNull();
26
+ expect(resolveAutoPinConfig(0)).toBeNull();
27
+ });
28
+ it("defaults firstUserGoal on when enabled with true", () => {
29
+ expect(resolveAutoPinConfig(true)).toMatchObject({ firstUserGoal: true });
30
+ });
31
+ it("accepts an object and can disable firstUserGoal", () => {
32
+ expect(resolveAutoPinConfig({ firstUserGoal: false })).toMatchObject({
33
+ firstUserGoal: false,
34
+ });
35
+ expect(resolveAutoPinConfig({ maxPinTokens: 50 }).maxPinTokens).toBe(50);
36
+ });
37
+ });
38
+
39
+ describe("buildAutoPinPredicate", () => {
40
+ it("returns null when auto-pin is off (→ default compaction, byte-identical)", () => {
41
+ expect(buildAutoPinPredicate(MSGS, false)).toBeNull();
42
+ expect(buildAutoPinPredicate(MSGS, undefined)).toBeNull();
43
+ });
44
+
45
+ it("pins the first user turn (the original task) when enabled", () => {
46
+ const isPinned = buildAutoPinPredicate(MSGS, true);
47
+ expect(isPinned(MSGS[1])).toBe(true); // first user
48
+ expect(isPinned(MSGS[3])).toBe(false); // second user NOT pinned
49
+ expect(isPinned(MSGS[0])).toBe(false); // system not pinned
50
+ expect(isPinned(MSGS[2])).toBe(false); // assistant not pinned
51
+ });
52
+
53
+ it("always honors explicit pins on top of the policy", () => {
54
+ const msgs = [
55
+ ...MSGS,
56
+ { role: "assistant", content: "important decision", pinned: true },
57
+ { role: "assistant", content: "legacy marker", _pin: true },
58
+ ];
59
+ const isPinned = buildAutoPinPredicate(msgs, true);
60
+ expect(isPinned(msgs[4])).toBe(true); // pinned:true
61
+ expect(isPinned(msgs[5])).toBe(true); // _pin
62
+ });
63
+
64
+ it("honors explicit pins even with firstUserGoal disabled", () => {
65
+ const msgs = [
66
+ MSGS[0],
67
+ MSGS[1],
68
+ { role: "user", content: "x", pinned: true },
69
+ ];
70
+ const isPinned = buildAutoPinPredicate(msgs, { firstUserGoal: false });
71
+ expect(isPinned(msgs[1])).toBe(false); // first user NOT auto-pinned
72
+ expect(isPinned(msgs[2])).toBe(true); // explicit pin still honored
73
+ });
74
+
75
+ it("skips a first user turn that exceeds the token cap", () => {
76
+ const big = { role: "user", content: "x".repeat(10_000) }; // ~2500 tokens
77
+ const msgs = [MSGS[0], big, MSGS[2]];
78
+ const isPinned = buildAutoPinPredicate(msgs, { maxPinTokens: 100 });
79
+ expect(isPinned(big)).toBe(false); // too big to pin
80
+ });
81
+
82
+ it("matches by identity, not by value (compaction filters the same array)", () => {
83
+ const isPinned = buildAutoPinPredicate(MSGS, true);
84
+ const clone = { ...MSGS[1] }; // same content, different object
85
+ expect(isPinned(clone)).toBe(false);
86
+ });
87
+ });
88
+
89
+ describe("describeAutoPin", () => {
90
+ it("reports disabled when off", () => {
91
+ expect(describeAutoPin(MSGS, false)).toEqual({
92
+ enabled: false,
93
+ reasons: [],
94
+ });
95
+ });
96
+ it("explains the original-task pin", () => {
97
+ const d = describeAutoPin(MSGS, true);
98
+ expect(d.enabled).toBe(true);
99
+ expect(d.reasons.some((r) => r.why.includes("original task"))).toBe(true);
100
+ expect(d.reasons[d.reasons.length - 1].preview).toContain(
101
+ "Refactor the auth",
102
+ );
103
+ });
104
+ });
@@ -221,6 +221,77 @@ export function killAllBackgroundShellTasks() {
221
221
  return killed;
222
222
  }
223
223
 
224
+ // SYNCHRONOUS whole-tree kill for use inside a process 'exit' / signal handler,
225
+ // where only synchronous work runs to completion (the async `spawn`/`taskkill`
226
+ // in _killTask would be cut off the instant the process terminates, leaving the
227
+ // grandchild orphaned). POSIX signals the process group with SIGKILL; Windows
228
+ // uses spawnSync taskkill /T so the shell's children die too.
229
+ function _killTaskSync(task) {
230
+ const child = task?.child;
231
+ if (!child || child.killed || task?.status !== "running") return false;
232
+ try {
233
+ if (process.platform === "win32") {
234
+ if (child.pid) {
235
+ spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
236
+ windowsHide: true,
237
+ });
238
+ } else {
239
+ child.kill();
240
+ }
241
+ } else if (child.pid) {
242
+ // Negative pid → whole process group (requires the detached spawn above).
243
+ try {
244
+ process.kill(-child.pid, "SIGKILL");
245
+ } catch (_err) {
246
+ child.kill("SIGKILL");
247
+ }
248
+ } else {
249
+ child.kill("SIGKILL");
250
+ }
251
+ return true;
252
+ } catch (_err) {
253
+ return false;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Synchronously kill every still-running background shell task. Safe to call
259
+ * from a process 'exit' or signal handler (uses spawnSync/process.kill, not the
260
+ * async spawn path which a terminating process would cut off). This is the
261
+ * orphan-reclaim net for the paths the normal `finally` reaper can't cover: a
262
+ * Ctrl-C / SIGTERM unwinds no `finally`, so without it a backgrounded dev server
263
+ * outlives a killed `cc agent -p`.
264
+ * @returns {number} count of tasks signalled
265
+ */
266
+ export function killAllBackgroundShellTasksSync() {
267
+ let killed = 0;
268
+ for (const task of _backgroundShellTasks.values()) {
269
+ if (_killTaskSync(task)) {
270
+ killed += 1;
271
+ }
272
+ }
273
+ return killed;
274
+ }
275
+
276
+ // Install a one-time process 'exit' net that synchronously reaps background
277
+ // shell tasks. `finally` blocks (headless/REPL) already reap on normal
278
+ // completion, but an explicit process.exit() elsewhere (serve shutdown, the
279
+ // headless signal handler that converts Ctrl-C → exit) would otherwise leave a
280
+ // backgrounded command orphaned. Installed lazily on the first background task,
281
+ // so a process that never backgrounds anything pays nothing.
282
+ let _bgExitReaperHooked = false;
283
+ function _ensureBgExitReaper() {
284
+ if (_bgExitReaperHooked) return;
285
+ _bgExitReaperHooked = true;
286
+ process.once("exit", () => {
287
+ try {
288
+ killAllBackgroundShellTasksSync();
289
+ } catch {
290
+ /* best-effort teardown on exit */
291
+ }
292
+ });
293
+ }
294
+
224
295
  // Idle-reap tuning (Claude-Code 2.1.193 "automatic memory-pressure reaping for
225
296
  // idle background shell commands"). A running task is a reap candidate when it
226
297
  // has produced no output for BG_IDLE_REAP_MS AND the system is under memory
@@ -324,16 +395,12 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
324
395
  ideResult: ide.result,
325
396
  };
326
397
  }
327
- const confirm = context.permissionConfirm || context.shellConfirm || null;
328
- const ok =
329
- typeof confirm === "function"
330
- ? await confirm({
331
- tool: name,
332
- args,
333
- rule: `hook:${outcome.hook}`,
334
- reason: outcome.reason || "a PreToolUse hook requests confirmation",
335
- })
336
- : false;
398
+ const ok = await requestInteractivePermission(name, args, context, cwd, {
399
+ tool: name,
400
+ args,
401
+ rule: `hook:${outcome.hook}`,
402
+ reason: outcome.reason || "a PreToolUse hook requests confirmation",
403
+ });
337
404
  return ok
338
405
  ? { blocked: false }
339
406
  : {
@@ -345,6 +412,87 @@ async function runSettingsPreToolUseHooks(name, args, context, cwd) {
345
412
  return { blocked: false };
346
413
  }
347
414
 
415
+ /**
416
+ * Run settings.json `PermissionRequest` hooks (Claude-Code parity). Fires at the
417
+ * exact moment a tool call would prompt the user for approval — BEFORE the
418
+ * prompt — so a policy hook can auto-approve (`allow`/`approve`), auto-reject
419
+ * (`deny`/`block`), or defer (`ask` / no decision) to the normal prompt.
420
+ * Returns `{ decision: "allow" | "deny" | null }`. No matching hook (or no
421
+ * `settingsHooks`) → `{ decision: null }` and the caller prompts exactly as
422
+ * before (default behaviour is byte-for-byte unchanged).
423
+ * @returns {{decision:("allow"|"deny"|null), reason?:string, hook?:string}}
424
+ */
425
+ function runSettingsPermissionRequestHooks(name, args, context, cwd, reason) {
426
+ const matched = collectHooks(
427
+ context.settingsHooks,
428
+ "PermissionRequest",
429
+ name,
430
+ );
431
+ if (!matched || matched.length === 0) return { decision: null };
432
+ const payload = {
433
+ hook_event_name: "PermissionRequest",
434
+ tool_name: umbrellaFor(name),
435
+ raw_tool_name: name,
436
+ tool_input: args,
437
+ permission_reason: reason || null,
438
+ cwd,
439
+ session_id: context.sessionId || null,
440
+ };
441
+ const outcome = runCommandHooks(matched, payload, {
442
+ cwd,
443
+ event: "PermissionRequest",
444
+ });
445
+ // Precedence: deny > ask > allow > defer. The runner normalizes deny/block →
446
+ // "block" and short-circuits block/ask, but it COLLAPSES an "allow" into the
447
+ // aggregate "continue" (it only short-circuits block/ask) — so an explicit
448
+ // auto-approve must be recovered from the per-hook `results`. A deny or ask
449
+ // by any hook still wins over an allow (safety: never let an allow override a
450
+ // gate that another hook wanted to block or prompt for).
451
+ if (outcome.decision === "block") {
452
+ return { decision: "deny", reason: outcome.reason, hook: outcome.hook };
453
+ }
454
+ if (outcome.decision === "ask") {
455
+ return { decision: null }; // a hook asked → defer to the confirmer
456
+ }
457
+ const allowHook =
458
+ outcome.decision === "allow"
459
+ ? { command: outcome.hook }
460
+ : (outcome.results || []).find((r) => r && r.decision === "allow");
461
+ if (allowHook) {
462
+ return { decision: "allow", hook: allowHook.command || null };
463
+ }
464
+ return { decision: null }; // no actionable decision → defer to the confirmer
465
+ }
466
+
467
+ /**
468
+ * Resolve an interactive permission gate. Gives `PermissionRequest` hooks first
469
+ * say (auto-allow → true without prompting, auto-deny → false without
470
+ * prompting), then falls back to the injected confirmer with the identical
471
+ * arguments the call site would have passed. When no `PermissionRequest` hook
472
+ * matches this is byte-for-byte the previous `confirm(confirmArgs)` call, so
473
+ * every existing permission gate keeps its exact behaviour absent a hook.
474
+ * @returns {Promise<boolean>}
475
+ */
476
+ async function requestInteractivePermission(
477
+ name,
478
+ args,
479
+ context,
480
+ cwd,
481
+ confirmArgs,
482
+ ) {
483
+ const verdict = runSettingsPermissionRequestHooks(
484
+ name,
485
+ args,
486
+ context,
487
+ cwd,
488
+ confirmArgs?.reason,
489
+ );
490
+ if (verdict.decision === "allow") return true;
491
+ if (verdict.decision === "deny") return false;
492
+ const confirm = context.permissionConfirm || context.shellConfirm || null;
493
+ return typeof confirm === "function" ? await confirm(confirmArgs) : false;
494
+ }
495
+
348
496
  // ─── Tool definitions ────────────────────────────────────────────────────
349
497
 
350
498
  export const AGENT_TOOLS = getCodingAgentFunctionToolDefinitions();
@@ -1082,16 +1230,12 @@ export async function executeTool(name, args, context = {}) {
1082
1230
  source: "settings rule",
1083
1231
  });
1084
1232
  if (ide) return ide.result;
1085
- const confirm = context.permissionConfirm || context.shellConfirm || null;
1086
- const ok =
1087
- typeof confirm === "function"
1088
- ? await confirm({
1089
- tool: name,
1090
- args,
1091
- rule: settingsVerdict.rule,
1092
- reason: `settings rule ${settingsVerdict.rule} requires confirmation`,
1093
- })
1094
- : false;
1233
+ const ok = await requestInteractivePermission(name, args, context, cwd, {
1234
+ tool: name,
1235
+ args,
1236
+ rule: settingsVerdict.rule,
1237
+ reason: `settings rule ${settingsVerdict.rule} requires confirmation`,
1238
+ });
1095
1239
  if (!ok) {
1096
1240
  return {
1097
1241
  error: `[Permission] Tool "${name}" requires confirmation (settings rule: ${settingsVerdict.rule}) but this run is non-interactive — denied. Do not retry; tell the user this action needs their approval.`,
@@ -1123,16 +1267,12 @@ export async function executeTool(name, args, context = {}) {
1123
1267
  await import("../lib/sensitive-file-guard.js");
1124
1268
  const sensReason = sensitiveFileReason(args.path);
1125
1269
  if (sensReason) {
1126
- const confirm = context.permissionConfirm || context.shellConfirm || null;
1127
- const ok =
1128
- typeof confirm === "function"
1129
- ? await confirm({
1130
- tool: name,
1131
- args,
1132
- rule: null,
1133
- reason: `sensitive file: ${sensReason}`,
1134
- })
1135
- : false;
1270
+ const ok = await requestInteractivePermission(name, args, context, cwd, {
1271
+ tool: name,
1272
+ args,
1273
+ rule: null,
1274
+ reason: `sensitive file: ${sensReason}`,
1275
+ });
1136
1276
  if (!ok) {
1137
1277
  return {
1138
1278
  error: `[Sensitive File] Writing "${args.path}" requires confirmation (${sensReason}) — denied. Add a settings allow rule to pre-authorize.`,
@@ -1155,16 +1295,12 @@ export async function executeTool(name, args, context = {}) {
1155
1295
  !planManager.isActive() &&
1156
1296
  isDangerousGitCommand(args?.command)
1157
1297
  ) {
1158
- const confirm = context.permissionConfirm || context.shellConfirm || null;
1159
- const ok =
1160
- typeof confirm === "function"
1161
- ? await confirm({
1162
- tool: name,
1163
- args,
1164
- rule: null,
1165
- reason: `destructive git command: git ${normalizeGitCommand(args.command)}`,
1166
- })
1167
- : false;
1298
+ const ok = await requestInteractivePermission(name, args, context, cwd, {
1299
+ tool: name,
1300
+ args,
1301
+ rule: null,
1302
+ reason: `destructive git command: git ${normalizeGitCommand(args.command)}`,
1303
+ });
1168
1304
  if (!ok) {
1169
1305
  return {
1170
1306
  error: `[Destructive Git] "git ${normalizeGitCommand(args.command)}" discards work irrecoverably and requires confirmation — denied. Add a settings allow rule to pre-authorize.`,
@@ -1199,17 +1335,18 @@ export async function executeTool(name, args, context = {}) {
1199
1335
  credReason = hit ? hit.reason : null;
1200
1336
  }
1201
1337
  if (credReason) {
1202
- const confirm =
1203
- context.permissionConfirm || context.shellConfirm || null;
1204
- const ok =
1205
- typeof confirm === "function"
1206
- ? await confirm({
1207
- tool: name,
1208
- args,
1209
- rule: null,
1210
- reason: `credential access: ${credReason}`,
1211
- })
1212
- : false;
1338
+ const ok = await requestInteractivePermission(
1339
+ name,
1340
+ args,
1341
+ context,
1342
+ cwd,
1343
+ {
1344
+ tool: name,
1345
+ args,
1346
+ rule: null,
1347
+ reason: `credential access: ${credReason}`,
1348
+ },
1349
+ );
1213
1350
  if (!ok) {
1214
1351
  const what =
1215
1352
  name === "read_file" ? `Reading "${args.path}"` : "This command";
@@ -1774,28 +1911,41 @@ const _codeIntelPool = new Map(); // root -> { ci, idleTimer }
1774
1911
  const CODE_INTEL_IDLE_MS = 60_000;
1775
1912
  let _codeIntelExitHooked = false;
1776
1913
 
1777
- async function _getSharedCodeIntel(cwd) {
1914
+ export async function _getSharedCodeIntel(cwd) {
1778
1915
  const root = path.resolve(cwd || process.cwd());
1779
1916
  let entry = _codeIntelPool.get(root);
1780
1917
  if (!entry) {
1781
1918
  const { CodeIntelligence } =
1782
1919
  await import("../lib/lsp/code-intelligence.js");
1783
- entry = {
1784
- ci: new CodeIntelligence({ projectRoot: root, coldStart: true }),
1785
- idleTimer: null,
1786
- };
1787
- _codeIntelPool.set(root, entry);
1788
- if (!_codeIntelExitHooked) {
1789
- _codeIntelExitHooked = true;
1790
- process.once("exit", () => {
1791
- for (const e of _codeIntelPool.values()) {
1792
- try {
1793
- e.ci.dispose();
1794
- } catch {
1795
- /* best-effort teardown on exit */
1920
+ // Re-check AFTER the await: two concurrent callers (the parallel read-only
1921
+ // batch runs several `code_intelligence` calls at once) both observed an
1922
+ // empty pool before this `await import`, so without this guard both would
1923
+ // construct a CodeIntelligence — each spawning its own language server — and
1924
+ // the second `set` would orphan the first (leaked server process) while its
1925
+ // stale idle-timer, still bound to `root`, later evicts the wrong (warm,
1926
+ // in-use) entry. This is exactly the orphaned-server/dangling-timer trap the
1927
+ // pool exists to prevent. Construction below has NO further await, so once
1928
+ // one continuation wins the re-check the other reuses its entry — the window
1929
+ // is fully closed.
1930
+ entry = _codeIntelPool.get(root);
1931
+ if (!entry) {
1932
+ entry = {
1933
+ ci: new CodeIntelligence({ projectRoot: root, coldStart: true }),
1934
+ idleTimer: null,
1935
+ };
1936
+ _codeIntelPool.set(root, entry);
1937
+ if (!_codeIntelExitHooked) {
1938
+ _codeIntelExitHooked = true;
1939
+ process.once("exit", () => {
1940
+ for (const e of _codeIntelPool.values()) {
1941
+ try {
1942
+ e.ci.dispose();
1943
+ } catch {
1944
+ /* best-effort teardown on exit */
1945
+ }
1796
1946
  }
1797
- }
1798
- });
1947
+ });
1948
+ }
1799
1949
  }
1800
1950
  }
1801
1951
  if (entry.idleTimer) clearTimeout(entry.idleTimer);
@@ -2327,6 +2477,9 @@ async function executeToolInner(
2327
2477
  task.endedAt = new Date().toISOString();
2328
2478
  }
2329
2479
  _backgroundShellTasks.set(id, task);
2480
+ // Arm the process-exit net so a Ctrl-C / hard exit can't orphan this
2481
+ // task (the normal `finally` reaper is bypassed by signals).
2482
+ _ensureBgExitReaper();
2330
2483
  return attachDescriptor(
2331
2484
  {
2332
2485
  background: true,
@@ -2463,6 +2616,14 @@ async function executeToolInner(
2463
2616
  return attachDescriptor(
2464
2617
  {
2465
2618
  error: err.message.substring(0, 2000),
2619
+ // Surface stdout too: a failing command (test runner / linter /
2620
+ // build) usually prints WHAT failed to stdout and only the summary
2621
+ // to stderr, so dropping it on non-zero exit blinds the agent to the
2622
+ // actual failure. Only attach it when there IS output (a timeout with
2623
+ // no output keeps the field absent), mirroring the success path.
2624
+ ...(err.stdout
2625
+ ? { stdout: String(err.stdout).substring(0, 30000) }
2626
+ : {}),
2466
2627
  stderr: (err.stderr || "").substring(0, 2000),
2467
2628
  exitCode: err.status,
2468
2629
  shellCommandPolicy: shellPolicy,
@@ -5440,10 +5601,22 @@ export async function* agentLoop(messages, options) {
5440
5601
  reason: preCompactReason,
5441
5602
  };
5442
5603
  }
5604
+ // Auto-pin (OPT-IN): when enabled, the original task (first user turn)
5605
+ // is pinned so compaction can't drop it. Off by default → no predicate
5606
+ // is passed and compaction is byte-identical to before.
5607
+ let pinOpts = {};
5608
+ if (options.autoPin) {
5609
+ const { buildAutoPinPredicate } = await import("./auto-pin.js");
5610
+ const isPinned = buildAutoPinPredicate(messages, options.autoPin);
5611
+ if (isPinned) pinOpts = { isPinned };
5612
+ }
5443
5613
  const { messages: compacted, stats } =
5444
5614
  !needFull || preCompactBlocked
5445
5615
  ? { messages, stats: { saved: 0 } }
5446
- : await compactor.compress(messages, { preserveToolPairs: true });
5616
+ : await compactor.compress(messages, {
5617
+ preserveToolPairs: true,
5618
+ ...pinOpts,
5619
+ });
5447
5620
  if (stats.saved > 0 && compacted.length < messages.length) {
5448
5621
  messages.splice(0, messages.length, ...compacted);
5449
5622
  // Persist the compaction so a later --resume rebuilds from the
@@ -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
+ }