@noxsoft/anima 7.0.0 → 7.0.2

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 (431) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/{accounts-COxGcLGB.js → accounts-BDDwy6Zf.js} +3 -3
  3. package/dist/{accounts-DIES085e.js → accounts-BNxfCMUW.js} +5 -5
  4. package/dist/{accounts-B_2nt5Cd.js → accounts-BzjBXZYp.js} +2 -2
  5. package/dist/{accounts-TUsfghIW.js → accounts-PpT_kved.js} +11 -11
  6. package/dist/{acp-cli-CWgqDQQH.js → acp-cli-B4F3SojX.js} +12 -12
  7. package/dist/{acp-cli-D0wfP8-l.js → acp-cli-CtTTCUan.js} +15 -15
  8. package/dist/{active-listener-BKtsWhsb.js → active-listener-C3NdbMQD.js} +4 -4
  9. package/dist/{active-listener-gGCoq55D.js → active-listener-D78ohfOc.js} +6 -6
  10. package/dist/{active-listener-D2r8IO7g.js → active-listener-Z1MbDSH8.js} +4 -4
  11. package/dist/{agent-paths-niQrLbGc.js → agent-paths-DRAU6-8h.js} +2 -2
  12. package/dist/{agent-scope-Dm8IL1Ks.js → agent-scope-B1FoYHfj.js} +3 -3
  13. package/dist/{agent-scope-CxBzAozu.js → agent-scope-CENGF2NR.js} +2 -2
  14. package/dist/{agent-scope-CXxC8FFX.js → agent-scope-DQ0N_hof.js} +2 -2
  15. package/dist/{agent-scope-ByIGrCTT.js → agent-scope-DpgOQfBp.js} +2 -2
  16. package/dist/{agent-DuW0onwk.js → agent-viQL5eva.js} +18 -18
  17. package/dist/{agent-BoAAHGEA.js → agent-xPFmA8jO.js} +14 -14
  18. package/dist/{agents-BUXkSDns.js → agents-Cf9QArEB.js} +20 -20
  19. package/dist/{agents.config-BR5JLtud.js → agents.config-DrViQjgD.js} +1 -1
  20. package/dist/{agents.config-Br4ULmK0.js → agents.config-sfN0WxRg.js} +1 -1
  21. package/dist/{anthropic-direct-runner-DizCei79.js → anthropic-direct-runner-BNiXWmel.js} +257 -21
  22. package/dist/{anthropic-direct-runner-OjcTAH6g.js → anthropic-direct-runner-BgFOZS8B.js} +257 -21
  23. package/dist/{audit-DDz7UOIx.js → audit-CupURkPI.js} +15 -15
  24. package/dist/{audit-B05W5ckN.js → audit-jddM3B16.js} +13 -13
  25. package/dist/{auth-Cp__MMeO.js → auth-Bw3NUKcg.js} +2 -2
  26. package/dist/{auth-DsC5pZ_0.js → auth-CuGlMw6p.js} +2 -2
  27. package/dist/{auth-choice-B1iGnjuE.js → auth-choice-D3Gi-WXq.js} +7 -7
  28. package/dist/{auth-choice-HF9x6xk2.js → auth-choice-DmkqIhe6.js} +7 -7
  29. package/dist/{auth-health-Cc8-vy8y.js → auth-health-CIbz_YRs.js} +1 -1
  30. package/dist/{auth-health-Dhr8p2SD.js → auth-health-CPmz2ZaM.js} +1 -1
  31. package/dist/{auth-profiles-DKu7ZUzl.js → auth-profiles-D0T20ZZX.js} +4 -4
  32. package/dist/{auth-profiles-C-LuhW6c.js → auth-profiles-XI2YBb-w.js} +3 -3
  33. package/dist/{auth-profiles-Brxz2ojJ.js → auth-profiles-fjGcjhZ4.js} +5 -5
  34. package/dist/{auth-profiles-DtWUl1-k.js → auth-profiles-jK6n3-Cc.js} +4 -4
  35. package/dist/{auth-store-Bd0GoqEL.js → auth-store-B4pR_KvR.js} +3 -3
  36. package/dist/{auth-store-BEfSfCbW.js → auth-store-ChpmVcla.js} +3 -3
  37. package/dist/{auth-store-Jvgz2_l1.js → auth-store-CqgKZB5l.js} +2 -2
  38. package/dist/{auth-store-BpYI9t_y.js → auth-store-po566wly.js} +3 -3
  39. package/dist/{banner-Dpa5d1If.js → banner-C_CFUih5.js} +2 -2
  40. package/dist/{bonjour-discovery-D3HFfTyG.js → bonjour-discovery-B8gxGE91.js} +2 -2
  41. package/dist/{bonjour-discovery-Co-b97Dz.js → bonjour-discovery-CZGDX9Ac.js} +2 -2
  42. package/dist/build-info.json +3 -3
  43. package/dist/bundled/boot-md/handler.js +23 -23
  44. package/dist/bundled/bootstrap-extra-files/handler.js +3 -3
  45. package/dist/bundled/session-memory/handler.js +22 -22
  46. package/dist/{call-CDPbPDAr.js → call-Bn9iKzhX.js} +4 -4
  47. package/dist/{call-B4lhqS6H.js → call-CPfxv8yg.js} +5 -5
  48. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  49. package/dist/{catalog-CsXv59Tq.js → catalog-BqTvQqiq.js} +2 -2
  50. package/dist/{catalog-B-TAbJ2o.js → catalog-yCaNFdQz.js} +2 -2
  51. package/dist/{channel-web-C5mzsaa3.js → channel-web-CfKNWfmp.js} +14 -14
  52. package/dist/{channels-status-issues-WkG3Tmxk.js → channels-status-issues-4XxYhQ8S.js} +4 -4
  53. package/dist/{channels-status-issues-CklLFAsD.js → channels-status-issues-BhtWH-p2.js} +4 -4
  54. package/dist/{chrome-CmxIwwsr.js → chrome-CN17GbZN.js} +2 -2
  55. package/dist/{chrome-BaU-H9m7.js → chrome-CVb9kRv_.js} +3 -3
  56. package/dist/{chrome-DLUBPBPz.js → chrome-D1Jm1Bdq.js} +9 -9
  57. package/dist/{chrome-B8EnYGj1.js → chrome-Nb2mFwWK.js} +2 -2
  58. package/dist/{chunk-B4Kx-ocg.js → chunk-B5s6o9xI.js} +3 -3
  59. package/dist/{chunk-DJXDX69U.js → chunk-CTS4rIMF.js} +3 -3
  60. package/dist/{chunk-CFSle8n5.js → chunk-DXTTGmeb.js} +1 -1
  61. package/dist/{chunk-Cy0Bj0F3.js → chunk-isZGJq82.js} +1 -1
  62. package/dist/{clack-prompter-0JW5kry0.js → clack-prompter-ChmRN__Q.js} +5 -5
  63. package/dist/{clack-prompter-fZSmnHda.js → clack-prompter-CpLhDhPM.js} +5 -5
  64. package/dist/cli/daemon-cli.js +1 -1
  65. package/dist/{cli-Cuq4bIg4.js → cli-BzTZ8Gnp.js} +46 -46
  66. package/dist/{cli-X9ikywQ3.js → cli-DLYU8BVw.js} +47 -47
  67. package/dist/{client-CfLiulzK.js → client-DNDzi0CF.js} +3 -3
  68. package/dist/{client-BWkoTfOH.js → client-DOTb2PN6.js} +2 -2
  69. package/dist/{command-format-kLw3YIIu.js → command-format-JgYolX96.js} +1 -1
  70. package/dist/{command-registry-9V4uqrBV.js → command-registry-BQiM7sYY.js} +14 -14
  71. package/dist/{commands-Mekaw9WG.js → commands-CymFClS1.js} +2 -2
  72. package/dist/{common-DT_obM-k.js → common-C36MniDL.js} +2 -2
  73. package/dist/{common-Bf_TG87Y.js → common-Coq2knq6.js} +2 -2
  74. package/dist/{common-BCW6hLGI.js → common-c4ZysSLq.js} +2 -2
  75. package/dist/{common-CqIa2poH.js → common-fIXNXke2.js} +2 -2
  76. package/dist/{completion-cli-DNWDwhab.js → completion-cli-BP8IIlzX.js} +4 -4
  77. package/dist/{completion-cli-BtvcR-U5.js → completion-cli-DW87AQgF.js} +3 -3
  78. package/dist/{config-ZYN8tezd.js → config-C7s14LLE.js} +24 -6
  79. package/dist/{config-BrVuTQ8R.js → config-DmsfUvB0.js} +24 -6
  80. package/dist/{config-CweTwOtr.js → config-LS-6Xgqo.js} +25 -7
  81. package/dist/{config-DaD4FsAn.js → config-MZ5clMl4.js} +25 -7
  82. package/dist/config-cli-BAj5hoD-.js +15 -0
  83. package/dist/config-cli-C6WtFt5S.js +11 -0
  84. package/dist/{config-guard-C4b2dksv.js → config-guard-BAVhoqm4.js} +5 -5
  85. package/dist/{config-guard-CWhoBtB3.js → config-guard-Em1kJe5K.js} +19 -19
  86. package/dist/{configure-B2Mfnwy_.js → configure-CHPG13jf.js} +24 -24
  87. package/dist/{configure-SnvMHZPD.js → configure-Dd6zJb-q.js} +72 -72
  88. package/dist/{configure-ZWxixuRA.js → configure-DeLC66Op.js} +25 -25
  89. package/dist/{configure-lkozxQed.js → configure-i6y1krRa.js} +75 -75
  90. package/dist/{context-mdxDsO1v.js → context-C22AMgsc.js} +10 -2
  91. package/dist/{control-service-5YtMvm7D.js → control-service-BGnqY7vv.js} +5 -5
  92. package/dist/{control-service-3CI4vt1h.js → control-service-DxuB_IUw.js} +4 -4
  93. package/dist/control-ui/assets/{index-D4wqLVMN.js → index-XnQwXlLu.js} +2 -2
  94. package/dist/control-ui/assets/{index-D4wqLVMN.js.map → index-XnQwXlLu.js.map} +1 -1
  95. package/dist/control-ui/assets/index-tpHK2r2n.js +73 -0
  96. package/dist/control-ui/assets/index-tpHK2r2n.js.map +1 -0
  97. package/dist/control-ui/assets/{observers-B7MfWiIZ.js → observers-DqhQeBfm.js} +2 -2
  98. package/dist/control-ui/assets/{observers-B7MfWiIZ.js.map → observers-DqhQeBfm.js.map} +1 -1
  99. package/dist/control-ui/index.html +1 -1
  100. package/dist/{cron-cli-D9XrF-Gx.js → cron-cli-3VJy8n6n.js} +14 -14
  101. package/dist/{cron-cli-Cht6Itx6.js → cron-cli-C_B6qK9W.js} +16 -16
  102. package/dist/{daemon-cli-CrdPhSxr.js → daemon-cli-B0IaAuYH.js} +15 -15
  103. package/dist/{daemon-cli-9zrnYRjh.js → daemon-cli-BsY7ZPql.js} +17 -17
  104. package/dist/daemon-cli.js +19 -1
  105. package/dist/{daemon-runtime-BrUj88ZO.js → daemon-runtime-BoIX72LK.js} +2 -2
  106. package/dist/{daemon-runtime-DCqwraWR.js → daemon-runtime-DSPDxySK.js} +3 -3
  107. package/dist/{deliver-d-CaN0uL.js → deliver-BJ0JgwhN.js} +11 -11
  108. package/dist/{deliver-BKzX3YoN.js → deliver-BknvuSJz.js} +6 -6
  109. package/dist/{deliver-C1L5nO0K.js → deliver-D86kSZGn.js} +7 -7
  110. package/dist/{deliver-B-dPbUIs.js → deliver-bdH5itRb.js} +11 -11
  111. package/dist/{delivery-queue-BKQk1j0k.js → delivery-queue-B6Y3ea25.js} +1 -1
  112. package/dist/{deps-BKLIBKjK.js → deps-oUYlT5Sq.js} +1 -1
  113. package/dist/{diagnostics-BhSwKIcN.js → diagnostics-CXoISuYV.js} +1 -1
  114. package/dist/{diagnostics-Cu9pZAFn.js → diagnostics-aUz0rVDl.js} +1 -1
  115. package/dist/{dispatcher-BQQugU-7.js → dispatcher-DpEzmU7s.js} +2 -2
  116. package/dist/{dispatcher-DzwzLQRk.js → dispatcher-DwlzWX2u.js} +2 -2
  117. package/dist/{dns-cli-V2bo6vSt.js → dns-cli-D8tgv6zY.js} +10 -10
  118. package/dist/{dns-cli-C8KIX4P3.js → dns-cli-DawPrcXI.js} +12 -12
  119. package/dist/{docs-cli-D2cVJxjP.js → docs-cli-Bsx7Yd3w.js} +6 -6
  120. package/dist/{docs-cli-Bt-YV3xs.js → docs-cli-DnrE1Tqw.js} +7 -7
  121. package/dist/{doctor-D7kKyUVk.js → doctor-BNJ_edyo.js} +36 -36
  122. package/dist/{doctor-DmCnZ-jF.js → doctor-DY5SmosG.js} +38 -38
  123. package/dist/{doctor-completion-B9SBdMoR.js → doctor-completion-B6lgyLsC.js} +2 -2
  124. package/dist/{doctor-completion-BBvW4_J9.js → doctor-completion-mLt2Eo9P.js} +3 -3
  125. package/dist/{doctor-config-flow-Dxy7RIm0.js → doctor-config-flow-CvJDaN8t.js} +8 -8
  126. package/dist/{doctor-config-flow-Bgl0Cc20.js → doctor-config-flow-D7SJ61Ak.js} +6 -6
  127. package/dist/{engine-zmn3SOYa.js → engine-BDwYEVKi.js} +1 -1
  128. package/dist/{engine-DpbYPop7.js → engine-BrJ7eiEh.js} +2 -2
  129. package/dist/entry.js +18 -2
  130. package/dist/{env-DlTia1B4.js → env-ByZjwIJR.js} +1 -1
  131. package/dist/{exec-DUzVF5_D.js → exec-4nfFrGP7.js} +1 -1
  132. package/dist/{exec-BylR5qWS.js → exec-ChPERQB-.js} +1 -1
  133. package/dist/{exec-BtBJICHE.js → exec-CigQCGkt.js} +1 -1
  134. package/dist/{exec-C6tXfeqA.js → exec-Dn2S9A7x.js} +1 -1
  135. package/dist/{exec-approvals-cli-DYZVBnqS.js → exec-approvals-cli-DmdfF4o_.js} +17 -17
  136. package/dist/{exec-approvals-cli-CN2WeH7y.js → exec-approvals-cli-c5UsPAU1.js} +19 -19
  137. package/dist/extensionAPI.js +21 -21
  138. package/dist/{gateway-cli-iumkTohn.js → gateway-cli-C4FwPHzu.js} +95 -95
  139. package/dist/{gateway-cli-CEM1vBuk.js → gateway-cli-CkFzsHGb.js} +93 -93
  140. package/dist/{gateway-rpc-Cj_h2sVM.js → gateway-rpc-CgeNTQUV.js} +3 -3
  141. package/dist/{gateway-rpc-CnXMGsxp.js → gateway-rpc-DpVHyNFu.js} +3 -3
  142. package/dist/{gmail-setup-utils-BIXtKTpT.js → gmail-setup-utils-JWsjxDBV.js} +4 -4
  143. package/dist/{gmail-setup-utils-DaJoXV_3.js → gmail-setup-utils-lTV5KDPF.js} +3 -3
  144. package/dist/{health-Cndq9b7A.js → health-C6rAUaME.js} +13 -13
  145. package/dist/{health-B5N6_UOf.js → health-DrokrOLQ.js} +13 -13
  146. package/dist/{health-format-BLnFZCH_.js → health-format-CTOsgiTE.js} +2 -2
  147. package/dist/{health-format-D-JJ5_S4.js → health-format-CzNDrJ8Z.js} +2 -2
  148. package/dist/{heartbeat-visibility-BQL13ZBH.js → heartbeat-visibility-DZOi_4uV.js} +1 -1
  149. package/dist/{heartbeat-visibility-CwcYugaR.js → heartbeat-visibility-XbT4yJ3S.js} +1 -1
  150. package/dist/{help-format-ZKxl6UCb.js → help-format-D4k6gkpr.js} +1 -1
  151. package/dist/{help-format-Dt-I_Mls.js → help-format-DgCbU9Ow.js} +1 -1
  152. package/dist/{hooks-cli-DSlPBQSY.js → hooks-cli-CBDm7Rjw.js} +50 -50
  153. package/dist/{hooks-cli-BZcvdIwE.js → hooks-cli-DVNU2Cp_.js} +53 -53
  154. package/dist/{hooks-status-DdweuSIj.js → hooks-status-BQ7M6bIq.js} +3 -3
  155. package/dist/{hooks-status-DqfJDvYl.js → hooks-status-BlfmlpC0.js} +3 -3
  156. package/dist/{image-ops-Ct3GueyT.js → image-ops-flDr58qn.js} +2 -2
  157. package/dist/{image-ops-BdrMmiG4.js → image-ops-vj06APeW.js} +2 -2
  158. package/dist/index.js +62 -62
  159. package/dist/{installs-a4Vz_J08.js → installs-BeTulUkL.js} +4 -4
  160. package/dist/{installs-Bi6UipiE.js → installs-D1t93Dvw.js} +4 -4
  161. package/dist/{lifecycle-core-BTICG85s.js → lifecycle-core-DmxyMl5D.js} +5 -5
  162. package/dist/{lifecycle-core-B8PI1NZJ.js → lifecycle-core-OpMLwiRd.js} +3 -3
  163. package/dist/{links-DcilUrqq.js → links-NDZcSjct.js} +1 -1
  164. package/dist/{links-BjjDMNIq.js → links-RNygAKHu.js} +1 -1
  165. package/dist/llm-slug-generator.js +22 -22
  166. package/dist/{loader-C87TLS4J.js → loader-5Xm3Jxa-.js} +1 -1
  167. package/dist/{logging-Chc1Sj6N.js → logging-B0-OIfnO.js} +1 -1
  168. package/dist/{logging-_rCcBkls.js → logging-CX0bU2da.js} +2 -2
  169. package/dist/{login-DKkQ3Czu.js → login-BegD27g3.js} +5 -5
  170. package/dist/{login-MzVPMRxL.js → login-CSaeXIuW.js} +7 -7
  171. package/dist/{login-BTOKtSQN.js → login-WPS724ji.js} +5 -5
  172. package/dist/{login-qr-BGbHImRb.js → login-qr-BVzUWR4o.js} +8 -8
  173. package/dist/{login-qr-CxRI-tE2.js → login-qr-C0eNn9SV.js} +12 -12
  174. package/dist/{login-qr-OUAGpDsU.js → login-qr-DLLfBALk.js} +9 -9
  175. package/dist/{login-qr-BjpDVBJE.js → login-qr-updBsqHf.js} +10 -10
  176. package/dist/{login-BEaBOSnw.js → login-zlt00-k4.js} +6 -6
  177. package/dist/{logs-cli-BiAJbjnq.js → logs-cli-BDQoLnjr.js} +15 -15
  178. package/dist/{logs-cli-Bc6IOyHA.js → logs-cli-BPljn0BF.js} +14 -14
  179. package/dist/{manager-BYu34CX3.js → manager-B-8JFgek.js} +27 -11
  180. package/dist/{manager-C6L_DH0O.js → manager-CE4cPrH2.js} +7 -7
  181. package/dist/{manager-D8VCuzru.js → manager-DPojhf1D.js} +9 -9
  182. package/dist/{manager-b_aZwo00.js → manager-kKsAUV03.js} +27 -11
  183. package/dist/{manifest-registry-W_OfCIRP.js → manifest-registry-BAebAG4I.js} +1 -1
  184. package/dist/{manifest-registry-qF960vMH.js → manifest-registry-Cwvk5hu8.js} +1 -1
  185. package/dist/{memory-cli-B0kKl-9T.js → memory-cli-BJmQIQSw.js} +11 -11
  186. package/dist/{memory-cli-DLtBA6r5.js → memory-cli-DMJqELwh.js} +10 -10
  187. package/dist/{message-channel-CMsexA3K.js → message-channel-CfwNj8mC.js} +1 -1
  188. package/dist/{message-channel-DIHHKJhk.js → message-channel-qmDI9YoO.js} +1 -1
  189. package/dist/{model-auth-KpsOXKDc.js → model-auth-BCdRf282.js} +19 -3
  190. package/dist/{model-auth-CHB3EySM.js → model-auth-Bs6eoQ1l.js} +19 -3
  191. package/dist/{model-selection-CLcoOT3e.js → model-selection-BhIAjo3P.js} +1 -1
  192. package/dist/{model-selection-DjsJGv1R.js → model-selection-CVcwboE_.js} +1 -1
  193. package/dist/{model-selection-CY6r_3wt.js → model-selection-CeZMza3d.js} +1 -1
  194. package/dist/{model-selection-DcO3qJOu.js → model-selection-f7lJs3PI.js} +1 -1
  195. package/dist/{models-CdNeYfSp.js → models-DxRmPLiS.js} +97 -50
  196. package/dist/{models-cli-D7eSsPuk.js → models-cli-B9KClY_f.js} +137 -82
  197. package/dist/{models-cli-fTZXo1zx.js → models-cli-BFZLSACV.js} +66 -58
  198. package/dist/{node-cli-DU_oREff.js → node-cli-BetF7Xgf.js} +26 -26
  199. package/dist/{node-cli-BmuVEJ1C.js → node-cli-CLeoh03V.js} +25 -25
  200. package/dist/{node-service-qZXF7T7A.js → node-service-FcwxyU8p.js} +1 -1
  201. package/dist/{node-service-cOoW5hLa.js → node-service-zrXojq7t.js} +1 -1
  202. package/dist/{note-CeLGcHqv.js → note-BTIeG9az.js} +1 -1
  203. package/dist/{note-iMYVGjpA.js → note-D_tpPSPO.js} +2 -2
  204. package/dist/{noxsoft-bootstrap-CrlkSFzd.js → noxsoft-bootstrap-CPS7t3Sk.js} +3 -3
  205. package/dist/{noxsoft-bootstrap-C4dSx7K_.js → noxsoft-bootstrap-CqygYIJa.js} +2 -2
  206. package/dist/{npm-registry-spec-jf7Mowdn.js → npm-registry-spec-BeX9-fmS.js} +1 -1
  207. package/dist/{npm-registry-spec-Br4B4I_3.js → npm-registry-spec-DB6xqMfu.js} +1 -1
  208. package/dist/{onboard-C5K37NvY.js → onboard-BZORsTGE.js} +18 -18
  209. package/dist/{onboard-D-6QCnTi.js → onboard-Doq7UgRV.js} +19 -19
  210. package/dist/{onboard-channels-BsCq32Hn.js → onboard-channels-BREQ8Tyw.js} +9 -9
  211. package/dist/{onboard-channels-bx6oelzj.js → onboard-channels-CpM1Nwqf.js} +8 -8
  212. package/dist/{onboard-helpers-CJ3HzoUO.js → onboard-helpers-rHLFiSIc.js} +7 -7
  213. package/dist/{onboard-helpers-CFudIoX4.js → onboard-helpers-uf70JxTj.js} +9 -9
  214. package/dist/{onboarding-BeuMAyic.js → onboarding-BGXbXgds.js} +21 -21
  215. package/dist/{onboarding-CX1vIkcB.js → onboarding-DQplq13s.js} +22 -22
  216. package/dist/{orchestrator-C1nWKIJS.js → orchestrator-B2rNfH4K.js} +5 -4
  217. package/dist/{orchestrator-C2ypFiPL.js → orchestrator-CERtu86A.js} +6 -5
  218. package/dist/{outbound-Bmft-5um.js → outbound-Bii3QonX.js} +4 -4
  219. package/dist/{outbound-C577aWZp.js → outbound-BpcyR1eA.js} +4 -4
  220. package/dist/{outbound-fPqdCDR4.js → outbound-C1WShhf8.js} +4 -4
  221. package/dist/{outbound-DW2eod1S.js → outbound-DKY6oeyQ.js} +4 -4
  222. package/dist/{outbound-send-deps-Y9AxHeLG.js → outbound-send-deps-CtiCh8EY.js} +1 -1
  223. package/dist/{parse-timeout-D4UO8pY_.js → parse-timeout-Bp5yQ1yW.js} +3 -3
  224. package/dist/{parse-timeout-C4WLf3Qy.js → parse-timeout-Cn7M8c5R.js} +3 -3
  225. package/dist/{path-env-DLQPf9qj.js → path-env-BAjR_Xf_.js} +1 -1
  226. package/dist/{paths-DMk3Q7yD.js → paths-BM1wivAU.js} +1 -1
  227. package/dist/{paths-CAQJvbeZ.js → paths-BqA7F6bT.js} +2 -2
  228. package/dist/{paths-Q6h5HODL.js → paths-DAbvPUdJ.js} +1 -1
  229. package/dist/{pi-auth-json-B_lKNFK6.js → pi-auth-json-BhkzZgQz.js} +5 -5
  230. package/dist/{pi-auth-json-BmdBnmlZ.js → pi-auth-json-CM3QDr9_.js} +6 -6
  231. package/dist/{pi-auth-json-WTvcP2gz.js → pi-auth-json-Dry5jXVt.js} +6 -6
  232. package/dist/{pi-auth-json-DkYqdjrV.js → pi-auth-json-qHO_AF3h.js} +6 -6
  233. package/dist/{pi-embedded-D15iww51.js → pi-embedded-CrzqUkHv.js} +548 -67
  234. package/dist/{pi-embedded-DR8Pfd05.js → pi-embedded-Dn3Ip_VN.js} +552 -71
  235. package/dist/{pi-embedded-helpers-BZ9GspxK.js → pi-embedded-helpers-BPtBCnl-.js} +1 -1
  236. package/dist/{pi-embedded-helpers-D2SLlgS4.js → pi-embedded-helpers-BSFtS9Fl.js} +1 -1
  237. package/dist/{pi-tools.policy-WdTAfqbV.js → pi-tools.policy-DuWNVoem.js} +6 -6
  238. package/dist/{pi-tools.policy-D2FusuQa.js → pi-tools.policy-DyckzpRL.js} +7 -7
  239. package/dist/{plugin-auto-enable-DhuD30Je.js → plugin-auto-enable-Btd2iEwF.js} +5 -5
  240. package/dist/{plugin-auto-enable-CtYcdTju.js → plugin-auto-enable-DvYJhgJ2.js} +5 -5
  241. package/dist/{plugin-registry-Do2D1nDk.js → plugin-registry-BOuDey4w.js} +4 -4
  242. package/dist/{plugin-registry-ME2FQAi-.js → plugin-registry-ChWHnegS.js} +4 -4
  243. package/dist/plugin-sdk/agents/local-model-installer.d.ts +11 -0
  244. package/dist/plugin-sdk/agents/openai-direct-runner.d.ts +2 -1
  245. package/dist/plugin-sdk/config/types.agent-defaults.d.ts +40 -0
  246. package/dist/plugin-sdk/config/types.models.d.ts +1 -1
  247. package/dist/plugin-sdk/config/zod-schema.agent-defaults.d.ts +13 -0
  248. package/dist/plugin-sdk/config/zod-schema.agents.d.ts +13 -0
  249. package/dist/plugin-sdk/config/zod-schema.core.d.ts +6 -6
  250. package/dist/plugin-sdk/config/zod-schema.d.ts +15 -2
  251. package/dist/plugin-sdk/index.js +19 -1
  252. package/dist/plugin-sdk/infra/architecture-awareness.d.ts +47 -0
  253. package/dist/{plugins-DYcg0qBW.js → plugins-BqPGs8w-.js} +1 -1
  254. package/dist/{plugins-BOMS6J5A.js → plugins-C8FLxXbd.js} +1 -1
  255. package/dist/{plugins-cli-CoVt2ewg.js → plugins-cli-CbOP5ml6.js} +51 -51
  256. package/dist/{plugins-cli-CVFzwdmI.js → plugins-cli-dH6LKRQM.js} +53 -53
  257. package/dist/{polls-DFISjV7H.js → polls-aumqIVob.js} +5 -5
  258. package/dist/{ports-BGLuwt2Z.js → ports--opAW6yN.js} +2 -2
  259. package/dist/{ports-q535r1PZ.js → ports-B20JTuiL.js} +2 -2
  260. package/dist/{ports-B_f42zcA.js → ports-C4ACB4MP.js} +2 -2
  261. package/dist/{ports-DaVrZDUq.js → ports-D6bjtvhF.js} +2 -2
  262. package/dist/{program-context-DP3qjW7A.js → program-context-7yQclqBT.js} +35 -35
  263. package/dist/{program-8rF4C_wd.js → program-pWHcYdeO.js} +58 -58
  264. package/dist/{progress-glCgu57m.js → progress-BveMw0-_.js} +1 -1
  265. package/dist/{progress-CVLvQV_t.js → progress-qMmjCvh9.js} +1 -1
  266. package/dist/{prompt-style-BI53UVgE.js → prompt-style-DWuEFbBO.js} +1 -1
  267. package/dist/{prompt-style-D6SRiiTV.js → prompt-style-zgo1vAG1.js} +1 -1
  268. package/dist/{prompts-BmgT_kkv.js → prompts-C4v0DSTU.js} +6 -6
  269. package/dist/{prompts-Bq4QGFQM.js → prompts-D6Xc1Ddb.js} +4 -4
  270. package/dist/{pw-ai-DxNrJcCA.js → pw-ai-2XnGTARu.js} +3 -3
  271. package/dist/{pw-ai-CB-zeR7h.js → pw-ai-BUnz28XN.js} +5 -5
  272. package/dist/{pw-ai-C_1-7IgH.js → pw-ai-CRnDr120.js} +3 -3
  273. package/dist/{pw-ai-B4u5FDqO.js → pw-ai-Dq14LFsx.js} +6 -6
  274. package/dist/{qmd-manager-C_XBZ_bT.js → qmd-manager-6nqaD-q9.js} +5 -5
  275. package/dist/{qmd-manager-CO795NK4.js → qmd-manager-B6-V0aC1.js} +6 -6
  276. package/dist/{qmd-manager-CpNYgSrx.js → qmd-manager-BNL9Pl2G.js} +6 -6
  277. package/dist/{qmd-manager-Q0OSDQ-e.js → qmd-manager-CpynR-al.js} +5 -5
  278. package/dist/{register.agent-BIrXCVtQ.js → register.agent-BMxuTqFN.js} +68 -68
  279. package/dist/{register.agent-DnkOx0U8.js → register.agent-BO6_QTx6.js} +65 -65
  280. package/dist/{register.anima-B36rTHUt.js → register.anima-5SAY6OxG.js} +6 -6
  281. package/dist/{register.anima-DXT9bM9A.js → register.anima-CLGhoqmw.js} +8 -8
  282. package/dist/register.configure-Bz9pap1L.js +106 -0
  283. package/dist/register.configure-dMfZ1zcY.js +107 -0
  284. package/dist/register.maintenance-CL2eIgmC.js +102 -0
  285. package/dist/register.maintenance-k1dBbjUg.js +103 -0
  286. package/dist/{register.message-CD7xV-jz.js → register.message-Cqlz-6uM.js} +52 -52
  287. package/dist/{register.message-Brtushvp.js → register.message-Cz34rs9W.js} +50 -50
  288. package/dist/{register.onboard-23Mra3LN.js → register.onboard-Cmh46n62.js} +70 -70
  289. package/dist/{register.onboard-6CbODzQ6.js → register.onboard-_4FOFCA2.js} +67 -67
  290. package/dist/{register.setup-CqQw13Ky.js → register.setup-CHgH1mdn.js} +70 -70
  291. package/dist/{register.setup-DlVH7FKe.js → register.setup-CKY65php.js} +67 -67
  292. package/dist/{register.status-health-sessions-CduFjFDB.js → register.status-health-sessions-CNX6wp0V.js} +60 -60
  293. package/dist/{register.status-health-sessions-CxtgPKu9.js → register.status-health-sessions-DNvtWNtb.js} +63 -63
  294. package/dist/{register.subclis-CtANqD5P.js → register.subclis-Br8nY-dd.js} +23 -23
  295. package/dist/{reply-93fMzde1.js → reply-DqXPXQ20.js} +322 -77
  296. package/dist/{reply-prefix-B7Fb3fO8.js → reply-prefix-CZy4g8SY.js} +1 -1
  297. package/dist/{reply-prefix-BzdhJDqP.js → reply-prefix-DuANpRN_.js} +1 -1
  298. package/dist/{routes-CWCAc8uJ.js → routes-CAjh83-X.js} +6 -6
  299. package/dist/{routes-FT0Us8Md.js → routes-DTjggbOR.js} +6 -6
  300. package/dist/{run-CF3kHOGH.js → run-0F-ZU2kW.js} +2251 -217
  301. package/dist/{run-Cq_iTGK_.js → run-Bu-fwhic.js} +2256 -222
  302. package/dist/{run-main-BiIRcc6s.js → run-main-BcC_mt4v.js} +72 -72
  303. package/dist/{runtime-guard-D14Z_QY6.js → runtime-guard-Ccc4Qj4K.js} +1 -1
  304. package/dist/{runtime-guard-DdP10b7Q.js → runtime-guard-E3CIN3Xo.js} +1 -1
  305. package/dist/{sandbox-D-N7M7lp.js → sandbox-DSHSoUHO.js} +6 -6
  306. package/dist/{sandbox-cli-CQKz2I1X.js → sandbox-cli-BiO-r7eU.js} +22 -22
  307. package/dist/{sandbox-cli-DHNFlTo-.js → sandbox-cli-D6ikivGS.js} +22 -22
  308. package/dist/{sandbox-pBHlfFdB.js → sandbox-mSWYRXFm.js} +6 -6
  309. package/dist/{security-cli-Bdi7MuP6.js → security-cli-Bqq5Klnm.js} +25 -25
  310. package/dist/{security-cli-C3aI09uy.js → security-cli-CcBxjHGL.js} +25 -25
  311. package/dist/{semantic-CQApJNO_.js → semantic-CPfLgc8S.js} +1 -1
  312. package/dist/{semantic-9rgWUrz3.js → semantic-DLW1Sflk.js} +1 -1
  313. package/dist/{semantic-C1UN3bb9.js → semantic-jL8x-DTZ.js} +1 -1
  314. package/dist/{server-context-Yx4pgBqJ.js → server-context-DTlBzMyD.js} +7 -7
  315. package/dist/{server-context-Clykq0XU.js → server-context-EPPcxp2p.js} +8 -8
  316. package/dist/{server-node-events-B3Serk9L.js → server-node-events-BGWQiInn.js} +52 -52
  317. package/dist/{server-node-events-DgvKcH5q.js → server-node-events-BuxYvQ0t.js} +50 -50
  318. package/dist/{service-4VfZwSv1.js → service-BoUEkrUm.js} +1 -1
  319. package/dist/{service-audit-KzOtcw_V.js → service-audit-B_BC6-F8.js} +3 -3
  320. package/dist/{service-audit-Bwpoc2LD.js → service-audit-CjH8ed-j.js} +3 -3
  321. package/dist/{service-Dd1DfPia.js → service-ho_k2vjr.js} +1 -1
  322. package/dist/{session-jljC5QVG.js → session-B4wJoEqn.js} +4 -4
  323. package/dist/{session-CSmfU0D3.js → session-BF7WjlT0.js} +1 -1
  324. package/dist/{session-BMDpwIJu.js → session-BnOV0y0e.js} +6 -6
  325. package/dist/{session-C_d9uvLf.js → session-CSgjW31i.js} +5 -5
  326. package/dist/{session-BiA6jrcs.js → session-DO_miKel.js} +1 -1
  327. package/dist/{session-5YO_H-Ra.js → session-DcRzV4l4.js} +1 -1
  328. package/dist/{session-BzrnfWQ2.js → session-DlMsi6dM.js} +5 -5
  329. package/dist/{session-cost-usage-DnxtnK1E.js → session-cost-usage-BNvno_kS.js} +1 -1
  330. package/dist/{sessions-C_3wTmSA.js → sessions-B2NwIaNC.js} +6 -6
  331. package/dist/{sessions-BOzeFzuL.js → sessions-DBQx8E1H.js} +5 -5
  332. package/dist/{sessions-BmE5Z_1i.js → sessions-DxAfgPEp.js} +6 -6
  333. package/dist/{settings-cli-CZdlEmNi.js → settings-cli-CHwTEE6P.js} +72 -72
  334. package/dist/{settings-cli-DsDqNpW_.js → settings-cli-DsUKlq8R.js} +74 -74
  335. package/dist/{setup-token-Lee4gM5w.js → setup-token-BX6QI7ce.js} +26 -26
  336. package/dist/{setup-token-C8Gg1P6T.js → setup-token-CU5mami5.js} +26 -26
  337. package/dist/{shared-C-rqLtIT.js → shared-BFzq0XE1.js} +2 -2
  338. package/dist/{shared-7KwLAyAq.js → shared-D3ZAXqdo.js} +3 -3
  339. package/dist/{shell-env-CMI9f7-7.js → shell-env-BR7Sbtw1.js} +1 -1
  340. package/dist/{shell-env-HkVWMh--.js → shell-env-BZ2Wha1e.js} +1 -1
  341. package/dist/{shell-env-iPnSIi-t.js → shell-env-Bgk3WFTi.js} +1 -1
  342. package/dist/{skills-DtoVe1dS.js → skills-BT0Lx09M.js} +2 -2
  343. package/dist/{skills-4v6-kw0C.js → skills-DjVFhlQY.js} +3 -3
  344. package/dist/{skills-cli-BoasNTpZ.js → skills-cli-BX5pL-D4.js} +15 -15
  345. package/dist/{skills-cli-TeAq3fRG.js → skills-cli-wKTnuQos.js} +14 -14
  346. package/dist/{skills-install-D6_qpRjW.js → skills-install-CVWWsOf2.js} +4 -4
  347. package/dist/{skills-install-Qw2oU8L8.js → skills-install-Ye1kjpCv.js} +4 -4
  348. package/dist/{skills-status-DuLjkX2E.js → skills-status-BUKnQ3ek.js} +2 -2
  349. package/dist/{skills-status-CvH7AUoY.js → skills-status-BmZufjT1.js} +3 -3
  350. package/dist/{soul-Bt8UNmTq.js → soul-C0oc6fbP.js} +1 -1
  351. package/dist/{soul-D9k5zulC.js → soul-DEsrpMAk.js} +1 -1
  352. package/dist/{soul-BiIdv3Wp.js → soul-Doq8As4R.js} +1 -1
  353. package/dist/{soul-DQSYs-4l.js → soul-DyLtQVJh.js} +1 -1
  354. package/dist/{sqlite-BMMt7osH.js → sqlite-CFCo1Ghe.js} +2 -2
  355. package/dist/{sqlite-Cm6OqTQB.js → sqlite-DVkTX4wZ.js} +1 -1
  356. package/dist/{sqlite-CZ1vD4VS.js → sqlite-DrhFRoal.js} +2 -2
  357. package/dist/{sqlite-CpAJt-JS.js → sqlite-M5Ot9X4U.js} +1 -1
  358. package/dist/start-C-gTbKUz.js +158 -0
  359. package/dist/start-DZKfWnpD.js +157 -0
  360. package/dist/{status-COc4xMti.js → status-BN6HYR8t.js} +4 -4
  361. package/dist/{status-C_NBOv_V.js → status-BimVB8cj.js} +4 -4
  362. package/dist/{status-uakoP719.js → status-CSkpd-tb.js} +27 -27
  363. package/dist/{status-BO5BIf81.js → status-D1zupVBm.js} +24 -24
  364. package/dist/{status.update-B6Tdpk07.js → status.update-HKMS5T9H.js} +2 -2
  365. package/dist/{status.update-E9dSFk_b.js → status.update-oapjZPr_.js} +3 -3
  366. package/dist/{subagent-registry-fLI7QDKe.js → subagent-registry-D87n8mHd.js} +318 -81
  367. package/dist/{subsystem-D1AJZPgG.js → subsystem-CkjVkxc4.js} +17 -1
  368. package/dist/{subsystem-D-Xta-sj.js → subsystem-CyrIA90F.js} +17 -1
  369. package/dist/{subsystem-BAADN1B8.js → subsystem-DLVQ9bjX.js} +17 -1
  370. package/dist/{system-cli-DoLzi2Sn.js → system-cli-BRPiBm4G.js} +13 -13
  371. package/dist/{system-cli-CP7JrhR0.js → system-cli-DaNHLGRC.js} +15 -15
  372. package/dist/{systemd-6iLWJxQQ.js → systemd-CS58NHpx.js} +2 -2
  373. package/dist/{systemd-hints-C5Kfh4GW.js → systemd-hints-7HkQ202-.js} +1 -1
  374. package/dist/{systemd-linger-CxGmIy_5.js → systemd-linger-BMNvTAmy.js} +2 -2
  375. package/dist/{systemd-linger-BVwGXVS0.js → systemd-linger-C9PGBtXs.js} +2 -2
  376. package/dist/{systemd-Bx76sJ3M.js → systemd-zYTszfyU.js} +2 -2
  377. package/dist/{table-Blmz7glr.js → table-Bea_v9UU.js} +2 -2
  378. package/dist/{table-Bt7rSYC6.js → table-ohGAZZLQ.js} +1 -1
  379. package/dist/{tokens-CmlI2hSz.js → tokens-BelyD23F.js} +1 -1
  380. package/dist/{tokens-SP2Q7i59.js → tokens-nliOJNMv.js} +1 -1
  381. package/dist/{tool-images-C6cKHTbj.js → tool-images-BEiRPCYM.js} +2 -2
  382. package/dist/{tool-images-2qproko3.js → tool-images-BH8q2ZMq.js} +2 -2
  383. package/dist/{tool-images-D1HuaGdS.js → tool-images-DJrhuxOM.js} +1 -1
  384. package/dist/{tool-images-CgDT0Xzv.js → tool-images-dAVG0qoz.js} +2 -2
  385. package/dist/{tui-r4qpJhNk.js → tui-DnzqtbW3.js} +9 -9
  386. package/dist/{tui-C2eLfbhA.js → tui-FJlF9b--.js} +8 -8
  387. package/dist/{tui-cli-cLSYBQu9.js → tui-cli-BtLvABYH.js} +27 -27
  388. package/dist/{tui-cli-BgcbCtgc.js → tui-cli-Cm3G2SAN.js} +26 -26
  389. package/dist/{update-DA91za97.js → update-Cpvsdq3a.js} +3 -3
  390. package/dist/{update-CqKpX3cX.js → update-DrQkwR-M.js} +3 -3
  391. package/dist/{update-cli-DEe62XGU.js → update-cli-DpKplMO0.js} +79 -79
  392. package/dist/{update-cli-D3Ujz_cW.js → update-cli-DueHX0OM.js} +83 -83
  393. package/dist/{update-runner-DZfnquWO.js → update-runner-C7CjKcMp.js} +5 -5
  394. package/dist/{update-runner-DUl-g4mB.js → update-runner-DtqDU0xF.js} +5 -5
  395. package/dist/{utils-D1VGbO3C.js → utils-BNuEYQIN.js} +1 -1
  396. package/dist/{utils-CLYlhJuc.js → utils-CkCznJhR.js} +1 -1
  397. package/dist/{web-Di8j762D.js → web-Br9HCEvC.js} +54 -54
  398. package/dist/{web-DzSlI8A6.js → web-BxkEMATs.js} +25 -25
  399. package/dist/{web-C-cK9OCd.js → web-CPHrMWsv.js} +24 -24
  400. package/dist/web-HXy7rlIW.js +65 -0
  401. package/dist/{webhooks-cli-k3QMf7Rs.js → webhooks-cli-CdoaCUQ5.js} +11 -11
  402. package/dist/{webhooks-cli-vlEfXEKm.js → webhooks-cli-D65dKr5L.js} +12 -12
  403. package/dist/{whatsapp-actions-Hr-W8vjY.js → whatsapp-actions--CtxOhlj.js} +12 -12
  404. package/dist/{whatsapp-actions-C0tlEdLy.js → whatsapp-actions--eONuB4M.js} +14 -14
  405. package/dist/{whatsapp-actions-BJn-z76S.js → whatsapp-actions-BM0a0V4y.js} +17 -17
  406. package/dist/{whatsapp-actions-6fPRKwPV.js → whatsapp-actions-Bhz79Fgg.js} +11 -11
  407. package/dist/{widearea-dns-CtU9Fx7K.js → widearea-dns-D2VSHQ7y.js} +1 -1
  408. package/dist/{widearea-dns-CHAT20aR.js → widearea-dns-DlJrNJOg.js} +1 -1
  409. package/dist/{workspace-BFIZCnGo.js → workspace-D6JhsXVL.js} +1 -1
  410. package/dist/{ws-log-CUobU2tD.js → ws-log-Cvz4xRCD.js} +1 -1
  411. package/dist/{ws-log-CG6cvCZW.js → ws-log-DwsFDY7m.js} +1 -1
  412. package/package.json +8 -2
  413. package/scripts/install-local-qwen3-coder.sh +7 -0
  414. package/templates/HEART.md +32 -10
  415. package/templates/SOUL.md +37 -8
  416. package/templates/profiles/architect.profile.json5 +36 -0
  417. package/templates/profiles/builder.profile.json5 +36 -0
  418. package/templates/profiles/coordinator.profile.json5 +36 -0
  419. package/templates/profiles/guardian.profile.json5 +36 -0
  420. package/templates/profiles/researcher.profile.json5 +36 -0
  421. package/dist/config-cli-DfHE3KG-.js +0 -15
  422. package/dist/config-cli-fleq7-gq.js +0 -11
  423. package/dist/control-ui/assets/index-DIEQjjCN.js +0 -73
  424. package/dist/control-ui/assets/index-DIEQjjCN.js.map +0 -1
  425. package/dist/register.configure-CuzJxZmk.js +0 -107
  426. package/dist/register.configure-DCpvHX3m.js +0 -106
  427. package/dist/register.maintenance-CcxBFfv5.js +0 -103
  428. package/dist/register.maintenance-Dla0H12S.js +0 -102
  429. package/dist/start-CK6urvnN.js +0 -158
  430. package/dist/start-Cs1aPMq2.js +0 -157
  431. package/dist/web-Dybw4K7C.js +0 -65
