chainlesschain 0.162.161 → 0.162.163
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-DMn528q-.js → AIOps-BQ0kgfA1.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-D9ZcDCJL.js → ActionButton-Dr1MDANe.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-A_K-8PX0.js → Analytics-CZ5vsaXW.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-D4pFW3X6.js → AppLayout-BIBga881.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Dz46COAd.js → Artifacts-vpXHRU3u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-cbMjbyg0.js → Audit-B8exuoub.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Dtf88GAj.js → BackgroundAgents-923evRRN.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-C5KRxn52.js → Backup-sr_sswlQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Dd2UK3BN.js → BaseInput-CnW-4XKT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-lptKTFdG.js → Chat-FZCsTZIL.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-_yspG6rG.js → Checkbox-DFrxB59H.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BGq_GAha.js → Codegen-_b6WEel8.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CePor71g.js → Col-CkbnnZ-K.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CLOWhIT7.js → Community-BxM_82zW.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CivJt2vT.js → Compact-DYinP45R.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Btmo1GGE.js → Compliance-DyXp3sSP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Ct35OlG4.js → Cowork-BEr2DhW7.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CTc2GmhB.js → Cron-B-Vwo1hN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-ZtiBETk1.js → Crosschain-DbLxKddd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BNqMQQXw.js → DID-BsQ_LFw9.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BmzZd-9V.js → Dashboard-lVlXQHu5.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CMVgvP2M.js → Dropdown-Beo_foO7.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CcqQFAfT.js → EmailListRenderer-DszHIXED.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CTqB1_4V.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CiKOOzlb.js → Federation-CuJLa2eb.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Wk-OT0gr.js → FormItemContext-Cxd1lxhc.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DbEQT6ue.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
- package/src/assets/web-panel/assets/{Git-0ZvCPSD9.js → Git-WqPKetx6.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CmVuKkPb.js → Governance-CWYtY3ke.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CTWE3Jwg.js → Inference-BnX1iiNV.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CBEIF00L.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DLhxDwVG.js → Logs-DqLMNntm.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BqYwEYou.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BgsCBrnc.js → Marketplace-_vGfmhNl.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-ClBUGDS2.js → McpTools-BeJDOkDj.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CnG_ZnYw.js → Memory-Da0EEZDO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BUOAbR1Z.js → MobileBridge-DSuzcCCy.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Dnt_9-WB.js → MobileProjects-D0RAk4e8.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CLjt4kCD.js → Mtc-BE9YgdA0.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-f24U9Aes.js → MtcAudit-fC2BpO5S.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-GrmGQbSq.js → Multisig-D24Xxqw3.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DADaWn-n.js → NLProgramming-Bs3oGvS8.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-xtf5RX6C.js → Notes-BFjmAA-3.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cd27Rgtx.js → NotificationSettings-BX_9Bhv0.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-fXRxlDjl.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-joCFlNB0.js → Organization-DBcdsVEP.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-B1HoURq0.js → Overflow-DDyaF9vC.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DiwsKbT7.js → P2P-BB78L4he.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Xe-BsW6S.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C23qi8kl.js → Permissions-INbnWv4j.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-BS1syX2f.js → PersonalDataHub-BST-VmTq.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-DRYwwDyh.js → Pipeline-CE7_tS2k.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BQKaDHPH.js → Privacy-hK5L8dOB.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BC6F9xiG.js → ProjectInit-BvkkUoDn.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-ClFPc1Nd.js → ProjectSettings-u7_EUtkm.js} +2 -2
- package/src/assets/web-panel/assets/Projects-5Fmt_8Nw.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CLWuOUNt.js → Providers-B9eanarR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DiqULTxb.js → QrScannerModal-C0dX5N2f.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-9A03P-E_.js → QuickAsk-CKTpG-PZ.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CJZAnbq_.js → Recommend-Px_w_XQM.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C9A1R77h.js → RemoteSession-Di0fnYkM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BIZc6Rky.js → Reputation-vesnWjXt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DfIPNtuG.js → Row-exbY6LkT.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C5znFcQ1.js → RssFeed-B3O3x70t.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DSs2z3OX.js → Search-uRA0DhNz.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CV6LcrLY.js → Security-CViqdH6K.js} +4 -4
- package/src/assets/web-panel/assets/{Services-cmhoX0Xj.js → Services-CkXS97WN.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-tCsg2po7.js → Skeleton-DxW5WYnE.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BK1KUDiy.js → Skills-CLxmGFse.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-yRgyeA82.js → Sla-Cr5fziXq.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-c1mo1mlp.js → SpeechSettings-H0JSb3cp.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-koIAfuT3.js → SyncSettings-DMYWMae7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DkkreAaB.js → Tasks-CbYyDdF4.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-fy5Ctf9M.js → Templates-Cpc8JjiK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Bw70Jf6S.js → Tenant-CRh77Jda.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BKh6Rx-4.js → Terminal-4D8tNBO5.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Dg54WP0c.js → TimelineRenderer-D41jzzbG.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BvvD5SxJ.js → Tokens-C_jWEy6b.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B7Ug9x4e.js → Trigger-Dk1ZUhsy.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BjvuMORY.js → Trust-CLVy4l7m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CZqhkx8p.js → UkeySign-C-FeqFDj.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-ClR50lhX.js → VideoEditing-CqVZzyMm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DlfppYjx.js → Wallet-DX9BW9pu.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-BirgVr8B.js → WebAuthn-7hQDpWw_.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-B8DP_bax.js → WorkflowEditor-6XqUAO0j.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DA8AUZzL.js → chat-5R_vkaJG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C38pLGp1.js → colors-huUPZdC-.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-81rtr98o.js → compact-item-CfJlgisE.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-D34ixq8t.js → createContext-jO8heofr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-D4bzqTrZ.js → hasIn-DY8Vxqx7.js} +1 -1
- package/src/assets/web-panel/assets/{index-E1OJRXs4.js → index-A48PtXw9.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3zCCZRS.js → index-B300RPnT.js} +3 -3
- package/src/assets/web-panel/assets/{index-Cd3KuNK5.js → index-B74OXFxC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dmo3dFHb.js → index-BAON7TzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ddk0e_Pi.js → index-BCqxyvqp.js} +1 -1
- package/src/assets/web-panel/assets/{index-9qoVpPt6.js → index-BHE8OIls.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBFSo1NF.js → index-BQS-uPOK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUQLXLQM.js → index-BRXOP09X.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6GfvEQG.js → index-BYLBPpt8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFyWKOBD.js → index-BfCx4pFT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP-zkXK8.js → index-BliaT02i.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMgnRwrK.js → index-BmqGerpC.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIP62a6r.js → index-BsBtRWbA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DkHqMnn3.js → index-BtEXSoPL.js} +1 -1
- package/src/assets/web-panel/assets/index-BxUKMRpP.js +1 -0
- package/src/assets/web-panel/assets/{index-B8mkLM4x.js → index-Bxax9vQ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZRJRItm0.js → index-C2iVSFpy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CP5alL4t.js → index-CA6sO5VD.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS3M5H9V.js → index-CA90TZxG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvfCEJG-.js → index-CLhBh-qE.js} +1 -1
- package/src/assets/web-panel/assets/{index-tKiC8T-4.js → index-CRElRhyS.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0QArPQt.js → index-Cbe9G0V4.js} +1 -1
- package/src/assets/web-panel/assets/{index-COe_cOLJ.js → index-ChvHubX3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvL_3J_n.js → index-Cis_Ip7H.js} +1 -1
- package/src/assets/web-panel/assets/{index-F76w1NiL.js → index-Cjxbu68P.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlxEphl7.js → index-CzKmteNS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuS-UPoj.js → index-D0pet5Uy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbhE3j5j.js → index-D1VLuww0.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_2VYPr1.js → index-DXBcvUnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DakfFZ-k.js → index-Db1cLWir.js} +1 -1
- package/src/assets/web-panel/assets/{index-fI5sXMLg.js → index-DoI2sZm7.js} +1 -1
- package/src/assets/web-panel/assets/index-DscU9iPI.js +1 -0
- package/src/assets/web-panel/assets/{index-CoVgZw_b.js → index-KRHroryI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvD7b2IK.js → index-d1xkbtNz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtGxuQrO.js → index-dKx57io7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BqIhLAbA.js → index-j3O2EEJe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXd2_LmH.js → index-p85YaaKw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DFHa1HIg.js → index-v3pTaEQm.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0Ih7Hwn.js → index-vXs3B8GJ.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-ByyvflIR.js → initDefaultProps-MCB7-raZ.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B3FQm2By.js → motion-UWDF9swV.js} +1 -1
- package/src/assets/web-panel/assets/{move-BSfns6Ef.js → move-CsTmgXZU.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CTG1Ieri.js → mtc-parser-fQy6KGLg.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DObM_2j6.js → omit-woPnEjd1.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DOfelQ1t.js → pickAttrs-Bs2dZ1gf.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CHZILds_.js → placementArrow-C7EVgPkg.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-REbucKSx.js → responsiveObserve-BH2kYCTN.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C6EZzD7o.js → slide-BFBP2V_e.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BxkWS-g3.js → statusUtils-xdDxDYse.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Chklyoq6.js → styleChecker-aAWgPPQf.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CRVwKrrI.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DV24ik_J.js → useFs-BrOih40N.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C7vIo4Pg.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-By-v5GlF.js → vnode-B8lutNSE.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B1w9Am3r.js → zoom-CffNre7V.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +254 -23
- package/src/commands/agent.js +105 -11
- package/src/commands/context.js +125 -14
- package/src/commands/hook.js +181 -7
- package/src/commands/plugin.js +239 -1
- package/src/commands/review.js +352 -2
- package/src/data/changelog.json +14 -4
- package/src/gateways/ws/remote-session-protocol.js +39 -0
- package/src/gateways/ws/session-protocol.js +5 -2
- package/src/lib/agent-schedule-store.js +289 -10
- package/src/lib/agents.js +19 -0
- package/src/lib/background-agent-supervisor.js +34 -7
- package/src/lib/channels/channel-manager.js +54 -2
- package/src/lib/cli-context-engineering.js +9 -2
- package/src/lib/context-breakdown.js +116 -0
- package/src/lib/docs-drift.js +166 -0
- package/src/lib/doctor-checkup.js +128 -0
- package/src/lib/hook-event-bus.cjs +0 -0
- package/src/lib/hook-event-log.cjs +193 -0
- package/src/lib/interaction-adapter.js +34 -4
- package/src/lib/json-schema-output.js +75 -12
- package/src/lib/json-schema-validate.js +831 -0
- package/src/lib/lsp/__tests__/diagnostics-scheduler.test.js +93 -0
- package/src/lib/lsp/diagnostics-scheduler.js +127 -0
- package/src/lib/operation-fingerprint.js +95 -0
- package/src/lib/plugin-runtime/capabilities.js +371 -0
- package/src/lib/plugin-runtime/capability-consent.js +175 -0
- package/src/lib/plugin-runtime/manifest.js +30 -0
- package/src/lib/plugin-runtime/policy.js +62 -0
- package/src/lib/plugin-runtime/scopes.js +20 -4
- package/src/lib/project-instructions.js +128 -0
- package/src/lib/remote-approval-bridge.js +16 -0
- package/src/lib/repl-goal.js +132 -0
- package/src/lib/repl-rewind.js +52 -0
- package/src/lib/review-pipeline.js +227 -0
- package/src/lib/runtime-checkup.js +318 -0
- package/src/lib/schedule-planner.js +17 -1
- package/src/lib/session-lifecycle.js +305 -0
- package/src/lib/settings-hook-events.cjs +52 -13
- package/src/lib/settings-loader.cjs +42 -0
- package/src/lib/side-effect-ledger-store.js +88 -0
- package/src/lib/side-effect-ledger.js +296 -0
- package/src/lib/sub-agent-context.js +74 -5
- package/src/lib/subagent-contract.js +383 -0
- package/src/lib/subagent-inheritance.js +112 -0
- package/src/lib/telemetry-ids.js +190 -0
- package/src/lib/turn-binding-store.js +119 -0
- package/src/lib/turn-binding.js +397 -0
- package/src/repl/agent-repl.js +136 -0
- package/src/repl/permission-tier.js +28 -0
- package/src/runtime/agent-core.js +387 -14
- package/src/runtime/coding-agent-contract-shared.cjs +60 -1
- package/src/runtime/headless-runner.js +267 -0
- package/src/runtime/headless-stream.js +60 -1
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C2KLtwHz.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-DxywvhTU.js +0 -1
- package/src/assets/web-panel/assets/Projects-Dn-f5aYe.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CHDIEOeN.js +0 -1
- package/src/assets/web-panel/assets/index-CY1NURWZ.js +0 -1
- package/src/assets/web-panel/assets/index-DokUYqpq.js +0 -1
|
@@ -26,6 +26,7 @@ import sharedHookRunner from "../lib/hook-runner.cjs";
|
|
|
26
26
|
import sharedHookEvents from "../lib/settings-hook-events.cjs";
|
|
27
27
|
import { mergeProviderOptions } from "../lib/provider-options.js";
|
|
28
28
|
import { applyCredentialProxy } from "../lib/credential-proxy.js";
|
|
29
|
+
import { buildTelemetryAttributes } from "../lib/telemetry-ids.js";
|
|
29
30
|
import { getPlanModeManager } from "../lib/plan-mode.js";
|
|
30
31
|
import { CLISkillLoader } from "../lib/skill-loader.js";
|
|
31
32
|
import { executeHooks, HookEvents } from "../lib/hook-manager.js";
|
|
@@ -1470,6 +1471,8 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1470
1471
|
try {
|
|
1471
1472
|
toolResult = await executeToolInner(name, args, {
|
|
1472
1473
|
skillLoader,
|
|
1474
|
+
// Subagent skill capability INTERSECT — forwarded to run_skill/list_skills.
|
|
1475
|
+
skillAllowlist: context.skillAllowlist ?? null,
|
|
1473
1476
|
cwd,
|
|
1474
1477
|
parentMessages: context.parentMessages,
|
|
1475
1478
|
interaction: context.interaction,
|
|
@@ -1495,6 +1498,13 @@ export async function executeTool(name, args, context = {}) {
|
|
|
1495
1498
|
settingsVerdict,
|
|
1496
1499
|
subAgentDepth: context.subAgentDepth || 0,
|
|
1497
1500
|
subAgentBudget: context.subAgentBudget || null,
|
|
1501
|
+
// Effective contract of THIS loop (parent ceiling for a nested spawn) +
|
|
1502
|
+
// the MCP tool definitions this loop exposes (inheritable by a spawn).
|
|
1503
|
+
subAgentContract: context.subAgentContract || null,
|
|
1504
|
+
extraToolDefinitions: context.extraToolDefinitions || null,
|
|
1505
|
+
// Parent memory source — forwarded so a memory-granted spawn inherits it.
|
|
1506
|
+
memoryDb: context.memoryDb || null,
|
|
1507
|
+
permanentMemory: context.permanentMemory || null,
|
|
1498
1508
|
interactiveApproval: context.interactiveApproval || false,
|
|
1499
1509
|
settingsHooks: context.settingsHooks || null,
|
|
1500
1510
|
signal: context.signal || null,
|
|
@@ -2095,6 +2105,7 @@ async function executeToolInner(
|
|
|
2095
2105
|
args,
|
|
2096
2106
|
{
|
|
2097
2107
|
skillLoader,
|
|
2108
|
+
skillAllowlist = null,
|
|
2098
2109
|
cwd,
|
|
2099
2110
|
parentMessages,
|
|
2100
2111
|
interaction,
|
|
@@ -2102,7 +2113,11 @@ async function executeToolInner(
|
|
|
2102
2113
|
hostManagedToolPolicy,
|
|
2103
2114
|
externalToolDescriptors,
|
|
2104
2115
|
externalToolExecutors,
|
|
2116
|
+
extraToolDefinitions = null,
|
|
2105
2117
|
mcpClient,
|
|
2118
|
+
memoryDb = null,
|
|
2119
|
+
permanentMemory = null,
|
|
2120
|
+
subAgentContract = null,
|
|
2106
2121
|
llmOptions,
|
|
2107
2122
|
shellPolicyOverrides,
|
|
2108
2123
|
classifyAllShell = false,
|
|
@@ -2127,6 +2142,13 @@ async function executeToolInner(
|
|
|
2127
2142
|
: null;
|
|
2128
2143
|
const runtimeDescriptor =
|
|
2129
2144
|
getRuntimeToolDescriptor(name) || localToolDescriptor;
|
|
2145
|
+
// Subagent skill capability INTERSECT: null = unrestricted; a list (possibly
|
|
2146
|
+
// empty) restricts which skills run_skill/list_skills expose in this loop.
|
|
2147
|
+
const _skillAllowlist = Array.isArray(skillAllowlist) ? skillAllowlist : null;
|
|
2148
|
+
const skillAllowed = (s) =>
|
|
2149
|
+
!_skillAllowlist ||
|
|
2150
|
+
_skillAllowlist.includes(s.id) ||
|
|
2151
|
+
_skillAllowlist.includes(s.dirName);
|
|
2130
2152
|
const hostToolPolicies =
|
|
2131
2153
|
hostManagedToolPolicy?.tools || hostManagedToolPolicy?.toolPolicies || null;
|
|
2132
2154
|
const hostToolPolicy =
|
|
@@ -2902,8 +2924,20 @@ async function executeToolInner(
|
|
|
2902
2924
|
// the run_shell gate). A settings `allow` rule (ruleAllowed) pre-
|
|
2903
2925
|
// authorizes; headless leaves interactiveApproval false so its existing
|
|
2904
2926
|
// per-permission-mode behavior is unchanged.
|
|
2927
|
+
//
|
|
2928
|
+
// A spawned sub-agent gets a dedicated CONFIRMER-LESS ApprovalGate for a
|
|
2929
|
+
// strict/trusted mode (2026-07-13). It has no human to prompt, so gate
|
|
2930
|
+
// run_code under it too — decide() auto-denies CONFIRM via no-confirmer,
|
|
2931
|
+
// enforcing the tier on arbitrary code exactly like run_shell/browser_act
|
|
2932
|
+
// (which already gate whenever an approvalGate is present). This is the
|
|
2933
|
+
// child's gate specifically: the main headless + REPL gates ALWAYS carry a
|
|
2934
|
+
// confirmer, so `!hasConfirmer()` leaves them byte-identical.
|
|
2935
|
+
const policyGateNoConfirmer =
|
|
2936
|
+
approvalGate &&
|
|
2937
|
+
typeof approvalGate.hasConfirmer === "function" &&
|
|
2938
|
+
!approvalGate.hasConfirmer();
|
|
2905
2939
|
if (
|
|
2906
|
-
interactiveApproval &&
|
|
2940
|
+
(interactiveApproval || policyGateNoConfirmer) &&
|
|
2907
2941
|
approvalGate &&
|
|
2908
2942
|
!ruleAllowed &&
|
|
2909
2943
|
typeof approvalGate.decide === "function"
|
|
@@ -2946,7 +2980,17 @@ async function executeToolInner(
|
|
|
2946
2980
|
llmOptions,
|
|
2947
2981
|
subAgentDepth,
|
|
2948
2982
|
subAgentBudget,
|
|
2983
|
+
subAgentContract,
|
|
2949
2984
|
settingsHooks,
|
|
2985
|
+
// Parent MCP plumbing — a spawn can inherit these into the child,
|
|
2986
|
+
// filtered by the resolved contract's mcpServers allow-list.
|
|
2987
|
+
mcpClient,
|
|
2988
|
+
externalToolDescriptors,
|
|
2989
|
+
externalToolExecutors,
|
|
2990
|
+
extraToolDefinitions,
|
|
2991
|
+
// Parent memory — inherited into the child only when contract grants it.
|
|
2992
|
+
memoryDb,
|
|
2993
|
+
permanentMemory,
|
|
2950
2994
|
signal,
|
|
2951
2995
|
backgroundSubAgents,
|
|
2952
2996
|
subAgentUsageSink,
|
|
@@ -3157,6 +3201,26 @@ async function executeToolInner(
|
|
|
3157
3201
|
const { parseDuration } = await import("../lib/loop.js");
|
|
3158
3202
|
const store = new AgentScheduleStore();
|
|
3159
3203
|
const action = String(args.action || "").toLowerCase();
|
|
3204
|
+
// Optional lifecycle knobs shared by all create actions: `expires` is a
|
|
3205
|
+
// duration after which an un-fired entry retires (never re-fires); `jitter`
|
|
3206
|
+
// is a per-entry spread so tasks sharing a cron minute fan out instead of
|
|
3207
|
+
// firing as a thundering herd.
|
|
3208
|
+
const expiresInMs = args.expires ? parseDuration(args.expires) : null;
|
|
3209
|
+
const jitterMs = args.jitter ? parseDuration(args.jitter) : 0;
|
|
3210
|
+
// Optional per-task execution policy for wakeup/cron: the scheduled run
|
|
3211
|
+
// can carry its OWN permission mode, worktree isolation and turn budget
|
|
3212
|
+
// instead of inheriting whatever ambient env `cc agenda run` runs in.
|
|
3213
|
+
// The store validates/drops each field; monitors ignore it.
|
|
3214
|
+
const runPolicy = {
|
|
3215
|
+
permissionMode: args.permission_mode || null,
|
|
3216
|
+
worktree: args.worktree === true,
|
|
3217
|
+
maxTurns: args.max_turns ?? null,
|
|
3218
|
+
goalCondition: args.goal_condition || null,
|
|
3219
|
+
maxOuterTurns: args.max_outer_turns ?? null,
|
|
3220
|
+
goalMaxTokens: args.goal_max_tokens ?? null,
|
|
3221
|
+
goalMaxCost: args.goal_max_cost ?? null,
|
|
3222
|
+
goalMaxTime: args.goal_max_time ?? null,
|
|
3223
|
+
};
|
|
3160
3224
|
if (action === "wakeup") {
|
|
3161
3225
|
if (!args.prompt) {
|
|
3162
3226
|
return attachDescriptor({
|
|
@@ -3168,6 +3232,9 @@ async function executeToolInner(
|
|
|
3168
3232
|
prompt: args.prompt,
|
|
3169
3233
|
delayMs,
|
|
3170
3234
|
label: args.label || null,
|
|
3235
|
+
expiresInMs,
|
|
3236
|
+
jitterMs,
|
|
3237
|
+
...runPolicy,
|
|
3171
3238
|
});
|
|
3172
3239
|
return attachDescriptor({
|
|
3173
3240
|
scheduled: entry,
|
|
@@ -3184,6 +3251,9 @@ async function executeToolInner(
|
|
|
3184
3251
|
prompt: args.prompt,
|
|
3185
3252
|
cron: args.cron,
|
|
3186
3253
|
label: args.label || null,
|
|
3254
|
+
expiresInMs,
|
|
3255
|
+
jitterMs,
|
|
3256
|
+
...runPolicy,
|
|
3187
3257
|
});
|
|
3188
3258
|
return attachDescriptor({ scheduled: entry });
|
|
3189
3259
|
}
|
|
@@ -3203,6 +3273,8 @@ async function executeToolInner(
|
|
|
3203
3273
|
notify: args.notify_title ? { title: args.notify_title } : null,
|
|
3204
3274
|
maxChecks: args.max_checks ?? null,
|
|
3205
3275
|
label: args.label || null,
|
|
3276
|
+
expiresInMs,
|
|
3277
|
+
jitterMs,
|
|
3206
3278
|
});
|
|
3207
3279
|
return attachDescriptor({ scheduled: entry });
|
|
3208
3280
|
}
|
|
@@ -3548,11 +3620,12 @@ async function executeToolInner(
|
|
|
3548
3620
|
}
|
|
3549
3621
|
|
|
3550
3622
|
case "run_skill": {
|
|
3551
|
-
const allSkills = skillLoader.getResolvedSkills();
|
|
3623
|
+
const allSkills = skillLoader.getResolvedSkills().filter(skillAllowed);
|
|
3552
3624
|
if (allSkills.length === 0) {
|
|
3553
3625
|
return attachDescriptor({
|
|
3554
|
-
error:
|
|
3555
|
-
"No skills
|
|
3626
|
+
error: _skillAllowlist
|
|
3627
|
+
? "No skills are available to this sub-agent (restricted by its contract's skill allow-list)."
|
|
3628
|
+
: "No skills found. Make sure you're in the ChainlessChain project root or have skills installed.",
|
|
3556
3629
|
});
|
|
3557
3630
|
}
|
|
3558
3631
|
const match = allSkills.find(
|
|
@@ -3693,9 +3766,13 @@ async function executeToolInner(
|
|
|
3693
3766
|
}
|
|
3694
3767
|
|
|
3695
3768
|
case "list_skills": {
|
|
3696
|
-
let skills = skillLoader.getResolvedSkills();
|
|
3769
|
+
let skills = skillLoader.getResolvedSkills().filter(skillAllowed);
|
|
3697
3770
|
if (skills.length === 0) {
|
|
3698
|
-
return attachDescriptor({
|
|
3771
|
+
return attachDescriptor({
|
|
3772
|
+
error: _skillAllowlist
|
|
3773
|
+
? "No skills are available to this sub-agent (restricted by its contract's skill allow-list)."
|
|
3774
|
+
: "No skills found.",
|
|
3775
|
+
});
|
|
3699
3776
|
}
|
|
3700
3777
|
if (args.category) {
|
|
3701
3778
|
skills = skills.filter(
|
|
@@ -4281,6 +4358,25 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4281
4358
|
error: `spawn_sub_agent: max nesting depth (${MAX_SUB_AGENT_DEPTH}) reached — complete the task directly instead of delegating further.`,
|
|
4282
4359
|
};
|
|
4283
4360
|
}
|
|
4361
|
+
// Contract-aware recursion tightening: a parent's EFFECTIVE contract can only
|
|
4362
|
+
// LOWER the absolute hard caps (fail-closed, tighten-only). Evaluated before
|
|
4363
|
+
// the breadth counter increments so spawnedCount is this-spawn-exclusive.
|
|
4364
|
+
if (ctx.subAgentContract) {
|
|
4365
|
+
try {
|
|
4366
|
+
const { enforceRecursionLimits } =
|
|
4367
|
+
await import("../lib/subagent-contract.js");
|
|
4368
|
+
const recur = enforceRecursionLimits({
|
|
4369
|
+
depth: currentDepth,
|
|
4370
|
+
spawnedCount: ctx.subAgentBudget?.spawned || 0,
|
|
4371
|
+
contract: ctx.subAgentContract,
|
|
4372
|
+
hardDepthCap: MAX_SUB_AGENT_DEPTH,
|
|
4373
|
+
hardChildrenCap: ctx.subAgentBudget?.max ?? MAX_SUB_AGENTS_PER_RUN,
|
|
4374
|
+
});
|
|
4375
|
+
if (!recur.ok) return { error: `spawn_sub_agent: ${recur.reason}` };
|
|
4376
|
+
} catch {
|
|
4377
|
+
/* contract module unavailable — hard caps above still apply */
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4284
4380
|
// Breadth cap: a shared counter (one object for the whole tree) bounds the
|
|
4285
4381
|
// TOTAL sub-agents a run may spawn, so a wide fan-out can't blow up even
|
|
4286
4382
|
// within the depth limit. Refuse + count BEFORE any work so the increment
|
|
@@ -4321,6 +4417,7 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4321
4417
|
// Explicit role/tools still win over the agent file's values.
|
|
4322
4418
|
let mdProfile = null;
|
|
4323
4419
|
let mdModel = null;
|
|
4420
|
+
let mdContract = null; // agent-file's normalized subagent contract (definition)
|
|
4324
4421
|
if (args.agent) {
|
|
4325
4422
|
try {
|
|
4326
4423
|
const { getAgent } = await import("../lib/agents.js");
|
|
@@ -4340,6 +4437,7 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4340
4437
|
subIsolation = "worktree";
|
|
4341
4438
|
}
|
|
4342
4439
|
if (md.model) mdModel = md.model;
|
|
4440
|
+
if (md.contract) mdContract = md.contract;
|
|
4343
4441
|
if (md.systemPrompt) {
|
|
4344
4442
|
mdProfile = { name: md.name, systemPrompt: md.systemPrompt };
|
|
4345
4443
|
}
|
|
@@ -4356,6 +4454,118 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4356
4454
|
};
|
|
4357
4455
|
}
|
|
4358
4456
|
|
|
4457
|
+
// Resolve the child's EFFECTIVE subagent contract: spawnArgs > agent-file
|
|
4458
|
+
// definition (mdContract) > parent ceiling (ctx.subAgentContract). The safe,
|
|
4459
|
+
// tighten-only fields are consumed below (isolation fail-closed, budget,
|
|
4460
|
+
// effort, context inheritance); this child's contract becomes the ceiling for
|
|
4461
|
+
// ITS own nested spawns (threaded via SubAgentContext.subAgentContract).
|
|
4462
|
+
let effectiveContract = null;
|
|
4463
|
+
let explicitContext = null;
|
|
4464
|
+
let skillAllowlist = null;
|
|
4465
|
+
// MCP-server + hook allow-lists for child INHERITANCE. Default `[]` = inherit
|
|
4466
|
+
// NONE, which equals today's behavior (a spawned child gets zero MCP tools /
|
|
4467
|
+
// zero Pre-PostToolUse hooks). `context: fork` resolves these to `null` (all);
|
|
4468
|
+
// an explicit list subsets them. See filterInherited* below.
|
|
4469
|
+
let mcpAllow = [];
|
|
4470
|
+
let hookAllow = [];
|
|
4471
|
+
// Was the child's permission mode EXPLICITLY driven — a `permissionMode` in the
|
|
4472
|
+
// spawn args / agent file, OR a NON-"default" parent ceiling from the run
|
|
4473
|
+
// (`--permission-mode manual|acceptEdits|…`)? Only then do we attach an
|
|
4474
|
+
// ApprovalGate to the child (below), so a plain default spawn stays ungated
|
|
4475
|
+
// = byte-identical.
|
|
4476
|
+
let permModeDriven = false;
|
|
4477
|
+
try {
|
|
4478
|
+
const { resolveSubagentContract, normalizeSubagentContract } =
|
|
4479
|
+
await import("../lib/subagent-contract.js");
|
|
4480
|
+
const spawnContract = normalizeSubagentContract(args);
|
|
4481
|
+
explicitContext = spawnContract.context ?? mdContract?.context ?? null;
|
|
4482
|
+
effectiveContract = resolveSubagentContract({
|
|
4483
|
+
parent: ctx.subAgentContract || {},
|
|
4484
|
+
definition: mdContract,
|
|
4485
|
+
spawnArgs: spawnContract,
|
|
4486
|
+
});
|
|
4487
|
+
permModeDriven =
|
|
4488
|
+
spawnContract.permissionMode != null ||
|
|
4489
|
+
mdContract?.permissionMode != null ||
|
|
4490
|
+
(ctx.subAgentContract?.permissionMode != null &&
|
|
4491
|
+
ctx.subAgentContract.permissionMode !== "default");
|
|
4492
|
+
// Skill capability INTERSECT: restrict the child's skills ONLY when it was
|
|
4493
|
+
// explicitly driven — an explicit `skills` list (spawn args or agent file)
|
|
4494
|
+
// OR an explicit `context` mode. A fully-defaulted spawn stays unrestricted
|
|
4495
|
+
// (null) so the silent-`fresh`→[] default can never strip ALL skills from a
|
|
4496
|
+
// plain sub-agent. `effectiveContract.skills` already encodes the intersect
|
|
4497
|
+
// against the parent ceiling (tighten-only across depth).
|
|
4498
|
+
const skillsDriven =
|
|
4499
|
+
spawnContract.skills != null ||
|
|
4500
|
+
mdContract?.skills != null ||
|
|
4501
|
+
explicitContext != null;
|
|
4502
|
+
skillAllowlist = skillsDriven ? (effectiveContract.skills ?? null) : null;
|
|
4503
|
+
// MCP/hooks work the OTHER way from skills: their pre-inheritance default is
|
|
4504
|
+
// "none", so the silent-`fresh`→[] resolution IS the safe current behavior —
|
|
4505
|
+
// no explicit-driven guard needed. A list (or `null` on fork) opts the child
|
|
4506
|
+
// into inheriting the corresponding parent capabilities.
|
|
4507
|
+
mcpAllow = effectiveContract.mcpServers ?? null;
|
|
4508
|
+
hookAllow = effectiveContract.hooks ?? null;
|
|
4509
|
+
} catch {
|
|
4510
|
+
effectiveContract = null; // contract resolution is best-effort
|
|
4511
|
+
skillAllowlist = null;
|
|
4512
|
+
mcpAllow = []; // inherit no MCP / hooks when resolution fails
|
|
4513
|
+
hookAllow = [];
|
|
4514
|
+
permModeDriven = false; // no gate when resolution fails
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4517
|
+
// Filter the parent loop's live MCP plumbing + settings hooks down to what the
|
|
4518
|
+
// child may inherit. Default `[]` → null → the spawn passes nothing, so a plain
|
|
4519
|
+
// sub-agent is byte-identical to before.
|
|
4520
|
+
let inheritedMcp = null;
|
|
4521
|
+
let inheritedHooks = null;
|
|
4522
|
+
try {
|
|
4523
|
+
const { filterInheritedMcp, filterInheritedHooks } =
|
|
4524
|
+
await import("../lib/subagent-inheritance.js");
|
|
4525
|
+
inheritedMcp = filterInheritedMcp(
|
|
4526
|
+
{
|
|
4527
|
+
extraToolDefinitions: ctx.extraToolDefinitions,
|
|
4528
|
+
externalToolDescriptors: ctx.externalToolDescriptors,
|
|
4529
|
+
externalToolExecutors: ctx.externalToolExecutors,
|
|
4530
|
+
mcpClient: ctx.mcpClient,
|
|
4531
|
+
},
|
|
4532
|
+
mcpAllow,
|
|
4533
|
+
);
|
|
4534
|
+
inheritedHooks = filterInheritedHooks(ctx.settingsHooks || null, hookAllow);
|
|
4535
|
+
} catch {
|
|
4536
|
+
inheritedMcp = null; // inheritance is best-effort; never break the spawn
|
|
4537
|
+
inheritedHooks = null;
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4540
|
+
// Memory INHERITANCE (contract `memory` boolean, tighten-only across depth):
|
|
4541
|
+
// grant the child the parent's hierarchical-memory DB ONLY when the resolved
|
|
4542
|
+
// contract says memory:true (explicit, or context:fork from a memory-bearing
|
|
4543
|
+
// parent). Default (silent-`fresh`→memory:false) → no db + memoryEnabled:false
|
|
4544
|
+
// → no recall = today's behavior. `effectiveContract.memory` already encodes
|
|
4545
|
+
// the intersect (a parent that denied memory can never re-grant downstream).
|
|
4546
|
+
const memoryGranted = effectiveContract?.memory === true;
|
|
4547
|
+
const inheritedMemory =
|
|
4548
|
+
memoryGranted && ctx.memoryDb
|
|
4549
|
+
? { db: ctx.memoryDb, permanentMemory: ctx.permanentMemory || null }
|
|
4550
|
+
: null;
|
|
4551
|
+
|
|
4552
|
+
// Worktree isolation must FAIL CLOSED: if requested but the cwd is not a git
|
|
4553
|
+
// repo, refuse the spawn instead of silently running in the parent checkout.
|
|
4554
|
+
if (subIsolation === "worktree") {
|
|
4555
|
+
try {
|
|
4556
|
+
const { resolveIsolationFailClosed } =
|
|
4557
|
+
await import("../lib/subagent-contract.js");
|
|
4558
|
+
const { isGitRepo } = await import("../lib/git-integration.js");
|
|
4559
|
+
const iso = resolveIsolationFailClosed({
|
|
4560
|
+
requested: "worktree",
|
|
4561
|
+
available: isGitRepo(ctx.cwd),
|
|
4562
|
+
});
|
|
4563
|
+
if (!iso.ok) return { error: `spawn_sub_agent: ${iso.reason}` };
|
|
4564
|
+
} catch {
|
|
4565
|
+
/* helper unavailable — sub-agent-context.js still fails closed at run() */
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4359
4569
|
// Phase 3: resolve declarative profile if requested. Explicit tools/context
|
|
4360
4570
|
// override profile defaults; missing fields fall back to the profile.
|
|
4361
4571
|
let profile = null;
|
|
@@ -4392,9 +4602,68 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4392
4602
|
allowedTools = base.filter((t) => !deny.has(t));
|
|
4393
4603
|
}
|
|
4394
4604
|
|
|
4395
|
-
//
|
|
4605
|
+
// permissionMode enforcement into the child gate. Reuses the runner's own
|
|
4606
|
+
// resolvers (single-sourced, no drift); best-effort so it never breaks a spawn.
|
|
4607
|
+
// • `plan` → clamp to the read-only tool set (same rule as resolveEnabledTools),
|
|
4608
|
+
// so a plan sub-agent physically cannot mutate anything.
|
|
4609
|
+
// • confirmer threading → hand the child the mode's NON-interactive confirmer
|
|
4610
|
+
// as `permissionConfirm` (governs the ask / sensitive-file / destructive-git
|
|
4611
|
+
// gates) ONLY when it is the autopilot (bypassPermissions) ALLOW confirmer.
|
|
4612
|
+
// Every other mode's headless child already denies implicitly (no confirmer),
|
|
4613
|
+
// so we leave it unset to stay byte-identical — crucially preserving the
|
|
4614
|
+
// parallel-read fast-path + IDE-diff branch, both of which key off whether a
|
|
4615
|
+
// permissionConfirm is present.
|
|
4616
|
+
// • ApprovalGate sessionPolicy → attach the child its OWN ApprovalGate seeded
|
|
4617
|
+
// with the mode's tier (perm.sessionPolicy) and NO confirmer, so run_shell /
|
|
4618
|
+
// browser_act are gated per tier headlessly: strict denies MED/HIGH, trusted
|
|
4619
|
+
// denies only HIGH, autopilot allows all (CONFIRM→no-confirmer→DENY). A fresh
|
|
4620
|
+
// per-child gate (never the shared singleton) means zero interference with
|
|
4621
|
+
// the parent's global confirmer. Attached ONLY when the mode was explicitly
|
|
4622
|
+
// driven (permModeDriven) AND the tier actually gates (non-autopilot) — a
|
|
4623
|
+
// plain default spawn stays ungated = byte-identical; autopilot's gate would
|
|
4624
|
+
// be a pure no-op so it is skipped.
|
|
4625
|
+
// `tightenPermissionMode` already stops a child EXCEEDING the parent's mode; a
|
|
4626
|
+
// fully-defaulted spawn (→ "default") touches neither tools nor confirmer nor gate.
|
|
4627
|
+
let childPermissionConfirm = null;
|
|
4628
|
+
let childApprovalGate = null;
|
|
4629
|
+
if (effectiveContract?.permissionMode) {
|
|
4630
|
+
try {
|
|
4631
|
+
const { resolvePermissionMode, resolveEnabledTools } =
|
|
4632
|
+
await import("./headless-runner.js");
|
|
4633
|
+
const perm = resolvePermissionMode(effectiveContract.permissionMode);
|
|
4634
|
+
if (perm.readOnly) {
|
|
4635
|
+
allowedTools = resolveEnabledTools({ allowedTools, readOnly: true });
|
|
4636
|
+
}
|
|
4637
|
+
// autopilot ⟺ bypassPermissions — the only mode whose headless confirmer
|
|
4638
|
+
// ALLOWS. Threading a deny confirmer would needlessly disable the child's
|
|
4639
|
+
// parallel-read fast-path, so restrict to the allow case.
|
|
4640
|
+
if (
|
|
4641
|
+
perm.sessionPolicy === "autopilot" &&
|
|
4642
|
+
typeof perm.confirmer === "function"
|
|
4643
|
+
) {
|
|
4644
|
+
childPermissionConfirm = perm.confirmer;
|
|
4645
|
+
}
|
|
4646
|
+
// Dedicated, confirmer-less child ApprovalGate for the strict/trusted tiers.
|
|
4647
|
+
if (permModeDriven && perm.sessionPolicy !== "autopilot") {
|
|
4648
|
+
const { ApprovalGate } = await import("@chainlesschain/session-core");
|
|
4649
|
+
childApprovalGate = new ApprovalGate({
|
|
4650
|
+
defaultPolicy: perm.sessionPolicy,
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
} catch {
|
|
4654
|
+
// Enforcement is best-effort — never break the spawn.
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
|
|
4658
|
+
// Auto-condense parent context if caller didn't provide explicit context.
|
|
4659
|
+
// An explicit `context: fresh` contract suppresses this inheritance (the
|
|
4660
|
+
// child starts clean); `fork` / unset keep the existing auto-condense.
|
|
4396
4661
|
let resolvedContext = inheritedContext || null;
|
|
4397
|
-
if (
|
|
4662
|
+
if (
|
|
4663
|
+
!resolvedContext &&
|
|
4664
|
+
explicitContext !== "fresh" &&
|
|
4665
|
+
Array.isArray(ctx.parentMessages)
|
|
4666
|
+
) {
|
|
4398
4667
|
const recentMsgs = ctx.parentMessages
|
|
4399
4668
|
.filter((m) => m.role === "assistant" && typeof m.content === "string")
|
|
4400
4669
|
.slice(-3)
|
|
@@ -4448,6 +4717,9 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4448
4717
|
const subLlmOptions = {
|
|
4449
4718
|
...parentLlm,
|
|
4450
4719
|
model: mdModel || parentLlm.model || undefined,
|
|
4720
|
+
// Contract `effort` is a compute hint (reasoning level), not authority —
|
|
4721
|
+
// forwarded to the child loop; harmless if the provider ignores it.
|
|
4722
|
+
...(effectiveContract?.effort ? { effort: effectiveContract.effort } : {}),
|
|
4451
4723
|
};
|
|
4452
4724
|
|
|
4453
4725
|
// 用量归因: forward the child's real token usage into the parent run's sink
|
|
@@ -4502,6 +4774,48 @@ async function _executeSpawnSubAgent(args, ctx) {
|
|
|
4502
4774
|
// worktree isolation. undefined keeps the profile/flag defaults intact.
|
|
4503
4775
|
...(subMaxTurns ? { maxIterations: subMaxTurns } : {}),
|
|
4504
4776
|
...(subIsolation === "worktree" ? { useWorktree: true } : {}),
|
|
4777
|
+
// Resolved contract (gap 2026-07-12): cap the child's token budget and hand
|
|
4778
|
+
// it its EFFECTIVE contract so its OWN spawns inherit this ceiling.
|
|
4779
|
+
...(effectiveContract?.budget?.tokens
|
|
4780
|
+
? { tokenBudget: effectiveContract.budget.tokens }
|
|
4781
|
+
: {}),
|
|
4782
|
+
...(effectiveContract ? { subAgentContract: effectiveContract } : {}),
|
|
4783
|
+
// Skill capability INTERSECT (2026-07-12): a non-null allow-list (possibly
|
|
4784
|
+
// empty) restricts run_skill/list_skills in the child loop.
|
|
4785
|
+
...(skillAllowlist != null ? { skillAllowlist } : {}),
|
|
4786
|
+
// MCP + hook capability INHERITANCE (2026-07-12): only non-null when the
|
|
4787
|
+
// contract opted the child into inheriting (context:fork or an explicit
|
|
4788
|
+
// list). filterInherited* already subset these to the allowed servers /
|
|
4789
|
+
// matchers. Absent → child inherits neither (today's default).
|
|
4790
|
+
...(inheritedMcp
|
|
4791
|
+
? {
|
|
4792
|
+
extraToolDefinitions: inheritedMcp.extraToolDefinitions,
|
|
4793
|
+
externalToolDescriptors: inheritedMcp.externalToolDescriptors,
|
|
4794
|
+
externalToolExecutors: inheritedMcp.externalToolExecutors,
|
|
4795
|
+
mcpClient: inheritedMcp.mcpClient,
|
|
4796
|
+
}
|
|
4797
|
+
: {}),
|
|
4798
|
+
...(inheritedHooks ? { settingsHooks: inheritedHooks } : {}),
|
|
4799
|
+
// Memory INHERITANCE (2026-07-12): grant the child the parent's memory DB
|
|
4800
|
+
// (namespaced by the child's task id) only when the contract allows; else
|
|
4801
|
+
// memoryEnabled:false hard-suppresses recall even if a db leaks through.
|
|
4802
|
+
...(inheritedMemory
|
|
4803
|
+
? {
|
|
4804
|
+
db: inheritedMemory.db,
|
|
4805
|
+
permanentMemory: inheritedMemory.permanentMemory,
|
|
4806
|
+
memoryEnabled: true,
|
|
4807
|
+
}
|
|
4808
|
+
: { memoryEnabled: false }),
|
|
4809
|
+
// permissionMode confirmer (2026-07-12): only set for the autopilot (bypass)
|
|
4810
|
+
// ALLOW confirmer; absent for every other mode so the child stays
|
|
4811
|
+
// implicitly-deny + byte-identical (parallel-read fast-path preserved).
|
|
4812
|
+
...(childPermissionConfirm
|
|
4813
|
+
? { permissionConfirm: childPermissionConfirm }
|
|
4814
|
+
: {}),
|
|
4815
|
+
// permissionMode ApprovalGate (2026-07-13): a dedicated confirmer-less gate
|
|
4816
|
+
// seeded with the mode's tier gates the child's run_shell / browser_act;
|
|
4817
|
+
// absent (ungated) for a plain default spawn = byte-identical.
|
|
4818
|
+
...(childApprovalGate ? { approvalGate: childApprovalGate } : {}),
|
|
4505
4819
|
});
|
|
4506
4820
|
subCtxRef = subCtx;
|
|
4507
4821
|
|
|
@@ -6041,6 +6355,30 @@ function _usageTokens(usage) {
|
|
|
6041
6355
|
return { input, output, cacheRead, cacheWrite };
|
|
6042
6356
|
}
|
|
6043
6357
|
|
|
6358
|
+
/**
|
|
6359
|
+
* Best-effort text of the first user prompt in a message list, for the
|
|
6360
|
+
* `--otlp-content` span attribute. Handles both string content and the
|
|
6361
|
+
* multimodal `[{type:"text",text}, …]` shape; returns "" when none is found.
|
|
6362
|
+
* Not exported (no shim-parity impact) — only the telemetry seam uses it.
|
|
6363
|
+
*/
|
|
6364
|
+
function extractInitialPromptText(messages) {
|
|
6365
|
+
if (!Array.isArray(messages)) return "";
|
|
6366
|
+
for (const msg of messages) {
|
|
6367
|
+
if (!msg || msg.role !== "user") continue;
|
|
6368
|
+
const content = msg.content;
|
|
6369
|
+
if (typeof content === "string") return content;
|
|
6370
|
+
if (Array.isArray(content)) {
|
|
6371
|
+
const text = content
|
|
6372
|
+
.filter((part) => part && part.type === "text" && part.text)
|
|
6373
|
+
.map((part) => part.text)
|
|
6374
|
+
.join("\n")
|
|
6375
|
+
.trim();
|
|
6376
|
+
if (text) return text;
|
|
6377
|
+
}
|
|
6378
|
+
}
|
|
6379
|
+
return "";
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6044
6382
|
export async function* agentLoop(messages, options) {
|
|
6045
6383
|
// Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
|
|
6046
6384
|
// When options.iterationBudget is provided (e.g. from parent agent),
|
|
@@ -6055,13 +6393,28 @@ export async function* agentLoop(messages, options) {
|
|
|
6055
6393
|
const toolContext = {
|
|
6056
6394
|
hookDb: options.hookDb || null,
|
|
6057
6395
|
skillLoader: options.skillLoader || _defaultSkillLoader,
|
|
6396
|
+
// Contract-driven skill allow-list (subagent capability INTERSECT). null =
|
|
6397
|
+
// unrestricted; [] = none; a list restricts run_skill/list_skills to those
|
|
6398
|
+
// ids/dirNames. Set by the spawn path from the resolved subagent contract.
|
|
6399
|
+
skillAllowlist: options.skillAllowlist ?? null,
|
|
6058
6400
|
cwd: options.cwd || process.cwd(),
|
|
6059
6401
|
planManager: options.planManager || null,
|
|
6060
6402
|
sessionId: options.sessionId || null,
|
|
6061
6403
|
hostManagedToolPolicy: options.hostManagedToolPolicy || null,
|
|
6062
6404
|
externalToolDescriptors: options.externalToolDescriptors || null,
|
|
6063
6405
|
externalToolExecutors: options.externalToolExecutors || null,
|
|
6406
|
+
// MCP tool DEFINITIONS the LLM sees (mcp__server__tool). Threaded here so a
|
|
6407
|
+
// spawn can inherit the parent's MCP tools into the child (filtered by the
|
|
6408
|
+
// contract's mcpServers allow-list). Otherwise consumed only at agentLoop.
|
|
6409
|
+
extraToolDefinitions: options.extraToolDefinitions || null,
|
|
6064
6410
|
mcpClient: options.mcpClient || null,
|
|
6411
|
+
// Parent memory source — a spawn can inherit the parent's hierarchical
|
|
6412
|
+
// memory DB into the child ONLY when the resolved contract grants memory
|
|
6413
|
+
// (context:fork from a memory-bearing parent, or explicit memory:true).
|
|
6414
|
+
// Read off this loop's context engine (REPL sets one with db+permanentMemory).
|
|
6415
|
+
memoryDb: options.contextEngine?.db ?? options.db ?? null,
|
|
6416
|
+
permanentMemory:
|
|
6417
|
+
options.contextEngine?.permanentMemory ?? options.permanentMemory ?? null,
|
|
6065
6418
|
// Parent LLM config — forwarded to spawn_sub_agent so a delegated subagent
|
|
6066
6419
|
// inherits the provider/key and can override just the model (cc agents `model:`).
|
|
6067
6420
|
llmOptions: {
|
|
@@ -6105,6 +6458,10 @@ export async function* agentLoop(messages, options) {
|
|
|
6105
6458
|
spawned: 0,
|
|
6106
6459
|
max: MAX_SUB_AGENTS_PER_RUN,
|
|
6107
6460
|
},
|
|
6461
|
+
// This loop's EFFECTIVE subagent contract (set when this loop IS a spawned
|
|
6462
|
+
// sub-agent). Threaded so a nested spawn_sub_agent sees it as the parent
|
|
6463
|
+
// ceiling (tighten-only). null at the top level (no ceiling).
|
|
6464
|
+
subAgentContract: options.subAgentContract || null,
|
|
6108
6465
|
// Abort signal — forwarded to background sub-agents so cancelling the
|
|
6109
6466
|
// parent run also cancels children still running detached.
|
|
6110
6467
|
signal,
|
|
@@ -6234,12 +6591,28 @@ export async function* agentLoop(messages, options) {
|
|
|
6234
6591
|
// an optional caller-provided workflow name) onto EVERY span this run emits,
|
|
6235
6592
|
// so a collector can group one run's model/tool spans into one workflow.
|
|
6236
6593
|
if (recorder && typeof recorder.setDefaultAttribute === "function") {
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6594
|
+
// Normalize the unified id set (P2 observability): every id is charset-
|
|
6595
|
+
// sanitized + length-capped, and only these allow-listed keys are stamped,
|
|
6596
|
+
// so span cardinality stays bounded and no content leaks in as an id.
|
|
6597
|
+
const telemetryCtx = {
|
|
6598
|
+
"workflow.run_id": runId,
|
|
6599
|
+
"workflow.name": options.workflowName || undefined,
|
|
6600
|
+
"session.id": options.sessionId || undefined,
|
|
6601
|
+
"agent.id": options.agentId || undefined,
|
|
6602
|
+
"parent_agent.id": options.parentAgentId || undefined,
|
|
6603
|
+
};
|
|
6604
|
+
// --otlp-content opt-in (P2): only when explicitly enabled do we feed the
|
|
6605
|
+
// initial prompt through the redactor with includeContent — so a debugger
|
|
6606
|
+
// can see which prompt a span belongs to. Default OFF omits the field
|
|
6607
|
+
// entirely (not just redacts it), keeping default OTLP output byte-identical.
|
|
6608
|
+
const includeContent = options.otlpIncludeContent === true;
|
|
6609
|
+
if (includeContent) {
|
|
6610
|
+
const promptText = extractInitialPromptText(messages);
|
|
6611
|
+
if (promptText) telemetryCtx.prompt = promptText;
|
|
6612
|
+
}
|
|
6613
|
+
const idAttrs = buildTelemetryAttributes(telemetryCtx, { includeContent });
|
|
6614
|
+
for (const [k, v] of Object.entries(idAttrs)) {
|
|
6615
|
+
recorder.setDefaultAttribute(k, v);
|
|
6243
6616
|
}
|
|
6244
6617
|
}
|
|
6245
6618
|
yield {
|
|
@@ -614,7 +614,7 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
614
614
|
type: "string",
|
|
615
615
|
enum: ["worktree"],
|
|
616
616
|
description:
|
|
617
|
-
|
|
617
|
+
'Optional isolation mode: "worktree" runs the sub-agent in a fresh git worktree so its file edits land on an isolated branch (diff/merge summary comes back with the result).',
|
|
618
618
|
},
|
|
619
619
|
},
|
|
620
620
|
required: ["task"],
|
|
@@ -959,6 +959,65 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
|
|
|
959
959
|
type: "string",
|
|
960
960
|
description: "Optional human label for list/cancel",
|
|
961
961
|
},
|
|
962
|
+
expires: {
|
|
963
|
+
type: "string",
|
|
964
|
+
description:
|
|
965
|
+
"For wakeup/cron/monitor: retire the entry after this long if it hasn't fired (e.g. 2h, 7d); it then never fires",
|
|
966
|
+
},
|
|
967
|
+
jitter: {
|
|
968
|
+
type: "string",
|
|
969
|
+
description:
|
|
970
|
+
"For wakeup/cron/monitor: a random-but-stable spread (e.g. 30s) so entries sharing a schedule fan out instead of firing all at once",
|
|
971
|
+
},
|
|
972
|
+
permission_mode: {
|
|
973
|
+
type: "string",
|
|
974
|
+
enum: [
|
|
975
|
+
"plan",
|
|
976
|
+
"manual",
|
|
977
|
+
"default",
|
|
978
|
+
"acceptEdits",
|
|
979
|
+
"auto",
|
|
980
|
+
"dontAsk",
|
|
981
|
+
"bypassPermissions",
|
|
982
|
+
],
|
|
983
|
+
description:
|
|
984
|
+
"For wakeup/cron: run the fired `cc agent` with this permission mode instead of the ambient one (e.g. `plan` for a read-only report, `manual` to gate every action)",
|
|
985
|
+
},
|
|
986
|
+
worktree: {
|
|
987
|
+
type: "boolean",
|
|
988
|
+
description:
|
|
989
|
+
"For wakeup/cron: run the fired `cc agent` in a fresh git worktree so its changes stay isolated",
|
|
990
|
+
},
|
|
991
|
+
max_turns: {
|
|
992
|
+
type: "number",
|
|
993
|
+
description:
|
|
994
|
+
"For wakeup/cron: cap the fired agent's loop iterations (a per-task budget)",
|
|
995
|
+
},
|
|
996
|
+
goal_condition: {
|
|
997
|
+
type: "string",
|
|
998
|
+
description:
|
|
999
|
+
"For wakeup/cron: run the fired agent to a completion condition, re-driving outer turns until met. Prefix exit-zero:/file-exists:/contains:/regex: (deterministic) or model:<desc> (model-judged). Invalid specs are dropped.",
|
|
1000
|
+
},
|
|
1001
|
+
max_outer_turns: {
|
|
1002
|
+
type: "number",
|
|
1003
|
+
description:
|
|
1004
|
+
"For wakeup/cron with goal_condition: max outer re-drive turns (default 10, hard cap 100)",
|
|
1005
|
+
},
|
|
1006
|
+
goal_max_tokens: {
|
|
1007
|
+
type: "number",
|
|
1008
|
+
description:
|
|
1009
|
+
"For wakeup/cron with goal_condition: token budget across outer turns",
|
|
1010
|
+
},
|
|
1011
|
+
goal_max_cost: {
|
|
1012
|
+
type: "number",
|
|
1013
|
+
description:
|
|
1014
|
+
"For wakeup/cron with goal_condition: cost budget (USD) across outer turns",
|
|
1015
|
+
},
|
|
1016
|
+
goal_max_time: {
|
|
1017
|
+
type: "number",
|
|
1018
|
+
description:
|
|
1019
|
+
"For wakeup/cron with goal_condition: wall-clock budget (ms) across outer turns",
|
|
1020
|
+
},
|
|
962
1021
|
id: {
|
|
963
1022
|
type: "string",
|
|
964
1023
|
description: "For cancel: the schedule entry id to remove",
|