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.
Files changed (209) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DMn528q-.js → AIOps-BQ0kgfA1.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-D9ZcDCJL.js → ActionButton-Dr1MDANe.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-A_K-8PX0.js → Analytics-CZ5vsaXW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-D4pFW3X6.js → AppLayout-BIBga881.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-Dz46COAd.js → Artifacts-vpXHRU3u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-cbMjbyg0.js → Audit-B8exuoub.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-Dtf88GAj.js → BackgroundAgents-923evRRN.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-C5KRxn52.js → Backup-sr_sswlQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Dd2UK3BN.js → BaseInput-CnW-4XKT.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-lptKTFdG.js → Chat-FZCsTZIL.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-_yspG6rG.js → Checkbox-DFrxB59H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BGq_GAha.js → Codegen-_b6WEel8.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CePor71g.js → Col-CkbnnZ-K.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CLOWhIT7.js → Community-BxM_82zW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CivJt2vT.js → Compact-DYinP45R.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Btmo1GGE.js → Compliance-DyXp3sSP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Ct35OlG4.js → Cowork-BEr2DhW7.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CTc2GmhB.js → Cron-B-Vwo1hN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-ZtiBETk1.js → Crosschain-DbLxKddd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BNqMQQXw.js → DID-BsQ_LFw9.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-BmzZd-9V.js → Dashboard-lVlXQHu5.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CMVgvP2M.js → Dropdown-Beo_foO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CcqQFAfT.js → EmailListRenderer-DszHIXED.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CTqB1_4V.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-CiKOOzlb.js → Federation-CuJLa2eb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Wk-OT0gr.js → FormItemContext-Cxd1lxhc.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DbEQT6ue.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-0ZvCPSD9.js → Git-WqPKetx6.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CmVuKkPb.js → Governance-CWYtY3ke.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CTWE3Jwg.js → Inference-BnX1iiNV.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBEIF00L.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-DLhxDwVG.js → Logs-DqLMNntm.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-BqYwEYou.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-BgsCBrnc.js → Marketplace-_vGfmhNl.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-ClBUGDS2.js → McpTools-BeJDOkDj.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-CnG_ZnYw.js → Memory-Da0EEZDO.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BUOAbR1Z.js → MobileBridge-DSuzcCCy.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-Dnt_9-WB.js → MobileProjects-D0RAk4e8.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-CLjt4kCD.js → Mtc-BE9YgdA0.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-f24U9Aes.js → MtcAudit-fC2BpO5S.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-GrmGQbSq.js → Multisig-D24Xxqw3.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-DADaWn-n.js → NLProgramming-Bs3oGvS8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-xtf5RX6C.js → Notes-BFjmAA-3.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-Cd27Rgtx.js → NotificationSettings-BX_9Bhv0.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-fXRxlDjl.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-joCFlNB0.js → Organization-DBcdsVEP.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-B1HoURq0.js → Overflow-DDyaF9vC.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DiwsKbT7.js → P2P-BB78L4he.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Xe-BsW6S.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-C23qi8kl.js → Permissions-INbnWv4j.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BS1syX2f.js → PersonalDataHub-BST-VmTq.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-DRYwwDyh.js → Pipeline-CE7_tS2k.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BQKaDHPH.js → Privacy-hK5L8dOB.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-BC6F9xiG.js → ProjectInit-BvkkUoDn.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-ClFPc1Nd.js → ProjectSettings-u7_EUtkm.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-5Fmt_8Nw.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-CLWuOUNt.js → Providers-B9eanarR.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DiqULTxb.js → QrScannerModal-C0dX5N2f.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-9A03P-E_.js → QuickAsk-CKTpG-PZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CJZAnbq_.js → Recommend-Px_w_XQM.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9A1R77h.js → RemoteSession-Di0fnYkM.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BIZc6Rky.js → Reputation-vesnWjXt.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DfIPNtuG.js → Row-exbY6LkT.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-C5znFcQ1.js → RssFeed-B3O3x70t.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DSs2z3OX.js → Search-uRA0DhNz.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CV6LcrLY.js → Security-CViqdH6K.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-cmhoX0Xj.js → Services-CkXS97WN.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-tCsg2po7.js → Skeleton-DxW5WYnE.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-BK1KUDiy.js → Skills-CLxmGFse.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-yRgyeA82.js → Sla-Cr5fziXq.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-c1mo1mlp.js → SpeechSettings-H0JSb3cp.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-koIAfuT3.js → SyncSettings-DMYWMae7.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DkkreAaB.js → Tasks-CbYyDdF4.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-fy5Ctf9M.js → Templates-Cpc8JjiK.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Bw70Jf6S.js → Tenant-CRh77Jda.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BKh6Rx-4.js → Terminal-4D8tNBO5.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Dg54WP0c.js → TimelineRenderer-D41jzzbG.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-BvvD5SxJ.js → Tokens-C_jWEy6b.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-B7Ug9x4e.js → Trigger-Dk1ZUhsy.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-BjvuMORY.js → Trust-CLVy4l7m.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CZqhkx8p.js → UkeySign-C-FeqFDj.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-ClR50lhX.js → VideoEditing-CqVZzyMm.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DlfppYjx.js → Wallet-DX9BW9pu.js} +2 -2
  86. package/src/assets/web-panel/assets/{WebAuthn-BirgVr8B.js → WebAuthn-7hQDpWw_.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-B8DP_bax.js → WorkflowEditor-6XqUAO0j.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DA8AUZzL.js → chat-5R_vkaJG.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-C38pLGp1.js → colors-huUPZdC-.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-81rtr98o.js → compact-item-CfJlgisE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-D34ixq8t.js → createContext-jO8heofr.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-DeIrCfH-.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-D4bzqTrZ.js → hasIn-DY8Vxqx7.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-E1OJRXs4.js → index-A48PtXw9.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-D3zCCZRS.js → index-B300RPnT.js} +3 -3
  96. package/src/assets/web-panel/assets/{index-Cd3KuNK5.js → index-B74OXFxC.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-Dmo3dFHb.js → index-BAON7TzU.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Ddk0e_Pi.js → index-BCqxyvqp.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-9qoVpPt6.js → index-BHE8OIls.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DBFSo1NF.js → index-BQS-uPOK.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DUQLXLQM.js → index-BRXOP09X.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-D6GfvEQG.js → index-BYLBPpt8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DFyWKOBD.js → index-BfCx4pFT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BP-zkXK8.js → index-BliaT02i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DMgnRwrK.js → index-BmqGerpC.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BIP62a6r.js → index-BsBtRWbA.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DkHqMnn3.js → index-BtEXSoPL.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BxUKMRpP.js +1 -0
  109. package/src/assets/web-panel/assets/{index-B8mkLM4x.js → index-Bxax9vQ6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-ZRJRItm0.js → index-C2iVSFpy.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CP5alL4t.js → index-CA6sO5VD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BS3M5H9V.js → index-CA90TZxG.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DvfCEJG-.js → index-CLhBh-qE.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-tKiC8T-4.js → index-CRElRhyS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B0QArPQt.js → index-Cbe9G0V4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-COe_cOLJ.js → index-ChvHubX3.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BvL_3J_n.js → index-Cis_Ip7H.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-F76w1NiL.js → index-Cjxbu68P.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BlxEphl7.js → index-CzKmteNS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CuS-UPoj.js → index-D0pet5Uy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CbhE3j5j.js → index-D1VLuww0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D_2VYPr1.js → index-DXBcvUnf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DakfFZ-k.js → index-Db1cLWir.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-fI5sXMLg.js → index-DoI2sZm7.js} +1 -1
  125. package/src/assets/web-panel/assets/index-DscU9iPI.js +1 -0
  126. package/src/assets/web-panel/assets/{index-CoVgZw_b.js → index-KRHroryI.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BvD7b2IK.js → index-d1xkbtNz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BtGxuQrO.js → index-dKx57io7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BqIhLAbA.js → index-j3O2EEJe.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BXd2_LmH.js → index-p85YaaKw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DFHa1HIg.js → index-v3pTaEQm.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D0Ih7Hwn.js → index-vXs3B8GJ.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-ByyvflIR.js → initDefaultProps-MCB7-raZ.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-B3FQm2By.js → motion-UWDF9swV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-BSfns6Ef.js → move-CsTmgXZU.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-CTG1Ieri.js → mtc-parser-fQy6KGLg.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-DObM_2j6.js → omit-woPnEjd1.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DOfelQ1t.js → pickAttrs-Bs2dZ1gf.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CHZILds_.js → placementArrow-C7EVgPkg.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-REbucKSx.js → responsiveObserve-BH2kYCTN.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-C6EZzD7o.js → slide-BFBP2V_e.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BxkWS-g3.js → statusUtils-xdDxDYse.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-Chklyoq6.js → styleChecker-aAWgPPQf.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CRVwKrrI.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DV24ik_J.js → useFs-BrOih40N.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-C7vIo4Pg.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-By-v5GlF.js → vnode-B8lutNSE.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B1w9Am3r.js → zoom-CffNre7V.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agenda.js +254 -23
  151. package/src/commands/agent.js +105 -11
  152. package/src/commands/context.js +125 -14
  153. package/src/commands/hook.js +181 -7
  154. package/src/commands/plugin.js +239 -1
  155. package/src/commands/review.js +352 -2
  156. package/src/data/changelog.json +14 -4
  157. package/src/gateways/ws/remote-session-protocol.js +39 -0
  158. package/src/gateways/ws/session-protocol.js +5 -2
  159. package/src/lib/agent-schedule-store.js +289 -10
  160. package/src/lib/agents.js +19 -0
  161. package/src/lib/background-agent-supervisor.js +34 -7
  162. package/src/lib/channels/channel-manager.js +54 -2
  163. package/src/lib/cli-context-engineering.js +9 -2
  164. package/src/lib/context-breakdown.js +116 -0
  165. package/src/lib/docs-drift.js +166 -0
  166. package/src/lib/doctor-checkup.js +128 -0
  167. package/src/lib/hook-event-bus.cjs +0 -0
  168. package/src/lib/hook-event-log.cjs +193 -0
  169. package/src/lib/interaction-adapter.js +34 -4
  170. package/src/lib/json-schema-output.js +75 -12
  171. package/src/lib/json-schema-validate.js +831 -0
  172. package/src/lib/lsp/__tests__/diagnostics-scheduler.test.js +93 -0
  173. package/src/lib/lsp/diagnostics-scheduler.js +127 -0
  174. package/src/lib/operation-fingerprint.js +95 -0
  175. package/src/lib/plugin-runtime/capabilities.js +371 -0
  176. package/src/lib/plugin-runtime/capability-consent.js +175 -0
  177. package/src/lib/plugin-runtime/manifest.js +30 -0
  178. package/src/lib/plugin-runtime/policy.js +62 -0
  179. package/src/lib/plugin-runtime/scopes.js +20 -4
  180. package/src/lib/project-instructions.js +128 -0
  181. package/src/lib/remote-approval-bridge.js +16 -0
  182. package/src/lib/repl-goal.js +132 -0
  183. package/src/lib/repl-rewind.js +52 -0
  184. package/src/lib/review-pipeline.js +227 -0
  185. package/src/lib/runtime-checkup.js +318 -0
  186. package/src/lib/schedule-planner.js +17 -1
  187. package/src/lib/session-lifecycle.js +305 -0
  188. package/src/lib/settings-hook-events.cjs +52 -13
  189. package/src/lib/settings-loader.cjs +42 -0
  190. package/src/lib/side-effect-ledger-store.js +88 -0
  191. package/src/lib/side-effect-ledger.js +296 -0
  192. package/src/lib/sub-agent-context.js +74 -5
  193. package/src/lib/subagent-contract.js +383 -0
  194. package/src/lib/subagent-inheritance.js +112 -0
  195. package/src/lib/telemetry-ids.js +190 -0
  196. package/src/lib/turn-binding-store.js +119 -0
  197. package/src/lib/turn-binding.js +397 -0
  198. package/src/repl/agent-repl.js +136 -0
  199. package/src/repl/permission-tier.js +28 -0
  200. package/src/runtime/agent-core.js +387 -14
  201. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  202. package/src/runtime/headless-runner.js +267 -0
  203. package/src/runtime/headless-stream.js +60 -1
  204. package/src/assets/web-panel/assets/ChatBubbleRenderer-C2KLtwHz.js +0 -1
  205. package/src/assets/web-panel/assets/MarkdownRenderer-DxywvhTU.js +0 -1
  206. package/src/assets/web-panel/assets/Projects-Dn-f5aYe.js +0 -1
  207. package/src/assets/web-panel/assets/devWarning-CHDIEOeN.js +0 -1
  208. package/src/assets/web-panel/assets/index-CY1NURWZ.js +0 -1
  209. 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 found. Make sure you're in the ChainlessChain project root or have skills installed.",
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({ error: "No skills found." });
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
- // Auto-condense parent context if caller didn't provide explicit context
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 (!resolvedContext && Array.isArray(ctx.parentMessages)) {
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
- recorder.setDefaultAttribute("workflow.run_id", runId);
6238
- if (options.workflowName) {
6239
- recorder.setDefaultAttribute(
6240
- "workflow.name",
6241
- String(options.workflowName),
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
- "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).",
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",