chainlesschain 0.166.109 → 0.166.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +14 -6
  2. package/package.json +3 -2
  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/.build-hash +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-DkrNbDzS.js → AIOps-CVac3rIN.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-DJjFXDhv.js → ActionButton-ChM9GxMl.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DyUbagRn.js → Analytics-CV5399Js.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-p7kJWSvn.js → AppLayout-CuonVXhg.js} +3 -3
  10. package/src/assets/web-panel/assets/{Artifacts-C3rVh4Ak.js → Artifacts-D9qKfgb6.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-Bch_L_YO.js → Audit-DUvLysQc.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-BNHy8zP6.js → BackgroundAgents-DfKohQeT.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-BUaye_Ys.js → Backup-CohoEI8R.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-D49mzAVz.js → BaseInput-BwdoA3xv.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-2MDzM_us.js → Chat-DRkuz1d2.js} +5 -5
  16. package/src/assets/web-panel/assets/ChatBubbleRenderer-v3pzeHNC.js +1 -0
  17. package/src/assets/web-panel/assets/{Checkbox-5p1T_KEq.js → Checkbox-B-GNGpKv.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-TKtiLaoZ.js → Codegen-Bs2lAsoZ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-BVCMG9mS.js → Col-Bk1mJKDZ.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-D09-QczH.js → Community-B_FDSCOo.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-QuRMCnj7.js → Compact-No1qSLLH.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CN0PV7fk.js → Compliance-CcrG43-d.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-5N2zhAty.js → Cowork-DLkj2ywK.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-BxT8UZ0S.js → Cron-sQHELVEz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-Dp-noQl4.js → Crosschain-CRTZ3GZg.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-B7MZFkjG.js → DID-Ds2bjvxE.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-DulJ3rKV.js → Dashboard-DOONG2NX.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DV4ov4zF.js → Dropdown-Br3PNA2L.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-DB2-ooz6.js → EmailListRenderer-DT6UUqjZ.js} +1 -1
  30. package/src/assets/web-panel/assets/{EvolutionSettings-B1Rb6p7q.js → EvolutionSettings-BSLuEjlT.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-U8aHayx-.js → FamilyGuardDashboard-2aEM9avE.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-BkQULBHY.js → Federation-D3B0tlQU.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-CtviG260.js → FormItemContext-DTVkVVQP.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-DnPirB24.js → GenericCardRenderer-BgFEgP4y.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-CK57Oro6.js → Git-3t71m6Xd.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-DbI-LB8-.js → Governance-DS7WvlaX.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-sEG4T1Jg.js → Inference-CeT-xWrI.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-9SLJR3h9.js → KnowledgeGraph-BQzMncp5.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-BIZYcISQ.js → Logs-CC5DOKhV.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-9nfWupWP.js → MarkdownRenderer-5jYM4kYG.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-CRmLLwAE.js → Marketplace-CpYDyy3o.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-DkOFkSO1.js → McpTools-DRdAZPMr.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-M29Kd9TU.js → Memory-CBLRr-l8.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-Tl6JvPw_.js → MobileBridge-Bnj9veNN.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-Bq-FPDzS.js → MobileProjects-lYFPD24p.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-DkUjX1F5.js → Mtc-BMU_C9vD.js} +2 -2
  47. package/src/assets/web-panel/assets/{MtcAudit-BCqr1I4q.js → MtcAudit-DQV-zumU.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-DmoqYGQ5.js → Multisig-LpqpqV1d.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-DNRQeRke.js → NLProgramming-C4OMklSO.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-CQSBeiY6.js → Notes-Cg0QCinP.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-gNY9bbH6.js → NotificationSettings-amoZoMna.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-DW0CNjh8.js → OrderTableRenderer-CxNvhf5n.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-CthnbKag.js → Organization-BetJmyX5.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-_GQ3sbXu.js → Overflow-DbeoPtVD.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-BTr7ea6W.js → P2P-B5sitQWT.js} +2 -2
  56. package/src/assets/web-panel/assets/PdhVaultBrowser-AP9SG2ic.js +7 -0
  57. package/src/assets/web-panel/assets/{Permissions-DMny2Cjs.js → Permissions-Bc7fgNKx.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-CyYlMSoI.js → PersonalDataHub-BwdGMcWZ.js} +3 -3
  59. package/src/assets/web-panel/assets/{Pipeline-FASu2Oc4.js → Pipeline-DVuJA9BA.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-9bC5FnsJ.js → Privacy-BGzwOYRG.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-BOYZwB5G.js → ProjectInit-BpHcC5br.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-45DViNG-.js → ProjectSettings-BI5G5gsc.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-C2fZzMZc.js → Projects-Ce92-l1V.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-BgjvZx9F.js → Providers-CRHCcLFS.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-Brw-3csB.js → QrScannerModal-qSeg5xag.js} +1 -1
  66. package/src/assets/web-panel/assets/QuickAsk-CA8oSceR.js +1 -0
  67. package/src/assets/web-panel/assets/{Recommend-DuQGxjIR.js → Recommend-DMvPodNo.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-T1cdSSRL.js → RemoteSession-BWsapEyN.js} +2 -2
  69. package/src/assets/web-panel/assets/{Reputation-9FT2dI4G.js → Reputation-BZJETVmC.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-yNIbNftX.js → Row-CUE1OSMp.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BIchNoPd.js → RssFeed-DvMazcUD.js} +2 -2
  72. package/src/assets/web-panel/assets/{Search-msDJ8UTZ.js → Search-e1c-NXWv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-DE46oBYT.js → Security-Dc9GZsx2.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-Dori7vc3.js → Services-AFz6ke-4.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-D3O3sUFz.js → Skeleton-gow6MYcY.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-BV0ct8SO.js → Skills-h6AxYjUP.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-DflIVOrh.js → Sla-Cr4CkJ0O.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-Cpbv2Zz6.js → SpeechSettings-Bd5yuBtH.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-Dgg6-wbe.js → SyncSettings-1t4QZMLW.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-Cb6e4Klx.js → Tasks-u-DrjLRM.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-GfdAATGz.js → Templates-C0yLUC9b.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-D0gecLXl.js → Tenant-ds1YT5iS.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-BFRPJt-0.js → Terminal-BoIskW2m.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-BobtChxX.js → TimelineRenderer-BkdVK24X.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-CifeZtHV.js → Tokens-djyDgmfk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-BHBvbBCf.js → Trigger-CYefDS3H.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-6lvqjKYp.js → Trust-CE_dbep_.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-CqzMc-G5.js → UkeySign-DwVs_fey.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-OIiRG582.js → VideoEditing-BTUqMHAV.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-4MeKq0q_.js → Wallet-Bu_E7Rrc.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-eHBRjGQG.js → WebAuthn-DTbre5AB.js} +4 -4
  92. package/src/assets/web-panel/assets/{WorkflowEditor-DlzYdEqW.js → WorkflowEditor-BsBzd3gt.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-CBcjOzkX.js → chat-tdifQQvu.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-zgwsYQSa.js → colors-D3SNW2Vx.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Dcd9H5Bn.js → compact-item-UBlCjrAW.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-DR1tO-8r.js → createContext-DKsnEnFt.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-BBnVfJHh.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-CtfbUH_p.js → hasIn-BkeySXWe.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BXfGHUz_.js → index-6Nvzgytx.js} +1 -1
  100. package/src/assets/web-panel/assets/index-B2w_3mtS.js +1 -0
  101. package/src/assets/web-panel/assets/{index-DYPMJ5Mb.js → index-BEiHKah0.js} +25 -25
  102. package/src/assets/web-panel/assets/{index-LN7Hkl1C.js → index-BIeFLaMV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DA2-Bhy8.js → index-BV2zQteq.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CwHgaYVH.js → index-BVvz3ntc.js} +1 -1
  105. package/src/assets/web-panel/assets/index-BoHurydC.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DVpp9YSQ.js → index-Bw0k3VgD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CaGNqkY0.js → index-C-KTHSUE.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DWuCGhHa.js → index-C9ImNw5O.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DTx4FyLU.js → index-CG_b1xyX.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cur8LNNn.js → index-CUjyL0ZS.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DfzOYmOj.js → index-CXkn77ZN.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cdhy5ckh.js → index-CZ6JUooI.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D1GxtY-h.js → index-Cuz2QCFT.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C0kP88Pb.js → index-D5Umd_Z-.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DHEiuvhA.js → index-D6es7MSG.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CPLLgPxA.js → index-D93iSDUw.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-B3QUK1ks.js → index-D9m1HQzl.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DWG98orp.js → index-D9um0YVl.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-hTcre7U9.js → index-DD6dD3LV.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-C5XGvJb0.js → index-DFMrIEIt.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DvPOpvvH.js → index-DSCiZJXb.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D8WPohye.js → index-DSe_7MSy.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-JSfldpw4.js → index-DYFjOyTH.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-5XaB_Tjg.js → index-DdyTv6yc.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BoxowMYS.js → index-DfrW-iVs.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DSvcz0RO.js → index-Dg8-Duze.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-J-ORW1i6.js → index-DiDgwRty.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CT7zIkqP.js → index-DlEIp1gJ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-D7ZOiIKb.js → index-DlO6HdD9.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DeAi4CNt.js → index-DtxUSZSS.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CmVIM-m2.js → index-DugZRCiI.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-oWOlF7CM.js → index-P-ailFQY.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-dvF6C39V.js → index-PWScrdlX.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CSQCHpDo.js → index-SpQ26gD6.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CmzQzTY5.js → index-TLBpgN30.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-z8lrkcb-.js → index-bOIcWW8F.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-BndPtS9D.js → index-pgGKNvmm.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-CVMtDrWZ.js → initDefaultProps-CVCXG2Hv.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-Dye3IU8k.js → motion-Dv00jw87.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-C251Zo34.js → move-BFz51iHf.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BDiOORoQ.js → mtc-parser-BK2woIDE.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-DTDA_RHv.js → omit-D2X_Gwoo.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-CqEUTPcT.js → pickAttrs-CLmFyJ1q.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-BOB8QWH3.js → placementArrow-CCzPddQm.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CZDXP1d9.js → responsiveObserve-hcm_Zlxd.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-DPaRw3E9.js → slide-BxfZEFVp.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-Ba9d7XZ7.js → statusUtils-kzB9pT-t.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-mZKU3lvW.js → styleChecker-CDFd7YCI.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-VcauLq0M.js → useFlexGapSupport-Czl7NNhS.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-Cal9KRcx.js → useFs-y0SAzirG.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-DISoRyQB.js → usePersonalDataHub-BvdPznrA.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-B58C6w6B.js → vnode-D8mNbyZ7.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-BPlBAMKB.js → zoom-_hun6w9W.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +1 -1
  156. package/src/commands/agent.js +12 -5
  157. package/src/commands/ask.js +96 -21
  158. package/src/commands/complete.js +14 -10
  159. package/src/commands/llm.js +23 -41
  160. package/src/commands/session.js +2 -2
  161. package/src/commands/stream.js +9 -5
  162. package/src/commands/ui.js +30 -2
  163. package/src/data/changelog.json +65 -0
  164. package/src/gateways/ws/llm-chat-protocol.js +131 -40
  165. package/src/gateways/ws/llm-creds.js +29 -79
  166. package/src/gateways/ws/message-dispatcher.js +2 -0
  167. package/src/gateways/ws/ws-agent-handler.js +1 -1
  168. package/src/gateways/ws/ws-session-gateway.js +26 -12
  169. package/src/lib/__tests__/provider-stream.test.js +70 -4
  170. package/src/lib/chat-core.js +186 -24
  171. package/src/lib/cowork-adapter.js +29 -15
  172. package/src/lib/evolution/agent-evolution-runtime-composition.js +10 -7
  173. package/src/lib/evolution/evolution-deployment-config.js +36 -4
  174. package/src/lib/evolution/evolution-deployment-loader.js +117 -4
  175. package/src/lib/evolution/evolution-deployment-profile.js +2 -2
  176. package/src/lib/evolution/evolution-evidence-projector.js +18 -3
  177. package/src/lib/evolution/evolution-file-witness.js +10 -0
  178. package/src/lib/evolution/evolution-ledger.js +237 -28
  179. package/src/lib/evolution/evolution-windows-directory-durability.js +322 -0
  180. package/src/lib/evolution/governed-hub-llm.js +2 -1
  181. package/src/lib/evolution/governed-model-turn.js +4 -1
  182. package/src/lib/evolution/local-ui-evolution-deployment.js +768 -0
  183. package/src/lib/evolution/local-ui-evolution-host.mjs +44 -0
  184. package/src/lib/evolution/local-ui-evolution-runtime.js +624 -0
  185. package/src/lib/evolution/opaque-hex-id.js +28 -0
  186. package/src/lib/hub-llm-client.js +22 -14
  187. package/src/lib/image-input.js +19 -3
  188. package/src/lib/llm-config-defaults.js +19 -30
  189. package/src/lib/llm-connection-config.js +4 -0
  190. package/src/lib/llm-connection-probe.js +5 -1
  191. package/src/lib/llm-connection-resolution.js +94 -0
  192. package/src/lib/llm-providers.js +33 -11
  193. package/src/lib/ollama-base-url.js +14 -0
  194. package/src/lib/provider-protocol.js +104 -0
  195. package/src/lib/provider-stream.js +163 -4
  196. package/src/lib/runnable-provider.js +1 -0
  197. package/src/repl/chat-repl.js +58 -11
  198. package/src/runtime/agent-core.js +26 -43
  199. package/src/runtime/headless-runner.js +11 -4
  200. package/src/runtime/headless-stream.js +10 -4
  201. package/src/runtime/policies/agent-policy.js +17 -11
  202. package/src/assets/web-panel/assets/ChatBubbleRenderer-DOX3Y5EJ.js +0 -1
  203. package/src/assets/web-panel/assets/PdhVaultBrowser-D9McHF9I.js +0 -7
  204. package/src/assets/web-panel/assets/QuickAsk-DWG-A5zB.js +0 -1
  205. package/src/assets/web-panel/assets/devWarning-DYhpRtve.js +0 -1
  206. package/src/assets/web-panel/assets/index-DPJXFIY7.js +0 -1
  207. package/src/assets/web-panel/assets/index-DjBhR5M_.js +0 -1