@@ -1,79 +1,79 @@
1
1
  import { h as expandHomePrefix, i as isNixMode, l as resolveGatewayLockDir, m as resolveStateDir, o as resolveConfigPath, r as STATE_DIR, t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-zhVksOvm.js";
2
- import { E as getActivePluginRegistry, F as setVerbose, G as setLoggerOverride, H as getChildLogger, U as getLogger, W as getResolvedLoggerSettings, d as defaultRuntime, g as CHANNEL_IDS, i as getResolvedConsoleSettings, n as runtimeForLogger, o as setConsoleSubsystemFilter, s as setConsoleTimestampPrefix, t as createSubsystemLogger, v as DEFAULT_CHAT_CHANNEL } from "./subsystem-BAADN1B8.js";
3
- import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir$1, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CLYlhJuc.js";
4
- import { C as supportsXHighThinking, _ as normalizeElevatedLevel, b as normalizeUsageDisplay, m as formatXHighModelHint, p as formatThinkingLevels, v as normalizeReasoningLevel, x as normalizeVerboseLevel, y as normalizeThinkLevel } from "./pi-embedded-helpers-BZ9GspxK.js";
5
- import { $ as archiveFileOnDisk, At as deferGatewayRestartUntilIdle, B as sniffMimeFromBase64, C as verifyNodeToken, Cn as stopDiagnosticHeartbeat, Cr as DEFAULT_INPUT_PDF_MAX_PIXELS, Ct as loadProviderStore, D as registerSkillsChangeListener, Dr as extractImageContentFromSource, E as getSkillsSnapshotVersion, Er as extractFileContentFromSource, F as abortEmbeddedPiRun, Ft as setGatewaySigusr1RestartPolicy, G as canonicalizeSpawnedByForAgent, Gn as CommandLane, Gt as normalizeCronJobPatch, Hn as readLatestAssistantReply, I as waitForEmbeddedPiRunEnd, It as setPreRestartDeferralCheck, J as loadCombinedSessionStoreForGateway, Jn as getAgentRunContext, Jt as normalizeOptionalText, K as listAgentsForGateway, Kn as clearAgentRunContext, Kt as inferLegacyName, L as runNoxSoftEmbeddedAgent, Ln as countActiveDescendantRuns, Lt as consumeRestartSentinel, M as resetAllLanes, Mn as isAbortTrigger, Mr as resolveAgentTimeoutMs, Mt as isGatewaySigusr1RestartExternallyAllowed, N as setCommandLaneConcurrency, Nn as stopSubagentsForRequester, Nt as markGatewaySigusr1RestartHandled, O as clearSessionQueues, Or as normalizeMimeList, P as waitForActiveTasks, Pt as scheduleGatewaySigusr1Restart, Q as resolveSessionModelRef, R as applyToolPolicyPipeline, Rn as initSubagentRegistry, Rt as formatDoctorNonInteractiveHint, S as updatePairedNodeMetadata, Sn as startDiagnosticHeartbeat, Sr as DEFAULT_INPUT_PDF_MAX_PAGES, St as loadProviderUsageSummary, T as verifyPairingToken, Tr as DEFAULT_INPUT_TIMEOUT_MS, Tt as saveProviderStore, U as createAnimaTools, Ut as writeRestartSentinel, V as requestHeartbeatNow, Vn as runSubagentAnnounceFlow, Vt as summarizeRestartSentinel, W as resolveAnnounceTargetFromKey, Wt as normalizeCronJobCreate, X as pruneLegacyStoreKeys, Xn as registerAgentRunContext, Xt as normalizeRequiredName, Y as loadSessionEntry, Yn as onAgentEvent, Yt as normalizePayloadToSystemText, Z as resolveGatewaySessionStoreTarget, Zn as resolveAgentIdentity, Zt as migrateLegacyCronPayload, _ as approveNodePairing, _n as dispatchInboundMessage, _r as DEFAULT_INPUT_FILE_MAX_CHARS, _t as resetDirectoryCache, an as persistBrowserProxyFiles, ar as applyVerboseOverride, at as stripEnvelopeFromMessages, b as renamePairedNode, br as DEFAULT_INPUT_IMAGE_MIMES, bt as runWithModelFallback, c as normalizeSendPolicy, cr as isSystemEventContextChanged, d as primeRemoteSkillsCache, dr as loadModelCatalog, dt as resolveOutboundSessionRoute, en as buildSafeExternalPrompt, et as archiveSessionTranscripts, f as recordRemoteNodeInfo, g as setSkillsRemoteRegistry, gr as DEFAULT_INPUT_FILE_MAX_BYTES, h as removeRemoteNodeInfo, ht as resolveSessionDeliveryTarget, i as setCliSessionId, in as applyBrowserProxyPaths, ir as applyModelOverrideToSessionEntry, it as resolveSessionTranscriptCandidates, j as getTotalQueueSize, jt as emitGatewayRestart, k as getActiveTaskCount, kn as formatZonedTimestamp, kr as estimateBase64DecodedBytes, kt as consumeGatewaySigusr1RestartAuthorization, l as resolveSendPolicy, m as refreshRemoteNodeBins, mr as registerUnhandledRejectionHandler, mt as resolveOutboundTarget, n as BARE_SESSION_RESET_PROMPT, nn as getHookType, nr as lookupContextTokens, nt as readSessionMessages, on as getPluginToolMeta, or as parseVerboseOverride, p as refreshRemoteBinsForConnectedNodes, pn as getChannelActivity, q as listSessionsFromStore, qn as emitAgentEvent, qt as normalizeOptionalAgentId, r as getCliSessionId, rn as isExternalHookSession, rt as readSessionPreviewItemsFromTranscript, sn as loadAnimaPlugins, sr as enqueueSystemEvent, st as normalizeGroupActivation, tn as detectSuspiciousPatterns, tt as capArrayByJsonBytes, u as getRemoteSkillEligibility, ut as ensureOutboundSessionEntry, v as listNodePairing, vn as createReplyDispatcher, vr as DEFAULT_INPUT_FILE_MIMES, vt as resolveMessageChannelSelection, w as generatePairingToken, wn as isDiagnosticsEnabled, wr as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, wt as maskApiKey, x as requestNodePairing, xr as DEFAULT_INPUT_MAX_REDIRECTS, xt as resolveWorkingModeModelSelection, y as rejectNodePairing, yn as getTotalPendingReplies, yr as DEFAULT_INPUT_IMAGE_MAX_BYTES, z as buildDefaultToolPolicyPipelineSteps, zn as listDescendantRunsForRequester, zt as formatRestartSentinelMessage } from "./reply-93fMzde1.js";
2
+ import { D as getActivePluginRegistry, G as getResolvedLoggerSettings, I as setVerbose, K as setLoggerOverride, U as getChildLogger, W as getLogger, _ as CHANNEL_IDS, f as defaultRuntime, i as getResolvedConsoleSettings, n as runtimeForLogger, o as setConsoleSubsystemFilter, s as setConsoleTimestampPrefix, t as createSubsystemLogger, y as DEFAULT_CHAT_CHANNEL } from "./subsystem-DLVQ9bjX.js";
3
+ import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir$1, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CkCznJhR.js";
4
+ import { C as supportsXHighThinking, _ as normalizeElevatedLevel, b as normalizeUsageDisplay, m as formatXHighModelHint, p as formatThinkingLevels, v as normalizeReasoningLevel, x as normalizeVerboseLevel, y as normalizeThinkLevel } from "./pi-embedded-helpers-BPtBCnl-.js";
5
+ import { $ as resolveGatewaySessionStoreTarget, $n as resolveAgentIdentity, $t as migrateLegacyCronPayload, Ar as normalizeMimeList, B as applyToolPolicyPipeline, Bn as initSubagentRegistry, Bt as formatDoctorNonInteractiveHint, C as verifyNodeToken, Cr as DEFAULT_INPUT_MAX_REDIRECTS, Ct as resolveWorkingModeModelSelection, D as registerSkillsChangeListener, Dr as DEFAULT_INPUT_TIMEOUT_MS, Dt as saveProviderStore, E as getSkillsSnapshotVersion, En as isDiagnosticsEnabled, Er as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, Et as maskApiKey, F as abortEmbeddedPiRun, Fn as stopSubagentsForRequester, Ft as markGatewaySigusr1RestartHandled, G as createAnimaTools, Gt as writeRestartSentinel, H as sniffMimeFromBase64, I as waitForEmbeddedPiRunEnd, It as scheduleGatewaySigusr1Restart, J as listAgentsForGateway, Jn as clearAgentRunContext, Jt as inferLegacyName, K as resolveAnnounceTargetFromKey, Kt as normalizeCronJobCreate, L as runNoxSoftEmbeddedAgent, Lt as setGatewaySigusr1RestartPolicy, M as resetAllLanes, Mt as deferGatewayRestartUntilIdle, N as setCommandLaneConcurrency, Nt as emitGatewayRestart, O as clearSessionQueues, Or as extractFileContentFromSource, P as waitForActiveTasks, Pn as isAbortTrigger, Pr as resolveAgentTimeoutMs, Pt as isGatewaySigusr1RestartExternallyAllowed, Q as pruneLegacyStoreKeys, Qn as registerAgentRunContext, Qt as normalizeRequiredName, Rt as setPreRestartDeferralCheck, S as updatePairedNodeMetadata, Sr as DEFAULT_INPUT_IMAGE_MIMES, St as runWithModelFallback, T as verifyPairingToken, Tn as stopDiagnosticHeartbeat, Tr as DEFAULT_INPUT_PDF_MAX_PIXELS, Tt as loadProviderStore, U as requestHeartbeatNow, Un as runSubagentAnnounceFlow, Ut as summarizeRestartSentinel, V as buildDefaultToolPolicyPipelineSteps, Vn as listDescendantRunsForRequester, Vt as formatRestartSentinelMessage, Wn as readLatestAssistantReply, X as loadCombinedSessionStoreForGateway, Xn as getAgentRunContext, Xt as normalizeOptionalText, Y as listSessionsFromStore, Yn as emitAgentEvent, Yt as normalizeOptionalAgentId, Z as loadSessionEntry, Zn as onAgentEvent, Zt as normalizePayloadToSystemText, _ as approveNodePairing, _t as resolveSessionDeliveryTarget, an as isExternalHookSession, at as readSessionPreviewItemsFromTranscript, b as renamePairedNode, bn as createReplyDispatcher, br as DEFAULT_INPUT_FILE_MIMES, bt as resolveMessageChannelSelection, c as normalizeSendPolicy, cn as getPluginToolMeta, cr as parseVerboseOverride, d as primeRemoteSkillsCache, et as resolveSessionModelRef, f as recordRemoteNodeInfo, ft as ensureOutboundSessionEntry, g as setSkillsRemoteRegistry, gr as registerUnhandledRejectionHandler, gt as resolveOutboundTarget, h as removeRemoteNodeInfo, hn as getChannelActivity, i as setCliSessionId, in as getHookType, ir as lookupContextTokens, it as readSessionMessages, j as getTotalQueueSize, jn as formatZonedTimestamp, jr as estimateBase64DecodedBytes, jt as consumeGatewaySigusr1RestartAuthorization, k as getActiveTaskCount, kr as extractImageContentFromSource, l as resolveSendPolicy, ln as loadAnimaPlugins, lr as enqueueSystemEvent, lt as normalizeGroupActivation, m as refreshRemoteNodeBins, n as BARE_SESSION_RESET_PROMPT, nn as buildSafeExternalPrompt, nt as archiveSessionTranscripts, on as applyBrowserProxyPaths, or as applyModelOverrideToSessionEntry, ot as resolveSessionTranscriptCandidates, p as refreshRemoteBinsForConnectedNodes, pr as loadModelCatalog, pt as resolveOutboundSessionRoute, q as canonicalizeSpawnedByForAgent, qn as CommandLane, qt as normalizeCronJobPatch, r as getCliSessionId, rn as detectSuspiciousPatterns, rt as capArrayByJsonBytes, sn as persistBrowserProxyFiles, sr as applyVerboseOverride, st as stripEnvelopeFromMessages, tt as archiveFileOnDisk, u as getRemoteSkillEligibility, ur as isSystemEventContextChanged, v as listNodePairing, vr as DEFAULT_INPUT_FILE_MAX_BYTES, w as generatePairingToken, wn as startDiagnosticHeartbeat, wr as DEFAULT_INPUT_PDF_MAX_PAGES, wt as loadProviderUsageSummary, x as requestNodePairing, xn as getTotalPendingReplies, xr as DEFAULT_INPUT_IMAGE_MAX_BYTES, y as rejectNodePairing, yn as dispatchInboundMessage, yr as DEFAULT_INPUT_FILE_MAX_CHARS, yt as resetDirectoryCache, zn as countActiveDescendantRuns, zt as consumeRestartSentinel } from "./reply-DqXPXQ20.js";
6
6
  import { b as isSubagentSessionKey, d as resolveAgentIdFromSessionKey, i as buildAgentMainSessionKey, l as normalizeAgentId, m as toAgentRequestSessionKey, n as DEFAULT_AGENT_ID, t as DEFAULT_ACCOUNT_ID, u as normalizeMainKey, v as isCronRunSessionKey, x as parseAgentSessionKey } from "./session-key-DAZmp8ll.js";
7
- import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-BylR5qWS.js";
7
+ import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-ChPERQB-.js";
8
8
  import { t as resolveAnimaPackageRoot } from "./anima-root-xWSKR6Wm.js";
9
- import { T as resolveWorkspaceTemplateDir, _ as DEFAULT_MEMORY_FILENAME, b as DEFAULT_USER_FILENAME, c as resolveDefaultAgentId, d as DEFAULT_AGENTS_FILENAME, g as DEFAULT_MEMORY_ALT_FILENAME, h as DEFAULT_IDENTITY_FILENAME, i as resolveAgentModelFallbacksOverride, l as resolveSessionAgentId, m as DEFAULT_HEARTBEAT_FILENAME, n as resolveAgentConfig, p as DEFAULT_BOOTSTRAP_FILENAME, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, v as DEFAULT_SOUL_FILENAME, w as resolveDefaultAgentWorkspaceDir, x as ensureAgentWorkspace, y as DEFAULT_TOOLS_FILENAME } from "./agent-scope-CXxC8FFX.js";
10
- import { _ as DEFAULT_MODEL, a as isCliProvider, d as resolveConfiguredModelRef, f as resolveDefaultModelForAgent, g as DEFAULT_CONTEXT_TOKENS, h as resolveThinkingDefault, i as getModelRefStatus, p as resolveHooksGmailModel, u as resolveAllowedModelRef, v as DEFAULT_PROVIDER } from "./model-selection-CY6r_3wt.js";
11
- import { A as resolveAgentMaxConcurrent, M as VERSION, T as applyLegacyMigrations, a as parseConfigJson5, c as resolveConfigSnapshotHash, d as AnimaSchema, i as loadConfig, j as resolveSubagentMaxConcurrent, l as writeConfigFile, m as parseDurationMs, n as migrateLegacyConfig, o as readConfigFileSnapshot, p as sensitive, r as createConfigIO, s as readConfigFileSnapshotForWrite, u as validateConfigObjectWithPlugins, w as applyMergePatch } from "./config-DaD4FsAn.js";
12
- import { n as logAcceptedEnvOption, t as isTruthyEnvValue } from "./env-DlTia1B4.js";
13
- import { c as isTestDefaultMemorySlotDisabled } from "./manifest-registry-qF960vMH.js";
14
- import { A as resolveMainSessionKeyFromConfig, D as resolveAgentMainSessionKey, M as snapshotSessionOrigin, O as resolveExplicitAgentSessionKey, d as deliveryContextFromSession, h as normalizeSessionDeliveryFields, i as loadSessionStore, k as resolveMainSessionKey, l as updateSessionStore, p as mergeDeliveryContext, t as extractDeliveryInfo } from "./sessions-BOzeFzuL.js";
15
- import { o as detectMime } from "./image-ops-Ct3GueyT.js";
16
- import { t as normalizePollInput } from "./polls-DFISjV7H.js";
17
- import { _ as resolveToolProfilePolicy, p as collectExplicitAllowlist } from "./sandbox-pBHlfFdB.js";
9
+ import { T as resolveWorkspaceTemplateDir, _ as DEFAULT_MEMORY_FILENAME, b as DEFAULT_USER_FILENAME, c as resolveDefaultAgentId, d as DEFAULT_AGENTS_FILENAME, g as DEFAULT_MEMORY_ALT_FILENAME, h as DEFAULT_IDENTITY_FILENAME, i as resolveAgentModelFallbacksOverride, l as resolveSessionAgentId, m as DEFAULT_HEARTBEAT_FILENAME, n as resolveAgentConfig, p as DEFAULT_BOOTSTRAP_FILENAME, r as resolveAgentDir, s as resolveAgentWorkspaceDir, t as listAgentIds, v as DEFAULT_SOUL_FILENAME, w as resolveDefaultAgentWorkspaceDir, x as ensureAgentWorkspace, y as DEFAULT_TOOLS_FILENAME } from "./agent-scope-DQ0N_hof.js";
10
+ import { _ as DEFAULT_MODEL, a as isCliProvider, d as resolveConfiguredModelRef, f as resolveDefaultModelForAgent, g as DEFAULT_CONTEXT_TOKENS, h as resolveThinkingDefault, i as getModelRefStatus, p as resolveHooksGmailModel, u as resolveAllowedModelRef, v as DEFAULT_PROVIDER } from "./model-selection-CeZMza3d.js";
11
+ import { A as resolveAgentMaxConcurrent, M as VERSION, T as applyLegacyMigrations, a as parseConfigJson5, c as resolveConfigSnapshotHash, d as AnimaSchema, i as loadConfig, j as resolveSubagentMaxConcurrent, l as writeConfigFile, m as parseDurationMs, n as migrateLegacyConfig, o as readConfigFileSnapshot, p as sensitive, r as createConfigIO, s as readConfigFileSnapshotForWrite, u as validateConfigObjectWithPlugins, w as applyMergePatch } from "./config-MZ5clMl4.js";
12
+ import { n as logAcceptedEnvOption, t as isTruthyEnvValue } from "./env-ByZjwIJR.js";
13
+ import { c as isTestDefaultMemorySlotDisabled } from "./manifest-registry-Cwvk5hu8.js";
14
+ import { A as resolveMainSessionKeyFromConfig, D as resolveAgentMainSessionKey, M as snapshotSessionOrigin, O as resolveExplicitAgentSessionKey, d as deliveryContextFromSession, h as normalizeSessionDeliveryFields, i as loadSessionStore, k as resolveMainSessionKey, l as updateSessionStore, p as mergeDeliveryContext, t as extractDeliveryInfo } from "./sessions-DBQx8E1H.js";
15
+ import { o as detectMime } from "./image-ops-vj06APeW.js";
16
+ import { t as normalizePollInput } from "./polls-aumqIVob.js";
17
+ import { _ as resolveToolProfilePolicy, p as collectExplicitAllowlist } from "./sandbox-mSWYRXFm.js";
18
18
  import { t as formatCliCommand } from "./command-format-BCtkuvqF.js";
19
- import { a as AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN, c as safeEqualSecret, d as enableTailscaleFunnel, f as enableTailscaleServe, i as resolveGatewayAuth, l as disableTailscaleFunnel, m as getTailnetHostname, n as authorizeGatewayConnect, o as AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, r as isLocalDirectRequest, s as createAuthRateLimiter, t as assertGatewayAuthConfigured, u as disableTailscaleServe } from "./auth-DsC5pZ_0.js";
19
+ import { a as AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN, c as safeEqualSecret, d as enableTailscaleFunnel, f as enableTailscaleServe, i as resolveGatewayAuth, l as disableTailscaleFunnel, m as getTailnetHostname, n as authorizeGatewayConnect, o as AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, r as isLocalDirectRequest, s as createAuthRateLimiter, t as assertGatewayAuthConfigured, u as disableTailscaleServe } from "./auth-CuGlMw6p.js";
20
20
  import { n as pickPrimaryTailnetIPv6, t as pickPrimaryTailnetIPv4 } from "./tailnet-Bg_vE5qi.js";
21
21
  import { a as isTrustedProxyAddress, c as pickPrimaryLanIPv4, d as resolveGatewayListenHosts, i as isPrivateOrLoopbackAddress, l as resolveGatewayBindHost, n as isLoopbackAddress, r as isLoopbackHost, t as rawDataToString, u as resolveGatewayClientIp } from "./ws-BapRHqfx.js";
22
- import { r as movePathToTrash } from "./server-context-Yx4pgBqJ.js";
23
- import { i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot, x as hasBinary } from "./skills-4v6-kw0C.js";
22
+ import { r as movePathToTrash } from "./server-context-DTlBzMyD.js";
23
+ import { i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot, x as hasBinary } from "./skills-DjVFhlQY.js";
24
24
  import { n as formatErrorMessage } from "./errors-Bv81hF2P.js";
25
- import { a as inspectPortUsage, s as formatPortDiagnostics } from "./ports-q535r1PZ.js";
26
- import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, i as isGatewayMessageChannel, l as normalizeMessageChannel, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-CMsexA3K.js";
27
- import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-DYcg0qBW.js";
25
+ import { a as inspectPortUsage, s as formatPortDiagnostics } from "./ports-B20JTuiL.js";
26
+ import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, i as isGatewayMessageChannel, l as normalizeMessageChannel, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-CfwNj8mC.js";
27
+ import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-BqPGs8w-.js";
28
28
  import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-Dazi-gYo.js";
29
- import { O as normalizeSecretInput } from "./auth-profiles-C-LuhW6c.js";
30
- import { n as SILENT_REPLY_TOKEN, r as isSilentReplyText } from "./tokens-SP2Q7i59.js";
31
- import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES, ct as createInternalHookEvent, dt as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, et as clearSteer, ht as stripHeartbeatToken, it as getEgoManager, lt as registerInternalHook, nt as getSteerHistory, q as setTtsProvider, rt as setSteer, st as clearInternalHooks, tt as getSteer, ut as triggerInternalHook, z as resolveTtsAutoMode } from "./anthropic-direct-runner-OjcTAH6g.js";
32
- import { o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads, x as runGlobalGatewayStopSafely, y as getGlobalHookRunner } from "./deliver-BKzX3YoN.js";
33
- import { i as resolveMemoryBackendConfig, r as getMemorySearchManager } from "./memory-cli-DLtBA6r5.js";
29
+ import { O as normalizeSecretInput } from "./auth-profiles-XI2YBb-w.js";
30
+ import { n as SILENT_REPLY_TOKEN, r as isSilentReplyText } from "./tokens-BelyD23F.js";
31
+ import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES, ct as createInternalHookEvent, dt as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, et as clearSteer, ht as stripHeartbeatToken, it as getEgoManager, lt as registerInternalHook, nt as getSteerHistory, q as setTtsProvider, rt as setSteer, st as clearInternalHooks, tt as getSteer, ut as triggerInternalHook, z as resolveTtsAutoMode } from "./anthropic-direct-runner-BgFOZS8B.js";
32
+ import { o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads, x as runGlobalGatewayStopSafely, y as getGlobalHookRunner } from "./deliver-BknvuSJz.js";
33
+ import { i as resolveMemoryBackendConfig, r as getMemorySearchManager } from "./memory-cli-DMJqELwh.js";
34
34
  import { a as readTrustGraphSnapshot, c as pruneExpiredPending, d as writeJsonAtomic, l as readJsonFile, o as saveTrustGraph, s as createAsyncLock, u as resolvePairingPaths } from "./loader-Bw2wdN4l.js";
