chainlesschain 0.166.103 → 0.166.104

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 (178) hide show
  1. package/package.json +1 -1
  2. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  3. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-BLrXpdnP.js → AIOps-Bah3HREy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C0xy9uFy.js → ActionButton-DY2aeZMc.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C6EephQP.js → Analytics-BFly8Xaa.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-ZCXdOD9y.js → AppLayout-dp0Zz-JM.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-D4MfZ-pl.js → Artifacts-DcIg09ai.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BQ8c3bBu.js → Audit-DkN-lsjZ.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BOA7_ljM.js → BackgroundAgents-BjzgusLx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-60uiQjGT.js → Backup-CU8S6qu9.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-BDvEbGs_.js → BaseInput-B1pC2gjL.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-B3IkAuFQ.js → Chat-B6xFp6gD.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-DxNCLZ_X.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-njGbfJWq.js → Checkbox-QHw7Vc4U.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CF7DtdxC.js → Codegen-gdecsfjg.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C_3Zix40.js → Col-B0_JmAmc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-CzU-ZC2d.js → Community-DRCcP5xR.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BgeqsPRh.js → Compact-DZtoGzhX.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-6l7cwiqp.js → Compliance-BfNTvEnT.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-D4s905fI.js → Cowork-CDXTLm84.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-CGTYSxRS.js → Cron-Big35CbN.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BnSo-Yt3.js → Crosschain-C8ArrAVr.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-Dm4vk2X1.js → DID-D_77BI2O.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-D7H-PZ_N.js → Dashboard-D2SxTzEp.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-COslkgxn.js → Dropdown-mR1-b9ud.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-CJWEetmk.js → EmailListRenderer-DPl7TE1c.js} +1 -1
  28. package/src/assets/web-panel/assets/{EvolutionSettings-BbzebAWe.js → EvolutionSettings-BxRYLRSH.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BsoTOXwk.js → FamilyGuardDashboard-rnGtXJ5Z.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-Ck5kM_5H.js → Federation-BpxenYK8.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-gShxCqtF.js → FormItemContext-Cz1kDf6l.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-DarN1G-z.js → GenericCardRenderer-CGV3KN6U.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-CJfpLyP5.js → Git-CIRP4UTx.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-CVcJmpQz.js → Governance-DX4r8Vmt.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference-ComU_ig8.js → Inference-DQKk02Hl.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-35hprFJy.js → KnowledgeGraph-DdD6J-By.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-BQDqBTz0.js → Logs-DA2w-1_m.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-BpdGAVCC.js → MarkdownRenderer-C9aSGETh.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-B9O9OKnn.js → Marketplace-BsJ6_6KW.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-VMKxA4Wc.js → McpTools-_6k30z6u.js} +3 -3
  41. package/src/assets/web-panel/assets/{Memory-CcBbf3Bc.js → Memory-C0_dYY0A.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-D7myz46N.js → MobileBridge-CCTnVqiC.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileProjects-Da5KBXyx.js → MobileProjects-RZdxDSwC.js} +1 -1
  44. package/src/assets/web-panel/assets/{Mtc-x3NPZfuL.js → Mtc-D4Ys8lvX.js} +5 -5
  45. package/src/assets/web-panel/assets/{MtcAudit-CwnzptEC.js → MtcAudit-BKTC--a2.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-CCAxf4Ye.js → Multisig-CGR1qOe1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-CptHKz-Q.js → NLProgramming-hKKD4qWb.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-0uEp4DCJ.js → Notes-pCn7Ec40.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-DthvrOLd.js → NotificationSettings-B9eEJXEs.js} +1 -1
  50. package/src/assets/web-panel/assets/OrderTableRenderer-CmJuhaT2.js +1 -0
  51. package/src/assets/web-panel/assets/{Organization-BWf2tmQQ.js → Organization-CcQbrnwW.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-BbvAtEth.js → Overflow-HmD4Mn72.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-zBo6enit.js → P2P-B7LIgx5H.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-D_LMQ4A4.js → PdhVaultBrowser-CXiHnBfX.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-C1ucb2ck.js → Permissions-CQRI3y_l.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-Bz64RddJ.js → PersonalDataHub-1EeHmsy5.js} +5 -5
  57. package/src/assets/web-panel/assets/{Pipeline-DXJt3y3_.js → Pipeline-BFyoIdn8.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-DiZ8b_ut.js → Privacy-BxWthXMF.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-DfLKKrJ4.js → ProjectInit-DfigBilu.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-Ct6kJuIY.js → ProjectSettings-CFmIcVc-.js} +2 -2
  61. package/src/assets/web-panel/assets/{Projects-BBUMlZ3m.js → Projects-CR7H59v3.js} +1 -1
  62. package/src/assets/web-panel/assets/{Providers-DYVQPHvc.js → Providers-2ulom-76.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-23Il7iPp.js → QrScannerModal-BwhO07b_.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-WgOZKrv1.js → QuickAsk-_Wg34OwF.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-CRIka6xT.js → Recommend-D_jq6Svg.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-DOy3GjVZ.js → RemoteSession-BlzvEfsa.js} +2 -2
  67. package/src/assets/web-panel/assets/{Reputation-BXyi6xeR.js → Reputation-9CTXHfL4.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-CoMxuIcR.js → Row-DlCYCN-t.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-87hPkoM6.js → RssFeed-BvlInFCH.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-B1EqFCO4.js → Search-CfaUzKuL.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-HcC41eGs.js → Security-bAWgLyve.js} +3 -3
  72. package/src/assets/web-panel/assets/{Services-BqLwmvCM.js → Services-C4LOGACW.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-CIMmjW4r.js → Skeleton-Dd0Uwh3_.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-CgurezJU.js → Skills-ZYF1kYCU.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-CJuRuqPE.js → Sla-DpsmIaEo.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-BUwsbd1o.js → SpeechSettings-BBXoJ4yc.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-qo04TOX-.js → SyncSettings-SOnROi2Y.js} +2 -2
  78. package/src/assets/web-panel/assets/Tasks-HFA_ZlDk.js +1 -0
  79. package/src/assets/web-panel/assets/{Templates-D1PTczPW.js → Templates-fsK-RCEc.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-Bi0FkEPR.js → Tenant-CuqYxamb.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-DfSt3bIF.js → Terminal-GKlI2R5F.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-B7bIVs4R.js → TimelineRenderer-DMDMHLeY.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-B6w5XrMb.js → Tokens-BoV60rh3.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-XXVSDWiF.js → Trigger-CE344-Ws.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-DYvlgcK-.js → Trust-DENwgkVc.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-XyYwJMBM.js → UkeySign-FrcM7yfm.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-C47WbtHH.js → VideoEditing-DAY9WaC2.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-CYfqTj_C.js → Wallet-BuUR5i82.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-3AduFn1l.js → WebAuthn-nfSQuq85.js} +3 -3
  90. package/src/assets/web-panel/assets/{WorkflowEditor-DHYEHjMJ.js → WorkflowEditor-DHhPOp9l.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-AeehErSw.js → chat-C4nsLzYy.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-DKXJ8paX.js → colors-BwXH0Wjn.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-BOlCiomD.js → compact-item-uApFG30S.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BAP6U85b.js → createContext-BpyYs4fe.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Bk6HGn3N.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-BLmeL1Xe.js → hasIn-MXCLi00p.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CdNSmvog.js → index-1LxX2IwJ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Dv8-g1pY.js → index-61CBBrHE.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-ClpwxOXo.js → index-B0P1Z_oa.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CgtaOzMo.js → index-B7jojVc7.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BRcJlKSa.js → index-BEGMfaOo.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-wyKpTsGM.js → index-BOhjapd5.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BuUwKmEI.js → index-BaBwXpAp.js} +1 -1
  104. package/src/assets/web-panel/assets/index-Bb1LPYdi.js +1 -0
  105. package/src/assets/web-panel/assets/{index-Co7pjF53.js → index-Btv--Nnt.js} +1 -1
  106. package/src/assets/web-panel/assets/index-C-FsTuFG.js +1 -0
  107. package/src/assets/web-panel/assets/{index-CFKzWIo8.js → index-CF0bUOP4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CaHhfpBi.js → index-CNjUPfJP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-ZMbXl5lo.js → index-CRmNdEk_.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BTnp4Nt3.js → index-C_oOpXhP.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-OLKhib9T.js → index-ChUTKrOq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-T28naovw.js → index-CiIpIYhs.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bpx54vfs.js → index-CwVvk2i7.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BAS2QqLn.js → index-D3_tj06B.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CRa4pAaM.js → index-DE4W4vLO.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-BSOenF5s.js → index-DLxKx5_J.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DE7cbDDe.js → index-DTXi_gQh.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DWYqOI1A.js → index-Daco83me.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DQLQuwpA.js → index-DffWVpZv.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CiqeF0Pf.js → index-Dkncxel3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-IlzfIRNh.js → index-Dn12gJJU.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Bn54FU3i.js → index-Dt-nq2eF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Bt-FCVzh.js → index-Du01X9N9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-EKNODw4P.js → index-DucLR6YX.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-C5AKY6bJ.js → index-Gz2rGWef.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C8qi53Ki.js → index-IvgszJ3C.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-urPvyUVk.js → index-S80LLjdx.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bs2O3f81.js → index-WKbnwbKa.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DpZqbc4w.js → index-XHBXtvqp.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-De2fPxdb.js → index-nn22NSDK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B0IjlB9m.js → index-pCIAE2yS.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-PEe9OWEQ.js → index-pRpMLhMt.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-FPXLIXoy.js → index-pgHGBAoF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BhcpWl5z.js → index-ugIUt5TL.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DsQ38fVY.js → index-xLGf7f9K.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-CDWa1rnn.js → initDefaultProps-C58CkKEo.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-CX2Cnsd5.js → motion-BLWPO2NV.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-Bfe1NcEF.js → move-BgrFMwYp.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-DbdqleHE.js → mtc-parser-Cq2AuGzl.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-CE2K7NVv.js → omit-Bb-9L7if.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-y_bZgxWG.js → pickAttrs-vLmOxYr1.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BOHR4zwr.js → placementArrow-BNySU_dJ.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-B8Jwq05-.js → responsiveObserve-xnbwagnm.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-BD7RJ3Kn.js → slide-BccbY5Cn.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-BuxDJeDx.js → statusUtils-DOp9d2-w.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DgYQ2Q0o.js → styleChecker-Bu1w6ho9.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DAHF0Fwi.js → useFlexGapSupport-ZsUOrVdL.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-D4kDyLFp.js → useFs-C0zpzKtO.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-BHtSxW4A.js → usePersonalDataHub-DO1OdgXK.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-CHKtdqW0.js → vnode-jTIRyhR5.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-CIZAMma9.js → zoom-DKvS7-Qs.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/commands/agent.js +56 -20
  154. package/src/commands/batch.js +17 -12
  155. package/src/commands/eval.js +24 -8
  156. package/src/data/changelog.json +13 -0
  157. package/src/harness/mcp-client.js +187 -26
  158. package/src/lib/background-session-transport.js +21 -5
  159. package/src/lib/decision-layer/runtime.js +180 -10
  160. package/src/lib/direct-model-usage.js +34 -0
  161. package/src/lib/evolution/agent-evolution-ingress.js +63 -7
  162. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +10 -0
  163. package/src/lib/evolution/evolution-evidence-projector.js +46 -7
  164. package/src/lib/evolution/skill-decision-projection.js +211 -0
  165. package/src/lib/evolution/skill-retrieval-revocation-authority.js +17 -2
  166. package/src/lib/skill-loader.js +16 -0
  167. package/src/repl/agent-repl.js +86 -7
  168. package/src/runtime/agent-core.js +111 -13
  169. package/src/runtime/headless-runner.js +212 -22
  170. package/src/runtime/headless-stream.js +189 -25
  171. package/src/runtime/mcp-config.js +26 -17
  172. package/src/runtime/mcp-tool-search.js +44 -14
  173. package/src/assets/web-panel/assets/ChatBubbleRenderer-n6BVStHf.js +0 -1
  174. package/src/assets/web-panel/assets/OrderTableRenderer-CxXuPzZg.js +0 -1
  175. package/src/assets/web-panel/assets/Tasks-QEFENVsp.js +0 -1
  176. package/src/assets/web-panel/assets/devWarning-rGldFhxo.js +0 -1
  177. package/src/assets/web-panel/assets/index-Cw-lfvvV.js +0 -1
  178. package/src/assets/web-panel/assets/index-QBXRSyoZ.js +0 -1
@@ -0,0 +1,211 @@
1
+ import { types } from "node:util";
2
+ import { createSkillDecisionRequest } from "../decision-layer/contracts.js";
3
+
4
+ const MAX_BYTES = 256 * 1024;
5
+
6
+ function canonical(value) {
7
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
8
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
9
+ return `{${Object.keys(value)
10
+ .sort()
11
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
12
+ .join(",")}}`;
13
+ }
14
+
15
+ // Capture descriptors before any authority await. Never evaluate source getters
16
+ // or allow caller mutation to change committed evidence or outbound bytes.
17
+ function snapshot(value) {
18
+ let nodes = 0;
19
+ let bytes = 0;
20
+ const seen = new Set();
21
+ const visit = (entry, depth) => {
22
+ if (++nodes > 4096 || depth > 16)
23
+ throw new TypeError("Skill decision input exceeds its structural budget");
24
+ if (typeof entry === "string") {
25
+ bytes += Buffer.byteLength(entry, "utf8");
26
+ if (bytes > MAX_BYTES)
27
+ throw new TypeError("Skill decision input exceeds its byte budget");
28
+ return entry;
29
+ }
30
+ if (
31
+ entry === null ||
32
+ typeof entry === "boolean" ||
33
+ (typeof entry === "number" && Number.isFinite(entry))
34
+ )
35
+ return entry;
36
+ if (
37
+ !entry ||
38
+ typeof entry !== "object" ||
39
+ types.isProxy(entry) ||
40
+ seen.has(entry)
41
+ )
42
+ throw new TypeError(
43
+ "Skill decision input must be acyclic plain JSON data",
44
+ );
45
+ const array = Array.isArray(entry);
46
+ if (
47
+ !array &&
48
+ ![Object.prototype, null].includes(Object.getPrototypeOf(entry))
49
+ )
50
+ throw new TypeError("Skill decision input must use plain objects");
51
+ const keys = Reflect.ownKeys(entry).filter(
52
+ (key) => !(array && key === "length"),
53
+ );
54
+ if (
55
+ array &&
56
+ (keys.length !== entry.length ||
57
+ keys.some((key, index) => key !== String(index)))
58
+ )
59
+ throw new TypeError("Skill decision arrays must be dense data");
60
+ seen.add(entry);
61
+ const output = array ? [] : Object.create(null);
62
+ for (const key of keys) {
63
+ const property = Object.getOwnPropertyDescriptor(entry, key);
64
+ if (
65
+ typeof key !== "string" ||
66
+ !property?.enumerable ||
67
+ !("value" in property)
68
+ )
69
+ throw new TypeError(
70
+ "Skill decision input cannot contain accessors or symbols",
71
+ );
72
+ bytes += Buffer.byteLength(key, "utf8");
73
+ if (bytes > MAX_BYTES)
74
+ throw new TypeError("Skill decision input exceeds its byte budget");
75
+ Object.defineProperty(output, key, {
76
+ value: visit(property.value, depth + 1),
77
+ enumerable: true,
78
+ });
79
+ }
80
+ seen.delete(entry);
81
+ return Object.freeze(output);
82
+ };
83
+ const result = visit(value, 0);
84
+ if (Buffer.byteLength(JSON.stringify(result), "utf8") > MAX_BYTES)
85
+ throw new TypeError("Skill decision input exceeds its byte budget");
86
+ return result;
87
+ }
88
+
89
+ function exact(value, keys) {
90
+ if (
91
+ !value ||
92
+ typeof value !== "object" ||
93
+ Array.isArray(value) ||
94
+ Object.keys(value).sort().join("\0") !== [...keys].sort().join("\0")
95
+ )
96
+ throw new TypeError("Skill decision has an unsupported protocol shape");
97
+ }
98
+
99
+ function text(value) {
100
+ if (typeof value !== "string" || !value.length)
101
+ throw new TypeError("Skill decision requires nonempty text");
102
+ }
103
+
104
+ export function snapshotSkillDecisionPayload(value) {
105
+ const payload = snapshot(value);
106
+ exact(payload, ["state", "questions"]);
107
+ exact(payload.state, ["task", "candidates"]);
108
+ text(payload.state.task);
109
+ const candidates = payload.state.candidates;
110
+ if (
111
+ !Array.isArray(candidates) ||
112
+ candidates.length < 1 ||
113
+ candidates.length > 8
114
+ )
115
+ throw new TypeError("Skill decision requires bounded candidates");
116
+ candidates.forEach((candidate, index) => {
117
+ exact(candidate, [
118
+ "candidateId",
119
+ "name",
120
+ "description",
121
+ "category",
122
+ "tags",
123
+ ]);
124
+ if (candidate.candidateId !== `c${index + 1}`)
125
+ throw new TypeError("Skill decision candidate identity changed");
126
+ for (const key of ["name", "description", "category"]) text(candidate[key]);
127
+ if (!Array.isArray(candidate.tags) || candidate.tags.length > 32)
128
+ throw new TypeError("Skill decision tags are unbounded");
129
+ candidate.tags.forEach(text);
130
+ });
131
+ const ids = candidates.map(({ candidateId }) => candidateId);
132
+ exact(payload.questions, [
133
+ "needs_skill",
134
+ "best_skill",
135
+ ...ids.map((id) => `fits_${id}`),
136
+ ]);
137
+ for (const key of ["needs_skill", ...ids.map((id) => `fits_${id}`)]) {
138
+ exact(payload.questions[key], ["type", "instructions"]);
139
+ if (payload.questions[key].type !== "noul")
140
+ throw new TypeError("Skill decision question type changed");
141
+ text(payload.questions[key].instructions);
142
+ }
143
+ const choice = payload.questions.best_skill;
144
+ exact(choice, ["type", "instructions", "criteria"]);
145
+ if (choice.type !== "choice")
146
+ throw new TypeError("Skill decision question type changed");
147
+ text(choice.instructions);
148
+ exact(choice.criteria, [...ids, "none"]);
149
+ Object.values(choice.criteria).forEach(text);
150
+ return payload;
151
+ }
152
+
153
+ export function snapshotSkillDecisionRequest(value) {
154
+ const request = snapshot(value);
155
+ const rebuilt = createSkillDecisionRequest({
156
+ decisionId: request.decisionId,
157
+ ...request.binding,
158
+ query: request.payload?.state?.task,
159
+ candidates: request.candidates,
160
+ });
161
+ if (canonical(request) !== canonical(rebuilt))
162
+ throw new TypeError("Skill decision request identity or contract changed");
163
+ snapshotSkillDecisionPayload(request.payload);
164
+ return request;
165
+ }
166
+
167
+ function textPath(path) {
168
+ return (
169
+ /^state\.task$/u.test(path) ||
170
+ /^state\.candidates\.\d+\.(?:name|description|category|tags\.\d+)$/u.test(
171
+ path,
172
+ ) ||
173
+ /^questions\.best_skill\.criteria\.c[1-8]$/u.test(path)
174
+ );
175
+ }
176
+
177
+ export function buildSkillDecisionPayload(original, projection) {
178
+ if (
179
+ projection.visibility !== "model-visible" ||
180
+ projection.truncated ||
181
+ (projection.redactionSummary?.byType?.["content-truncation"] ?? 0) > 0
182
+ )
183
+ throw new Error("Skill decision projection is opaque or truncated");
184
+ const captured = snapshotSkillDecisionPayload(original);
185
+ const projected = snapshotSkillDecisionPayload(projection.content);
186
+ const compare = (before, after, path = "") => {
187
+ if (
188
+ typeof before === "string" &&
189
+ typeof after === "string" &&
190
+ textPath(path)
191
+ )
192
+ return;
193
+ if (before === null || typeof before !== "object") {
194
+ if (before !== after)
195
+ throw new Error("Skill decision projection changed protocol metadata");
196
+ return;
197
+ }
198
+ if (
199
+ !after ||
200
+ typeof after !== "object" ||
201
+ Array.isArray(before) !== Array.isArray(after) ||
202
+ Object.keys(before).sort().join("\0") !==
203
+ Object.keys(after).sort().join("\0")
204
+ )
205
+ throw new Error("Skill decision projection changed protocol structure");
206
+ for (const key of Object.keys(before))
207
+ compare(before[key], after[key], path ? `${path}.${key}` : key);
208
+ };
209
+ compare(captured, projected);
210
+ return projected;
211
+ }
@@ -239,9 +239,19 @@ export async function openSkillRetrievalRevocationAuthority({
239
239
  ) {
240
240
  throw new Error("Retrieval revocation load is not authoritative");
241
241
  }
242
- state = loaded.found
242
+ const loadedState = loaded.found
243
243
  ? verifySkillRetrievalRevocationState(loaded.state, tenantId)
244
244
  : null;
245
+ if (
246
+ state !== null &&
247
+ (loadedState === null ||
248
+ loadedState.revision < state.revision ||
249
+ (loadedState.revision === state.revision &&
250
+ loadedState.stateDigest !== state.stateDigest))
251
+ ) {
252
+ throw new Error("Retrieval revocation state regressed");
253
+ }
254
+ state = loadedState;
245
255
  return state;
246
256
  }
247
257
 
@@ -265,6 +275,10 @@ export async function openSkillRetrievalRevocationAuthority({
265
275
  receiptDigest: entry?.propagationRequestDigest ?? null,
266
276
  });
267
277
  },
