chainlesschain 0.162.148 → 0.162.149

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 (231) 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-BJif14yR.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-CXek6gJY.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-BgQhdAJi.js} +2 -2
  7. package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-DTYl5NtR.js} +3 -3
  8. package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-BzATQAeR.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-BzdPEQhL.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-BM0KVh8E.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-D7fHXHkz.js} +4 -4
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-Wm34RR-b.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CAiSQ9vO.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-Df40CrEe.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-DIT2fNFU.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-CSTIbW2k.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-DtqXZZUi.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-CUtzw6o7.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-CezmlnmU.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-P4BITarg.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-BRRiYwvq.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-L5ijB9i4.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard-CFxro4ob.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BmAFCQ71.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-DJBCEuon.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-DsVCpHMF.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DP3bP5th.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-I3g_bAw9.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-jHS5-ioS.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-CPL7sfx9.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-Jf236h4C.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-Cm2tcSKg.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-CZK82rhi.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-CZCeji7a.js} +4 -4
  37. package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-3l2KVS9k.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-VYdpoLh6.js} +1 -1
  39. package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-CiB9cmm1.js} +1 -1
  40. package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-wCFZbBuL.js} +2 -2
  41. package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-1a3THIyG.js} +4 -4
  42. package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-DMzLB2hG.js} +3 -3
  43. package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-D9wZbf6l.js} +1 -1
  44. package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-hrFe2ZM-.js} +3 -3
  45. package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-CHGX5Jwm.js} +1 -1
  46. package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
  47. package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-By7FkhtY.js} +4 -4
  48. package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow--khGSzJn.js} +1 -1
  49. package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-CAG9dH35.js} +2 -2
  50. package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
  51. package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-D35ec6JA.js} +4 -4
  52. package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CxBF7hW_.js} +2 -2
  53. package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-s6EtOO1s.js} +1 -1
  54. package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-BbJYmWmO.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-VGQq1jMT.js} +2 -2
  56. package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-B1ew3Teh.js} +2 -2
  57. package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +1 -0
  58. package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-mrO47FIK.js} +1 -1
  59. package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-IJF2mHyw.js} +1 -1
  60. package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CU0wN_Z0.js} +1 -1
  61. package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-DhN37R6G.js} +1 -1
  62. package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-D_xOX_hu.js} +2 -2
  63. package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-D_Ssv7uH.js} +1 -1
  64. package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-Kx5dKxX7.js} +1 -1
  65. package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-BlGwqZkY.js} +2 -2
  66. package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-hLFQzxpb.js} +1 -1
  67. package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DMlvsVt4.js} +3 -3
  68. package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-Bd0Qsj2m.js} +2 -2
  69. package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-DFclq9X3.js} +1 -1
  70. package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-BNtln2qY.js} +1 -1
  71. package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-Dch5H19G.js} +1 -1
  72. package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-2UfQcU1g.js} +1 -1
  73. package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-CD9YQr4i.js} +2 -2
  74. package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-BNu-7MzI.js} +1 -1
  75. package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-D1n9CaIR.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-DenL3iBW.js} +1 -1
  77. package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
  78. package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-D87IfukO.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BkiZc8E3.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-BNRSytGN.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-Bwbd4X2m.js} +1 -1
  82. package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-B8O2bs0o.js} +1 -1
  83. package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-C2fL8zmH.js} +1 -1
  84. package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-BORYDdE1.js} +4 -4
  85. package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-C0V5S2FM.js} +4 -4
  86. package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-2dR0fcHL.js} +1 -1
  87. package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-BmuAFAn8.js} +1 -1
  88. package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-CN3smMcK.js} +1 -1
  89. package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-DCnxb4kW.js} +1 -1
  90. package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-vlR43pHn.js} +1 -1
  91. package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
  92. package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-D04tHYFd.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-0DnaZGkC.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-308gCGh7.js → index-430S68MN.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-B78xL3s2.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BTPEZTk1.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-BXRg8GFQ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-BasvsWDM.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-BeGDEXFs.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-BqhASEL4.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-BudvKQfG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-C-6NO5il.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-C5Sxb1sE.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-CP9m9Ggr.js} +3 -3
  105. package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-CTOCnIQ7.js} +1 -1
  106. package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
  107. package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-ClhAHDK3.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-CnXebZLL.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-CngCC8hC.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-Cr-A0FYJ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-CsAsCPJ6.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-D28DeAAB.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-D5LZVZ0L.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-D65_XseV.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-D8vwp4qp.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-DDwLgQY9.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-DIpY0qM5.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-DTWg-18U.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-DTtRscUa.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-DYAtmqiv.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-D_n8_vfI.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CahryTX0.js → index-DbADHwhr.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-DbVsQBOH.js} +1 -1
  124. package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +1 -0
  125. package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-HhNjnCfe.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-ToUh2b1-.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DISWAYce.js → index-TslMTwNy.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-_FIkN3jd.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-hpvvtAZ3.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-jzR7Ujuw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-ljuBiQW9.js} +1 -1
  132. package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-C3wUb4je.js} +1 -1
  133. package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D-jYFUNA.js} +1 -1
  134. package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-tqb8nwJ2.js} +1 -1
  135. package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DSOjMJMz.js} +1 -1
  136. package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-CJ7VimIW.js} +1 -1
  137. package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-qXiG1iT3.js} +1 -1
  138. package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-CkGBcy1Z.js} +1 -1
  139. package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-D3WvTlLO.js} +1 -1
  140. package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BEE2ftge.js} +1 -1
  141. package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-CsfBpxiG.js} +1 -1
  142. package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-xPrIiJPI.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-C5Yd-SCR.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-D_MDS8HI.js} +1 -1
  145. package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub-BqJh-usA.js} +1 -1
  146. package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-chm_p-gz.js} +1 -1
  147. package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-3-YDNz0Z.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/agent.js +44 -5
  152. package/src/commands/codeintel.js +252 -0
  153. package/src/commands/eval.js +248 -0
  154. package/src/commands/plugin.js +354 -0
  155. package/src/commands/team.js +456 -0
  156. package/src/gateways/ws/message-dispatcher.js +45 -2
  157. package/src/gateways/ws/remote-session-protocol.js +37 -0
  158. package/src/gateways/ws/ws-server.js +1 -1
  159. package/src/harness/prompt-compressor.js +27 -1
  160. package/src/harness/remote-command-ledger.js +158 -0
  161. package/src/harness/remote-session-push-apns.js +246 -0
  162. package/src/harness/remote-session-push-errors.js +15 -0
  163. package/src/harness/remote-session-push-fcm.js +9 -25
  164. package/src/harness/remote-session-push-huawei.js +174 -0
  165. package/src/harness/remote-session-push-oppo.js +171 -0
  166. package/src/harness/remote-session-push-senders.js +42 -0
  167. package/src/harness/remote-session-push-vivo.js +179 -0
  168. package/src/harness/remote-session-push-web.js +299 -0
  169. package/src/harness/remote-session-push-xiaomi.js +99 -0
  170. package/src/index.js +6 -0
  171. package/src/lib/agent-core.js +2 -0
  172. package/src/lib/agent-sandbox.js +161 -5
  173. package/src/lib/agent-team/task-lease.js +434 -0
  174. package/src/lib/agent-team/team-budget.js +165 -0
  175. package/src/lib/agent-team/team-mailbox.js +106 -0
  176. package/src/lib/agent-team/team-runner.js +266 -0
  177. package/src/lib/agent-team/team-worktree.js +204 -0
  178. package/src/lib/agents.js +18 -0
  179. package/src/lib/async-hook-supervisor.cjs +305 -0
  180. package/src/lib/eval/runner.js +163 -0
  181. package/src/lib/eval/tasks.js +456 -0
  182. package/src/lib/eval/trend.js +185 -0
  183. package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
  184. package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
  185. package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
  186. package/src/lib/lsp/__tests__/lsp-client.test.js +258 -0
  187. package/src/lib/lsp/__tests__/lsp-manager.test.js +264 -0
  188. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
  189. package/src/lib/lsp/code-intelligence.js +356 -0
  190. package/src/lib/lsp/jsonrpc-stream.js +139 -0
  191. package/src/lib/lsp/lsp-client.js +318 -0
  192. package/src/lib/lsp/lsp-manager.js +323 -0
  193. package/src/lib/lsp/lsp-server-registry.js +196 -0
  194. package/src/lib/plugin-monitor-supervisor.js +238 -0
  195. package/src/lib/plugin-runtime/agents.js +51 -0
  196. package/src/lib/plugin-runtime/bin.js +100 -0
  197. package/src/lib/plugin-runtime/hooks.js +100 -0
  198. package/src/lib/plugin-runtime/install.js +388 -0
  199. package/src/lib/plugin-runtime/lsp.js +75 -0
  200. package/src/lib/plugin-runtime/manifest.js +458 -0
  201. package/src/lib/plugin-runtime/mcp.js +89 -0
  202. package/src/lib/plugin-runtime/monitors.js +100 -0
  203. package/src/lib/plugin-runtime/policy.js +144 -0
  204. package/src/lib/plugin-runtime/reload.js +79 -0
  205. package/src/lib/plugin-runtime/scopes.js +197 -0
  206. package/src/lib/plugin-runtime/settings.js +119 -0
  207. package/src/lib/plugin-runtime/signature.js +107 -0
  208. package/src/lib/plugin-runtime/skills.js +43 -0
  209. package/src/lib/plugin-runtime/trust.js +140 -0
  210. package/src/lib/sandbox-egress-proxy.js +162 -0
  211. package/src/lib/sandbox-network-policy.js +134 -0
  212. package/src/lib/sandbox-v2.js +10 -4
  213. package/src/lib/settings-hook-events.cjs +78 -6
  214. package/src/lib/settings-hooks.cjs +29 -3
  215. package/src/lib/settings-loader.cjs +35 -1
  216. package/src/lib/skill-loader.js +18 -0
  217. package/src/lib/telemetry/span-recorder.js +237 -0
  218. package/src/repl/agent-repl.js +400 -4
  219. package/src/runtime/agent-core.js +546 -52
  220. package/src/runtime/coding-agent-contract-shared.cjs +82 -8
  221. package/src/runtime/coding-agent-policy.cjs +32 -7
  222. package/src/runtime/fallback-model.js +134 -7
  223. package/src/runtime/headless-runner.js +353 -108
  224. package/src/runtime/mcp-config.js +46 -0
  225. package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
  226. package/src/assets/web-panel/assets/OrderTableRenderer-DIp8Xcbd.js +0 -1
  227. package/src/assets/web-panel/assets/Projects-Dpid9CDg.js +0 -1
  228. package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
  229. package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
  230. package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
  231. package/src/assets/web-panel/assets/index-BXiw9dQf.js +0 -1
