chainlesschain 0.162.167 → 0.162.169
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/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-BZJrchIA.js → AIOps-DCeNRK8z.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Bhlrylt7.js → ActionButton-ZfD0HgVe.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-pHtXVQYs.js → Analytics-Ccn40f9E.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-ORpuri4R.js → AppLayout-DyXs4nSt.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-eC77yr1_.js → Artifacts-CYjMh5KR.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-B2dZIiqz.js → Audit-DZIhvx88.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-COZiTiwP.js → BackgroundAgents-C1XJsa2d.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-vY_Srl5d.js → Backup-CMXC0oUN.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Dili5BdM.js → BaseInput-CRVmIH1t.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-EzKBf0XL.js → Chat-acBud5Yj.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-D1CtRAyy.js → ChatBubbleRenderer-CTjqFhdD.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Beh6BmuW.js → Checkbox-CZem825o.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-C3fA6kBU.js → Codegen-B9UjZsww.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CefMqONH.js → Col-Dtu6Frtj.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CBAxFJtB.js → Community-9bV55Huv.js} +1 -1
- package/src/assets/web-panel/assets/{Compact--flHc-sM.js → Compact-g-kQ4c84.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CPeNLvA6.js → Compliance-CQjHzBO1.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bv5yv4BU.js → Cowork-D1Qm_Xhi.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-C22yMEas.js → Cron-992qo8zX.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Dsb2bcpu.js → Crosschain-4TiqEwuc.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DAxei4w0.js → DID-BbsXzXCb.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DEUwuVZ8.js → Dashboard-Dx5aO9d3.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BVp4si2j.js → Dropdown-BhzW6i6i.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CyFkybOG.js → EmailListRenderer-qEib4yiq.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BWmmsbVx.js → FamilyGuardDashboard-BZ3N-u3h.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Cxw27A5d.js → Federation-CcOqwzHY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Q9XthzL1.js → FormItemContext-dbi8mohd.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DS5DIdsW.js → GenericCardRenderer-57dqulUe.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BOliHfxP.js → Git-DtSV7C5g.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CD4oxa-W.js → Governance-CLnmH-RD.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CMlcivo8.js → Inference-wXiLeDnk.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BwhERcC4.js → KnowledgeGraph-NGdIX2Gx.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BjVcZR5P.js → Logs-Dz0HQn69.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CSRZ9da3.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-DQLU3E2w.js → Marketplace-DpPDmY3W.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CeK0XkpB.js → McpTools-UMysoRQm.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-DLFkgwaz.js → Memory-DsnBpQnY.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bh6ymXyj.js → MobileBridge-DweooAfG.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D950tUIe.js → MobileProjects-UdLfZOVA.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bx5x9_2m.js → Mtc-oXWhJNgC.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-a1-Fexsq.js → MtcAudit-CXTD7qrm.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-eaw0X8X1.js → Multisig-BKt5mJ9O.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BaNsPV4k.js → NLProgramming-C-zcYzje.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-C7pevN2X.js → Notes-CGaZ_jep.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-HDqeoP0r.js → NotificationSettings-BwX-tDbq.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DvzVl5T3.js → OrderTableRenderer-Cc9Se8H-.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D_xHDlGW.js → Organization-8BXdC7Im.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-B5sRnueL.js → Overflow-zCisFuGU.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-C-IRjOPW.js → P2P-BehHH9Ro.js} +2 -2
- package/src/assets/web-panel/assets/PdhVaultBrowser-DVuXYKAR.js +7 -0
- package/src/assets/web-panel/assets/{Permissions-Bm_T1uWw.js → Permissions-BVDATm3p.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DujDllye.js → PersonalDataHub-C43nnYoi.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-DyNPOLIG.js → Pipeline-D9E7aSVD.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CYKTW3iK.js → Privacy-CRbIchdL.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CGf03uZ0.js → ProjectInit-CTsy8lhL.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DIec4bTI.js → ProjectSettings-CPEKWWJ5.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BY43-bmi.js → Projects-CNnW0v5N.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-RrnUmgPk.js → Providers-BeZnDL9Y.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BBWfqd4y.js → QrScannerModal-CaydwuIU.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BsICSrFp.js → QuickAsk-qYWbu7kw.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B8y0b4ys.js → Recommend-RV_ogHrM.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-0QgdB1Oe.js → RemoteSession-Mah1i41_.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DBJGM0YO.js → Reputation-xZQhGktc.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DtWQ5otv.js → Row-B6cVbPbJ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-2zBxHd5H.js → RssFeed-C6hKjDPz.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BofliSkZ.js → Search-0jti_-Pe.js} +1 -1
- package/src/assets/web-panel/assets/{Security-ockRBBEL.js → Security-Cy48feGz.js} +3 -3
- package/src/assets/web-panel/assets/{Services-QyvG7Vd0.js → Services-BW3ObFxH.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DwbOndX3.js → Skeleton-CfrZRvAM.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-cHWa63sZ.js → Skills-yoccdk3t.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BHnTEydQ.js → Sla-AKBBdri6.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DE2hrrLb.js → SpeechSettings-DH87GbkH.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CZ9vnbJH.js → SyncSettings-DUuO-ZSw.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-ZTUfOzMZ.js +1 -0
- package/src/assets/web-panel/assets/{Templates-DiR3ad4X.js → Templates-shPrQ4NE.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-NqcW3frV.js → Tenant-C_inPunR.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-iYeqRQZ0.js → Terminal-CvVtx6ie.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C5GOoxFz.js → TimelineRenderer-Bzp_T3Xc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-82dz0XdU.js → Tokens-kUFRLdtp.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B3JCNrOi.js → Trigger-BsTU2blQ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BehFYsN_.js → Trust-DEKkuAsi.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BfOxJjVf.js → UkeySign-D1KEEgbK.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DdITCCPz.js → VideoEditing-C2yYkm9W.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-B0EhkCmD.js → Wallet-BKKod4qb.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DJ4i1f3-.js → WebAuthn-XXUgED6d.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-Dr9XTqIT.js → WorkflowEditor-DnlEyYTi.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Dc2nFyOj.js → chat-BaVjI6nu.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Drw9djBR.js → colors-DQ0yx43H.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BdLXbU52.js → compact-item-DEyWDp0v.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BpQMZiWY.js → createContext-SuPOfylZ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-C_RK48j-.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CbYwawHf.js → hasIn-CQz1JNtC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DO7LD_1t.js → index-3xcB2i6I.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bo_KwJTF.js → index-57GdL9ro.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJQiSQw7.js → index-BHJhyrFr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTeqWdAS.js → index-BIgWgye3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSmZjrFy.js → index-BJEIjYhV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7Ipfs4F.js → index-BffmUosH.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9_u4Qhe.js → index-ByGF9ev7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyU21xVi.js → index-C-IKjQD3.js} +1 -1
- package/src/assets/web-panel/assets/{index-GG_MXjSL.js → index-C2k8eHyq.js} +3 -3
- package/src/assets/web-panel/assets/{index-Blv7c80V.js → index-C3Vlo45C.js} +1 -1
- package/src/assets/web-panel/assets/index-CDqhisfz.js +1 -0
- package/src/assets/web-panel/assets/{index-C1eo1Qkd.js → index-CEfLMvKf.js} +1 -1
- package/src/assets/web-panel/assets/{index-COoIIMbO.js → index-CGu-b0RE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bu8Vy-mj.js → index-CI5jlfyn.js} +1 -1
- package/src/assets/web-panel/assets/index-CKUoX6Yi.js +1 -0
- package/src/assets/web-panel/assets/{index-BmQlot_u.js → index-COsmMpAO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWUN8c01.js → index-CZrARoXd.js} +1 -1
- package/src/assets/web-panel/assets/{index-IG3__pGj.js → index-C_fcNCoV.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1ANzVc6.js → index-CfVW-cd_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BAq-4KqY.js → index-CgvnudM_.js} +1 -1
- package/src/assets/web-panel/assets/{index-aHrUfrv1.js → index-CnhJU5To.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_lkTnZ0.js → index-CpR6M7eQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWUoc9-s.js → index-D-gXgEsU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZxzdAI0.js → index-D5SY6Lwu.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiGDrEt4.js → index-DB8AJ6T-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHMeEu-P.js → index-DNYVGZzG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3Alz2QD.js → index-DWIuZ9bI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKQlF7Pw.js → index-DXczrVij.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKmz3Is-.js → index-Dfh6OJKz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIcEW07n.js → index-DhBeUGCp.js} +1 -1
- package/src/assets/web-panel/assets/{index-UX_Mfyfv.js → index-DjZ6szGM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CBwQwtsE.js → index-DpPv8zeU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpmlTfpv.js → index-XjQfQBXH.js} +1 -1
- package/src/assets/web-panel/assets/{index-_vVucYqC.js → index-ZKOZ-YFJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-XYe6kBmi.js → index-ZMgCYtI7.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KboakZ.js → index-fGgx2f1y.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3uRI8_y.js → index-fcA6tfBA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBueKSTR.js → index-v8PYYw05.js} +1 -1
- package/src/assets/web-panel/assets/{index-VIyNlXPX.js → index-wBsuJZcz.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DybC85E4.js → initDefaultProps-KetDXPXx.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BNNuyDg7.js → motion-BfAtwo2m.js} +1 -1
- package/src/assets/web-panel/assets/{move-Bm8gSjEN.js → move-BnJvzHgx.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CuHQvzzr.js → mtc-parser-DZxDclhM.js} +1 -1
- package/src/assets/web-panel/assets/{omit-lv4NO7Ng.js → omit-CmqfX2P_.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BoSMfjHq.js → pickAttrs-B2B2ahzC.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BOMDSvgz.js → placementArrow-C1SIgWRj.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C064Ubmu.js → responsiveObserve-Dy_adDTl.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CeqhbKoG.js → slide-hDO9sBPk.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CunFoYww.js → statusUtils-DlpTjRpg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cxz3wPL7.js → styleChecker-DAv9eo6h.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DJsIaU2S.js → useFlexGapSupport-vfoXaeH8.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DnAygceJ.js → useFs-Dzji-S7v.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CrEUY7ta.js → usePersonalDataHub-jKcLN_Gj.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cq3rN2eT.js → vnode-DAWM5GgY.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-TMzcOx__.js → zoom-q3XgSOUd.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/plugin.js +49 -5
- package/src/data/changelog.json +18 -4
- package/src/harness/worktree-isolator.js +10 -2
- package/src/lib/agent-core.js +1 -0
- package/src/lib/background-agent-phase.js +22 -0
- package/src/lib/background-phase-reporter.js +153 -0
- package/src/lib/hook-runner.cjs +248 -36
- package/src/lib/install-command-policy.js +40 -1
- package/src/lib/lsp/code-intelligence.js +5 -1
- package/src/lib/lsp/workspace-roots.js +115 -0
- package/src/lib/session-lifecycle.js +3 -0
- package/src/lib/settings-hook-events.cjs +16 -3
- package/src/lib/sub-agent-context.js +13 -0
- package/src/runtime/agent-core.js +442 -89
- package/src/runtime/headless-runner.js +75 -8
- package/src/runtime/headless-stream.js +175 -2
- package/src/workers/background-agent-worker.js +22 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-BfRhXdyG.js +0 -1
- package/src/assets/web-panel/assets/PdhVaultBrowser-BqgUPlcU.js +0 -7
- package/src/assets/web-panel/assets/Tasks-DWgoAHvM.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CnfpDzMr.js +0 -1
- package/src/assets/web-panel/assets/index-CYTEB0tb.js +0 -1
- package/src/assets/web-panel/assets/index-CgsEp5bm.js +0 -1
|
@@ -28,6 +28,11 @@ import { mergeProviderOptions } from "../lib/provider-options.js";
|
|
|
28
28
|
import { applyCredentialProxy } from "../lib/credential-proxy.js";
|
|
29
29
|
import { describeBackgroundCommand } from "../lib/terminal-context.js";
|
|
30
30
|
import { buildTelemetryAttributes } from "../lib/telemetry-ids.js";
|
|
31
|
+
import {
|
|
32
|
+
workspaceRootsFor,
|
|
33
|
+
pickRootForFile,
|
|
34
|
+
mergeWorkspaceSymbolResults,
|
|
35
|
+
} from "../lib/lsp/workspace-roots.js";
|
|
31
36
|
import { getPlanModeManager } from "../lib/plan-mode.js";
|
|
32
37
|
import { CLISkillLoader } from "../lib/skill-loader.js";
|
|
33
38
|
import { executeHooks, HookEvents } from "../lib/hook-manager.js";
|
|
@@ -91,9 +96,14 @@ const { isDangerousGitCommand, isReadOnlyGitCommand, normalizeGitCommand } =
|
|
|
91
96
|
const { evaluateShellCommandPolicy } = sharedShellPolicy;
|
|
92
97
|
const { evaluatePermissionRules } = sharedPermissionRules;
|
|
93
98
|
const { collectHooks, umbrellaFor } = sharedSettingsHooks;
|
|
94
|
-
const { runHooks: runCommandHooks } =
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
const { runHooks: runCommandHooks, runHooksParallel: runCommandHooksParallel } =
|
|
100
|
+
sharedHookRunner;
|
|
101
|
+
const {
|
|
102
|
+
runObserveHooks,
|
|
103
|
+
aggregateContext,
|
|
104
|
+
partitionAsyncHooks,
|
|
105
|
+
withDeliveryId,
|
|
106
|
+
} = sharedHookEvents;
|
|
97
107
|
|
|
98
108
|
// ─── Background shell tasks ────────────────────────────────────────────────
|
|
99
109
|
//
|
|
@@ -409,6 +419,19 @@ function _resolveShellTimeout(raw) {
|
|
|
409
419
|
return Math.min(Math.floor(n), MAX_SHELL_TIMEOUT_MS);
|
|
410
420
|
}
|
|
411
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Opt-in: run ALL matching decision hooks and take the STRICTEST outcome
|
|
424
|
+
* (block > ask > allow > continue) instead of the default in-order first-wins
|
|
425
|
+
* short-circuit — so an earlier hook's `ask` can no longer mask a later hook's
|
|
426
|
+
* `block`. Default off = byte-identical short-circuit behavior. Flipping the
|
|
427
|
+
* default is a product decision (all matching hooks then run every time, which
|
|
428
|
+
* changes hook-execution volume + side effects).
|
|
429
|
+
*/
|
|
430
|
+
function _hookStrictMergeEnabled() {
|
|
431
|
+
const v = process.env.CC_HOOK_STRICT_MERGE;
|
|
432
|
+
return v === "1" || v === "true";
|
|
433
|
+
}
|
|
434
|
+
|
|
412
435
|
/**
|
|
413
436
|
* Run settings.json `PreToolUse` hooks (decision-capable). DB hooks are handled
|
|
414
437
|
* separately + stay observe-only. A `block` decision stops the tool; an `ask`
|
|
@@ -419,18 +442,36 @@ function _resolveShellTimeout(raw) {
|
|
|
419
442
|
async function runSettingsPreToolUseHooks(name, args, context, cwd) {
|
|
420
443
|
const matched = collectHooks(context.settingsHooks, "PreToolUse", name);
|
|
421
444
|
if (!matched || matched.length === 0) return { blocked: false };
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
445
|
+
// Unified-bus envelope (P2): stamps event_id + threads trace_id (this run) /
|
|
446
|
+
// parent_id (spawning run) from the loop context. Additive fields — a hook
|
|
447
|
+
// that ignores them is unaffected; absent context omits them entirely.
|
|
448
|
+
const payload = withDeliveryId(
|
|
449
|
+
"PreToolUse",
|
|
450
|
+
{
|
|
451
|
+
hook_event_name: "PreToolUse",
|
|
452
|
+
tool_name: umbrellaFor(name),
|
|
453
|
+
raw_tool_name: name,
|
|
454
|
+
tool_input: args,
|
|
455
|
+
cwd,
|
|
456
|
+
session_id: context.sessionId || null,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
sessionId: context.sessionId || null,
|
|
460
|
+
traceId: context.hookTraceId || null,
|
|
461
|
+
parentId: context.hookParentId || null,
|
|
462
|
+
},
|
|
463
|
+
);
|
|
464
|
+
// Strict merge (opt-in) runs ALL matching hooks and takes the strictest
|
|
465
|
+
// decision. This path is async so the hooks run in TRUE PARALLEL
|
|
466
|
+
// (runHooksParallel) — wall-clock is the slowest hook, not their sum — while
|
|
467
|
+
// still yielding the same strictest outcome. Default path is the sync in-order
|
|
468
|
+
// short-circuit runner (byte-identical).
|
|
469
|
+
const outcome = _hookStrictMergeEnabled()
|
|
470
|
+
? await runCommandHooksParallel(matched, payload, {
|
|
471
|
+
cwd,
|
|
472
|
+
event: "PreToolUse",
|
|
473
|
+
})
|
|
474
|
+
: runCommandHooks(matched, payload, { cwd, event: "PreToolUse" });
|
|
434
475
|
if (outcome.decision === "block") {
|
|
435
476
|
return { blocked: true, reason: outcome.reason, hook: outcome.hook };
|
|
436
477
|
}
|
|
@@ -490,18 +531,27 @@ function runSettingsPermissionRequestHooks(name, args, context, cwd, reason) {
|
|
|
490
531
|
name,
|
|
491
532
|
);
|
|
492
533
|
if (!matched || matched.length === 0) return { decision: null };
|
|
493
|
-
const payload =
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
534
|
+
const payload = withDeliveryId(
|
|
535
|
+
"PermissionRequest",
|
|
536
|
+
{
|
|
537
|
+
hook_event_name: "PermissionRequest",
|
|
538
|
+
tool_name: umbrellaFor(name),
|
|
539
|
+
raw_tool_name: name,
|
|
540
|
+
tool_input: args,
|
|
541
|
+
permission_reason: reason || null,
|
|
542
|
+
cwd,
|
|
543
|
+
session_id: context.sessionId || null,
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
sessionId: context.sessionId || null,
|
|
547
|
+
traceId: context.hookTraceId || null,
|
|
548
|
+
parentId: context.hookParentId || null,
|
|
549
|
+
},
|
|
550
|
+
);
|
|
502
551
|
const outcome = runCommandHooks(matched, payload, {
|
|
503
552
|
cwd,
|
|
504
553
|
event: "PermissionRequest",
|
|
554
|
+
mergeStrict: _hookStrictMergeEnabled(),
|
|
505
555
|
});
|
|
506
556
|
// Precedence: deny > ask > allow > defer. The runner normalizes deny/block →
|
|
507
557
|
// "block" and short-circuits block/ask, but it COLLAPSES an "allow" into the
|
|
@@ -726,6 +776,7 @@ Key behaviors:
|
|
|
726
776
|
- When asked about files or code, use read_file and search_files to find information
|
|
727
777
|
- 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.
|
|
728
778
|
- 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.
|
|
779
|
+
- If a tool result includes a "subtreeInstructions" array, you just entered a subdirectory that carries its own cc.md/CLAUDE.md/AGENTS.md — treat that content as authoritative project rules for work in that subtree (it is injected once, the first time you touch the subtree).
|
|
729
780
|
- You have multi-layer skills (built-in, marketplace, global, project-level) — use list_skills to discover them and run_skill to execute them
|
|
730
781
|
- Always explain what you're doing and show results
|
|
731
782
|
- Be concise but thorough
|
|
@@ -1642,18 +1693,26 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1642
1693
|
// ran synchronously here, defeating fire-and-forget (a background lint /
|
|
1643
1694
|
// test after each edit would stall the turn).
|
|
1644
1695
|
const { sync, async: asyncHooks } = partitionAsyncHooks(matched);
|
|
1645
|
-
const payload =
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1696
|
+
const payload = withDeliveryId(
|
|
1697
|
+
"PostToolUse",
|
|
1698
|
+
{
|
|
1699
|
+
hook_event_name: "PostToolUse",
|
|
1700
|
+
tool_name: umbrellaFor(name),
|
|
1701
|
+
raw_tool_name: name,
|
|
1702
|
+
tool_input: args,
|
|
1703
|
+
tool_response:
|
|
1704
|
+
typeof toolResult === "object"
|
|
1705
|
+
? JSON.stringify(toolResult).substring(0, 2000)
|
|
1706
|
+
: String(toolResult).substring(0, 2000),
|
|
1707
|
+
cwd,
|
|
1708
|
+
session_id: context.sessionId || null,
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
sessionId: context.sessionId || null,
|
|
1712
|
+
traceId: context.hookTraceId || null,
|
|
1713
|
+
parentId: context.hookParentId || null,
|
|
1714
|
+
},
|
|
1715
|
+
);
|
|
1657
1716
|
if (sync.length > 0) {
|
|
1658
1717
|
const outcome = runCommandHooks(sync, payload, {
|
|
1659
1718
|
cwd,
|
|
@@ -1700,7 +1759,11 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1700
1759
|
? JSON.stringify(toolResult).substring(0, 2000)
|
|
1701
1760
|
: String(toolResult).substring(0, 2000),
|
|
1702
1761
|
},
|
|
1703
|
-
{
|
|
1762
|
+
{
|
|
1763
|
+
cwd,
|
|
1764
|
+
traceId: context.hookTraceId || null,
|
|
1765
|
+
parentId: context.hookParentId || null,
|
|
1766
|
+
},
|
|
1704
1767
|
);
|
|
1705
1768
|
if (outcome.decision === "block" && outcome.reason) {
|
|
1706
1769
|
toolResult.hookFeedback = toolResult.hookFeedback
|
|
@@ -2056,6 +2119,21 @@ const _codeIntelPool = new Map(); // root -> { ci, idleTimer }
|
|
|
2056
2119
|
const CODE_INTEL_IDLE_MS = 60_000;
|
|
2057
2120
|
let _codeIntelExitHooked = false;
|
|
2058
2121
|
|
|
2122
|
+
// Restart backoff between language-server re-spawns (P2 LSP): DEFAULT ON at a
|
|
2123
|
+
// 1s base (1s→2s→4s→8s cap, see lsp-manager) so a server that crashes on
|
|
2124
|
+
// startup can't burst `maxRestarts` spawns back-to-back before quarantine.
|
|
2125
|
+
// `CC_LSP_RESTART_BACKOFF_MS` overrides the base; 0 restores the legacy
|
|
2126
|
+
// immediate-respawn behavior.
|
|
2127
|
+
const DEFAULT_LSP_RESTART_BACKOFF_MS = 1000;
|
|
2128
|
+
function _lspRestartBackoffBaseMs() {
|
|
2129
|
+
const raw = process.env.CC_LSP_RESTART_BACKOFF_MS;
|
|
2130
|
+
if (raw !== undefined && raw !== "") {
|
|
2131
|
+
const n = Number(raw);
|
|
2132
|
+
if (Number.isFinite(n)) return Math.max(0, n);
|
|
2133
|
+
}
|
|
2134
|
+
return DEFAULT_LSP_RESTART_BACKOFF_MS;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2059
2137
|
export async function _getSharedCodeIntel(cwd) {
|
|
2060
2138
|
const root = path.resolve(cwd || process.cwd());
|
|
2061
2139
|
let entry = _codeIntelPool.get(root);
|
|
@@ -2075,7 +2153,11 @@ export async function _getSharedCodeIntel(cwd) {
|
|
|
2075
2153
|
entry = _codeIntelPool.get(root);
|
|
2076
2154
|
if (!entry) {
|
|
2077
2155
|
entry = {
|
|
2078
|
-
ci: new CodeIntelligence({
|
|
2156
|
+
ci: new CodeIntelligence({
|
|
2157
|
+
projectRoot: root,
|
|
2158
|
+
coldStart: true,
|
|
2159
|
+
lspOptions: { restartBackoffBaseMs: _lspRestartBackoffBaseMs() },
|
|
2160
|
+
}),
|
|
2079
2161
|
idleTimer: null,
|
|
2080
2162
|
};
|
|
2081
2163
|
_codeIntelPool.set(root, entry);
|
|
@@ -2128,17 +2210,29 @@ const EDIT_DIAGNOSTICS_WALL_MS = 6000;
|
|
|
2128
2210
|
* never cold-starts a server that isn't there) and fully bounded in time.
|
|
2129
2211
|
* Disable entirely with CC_EDIT_DIAGNOSTICS=0.
|
|
2130
2212
|
*/
|
|
2131
|
-
async function _postEditDiagnostics(filePath, cwd) {
|
|
2213
|
+
async function _postEditDiagnostics(filePath, cwd, additionalDirectories) {
|
|
2132
2214
|
if (process.env.CC_EDIT_DIAGNOSTICS === "0") return null;
|
|
2215
|
+
// Multi-root workspace: resolve the server against the root that CONTAINS
|
|
2216
|
+
// the edited file (mirrors the code_intelligence handler) — editing a file
|
|
2217
|
+
// inside an `--add-dir` root probes/uses THAT project's language server
|
|
2218
|
+
// instead of degrading to "no diagnostics" via the cwd root. Single-root
|
|
2219
|
+
// sessions (no additionalDirectories) resolve to cwd exactly as before.
|
|
2220
|
+
let root;
|
|
2221
|
+
try {
|
|
2222
|
+
root = pickRootForFile(
|
|
2223
|
+
path.resolve(cwd || process.cwd(), filePath),
|
|
2224
|
+
workspaceRootsFor(cwd, additionalDirectories),
|
|
2225
|
+
);
|
|
2226
|
+
} catch {
|
|
2227
|
+
root = path.resolve(cwd || process.cwd());
|
|
2228
|
+
}
|
|
2133
2229
|
let hasServer = false;
|
|
2134
2230
|
try {
|
|
2135
2231
|
const { languageIdForFile, resolveServer } =
|
|
2136
2232
|
await import("../lib/lsp/lsp-server-registry.js");
|
|
2137
2233
|
const languageId = languageIdForFile(filePath);
|
|
2138
2234
|
if (!languageId) return null; // unsupported file type — no LSP, no cost
|
|
2139
|
-
hasServer = Boolean(
|
|
2140
|
-
resolveServer(languageId, path.resolve(cwd || process.cwd())),
|
|
2141
|
-
);
|
|
2235
|
+
hasServer = Boolean(resolveServer(languageId, root));
|
|
2142
2236
|
} catch {
|
|
2143
2237
|
return null;
|
|
2144
2238
|
}
|
|
@@ -2146,13 +2240,23 @@ async function _postEditDiagnostics(filePath, cwd) {
|
|
|
2146
2240
|
|
|
2147
2241
|
const query = (async () => {
|
|
2148
2242
|
try {
|
|
2149
|
-
const ci = await _getSharedCodeIntel(
|
|
2243
|
+
const ci = await _getSharedCodeIntel(root);
|
|
2150
2244
|
const res = await ci.refreshFile(filePath, { timeoutMs: 3000 });
|
|
2151
2245
|
if (!res || res.available === false) return null;
|
|
2152
2246
|
const diags = (res.diagnostics || []).filter(
|
|
2153
2247
|
(d) => d.severity === "error" || d.severity === "warning",
|
|
2154
2248
|
);
|
|
2155
|
-
|
|
2249
|
+
if (!diags.length) return null;
|
|
2250
|
+
// Bound by a TOKEN budget, severity-prioritized (errors survive over
|
|
2251
|
+
// warnings), replacing the arbitrary unsorted count-cap of 20. Guarantees
|
|
2252
|
+
// the model sees the most severe diagnostics first and can't be flooded by
|
|
2253
|
+
// a pathological diagnostics dump. Budget is overridable via
|
|
2254
|
+
// CC_EDIT_DIAGNOSTICS_TOKENS (default 2000).
|
|
2255
|
+
const { capDiagnostics } =
|
|
2256
|
+
await import("../lib/lsp/diagnostics-scheduler.js");
|
|
2257
|
+
const budget = Number(process.env.CC_EDIT_DIAGNOSTICS_TOKENS) || 2000;
|
|
2258
|
+
const { kept } = capDiagnostics(diags, { maxTokens: budget });
|
|
2259
|
+
return kept.length ? kept : null;
|
|
2156
2260
|
} catch {
|
|
2157
2261
|
return null;
|
|
2158
2262
|
}
|
|
@@ -2171,11 +2275,110 @@ async function _postEditDiagnostics(filePath, cwd) {
|
|
|
2171
2275
|
}
|
|
2172
2276
|
|
|
2173
2277
|
/** Merge post-edit diagnostics into a successful edit result (no-op if none). */
|
|
2174
|
-
async function _withPostEditDiagnostics(
|
|
2175
|
-
|
|
2278
|
+
async function _withPostEditDiagnostics(
|
|
2279
|
+
result,
|
|
2280
|
+
filePath,
|
|
2281
|
+
cwd,
|
|
2282
|
+
additionalDirectories,
|
|
2283
|
+
) {
|
|
2284
|
+
const newDiagnostics = await _postEditDiagnostics(
|
|
2285
|
+
filePath,
|
|
2286
|
+
cwd,
|
|
2287
|
+
additionalDirectories,
|
|
2288
|
+
);
|
|
2176
2289
|
return newDiagnostics ? { ...result, newDiagnostics } : result;
|
|
2177
2290
|
}
|
|
2178
2291
|
|
|
2292
|
+
// ─── Lazy subtree instruction injection (large-monorepo lever) ─────────────
|
|
2293
|
+
//
|
|
2294
|
+
// cc.md / CLAUDE.md / AGENTS.md that sit BELOW the startup cwd are intentionally
|
|
2295
|
+
// NOT loaded up front — they cost tokens for subtrees a run may never touch.
|
|
2296
|
+
// When a tool first ACCESSES a path inside such a subtree, we inject that
|
|
2297
|
+
// subtree's directory instructions into the tool result (the SAME channel as
|
|
2298
|
+
// newDiagnostics), exactly once per subtree per process. A stateful
|
|
2299
|
+
// SubtreeInstructionLoader per root remembers what it already injected so a
|
|
2300
|
+
// second access to the same subtree is a no-op. Disable with
|
|
2301
|
+
// CC_SUBTREE_INSTRUCTIONS=0. Common case (no cc.md below cwd) → zero cost.
|
|
2302
|
+
const _subtreeLoaderPool = new Map(); // root -> SubtreeInstructionLoader
|
|
2303
|
+
|
|
2304
|
+
async function _getSubtreeLoader(cwd) {
|
|
2305
|
+
const root = path.resolve(cwd || process.cwd());
|
|
2306
|
+
let loader = _subtreeLoaderPool.get(root);
|
|
2307
|
+
if (!loader) {
|
|
2308
|
+
const { SubtreeInstructionLoader } =
|
|
2309
|
+
await import("../lib/project-instructions.js");
|
|
2310
|
+
let instructionExcludes;
|
|
2311
|
+
try {
|
|
2312
|
+
const { readStringArraySetting } =
|
|
2313
|
+
await import("../lib/settings-loader.cjs");
|
|
2314
|
+
instructionExcludes = readStringArraySetting("instructionExcludes", {
|
|
2315
|
+
cwd: root,
|
|
2316
|
+
});
|
|
2317
|
+
} catch {
|
|
2318
|
+
instructionExcludes = undefined; // fail-open — honor no excludes
|
|
2319
|
+
}
|
|
2320
|
+
loader = new SubtreeInstructionLoader({
|
|
2321
|
+
repoRoot: root,
|
|
2322
|
+
baseDir: root,
|
|
2323
|
+
instructionExcludes,
|
|
2324
|
+
});
|
|
2325
|
+
_subtreeLoaderPool.set(root, loader);
|
|
2326
|
+
}
|
|
2327
|
+
return loader;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
/** Test/shutdown hook: forget every remembered subtree-injection set. */
|
|
2331
|
+
export function _resetSubtreeInstructionLoaders() {
|
|
2332
|
+
_subtreeLoaderPool.clear();
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
/**
|
|
2336
|
+
* Freshly-discovered subtree instruction files for a path a tool just touched,
|
|
2337
|
+
* with their (capped) content read for inline injection — or null when the
|
|
2338
|
+
* subtree has no NEW cc.md/CLAUDE.md/AGENTS.md (the common case → zero cost).
|
|
2339
|
+
*/
|
|
2340
|
+
async function _subtreeInstructionsFor(accessedPath, cwd) {
|
|
2341
|
+
if (process.env.CC_SUBTREE_INSTRUCTIONS === "0") return null;
|
|
2342
|
+
try {
|
|
2343
|
+
const loader = await _getSubtreeLoader(cwd);
|
|
2344
|
+
const fresh = loader.onAccess(accessedPath);
|
|
2345
|
+
if (!fresh || !fresh.length) return null;
|
|
2346
|
+
const { DEFAULT_MAX_FILE_BYTES } =
|
|
2347
|
+
await import("../lib/project-instructions.js");
|
|
2348
|
+
const out = [];
|
|
2349
|
+
for (const f of fresh) {
|
|
2350
|
+
try {
|
|
2351
|
+
const buf = fs.readFileSync(f.path);
|
|
2352
|
+
const truncated = buf.length > DEFAULT_MAX_FILE_BYTES;
|
|
2353
|
+
const content = (
|
|
2354
|
+
truncated ? buf.slice(0, DEFAULT_MAX_FILE_BYTES) : buf
|
|
2355
|
+
).toString("utf-8");
|
|
2356
|
+
out.push({
|
|
2357
|
+
path: f.path,
|
|
2358
|
+
scope: f.scope || "project",
|
|
2359
|
+
content,
|
|
2360
|
+
...(truncated ? { truncated: true } : {}),
|
|
2361
|
+
});
|
|
2362
|
+
} catch {
|
|
2363
|
+
// discovered file vanished before we could read it — skip it
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
return out.length ? out : null;
|
|
2367
|
+
} catch {
|
|
2368
|
+
return null; // fail-open: never break a tool because injection errored
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
/**
|
|
2373
|
+
* Attach freshly-discovered subtree instructions to a SUCCESSFUL tool result
|
|
2374
|
+
* (no-op on an error result or when the subtree has nothing new).
|
|
2375
|
+
*/
|
|
2376
|
+
async function _withSubtreeInstructions(result, accessedPath, cwd) {
|
|
2377
|
+
if (result && result.error) return result;
|
|
2378
|
+
const subtreeInstructions = await _subtreeInstructionsFor(accessedPath, cwd);
|
|
2379
|
+
return subtreeInstructions ? { ...result, subtreeInstructions } : result;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2179
2382
|
/**
|
|
2180
2383
|
* Drop skills whose `paths:` frontmatter scopes them to a different subtree than
|
|
2181
2384
|
* the agent's cwd (large-monorepo lazy skill surface). A skill with no `paths`
|
|
@@ -2230,6 +2433,9 @@ async function executeToolInner(
|
|
|
2230
2433
|
signal = null,
|
|
2231
2434
|
backgroundSubAgents = null,
|
|
2232
2435
|
subAgentUsageSink = null,
|
|
2436
|
+
// Hook-envelope tracing: this run's trace id, threaded into child loops
|
|
2437
|
+
// (spawn_sub_agent / isolated run_skill) as their parent_id.
|
|
2438
|
+
hookTraceId = null,
|
|
2233
2439
|
},
|
|
2234
2440
|
) {
|
|
2235
2441
|
const localToolDescriptor =
|
|
@@ -2338,18 +2544,30 @@ async function executeToolInner(
|
|
|
2338
2544
|
}
|
|
2339
2545
|
|
|
2340
2546
|
if (rendered.length > 50000) {
|
|
2341
|
-
return attachDescriptor(
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2547
|
+
return attachDescriptor(
|
|
2548
|
+
await _withSubtreeInstructions(
|
|
2549
|
+
{
|
|
2550
|
+
content: rendered.substring(0, 50000) + "\n...(truncated)",
|
|
2551
|
+
size: rendered.length,
|
|
2552
|
+
hashed: args.hashed === true,
|
|
2553
|
+
...(range ? { range } : {}),
|
|
2554
|
+
},
|
|
2555
|
+
filePath,
|
|
2556
|
+
cwd,
|
|
2557
|
+
),
|
|
2558
|
+
);
|
|
2347
2559
|
}
|
|
2348
|
-
return attachDescriptor(
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2560
|
+
return attachDescriptor(
|
|
2561
|
+
await _withSubtreeInstructions(
|
|
2562
|
+
{
|
|
2563
|
+
content: rendered,
|
|
2564
|
+
hashed: args.hashed === true,
|
|
2565
|
+
...(range ? { range } : {}),
|
|
2566
|
+
},
|
|
2567
|
+
filePath,
|
|
2568
|
+
cwd,
|
|
2569
|
+
),
|
|
2570
|
+
);
|
|
2353
2571
|
}
|
|
2354
2572
|
|
|
2355
2573
|
case "write_file": {
|
|
@@ -2368,8 +2586,13 @@ async function executeToolInner(
|
|
|
2368
2586
|
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
2369
2587
|
_recordFileObservation(filePath);
|
|
2370
2588
|
return attachDescriptor(
|
|
2371
|
-
await
|
|
2372
|
-
|
|
2589
|
+
await _withSubtreeInstructions(
|
|
2590
|
+
await _withPostEditDiagnostics(
|
|
2591
|
+
{ success: true, path: filePath, size: wrote.size },
|
|
2592
|
+
filePath,
|
|
2593
|
+
cwd,
|
|
2594
|
+
additionalDirectories,
|
|
2595
|
+
),
|
|
2373
2596
|
filePath,
|
|
2374
2597
|
cwd,
|
|
2375
2598
|
),
|
|
@@ -2457,13 +2680,18 @@ async function executeToolInner(
|
|
|
2457
2680
|
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
2458
2681
|
_recordFileObservation(filePath);
|
|
2459
2682
|
return attachDescriptor(
|
|
2460
|
-
await
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2683
|
+
await _withSubtreeInstructions(
|
|
2684
|
+
await _withPostEditDiagnostics(
|
|
2685
|
+
{
|
|
2686
|
+
success: true,
|
|
2687
|
+
path: filePath,
|
|
2688
|
+
size: wrote.size,
|
|
2689
|
+
replaced: replaceAll ? count : 1,
|
|
2690
|
+
},
|
|
2691
|
+
filePath,
|
|
2692
|
+
cwd,
|
|
2693
|
+
additionalDirectories,
|
|
2694
|
+
),
|
|
2467
2695
|
filePath,
|
|
2468
2696
|
cwd,
|
|
2469
2697
|
),
|
|
@@ -2515,14 +2743,19 @@ async function executeToolInner(
|
|
|
2515
2743
|
if (wrote.error) return attachDescriptor({ error: wrote.error });
|
|
2516
2744
|
_recordFileObservation(filePath);
|
|
2517
2745
|
return attachDescriptor(
|
|
2518
|
-
await
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2746
|
+
await _withSubtreeInstructions(
|
|
2747
|
+
await _withPostEditDiagnostics(
|
|
2748
|
+
{
|
|
2749
|
+
success: true,
|
|
2750
|
+
path: filePath,
|
|
2751
|
+
size: wrote.size,
|
|
2752
|
+
lineNumber: result.lineNumber,
|
|
2753
|
+
previousContent: result.previousContent,
|
|
2754
|
+
},
|
|
2755
|
+
filePath,
|
|
2756
|
+
cwd,
|
|
2757
|
+
additionalDirectories,
|
|
2758
|
+
),
|
|
2526
2759
|
filePath,
|
|
2527
2760
|
cwd,
|
|
2528
2761
|
),
|
|
@@ -3104,6 +3337,8 @@ async function executeToolInner(
|
|
|
3104
3337
|
subAgentBudget,
|
|
3105
3338
|
subAgentContract,
|
|
3106
3339
|
settingsHooks,
|
|
3340
|
+
// Parent trace for the child's hook envelopes (parent_id).
|
|
3341
|
+
hookTraceId,
|
|
3107
3342
|
// Parent MCP plumbing — a spawn can inherit these into the child,
|
|
3108
3343
|
// filtered by the resolved contract's mcpServers allow-list.
|
|
3109
3344
|
mcpClient,
|
|
@@ -3675,8 +3910,15 @@ async function executeToolInner(
|
|
|
3675
3910
|
error: `code_intelligence action "rename_preview" requires "new_name".`,
|
|
3676
3911
|
});
|
|
3677
3912
|
}
|
|
3678
|
-
|
|
3913
|
+
// Multi-root workspace (P2 LSP): key the shared server pool on the root
|
|
3914
|
+
// that actually CONTAINS the file — a file inside an `--add-dir` root
|
|
3915
|
+
// gets that project's language server, not the cwd's. Single-root
|
|
3916
|
+
// sessions resolve to cwd exactly as before (byte-identical).
|
|
3679
3917
|
const file = args.file ? path.resolve(cwd, args.file) : null;
|
|
3918
|
+
const wsRoots = workspaceRootsFor(cwd, additionalDirectories);
|
|
3919
|
+
const ci = await _getSharedCodeIntel(
|
|
3920
|
+
file ? pickRootForFile(file, wsRoots) : cwd,
|
|
3921
|
+
);
|
|
3680
3922
|
let res;
|
|
3681
3923
|
try {
|
|
3682
3924
|
switch (action) {
|
|
@@ -3692,9 +3934,29 @@ async function executeToolInner(
|
|
|
3692
3934
|
case "document_symbols":
|
|
3693
3935
|
res = await ci.documentSymbols(file);
|
|
3694
3936
|
break;
|
|
3695
|
-
case "workspace_symbols":
|
|
3696
|
-
|
|
3937
|
+
case "workspace_symbols": {
|
|
3938
|
+
// Fan out across EVERY workspace root and merge — symbols from an
|
|
3939
|
+
// --add-dir project are stamped with their `root` so same-named
|
|
3940
|
+
// symbols stay unambiguous. One root → the exact legacy call.
|
|
3941
|
+
if (wsRoots.length > 1) {
|
|
3942
|
+
const perRoot = [];
|
|
3943
|
+
for (const r of wsRoots) {
|
|
3944
|
+
try {
|
|
3945
|
+
const rci = await _getSharedCodeIntel(r);
|
|
3946
|
+
perRoot.push(await rci.workspaceSymbols(String(args.query)));
|
|
3947
|
+
} catch (err) {
|
|
3948
|
+
perRoot.push({
|
|
3949
|
+
available: false,
|
|
3950
|
+
reason: err?.message || "workspace_symbols failed",
|
|
3951
|
+
});
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
res = mergeWorkspaceSymbolResults(perRoot, wsRoots);
|
|
3955
|
+
} else {
|
|
3956
|
+
res = await ci.workspaceSymbols(String(args.query));
|
|
3957
|
+
}
|
|
3697
3958
|
break;
|
|
3959
|
+
}
|
|
3698
3960
|
case "diagnostics":
|
|
3699
3961
|
res = await ci.diagnostics(file);
|
|
3700
3962
|
break;
|
|
@@ -3743,12 +4005,18 @@ async function executeToolInner(
|
|
|
3743
4005
|
});
|
|
3744
4006
|
}
|
|
3745
4007
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
3746
|
-
return attachDescriptor(
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
4008
|
+
return attachDescriptor(
|
|
4009
|
+
await _withSubtreeInstructions(
|
|
4010
|
+
{
|
|
4011
|
+
entries: entries.map((e) => ({
|
|
4012
|
+
name: e.name,
|
|
4013
|
+
type: e.isDirectory() ? "dir" : "file",
|
|
4014
|
+
})),
|
|
4015
|
+
},
|
|
4016
|
+
dirPath,
|
|
4017
|
+
cwd,
|
|
4018
|
+
),
|
|
4019
|
+
);
|
|
3752
4020
|
}
|
|
3753
4021
|
|
|
3754
4022
|
case "run_skill": {
|
|
@@ -3788,6 +4056,7 @@ async function executeToolInner(
|
|
|
3788
4056
|
role: `skill-${args.skill_name}`,
|
|
3789
4057
|
task: `Execute the "${args.skill_name}" skill with input: ${(args.input || "").substring(0, 200)}`,
|
|
3790
4058
|
allowedTools: ["read_file", "search_files", "list_dir"],
|
|
4059
|
+
hookParentTraceId: hookTraceId || null,
|
|
3791
4060
|
cwd,
|
|
3792
4061
|
onUsage: skillUsageSink
|
|
3793
4062
|
? (u) => {
|
|
@@ -4251,6 +4520,32 @@ async function _executeRunCode(args, cwd) {
|
|
|
4251
4520
|
};
|
|
4252
4521
|
}
|
|
4253
4522
|
|
|
4523
|
+
// Unified install-command audit (P0 sandbox slice): the same opt-in
|
|
4524
|
+
// trail (CC_INSTALL_AUDIT / settings installPolicy) that records
|
|
4525
|
+
// run_shell installs also records run_code auto-installs — one audit
|
|
4526
|
+
// file covers every "fetch and run third-party code" entry point.
|
|
4527
|
+
// Best-effort; default (policy off) writes nothing.
|
|
4528
|
+
let auditUnifiedInstall = () => {};
|
|
4529
|
+
try {
|
|
4530
|
+
const icp = await import("../lib/install-command-policy.js");
|
|
4531
|
+
const unified = icp.resolveInstallPolicy({});
|
|
4532
|
+
if (unified.audit) {
|
|
4533
|
+
const installCommand = `${interpreter} -m pip install ${packageName}`;
|
|
4534
|
+
const cls = icp.classifyInstallCommand(installCommand);
|
|
4535
|
+
auditUnifiedInstall = (outcome, detail) =>
|
|
4536
|
+
icp.recordInstallCommandAudit({
|
|
4537
|
+
source: "run_code_auto_install",
|
|
4538
|
+
command: installCommand,
|
|
4539
|
+
outcome,
|
|
4540
|
+
...(detail ? { detail } : {}),
|
|
4541
|
+
installs: cls.installs,
|
|
4542
|
+
global: icp.hasGlobalInstall(cls),
|
|
4543
|
+
});
|
|
4544
|
+
}
|
|
4545
|
+
} catch {
|
|
4546
|
+
/* unified audit must never affect the install itself */
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4254
4549
|
// Attempt pip install
|
|
4255
4550
|
try {
|
|
4256
4551
|
execSync(`${interpreter} -m pip install ${packageName}`, {
|
|
@@ -4265,6 +4560,7 @@ async function _executeRunCode(args, cwd) {
|
|
|
4265
4560
|
cwd,
|
|
4266
4561
|
outcome: "installed",
|
|
4267
4562
|
});
|
|
4563
|
+
auditUnifiedInstall("installed");
|
|
4268
4564
|
|
|
4269
4565
|
// Retry execution
|
|
4270
4566
|
const retryStart = Date.now();
|
|
@@ -4292,6 +4588,10 @@ async function _executeRunCode(args, cwd) {
|
|
|
4292
4588
|
outcome: "failed",
|
|
4293
4589
|
detail: (pipErr.stderr || pipErr.message || "").substring(0, 200),
|
|
4294
4590
|
});
|
|
4591
|
+
auditUnifiedInstall(
|
|
4592
|
+
"failed",
|
|
4593
|
+
(pipErr.stderr || pipErr.message || "").substring(0, 200),
|
|
4594
|
+
);
|
|
4295
4595
|
return {
|
|
4296
4596
|
error: (stderr || message).substring(0, 5000),
|
|
4297
4597
|
stderr: stderr.substring(0, 5000),
|
|
@@ -4556,6 +4856,8 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4556
4856
|
let mdProfile = null;
|
|
4557
4857
|
let mdModel = null;
|
|
4558
4858
|
let mdContract = null; // agent-file's normalized subagent contract (definition)
|
|
4859
|
+
let mdSparsePaths = null; // agent-file worktree sparse-checkout paths
|
|
4860
|
+
let mdSymlinkDirectories = null; // agent-file worktree dep-dir symlinks
|
|
4559
4861
|
if (args.agent) {
|
|
4560
4862
|
try {
|
|
4561
4863
|
const { getAgent } = await import("../lib/agents.js");
|
|
@@ -4574,6 +4876,10 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4574
4876
|
if (!subIsolation && md.isolation === "worktree") {
|
|
4575
4877
|
subIsolation = "worktree";
|
|
4576
4878
|
}
|
|
4879
|
+
if (md.sparsePaths != null) mdSparsePaths = md.sparsePaths;
|
|
4880
|
+
if (md.symlinkDirectories != null) {
|
|
4881
|
+
mdSymlinkDirectories = md.symlinkDirectories;
|
|
4882
|
+
}
|
|
4577
4883
|
if (md.model) mdModel = md.model;
|
|
4578
4884
|
if (md.contract) mdContract = md.contract;
|
|
4579
4885
|
if (md.systemPrompt) {
|
|
@@ -4894,10 +5200,37 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4894
5200
|
}
|
|
4895
5201
|
: null;
|
|
4896
5202
|
|
|
5203
|
+
// Worktree sparse-checkout + dependency symlink (large-monorepo lever): when
|
|
5204
|
+
// the child runs in an isolated worktree, only materialize the packages it
|
|
5205
|
+
// needs (sparsePaths) and reuse approved dep dirs (e.g. node_modules) from the
|
|
5206
|
+
// main checkout (symlinkDirectories). Mirrors the team-worktree passthrough;
|
|
5207
|
+
// spawn args win over the agent-file's values. Resolved ONLY when isolation is
|
|
5208
|
+
// on → absent for a non-worktree spawn = byte-identical (full checkout).
|
|
5209
|
+
let subWorktreeOptions = null;
|
|
5210
|
+
if (subIsolation === "worktree") {
|
|
5211
|
+
try {
|
|
5212
|
+
const { normalizeSparsePaths } =
|
|
5213
|
+
await import("../lib/worktree-sparse.js");
|
|
5214
|
+
const sparse = normalizeSparsePaths(
|
|
5215
|
+
args.sparsePaths ?? mdSparsePaths ?? null,
|
|
5216
|
+
);
|
|
5217
|
+
const symlink = args.symlinkDirectories ?? mdSymlinkDirectories ?? null;
|
|
5218
|
+
const wtOpts = {};
|
|
5219
|
+
if (sparse) wtOpts.sparsePaths = sparse;
|
|
5220
|
+
if (symlink != null) wtOpts.symlinkDirectories = symlink;
|
|
5221
|
+
if (Object.keys(wtOpts).length) subWorktreeOptions = wtOpts;
|
|
5222
|
+
} catch {
|
|
5223
|
+
subWorktreeOptions = null; // best-effort → full checkout on any error
|
|
5224
|
+
}
|
|
5225
|
+
}
|
|
5226
|
+
|
|
4897
5227
|
const subCtx = SubAgentContext.create({
|
|
4898
5228
|
role,
|
|
4899
5229
|
task,
|
|
4900
5230
|
parentId: parentSessionId,
|
|
5231
|
+
// Hook-envelope tracing: the child loop stamps its own runId as trace_id
|
|
5232
|
+
// and THIS run's id as parent_id on every settings-hook payload it fires.
|
|
5233
|
+
hookParentTraceId: ctx.hookTraceId || null,
|
|
4901
5234
|
inheritedContext: resolvedContext,
|
|
4902
5235
|
allowedTools: allowedTools || null,
|
|
4903
5236
|
cwd: ctx.cwd,
|
|
@@ -4912,6 +5245,9 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4912
5245
|
// worktree isolation. undefined keeps the profile/flag defaults intact.
|
|
4913
5246
|
...(subMaxTurns ? { maxIterations: subMaxTurns } : {}),
|
|
4914
5247
|
...(subIsolation === "worktree" ? { useWorktree: true } : {}),
|
|
5248
|
+
// Worktree sparse-checkout / dep-symlink options (large-monorepo); only
|
|
5249
|
+
// present for a worktree spawn that requested them (else full checkout).
|
|
5250
|
+
...(subWorktreeOptions ? { worktreeOptions: subWorktreeOptions } : {}),
|
|
4915
5251
|
// Resolved contract (gap 2026-07-12): cap the child's token budget and hand
|
|
4916
5252
|
// it its EFFECTIVE contract so its OWN spawns inherit this ceiling.
|
|
4917
5253
|
...(effectiveContract?.budget?.tokens
|
|
@@ -4993,7 +5329,15 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4993
5329
|
// refuses to finish while any background sub-agent is still running). Only
|
|
4994
5330
|
// available when the calling loop provided the per-run map — a bare
|
|
4995
5331
|
// executeTool() without it falls through to the blocking path.
|
|
4996
|
-
|
|
5332
|
+
//
|
|
5333
|
+
// `background` is driven by the resolved contract (spawn arg > agent-file
|
|
5334
|
+
// definition), so an agent-file can declare `background: true` and be spawned
|
|
5335
|
+
// detached without the caller passing it. `effectiveContract.background`
|
|
5336
|
+
// already folds in `args.background`; the explicit OR keeps the path
|
|
5337
|
+
// byte-identical if contract resolution was skipped (effectiveContract null).
|
|
5338
|
+
const wantsBackground =
|
|
5339
|
+
args.background === true || effectiveContract?.background === true;
|
|
5340
|
+
if (wantsBackground && ctx.backgroundSubAgents instanceof Map) {
|
|
4997
5341
|
// Cancel with the parent: forward the loop's abort signal so killing the
|
|
4998
5342
|
// run doesn't orphan a detached child mid-LLM-call.
|
|
4999
5343
|
subCtx._signal = subCtx._signal || ctx.signal || null;
|
|
@@ -6544,9 +6888,23 @@ export async function* agentLoop(messages, options) {
|
|
|
6544
6888
|
// Optional OpenTelemetry recorder (TelemetryRecorder). When present, the loop
|
|
6545
6889
|
// emits model/tool/retry spans + a per-run counter; when absent, zero cost.
|
|
6546
6890
|
const recorder = options.recorder || null;
|
|
6891
|
+
// Phase 5 run bookends — a stable runId lets envelope subscribers correlate
|
|
6892
|
+
// every tool_call / tool_result / message / ended event back to one run.
|
|
6893
|
+
// Minted BEFORE the tool context so hook envelopes can carry it as trace_id.
|
|
6894
|
+
const runId =
|
|
6895
|
+
options.runId ||
|
|
6896
|
+
`run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6897
|
+
|
|
6547
6898
|
const toolContext = {
|
|
6548
6899
|
hookDb: options.hookDb || null,
|
|
6549
6900
|
skillLoader: options.skillLoader || _defaultSkillLoader,
|
|
6901
|
+
// Hook-envelope tracing (P2 unified event bus): every settings-hook payload
|
|
6902
|
+
// fired during this run carries trace_id = this run's id; a spawned child
|
|
6903
|
+
// loop carries parent_id = the spawning run's id (threaded by
|
|
6904
|
+
// spawn_sub_agent via hookParentTraceId), so a subagent's hook events
|
|
6905
|
+
// correlate back to the parent trace. Null parent at the top level.
|
|
6906
|
+
hookTraceId: runId,
|
|
6907
|
+
hookParentId: options.hookParentTraceId || null,
|
|
6550
6908
|
// Contract-driven skill allow-list (subagent capability INTERSECT). null =
|
|
6551
6909
|
// unrestricted; [] = none; a list restricts run_skill/list_skills to those
|
|
6552
6910
|
// ids/dirNames. Set by the spawn path from the resolved subagent contract.
|
|
@@ -6736,11 +7094,6 @@ export async function* agentLoop(messages, options) {
|
|
|
6736
7094
|
});
|
|
6737
7095
|
}
|
|
6738
7096
|
|
|
6739
|
-
// Phase 5 run bookends — a stable runId lets envelope subscribers correlate
|
|
6740
|
-
// every tool_call / tool_result / message / ended event back to one run.
|
|
6741
|
-
const runId =
|
|
6742
|
-
options.runId ||
|
|
6743
|
-
`run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6744
7097
|
// Workflow-tracing attributes (Claude-Code 2.1.202): stamp the run id (and
|
|
6745
7098
|
// an optional caller-provided workflow name) onto EVERY span this run emits,
|
|
6746
7099
|
// so a collector can group one run's model/tool spans into one workflow.
|