chainlesschain 0.166.45 → 0.166.47

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 (230) hide show
  1. package/README.md +6 -4
  2. package/package.json +3 -1
  3. package/src/assets/web-panel/assets/{AIOps-CrMhYNeH.js → AIOps-CDpT0WXV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DLY5blZ3.js → ActionButton-BNfUljWx.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-C0NhKQB0.js → Analytics-C9323DEx.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DLyepgMq.js → AppLayout-CRrjb2db.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-Depk01Sa.js → Artifacts-Db-DGqJS.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-jKe6NQ3J.js → Audit-CJdHRJ0W.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-CG4GKT9s.js → BackgroundAgents-_RSCnTYs.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DYWCkzwM.js → Backup-8GFQi-dM.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-xm5vodmJ.js → BaseInput-C87NtgUZ.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-B20_PNd3.js → Chat-DcTMWS8G.js} +5 -5
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-D4d-eh0F.js → ChatBubbleRenderer-5ZDPTW_H.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CfgwkV_r.js → Checkbox-DM9EmYee.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-DbnoWI44.js → Codegen-M2YqDRNN.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CfzzhgGw.js → Col-oB1LLxyf.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BU7mGM8p.js → Community-TcVZK5tF.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DpYim0an.js → Compact-XKmq-3K_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BNJcO6ra.js → Compliance-C6CBiDRy.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-CecZ-pWj.js → Cowork-Dz9CGqXH.js} +4 -4
  21. package/src/assets/web-panel/assets/{Cron-BG_C1fe3.js → Cron-CxJDgDVL.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BAeILQLL.js → Crosschain-DMP0JsaQ.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-CYqOEkXi.js → DID-I5IQeogb.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-BYU8wmL1.js → Dashboard-BfLW2MXj.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DYlJeTTa.js → Dropdown-BK8EjAlh.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-C0M6py3K.js → EmailListRenderer-C3iqSv5I.js} +1 -1
  27. package/src/assets/web-panel/assets/EvolutionSettings-BJmBzpW6.js +1 -0
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-Du90iDch.js → FamilyGuardDashboard-T19rnqm-.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DkFp_hzB.js → Federation-CozrbPMy.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BGgtGTfG.js → FormItemContext-k1dCLNNJ.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-BIDsSCei.js → GenericCardRenderer-DbaW_Jvt.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git--d3Te93J.js → Git-CQHRyEIt.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-CZwD0vmO.js → Governance-BMJYZgMy.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-DiDcSFEW.js → Inference-B7mpNU7V.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Bi0hSfzg.js → KnowledgeGraph-Br0Blg6B.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-C8NHSc0K.js → Logs-HIdk0S6M.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-DfDuADqF.js → MarkdownRenderer-HxeocBHy.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-D6kD0bNf.js → Marketplace-Bkfq69UP.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-mTSamCr5.js → McpTools-dtmx0bHO.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-YQBXfLjU.js → Memory-CrRlOuEf.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-D2awuupp.js → MobileBridge-D4ZIZiAH.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-Bl7lZc_-.js → MobileProjects-C90Rw7GE.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-CBqjLK2Q.js → Mtc-CC6k_uh4.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-C4poRInZ.js → MtcAudit-T_CUIYUy.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-C_J0zXYZ.js → Multisig-N_e0RRWk.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Ci43sWOU.js → NLProgramming-Iq3VzEv0.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-DN7A-GFZ.js → Notes-B5sKJUJt.js} +4 -4
  48. package/src/assets/web-panel/assets/{NotificationSettings-D5jNm71V.js → NotificationSettings-K8UVp8qQ.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-C9UsPOG8.js → OrderTableRenderer-BWNyM-6m.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-BapYMJrP.js → Organization-DMO1NJxI.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-BwxwjhFw.js → Overflow-BBY6b31Z.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-TObXXOu3.js → P2P-DmsL0y0v.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-UwJvMbFO.js → PdhVaultBrowser-C65AX_RY.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-Damnxv-O.js → Permissions-CWcpRtWa.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-d_7jlBb-.js → PersonalDataHub--ZNDIdsn.js} +3 -3
  56. package/src/assets/web-panel/assets/{Pipeline-DA87VjLw.js → Pipeline-DCdj5NGb.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DzO_UvWn.js → Privacy-BRKu7Q3E.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-CwqECDhs.js → ProjectInit-BJ7ozIAb.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CT5YdIl6.js → ProjectSettings-B2cVLiXW.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-D3KdZPCv.js → Projects-Ce9r_W-p.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-BjBIS8kk.js → Providers-BDespvGZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-mMFqiKkI.js → QrScannerModal-BUNO4fDU.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BwGi9ZU1.js → QuickAsk-D2TUqeBV.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-BY65SR5s.js → Recommend-BNNQPHBH.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-BGsVH_XU.js → RemoteSession-D2t1V_Cm.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-DpPbxSgI.js → Reputation-DVK5hEqI.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-B5YBYoVz.js → Row-BB9RTwmE.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-DvM5jaoh.js → RssFeed-DmEO7liZ.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-6RX39EPO.js → Search-UwEYjnDB.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Beal-7cM.js → Security-DebqSsMp.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-D6rjVLe-.js → Services-CuuPts3C.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-Dur7R7vz.js → Skeleton-DbWpmbgf.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-D1QkGwMk.js → Skills-BaK7SxzT.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-3ObrX8Es.js → Sla-BFHv51Jb.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DG_KAiH1.js → SpeechSettings-B9b1R6uK.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-BaHq0SJ2.js → SyncSettings-BnO0CN02.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-BDpAkBAB.js → Tasks-Dy01Vq49.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-tYdoRd-c.js → Templates-BCy2AMHc.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-yGS5zIQ1.js → Tenant-4Tx9y_Y1.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-B3QbmVFa.js → Terminal-CPOoUH8J.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-B_7Pr5dS.js → TimelineRenderer-C85GlJzu.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DPvpDa53.js → Tokens-_xneFBGF.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-DcSAD0GZ.js → Trigger-BizG4rTr.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-BBzEq-uy.js → Trust-Dh8EIn3n.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-Dssav7-5.js → UkeySign-C5NWoHWQ.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CPOZf4yi.js → VideoEditing-0YfrySL6.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BzBVRXEq.js → Wallet-BZVH8Ags.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DdSGaCCh.js → WebAuthn-kNofa6vm.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-CKjBFoSs.js → WorkflowEditor-BNO34_j0.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-B9aMGLg0.js → chat-ifQ2FlwD.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-GFCMctSV.js → colors-1TL8H6EH.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-BqSDFAYJ.js → compact-item-DBfRjs_T.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BwUm3Ffp.js → createContext-Bs6V4iBf.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BHnbnuYL.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DN1kMJOT.js → hasIn-Bz6LMY_0.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B4dI_csl.js → index--WvT2Il3.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B7Yu5wwV.js → index-84mi5rsv.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DAK28GY8.js → index-B5SJCUZ7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CmEHzOsw.js → index-B6RClMZl.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-By9GbJhu.js → index-BB1Af7Fv.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-7W1e9Vi4.js → index-BQL_tldd.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BDL2MwxN.js → index-BXJXMaZe.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CEMl-Qte.js → index-BhPDhmvN.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BCnbC2z7.js → index-BiZ7Nz5-.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cwih67e8.js → index-BoCTDcn6.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DI90Wo_P.js → index-BxqC3LiO.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-THsr_UUJ.js → index-C6c0j-Ki.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-PKwZkQDC.js → index-C7d6QlfR.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-rCTtYCGu.js → index-C9QYxq61.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Ci0zIpSX.js → index-CBh48_Mh.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-tMF_deZb.js → index-CNKHoXWI.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B9Pe-EyB.js → index-CXVBQ82k.js} +3 -3
  113. package/src/assets/web-panel/assets/{index-B4MfxM5W.js → index-CXtiLagi.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DaFr7XB1.js → index-Cn7EIWmI.js} +1 -1
  115. package/src/assets/web-panel/assets/index-CsRz9-ZH.js +1 -0
  116. package/src/assets/web-panel/assets/{index-D8jcp9Rj.js → index-CtB45Hok.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cli0VREv.js → index-Cu_V6ZJH.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DO-IKERp.js → index-Cykt9BbN.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-y7cfh9rP.js → index-DDhy4eCS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CH2wK5dx.js → index-DEPPzM8I.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BeycSy7n.js → index-DMVktlxG.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BwssFWI1.js → index-DOww5Qny.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-PJt1Zrx2.js → index-DWPp-d07.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DnjMFf3m.js +1 -0
  125. package/src/assets/web-panel/assets/{index-BPGlcDvs.js → index-DvM2xBUA.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BL_0ndQc.js → index-EK3XtMxA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DnJt9DaX.js → index-XJIV7HEw.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BpiR6uzH.js → index-cNwsev6n.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-bwb4cg_D.js → index-f_7L9GvP.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DbDhKroq.js → index-lUL3bD2z.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DvvJWwT8.js → index-pOUFfRdN.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BBMW8bQO.js → index-rGhS02f8.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CRikhGgx.js → index-vHtnQsnV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BHUxz4dy.js → index-vb1XIxEB.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-B6e5CZ5p.js → initDefaultProps-DpRIjwTt.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-riMM1KfJ.js → motion-CG4_9o-Z.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-C1NSDMjc.js → move-Dku1fXtf.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-Bibuny9B.js → mtc-parser-BvAVeX-3.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-Cmf6FoRn.js → omit-DFu0x78D.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-BSsYBUCY.js → pickAttrs-CmI-FNcK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-gSB7-GAS.js → placementArrow-CGERYewB.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-qaqttvKa.js → responsiveObserve-D7G8bF5J.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-RX2gxl3k.js → slide-B2sXwWXy.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-DEJmkhqw.js → statusUtils-DI5yh0n3.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CIWmJ78O.js → styleChecker-deGJ5dp_.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-D-UPSQBE.js → useFlexGapSupport-OqHBXEmi.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-9QFtqADc.js → useFs-Dqgx0Qv_.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-DM6kyift.js → usePersonalDataHub-Be-lveh2.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-C-q6FNSJ.js → vnode-CFxB876W.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-D4WJY2m8.js → zoom-BFOYrl4e.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +3 -3
  153. package/src/commands/agent.js +11 -7
  154. package/src/commands/background-session.js +31 -3
  155. package/src/commands/chat.js +5 -1
  156. package/src/commands/eval.js +86 -28
  157. package/src/commands/llm.js +104 -1
  158. package/src/commands/plugin.js +130 -1
  159. package/src/commands/sandbox.js +121 -2
  160. package/src/commands/serve.js +11 -2
  161. package/src/commands/skill.js +29 -0
  162. package/src/data/changelog.json +26 -0
  163. package/src/gateways/ws/session-protocol.js +13 -0
  164. package/src/gateways/ws/ws-agent-handler.js +17 -0
  165. package/src/gateways/ws/ws-server.js +3 -0
  166. package/src/gateways/ws/ws-session-state.js +131 -0
  167. package/src/generated/cc-agent-protocol.schema.json +4 -0
  168. package/src/lazy-dispatch.js +8 -1
  169. package/src/lib/agent-sandbox.js +337 -53
  170. package/src/lib/app-server/cli-agent-kernel-adapter.js +28 -0
  171. package/src/lib/app-server/server.js +93 -0
  172. package/src/lib/background-agent-keeper-client.js +2 -0
  173. package/src/lib/background-agent-keeper-protocol.js +7 -0
  174. package/src/lib/background-agent-supervisor.js +194 -47
  175. package/src/lib/codex-app-server-adapter.js +81 -27
  176. package/src/lib/context-memory-kernel/durable-memory-port.js +31 -8
  177. package/src/lib/context-memory-kernel/provider-context.js +53 -19
  178. package/src/lib/deferred-question-context.js +122 -0
  179. package/src/lib/eval/evidence.js +202 -64
  180. package/src/lib/eval/plugin-suite.js +917 -0
  181. package/src/lib/eval/runner.js +10 -3
  182. package/src/lib/evolution/agent-evolution-ingress.js +60 -8
  183. package/src/lib/evolution/agent-evolution-runtime-composition.js +236 -1
  184. package/src/lib/evolution/agent-model-projection.js +97 -1
  185. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +154 -1
  186. package/src/lib/evolution/evolution-artifact-ports.js +6 -0
  187. package/src/lib/evolution/evolution-deployment-loader.js +188 -10
  188. package/src/lib/evolution/evolution-eval-gate.js +8 -0
  189. package/src/lib/evolution/evolution-eval-runtime-composition.js +255 -0
  190. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +58 -2
  191. package/src/lib/evolution/evolution-ledger-file-backend.js +23 -2
  192. package/src/lib/evolution/evolution-ledger-manifest-chain.js +223 -7
  193. package/src/lib/evolution/evolution-ledger-v2-journal.js +525 -0
  194. package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +46 -2
  195. package/src/lib/evolution/evolution-ledger.js +57 -1
  196. package/src/lib/evolution/evolution-run-ledger-adapter.js +93 -0
  197. package/src/lib/evolution/evolution-run-wiki-evidence-resolver.js +259 -0
  198. package/src/lib/evolution/governed-hub-llm.js +238 -5
  199. package/src/lib/evolution/governed-hub-resolver.js +82 -4
  200. package/src/lib/evolution/governed-hub-skill.js +11 -5
  201. package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +10 -1
  202. package/src/lib/evolution/skill-runtime-revalidation-ledger-adapter.js +254 -0
  203. package/src/lib/evolution/skill-runtime-revalidation.js +530 -0
  204. package/src/lib/evolution/skill-target-matrix-eval.js +4 -4
  205. package/src/lib/evolution/wiki-evidence-commit-guard.js +366 -0
  206. package/src/lib/evolution/wiki-informed-skill-proposer.js +13 -1
  207. package/src/lib/goal-context.js +14 -5
  208. package/src/lib/interaction-adapter.js +166 -1
  209. package/src/lib/model-capabilities.js +271 -0
  210. package/src/lib/model-context-catalog.js +105 -0
  211. package/src/lib/model-context-window.js +4 -49
  212. package/src/lib/openai-responses.js +428 -0
  213. package/src/lib/plugin-runtime/scopes.js +7 -1
  214. package/src/lib/remote-read-loop-guard.js +2 -2
  215. package/src/lib/runtime-usage-ledger.js +16 -0
  216. package/src/lib/skill-loader.js +12 -0
  217. package/src/lib/with-file-lock.js +3 -1
  218. package/src/repl/agent-repl.js +6 -2
  219. package/src/runtime/agent-core.js +357 -44
  220. package/src/runtime/agent-runtime.js +7 -0
  221. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  222. package/src/runtime/headless-runner.js +4 -1
  223. package/src/runtime/headless-stream.js +133 -44
  224. package/src/runtime/runtime-factory.js +26 -2
  225. package/src/workers/background-agent-keeper.js +23 -13
  226. package/src/workers/background-agent-worker.js +27 -13
  227. package/src/assets/web-panel/assets/EvolutionSettings-_QnjPFZJ.js +0 -1
  228. package/src/assets/web-panel/assets/devWarning-LxtRBsCm.js +0 -1
  229. package/src/assets/web-panel/assets/index-36hxBH-o.js +0 -1
  230. package/src/assets/web-panel/assets/index-C0b78zWZ.js +0 -1
@@ -0,0 +1,366 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { types } from "node:util";
3
+ import {
4
+ captureWikiRevisionReader,
5
+ WikiMaintainerLedgerAdapter,
6
+ } from "./wiki-maintainer-ledger-adapter.js";
7
+ import {
8
+ digestWikiState,
9
+ WIKI_EVIDENCE_SCHEMA,
10
+ WIKI_PATTERN_STATUS,
11
+ } from "./evidence-backed-wiki-maintainer.js";
12
+ import { verifyWikiRevision } from "./wiki-revision-protocol.js";
13
+
14
+ export const WIKI_EVIDENCE_COMMIT_REQUEST_SCHEMA =
15
+ "chainlesschain.wiki-evidence-commit-request/v1";
16
+ export const WIKI_EVIDENCE_COMMIT_LEASE_SCHEMA =
17
+ "chainlesschain.wiki-evidence-commit-lease/v1";
18
+ export const WIKI_EVIDENCE_COMMIT_DENIED_CODE =
19
+ "CC_WIKI_EVIDENCE_COMMIT_DENIED";
20
+ export const WIKI_EVIDENCE_COMMIT_RELEASE_FAILED_CODE =
21
+ "CC_WIKI_EVIDENCE_COMMIT_RELEASE_FAILED";
22
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
23
+ const USED_LEASES = new WeakSet();
24
+ const TERMINAL = new Set([
25
+ WIKI_PATTERN_STATUS.QUARANTINED,
26
+ WIKI_PATTERN_STATUS.REVOKED,
27
+ WIKI_PATTERN_STATUS.TOMBSTONED,
28
+ ]);
29
+ const EVIDENCE_KEYS = [
30
+ "schema",
31
+ "tenantId",
32
+ "ref",
33
+ "sourceDigest",
34
+ "projectionDigest",
35
+ "artifactRef",
36
+ "trustedProjection",
37
+ "trustDomain",
38
+ "kind",
39
+ "status",
40
+ "observedAt",
41
+ "expiresAt",
42
+ "data",
43
+ ];
44
+ const LEASE_KEYS = [
45
+ "schema",
46
+ "tenantId",
47
+ "runId",
48
+ "requestDigest",
49
+ "evidenceBindingDigest",
50
+ "leaseId",
51
+ "assertCurrent",
52
+ "release",
53
+ ];
54
+ function denied(message, cause) {
55
+ const error = new Error(message, cause === undefined ? undefined : { cause });
56
+ error.code = WIKI_EVIDENCE_COMMIT_DENIED_CODE;
57
+ return error;
58
+ }
59
+ function record(value, label, keys) {
60
+ if (
61
+ !value ||
62
+ typeof value !== "object" ||
63
+ types.isProxy(value) ||
64
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
65
+ )
66
+ throw denied(`${label} must be a plain non-Proxy record`);
67
+ const own = Reflect.ownKeys(value);
68
+ if (
69
+ (keys &&
70
+ (own.length !== keys.length || own.some((key) => !keys.includes(key)))) ||
71
+ own.some(
72
+ (key) =>
73
+ typeof key !== "string" ||
74
+ !Object.hasOwn(Object.getOwnPropertyDescriptor(value, key), "value"),
75
+ )
76
+ )
77
+ throw denied(`${label} must contain only exact own data fields`);
78
+ return value;
79
+ }
80
+ function syncMethod(value, name, label) {
81
+ const method = Object.getOwnPropertyDescriptor(value, name)?.value;
82
+ if (
83
+ typeof method !== "function" ||
84
+ types.isProxy(method) ||
85
+ types.isAsyncFunction(method) ||
86
+ types.isGeneratorFunction(method)
87
+ )
88
+ throw denied(`${label}.${name} must be synchronous`);
89
+ return (...args) => Reflect.apply(method, value, args);
90
+ }
91
+ // Copy before calling any host authority. No getters, toJSON, custom prototypes,
92
+ // proxies, sparse arrays or mutable aliases survive into the publish request.
93
+ function snapshot(value, budget = { nodes: 0, bytes: 0 }, depth = 0) {
94
+ if (++budget.nodes > 100_000 || depth > 32)
95
+ throw denied("Wiki revision exceeds its structural budget");
96
+ if (value === null || typeof value === "boolean") return value;
97
+ if (typeof value === "string") {
98
+ budget.bytes += Buffer.byteLength(value, "utf8");
99
+ if (budget.bytes > 1_048_576)
100
+ throw denied("Wiki revision strings exceed their byte budget");
101
+ return value;
102
+ }
103
+ if (typeof value === "number" && Number.isFinite(value)) return value;
104
+ if (typeof value !== "object" || types.isProxy(value))
105
+ throw denied("Wiki revision is not plain data");
106
+ if (Array.isArray(value)) {
107
+ if (
108
+ Object.getPrototypeOf(value) !== Array.prototype ||
109
+ value.length > 100_000 ||
110
+ Reflect.ownKeys(value).length !== value.length + 1
111
+ )
112
+ throw denied("Wiki revision array is not dense and bounded");
113
+ const result = [];
114
+ for (let i = 0; i < value.length; i++) {
115
+ const entry = Object.getOwnPropertyDescriptor(value, String(i));
116
+ if (!entry || !Object.hasOwn(entry, "value"))
117
+ throw denied("Wiki revision array contains an accessor or hole");
118
+ result.push(snapshot(entry.value, budget, depth + 1));
119
+ }
120
+ return Object.freeze(result);
121
+ }
122
+ record(value, "Wiki revision record");
123
+ return Object.freeze(
124
+ Object.fromEntries(
125
+ Object.keys(value).map((key) => [
126
+ key,
127
+ snapshot(
128
+ Object.getOwnPropertyDescriptor(value, key).value,
129
+ budget,
130
+ depth + 1,
131
+ ),
132
+ ]),
133
+ ),
134
+ );
135
+ }
136
+ function bindingsFor(revision) {
137
+ const state = revision.state;
138
+ record(state.evidence, "Wiki evidence map");
139
+ record(state.patterns, "Wiki pattern map");
140
+ const refs = new Set();
141
+ const add = (ref) => {
142
+ if (typeof ref !== "string" || !ref || !Object.hasOwn(state.evidence, ref))
143
+ throw denied("Wiki dependency has no bound evidence");
144
+ refs.add(ref);
145
+ if (refs.size > 256)
146
+ throw denied(
147
+ "Wiki active dependencies exceed 256; none may be truncated",
148
+ );
149
+ };
150
+ // Old evidence can be reused by upsert or merged into an existing pattern.
151
+ // Therefore scope is the OUTPUT dependencies, not just this request's refs.
152
+ for (const pattern of Object.values(state.patterns)) {
153
+ if (TERMINAL.has(pattern.status)) continue;
154
+ if (!Object.values(WIKI_PATTERN_STATUS).includes(pattern.status))
155
+ throw denied("Wiki pattern status is invalid");
156
+ for (const field of ["positiveEvidence", "negativeEvidence"]) {
157
+ if (!Array.isArray(pattern[field]))
158
+ throw denied("Wiki pattern dependencies are invalid");
159
+ for (const ref of pattern[field]) add(ref);
160
+ }
161
+ }
162
+ // Newly read active evidence is also admitted even when no pattern is made.
163
+ // Terminal cleanup may retain old tombstones without reauthorizing learning.
164
+ for (const ref of revision.evidenceRefs) {
165
+ const item = state.evidence[ref];
166
+ if (!item || !["active", "revoked", "deleted"].includes(item.status))
167
+ throw denied("Wiki request evidence status is invalid");
168
+ if (item.status === "active") add(ref);
169
+ }
170
+ return Object.freeze(
171
+ [...refs].sort().map((ref) => {
172
+ const item = record(
173
+ state.evidence[ref],
174
+ "Wiki evidence binding",
175
+ EVIDENCE_KEYS,
176
+ );
177
+ if (
178
+ item.schema !== WIKI_EVIDENCE_SCHEMA ||
179
+ item.tenantId !== revision.tenantId ||
180
+ item.ref !== ref ||
181
+ item.trustedProjection !== true ||
182
+ item.status !== "active" ||
183
+ !DIGEST.test(item.sourceDigest) ||
184
+ !DIGEST.test(item.projectionDigest) ||
185
+ typeof item.artifactRef !== "string" ||
186
+ !item.artifactRef ||
187
+ typeof item.trustDomain !== "string" ||
188
+ !item.trustDomain ||
189
+ !["tool-observation", "grader-receipt"].includes(item.kind) ||
190
+ !Number.isFinite(Date.parse(item.observedAt)) ||
191
+ !Number.isFinite(Date.parse(item.expiresAt))
192
+ )
193
+ throw denied(
194
+ "Wiki active dependency is not authenticated typed evidence",
195
+ );
196
+ record(item.data, "Wiki trusted metadata");
197
+ for (const key of [
198
+ "rawRecordDigest",
199
+ "manifestDigest",
200
+ "projectionReceiptDigest",
201
+ "schemaDigest",
202
+ ])
203
+ if (!DIGEST.test(item.data[key]))
204
+ throw denied("Wiki evidence identity digest is missing");
205
+ return item;
206
+ }),
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Explicit host trust boundary. acquireCurrentEvidence MUST synchronously hold
212
+ * a cross-evidence state/principal/ACL lock (or irrevocable scoped grant) until
213
+ * release. A timestamp/read receipt alone is not that contract. Deployments must
214
+ * prove the coordinator's cross-process semantics; this wrapper does not defend
215
+ * against a malicious authority. The authority NEVER receives publish authority.
216
+ */
217
+ export function createWikiEvidenceCommitGuard({
218
+ wikiAdapter,
219
+ commitCoordinator,
220
+ principalEnvelope,
221
+ clock = Date.now,
222
+ } = {}) {
223
+ if (
224
+ !wikiAdapter ||
225
+ types.isProxy(wikiAdapter) ||
226
+ Object.getPrototypeOf(wikiAdapter) !== WikiMaintainerLedgerAdapter.prototype
227
+ )
228
+ throw denied("a genuine production Wiki adapter is required");
229
+ const reader = captureWikiRevisionReader(wikiAdapter);
230
+ const publish = syncMethod(wikiAdapter, "commitRevision", "Wiki adapter");
231
+ record(commitCoordinator, "Wiki commit coordinator", [
232
+ "acquireCurrentEvidence",
233
+ ]);
234
+ const acquire = syncMethod(
235
+ commitCoordinator,
236
+ "acquireCurrentEvidence",
237
+ "Wiki commit coordinator",
238
+ );
239
+ if (
240
+ typeof principalEnvelope !== "string" ||
241
+ !principalEnvelope ||
242
+ principalEnvelope.length > 65_536 ||
243
+ typeof clock !== "function" ||
244
+ types.isProxy(clock) ||
245
+ types.isAsyncFunction(clock)
246
+ )
247
+ throw denied("Wiki principal envelope and synchronous clock are required");
248
+ let committing = false;
249
+ return Object.freeze({
250
+ commitRevision(input) {
251
+ if (committing) throw denied("Wiki publication is not reentrant");
252
+ const owned = snapshot(input);
253
+ record(
254
+ owned,
255
+ "Wiki commit input",
256
+ Object.hasOwn(owned, "expectedLedgerHead")
257
+ ? ["expectedStateDigest", "revision", "expectedLedgerHead"]
258
+ : ["expectedStateDigest", "revision"],
259
+ );
260
+ const revision = verifyWikiRevision(owned.revision, reader.descriptor);
261
+ if (
262
+ !DIGEST.test(owned.expectedStateDigest) ||
263
+ owned.expectedStateDigest !== revision.priorStateDigest
264
+ )
265
+ throw denied("Wiki prior state is not bound to the publication");
266
+ const evidenceBindings = bindingsFor(revision);
267
+ const requestedAt = new Date(clock()).toISOString();
268
+ if (
269
+ evidenceBindings.some(
270
+ (item) => Date.parse(item.expiresAt) <= Date.parse(requestedAt),
271
+ )
272
+ )
273
+ throw denied("Wiki evidence expired before lease acquisition");
274
+ const evidenceBindingDigest = digestWikiState({
275
+ domain: "chainlesschain.wiki-evidence-bindings/v1",
276
+ tenantId: reader.descriptor.tenantId,
277
+ runId: reader.descriptor.evolutionRunId,
278
+ evidenceBindings,
279
+ });
280
+ const core = Object.freeze({
281
+ schema: WIKI_EVIDENCE_COMMIT_REQUEST_SCHEMA,
282
+ tenantId: reader.descriptor.tenantId,
283
+ runId: reader.descriptor.evolutionRunId,
284
+ principalEnvelope,
285
+ expectedStateDigest: owned.expectedStateDigest,
286
+ revisionId: revision.revisionId,
287
+ stateDigest: revision.stateDigest,
288
+ evidenceRefs: revision.evidenceRefs,
289
+ evidenceBindings,
290
+ evidenceBindingDigest,
291
+ requestNonce: randomUUID(),
292
+ requestedAt,
293
+ });
294
+ const request = Object.freeze({
295
+ ...core,
296
+ requestDigest: digestWikiState(core),
297
+ });
298
+ let release;
299
+ let receipt;
300
+ let failure;
301
+ committing = true;
302
+ try {
303
+ const lease = acquire(request);
304
+ record(lease, "Wiki commit lease", LEASE_KEYS);
305
+ if (!Object.isFrozen(lease))
306
+ throw denied("Wiki commit lease must be immutable");
307
+ if (USED_LEASES.has(lease))
308
+ throw denied("Wiki commit lease cannot be reused");
309
+ USED_LEASES.add(lease);
310
+ release = syncMethod(lease, "release", "Wiki commit lease");
311
+ const assertCurrent = syncMethod(
312
+ lease,
313
+ "assertCurrent",
314
+ "Wiki commit lease",
315
+ );
316
+ if (
317
+ lease.schema !== WIKI_EVIDENCE_COMMIT_LEASE_SCHEMA ||
318
+ lease.tenantId !== request.tenantId ||
319
+ lease.runId !== request.runId ||
320
+ lease.requestDigest !== request.requestDigest ||
321
+ lease.evidenceBindingDigest !== request.evidenceBindingDigest ||
322
+ typeof lease.leaseId !== "string" ||
323
+ !lease.leaseId ||
324
+ lease.leaseId.length > 256
325
+ )
326
+ throw denied("Wiki commit lease is not bound to the entire request");
327
+ if (assertCurrent() !== true)
328
+ throw denied("Wiki commit lease is not currently held");
329
+ // No await or host-supplied publish callback between lease and real write.
330
+ receipt = publish(owned);
331
+ } catch (error) {
332
+ failure = error?.code
333
+ ? error
334
+ : denied("Wiki evidence commit authorization failed", error);
335
+ } finally {
336
+ let releaseFailure;
337
+ try {
338
+ if (release && release() !== undefined)
339
+ releaseFailure = new Error(
340
+ "Wiki commit lease release must be synchronous and return undefined",
341
+ );
342
+ } catch (error) {
343
+ releaseFailure = error;
344
+ }
345
+ if (releaseFailure) {
346
+ if (receipt?.committed === true) {
347
+ failure = new Error(
348
+ "Wiki was committed but its authorization lease failed to release",
349
+ { cause: releaseFailure },
350
+ );
351
+ failure.code = WIKI_EVIDENCE_COMMIT_RELEASE_FAILED_CODE;
352
+ failure.committed = true;
353
+ failure.receipt = receipt;
354
+ } else if (!failure)
355
+ failure = denied(
356
+ "Wiki commit lease failed to release",
357
+ releaseFailure,
358
+ );
359
+ }
360
+ committing = false;
361
+ }
362
+ if (failure) throw failure;
363
+ return receipt;
364
+ },
365
+ });
366
+ }
@@ -2,6 +2,8 @@ import { createHash } from "node:crypto";
2
2
 
3
3
  export const WIKI_SKILL_PROPOSAL_SCHEMA =
4
4
  "chainlesschain.wiki-informed-skill-proposal/v2";
5
+ export const WIKI_SKILL_CANDIDATE_CONTENT_SCHEMA =
6
+ "chainlesschain.wiki-informed-skill-candidate-content/v1";
5
7
  export const WIKI_PROPOSAL_STATUS = Object.freeze({
6
8
  PROPOSAL: "proposal",
7
9
  NO_PROPOSAL: "no-proposal",
@@ -42,6 +44,16 @@ export function computeWikiSkillProposalDigest(proposal) {
42
44
  return hash(proposal);
43
45
  }
44
46
 
47
+ function candidateContent(proposal) {
48
+ // Evidence digests belong to the candidate's ID-bound metadata, not its
49
+ // plaintext body. Keep the full proposal (and its independent digest) intact.
50
+ // This is a representation change, not a schema-based secret-scan exemption:
51
+ // PURPOSE references, machineDiff and all other body fields remain scanned.
52
+ const body = { ...proposal, schema: WIKI_SKILL_CANDIDATE_CONTENT_SCHEMA };
53
+ delete body.sourceEvidenceRefs;
54
+ return canonical(body);
55
+ }
56
+
45
57
  function deepFreeze(value) {
46
58
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
47
59
  Object.freeze(value);
@@ -408,7 +420,7 @@ export class WikiInformedSkillProposer {
408
420
  proposerModel: proposal.proposerModel,
409
421
  requestedCapabilities: proposal.requestedCapabilities,
410
422
  sourceEvidenceRefs: proposal.sourceEvidenceRefs,
411
- content: canonical(proposal),
423
+ content: candidateContent(proposal),
412
424
  });
413
425
  const created = await this._ports.createCandidate(candidateInput);
414
426
  const candidate = created?.candidate;
@@ -63,25 +63,34 @@ export function goalPrepareCall(goal) {
63
63
 
64
64
  /**
65
65
  * Compose several `prepareCall` functions into one. Each is invoked per turn
66
- * and their non-empty `systemSuffix` strings are concatenated. A failing
67
- * member is skipped, never fatal.
66
+ * and their non-empty transient system/user contexts are concatenated without
67
+ * changing authority: `systemSuffix` stays system, `userContext` stays user.
68
+ * A failing member is skipped, never fatal.
68
69
  * @param {Array<Function|null|undefined>} fns
69
70
  * @returns {(ctx:object) => Promise<{systemSuffix:string}|null>}
70
71
  */
71
72
  export function composePrepareCall(fns) {
72
73
  const list = (fns || []).filter((f) => typeof f === "function");
73
74
  return async (ctx) => {
74
- const parts = [];
75
+ const systemParts = [];
76
+ const userParts = [];
75
77
  for (const fn of list) {
76
78
  try {
77
79
  const r = await fn(ctx);
78
80
  if (r && typeof r.systemSuffix === "string" && r.systemSuffix.trim()) {
79
- parts.push(r.systemSuffix);
81
+ systemParts.push(r.systemSuffix);
82
+ }
83
+ if (r && typeof r.userContext === "string" && r.userContext.trim()) {
84
+ userParts.push(r.userContext);
80
85
  }
81
86
  } catch {
82
87
  /* a failing member must not break the turn */
83
88
  }
84
89
  }
85
- return parts.length ? { systemSuffix: parts.join("\n\n") } : null;
90
+ if (systemParts.length === 0 && userParts.length === 0) return null;
91
+ return {
92
+ ...(systemParts.length ? { systemSuffix: systemParts.join("\n\n") } : {}),
93
+ ...(userParts.length ? { userContext: userParts.join("\n\n") } : {}),
94
+ };
86
95
  };
87
96
  }
@@ -20,6 +20,7 @@ import {
20
20
  normalizeInteractionBinding,
21
21
  sameInteractionBinding,
22
22
  } from "./interaction-binding.js";
23
+ import { DeferredQuestionContext } from "./deferred-question-context.js";
23
24
  import { createEnvelope } from "@chainlesschain/session-core";
24
25
 
25
26
  // Phase 5: parallel service-envelope emission. Map WS agent-handler event
@@ -133,6 +134,35 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
133
134
  // global default tracker.
134
135
  this._sequenceTracker = new CodingAgentSequenceTracker();
135
136
  this._questionSequence = 0;
137
+ const deferredState =
138
+ options.deferredState && typeof options.deferredState === "object"
139
+ ? options.deferredState
140
+ : {};
141
+ this._contextRevision = Number.isSafeInteger(deferredState.contextRevision)
142
+ ? deferredState.contextRevision
143
+ : 0;
144
+ this.onDeferredQuestionChange =
145
+ typeof options.onDeferredQuestionChange === "function"
146
+ ? options.onDeferredQuestionChange
147
+ : null;
148
+ this._deferredQuestionContext = new DeferredQuestionContext({
149
+ sessionId: this.sessionId,
150
+ initialAnswers: Array.isArray(deferredState.deferredAnswers)
151
+ ? deferredState.deferredAnswers.map((answer) => ({
152
+ ...answer,
153
+ questionId: answer.questionId || answer.requestId,
154
+ }))
155
+ : [],
156
+ onConsumed: (questionIds) =>
157
+ this._notifyDeferredQuestion("question.deferred.consumed", {
158
+ questionIds,
159
+ }),
160
+ });
161
+ for (const pending of Array.isArray(deferredState.deferredQuestions)
162
+ ? deferredState.deferredQuestions
163
+ : []) {
164
+ this._restoreDeferredQuestion(pending);
165
+ }
136
166
  // Phase 5: parallel service-envelope emission. Opt-in (default off) so
137
167
  // legacy callers that count ws.send invocations stay green.
138
168
  this.enablePhase5Envelopes = options.enablePhase5Envelopes === true;
@@ -155,6 +185,54 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
155
185
  }
156
186
  }
157
187
 
188
+ _notifyDeferredQuestion(type, payload) {
189
+ if (!this.onDeferredQuestionChange) return;
190
+ try {
191
+ this.onDeferredQuestionChange({ type, payload });
192
+ } catch {
193
+ // The live interaction remains available; the host journal owns retry.
194
+ }
195
+ }
196
+
197
+ _restoreDeferredQuestion(record) {
198
+ const requestId = String(record?.requestId || "").trim();
199
+ const binding = normalizeInteractionBinding(record?.binding);
200
+ if (!requestId || binding.sessionId !== String(this.sessionId)) return;
201
+ this._questionSequence = Math.max(
202
+ this._questionSequence,
203
+ Number.isSafeInteger(binding.sequence) ? binding.sequence : 0,
204
+ );
205
+ this._pending.set(requestId, {
206
+ resolve: (answer) => {
207
+ const resolvedRevision = this._contextRevision;
208
+ this._deferredQuestionContext.record({
209
+ questionId: requestId,
210
+ question: record.question,
211
+ answer,
212
+ binding,
213
+ requestedRevision: record.contextRevision,
214
+ resolvedRevision,
215
+ });
216
+ this._notifyDeferredQuestion("question.deferred.resolved", {
217
+ requestId,
218
+ question: record.question,
219
+ answer,
220
+ binding,
221
+ requestedRevision: record.contextRevision,
222
+ resolvedRevision,
223
+ });
224
+ },
225
+ reject: () => {},
226
+ timeoutId: null,
227
+ kind: "question",
228
+ approvalRequest: false,
229
+ binding,
230
+ bindingType: "interaction",
231
+ requireBinding: true,
232
+ deferred: true,
233
+ });
234
+ }
235
+
158
236
  /** Generate a unique request id */
159
237
  _requestId() {
160
238
  return `q-${Date.now()}-${createHash("sha256").update(Math.random().toString()).digest("hex").slice(0, 6)}`;
@@ -207,6 +285,10 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
207
285
  bindingType: options.bindingType || null,
208
286
  requireBinding: options.requireBinding === true,
209
287
  });
288
+ options.onRegistered?.({
289
+ requestId,
290
+ binding: options.binding || null,
291
+ });
210
292
 
211
293
  if (approvalRequest) {
212
294
  this._notifyPendingApproval("approval.requested", {
@@ -337,7 +419,7 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
337
419
  * elicitation, while exposing the stream protocol's `question_request`
338
420
  * shape so capable UIs can render options natively.
339
421
  */
340
- async askUser({
422
+ askUser({
341
423
  question,
342
424
  options = null,
343
425
  multiSelect = false,
@@ -348,6 +430,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
348
430
  turnId,
349
431
  toolUseId,
350
432
  metadata = null,
433
+ mode = "blocking",
434
+ purpose = "decision",
435
+ onRegistered = null,
351
436
  } = {}) {
352
437
  const normalizedTurnId =
353
438
  turnId === undefined || turnId === null ? null : String(turnId);
@@ -380,6 +465,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
380
465
  question: typeof question === "string" ? question : "",
381
466
  options: Array.isArray(options) ? options : null,
382
467
  multiSelect: multiSelect === true,
468
+ mode,
469
+ blocking: mode !== "deferred",
470
+ purpose,
383
471
  ...(defaultValue !== undefined ? { defaultValue } : {}),
384
472
  ...(onTimeout ? { onTimeout } : {}),
385
473
  ...(onReject ? { onReject } : {}),
@@ -401,10 +489,84 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
401
489
  binding,
402
490
  bindingType: "interaction",
403
491
  ...(Number(timeoutMs) > 0 ? { timeoutMs: Number(timeoutMs) } : {}),
492
+ ...(typeof onRegistered === "function" ? { onRegistered } : {}),
404
493
  },
405
494
  );
406
495
  }
407
496
 
497
+ /**
498
+ * Register an informational question without blocking the current agent loop.
499
+ * Its answer is retained as one-shot, user-authority context for prepareCall.
500
+ */
501
+ deferUserQuestion(request = {}) {
502
+ let registration = null;
503
+ const requestedRevision = this._contextRevision;
504
+ const answer = this.askUser({
505
+ ...request,
506
+ mode: "deferred",
507
+ onRegistered: (value) => {
508
+ registration = value;
509
+ },
510
+ });
511
+ this._notifyDeferredQuestion("question.deferred.requested", {
512
+ requestId: registration?.requestId,
513
+ question: request.question,
514
+ options: request.options,
515
+ multiSelect: request.multiSelect === true,
516
+ purpose: request.purpose || "information",
517
+ binding: registration?.binding || null,
518
+ contextRevision: requestedRevision,
519
+ });
520
+ answer
521
+ .then((value) => {
522
+ this._deferredQuestionContext.record({
523
+ questionId: registration?.requestId,
524
+ question: request.question,
525
+ answer: value,
526
+ binding: registration?.binding || null,
527
+ requestedRevision,
528
+ resolvedRevision: this._contextRevision,
529
+ });
530
+ this._notifyDeferredQuestion("question.deferred.resolved", {
531
+ requestId: registration?.requestId,
532
+ question: request.question,
533
+ answer: value,
534
+ binding: registration?.binding || null,
535
+ requestedRevision,
536
+ resolvedRevision: this._contextRevision,
537
+ });
538
+ })
539
+ .catch(() => {
540
+ // Timeout/disconnect simply leaves no asynchronous user context.
541
+ });
542
+ return Object.freeze({
543
+ questionId: registration?.requestId,
544
+ status: "pending",
545
+ mode: "deferred",
546
+ purpose: request.purpose || "information",
547
+ authorization: false,
548
+ binding: registration?.binding || null,
549
+ contextRevision: requestedRevision,
550
+ });
551
+ }
552
+
553
+ beginContextRevision(revision = null) {
554
+ this._contextRevision = Number.isSafeInteger(revision)
555
+ ? revision
556
+ : this._contextRevision + 1;
557
+ this._notifyDeferredQuestion("question.context.revision", {
558
+ contextRevision: this._contextRevision,
559
+ });
560
+ return this._contextRevision;
561
+ }
562
+
563
+ createDeferredPrepareCall() {
564
+ return () =>
565
+ this._deferredQuestionContext.prepareCall({
566
+ currentRevision: this._contextRevision,
567
+ });
568
+ }
569
+
408
570
  /**
409
571
  * Called by ws-server when a session-answer message arrives.
410
572
  * Resolves the corresponding pending promise.
@@ -502,6 +664,9 @@ export class WebSocketInteractionAdapter extends InteractionAdapter {
502
664
  }
503
665
  pending.reject(error);
504
666
  }
667
+ // Deferred questions and already-resolved answers are durable host state;
668
+ // a transport disconnect must not erase them. Blocking questions and
669
+ // approvals above are still rejected fail-closed.
505
670
  }
506
671
 
507
672
  emit(eventType, data) {