@@ -83,7 +83,8 @@ const { evaluateShellCommandPolicy } = sharedShellPolicy;
83
83
  const { evaluatePermissionRules } = sharedPermissionRules;
84
84
  const { collectHooks, umbrellaFor } = sharedSettingsHooks;
85
85
  const { runHooks: runCommandHooks } = sharedHookRunner;
86
- const { runObserveHooks } = sharedHookEvents;
86
+ const { runObserveHooks, aggregateContext, partitionAsyncHooks } =
87
+ sharedHookEvents;
87
88
 
88
89
  // ─── Background shell tasks ────────────────────────────────────────────────
89
90
  //
@@ -514,6 +515,8 @@ Key behaviors:
514
515
  - For long-running commands (builds, full test suites, dev servers) set run_shell { run_in_background: true } to get a task_id back immediately, then poll output and completion with check_shell { task_id }. Kill a backgrounded server with check_shell { task_id, kill: true } when finished
515
516
  - When asked about git status, diff, log, or other repository operations, use the git tool instead of run_shell
516
517
  - When asked about files or code, use read_file and search_files to find information
518
+ - Before renaming or changing a symbol, use code_intelligence (action: references/definition) to find every real usage instead of guessing with text search. It degrades to "unavailable" when no language server is installed — fall back to search_files then.
519
+ - After an edit, if the tool result includes a "newDiagnostics" array, you just introduced (or exposed) those errors/warnings — read them and fix before moving on. You can also run code_intelligence (action: diagnostics) on any file to check it on demand.
517
520
  - You have multi-layer skills (built-in, marketplace, global, project-level) — use list_skills to discover them and run_skill to execute them