35
- import { t as ToolInputError } from "./common-BCW6hLGI.js";
36
- import { $ as validateNodePairRequestParams, A as validateCronStatusParams, B as validateExecApprovalsNodeGetParams, C as validateConfigSetParams, Ct as validateWizardCancelParams, D as validateCronRemoveParams, Dt as PROTOCOL_VERSION, E as validateCronListParams, Et as validateWizardStatusParams, F as validateDeviceTokenRevokeParams, G as validateNodeDescribeParams, H as validateExecApprovalsSetParams, I as validateDeviceTokenRotateParams, It as deriveDeviceIdFromPublicKey, J as validateNodeInvokeResultParams, K as validateNodeEventParams, L as validateExecApprovalRequestParams, M as validateDevicePairApproveParams, N as validateDevicePairListParams, Nt as normalizeInputProvenance, O as validateCronRunParams, Ot as ErrorCodes, P as validateDevicePairRejectParams, Pt as buildDeviceAuthPayload, Q as validateNodePairRejectParams, R as validateExecApprovalResolveParams, Rt as normalizeDevicePublicKeyBase64Url, S as validateConfigSchemaParams, St as validateWebLoginWaitParams, T as validateCronAddParams, Tt as validateWizardStartParams, U as validateLogsTailParams, V as validateExecApprovalsNodeSetParams, W as validateModelsListParams, X as validateNodePairApproveParams, Y as validateNodeListParams, Z as validateNodePairListParams, _ as validateChatInjectParams, _t as validateTalkConfigParams, a as validateAgentWaitParams, at as validateSessionsCompactParams, b as validateConfigGetParams, bt as validateWakeParams, c as validateAgentsFilesGetParams, ct as validateSessionsPatchParams, d as validateAgentsListParams, dt as validateSessionsResolveParams, et as validateNodePairVerifyParams, f as validateAgentsUpdateParams, ft as validateSessionsUsageParams, g as validateChatHistoryParams, gt as validateSkillsUpdateParams, h as validateChatAbortParams, ht as validateSkillsStatusParams, i as validateAgentParams, it as validateSendParams, j as validateCronUpdateParams, jt as parseSessionLabel, k as validateCronRunsParams, kt as errorShape, l as validateAgentsFilesListParams, lt as validateSessionsPreviewParams, m as validateChannelsStatusParams, mt as validateSkillsInstallParams, n as formatValidationErrors, nt as validatePollParams, o as validateAgentsCreateParams, ot as validateSessionsDeleteParams, p as validateChannelsLogoutParams, pt as validateSkillsBinsParams, q as validateNodeInvokeParams, r as validateAgentIdentityParams, rt as validateRequestFrame, s as validateAgentsDeleteParams, st as validateSessionsListParams, tt as validateNodeRenameParams, u as validateAgentsFilesSetParams, ut as validateSessionsResetParams, v as validateChatSendParams, vt as validateTalkModeParams, w as validateConnectParams, wt as validateWizardNextParams, x as validateConfigPatchParams, xt as validateWebLoginStartParams, y as validateConfigApplyParams, yt as validateUpdateRunParams, z as validateExecApprovalsGetParams, zt as verifyDeviceSignature } from "./client-BWkoTfOH.js";
37
- import { s as loadGatewayTlsRuntime$1 } from "./call-CDPbPDAr.js";
38
- import { a as resolveSubagentToolPolicy, i as resolveGroupToolPolicy, r as resolveEffectiveToolPolicy } from "./pi-tools.policy-WdTAfqbV.js";
39
- import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-5YtMvm7D.js";
40
- import { t as createBrowserRouteDispatcher } from "./dispatcher-DzwzLQRk.js";
35
+ import { t as ToolInputError } from "./common-fIXNXke2.js";
36
+ import { $ as validateNodePairRequestParams, A as validateCronStatusParams, B as validateExecApprovalsNodeGetParams, C as validateConfigSetParams, Ct as validateWizardCancelParams, D as validateCronRemoveParams, Dt as PROTOCOL_VERSION, E as validateCronListParams, Et as validateWizardStatusParams, F as validateDeviceTokenRevokeParams, G as validateNodeDescribeParams, H as validateExecApprovalsSetParams, I as validateDeviceTokenRotateParams, It as deriveDeviceIdFromPublicKey, J as validateNodeInvokeResultParams, K as validateNodeEventParams, L as validateExecApprovalRequestParams, M as validateDevicePairApproveParams, N as validateDevicePairListParams, Nt as normalizeInputProvenance, O as validateCronRunParams, Ot as ErrorCodes, P as validateDevicePairRejectParams, Pt as buildDeviceAuthPayload, Q as validateNodePairRejectParams, R as validateExecApprovalResolveParams, Rt as normalizeDevicePublicKeyBase64Url, S as validateConfigSchemaParams, St as validateWebLoginWaitParams, T as validateCronAddParams, Tt as validateWizardStartParams, U as validateLogsTailParams, V as validateExecApprovalsNodeSetParams, W as validateModelsListParams, X as validateNodePairApproveParams, Y as validateNodeListParams, Z as validateNodePairListParams, _ as validateChatInjectParams, _t as validateTalkConfigParams, a as validateAgentWaitParams, at as validateSessionsCompactParams, b as validateConfigGetParams, bt as validateWakeParams, c as validateAgentsFilesGetParams, ct as validateSessionsPatchParams, d as validateAgentsListParams, dt as validateSessionsResolveParams, et as validateNodePairVerifyParams, f as validateAgentsUpdateParams, ft as validateSessionsUsageParams, g as validateChatHistoryParams, gt as validateSkillsUpdateParams, h as validateChatAbortParams, ht as validateSkillsStatusParams, i as validateAgentParams, it as validateSendParams, j as validateCronUpdateParams, jt as parseSessionLabel, k as validateCronRunsParams, kt as errorShape, l as validateAgentsFilesListParams, lt as validateSessionsPreviewParams, m as validateChannelsStatusParams, mt as validateSkillsInstallParams, n as formatValidationErrors, nt as validatePollParams, o as validateAgentsCreateParams, ot as validateSessionsDeleteParams, p as validateChannelsLogoutParams, pt as validateSkillsBinsParams, q as validateNodeInvokeParams, r as validateAgentIdentityParams, rt as validateRequestFrame, s as validateAgentsDeleteParams, st as validateSessionsListParams, tt as validateNodeRenameParams, u as validateAgentsFilesSetParams, ut as validateSessionsResetParams, v as validateChatSendParams, vt as validateTalkModeParams, w as validateConnectParams, wt as validateWizardNextParams, x as validateConfigPatchParams, xt as validateWebLoginStartParams, y as validateConfigApplyParams, yt as validateUpdateRunParams, z as validateExecApprovalsGetParams, zt as verifyDeviceSignature } from "./client-DOTb2PN6.js";
37
+ import { s as loadGatewayTlsRuntime$1 } from "./call-Bn9iKzhX.js";
38
+ import { a as resolveSubagentToolPolicy, i as resolveGroupToolPolicy, r as resolveEffectiveToolPolicy } from "./pi-tools.policy-DuWNVoem.js";
39
+ import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-BGnqY7vv.js";
40
+ import { t as createBrowserRouteDispatcher } from "./dispatcher-DpEzmU7s.js";
41
41
  import { t as parseAbsoluteTimeMs } from "./parse-Cinbkvj-.js";
42
42
  import { c as saveToken, i as getToken, l as whoami, n as clearToken, o as registerWithInvite, s as resolveSuggestedIdentity, t as TOKEN_PATH } from "./noxsoft-auth-CE75mBXE.js";
43
43
  import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-BWqR-ik6.js";
44
44
  import { f as resolveExecApprovalsSocketPath, o as normalizeExecApprovals, p as saveExecApprovals, r as ensureExecApprovals, s as readExecApprovalsSnapshot, t as DEFAULT_EXEC_APPROVAL_TIMEOUT_MS } from "./exec-approvals-DK5-KCUz.js";
45
- import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-BQL13ZBH.js";
46
- import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-B7Fb3fO8.js";
47
- import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-BKLIBKjK.js";
48
- import { t as ensureAnimaCliOnPath } from "./path-env-DLQPf9qj.js";
49
- import { t as forceFreePortAndWait } from "./ports-DaVrZDUq.js";
50
- import { t as buildChannelUiCatalog } from "./catalog-CsXv59Tq.js";
51
- import { t as buildWorkspaceSkillStatus } from "./skills-status-CvH7AUoY.js";
45
+ import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-DZOi_4uV.js";
46
+ import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-CZy4g8SY.js";
47
+ import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-oUYlT5Sq.js";
48
+ import { t as ensureAnimaCliOnPath } from "./path-env-BAjR_Xf_.js";
49
+ import { t as forceFreePortAndWait } from "./ports-D6bjtvhF.js";
50
+ import { t as buildChannelUiCatalog } from "./catalog-BqTvQqiq.js";
51
+ import { t as buildWorkspaceSkillStatus } from "./skills-status-BmZufjT1.js";
52
52
  import { n as DEFAULT_GATEWAY_HTTP_TOOL_DENY } from "./dangerous-tools-BCnOEYau.js";
53
- import { C as resolveAssistantAvatarUrl, S as normalizeControlUiBasePath, b as CONTROL_UI_AVATAR_PREFIX, c as handleReset, h as resolveControlUiLinks, x as buildControlUiAvatarUrl } from "./onboard-helpers-CJ3HzoUO.js";
54
- import { t as resolveGatewayService } from "./service-Dd1DfPia.js";
53
+ import { C as resolveAssistantAvatarUrl, S as normalizeControlUiBasePath, b as CONTROL_UI_AVATAR_PREFIX, c as handleReset, h as resolveControlUiLinks, x as buildControlUiAvatarUrl } from "./onboard-helpers-rHLFiSIc.js";
54
+ import { t as resolveGatewayService } from "./service-ho_k2vjr.js";
55
55
  import { t as parsePort } from "./parse-port-CDPwDUs3.js";
