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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-BJif14yR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-CXek6gJY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-BgQhdAJi.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-DTYl5NtR.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-BzATQAeR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-BzdPEQhL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-BM0KVh8E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-D7fHXHkz.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Wm34RR-b.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CAiSQ9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-Df40CrEe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-DIT2fNFU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-CSTIbW2k.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-DtqXZZUi.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-CUtzw6o7.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-CezmlnmU.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-P4BITarg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-BRRiYwvq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-L5ijB9i4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard-CFxro4ob.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BmAFCQ71.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-DJBCEuon.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-DsVCpHMF.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DP3bP5th.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-I3g_bAw9.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-jHS5-ioS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-CPL7sfx9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-Jf236h4C.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-Cm2tcSKg.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-CZK82rhi.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-CZCeji7a.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-3l2KVS9k.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-VYdpoLh6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-CiB9cmm1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-wCFZbBuL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-1a3THIyG.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-DMzLB2hG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-D9wZbf6l.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-hrFe2ZM-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-CHGX5Jwm.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
- package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-By7FkhtY.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow--khGSzJn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-CAG9dH35.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-D35ec6JA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CxBF7hW_.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-s6EtOO1s.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-BbJYmWmO.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-VGQq1jMT.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-B1ew3Teh.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-mrO47FIK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-IJF2mHyw.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CU0wN_Z0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-DhN37R6G.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-D_xOX_hu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-D_Ssv7uH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-Kx5dKxX7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-BlGwqZkY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-hLFQzxpb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DMlvsVt4.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-Bd0Qsj2m.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-DFclq9X3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-BNtln2qY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-Dch5H19G.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-2UfQcU1g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-CD9YQr4i.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-BNu-7MzI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-D1n9CaIR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-DenL3iBW.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-D87IfukO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BkiZc8E3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-BNRSytGN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-Bwbd4X2m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-B8O2bs0o.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-C2fL8zmH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-BORYDdE1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-C0V5S2FM.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-2dR0fcHL.js} +1 -1
- package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-BmuAFAn8.js} +1 -1
- package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-CN3smMcK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-DCnxb4kW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-vlR43pHn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-D04tHYFd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-0DnaZGkC.js} +1 -1
- package/src/assets/web-panel/assets/{index-308gCGh7.js → index-430S68MN.js} +1 -1
- package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-B78xL3s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BTPEZTk1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-BXRg8GFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-BasvsWDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-BeGDEXFs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-BqhASEL4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-BudvKQfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-C-6NO5il.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-C5Sxb1sE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-CP9m9Ggr.js} +3 -3
- package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-CTOCnIQ7.js} +1 -1
- package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
- package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-ClhAHDK3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-CnXebZLL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-CngCC8hC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-Cr-A0FYJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-CsAsCPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-D28DeAAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-D5LZVZ0L.js} +1 -1
- package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-D65_XseV.js} +1 -1
- package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-D8vwp4qp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-DDwLgQY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-DIpY0qM5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-DTWg-18U.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-DTtRscUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-DYAtmqiv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-D_n8_vfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CahryTX0.js → index-DbADHwhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-DbVsQBOH.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +1 -0
- package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-HhNjnCfe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-ToUh2b1-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DISWAYce.js → index-TslMTwNy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-_FIkN3jd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-hpvvtAZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-jzR7Ujuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-ljuBiQW9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-C3wUb4je.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D-jYFUNA.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-tqb8nwJ2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DSOjMJMz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-CJ7VimIW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-qXiG1iT3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-CkGBcy1Z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-D3WvTlLO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BEE2ftge.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-CsfBpxiG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-xPrIiJPI.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-C5Yd-SCR.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-D_MDS8HI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub-BqJh-usA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-chm_p-gz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-3-YDNz0Z.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/assets/web-panel/remote-session-sw.js +43 -0
- package/src/command-manifest.json +22 -1
- package/src/commands/agent.js +44 -5
- package/src/commands/codeintel.js +252 -0
- package/src/commands/eval.js +248 -0
- package/src/commands/plugin.js +354 -0
- package/src/commands/team.js +456 -0
- package/src/gateways/ws/message-dispatcher.js +45 -2
- package/src/gateways/ws/remote-session-protocol.js +37 -0
- package/src/gateways/ws/ws-server.js +1 -1
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +158 -0
- package/src/harness/remote-session-push-apns.js +246 -0
- package/src/harness/remote-session-push-errors.js +15 -0
- package/src/harness/remote-session-push-fcm.js +9 -25
- package/src/harness/remote-session-push-huawei.js +174 -0
- package/src/harness/remote-session-push-oppo.js +171 -0
- package/src/harness/remote-session-push-senders.js +42 -0
- package/src/harness/remote-session-push-vivo.js +179 -0
- package/src/harness/remote-session-push-web.js +299 -0
- package/src/harness/remote-session-push-xiaomi.js +99 -0
- package/src/index.js +6 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -5
- package/src/lib/agent-team/task-lease.js +434 -0
- package/src/lib/agent-team/team-budget.js +165 -0
- package/src/lib/agent-team/team-mailbox.js +106 -0
- package/src/lib/agent-team/team-runner.js +266 -0
- package/src/lib/agent-team/team-worktree.js +204 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +305 -0
- package/src/lib/eval/runner.js +163 -0
- package/src/lib/eval/tasks.js +456 -0
- package/src/lib/eval/trend.js +185 -0
- package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
- package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
- package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +258 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +264 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/code-intelligence.js +356 -0
- package/src/lib/lsp/jsonrpc-stream.js +139 -0
- package/src/lib/lsp/lsp-client.js +318 -0
- package/src/lib/lsp/lsp-manager.js +323 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/agents.js +51 -0
- package/src/lib/plugin-runtime/bin.js +100 -0
- package/src/lib/plugin-runtime/hooks.js +100 -0
- package/src/lib/plugin-runtime/install.js +388 -0
- package/src/lib/plugin-runtime/lsp.js +75 -0
- package/src/lib/plugin-runtime/manifest.js +458 -0
- package/src/lib/plugin-runtime/mcp.js +89 -0
- package/src/lib/plugin-runtime/monitors.js +100 -0
- package/src/lib/plugin-runtime/policy.js +144 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/scopes.js +197 -0
- package/src/lib/plugin-runtime/settings.js +119 -0
- package/src/lib/plugin-runtime/signature.js +107 -0
- package/src/lib/plugin-runtime/skills.js +43 -0
- package/src/lib/plugin-runtime/trust.js +140 -0
- package/src/lib/sandbox-egress-proxy.js +162 -0
- package/src/lib/sandbox-network-policy.js +134 -0
- package/src/lib/sandbox-v2.js +10 -4
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +29 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/skill-loader.js +18 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/repl/agent-repl.js +400 -4
- package/src/runtime/agent-core.js +546 -52
- package/src/runtime/coding-agent-contract-shared.cjs +82 -8
- package/src/runtime/coding-agent-policy.cjs +32 -7
- package/src/runtime/fallback-model.js +134 -7
- package/src/runtime/headless-runner.js +353 -108
- package/src/runtime/mcp-config.js +46 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DIp8Xcbd.js +0 -1
- package/src/assets/web-panel/assets/Projects-Dpid9CDg.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
- package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
- package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
- 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 } =
|
|
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
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
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
|
-
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
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
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
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
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
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
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
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
|
|
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
|
|
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":
|