@@ -0,0 +1,44 @@
1
+ // This single-file entrypoint is signed into a per-user local deployment.
2
+ // Command scopes remain explicit; a UI composition cannot borrow agent modes.
3
+ export async function createChainlessChainCommandDependencies({
4
+ commandName,
5
+ descriptor,
6
+ factories,
7
+ }) {
8
+ const modes = {
9
+ ui: [
10
+ "ws-llm-chat",
11
+ "ws-stream",
12
+ "ws-chat-session",
13
+ "legacy-websocket",
14
+ "intent-understand",
15
+ "intent-understand-stream",
16
+ "intent-classify",
17
+ ],
18
+ agent: ["interactive", "headless", "headless-stream"],
19
+ ask: ["ask"],
20
+ chat: ["chat", "chat-agent"],
21
+ };
22
+ if (
23
+ !Object.hasOwn(modes, commandName) ||
24
+ !descriptor.commands.includes(commandName)
25
+ )
26
+ throw new Error("Local Evolution host does not admit this command");
27
+ const createComposition = factories?.createLocalUiEvolutionComposition;
28
+ if (typeof createComposition !== "function") {
29
+ throw new Error("Local UI Evolution host factory is unavailable");
30
+ }
31
+ return Object.freeze({
32
+ evolutionCompositionFactory: async (context) => {
33
+ if (!modes[commandName].includes(context?.mode))
34
+ throw new Error(
35
+ "Local Evolution host does not admit this command mode",
36
+ );
37
+ return createComposition({
38
+ commandName,
39
+ context,
40
+ handlerArtifactDigest: descriptor.moduleDigest,
41
+ });
42
+ },
43
+ });
44
+ }
@@ -0,0 +1,624 @@
1
+ import {
2
+ createCipheriv,
3
+ createHash,
4
+ createHmac,
5
+ createPrivateKey,
6
+ createPublicKey,
7
+ randomBytes,
8
+ randomUUID,
9
+ sign,
10
+ verify,
11
+ } from "node:crypto";
12
+ import fs from "node:fs";
13
+ import { join, resolve } from "node:path";
14
+ import { resolveConfigDataRoot } from "../paths.js";
15
+ import { EVOLUTION_ARTIFACT_AUTHORITY_DECISION_SCHEMA } from "./evolution-artifact-ports.js";
16
+ import {
17
+ EVOLUTION_KEYED_COMMITMENT_SCHEMA,
18
+ EVOLUTION_PROJECTION_ATTESTATION_SCHEMA,
19
+ EVOLUTION_PROJECTION_ATTESTATION_VERIFICATION_SCHEMA,
20
+ EVOLUTION_RAW_STORAGE_POLICY_SCHEMA,
21
+ EVOLUTION_SOURCE_VERIFICATION_SCHEMA,
22
+ } from "./evolution-evidence-projector.js";
23
+ import { createAgentEvolutionRuntimeComposition } from "./agent-evolution-runtime-composition.js";
24
+ import { loadLocalUiEvolutionDeployment } from "./local-ui-evolution-deployment.js";
25
+ import { createLocalUiEvolutionFileSystem } from "./evolution-windows-directory-durability.js";
26
+ import {
27
+ formatLocalUiTenantReferenceId,
28
+ formatOpaqueHexId,
29
+ } from "./opaque-hex-id.js";
30
+
31
+ const SOURCE_ENVELOPE_SCHEMA = "chainlesschain.local-ui-source-envelope/v1";
32
+ const TRUST_POLICY_REVISION = 1;
33
+ const DECISION_TTL_MS = 30_000;
34
+
35
+ function localUiStateStorageRoot(root) {
36
+ const absoluteRoot = resolve(root);
37
+ if (
38
+ process.platform !== "darwin" ||
39
+ (absoluteRoot !== "/var" && !absoluteRoot.startsWith("/var/"))
40
+ ) {
41
+ return absoluteRoot;
42
+ }
43
+
44
+ // macOS exposes the temporary tree through the root-owned /var alias.
45
+ // Translate only that exact system prefix; leave the user-controlled suffix
46
+ // intact so ArtifactStore can still reject links below the deployment root.
47
+ const alias = fs.lstatSync("/var");
48
+ const canonical = fs.realpathSync("/var");
49
+ const target = fs.lstatSync(canonical);
50
+ if (
51
+ !alias.isSymbolicLink() ||
52
+ Number(alias.uid) !== 0 ||
53
+ canonical !== "/private/var" ||
54
+ target.isSymbolicLink() ||
55
+ !target.isDirectory() ||
56
+ Number(target.uid) !== 0 ||
57
+ (Number(target.mode) & 0o022) !== 0
58
+ ) {
59
+ throw new Error("Local Evolution host has an unsafe macOS /var alias");
60
+ }
61
+
62
+ return absoluteRoot === "/var"
63
+ ? canonical
64
+ : join(canonical, absoluteRoot.slice("/var/".length));
65
+ }
66
+
67
+ function canonical(value) {
68
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
69
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
70
+ return `{${Object.keys(value)
71
+ .sort()
72
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
73
+ .join(",")}}`;
74
+ }
75
+
76
+ function domainDigest(value, domain) {
77
+ return `sha256:${createHash("sha256")
78
+ .update(`${domain}\0${canonical(value)}`, "utf8")
79
+ .digest("hex")}`;
80
+ }
81
+
82
+ function sha256(value) {
83
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
84
+ }
85
+
86
+ function sourceRefSegment(value, purpose) {
87
+ return formatOpaqueHexId(
88
+ createHash("sha256")
89
+ .update(
90
+ `chainlesschain.local-ui-source-ref/v1\0${purpose}\0${value}`,
91
+ "utf8",
92
+ )
93
+ .digest("hex"),
94
+ );
95
+ }
96
+
97
+ function dateAfter(iso, durationMs = DECISION_TTL_MS) {
98
+ return new Date(Date.parse(iso) + durationMs).toISOString();
99
+ }
100
+
101
+ function encodedMessage(message) {
102
+ if (typeof message === "string") return Buffer.from(message, "utf8");
103
+ if (Buffer.isBuffer(message)) return message;
104
+ return Buffer.from(canonical(message), "utf8");
105
+ }
106
+
107
+ function ed25519Authority(label, tenantId, privateKeyPem) {
108
+ const privateKey = createPrivateKey(privateKeyPem);
109
+ const publicKey = createPublicKey(privateKey);
110
+ const trust = Object.freeze({
111
+ algorithm: "ed25519",
112
+ keyId: `local://${tenantId}/${label}`,
113
+ trustPolicyDigest: sha256(`chainlesschain.local-ui.${label}.policy/v1`),
114
+ });
115
+ return Object.freeze({
116
+ trust,
117
+ signer: Object.freeze({
118
+ sign({ message }) {
119
+ return {
120
+ ...trust,
121
+ value: sign(null, encodedMessage(message), privateKey).toString(
122
+ "base64url",
123
+ ),
124
+ };
125
+ },
126
+ }),
127
+ verifier: Object.freeze({
128
+ verify({ message, signature }) {
129
+ return (
130
+ signature?.algorithm === trust.algorithm &&
131
+ signature?.keyId === trust.keyId &&
132
+ signature?.trustPolicyDigest === trust.trustPolicyDigest &&
133
+ verify(
134
+ null,
135
+ encodedMessage(message),
136
+ publicKey,
137
+ Buffer.from(signature.value || "", "base64url"),
138
+ )
139
+ );
140
+ },
141
+ }),
142
+ });
143
+ }
144
+
145
+ function ed25519ArtifactAuthority(tenantId, privateKeyPem) {
146
+ const privateKey = createPrivateKey(privateKeyPem);
147
+ const publicKey = createPublicKey(privateKey);
148
+ const trust = Object.freeze({
149
+ algorithm: "ed25519",
150
+ keyId: `local://${tenantId}/artifact`,
151
+ trustPolicyDigest: sha256("chainlesschain.local-ui.artifact.policy/v1"),
152
+ });
153
+ return Object.freeze({
154
+ trust,
155
+ signer: Object.freeze({
156
+ sign({ message }) {
157
+ return {
158
+ algorithm: trust.algorithm,
159
+ keyId: trust.keyId,
160
+ value: sign(null, encodedMessage(message), privateKey).toString(
161
+ "base64url",
162
+ ),
163
+ };
164
+ },
165
+ }),
166
+ verifier: Object.freeze({
167
+ verify({ message, signature }) {
168
+ return (
169
+ signature?.algorithm === trust.algorithm &&
170
+ signature?.keyId === trust.keyId &&
171
+ verify(
172
+ null,
173
+ encodedMessage(message),
174
+ publicKey,
175
+ Buffer.from(signature.value || "", "base64url"),
176
+ )
177
+ );
178
+ },
179
+ }),
180
+ });
181
+ }
182
+
183
+ export function createLocalUiEvolutionAuthorities(identity, runId) {
184
+ const { tenantId, keys } = identity;
185
+ const commitmentKey = keys.commitment;
186
+ const rawKey = keys["raw-encryption"];
187
+ const sourcePrivateKey = createPrivateKey(keys.source.privateKey);
188
+ const sourcePublicKey = createPublicKey(sourcePrivateKey);
189
+ const attestationPrivateKey = createPrivateKey(keys.attestation.privateKey);
190
+ const attestationPublicKey = createPublicKey(attestationPrivateKey);
191
+ const attestationTrust = Object.freeze({
192
+ algorithm: "ed25519",
193
+ keyId: `local-ui-${tenantId}-attestation`,
194
+ trustPolicyDigest: sha256("chainlesschain.local-ui.attestation.policy/v1"),
195
+ });
196
+ const artifactAuthority = ed25519ArtifactAuthority(
197
+ tenantId,
198
+ keys.artifact.privateKey,
199
+ );
200
+
201
+ const sourceEnvelope = Object.freeze({
202
+ issue(request) {
203
+ if (
204
+ request?.tenantId !== tenantId ||
205
+ request.runId !== runId ||
206
+ typeof request.eventId !== "string" ||
207
+ !request.kind
208
+ ) {
209
+ throw new Error("Local UI source envelope request is not authorized");
210
+ }
211
+ const body = {
212
+ schema: SOURCE_ENVELOPE_SCHEMA,
213
+ tenantId,
214
+ runId: request.runId,
215
+ eventId: request.eventId,
216
+ kind: request.kind,
217
+ occurredAt: request.occurredAt,
218
+ evidenceDigest: domainDigest(
219
+ request.evidence,
220
+ "chainlesschain.evolution-raw-plaintext/v2",
221
+ ),
222
+ };
223
+ const payload = Buffer.from(canonical(body), "utf8").toString(
224
+ "base64url",
225
+ );
226
+ const signature = sign(
227
+ null,
228
+ Buffer.from(payload, "utf8"),
229
+ sourcePrivateKey,
230
+ ).toString("base64url");
231
+ return `cc-local-source:v1:${payload}.${signature}`;
232
+ },
233
+ });
234
+
235
+ const sourceVerifier = Object.freeze({
236
+ async verify(request) {
237
+ const match =
238
+ typeof request?.sourceEnvelope === "string"
239
+ ? /^cc-local-source:v1:([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)$/u.exec(
240
+ request.sourceEnvelope,
241
+ )
242
+ : null;
243
+ if (!match) throw new Error("Local UI source envelope is malformed");
244
+ const [encodedPayload, encodedSignature] = match.slice(1);
245
+ if (
246
+ !verify(
247
+ null,
248
+ Buffer.from(encodedPayload, "utf8"),
249
+ sourcePublicKey,
250
+ Buffer.from(encodedSignature, "base64url"),
251
+ )
252
+ ) {
253
+ throw new Error("Local UI source envelope signature rejected");
254
+ }
255
+ const body = JSON.parse(
256
+ Buffer.from(encodedPayload, "base64url").toString("utf8"),
257
+ );
258
+ const expectedInputDigest = domainDigest(
259
+ request.payload,
260
+ "chainlesschain.evolution-raw-plaintext/v2",
261
+ );
262
+ if (
263
+ body.schema !== SOURCE_ENVELOPE_SCHEMA ||
264
+ body.tenantId !== tenantId ||
265
+ body.runId !== runId ||
266
+ body.evidenceDigest !== expectedInputDigest ||
267
+ expectedInputDigest !== request.sourceInputDigest
268
+ ) {
269
+ throw new Error(
270
+ "Local UI source envelope is not bound to this evidence",
271
+ );
272
+ }
273
+ const sourceKind =
274
+ body.kind === "response-completed"
275
+ ? "model-inference"
276
+ : "user-statement";
277
+ const requestedAt = request.requestedAt;
278
+ const core = {
279
+ schema: EVOLUTION_SOURCE_VERIFICATION_SCHEMA,
280
+ verified: true,
281
+ sourceEnvelopeDigest: request.sourceEnvelopeDigest,
282
+ sourceInputDigest: request.sourceInputDigest,
283
+ tenantId,
284
+ principalId: "local-ui-user",
285
+ sourceKind,
286
+ trust: "untrusted",
287
+ authenticated: true,
288
+ sourceRef: `rollout://${formatLocalUiTenantReferenceId(tenantId)}/quick-ask/${sourceRefSegment(body.runId, "run")}/${sourceRefSegment(body.eventId, "event")}`,
289
+ sensitivity: "internal",
290
+ schemaDigest: null,
291
+ compilable: false,
292
+ trustedPayload: null,
293
+ requestNonce: request.requestNonce,
294
+ requestedAt,
295
+ checkedAt: requestedAt,
296
+ decisionExpiresAt: dateAfter(requestedAt),
297
+ verifierPolicyDigest: sha256(
298
+ "chainlesschain.local-ui.source-verifier.policy/v1",
299
+ ),
300
+ verifierPolicyRevision: TRUST_POLICY_REVISION,
301
+ schemaPolicyDigest: sha256(
302
+ "chainlesschain.local-ui.source-schema.policy/v1",
303
+ ),
304
+ schemaPolicyRevision: TRUST_POLICY_REVISION,
305
+ };
306
+ return {
307
+ ...core,
308
+ verificationReceiptDigest: domainDigest(
309
+ core,
310
+ "chainlesschain.evolution-source-verification/v1",
311
+ ),
312
+ };
313
+ },
314
+ });
315
+
316
+ const keyedCommitter = Object.freeze({
317
+ async commit(request) {
318
+ const commitment = (purpose, inputDigest) =>
319
+ `hmac-sha256:${createHmac("sha256", commitmentKey)
320
+ .update(`${tenantId}\0${purpose}\0${inputDigest}`, "utf8")
321
+ .digest("hex")}`;
322
+ const core = {
323
+ schema: EVOLUTION_KEYED_COMMITMENT_SCHEMA,
324
+ committed: true,
325
+ tenantId,
326
+ algorithm: "hmac-sha256",
327
+ keyId: `kms://${tenantId}/local-ui-commitment-v1`,
328
+ keyVersion: 1,
329
+ sourcePurpose: request.sourcePurpose,
330
+ sourceInputDigest: request.sourceInputDigest,
331
+ sourceCommitment: commitment(
332
+ request.sourcePurpose,
333
+ request.sourceInputDigest,
334
+ ),
335
+ trustedPayloadPurpose: request.trustedPayloadPurpose,
336
+ trustedPayloadInputDigest: request.trustedPayloadInputDigest,
337
+ trustedPayloadCommitment:
338
+ request.trustedPayloadInputDigest === null
339
+ ? null
340
+ : commitment(
341
+ request.trustedPayloadPurpose,
342
+ request.trustedPayloadInputDigest,
343
+ ),
344
+ requestNonce: request.requestNonce,
345
+ requestedAt: request.requestedAt,
346
+ checkedAt: request.requestedAt,
347
+ decisionExpiresAt: dateAfter(request.requestedAt),
348
+ policyDigest: sha256("chainlesschain.local-ui.commitment.policy/v1"),
349
+ policyRevision: TRUST_POLICY_REVISION,
350
+ };
351
+ return {
352
+ ...core,
353
+ commitmentReceiptDigest: domainDigest(
354
+ core,
355
+ "chainlesschain.evolution-keyed-commitment/v1",
356
+ ),
357
+ };
358
+ },
359
+ });
360
+
361
+ const storagePolicy = Object.freeze({
362
+ async resolve(request) {
363
+ const allowed =
364
+ request.tenantId === tenantId &&
365
+ request.principalId === "local-ui-user" &&
366
+ ["user-statement", "model-inference"].includes(request.sourceKind) &&
367
+ request.sensitivity !== "restricted";
368
+ if (!allowed) throw new Error("Local UI evidence storage policy denied");
369
+ const core = {
370
+ schema: EVOLUTION_RAW_STORAGE_POLICY_SCHEMA,
371
+ allowed: true,
372
+ tenantId,
373
+ principalId: request.principalId,
374
+ sourceKind: request.sourceKind,
375
+ sourceCommitment: request.sourceCommitment,
376
+ commitmentReceiptDigest: request.commitmentReceiptDigest,
377
+ sourceVerificationReceiptDigest:
378
+ request.sourceVerificationReceiptDigest,
379
+ sensitivity: request.sensitivity,
380
+ retention: {
381
+ expiresAt: new Date(
382
+ Date.now() + 365 * 24 * 60 * 60 * 1000,
383
+ ).toISOString(),
384
+ deletionClass: "user-delete",
385
+ },
386
+ acl: [request.principalId, "service-evolution"],
387
+ requestNonce: request.requestNonce,
388
+ requestedAt: request.requestedAt,
389
+ checkedAt: request.requestedAt,
390
+ decisionExpiresAt: dateAfter(request.requestedAt),
391
+ policyDigest: sha256("chainlesschain.local-ui.storage.policy/v1"),
392
+ policyRevision: TRUST_POLICY_REVISION,
393
+ };
394
+ return {
395
+ ...core,
396
+ policyReceiptDigest: domainDigest(
397
+ core,
398
+ "chainlesschain.evolution-raw-storage-policy/v1",
399
+ ),
400
+ };
401
+ },
402
+ });
403
+
404
+ const rawEncryptor = Object.freeze({
405
+ async encrypt({ aad, plaintext }) {
406
+ const iv = randomBytes(12);
407
+ const cipher = createCipheriv("aes-256-gcm", rawKey, iv);
408
+ cipher.setAAD(aad);
409
+ const ciphertext = Buffer.concat([
410
+ cipher.update(plaintext),
411
+ cipher.final(),
412
+ ]);
413
+ return {
414
+ algorithm: "aes-256-gcm",
415
+ keyRef: `kms://${tenantId}/local-ui-raw-v1`,
416
+ sealedBytes: Buffer.concat([iv, cipher.getAuthTag(), ciphertext]),
417
+ };
418
+ },
419
+ });
420
+
421
+ const signedAttestationCore = (input) => ({
422
+ schema: EVOLUTION_PROJECTION_ATTESTATION_SCHEMA,
423
+ algorithm: attestationTrust.algorithm,
424
+ keyId: attestationTrust.keyId,
425
+ issuer: `local-ui-${tenantId}-evolution-projector`,
426
+ trustPolicyDigest: attestationTrust.trustPolicyDigest,
427
+ receiptDigest: input.receiptDigest,
428
+ tenantId: input.tenantId,
429
+ evidenceId: input.evidenceId,
430
+ });
431
+ const attestationSigner = Object.freeze({
432
+ async sign(input) {
433
+ const core = signedAttestationCore(input);
434
+ const attested = {
435
+ ...core,
436
+ signature: sign(
437
+ null,
438
+ Buffer.from(canonical(core), "utf8"),
439
+ attestationPrivateKey,
440
+ ).toString("base64url"),
441
+ };
442
+ return {
443
+ ...attested,
444
+ attestationDigest: domainDigest(
445
+ attested,
446
+ "chainlesschain.evolution-projection-attestation/v1",
447
+ ),
448
+ };
449
+ },
450
+ });
451
+ const attestationVerifier = Object.freeze({
452
+ async verify(value, expected) {
453
+ const core = { ...value };
454
+ delete core.signature;
455
+ delete core.attestationDigest;
456
+ if (
457
+ !verify(
458
+ null,
459
+ Buffer.from(canonical(core), "utf8"),
460
+ attestationPublicKey,
461
+ Buffer.from(value.signature || "", "base64url"),
462
+ ) ||
463
+ value.attestationDigest !== expected.attestationDigest
464
+ ) {
465
+ throw new Error("Local UI projection attestation rejected");
466
+ }
467
+ const decision = {
468
+ schema: EVOLUTION_PROJECTION_ATTESTATION_VERIFICATION_SCHEMA,
469
+ verified: true,
470
+ attestationDigest: value.attestationDigest,
471
+ receiptDigest: value.receiptDigest,
472
+ tenantId: value.tenantId,
473
+ evidenceId: value.evidenceId,
474
+ issuer: value.issuer,
475
+ keyId: value.keyId,
476
+ trustPolicyDigest: value.trustPolicyDigest,
477
+ trustPolicyRevision: TRUST_POLICY_REVISION,
478
+ requestNonce: expected.requestNonce,
479
+ requestedAt: expected.requestedAt,
480
+ checkedAt: expected.requestedAt,
481
+ decisionExpiresAt: dateAfter(expected.requestedAt),
482
+ };
483
+ return {
484
+ ...decision,
485
+ verificationReceiptDigest: domainDigest(
486
+ decision,
487
+ "chainlesschain.evolution-attestation-verification/v1",
488
+ ),
489
+ };
490
+ },
491
+ });
492
+
493
+ return Object.freeze({
494
+ tenantId,
495
+ authorities: Object.freeze({
496
+ artifact: Object.freeze({
497
+ envelopeSigner: artifactAuthority.signer,
498
+ envelopeVerifier: artifactAuthority.verifier,
499
+ currentAuthorityResolver: Object.freeze({
500
+ resolve(request) {
501
+ const now = request.requestedAt;
502
+ const core = {
503
+ action: request.action,
504
+ algorithm: artifactAuthority.trust.algorithm,
505
+ allowed: true,
506
+ audience: request.audience,
507
+ checkedAt: now,
508
+ decisionExpiresAt: dateAfter(now),
509
+ digest: request.digest,
510
+ issuedAt: request.issuedAt,
511
+ issuedPolicyDigest: request.issuedPolicyDigest,
512
+ issuedPolicyRevision: request.issuedPolicyRevision,
513
+ issuedPolicyTrusted: true,
514
+ keyId: request.keyId || artifactAuthority.trust.keyId,
515
+ policyDigest: artifactAuthority.trust.trustPolicyDigest,
516
+ policyRevision: TRUST_POLICY_REVISION,
517
+ purpose: request.purpose,
518
+ requestedAt: now,
519
+ retention: request.retention,
520
+ revocationRevision: TRUST_POLICY_REVISION,
521
+ revoked: false,
522
+ schema: EVOLUTION_ARTIFACT_AUTHORITY_DECISION_SCHEMA,
523
+ tenantId: request.tenantId,
524
+ type: request.type,
525
+ };
526
+ return {
527
+ ...core,
528
+ receiptDigest: domainDigest(
529
+ core,
530
+ "chainlesschain.evolution-artifact-authority-decision/v1",
531
+ ),
532
+ };
533
+ },
534
+ }),
535
+ }),
536
+ ledger: ed25519Authority("ledger", tenantId, keys.ledger.privateKey),
537
+ witness: ed25519Authority("witness", tenantId, keys.witness.privateKey),
538
+ rawEncryptor,
539
+ sourceEnvelope,
540
+ sourceVerifier,
541
+ keyedCommitter,
542
+ storagePolicy,
543
+ attestationSigner,
544
+ attestationVerifier,
545
+ }),
546
+ });
547
+ }
548
+
549
+ export async function createLocalUiEvolutionComposition({
550
+ context,
551
+ commandName = "ui",
552
+ handlerArtifactDigest,
553
+ localDirectory,
554
+ env = process.env,
555
+ cwd = process.cwd(),
556
+ } = {}) {
557
+ if (
558
+ !(
559
+ {
560
+ ui: [
561
+ "ws-llm-chat",
562
+ "ws-stream",
563
+ "ws-chat-session",
564
+ "legacy-websocket",
565
+ "intent-understand",
566
+ "intent-understand-stream",
567
+ "intent-classify",
568
+ ],
569
+ agent: ["interactive", "headless", "headless-stream"],
570
+ ask: ["ask"],
571
+ chat: ["chat", "chat-agent"],
572
+ }[commandName] || []
573
+ ).includes(context?.mode) ||
574
+ typeof context.runId !== "string" ||
575
+ !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u.test(context.runId)
576
+ ) {
577
+ throw new Error(
578
+ "Local Evolution host does not admit this command mode or Run",
579
+ );
580
+ }
581
+ const root =
582
+ localDirectory ||
583
+ join(resolveConfigDataRoot().path, "evolution", "local-ui-host");
584
+ const deployment = await loadLocalUiEvolutionDeployment(root, { env, cwd });
585
+ if (!deployment.descriptor.commands.includes(commandName))
586
+ throw new Error("Local Evolution deployment does not admit this command");
587
+ if (deployment.moduleDigest !== handlerArtifactDigest) {
588
+ throw new Error(
589
+ "Local Evolution handler digest does not match its signed deployment",
590
+ );
591
+ }
592
+ const identity = createLocalUiEvolutionAuthorities(deployment, context.runId);
593
+ const ledger = ed25519Authority(
594
+ "ledger",
595
+ deployment.tenantId,
596
+ deployment.keys.ledger.privateKey,
597
+ );
598
+ const witness = ed25519Authority(
599
+ "witness",
600
+ deployment.tenantId,
601
+ deployment.keys.witness.privateKey,
602
+ );
603
+ const fsImpl = createLocalUiEvolutionFileSystem();
604
+ const stateRootDir = join(localUiStateStorageRoot(root), "state");
605
+ return createAgentEvolutionRuntimeComposition({
606
+ tenantId: identity.tenantId,
607
+ runId: context.runId,
608
+ audience:
609
+ commandName === "ui" ? "local-ui-quick-ask" : `local-${commandName}`,
610
+ stateRootDir,
611
+ witnessId: `local-ui-witness-${identity.tenantId}`,
612
+ authorities: {
613
+ ...identity.authorities,
614
+ ledger,
615
+ witness,
616
+ },
617
+ evidenceIdGenerator: async () =>
618
+ `evidence-${formatOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
619
+ ingressIdGenerator: () =>
620
+ `ingress-${formatOpaqueHexId(randomUUID().replaceAll("-", ""))}`,
621
+ secure: true,
622
+ fsImpl,
623
+ });
624
+ }
@@ -0,0 +1,28 @@
1
+ const OPAQUE_HEX_GROUP_LENGTH = 8;
2
+
3
+ export function formatOpaqueHexId(value) {
4
+ if (typeof value !== "string" || !/^[a-f0-9]+$/u.test(value)) {
5
+ throw new TypeError(
6
+ "Opaque ID must be a non-empty lowercase hexadecimal string",
7
+ );
8
+ }
9
+
10
+ const groups = [];
11
+ for (
12
+ let offset = 0;
13
+ offset < value.length;
14
+ offset += OPAQUE_HEX_GROUP_LENGTH
15
+ ) {
16
+ groups.push(value.slice(offset, offset + OPAQUE_HEX_GROUP_LENGTH));
17
+ }
18
+ return groups.join("-");
19
+ }
20
+
21
+ export function formatLocalUiTenantReferenceId(value) {
22
+ const prefix = "local-ui-";
23
+ if (typeof value !== "string" || !value.startsWith(prefix)) return value;
24
+
25
+ const hex = value.slice(prefix.length).replaceAll("-", "");
26
+ if (!/^[a-f0-9]{32}$/u.test(hex)) return value;
27
+ return `${prefix}${formatOpaqueHexId(hex)}`;
28
+ }