chainlesschain 0.166.103 → 0.166.105

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 (183) hide show
  1. package/README.md +14 -2
  2. package/package.json +2 -1
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-BLrXpdnP.js → AIOps-CcD-AVDw.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-C0xy9uFy.js → ActionButton-CYy-g36A.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-C6EephQP.js → Analytics-DPFDNaBL.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-ZCXdOD9y.js → AppLayout-PVIuRdgN.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-D4MfZ-pl.js → Artifacts-BXGD3bM-.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-BQ8c3bBu.js → Audit-Bu5s_4tu.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BOA7_ljM.js → BackgroundAgents-BHmgpnzo.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-60uiQjGT.js → Backup-C872jURx.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-BDvEbGs_.js → BaseInput-Bh7xO30H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-B3IkAuFQ.js → Chat-DaacK53T.js} +6 -6
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-PYrSKMM_.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-njGbfJWq.js → Checkbox-Cnt7_8I2.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-CF7DtdxC.js → Codegen-AZRz6kmW.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-C_3Zix40.js → Col-BtOXzMFt.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-CzU-ZC2d.js → Community-BFmFygS7.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-BgeqsPRh.js → Compact-2z5CfKpR.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-6l7cwiqp.js → Compliance-B5hKrPVF.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-D4s905fI.js → Cowork-CxEyp2ww.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-CGTYSxRS.js → Cron-B-8f9_qF.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-BnSo-Yt3.js → Crosschain-XARMOcc4.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Dm4vk2X1.js → DID-DwLtEKAe.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-D7H-PZ_N.js → Dashboard-Cjw0L_iA.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-COslkgxn.js → Dropdown-BBGWIP7l.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CJWEetmk.js → EmailListRenderer-DG4kZFkm.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-BbzebAWe.js → EvolutionSettings-D8vuNDde.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BsoTOXwk.js → FamilyGuardDashboard-COXCzDXI.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-Ck5kM_5H.js → Federation-Btb-aMhW.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-gShxCqtF.js → FormItemContext-TO-E6l6e.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DarN1G-z.js → GenericCardRenderer-BWXdJCT0.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CJfpLyP5.js → Git-B6icGhEv.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-CVcJmpQz.js → Governance-C5Y__K6S.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-ComU_ig8.js → Inference-DZGEKnbN.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-35hprFJy.js → KnowledgeGraph--Kga5yvm.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BQDqBTz0.js → Logs-BXs3EPKj.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-BsSiyE-A.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-B9O9OKnn.js → Marketplace-JSEAu0su.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-VMKxA4Wc.js → McpTools-BUBifkKP.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-CcBbf3Bc.js → Memory-Cb7sE4a-.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-D7myz46N.js → MobileBridge-BOzmmNV2.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-Da5KBXyx.js → MobileProjects-ClRlavdF.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-x3NPZfuL.js → Mtc-DmMWErG1.js} +5 -5
  46. package/src/assets/web-panel/assets/{MtcAudit-CwnzptEC.js → MtcAudit-DnGkRVXF.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-CCAxf4Ye.js → Multisig-C3mzascL.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-CptHKz-Q.js → NLProgramming-CrmMZZ1t.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-0uEp4DCJ.js → Notes-4S6uts9Q.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-DthvrOLd.js → NotificationSettings-gn3UrWwW.js} +1 -1
  51. package/src/assets/web-panel/assets/OrderTableRenderer-CDVAlBUf.js +1 -0
  52. package/src/assets/web-panel/assets/{Organization-BWf2tmQQ.js → Organization-fMlkdgpW.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-BbvAtEth.js → Overflow-DGwCUPU1.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-zBo6enit.js → P2P-BDGcpPmf.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-D_LMQ4A4.js → PdhVaultBrowser-CccRPAB9.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-C1ucb2ck.js → Permissions-C33dNECx.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-Bz64RddJ.js → PersonalDataHub-BeqbCStc.js} +5 -5
  58. package/src/assets/web-panel/assets/{Pipeline-DXJt3y3_.js → Pipeline-CTmasgbQ.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-DiZ8b_ut.js → Privacy-ktd37BSp.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-DfLKKrJ4.js → ProjectInit-KOGvQtBX.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-Ct6kJuIY.js → ProjectSettings-CUvH_hC6.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-BBUMlZ3m.js → Projects-BvP_Os-1.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-DYVQPHvc.js → Providers-C-hmLXlO.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-23Il7iPp.js → QrScannerModal-DzG_rbSL.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-WgOZKrv1.js → QuickAsk-ShwiATg4.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-CRIka6xT.js → Recommend-DkV8Ruep.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-DOy3GjVZ.js → RemoteSession-Ca2dOx_G.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-BXyi6xeR.js → Reputation-DYA5BJ6A.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-CoMxuIcR.js → Row-DvGDgxLS.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-87hPkoM6.js → RssFeed-DbwTBSPw.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-B1EqFCO4.js → Search-C5XAMQ9T.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-HcC41eGs.js → Security-CKWT60Qt.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-BqLwmvCM.js → Services-CZvASlWL.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-CIMmjW4r.js → Skeleton-DFkMVqws.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-CgurezJU.js → Skills-hslZ_GyP.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-CJuRuqPE.js → Sla-BLW5rmZ5.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-BUwsbd1o.js → SpeechSettings-Cn4ffCi5.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-qo04TOX-.js → SyncSettings-D1K0nJuo.js} +2 -2
  79. package/src/assets/web-panel/assets/Tasks-CCRu0xoN.js +1 -0
  80. package/src/assets/web-panel/assets/{Templates-D1PTczPW.js → Templates-L19GYuS4.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-Bi0FkEPR.js → Tenant-CZ6HE3PF.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-DfSt3bIF.js → Terminal-BiAwzaBT.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-B7bIVs4R.js → TimelineRenderer-DRHSWQT5.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-B6w5XrMb.js → Tokens-Cm-XATgh.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-XXVSDWiF.js → Trigger-BVXtx3kn.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-DYvlgcK-.js → Trust-Bb0TmkLJ.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-XyYwJMBM.js → UkeySign-Dbt9R7l0.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-C47WbtHH.js → VideoEditing-z0-Ti8eJ.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-CYfqTj_C.js → Wallet-BZh31tI-.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-3AduFn1l.js → WebAuthn-DQ0yIr5K.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-DHYEHjMJ.js → WorkflowEditor-DCt_yuWG.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-AeehErSw.js → chat-BLgtmFeS.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-DKXJ8paX.js → colors-xU4UH8M1.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-BOlCiomD.js → compact-item-C4w4v16T.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BAP6U85b.js → createContext-CZM9hAaj.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-BZUZgMkO.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-BLmeL1Xe.js → hasIn-DvHqEpE7.js} +1 -1
  98. package/src/assets/web-panel/assets/index--_-pNhk0.js +1 -0
  99. package/src/assets/web-panel/assets/{index-wyKpTsGM.js → index-55Rkd1KA.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CaHhfpBi.js → index-B0IvKEVO.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-EKNODw4P.js → index-B4jIcYSv.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-C5AKY6bJ.js → index-B64Vf-8H.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-C8qi53Ki.js → index-B6AQ2b5U.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-FPXLIXoy.js → index-BHK3CQHq.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CRa4pAaM.js → index-BHg9NoJ5.js} +3 -3
  106. package/src/assets/web-panel/assets/{index-Bn54FU3i.js → index-BIWumeQl.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CdNSmvog.js → index-BRhD9RMV.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-De2fPxdb.js → index-BTxhDCtg.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CgtaOzMo.js → index-BaQZvM4m.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CFKzWIo8.js → index-Bf8Wm4ks.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-OLKhib9T.js → index-Bh14b1k0.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B0IjlB9m.js → index-BkTKWAgq.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-PEe9OWEQ.js → index-BlN5KPq6.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DpZqbc4w.js → index-C6ZrTSw5.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Bpx54vfs.js → index-C9YnZFv2.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DsQ38fVY.js → index-CExiTobS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BTnp4Nt3.js → index-CF9ebAIB.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BuUwKmEI.js → index-CKugxsw3.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-IlzfIRNh.js → index-CPC7OGCe.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BSOenF5s.js → index-CPDw21XY.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-T28naovw.js → index-CQEzxnYS.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DWYqOI1A.js → index-CRLP56-W.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-ZMbXl5lo.js → index-CYrQnkqA.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DQLQuwpA.js → index-Cm00787d.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Co7pjF53.js → index-CpiuuIww.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-urPvyUVk.js → index-CqDiY-jl.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BhcpWl5z.js → index-CyV8fNBg.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BAS2QqLn.js → index-DGdLkss8.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BRcJlKSa.js → index-DifMvnMQ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dv8-g1pY.js → index-Ds1tM734.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DE7cbDDe.js → index-Ds6wKTMh.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Bt-FCVzh.js → index-QHmhwgfc.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-Bs2O3f81.js → index-SNV95Zen.js} +1 -1
  134. package/src/assets/web-panel/assets/index-ftwO31cp.js +1 -0
  135. package/src/assets/web-panel/assets/{index-ClpwxOXo.js → index-n6SAUmmI.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CiqeF0Pf.js → index-wYRjvvbl.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-CDWa1rnn.js → initDefaultProps-C44ZER6c.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-CX2Cnsd5.js → motion-DbNzzScd.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-Bfe1NcEF.js → move-BrRrDH2u.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-DbdqleHE.js → mtc-parser-DZ_T_Pw8.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-CE2K7NVv.js → omit-5QHHk_V3.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-y_bZgxWG.js → pickAttrs-BdxEl0G2.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-BOHR4zwr.js → placementArrow-CGyD615D.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-B8Jwq05-.js → responsiveObserve-QqcWAQmN.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-BD7RJ3Kn.js → slide-BumQrJ5R.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-BuxDJeDx.js → statusUtils-Pf64olM7.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DgYQ2Q0o.js → styleChecker-DNwm5SQZ.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-DAHF0Fwi.js → useFlexGapSupport-c3gLKs6e.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-D4kDyLFp.js → useFs-TzTd6NDM.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BHtSxW4A.js → usePersonalDataHub-BP5E_0Fe.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-CHKtdqW0.js → vnode-CVEkRfZU.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-CIZAMma9.js → zoom-C18X_8TJ.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/commands/agent.js +56 -20
  155. package/src/commands/batch.js +17 -12
  156. package/src/commands/eval.js +24 -8
  157. package/src/data/changelog.json +26 -0
  158. package/src/harness/mcp-client.js +187 -26
  159. package/src/lib/background-session-transport.js +21 -5
  160. package/src/lib/coding-task-guidance.js +13 -0
  161. package/src/lib/decision-layer/runtime.js +180 -10
  162. package/src/lib/direct-model-usage.js +34 -0
  163. package/src/lib/evolution/agent-evolution-ingress.js +63 -7
  164. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +10 -0
  165. package/src/lib/evolution/evolution-evidence-projector.js +46 -7
  166. package/src/lib/evolution/skill-decision-projection.js +211 -0
  167. package/src/lib/evolution/skill-retrieval-revocation-authority.js +17 -2
  168. package/src/lib/file-import-diagnostics.js +246 -0
  169. package/src/lib/skill-loader.js +16 -0
  170. package/src/lib/transport-error-details.js +83 -0
  171. package/src/repl/agent-repl.js +86 -7
  172. package/src/runtime/agent-core.js +248 -15
  173. package/src/runtime/headless-runner.js +295 -71
  174. package/src/runtime/headless-stream.js +197 -27
  175. package/src/runtime/mcp-config.js +26 -17
  176. package/src/runtime/mcp-tool-search.js +44 -14
  177. package/src/assets/web-panel/assets/ChatBubbleRenderer-n6BVStHf.js +0 -1
  178. package/src/assets/web-panel/assets/MarkdownRenderer-BpdGAVCC.js +0 -1
  179. package/src/assets/web-panel/assets/OrderTableRenderer-CxXuPzZg.js +0 -1
  180. package/src/assets/web-panel/assets/Tasks-QEFENVsp.js +0 -1
  181. package/src/assets/web-panel/assets/devWarning-rGldFhxo.js +0 -1
  182. package/src/assets/web-panel/assets/index-Cw-lfvvV.js +0 -1
  183. package/src/assets/web-panel/assets/index-QBXRSyoZ.js +0 -1
@@ -12,12 +12,17 @@ import {
12
12
  SKILL_DECISION_OBSERVATION_SCHEMA,
13
13
  } from "./contracts.js";
14
14
  import { captureDecisionProviderAuthority } from "./provider-authority.js";
15
+ import {
16
+ AGENT_EVOLUTION_INGRESS_FAILED_CODE,
17
+ captureAgentEvolutionIngress,
18
+ } from "../evolution/agent-evolution-ingress.js";
15
19
 
16
20
  export const SKILL_DECISION_RUNTIME_SCHEMA =
17
21
  "chainlesschain.skill-decision-runtime/v1";
18
22
 
19
23
  const RUNTIMES = new WeakSet();
20
24
  const DEFAULT_TIMEOUT_MS = 800;
25
+ const DEFAULT_BOUNDARY_TIMEOUT_MS = 30_000;
21
26
 
22
27
  function requiredText(value, label, maximum = 256) {
23
28
  if (
@@ -36,6 +41,53 @@ function timeoutSignal(signal, timeoutMs) {
36
41
  return signal ? AbortSignal.any([signal, deadline]) : deadline;
37
42
  }
38
43
 
44
+ function settleBeforeAbort(value, signal) {
45
+ const pending = Promise.resolve(value);
46
+ if (!signal) return pending;
47
+ return new Promise((resolve, reject) => {
48
+ let settled = false;
49
+ const finish = (settle, result) => {
50
+ if (settled) return;
51
+ settled = true;
52
+ signal.removeEventListener("abort", onAbort);
53
+ settle(result);
54
+ };
55
+ const onAbort = () => finish(reject, signal.reason);
56
+ pending.then(
57
+ (result) => finish(resolve, result),
58
+ (error) => finish(reject, error),
59
+ );
60
+ if (signal.aborted) {
61
+ onAbort();
62
+ return;
63
+ }
64
+ signal.addEventListener("abort", onAbort, { once: true });
65
+ });
66
+ }
67
+
68
+ async function waitForGovernedPhase(value, signal, timeoutMs, onTimeout) {
69
+ const phaseSignal = timeoutSignal(signal, timeoutMs);
70
+ try {
71
+ return await settleBeforeAbort(value, phaseSignal);
72
+ } catch (error) {
73
+ if (signal?.aborted) throw signal.reason;
74
+ if (
75
+ phaseSignal.aborted &&
76
+ phaseSignal.reason === error &&
77
+ error?.name === "TimeoutError"
78
+ ) {
79
+ throw onTimeout(error);
80
+ }
81
+ throw error;
82
+ }
83
+ }
84
+
85
+ function admissionTimeoutError(cause) {
86
+ const error = new Error("Skill decision admission timed out", { cause });
87
+ error.code = AGENT_EVOLUTION_INGRESS_FAILED_CODE;
88
+ return error;
89
+ }
90
+
39
91
  function projectUnavailable(error) {
40
92
  if (error?.name === "TimeoutError") return "provider-timeout";
41
93
  if (error?.code === "CC_DECISION_USAGE_UNKNOWN_BLOCKED") {
@@ -66,7 +118,7 @@ function asPersistenceError(error) {
66
118
  return markRuntimeLedgerPersistenceError(error);
67
119
  } catch {
68
120
  return markRuntimeLedgerPersistenceError(
69
- new Error("decision observation persistence failed", { cause: error }),
121
+ new Error("decision persistence failed", { cause: error }),
70
122
  );
71
123
  }
72
124
  }
@@ -76,12 +128,14 @@ export function createSkillDecisionRuntime({
76
128
  tenantId = null,
77
129
  sessionId = null,
78
130
  provider = null,
131
+ evolutionIngress = null,
79
132
  persist = null,
80
133
  observe = null,
81
134
  sessionBudget = null,
82
135
  initialUsageUnknown = false,
83
136
  thresholds = {},
84
137
  timeoutMs = DEFAULT_TIMEOUT_MS,
138
+ boundaryTimeoutMs = DEFAULT_BOUNDARY_TIMEOUT_MS,
85
139
  idGenerator = () => randomUUID(),
86
140
  now = () => new Date(),
87
141
  } = {}) {
@@ -95,17 +149,36 @@ export function createSkillDecisionRuntime({
95
149
  "decision timeout must be an integer from 50 to 30000 ms",
96
150
  );
97
151
  }
152
+ if (
153
+ !Number.isSafeInteger(boundaryTimeoutMs) ||
154
+ boundaryTimeoutMs < 50 ||
155
+ boundaryTimeoutMs > 30_000
156
+ ) {
157
+ throw new TypeError(
158
+ "decision boundary timeout must be an integer from 50 to 30000 ms",
159
+ );
160
+ }
98
161
  if (typeof idGenerator !== "function" || typeof now !== "function") {
99
162
  throw new TypeError("decision runtime generators must be functions");
100
163
  }
101
164
  let providerAuthority = null;
102
165
  let boundTenantId = null;
103
166
  let boundSessionId = null;
167
+ let ingress = null;
104
168
  let unknownUsageSeen = initialUsageUnknown === true;
169
+ const unsettledUsageCalls = new Set();
170
+ let usageCallInFlight = false;
171
+ let usagePersistenceFailure = null;
172
+ let observationPersistenceFailure = null;
105
173
  if (normalizedMode !== "off") {
106
174
  providerAuthority = captureDecisionProviderAuthority(provider);
107
175
  boundTenantId = requiredText(tenantId, "tenantId");
108
176
  boundSessionId = requiredText(sessionId, "sessionId");
177
+ if (evolutionIngress !== null) {
178
+ ingress = captureAgentEvolutionIngress(evolutionIngress, {
179
+ tenantId: boundTenantId,
180
+ });
181
+ }
109
182
  if (typeof persist !== "function" || typeof observe !== "function") {
110
183
  throw new TypeError(
111
184
  "enabled decision runtime requires usage and observation persistence",
@@ -118,13 +191,33 @@ export function createSkillDecisionRuntime({
118
191
  try {
119
192
  await observe(observation);
120
193
  } catch (error) {
121
- throw asPersistenceError(error);
194
+ observationPersistenceFailure ??= asPersistenceError(error);
195
+ throw observationPersistenceFailure;
122
196
  }
123
197
  };
124
198
 
125
199
  const persistDecisionUsage = async (type, event) => {
126
- await persist(type, event);
127
- if (type === "model_usage_unknown") unknownUsageSeen = true;
200
+ if (usagePersistenceFailure !== null) throw usagePersistenceFailure;
201
+ try {
202
+ await waitForGovernedPhase(
203
+ persist(type, event),
204
+ null,
205
+ boundaryTimeoutMs,
206
+ (cause) =>
207
+ new Error("Skill decision usage persistence timed out", { cause }),
208
+ );
209
+ } catch (error) {
210
+ usagePersistenceFailure ??= asPersistenceError(error);
211
+ throw usagePersistenceFailure;
212
+ }
213
+ if (type === "model_usage_started") {
214
+ unsettledUsageCalls.add(event.callId);
215
+ } else if (type === "model_usage_unknown") {
216
+ unknownUsageSeen = true;
217
+ unsettledUsageCalls.delete(event.callId);
218
+ } else if (type === "token_usage") {
219
+ unsettledUsageCalls.delete(event.callId);
220
+ }
128
221
  };
129
222
 
130
223
  const runtime = Object.freeze({
@@ -148,6 +241,10 @@ export function createSkillDecisionRuntime({
148
241
  });
149
242
  }
150
243
  if (signal?.aborted) throw signal.reason;
244
+ if (usagePersistenceFailure !== null) throw usagePersistenceFailure;
245
+ if (observationPersistenceFailure !== null) {
246
+ throw observationPersistenceFailure;
247
+ }
151
248
  const decisionId = `skill-${requiredText(idGenerator(), "decisionId")}`;
152
249
  const request = createSkillDecisionRequest({
153
250
  decisionId,
@@ -167,11 +264,41 @@ export function createSkillDecisionRuntime({
167
264
  }
168
265
  let outcome;
169
266
  let requestSignal = null;
170
- if (unknownUsageSeen) {
267
+ let admissionReceipt = null;
268
+ if (unknownUsageSeen || unsettledUsageCalls.size > 0) {
171
269
  outcome = unavailableOutcome("provider-usage-unknown-blocked");
172
270
  } else {
173
271
  try {
174
- const { result: raw, settlement } = await runMeteredDirectModelCall({
272
+ // Admission is not provider work: a refusal must not create an
273
+ // unknown-charge ledger row or consume the provider's budget turn.
274
+ const admission =
275
+ ingress === null
276
+ ? null
277
+ : await waitForGovernedPhase(
278
+ ingress.prepareSkillDecisionRequest(request),
279
+ signal,
280
+ boundaryTimeoutMs,
281
+ admissionTimeoutError,
282
+ );
283
+ signal?.throwIfAborted();
284
+ admissionReceipt = admission?.receipt ?? null;
285
+ // Keep local content identities out of the provider capability. Only
286
+ // the authenticated projection is available for governed dispatch.
287
+ const providerRequest =
288
+ admission === null
289
+ ? request
290
+ : Object.freeze({ payload: admission.payload });
291
+ if (
292
+ unknownUsageSeen ||
293
+ unsettledUsageCalls.size > 0 ||
294
+ usageCallInFlight
295
+ ) {
296
+ const error = new Error("decision usage recovery is required");
297
+ error.code = "CC_DECISION_USAGE_UNKNOWN_BLOCKED";
298
+ throw error;
299
+ }
300
+ usageCallInFlight = true;
301
+ const meteredCall = runMeteredDirectModelCall({
175
302
  sessionId: boundSessionId,
176
303
  persist: persistDecisionUsage,
177
304
  provider: providerAuthority.provider,
@@ -180,18 +307,36 @@ export function createSkillDecisionRuntime({
180
307
  operationId: `decision:${decisionId}`,
181
308
  sessionBudget,
182
309
  includeSettlement: true,
310
+ dispatchSignal: signal ?? null,
183
311
  call: () => {
184
312
  if (unknownUsageSeen) {
185
313
  const error = new Error("decision usage recovery is required");
186
314
  error.code = "CC_DECISION_USAGE_UNKNOWN_BLOCKED";
187
315
  throw error;
188
316
  }
317
+ // timeoutMs remains the provider-call deadline. Admission uses
318
+ // its own authority lifecycle and checks caller cancellation.
189
319
  requestSignal = timeoutSignal(signal, timeoutMs);
190
- return providerAuthority.decide(request, {
191
- signal: requestSignal,
192
- });
320
+ requestSignal.throwIfAborted();
321
+ return settleBeforeAbort(
322
+ providerAuthority.decide(providerRequest, {
323
+ signal: requestSignal,
324
+ }),
325
+ requestSignal,
326
+ );
193
327
  },
194
328
  });
329
+ let meteredResult;
330
+ try {
331
+ meteredResult = await meteredCall;
332
+ } finally {
333
+ usageCallInFlight = false;
334
+ }
335
+ const { result: raw, settlement } = meteredResult;
336
+ // A provider may resolve successfully after ignoring cancellation or
337
+ // its deadline. Usage is settled above; the late answer is unusable.
338
+ signal?.throwIfAborted();
339
+ requestSignal?.throwIfAborted();
195
340
  if (settlement !== "known") {
196
341
  // The metered call already persisted model_usage_unknown. Without a
197
342
  // budget root it may return the answer; never expose that answer.
@@ -254,6 +399,7 @@ export function createSkillDecisionRuntime({
254
399
  reasonCode: outcome.reasonCode,
255
400
  selectedDigest: outcome.selectedDigest,
256
401
  resultDigest: outcome.resultDigest,
402
+ ...(admissionReceipt === null ? {} : { admissionReceipt }),
257
403
  startedAt: startedAt.toISOString(),
258
404
  finishedAt: finishedAt.toISOString(),
259
405
  latencyMs: Math.max(0, finishedAt.getTime() - startedAt.getTime()),
@@ -265,7 +411,31 @@ export function createSkillDecisionRuntime({
265
411
  observationCore,
266
412
  ),
267
413
  });
268
- await writeObservation(observation);
414
+ try {
415
+ await waitForGovernedPhase(
416
+ writeObservation(observation),
417
+ signal,
418
+ boundaryTimeoutMs,
419
+ (cause) =>
420
+ asPersistenceError(
421
+ new Error("Skill decision observation timed out", { cause }),
422
+ ),
423
+ );
424
+ } catch (error) {
425
+ if (error?.runtimeLedgerPersistence === true) {
426
+ observationPersistenceFailure ??= error;
427
+ } else if (signal?.aborted) {
428
+ // The caller stops waiting before the durable observer acknowledges
429
+ // the write. Its eventual outcome is unknown to this runtime.
430
+ observationPersistenceFailure ??= asPersistenceError(
431
+ new Error("Skill decision observation completion is unknown", {
432
+ cause: error,
433
+ }),
434
+ );
435
+ }
436
+ throw error;
437
+ }
438
+ signal?.throwIfAborted();
269
439
  return Object.freeze({
270
440
  schema: SKILL_DECISION_OBSERVATION_SCHEMA,
271
441
  decisionId,
@@ -54,6 +54,8 @@ function unknownUsageEvent({
54
54
  * Meter one direct provider call that runs outside agentLoop. The started row is
55
55
  * durable before the callback resumes into provider work; every outcome then
56
56
  * settles known or unknown without persisting prompts, responses, or errors.
57
+ * A dispatch signal closes an acknowledged, canceled pre-dispatch intent with
58
+ * locally proven zero usage; cancellation after dispatch remains unknown.
57
59
  * Opt-in includeSettlement returns that persisted status with the raw result.
58
60
  */
59
61
  export async function runMeteredDirectModelCall({
@@ -65,11 +67,20 @@ export async function runMeteredDirectModelCall({
65
67
  operationId = undefined,
66
68
  sessionBudget = null,
67
69
  includeSettlement = false,
70
+ dispatchSignal = null,
68
71
  call,
69
72
  }) {
70
73
  if (typeof call !== "function") {
71
74
  throw new TypeError("metered direct model call requires a callback");
72
75
  }
76
+ if (
77
+ dispatchSignal !== null &&
78
+ (typeof dispatchSignal.aborted !== "boolean" ||
79
+ typeof dispatchSignal.throwIfAborted !== "function")
80
+ ) {
81
+ throw new TypeError("metered direct model dispatch signal is invalid");
82
+ }
83
+ dispatchSignal?.throwIfAborted();
73
84
  const callId = `direct-${randomUUID()}`;
74
85
  if (typeof sessionBudget?.consumeTurn === "function") {
75
86
  const admission = sessionBudget.consumeTurn({ id: callId });
@@ -84,6 +95,7 @@ export async function runMeteredDirectModelCall({
84
95
  if (includeSettlement) {
85
96
  throw new TypeError("direct model settlement requires a session");
86
97
  }
98
+ dispatchSignal?.throwIfAborted();
87
99
  return call();
88
100
  }
89
101
  if (typeof persist !== "function") {
@@ -102,6 +114,28 @@ export async function runMeteredDirectModelCall({
102
114
  { callId, provider, model, source, operationId },
103
115
  `direct model call ${source}`,
104
116
  );
117
+ if (dispatchSignal?.aborted) {
118
+ // A durable intent exists, but no provider callback has run. Close it as
119
+ // a locally proven zero-charge call instead of inventing unknown usage.
120
+ const notDispatched = {
121
+ ...projectRuntimeTokenUsage({
122
+ callId,
123
+ provider,
124
+ model,
125
+ source,
126
+ operationId,
127
+ usage: { input_tokens: 0, output_tokens: 0 },
128
+ }),
129
+ settlementOrigin: "local-no-dispatch",
130
+ };
131
+ await persistUsageEvent(persist, "token_usage", notDispatched);
132
+ recordSessionBudgetUsage(
133
+ sessionBudget,
134
+ { callId, usage: null },
135
+ `direct model call ${source} canceled before dispatch`,
136
+ );
137
+ throw dispatchSignal.reason;
138
+ }
105
139
  let result;
106
140
  let settlement = "unknown";
107
141
  const returnSettled = (status) =>
@@ -5,6 +5,7 @@ import evolutionRun from "@chainlesschain/session-core/evolution-run";
5
5
  import { EvolutionEvidenceArtifactAdapter } from "./evolution-evidence-artifact-adapter.js";
6
6
  import {
7
7
  EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST,
8
+ EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST,
8
9
  EVOLUTION_PROJECTION_RULESET_DIGEST,
9
10
  } from "./evolution-evidence-projector.js";
10
11
  import { EvolutionRunLedgerAdapter } from "./evolution-run-ledger-adapter.js";
@@ -15,6 +16,10 @@ import {
15
16
  snapshotAgentModelRequest,
16
17
  buildAgentModelRequest,
17
18
  } from "./agent-model-projection.js";
19
+ import {
20
+ snapshotSkillDecisionRequest,
21
+ buildSkillDecisionPayload,
22
+ } from "./skill-decision-projection.js";
18
23
 
19
24
  const { EVOLUTION_RUN_EVENT_SCHEMA, EVENT_TYPES } = evolutionRun;
20
25
 
@@ -265,6 +270,7 @@ export function createAgentEvolutionIngress({
265
270
  options = {},
266
271
  modelRequest = null,
267
272
  cacheRecord = false,
273
+ skillDecisionRequest = null,
268
274
  ) =>
269
275
  guardIngress(
270
276
  serialize(async () => {
@@ -304,7 +310,7 @@ export function createAgentEvolutionIngress({
304
310
  ),
305
311
  responseEvent: clone(lastModelResponse.event),
306
312
  }
307
- : (modelRequest ?? clone(evidence));
313
+ : (skillDecisionRequest?.payload ?? modelRequest ?? clone(evidence));
308
314
  const sourceEnvelope = await issueSourceEnvelope(
309
315
  Object.freeze({
310
316
  schema: AGENT_EVOLUTION_INGRESS_SCHEMA,
@@ -321,12 +327,17 @@ export function createAgentEvolutionIngress({
321
327
  payload,
322
328
  };
323
329
  const persisted =
324
- modelRequest === null
325
- ? await evidenceAdapter.projectAndPersist(projectionInput)
326
- : await EvolutionEvidenceArtifactAdapter.prototype.projectAndPersistAgentModelRequest.call(
330
+ skillDecisionRequest !== null
331
+ ? await EvolutionEvidenceArtifactAdapter.prototype.projectAndPersistSkillDecisionRequest.call(
327
332
  evidenceAdapter,
328
333
  projectionInput,
329
- );
334
+ )
335
+ : modelRequest === null
336
+ ? await evidenceAdapter.projectAndPersist(projectionInput)
337
+ : await EvolutionEvidenceArtifactAdapter.prototype.projectAndPersistAgentModelRequest.call(
338
+ evidenceAdapter,
339
+ projectionInput,
340
+ );
330
341
  if (
331
342
  persisted?.tenantId !== descriptor.tenantId ||
332
343
  persisted.evidenceId == null ||
@@ -353,6 +364,13 @@ export function createAgentEvolutionIngress({
353
364
  evidenceKind: kind,
354
365
  occurredAt,
355
366
  derivationManifestDigest: persisted.manifest.digest,
367
+ ...(skillDecisionRequest === null
368
+ ? {}
369
+ : {
370
+ decisionId: skillDecisionRequest.decisionId,
371
+ requestDigest: skillDecisionRequest.requestDigest,
372
+ candidateSetDigest: skillDecisionRequest.candidateSetDigest,
373
+ }),
356
374
  },
357
375
  });
358
376
  if (kind === "model-input") {
@@ -370,7 +388,8 @@ export function createAgentEvolutionIngress({
370
388
  eventId,
371
389
  artifact: persisted,
372
390
  });
373
- if (modelRequest === null) return appended;
391
+ if (modelRequest === null && skillDecisionRequest === null)
392
+ return appended;
374
393
  // Resolve the published manifest and every attested component afresh.
375
394
  // The prototype call requires the real adapter's private state; an
376
395
  // instance-shaped facade cannot mint model-visible input.
@@ -383,7 +402,9 @@ export function createAgentEvolutionIngress({
383
402
  resolved.verification.verified !== true ||
384
403
  resolved.verification.tenantId !== descriptor.tenantId ||
385
404
  resolved.bundle.modelProjection.rulesetDigest !==
386
- EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST ||
405
+ (skillDecisionRequest === null
406
+ ? EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST
407
+ : EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST) ||
387
408
  resolved.bundle.modelProjection.evidenceId !== persisted.evidenceId
388
409
  ) {
389
410
  throw new Error("Agent model projection readback is unbound");
@@ -396,6 +417,26 @@ export function createAgentEvolutionIngress({
396
417
  throw new Error("Agent model projection Run frontier changed");
397
418
  }
398
419
  if (admissionFailure !== null) throw admissionFailure;
420
+ if (skillDecisionRequest !== null) {
421
+ return Object.freeze({
422
+ payload: buildSkillDecisionPayload(
423
+ skillDecisionRequest.payload,
424
+ resolved.bundle.modelProjection,
425
+ ),
426
+ receipt: Object.freeze({
427
+ tenantId: descriptor.tenantId,
428
+ runId: descriptor.runId,
429
+ eventId,
430
+ evidenceId: persisted.evidenceId,
431
+ requestDigest: skillDecisionRequest.requestDigest,
432
+ candidateSetDigest: skillDecisionRequest.candidateSetDigest,
433
+ manifestDigest: persisted.manifest.digest,
434
+ projectionDigest:
435
+ resolved.bundle.modelProjection.projectionDigest,
436
+ rulesetDigest: resolved.bundle.modelProjection.rulesetDigest,
437
+ }),
438
+ });
439
+ }
399
440
  return buildAgentModelRequest(
400
441
  modelRequest,
401
442
  resolved.bundle.modelProjection,
@@ -522,6 +563,21 @@ export function createAgentEvolutionIngress({
522
563
  return response;
523
564
  },
524
565
  start: () => guardIngress(serialize(() => appendStarted())),
566
+ prepareSkillDecisionRequest: (request) => {
567
+ if (admissionFailure !== null) return Promise.reject(admissionFailure);
568
+ let snapshot;
569
+ try {
570
+ snapshot = snapshotSkillDecisionRequest(request);
571
+ if (snapshot.binding.tenantId !== descriptor.tenantId)
572
+ throw new TypeError(
573
+ "Skill decision request belongs to another tenant",
574
+ );
575
+ } catch (error) {
576
+ admissionFailure = ingressFailure(error);
577
+ return Promise.reject(admissionFailure);
578
+ }
579
+ return ingest("skill-decision-input", null, {}, null, false, snapshot);
580
+ },
525
581
  prepareModelRequest: (request) => {
526
582
  if (admissionFailure !== null) return Promise.reject(admissionFailure);
527
583
  let snapshot;
@@ -340,6 +340,7 @@ export class EvolutionEvidenceArtifactAdapter {
340
340
  #ttlMs;
341
341
  #project;
342
342
  #projectAgentModel;
343
+ #projectSkillDecision;
343
344
  #verify;
344
345
  #ports;
345
346
  #resolveManifestRef;
@@ -385,6 +386,11 @@ export class EvolutionEvidenceArtifactAdapter {
385
386
  "projectAgentModelRequest",
386
387
  "projector",
387
388
  );
389
+ this.#projectSkillDecision = captureMethod(
390
+ projector,
391
+ "projectSkillDecisionRequest",
392
+ "projector",
393
+ );
388
394
  this.#verify = captureMethod(bundleVerifier, "verify", "bundleVerifier");
389
395
  this.#ports = artifactPorts;
390
396
  this.#resolveManifestRef =
@@ -411,6 +417,10 @@ export class EvolutionEvidenceArtifactAdapter {
411
417
  return this.#persist(await this.#projectAgentModel(input));
412
418
  }
413
419
 
420
+ async projectAndPersistSkillDecisionRequest(input) {
421
+ return this.#persist(await this.#projectSkillDecision(input));
422
+ }
423
+
414
424
  async #persist(bundle) {
415
425
  const verification = await this.#verify(bundle);
416
426
  if (
@@ -14,6 +14,7 @@ import {
14
14
  projectAgentOpaqueTransportBlock,
15
15
  snapshotAgentModelRequest,
16
16
  } from "./agent-model-projection.js";
17
+ import { snapshotSkillDecisionPayload } from "./skill-decision-projection.js";
17
18
  import {
18
19
  createEvidenceJsonTextBoundary,
19
20
  EVIDENCE_JSON_POLICY,
@@ -523,7 +524,24 @@ export const EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST = `sha256:${createH
523
524
  "utf8",
524
525
  )
525
526
  .digest("hex")}`;
527
+ const SKILL_DECISION_RULESET = Object.freeze({
528
+ ...AGENT_MODEL_RULESET_V2,
529
+ schema: "chainlesschain.evolution-skill-decision-projection-rules/v1",
530
+ version: 1,
531
+ protocol: "skill-routing-state-and-questions/v1",
532
+ maxInputBytes: 256 * 1024,
533
+ });
534
+ export const EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST = `sha256:${createHash(
535
+ "sha256",
536
+ )
537
+ .update(
538
+ `${SKILL_DECISION_RULESET.schema}\0${JSON.stringify(SKILL_DECISION_RULESET)}`,
539
+ "utf8",
540
+ )
541
+ .digest("hex")}`;
542
+
526
543
  const SUPPORTED_RULESETS = new Map([
544
+ [EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST, SKILL_DECISION_RULESET],
527
545
  [RULESET_DIGEST, RULESET],
528
546
  [RAW_RULESET_V3_DIGEST, RAW_RULESET_V3],
529
547
  [AGENT_MODEL_RULESET_V1_DIGEST, AGENT_MODEL_RULESET_V1],
@@ -601,7 +619,8 @@ const AGENT_VALUE_PATTERNS = Object.freeze([
601
619
  ]);
602
620
 
603
621
  function valuePatterns(ruleset) {
604
- return ruleset === AGENT_MODEL_RULESET ||
622
+ return ruleset === SKILL_DECISION_RULESET ||
623
+ ruleset === AGENT_MODEL_RULESET ||
605
624
  ruleset === AGENT_MODEL_RULESET_V2 ||
606
625
  ruleset === AGENT_MODEL_RULESET_V1
607
626
  ? AGENT_VALUE_PATTERNS
@@ -3101,6 +3120,18 @@ function validateBundleIntegrity(bundle) {
3101
3120
  );
3102
3121
  }
3103
3122
  if (model.content !== null) {
3123
+ if (ruleset === SKILL_DECISION_RULESET) {
3124
+ snapshotSkillDecisionPayload(model.content);
3125
+ if (
3126
+ model.truncated ||
3127
+ (model.redactionSummary.byType["content-truncation"] ?? 0) > 0
3128
+ ) {
3129
+ throw projectionError(
3130
+ EVOLUTION_PROJECTION_INVALID_CODE,
3131
+ "Skill decision projection cannot be truncated",
3132
+ );
3133
+ }
3134
+ }
3104
3135
  if (
3105
3136
  ruleset === AGENT_MODEL_RULESET ||
3106
3137
  ruleset === AGENT_MODEL_RULESET_V2 ||
@@ -3368,6 +3399,10 @@ export class EvolutionEvidenceProjector {
3368
3399
  return this.#project(input, AGENT_MODEL_RULESET);
3369
3400
  }
3370
3401
 
3402
+ projectSkillDecisionRequest(input) {
3403
+ return this.#project(input, SKILL_DECISION_RULESET);
3404
+ }
3405
+
3371
3406
  async #project(input, ruleset) {
3372
3407
  assertExactRecord(input, INPUT_KEYS, "evidence projection input");
3373
3408
  if (
@@ -3381,9 +3416,11 @@ export class EvolutionEvidenceProjector {
3381
3416
  );
3382
3417
  }
3383
3418
  const payload =
3384
- ruleset === AGENT_MODEL_RULESET
3385
- ? snapshotAgentModelRequest(input.payload)
3386
- : cloneCanonical(input.payload);
3419
+ ruleset === SKILL_DECISION_RULESET
3420
+ ? snapshotSkillDecisionPayload(input.payload)
3421
+ : ruleset === AGENT_MODEL_RULESET
3422
+ ? snapshotAgentModelRequest(input.payload)
3423
+ : cloneCanonical(input.payload);
3387
3424
  const sourceInputDigest = digest(
3388
3425
  payload,
3389
3426
  "chainlesschain.evolution-raw-plaintext/v2",
@@ -3627,9 +3664,11 @@ export class EvolutionEvidenceProjector {
3627
3664
  const modelProjection = buildModelProjection(
3628
3665
  rawRecord,
3629
3666
  sanitized,
3630
- ruleset === AGENT_MODEL_RULESET
3631
- ? EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST
3632
- : RAW_RULESET_V3_DIGEST,
3667
+ ruleset === SKILL_DECISION_RULESET
3668
+ ? EVOLUTION_SKILL_DECISION_PROJECTION_RULESET_DIGEST
3669
+ : ruleset === AGENT_MODEL_RULESET
3670
+ ? EVOLUTION_AGENT_MODEL_PROJECTION_RULESET_DIGEST
3671
+ : RAW_RULESET_V3_DIGEST,
3633
3672
  );
3634
3673
  const trustedSanitized = source.compilable
3635
3674
  ? sanitizePayload(