chainlesschain 0.162.177 → 0.162.178

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 (207) hide show
  1. package/README.md +10 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DT-VPWuN.js → AIOps-DRftcSue.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DyOreOVN.js → ActionButton-BFvUaMKR.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DslNsKux.js → Analytics-C0-Rru4P.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-bx-BCljj.js +9 -0
  8. package/src/assets/web-panel/assets/{Artifacts-DoaAtEcY.js → Artifacts-DAJ8B3Un.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DPG3WdJ9.js → Audit-hOk4L1lr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-C_JO6R5G.js → BackgroundAgents-3pbDpM9z.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Cv3BooyX.js → Backup-BtziGDn9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-D2uQXWu-.js → BaseInput-kmshphJ3.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-B4Vczyf3.js → Chat-d7tGSosA.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-p-_PBpX_.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-qTgk4VeP.js → Checkbox-C4Eh4Tz5.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-DlLWcIzj.js → Codegen-r7gmhZyA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DNO73qp7.js → Col-U7mDaGlL.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-vRDNpc4j.js → Community-Bpu8aH1T.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-CEWtIXni.js → Compact-CHb2XBJe.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-KgYC9Fxi.js → Compliance-Cn_r4dey.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-zj5wvkpT.js → Cowork-CdMWfDcz.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-BLDoCozU.js → Cron-BtnREHWy.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BYnff4VY.js → Crosschain-BKI9TSzr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-C75VFsVm.js → DID-pkNubzzJ.js} +2 -2
  25. package/src/assets/web-panel/assets/Dashboard-1rrz1snN.js +3 -0
  26. package/src/assets/web-panel/assets/{Dropdown-B5dbO4Kx.js → Dropdown-DLyFVxDc.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BDipZtHJ.js → EmailListRenderer-CtUWfYVq.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DmIzt8jj.js → FamilyGuardDashboard-B8eX2Qe4.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-D9QOS-uW.js → Federation-D8lsDGus.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-B47HRwAm.js → FormItemContext-CRYt_aIw.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-3R4mXmgV.js → GenericCardRenderer-ClKzY8_N.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DrNXZf1C.js → Git-BXj5Dney.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BGd7JR98.js → Governance-Cm-I8RMz.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-D-2he1ed.js → Inference-CPvbhvgt.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-C0sHIbK5.js → KnowledgeGraph-sYNDZaoi.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DiBQO-jL.js → Logs-BZY_3CRZ.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-DL_5eV2D.js → MarkdownRenderer-up0BW6pv.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-VR59OdrC.js → Marketplace-C5MEJ2Qe.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DrPzwSd3.js → McpTools-ihdrn43I.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-CBgKALwQ.js → Memory-Br8bPMlh.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-pQ9Tn-Mn.js → MobileBridge-BNntgrYW.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-mASB6jyj.js → MobileProjects-BdV915mw.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-DTMgE7HC.js → Mtc-A3wDmlqL.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-CwTnCtiL.js → MtcAudit-CF-mBLWM.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-ngm6WU8f.js → Multisig-Iy2k_MSo.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-4YO4Qww9.js → NLProgramming-kk0W7HUb.js} +1 -1
  47. package/src/assets/web-panel/assets/Notes-B5u5t_4S.js +7 -0
  48. package/src/assets/web-panel/assets/{NotificationSettings-r2H8yPKb.js → NotificationSettings-BN0POf6_.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-CHkSUrXB.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-DrO7jRWS.js → Organization-DwWQESuN.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-Cnwu4i4t.js → Overflow-DG6klrcC.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-zi-cDwA_.js → P2P-BwJcKkgZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-DBEryDv7.js → PdhVaultBrowser-BwOHG0in.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-lqXK2C3x.js → Permissions-CGMXe4TX.js} +4 -4
  55. package/src/assets/web-panel/assets/PersonalDataHub-ziYqg7-A.js +8 -0
  56. package/src/assets/web-panel/assets/{Pipeline-Kep5KAGW.js → Pipeline-BzJfeoGt.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-CHdvOAh1.js → Privacy-DLh3Bblx.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-BhKfdwe9.js → ProjectInit-USVWW36c.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Yavk9K8A.js → ProjectSettings-CiiOn62h.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-6V-PNmcA.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-Bfym6SQn.js → Providers-CBPWWzpu.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-CLSQcrlR.js → QrScannerModal-QVwoRUq3.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-VPa2WKQN.js → QuickAsk-CXnR-2Vn.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-CGXR8jVU.js → Recommend-ClaNzjmF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-C_20YfMB.js → RemoteSession-C3rGw8vJ.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-awZh68UV.js → Reputation-Ci-OWAoP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Ctb4oiYN.js → Row-DRhAwXOS.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-D3sdjvUV.js → RssFeed-BQLJQ2_y.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-C-zfA4kT.js → Search-QQUpkfFd.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-D9XlmHA3.js → Security-DdXKzuKT.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-C3UKnr41.js → Services-x2Zh62Qd.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-BQxagZfF.js → Skeleton-GdY4KwRz.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-Bv4tY1Fd.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-DveENvek.js → Sla-C3QnM-11.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BFjXLNgF.js → SpeechSettings-Bl3ytvmI.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-BpHgbaP9.js → SyncSettings-Wp9IwTsP.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-CF3rIrDe.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-DEiaoyve.js → Templates-CGfvVVM9.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-ITg774nY.js → Tenant-CVXmwQ3U.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-D3ZBcvnb.js → Terminal-bvwrt03E.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BsLU6JjU.js → TimelineRenderer-D0g74KhH.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-Cb5Pquc9.js → Tokens-BH4IwzlT.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-Dwfy-m9N.js → Trigger-DJOpM533.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-CnlSRGG0.js → Trust-B43EPRLr.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-Y4TlvKVo.js → UkeySign-BmBFG-em.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-BpKLtTTO.js → VideoEditing-n9oJwB8k.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-LLBzIq8x.js → Wallet-D5QeivHp.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-BeSRiusP.js → WebAuthn-DcNy7igO.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-BK_l1tXO.js → WorkflowEditor-BVjc7-5-.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-DYd-X7Wg.js → chat-Dy5Tfj3m.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-C9uip71j.js → colors-C4c2CNZU.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-JBrfjUP6.js → compact-item-DPa3O5vN.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-D03fbuiq.js → createContext-TLcfNA6_.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BAwdDA_5.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DLQOR6l-.js → hasIn-D41LVFkp.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-rIxxfdTN.js → index-0oevYjfT.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CChaISMb.js → index-388pLGJ1.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BfOs_bmg.js → index-B8nLBZbv.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-D-1SqEpQ.js → index-BPTGAjee.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BYv-LWdy.js → index-BPtrOBmW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Cpn19c9k.js → index-BQiIxHBy.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BpYKLrUT.js → index-BSM1cHVK.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CS63r8h3.js → index-BYPr9fkz.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Q7mmL6w4.js → index-BsQ7Ev6i.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-SO6lgMg0.js → index-Btk9qLL2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-iahNhZwm.js → index-BvmziZSG.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BnQgRjsm.js → index-BvopLC_T.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Ba-Ps_AJ.js → index-C7xhTVyP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BuNFCZCx.js → index-CNk_ZooP.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CL0ixCxk.js → index-CQrEHoOG.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CChQBM1d.js → index-CSxex23J.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-5YN7F5bO.js → index-CVjgm992.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CwoDpKzW.js → index-CWK4zOPe.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BcBZwlnG.js → index-CX0VsoP4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B46YcgsV.js → index-CXl0YPNG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-l08afIbV.js → index-Cip7LWml.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-6GuLRpBQ.js → index-Cq_hlWNt.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DYLZY4uh.js → index-CzZ49YZL.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B0X76LjX.js → index-D2Nkivlm.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BdH9l8wl.js → index-DBUgRs4D.js} +1 -1
  121. package/src/assets/web-panel/assets/index-DEjVkdzq.js +1 -0
  122. package/src/assets/web-panel/assets/{index-D4UY5Z60.js → index-DNsBj2Ob.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Ce0Bf1YF.js → index-Di4X8cfI.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-ByHO3SqZ.js → index-DljFsVGI.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C2zmOsyD.js → index-Korc3QIe.js} +1 -1
  126. package/src/assets/web-panel/assets/index-LMOc5xrS.js +1 -0
  127. package/src/assets/web-panel/assets/{index-BNCdRmWf.js → index-MKZDje0H.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-D8KvuDz0.js → index-OHAfRu_3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DaokIHt3.js → index-WEJr8PL5.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CILVyK3b.js → index-WinCp6du.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CVZWiUE_.js → index-Yjr6cqSK.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CqWk1FLG.js → index-iQ6gtFih.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DNMAAUVk.js → index-pt3s7yvV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Ck17i0OD.js → index-tQ3igERQ.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-CG9z1c6w.js → initDefaultProps-BFZu87WZ.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-B1Ky7Zff.js → motion-DJIo7qvt.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DME8X2P1.js → move-Cfr5qknL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-cuJYSWB7.js → mtc-parser-Bgc-OadH.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-UI_EE0uZ.js → omit-X3WI-TNp.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-BIoE_Z_V.js → pickAttrs-pJhU9IxK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B48-9HKN.js → placementArrow-BNg_Vj3T.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-D7x_GDgD.js → responsiveObserve-TeSEGQ5-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-DjzC5_7m.js → slide-D_t_P0bv.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C6BIymhh.js → statusUtils-DrrIMwAT.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-B4dGrdD9.js → styleChecker-BjZCuFRm.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-Bken4aNE.js → useFlexGapSupport-rTFdlqdQ.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-BMlqECqv.js → useFs-BidCpfLX.js} +1 -1
  148. package/src/assets/web-panel/assets/usePersonalDataHub-C5mHttA9.js +1 -0
  149. package/src/assets/web-panel/assets/{vnode-CKJQmxa0.js → vnode-BrbcRiHZ.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-CaZunuau.js → zoom-PISX7AkR.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/commands/__tests__/hub-wechat.test.js +45 -0
  153. package/src/commands/background-session.js +5 -2
  154. package/src/commands/context.js +33 -1
  155. package/src/commands/hub.js +473 -39
  156. package/src/commands/init.js +212 -110
  157. package/src/commands/llm.js +22 -6
  158. package/src/commands/skill.js +11 -1
  159. package/src/data/changelog.json +76 -270
  160. package/src/gateways/ws/personal-data-hub-protocol.js +22 -2
  161. package/src/harness/golden-transcript.js +3 -0
  162. package/src/index.js +18 -7
  163. package/src/lib/approval-authority-store.js +891 -0
  164. package/src/lib/background-interaction-resolver.js +399 -54
  165. package/src/lib/background-session-transport.js +3 -1
  166. package/src/lib/context-breakdown.js +82 -3
  167. package/src/lib/event-runtime-host.js +225 -0
  168. package/src/lib/event-runtime-store.js +102 -21
  169. package/src/lib/event-runtime-worker.js +70 -2
  170. package/src/lib/hooks-v2-producers.js +33 -8
  171. package/src/lib/hooks-v2-runtime.js +360 -40
  172. package/src/lib/interaction-adapter.js +66 -0
  173. package/src/lib/operation-fingerprint.js +107 -2
  174. package/src/lib/pdh-dedicated-batch-collectors.js +124 -0
  175. package/src/lib/personal-data-hub-wiring.js +327 -52
  176. package/src/lib/process-execution-broker/credential-agent.js +954 -111
  177. package/src/lib/process-execution-broker/index.js +369 -85
  178. package/src/lib/process-execution-broker/platform-sandbox.js +320 -140
  179. package/src/lib/provider-http-error.js +94 -0
  180. package/src/lib/remote-approval-bridge.js +222 -26
  181. package/src/lib/side-effect-ledger-store.js +65 -14
  182. package/src/lib/skill-loader.js +215 -12
  183. package/src/lib/skill-process-broker.js +15 -1
  184. package/src/lib/sub-agent-context.js +42 -0
  185. package/src/lib/turn-binding-store.js +64 -15
  186. package/src/lib/turn-binding.js +85 -16
  187. package/src/repl/agent-repl.js +40 -6
  188. package/src/repl/remote-approval.js +13 -13
  189. package/src/repl/repl-turn-binding.js +46 -16
  190. package/src/runtime/agent-core.js +286 -35
  191. package/src/runtime/coding-agent-contract-shared.cjs +16 -0
  192. package/src/runtime/headless-runner.js +147 -34
  193. package/src/runtime/headless-stream.js +147 -7
  194. package/src/workers/background-agent-worker.js +70 -38
  195. package/src/assets/web-panel/assets/AppLayout-BNJJAYME.js +0 -9
  196. package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +0 -1
  197. package/src/assets/web-panel/assets/Dashboard-C4Gutni2.js +0 -3
  198. package/src/assets/web-panel/assets/Notes-DP1k45mF.js +0 -7
  199. package/src/assets/web-panel/assets/OrderTableRenderer-CoM_Zw0J.js +0 -1
  200. package/src/assets/web-panel/assets/PersonalDataHub-b63T3HJM.js +0 -9
  201. package/src/assets/web-panel/assets/Projects-CN8E_5Mq.js +0 -1
  202. package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +0 -1
  203. package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +0 -1
  204. package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +0 -1
  205. package/src/assets/web-panel/assets/index-Bh8BPtqR.js +0 -1
  206. package/src/assets/web-panel/assets/index-CeVrHLzZ.js +0 -1
  207. package/src/assets/web-panel/assets/usePersonalDataHub-DLdrfmMa.js +0 -1
@@ -31,6 +31,7 @@ import { parseSkillMcpServers } from "./skill-mcp.js";
31
31
  import { normalizeSkillPaths } from "./skill-path-scope.js";
32
32
  import { discoverPluginSkillLayers } from "./plugin-runtime/skills.js";
33
33
  import settingsLoader from "./settings-loader.cjs";
34
+ import contextSourceLedger from "./context-source-ledger.js";
34
35
 
35
36
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
36
37
 
@@ -218,6 +219,40 @@ export function parseSkillMd(content) {
218
219
  return { data, body };
219
220
  }
220
221
 
222
+ /**
223
+ * Read only enough of SKILL.md to parse its YAML frontmatter. The markdown
224
+ * body is deliberately not retained during discovery; it is materialized by
225
+ * CLISkillLoader.materializeSkill() only when a persona or run_skill needs it.
226
+ */
227
+ function readSkillFrontmatter(skillMd, maxBytes = 256 * 1024) {
228
+ const fd = fs.openSync(skillMd, "r");
229
+ const chunks = [];
230
+ let total = 0;
231
+ try {
232
+ while (total < maxBytes) {
233
+ const chunk = Buffer.allocUnsafe(Math.min(8192, maxBytes - total));
234
+ const read = fs.readSync(fd, chunk, 0, chunk.length, total);
235
+ if (read <= 0) break;
236
+ chunks.push(chunk.subarray(0, read));
237
+ total += read;
238
+ const prefix = Buffer.concat(chunks).toString("utf-8").replace(/^\uFEFF/, "");
239
+ const firstLineEnd = prefix.search(/\r?\n/);
240
+ if (firstLineEnd >= 0 && prefix.slice(0, firstLineEnd).trim() !== "---") {
241
+ return {};
242
+ }
243
+ const match = prefix.match(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/);
244
+ if (match) return parseSkillMd(match[0]).data;
245
+ }
246
+ return {};
247
+ } finally {
248
+ fs.closeSync(fd);
249
+ }
250
+ }
251
+
252
+ function estimateSkillTokens(text) {
253
+ return Math.ceil(String(text || "").length / 4);
254
+ }
255
+
221
256
  /**
222
257
  * Substitute $ARGUMENTS / $1 / $2 / ... placeholders in a skill body.
223
258
  * Inspired by open-agents substituteArguments.
@@ -290,8 +325,17 @@ export function prepareSkillBody(skill, args) {
290
325
  * Multi-layer CLI skill loader
291
326
  */
292
327
  export class CLISkillLoader {
293
- constructor() {
328
+ constructor({ contextLedger = contextSourceLedger } = {}) {
294
329
  this._cache = null;
330
+ this._bodyCache = new Map();
331
+ this._contextLedger = contextLedger;
332
+ this._cacheStats = {
333
+ descriptorScans: 0,
334
+ descriptorCacheHits: 0,
335
+ bodyCacheHits: 0,
336
+ bodyCacheMisses: 0,
337
+ bodyLoads: new Map(),
338
+ };
295
339
  }
296
340
 
297
341
  /**
@@ -449,8 +493,8 @@ export class CLISkillLoader {
449
493
  continue;
450
494
  }
451
495
  try {
452
- const content = fs.readFileSync(skillMd, "utf-8");
453
- const { data, body } = parseSkillMd(content);
496
+ const data = readSkillFrontmatter(skillMd);
497
+ const stat = fs.statSync(skillMd);
454
498
 
455
499
  out.push({
456
500
  id: data.name || entry.name,
@@ -474,12 +518,17 @@ export class CLISkillLoader {
474
518
  paths: normalizeSkillPaths(data),
475
519
  dirName: entry.name,
476
520
  hasHandler: fs.existsSync(path.join(skillDir, "handler.js")),
477
- body,
478
- // Skill-Embedded MCP: inline server declarations in a
479
- // ```mcp-servers fenced code block. Empty array if absent.
480
- mcpServers: parseSkillMcpServers(body),
521
+ isolation: data.isolation === true,
522
+ // Two-tier loading: discovery retains only the descriptor. The body
523
+ // and embedded MCP declarations are populated on first use.
524
+ body: null,
525
+ bodyLoaded: false,
526
+ mcpServers: [],
481
527
  source: layer,
482
528
  skillDir,
529
+ skillMdPath: skillMd,
530
+ skillFileBytes: stat.size,
531
+ skillFileMtimeMs: stat.mtimeMs,
483
532
  });
484
533
  } catch {
485
534
  // Skip malformed skill files
@@ -487,12 +536,149 @@ export class CLISkillLoader {
487
536
  }
488
537
  }
489
538
 
539
+ /**
540
+ * Materialize a descriptor's markdown body and embedded MCP declarations.
541
+ * Cache entries are invalidated by file size/mtime and never include content
542
+ * in provenance records.
543
+ */
544
+ materializeSkill(skill, context = {}) {
545
+ if (!skill || !skill.skillMdPath) {
546
+ throw new TypeError("A discovered skill descriptor is required");
547
+ }
548
+ const stat = fs.statSync(skill.skillMdPath);
549
+ const signature = `${stat.size}:${stat.mtimeMs}`;
550
+ let cached = this._bodyCache.get(skill.skillMdPath);
551
+ let cacheRead = false;
552
+ if (cached?.signature === signature) {
553
+ this._cacheStats.bodyCacheHits += 1;
554
+ cacheRead = true;
555
+ } else {
556
+ const content = fs.readFileSync(skill.skillMdPath, "utf-8");
557
+ const { body } = parseSkillMd(content);
558
+ cached = {
559
+ signature,
560
+ body,
561
+ mcpServers: parseSkillMcpServers(body),
562
+ rawChars: body.length,
563
+ estimatedTokens: estimateSkillTokens(body),
564
+ };
565
+ this._bodyCache.set(skill.skillMdPath, cached);
566
+ this._cacheStats.bodyCacheMisses += 1;
567
+ }
568
+
569
+ skill.body = cached.body;
570
+ skill.mcpServers = cached.mcpServers;
571
+ skill.bodyLoaded = true;
572
+ skill.skillFileBytes = stat.size;
573
+ skill.skillFileMtimeMs = stat.mtimeMs;
574
+
575
+ const prior = this._cacheStats.bodyLoads.get(skill.skillMdPath) || {
576
+ id: skill.id,
577
+ source: skill.source,
578
+ rawChars: cached.rawChars,
579
+ estimatedTokens: cached.estimatedTokens,
580
+ loads: 0,
581
+ cacheHits: 0,
582
+ loadedBecause: [],
583
+ };
584
+ prior.loads += 1;
585
+ if (cacheRead) prior.cacheHits += 1;
586
+ const reason = context.loadedBecause || "explicit";
587
+ if (!prior.loadedBecause.includes(reason)) prior.loadedBecause.push(reason);
588
+ this._cacheStats.bodyLoads.set(skill.skillMdPath, prior);
589
+
590
+ try {
591
+ this._contextLedger?.recordRead?.({
592
+ sessionId: context.sessionId || "unknown",
593
+ turnId: context.turnId || "unknown",
594
+ sourceType: "skill",
595
+ sourceId: skill.id,
596
+ permissionMode: context.permissionMode || "agent",
597
+ tokenCount: cached.estimatedTokens,
598
+ metadata: {
599
+ source: skill.source,
600
+ loadedBecause: reason,
601
+ cacheRead,
602
+ bodyIncluded: context.bodyIncluded === true,
603
+ },
604
+ });
605
+ } catch {
606
+ // Context observability must never make a valid skill unloadable.
607
+ }
608
+ return skill;
609
+ }
610
+
611
+ /** Record descriptor text that was actually returned to a model/tool caller. */
612
+ recordDescriptorUse(skills, context = {}) {
613
+ for (const skill of Array.isArray(skills) ? skills : []) {
614
+ const descriptorText = `${skill.id}\n${skill.description || ""}\n${skill.category || ""}`;
615
+ try {
616
+ this._contextLedger?.recordRead?.({
617
+ sessionId: context.sessionId || "unknown",
618
+ turnId: context.turnId || "unknown",
619
+ sourceType: "skill_descriptor",
620
+ sourceId: skill.id,
621
+ permissionMode: context.permissionMode || "agent",
622
+ tokenCount: estimateSkillTokens(descriptorText),
623
+ metadata: {
624
+ source: skill.source,
625
+ loadedBecause: context.loadedBecause || "list_skills",
626
+ bodyLoaded: skill.bodyLoaded === true,
627
+ },
628
+ });
629
+ } catch {
630
+ // Observability only.
631
+ }
632
+ }
633
+ }
634
+
635
+ /** Content-free cache/cost snapshot for `cc context --sources`. */
636
+ getCacheLedger() {
637
+ const descriptors = Array.isArray(this._cache) ? this._cache : [];
638
+ const descriptorChars = descriptors.reduce(
639
+ (sum, skill) =>
640
+ sum +
641
+ `${skill.id}\n${skill.displayName}\n${skill.description}\n${skill.category}`.length,
642
+ 0,
643
+ );
644
+ const residentPaths = new Set(this._bodyCache.keys());
645
+ const lazyFileBytes = descriptors.reduce(
646
+ (sum, skill) =>
647
+ sum +
648
+ (residentPaths.has(skill.skillMdPath) ? 0 : skill.skillFileBytes || 0),
649
+ 0,
650
+ );
651
+ const bodyLoads = Array.from(this._cacheStats.bodyLoads.values()).map(
652
+ (entry) => ({ ...entry, loadedBecause: [...entry.loadedBecause] }),
653
+ );
654
+ return {
655
+ descriptors: {
656
+ resident: descriptors.length,
657
+ scans: this._cacheStats.descriptorScans,
658
+ cacheHits: this._cacheStats.descriptorCacheHits,
659
+ rawChars: descriptorChars,
660
+ estimatedTokens: estimateSkillTokens(descriptorChars),
661
+ },
662
+ bodies: {
663
+ resident: this._bodyCache.size,
664
+ cacheHits: this._cacheStats.bodyCacheHits,
665
+ cacheMisses: this._cacheStats.bodyCacheMisses,
666
+ entries: bodyLoads,
667
+ },
668
+ savings: {
669
+ lazyFileBytes,
670
+ estimatedTokensAvoided: estimateSkillTokens(lazyFileBytes),
671
+ },
672
+ };
673
+ }
674
+
490
675
  /**
491
676
  * Load all skills from all layers, applying priority override
492
677
  * Higher-priority layers override same-name skills from lower layers.
493
678
  * @returns {object[]} Resolved skill list
494
679
  */
495
680
  loadAll(options = {}) {
681
+ this._cacheStats.descriptorScans += 1;
496
682
  // `cc agent --bare` (CC_SKILLS=0): no skill layer loads at all — cache the
497
683
  // empty result so getResolvedSkills stays cheap on every call.
498
684
  if (allSkillsDisabled(options)) {
@@ -552,7 +738,10 @@ export class CLISkillLoader {
552
738
  * @returns {object[]}
553
739
  */
554
740
  getResolvedSkills() {
555
- if (this._cache) return this._cache;
741
+ if (this._cache) {
742
+ this._cacheStats.descriptorCacheHits += 1;
743
+ return this._cache;
744
+ }
556
745
  return this.loadAll();
557
746
  }
558
747
 
@@ -560,10 +749,16 @@ export class CLISkillLoader {
560
749
  * Get auto-activated persona skills
561
750
  * @returns {object[]} skills with category "persona" and activation "auto"
562
751
  */
563
- getAutoActivatedPersonas() {
564
- return this.getResolvedSkills().filter(
565
- (s) => s.category === "persona" && s.activation === "auto",
566
- );
752
+ getAutoActivatedPersonas(context = {}) {
753
+ return this.getResolvedSkills()
754
+ .filter((s) => s.category === "persona" && s.activation === "auto")
755
+ .map((skill) =>
756
+ this.materializeSkill(skill, {
757
+ ...context,
758
+ loadedBecause: context.loadedBecause || "persona_auto",
759
+ bodyIncluded: true,
760
+ }),
761
+ );
567
762
  }
568
763
 
569
764
  /**
@@ -571,6 +766,14 @@ export class CLISkillLoader {
571
766
  */
572
767
  clearCache() {
573
768
  this._cache = null;
769
+ this._bodyCache.clear();
770
+ this._cacheStats = {
771
+ descriptorScans: 0,
772
+ descriptorCacheHits: 0,
773
+ bodyCacheHits: 0,
774
+ bodyCacheMisses: 0,
775
+ bodyLoads: new Map(),
776
+ };
574
777
  }
575
778
  }
576
779
 
@@ -2,6 +2,7 @@ import executionBroker from "./process-execution-broker/index.js";
2
2
 
3
3
  const defaultRunSync = executionBroker.spawnSync.bind(executionBroker);
4
4
  const defaultRunFileSync = executionBroker.execFileSync.bind(executionBroker);
5
+ const defaultRun = executionBroker.spawn.bind(executionBroker);
5
6
 
6
7
  function skillIdentity(skill = {}) {
7
8
  const identity = String(
@@ -31,7 +32,11 @@ function declaresShellExecution(skill = {}) {
31
32
  */
32
33
  export function createSkillProcessBroker(
33
34
  skill,
34
- { runSync = defaultRunSync, runFileSync = defaultRunFileSync } = {},
35
+ {
36
+ run = defaultRun,
37
+ runSync = defaultRunSync,
38
+ runFileSync = defaultRunFileSync,
39
+ } = {},
35
40
  ) {
36
41
  if (!declaresShellExecution(skill)) return null;
37
42
 
@@ -46,6 +51,14 @@ export function createSkillProcessBroker(
46
51
  });
47
52
 
48
53
  return Object.freeze({
54
+ run(file, args = [], options = {}) {
55
+ return run(file, args, {
56
+ ...options,
57
+ ...metadata,
58
+ shell: false,
59
+ });
60
+ },
61
+
49
62
  runSync(file, args = [], options = {}) {
50
63
  return runSync(file, args, {
51
64
  ...options,
@@ -64,6 +77,7 @@ export function createSkillProcessBroker(
64
77
  }
65
78
 
66
79
  export const _skillProcessDefaults = {
80
+ run: defaultRun,
67
81
  runSync: defaultRunSync,
68
82
  runFileSync: defaultRunFileSync,
69
83
  };
@@ -110,6 +110,11 @@ export class SubAgentContext {
110
110
  // symlinkDirectories } — only materialize the needed packages and reuse
111
111
  // approved dep dirs. null → full checkout (byte-identical default).
112
112
  this._worktreeOptions = options.worktreeOptions || null;
113
+ // Recovery lineage collected from the child agent-loop event stream.
114
+ // These are provider/runtime identifiers, not reconstructed log labels.
115
+ this._runId = null;
116
+ this._checkpointIds = [];
117
+ this._toolUseIds = [];
113
118
 
114
119
  // ── Isolated state ──────────────────────────────────────────────
115
120
  // Independent message history — never shared with parent
@@ -399,6 +404,22 @@ export class SubAgentContext {
399
404
  for await (const event of gen) {
400
405
  this._iterationCount++;
401
406
 
407
+ if (event.type === "run-started" && event.runId) {
408
+ this._runId = String(event.runId);
409
+ }
410
+ if (event.type === "checkpoint" && event.id) {
411
+ const checkpointId = String(event.id);
412
+ if (!this._checkpointIds.includes(checkpointId)) {
413
+ this._checkpointIds.push(checkpointId);
414
+ }
415
+ }
416
+ if (event.type === "tool-executing" && event.tool_use_id) {
417
+ const toolUseId = String(event.tool_use_id);
418
+ if (!this._toolUseIds.includes(toolUseId)) {
419
+ this._toolUseIds.push(toolUseId);
420
+ }
421
+ }
422
+
402
423
  if (event.type === "token-usage" && this._onUsage) {
403
424
  // Forward real usage to the spawner. A nested child's event already
404
425
  // carries its own attribution frame — preserve it (deepest wins).
@@ -604,6 +625,26 @@ export class SubAgentContext {
604
625
  }
605
626
  }
606
627
 
628
+ /**
629
+ * Serializable child→parent recovery lineage. Worktree paths are included
630
+ * for local restore UX; executable merge callbacks and tool payloads are not.
631
+ */
632
+ recoveryBinding(result = this.result) {
633
+ const worktree = result?.worktree || null;
634
+ return {
635
+ childAgentId: this.id,
636
+ parentAgentId: this.parentId || null,
637
+ traceId: this._runId,
638
+ parentTraceId: this._hookParentTraceId || null,
639
+ checkpointIds: [...this._checkpointIds],
640
+ toolUseIds: [...this._toolUseIds],
641
+ worktreeId:
642
+ worktree?.branch || this._worktreeBranch || null,
643
+ worktreePath:
644
+ worktree?.path || this._worktreePath || null,
645
+ };
646
+ }
647
+
607
648
  /**
608
649
  * True when this sub-agent has been asked to stop — either via an external
609
650
  * abort signal or via this context's own `abort()` (precise single-agent
@@ -647,6 +688,7 @@ export class SubAgentContext {
647
688
  iterationCount: this._iterationCount,
648
689
  createdAt: this.createdAt,
649
690
  completedAt: this.completedAt,
691
+ recoveryBinding: this.recoveryBinding(),
650
692
  worktree: this._worktreePath
651
693
  ? { path: this._worktreePath, branch: this._worktreeBranch }
652
694
  : null,
@@ -35,46 +35,85 @@ export const _deps = {
35
35
  readEvents: storeReadEvents,
36
36
  };
37
37
 
38
+ export class TurnBindingPersistenceError extends Error {
39
+ constructor(operation, sessionId, cause) {
40
+ const detail = cause?.message || String(cause || "unknown persistence error");
41
+ super(`Turn binding ${operation} failed for ${sessionId}: ${detail}`, {
42
+ cause,
43
+ });
44
+ this.name = "TurnBindingPersistenceError";
45
+ this.code =
46
+ operation === "read"
47
+ ? "TURN_BINDING_READ_FAILED"
48
+ : "TURN_BINDING_PERSIST_FAILED";
49
+ this.operation = operation;
50
+ this.sessionId = sessionId;
51
+ }
52
+ }
53
+
54
+ function persistenceFailure(operation, sessionId, cause, failIfUnavailable) {
55
+ if (!failIfUnavailable) return false;
56
+ throw new TurnBindingPersistenceError(operation, sessionId, cause);
57
+ }
58
+
38
59
  /**
39
60
  * Append the current binding table as a new chained event. Each call writes the
40
61
  * FULL table (append-only snapshot), so the newest event is authoritative — a
41
62
  * later `loadTurnBindingLog` never has to merge partial deltas across a chain
42
63
  * that a compaction could have truncated.
43
64
  *
44
- * Best-effort: a persistence failure must not crash the turn it is recording, so
45
- * the caller gets `false` instead of a throw.
65
+ * Turn/checkpoint binding is Critical state. Persistence fails closed by
66
+ * default; diagnostic callers may explicitly opt into advisory behavior.
46
67
  *
47
68
  * @param {string} sessionId
48
69
  * @param {TurnBindingLog|object} log a TurnBindingLog or its toJSON() shape
70
+ * @param {{failIfUnavailable?:boolean}} [opts]
49
71
  * @returns {boolean} true when the snapshot was written
50
72
  */
51
- export function persistTurnBinding(sessionId, log) {
52
- if (!sessionId || !log) return false;
53
- const snapshot = log instanceof TurnBindingLog ? log.toJSON() : log;
54
- if (!snapshot || !Array.isArray(snapshot.turns)) return false;
73
+ export function persistTurnBinding(sessionId, log, opts = {}) {
74
+ const failIfUnavailable = opts.failIfUnavailable !== false;
75
+ if (!sessionId || !log) {
76
+ return persistenceFailure(
77
+ "write",
78
+ sessionId || "<missing-session>",
79
+ new TypeError("sessionId and binding log are required"),
80
+ failIfUnavailable,
81
+ );
82
+ }
55
83
  try {
84
+ const snapshot = log instanceof TurnBindingLog ? log.toJSON() : log;
85
+ if (!snapshot || !Array.isArray(snapshot.turns)) {
86
+ throw new TypeError("binding snapshot must contain a turns array");
87
+ }
56
88
  _deps.appendEvent(sessionId, TURN_BINDING_EVENT, snapshot);
57
89
  return true;
58
- } catch {
59
- // Persistence is advisory — never let it break the agent turn.
60
- return false;
90
+ } catch (error) {
91
+ return persistenceFailure("write", sessionId, error, failIfUnavailable);
61
92
  }
62
93
  }
63
94
 
64
95
  /**
65
96
  * Rebuild the TurnBindingLog from the LATEST persisted snapshot in a session
66
- * transcript. Returns an empty log when the session has no binding event (or on
67
- * any read error) so callers can treat "never persisted" and "unreadable" alike.
97
+ * transcript. Returns an empty log when the session has no binding event.
98
+ * Read/corruption errors fail closed unless explicitly marked advisory.
68
99
  *
69
100
  * @param {string} sessionId
101
+ * @param {{failIfUnavailable?:boolean}} [opts]
70
102
  * @returns {TurnBindingLog}
71
103
  */
72
- export function loadTurnBindingLog(sessionId) {
104
+ export function loadTurnBindingLog(sessionId, opts = {}) {
105
+ const failIfUnavailable = opts.failIfUnavailable !== false;
73
106
  let events = [];
74
107
  try {
75
108
  events = _deps.readEvents(sessionId) || [];
76
- } catch {
77
- return new TurnBindingLog();
109
+ } catch (error) {
110
+ const fallback = persistenceFailure(
111
+ "read",
112
+ sessionId || "<missing-session>",
113
+ error,
114
+ failIfUnavailable,
115
+ );
116
+ if (fallback === false) return new TurnBindingLog();
78
117
  }
79
118
  // Newest snapshot wins — scan backwards for the first well-formed one.
80
119
  for (let i = events.length - 1; i >= 0; i--) {
@@ -85,7 +124,17 @@ export function loadTurnBindingLog(sessionId) {
85
124
  e.data &&
86
125
  Array.isArray(e.data.turns)
87
126
  ) {
88
- return TurnBindingLog.fromJSON(e.data);
127
+ try {
128
+ return TurnBindingLog.fromJSON(e.data);
129
+ } catch (error) {
130
+ const fallback = persistenceFailure(
131
+ "read",
132
+ sessionId || "<missing-session>",
133
+ error,
134
+ failIfUnavailable,
135
+ );
136
+ if (fallback === false) return new TurnBindingLog();
137
+ }
89
138
  }
90
139
  }
91
140
  return new TurnBindingLog();