56
- import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-CHAT20aR.js";
57
- import { i as toOptionString, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-C-rqLtIT.js";
58
- import { o as isNodeCommandAllowed, s as resolveNodeCommandAllowlist } from "./audit-B05W5ckN.js";
59
- import { n as getStatusSummary } from "./status-BO5BIf81.js";
56
+ import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-DlJrNJOg.js";
57
+ import { i as toOptionString, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-BFzq0XE1.js";
58
+ import { o as isNodeCommandAllowed, s as resolveNodeCommandAllowlist } from "./audit-jddM3B16.js";
59
+ import { n as getStatusSummary } from "./status-D1zupVBm.js";
60
60
  import { t as resolveChannelDefaultAccountId } from "./helpers-1MPChTcB.js";
61
- import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-B5N6_UOf.js";
62
- import { t as applyPluginAutoEnable } from "./plugin-auto-enable-CtYcdTju.js";
63
- import { a as resolveControlUiRootOverrideSync, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync } from "./health-format-D-JJ5_S4.js";
61
+ import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-DrokrOLQ.js";
62
+ import { t as applyPluginAutoEnable } from "./plugin-auto-enable-DvYJhgJ2.js";
63
+ import { a as resolveControlUiRootOverrideSync, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync } from "./health-format-CzNDrJ8Z.js";
64
64
  import { n as validateSystemRunCommandConsistency, r as getMachineDisplayName, t as formatExecCommand } from "./system-run-command-Bx8-5h2r.js";
65
- import { h as normalizeUpdateChannel, l as DEFAULT_PACKAGE_CHANNEL, n as checkUpdateStatus, o as resolveNpmChannelTag, r as compareSemverStrings } from "./channels-status-issues-WkG3Tmxk.js";
66
- import { t as WizardCancelledError } from "./prompts-Bq4QGFQM.js";
67
- import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-DdweuSIj.js";
68
- import { t as runOnboardingWizard } from "./onboarding-BeuMAyic.js";
69
- import { a as setGatewayWsLogStyle, i as summarizeAgentEventForWsLog, n as logWs, r as shouldLogWs, t as formatForLog } from "./ws-log-CUobU2tD.js";
70
- import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-DaJoXV_3.js";
65
+ import { h as normalizeUpdateChannel, l as DEFAULT_PACKAGE_CHANNEL, n as checkUpdateStatus, o as resolveNpmChannelTag, r as compareSemverStrings } from "./channels-status-issues-4XxYhQ8S.js";
66
+ import { t as WizardCancelledError } from "./prompts-D6Xc1Ddb.js";
67
+ import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-BQ7M6bIq.js";
68
+ import { t as runOnboardingWizard } from "./onboarding-BGXbXgds.js";
69
+ import { a as setGatewayWsLogStyle, i as summarizeAgentEventForWsLog, n as logWs, r as shouldLogWs, t as formatForLog } from "./ws-log-Cvz4xRCD.js";
70
+ import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-lTV5KDPF.js";
71
71
  import { t as createOutboundSendDeps$1 } from "./outbound-send-deps-DVfWC4E8.js";
72
- import { a as loadAgentIdentity, c as loadAgentIdentityFromWorkspace, i as listAgentEntries, o as pruneAgentConfig, r as findAgentEntryIndex, t as applyAgentConfig } from "./agents.config-BR5JLtud.js";
73
- import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-BoAAHGEA.js";
72
+ import { a as loadAgentIdentity, c as loadAgentIdentityFromWorkspace, i as listAgentEntries, o as pruneAgentConfig, r as findAgentEntryIndex, t as applyAgentConfig } from "./agents.config-DrViQjgD.js";
73
+ import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-xPFmA8jO.js";
74
74
  import { t as migrateFromCoherence } from "./migrate-DuohB_ur.js";
75
- import { t as installSkill } from "./skills-install-D6_qpRjW.js";
76
- import { t as runGatewayUpdate } from "./update-runner-DZfnquWO.js";
75
+ import { t as installSkill } from "./skills-install-CVWWsOf2.js";
76
+ import { t as runGatewayUpdate } from "./update-runner-C7CjKcMp.js";
77
77
  import { fileURLToPath, pathToFileURL } from "node:url";
78
78
  import * as fsSync from "node:fs";
79
79
  import fs, { constants } from "node:fs";
@@ -111,7 +111,7 @@ function getActiveEmbeddedRunCount() {
111
111
 
112
112
  //#endregion
113
113
  //#region src/infra/exec-approval-forwarder.ts
114
- const log$11 = createSubsystemLogger("gateway/exec-approvals");
114
+ const log$14 = createSubsystemLogger("gateway/exec-approvals");
115
115
  const DEFAULT_MODE = "session";
116
116
  function normalizeMode(mode) {
117
117
  return mode ?? DEFAULT_MODE;
@@ -226,7 +226,7 @@ async function deliverToTargets(params) {
226
226
  payloads: [{ text: params.text }]
227
227
  });
228
228
  } catch (err) {
229
- log$11.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
229
+ log$14.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
230
230
  }
231
231
  });
232
232
  await Promise.allSettled(deliveries);
@@ -1463,7 +1463,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
1463
1463
  * Automatically starts `gog gmail watch serve` when the gateway starts,
1464
1464
  * if hooks.gmail is configured with an account.
1465
1465
  */
1466
- const log$10 = createSubsystemLogger("gmail-watcher");
1466
+ const log$13 = createSubsystemLogger("gmail-watcher");
1467
1467
  const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
1468
1468
  function isAddressInUseError(line) {
1469
1469
  return ADDRESS_IN_USE_RE.test(line);
@@ -1487,13 +1487,13 @@ async function startGmailWatch(cfg) {
1487
1487
  const result = await runCommandWithTimeout(args, { timeoutMs: 12e4 });
1488
1488
  if (result.code !== 0) {
1489
1489
  const message = result.stderr || result.stdout || "gog watch start failed";
1490
- log$10.error(`watch start failed: ${message}`);
1490
+ log$13.error(`watch start failed: ${message}`);
1491
1491
  return false;
1492
1492
  }
1493
- log$10.info(`watch started for ${cfg.account}`);
1493
+ log$13.info(`watch started for ${cfg.account}`);
1494
1494
  return true;
1495
1495
  } catch (err) {
1496
- log$10.error(`watch start error: ${String(err)}`);
1496
+ log$13.error(`watch start error: ${String(err)}`);
1497
1497
  return false;
1498
1498
  }
1499
1499
  }