518
521
  - Always explain what you're doing and show results
519
522
  - Be concise but thorough
@@ -1302,6 +1305,7 @@ export async function executeTool(name, args, context = {}) {
1302
1305
  subAgentDepth: context.subAgentDepth || 0,
1303
1306
  subAgentBudget: context.subAgentBudget || null,
1304
1307
  interactiveApproval: context.interactiveApproval || false,
1308
+ settingsHooks: context.settingsHooks || null,
1305
1309
  });
1306
1310
  } catch (err) {
1307
1311
  if (hookDb) {
@@ -1354,24 +1358,36 @@ export async function executeTool(name, args, context = {}) {
1354
1358
  try {
1355
1359
  const matched = collectHooks(context.settingsHooks, "PostToolUse", name);
1356
1360
  if (matched && matched.length > 0) {
1357
- const outcome = runCommandHooks(
1358
- matched,
1359
- {
1360
- hook_event_name: "PostToolUse",
1361
- tool_name: umbrellaFor(name),
1362
- raw_tool_name: name,
1363
- tool_input: args,
1364
- tool_response:
1365
- typeof toolResult === "object"
1366
- ? JSON.stringify(toolResult).substring(0, 2000)
1367
- : String(toolResult).substring(0, 2000),
1361
+ // `async:true` PostToolUse hooks must NOT block the tool loop — split
1362
+ // them off the synchronous decision path. Without this an async hook
1363
+ // ran synchronously here, defeating fire-and-forget (a background lint /
1364
+ // test after each edit would stall the turn).
1365
+ const { sync, async: asyncHooks } = partitionAsyncHooks(matched);
1366
+ const payload = {
1367
+ hook_event_name: "PostToolUse",
1368
+ tool_name: umbrellaFor(name),
1369
+ raw_tool_name: name,
1370
+ tool_input: args,
1371
+ tool_response:
1372
+ typeof toolResult === "object"
1373
+ ? JSON.stringify(toolResult).substring(0, 2000)
1374
+ : String(toolResult).substring(0, 2000),
1375
+ cwd,
1376
+ session_id: context.sessionId || null,
1377
+ };
1378
+ if (sync.length > 0) {
1379
+ const outcome = runCommandHooks(sync, payload, {
1368
1380
  cwd,
1369
- session_id: context.sessionId || null,
1370
- },
1371
- { cwd, event: "PostToolUse" },
1372
- );
1373
- if (outcome.decision === "block" && outcome.reason) {
1374
- toolResult.hookFeedback = outcome.reason;
1381
+ event: "PostToolUse",
1382
+ });
1383
+ if (outcome.decision === "block" && outcome.reason) {
1384
+ toolResult.hookFeedback = outcome.reason;
1385
+ }
1386
+ }
1387
+ // Fire-and-forget the async hooks onto the REPL-owned supervisor when
1388
+ // one is wired; results/rewakes drain into the next turn's context.
1389
+ if (asyncHooks.length > 0 && context.hookSupervisor) {
1390
+ context.hookSupervisor.dispatch(asyncHooks, payload, { cwd });
1375
1391
  }
1376
1392
  }
1377
1393
  } catch (_err) {
@@ -1482,6 +1498,58 @@ export function writeFileVerified(filePath, content, fsImpl = fs) {
1482
1498
  return { size: actual };
1483
1499
  }
1484
1500
 
1501
+ // ── Edit-concurrency (read-freshness) guard ──────────────────────────────
1502
+ //
1503
+ // Per-session map: resolved file path → the mtimeMs the agent last OBSERVED for
1504
+ // it (via read_file, or the write/edit it just made). If a file's on-disk mtime
1505
+ // is NEWER than that when the agent tries to edit it, an external process wrote
1506
+ // the file between the agent's read and its edit — so the edit is refused and
1507
+ // the agent is told to re-read, instead of clobbering the concurrent change on
1508
+ // top of a stale understanding. A file the agent never observed is NOT blocked
1509
+ // (first-touch / create stays frictionless). Disable with CC_EDIT_FRESHNESS=0.
1510
+ const _fileObservedMtimes = new Map();
1511
+
1512
+ /** Record the current mtime of a file the agent just read or wrote. */
1513
+ function _recordFileObservation(filePath, fsImpl = fs) {
1514
+ try {
1515
+ _fileObservedMtimes.set(filePath, fsImpl.statSync(filePath).mtimeMs);
1516
+ } catch {
1517
+ /* best-effort — a stat failure just means no freshness baseline */
1518
+ }
1519
+ }
1520
+
1521
+ /**
1522
+ * Return an error string when `filePath` changed on disk since the agent last
1523
+ * observed it (external concurrent edit), else null (fresh, un-observed, or
1524
+ * disabled).
1525
+ */
1526
+ function _checkFileFreshness(filePath, fsImpl = fs) {
1527
+ if (process.env.CC_EDIT_FRESHNESS === "0") return null;
1528
+ const known = _fileObservedMtimes.get(filePath);
1529
+ if (known === undefined) return null; // never observed → don't block
1530
+ let current;
1531
+ try {
1532
+ current = fsImpl.statSync(filePath).mtimeMs;
1533
+ } catch {
1534
+ return null;
1535
+ }
1536
+ if (current > known) {
1537
+ return (
1538
+ `File changed on disk since you last read it: ${filePath}. Another ` +
1539
+ `process modified it after your last read — re-read the file before ` +
1540
+ `editing so your change is based on its current content (this guard ` +
1541
+ `prevents silently clobbering a concurrent edit; set CC_EDIT_FRESHNESS=0 ` +
1542
+ `to disable).`
1543
+ );
1544
+ }
1545
+ return null;
1546
+ }
1547
+
1548
+ /** Clear the read-freshness map (new session / tests). */
1549
+ export function _resetFileFreshness() {
1550
+ _fileObservedMtimes.clear();
1551
+ }
1552
+
1485
1553
  // Short, stable-ish id for a freshly inserted notebook cell (nbformat 4.5+).
1486
1554
  function _newNotebookCellId() {
1487
1555
  return Math.random().toString(36).slice(2, 10);
@@ -1693,6 +1761,126 @@ export function _ingestSearchOutput(output, ctx) {
1693
1761
  /**
1694
1762
  * Inner tool execution — no hooks, no plan-mode checks.
1695
1763
  */
1764
+ // ─── Shared code-intelligence (LSP) pool ──────────────────────────────────
1765
+ //
1766
+ // A language server is expensive to start (spawns a process + indexes the
1767
+ // project), so the `code_intelligence` tool reuses ONE warm CodeIntelligence
1768
+ // per project root across tool calls within a run. To avoid the resource-leak
1769
+ // trap (orphaned server processes / dangling timers), each root auto-disposes
1770
+ // after an idle window, and a process-exit hook is registered once as a
1771
+ // backstop. `coldStart:true` makes the first query per file wait for the
1772
+ // project to load; warmed queries return immediately.
1773
+ const _codeIntelPool = new Map(); // root -> { ci, idleTimer }
1774
+ const CODE_INTEL_IDLE_MS = 60_000;
1775
+ let _codeIntelExitHooked = false;
1776
+
1777
+ async function _getSharedCodeIntel(cwd) {
1778
+ const root = path.resolve(cwd || process.cwd());
1779
+ let entry = _codeIntelPool.get(root);
1780
+ if (!entry) {
1781
+ const { CodeIntelligence } =
1782
+ 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 */
1796
+ }
1797
+ }
1798
+ });
1799
+ }
1800
+ }
1801
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
1802
+ entry.idleTimer = setTimeout(() => {
1803
+ _codeIntelPool.delete(root);
1804
+ entry.ci.dispose().catch(() => {});
1805
+ }, CODE_INTEL_IDLE_MS);
1806
+ if (typeof entry.idleTimer.unref === "function") entry.idleTimer.unref();
1807
+ return entry.ci;
1808
+ }
1809
+
1810
+ /** Dispose every pooled language server now (used by tests + explicit shutdown). */
1811
+ export async function disposeSharedCodeIntel() {
1812
+ const entries = [..._codeIntelPool.values()];
1813
+ _codeIntelPool.clear();
1814
+ for (const e of entries) {
1815
+ if (e.idleTimer) clearTimeout(e.idleTimer);
1816
+ }
1817
+ await Promise.all(entries.map((e) => e.ci.dispose().catch(() => {})));
1818
+ }
1819
+
1820
+ // Hard wall-clock cap so post-edit diagnostics never stall the agent: a cold
1821
+ // language server can take seconds to load a project on the FIRST edit; after
1822
+ // that the server is warm and answers in well under a second.
1823
+ const EDIT_DIAGNOSTICS_WALL_MS = 6000;
1824
+
1825
+ /**
1826
+ * Best-effort "did this edit introduce a new error?" check. Run after a
1827
+ * successful workspace edit so the model sees fresh type/syntax errors in the
1828
+ * SAME turn instead of discovering them on the next build (the plan's
1829
+ * "编辑后执行增量诊断并将相关错误回喂 Agent"). Returns a compact array of
1830
+ * error/warning diagnostics, or null.
1831
+ *
1832
+ * Zero cost when no language server is installed for the file (probes first,
1833
+ * never cold-starts a server that isn't there) and fully bounded in time.
1834
+ * Disable entirely with CC_EDIT_DIAGNOSTICS=0.
1835
+ */
1836
+ async function _postEditDiagnostics(filePath, cwd) {
1837
+ if (process.env.CC_EDIT_DIAGNOSTICS === "0") return null;
1838
+ let hasServer = false;
1839
+ try {
1840
+ const { languageIdForFile, resolveServer } =
1841
+ await import("../lib/lsp/lsp-server-registry.js");
1842
+ const languageId = languageIdForFile(filePath);
1843
+ if (!languageId) return null; // unsupported file type — no LSP, no cost
1844
+ hasServer = Boolean(
1845
+ resolveServer(languageId, path.resolve(cwd || process.cwd())),
1846
+ );
1847
+ } catch {
1848
+ return null;
1849
+ }
1850
+ if (!hasServer) return null;
1851
+
1852
+ const query = (async () => {
1853
+ try {
1854
+ const ci = await _getSharedCodeIntel(cwd);
1855
+ const res = await ci.refreshFile(filePath, { timeoutMs: 3000 });
1856
+ if (!res || res.available === false) return null;
1857
+ const diags = (res.diagnostics || []).filter(
1858
+ (d) => d.severity === "error" || d.severity === "warning",
1859
+ );
1860
+ return diags.length ? diags.slice(0, 20) : null;
1861
+ } catch {
1862
+ return null;
1863
+ }
1864
+ })();
1865
+
1866
+ let capTimer;
1867
+ const cap = new Promise((resolve) => {
1868
+ capTimer = setTimeout(() => resolve(null), EDIT_DIAGNOSTICS_WALL_MS);
1869
+ if (typeof capTimer.unref === "function") capTimer.unref();
1870
+ });
1871
+ try {
1872
+ return await Promise.race([query, cap]);
1873
+ } finally {
1874
+ clearTimeout(capTimer);
1875
+ }
1876
+ }
1877
+
1878
+ /** Merge post-edit diagnostics into a successful edit result (no-op if none). */
1879
+ async function _withPostEditDiagnostics(result, filePath, cwd) {
1880
+ const newDiagnostics = await _postEditDiagnostics(filePath, cwd);
1881
+ return newDiagnostics ? { ...result, newDiagnostics } : result;
1882
+ }
1883
+
1696
1884
  async function executeToolInner(
1697
1885
  name,
1698
1886
  args,
@@ -1717,6 +1905,7 @@ async function executeToolInner(
1717
1905
  subAgentDepth = 0,
1718
1906
  subAgentBudget = null,
1719
1907
  interactiveApproval = false,
1908
+ settingsHooks = null,
1720
1909
  },
1721
1910
  ) {
1722
1911
  const localToolDescriptor =
@@ -1769,6 +1958,9 @@ async function executeToolInner(
1769
1958
  });
1770
1959
  }
1771
1960
  const content = fs.readFileSync(filePath, "utf8");
1961
+ // Record the mtime so a later edit can detect an external change that
1962
+ // happened between this read and the edit (read-freshness guard).
1963
+ _recordFileObservation(filePath);
1772
1964
  // Jupyter notebooks: render a compact cell listing (index/id/type/source,
1773
1965
  // outputs summarized) so the model can find cells for notebook_edit
1774
1966
  // without ingesting raw JSON / base64 output blobs. `raw:true` returns the
@@ -1832,16 +2024,25 @@ async function executeToolInner(
1832
2024
  case "write_file": {
1833
2025
  const filePath = path.resolve(cwd, args.path);
1834
2026
  const dir = path.dirname(filePath);
2027
+ // Overwriting an existing file: refuse if it changed on disk since the
2028
+ // agent last observed it (external concurrent edit).
2029
+ if (fs.existsSync(filePath)) {
2030
+ const stale = _checkFileFreshness(filePath);
2031
+ if (stale) return attachDescriptor({ error: stale });
2032
+ }
1835
2033
  if (!fs.existsSync(dir)) {
1836
2034
  fs.mkdirSync(dir, { recursive: true });
1837
2035
  }
1838
2036
  const wrote = writeFileVerified(filePath, args.content);
1839
2037
  if (wrote.error) return attachDescriptor({ error: wrote.error });
1840
- return attachDescriptor({
1841
- success: true,
1842
- path: filePath,
1843
- size: wrote.size,
1844
- });
2038
+ _recordFileObservation(filePath);
2039
+ return attachDescriptor(
2040
+ await _withPostEditDiagnostics(
2041
+ { success: true, path: filePath, size: wrote.size },
2042
+ filePath,
2043
+ cwd,
2044
+ ),
2045
+ );
1845
2046
  }
1846
2047
 
1847
2048
  case "notebook_edit": {
@@ -1876,6 +2077,8 @@ async function executeToolInner(
1876
2077
  if (typeof args.new_string !== "string") {
1877
2078
  return attachDescriptor({ error: "new_string must be a string" });
1878
2079
  }
2080
+ const staleEdit = _checkFileFreshness(filePath);
2081
+ if (staleEdit) return attachDescriptor({ error: staleEdit });
1879
2082
  const content = fs.readFileSync(filePath, "utf8");
1880
2083
  const replaceAll = args.replace_all === true;
1881
2084
  const { count, newContent } = applyLiteralEdit(
@@ -1897,12 +2100,19 @@ async function executeToolInner(
1897
2100
  }
1898
2101
  const wrote = writeFileVerified(filePath, newContent);
1899
2102
  if (wrote.error) return attachDescriptor({ error: wrote.error });
1900
- return attachDescriptor({
1901
- success: true,
1902
- path: filePath,
1903
- size: wrote.size,
1904
- replaced: replaceAll ? count : 1,
1905
- });
2103
+ _recordFileObservation(filePath);
2104
+ return attachDescriptor(
2105
+ await _withPostEditDiagnostics(
2106
+ {
2107
+ success: true,
2108
+ path: filePath,
2109
+ size: wrote.size,
2110
+ replaced: replaceAll ? count : 1,
2111
+ },
2112
+ filePath,
2113
+ cwd,
2114
+ ),
2115
+ );
1906
2116
  }
1907
2117
 
1908
2118
  case "edit_file_hashed": {
@@ -1922,6 +2132,8 @@ async function executeToolInner(
1922
2132
  if (typeof args.new_line !== "string") {
1923
2133
  return attachDescriptor({ error: "new_line must be a string" });
1924
2134
  }
2135
+ const staleHashed = _checkFileFreshness(filePath);
2136
+ if (staleHashed) return attachDescriptor({ error: staleHashed });
1925
2137
  const original = fs.readFileSync(filePath, "utf8");
1926
2138
  const result = replaceByHash(original, {
1927
2139
  anchorHash: args.anchor_hash,
@@ -1946,13 +2158,20 @@ async function executeToolInner(
1946
2158
  }
1947
2159
  const wrote = writeFileVerified(filePath, result.content);
1948
2160
  if (wrote.error) return attachDescriptor({ error: wrote.error });
1949
- return attachDescriptor({
1950
- success: true,
1951
- path: filePath,
1952
- size: wrote.size,
1953
- lineNumber: result.lineNumber,
1954
- previousContent: result.previousContent,
1955
- });
2161
+ _recordFileObservation(filePath);
2162
+ return attachDescriptor(
2163
+ await _withPostEditDiagnostics(
2164
+ {
2165
+ success: true,
2166
+ path: filePath,
2167
+ size: wrote.size,
2168
+ lineNumber: result.lineNumber,
2169
+ previousContent: result.previousContent,
2170
+ },
2171
+ filePath,
2172
+ cwd,
2173
+ ),
2174
+ );
1956
2175
  }
1957
2176
 
1958
2177
  case "run_shell": {
@@ -2125,20 +2344,65 @@ async function executeToolInner(
2125
2344
  if (sandbox) {
2126
2345
  const { executeSandboxedShell, sandboxSummary } =
2127
2346
  await import("../lib/agent-sandbox.js");
2128
- const result = executeSandboxedShell(args.command, sandbox, {
2129
- cwd: args.cwd || cwd,
2130
- timeout: _resolveShellTimeout(args.timeout),
2131
- maxBuffer: 1024 * 1024,
2132
- env: {
2133
- CLAUDECODE: "1",
2134
- ...(sessionId
2135
- ? {
2136
- CC_SESSION_ID: String(sessionId),
2137
- CLAUDE_CODE_SESSION_ID: String(sessionId),
2138
- }
2139
- : {}),
2140
- },
2141
- });
2347
+ // Domain-restricted networking is ENFORCED by routing the sandboxed
2348
+ // process's egress through a local filtering proxy (see
2349
+ // sandbox-egress-proxy.js). Start it only when the policy actually
2350
+ // restricts domains and network is on; tear it down after the command.
2351
+ const sboxPolicy = sandbox.policy || {};
2352
+ const needsEgress =
2353
+ sandbox.network === true &&
2354
+ (sboxPolicy.allowedDomains?.length || 0) +
2355
+ (sboxPolicy.deniedDomains?.length || 0) >
2356
+ 0;
2357
+ let egressProxy = null;
2358
+ let proxyHandle = null;
2359
+ if (needsEgress) {
2360
+ try {
2361
+ const { createEgressProxy } =
2362
+ await import("../lib/sandbox-egress-proxy.js");
2363
+ proxyHandle = createEgressProxy(
2364
+ {
2365
+ allowedDomains: sboxPolicy.allowedDomains || [],
2366
+ deniedDomains: sboxPolicy.deniedDomains || [],
2367
+ allowPrivate: sboxPolicy.allowPrivate === true,
2368
+ },
2369
+ { bindHost: "0.0.0.0" }, // reachable from the container/netns
2370
+ );
2371
+ const listened = await proxyHandle.listen();
2372
+ egressProxy = { port: listened.port };
2373
+ } catch {
2374
+ // If the proxy can't start, leave egressProxy null so the sandbox
2375
+ // fails closed (refuses) rather than running without enforcement.
2376
+ proxyHandle = null;
2377
+ egressProxy = null;
2378
+ }
2379
+ }
2380
+ let result;
2381
+ try {
2382
+ result = executeSandboxedShell(args.command, sandbox, {
2383
+ cwd: args.cwd || cwd,
2384
+ timeout: _resolveShellTimeout(args.timeout),
2385
+ maxBuffer: 1024 * 1024,
2386
+ egressProxy,
2387
+ env: {
2388
+ CLAUDECODE: "1",
2389
+ ...(sessionId
2390
+ ? {
2391
+ CC_SESSION_ID: String(sessionId),
2392
+ CLAUDE_CODE_SESSION_ID: String(sessionId),
2393
+ }
2394
+ : {}),
2395
+ },
2396
+ });
2397
+ } finally {
2398
+ if (proxyHandle) {
2399
+ try {
2400
+ await proxyHandle.close();
2401
+ } catch {
2402
+ /* best-effort teardown */
2403
+ }
2404
+ }
2405
+ }
2142
2406
  const common = {
2143
2407
  sandbox: sandboxSummary(sandbox),
2144
2408
  shellCommandPolicy: shellPolicy,
@@ -2351,6 +2615,7 @@ async function executeToolInner(
2351
2615
  llmOptions,
2352
2616
  subAgentDepth,
2353
2617
  subAgentBudget,
2618
+ settingsHooks,
2354
2619
  }),
2355
2620
  );
2356
2621
  }
@@ -2615,6 +2880,95 @@ async function executeToolInner(
2615
2880
  return attachDescriptor(isContent ? { matches: hits } : { files: hits });
2616
2881
  }
2617
2882
 
2883
+ case "code_intelligence": {
2884
+ const action = String(args.action || "").trim();
2885
+ const positionActions = new Set([
2886
+ "definition",
2887
+ "references",
2888
+ "hover",
2889
+ "rename_preview",
2890
+ ]);
2891
+ // Validate up front so the model gets a precise correction instead of a
2892
+ // cryptic crash deep in the LSP layer.
2893
+ if (action !== "workspace_symbols" && !args.file) {
2894
+ return attachDescriptor({
2895
+ error: `code_intelligence action "${action}" requires "file".`,
2896
+ });
2897
+ }
2898
+ if (
2899
+ positionActions.has(action) &&
2900
+ (args.line == null || args.col == null)
2901
+ ) {
2902
+ return attachDescriptor({
2903
+ error: `code_intelligence action "${action}" requires 1-based "line" and "col".`,
2904
+ });
2905
+ }
2906
+ if (action === "workspace_symbols" && !args.query) {
2907
+ return attachDescriptor({
2908
+ error: `code_intelligence action "workspace_symbols" requires "query".`,
2909
+ });
2910
+ }
2911
+ if (action === "rename_preview" && !args.new_name) {
2912
+ return attachDescriptor({
2913
+ error: `code_intelligence action "rename_preview" requires "new_name".`,
2914
+ });
2915
+ }
2916
+ const ci = await _getSharedCodeIntel(cwd);
2917
+ const file = args.file ? path.resolve(cwd, args.file) : null;
2918
+ let res;
2919
+ try {
2920
+ switch (action) {
2921
+ case "definition":
2922
+ res = await ci.definition(file, args.line, args.col);
2923
+ break;
2924
+ case "references":
2925
+ res = await ci.references(file, args.line, args.col);
2926
+ break;
2927
+ case "hover":
2928
+ res = await ci.hover(file, args.line, args.col);
2929
+ break;
2930
+ case "document_symbols":
2931
+ res = await ci.documentSymbols(file);
2932
+ break;
2933
+ case "workspace_symbols":
2934
+ res = await ci.workspaceSymbols(String(args.query));
2935
+ break;
2936
+ case "diagnostics":
2937
+ res = await ci.diagnostics(file);
2938
+ break;
2939
+ case "rename_preview":
2940
+ res = await ci.renamePreview(
2941
+ file,
2942
+ args.line,
2943
+ args.col,
2944
+ args.new_name,
2945
+ );
2946
+ break;
2947
+ default:
2948
+ return attachDescriptor({
2949
+ error:
2950
+ `Unknown code_intelligence action "${action}". Valid: definition, ` +
2951
+ `references, hover, document_symbols, workspace_symbols, ` +
2952
+ `diagnostics, rename_preview.`,
2953
+ });
2954
+ }
2955
+ } catch (err) {
2956
+ return attachDescriptor({
2957
+ error: `code_intelligence failed: ${err.message}`,
2958
+ });
2959
+ }
2960
+ // No language server installed for this file — tell the agent to fall back
2961
+ // rather than looping on an empty result.
2962
+ if (res && res.available === false) {
2963
+ return attachDescriptor({
2964
+ unavailable: true,
2965
+ reason: res.reason,
2966
+ hint: "No language server available — use search_files / read_file instead.",
2967
+ });
2968
+ }
2969
+ return attachDescriptor(res);
2970
+ }
2971
+
2618
2972
  case "list_dir": {
2619
2973
  const dirPath = args.path ? path.resolve(cwd, args.path) : cwd;
2620
2974
  if (!fs.existsSync(dirPath)) {
@@ -3296,6 +3650,39 @@ async function _executeSpawnSubAgent(args, ctx) {
3296
3650
  const parentSessionId = ctx.sessionId || null;
3297
3651
  const interaction = ctx.interaction || null;
3298
3652
 
3653
+ // settings.json SubagentStart hooks (Claude-Code parity): fire BEFORE the
3654
+ // sub-agent runs, so a policy hook can VETO the spawn (`block`) or INJECT
3655
+ // extra context that gets prepended to the child's inherited context. This is
3656
+ // the mirror of the existing SubagentStop fire (which runs after the summary
3657
+ // returns). Best-effort — a hook error never blocks the spawn.
3658
+ if (ctx.settingsHooks) {
3659
+ try {
3660
+ const startOutcome = runObserveHooks(
3661
+ ctx.settingsHooks,
3662
+ "SubagentStart",
3663
+ {
3664
+ session_id: parentSessionId,
3665
+ role: role || args.agent || null,
3666
+ subagent_task: String(task || "").substring(0, 2000),
3667
+ },
3668
+ { cwd: ctx.cwd },
3669
+ );
3670
+ if (startOutcome.decision === "block" && startOutcome.reason) {
3671
+ return {
3672
+ error: `spawn_sub_agent blocked by SubagentStart hook: ${startOutcome.reason}`,
3673
+ };
3674
+ }
3675
+ const injected = aggregateContext(startOutcome.results);
3676
+ if (injected) {
3677
+ resolvedContext = resolvedContext
3678
+ ? `${resolvedContext}\n---\n${injected}`
3679
+ : injected;
3680
+ }
3681
+ } catch (_err) {
3682
+ // SubagentStart hooks are best-effort — never break the spawn.
3683
+ }
3684
+ }
3685
+
3299
3686
  // Inherit the parent's provider / base-url / key; a named subagent's `model:`
3300
3687
  // frontmatter (mdModel) overrides just the model, else keep the parent's.
3301
3688
  const parentLlm = ctx.llmOptions || {};
@@ -4629,6 +5016,7 @@ export function _normalizeAnthropicResponse(data) {
4629
5016
  const _CHECKPOINT_READ_ONLY = new Set([
4630
5017
  "read_file",
4631
5018
  "search_files",
5019
+ "code_intelligence",
4632
5020
  "list_dir",
4633
5021
  "list_skills",
4634
5022
  "search_sessions",
@@ -4724,6 +5112,54 @@ async function _getAutoCompactor(options) {
4724
5112
  return compressor;
4725
5113
  }
4726
5114
 
5115
+ /**
5116
+ * Run `fn` inside an OpenTelemetry span when `options.recorder` is attached,
5117
+ * else run it bare (zero overhead on the un-instrumented path). `onResult`
5118
+ * gets (span, result) to stamp result-derived attributes (token usage, tool
5119
+ * status). An exception is recorded on the span (category `errCategory`) and
5120
+ * re-thrown so the loop's own error handling is unchanged. Kept dependency-free
5121
+ * — the recorder is the OTel-shaped TelemetryRecorder passed in by the caller
5122
+ * (eval / a future `--otlp` agent flag); the real agent loop now EMITS
5123
+ * model/tool/retry spans, not just eval.
5124
+ */
5125
+ async function _withSpan(recorder, name, attrs, fn, onResult, errCategory) {
5126
+ if (!recorder || typeof recorder.startSpan !== "function") return fn();
5127
+ const span = recorder.startSpan(name, attrs);
5128
+ try {
5129
+ const r = await fn();
5130
+ if (onResult) {
5131
+ try {
5132
+ onResult(span, r);
5133
+ } catch {
5134
+ /* attribute stamping is best-effort */
5135
+ }
5136
+ }
5137
+ span.end();
5138
+ return r;
5139
+ } catch (err) {
5140
+ try {
5141
+ span.recordException(err, errCategory || "error");
5142
+ span.end();
5143
+ } catch {
5144
+ /* span teardown is best-effort */
5145
+ }
5146
+ throw err;
5147
+ }
5148
+ }
5149
+
5150
+ /** Normalize the varied provider usage shapes into input/output/cache tokens. */
5151
+ function _usageTokens(usage) {
5152
+ if (!usage || typeof usage !== "object") return null;
5153
+ const input = usage.prompt_tokens ?? usage.input_tokens ?? null;
5154
+ const output = usage.completion_tokens ?? usage.output_tokens ?? null;
5155
+ const cacheRead =
5156
+ usage.cache_read_input_tokens ??
5157
+ usage.prompt_tokens_details?.cached_tokens ??
5158
+ null;
5159
+ const cacheWrite = usage.cache_creation_input_tokens ?? null;
5160
+ return { input, output, cacheRead, cacheWrite };
5161
+ }
5162
+
4727
5163
  export async function* agentLoop(messages, options) {
4728
5164
  // Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
4729
5165
  // When options.iterationBudget is provided (e.g. from parent agent),
@@ -4732,6 +5168,9 @@ export async function* agentLoop(messages, options) {
4732
5168
  await import("../lib/iteration-budget.js");
4733
5169
  const budget = options.iterationBudget || new IterationBudget();
4734
5170
  const signal = options.signal || null;
5171
+ // Optional OpenTelemetry recorder (TelemetryRecorder). When present, the loop
5172
+ // emits model/tool/retry spans + a per-run counter; when absent, zero cost.
5173
+ const recorder = options.recorder || null;
4735
5174
  const toolContext = {
4736
5175
  hookDb: options.hookDb || null,
4737
5176
  skillLoader: options.skillLoader || _defaultSkillLoader,
@@ -4768,6 +5207,10 @@ export async function* agentLoop(messages, options) {
4768
5207
  permissionRules: options.permissionRules || null,
4769
5208
  permissionConfirm: options.permissionConfirm || null,
4770
5209
  settingsHooks: options.settingsHooks || null,
5210
+ // Async-hook supervisor (REPL-owned): lets PostToolUse `async:true` hooks
5211
+ // run fire-and-forget instead of blocking the tool loop. Optional — when
5212
+ // absent, async PostToolUse hooks are simply skipped (never run sync).
5213
+ hookSupervisor: options.hookSupervisor || null,
4771
5214
  autoCheckpoint: options.autoCheckpoint || false,
4772
5215
  checkpointSession:
4773
5216
  options.checkpointSession || options.sessionId || "agent",
@@ -4849,6 +5292,9 @@ export async function* agentLoop(messages, options) {
4849
5292
  await import("../lib/runnable-provider.js");
4850
5293
  llmCall = makeRunnableProviderFallback(llmCall, {
4851
5294
  onFallback: ({ from, to, reason, fromModel, toModel }) => {
5295
+ // Telemetry: a provider/model fallback is a reliability signal — count
5296
+ // it (keyed by reason) so a run's OTLP export shows retry/fallback rate.
5297
+ if (recorder) recorder.counter("agent.model.fallback", 1, { reason });
4852
5298
  // Switching VENDORS (or relabelling via baseUrl) must NEVER be silent —
4853
5299
  // a user who configured volcengine deserves to know it ran on another
4854
5300
  // provider/model. Build a human message and hand it to the driver's
@@ -5064,7 +5510,38 @@ export async function* agentLoop(messages, options) {
5064
5510
  }
5065
5511
  }
5066
5512
 
5067
- const result = await llmCall(callMessages, options);
5513
+ const result = await _withSpan(
5514
+ recorder,
5515
+ "agent.model",
5516
+ {
5517
+ "gen_ai.system": options.provider || "ollama",
5518
+ "gen_ai.request.model": options.model || "unknown",
5519
+ "agent.iteration": budget.consumed,
5520
+ },
5521
+ () => llmCall(callMessages, options),
5522
+ (span, r) => {
5523
+ const t = _usageTokens(r?.usage);
5524
+ if (t) {
5525
+ if (t.input != null)
5526
+ span.setAttribute("gen_ai.usage.input_tokens", t.input);
5527
+ if (t.output != null)
5528
+ span.setAttribute("gen_ai.usage.output_tokens", t.output);
5529
+ // Cache read/write tokens — the prompt-caching hit rate the plan's
5530
+ // reliability telemetry cares about.
5531
+ if (t.cacheRead != null)
5532
+ span.setAttribute("gen_ai.usage.cache_read_tokens", t.cacheRead);
5533
+ if (t.cacheWrite != null)
5534
+ span.setAttribute("gen_ai.usage.cache_write_tokens", t.cacheWrite);
5535
+ }
5536
+ span.setAttribute(
5537
+ "agent.has_tool_calls",
5538
+ Array.isArray(r?.message?.tool_calls) &&
5539
+ r.message.tool_calls.length > 0,
5540
+ );
5541
+ },
5542
+ "model_error",
5543
+ );
5544
+ if (recorder) recorder.counter("agent.model.calls", 1);
5068
5545
  throwIfAborted(signal);
5069
5546
  const msg = result?.message;
5070
5547
 
@@ -5303,11 +5780,24 @@ export async function* agentLoop(messages, options) {
5303
5780
  let toolResult;
5304
5781
  let toolError = null;
5305
5782
  try {
5306
- toolResult = await executeTool(toolName, toolArgs, toolContext);
5783
+ toolResult = await _withSpan(
5784
+ recorder,
5785
+ "agent.tool",
5786
+ { "tool.name": toolName },
5787
+ () => executeTool(toolName, toolArgs, toolContext),
5788
+ (span, r) => {
5789
+ span.setAttribute(
5790
+ "tool.is_error",
5791
+ !!(r && typeof r === "object" && r.error),
5792
+ );
5793
+ },
5794
+ "tool_error",
5795
+ );
5307
5796
  } catch (err) {
5308
5797
  toolResult = { error: err.message };
5309
5798
  toolError = err.message;
5310
5799
  }
5800
+ if (recorder) recorder.counter("agent.tool.calls", 1);
5311
5801
 
5312
5802
  throwIfAborted(signal);
5313
5803
 
@@ -5371,6 +5861,10 @@ export function formatToolArgs(name, args) {
5371
5861
  return args.command;
5372
5862
  case "search_files":
5373
5863
  return args.pattern;
5864
+ case "code_intelligence":
5865
+ return args.action === "workspace_symbols"
5866
+ ? `${args.action} ${args.query || ""}`.trim()
5867
+ : `${args.action} ${args.file || ""}${args.line != null ? `:${args.line}:${args.col}` : ""}`.trim();
5374
5868
  case "list_dir":
5375
5869
  return args.path || ".";
5376
5870
  case "run_skill":