278
+ async refresh() {
279
+ const current = await reload();
280
+ return current?.stateDigest ?? null;
281
+ },
268
282
  async invalidateRetrieval(input) {
269
283
  const request = normalizeRequest(input, tenantId);
270
284
  const contentDigest = request.dependency.digest;
@@ -351,7 +365,8 @@ export function captureSkillRetrievalRevocationReader(reader) {
351
365
  if (
352
366
  !readerBrand.has(reader) ||
353
367
  reader.schema !== SKILL_RETRIEVAL_REVOCATION_READER_SCHEMA ||
354
- typeof reader.inspect !== "function"
368
+ typeof reader.inspect !== "function" ||
369
+ typeof reader.refresh !== "function"
355
370
  ) {
356
371
  throw new TypeError(
357
372
  "A branded Skill retrieval revocation reader is required",
@@ -392,6 +392,7 @@ export class CLISkillLoader {
392
392
  throw new TypeError("reauthorizeSkill must be a function or null");
393
393
  }
394
394
  this._cache = null;
395
+ this._lastLoadOptions = null;
395
396
  bindSkillRuntimeAdmission(this, skillRuntimeAdmission, {
396
397
  required: skillRuntimeAdmissionRequired,
397
398
  });
@@ -1264,6 +1265,12 @@ export class CLISkillLoader {
1264
1265
  */
1265
1266
  loadAll(options = {}) {
1266
1267
  this._cacheStats.descriptorScans += 1;
1268
+ // Preserve explicit layer policy for a later freshness scan. In particular,
1269
+ // refreshing an opt-in decision must not re-enable a disabled skill layer.
1270
+ this._lastLoadOptions = {
1271
+ ...options,
1272
+ ...(options.env ? { env: { ...options.env } } : {}),
1273
+ };
1267
1274
  // `cc agent --bare` (CC_SKILLS=0): no skill layer loads at all — cache the
1268
1275
  // empty result so getResolvedSkills stays cheap on every call.
1269
1276
  if (allSkillsDisabled(options)) {
@@ -1336,6 +1343,14 @@ export class CLISkillLoader {
1336
1343
  return this.loadAll();
1337
1344
  }
1338
1345
 
1346
+ /** Rescan the same layers under the options used for the current cache. */
1347
+ refreshResolvedSkills() {
1348
+ // A failed scan must not leave a previous descriptor set available as if
1349
+ // it were current on the next lookup.
1350
+ this._cache = null;
1351
+ return this.loadAll(this._lastLoadOptions ?? {});
1352
+ }
1353
+
1339
1354
  /**
1340
1355
  * Get auto-activated persona skills
1341
1356
  * @returns {object[]} skills with category "persona" and activation "auto"
@@ -1367,6 +1382,7 @@ export class CLISkillLoader {
1367
1382
  */
1368
1383
  clearCache() {
1369
1384
  this._cache = null;
1385
+ this._lastLoadOptions = null;
1370
1386
  this._bodyCache.clear();
1371
1387
  this._cacheStats = {
1372
1388
  descriptorScans: 0,
@@ -86,7 +86,9 @@ import {
86
86
  } from "../lib/session-host-snapshot.js";
87
87
  import { acquireSessionHostLease } from "../lib/session-host-lease.js";
88
88
  import {
89
+ normalizeSessionBudgetRootConfig,
89
90
  openProductionSessionBudgetRoot,
91
+ readProductionSessionBudget,
90
92
  sessionBudgetAdmissionError,
91
93
  } from "../lib/session-budget-production-root.js";
92
94
  import {
@@ -2265,9 +2267,25 @@ export function prepareReplJsonlResumeCandidate(sessionId, dependencies = {}) {
2265
2267
  error.code = "CC_REPL_SESSION_AUTHORITY_MISMATCH";
2266
2268
  throw error;
2267
2269
  }
2270
+ const declaredBudget = dataDescriptorValue(
2271
+ stateDescriptors,
2272
+ "sessionBudgetRoot",
2273
+ "REPL session host resume state",
2274
+ { required: false },
2275
+ );
2276
+ const sessionBudgetRoot =
2277
+ declaredBudget == null
2278
+ ? null
2279
+ : normalizeSessionBudgetRootConfig(
2280
+ snapshotReplMessageValue(
2281
+ declaredBudget,
2282
+ "REPL session budget declaration",
2283
+ ),
2284
+ );
2268
2285
  return brandedFrozenCandidate(REPL_JSONL_RESUME_CANDIDATES, {
2269
2286
  ok: true,
2270
2287
  sessionId,
2288
+ sessionBudgetRoot,
2271
2289
  rebuiltMessages,
2272
2290
  replayMessages: normalizedReplay.replayMessages,
2273
2291
  canonicalSystemMessages: normalizedReplay.canonicalSystemMessages,
@@ -2786,8 +2804,60 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2786
2804
  ) {
2787
2805
  leaseScope.lease = acquireHostLease(leaseSessionId, { hostKind: "repl" });
2788
2806
  }
2789
- let workspaceOptions = options;
2790
- if (options.sessionBudgetRoot?.enabled === true) {
2807
+ const injectedBudgetReader = dependencyDataFunction(
2808
+ startupDependencies,
2809
+ "readProductionSessionBudget",
2810
+ null,
2811
+ );
2812
+ const sidecar =
2813
+ leaseSessionId &&
2814
+ (process.platform === "linux" || injectedBudgetReader !== null)
2815
+ ? synchronousReplRecoveryValue(
2816
+ (injectedBudgetReader || readProductionSessionBudget)(leaseSessionId),
2817
+ "REPL budget sidecar",
2818
+ )
2819
+ : null;
2820
+ let effectiveBudgetConfig = options.sessionBudgetRoot;
2821
+ const declaredBudgetConfig = startupCandidate?.sessionBudgetRoot;
2822
+ const requestedBudgetConfig = options.sessionBudgetRoot?.enabled
2823
+ ? normalizeSessionBudgetRootConfig(options.sessionBudgetRoot)
2824
+ : null;
2825
+ if (startupCandidate?.ok === true) {
2826
+ if (declaredBudgetConfig?.enabled) {
2827
+ if (!sidecar) {
2828
+ throw Object.assign(
2829
+ new Error("budgeted REPL session is missing its budget sidecar"),
2830
+ { code: "CC_SESSION_BUDGET_SIDECAR_REQUIRED" },
2831
+ );
2832
+ }
2833
+ if (
2834
+ requestedBudgetConfig &&
2835
+ JSON.stringify(requestedBudgetConfig) !==
2836
+ JSON.stringify(declaredBudgetConfig)
2837
+ ) {
2838
+ throw Object.assign(
2839
+ new Error("REPL budget does not match its JSONL declaration"),
2840
+ { code: "CC_SESSION_BUDGET_CONFIG_MISMATCH" },
2841
+ );
2842
+ }
2843
+ effectiveBudgetConfig = declaredBudgetConfig;
2844
+ } else if (requestedBudgetConfig || sidecar) {
2845
+ throw Object.assign(
2846
+ new Error("existing REPL session has no canonical budget declaration"),
2847
+ { code: "CC_SESSION_BUDGET_JSONL_REQUIRED" },
2848
+ );
2849
+ }
2850
+ } else if (sidecar) {
2851
+ throw Object.assign(
2852
+ new Error("budget sidecar has no canonical REPL session"),
2853
+ { code: "CC_SESSION_BUDGET_JSONL_REQUIRED" },
2854
+ );
2855
+ }
2856
+ let workspaceOptions = {
2857
+ ...options,
2858
+ sessionBudgetRoot: effectiveBudgetConfig,
2859
+ };
2860
+ if (effectiveBudgetConfig?.enabled === true) {
2791
2861
  if (!leaseSessionId || !leaseScope?.lease) {
2792
2862
  throw Object.assign(
2793
2863
  new Error(
@@ -2813,10 +2883,10 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2813
2883
  "CC_REPL_STARTUP_DEPENDENCY_INVALID",
2814
2884
  );
2815
2885
  }
2816
- const root = openBudgetRoot(leaseSessionId, options.sessionBudgetRoot, {
2886
+ const root = openBudgetRoot(leaseSessionId, effectiveBudgetConfig, {
2817
2887
  persist: true,
2818
2888
  signal: combineReplSignals(options.signal, leaseScope.lease.signal),
2819
- table: options.sessionBudgetRoot.table,
2889
+ table: effectiveBudgetConfig.table,
2820
2890
  });
2821
2891
  if (
2822
2892
  root?.enabled !== true ||
@@ -2850,7 +2920,7 @@ export async function runReplStartupBoundary(options, startupDependencies) {
2850
2920
  throw admissionError;
2851
2921
  }
2852
2922
  budgetScope.root = root;
2853
- workspaceOptions = { ...options, ...root.options };
2923
+ workspaceOptions = { ...workspaceOptions, ...root.options };
2854
2924
  }
2855
2925
  const trustedWorkspaceRoot = readCwd();
2856
2926
  return enterWorkspace(trustedWorkspaceRoot, () =>
@@ -2867,11 +2937,17 @@ export function startReplJsonlSession(
2867
2937
  options = {},
2868
2938
  ) {
2869
2939
  const requireDurable =
2870
- observabilityScope != null || options.requireDurable === true;
2940
+ observabilityScope != null ||
2941
+ options.requireDurable === true ||
2942
+ options.sessionBudgetRoot?.enabled === true;
2871
2943
  try {
2944
+ const budgetDeclaration = options.sessionBudgetRoot?.enabled
2945
+ ? normalizeSessionBudgetRootConfig(options.sessionBudgetRoot)
2946
+ : null;
2872
2947
  const sessionId = startSession(requestedId, {
2873
2948
  ...meta,
2874
2949
  ...(observabilityScope != null ? { observabilityScope } : {}),
2950
+ ...(budgetDeclaration ? { sessionBudgetRoot: budgetDeclaration } : {}),
2875
2951
  });
2876
2952
  if (requireDurable && (typeof sessionId !== "string" || !sessionId)) {
2877
2953
  throw new Error("JSONL session creation returned no durable session id");
@@ -3966,7 +4042,10 @@ async function startAgentReplInWorkspaceOwned(
3966
4042
  options.sessionId || null,
3967
4043
  meta,
3968
4044
  options.observabilityScope,
3969
- { requireDurable: Boolean(_sessionBudget) },
4045
+ {
4046
+ requireDurable: Boolean(_sessionBudget),
4047
+ sessionBudgetRoot: options.sessionBudgetRoot,
4048
+ },
3970
4049
  );
3971
4050
  if (
3972
4051
  sessionId &&
@@ -9289,24 +9289,122 @@ async function executeToolInner(
9289
9289
  ? null
9290
9290
  : captureSkillDecisionRuntime(skillDecisionRuntime);
9291
9291
  if (decisionRuntime !== null && routing.candidates.length > 0) {
9292
- const decision = await decisionRuntime.suggest({
9293
- query: args.query,
9294
- candidates: routing.candidates.slice(0, 5).map((candidate) => {
9295
- const descriptor = byDigest.get(candidate.digest);
9292
+ const decisionInputsFor = (result, descriptors) => {
9293
+ const descriptorsByDigest = new Map(
9294
+ descriptors.map((skill) => [
9295
+ skill.executionIdentity?.contentDigest,
9296
+ skill,
9297
+ ]),
9298
+ );
9299
+ return result.candidates.slice(0, 5).map((candidate) => {
9300
+ const descriptor = descriptorsByDigest.get(candidate.digest);
9296
9301
  return {
9297
- id: candidate.id,
9298
- displayName: candidate.displayName,
9299
- description: descriptor?.description || candidate.displayName,
9300
- category: candidate.category,
9301
- digest: candidate.digest,
9302
- tags: Array.isArray(descriptor?.tags) ? descriptor.tags : [],
9302
+ candidate: {
9303
+ id: candidate.id,
9304
+ displayName: candidate.displayName,
9305
+ description: descriptor?.description || candidate.displayName,
9306
+ category: candidate.category,
9307
+ digest: candidate.digest,
9308
+ tags: Array.isArray(descriptor?.tags)
9309
+ ? [...descriptor.tags]
9310
+ : [],
9311
+ },
9312
+ // Scope and compatibility are local routing inputs. They never
9313
+ // leave the process, but changes invalidate the old advice.
9314
+ identity: {
9315
+ namespace: candidate.namespace,
9316
+ version: candidate.version,
9317
+ paths: descriptor?.paths ?? null,
9318
+ os: descriptor?.os ?? null,
9319
+ capabilities: descriptor?.capabilities ?? null,
9320
+ },
9303
9321
  };
9304
- }),
9322
+ });
9323
+ };
9324
+ const decisionInputs = decisionInputsFor(routing, skills);
9325
+ const snapshotFor = (result, inputs) =>
9326
+ JSON.stringify({
9327
+ candidates: inputs,
9328
+ selectedDigest: result.selected?.digest ?? null,
9329
+ conflicts: result.conflicts,
9330
+ });
9331
+ const decisionSnapshot = snapshotFor(routing, decisionInputs);
9332
+ const contextRevision = `routing-${createHash("sha256")
9333
+ .update(
9334
+ JSON.stringify({
9335
+ query: args.query,
9336
+ category: args.category ?? null,
9337
+ cwd,
9338
+ turnId: turnId || toolCallId || "list-skills",
9339
+ toolCallId: toolCallId ?? null,
9340
+ decisionSnapshot,
9341
+ }),
9342
+ )
9343
+ .digest("hex")}`;
9344
+ const decision = await decisionRuntime.suggest({
9345
+ query: args.query,
9346
+ candidates: decisionInputs.map(({ candidate }) => candidate),
9305
9347
  turnId: turnId || toolCallId || "list-skills",
9306
- contextRevision: "skill-routing-v1",
9348
+ contextRevision,
9307
9349
  signal,
9308
9350
  });
9309
- if (decision.visible) {
9351
+ signal?.throwIfAborted();
9352
+ // The provider result belongs to the captured candidate set. Recheck
9353
+ // the persisted revocations and live loader before exposing advice.
9354
+ await skillRetrievalRevocationReader?.refresh();
9355
+ signal?.throwIfAborted();
9356
+ const refreshedSkills =
9357
+ typeof skillLoader.refreshResolvedSkills === "function"
9358
+ ? await skillLoader.refreshResolvedSkills()
9359
+ : skillLoader.getResolvedSkills();
9360
+ signal?.throwIfAborted();
9361
+ let currentSkills = await filterSkillsByCwd(
9362
+ refreshedSkills.filter(skillAllowed),
9363
+ cwd,
9364
+ );
9365
+ if (args.category) {
9366
+ currentSkills = currentSkills.filter(
9367
+ (s) => s.category.toLowerCase() === args.category.toLowerCase(),
9368
+ );
9369
+ }
9370
+ // The vector index can change while the decision provider is in
9371
+ // flight. Score the refreshed corpus again before comparing routing.
9372
+ vector =
9373
+ vectorAuthority === null
9374
+ ? null
9375
+ : await vectorAuthority.score({
9376
+ query: args.query,
9377
+ skills: currentSkills,
9378
+ });
9379
+ signal?.throwIfAborted();
9380
+ const currentRouting = routeSkillDescriptors({
9381
+ skills: currentSkills,
9382
+ query: args.query,
9383
+ target: { os: process.platform },
9384
+ vectorScores: vector?.scores ?? null,
9385
+ outcomeMetrics: outcomeAuthority.metrics,
9386
+ revocationReader: skillRetrievalRevocationReader ?? null,
9387
+ topK: Math.min(64, Math.max(1, currentSkills.length)),
9388
+ });
9389
+ const currentByDigest = new Map(
9390
+ currentSkills.map((skill) => [
9391
+ skill.executionIdentity?.contentDigest,
9392
+ skill,
9393
+ ]),
9394
+ );
9395
+ const currentDecisionSnapshot = snapshotFor(
9396
+ currentRouting,
9397
+ decisionInputsFor(currentRouting, currentSkills),
9398
+ );
9399
+ routing = currentRouting;
9400
+ skills = routing.candidates
9401
+ .map(({ digest }) => currentByDigest.get(digest))
9402
+ .filter(Boolean);
9403
+ signal?.throwIfAborted();
9404
+ if (
9405
+ decision.visible &&
9406
+ currentDecisionSnapshot === decisionSnapshot
9407
+ ) {
9310
9408
  routing = Object.freeze({
9311
9409
  ...routing,
9312
9410
  decisionSuggestion: Object.freeze({