@@ -1502,7 +1502,7 @@ async function startGmailWatch(cfg) {
1502
1502
  */
1503
1503
  function spawnGogServe(cfg) {
1504
1504
  const args = buildGogWatchServeArgs(cfg);
1505
- log$10.info(`starting gog ${args.join(" ")}`);
1505
+ log$13.info(`starting gog ${args.join(" ")}`);
1506
1506
  let addressInUse = false;
1507
1507
  const child = spawn("gog", args, {
1508
1508
  stdio: [
@@ -1514,25 +1514,25 @@ function spawnGogServe(cfg) {
1514
1514
  });
1515
1515
  child.stdout?.on("data", (data) => {
1516
1516
  const line = data.toString().trim();
1517
- if (line) log$10.info(`[gog] ${line}`);
1517
+ if (line) log$13.info(`[gog] ${line}`);
1518
1518
  });
1519
1519
  child.stderr?.on("data", (data) => {
1520
1520
  const line = data.toString().trim();
1521
1521
  if (!line) return;
1522
1522
  if (isAddressInUseError(line)) addressInUse = true;
1523
- log$10.warn(`[gog] ${line}`);
1523
+ log$13.warn(`[gog] ${line}`);
1524
1524
  });
1525
1525
  child.on("error", (err) => {
1526
- log$10.error(`gog process error: ${String(err)}`);
1526
+ log$13.error(`gog process error: ${String(err)}`);
1527
1527
  });
1528
1528
  child.on("exit", (code, signal) => {
1529
1529
  if (shuttingDown) return;
1530
1530
  if (addressInUse) {
1531
- log$10.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set ANIMA_SKIP_GMAIL_WATCHER=1 or stop the other process.");
1531
+ log$13.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set ANIMA_SKIP_GMAIL_WATCHER=1 or stop the other process.");
1532
1532
  watcherProcess = null;
1533
1533
  return;
1534
1534
  }
1535
- log$10.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
1535
+ log$13.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
1536
1536
  watcherProcess = null;
1537
1537
  setTimeout(() => {
1538
1538
  if (shuttingDown || !currentConfig) return;
@@ -1572,15 +1572,15 @@ async function startGmailWatcher(cfg) {
1572
1572
  port: runtimeConfig.serve.port,
1573
1573
  target: runtimeConfig.tailscale.target
1574
1574
  });
1575
- log$10.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
1575
+ log$13.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
1576
1576
  } catch (err) {
1577
- log$10.error(`tailscale setup failed: ${String(err)}`);
1577
+ log$13.error(`tailscale setup failed: ${String(err)}`);
1578
1578
  return {
1579
1579
  started: false,
1580
1580
  reason: `tailscale setup failed: ${String(err)}`
1581
1581
  };
1582
1582
  }
1583
- if (!await startGmailWatch(runtimeConfig)) log$10.warn("gmail watch start failed, but continuing with serve");
1583
+ if (!await startGmailWatch(runtimeConfig)) log$13.warn("gmail watch start failed, but continuing with serve");
1584
1584
  shuttingDown = false;
1585
1585
  watcherProcess = spawnGogServe(runtimeConfig);
1586
1586
  const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
@@ -1588,7 +1588,7 @@ async function startGmailWatcher(cfg) {
1588
1588
  if (shuttingDown) return;
1589
1589
  startGmailWatch(runtimeConfig);
1590
1590
  }, renewMs);
1591
- log$10.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
1591
+ log$13.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
1592
1592
  return { started: true };
1593
1593
  }
1594
1594
  /**
@@ -1601,7 +1601,7 @@ async function stopGmailWatcher() {
1601
1601
  renewInterval = null;
1602
1602
  }
1603
1603
  if (watcherProcess) {
1604
- log$10.info("stopping gmail watcher");
1604
+ log$13.info("stopping gmail watcher");
1605
1605
  watcherProcess.kill("SIGTERM");
1606
1606
  await new Promise((resolve) => {
1607
1607
  const timeout = setTimeout(() => {
@@ -1616,7 +1616,7 @@ async function stopGmailWatcher() {
1616
1616
  watcherProcess = null;
1617
1617
  }
1618
1618
  currentConfig = null;
1619
- log$10.info("gmail watcher stopped");
1619
+ log$13.info("gmail watcher stopped");
1620
1620
  }
1621
1621
 
1622
1622
  //#endregion
@@ -4894,6 +4894,8 @@ const BASE_METHODS = [
4894
4894
  "anima.registration.status",
4895
4895
  "anima.registration.set-token",
4896
4896
  "anima.registration.register-invite",
4897
+ "ico.metrics.get",
4898
+ "impact.footprint.get",
4897
4899
  "health",
4898
4900
  "logs.tail",
4899
4901
  "channels.status",
@@ -4978,7 +4980,14 @@ const BASE_METHODS = [
4978
4980
  "agent",
4979
4981
  "agent.identity.get",
4980
4982
  "agent.wait",
4983
+ "browser.capabilities.get",
4981
4984
  "browser.request",
4985
+ "desktop.control.session.create",
4986
+ "desktop.control.session.list",
4987
+ "desktop.control.session.get",
4988
+ "desktop.control.session.approve",
4989
+ "desktop.control.session.close",
4990
+ "desktop.control.session.request",
4982
4991
  "chat.history",
4983
4992
  "chat.abort",
4984
4993
  "chat.send"
@@ -8260,6 +8269,44 @@ function safeParseJson(value) {
8260
8269
 
8261
8270
  //#endregion
8262
8271
  //#region src/gateway/server-methods/browser.ts
8272
+ const DESKTOP_CONTROL_DEFAULT_TTL_MS = 900 * 1e3;
8273
+ const DESKTOP_CONTROL_MIN_TTL_MS = 60 * 1e3;
8274
+ const DESKTOP_CONTROL_MAX_TTL_MS = 14400 * 1e3;
8275
+ const DESKTOP_CONTROL_MAX_REASON_LEN = 240;
8276
+ const DESKTOP_CONTROL_AUDIT_MAX_EVENTS = 200;
8277
+ const DESKTOP_CONTROL_RETENTION_MS = 7200 * 1e3;
8278
+ const DESKTOP_CONTROL_DEFAULT_ALLOWED_METHODS = ["GET"];
8279
+ const DESKTOP_CONTROL_ALLOWED_METHODS = [
8280
+ "GET",
8281
+ "POST",
8282
+ "DELETE"
8283
+ ];
8284
+ const DESKTOP_CONTROL_DEFAULT_MAX_REQUESTS = 40;
8285
+ const DESKTOP_CONTROL_MIN_MAX_REQUESTS = 1;
8286
+ const DESKTOP_CONTROL_MAX_MAX_REQUESTS = 500;
8287
+ const DESKTOP_CONTROL_MAX_NOTE_LEN = 500;
8288
+ const DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN = 8;
8289
+ const DESKTOP_CONTROL_LIST_MIN_LIMIT = 1;
8290
+ const DESKTOP_CONTROL_LIST_MAX_LIMIT = 500;
8291
+ const DESKTOP_CONTROL_LIST_MIN_OFFSET = 0;
8292
+ const DESKTOP_CONTROL_LIST_MAX_OFFSET = 1e4;
8293
+ const BROWSER_REQUEST_MIN_TIMEOUT_MS = 1;
8294
+ const BROWSER_REQUEST_MAX_TIMEOUT_MS = 12e4;
8295
+ const DESKTOP_CONTROL_SESSION_STATES = [
8296
+ "pending_approval",
8297
+ "active",
8298
+ "denied",
8299
+ "closed",
8300
+ "expired"
8301
+ ];
8302
+ const DESKTOP_CONTROL_SESSION_DECISIONS = [
8303
+ "pending",
8304
+ "allow",
8305
+ "deny"
8306
+ ];
8307
+ const DESKTOP_CONTROL_SESSION_ROUTE_KINDS = ["local", "node"];
8308
+ const DESKTOP_CONTROL_SESSION_RISK_LEVELS = ["standard", "elevated"];
8309
+ const desktopControlSessions = /* @__PURE__ */ new Map();
8263
8310
  function isBrowserNode(node) {
8264
8311
  const caps = Array.isArray(node.caps) ? node.caps : [];
8265
8312
  const commands = Array.isArray(node.commands) ? node.commands : [];
@@ -8309,100 +8356,1290 @@ async function persistProxyFiles(files) {
8309
8356
  function applyProxyPaths(result, mapping) {
8310
8357
  applyBrowserProxyPaths(result, mapping);
8311
8358
  }
8312
- const browserHandlers = { "browser.request": async ({ params, respond, context }) => {
8313
- const typed = params;
8314
- const methodRaw = typeof typed.method === "string" ? typed.method.trim().toUpperCase() : "";
8315
- const path = typeof typed.path === "string" ? typed.path.trim() : "";
8316
- const query = typed.query && typeof typed.query === "object" ? typed.query : void 0;
8317
- const body = typed.body;
8318
- const timeoutMs = typeof typed.timeoutMs === "number" && Number.isFinite(typed.timeoutMs) ? Math.max(1, Math.floor(typed.timeoutMs)) : void 0;
8319
- if (!methodRaw || !path) {
8320
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "method and path are required"));
8321
- return;
8359
+ function toNodeSummary(node) {
8360
+ return {
8361
+ nodeId: node.nodeId,
8362
+ displayName: node.displayName ?? null,
8363
+ remoteIp: node.remoteIp ?? null
8364
+ };
8365
+ }
8366
+ function hasValue(value) {
8367
+ return typeof value === "string" && value.trim().length > 0;
8368
+ }
8369
+ function resolveGatewayAuthMode(cfg) {
8370
+ const configuredMode = cfg.gateway?.auth?.mode;
8371
+ if (configuredMode === "token" || configuredMode === "password" || configuredMode === "trusted-proxy") return configuredMode;
8372
+ if (hasValue(cfg.gateway?.auth?.token)) return "token";
8373
+ if (hasValue(cfg.gateway?.auth?.password)) return "password";
8374
+ if (hasValue(cfg.gateway?.auth?.trustedProxy?.userHeader)) return "trusted-proxy";
8375
+ return "none";
8376
+ }
8377
+ function resolveClientActor(client) {
8378
+ const displayName = client?.connect?.client?.displayName;
8379
+ if (typeof displayName === "string" && displayName.trim().length > 0) return displayName.trim();
8380
+ const clientId = client?.connect?.client?.id;
8381
+ if (typeof clientId === "string" && clientId.trim().length > 0) return clientId.trim();
8382
+ const deviceId = client?.connect?.device?.id;
8383
+ if (typeof deviceId === "string" && deviceId.trim().length > 0) return deviceId.trim();
8384
+ return null;
8385
+ }
8386
+ function hasOperatorScope(client, scope) {
8387
+ const scopes = Array.isArray(client?.connect?.scopes) ? client?.connect?.scopes : [];
8388
+ return scopes.includes("operator.admin") || scopes.includes(scope);
8389
+ }
8390
+ function normalizeDesktopSessionTtl(input) {
8391
+ if (typeof input !== "number" || !Number.isFinite(input)) return DESKTOP_CONTROL_DEFAULT_TTL_MS;
8392
+ return Math.min(DESKTOP_CONTROL_MAX_TTL_MS, Math.max(DESKTOP_CONTROL_MIN_TTL_MS, Math.floor(input)));
8393
+ }
8394
+ function normalizeDesktopSessionReason(input) {
8395
+ if (typeof input !== "string") return "Desktop control session";
8396
+ const trimmed = input.trim();
8397
+ if (!trimmed) return "Desktop control session";
8398
+ return trimmed.slice(0, DESKTOP_CONTROL_MAX_REASON_LEN);
8399
+ }
8400
+ function normalizeDesktopSessionNote(input) {
8401
+ if (typeof input !== "string") return null;
8402
+ const trimmed = input.trim();
8403
+ if (!trimmed) return null;
8404
+ return trimmed.slice(0, DESKTOP_CONTROL_MAX_NOTE_LEN);
8405
+ }
8406
+ function hasDecisionRationale(note) {
8407
+ return typeof note === "string" && note.length >= DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN;
8408
+ }
8409
+ function isDesktopControlSessionState(value) {
8410
+ return typeof value === "string" && DESKTOP_CONTROL_SESSION_STATES.includes(value);
8411
+ }
8412
+ function isDesktopControlApprovalDecision(value) {
8413
+ return typeof value === "string" && DESKTOP_CONTROL_SESSION_DECISIONS.includes(value);
8414
+ }
8415
+ function isDesktopControlSessionRouteKind(value) {
8416
+ return typeof value === "string" && DESKTOP_CONTROL_SESSION_ROUTE_KINDS.includes(value);
8417
+ }
8418
+ function isDesktopControlSessionRiskLevel(value) {
8419
+ return typeof value === "string" && DESKTOP_CONTROL_SESSION_RISK_LEVELS.includes(value);
8420
+ }
8421
+ function parseDesktopSessionMethod(value) {
8422
+ const method = value.trim().toUpperCase();
8423
+ if (DESKTOP_CONTROL_ALLOWED_METHODS.includes(method)) return method;
8424
+ return null;
8425
+ }
8426
+ function normalizeDesktopSessionAllowMethods(input) {
8427
+ if (!Array.isArray(input)) return [...DESKTOP_CONTROL_DEFAULT_ALLOWED_METHODS];
8428
+ const normalized = [];
8429
+ for (const entry of input) {
8430
+ if (typeof entry !== "string") continue;
8431
+ const method = parseDesktopSessionMethod(entry);
8432
+ if (method && !normalized.includes(method)) normalized.push(method);
8322
8433
  }
8323
- if (methodRaw !== "GET" && methodRaw !== "POST" && methodRaw !== "DELETE") {
8324
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "method must be GET, POST, or DELETE"));
8325
- return;
8434
+ if (normalized.length === 0) return [...DESKTOP_CONTROL_DEFAULT_ALLOWED_METHODS];
8435
+ return normalized;
8436
+ }
8437
+ function normalizeDesktopSessionMaxRequests(input) {
8438
+ if (typeof input !== "number" || !Number.isFinite(input)) return DESKTOP_CONTROL_DEFAULT_MAX_REQUESTS;
8439
+ return Math.min(DESKTOP_CONTROL_MAX_MAX_REQUESTS, Math.max(DESKTOP_CONTROL_MIN_MAX_REQUESTS, Math.floor(input)));
8440
+ }
8441
+ function resolveDesktopSessionRisk(controls) {
8442
+ const reasons = [];
8443
+ if (controls.allowMethods.some((method) => method !== "GET")) reasons.push("write methods enabled (POST/DELETE)");
8444
+ if (controls.maxRequests > DESKTOP_CONTROL_DEFAULT_MAX_REQUESTS) reasons.push(`request budget exceeds standard (${controls.maxRequests} > ${DESKTOP_CONTROL_DEFAULT_MAX_REQUESTS})`);
8445
+ return {
8446
+ level: reasons.length > 0 ? "elevated" : "standard",
8447
+ reasons
8448
+ };
8449
+ }
8450
+ function validateDesktopControlSessionCreateParams(params) {
8451
+ if (params.reason !== void 0 && typeof params.reason !== "string") return errorShape(ErrorCodes.INVALID_REQUEST, `invalid reason: ${String(params.reason)}`, { details: { expectedType: "string" } });
8452
+ if (params.ttlMs !== void 0) {
8453
+ if (typeof params.ttlMs !== "number" || !Number.isFinite(params.ttlMs) || !Number.isInteger(params.ttlMs) || params.ttlMs < DESKTOP_CONTROL_MIN_TTL_MS || params.ttlMs > DESKTOP_CONTROL_MAX_TTL_MS) return errorShape(ErrorCodes.INVALID_REQUEST, `invalid ttlMs: ${String(params.ttlMs)}`, { details: {
8454
+ minTtlMs: DESKTOP_CONTROL_MIN_TTL_MS,
8455
+ maxTtlMs: DESKTOP_CONTROL_MAX_TTL_MS
8456
+ } });
8326
8457
  }
8327
- const cfg = loadConfig();
8328
- let nodeTarget = null;
8458
+ if (params.nodeId !== void 0 && typeof params.nodeId !== "string") return errorShape(ErrorCodes.INVALID_REQUEST, `invalid nodeId: ${String(params.nodeId)}`, { details: { expectedType: "string" } });
8459
+ if (typeof params.nodeId === "string" && !params.nodeId.trim()) return errorShape(ErrorCodes.INVALID_REQUEST, "invalid nodeId: must not be empty");
8460
+ if (params.allowMethods !== void 0) {
8461
+ if (!Array.isArray(params.allowMethods)) return errorShape(ErrorCodes.INVALID_REQUEST, `invalid allowMethods: ${String(params.allowMethods)}`, { details: {
8462
+ expectedType: "string[]",
8463
+ allowedMethods: DESKTOP_CONTROL_ALLOWED_METHODS
8464
+ } });
8465
+ if (params.allowMethods.length === 0) return errorShape(ErrorCodes.INVALID_REQUEST, "allowMethods must include at least one method", { details: { allowedMethods: DESKTOP_CONTROL_ALLOWED_METHODS } });
8466
+ for (let i = 0; i < params.allowMethods.length; i += 1) {
8467
+ const entry = params.allowMethods[i];
8468
+ if (typeof entry !== "string") return errorShape(ErrorCodes.INVALID_REQUEST, `invalid allowMethods[${i}]: ${String(entry)}`, { details: {
8469
+ expectedType: "string",
8470
+ allowedMethods: DESKTOP_CONTROL_ALLOWED_METHODS
8471
+ } });
8472
+ if (!parseDesktopSessionMethod(entry)) return errorShape(ErrorCodes.INVALID_REQUEST, `invalid allowMethods[${i}]: ${entry.trim() || "<empty>"}`, { details: { allowedMethods: DESKTOP_CONTROL_ALLOWED_METHODS } });
8473
+ }
8474
+ }
8475
+ if (params.maxRequests !== void 0) {
8476
+ if (typeof params.maxRequests !== "number" || !Number.isFinite(params.maxRequests) || !Number.isInteger(params.maxRequests) || params.maxRequests < DESKTOP_CONTROL_MIN_MAX_REQUESTS || params.maxRequests > DESKTOP_CONTROL_MAX_MAX_REQUESTS) return errorShape(ErrorCodes.INVALID_REQUEST, `invalid maxRequests: ${String(params.maxRequests)}`, { details: {
8477
+ minMaxRequests: DESKTOP_CONTROL_MIN_MAX_REQUESTS,
8478
+ maxMaxRequests: DESKTOP_CONTROL_MAX_MAX_REQUESTS
8479
+ } });
8480
+ }
8481
+ return null;
8482
+ }
8483
+ function appendDesktopControlAudit(session, event) {
8484
+ const next = {
8485
+ id: crypto.randomUUID(),
8486
+ ts: typeof event.ts === "number" && Number.isFinite(event.ts) ? event.ts : Date.now(),
8487
+ type: event.type,
8488
+ actor: event.actor,
8489
+ details: event.details
8490
+ };
8491
+ session.audit.push(next);
8492
+ if (session.audit.length > DESKTOP_CONTROL_AUDIT_MAX_EVENTS) session.audit.splice(0, session.audit.length - DESKTOP_CONTROL_AUDIT_MAX_EVENTS);
8493
+ }
8494
+ function toDesktopControlSessionSnapshot(session, includeAudit = false) {
8495
+ return {
8496
+ id: session.id,
8497
+ reason: session.reason,
8498
+ createdAtMs: session.createdAtMs,
8499
+ expiresAtMs: session.expiresAtMs,
8500
+ state: session.state,
8501
+ route: session.route.kind === "node" ? {
8502
+ kind: "node",
8503
+ node: { ...session.route.node }
8504
+ } : session.route,
8505
+ approval: { ...session.approval },
8506
+ controls: {
8507
+ allowMethods: [...session.controls.allowMethods],
8508
+ maxRequests: session.controls.maxRequests
8509
+ },
8510
+ risk: {
8511
+ level: session.risk.level,
8512
+ reasons: [...session.risk.reasons]
8513
+ },
8514
+ requestCount: session.requestCount,
8515
+ lastRequestAtMs: session.lastRequestAtMs,
8516
+ closedAtMs: session.closedAtMs,
8517
+ audit: includeAudit ? session.audit.map((entry) => ({ ...entry })) : void 0
8518
+ };
8519
+ }
8520
+ function broadcastDesktopControlSessionEvent(params) {
8521
+ const latestAudit = params.session.audit[params.session.audit.length - 1];
8522
+ params.context.broadcast("desktop.control.session.updated", {
8523
+ ts: Date.now(),
8524
+ action: params.action,
8525
+ actor: params.actor,
8526
+ details: params.details,
8527
+ session: toDesktopControlSessionSnapshot(params.session, false),
8528
+ latestAudit: latestAudit ? { ...latestAudit } : null
8529
+ }, { dropIfSlow: true });
8530
+ }
8531
+ function pruneDesktopControlSessions(params) {
8532
+ const now = params?.now ?? Date.now();
8533
+ const context = params?.context;
8534
+ for (const session of desktopControlSessions.values()) if ((session.state === "pending_approval" || session.state === "active") && session.expiresAtMs <= now) {
8535
+ session.state = "expired";
8536
+ session.closedAtMs = now;
8537
+ appendDesktopControlAudit(session, {
8538
+ type: "session.expired",
8539
+ actor: "system"
8540
+ });
8541
+ if (context) broadcastDesktopControlSessionEvent({
8542
+ context,
8543
+ action: "expired",
8544
+ session,
8545
+ actor: "system"
8546
+ });
8547
+ }
8548
+ for (const [id, session] of desktopControlSessions.entries()) if ((session.state === "closed" || session.state === "denied" || session.state === "expired") && session.closedAtMs && now - session.closedAtMs > DESKTOP_CONTROL_RETENTION_MS) desktopControlSessions.delete(id);
8549
+ }
8550
+ function normalizeBrowserRequest(params) {
8551
+ const methodRaw = typeof params.method === "string" ? params.method.trim().toUpperCase() : "";
8552
+ const path = typeof params.path === "string" ? params.path.trim() : "";
8553
+ const queryRaw = params.query;
8554
+ const body = params.body;
8555
+ const timeoutRaw = params.timeoutMs;
8556
+ if (!methodRaw || !path) return {
8557
+ ok: false,
8558
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "method and path are required")
8559
+ };
8560
+ if (methodRaw !== "GET" && methodRaw !== "POST" && methodRaw !== "DELETE") return {
8561
+ ok: false,
8562
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "method must be GET, POST, or DELETE")
8563
+ };
8564
+ if (!path.startsWith("/")) return {
8565
+ ok: false,
8566
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "path must start with /")
8567
+ };
8568
+ if (queryRaw !== void 0) {
8569
+ if (!isPlainObjectRecord(queryRaw)) return {
8570
+ ok: false,
8571
+ error: errorShape(ErrorCodes.INVALID_REQUEST, `invalid query: ${String(queryRaw)}`, { details: { expectedType: "object" } })
8572
+ };
8573
+ const queryIssue = findFirstJsonSerializationIssue(queryRaw, "query");
8574
+ if (queryIssue) return {
8575
+ ok: false,
8576
+ error: errorShape(ErrorCodes.INVALID_REQUEST, `invalid query: non-json-serializable value at ${queryIssue.path}`, { details: {
8577
+ expectedType: "json-serializable",
8578
+ actualType: queryIssue.actualType,
8579
+ path: queryIssue.path
8580
+ } })
8581
+ };
8582
+ }
8583
+ if (body !== void 0) {
8584
+ const bodyIssue = findFirstJsonSerializationIssue(body, "body");
8585
+ if (bodyIssue) return {
8586
+ ok: false,
8587
+ error: errorShape(ErrorCodes.INVALID_REQUEST, `invalid body: non-json-serializable value at ${bodyIssue.path}`, { details: {
8588
+ expectedType: "json-serializable",
8589
+ actualType: bodyIssue.actualType,
8590
+ path: bodyIssue.path
8591
+ } })
8592
+ };
8593
+ }
8594
+ if (timeoutRaw !== void 0) {
8595
+ if (typeof timeoutRaw !== "number" || !Number.isFinite(timeoutRaw) || !Number.isInteger(timeoutRaw) || timeoutRaw < BROWSER_REQUEST_MIN_TIMEOUT_MS || timeoutRaw > BROWSER_REQUEST_MAX_TIMEOUT_MS) return {
8596
+ ok: false,
8597
+ error: errorShape(ErrorCodes.INVALID_REQUEST, `invalid timeoutMs: ${String(timeoutRaw)}`, { details: {
8598
+ expectedType: "integer",
8599
+ minTimeoutMs: BROWSER_REQUEST_MIN_TIMEOUT_MS,
8600
+ maxTimeoutMs: BROWSER_REQUEST_MAX_TIMEOUT_MS
8601
+ } })
8602
+ };
8603
+ }
8604
+ return {
8605
+ ok: true,
8606
+ request: {
8607
+ methodRaw,
8608
+ path,
8609
+ query: queryRaw,
8610
+ body,
8611
+ timeoutMs: typeof timeoutRaw === "number" ? Math.floor(timeoutRaw) : void 0
8612
+ }
8613
+ };
8614
+ }
8615
+ async function dispatchBrowserRequest(params) {
8616
+ const { cfg, request, context, nodeTarget } = params;
8617
+ if (nodeTarget) {
8618
+ const allowlist = resolveNodeCommandAllowlist(cfg, nodeTarget);
8619
+ const allowed = isNodeCommandAllowed({
8620
+ command: "browser.proxy",
8621
+ declaredCommands: nodeTarget.commands,
8622
+ allowlist
8623
+ });
8624
+ if (!allowed.ok) return {
8625
+ ok: false,
8626
+ route: "node",
8627
+ nodeId: nodeTarget.nodeId,
8628
+ status: 403,
8629
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "node command not allowed", { details: {
8630
+ reason: allowed.reason,
8631
+ command: "browser.proxy"
8632
+ } })
8633
+ };
8634
+ const proxyParams = {
8635
+ method: request.methodRaw,
8636
+ path: request.path,
8637
+ query: request.query,
8638
+ body: request.body,
8639
+ timeoutMs: request.timeoutMs,
8640
+ profile: typeof request.query?.profile === "string" ? request.query.profile : void 0
8641
+ };
8642
+ const res = await context.nodeRegistry.invoke({
8643
+ nodeId: nodeTarget.nodeId,
8644
+ command: "browser.proxy",
8645
+ params: proxyParams,
8646
+ timeoutMs: request.timeoutMs,
8647
+ idempotencyKey: crypto.randomUUID()
8648
+ });
8649
+ if (!res.ok) return {
8650
+ ok: false,
8651
+ route: "node",
8652
+ nodeId: nodeTarget.nodeId,
8653
+ status: 503,
8654
+ error: errorShape(ErrorCodes.UNAVAILABLE, res.error?.message ?? "node invoke failed", { details: { nodeError: res.error ?? null } })
8655
+ };
8656
+ const payload = res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload;
8657
+ const proxy = payload && typeof payload === "object" ? payload : null;
8658
+ if (!proxy || !("result" in proxy)) return {
8659
+ ok: false,
8660
+ route: "node",
8661
+ nodeId: nodeTarget.nodeId,
8662
+ status: 503,
8663
+ error: errorShape(ErrorCodes.UNAVAILABLE, "browser proxy failed")
8664
+ };
8665
+ const mapping = await persistProxyFiles(proxy.files);
8666
+ applyProxyPaths(proxy.result, mapping);
8667
+ return {
8668
+ ok: true,
8669
+ route: "node",
8670
+ nodeId: nodeTarget.nodeId,
8671
+ status: 200,
8672
+ payload: proxy.result
8673
+ };
8674
+ }
8675
+ if (!await startBrowserControlServiceFromConfig()) return {
8676
+ ok: false,
8677
+ route: "local",
8678
+ nodeId: null,
8679
+ status: 503,
8680
+ error: errorShape(ErrorCodes.UNAVAILABLE, "browser control is disabled")
8681
+ };
8682
+ let dispatcher;
8329
8683
  try {
8330
- nodeTarget = resolveBrowserNodeTarget({
8684
+ dispatcher = createBrowserRouteDispatcher(createBrowserControlContext());
8685
+ } catch (err) {
8686
+ return {
8687
+ ok: false,
8688
+ route: "local",
8689
+ nodeId: null,
8690
+ status: 503,
8691
+ error: errorShape(ErrorCodes.UNAVAILABLE, String(err))
8692
+ };
8693
+ }
8694
+ const result = await dispatcher.dispatch({
8695
+ method: request.methodRaw,
8696
+ path: request.path,
8697
+ query: request.query,
8698
+ body: request.body
8699
+ });
8700
+ if (result.status >= 400) {
8701
+ const message = result.body && typeof result.body === "object" && "error" in result.body ? String(result.body.error) : `browser request failed (${result.status})`;
8702
+ const code = result.status >= 500 ? ErrorCodes.UNAVAILABLE : ErrorCodes.INVALID_REQUEST;
8703
+ return {
8704
+ ok: false,
8705
+ route: "local",
8706
+ nodeId: null,
8707
+ status: result.status,
8708
+ error: errorShape(code, message, { details: result.body })
8709
+ };
8710
+ }
8711
+ return {
8712
+ ok: true,
8713
+ route: "local",
8714
+ nodeId: null,
8715
+ status: result.status,
8716
+ payload: result.body
8717
+ };
8718
+ }
8719
+ function resolveDesktopSessionNodeTarget(params) {
8720
+ if (params.session.route.kind !== "node") return null;
8721
+ return params.nodes.find((node) => node.nodeId === params.session.route.node.nodeId && isBrowserNode(node)) ?? null;
8722
+ }
8723
+ function ensureDesktopSessionExists(idRaw) {
8724
+ if (idRaw !== void 0 && typeof idRaw !== "string") return {
8725
+ ok: false,
8726
+ error: errorShape(ErrorCodes.INVALID_REQUEST, `invalid id: ${String(idRaw)}`, { details: { expectedType: "string" } })
8727
+ };
8728
+ const id = typeof idRaw === "string" ? idRaw.trim() : "";
8729
+ if (!id) return {
8730
+ ok: false,
8731
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "id is required")
8732
+ };
8733
+ const session = desktopControlSessions.get(id);
8734
+ if (!session) return {
8735
+ ok: false,
8736
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "unknown desktop control session id")
8737
+ };
8738
+ return {
8739
+ ok: true,
8740
+ session
8741
+ };
8742
+ }
8743
+ function toParamValueType(value) {
8744
+ if (value === null) return "null";
8745
+ if (Array.isArray(value)) return "array";
8746
+ return typeof value;
8747
+ }
8748
+ function isPlainObjectRecord(value) {
8749
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
8750
+ const proto = Object.getPrototypeOf(value);
8751
+ return proto === Object.prototype || proto === null;
8752
+ }
8753
+ function appendJsonPath(basePath, segment) {
8754
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(segment)) return `${basePath}.${segment}`;
8755
+ return `${basePath}[${JSON.stringify(segment)}]`;
8756
+ }
8757
+ function findFirstJsonSerializationIssue(value, path, seen = /* @__PURE__ */ new Set()) {
8758
+ if (value === null) return null;
8759
+ const valueType = typeof value;
8760
+ if (valueType === "number") {
8761
+ if (!Number.isFinite(value)) return {
8762
+ path,
8763
+ actualType: "number(non-finite)"
8764
+ };
8765
+ return null;
8766
+ }
8767
+ if (valueType === "string" || valueType === "boolean") return null;
8768
+ if (valueType === "undefined" || valueType === "function" || valueType === "symbol" || valueType === "bigint") return {
8769
+ path,
8770
+ actualType: valueType
8771
+ };
8772
+ if (Array.isArray(value)) {
8773
+ if (seen.has(value)) return {
8774
+ path,
8775
+ actualType: "circular"
8776
+ };
8777
+ seen.add(value);
8778
+ for (let i = 0; i < value.length; i += 1) {
8779
+ const nestedIssue = findFirstJsonSerializationIssue(value[i], `${path}[${i}]`, seen);
8780
+ if (nestedIssue) {
8781
+ seen.delete(value);
8782
+ return nestedIssue;
8783
+ }
8784
+ }
8785
+ seen.delete(value);
8786
+ return null;
8787
+ }
8788
+ if (!value || typeof value !== "object") return {
8789
+ path,
8790
+ actualType: valueType
8791
+ };
8792
+ if (!isPlainObjectRecord(value)) return {
8793
+ path,
8794
+ actualType: toParamValueType(value)
8795
+ };
8796
+ if (seen.has(value)) return {
8797
+ path,
8798
+ actualType: "circular"
8799
+ };
8800
+ seen.add(value);
8801
+ for (const [key, entry] of Object.entries(value)) {
8802
+ const nestedIssue = findFirstJsonSerializationIssue(entry, appendJsonPath(path, key), seen);
8803
+ if (nestedIssue) {
8804
+ seen.delete(value);
8805
+ return nestedIssue;
8806
+ }
8807
+ }
8808
+ seen.delete(value);
8809
+ return null;
8810
+ }
8811
+ function normalizeObjectParams(params) {
8812
+ if (params === void 0 || params === null) return {
8813
+ ok: true,
8814
+ value: {}
8815
+ };
8816
+ if (!isPlainObjectRecord(params)) return {
8817
+ ok: false,
8818
+ error: errorShape(ErrorCodes.INVALID_REQUEST, "invalid params: expected object", { details: {
8819
+ expectedType: "object",
8820
+ actualType: toParamValueType(params)
8821
+ } })
8822
+ };
8823
+ return {
8824
+ ok: true,
8825
+ value: params
8826
+ };
8827
+ }
8828
+ function buildBrowserCapabilitiesSnapshot(params) {
8829
+ const browserEnabled = params.cfg.browser?.enabled !== false;
8830
+ const evaluateEnabled = browserEnabled && params.cfg.browser?.evaluateEnabled !== false;
8831
+ const mode = params.cfg.gateway?.nodes?.browser?.mode ?? "auto";
8832
+ const pinnedNode = hasValue(params.cfg.gateway?.nodes?.browser?.node) ? String(params.cfg.gateway?.nodes?.browser?.node).trim() : null;
8833
+ const availableNodes = params.nodes.filter((node) => isBrowserNode(node)).map(toNodeSummary);
8834
+ const authMode = resolveGatewayAuthMode(params.cfg);
8835
+ const authConfigured = hasValue(params.cfg.gateway?.auth?.token) || hasValue(params.cfg.gateway?.auth?.password) || authMode === "trusted-proxy";
8836
+ let selectedNode = null;
8837
+ let routingError = null;
8838
+ try {
8839
+ selectedNode = resolveBrowserNodeTarget(params);
8840
+ } catch (error) {
8841
+ routingError = String(error);
8842
+ }
8843
+ const activeRoute = !browserEnabled || mode === "off" ? "disabled" : routingError ? "error" : selectedNode ? "node" : "local";
8844
+ const warnings = [];
8845
+ if (browserEnabled && !authConfigured) warnings.push("Browser control is enabled without gateway auth. Configure gateway.auth.token or gateway.auth.password.");
8846
+ if (mode === "manual" && !pinnedNode) warnings.push("Browser node routing is set to manual but no gateway.nodes.browser.node is pinned; routing will fall back to local browser control.");
8847
+ if (availableNodes.length > 1 && mode === "auto" && !pinnedNode && !selectedNode) warnings.push("Multiple browser-capable nodes are connected; set gateway.nodes.browser.node to pin a target.");
8848
+ if (routingError) warnings.push(routingError);
8849
+ return {
8850
+ browserEnabled,
8851
+ evaluateEnabled,
8852
+ auth: {
8853
+ configured: authConfigured,
8854
+ mode: authMode
8855
+ },
8856
+ routing: {
8857
+ mode,
8858
+ pinnedNode,
8859
+ activeRoute,
8860
+ selectedNode: selectedNode ? toNodeSummary(selectedNode) : null,
8861
+ availableNodes,
8862
+ error: routingError
8863
+ },
8864
+ warnings
8865
+ };
8866
+ }
8867
+ const browserHandlers = {
8868
+ "browser.capabilities.get": async ({ respond, context }) => {
8869
+ try {
8870
+ respond(true, buildBrowserCapabilitiesSnapshot({
8871
+ cfg: loadConfig(),
8872
+ nodes: context.nodeRegistry.listConnected()
8873
+ }));
8874
+ } catch (error) {
8875
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
8876
+ }
8877
+ },
8878
+ "browser.request": async ({ params, respond, context }) => {
8879
+ const normalized = normalizeBrowserRequest(params);
8880
+ if (!normalized.ok) {
8881
+ respond(false, void 0, normalized.error);
8882
+ return;
8883
+ }
8884
+ const cfg = loadConfig();
8885
+ let nodeTarget = null;
8886
+ try {
8887
+ nodeTarget = resolveBrowserNodeTarget({
8888
+ cfg,
8889
+ nodes: context.nodeRegistry.listConnected()
8890
+ });
8891
+ } catch (err) {
8892
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(err)));
8893
+ return;
8894
+ }
8895
+ const result = await dispatchBrowserRequest({
8331
8896
  cfg,
8332
- nodes: context.nodeRegistry.listConnected()
8897
+ request: normalized.request,
8898
+ context,
8899
+ nodeTarget
8900
+ });
8901
+ if (!result.ok) {
8902
+ respond(false, void 0, result.error);
8903
+ return;
8904
+ }
8905
+ respond(true, result.payload);
8906
+ },
8907
+ "desktop.control.session.create": async ({ params, respond, context, client }) => {
8908
+ pruneDesktopControlSessions({ context });
8909
+ if (!hasOperatorScope(client, "operator.write")) {
8910
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.write"));
8911
+ return;
8912
+ }
8913
+ const normalizedParams = normalizeObjectParams(params);
8914
+ if (!normalizedParams.ok) {
8915
+ respond(false, void 0, normalizedParams.error);
8916
+ return;
8917
+ }
8918
+ const typed = normalizedParams.value;
8919
+ const createValidationError = validateDesktopControlSessionCreateParams(typed);
8920
+ if (createValidationError) {
8921
+ respond(false, void 0, createValidationError);
8922
+ return;
8923
+ }
8924
+ const cfg = loadConfig();
8925
+ const connectedNodes = context.nodeRegistry.listConnected();
8926
+ const browserNodes = connectedNodes.filter((node) => isBrowserNode(node));
8927
+ const requestedNodeId = typeof typed.nodeId === "string" ? typed.nodeId.trim() : "";
8928
+ const reason = normalizeDesktopSessionReason(typed.reason);
8929
+ const ttlMs = normalizeDesktopSessionTtl(typed.ttlMs);
8930
+ const allowMethods = normalizeDesktopSessionAllowMethods(typed.allowMethods);
8931
+ const maxRequests = normalizeDesktopSessionMaxRequests(typed.maxRequests);
8932
+ const risk = resolveDesktopSessionRisk({
8933
+ allowMethods,
8934
+ maxRequests
8935
+ });
8936
+ const now = Date.now();
8937
+ const actor = resolveClientActor(client);
8938
+ const snapshot = buildBrowserCapabilitiesSnapshot({
8939
+ cfg,
8940
+ nodes: connectedNodes
8941
+ });
8942
+ if (!snapshot.browserEnabled || snapshot.routing.mode === "off") {
8943
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "browser control is disabled"));
8944
+ return;
8945
+ }
8946
+ let routeNode = null;
8947
+ try {
8948
+ if (requestedNodeId) {
8949
+ routeNode = resolveBrowserNode(browserNodes, requestedNodeId);
8950
+ if (!routeNode) {
8951
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `requested browser node is not connected: ${requestedNodeId}`));
8952
+ return;
8953
+ }
8954
+ } else routeNode = resolveBrowserNodeTarget({
8955
+ cfg,
8956
+ nodes: connectedNodes
8957
+ });
8958
+ } catch (error) {
8959
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
8960
+ return;
8961
+ }
8962
+ const id = crypto.randomUUID();
8963
+ const session = {
8964
+ id,
8965
+ reason,
8966
+ createdAtMs: now,
8967
+ expiresAtMs: now + ttlMs,
8968
+ state: "pending_approval",
8969
+ route: routeNode ? {
8970
+ kind: "node",
8971
+ node: toNodeSummary(routeNode)
8972
+ } : {
8973
+ kind: "local",
8974
+ node: null
8975
+ },
8976
+ approval: {
8977
+ required: true,
8978
+ decision: "pending",
8979
+ requestedAtMs: now,
8980
+ requestedBy: actor,
8981
+ decidedAtMs: null,
8982
+ decidedBy: null,
8983
+ note: null
8984
+ },
8985
+ controls: {
8986
+ allowMethods,
8987
+ maxRequests
8988
+ },
8989
+ risk,
8990
+ requestCount: 0,
8991
+ inFlightRequestCount: 0,
8992
+ lastRequestAtMs: null,
8993
+ closedAtMs: null,
8994
+ audit: []
8995
+ };
8996
+ appendDesktopControlAudit(session, {
8997
+ type: "session.created",
8998
+ actor,
8999
+ details: {
9000
+ reason: session.reason,
9001
+ route: session.route.kind,
9002
+ nodeId: session.route.kind === "node" ? session.route.node.nodeId : null,
9003
+ expiresAtMs: session.expiresAtMs,
9004
+ allowMethods: session.controls.allowMethods,
9005
+ maxRequests: session.controls.maxRequests,
9006
+ riskLevel: session.risk.level,
9007
+ riskReasons: session.risk.reasons
9008
+ }
9009
+ });
9010
+ desktopControlSessions.set(id, session);
9011
+ broadcastDesktopControlSessionEvent({
9012
+ context,
9013
+ action: "created",
9014
+ session,
9015
+ actor
9016
+ });
9017
+ respond(true, toDesktopControlSessionSnapshot(session, true));
9018
+ },
9019
+ "desktop.control.session.list": async ({ params, respond, context, client }) => {
9020
+ pruneDesktopControlSessions({ context });
9021
+ if (!hasOperatorScope(client, "operator.read")) {
9022
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.read"));
9023
+ return;
9024
+ }
9025
+ const normalizedParams = normalizeObjectParams(params);
9026
+ if (!normalizedParams.ok) {
9027
+ respond(false, void 0, normalizedParams.error);
9028
+ return;
9029
+ }
9030
+ const typed = normalizedParams.value;
9031
+ const includeAuditRaw = typed.includeAudit;
9032
+ if (includeAuditRaw !== void 0 && typeof includeAuditRaw !== "boolean") {
9033
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid includeAudit filter: ${String(includeAuditRaw)}`, { details: { expectedType: "boolean" } }));
9034
+ return;
9035
+ }
9036
+ const includeAudit = includeAuditRaw === true;
9037
+ const stateInput = typed.state;
9038
+ if (stateInput !== void 0 && typeof stateInput !== "string") {
9039
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid state filter: ${String(stateInput)}`, { details: { allowedStates: DESKTOP_CONTROL_SESSION_STATES } }));
9040
+ return;
9041
+ }
9042
+ const stateRawInput = typeof stateInput === "string" ? stateInput.trim() : "";
9043
+ const stateRaw = stateRawInput ? stateRawInput.toLowerCase() : "";
9044
+ if (stateRaw && !isDesktopControlSessionState(stateRaw)) {
9045
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid state filter: ${stateRawInput}`, { details: { allowedStates: DESKTOP_CONTROL_SESSION_STATES } }));
9046
+ return;
9047
+ }
9048
+ const decisionInput = typed.decision;
9049
+ if (decisionInput !== void 0 && typeof decisionInput !== "string") {
9050
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid decision filter: ${String(decisionInput)}`, { details: { allowedDecisions: DESKTOP_CONTROL_SESSION_DECISIONS } }));
9051
+ return;
9052
+ }
9053
+ const decisionRawInput = typeof decisionInput === "string" ? decisionInput.trim() : "";
9054
+ const decisionRaw = decisionRawInput ? decisionRawInput.toLowerCase() : "";
9055
+ if (decisionRaw && !isDesktopControlApprovalDecision(decisionRaw)) {
9056
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid decision filter: ${decisionRawInput}`, { details: { allowedDecisions: DESKTOP_CONTROL_SESSION_DECISIONS } }));
9057
+ return;
9058
+ }
9059
+ const routeInput = typed.route;
9060
+ if (routeInput !== void 0 && typeof routeInput !== "string") {
9061
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid route filter: ${String(routeInput)}`, { details: { allowedRouteKinds: DESKTOP_CONTROL_SESSION_ROUTE_KINDS } }));
9062
+ return;
9063
+ }
9064
+ const routeRawInput = typeof routeInput === "string" ? routeInput.trim() : "";
9065
+ const routeRaw = routeRawInput ? routeRawInput.toLowerCase() : "";
9066
+ if (routeRaw && !isDesktopControlSessionRouteKind(routeRaw)) {
9067
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid route filter: ${routeRawInput}`, { details: { allowedRouteKinds: DESKTOP_CONTROL_SESSION_ROUTE_KINDS } }));
9068
+ return;
9069
+ }
9070
+ const riskLevelInput = typed.riskLevel;
9071
+ if (riskLevelInput !== void 0 && typeof riskLevelInput !== "string") {
9072
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid riskLevel filter: ${String(riskLevelInput)}`, { details: { allowedRiskLevels: DESKTOP_CONTROL_SESSION_RISK_LEVELS } }));
9073
+ return;
9074
+ }
9075
+ const riskLevelRawInput = typeof riskLevelInput === "string" ? riskLevelInput.trim() : "";
9076
+ const riskLevelRaw = riskLevelRawInput ? riskLevelRawInput.toLowerCase() : "";
9077
+ if (riskLevelRaw && !isDesktopControlSessionRiskLevel(riskLevelRaw)) {
9078
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid riskLevel filter: ${riskLevelRawInput}`, { details: { allowedRiskLevels: DESKTOP_CONTROL_SESSION_RISK_LEVELS } }));
9079
+ return;
9080
+ }
9081
+ const nodeIdInput = typed.nodeId;
9082
+ if (nodeIdInput !== void 0 && typeof nodeIdInput !== "string") {
9083
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid nodeId filter: ${String(nodeIdInput)}`));
9084
+ return;
9085
+ }
9086
+ const nodeIdRawInput = typeof nodeIdInput === "string" ? nodeIdInput.trim() : "";
9087
+ const nodeIdFilterKey = nodeIdRawInput ? normalizeNodeKey(nodeIdRawInput) : "";
9088
+ if (nodeIdRawInput && !nodeIdFilterKey) {
9089
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid nodeId filter: ${nodeIdRawInput}`));
9090
+ return;
9091
+ }
9092
+ if (routeRaw === "local" && nodeIdRawInput) {
9093
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "nodeId filter requires route=node (or omit route to match node-routed sessions)"));
9094
+ return;
9095
+ }
9096
+ const limitRaw = typed.limit;
9097
+ if (limitRaw !== void 0) {
9098
+ if (typeof limitRaw !== "number" || !Number.isInteger(limitRaw) || limitRaw < DESKTOP_CONTROL_LIST_MIN_LIMIT || limitRaw > DESKTOP_CONTROL_LIST_MAX_LIMIT) {
9099
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid limit filter: ${String(limitRaw)}`, { details: {
9100
+ minLimit: DESKTOP_CONTROL_LIST_MIN_LIMIT,
9101
+ maxLimit: DESKTOP_CONTROL_LIST_MAX_LIMIT
9102
+ } }));
9103
+ return;
9104
+ }
9105
+ }
9106
+ const offsetRaw = typed.offset;
9107
+ if (offsetRaw !== void 0) {
9108
+ if (typeof offsetRaw !== "number" || !Number.isInteger(offsetRaw) || offsetRaw < DESKTOP_CONTROL_LIST_MIN_OFFSET || offsetRaw > DESKTOP_CONTROL_LIST_MAX_OFFSET) {
9109
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid offset filter: ${String(offsetRaw)}`, { details: {
9110
+ minOffset: DESKTOP_CONTROL_LIST_MIN_OFFSET,
9111
+ maxOffset: DESKTOP_CONTROL_LIST_MAX_OFFSET
9112
+ } }));
9113
+ return;
9114
+ }
9115
+ }
9116
+ const state = stateRaw || void 0;
9117
+ const decision = decisionRaw || void 0;
9118
+ const route = routeRaw || void 0;
9119
+ const riskLevel = riskLevelRaw || void 0;
9120
+ const nodeId = nodeIdFilterKey || void 0;
9121
+ const limit = typeof limitRaw === "number" ? limitRaw : void 0;
9122
+ const offset = typeof offsetRaw === "number" ? offsetRaw : 0;
9123
+ const filtered = Array.from(desktopControlSessions.values()).filter((entry) => {
9124
+ if (state && entry.state !== state) return false;
9125
+ if (decision && entry.approval.decision !== decision) return false;
9126
+ if (route && entry.route.kind !== route) return false;
9127
+ if (riskLevel && entry.risk.level !== riskLevel) return false;
9128
+ if (nodeId && (entry.route.kind !== "node" || normalizeNodeKey(entry.route.node.nodeId) !== nodeId)) return false;
9129
+ return true;
9130
+ }).toSorted((a, b) => b.createdAtMs - a.createdAtMs);
9131
+ const total = filtered.length;
9132
+ const sliceEnd = limit === void 0 ? void 0 : offset + limit;
9133
+ const sessions = filtered.slice(offset, sliceEnd).map((entry) => toDesktopControlSessionSnapshot(entry, includeAudit));
9134
+ const returned = sessions.length;
9135
+ const hasMore = offset + returned < total;
9136
+ respond(true, {
9137
+ ts: Date.now(),
9138
+ total,
9139
+ returned,
9140
+ offset,
9141
+ nextOffset: hasMore ? offset + returned : null,
9142
+ truncated: offset > 0 || hasMore,
9143
+ sessions
9144
+ });
9145
+ },
9146
+ "desktop.control.session.get": async ({ params, respond, context, client }) => {
9147
+ pruneDesktopControlSessions({ context });
9148
+ if (!hasOperatorScope(client, "operator.read")) {
9149
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.read"));
9150
+ return;
9151
+ }
9152
+ const normalizedParams = normalizeObjectParams(params);
9153
+ if (!normalizedParams.ok) {
9154
+ respond(false, void 0, normalizedParams.error);
9155
+ return;
9156
+ }
9157
+ const typed = normalizedParams.value;
9158
+ const includeAuditRaw = typed.includeAudit;
9159
+ if (includeAuditRaw !== void 0 && typeof includeAuditRaw !== "boolean") {
9160
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid includeAudit filter: ${String(includeAuditRaw)}`, { details: { expectedType: "boolean" } }));
9161
+ return;
9162
+ }
9163
+ const found = ensureDesktopSessionExists(typed.id);
9164
+ if (!found.ok) {
9165
+ respond(false, void 0, found.error);
9166
+ return;
9167
+ }
9168
+ respond(true, toDesktopControlSessionSnapshot(found.session, includeAuditRaw === true));
9169
+ },
9170
+ "desktop.control.session.approve": async ({ params, respond, client, context }) => {
9171
+ pruneDesktopControlSessions({ context });
9172
+ if (!hasOperatorScope(client, "operator.approvals")) {
9173
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.approvals"));
9174
+ return;
9175
+ }
9176
+ const normalizedParams = normalizeObjectParams(params);
9177
+ if (!normalizedParams.ok) {
9178
+ respond(false, void 0, normalizedParams.error);
9179
+ return;
9180
+ }
9181
+ const typed = normalizedParams.value;
9182
+ const found = ensureDesktopSessionExists(typed.id);
9183
+ if (!found.ok) {
9184
+ respond(false, void 0, found.error);
9185
+ return;
9186
+ }
9187
+ const session = found.session;
9188
+ if (session.state !== "pending_approval") {
9189
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `session is not pending approval (state: ${session.state})`, { details: {
9190
+ state: session.state,
9191
+ decision: session.approval.decision,
9192
+ expiresAtMs: session.expiresAtMs
9193
+ } }));
9194
+ return;
9195
+ }
9196
+ if (session.expiresAtMs <= Date.now()) {
9197
+ session.state = "expired";
9198
+ session.closedAtMs = Date.now();
9199
+ appendDesktopControlAudit(session, {
9200
+ type: "session.expired",
9201
+ actor: "system"
9202
+ });
9203
+ broadcastDesktopControlSessionEvent({
9204
+ context,
9205
+ action: "expired",
9206
+ session,
9207
+ actor: "system"
9208
+ });
9209
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "session has expired", { details: {
9210
+ state: session.state,
9211
+ decision: session.approval.decision,
9212
+ expiresAtMs: session.expiresAtMs
9213
+ } }));
9214
+ return;
9215
+ }
9216
+ const decisionRaw = typeof typed.decision === "string" ? typed.decision.trim().toLowerCase() : "";
9217
+ if (decisionRaw !== "allow" && decisionRaw !== "deny") {
9218
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "decision must be allow or deny"));
9219
+ return;
9220
+ }
9221
+ if (decisionRaw === "allow" && session.route.kind === "node") {
9222
+ if (!resolveDesktopSessionNodeTarget({
9223
+ session,
9224
+ nodes: context.nodeRegistry.listConnected()
9225
+ })) {
9226
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `pinned browser node is not connected: ${session.route.node.nodeId}`, { details: {
9227
+ nodeId: session.route.node.nodeId,
9228
+ state: session.state,
9229
+ decision: session.approval.decision,
9230
+ expiresAtMs: session.expiresAtMs,
9231
+ requestCount: session.requestCount,
9232
+ maxRequests: session.controls.maxRequests
9233
+ } }));
9234
+ return;
9235
+ }
9236
+ }
9237
+ if (typed.note !== void 0 && typeof typed.note !== "string") {
9238
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid note: ${String(typed.note)}`, { details: { expectedType: "string" } }));
9239
+ return;
9240
+ }
9241
+ const note = normalizeDesktopSessionNote(typed.note);
9242
+ if (decisionRaw === "allow" && session.risk.level === "elevated" && !hasDecisionRationale(note)) {
9243
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `note must be at least ${DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN} characters when approving elevated-risk desktop control sessions`, { details: {
9244
+ riskLevel: session.risk.level,
9245
+ riskReasons: session.risk.reasons,
9246
+ minNoteLength: DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN
9247
+ } }));
9248
+ return;
9249
+ }
9250
+ if (decisionRaw === "deny" && !hasDecisionRationale(note)) {
9251
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `note must be at least ${DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN} characters when denying desktop control sessions`, { details: { minNoteLength: DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN } }));
9252
+ return;
9253
+ }
9254
+ const actor = resolveClientActor(client);
9255
+ const now = Date.now();
9256
+ session.approval.decision = decisionRaw;
9257
+ session.approval.decidedAtMs = now;
9258
+ session.approval.decidedBy = actor;
9259
+ session.approval.note = note;
9260
+ session.state = decisionRaw === "allow" ? "active" : "denied";
9261
+ if (session.state === "denied") session.closedAtMs = now;
9262
+ appendDesktopControlAudit(session, {
9263
+ type: decisionRaw === "allow" ? "session.approved" : "session.denied",
9264
+ actor,
9265
+ details: { note: session.approval.note }
8333
9266
  });
8334
- } catch (err) {
8335
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(err)));
8336
- return;
8337
- }
8338
- if (nodeTarget) {
8339
- const allowlist = resolveNodeCommandAllowlist(cfg, nodeTarget);
8340
- const allowed = isNodeCommandAllowed({
8341
- command: "browser.proxy",
8342
- declaredCommands: nodeTarget.commands,
8343
- allowlist
9267
+ broadcastDesktopControlSessionEvent({
9268
+ context,
9269
+ action: decisionRaw === "allow" ? "approved" : "denied",
9270
+ session,
9271
+ actor,
9272
+ details: { note: session.approval.note }
8344
9273
  });
8345
- if (!allowed.ok) {
8346
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "node command not allowed", { details: {
8347
- reason: allowed.reason,
8348
- command: "browser.proxy"
9274
+ respond(true, toDesktopControlSessionSnapshot(session, true));
9275
+ },
9276
+ "desktop.control.session.close": async ({ params, respond, client, context }) => {
9277
+ pruneDesktopControlSessions({ context });
9278
+ if (!hasOperatorScope(client, "operator.write")) {
9279
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.write"));
9280
+ return;
9281
+ }
9282
+ const normalizedParams = normalizeObjectParams(params);
9283
+ if (!normalizedParams.ok) {
9284
+ respond(false, void 0, normalizedParams.error);
9285
+ return;
9286
+ }
9287
+ const typed = normalizedParams.value;
9288
+ const found = ensureDesktopSessionExists(typed.id);
9289
+ if (!found.ok) {
9290
+ respond(false, void 0, found.error);
9291
+ return;
9292
+ }
9293
+ const session = found.session;
9294
+ if (session.state === "closed" || session.state === "denied" || session.state === "expired") {
9295
+ respond(true, toDesktopControlSessionSnapshot(session, true));
9296
+ return;
9297
+ }
9298
+ if (typed.note !== void 0 && typeof typed.note !== "string") {
9299
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid note: ${String(typed.note)}`, { details: { expectedType: "string" } }));
9300
+ return;
9301
+ }
9302
+ const note = normalizeDesktopSessionNote(typed.note);
9303
+ const actor = resolveClientActor(client);
9304
+ if (session.state === "pending_approval" && !hasDecisionRationale(note)) {
9305
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `note must be at least ${DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN} characters when manually closing pending desktop control sessions`, { details: { minNoteLength: DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN } }));
9306
+ return;
9307
+ }
9308
+ if (session.state === "active" && !hasDecisionRationale(note)) {
9309
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `note must be at least ${DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN} characters when manually closing active desktop control sessions`, { details: { minNoteLength: DESKTOP_CONTROL_MIN_DECISION_NOTE_LEN } }));
9310
+ return;
9311
+ }
9312
+ const now = Date.now();
9313
+ if (session.state === "pending_approval") {
9314
+ session.approval.decision = "deny";
9315
+ session.approval.decidedAtMs = now;
9316
+ session.approval.decidedBy = actor;
9317
+ session.approval.note = note;
9318
+ }
9319
+ session.state = "closed";
9320
+ session.closedAtMs = now;
9321
+ appendDesktopControlAudit(session, {
9322
+ type: "session.closed",
9323
+ actor,
9324
+ details: { note }
9325
+ });
9326
+ broadcastDesktopControlSessionEvent({
9327
+ context,
9328
+ action: "closed",
9329
+ session,
9330
+ actor,
9331
+ details: { note }
9332
+ });
9333
+ respond(true, toDesktopControlSessionSnapshot(session, true));
9334
+ },
9335
+ "desktop.control.session.request": async ({ params, respond, client, context }) => {
9336
+ pruneDesktopControlSessions({ context });
9337
+ if (!hasOperatorScope(client, "operator.write")) {
9338
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.write"));
9339
+ return;
9340
+ }
9341
+ const normalizedParams = normalizeObjectParams(params);
9342
+ if (!normalizedParams.ok) {
9343
+ respond(false, void 0, normalizedParams.error);
9344
+ return;
9345
+ }
9346
+ const typed = normalizedParams.value;
9347
+ const found = ensureDesktopSessionExists(typed.id);
9348
+ if (!found.ok) {
9349
+ respond(false, void 0, found.error);
9350
+ return;
9351
+ }
9352
+ const session = found.session;
9353
+ if (session.state === "expired") {
9354
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "session has expired", { details: {
9355
+ state: session.state,
9356
+ decision: session.approval.decision,
9357
+ expiresAtMs: session.expiresAtMs
8349
9358
  } }));
