chainlesschain 0.162.162 → 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 (192) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BQ0kgfA1.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-Dr1MDANe.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-CZ5vsaXW.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-BIBga881.js} +3 -3
  6. package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-vpXHRU3u.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-B8exuoub.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-923evRRN.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-sr_sswlQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-CnW-4XKT.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-FZCsTZIL.js} +6 -6
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DDPd9qT0.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DFrxB59H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-_b6WEel8.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-CkbnnZ-K.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-BxM_82zW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-DYinP45R.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DyXp3sSP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BEr2DhW7.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-B-Vwo1hN.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-DbLxKddd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BsQ_LFw9.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-lVlXQHu5.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-Beo_foO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DszHIXED.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-DcAwoh81.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-CuJLa2eb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-Cxd1lxhc.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BBvq5Jsw.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-WqPKetx6.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CWYtY3ke.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-BnX1iiNV.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BJuM8GN5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-DqLMNntm.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-BqYwEYou.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-_vGfmhNl.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-BeJDOkDj.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-Da0EEZDO.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-DSuzcCCy.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-D0RAk4e8.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-BE9YgdA0.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-fC2BpO5S.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-D24Xxqw3.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-Bs3oGvS8.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-BFjmAA-3.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-BX_9Bhv0.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-wjkZwJXI.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-DBcdsVEP.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-DDyaF9vC.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-BB78L4he.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-DqTiZFum.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-INbnWv4j.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-BST-VmTq.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CE7_tS2k.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-hK5L8dOB.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-BvkkUoDn.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-u7_EUtkm.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-5Fmt_8Nw.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-B9eanarR.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-C0dX5N2f.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-CKTpG-PZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-Px_w_XQM.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-Di0fnYkM.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-vesnWjXt.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-exbY6LkT.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-B3O3x70t.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-uRA0DhNz.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-CViqdH6K.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CkXS97WN.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DxW5WYnE.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-CLxmGFse.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Cr5fziXq.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-H0JSb3cp.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-DMYWMae7.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-CbYyDdF4.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-Cpc8JjiK.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-CRh77Jda.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal-4D8tNBO5.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D41jzzbG.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-C_jWEy6b.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-Dk1ZUhsy.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-CLVy4l7m.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-C-FeqFDj.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-CqVZzyMm.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-DX9BW9pu.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-7hQDpWw_.js} +2 -2
  87. package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-6XqUAO0j.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-5R_vkaJG.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-huUPZdC-.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-CfJlgisE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BvjJ48bs.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-DVC0157C.js → hasIn-DY8Vxqx7.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-A48PtXw9.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-B300RPnT.js} +3 -3
  96. package/src/assets/web-panel/assets/{index-BbepULSc.js → index-B74OXFxC.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-BAON7TzU.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-BCqxyvqp.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BHE8OIls.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-BQS-uPOK.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-BRXOP09X.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-BYLBPpt8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-BfCx4pFT.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-BliaT02i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BmqGerpC.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-BsBtRWbA.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C229ABfy.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-C_H_S-DY.js → index-Bxax9vQ6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-C2iVSFpy.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-CA6sO5VD.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CA90TZxG.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-CLhBh-qE.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-CRElRhyS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-Cbe9G0V4.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-ChvHubX3.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-Cis_Ip7H.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-Cjxbu68P.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-CzKmteNS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-D0pet5Uy.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D1VLuww0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-DXBcvUnf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-Db1cLWir.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DuGszaWY.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-4wcgvPoF.js → index-KRHroryI.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-d1xkbtNz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-dKx57io7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-j3O2EEJe.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-p85YaaKw.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-v3pTaEQm.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-vXs3B8GJ.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-MCB7-raZ.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-UWDF9swV.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CsTmgXZU.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-fQy6KGLg.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-woPnEjd1.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-Bs2dZ1gf.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-C7EVgPkg.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BH2kYCTN.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BFBP2V_e.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-xdDxDYse.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-aAWgPPQf.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-DkGSAWoO.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-BrOih40N.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-eIYrgMqO.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-B8lutNSE.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DXlf9sA3.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 +347 -2
  156. package/src/data/changelog.json +9 -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/channels/channel-manager.js +54 -2
  161. package/src/lib/cli-context-engineering.js +9 -2
  162. package/src/lib/context-breakdown.js +116 -0
  163. package/src/lib/doctor-checkup.js +52 -0
  164. package/src/lib/hook-event-log.cjs +193 -0
  165. package/src/lib/interaction-adapter.js +34 -4
  166. package/src/lib/json-schema-output.js +46 -12
  167. package/src/lib/json-schema-validate.js +143 -3
  168. package/src/lib/plugin-runtime/capability-consent.js +175 -0
  169. package/src/lib/plugin-runtime/policy.js +62 -0
  170. package/src/lib/plugin-runtime/scopes.js +20 -4
  171. package/src/lib/project-instructions.js +128 -0
  172. package/src/lib/repl-goal.js +132 -0
  173. package/src/lib/repl-rewind.js +52 -0
  174. package/src/lib/schedule-planner.js +17 -1
  175. package/src/lib/settings-hook-events.cjs +13 -0
  176. package/src/lib/settings-loader.cjs +42 -0
  177. package/src/lib/side-effect-ledger-store.js +88 -0
  178. package/src/lib/side-effect-ledger.js +296 -0
  179. package/src/lib/sub-agent-context.js +74 -5
  180. package/src/lib/subagent-inheritance.js +112 -0
  181. package/src/lib/turn-binding-store.js +119 -0
  182. package/src/repl/agent-repl.js +136 -0
  183. package/src/repl/permission-tier.js +28 -0
  184. package/src/runtime/agent-core.js +376 -10
  185. package/src/runtime/coding-agent-contract-shared.cjs +60 -1
  186. package/src/runtime/headless-runner.js +267 -0
  187. package/src/runtime/headless-stream.js +60 -1
  188. package/src/assets/web-panel/assets/ChatBubbleRenderer-HwJR-C6B.js +0 -1
  189. package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
  190. package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
  191. package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
  192. package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
@@ -1471,6 +1471,8 @@ export async function executeTool(name, args, context = {}) {
1471
1471
  try {
1472
1472
  toolResult = await executeToolInner(name, args, {
1473
1473
  skillLoader,
1474
+ // Subagent skill capability INTERSECT — forwarded to run_skill/list_skills.
1475
+ skillAllowlist: context.skillAllowlist ?? null,
1474
1476
  cwd,
1475
1477
  parentMessages: context.parentMessages,
1476
1478
  interaction: context.interaction,
@@ -1496,6 +1498,13 @@ export async function executeTool(name, args, context = {}) {
1496
1498
  settingsVerdict,
1497
1499
  subAgentDepth: context.subAgentDepth || 0,
1498
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,
1499
1508
  interactiveApproval: context.interactiveApproval || false,
1500
1509
  settingsHooks: context.settingsHooks || null,
1501
1510
  signal: context.signal || null,
@@ -2096,6 +2105,7 @@ async function executeToolInner(
2096
2105
  args,
2097
2106
  {
2098
2107
  skillLoader,
2108
+ skillAllowlist = null,
2099
2109
  cwd,
2100
2110
  parentMessages,
2101
2111
  interaction,
@@ -2103,7 +2113,11 @@ async function executeToolInner(
2103
2113
  hostManagedToolPolicy,
2104
2114
  externalToolDescriptors,
2105
2115
  externalToolExecutors,
2116
+ extraToolDefinitions = null,
2106
2117
  mcpClient,
2118
+ memoryDb = null,
2119
+ permanentMemory = null,
2120
+ subAgentContract = null,
2107
2121
  llmOptions,
2108
2122
  shellPolicyOverrides,
2109
2123
  classifyAllShell = false,
@@ -2128,6 +2142,13 @@ async function executeToolInner(
2128
2142
  : null;
2129
2143
  const runtimeDescriptor =
2130
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);
2131
2152
  const hostToolPolicies =
2132
2153
  hostManagedToolPolicy?.tools || hostManagedToolPolicy?.toolPolicies || null;
2133
2154
  const hostToolPolicy =
@@ -2903,8 +2924,20 @@ async function executeToolInner(
2903
2924
  // the run_shell gate). A settings `allow` rule (ruleAllowed) pre-
2904
2925
  // authorizes; headless leaves interactiveApproval false so its existing
2905
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();
2906
2939
  if (
2907
- interactiveApproval &&
2940
+ (interactiveApproval || policyGateNoConfirmer) &&
2908
2941
  approvalGate &&
2909
2942
  !ruleAllowed &&
2910
2943
  typeof approvalGate.decide === "function"
@@ -2947,7 +2980,17 @@ async function executeToolInner(
2947
2980
  llmOptions,
2948
2981
  subAgentDepth,
2949
2982
  subAgentBudget,
2983
+ subAgentContract,
2950
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,
2951
2994
  signal,
2952
2995
  backgroundSubAgents,
2953
2996
  subAgentUsageSink,
@@ -3158,6 +3201,26 @@ async function executeToolInner(
3158
3201
  const { parseDuration } = await import("../lib/loop.js");
3159
3202
  const store = new AgentScheduleStore();
3160
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
+ };
3161
3224
  if (action === "wakeup") {
3162
3225
  if (!args.prompt) {
3163
3226
  return attachDescriptor({
@@ -3169,6 +3232,9 @@ async function executeToolInner(
3169
3232
  prompt: args.prompt,
3170
3233
  delayMs,
3171
3234
  label: args.label || null,
3235
+ expiresInMs,
3236
+ jitterMs,
3237
+ ...runPolicy,
3172
3238
  });
3173
3239
  return attachDescriptor({
3174
3240
  scheduled: entry,
@@ -3185,6 +3251,9 @@ async function executeToolInner(
3185
3251
  prompt: args.prompt,
3186
3252
  cron: args.cron,
3187
3253
  label: args.label || null,
3254
+ expiresInMs,
3255
+ jitterMs,
3256
+ ...runPolicy,
3188
3257
  });
3189
3258
  return attachDescriptor({ scheduled: entry });
3190
3259
  }
@@ -3204,6 +3273,8 @@ async function executeToolInner(
3204
3273
  notify: args.notify_title ? { title: args.notify_title } : null,
3205
3274
  maxChecks: args.max_checks ?? null,
3206
3275
  label: args.label || null,
3276
+ expiresInMs,
3277
+ jitterMs,
3207
3278
  });
3208
3279
  return attachDescriptor({ scheduled: entry });
3209
3280
  }
@@ -3549,11 +3620,12 @@ async function executeToolInner(
3549
3620
  }
3550
3621
 
3551
3622
  case "run_skill": {
3552
- const allSkills = skillLoader.getResolvedSkills();
3623
+ const allSkills = skillLoader.getResolvedSkills().filter(skillAllowed);
3553
3624
  if (allSkills.length === 0) {
3554
3625
  return attachDescriptor({
3555
- error:
3556
- "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.",
3557
3629
  });
3558
3630
  }
3559
3631
  const match = allSkills.find(
@@ -3694,9 +3766,13 @@ async function executeToolInner(
3694
3766
  }
3695
3767
 
3696
3768
  case "list_skills": {
3697
- let skills = skillLoader.getResolvedSkills();
3769
+ let skills = skillLoader.getResolvedSkills().filter(skillAllowed);
3698
3770
  if (skills.length === 0) {
3699
- 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
+ });
3700
3776
  }
3701
3777
  if (args.category) {
3702
3778
  skills = skills.filter(
@@ -4282,6 +4358,25 @@ async function _executeSpawnSubAgent(args, ctx) {
4282
4358
  error: `spawn_sub_agent: max nesting depth (${MAX_SUB_AGENT_DEPTH}) reached — complete the task directly instead of delegating further.`,
4283
4359
  };
4284
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
+ }
4285
4380
  // Breadth cap: a shared counter (one object for the whole tree) bounds the
4286
4381
  // TOTAL sub-agents a run may spawn, so a wide fan-out can't blow up even
4287
4382
  // within the depth limit. Refuse + count BEFORE any work so the increment
@@ -4322,6 +4417,7 @@ async function _executeSpawnSubAgent(args, ctx) {
4322
4417
  // Explicit role/tools still win over the agent file's values.
4323
4418
  let mdProfile = null;
4324
4419
  let mdModel = null;
4420
+ let mdContract = null; // agent-file's normalized subagent contract (definition)
4325
4421
  if (args.agent) {
4326
4422
  try {
4327
4423
  const { getAgent } = await import("../lib/agents.js");
@@ -4341,6 +4437,7 @@ async function _executeSpawnSubAgent(args, ctx) {
4341
4437
  subIsolation = "worktree";
4342
4438
  }
4343
4439
  if (md.model) mdModel = md.model;
4440
+ if (md.contract) mdContract = md.contract;
4344
4441
  if (md.systemPrompt) {
4345
4442
  mdProfile = { name: md.name, systemPrompt: md.systemPrompt };
4346
4443
  }
@@ -4357,6 +4454,118 @@ async function _executeSpawnSubAgent(args, ctx) {
4357
4454
  };
4358
4455
  }
4359
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
+
4360
4569
  // Phase 3: resolve declarative profile if requested. Explicit tools/context
4361
4570
  // override profile defaults; missing fields fall back to the profile.
4362
4571
  let profile = null;
@@ -4393,9 +4602,68 @@ async function _executeSpawnSubAgent(args, ctx) {
4393
4602
  allowedTools = base.filter((t) => !deny.has(t));
4394
4603
  }
4395
4604
 
4396
- // 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.
4397
4661
  let resolvedContext = inheritedContext || null;
4398
- if (!resolvedContext && Array.isArray(ctx.parentMessages)) {
4662
+ if (
4663
+ !resolvedContext &&
4664
+ explicitContext !== "fresh" &&
4665
+ Array.isArray(ctx.parentMessages)
4666
+ ) {
4399
4667
  const recentMsgs = ctx.parentMessages
4400
4668
  .filter((m) => m.role === "assistant" && typeof m.content === "string")
4401
4669
  .slice(-3)
@@ -4449,6 +4717,9 @@ async function _executeSpawnSubAgent(args, ctx) {
4449
4717
  const subLlmOptions = {
4450
4718
  ...parentLlm,
4451
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 } : {}),
4452
4723
  };
4453
4724
 
4454
4725
  // 用量归因: forward the child's real token usage into the parent run's sink
@@ -4503,6 +4774,48 @@ async function _executeSpawnSubAgent(args, ctx) {
4503
4774
  // worktree isolation. undefined keeps the profile/flag defaults intact.
4504
4775
  ...(subMaxTurns ? { maxIterations: subMaxTurns } : {}),
4505
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 } : {}),
4506
4819
  });
4507
4820
  subCtxRef = subCtx;
4508
4821
 
@@ -6042,6 +6355,30 @@ function _usageTokens(usage) {
6042
6355
  return { input, output, cacheRead, cacheWrite };
6043
6356
  }
6044
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
+
6045
6382
  export async function* agentLoop(messages, options) {
6046
6383
  // Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
6047
6384
  // When options.iterationBudget is provided (e.g. from parent agent),
@@ -6056,13 +6393,28 @@ export async function* agentLoop(messages, options) {
6056
6393
  const toolContext = {
6057
6394
  hookDb: options.hookDb || null,
6058
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,
6059
6400
  cwd: options.cwd || process.cwd(),
6060
6401
  planManager: options.planManager || null,
6061
6402
  sessionId: options.sessionId || null,
6062
6403
  hostManagedToolPolicy: options.hostManagedToolPolicy || null,
6063
6404
  externalToolDescriptors: options.externalToolDescriptors || null,
6064
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,
6065
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,
6066
6418
  // Parent LLM config — forwarded to spawn_sub_agent so a delegated subagent
6067
6419
  // inherits the provider/key and can override just the model (cc agents `model:`).
6068
6420
  llmOptions: {
@@ -6106,6 +6458,10 @@ export async function* agentLoop(messages, options) {
6106
6458
  spawned: 0,
6107
6459
  max: MAX_SUB_AGENTS_PER_RUN,
6108
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,
6109
6465
  // Abort signal — forwarded to background sub-agents so cancelling the
6110
6466
  // parent run also cancels children still running detached.
6111
6467
  signal,
@@ -6238,13 +6594,23 @@ export async function* agentLoop(messages, options) {
6238
6594
  // Normalize the unified id set (P2 observability): every id is charset-
6239
6595
  // sanitized + length-capped, and only these allow-listed keys are stamped,
6240
6596
  // so span cardinality stays bounded and no content leaks in as an id.
6241
- const idAttrs = buildTelemetryAttributes({
6597
+ const telemetryCtx = {
6242
6598
  "workflow.run_id": runId,
6243
6599
  "workflow.name": options.workflowName || undefined,
6244
6600
  "session.id": options.sessionId || undefined,
6245
6601
  "agent.id": options.agentId || undefined,
6246
6602
  "parent_agent.id": options.parentAgentId || undefined,
6247
- });
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 });
6248
6614
  for (const [k, v] of Object.entries(idAttrs)) {
6249
6615
  recorder.setDefaultAttribute(k, v);
6250
6616
  }
@@ -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",