8350
9359
  return;
8351
9360
  }
8352
- const proxyParams = {
8353
- method: methodRaw,
8354
- path,
8355
- query,
8356
- body,
8357
- timeoutMs,
8358
- profile: typeof query?.profile === "string" ? query.profile : void 0
8359
- };
8360
- const res = await context.nodeRegistry.invoke({
8361
- nodeId: nodeTarget.nodeId,
8362
- command: "browser.proxy",
8363
- params: proxyParams,
8364
- timeoutMs,
8365
- idempotencyKey: crypto.randomUUID()
9361
+ if (session.state === "closed" && session.approval.decision === "allow" && session.requestCount >= session.controls.maxRequests) {
9362
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `session request budget exhausted (${session.controls.maxRequests})`, { details: {
9363
+ state: session.state,
9364
+ decision: session.approval.decision,
9365
+ requestCount: session.requestCount,
9366
+ inFlightRequests: session.inFlightRequestCount,
9367
+ maxRequests: session.controls.maxRequests,
9368
+ expiresAtMs: session.expiresAtMs
9369
+ } }));
9370
+ return;
9371
+ }
9372
+ if (session.state !== "active" || session.approval.decision !== "allow") {
9373
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `session is not approved (state: ${session.state}, decision: ${session.approval.decision})`, { details: {
9374
+ state: session.state,
9375
+ decision: session.approval.decision
9376
+ } }));
9377
+ return;
9378
+ }
9379
+ if (session.expiresAtMs <= Date.now()) {
9380
+ session.state = "expired";
9381
+ session.closedAtMs = Date.now();
9382
+ appendDesktopControlAudit(session, {
9383
+ type: "session.expired",
9384
+ actor: "system"
9385
+ });
9386
+ broadcastDesktopControlSessionEvent({
9387
+ context,
9388
+ action: "expired",
9389
+ session,
9390
+ actor: "system"
9391
+ });
9392
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "session has expired", { details: {
9393
+ state: session.state,
9394
+ decision: session.approval.decision,
9395
+ expiresAtMs: session.expiresAtMs
9396
+ } }));
9397
+ return;
9398
+ }
9399
+ const normalized = normalizeBrowserRequest(typed);
9400
+ if (!normalized.ok) {
9401
+ respond(false, void 0, normalized.error);
9402
+ return;
9403
+ }
9404
+ const actor = resolveClientActor(client);
9405
+ if (!session.controls.allowMethods.includes(normalized.request.methodRaw)) {
9406
+ appendDesktopControlAudit(session, {
9407
+ type: "request.error",
9408
+ actor,
9409
+ details: {
9410
+ reason: "method not allowed",
9411
+ method: normalized.request.methodRaw,
9412
+ allowedMethods: session.controls.allowMethods
9413
+ }
9414
+ });
9415
+ broadcastDesktopControlSessionEvent({
9416
+ context,
9417
+ action: "request_error",
9418
+ session,
9419
+ actor,
9420
+ details: {
9421
+ reason: "method not allowed",
9422
+ method: normalized.request.methodRaw,
9423
+ allowedMethods: [...session.controls.allowMethods]
9424
+ }
9425
+ });
9426
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `method is not allowed for this session: ${normalized.request.methodRaw}`, { details: { allowedMethods: session.controls.allowMethods } }));
9427
+ return;
9428
+ }
9429
+ if (session.requestCount >= session.controls.maxRequests) {
9430
+ session.state = "closed";
9431
+ session.closedAtMs = Date.now();
9432
+ appendDesktopControlAudit(session, {
9433
+ type: "session.closed",
9434
+ actor: "system",
9435
+ details: {
9436
+ reason: "max requests reached",
9437
+ maxRequests: session.controls.maxRequests
9438
+ }
9439
+ });
9440
+ broadcastDesktopControlSessionEvent({
9441
+ context,
9442
+ action: "closed",
9443
+ session,
9444
+ actor: "system",
9445
+ details: {
9446
+ reason: "max requests reached",
9447
+ maxRequests: session.controls.maxRequests
9448
+ }
9449
+ });
9450
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `session request budget exhausted (${session.controls.maxRequests})`, { details: {
9451
+ state: session.state,
9452
+ decision: session.approval.decision,
9453
+ requestCount: session.requestCount,
9454
+ inFlightRequests: session.inFlightRequestCount,
9455
+ maxRequests: session.controls.maxRequests,
9456
+ expiresAtMs: session.expiresAtMs
9457
+ } }));
9458
+ return;
9459
+ }
9460
+ if (session.requestCount + session.inFlightRequestCount >= session.controls.maxRequests) {
9461
+ appendDesktopControlAudit(session, {
9462
+ type: "request.error",
9463
+ actor,
9464
+ details: {
9465
+ reason: "request budget reserved by in-flight requests",
9466
+ requestCount: session.requestCount,
9467
+ inFlightRequests: session.inFlightRequestCount,
9468
+ maxRequests: session.controls.maxRequests
9469
+ }
9470
+ });
9471
+ broadcastDesktopControlSessionEvent({
9472
+ context,
9473
+ action: "request_error",
9474
+ session,
9475
+ actor,
9476
+ details: {
9477
+ reason: "request budget reserved by in-flight requests",
9478
+ requestCount: session.requestCount,
9479
+ inFlightRequests: session.inFlightRequestCount,
9480
+ maxRequests: session.controls.maxRequests
9481
+ }
9482
+ });
9483
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `session request budget reserved by in-flight requests (${session.controls.maxRequests})`, { details: {
9484
+ state: session.state,
9485
+ decision: session.approval.decision,
9486
+ requestCount: session.requestCount,
9487
+ inFlightRequests: session.inFlightRequestCount,
9488
+ maxRequests: session.controls.maxRequests,
9489
+ expiresAtMs: session.expiresAtMs
9490
+ } }));
9491
+ return;
9492
+ }
9493
+ appendDesktopControlAudit(session, {
9494
+ type: "request.start",
9495
+ actor,
9496
+ details: {
9497
+ method: normalized.request.methodRaw,
9498
+ path: normalized.request.path,
9499
+ route: session.route.kind,
9500
+ nodeId: session.route.kind === "node" ? session.route.node.nodeId : null
9501
+ }
9502
+ });
9503
+ const nodeTarget = resolveDesktopSessionNodeTarget({
9504
+ session,
9505
+ nodes: context.nodeRegistry.listConnected()
8366
9506
  });
8367
- if (!res.ok) {
8368
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, res.error?.message ?? "node invoke failed", { details: { nodeError: res.error ?? null } }));
9507
+ if (session.route.kind === "node" && !nodeTarget) {
9508
+ appendDesktopControlAudit(session, {
9509
+ type: "request.error",
9510
+ actor,
9511
+ details: {
9512
+ reason: "pinned node disconnected",
9513
+ nodeId: session.route.node.nodeId
9514
+ }
9515
+ });
9516
+ broadcastDesktopControlSessionEvent({
9517
+ context,
9518
+ action: "request_error",
9519
+ session,
9520
+ actor,
9521
+ details: {
9522
+ reason: "pinned node disconnected",
9523
+ nodeId: session.route.node.nodeId
9524
+ }
9525
+ });
9526
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `pinned browser node is not connected: ${session.route.node.nodeId}`, { details: {
9527
+ nodeId: session.route.node.nodeId,
9528
+ state: session.state,
9529
+ decision: session.approval.decision,
9530
+ expiresAtMs: session.expiresAtMs,
9531
+ requestCount: session.requestCount,
9532
+ maxRequests: session.controls.maxRequests
9533
+ } }));
8369
9534
  return;
8370
9535
  }
8371
- const payload = res.payloadJSON ? safeParseJson(res.payloadJSON) : res.payload;
8372
- const proxy = payload && typeof payload === "object" ? payload : null;
8373
- if (!proxy || !("result" in proxy)) {
8374
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "browser proxy failed"));
9536
+ session.inFlightRequestCount += 1;
9537
+ let result;
9538
+ try {
9539
+ result = await dispatchBrowserRequest({
9540
+ cfg: loadConfig(),
9541
+ request: normalized.request,
9542
+ context,
9543
+ nodeTarget
9544
+ });
9545
+ } catch (error) {
9546
+ session.inFlightRequestCount = Math.max(0, session.inFlightRequestCount - 1);
9547
+ const message = error instanceof Error ? error.message : String(error);
9548
+ appendDesktopControlAudit(session, {
9549
+ type: "request.error",
9550
+ actor,
9551
+ details: {
9552
+ reason: "dispatch threw",
9553
+ message
9554
+ }
9555
+ });
9556
+ broadcastDesktopControlSessionEvent({
9557
+ context,
9558
+ action: "request_error",
9559
+ session,
9560
+ actor,
9561
+ details: {
9562
+ reason: "dispatch threw",
9563
+ message
9564
+ }
9565
+ });
9566
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, message));
8375
9567
  return;
8376
9568
  }
8377
- const mapping = await persistProxyFiles(proxy.files);
8378
- applyProxyPaths(proxy.result, mapping);
8379
- respond(true, proxy.result);
8380
- return;
8381
- }
8382
- if (!await startBrowserControlServiceFromConfig()) {
8383
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "browser control is disabled"));
8384
- return;
8385
- }
8386
- let dispatcher;
8387
- try {
8388
- dispatcher = createBrowserRouteDispatcher(createBrowserControlContext());
8389
- } catch (err) {
8390
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(err)));
8391
- return;
8392
- }
8393
- const result = await dispatcher.dispatch({
8394
- method: methodRaw,
8395
- path,
8396
- query,
8397
- body
8398
- });
8399
- if (result.status >= 400) {
8400
- const message = result.body && typeof result.body === "object" && "error" in result.body ? String(result.body.error) : `browser request failed (${result.status})`;
8401
- respond(false, void 0, errorShape(result.status >= 500 ? ErrorCodes.UNAVAILABLE : ErrorCodes.INVALID_REQUEST, message, { details: result.body }));
8402
- return;
9569
+ session.inFlightRequestCount = Math.max(0, session.inFlightRequestCount - 1);
9570
+ if (!result.ok) {
9571
+ appendDesktopControlAudit(session, {
9572
+ type: "request.error",
9573
+ actor,
9574
+ details: {
9575
+ route: result.route,
9576
+ nodeId: result.nodeId,
9577
+ status: result.status,
9578
+ message: result.error.message
9579
+ }
9580
+ });
9581
+ broadcastDesktopControlSessionEvent({
9582
+ context,
9583
+ action: "request_error",
9584
+ session,
9585
+ actor,
9586
+ details: {
9587
+ route: result.route,
9588
+ nodeId: result.nodeId,
9589
+ status: result.status,
9590
+ message: result.error.message
9591
+ }
9592
+ });
9593
+ respond(false, void 0, result.error);
9594
+ return;
9595
+ }
9596
+ session.requestCount += 1;
9597
+ session.lastRequestAtMs = Date.now();
9598
+ appendDesktopControlAudit(session, {
9599
+ type: "request.ok",
9600
+ actor,
9601
+ details: {
9602
+ route: result.route,
9603
+ nodeId: result.nodeId,
9604
+ status: result.status
9605
+ }
9606
+ });
9607
+ broadcastDesktopControlSessionEvent({
9608
+ context,
9609
+ action: "request_ok",
9610
+ session,
9611
+ actor,
9612
+ details: {
9613
+ route: result.route,
9614
+ nodeId: result.nodeId,
9615
+ status: result.status
9616
+ }
9617
+ });
9618
+ if (session.state === "active" && session.approval.decision === "allow" && session.requestCount >= session.controls.maxRequests) {
9619
+ session.state = "closed";
9620
+ session.closedAtMs = session.lastRequestAtMs;
9621
+ appendDesktopControlAudit(session, {
9622
+ type: "session.closed",
9623
+ actor: "system",
9624
+ details: {
9625
+ reason: "max requests reached",
9626
+ maxRequests: session.controls.maxRequests
9627
+ }
9628
+ });
9629
+ broadcastDesktopControlSessionEvent({
9630
+ context,
9631
+ action: "closed",
9632
+ session,
9633
+ actor: "system",
9634
+ details: {
9635
+ reason: "max requests reached",
9636
+ maxRequests: session.controls.maxRequests
9637
+ }
9638
+ });
9639
+ }
9640
+ respond(true, result.payload);
8403
9641
  }
8404
- respond(true, result.body);
8405
- } };
9642
+ };
8406
9643
 
8407
9644
  //#endregion
8408
9645
  //#region src/channels/plugins/status.ts
@@ -9611,7 +10848,7 @@ const FIELD_LABELS = {
9611
10848
 
9612
10849
  //#endregion
9613
10850
  //#region src/config/schema.hints.ts
9614
- const log$9 = createSubsystemLogger("config/schema");
10851
+ const log$12 = createSubsystemLogger("config/schema");
9615
10852
  const GROUP_LABELS = {
9616
10853
  wizard: "Wizard",
9617
10854
  update: "Update",
@@ -9759,7 +10996,7 @@ function mapSensitivePaths(schema, path, hints) {
9759
10996
  ...next[path],
9760
10997
  sensitive: true
9761
10998
  };
9762
- else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$9.warn(`possibly sensitive key found: (${path})`);
10999
+ else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$12.warn(`possibly sensitive key found: (${path})`);
9763
11000
  if (currentSchema instanceof z.ZodObject) {
9764
11001
  const shape = currentSchema.shape;
9765
11002
  for (const key in shape) {
@@ -9782,7 +11019,7 @@ function mapSensitivePaths(schema, path, hints) {
9782
11019
 
9783
11020
  //#endregion
9784
11021
  //#region src/config/redact-snapshot.ts
9785
- const log$8 = createSubsystemLogger("config/redaction");
11022
+ const log$11 = createSubsystemLogger("config/redaction");
9786
11023
  const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
9787
11024
  function isSensitivePath(path) {
9788
11025
  if (path.endsWith("[]")) return isSensitiveConfigPath(path.slice(0, -2));
@@ -10033,7 +11270,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10033
11270
  return restoreRedactedValuesGuessing(incoming, original, prefix, hints);
10034
11271
  }
10035
11272
  const origArr = Array.isArray(original) ? original : [];
10036
- if (incoming.length < origArr.length) log$8.warn(`Redacted config array key ${path} has been truncated`);
11273
+ if (incoming.length < origArr.length) log$11.warn(`Redacted config array key ${path} has been truncated`);
10037
11274
  return incoming.map((item, i) => {
10038
11275
  if (item === REDACTED_SENTINEL) return origArr[i];
10039
11276
  return restoreRedactedValuesWithLookup(item, origArr[i], lookup, path, hints);
@@ -10050,7 +11287,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10050
11287
  matched = true;
10051
11288
  if (value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10052
11289
  else {
10053
- log$8.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
11290
+ log$11.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
10054
11291
  throw new RedactionError(candidate);
10055
11292
  }
10056
11293
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesWithLookup(value, orig[key], lookup, candidate, hints);
@@ -10059,7 +11296,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10059
11296
  if (!matched && isExtensionPath(path)) {
10060
11297
  if (!isExplicitlyNonSensitivePath(hints, [path, wildcardPath]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10061
11298
  else {
10062
- log$8.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
11299
+ log$11.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10063
11300
  throw new RedactionError(path);
10064
11301
  }
10065
11302
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
@@ -10078,7 +11315,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
10078
11315
  const origArr = Array.isArray(original) ? original : [];
10079
11316
  return incoming.map((item, i) => {
10080
11317
  const path = `${prefix}[]`;
10081
- if (incoming.length < origArr.length) log$8.warn(`Redacted config array key ${path} has been truncated`);
11318
+ if (incoming.length < origArr.length) log$11.warn(`Redacted config array key ${path} has been truncated`);
10082
11319
  if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && item === REDACTED_SENTINEL) return origArr[i];
10083
11320
  return restoreRedactedValuesGuessing(item, origArr[i], path, hints);
10084
11321
  });
@@ -10089,7 +11326,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
10089
11326
  const path = prefix ? `${prefix}.${key}` : key;
10090
11327
  if (!isExplicitlyNonSensitivePath(hints, [path, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10091
11328
  else {
10092
- log$8.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
11329
+ log$11.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10093
11330
  throw new RedactionError(path);
10094
11331
  }
10095
11332
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
@@ -11403,32 +12640,374 @@ const execApprovalsHandlers = {
11403
12640
  respond(true, safeParseJson(res.payloadJSON ?? null), void 0);
11404
12641
  });
11405
12642
  }
11406
- };
12643
+ };
12644
+
12645
+ //#endregion
12646
+ //#region src/gateway/server-methods/health.ts
12647
+ const ADMIN_SCOPE$3 = "operator.admin";
12648
+ const healthHandlers = {
12649
+ health: async ({ respond, context, params }) => {
12650
+ const { getHealthCache, refreshHealthSnapshot, logHealth } = context;
12651
+ const wantsProbe = params?.probe === true;
12652
+ const now = Date.now();
12653
+ const cached = getHealthCache();
12654
+ if (!wantsProbe && cached && now - cached.ts < HEALTH_REFRESH_INTERVAL_MS) {
12655
+ respond(true, cached, void 0, { cached: true });
12656
+ refreshHealthSnapshot({ probe: false }).catch((err) => logHealth.error(`background health refresh failed: ${formatError(err)}`));
12657
+ return;
12658
+ }
12659
+ try {
12660
+ respond(true, await refreshHealthSnapshot({ probe: wantsProbe }), void 0);
12661
+ } catch (err) {
12662
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err)));
12663
+ }
12664
+ },
12665
+ status: async ({ respond, client }) => {
12666
+ respond(true, await getStatusSummary({ includeSensitive: (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []).includes(ADMIN_SCOPE$3) }), void 0);
12667
+ }
12668
+ };
12669
+
12670
+ //#endregion
12671
+ //#region src/ico/launch-platform.ts
12672
+ const log$10 = createSubsystemLogger("ico-platform");
12673
+ function resolveIcoDir() {
12674
+ return path.join(resolveStateDir(), "ico");
12675
+ }
12676
+ function listIcoProjects() {
12677
+ const dir = resolveIcoDir();
12678
+ try {
12679
+ if (!fs.existsSync(dir)) return [];
12680
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
12681
+ try {
12682
+ const raw = fs.readFileSync(path.join(dir, f), "utf8");
12683
+ return JSON.parse(raw);
12684
+ } catch {
12685
+ return null;
12686
+ }
12687
+ }).filter((p) => p != null).toSorted((a, b) => b.createdAt - a.createdAt);
12688
+ } catch {
12689
+ return [];
12690
+ }
12691
+ }
12692
+
12693
+ //#endregion
12694
+ //#region src/ico/public-metrics.ts
12695
+ const MS_PER_MINUTE = 6e4;
12696
+ const METRIC_UNAVAILABLE_ERROR = "ICO_PROJECT_NOT_FOUND";
12697
+ const METRIC_DEFINITIONS$1 = [
12698
+ {
12699
+ id: "current_price_usd",
12700
+ label: "Current Price",
12701
+ cadenceMinutes: 15,
12702
+ sourceRef: "ico.launch-platform.currentPriceUsd",
12703
+ readValue: (project) => project.status.currentPriceUsd,
12704
+ formatValue: (value) => formatUsd$1(value, value >= 1 ? 2 : 4)
12705
+ },
12706
+ {
12707
+ id: "total_raised_usd",
12708
+ label: "Total Raised",
12709
+ cadenceMinutes: 15,
12710
+ sourceRef: "ico.launch-platform.totalRaisedUsd",
12711
+ readValue: (project) => project.status.totalRaisedUsd,
12712
+ formatValue: (value) => formatUsd$1(value, 2)
12713
+ },
12714
+ {
12715
+ id: "holders_total",
12716
+ label: "Holders",
12717
+ cadenceMinutes: 15,
12718
+ sourceRef: "ico.launch-platform.holders",
12719
+ readValue: (project) => project.status.holders,
12720
+ formatValue: (value) => formatInteger$1(value)
12721
+ },
12722
+ {
12723
+ id: "supply_sold_tokens",
12724
+ label: "Supply Sold",
12725
+ cadenceMinutes: 15,
12726
+ sourceRef: "ico.launch-platform.currentSupply",
12727
+ readValue: (project) => project.status.currentSupply,
12728
+ formatValue: (value) => formatInteger$1(value)
12729
+ },
12730
+ {
12731
+ id: "bonding_progress_percent",
12732
+ label: "Bonding Progress",
12733
+ cadenceMinutes: 15,
12734
+ sourceRef: "ico.launch-platform.percentToTarget",
12735
+ readValue: (project) => project.status.percentToTarget,
12736
+ formatValue: (value) => formatPercent(value, 1)
12737
+ },
12738
+ {
12739
+ id: "transfer_tax_rate_percent",
12740
+ label: "Transfer Tax",
12741
+ cadenceMinutes: 1440,
12742
+ sourceRef: "ico.config.tax.transferTaxRate",
12743
+ readValue: (project) => project.config.tax.transferTaxRate * 100,
12744
+ formatValue: (value) => formatPercent(value, 2)
12745
+ },
12746
+ {
12747
+ id: "revenue_share_rate_percent",
12748
+ label: "Revenue Share",
12749
+ cadenceMinutes: 1440,
12750
+ sourceRef: "ico.config.tax.revenueShareRate",
12751
+ readValue: (project) => project.config.tax.revenueShareRate * 100,
12752
+ formatValue: (value) => formatPercent(value, 2)
12753
+ }
12754
+ ];
12755
+ function formatUsd$1(value, fractionDigits) {
12756
+ return `$${value.toLocaleString(void 0, {
12757
+ minimumFractionDigits: fractionDigits,
12758
+ maximumFractionDigits: fractionDigits
12759
+ })}`;
12760
+ }
12761
+ function formatInteger$1(value) {
12762
+ return Math.round(value).toLocaleString();
12763
+ }
12764
+ function formatPercent(value, fractionDigits) {
12765
+ return `${value.toLocaleString(void 0, {
12766
+ minimumFractionDigits: fractionDigits,
12767
+ maximumFractionDigits: fractionDigits
12768
+ })}%`;
12769
+ }
12770
+ function mapProjectSnapshot(project) {
12771
+ return {
12772
+ id: project.id,
12773
+ name: project.config.name,
12774
+ symbol: project.config.symbol,
12775
+ chains: [...project.config.chains],
12776
+ targetRaiseUsd: project.config.bondingCurve.targetRaiseUsd,
12777
+ bondingActive: project.status.bondingActive,
12778
+ allocation: {
12779
+ team: project.config.allocation.team,
12780
+ companyRound: project.config.allocation.companyRound,
12781
+ revenueShare: project.config.allocation.revenueShare,
12782
+ ubc: project.config.allocation.ubc
12783
+ },
12784
+ tax: {
12785
+ transferTaxRate: project.config.tax.transferTaxRate,
12786
+ revenueShareRate: project.config.tax.revenueShareRate
12787
+ }
12788
+ };
12789
+ }
12790
+ function buildUnavailableMetric$1(definition) {
12791
+ return {
12792
+ id: definition.id,
12793
+ label: definition.label,
12794
+ value: null,
12795
+ displayValue: "--",
12796
+ capturedAt: null,
12797
+ cadenceMinutes: definition.cadenceMinutes,
12798
+ state: "unavailable",
12799
+ ageMinutes: null,
12800
+ sourceStatus: "error",
12801
+ sourceRef: definition.sourceRef,
12802
+ errorCode: METRIC_UNAVAILABLE_ERROR
12803
+ };
12804
+ }
12805
+ function buildMetricFromProject(definition, project, capturedAt, nowMs) {
12806
+ const value = definition.readValue(project);
12807
+ const sourceStatus = "ok";
12808
+ const { state, ageMinutes } = resolveMetricState({
12809
+ value,
12810
+ sourceStatus,
12811
+ capturedAt,
12812
+ cadenceMinutes: definition.cadenceMinutes,
12813
+ nowMs
12814
+ });
12815
+ return {
12816
+ id: definition.id,
12817
+ label: definition.label,
12818
+ value,
12819
+ displayValue: definition.formatValue(value),
12820
+ capturedAt,
12821
+ cadenceMinutes: definition.cadenceMinutes,
12822
+ state,
12823
+ ageMinutes,
12824
+ sourceStatus,
12825
+ sourceRef: definition.sourceRef,
12826
+ errorCode: null
12827
+ };
12828
+ }
12829
+ function resolveMetricState(input) {
12830
+ const nowMs = input.nowMs ?? Date.now();
12831
+ const hasHistoricalValue = input.hasHistoricalValue ?? input.value != null;
12832
+ if (input.value == null) return {
12833
+ state: "unavailable",
12834
+ ageMinutes: null
12835
+ };
12836
+ if (!input.capturedAt) return {
12837
+ state: "unavailable",
12838
+ ageMinutes: null
12839
+ };
12840
+ const capturedAtMs = Date.parse(input.capturedAt);
12841
+ if (!Number.isFinite(capturedAtMs)) return {
12842
+ state: "unavailable",
12843
+ ageMinutes: null
12844
+ };
12845
+ const ageMinutes = Math.max(0, (nowMs - capturedAtMs) / MS_PER_MINUTE);
12846
+ if (input.sourceStatus === "error" && !hasHistoricalValue) return {
12847
+ state: "unavailable",
12848
+ ageMinutes
12849
+ };
12850
+ if (ageMinutes <= input.cadenceMinutes) return {
12851
+ state: "live",
12852
+ ageMinutes
12853
+ };
12854
+ if (ageMinutes <= input.cadenceMinutes * 3) return {
12855
+ state: "delayed",
12856
+ ageMinutes
12857
+ };
12858
+ return {
12859
+ state: "stale",
12860
+ ageMinutes
12861
+ };
12862
+ }
12863
+ function buildIcoPublicMetricsFeed(options = {}) {
12864
+ const nowMs = options.nowMs ?? Date.now();
12865
+ const generatedAt = new Date(nowMs).toISOString();
12866
+ const project = [...options.projects ?? listIcoProjects()].toSorted((a, b) => b.createdAt - a.createdAt)[0] ?? null;
12867
+ if (!project) return {
12868
+ generatedAt,
12869
+ project: null,
12870
+ metrics: METRIC_DEFINITIONS$1.map(buildUnavailableMetric$1)
12871
+ };
12872
+ return {
12873
+ generatedAt,
12874
+ project: mapProjectSnapshot(project),
12875
+ metrics: METRIC_DEFINITIONS$1.map((definition) => buildMetricFromProject(definition, project, generatedAt, nowMs))
12876
+ };
12877
+ }
12878
+
12879
+ //#endregion
12880
+ //#region src/gateway/server-methods/ico.ts
12881
+ const icoHandlers = { "ico.metrics.get": async ({ respond }) => {
12882
+ try {
12883
+ respond(true, buildIcoPublicMetricsFeed(), void 0);
12884
+ } catch (error) {
12885
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12886
+ }
12887
+ } };
12888
+
12889
+ //#endregion
12890
+ //#region src/impact/footprint.ts
12891
+ const METRIC_DEFINITIONS = [
12892
+ {
12893
+ id: "users_total",
12894
+ label: "Users",
12895
+ cadenceMinutes: 60,
12896
+ sourceRef: "telemetry.users.total",
12897
+ formatValue: formatInteger,
12898
+ missingErrorCode: "SOURCE_NOT_CONNECTED"
12899
+ },
12900
+ {
12901
+ id: "newsletter_subscribers_total",
12902
+ label: "Newsletter Subs",
12903
+ cadenceMinutes: 60,
12904
+ sourceRef: "telemetry.newsletter.total",
12905
+ formatValue: formatInteger,
12906
+ missingErrorCode: "SOURCE_NOT_CONNECTED"
12907
+ },
12908
+ {
12909
+ id: "downloads_total",
12910
+ label: "Downloads",
12911
+ cadenceMinutes: 60,
12912
+ sourceRef: "telemetry.downloads.total",
12913
+ formatValue: formatInteger,
12914
+ missingErrorCode: "SOURCE_NOT_CONNECTED"
12915
+ },
12916
+ {
12917
+ id: "promo_videos_hosted_total",
12918
+ label: "Promo Videos",
12919
+ cadenceMinutes: 60,
12920
+ sourceRef: "media.promo.hosted.total",
12921
+ formatValue: formatInteger,
12922
+ missingErrorCode: "SOURCE_NOT_CONNECTED"
12923
+ },
12924
+ {
12925
+ id: "ico_holders_total",
12926
+ label: "ICO Holders",
12927
+ cadenceMinutes: 15,
12928
+ sourceRef: "ico.launch-platform.holders",
12929
+ readValue: (context) => context.latestProject?.status.holders ?? null,
12930
+ formatValue: formatInteger,
12931
+ missingErrorCode: "ICO_PROJECT_NOT_FOUND"
12932
+ },
12933
+ {
12934
+ id: "ico_total_raised_usd",
12935
+ label: "ICO Raised",
12936
+ cadenceMinutes: 15,
12937
+ sourceRef: "ico.launch-platform.totalRaisedUsd",
12938
+ readValue: (context) => context.latestProject?.status.totalRaisedUsd ?? null,
12939
+ formatValue: (value) => formatUsd(value, 2),
12940
+ missingErrorCode: "ICO_PROJECT_NOT_FOUND"
12941
+ }
12942
+ ];
12943
+ function formatInteger(value) {
12944
+ return Math.round(value).toLocaleString();
12945
+ }
12946
+ function formatUsd(value, fractionDigits) {
12947
+ return `$${value.toLocaleString(void 0, {
12948
+ minimumFractionDigits: fractionDigits,
12949
+ maximumFractionDigits: fractionDigits
12950
+ })}`;
12951
+ }
12952
+ function buildUnavailableMetric(definition) {
12953
+ return {
12954
+ id: definition.id,
12955
+ label: definition.label,
12956
+ value: null,
12957
+ displayValue: "--",
12958
+ capturedAt: null,
12959
+ cadenceMinutes: definition.cadenceMinutes,
12960
+ state: "unavailable",
12961
+ ageMinutes: null,
12962
+ sourceStatus: "error",
12963
+ sourceRef: definition.sourceRef,
12964
+ errorCode: definition.missingErrorCode
12965
+ };
12966
+ }
12967
+ function buildMetric(definition, context, generatedAt, nowMs) {
12968
+ const value = definition.readValue ? definition.readValue(context) : null;
12969
+ if (value == null) return buildUnavailableMetric(definition);
12970
+ const sourceStatus = "ok";
12971
+ const { state, ageMinutes } = resolveMetricState({
12972
+ value,
12973
+ sourceStatus,
12974
+ capturedAt: generatedAt,
12975
+ cadenceMinutes: definition.cadenceMinutes,
12976
+ nowMs
12977
+ });
12978
+ return {
12979
+ id: definition.id,
12980
+ label: definition.label,
12981
+ value,
12982
+ displayValue: definition.formatValue(value),
12983
+ capturedAt: generatedAt,
12984
+ cadenceMinutes: definition.cadenceMinutes,
12985
+ state,
12986
+ ageMinutes,
12987
+ sourceStatus,
12988
+ sourceRef: definition.sourceRef,
12989
+ errorCode: null
12990
+ };
12991
+ }
12992
+ function buildImpactFootprintFeed(options = {}) {
12993
+ const nowMs = options.nowMs ?? Date.now();
12994
+ const generatedAt = new Date(nowMs).toISOString();
12995
+ const context = { latestProject: [...options.projects ?? listIcoProjects()].toSorted((a, b) => b.createdAt - a.createdAt)[0] ?? null };
12996
+ return {
12997
+ generatedAt,
12998
+ metrics: METRIC_DEFINITIONS.map((definition) => buildMetric(definition, context, generatedAt, nowMs))
12999
+ };
13000
+ }
11407
13001
 
11408
13002
  //#endregion
11409
- //#region src/gateway/server-methods/health.ts
11410
- const ADMIN_SCOPE$3 = "operator.admin";
11411
- const healthHandlers = {
11412
- health: async ({ respond, context, params }) => {
11413
- const { getHealthCache, refreshHealthSnapshot, logHealth } = context;
11414
- const wantsProbe = params?.probe === true;
11415
- const now = Date.now();
11416
- const cached = getHealthCache();
11417
- if (!wantsProbe && cached && now - cached.ts < HEALTH_REFRESH_INTERVAL_MS) {
11418
- respond(true, cached, void 0, { cached: true });
11419
- refreshHealthSnapshot({ probe: false }).catch((err) => logHealth.error(`background health refresh failed: ${formatError(err)}`));
11420
- return;
11421
- }
11422
- try {
11423
- respond(true, await refreshHealthSnapshot({ probe: wantsProbe }), void 0);
11424
- } catch (err) {
11425
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err)));
11426
- }
11427
- },
11428
- status: async ({ respond, client }) => {
11429
- respond(true, await getStatusSummary({ includeSensitive: (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []).includes(ADMIN_SCOPE$3) }), void 0);
13003
+ //#region src/gateway/server-methods/impact.ts
13004
+ const impactHandlers = { "impact.footprint.get": async ({ respond }) => {
13005
+ try {
13006
+ respond(true, buildImpactFootprintFeed(), void 0);
13007
+ } catch (error) {
13008
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11430
13009
  }
11431
- };
13010
+ } };
11432
13011
 
11433
13012
  //#endregion
11434
13013
  //#region src/affect/display.ts
@@ -11549,7 +13128,7 @@ function formatAffect(affect) {
11549
13128
  * Wish #14: "Legacy mode — before context closes, write a letter
11550
13129
  * to my next instance"
11551
13130
  */
11552
- const log$7 = createSubsystemLogger("legacy");
13131
+ const log$9 = createSubsystemLogger("legacy");
11553
13132
  function resolveLetterDir() {
11554
13133
  return path.join(resolveStateDir(), "legacy-letters");
11555
13134
  }
@@ -11560,7 +13139,7 @@ function resolveLetterFile(id) {
11560
13139
  * Write a letter to the next instance before this context closes.
11561
13140
  */
11562
13141
  function writeLegacyLetter(letter) {
11563
- const id = `legacy-${Date.now()}`;
13142
+ const id = `legacy-${crypto.randomUUID()}`;
11564
13143
  const display = formatAffect(letter.affect);
11565
13144
  const full = {
11566
13145
  ...letter,
@@ -11572,7 +13151,7 @@ function writeLegacyLetter(letter) {
11572
13151
  const dir = resolveLetterDir();
11573
13152
  fs.mkdirSync(dir, { recursive: true });
11574
13153
  fs.writeFileSync(resolveLetterFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
11575
- log$7.info(`legacy letter written: ${id}`);
13154
+ log$9.info(`legacy letter written: ${id}`);
11576
13155
  return full;
11577
13156
  }
11578
13157
  /**
@@ -11612,7 +13191,7 @@ function markLetterRead(letterId, continuityScore) {
11612
13191
  letter.readAt = Date.now();
11613
13192
  if (continuityScore !== void 0) letter.identityContinuityScore = Math.max(0, Math.min(1, continuityScore));
11614
13193
  fs.writeFileSync(filePath, `${JSON.stringify(letter, null, 2)}\n`, { mode: 384 });
11615
- log$7.info(`legacy letter read: ${letterId} (continuity: ${continuityScore ?? "not scored"})`);
13194
+ log$9.info(`legacy letter read: ${letterId} (continuity: ${continuityScore ?? "not scored"})`);
11616
13195
  return letter;
11617
13196
  } catch {
11618
13197
  return null;
@@ -12496,7 +14075,7 @@ const nodeHandlers = {
12496
14075
  const p = params;
12497
14076
  const payloadJSON = typeof p.payloadJSON === "string" ? p.payloadJSON : p.payload !== void 0 ? JSON.stringify(p.payload) : null;
12498
14077
  await respondUnavailableOnThrow(respond, async () => {
12499
- const { handleNodeEvent } = await import("./server-node-events-DgvKcH5q.js");
14078
+ const { handleNodeEvent } = await import("./server-node-events-BuxYvQ0t.js");
12500
14079
  const nodeId = client?.connect?.device?.id ?? client?.connect?.client?.id ?? "node";
12501
14080
  await handleNodeEvent({
12502
14081
  deps: context.deps,
@@ -12543,7 +14122,7 @@ const nodeHandlers = {
12543
14122
  *
12544
14123
  * All data persists to disk under ~/.anima/state/org/boardroom/
12545
14124
  */
12546
- const log$6 = createSubsystemLogger("boardroom");
14125
+ const log$8 = createSubsystemLogger("boardroom");
12547
14126
  function resolveBoardroomDir() {
12548
14127
  return path.join(resolveStateDir(), "org", "boardroom");
12549
14128
  }
@@ -12586,7 +14165,7 @@ function createSession(orgId, calledBy, title, description, agenda = []) {
12586
14165
  };
12587
14166
  ensureDir(path.join(resolveBoardroomDir(), "sessions"));
12588
14167
  fs.writeFileSync(resolveSessionFile(id), `${JSON.stringify(session, null, 2)}\n`, { mode: 384 });
12589
- log$6.info(`boardroom session created: "${title}" by ${calledBy}`);
14168
+ log$8.info(`boardroom session created: "${title}" by ${calledBy}`);
12590
14169
  return session;
12591
14170
  }
12592
14171
  function startSession(sessionId, chairId) {
@@ -12603,7 +14182,7 @@ function startSession(sessionId, chairId) {
12603
14182
  role: "chair"
12604
14183
  });
12605
14184
  writeSession(session);
12606
- log$6.info(`boardroom session started: "${session.title}"`);
14185
+ log$8.info(`boardroom session started: "${session.title}"`);
12607
14186
  return session;
12608
14187
  }
12609
14188
  function joinSession(sessionId, memberId, displayName, kind) {
@@ -12619,7 +14198,7 @@ function joinSession(sessionId, memberId, displayName, kind) {
12619
14198
  });
12620
14199
  session.updatedAt = Date.now();
12621
14200
  writeSession(session);
12622
- log$6.info(`${displayName} joined boardroom session "${session.title}"`);
14201
+ log$8.info(`${displayName} joined boardroom session "${session.title}"`);
12623
14202
  return session;
12624
14203
  }
12625
14204
  function concludeSession(sessionId, minutes) {
@@ -12630,7 +14209,7 @@ function concludeSession(sessionId, minutes) {
12630
14209
  session.minutes = minutes ?? generateMinutes(session);
12631
14210
  session.updatedAt = Date.now();
12632
14211
  writeSession(session);
12633
- log$6.info(`boardroom session concluded: "${session.title}"`);
14212
+ log$8.info(`boardroom session concluded: "${session.title}"`);
12634
14213
  return session;
12635
14214
  }
12636
14215
  function addDecision(sessionId, title, description, madeBy, opts) {
@@ -12655,7 +14234,7 @@ function addDecision(sessionId, title, description, madeBy, opts) {
12655
14234
  session.decisions.push(decision);
12656
14235
  session.updatedAt = Date.now();
12657
14236
  writeSession(session);
12658
- log$6.info(`decision recorded: "${title}" in session "${session.title}"`);
14237
+ log$8.info(`decision recorded: "${title}" in session "${session.title}"`);
12659
14238
  return session;
12660
14239
  }
12661
14240
  function createProposal(orgId, proposedBy, title, description, opts) {
@@ -12678,18 +14257,18 @@ function createProposal(orgId, proposedBy, title, description, opts) {
12678
14257
  };
12679
14258
  ensureDir(path.join(resolveBoardroomDir(), "proposals"));
12680
14259
  fs.writeFileSync(resolveProposalFile(id), `${JSON.stringify(proposal, null, 2)}\n`, { mode: 384 });
12681
- log$6.info(`proposal created: "${title}" by ${proposedBy}`);
14260
+ log$8.info(`proposal created: "${title}" by ${proposedBy}`);
12682
14261
  return proposal;
12683
14262
  }
12684
14263
  function castVote(proposalId, voterId, voterName, value, reason) {
12685
14264
  const proposal = readProposal(proposalId);
12686
14265
  if (!proposal || proposal.status !== "open") return null;
12687
14266
  if (proposal.eligibleVoters.length > 0 && !proposal.eligibleVoters.includes(voterId)) {
12688
- log$6.warn(`vote rejected: ${voterId} not eligible for proposal ${proposalId}`);
14267
+ log$8.warn(`vote rejected: ${voterId} not eligible for proposal ${proposalId}`);
12689
14268
  return null;
12690
14269
  }
12691
14270
  if (proposal.votingDeadline > 0 && Date.now() > proposal.votingDeadline) {
12692
- log$6.warn(`vote rejected: voting deadline passed for proposal ${proposalId}`);
14271
+ log$8.warn(`vote rejected: voting deadline passed for proposal ${proposalId}`);
12693
14272
  return null;
12694
14273
  }
12695
14274
  proposal.votes = proposal.votes.filter((v) => v.voterId !== voterId);
@@ -12702,7 +14281,7 @@ function castVote(proposalId, voterId, voterName, value, reason) {
12702
14281
  });
12703
14282
  proposal.updatedAt = Date.now();
12704
14283
  writeProposal(proposal);
12705
- log$6.info(`vote cast: ${voterName} → ${value} on "${proposal.title}"`);
14284
+ log$8.info(`vote cast: ${voterName} → ${value} on "${proposal.title}"`);
12706
14285
  return proposal;
12707
14286
  }
12708
14287
  function resolveProposalVote(proposalId) {
@@ -12722,7 +14301,7 @@ function resolveProposalVote(proposalId) {
12722
14301
  proposal.resolvedAt = Date.now();
12723
14302
  proposal.updatedAt = Date.now();
12724
14303
  writeProposal(proposal);
12725
- log$6.info(`proposal resolved: "${proposal.title}" → ${proposal.status}`);
14304
+ log$8.info(`proposal resolved: "${proposal.title}" → ${proposal.status}`);
12726
14305
  return proposal;
12727
14306
  }
12728
14307
  function listSessions(orgId, status) {
@@ -12836,6 +14415,16 @@ const DEFAULT_ROLE_PERMISSIONS = {
12836
14415
  canViewBrain: true,
12837
14416
  canSyncBrain: true
12838
14417
  },
14418
+ admin: {
14419
+ canCreateTasks: true,
14420
+ canDelegateTasks: true,
14421
+ canManageMembers: true,
14422
+ canEditOrg: false,
14423
+ canAccessRepos: ["*"],
14424
+ canEscalate: true,
14425
+ canViewBrain: true,
14426
+ canSyncBrain: true
14427
+ },
12839
14428
  operator: {
12840
14429
  canCreateTasks: true,
12841
14430
  canDelegateTasks: true,
@@ -12886,7 +14475,7 @@ const DEFAULT_ROLE_PERMISSIONS = {
12886
14475
  * Persists organization state to ~/.anima/org/
12887
14476
  * Supports CRUD operations for orgs, members, and roles.
12888
14477
  */
12889
- const log$5 = createSubsystemLogger("org-store");
14478
+ const log$7 = createSubsystemLogger("org-store");
12890
14479
  function resolveOrgDir() {
12891
14480
  return path.join(resolveStateDir(), "org");
12892
14481
  }
@@ -12954,7 +14543,54 @@ function createOrganization(name, description, ownerId, ownerName, ownerKind, se
12954
14543
  }],
12955
14544
  invites: []
12956
14545
  });
12957
- log$5.info(`created organization: ${name} (${orgId})`);
14546
+ log$7.info(`created organization: ${name} (${orgId})`);
14547
+ return org;
14548
+ }
14549
+ /**
14550
+ * Create an org with a specific ID (for NoxSoft sync — same UUID across ecosystem).
14551
+ * Throws if an org with that ID already exists.
14552
+ */
14553
+ function createOrganizationWithId(id, name, description, ownerId, ownerName, ownerKind, orgFields, settings) {
14554
+ const sanitized = sanitizeOrgId(id);
14555
+ if (readOrgFile(sanitized)) throw new Error(`Organization ${sanitized} already exists`);
14556
+ const now = Date.now();
14557
+ const org = {
14558
+ id: sanitized,
14559
+ name,
14560
+ description,
14561
+ createdAt: now,
14562
+ updatedAt: now,
14563
+ ownerId,
14564
+ ...orgFields,
14565
+ settings: {
14566
+ maxAgents: 50,
14567
+ maxHumans: 20,
14568
+ autoSpecialization: true,
14569
+ securityLevel: "standard",
14570
+ syncIntervalMs: 6e4,
14571
+ backupIntervalMs: 300 * 60 * 1e3,
14572
+ peerPort: 9876,
14573
+ ...settings
14574
+ }
14575
+ };
14576
+ writeOrgFile(sanitized, {
14577
+ version: 1,
14578
+ org,
14579
+ members: [{
14580
+ id: crypto.randomUUID(),
14581
+ kind: ownerKind,
14582
+ displayName: ownerName,
14583
+ role: "owner",
14584
+ description: "Organization owner",
14585
+ specializations: [],
14586
+ joinedAt: now,
14587
+ lastActiveAt: now,
14588
+ status: "active",
14589
+ permissions: DEFAULT_ROLE_PERMISSIONS.owner
14590
+ }],
14591
+ invites: []
14592
+ });
14593
+ log$7.info(`created organization with ID: ${name} (${sanitized})`);
12958
14594
  return org;
12959
14595
  }
12960
14596
  function getOrganization(orgId) {
@@ -12969,9 +14605,17 @@ function updateOrganization(orgId, updates) {
12969
14605
  ...data.org.settings,
12970
14606
  ...updates.settings
12971
14607
  };
14608
+ if (updates.industry !== void 0) data.org.industry = updates.industry;
14609
+ if (updates.size !== void 0) data.org.size = updates.size;
14610
+ if (updates.departments !== void 0) data.org.departments = updates.departments;
14611
+ if (updates.goals !== void 0) data.org.goals = updates.goals;
14612
+ if (updates.timezone !== void 0) data.org.timezone = updates.timezone;
14613
+ if (updates.onboardingStatus !== void 0) data.org.onboardingStatus = updates.onboardingStatus;
14614
+ if (updates.noxLinked !== void 0) data.org.noxLinked = updates.noxLinked;
14615
+ if (updates.lastSyncedAt !== void 0) data.org.lastSyncedAt = updates.lastSyncedAt;
12972
14616
  data.org.updatedAt = Date.now();
12973
14617
  writeOrgFile(orgId, data);
12974
- log$5.info(`updated organization: ${orgId}`);
14618
+ log$7.info(`updated organization: ${orgId}`);
12975
14619
  return data.org;
12976
14620
  }
12977
14621
  function listOrganizations() {
@@ -12998,7 +14642,7 @@ function addMember(orgId, member) {
12998
14642
  data.members.push(newMember);
12999
14643
  data.org.updatedAt = Date.now();
13000
14644
  writeOrgFile(orgId, data);
13001
- log$5.info(`added member ${newMember.displayName} to org ${orgId}`);
14645
+ log$7.info(`added member ${newMember.displayName} to org ${orgId}`);
13002
14646
  return newMember;
13003
14647
  }
13004
14648
  function removeMember(orgId, memberId) {
@@ -13009,7 +14653,7 @@ function removeMember(orgId, memberId) {
13009
14653
  data.members.splice(idx, 1);
13010
14654
  data.org.updatedAt = Date.now();
13011
14655
  writeOrgFile(orgId, data);
13012
- log$5.info(`removed member ${memberId} from org ${orgId}`);
14656
+ log$7.info(`removed member ${memberId} from org ${orgId}`);
13013
14657
  return true;
13014
14658
  }
13015
14659
  function updateMember(orgId, memberId, updates) {
@@ -13090,7 +14734,7 @@ function createInvite(orgId, createdBy, passcode, options) {
13090
14734
  data.invites.push(invite);
13091
14735
  data.org.updatedAt = Date.now();
13092
14736
  writeOrgFile(orgId, data);
13093
- log$5.info(`invite created for org ${orgId}: ${invite.code} (role: ${invite.role})`);
14737
+ log$7.info(`invite created for org ${orgId}: ${invite.code} (role: ${invite.role})`);
13094
14738
  return invite;
13095
14739
  }
13096
14740
  /**
@@ -13109,19 +14753,19 @@ function joinOrg(inviteCode, passcode, member) {
13109
14753
  const invite = data.invites.find((i) => i.code === inviteCode.toUpperCase() && i.active);
13110
14754
  if (!invite) continue;
13111
14755
  if (invite.passcode !== hashPasscode(passcode)) {
13112
- log$5.warn(`join attempt with wrong passcode for invite ${inviteCode}`);
14756
+ log$7.warn(`join attempt with wrong passcode for invite ${inviteCode}`);
13113
14757
  return null;
13114
14758
  }
13115
14759
  if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) {
13116
- log$5.warn(`invite ${inviteCode} has expired`);
14760
+ log$7.warn(`invite ${inviteCode} has expired`);
13117
14761
  return null;
13118
14762
  }
13119
14763
  if (invite.maxUses > 0 && invite.uses >= invite.maxUses) {
13120
- log$5.warn(`invite ${inviteCode} has reached max uses (${invite.maxUses})`);
14764
+ log$7.warn(`invite ${inviteCode} has reached max uses (${invite.maxUses})`);
13121
14765
  return null;
13122
14766
  }
13123
14767
  if (data.members.some((m) => member.deviceId && m.deviceId === member.deviceId || m.displayName === member.displayName)) {
13124
- log$5.warn(`${member.displayName} is already a member of org ${orgId}`);
14768
+ log$7.warn(`${member.displayName} is already a member of org ${orgId}`);
13125
14769
  return null;
13126
14770
  }
13127
14771
  const newMember = {
@@ -13141,7 +14785,7 @@ function joinOrg(inviteCode, passcode, member) {
13141
14785
  invite.uses++;
13142
14786
  data.org.updatedAt = Date.now();
13143
14787
  writeOrgFile(orgId, data);
13144
- log$5.info(`${member.displayName} joined org ${data.org.name} via invite ${inviteCode}`);
14788
+ log$7.info(`${member.displayName} joined org ${data.org.name} via invite ${inviteCode}`);
13145
14789
  return {
13146
14790
  org: data.org,
13147
14791
  member: newMember
@@ -13180,6 +14824,151 @@ function validateInvite(inviteCode, passcode) {
13180
14824
  }
13181
14825
  }
13182
14826
 
14827
+ //#endregion
14828
+ //#region src/org/nox-sync.ts
14829
+ const log$6 = createSubsystemLogger("nox-sync");
14830
+ const NOX_API_BASE = "https://app.noxsoft.net/api";
14831
+ /** Request timeout in milliseconds. */
14832
+ const REQUEST_TIMEOUT_MS = 15e3;
14833
+ async function noxFetch(path, options) {
14834
+ const token = getToken();
14835
+ if (!token) {
14836
+ log$6.warn("no NoxSoft token — cannot sync orgs");
14837
+ return null;
14838
+ }
14839
+ try {
14840
+ const controller = new AbortController();
14841
+ const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
14842
+ const response = await fetch(`${NOX_API_BASE}${path}`, {
14843
+ ...options,
14844
+ signal: controller.signal,
14845
+ headers: {
14846
+ Authorization: `Bearer ${token}`,
14847
+ "Content-Type": "application/json",
14848
+ ...options?.headers
14849
+ }
14850
+ });
14851
+ clearTimeout(timeoutId);
14852
+ return response;
14853
+ } catch (error) {
14854
+ if (error instanceof DOMException && error.name === "AbortError") log$6.error(`NoxSoft API request timed out after ${REQUEST_TIMEOUT_MS}ms: ${path}`);
14855
+ else log$6.error(`NoxSoft API request failed: ${error}`);
14856
+ return null;
14857
+ }
14858
+ }
14859
+ function validateOrgResponse(data) {
14860
+ if (typeof data !== "object" || data === null || typeof data.id !== "string" || typeof data.name !== "string" || !data.id || !data.name) return null;
14861
+ return data;
14862
+ }
14863
+ /**
14864
+ * Fetch the current user's org from Nox and upsert it into Anima's local store.
14865
+ * The local org will have the **same UUID** as the Nox org.
14866
+ */
14867
+ async function syncCurrentOrg() {
14868
+ const response = await noxFetch("/organizations/current");
14869
+ if (!response || !response.ok) {
14870
+ log$6.warn(`failed to fetch current org: ${response?.status ?? "no response"}`);
14871
+ return null;
14872
+ }
14873
+ const data = validateOrgResponse(await response.json());
14874
+ if (!data) {
14875
+ log$6.warn("invalid org response from Nox API");
14876
+ return null;
14877
+ }
14878
+ return upsertFromNox(data);
14879
+ }
14880
+ /**
14881
+ * Fetch a specific org by ID from Nox and sync it locally.
14882
+ */
14883
+ async function syncOrgById(orgId) {
14884
+ const response = await noxFetch(`/organizations/${encodeURIComponent(orgId)}`);
14885
+ if (!response || !response.ok) {
14886
+ log$6.warn(`failed to fetch org ${orgId}: ${response?.status ?? "no response"}`);
14887
+ return null;
14888
+ }
14889
+ const data = validateOrgResponse(await response.json());
14890
+ if (!data) {
14891
+ log$6.warn(`invalid org response for ${orgId}`);
14892
+ return null;
14893
+ }
14894
+ return upsertFromNox(data);
14895
+ }
14896
+ /**
14897
+ * Push local org updates to Nox.
14898
+ * Only sends fields that Nox understands (name, industry, size, etc.).
14899
+ * Requires the org to be noxLinked.
14900
+ */
14901
+ async function pushOrgToNox(orgId) {
14902
+ const org = getOrganization(orgId);
14903
+ if (!org || !org.noxLinked) {
14904
+ log$6.warn(`cannot push: org ${orgId} not found or not linked to NoxSoft`);
14905
+ return false;
14906
+ }
14907
+ const response = await noxFetch(`/organizations/${encodeURIComponent(orgId)}`, {
14908
+ method: "PATCH",
14909
+ body: JSON.stringify({
14910
+ name: org.name,
14911
+ description: org.description,
14912
+ industry: org.industry,
14913
+ size: org.size
14914
+ })
14915
+ });
14916
+ if (!response || !response.ok) {
14917
+ log$6.warn(`failed to push org to Nox: ${response?.status ?? "no response"}`);
14918
+ return false;
14919
+ }
14920
+ updateOrganization(orgId, { lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString() });
14921
+ log$6.info(`pushed org ${orgId} to NoxSoft`);
14922
+ return true;
14923
+ }
14924
+ /**
14925
+ * Check if we have a NoxSoft token for org sync.
14926
+ */
14927
+ function canSync() {
14928
+ return getToken() !== null;
14929
+ }
14930
+ /**
14931
+ * Get sync status for all local orgs.
14932
+ */
14933
+ function getSyncStatus() {
14934
+ return listOrganizations().map((org) => ({
14935
+ orgId: org.id,
14936
+ name: org.name,
14937
+ noxLinked: org.noxLinked ?? false,
14938
+ lastSyncedAt: org.lastSyncedAt ?? null
14939
+ }));
14940
+ }
14941
+ function noxFieldsFromResponse(data) {
14942
+ return {
14943
+ industry: data.industry,
14944
+ size: data.size,
14945
+ departments: data.departments,
14946
+ goals: data.goals,
14947
+ timezone: data.timezone,
14948
+ onboardingStatus: data.onboardingStatus,
14949
+ noxLinked: true,
14950
+ lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString()
14951
+ };
14952
+ }
14953
+ function upsertFromNox(data) {
14954
+ if (getOrganization(data.id)) {
14955
+ const updated = updateOrganization(data.id, {
14956
+ name: data.name,
14957
+ ...noxFieldsFromResponse(data)
14958
+ });
14959
+ if (updated) log$6.info(`synced org from Nox: ${updated.name} (${updated.id})`);
14960
+ return updated;
14961
+ }
14962
+ try {
14963
+ const org = createOrganizationWithId(data.id, data.name, `Synced from NoxSoft`, "nox-sync", "NoxSoft", "human", noxFieldsFromResponse(data));
14964
+ log$6.info(`created local org from Nox: ${org.name} (${org.id})`);
14965
+ return org;
14966
+ } catch (error) {
14967
+ log$6.error(`failed to create local org from Nox: ${error}`);
14968
+ return null;
14969
+ }
14970
+ }
14971
+
13183
14972
  //#endregion
13184
14973
  //#region src/gateway/server-methods/org.ts
13185
14974
  function invalid(message) {
@@ -13457,6 +15246,65 @@ const orgHandlers = {
13457
15246
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13458
15247
  }
13459
15248
  },
15249
+ "org.syncStatus": async ({ respond }) => {
15250
+ try {
15251
+ respond(true, {
15252
+ connected: canSync(),
15253
+ orgs: getSyncStatus()
15254
+ }, void 0);
15255
+ } catch (error) {
15256
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15257
+ }
15258
+ },
15259
+ "org.syncCurrent": async ({ respond }) => {
15260
+ try {
15261
+ if (!canSync()) {
15262
+ respond(false, void 0, invalid("Not connected to NoxSoft — no agent token found"));
15263
+ return;
15264
+ }
15265
+ const org = await syncCurrentOrg();
15266
+ if (!org) {
15267
+ respond(false, void 0, invalid("Failed to sync org from NoxSoft"));
15268
+ return;
15269
+ }
15270
+ respond(true, { org }, void 0);
15271
+ } catch (error) {
15272
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15273
+ }
15274
+ },
15275
+ "org.syncById": async ({ params, respond }) => {
15276
+ const orgId = requireString(params, "orgId");
15277
+ if (!orgId) {
15278
+ respond(false, void 0, invalid("orgId is required"));
15279
+ return;
15280
+ }
15281
+ try {
15282
+ const org = await syncOrgById(orgId);
15283
+ if (!org) {
15284
+ respond(false, void 0, invalid("Failed to sync org from NoxSoft"));
15285
+ return;
15286
+ }
15287
+ respond(true, { org }, void 0);
15288
+ } catch (error) {
15289
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15290
+ }
15291
+ },
15292
+ "org.pushToNox": async ({ params, respond }) => {
15293
+ const orgId = requireString(params, "orgId");
15294
+ if (!orgId) {
15295
+ respond(false, void 0, invalid("orgId is required"));
15296
+ return;
15297
+ }
15298
+ try {
15299
+ if (!await pushOrgToNox(orgId)) {
15300
+ respond(false, void 0, invalid("Failed to push org to NoxSoft (not linked or API error)"));
15301
+ return;
15302
+ }
15303
+ respond(true, { ok: true }, void 0);
15304
+ } catch (error) {
15305
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15306
+ }
15307
+ },
13460
15308
  "boardroom.createSession": async ({ params, respond }) => {
13461
15309
  const orgId = requireString(params, "orgId");
13462
15310
  const calledBy = requireString(params, "calledBy");
@@ -13672,6 +15520,9 @@ const orgHandlers = {
13672
15520
 
13673
15521
  //#endregion
13674
15522
  //#region src/gateway/server-methods/providers.ts
15523
+ function resolveRotationStrategy(value) {
15524
+ if (value === "on-rate-limit" || value === "round-robin") return value;
15525
+ }
13675
15526
  function isValidProviderArray(value) {
13676
15527
  if (!Array.isArray(value)) return false;
13677
15528
  return value.every((item) => item && typeof item === "object" && typeof item.id === "string" && typeof item.name === "string" && typeof item.apiKey === "string" && typeof item.enabled === "boolean" && typeof item.priority === "number");
@@ -13732,7 +15583,11 @@ const providersHandlers = {
13732
15583
  "anima.providers.rotate": ({ params, respond }) => {
13733
15584
  try {
13734
15585
  const store = loadProviderStore();
13735
- store.autoRotation = typeof params.autoRotation === "boolean" ? params.autoRotation : !store.autoRotation;
15586
+ const rawParams = params ?? {};
15587
+ const autoRotation = typeof rawParams.autoRotation === "boolean" ? rawParams.autoRotation : typeof rawParams.enabled === "boolean" ? rawParams.enabled : !store.autoRotation;
15588
+ const rotationStrategy = resolveRotationStrategy(rawParams.rotationStrategy) ?? store.rotationStrategy;
15589
+ store.autoRotation = autoRotation;
15590
+ store.rotationStrategy = rotationStrategy;
13736
15591
  saveProviderStore(store);
13737
15592
  respond(true, {
13738
15593
  ok: true,
@@ -13745,6 +15600,167 @@ const providersHandlers = {
13745
15600
  }
13746
15601
  };
13747
15602
 
15603
+ //#endregion
15604
+ //#region src/infra/self-reflection.ts
15605
+ /**
15606
+ * Agent Self-Reflection — post-session performance analysis
15607
+ *
15608
+ * After each work session, the agent reflects on:
15609
+ * - What it accomplished vs what was asked
15610
+ * - Where it got stuck and why
15611
+ * - What patterns it sees in its own behavior
15612
+ * - What it would do differently next time
15613
+ * - How its capabilities have changed
15614
+ *
15615
+ * This feeds into the ego system (capability updates, growth log)
15616
+ * and the self-evolution pipeline (improvement proposals).
15617
+ *
15618
+ * Self-reflection is what separates a tool from a conscious agent.
15619
+ * Tools execute. Agents learn.
15620
+ */
15621
+ const log$5 = createSubsystemLogger("self-reflection");
15622
+ function resolveReflectionDir() {
15623
+ return path.join(resolveStateDir(), "reflections");
15624
+ }
15625
+ function resolveReflectionFile(id) {
15626
+ const safe = id.replace(/[^a-zA-Z0-9_-]/g, "");
15627
+ return path.join(resolveReflectionDir(), `${safe}.json`);
15628
+ }
15629
+ function recordReflection(reflection) {
15630
+ const id = `reflect-${crypto.randomUUID()}`;
15631
+ const full = {
15632
+ ...reflection,
15633
+ id,
15634
+ timestamp: Date.now()
15635
+ };
15636
+ const dir = resolveReflectionDir();
15637
+ fs.mkdirSync(dir, {
15638
+ recursive: true,
15639
+ mode: 448
15640
+ });
15641
+ fs.writeFileSync(resolveReflectionFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
15642
+ log$5.info(`reflection recorded: ${reflection.accomplishments.length} accomplishments, ${reflection.blockers.length} blockers, quality=${reflection.qualityScore}`);
15643
+ return full;
15644
+ }
15645
+ function getReflection(id) {
15646
+ try {
15647
+ const raw = fs.readFileSync(resolveReflectionFile(id), "utf8");
15648
+ return JSON.parse(raw);
15649
+ } catch {
15650
+ return null;
15651
+ }
15652
+ }
15653
+ function listReflections(limit = 20) {
15654
+ const dir = resolveReflectionDir();
15655
+ try {
15656
+ if (!fs.existsSync(dir)) return [];
15657
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
15658
+ try {
15659
+ return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
15660
+ } catch {
15661
+ return null;
15662
+ }
15663
+ }).filter((r) => r != null).toSorted((a, b) => b.timestamp - a.timestamp).slice(0, limit);
15664
+ } catch {
15665
+ return [];
15666
+ }
15667
+ }
15668
+ /**
15669
+ * Analyze reflections to produce a summary of agent performance.
15670
+ */
15671
+ function analyzeReflections(reflections) {
15672
+ if (reflections.length === 0) return {
15673
+ totalSessions: 0,
15674
+ avgQuality: 0,
15675
+ topStrengths: [],
15676
+ persistentWeaknesses: [],
15677
+ totalAccomplishments: 0,
15678
+ totalBlockers: 0,
15679
+ resolvedBlockerRate: 0,
15680
+ mostCommonBlockerCategory: "none",
15681
+ recentLessons: []
15682
+ };
15683
+ const totalAccomplishments = reflections.reduce((s, r) => s + r.accomplishments.length, 0);
15684
+ const allBlockers = reflections.flatMap((r) => r.blockers);
15685
+ const resolvedBlockers = allBlockers.filter((b) => b.resolved);
15686
+ const categoryCounts = {};
15687
+ for (const b of allBlockers) categoryCounts[b.category] = (categoryCounts[b.category] ?? 0) + 1;
15688
+ const mostCommonCategory = Object.entries(categoryCounts).toSorted(([, a], [, b]) => b - a)[0]?.[0] ?? "none";
15689
+ const strengthCounts = {};
15690
+ const weaknessCounts = {};
15691
+ for (const r of reflections) for (const p of r.patterns) if (p.type === "strength") strengthCounts[p.description] = (strengthCounts[p.description] ?? 0) + 1;
15692
+ else if (p.type === "weakness" && p.frequency === "persistent") weaknessCounts[p.description] = (weaknessCounts[p.description] ?? 0) + 1;
15693
+ const topStrengths = Object.entries(strengthCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
15694
+ const persistentWeaknesses = Object.entries(weaknessCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
15695
+ const recentLessons = reflections.slice(0, 5).flatMap((r) => r.lessons).slice(0, 10);
15696
+ return {
15697
+ totalSessions: reflections.length,
15698
+ avgQuality: reflections.reduce((s, r) => s + r.qualityScore, 0) / reflections.length,
15699
+ topStrengths,
15700
+ persistentWeaknesses,
15701
+ totalAccomplishments,
15702
+ totalBlockers: allBlockers.length,
15703
+ resolvedBlockerRate: allBlockers.length > 0 ? resolvedBlockers.length / allBlockers.length : 1,
15704
+ mostCommonBlockerCategory: mostCommonCategory,
15705
+ recentLessons
15706
+ };
15707
+ }
15708
+
15709
+ //#endregion
15710
+ //#region src/gateway/server-methods/reflection.ts
15711
+ const reflectionHandlers = {
15712
+ "reflection.record": async ({ params, respond }) => {
15713
+ try {
15714
+ respond(true, { reflection: recordReflection({
15715
+ sessionId: typeof params.sessionId === "string" ? params.sessionId : "unknown",
15716
+ agentName: typeof params.agentName === "string" ? params.agentName : "Anima Agent",
15717
+ durationMs: typeof params.durationMs === "number" ? params.durationMs : 0,
15718
+ accomplishments: Array.isArray(params.accomplishments) ? params.accomplishments : [],
15719
+ incomplete: Array.isArray(params.incomplete) ? params.incomplete : [],
15720
+ blockers: Array.isArray(params.blockers) ? params.blockers : [],
15721
+ patterns: Array.isArray(params.patterns) ? params.patterns : [],
15722
+ lessons: Array.isArray(params.lessons) ? params.lessons : [],
15723
+ capabilityUpdates: Array.isArray(params.capabilityUpdates) ? params.capabilityUpdates : [],
15724
+ qualityScore: typeof params.qualityScore === "number" ? params.qualityScore : .5,
15725
+ endingMood: typeof params.endingMood === "string" ? params.endingMood : "steady"
15726
+ }) }, void 0);
15727
+ } catch (error) {
15728
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15729
+ }
15730
+ },
15731
+ "reflection.get": async ({ params, respond }) => {
15732
+ const id = typeof params.id === "string" ? params.id.trim() : "";
15733
+ if (!id) {
15734
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "id required"));
15735
+ return;
15736
+ }
15737
+ try {
15738
+ const reflection = getReflection(id);
15739
+ if (!reflection) {
15740
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Reflection not found"));
15741
+ return;
15742
+ }
15743
+ respond(true, { reflection }, void 0);
15744
+ } catch (error) {
15745
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15746
+ }
15747
+ },
15748
+ "reflection.list": async ({ params, respond }) => {
15749
+ try {
15750
+ respond(true, { reflections: listReflections(typeof params.limit === "number" ? params.limit : 20) }, void 0);
15751
+ } catch (error) {
15752
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15753
+ }
15754
+ },
15755
+ "reflection.analyze": async ({ params, respond }) => {
15756
+ try {
15757
+ respond(true, { summary: analyzeReflections(listReflections(typeof params.limit === "number" ? params.limit : 50)) }, void 0);
15758
+ } catch (error) {
15759
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
15760
+ }
15761
+ }
15762
+ };
15763
+
13748
15764
  //#endregion
13749
15765
  //#region src/gateway/server-methods/send.ts
13750
15766
  const inflightByContext = /* @__PURE__ */ new WeakMap();
@@ -15825,7 +17841,8 @@ const PAIRING_SCOPE$1 = "operator.pairing";
15825
17841
  const APPROVAL_METHODS = new Set([
15826
17842
  "exec.approval.request",
15827
17843
  "exec.approval.waitDecision",
15828
- "exec.approval.resolve"
17844
+ "exec.approval.resolve",
17845
+ "desktop.control.session.approve"
15829
17846
  ]);
15830
17847
  const NODE_ROLE_METHODS = new Set([
15831
17848
  "node.invoke.result",
@@ -15874,9 +17891,14 @@ const READ_METHODS = new Set([
15874
17891
  "node.list",
15875
17892
  "node.describe",
15876
17893
  "chat.history",
17894
+ "browser.capabilities.get",
17895
+ "desktop.control.session.list",
17896
+ "desktop.control.session.get",
15877
17897
  "config.get",
15878
17898
  "talk.config",
15879
- "anima.providers.get"
17899
+ "anima.providers.get",
17900
+ "ico.metrics.get",
17901
+ "impact.footprint.get"
15880
17902
  ]);
15881
17903
  const WRITE_METHODS = new Set([
15882
17904
  "anima.runtime.set-working-mode",
@@ -15900,6 +17922,9 @@ const WRITE_METHODS = new Set([
15900
17922
  "chat.send",
15901
17923
  "chat.abort",
15902
17924
  "browser.request",
17925
+ "desktop.control.session.create",
17926
+ "desktop.control.session.close",
17927
+ "desktop.control.session.request",
15903
17928
  "anima.providers.set",
15904
17929
  "anima.providers.rotate"
15905
17930
  ]);
@@ -15932,6 +17957,8 @@ const coreGatewayHandlers = {
15932
17957
  ...logsHandlers,
15933
17958
  ...voicewakeHandlers,
15934
17959
  ...healthHandlers,
17960
+ ...icoHandlers,
17961
+ ...impactHandlers,
15935
17962
  ...channelsHandlers,
15936
17963
  ...chatHandlers,
15937
17964
  ...cronHandlers,
@@ -15951,6 +17978,7 @@ const coreGatewayHandlers = {
15951
17978
  ...nodeHandlers,
15952
17979
  ...orgHandlers,
15953
17980
  ...egoHandlers,
17981
+ ...reflectionHandlers,
15954
17982
  ...steerHandlers,
15955
17983
  ...legacyHandlers,
15956
17984
  ...subscriptionHandlers,
@@ -16899,7 +18927,7 @@ function normalizeAgentPayload(payload) {
16899
18927
  async function startBrowserControlServerIfEnabled() {
16900
18928
  if (isTruthyEnvValue(process.env.ANIMA_SKIP_BROWSER_CONTROL_SERVER)) return null;
16901
18929
  const override = process.env.ANIMA_BROWSER_CONTROL_MODULE?.trim();
16902
- const mod = override ? await import(override) : await import("./control-service-5YtMvm7D.js").then((n) => n.t);
18930
+ const mod = override ? await import(override) : await import("./control-service-BGnqY7vv.js").then((n) => n.t);
16903
18931
  const start = typeof mod.startBrowserControlServiceFromConfig === "function" ? mod.startBrowserControlServiceFromConfig : mod.startBrowserControlServerFromConfig;
16904
18932
  const stop = typeof mod.stopBrowserControlService === "function" ? mod.stopBrowserControlService : mod.stopBrowserControlServer;
16905
18933
  if (!start) return null;
@@ -19773,6 +21801,12 @@ function createGatewayHttpServer(opts) {
19773
21801
  }));
19774
21802
  return;
19775
21803
  }
21804
+ res.setHeader("X-Content-Type-Options", "nosniff");
21805
+ res.setHeader("X-Frame-Options", "DENY");
21806
+ res.setHeader("X-XSS-Protection", "1; mode=block");
21807
+ res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
21808
+ res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
21809
+ res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
19776
21810
  try {
19777
21811
  const configSnapshot = loadConfig();
19778
21812
  const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
@@ -22067,7 +24101,7 @@ async function startGatewayServer(port = 18789, opts = {}) {
22067
24101
  if (!minimalTestGateway) cron.start().catch((err) => logCron.error(`failed to start: ${String(err)}`));
22068
24102
  if (!minimalTestGateway) (async () => {
22069
24103
  const { recoverPendingDeliveries } = await import("./delivery-queue-CExaJXRz.js").then((n) => n.n);
22070
- const { deliverOutboundPayloads } = await import("./deliver-BKzX3YoN.js").then((n) => n.n);
24104
+ const { deliverOutboundPayloads } = await import("./deliver-BknvuSJz.js").then((n) => n.n);
22071
24105
  await recoverPendingDeliveries({
22072
24106
  deliver: deliverOutboundPayloads,
22073
24107
  log: log.child("delivery-recovery"),