@gguf/claw 2026.2.9 → 2026.2.13

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 (715) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/LICENSE +1 -1
  3. package/dist/{accounts-MyAvfCVH.js → accounts-54zZMYCo.js} +5 -2
  4. package/dist/{accounts-DbzMEfKN.js → accounts-Bvh0DFxS.js} +5 -2
  5. package/dist/{acp-cli-MZ3h1E1n.js → acp-cli-BslcOPdx.js} +146 -25
  6. package/dist/{acp-cli-DKJRTfwB.js → acp-cli-D6rk5cOh.js} +145 -24
  7. package/dist/{agent-whSJT2Lk.js → agent-C0yL70cy.js} +26 -20
  8. package/dist/{agent-c1QNeDmV.js → agent-DjZxytiC.js} +26 -20
  9. package/dist/{agent-scope-D3me2AZa.js → agent-scope-Bkr9fZtl.js} +31 -14
  10. package/dist/{agent-scope-Dp8sREli.js → agent-scope-DASgjz2_.js} +199 -14
  11. package/dist/{agent-scope-DnyDZ5RH.js → agent-scope-GYIs5dyU.js} +30 -13
  12. package/dist/{agent-scope-Dpav7C-i.js → agent-scope-okUOVjE5.js} +32 -11
  13. package/dist/audio-preflight-B0kLz-Ma.js +60 -0
  14. package/dist/audio-preflight-BCs_J33s.js +60 -0
  15. package/dist/audio-preflight-CTl2RCyF.js +71 -0
  16. package/dist/audio-preflight-MhF6YlAY.js +74 -0
  17. package/dist/{audit-BFYy1qSw.js → audit-BYfhZ7LA.js} +454 -31
  18. package/dist/{audit-Dn2cBl2x.js → audit-CfPZ_5Id.js} +452 -29
  19. package/dist/auth-9nTeB2Je.js +602 -0
  20. package/dist/auth-CLhyWwAU.js +593 -0
  21. package/dist/{auth-health-Cx5exPMV.js → auth-health-CWiLyzSr.js} +1 -1
  22. package/dist/{auth-health-DjT4fUpw.js → auth-health-qD4RND47.js} +1 -1
  23. package/dist/{auth-profiles-FJ3VY25a.js → auth-profiles-Cp9MtUdM.js} +353 -33
  24. package/dist/build-info.json +2 -2
  25. package/dist/bundled/boot-md/handler.js +33 -25
  26. package/dist/bundled/session-memory/handler.js +33 -22
  27. package/dist/{call-CD2IZCHT.js → call-CjEdFGAf.js} +7 -7
  28. package/dist/{call-CM25qgxz.js → call-DAfkvtVq.js} +6 -6
  29. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  30. package/dist/canvas-host/a2ui/a2ui.bundle.js +7 -1
  31. package/dist/{channel-options-CremuJyh.js → channel-options-B8dPzlyO.js} +4 -4
  32. package/dist/{channel-options-D-JnJ4Ft.js → channel-options-Bq5IC5Tv.js} +12 -7
  33. package/dist/{channel-selection-DAHCVAX4.js → channel-selection-BaW1xXEa.js} +2 -2
  34. package/dist/{channel-selection-DPV9hvY8.js → channel-selection-dR0jCgTn.js} +2 -2
  35. package/dist/{channels-cli-6deHFr9t.js → channels-cli-hPo28hWS.js} +61 -56
  36. package/dist/{channels-cli-D3tKmhlt.js → channels-cli-zi3rO0jq.js} +62 -57
  37. package/dist/{channels-status-issues-BN1ICfdy.js → channels-status-issues-kb-M2Fi0.js} +1 -1
  38. package/dist/{channels-status-issues-DFhI_u0p.js → channels-status-issues-ketdwZun.js} +1 -1
  39. package/dist/{chrome-B2UjqY-9.js → chrome--Fe8F5Kf.js} +24 -12
  40. package/dist/{chrome-COabMr6f.js → chrome-BWeMtFGf.js} +24 -12
  41. package/dist/{chrome-CQd_MVOA.js → chrome-Bx24uq7B.js} +27 -15
  42. package/dist/{chrome-CxRJz4ZD.js → chrome-n_3rtK2c.js} +22 -11
  43. package/dist/{clack-prompter-BkNZ4Xdw.js → clack-prompter-B-tJmODa.js} +5 -5
  44. package/dist/{clack-prompter-DuBVnTKy.js → clack-prompter-DpuKn_Uy.js} +5 -5
  45. package/dist/cli/daemon-cli.js +8 -1
  46. package/dist/cli-9lwO6Ttx.js +94 -0
  47. package/dist/cli-CNNdyxPO.js +91 -0
  48. package/dist/{client-DMloFP_O.js → client-BhZjzrH2.js} +73 -9
  49. package/dist/{client-C0gQ7hrj.js → client-DyAxKXKY.js} +73 -9
  50. package/dist/{command-format-ayFsmwwz.js → command-format-Bxe0mWee.js} +1 -1
  51. package/dist/{command-options-BQdH6qnK.js → command-options-BDV7Xgs-.js} +9 -4
  52. package/dist/{commands-BWHYcc83.js → commands-gOiRcfoU.js} +4 -4
  53. package/dist/{tui-formatters-BDP_71Xt.js → commands-registry-6NUFrejL.js} +6 -114
  54. package/dist/{tui-formatters-CIx4sCQO.js → commands-registry-DGgkLQ7A.js} +6 -114
  55. package/dist/{completion-cli-DEJia0V1.js → completion-cli-B1kHKJZX.js} +30 -30
  56. package/dist/{completion-cli-D_0fx2O6.js → completion-cli-Drks7xRK.js} +3 -3
  57. package/dist/{config-CQt4vGxI.js → config-7NCznPmF.js} +336 -97
  58. package/dist/{config-fCnPoWjU.js → config-B8v0zg48.js} +295 -99
  59. package/dist/{config-Bj2eDa02.js → config-CeWMHOiQ.js} +295 -99
  60. package/dist/{config-ethqi73X.js → config-D8pgDSNo.js} +358 -99
  61. package/dist/{config-guard-BJuqQvng.js → config-guard-RbHxYc9j.js} +212 -63
  62. package/dist/{configure-skrLiSwW.js → configure-DLp2Xz7L.js} +59 -40
  63. package/dist/{configure-C-pYuYg_.js → configure-Su1S0gi-.js} +58 -39
  64. package/dist/control-auth-BlWU-jBl.js +54 -0
  65. package/dist/control-auth-C8rIqEdA.js +54 -0
  66. package/dist/{control-service-BDgF-FZ0.js → control-service-BNDthc1N.js} +11 -5
  67. package/dist/{control-service-Djd_WI3_.js → control-service-COF59GQe.js} +10 -4
  68. package/dist/control-ui/assets/{index-CnB9IO4a.js → index-BECn2L1T.js} +369 -368
  69. package/dist/control-ui/assets/index-BECn2L1T.js.map +1 -0
  70. package/dist/control-ui/assets/index-DRPcd1Z4.css +1 -0
  71. package/dist/control-ui/index.html +2 -2
  72. package/dist/{cron-cli-CB6CufAb.js → cron-cli-CSy4-JGS.js} +20 -20
  73. package/dist/{cron-cli-Db6fardJ.js → cron-cli-Db3uCDIT.js} +21 -21
  74. package/dist/{daemon-cli-Xe22v7lZ.js → daemon-cli-BLbzcTuD.js} +61 -22
  75. package/dist/{daemon-cli-BlHK0ly2.js → daemon-cli-DR0D35MO.js} +60 -21
  76. package/dist/{daemon-runtime-CMqH8BUE.js → daemon-runtime-ZWXvLDxx.js} +3 -3
  77. package/dist/{daemon-runtime-DwQFvDXZ.js → daemon-runtime-pVcZ2KDE.js} +3 -3
  78. package/dist/{deliver-CD7-BhYD.js → deliver-BHNoC9Yk.js} +396 -290
  79. package/dist/{deliver-BdGjIcTC.js → deliver-C_5eGQrX.js} +392 -286
  80. package/dist/{deliver-nTKaXF--.js → deliver-DPHZlWgr.js} +392 -287
  81. package/dist/{deliver-CDMGxRoW.js → deliver-geVWJ52I.js} +394 -288
  82. package/dist/{deps-BDQ_K8zf.js → deps-CP0dcOgD.js} +2 -2
  83. package/dist/{deps-D60FbgTP.js → deps-DW5r2IEk.js} +2 -2
  84. package/dist/{devices-cli-N559801X.js → devices-cli-BViqX5pl.js} +15 -15
  85. package/dist/{devices-cli-IxmPLIk8.js → devices-cli-DpYaY-iM.js} +14 -14
  86. package/dist/{directory-cli-Caq-OYk8.js → directory-cli-BWD1DdKf.js} +16 -16
  87. package/dist/{directory-cli-ClrdmQL-.js → directory-cli-BcvZfkfo.js} +17 -17
  88. package/dist/{dispatcher-BfXtm4Dl.js → dispatcher-4Qn951N3.js} +5 -3
  89. package/dist/{dns-cli-DgVO0Pkw.js → dns-cli-_Ych2tu9.js} +12 -12
  90. package/dist/{dns-cli-BTNZkWHs.js → dns-cli-gQCxUXgU.js} +13 -13
  91. package/dist/{docs-cli-9Xan7C6D.js → docs-cli-Bseiau7J.js} +7 -7
  92. package/dist/{docs-cli-DZULc91f.js → docs-cli-DzBTlWQE.js} +8 -8
  93. package/dist/{doctor-D39rZvNH.js → doctor-BNkYYahD.js} +37 -36
  94. package/dist/{doctor-Dq1YeYdH.js → doctor-DzIgdPx1.js} +37 -36
  95. package/dist/entry.js +77 -21
  96. package/dist/{env-B5YXooWp.js → env-BUuSkE19.js} +1 -1
  97. package/dist/{exec-DFOtZbI0.js → exec-BPQSKwGa.js} +5 -3
  98. package/dist/{exec-B8JKbXKW.js → exec-DqZFMawz.js} +5 -3
  99. package/dist/{exec-Bas1hoSJ.js → exec-EKUaAU91.js} +57 -18
  100. package/dist/{exec-CiH_vkWn.js → exec-_PSUrMP8.js} +528 -19
  101. package/dist/{exec-approvals-DGPTjO0N.js → exec-approvals-Bqk-tIxY.js} +134 -51
  102. package/dist/{exec-approvals-C9InMoAB.js → exec-approvals-C67V-ljH.js} +134 -51
  103. package/dist/{exec-approvals-cli-EASbqFd-.js → exec-approvals-cli-D6vfSqQu.js} +22 -22
  104. package/dist/{exec-approvals-cli-DPHItoxG.js → exec-approvals-cli-DAdoki_R.js} +21 -21
  105. package/dist/extensionAPI.js +8518 -9140
  106. package/dist/fetch-Bz1WxfzV.js +285 -0
  107. package/dist/fetch-D2O8s8I1.js +285 -0
  108. package/dist/fetch-Dm-nCwa_.js +285 -0
  109. package/dist/fetch-wuOZDzdT.js +285 -0
  110. package/dist/{gateway-cli-BFqUIif8.js → gateway-cli-C-k7JPlr.js} +1868 -1072
  111. package/dist/{gateway-cli-v4kSPsLE.js → gateway-cli-DIIJ9Z0Y.js} +1870 -1074
  112. package/dist/{gateway-rpc-D6LrkcSA.js → gateway-rpc-D6jLh81b.js} +3 -3
  113. package/dist/{gateway-rpc-dHFK02Kk.js → gateway-rpc-aqysUyf5.js} +3 -3
  114. package/dist/{github-copilot-auth-CQIWc0hC.js → github-copilot-auth-BUqfX7hG.js} +316 -52
  115. package/dist/{github-copilot-auth-D2jfnapd.js → github-copilot-auth-By-nyRb6.js} +316 -52
  116. package/dist/{github-copilot-token-SLWintYd.js → github-copilot-token-C9W4SY9o.js} +7 -4
  117. package/dist/{github-copilot-token-BW-SEg7E.js → github-copilot-token-CiF5Iyi2.js} +6 -3
  118. package/dist/{github-copilot-token-C9IJh2Pn.js → github-copilot-token-DatTe1w-.js} +6 -3
  119. package/dist/{github-copilot-token-wCk9Fg_E.js → github-copilot-token-c9Igt3ZH.js} +6 -3
  120. package/dist/{gmail-setup-utils-CVNgLkXL.js → gmail-setup-utils-HvKMdooP.js} +4 -4
  121. package/dist/{gmail-setup-utils-CAM1vbUS.js → gmail-setup-utils-c-iF00aL.js} +3 -3
  122. package/dist/{health-format-C77hrjEQ.js → health-format-BORnJOeS.js} +106 -44
  123. package/dist/{health-format-DDYtlkB9.js → health-format-Nd0jcoqM.js} +105 -43
  124. package/dist/{help-format-CUnac_bT.js → help-format-Cd5PLwXe.js} +1 -1
  125. package/dist/{help-format-aiW76js8.js → help-format-DYBEvMOX.js} +1 -1
  126. package/dist/{hooks-cli-DsflBRxX.js → hooks-cli-3KdsbdRi.js} +53 -47
  127. package/dist/{hooks-cli-C7kctMuZ.js → hooks-cli-BThja6wK.js} +53 -47
  128. package/dist/{hooks-status-DRAVHSPg.js → hooks-status-BbIz0zmm.js} +6 -5
  129. package/dist/{hooks-status-lHWrY64E.js → hooks-status-DPJORMB6.js} +6 -5
  130. package/dist/{image--gbzucyh.js → image-BaJKrmCs.js} +12 -8
  131. package/dist/{image-ORs4LLwg.js → image-D-5pUELC.js} +13 -9
  132. package/dist/{image-DMnjYGdA.js → image-TvL5YI_W.js} +13 -9
  133. package/dist/{image-BVNytEIn.js → image-bodq5cUH.js} +13 -9
  134. package/dist/index.js +261 -109
  135. package/dist/{installs-CXGV291R.js → installs-BrOMqREO.js} +7 -6
  136. package/dist/{installs-89zeUsVn.js → installs-z69au9Te.js} +7 -6
  137. package/dist/{links-Dg90NTyF.js → links-AVB88xxH.js} +1 -1
  138. package/dist/{links-7M-j83As.js → links-DpxpaKe1.js} +1 -1
  139. package/dist/llm-slug-generator.js +18 -19
  140. package/dist/{loader-BnzQyT31.js → loader-CS-5lMQa.js} +3694 -4531
  141. package/dist/{logging-DuK6YXuK.js → logging-B3KnAryz.js} +2 -2
  142. package/dist/{logging-CNq0UUgf.js → logging-DEPo2hji.js} +1 -1
  143. package/dist/{login-qr-CJ__cE3-.js → login-qr--28WL1TN.js} +11 -5
  144. package/dist/{login-qr-BVeOFfNW.js → login-qr-4o2aC2UE.js} +9 -4
  145. package/dist/{login-qr-BJChByHH.js → login-qr-BUdeu1Sl.js} +8 -2
  146. package/dist/{login-qr-KUOtNJaQ.js → login-qr-BsYM2E1y.js} +12 -6
  147. package/dist/{logs-cli-Cm7AiarR.js → logs-cli-DDMD5w5_.js} +38 -22
  148. package/dist/{logs-cli-BWmtAsjp.js → logs-cli-DvPoVKCN.js} +38 -22
  149. package/dist/{manager-C-jXr9ks.js → manager-CXo1uqmO.js} +102 -86
  150. package/dist/{manager-CMFBuvVd.js → manager-ChW0jk7T.js} +101 -85
  151. package/dist/{manager-D2Ndphg3.js → manager-DUOe7ud6.js} +100 -85
  152. package/dist/{manager-BsdlwsL5.js → manager-PoxUqdN_.js} +98 -82
  153. package/dist/{manifest-registry-D5SiA3xq.js → manifest-registry-CVsqjgX0.js} +40 -2
  154. package/dist/{manifest-registry-DyMRD3rY.js → manifest-registry-jeAPx6AW.js} +40 -2
  155. package/dist/{message-channel-CHRYQtAM.js → message-channel-CTtrEkmW.js} +1 -1
  156. package/dist/{message-channel-BlgPSDAh.js → message-channel-DWcu72r7.js} +1 -1
  157. package/dist/{model-auth-BqjMkNFs.js → model-auth-BvODRbV0.js} +362 -35
  158. package/dist/{model-selection-DbsbOAoh.js → model-selection-B53OvWCf.js} +353 -33
  159. package/dist/{model-selection-DlV6wnTr.js → model-selection-vC82fEiP.js} +331 -30
  160. package/dist/{models-cli-DIFBrK4W.js → models-cli-DqsKsOgd.js} +66 -55
  161. package/dist/{models-cli-0XhQQbMW.js → models-cli-NV0bnh8l.js} +66 -55
  162. package/dist/{node-cli-BMUfVCSq.js → node-cli-C7YleuBk.js} +54 -44
  163. package/dist/{node-cli-DY4lzhDA.js → node-cli-CxwoHnZ6.js} +54 -44
  164. package/dist/{node-service-DQ-tiSie.js → node-service-C7f_uvx9.js} +2 -2
  165. package/dist/{node-service-u8g85nD3.js → node-service-De_WkxJe.js} +2 -2
  166. package/dist/{nodes-cli-BX6oWnLC.js → nodes-cli-BxrMVI9V.js} +25 -23
  167. package/dist/{nodes-cli-CVHzcQo2.js → nodes-cli-Clb0ocwB.js} +24 -22
  168. package/dist/{nodes-screen-DGlNPbk4.js → nodes-screen-CVL9363A.js} +48 -6
  169. package/dist/{nodes-screen-lykd2cny.js → nodes-screen-DsHJIN2I.js} +47 -5
  170. package/dist/{note-Ci08TSbV.js → note-Duiadw1g.js} +1 -1
  171. package/dist/{note-DVO1KLaW.js → note-uC6iDp4y.js} +2 -2
  172. package/dist/{onboard-channels-DTkFFbzS.js → onboard-channels-C5Iaafwb.js} +10 -10
  173. package/dist/{onboard-channels-CtDnwaF5.js → onboard-channels-C5uL3i8d.js} +11 -11
  174. package/dist/{onboard-skills-BnAcpzfX.js → onboard-skills-BFxdI1Y1.js} +1143 -112
  175. package/dist/{onboard-skills-DuoDzEmI.js → onboard-skills-DUG8Y0se.js} +1142 -111
  176. package/dist/{onboarding-DvhiiHh0.js → onboarding-ClzElK4D.js} +56 -48
  177. package/dist/{openclaw-root-93W6UrUK.js → openclaw-root-BKsZvO6K.js} +6 -2
  178. package/dist/{openclaw-root-9ILYSmJ9.js → openclaw-root-CEnmuBUN.js} +6 -2
  179. package/dist/{pairing-cli-BKJHBxwT.js → pairing-cli-BWWFZF7Q.js} +16 -16
  180. package/dist/{pairing-cli-DJHjPBwu.js → pairing-cli-BrFLxnug.js} +16 -16
  181. package/dist/{pairing-labels-xImhiJax.js → pairing-labels-C8KULWNH.js} +1 -1
  182. package/dist/{pairing-labels-CHxlh3tT.js → pairing-labels-Dt2vXyI7.js} +1 -1
  183. package/dist/{pairing-store-CO6umWFP.js → pairing-store-Dz-ArTQS.js} +3 -3
  184. package/dist/{pairing-store-BpPUNzmB.js → pairing-store-gQdv7Ruh.js} +2 -2
  185. package/dist/{path-env-Nq83EHH9.js → path-env-BRKerjt1.js} +2 -2
  186. package/dist/{path-env-CXWUFfFv.js → path-env-OJAyUeWW.js} +1 -1
  187. package/dist/paths-BZK4Ct0I.js +81 -0
  188. package/dist/paths-DWYi0R_2.js +78 -0
  189. package/dist/{paths-Bkhd_qY8.js → paths-DdKf4lHp.js} +35 -5
  190. package/dist/paths-SFzVNGbc.js +78 -0
  191. package/dist/pi-auth-json-D7hGObyW.js +12 -0
  192. package/dist/pi-auth-json-DgvHjfJy.js +8 -0
  193. package/dist/pi-auth-json-la6lnAzY.js +79 -0
  194. package/dist/pi-auth-json-p3vsMR7W.js +79 -0
  195. package/dist/{pi-embedded-C1qKCgDT.js → pi-embedded-De6SeAPs.js} +9518 -9968
  196. package/dist/{pi-embedded-helpers-DtPn5RC8.js → pi-embedded-helpers-BrUBxrE2.js} +70 -10
  197. package/dist/{pi-embedded-helpers-DhEkdWB1.js → pi-embedded-helpers-D0mqOwwq.js} +821 -128
  198. package/dist/{pi-embedded-helpers-7AjuNiiJ.js → pi-embedded-helpers-DpJb0kUk.js} +69 -9
  199. package/dist/{pi-embedded-helpers-BTkXgwJ7.js → pi-embedded-helpers-ZI1UCSRM.js} +927 -136
  200. package/dist/{pi-tools.policy-gG96mWwA.js → pi-tools.policy-z5Wd_2WN.js} +4 -4
  201. package/dist/{plugin-auto-enable-D5ye7QnB.js → plugin-auto-enable-B8mX3rX3.js} +14 -5
  202. package/dist/{plugin-auto-enable-BROgMZcf.js → plugin-auto-enable-OO0eDINB.js} +14 -5
  203. package/dist/plugin-sdk/agents/apply-patch-update.d.ts +3 -1
  204. package/dist/plugin-sdk/agents/apply-patch.d.ts +11 -3
  205. package/dist/plugin-sdk/agents/auth-profiles/profiles.d.ts +5 -0
  206. package/dist/plugin-sdk/agents/auth-profiles.d.ts +1 -1
  207. package/dist/plugin-sdk/agents/bash-process-registry.d.ts +1 -0
  208. package/dist/plugin-sdk/agents/bash-tools.exec.d.ts +26 -0
  209. package/dist/plugin-sdk/agents/current-time.d.ts +17 -0
  210. package/dist/plugin-sdk/agents/huggingface-models.d.ts +17 -0
  211. package/dist/plugin-sdk/agents/models-config.providers.d.ts +10 -0
  212. package/dist/plugin-sdk/agents/openclaw-tools.d.ts +2 -0
  213. package/dist/plugin-sdk/agents/pi-auth-json.d.ts +14 -0
  214. package/dist/plugin-sdk/agents/pi-embedded-helpers/errors.d.ts +5 -1
  215. package/dist/plugin-sdk/agents/pi-embedded-helpers.d.ts +1 -1
  216. package/dist/plugin-sdk/agents/pi-embedded-runner/google.d.ts +1 -0
  217. package/dist/plugin-sdk/agents/pi-embedded-runner/run/images.d.ts +9 -4
  218. package/dist/plugin-sdk/agents/pi-embedded-runner/run/params.d.ts +2 -0
  219. package/dist/plugin-sdk/agents/pi-embedded-runner/run/payloads.d.ts +1 -0
  220. package/dist/plugin-sdk/agents/pi-embedded-runner/run/types.d.ts +2 -0
  221. package/dist/plugin-sdk/agents/pi-embedded-runner/types.d.ts +15 -0
  222. package/dist/plugin-sdk/agents/pi-embedded-subscribe.handlers.tools.d.ts +1 -1
  223. package/dist/plugin-sdk/agents/pi-embedded-subscribe.handlers.types.d.ts +2 -0
  224. package/dist/plugin-sdk/agents/pi-embedded-subscribe.types.d.ts +2 -0
  225. package/dist/plugin-sdk/agents/pi-tools.read.d.ts +8 -3
  226. package/dist/plugin-sdk/agents/sandbox/constants.d.ts +1 -1
  227. package/dist/plugin-sdk/agents/sandbox/docker.d.ts +14 -3
  228. package/dist/plugin-sdk/agents/sandbox/fs-bridge.d.ts +56 -0
  229. package/dist/plugin-sdk/agents/sandbox/types.d.ts +2 -0
  230. package/dist/plugin-sdk/agents/session-tool-result-guard-wrapper.d.ts +2 -0
  231. package/dist/plugin-sdk/agents/session-tool-result-guard.d.ts +4 -0
  232. package/dist/plugin-sdk/agents/subagent-registry.d.ts +3 -1
  233. package/dist/plugin-sdk/agents/tools/agent-step.d.ts +3 -0
  234. package/dist/plugin-sdk/agents/tools/browser-tool.schema.d.ts +2 -2
  235. package/dist/plugin-sdk/agents/tools/common.d.ts +4 -0
  236. package/dist/plugin-sdk/agents/tools/image-tool.d.ts +9 -1
  237. package/dist/plugin-sdk/agents/tools/web-search.d.ts +10 -1
  238. package/dist/plugin-sdk/agents/usage.d.ts +1 -0
  239. package/dist/plugin-sdk/auto-reply/reply/commands-status.d.ts +1 -0
  240. package/dist/plugin-sdk/auto-reply/reply/get-reply-directives.d.ts +1 -0
  241. package/dist/plugin-sdk/auto-reply/reply/memory-flush.d.ts +2 -2
  242. package/dist/plugin-sdk/auto-reply/reply/mentions.d.ts +1 -0
  243. package/dist/plugin-sdk/auto-reply/reply/model-selection.d.ts +3 -0
  244. package/dist/plugin-sdk/auto-reply/reply/reply-reference.d.ts +1 -1
  245. package/dist/plugin-sdk/auto-reply/reply/session-run-accounting.d.ts +11 -0
  246. package/dist/plugin-sdk/auto-reply/reply/session-usage.d.ts +8 -0
  247. package/dist/plugin-sdk/auto-reply/status.d.ts +2 -0
  248. package/dist/plugin-sdk/auto-reply/templating.d.ts +3 -0
  249. package/dist/plugin-sdk/auto-reply/thinking.d.ts +1 -1
  250. package/dist/plugin-sdk/auto-reply/types.d.ts +2 -0
  251. package/dist/plugin-sdk/browser/cdp.helpers.d.ts +2 -1
  252. package/dist/plugin-sdk/browser/client-actions-core.d.ts +1 -0
  253. package/dist/plugin-sdk/browser/control-auth.d.ts +13 -0
  254. package/dist/plugin-sdk/browser/pw-ai.d.ts +1 -1
  255. package/dist/plugin-sdk/browser/pw-session.d.ts +25 -0
  256. package/dist/plugin-sdk/browser/pw-tools-core.interactions.d.ts +2 -0
  257. package/dist/plugin-sdk/browser/routes/dispatcher.d.ts +1 -0
  258. package/dist/plugin-sdk/browser/routes/types.d.ts +5 -0
  259. package/dist/plugin-sdk/channels/plugins/onboarding/signal.d.ts +1 -0
  260. package/dist/plugin-sdk/channels/registry.d.ts +2 -2
  261. package/dist/plugin-sdk/cli/nodes-camera.d.ts +8 -2
  262. package/dist/plugin-sdk/cli/prompt.d.ts +1 -0
  263. package/dist/plugin-sdk/commands/agent/types.d.ts +2 -0
  264. package/dist/plugin-sdk/commands/onboard-helpers.d.ts +1 -0
  265. package/dist/plugin-sdk/commands/onboard-types.d.ts +9 -1
  266. package/dist/plugin-sdk/commands/signal-install.d.ts +20 -0
  267. package/dist/plugin-sdk/config/config.d.ts +1 -1
  268. package/dist/plugin-sdk/config/group-policy.d.ts +3 -0
  269. package/dist/plugin-sdk/config/merge-patch.d.ts +1 -0
  270. package/dist/plugin-sdk/config/sessions/paths.d.ts +14 -4
  271. package/dist/plugin-sdk/config/sessions/store.d.ts +8 -0
  272. package/dist/plugin-sdk/config/sessions/types.d.ts +8 -0
  273. package/dist/plugin-sdk/config/types.agents.d.ts +2 -0
  274. package/dist/plugin-sdk/config/types.channels.d.ts +2 -0
  275. package/dist/plugin-sdk/config/types.d.ts +1 -0
  276. package/dist/plugin-sdk/config/types.discord.d.ts +5 -0
  277. package/dist/plugin-sdk/config/types.gateway.d.ts +35 -0
  278. package/dist/plugin-sdk/config/types.hooks.d.ts +23 -1
  279. package/dist/plugin-sdk/config/types.irc.d.ts +96 -0
  280. package/dist/plugin-sdk/config/types.memory.d.ts +2 -0
  281. package/dist/plugin-sdk/config/types.openclaw.d.ts +6 -0
  282. package/dist/plugin-sdk/config/types.queue.d.ts +1 -0
  283. package/dist/plugin-sdk/config/types.slack.d.ts +2 -0
  284. package/dist/plugin-sdk/config/types.telegram.d.ts +2 -0
  285. package/dist/plugin-sdk/config/validation.d.ts +20 -0
  286. package/dist/plugin-sdk/config/zod-schema.agents.d.ts +1 -0
  287. package/dist/plugin-sdk/config/zod-schema.core.d.ts +2 -0
  288. package/dist/plugin-sdk/config/zod-schema.d.ts +193 -2
  289. package/dist/plugin-sdk/config/zod-schema.hooks.d.ts +3 -2
  290. package/dist/plugin-sdk/config/zod-schema.providers-core.d.ts +378 -0
  291. package/dist/plugin-sdk/config/zod-schema.providers.d.ts +176 -0
  292. package/dist/plugin-sdk/config/zod-schema.sensitive.d.ts +2 -0
  293. package/dist/plugin-sdk/config/zod-schema.session.d.ts +1 -0
  294. package/dist/plugin-sdk/cron/service/jobs.d.ts +8 -0
  295. package/dist/plugin-sdk/cron/service/state.d.ts +1 -0
  296. package/dist/plugin-sdk/cron/types.d.ts +2 -0
  297. package/dist/plugin-sdk/discord/monitor/allow-list.d.ts +15 -0
  298. package/dist/plugin-sdk/discord/send.types.d.ts +5 -0
  299. package/dist/plugin-sdk/gateway/auth-rate-limit.d.ts +59 -0
  300. package/dist/plugin-sdk/gateway/auth.d.ts +47 -0
  301. package/dist/plugin-sdk/gateway/net.d.ts +5 -0
  302. package/dist/plugin-sdk/gateway/protocol/index.d.ts +7 -7
  303. package/dist/plugin-sdk/gateway/protocol/schema/agent.d.ts +7 -1
  304. package/dist/plugin-sdk/gateway/protocol/schema/channels.d.ts +21 -0
  305. package/dist/plugin-sdk/gateway/protocol/schema/types.d.ts +3 -1
  306. package/dist/plugin-sdk/gateway/session-utils.fs.d.ts +3 -1
  307. package/dist/plugin-sdk/gateway/session-utils.types.d.ts +1 -0
  308. package/dist/plugin-sdk/imessage/send.d.ts +12 -0
  309. package/dist/plugin-sdk/index.js +2147 -900
  310. package/dist/plugin-sdk/infra/binaries.d.ts +3 -0
  311. package/dist/plugin-sdk/infra/brew.d.ts +8 -0
  312. package/dist/plugin-sdk/infra/heartbeat-active-hours.d.ts +5 -0
  313. package/dist/plugin-sdk/infra/heartbeat-runner.d.ts +1 -0
  314. package/dist/plugin-sdk/infra/heartbeat-wake.d.ts +8 -1
  315. package/dist/plugin-sdk/infra/net/fetch-guard.d.ts +1 -0
  316. package/dist/plugin-sdk/infra/net/ssrf.d.ts +1 -0
  317. package/dist/plugin-sdk/infra/outbound/message.d.ts +2 -0
  318. package/dist/plugin-sdk/infra/outbound/outbound-send-service.d.ts +2 -0
  319. package/dist/plugin-sdk/infra/session-cost-usage.d.ts +3 -0
  320. package/dist/plugin-sdk/infra/tailscale.d.ts +34 -0
  321. package/dist/plugin-sdk/infra/tmp-openclaw-dir.d.ts +10 -0
  322. package/dist/plugin-sdk/logging/console.d.ts +4 -0
  323. package/dist/plugin-sdk/logging/logger.d.ts +1 -1
  324. package/dist/plugin-sdk/logging/state.d.ts +1 -0
  325. package/dist/plugin-sdk/logging.d.ts +2 -2
  326. package/dist/plugin-sdk/markdown/ir.d.ts +1 -1
  327. package/dist/plugin-sdk/markdown/whatsapp.d.ts +14 -0
  328. package/dist/plugin-sdk/media/input-files.d.ts +5 -0
  329. package/dist/plugin-sdk/media/store.d.ts +10 -0
  330. package/dist/plugin-sdk/media-understanding/audio-preflight.d.ts +16 -0
  331. package/dist/plugin-sdk/media-understanding/types.d.ts +1 -0
  332. package/dist/plugin-sdk/memory/backend-config.d.ts +2 -1
  333. package/dist/plugin-sdk/memory/embedding-chunk-limits.d.ts +3 -0
  334. package/dist/plugin-sdk/memory/embedding-input-limits.d.ts +2 -0
  335. package/dist/plugin-sdk/memory/embedding-model-limits.d.ts +2 -0
  336. package/dist/plugin-sdk/memory/embeddings.d.ts +1 -0
  337. package/dist/plugin-sdk/memory/internal.d.ts +11 -0
  338. package/dist/plugin-sdk/memory/manager.d.ts +0 -6
  339. package/dist/plugin-sdk/memory/qmd-manager.d.ts +2 -0
  340. package/dist/plugin-sdk/memory/qmd-query-parser.d.ts +8 -0
  341. package/dist/plugin-sdk/memory/session-files.d.ts +2 -0
  342. package/dist/plugin-sdk/process/command-queue.d.ts +16 -0
  343. package/dist/plugin-sdk/providers/github-copilot-token.d.ts +3 -0
  344. package/dist/plugin-sdk/routing/resolve-route.d.ts +3 -1
  345. package/dist/plugin-sdk/security/external-content.d.ts +1 -1
  346. package/dist/plugin-sdk/security/secret-equal.d.ts +1 -0
  347. package/dist/plugin-sdk/sessions/input-provenance.d.ts +16 -0
  348. package/dist/plugin-sdk/signal/monitor/event-handler.types.d.ts +8 -0
  349. package/dist/plugin-sdk/signal/monitor/mentions.d.ts +2 -0
  350. package/dist/plugin-sdk/slack/monitor/commands.d.ts +5 -0
  351. package/dist/plugin-sdk/slack/monitor/media.d.ts +21 -0
  352. package/dist/plugin-sdk/slack/types.d.ts +1 -0
  353. package/dist/plugin-sdk/telegram/bot-message-context.d.ts +2 -1
  354. package/dist/plugin-sdk/telegram/fetch.d.ts +1 -0
  355. package/dist/plugin-sdk/telegram/monitor.d.ts +1 -0
  356. package/dist/plugin-sdk/telegram/send.d.ts +3 -0
  357. package/dist/plugin-sdk/tts/tts.d.ts +2 -2
  358. package/dist/plugin-sdk/utils/fetch-timeout.d.ts +2 -0
  359. package/dist/plugin-sdk/web/media.d.ts +12 -2
  360. package/dist/{plugins-CQw3z3Nw.js → plugins-CTjLu-z-.js} +4 -4
  361. package/dist/{plugins-B7F0Ly9G.js → plugins-CxrdL_IZ.js} +3 -3
  362. package/dist/{plugins-cli-CJ74eHvr.js → plugins-cli-CbX97Kvt.js} +259 -49
  363. package/dist/{plugins-cli-ubDwUAzK.js → plugins-cli-Dn9OeO53.js} +257 -47
  364. package/dist/{ports-kYsTYQdA.js → ports-C8YYHVlc.js} +2 -2
  365. package/dist/{program-1bQ15ivo.js → program-D-mNC0It.js} +86 -83
  366. package/dist/{progress-Da1ehW-x.js → progress-COHv-uNT.js} +1 -1
  367. package/dist/{progress-COzt9PNY.js → progress-DZb6yPcJ.js} +1 -1
  368. package/dist/{prompt-style-Dc0C5HC9.js → prompt-style-Cf1r1L6k.js} +1 -1
  369. package/dist/{prompt-style-DjZDxcFg.js → prompt-style-lSlXMhsd.js} +1 -1
  370. package/dist/{pw-ai-CQ4-gUNR.js → pw-ai-6GzTgK5g.js} +205 -32
  371. package/dist/{pw-ai-1NN0FrJb.js → pw-ai-C8YhJRaI.js} +207 -32
  372. package/dist/{pw-ai-qEMUq5Mt.js → pw-ai-CKGenizV.js} +203 -29
  373. package/dist/{pw-ai-IOqEXO1O.js → pw-ai-D7devT89.js} +206 -32
  374. package/dist/{qmd-manager-CEwp3el1.js → qmd-manager-CQzWovq-.js} +71 -90
  375. package/dist/{qmd-manager-D6N3qvQ5.js → qmd-manager-Cs8RJVQp.js} +73 -90
  376. package/dist/{qmd-manager-C48QzrRe.js → qmd-manager-DdgrQ2kc.js} +71 -88
  377. package/dist/{qmd-manager-DaUqCKB_.js → qmd-manager-dyIoOvKl.js} +73 -90
  378. package/dist/{register.subclis-Cm-VJ5nP.js → register.subclis-ifHtmF3e.js} +29 -29
  379. package/dist/{reply-CBs4e9Rm.js → reply-VIHqsQ-k.js} +7906 -8743
  380. package/dist/{routes-9ygR0GOk.js → routes-CaCvio4Q.js} +36 -15
  381. package/dist/{routes-BrWrBk2R.js → routes-Cpfxk96k.js} +36 -14
  382. package/dist/{rpc-Cjuz2Gv1.js → rpc-BhB01Bhj.js} +3 -3
  383. package/dist/{rpc-DhkLVY5H.js → rpc-C5WsS_Ne.js} +3 -3
  384. package/dist/{run-main-BlZ5l-X9.js → run-main-DVy6KJTe.js} +88 -85
  385. package/dist/runner-B7CKBC80.js +1800 -0
  386. package/dist/runner-BEy5ZGFv.js +1901 -0
  387. package/dist/runner-Bv0BmJPF.js +1800 -0
  388. package/dist/runner-ChqVEgPx.js +1901 -0
  389. package/dist/{sandbox-qt49csTr.js → sandbox-BAChxjC5.js} +627 -157
  390. package/dist/{sandbox-CPZiaKcS.js → sandbox-DNHDwHw8.js} +628 -158
  391. package/dist/{sandbox-cli-C6_iNuqO.js → sandbox-cli-9oq67QEg.js} +22 -22
  392. package/dist/{sandbox-cli-C_wK-KAE.js → sandbox-cli-BiNq9yUe.js} +22 -22
  393. package/dist/{security-cli-CTTD1vms.js → security-cli-CRg03hvq.js} +28 -28
  394. package/dist/{security-cli-DRpGF2Yc.js → security-cli-LmBBHnmh.js} +28 -28
  395. package/dist/{server-context-lyNcqJYD.js → server-context-FwqBRH3K.js} +10 -10
  396. package/dist/{server-context-39mkstUs.js → server-context-RY7lRaxl.js} +9 -9
  397. package/dist/{server-node-events-V_G9BRRw.js → server-node-events-BbHOZX3O.js} +48 -43
  398. package/dist/{server-node-events-o9G18PaE.js → server-node-events-CngNLVL-.js} +50 -45
  399. package/dist/{service-DOlJdIqe.js → service-BnqdBTAK.js} +8 -4
  400. package/dist/{service-DDPRbf8a.js → service-DZN7KRok.js} +8 -4
  401. package/dist/{service-audit-VDRrWefh.js → service-audit-0Eil3ISN.js} +4 -4
  402. package/dist/{service-audit-CVy00Ze_.js → service-audit-B8KIOe8A.js} +4 -4
  403. package/dist/{session-cost-usage-CcCEQNuc.js → session-cost-usage-B-tyjp76.js} +14 -14
  404. package/dist/{session-cost-usage-PvyVZz-g.js → session-cost-usage-BYUb7fov.js} +14 -14
  405. package/dist/{shared-BnpC3wMU.js → shared-BCdNboU1.js} +3 -3
  406. package/dist/{shared-CagUDdmp.js → shared-CsAwU6-q.js} +3 -3
  407. package/dist/{shared-BDk_zC9p.js → shared-Csn6DLBA.js} +5 -5
  408. package/dist/{shared-C92wo-6f.js → shared-DEanAgja.js} +4 -4
  409. package/dist/{skill-scanner-C_fQzVDu.js → skill-scanner-BrGkh5K7.js} +1 -1
  410. package/dist/{skill-scanner-DrVEHfC6.js → skill-scanner-CucvxYhu.js} +1 -1
  411. package/dist/{skills-Ccsv3IQq.js → skills-CE7by2IF.js} +151 -8
  412. package/dist/{skills-_eKGrw9z.js → skills-Dz15dAM4.js} +152 -9
  413. package/dist/{skills-cli-DqvLjooh.js → skills-cli-B5b75pDK.js} +13 -13
  414. package/dist/{skills-cli-DUncybht.js → skills-cli-CbCDrYwp.js} +13 -13
  415. package/dist/{skills-status-Cp2ZFhIx.js → skills-status-B99Us6yS.js} +2 -2
  416. package/dist/{skills-status-Ck0CCFZG.js → skills-status-ChM7JE47.js} +3 -3
  417. package/dist/{sqlite-DODNHWJb.js → sqlite-2UsPaJz5.js} +97 -2
  418. package/dist/{sqlite-cSdsHVEw.js → sqlite-CASnHrgX.js} +97 -1
  419. package/dist/{sqlite-Bwo2rASR.js → sqlite-CVWiMkGu.js} +97 -1
  420. package/dist/{sqlite-CpqIbY4-.js → sqlite-CcIWkGaM.js} +97 -1
  421. package/dist/{status-Bmx9_1C7.js → status-CKuX1-zb.js} +3 -3
  422. package/dist/{status-CBGgwlTW.js → status-Cm4q6o-I.js} +57 -49
  423. package/dist/{status-DkJgtvSz.js → status-DD2iqGc9.js} +4 -4
  424. package/dist/{subsystem-DPnkvS73.js → subsystem-DHfJG4gk.js} +73 -20
  425. package/dist/{system-cli-9fQ1uLiz.js → system-cli-BVJDR474.js} +87 -15
  426. package/dist/{system-cli-Gq8OWHFg.js → system-cli-C3Y_9VpI.js} +88 -16
  427. package/dist/{systemd-Pa7LURHB.js → systemd-DxddcFsa.js} +3 -3
  428. package/dist/{systemd-hints-zi4ohCOY.js → systemd-hints-BVLopJ9O.js} +1 -1
  429. package/dist/{systemd-linger-CDo2UbHM.js → systemd-linger-BThjV1Sr.js} +2 -2
  430. package/dist/{systemd-linger-6_naJcJp.js → systemd-linger-D3Va1Cv7.js} +2 -2
  431. package/dist/{systemd-BEWwfwn0.js → systemd-s3S2HVog.js} +3 -3
  432. package/dist/{table-Bb9gAVIp.js → table-BIk8Aan_.js} +2 -2
  433. package/dist/{table-cCoGqLsk.js → table-Bvka_vkc.js} +1 -1
  434. package/dist/{tool-display-DUVhO36P.js → tool-display-DbdMQFZx.js} +2 -2
  435. package/dist/{tool-display-DNOVCI6J.js → tool-display-kpW5Hg2z.js} +2 -2
  436. package/dist/{tui-DDVqLwqT.js → tui-B40Z2jMa.js} +120 -14
  437. package/dist/{tui-cli-CurbazQf.js → tui-cli-Bwa6K7xR.js} +28 -28
  438. package/dist/{tui-cli-BeN2K38I.js → tui-cli-DD6g7uZb.js} +27 -27
  439. package/dist/{tui-B9zLJxf6.js → tui-lFMZUnx6.js} +121 -13
  440. package/dist/{update-Ct9sqJC_.js → update-Bos8PPCG.js} +3 -3
  441. package/dist/{update--i077azM.js → update-Cg8MtrEr.js} +3 -3
  442. package/dist/{update-cli-CT5W0kpw.js → update-cli-CC-wTeje.js} +92 -73
  443. package/dist/{update-cli-C87lNK1S.js → update-cli-CULnXFL_.js} +91 -72
  444. package/dist/{update-runner-BIttRDyV.js → update-runner-BaLsla0c.js} +11 -11
  445. package/dist/{update-runner-xbeVkAD9.js → update-runner-Dbsdl5AU.js} +10 -10
  446. package/dist/{utils-Dk86IbEs.js → utils-BLJAc3ZV.js} +1 -1
  447. package/dist/{utils-BTaR--Ln.js → utils-BtIMES3N.js} +1 -1
  448. package/dist/{webhooks-cli-Db3zyJaw.js → webhooks-cli-ClHLUu_j.js} +21 -13
  449. package/dist/{webhooks-cli-DUUa8gVY.js → webhooks-cli-DVXr2uyN.js} +21 -13
  450. package/dist/{widearea-dns-BgYasW6m.js → widearea-dns-C4RnIR9O.js} +3 -3
  451. package/dist/{widearea-dns-CMIG6-74.js → widearea-dns-Ypwgjpsr.js} +3 -3
  452. package/dist/{ws-C0k_dhCP.js → ws-BcJt4pcg.js} +24 -2
  453. package/dist/{ws-DtDKpbLR.js → ws-MC-rTJLe.js} +24 -2
  454. package/dist/{ws-log-cMNgAyLy.js → ws-log-WrJ4QYu7.js} +1 -1
  455. package/dist/{ws-log-C6vm_XMA.js → ws-log-lip4ETlm.js} +2 -2
  456. package/dist/{wsl-rfIr_Sde.js → wsl-BvTIzy-8.js} +5 -3
  457. package/docs/assets/install-script.svg +1 -0
  458. package/docs/automation/hooks.md +1 -38
  459. package/docs/automation/webhook.md +52 -2
  460. package/docs/channels/discord.md +389 -381
  461. package/docs/channels/grammy.md +1 -1
  462. package/docs/channels/imessage.md +229 -218
  463. package/docs/channels/index.md +1 -0
  464. package/docs/channels/irc.md +234 -0
  465. package/docs/channels/msteams.md +2 -0
  466. package/docs/channels/pairing.md +1 -1
  467. package/docs/channels/slack.md +295 -415
  468. package/docs/channels/telegram.md +397 -460
  469. package/docs/channels/whatsapp.md +338 -310
  470. package/docs/ci.md +0 -12
  471. package/docs/cli/hooks.md +1 -14
  472. package/docs/cli/index.md +6 -1
  473. package/docs/cli/logs.md +4 -0
  474. package/docs/cli/onboard.md +33 -0
  475. package/docs/cli/plugins.md +20 -1
  476. package/docs/cli/security.md +2 -0
  477. package/docs/concepts/architecture.md +0 -16
  478. package/docs/concepts/memory.md +7 -4
  479. package/docs/concepts/model-providers.md +27 -0
  480. package/docs/concepts/session-tool.md +1 -0
  481. package/docs/concepts/system-prompt.md +13 -0
  482. package/docs/docs.json +18 -12
  483. package/docs/experiments/plans/browser-evaluate-cdp-refactor.md +229 -0
  484. package/docs/gateway/configuration-examples.md +9 -2
  485. package/docs/gateway/configuration-reference.md +2345 -0
  486. package/docs/gateway/configuration.md +338 -3297
  487. package/docs/gateway/index.md +162 -238
  488. package/docs/gateway/openai-http-api.md +1 -0
  489. package/docs/gateway/openresponses-http-api.md +16 -0
  490. package/docs/gateway/remote-gateway-readme.md +0 -16
  491. package/docs/gateway/security/index.md +4 -16
  492. package/docs/gateway/tools-invoke-http-api.md +26 -1
  493. package/docs/help/faq.md +9 -0
  494. package/docs/help/testing.md +11 -0
  495. package/docs/install/docker.md +18 -0
  496. package/docs/install/hetzner.md +21 -0
  497. package/docs/install/installer.md +20 -0
  498. package/docs/nodes/audio.md +19 -0
  499. package/docs/platforms/mac/release.md +7 -7
  500. package/docs/providers/glm.md +3 -3
  501. package/docs/providers/huggingface.md +209 -0
  502. package/docs/providers/index.md +3 -0
  503. package/docs/providers/litellm.md +153 -0
  504. package/docs/providers/together.md +2 -2
  505. package/docs/providers/vllm.md +92 -0
  506. package/docs/providers/zai.md +2 -2
  507. package/docs/reference/credits.md +4 -28
  508. package/docs/reference/test.md +2 -1
  509. package/docs/reference/token-use.md +1 -1
  510. package/docs/reference/transcript-hygiene.md +18 -0
  511. package/docs/start/getting-started.md +5 -0
  512. package/docs/start/onboarding-overview.md +51 -0
  513. package/docs/start/onboarding.md +1 -0
  514. package/docs/start/openclaw.md +0 -16
  515. package/docs/start/wizard-cli-automation.md +17 -0
  516. package/docs/start/wizard-cli-reference.md +12 -0
  517. package/docs/start/wizard.md +3 -1
  518. package/docs/tools/browser.md +6 -0
  519. package/docs/zh-CN/automation/hooks.md +1 -38
  520. package/docs/zh-CN/cli/hooks.md +1 -14
  521. package/extensions/bluebubbles/package.json +1 -1
  522. package/extensions/bluebubbles/src/monitor.test.ts +40 -28
  523. package/extensions/bluebubbles/src/monitor.ts +0 -4
  524. package/extensions/copilot-proxy/package.json +1 -1
  525. package/extensions/diagnostics-otel/package.json +10 -10
  526. package/extensions/discord/package.json +1 -1
  527. package/extensions/feishu/package.json +2 -5
  528. package/extensions/feishu/src/bot.checkBotMentioned.test.ts +64 -0
  529. package/extensions/feishu/src/bot.test.ts +265 -0
  530. package/extensions/feishu/src/bot.ts +73 -18
  531. package/extensions/feishu/src/channel.test.ts +48 -0
  532. package/extensions/feishu/src/channel.ts +1 -3
  533. package/extensions/feishu/src/config-schema.ts +6 -0
  534. package/extensions/feishu/src/docx.ts +14 -4
  535. package/extensions/feishu/src/media.test.ts +151 -0
  536. package/extensions/feishu/src/media.ts +27 -13
  537. package/extensions/feishu/src/reply-dispatcher.test.ts +116 -0
  538. package/extensions/feishu/src/reply-dispatcher.ts +124 -67
  539. package/extensions/feishu/src/streaming-card.ts +223 -0
  540. package/extensions/feishu/src/targets.test.ts +16 -0
  541. package/extensions/feishu/src/targets.ts +1 -1
  542. package/extensions/google-antigravity-auth/package.json +1 -1
  543. package/extensions/google-gemini-cli-auth/oauth.test.ts +4 -1
  544. package/extensions/google-gemini-cli-auth/package.json +1 -1
  545. package/extensions/googlechat/package.json +1 -1
  546. package/extensions/googlechat/src/channel.ts +3 -20
  547. package/extensions/googlechat/src/resolve-target.test.ts +138 -0
  548. package/extensions/imessage/package.json +1 -1
  549. package/extensions/irc/index.ts +17 -0
  550. package/extensions/irc/openclaw.plugin.json +9 -0
  551. package/extensions/irc/package.json +14 -0
  552. package/extensions/irc/src/accounts.ts +268 -0
  553. package/extensions/irc/src/channel.ts +367 -0
  554. package/extensions/irc/src/client.test.ts +43 -0
  555. package/extensions/irc/src/client.ts +439 -0
  556. package/extensions/irc/src/config-schema.test.ts +27 -0
  557. package/extensions/irc/src/config-schema.ts +97 -0
  558. package/extensions/irc/src/control-chars.ts +22 -0
  559. package/extensions/irc/src/inbound.ts +334 -0
  560. package/extensions/irc/src/monitor.test.ts +43 -0
  561. package/extensions/irc/src/monitor.ts +158 -0
  562. package/extensions/irc/src/normalize.test.ts +46 -0
  563. package/extensions/irc/src/normalize.ts +117 -0
  564. package/extensions/irc/src/onboarding.test.ts +118 -0
  565. package/extensions/irc/src/onboarding.ts +479 -0
  566. package/extensions/irc/src/policy.test.ts +132 -0
  567. package/extensions/irc/src/policy.ts +157 -0
  568. package/extensions/irc/src/probe.ts +64 -0
  569. package/extensions/irc/src/protocol.test.ts +44 -0
  570. package/extensions/irc/src/protocol.ts +169 -0
  571. package/extensions/irc/src/runtime.ts +14 -0
  572. package/extensions/irc/src/send.ts +99 -0
  573. package/extensions/irc/src/types.ts +94 -0
  574. package/extensions/line/package.json +1 -1
  575. package/extensions/llm-task/package.json +1 -1
  576. package/extensions/lobster/package.json +1 -1
  577. package/extensions/matrix/CHANGELOG.md +6 -0
  578. package/extensions/matrix/node_modules/.bin/markdown-it +2 -2
  579. package/extensions/matrix/node_modules/.bin/markdown-it.CMD +2 -2
  580. package/extensions/matrix/node_modules/.bin/markdown-it.ps1 +2 -2
  581. package/extensions/matrix/package.json +2 -2
  582. package/extensions/matrix/src/matrix/monitor/media.ts +4 -2
  583. package/extensions/mattermost/package.json +1 -1
  584. package/extensions/memory-core/package.json +1 -1
  585. package/extensions/memory-lancedb/index.ts +6 -2
  586. package/extensions/memory-lancedb/node_modules/.bin/openai +2 -2
  587. package/extensions/memory-lancedb/node_modules/.bin/openai.CMD +2 -2
  588. package/extensions/memory-lancedb/node_modules/.bin/openai.ps1 +2 -2
  589. package/extensions/memory-lancedb/package.json +2 -2
  590. package/extensions/minimax-portal-auth/index.ts +7 -5
  591. package/extensions/minimax-portal-auth/package.json +1 -1
  592. package/extensions/msteams/CHANGELOG.md +6 -0
  593. package/extensions/msteams/package.json +1 -1
  594. package/extensions/msteams/src/media-helpers.test.ts +9 -0
  595. package/extensions/msteams/src/media-helpers.ts +15 -1
  596. package/extensions/msteams/src/mentions.test.ts +235 -0
  597. package/extensions/msteams/src/mentions.ts +114 -0
  598. package/extensions/msteams/src/messenger.test.ts +81 -1
  599. package/extensions/msteams/src/messenger.ts +11 -2
  600. package/extensions/nextcloud-talk/package.json +1 -1
  601. package/extensions/nostr/CHANGELOG.md +6 -0
  602. package/extensions/nostr/package.json +2 -2
  603. package/extensions/open-prose/package.json +1 -1
  604. package/extensions/signal/package.json +1 -1
  605. package/extensions/slack/package.json +1 -1
  606. package/extensions/telegram/package.json +1 -1
  607. package/extensions/telegram/src/channel.ts +1 -0
  608. package/extensions/tlon/package.json +1 -1
  609. package/extensions/twitch/CHANGELOG.md +6 -0
  610. package/extensions/twitch/package.json +1 -1
  611. package/extensions/twitch/src/onboarding.test.ts +5 -0
  612. package/extensions/twitch/src/outbound.test.ts +17 -6
  613. package/extensions/twitch/src/outbound.ts +12 -10
  614. package/extensions/voice-call/CHANGELOG.md +6 -0
  615. package/extensions/voice-call/package.json +1 -1
  616. package/extensions/voice-call/src/media-stream.ts +7 -1
  617. package/extensions/voice-call/src/providers/twilio.test.ts +5 -3
  618. package/extensions/voice-call/src/providers/twilio.ts +12 -1
  619. package/extensions/whatsapp/package.json +1 -1
  620. package/extensions/whatsapp/src/channel.ts +6 -16
  621. package/extensions/whatsapp/src/resolve-target.test.ts +154 -0
  622. package/extensions/zalo/CHANGELOG.md +6 -0
  623. package/extensions/zalo/package.json +1 -1
  624. package/extensions/zalouser/CHANGELOG.md +6 -0
  625. package/extensions/zalouser/package.json +1 -1
  626. package/package.json +26 -22
  627. package/dist/auth-BcNHFK-i.js +0 -184
  628. package/dist/auth-DkjJ3pm-.js +0 -184
  629. package/dist/boolean-M-esQJt6.js +0 -30
  630. package/dist/bundled/soul-evil/HOOK.md +0 -71
  631. package/dist/bundled/soul-evil/handler.js +0 -194
  632. package/dist/cli-B631__JU.js +0 -89
  633. package/dist/cli-DVhCVZZ6.js +0 -86
  634. package/dist/config-CI7EpvlP.js +0 -15
  635. package/dist/constants-DuoCkWRh.js +0 -65
  636. package/dist/control-ui/assets/index-CnB9IO4a.js.map +0 -1
  637. package/dist/control-ui/assets/index-DWhx-9JL.css +0 -1
  638. package/dist/date-time-c6HTX6IW.js +0 -187
  639. package/dist/frontmatter-xwTm0734.js +0 -105
  640. package/dist/parse-DqAvJRIf.js +0 -23
  641. package/dist/parse-duration-De_tAQSe.js +0 -24
  642. package/dist/parse-timeout-DV8NQQWk.js +0 -16
  643. package/dist/paths-IivnSNkP.js +0 -51
  644. package/dist/paths-MnZaxqPw.js +0 -48
  645. package/dist/paths-uoGO2aiO.js +0 -48
  646. package/dist/pi-model-discovery-DzFOAbQt.js +0 -20
  647. package/dist/plugin-sdk/tui/tui-formatters.d.ts +0 -31
  648. package/dist/session-key-nXYQSv-a.js +0 -167
  649. package/dist/tailscale-DU6DgqVy.js +0 -225
  650. package/dist/tailscale-DzJUWmKf.js +0 -252
  651. package/dist/utils-dp_OM900.js +0 -476
  652. package/docs/hooks/soul-evil.md +0 -69
  653. package/docs/zh-CN/hooks/soul-evil.md +0 -72
  654. package/skills/local-places/SERVER_README.md +0 -101
  655. package/skills/local-places/SKILL.md +0 -102
  656. package/skills/local-places/pyproject.toml +0 -21
  657. package/skills/local-places/src/local_places/__init__.py +0 -2
  658. package/skills/local-places/src/local_places/google_places.py +0 -314
  659. package/skills/local-places/src/local_places/main.py +0 -65
  660. package/skills/local-places/src/local_places/schemas.py +0 -107
  661. /package/dist/{archive-CXhvR9nU.js → archive-aSMUcOc6.js} +0 -0
  662. /package/dist/{archive-D0z3LZDK.js → archive-beaSfAzA.js} +0 -0
  663. /package/dist/{brew-BIrWdDps.js → brew-DlQQMJ3n.js} +0 -0
  664. /package/dist/{brew-B7YK4ZoL.js → brew-ROHf0-Xp.js} +0 -0
  665. /package/dist/{cli-utils-PlLcDZlM.js → cli-utils-CRhVAaLV.js} +0 -0
  666. /package/dist/{cli-utils-R-ECs5cY.js → cli-utils-CodyYLHe.js} +0 -0
  667. /package/dist/{command-format-BUxhT1xL.js → command-format-qUVxzqYm.js} +0 -0
  668. /package/dist/{constants-CNTiY-ZN.js → constants-BvQ6S8j5.js} +0 -0
  669. /package/dist/{errors-D3tYRJWG.js → errors-B91HIDPD.js} +0 -0
  670. /package/dist/{errors-B0eT3jVv.js → errors-Bv81hF2P.js} +0 -0
  671. /package/dist/{errors-x4NYs-1P.js → errors-Cojm0Kl7.js} +0 -0
  672. /package/dist/{format-CaxeRcue.js → format-CL8VOhxX.js} +0 -0
  673. /package/dist/{format-DLOJPZmo.js → format-DcfK-dwd.js} +0 -0
  674. /package/dist/{format-duration-CEmFWLyX.js → format-duration--hQihAvf.js} +0 -0
  675. /package/dist/{format-duration-DCXJx2ba.js → format-duration-84n6_DgO.js} +0 -0
  676. /package/dist/{format-relative-79_Y1n2Y.js → format-relative-Cywx6ldk.js} +0 -0
  677. /package/dist/{format-relative-Db7eqEu8.js → format-relative-cegC_FF5.js} +0 -0
  678. /package/dist/{helpers-CQI-5xS9.js → helpers-8O7IVGO-.js} +0 -0
  679. /package/dist/{helpers-DdwqKAAS.js → helpers-ByYj2Aq5.js} +0 -0
  680. /package/dist/{helpers-CRzoyyXS.js → helpers-CUVSCDJV.js} +0 -0
  681. /package/dist/{helpers-C89IG08W.js → helpers-HyeZXsnu.js} +0 -0
  682. /package/dist/{is-main-qJ675wPV.js → is-main-B9A8S9YC.js} +0 -0
  683. /package/dist/{is-main-WWuz28Ip.js → is-main-BWoXGz7p.js} +0 -0
  684. /package/dist/{logging-BzvBIA3Y.js → logging-D-Jq2wIo.js} +0 -0
  685. /package/dist/{logging-CfEk_PnX.js → logging-fywhKCmE.js} +0 -0
  686. /package/dist/{parse-Cjiudy6x.js → parse-Bw0oH-rT.js} +0 -0
  687. /package/dist/{parse-log-line-CUrpqe1w.js → parse-log-line-BuRiE-Ij.js} +0 -0
  688. /package/dist/{parse-log-line-D2UGw0wR.js → parse-log-line-CfVgwy6x.js} +0 -0
  689. /package/dist/{parse-timeout-DFSPLxpY.js → parse-timeout-D1XX_zN_.js} +0 -0
  690. /package/dist/{pi-model-discovery-CV2V1HHz.js → pi-model-discovery-DqgqUyAv.js} +0 -0
  691. /package/dist/{pi-model-discovery-DzEIEgHL.js → pi-model-discovery-EwKVHlZB.js} +0 -0
  692. /package/dist/{prompts--d-6l5Ln.js → prompts-Bg96reub.js} +0 -0
  693. /package/dist/{prompts-CXLLIBwP.js → prompts-Dszjy1n_.js} +0 -0
  694. /package/dist/{redact-BRmQPYDR.js → redact-BIMJ3ntQ.js} +0 -0
  695. /package/dist/{redact-BHmk44DI.js → redact-BRsnXqwD.js} +0 -0
  696. /package/dist/{redact-DAKeu7PA.js → redact-UvkXqguc.js} +0 -0
  697. /package/dist/{status-Cv36yYdi.js → status-C_dMhoE0.js} +0 -0
  698. /package/dist/{status-Drziap9H.js → status-DCkF_L3U.js} +0 -0
  699. /package/dist/{systemd-hints-CH4pbCFD.js → systemd-hints-CXNtLw9Q.js} +0 -0
  700. /package/dist/{tailnet-CL5GtL7t.js → tailnet-DATIFSsY.js} +0 -0
  701. /package/dist/{tailnet-DGRSvYuQ.js → tailnet-uoFvUSsw.js} +0 -0
  702. /package/dist/{transcript-events-BlIONGVn.js → transcript-events-BHS7QoRl.js} +0 -0
  703. /package/dist/{transcript-events-C1hdue6u.js → transcript-events-Bp7fGnwv.js} +0 -0
  704. /package/dist/{transcript-events-CZ8CG4ht.js → transcript-events-Ch7wLX-j.js} +0 -0
  705. /package/dist/{usage-format-6Uar63S0.js → usage-format-Bhl_WCWP.js} +0 -0
  706. /package/dist/{usage-format-hd37en6b.js → usage-format-CpORtVCG.js} +0 -0
  707. /package/extensions/{feishu → irc}/node_modules/.bin/claw +0 -0
  708. /package/extensions/{feishu → irc}/node_modules/.bin/claw.CMD +0 -0
  709. /package/extensions/{feishu → irc}/node_modules/.bin/claw.ps1 +0 -0
  710. /package/extensions/{feishu → irc}/node_modules/.bin/moltbot +0 -0
  711. /package/extensions/{feishu → irc}/node_modules/.bin/moltbot.CMD +0 -0
  712. /package/extensions/{feishu → irc}/node_modules/.bin/moltbot.ps1 +0 -0
  713. /package/extensions/{feishu → irc}/node_modules/.bin/pigbot +0 -0
  714. /package/extensions/{feishu → irc}/node_modules/.bin/pigbot.CMD +0 -0
  715. /package/extensions/{feishu → irc}/node_modules/.bin/pigbot.ps1 +0 -0
@@ -1,94 +1,97 @@
1
- import { $ as DEFAULT_CHAT_CHANNEL, A as getChildLogger, B as resolveConfigPath, C as setVerbose, D as colorize, F as CONFIG_PATH, L as STATE_DIR, M as getResolvedLoggerSettings, O as isRich, R as isNixMode, U as resolveGatewayLockDir, W as resolveGatewayPort, X as resolveStateDir, Z as CHANNEL_IDS, dt as expandHomePrefix, j as getLogger, k as theme, l as setConsoleSubsystemFilter, n as isTruthyEnvValue, o as createSubsystemLogger, p as defaultRuntime, r as logAcceptedEnvOption, s as runtimeForLogger, st as getActivePluginRegistry, u as setConsoleTimestampPrefix } from "./entry.js";
2
- import { Ct as DEFAULT_CONTEXT_TOKENS, D as isCliProvider, E as getModelRefStatus, F as resolveHooksGmailModel, L as resolveThinkingDefault, N as resolveConfiguredModelRef, P as resolveDefaultModelForAgent, Tt as DEFAULT_PROVIDER, j as resolveAllowedModelRef, pt as normalizeSecretInput, wt as DEFAULT_MODEL } from "./auth-profiles-FJ3VY25a.js";
3
- import { t as formatCliCommand } from "./command-format-ayFsmwwz.js";
1
+ import { $ as CHANNEL_IDS, A as theme, G as resolveGatewayPort, I as CONFIG_PATH, M as getLogger, N as getResolvedLoggerSettings, O as colorize, R as STATE_DIR, V as resolveConfigPath, W as resolveGatewayLockDir, Z as resolveStateDir, j as getChildLogger, k as isRich, l as setConsoleSubsystemFilter, lt as getActivePluginRegistry, n as isTruthyEnvValue, o as createSubsystemLogger, p as defaultRuntime, pt as expandHomePrefix, r as logAcceptedEnvOption, s as runtimeForLogger, tt as DEFAULT_CHAT_CHANNEL, u as setConsoleTimestampPrefix, w as setVerbose, z as isNixMode } from "./entry.js";
2
+ import { At as DEFAULT_MODEL, D as getModelRefStatus, F as resolveDefaultModelForAgent, I as resolveHooksGmailModel, M as resolveAllowedModelRef, O as isCliProvider, P as resolveConfiguredModelRef, R as resolveThinkingDefault, jt as DEFAULT_PROVIDER, kt as DEFAULT_CONTEXT_TOKENS, mt as normalizeSecretInput } from "./auth-profiles-Cp9MtUdM.js";
3
+ import { t as formatCliCommand } from "./command-format-Bxe0mWee.js";
4
4
  import { _ as isCronRunSessionKey, 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 isSubagentSessionKey, y as parseAgentSessionKey } from "./session-key-DVvxnFKg.js";
5
- import { E as truncateUtf16Safe, S as shortenHomePath, n as clamp, s as ensureDir, t as CONFIG_DIR, u as isPlainObject, y as resolveUserPath } from "./utils-Dk86IbEs.js";
6
- import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-B8JKbXKW.js";
7
- import { t as resolveOpenClawPackageRoot } from "./openclaw-root-9ILYSmJ9.js";
8
- 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-D3me2AZa.js";
9
- import "./github-copilot-token-SLWintYd.js";
10
- import "./pi-model-discovery-DzEIEgHL.js";
11
- import { A as resolveAgentMaxConcurrent, E as applyLegacyMigrations, M as VERSION, a as parseConfigJson5, c as writeConfigFile, g as parseDurationMs, i as loadConfig, j as resolveSubagentMaxConcurrent, l as validateConfigObjectWithPlugins, n as migrateLegacyConfig, o as readConfigFileSnapshot, r as createConfigIO, s as resolveConfigSnapshotHash, u as OpenClawSchema } from "./config-fCnPoWjU.js";
12
- import { s as isTestDefaultMemorySlotDisabled } from "./manifest-registry-D5SiA3xq.js";
13
- import { n as movePathToTrash } from "./server-context-lyNcqJYD.js";
14
- import { n as pickPrimaryTailnetIPv6, t as pickPrimaryTailnetIPv4 } from "./tailnet-CL5GtL7t.js";
15
- import { c as resolveGatewayBindHost, i as isTrustedProxyAddress, l as resolveGatewayClientIp, n as isLoopbackAddress, r as isLoopbackHost, t as rawDataToString, u as resolveGatewayListenHosts } from "./ws-C0k_dhCP.js";
16
- import { c as ensurePortAvailable, d as formatPortDiagnostics, l as inspectPortUsage } from "./chrome-B2UjqY-9.js";
17
- import { a as isErrno, n as formatErrorMessage } from "./errors-x4NYs-1P.js";
18
- import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-Djd_WI3_.js";
19
- import { t as ensureOpenClawCliOnPath } from "./path-env-CXWUFfFv.js";
20
- import { i as enableTailscaleServe, n as disableTailscaleServe, o as getTailnetHostname, r as enableTailscaleFunnel, t as disableTailscaleFunnel } from "./tailscale-DU6DgqVy.js";
21
- import { i as resolveGatewayAuth, n as authorizeGatewayConnect, r as isLocalDirectRequest, t as assertGatewayAuthConfigured } from "./auth-BcNHFK-i.js";
22
- import { $ as validateNodePairRequestParams, A as validateCronStatusParams, At as parseSessionLabel, B as validateExecApprovalsNodeGetParams, C as validateConfigSetParams, Ct as validateWizardNextParams, D as validateCronRemoveParams, Dt as ErrorCodes, E as validateCronListParams, Et as PROTOCOL_VERSION, F as validateDeviceTokenRevokeParams, Ft as normalizeDevicePublicKeyBase64Url, G as validateNodeDescribeParams, H as validateExecApprovalsSetParams, I as validateDeviceTokenRotateParams, It as verifyDeviceSignature, J as validateNodeInvokeResultParams, K as validateNodeEventParams, L as validateExecApprovalRequestParams, M as validateDevicePairApproveParams, N as validateDevicePairListParams, Nt as deriveDeviceIdFromPublicKey, O as validateCronRunParams, Ot as errorShape, P as validateDevicePairRejectParams, Q as validateNodePairRejectParams, R as validateExecApprovalResolveParams, S as validateConfigSchemaParams, St as validateWizardCancelParams, T as validateCronAddParams, Tt as validateWizardStatusParams, U as validateLogsTailParams, V as validateExecApprovalsNodeSetParams, W as validateModelsListParams, X as validateNodePairApproveParams, Y as validateNodeListParams, Z as validateNodePairListParams, _ as validateChatInjectParams, _t as validateTalkModeParams, a as validateAgentWaitParams, at as validateSessionsCompactParams, b as validateConfigGetParams, bt as validateWebLoginStartParams, 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 buildDeviceAuthPayload, k as validateCronRunsParams, 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 validateUpdateRunParams, w as validateConnectParams, wt as validateWizardStartParams, x as validateConfigPatchParams, xt as validateWebLoginWaitParams, y as validateConfigApplyParams, yt as validateWakeParams, z as validateExecApprovalsGetParams } from "./client-DMloFP_O.js";
23
- import { n as callGateway, o as loadGatewayTlsRuntime$1 } from "./call-CD2IZCHT.js";
24
- import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, h as GATEWAY_CLIENT_NAMES, i as isGatewayMessageChannel, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-BlgPSDAh.js";
25
- import { t as formatDocsLink } from "./links-7M-j83As.js";
26
- import { r as buildChannelUiCatalog, t as applyPluginAutoEnable } from "./plugin-auto-enable-BROgMZcf.js";
27
- import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-B7F0Ly9G.js";
28
- import "./logging-CfEk_PnX.js";
29
- import "./accounts-DbzMEfKN.js";
30
- import { $ as setSkillsRemoteRegistry, $n as isSystemEventContextChanged, A as resolveHeartbeatVisibility, An as authorizeGatewaySigusr1Restart, Ar as formatUserTime, At as waitForEmbeddedPiRunEnd, B as createReplyDispatcher, Br as createInternalHookEvent, Bt as resolveAgentTimeoutMs, C as buildControlUiAvatarUrl, Cn as resolveOutboundTarget, Cr as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, Ct as extractImageContentFromSource, Dr as stripHeartbeatToken, Dt as stopSubagentsForRequester, En as resetDirectoryCache, Et as isAbortTrigger, Fn as consumeRestartSentinel, Fr as getMemorySearchManager, Ft as buildSafeExternalPrompt, Gn as normalizeOptionalAgentId, Gt as registerAgentRunContext, H as getCliSessionId, Hn as normalizeCronJobCreate, Hr as triggerInternalHook, Ht as emitAgentEvent, In as formatDoctorNonInteractiveHint, Ir as resolveMemoryBackendConfig, It as detectSuspiciousPatterns, J as getRemoteSkillEligibility, Jn as normalizeRequiredName, K as normalizeSendPolicy, Kn as normalizeOptionalText, Kt as runSubagentAnnounceFlow, L as getChannelActivity, Ln as formatRestartSentinelMessage, Lr as resolveAgentIdentity, Lt as getHookType, M as getLastHeartbeatEvent, Mn as isGatewaySigusr1RestartExternallyAllowed, Mr as resolveUserTimezone, N as onHeartbeatEvent, Nn as scheduleGatewaySigusr1Restart, Nt as registerUnhandledRejectionHandler, O as createReplyPrefixOptions, On as runWithModelFallback, Ot as runEmbeddedPiAgent, Pn as setGatewaySigusr1RestartPolicy, Pt as createOpenClawTools, Q as refreshRemoteNodeBins, Qn as enqueueSystemEvent, Rt as isExternalHookSession, S as CONTROL_UI_AVATAR_PREFIX, Sr as isDiagnosticsEnabled, St as extractFileContentFromSource, T as resolveAssistantAvatarUrl, Tt as formatZonedTimestamp, U as setCliSessionId, Un as normalizeCronJobPatch, Ut as getAgentRunContext, Vn as writeRestartSentinel, Vr as registerInternalHook, Vt as clearAgentRunContext, W as runCliAgent, Wn as inferLegacyName, Wt as onAgentEvent, X as recordRemoteNodeInfo, Xt as applyModelOverrideToSessionEntry, Y as primeRemoteSkillsCache, Yn as migrateLegacyCronPayload, Yt as loadModelCatalog, Z as refreshRemoteBinsForConnectedNodes, Zt as loadProviderUsageSummary, _t as DEFAULT_INPUT_MAX_REDIRECTS, an as resolveGatewaySessionStoreTarget, ar as OPENAI_TTS_VOICES, at as updatePairedNodeMetadata, br as startDiagnosticHeartbeat, bt as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, cn as capArrayByJsonBytes, cr as isTtsProviderConfigured, ct as registerSkillsChangeListener, d as handleReset, dn as resolveSessionTranscriptCandidates, dr as resolveTtsConfig, dt as parseVerboseOverride, et as approveNodePairing, fn as stripEnvelopeFromMessages, fr as resolveTtsPrefsPath, ft as DEFAULT_INPUT_FILE_MAX_BYTES, gn as normalizeGroupActivation, gr as textToSpeech, gt as DEFAULT_INPUT_IMAGE_MIMES, hn as clearSessionQueues, hr as setTtsProvider, ht as DEFAULT_INPUT_IMAGE_MAX_BYTES, in as loadSessionEntry, ir as OPENAI_TTS_MODELS, it as requestNodePairing, jn as consumeGatewaySigusr1RestartAuthorization, jr as resolveUserTimeFormat, k as buildHistoryContextFromEntries, kr as normalizePollInput, kt as abortEmbeddedPiRun, ln as readSessionMessages, lr as resolveTtsApiKey, mn as lookupContextTokens, mr as setTtsEnabled, mt as DEFAULT_INPUT_FILE_MIMES, n as handleSlackHttpRequest, nn as listSessionsFromStore, nt as rejectNodePairing, on as resolveSessionModelRef, or as getTtsProvider, ot as verifyNodeToken, pr as resolveTtsProviderOrder, pt as DEFAULT_INPUT_FILE_MAX_CHARS, q as resolveSendPolicy, qn as normalizePayloadToSystemText, qt as resolveAnnounceTargetFromKey, rn as loadCombinedSessionStoreForGateway, rr as getPluginToolMeta, rt as renamePairedNode, sn as archiveFileOnDisk, sr as isTtsEnabled, st as getSkillsSnapshotVersion, t as loadOpenClawPlugins, tn as listAgentsForGateway, tr as requestHeartbeatNow, tt as listNodePairing, un as readSessionPreviewItemsFromTranscript, ur as resolveTtsAutoMode, ut as applyVerboseOverride, vn as ensureOutboundSessionEntry, vr as setCommandLaneConcurrency, vt as DEFAULT_INPUT_PDF_MAX_PAGES, w as normalizeControlUiBasePath, wn as resolveSessionDeliveryTarget, wt as normalizeMimeList, xr as stopDiagnosticHeartbeat, xt as DEFAULT_INPUT_TIMEOUT_MS, yn as resolveOutboundSessionRoute, yr as CommandLane, yt as DEFAULT_INPUT_PDF_MAX_PIXELS, z as dispatchInboundMessage, zn as summarizeRestartSentinel, zr as clearInternalHooks, zt as initSubagentRegistry } from "./loader-BnzQyT31.js";
31
- import { n as withProgress } from "./progress-Da1ehW-x.js";
32
- import "./prompt-style-Dc0C5HC9.js";
33
- import "./note-Ci08TSbV.js";
34
- import { t as WizardCancelledError } from "./prompts-CXLLIBwP.js";
35
- import { t as resolveChannelDefaultAccountId } from "./helpers-DdwqKAAS.js";
36
- import "./onboard-channels-CtDnwaF5.js";
37
- import "./archive-D0z3LZDK.js";
38
- import "./skill-scanner-C_fQzVDu.js";
39
- import "./installs-89zeUsVn.js";
40
- import "./manager-CMFBuvVd.js";
41
- import { a as resolveSessionTranscriptsDirForAgent, n as resolveSessionFilePath, o as resolveStorePath, r as resolveSessionTranscriptPath } from "./paths-IivnSNkP.js";
42
- import "./sqlite-DODNHWJb.js";
43
- import { m as detectMime, r as saveMediaBuffer } from "./routes-9ygR0GOk.js";
44
- import { B as normalizeThinkLevel, F as formatXHighModelHint, H as normalizeVerboseLevel, P as formatThinkingLevels, R as normalizeElevatedLevel, V as normalizeUsageDisplay, W as supportsXHighThinking, z as normalizeReasoningLevel } from "./pi-embedded-helpers-7AjuNiiJ.js";
45
- import { o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads } from "./deliver-CD7-BhYD.js";
46
- import { $ as stripPluginOnlyAllowlist, F as resolveExplicitAgentSessionKey, I as resolveMainSessionKey, J as collectExplicitAllowlist, L as resolveMainSessionKeyFromConfig, P as resolveAgentMainSessionKey, Q as resolveToolProfilePolicy, S as mergeDeliveryContext, Y as expandPolicyWithPluginGroups, Z as normalizeToolName, b as deliveryContextFromSession, d as loadSessionStore, g as updateSessionStore, q as buildPluginToolGroups, w as normalizeSessionDeliveryFields, z as snapshotSessionOrigin } from "./sandbox-CPZiaKcS.js";
47
- import "./tui-formatters-BDP_71Xt.js";
48
- import { i as getMachineDisplayName, r as createBrowserRouteDispatcher } from "./wsl-rfIr_Sde.js";
49
- import { d as hasBinary, i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot } from "./skills-Ccsv3IQq.js";
50
- import "./image-ORs4LLwg.js";
51
- import { c as normalizeExecApprovals, g as saveExecApprovals, l as readExecApprovalsSnapshot, m as resolveExecApprovalsSocketPath, r as ensureExecApprovals } from "./exec-approvals-C9InMoAB.js";
52
- import "./redact-BHmk44DI.js";
53
- import "./tool-display-DUVhO36P.js";
54
- import { t as parseAbsoluteTimeMs } from "./parse-Cjiudy6x.js";
55
- import { n as resolveMessageChannelSelection } from "./channel-selection-DAHCVAX4.js";
56
- import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-CcCEQNuc.js";
57
- import { n as formatTokenCount, r as formatUsd } from "./usage-format-6Uar63S0.js";
58
- import { c as resolveSubagentToolPolicy, i as filterToolsByPolicy, o as resolveEffectiveToolPolicy, s as resolveGroupToolPolicy } from "./commands-BWHYcc83.js";
59
- import "./pairing-store-CO6umWFP.js";
60
- import "./login-qr-KUOtNJaQ.js";
61
- import { n as runCommandWithRuntime } from "./cli-utils-PlLcDZlM.js";
62
- import "./pairing-labels-CHxlh3tT.js";
63
- import { t as buildChannelAccountSnapshot } from "./status-Drziap9H.js";
64
- import "./channels-status-issues-DFhI_u0p.js";
65
- import "./register.subclis-Cm-VJ5nP.js";
66
- import "./completion-cli-D_0fx2O6.js";
67
- import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-BDQ_K8zf.js";
68
- import "./daemon-runtime-CMqH8BUE.js";
69
- import "./service-DDPRbf8a.js";
70
- import "./systemd-BEWwfwn0.js";
71
- import "./shared-BDk_zC9p.js";
72
- import { a as runDaemonStop, i as runDaemonStart, n as runDaemonStatus, o as runDaemonUninstall, r as runDaemonRestart, s as runDaemonInstall } from "./daemon-cli-Xe22v7lZ.js";
73
- import "./service-audit-CVy00Ze_.js";
74
- import "./table-cCoGqLsk.js";
75
- import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-CMIG6-74.js";
76
- import { a as toOptionString, i as parsePort$1, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-CagUDdmp.js";
77
- import { i as probeGateway } from "./audit-BFYy1qSw.js";
78
- import { g as discoverGatewayBeacons, n as installSkill } from "./onboard-skills-BnAcpzfX.js";
79
- import { a as resolveControlUiRootOverrideSync, c as getHealthSnapshot, d as runHeartbeatOnce, f as setHeartbeatsEnabled, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync, p as startHeartbeatRunner, s as formatHealthChannelLines } from "./health-format-DDYtlkB9.js";
80
- import { S as normalizeUpdateChannel, _ as resolveNpmChannelTag, h as compareSemverStrings, m as checkUpdateStatus, t as runGatewayUpdate, y as DEFAULT_PACKAGE_CHANNEL } from "./update-runner-BIttRDyV.js";
81
- import "./github-copilot-auth-CQIWc0hC.js";
82
- import "./logging-DuK6YXuK.js";
83
- import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-DRAVHSPg.js";
84
- import { a as findAgentEntryIndex, c as pruneAgentConfig, f as runOnboardingWizard, n as getStatusSummary, o as listAgentEntries, r as applyAgentConfig, s as loadAgentIdentity, u as loadAgentIdentityFromWorkspace } from "./status-CBGgwlTW.js";
85
- import { t as buildWorkspaceSkillStatus } from "./skills-status-Cp2ZFhIx.js";
86
- import "./tui-B9zLJxf6.js";
87
- import { i as setGatewayWsLogStyle, n as logWs, r as summarizeAgentEventForWsLog, t as formatForLog } from "./ws-log-cMNgAyLy.js";
88
- import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-CVNgLkXL.js";
89
- import { a as createOutboundSendDeps$1, i as resolveAgentOutboundTarget, r as resolveAgentDeliveryPlan, t as agentCommand } from "./agent-whSJT2Lk.js";
90
- import "./node-service-u8g85nD3.js";
91
- import { n as forceFreePortAndWait } from "./ports-kYsTYQdA.js";
5
+ import { E as truncateUtf16Safe, S as shortenHomePath, n as clamp, s as ensureDir, t as CONFIG_DIR, u as isPlainObject, y as resolveUserPath } from "./utils-BLJAc3ZV.js";
6
+ import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-DqZFMawz.js";
7
+ import { t as resolveOpenClawPackageRoot } from "./openclaw-root-BKsZvO6K.js";
8
+ 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-Bkr9fZtl.js";
9
+ import "./github-copilot-token-C9W4SY9o.js";
10
+ import "./pi-model-discovery-EhM2JAQo.js";
11
+ import { D as applyLegacyMigrations, E as applyMergePatch, M as resolveAgentMaxConcurrent, N as resolveSubagentMaxConcurrent, P as VERSION, a as parseConfigJson5, c as writeConfigFile, g as parseDurationMs, h as sensitive, i as loadConfig, l as validateConfigObjectWithPlugins, n as migrateLegacyConfig, o as readConfigFileSnapshot, r as createConfigIO, s as resolveConfigSnapshotHash, u as OpenClawSchema } from "./config-B8v0zg48.js";
12
+ import { c as isTestDefaultMemorySlotDisabled } from "./manifest-registry-CVsqjgX0.js";
13
+ import { n as movePathToTrash } from "./server-context-FwqBRH3K.js";
14
+ import { n as pickPrimaryTailnetIPv6, t as pickPrimaryTailnetIPv4 } from "./tailnet-uoFvUSsw.js";
15
+ import { a as isTrustedProxyAddress, d as resolveGatewayListenHosts, i as isPrivateOrLoopbackAddress, l as resolveGatewayBindHost, n as isLoopbackAddress, r as isLoopbackHost, t as rawDataToString, u as resolveGatewayClientIp } from "./ws-MC-rTJLe.js";
16
+ import { c as ensurePortAvailable, d as formatPortDiagnostics, l as inspectPortUsage } from "./chrome--Fe8F5Kf.js";
17
+ 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-CLhyWwAU.js";
18
+ import "./control-auth-BlWU-jBl.js";
19
+ import { a as isErrno, n as formatErrorMessage } from "./errors-Cojm0Kl7.js";
20
+ import { n as createBrowserControlContext, r as startBrowserControlServiceFromConfig } from "./control-service-COF59GQe.js";
21
+ import { t as ensureOpenClawCliOnPath } from "./path-env-OJAyUeWW.js";
22
+ import { $ as validateNodePairRequestParams, A as validateCronStatusParams, B as validateExecApprovalsNodeGetParams, Bt as verifyDeviceSignature, C as validateConfigSetParams, Ct as validateWizardCancelParams, D as validateCronRemoveParams, Dt as PROTOCOL_VERSION, E as validateCronListParams, Et as validateWizardStatusParams, F as validateDeviceTokenRevokeParams, Ft as buildDeviceAuthPayload, G as validateNodeDescribeParams, H as validateExecApprovalsSetParams, I as validateDeviceTokenRotateParams, J as validateNodeInvokeResultParams, K as validateNodeEventParams, L as validateExecApprovalRequestParams, Lt as deriveDeviceIdFromPublicKey, M as validateDevicePairApproveParams, N as validateDevicePairListParams, O as validateCronRunParams, Ot as ErrorCodes, P as validateDevicePairRejectParams, Pt as normalizeInputProvenance, Q as validateNodePairRejectParams, R as validateExecApprovalResolveParams, 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 normalizeDevicePublicKeyBase64Url } from "./client-BhZjzrH2.js";
23
+ import { n as callGateway, o as loadGatewayTlsRuntime$1 } from "./call-CjEdFGAf.js";
24
+ import { f as GATEWAY_CLIENT_CAPS, g as hasGatewayClientCap, h as GATEWAY_CLIENT_NAMES, i as isGatewayMessageChannel, l as normalizeMessageChannel, m as GATEWAY_CLIENT_MODES, n as isDeliverableMessageChannel, p as GATEWAY_CLIENT_IDS, r as isGatewayCliClient, s as isWebchatClient, t as INTERNAL_MESSAGE_CHANNEL } from "./message-channel-DWcu72r7.js";
25
+ import { t as formatDocsLink } from "./links-DpxpaKe1.js";
26
+ import { r as buildChannelUiCatalog, t as applyPluginAutoEnable } from "./plugin-auto-enable-OO0eDINB.js";
27
+ import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin } from "./plugins-CxrdL_IZ.js";
28
+ import "./logging-D-Jq2wIo.js";
29
+ import "./accounts-Bvh0DFxS.js";
30
+ import { $ as recordRemoteNodeInfo, $n as isSystemEventContextChanged, $t as loadCombinedSessionStoreForGateway, A as buildHistoryContextFromEntries, An as setGatewaySigusr1RestartPolicy, Ar as stripHeartbeatToken, At as stopSubagentsForRequester, B as getChannelActivity, Bn as inferLegacyName, Bt as getAgentRunContext, C as CONTROL_UI_AVATAR_PREFIX, Cr as startDiagnosticHeartbeat, Ct as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, Dn as consumeGatewaySigusr1RestartAuthorization, Dt as normalizeMimeList, E as resolveAssistantAvatarUrl, En as authorizeGatewaySigusr1Restart, Er as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, Et as extractImageContentFromSource, Fn as summarizeRestartSentinel, Ft as createOpenClawTools, G as getCliSessionId, Gn as migrateLegacyCronPayload, H as dispatchInboundMessage, Hn as normalizeOptionalText, Hr as createInternalHookEvent, Ht as registerAgentRunContext, Ir as ToolInputError, It as initSubagentRegistry, Jn as buildSafeExternalPrompt, K as setCliSessionId, Kt as applyModelOverrideToSessionEntry, L as resolveCronStyleNow, Ln as writeRestartSentinel, Lr as getMemorySearchManager, Lt as resolveAgentTimeoutMs, Mn as formatDoctorNonInteractiveHint, Mr as normalizePollInput, Mt as abortEmbeddedPiRun, N as getLastHeartbeatEvent, Nn as formatRestartSentinelMessage, Nr as resolveUserTimezone, Nt as waitForEmbeddedPiRunEnd, On as isGatewaySigusr1RestartExternallyAllowed, Ot as formatZonedTimestamp, P as onHeartbeatEvent, Q as primeRemoteSkillsCache, Qn as enqueueSystemEvent, Qt as listSessionsFromStore, Rn as normalizeCronJobCreate, Rr as resolveMemoryBackendConfig, Rt as clearAgentRunContext, Sn as resetDirectoryCache, Sr as CommandLane, St as DEFAULT_INPUT_PDF_MAX_PIXELS, T as normalizeControlUiBasePath, Tr as isDiagnosticsEnabled, Tt as extractFileContentFromSource, U as createReplyDispatcher, Un as normalizePayloadToSystemText, Ur as registerInternalHook, Ut as runSubagentAnnounceFlow, Vn as normalizeOptionalAgentId, Vr as clearInternalHooks, Vt as onAgentEvent, Wn as normalizeRequiredName, Wr as triggerInternalHook, Wt as resolveAnnounceTargetFromKey, X as resolveSendPolicy, Xn as getHookType, Y as normalizeSendPolicy, Yn as detectSuspiciousPatterns, Z as getRemoteSkillEligibility, Zn as isExternalHookSession, Zt as listAgentsForGateway, _t as DEFAULT_INPUT_FILE_MIMES, an as readSessionMessages, ar as OPENAI_TTS_VOICES, at as rejectNodePairing, bn as resolveSessionDeliveryTarget, br as setCommandLaneConcurrency, bt as DEFAULT_INPUT_MAX_REDIRECTS, cn as stripEnvelopeFromMessages, cr as isTtsProviderConfigured, ct as updatePairedNodeMetadata, d as handleReset, dn as clearSessionQueues, dr as resolveTtsConfig, dt as registerSkillsChangeListener, en as loadSessionEntry, et as refreshRemoteBinsForConnectedNodes, fn as normalizeGroupActivation, fr as resolveTtsPrefsPath, gr as textToSpeech, gt as DEFAULT_INPUT_FILE_MAX_CHARS, hn as resolveOutboundSessionRoute, hr as setTtsProvider, ht as DEFAULT_INPUT_FILE_MAX_BYTES, in as capArrayByJsonBytes, ir as OPENAI_TTS_MODELS, it as listNodePairing, j as resolveHeartbeatVisibility, jn as consumeRestartSentinel, jt as runEmbeddedPiAgent, k as createReplyPrefixOptions, kn as scheduleGatewaySigusr1Restart, kt as isAbortTrigger, lr as resolveTtsApiKey, lt as verifyNodeToken, mn as ensureOutboundSessionEntry, mr as setTtsEnabled, mt as parseVerboseOverride, n as handleSlackHttpRequest, nn as resolveSessionModelRef, nt as setSkillsRemoteRegistry, on as readSessionPreviewItemsFromTranscript, or as getTtsProvider, ot as renamePairedNode, pr as resolveTtsProviderOrder, pt as applyVerboseOverride, q as runCliAgent, qt as loadProviderUsageSummary, rn as archiveFileOnDisk, rr as getPluginToolMeta, rt as approveNodePairing, sn as resolveSessionTranscriptCandidates, sr as isTtsEnabled, st as requestNodePairing, t as loadOpenClawPlugins, tn as resolveGatewaySessionStoreTarget, tr as requestHeartbeatNow, tt as refreshRemoteNodeBins, un as lookupContextTokens, ur as resolveTtsAutoMode, ut as getSkillsSnapshotVersion, vr as getActiveTaskCount, vt as DEFAULT_INPUT_IMAGE_MAX_BYTES, w as buildControlUiAvatarUrl, wn as runWithModelFallback, wr as stopDiagnosticHeartbeat, wt as DEFAULT_INPUT_TIMEOUT_MS, xr as waitForActiveTasks, xt as DEFAULT_INPUT_PDF_MAX_PAGES, yn as resolveOutboundTarget, yt as DEFAULT_INPUT_IMAGE_MIMES, zn as normalizeCronJobPatch, zr as resolveAgentIdentity, zt as emitAgentEvent } from "./loader-CS-5lMQa.js";
31
+ import { n as withProgress } from "./progress-COHv-uNT.js";
32
+ import "./prompt-style-Cf1r1L6k.js";
33
+ import "./note-Duiadw1g.js";
34
+ import { t as WizardCancelledError } from "./prompts-Dszjy1n_.js";
35
+ import { t as resolveChannelDefaultAccountId } from "./helpers-ByYj2Aq5.js";
36
+ import "./onboard-channels-C5uL3i8d.js";
37
+ import "./archive-aSMUcOc6.js";
38
+ import "./skill-scanner-BrGkh5K7.js";
39
+ import "./installs-z69au9Te.js";
40
+ import { J as getGlobalHookRunner, o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads } from "./deliver-BHNoC9Yk.js";
41
+ import "./manager-ChW0jk7T.js";
42
+ import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-BZK4Ct0I.js";
43
+ import "./sqlite-2UsPaJz5.js";
44
+ import "./redact-UvkXqguc.js";
45
+ import { p as detectMime, r as saveMediaBuffer } from "./routes-CaCvio4Q.js";
46
+ import { B as normalizeElevatedLevel, H as normalizeThinkLevel, I as formatThinkingLevels, K as supportsXHighThinking, L as formatXHighModelHint, U as normalizeUsageDisplay, V as normalizeReasoningLevel, W as normalizeVerboseLevel } from "./pi-embedded-helpers-DpJb0kUk.js";
47
+ import "./fetch-wuOZDzdT.js";
48
+ import { $ as normalizeToolName, I as resolveAgentMainSessionKey, L as resolveExplicitAgentSessionKey, R as resolveMainSessionKey, S as mergeDeliveryContext, V as snapshotSessionOrigin, X as collectExplicitAllowlist, Y as buildPluginToolGroups, Z as expandPolicyWithPluginGroups, b as deliveryContextFromSession, d as loadSessionStore, et as resolveToolProfilePolicy, g as updateSessionStore, tt as stripPluginOnlyAllowlist, w as normalizeSessionDeliveryFields, z as resolveMainSessionKeyFromConfig } from "./sandbox-DNHDwHw8.js";
49
+ import "./commands-registry-6NUFrejL.js";
50
+ import { i as getMachineDisplayName, r as createBrowserRouteDispatcher } from "./wsl-BvTIzy-8.js";
51
+ import { h as hasBinary, i as loadWorkspaceSkillEntries, r as buildWorkspaceSkillSnapshot } from "./skills-CE7by2IF.js";
52
+ import "./image-D-5pUELC.js";
53
+ import { c as normalizeExecApprovals, g as saveExecApprovals, l as readExecApprovalsSnapshot, m as resolveExecApprovalsSocketPath, r as ensureExecApprovals } from "./exec-approvals-C67V-ljH.js";
54
+ import "./nodes-screen-CVL9363A.js";
55
+ import "./tool-display-DbdMQFZx.js";
56
+ import { t as parseAbsoluteTimeMs } from "./parse-Bw0oH-rT.js";
57
+ import { n as resolveMessageChannelSelection } from "./channel-selection-BaW1xXEa.js";
58
+ import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-B-tyjp76.js";
59
+ import { n as formatTokenCount, r as formatUsd } from "./usage-format-Bhl_WCWP.js";
60
+ import { _ as loadModelCatalog, h as registerUnhandledRejectionHandler } from "./runner-BEy5ZGFv.js";
61
+ import { c as resolveSubagentToolPolicy, i as filterToolsByPolicy, o as resolveEffectiveToolPolicy, s as resolveGroupToolPolicy } from "./commands-gOiRcfoU.js";
62
+ import "./pairing-store-Dz-ArTQS.js";
63
+ import "./login-qr-BsYM2E1y.js";
64
+ import { n as runCommandWithRuntime } from "./cli-utils-CodyYLHe.js";
65
+ import "./pairing-labels-Dt2vXyI7.js";
66
+ import { t as buildChannelAccountSnapshot } from "./status-DCkF_L3U.js";
67
+ import "./channels-status-issues-kb-M2Fi0.js";
68
+ import "./register.subclis-ifHtmF3e.js";
69
+ import "./completion-cli-Drks7xRK.js";
70
+ import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-CP0dcOgD.js";
71
+ import "./daemon-runtime-ZWXvLDxx.js";
72
+ import "./service-DZN7KRok.js";
73
+ import "./systemd-s3S2HVog.js";
74
+ import "./shared-Csn6DLBA.js";
75
+ import { a as runDaemonStop, i as runDaemonStart, n as runDaemonStatus, o as runDaemonUninstall, r as runDaemonRestart, s as runDaemonInstall } from "./daemon-cli-BLbzcTuD.js";
76
+ import "./service-audit-B8KIOe8A.js";
77
+ import "./table-Bvka_vkc.js";
78
+ import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-C4RnIR9O.js";
79
+ import { a as toOptionString, i as parsePort$1, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-CsAwU6-q.js";
80
+ import { a as resolveNodeCommandAllowlist, i as isNodeCommandAllowed, o as probeGateway } from "./audit-BYfhZ7LA.js";
81
+ import { S as discoverGatewayBeacons, n as installSkill } from "./onboard-skills-BFxdI1Y1.js";
82
+ import { a as resolveControlUiRootOverrideSync, c as getHealthSnapshot, d as runHeartbeatOnce, f as setHeartbeatsEnabled, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync, p as startHeartbeatRunner, s as formatHealthChannelLines } from "./health-format-Nd0jcoqM.js";
83
+ import { S as normalizeUpdateChannel, _ as resolveNpmChannelTag, h as compareSemverStrings, m as checkUpdateStatus, t as runGatewayUpdate, y as DEFAULT_PACKAGE_CHANNEL } from "./update-runner-BaLsla0c.js";
84
+ import "./github-copilot-auth-BUqfX7hG.js";
85
+ import "./logging-B3KnAryz.js";
86
+ import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-BbIz0zmm.js";
87
+ import { a as findAgentEntryIndex, c as pruneAgentConfig, f as runOnboardingWizard, n as getStatusSummary, o as listAgentEntries, r as applyAgentConfig, s as loadAgentIdentity, u as loadAgentIdentityFromWorkspace } from "./status-Cm4q6o-I.js";
88
+ import { t as buildWorkspaceSkillStatus } from "./skills-status-B99Us6yS.js";
89
+ import "./tui-B40Z2jMa.js";
90
+ import { i as setGatewayWsLogStyle, n as logWs, r as summarizeAgentEventForWsLog, t as formatForLog } from "./ws-log-WrJ4QYu7.js";
91
+ import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-HvKMdooP.js";
92
+ import { a as createOutboundSendDeps$1, i as resolveAgentOutboundTarget, r as resolveAgentDeliveryPlan, t as agentCommand } from "./agent-DjZxytiC.js";
93
+ import "./node-service-C7f_uvx9.js";
94
+ import { n as forceFreePortAndWait } from "./ports-C8YYHVlc.js";
92
95
  import { spawn, spawnSync } from "node:child_process";
93
96
  import path from "node:path";
94
97
  import os from "node:os";
@@ -862,7 +865,7 @@ function truncateCloseReason(reason, maxBytes = CLOSE_REASON_MAX_BYTES) {
862
865
 
863
866
  //#endregion
864
867
  //#region src/infra/exec-approval-forwarder.ts
865
- const log$3 = createSubsystemLogger("gateway/exec-approvals");
868
+ const log$5 = createSubsystemLogger("gateway/exec-approvals");
866
869
  const DEFAULT_MODE = "session";
867
870
  function normalizeMode(mode) {
868
871
  return mode ?? DEFAULT_MODE;
@@ -977,7 +980,7 @@ async function deliverToTargets(params) {
977
980
  payloads: [{ text: params.text }]
978
981
  });
979
982
  } catch (err) {
980
- log$3.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
983
+ log$5.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
981
984
  }
982
985
  });
983
986
  await Promise.allSettled(deliveries);
@@ -1204,6 +1207,10 @@ const BASE_RELOAD_RULES = [
1204
1207
  }
1205
1208
  ];
1206
1209
  const BASE_RELOAD_RULES_TAIL = [
1210
+ {
1211
+ prefix: "meta",
1212
+ kind: "none"
1213
+ },
1207
1214
  {
1208
1215
  prefix: "identity",
1209
1216
  kind: "none"
@@ -2100,10 +2107,6 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
2100
2107
  const last = agentRunSeq.get(evt.runId) ?? 0;
2101
2108
  const isToolEvent = evt.stream === "tool";
2102
2109
  const toolVerbose = isToolEvent ? resolveToolVerboseLevel(evt.runId, sessionKey) : "off";
2103
- if (isToolEvent && toolVerbose === "off") {
2104
- agentRunSeq.set(evt.runId, evt.seq);
2105
- return;
2106
- }
2107
2110
  const toolPayload = isToolEvent && toolVerbose !== "full" ? (() => {
2108
2111
  const data = evt.data ? { ...evt.data } : {};
2109
2112
  delete data.result;
@@ -2135,7 +2138,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
2135
2138
  } else broadcast("agent", agentPayload);
2136
2139
  const lifecyclePhase = evt.stream === "lifecycle" && typeof evt.data?.phase === "string" ? evt.data.phase : null;
2137
2140
  if (sessionKey) {
2138
- nodeSendToSession(sessionKey, "agent", isToolEvent ? toolPayload : agentPayload);
2141
+ if (!isToolEvent || toolVerbose !== "off") nodeSendToSession(sessionKey, "agent", isToolEvent ? toolPayload : agentPayload);
2139
2142
  if (!isAborted && evt.stream === "assistant" && typeof evt.data?.text === "string") emitChatDelta(sessionKey, clientRunId, evt.seq, evt.data.text);
2140
2143
  else if (!isAborted && (lifecyclePhase === "end" || lifecyclePhase === "error")) if (chatLink) {
2141
2144
  const finished = chatRunState.registry.shift(evt.runId);
@@ -2168,7 +2171,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
2168
2171
  * Automatically starts `gog gmail watch serve` when the gateway starts,
2169
2172
  * if hooks.gmail is configured with an account.
2170
2173
  */
2171
- const log$2 = createSubsystemLogger("gmail-watcher");
2174
+ const log$4 = createSubsystemLogger("gmail-watcher");
2172
2175
  const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
2173
2176
  function isAddressInUseError(line) {
2174
2177
  return ADDRESS_IN_USE_RE.test(line);
@@ -2192,13 +2195,13 @@ async function startGmailWatch(cfg) {
2192
2195
  const result = await runCommandWithTimeout(args, { timeoutMs: 12e4 });
2193
2196
  if (result.code !== 0) {
2194
2197
  const message = result.stderr || result.stdout || "gog watch start failed";
2195
- log$2.error(`watch start failed: ${message}`);
2198
+ log$4.error(`watch start failed: ${message}`);
2196
2199
  return false;
2197
2200
  }
2198
- log$2.info(`watch started for ${cfg.account}`);
2201
+ log$4.info(`watch started for ${cfg.account}`);
2199
2202
  return true;
2200
2203
  } catch (err) {
2201
- log$2.error(`watch start error: ${String(err)}`);
2204
+ log$4.error(`watch start error: ${String(err)}`);
2202
2205
  return false;
2203
2206
  }
2204
2207
  }
@@ -2207,7 +2210,7 @@ async function startGmailWatch(cfg) {
2207
2210
  */
2208
2211
  function spawnGogServe(cfg) {
2209
2212
  const args = buildGogWatchServeArgs(cfg);
2210
- log$2.info(`starting gog ${args.join(" ")}`);
2213
+ log$4.info(`starting gog ${args.join(" ")}`);
2211
2214
  let addressInUse = false;
2212
2215
  const child = spawn("gog", args, {
2213
2216
  stdio: [
@@ -2219,25 +2222,25 @@ function spawnGogServe(cfg) {
2219
2222
  });
2220
2223
  child.stdout?.on("data", (data) => {
2221
2224
  const line = data.toString().trim();
2222
- if (line) log$2.info(`[gog] ${line}`);
2225
+ if (line) log$4.info(`[gog] ${line}`);
2223
2226
  });
2224
2227
  child.stderr?.on("data", (data) => {
2225
2228
  const line = data.toString().trim();
2226
2229
  if (!line) return;
2227
2230
  if (isAddressInUseError(line)) addressInUse = true;
2228
- log$2.warn(`[gog] ${line}`);
2231
+ log$4.warn(`[gog] ${line}`);
2229
2232
  });
2230
2233
  child.on("error", (err) => {
2231
- log$2.error(`gog process error: ${String(err)}`);
2234
+ log$4.error(`gog process error: ${String(err)}`);
2232
2235
  });
2233
2236
  child.on("exit", (code, signal) => {
2234
2237
  if (shuttingDown) return;
2235
2238
  if (addressInUse) {
2236
- log$2.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set OPENCLAW_SKIP_GMAIL_WATCHER=1 or stop the other process.");
2239
+ log$4.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set OPENCLAW_SKIP_GMAIL_WATCHER=1 or stop the other process.");
2237
2240
  watcherProcess = null;
2238
2241
  return;
2239
2242
  }
2240
- log$2.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
2243
+ log$4.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
2241
2244
  watcherProcess = null;
2242
2245
  setTimeout(() => {
2243
2246
  if (shuttingDown || !currentConfig) return;
@@ -2277,15 +2280,15 @@ async function startGmailWatcher(cfg) {
2277
2280
  port: runtimeConfig.serve.port,
2278
2281
  target: runtimeConfig.tailscale.target
2279
2282
  });
2280
- log$2.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
2283
+ log$4.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
2281
2284
  } catch (err) {
2282
- log$2.error(`tailscale setup failed: ${String(err)}`);
2285
+ log$4.error(`tailscale setup failed: ${String(err)}`);
2283
2286
  return {
2284
2287
  started: false,
2285
2288
  reason: `tailscale setup failed: ${String(err)}`
2286
2289
  };
2287
2290
  }
2288
- if (!await startGmailWatch(runtimeConfig)) log$2.warn("gmail watch start failed, but continuing with serve");
2291
+ if (!await startGmailWatch(runtimeConfig)) log$4.warn("gmail watch start failed, but continuing with serve");
2289
2292
  shuttingDown = false;
2290
2293
  watcherProcess = spawnGogServe(runtimeConfig);
2291
2294
  const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
@@ -2293,7 +2296,7 @@ async function startGmailWatcher(cfg) {
2293
2296
  if (shuttingDown) return;
2294
2297
  startGmailWatch(runtimeConfig);
2295
2298
  }, renewMs);
2296
- log$2.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
2299
+ log$4.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
2297
2300
  return { started: true };
2298
2301
  }
2299
2302
  /**
@@ -2306,7 +2309,7 @@ async function stopGmailWatcher() {
2306
2309
  renewInterval = null;
2307
2310
  }
2308
2311
  if (watcherProcess) {
2309
- log$2.info("stopping gmail watcher");
2312
+ log$4.info("stopping gmail watcher");
2310
2313
  watcherProcess.kill("SIGTERM");
2311
2314
  await new Promise((resolve) => {
2312
2315
  const timeout = setTimeout(() => {
@@ -2321,7 +2324,7 @@ async function stopGmailWatcher() {
2321
2324
  watcherProcess = null;
2322
2325
  }
2323
2326
  currentConfig = null;
2324
- log$2.info("gmail watcher stopped");
2327
+ log$4.info("gmail watcher stopped");
2325
2328
  }
2326
2329
 
2327
2330
  //#endregion
@@ -2551,6 +2554,8 @@ function resolveCronSession(params) {
2551
2554
  thinkingLevel: entry?.thinkingLevel,
2552
2555
  verboseLevel: entry?.verboseLevel,
2553
2556
  model: entry?.model,
2557
+ modelOverride: entry?.modelOverride,
2558
+ providerOverride: entry?.providerOverride,
2554
2559
  contextTokens: entry?.contextTokens,
2555
2560
  sendPolicy: entry?.sendPolicy,
2556
2561
  lastChannel: entry?.lastChannel,
@@ -2584,9 +2589,8 @@ async function runCronIsolatedAgentTurn(params) {
2584
2589
  const defaultAgentId = resolveDefaultAgentId(params.cfg);
2585
2590
  const requestedAgentId = typeof params.agentId === "string" && params.agentId.trim() ? params.agentId : typeof params.job.agentId === "string" && params.job.agentId.trim() ? params.job.agentId : void 0;
2586
2591
  const normalizedRequested = requestedAgentId ? normalizeAgentId(requestedAgentId) : void 0;
2587
- const agentConfigOverride = normalizedRequested ? resolveAgentConfig(params.cfg, normalizedRequested) : void 0;
2588
- const { model: overrideModel, ...agentOverrideRest } = agentConfigOverride ?? {};
2589
- const agentId = agentConfigOverride ? normalizedRequested ?? defaultAgentId : defaultAgentId;
2592
+ const { model: overrideModel, ...agentOverrideRest } = (normalizedRequested ? resolveAgentConfig(params.cfg, normalizedRequested) : void 0) ?? {};
2593
+ const agentId = normalizedRequested ?? defaultAgentId;
2590
2594
  const agentCfg = Object.assign({}, params.cfg.agents?.defaults, agentOverrideRest);
2591
2595
  if (typeof overrideModel === "string") agentCfg.model = { primary: overrideModel };
2592
2596
  else if (overrideModel) agentCfg.model = overrideModel;
@@ -2618,6 +2622,7 @@ async function runCronIsolatedAgentTurn(params) {
2618
2622
  return catalog;
2619
2623
  };
2620
2624
  const isGmailHook = baseSessionKey.startsWith("hook:gmail:");
2625
+ let hooksGmailModelApplied = false;
2621
2626
  const hooksGmailModelRef = isGmailHook ? resolveHooksGmailModel({
2622
2627
  cfg: params.cfg,
2623
2628
  defaultProvider: DEFAULT_PROVIDER
@@ -2632,6 +2637,7 @@ async function runCronIsolatedAgentTurn(params) {
2632
2637
  }).allowed) {
2633
2638
  provider = hooksGmailModelRef.provider;
2634
2639
  model = hooksGmailModelRef.model;
2640
+ hooksGmailModelApplied = true;
2635
2641
  }
2636
2642
  }
2637
2643
  const modelOverrideRaw = params.job.payload.kind === "agentTurn" ? params.job.payload.model : void 0;
@@ -2677,6 +2683,23 @@ async function runCronIsolatedAgentTurn(params) {
2677
2683
  const labelSuffix = typeof params.job.name === "string" && params.job.name.trim() ? params.job.name.trim() : params.job.id;
2678
2684
  cronSession.sessionEntry.label = `Cron: ${labelSuffix}`;
2679
2685
  }
2686
+ if (!modelOverride && !hooksGmailModelApplied) {
2687
+ const sessionModelOverride = cronSession.sessionEntry.modelOverride?.trim();
2688
+ if (sessionModelOverride) {
2689
+ const sessionProviderOverride = cronSession.sessionEntry.providerOverride?.trim() || resolvedDefault.provider;
2690
+ const resolvedSessionOverride = resolveAllowedModelRef({
2691
+ cfg: cfgWithAgentDefaults,
2692
+ catalog: await loadCatalog(),
2693
+ raw: `${sessionProviderOverride}/${sessionModelOverride}`,
2694
+ defaultProvider: resolvedDefault.provider,
2695
+ defaultModel: resolvedDefault.model
2696
+ });
2697
+ if (!("error" in resolvedSessionOverride)) {
2698
+ provider = resolvedSessionOverride.ref.provider;
2699
+ model = resolvedSessionOverride.ref.model;
2700
+ }
2701
+ }
2702
+ }
2680
2703
  const hooksGmailThinking = isGmailHook ? normalizeThinkLevel(params.cfg.hooks?.gmail?.thinking) : void 0;
2681
2704
  const thinkOverride = normalizeThinkLevel(agentCfg?.thinkingDefault);
2682
2705
  let thinkLevel = normalizeThinkLevel((params.job.payload.kind === "agentTurn" ? params.job.payload.thinking : void 0) ?? void 0) ?? hooksGmailThinking ?? thinkOverride;
@@ -2701,10 +2724,7 @@ async function runCronIsolatedAgentTurn(params) {
2701
2724
  channel: deliveryPlan.channel ?? "last",
2702
2725
  to: deliveryPlan.to
2703
2726
  });
2704
- const userTimezone = resolveUserTimezone(params.cfg.agents?.defaults?.userTimezone);
2705
- const userTimeFormat = resolveUserTimeFormat(params.cfg.agents?.defaults?.timeFormat);
2706
- const formattedTime = formatUserTime(new Date(now), userTimezone, userTimeFormat) ?? new Date(now).toISOString();
2707
- const timeLine = `Current time: ${formattedTime} (${userTimezone})`;
2727
+ const { formattedTime, timeLine } = resolveCronStyleNow(params.cfg, now);
2708
2728
  const base = `[cron:${params.job.id} ${params.job.name}] ${params.message}`.trim();
2709
2729
  const isExternalHook = isExternalHookSession(baseSessionKey);
2710
2730
  const allowUnsafeExternalContent = agentPayload?.allowUnsafeExternalContent === true || isGmailHook && params.cfg.hooks?.gmail?.allowUnsafeExternalContent === true;
@@ -2817,6 +2837,7 @@ async function runCronIsolatedAgentTurn(params) {
2817
2837
  const payloads = runResult.payloads ?? [];
2818
2838
  {
2819
2839
  const usage = runResult.meta.agentMeta?.usage;
2840
+ const promptTokens = runResult.meta.agentMeta?.promptTokens;
2820
2841
  const modelUsed = runResult.meta.agentMeta?.model ?? fallbackModel ?? model;
2821
2842
  const providerUsed = runResult.meta.agentMeta?.provider ?? fallbackProvider ?? provider;
2822
2843
  const contextTokens = agentCfg?.contextTokens ?? lookupContextTokens(modelUsed) ?? DEFAULT_CONTEXT_TOKENS;
@@ -2830,12 +2851,15 @@ async function runCronIsolatedAgentTurn(params) {
2830
2851
  if (hasNonzeroUsage(usage)) {
2831
2852
  const input = usage.input ?? 0;
2832
2853
  const output = usage.output ?? 0;
2833
- cronSession.sessionEntry.inputTokens = input;
2834
- cronSession.sessionEntry.outputTokens = output;
2835
- cronSession.sessionEntry.totalTokens = deriveSessionTotalTokens({
2854
+ const totalTokens = deriveSessionTotalTokens({
2836
2855
  usage,
2837
- contextTokens
2856
+ contextTokens,
2857
+ promptTokens
2838
2858
  }) ?? input;
2859
+ cronSession.sessionEntry.inputTokens = input;
2860
+ cronSession.sessionEntry.outputTokens = output;
2861
+ cronSession.sessionEntry.totalTokens = totalTokens;
2862
+ cronSession.sessionEntry.totalTokensFresh = true;
2839
2863
  }
2840
2864
  await persistSessionEntry();
2841
2865
  }
@@ -3052,10 +3076,10 @@ function computeNextRunAtMs(schedule, nowMs) {
3052
3076
  catch: false
3053
3077
  });
3054
3078
  const nowSecondMs = Math.floor(nowMs / 1e3) * 1e3;
3055
- const next = cron.nextRun(/* @__PURE__ */ new Date(nowSecondMs - 1));
3079
+ const next = cron.nextRun(new Date(nowSecondMs));
3056
3080
  if (!next) return;
3057
3081
  const nextMs = next.getTime();
3058
- return Number.isFinite(nextMs) && nextMs >= nowSecondMs ? nextMs : void 0;
3082
+ return Number.isFinite(nextMs) && nextMs > nowSecondMs ? nextMs : void 0;
3059
3083
  }
3060
3084
 
3061
3085
  //#endregion
@@ -3098,6 +3122,8 @@ function computeJobNextRunAtMs(job, nowMs) {
3098
3122
  }
3099
3123
  return computeNextRunAtMs(job.schedule, nowMs);
3100
3124
  }
3125
+ /** Maximum consecutive schedule errors before auto-disabling a job. */
3126
+ const MAX_SCHEDULE_ERRORS = 3;
3101
3127
  function recomputeNextRuns(state) {
3102
3128
  if (!state.store) return false;
3103
3129
  let changed = false;
@@ -3128,12 +3154,82 @@ function recomputeNextRuns(state) {
3128
3154
  changed = true;
3129
3155
  }
3130
3156
  const nextRun = job.state.nextRunAtMs;
3131
- if (nextRun === void 0 || now >= nextRun) {
3157
+ if (nextRun === void 0 || now >= nextRun) try {
3132
3158
  const newNext = computeJobNextRunAtMs(job, now);
3133
3159
  if (job.state.nextRunAtMs !== newNext) {
3134
3160
  job.state.nextRunAtMs = newNext;
3135
3161
  changed = true;
3136
3162
  }
3163
+ if (job.state.scheduleErrorCount) {
3164
+ job.state.scheduleErrorCount = void 0;
3165
+ changed = true;
3166
+ }
3167
+ } catch (err) {
3168
+ const errorCount = (job.state.scheduleErrorCount ?? 0) + 1;
3169
+ job.state.scheduleErrorCount = errorCount;
3170
+ job.state.nextRunAtMs = void 0;
3171
+ job.state.lastError = `schedule error: ${String(err)}`;
3172
+ changed = true;
3173
+ if (errorCount >= MAX_SCHEDULE_ERRORS) {
3174
+ job.enabled = false;
3175
+ state.deps.log.error({
3176
+ jobId: job.id,
3177
+ name: job.name,
3178
+ errorCount,
3179
+ err: String(err)
3180
+ }, "cron: auto-disabled job after repeated schedule errors");
3181
+ } else state.deps.log.warn({
3182
+ jobId: job.id,
3183
+ name: job.name,
3184
+ errorCount,
3185
+ err: String(err)
3186
+ }, "cron: failed to compute next run for job (skipping)");
3187
+ }
3188
+ }
3189
+ return changed;
3190
+ }
3191
+ /**
3192
+ * Maintenance-only version of recomputeNextRuns that handles disabled jobs
3193
+ * and stuck markers, but does NOT recompute nextRunAtMs for enabled jobs
3194
+ * with existing values. Used during timer ticks when no due jobs were found
3195
+ * to prevent silently advancing past-due nextRunAtMs values without execution
3196
+ * (see #13992).
3197
+ */
3198
+ function recomputeNextRunsForMaintenance(state) {
3199
+ if (!state.store) return false;
3200
+ let changed = false;
3201
+ const now = state.deps.nowMs();
3202
+ for (const job of state.store.jobs) {
3203
+ if (!job.state) {
3204
+ job.state = {};
3205
+ changed = true;
3206
+ }
3207
+ if (!job.enabled) {
3208
+ if (job.state.nextRunAtMs !== void 0) {
3209
+ job.state.nextRunAtMs = void 0;
3210
+ changed = true;
3211
+ }
3212
+ if (job.state.runningAtMs !== void 0) {
3213
+ job.state.runningAtMs = void 0;
3214
+ changed = true;
3215
+ }
3216
+ continue;
3217
+ }
3218
+ const runningAt = job.state.runningAtMs;
3219
+ if (typeof runningAt === "number" && now - runningAt > STUCK_RUN_MS) {
3220
+ state.deps.log.warn({
3221
+ jobId: job.id,
3222
+ runningAtMs: runningAt
3223
+ }, "cron: clearing stuck running marker");
3224
+ job.state.runningAtMs = void 0;
3225
+ changed = true;
3226
+ }
3227
+ if (job.state.nextRunAtMs === void 0) {
3228
+ const newNext = computeJobNextRunAtMs(job, now);
3229
+ if (newNext !== void 0) {
3230
+ job.state.nextRunAtMs = newNext;
3231
+ changed = true;
3232
+ }
3137
3233
  }
3138
3234
  }
3139
3235
  return changed;
@@ -3793,7 +3889,7 @@ function applyJobResult(state, job, result) {
3793
3889
  job.updatedAtMs = result.endedAt;
3794
3890
  if (result.status === "error") job.state.consecutiveErrors = (job.state.consecutiveErrors ?? 0) + 1;
3795
3891
  else job.state.consecutiveErrors = 0;
3796
- const shouldDelete = job.schedule.kind === "at" && result.status === "ok" && job.deleteAfterRun === true;
3892
+ const shouldDelete = job.schedule.kind === "at" && job.deleteAfterRun === true && result.status === "ok";
3797
3893
  if (!shouldDelete) if (job.schedule.kind === "at") {
3798
3894
  job.enabled = false;
3799
3895
  job.state.nextRunAtMs = void 0;
@@ -3854,7 +3950,17 @@ function armTimer(state) {
3854
3950
  }, "cron: timer armed");
3855
3951
  }
3856
3952
  async function onTimer(state) {
3857
- if (state.running) return;
3953
+ if (state.running) {
3954
+ if (state.timer) clearTimeout(state.timer);
3955
+ state.timer = setTimeout(async () => {
3956
+ try {
3957
+ await onTimer(state);
3958
+ } catch (err) {
3959
+ state.deps.log.error({ err: String(err) }, "cron: timer tick failed");
3960
+ }
3961
+ }, MAX_TIMER_DELAY_MS);
3962
+ return;
3963
+ }
3858
3964
  state.running = true;
3859
3965
  try {
3860
3966
  const dueJobs = await locked(state, async () => {
@@ -3864,7 +3970,7 @@ async function onTimer(state) {
3864
3970
  });
3865
3971
  const due = findDueJobs(state);
3866
3972
  if (due.length === 0) {
3867
- if (recomputeNextRuns(state)) await persist(state);
3973
+ if (recomputeNextRunsForMaintenance(state)) await persist(state);
3868
3974
  return [];
3869
3975
  }
3870
3976
  const now = state.deps.nowMs();
@@ -4000,7 +4106,7 @@ async function runMissedJobs(state) {
4000
4106
  if (!j.enabled) return false;
4001
4107
  if (typeof j.state.runningAtMs === "number") return false;
4002
4108
  const next = j.state.nextRunAtMs;
4003
- if (j.schedule.kind === "at" && j.state.lastStatus === "ok") return false;
4109
+ if (j.schedule.kind === "at" && j.state.lastStatus) return false;
4004
4110
  return typeof next === "number" && now >= next;
4005
4111
  });
4006
4112
  if (missed.length > 0) {
@@ -4026,7 +4132,10 @@ async function executeJobCore(state, job) {
4026
4132
  const waitStartedAt = state.deps.nowMs();
4027
4133
  let heartbeatResult;
4028
4134
  for (;;) {
4029
- heartbeatResult = await state.deps.runHeartbeatOnce({ reason });
4135
+ heartbeatResult = await state.deps.runHeartbeatOnce({
4136
+ reason,
4137
+ agentId: job.agentId
4138
+ });
4030
4139
  if (heartbeatResult.status !== "skipped" || heartbeatResult.reason !== "requests-in-flight") break;
4031
4140
  if (state.deps.nowMs() - waitStartedAt > maxWaitMs) {
4032
4141
  state.deps.requestHeartbeatNow({ reason });
@@ -4405,9 +4514,12 @@ function buildGatewayCronService(params) {
4405
4514
  },
4406
4515
  requestHeartbeatNow,
4407
4516
  runHeartbeatOnce: async (opts) => {
4517
+ const runtimeConfig = loadConfig();
4518
+ const agentId = opts?.agentId ? resolveCronAgent(opts.agentId).agentId : void 0;
4408
4519
  return await runHeartbeatOnce({
4409
- cfg: loadConfig(),
4520
+ cfg: runtimeConfig,
4410
4521
  reason: opts?.reason,
4522
+ agentId,
4411
4523
  deps: {
4412
4524
  ...params.deps,
4413
4525
  runtime: defaultRuntime
@@ -4788,8 +4900,8 @@ function abortChatRunsForSessionKey(ops, params) {
4788
4900
 
4789
4901
  //#endregion
4790
4902
  //#region src/gateway/server-constants.ts
4791
- const MAX_PAYLOAD_BYTES = 512 * 1024;
4792
- const MAX_BUFFERED_BYTES = 1.5 * 1024 * 1024;
4903
+ const MAX_PAYLOAD_BYTES = 8 * 1024 * 1024;
4904
+ const MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
4793
4905
  const DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES = 6 * 1024 * 1024;
4794
4906
  let maxChatHistoryMessagesBytes = DEFAULT_MAX_CHAT_HISTORY_MESSAGES_BYTES;
4795
4907
  const getMaxChatHistoryMessagesBytes = () => maxChatHistoryMessagesBytes;
@@ -5241,6 +5353,7 @@ const BASE_METHODS = [
5241
5353
  "wizard.next",
5242
5354
  "wizard.cancel",
5243
5355
  "wizard.status",
5356
+ "talk.config",
5244
5357
  "talk.mode",
5245
5358
  "models.list",
5246
5359
  "agents.list",
@@ -5502,7 +5615,7 @@ function ensureAgentRunListener() {
5502
5615
  agentRunStarts.delete(evt.runId);
5503
5616
  recordAgentRunSnapshot({
5504
5617
  runId: evt.runId,
5505
- status: phase === "error" ? "error" : "ok",
5618
+ status: phase === "error" ? "error" : evt.data?.aborted ? "timeout" : "ok",
5506
5619
  startedAt,
5507
5620
  endedAt,
5508
5621
  error,
@@ -5544,7 +5657,7 @@ async function waitForAgentJob(params) {
5544
5657
  const error = typeof evt.data?.error === "string" ? evt.data.error : void 0;
5545
5658
  const snapshot = {
5546
5659
  runId: evt.runId,
5547
- status: phase === "error" ? "error" : "ok",
5660
+ status: phase === "error" ? "error" : evt.data?.aborted ? "timeout" : "ok",
5548
5661
  startedAt,
5549
5662
  endedAt,
5550
5663
  error,
@@ -5629,6 +5742,7 @@ const agentHandlers = {
5629
5742
  let resolvedGroupChannel = groupChannelRaw || void 0;
5630
5743
  let resolvedGroupSpace = groupSpaceRaw || void 0;
5631
5744
  let spawnedByValue = typeof request.spawnedBy === "string" ? request.spawnedBy.trim() : void 0;
5745
+ const inputProvenance = normalizeInputProvenance(request.inputProvenance);
5632
5746
  const cached = context.dedupe.get(`agent:${idem}`);
5633
5747
  if (cached) {
5634
5748
  respond(cached.ok, cached.payload, cached.error, { cached: true });
@@ -5765,7 +5879,10 @@ const agentHandlers = {
5765
5879
  const runId = idem;
5766
5880
  const connId = typeof client?.connId === "string" ? client.connId : void 0;
5767
5881
  const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
5768
- if (connId && wantsToolEvents) context.registerToolEventRecipient(runId, connId);
5882
+ if (connId && wantsToolEvents) {
5883
+ context.registerToolEventRecipient(runId, connId);
5884
+ for (const [activeRunId, active] of context.chatAbortControllers) if (activeRunId !== runId && active.sessionKey === requestedSessionKey) context.registerToolEventRecipient(activeRunId, connId);
5885
+ }
5769
5886
  const wantsDelivery = request.deliver === true;
5770
5887
  const explicitTo = typeof request.replyTo === "string" && request.replyTo.trim() ? request.replyTo.trim() : typeof request.to === "string" && request.to.trim() ? request.to.trim() : void 0;
5771
5888
  const explicitThreadId = typeof request.threadId === "string" && request.threadId.trim() ? request.threadId.trim() : void 0;
@@ -5832,7 +5949,8 @@ const agentHandlers = {
5832
5949
  messageChannel: resolvedChannel,
5833
5950
  runId,
5834
5951
  lane: request.lane,
5835
- extraSystemPrompt: request.extraSystemPrompt
5952
+ extraSystemPrompt: request.extraSystemPrompt,
5953
+ inputProvenance
5836
5954
  }, defaultRuntime, context.deps).then((result) => {
5837
5955
  const payload = {
5838
5956
  runId,
@@ -6246,143 +6364,6 @@ const agentsHandlers = {
6246
6364
  }
6247
6365
  };
6248
6366
 
6249
- //#endregion
6250
- //#region src/gateway/node-command-policy.ts
6251
- const CANVAS_COMMANDS = [
6252
- "canvas.present",
6253
- "canvas.hide",
6254
- "canvas.navigate",
6255
- "canvas.eval",
6256
- "canvas.snapshot",
6257
- "canvas.a2ui.push",
6258
- "canvas.a2ui.pushJSONL",
6259
- "canvas.a2ui.reset"
6260
- ];
6261
- const CAMERA_COMMANDS = ["camera.list"];
6262
- const CAMERA_DANGEROUS_COMMANDS = ["camera.snap", "camera.clip"];
6263
- const SCREEN_DANGEROUS_COMMANDS = ["screen.record"];
6264
- const LOCATION_COMMANDS = ["location.get"];
6265
- const DEVICE_COMMANDS = ["device.info", "device.status"];
6266
- const CONTACTS_COMMANDS = ["contacts.search"];
6267
- const CONTACTS_DANGEROUS_COMMANDS = ["contacts.add"];
6268
- const CALENDAR_COMMANDS = ["calendar.events"];
6269
- const CALENDAR_DANGEROUS_COMMANDS = ["calendar.add"];
6270
- const REMINDERS_COMMANDS = ["reminders.list"];
6271
- const REMINDERS_DANGEROUS_COMMANDS = ["reminders.add"];
6272
- const PHOTOS_COMMANDS = ["photos.latest"];
6273
- const MOTION_COMMANDS = ["motion.activity", "motion.pedometer"];
6274
- const SMS_DANGEROUS_COMMANDS = ["sms.send"];
6275
- const IOS_SYSTEM_COMMANDS = ["system.notify"];
6276
- const SYSTEM_COMMANDS = [
6277
- "system.run",
6278
- "system.which",
6279
- "system.notify",
6280
- "system.execApprovals.get",
6281
- "system.execApprovals.set",
6282
- "browser.proxy"
6283
- ];
6284
- const DEFAULT_DANGEROUS_NODE_COMMANDS = [
6285
- ...CAMERA_DANGEROUS_COMMANDS,
6286
- ...SCREEN_DANGEROUS_COMMANDS,
6287
- ...CONTACTS_DANGEROUS_COMMANDS,
6288
- ...CALENDAR_DANGEROUS_COMMANDS,
6289
- ...REMINDERS_DANGEROUS_COMMANDS,
6290
- ...SMS_DANGEROUS_COMMANDS
6291
- ];
6292
- const PLATFORM_DEFAULTS = {
6293
- ios: [
6294
- ...CANVAS_COMMANDS,
6295
- ...CAMERA_COMMANDS,
6296
- ...LOCATION_COMMANDS,
6297
- ...DEVICE_COMMANDS,
6298
- ...CONTACTS_COMMANDS,
6299
- ...CALENDAR_COMMANDS,
6300
- ...REMINDERS_COMMANDS,
6301
- ...PHOTOS_COMMANDS,
6302
- ...MOTION_COMMANDS,
6303
- ...IOS_SYSTEM_COMMANDS
6304
- ],
6305
- android: [
6306
- ...CANVAS_COMMANDS,
6307
- ...CAMERA_COMMANDS,
6308
- ...LOCATION_COMMANDS,
6309
- ...DEVICE_COMMANDS,
6310
- ...CONTACTS_COMMANDS,
6311
- ...CALENDAR_COMMANDS,
6312
- ...REMINDERS_COMMANDS,
6313
- ...PHOTOS_COMMANDS,
6314
- ...MOTION_COMMANDS
6315
- ],
6316
- macos: [
6317
- ...CANVAS_COMMANDS,
6318
- ...CAMERA_COMMANDS,
6319
- ...LOCATION_COMMANDS,
6320
- ...DEVICE_COMMANDS,
6321
- ...CONTACTS_COMMANDS,
6322
- ...CALENDAR_COMMANDS,
6323
- ...REMINDERS_COMMANDS,
6324
- ...PHOTOS_COMMANDS,
6325
- ...MOTION_COMMANDS,
6326
- ...SYSTEM_COMMANDS
6327
- ],
6328
- linux: [...SYSTEM_COMMANDS],
6329
- windows: [...SYSTEM_COMMANDS],
6330
- unknown: [
6331
- ...CANVAS_COMMANDS,
6332
- ...CAMERA_COMMANDS,
6333
- ...LOCATION_COMMANDS,
6334
- ...SYSTEM_COMMANDS
6335
- ]
6336
- };
6337
- function normalizePlatformId(platform, deviceFamily) {
6338
- const raw = (platform ?? "").trim().toLowerCase();
6339
- if (raw.startsWith("ios")) return "ios";
6340
- if (raw.startsWith("android")) return "android";
6341
- if (raw.startsWith("mac")) return "macos";
6342
- if (raw.startsWith("darwin")) return "macos";
6343
- if (raw.startsWith("win")) return "windows";
6344
- if (raw.startsWith("linux")) return "linux";
6345
- const family = (deviceFamily ?? "").trim().toLowerCase();
6346
- if (family.includes("iphone") || family.includes("ipad") || family.includes("ios")) return "ios";
6347
- if (family.includes("android")) return "android";
6348
- if (family.includes("mac")) return "macos";
6349
- if (family.includes("windows")) return "windows";
6350
- if (family.includes("linux")) return "linux";
6351
- return "unknown";
6352
- }
6353
- function resolveNodeCommandAllowlist(cfg, node) {
6354
- const base = PLATFORM_DEFAULTS[normalizePlatformId(node?.platform, node?.deviceFamily)] ?? PLATFORM_DEFAULTS.unknown;
6355
- const extra = cfg.gateway?.nodes?.allowCommands ?? [];
6356
- const deny = new Set(cfg.gateway?.nodes?.denyCommands ?? []);
6357
- const allow = new Set([...base, ...extra].map((cmd) => cmd.trim()).filter(Boolean));
6358
- for (const blocked of deny) {
6359
- const trimmed = blocked.trim();
6360
- if (trimmed) allow.delete(trimmed);
6361
- }
6362
- return allow;
6363
- }
6364
- function isNodeCommandAllowed(params) {
6365
- const command = params.command.trim();
6366
- if (!command) return {
6367
- ok: false,
6368
- reason: "command required"
6369
- };
6370
- if (!params.allowlist.has(command)) return {
6371
- ok: false,
6372
- reason: "command not allowlisted"
6373
- };
6374
- if (Array.isArray(params.declaredCommands) && params.declaredCommands.length > 0) {
6375
- if (!params.declaredCommands.includes(command)) return {
6376
- ok: false,
6377
- reason: "command not declared by node"
6378
- };
6379
- } else return {
6380
- ok: false,
6381
- reason: "node did not declare commands"
6382
- };
6383
- return { ok: true };
6384
- }
6385
-
6386
6367
  //#endregion
6387
6368
  //#region src/gateway/server-methods/nodes.helpers.ts
6388
6369
  function respondInvalidParams(params) {
@@ -6753,9 +6734,13 @@ const channelsHandlers = {
6753
6734
  //#region src/gateway/server-methods/chat.ts
6754
6735
  function resolveTranscriptPath(params) {
6755
6736
  const { sessionId, storePath, sessionFile } = params;
6756
- if (sessionFile) return sessionFile;
6757
- if (!storePath) return null;
6758
- return path.join(path.dirname(storePath), `${sessionId}.jsonl`);
6737
+ if (!storePath && !sessionFile) return null;
6738
+ try {
6739
+ const sessionsDir = storePath ? path.dirname(storePath) : void 0;
6740
+ return resolveSessionFilePath(sessionId, sessionFile ? { sessionFile } : void 0, sessionsDir ? { sessionsDir } : void 0);
6741
+ } catch {
6742
+ return null;
6743
+ }
6759
6744
  }
6760
6745
  function ensureTranscriptFile(params) {
6761
6746
  if (fs.existsSync(params.transcriptPath)) return { ok: true };
@@ -7119,7 +7104,10 @@ const chatHandlers = {
7119
7104
  agentRunStarted = true;
7120
7105
  const connId = typeof client?.connId === "string" ? client.connId : void 0;
7121
7106
  const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
7122
- if (connId && wantsToolEvents) context.registerToolEventRecipient(runId, connId);
7107
+ if (connId && wantsToolEvents) {
7108
+ context.registerToolEventRecipient(runId, connId);
7109
+ for (const [activeRunId, active] of context.chatAbortControllers) if (activeRunId !== runId && active.sessionKey === p.sessionKey) context.registerToolEventRecipient(activeRunId, connId);
7110
+ }
7123
7111
  },
7124
7112
  onModelSelected
7125
7113
  }
@@ -7252,491 +7240,30 @@ const chatHandlers = {
7252
7240
  };
7253
7241
 
7254
7242
  //#endregion
7255
- //#region src/config/merge-patch.ts
7256
- function applyMergePatch(base, patch) {
7257
- if (!isPlainObject(patch)) return patch;
7258
- const result = isPlainObject(base) ? { ...base } : {};
7259
- for (const [key, value] of Object.entries(patch)) {
7260
- if (value === null) {
7261
- delete result[key];
7262
- continue;
7263
- }
7264
- if (isPlainObject(value)) {
7265
- const baseValue = result[key];
7266
- result[key] = applyMergePatch(isPlainObject(baseValue) ? baseValue : {}, value);
7267
- continue;
7268
- }
7269
- result[key] = value;
7270
- }
7271
- return result;
7272
- }
7273
-
7274
- //#endregion
7275
- //#region src/config/redact-snapshot.ts
7276
- /**
7277
- * Sentinel value used to replace sensitive config fields in gateway responses.
7278
- * Write-side handlers (config.set, config.apply, config.patch) detect this
7279
- * sentinel and restore the original value from the on-disk config, so a
7280
- * round-trip through the Web UI does not corrupt credentials.
7281
- */
7282
- const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
7283
- /**
7284
- * Patterns that identify sensitive config field names.
7285
- * Aligned with the UI-hint logic in schema.ts.
7286
- */
7287
- const SENSITIVE_KEY_PATTERNS = [
7288
- /token/i,
7289
- /password/i,
7290
- /secret/i,
7291
- /api.?key/i
7292
- ];
7293
- function isSensitiveKey(key) {
7294
- return SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(key));
7295
- }
7296
- /**
7297
- * Deep-walk an object and replace values whose key matches a sensitive pattern
7298
- * with the redaction sentinel.
7299
- */
7300
- function redactObject(obj) {
7301
- if (obj === null || obj === void 0) return obj;
7302
- if (typeof obj !== "object") return obj;
7303
- if (Array.isArray(obj)) return obj.map(redactObject);
7304
- const result = {};
7305
- for (const [key, value] of Object.entries(obj)) if (isSensitiveKey(key) && value !== null && value !== void 0) result[key] = REDACTED_SENTINEL;
7306
- else if (typeof value === "object" && value !== null) result[key] = redactObject(value);
7307
- else result[key] = value;
7308
- return result;
7309
- }
7310
- function redactConfigObject(value) {
7311
- return redactObject(value);
7312
- }
7313
- /**
7314
- * Collect all sensitive string values from a config object.
7315
- * Used for text-based redaction of the raw JSON5 source.
7316
- */
7317
- function collectSensitiveValues(obj) {
7318
- const values = [];
7319
- if (obj === null || obj === void 0 || typeof obj !== "object") return values;
7320
- if (Array.isArray(obj)) {
7321
- for (const item of obj) values.push(...collectSensitiveValues(item));
7322
- return values;
7323
- }
7324
- for (const [key, value] of Object.entries(obj)) if (isSensitiveKey(key) && typeof value === "string" && value.length > 0) values.push(value);
7325
- else if (typeof value === "object" && value !== null) values.push(...collectSensitiveValues(value));
7326
- return values;
7327
- }
7328
- /**
7329
- * Replace known sensitive values in a raw JSON5 string with the sentinel.
7330
- * Values are replaced longest-first to avoid partial matches.
7331
- */
7332
- function redactRawText(raw, config) {
7333
- const sensitiveValues = collectSensitiveValues(config);
7334
- sensitiveValues.sort((a, b) => b.length - a.length);
7335
- let result = raw;
7336
- for (const value of sensitiveValues) {
7337
- const escaped = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7338
- result = result.replace(new RegExp(escaped, "g"), REDACTED_SENTINEL);
7339
- }
7340
- result = result.replace(/(^|[{\s,])((["'])([^"']+)\3|([A-Za-z0-9_$.-]+))(\s*:\s*)(["'])([^"']*)\7/g, (match, prefix, keyExpr, _keyQuote, keyQuoted, keyBare, sep, valQuote, val) => {
7341
- const key = keyQuoted ?? keyBare;
7342
- if (!key || !isSensitiveKey(key)) return match;
7343
- if (val === REDACTED_SENTINEL) return match;
7344
- return `${prefix}${keyExpr}${sep}${valQuote}${REDACTED_SENTINEL}${valQuote}`;
7345
- });
7346
- return result;
7347
- }
7348
- /**
7349
- * Returns a copy of the config snapshot with all sensitive fields
7350
- * replaced by {@link REDACTED_SENTINEL}. The `hash` is preserved
7351
- * (it tracks config identity, not content).
7352
- *
7353
- * Both `config` (the parsed object) and `raw` (the JSON5 source) are scrubbed
7354
- * so no credential can leak through either path.
7355
- */
7356
- function redactConfigSnapshot(snapshot) {
7357
- const redactedConfig = redactConfigObject(snapshot.config);
7358
- const redactedRaw = snapshot.raw ? redactRawText(snapshot.raw, snapshot.config) : null;
7359
- const redactedParsed = snapshot.parsed ? redactConfigObject(snapshot.parsed) : snapshot.parsed;
7360
- return {
7361
- ...snapshot,
7362
- config: redactedConfig,
7363
- raw: redactedRaw,
7364
- parsed: redactedParsed
7365
- };
7366
- }
7367
- /**
7368
- * Deep-walk `incoming` and replace any {@link REDACTED_SENTINEL} values
7369
- * (on sensitive keys) with the corresponding value from `original`.
7370
- *
7371
- * This is called by config.set / config.apply / config.patch before writing,
7372
- * so that credentials survive a Web UI round-trip unmodified.
7373
- */
7374
- function restoreRedactedValues(incoming, original) {
7375
- if (incoming === null || incoming === void 0) return incoming;
7376
- if (typeof incoming !== "object") return incoming;
7377
- if (Array.isArray(incoming)) {
7378
- const origArr = Array.isArray(original) ? original : [];
7379
- return incoming.map((item, i) => restoreRedactedValues(item, origArr[i]));
7380
- }
7381
- const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
7382
- const result = {};
7383
- for (const [key, value] of Object.entries(incoming)) if (isSensitiveKey(key) && value === REDACTED_SENTINEL) {
7384
- if (!(key in orig)) throw new Error(`config write rejected: "${key}" is redacted; set an explicit value instead of ${REDACTED_SENTINEL}`);
7385
- result[key] = orig[key];
7386
- } else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValues(value, orig[key]);
7387
- else result[key] = value;
7388
- return result;
7389
- }
7390
-
7391
- //#endregion
7392
- //#region src/config/schema.ts
7393
- const GROUP_LABELS = {
7394
- wizard: "Wizard",
7395
- update: "Update",
7396
- diagnostics: "Diagnostics",
7397
- logging: "Logging",
7398
- gateway: "Gateway",
7399
- nodeHost: "Node Host",
7400
- agents: "Agents",
7401
- tools: "Tools",
7402
- bindings: "Bindings",
7403
- audio: "Audio",
7404
- models: "Models",
7405
- messages: "Messages",
7406
- commands: "Commands",
7407
- session: "Session",
7408
- cron: "Cron",
7409
- hooks: "Hooks",
7410
- ui: "UI",
7411
- browser: "Browser",
7412
- talk: "Talk",
7413
- channels: "Messaging Channels",
7414
- skills: "Skills",
7415
- plugins: "Plugins",
7416
- discovery: "Discovery",
7417
- presence: "Presence",
7418
- voicewake: "Voice Wake"
7243
+ //#region src/config/schema.irc.ts
7244
+ const IRC_FIELD_LABELS = {
7245
+ "channels.irc": "IRC",
7246
+ "channels.irc.dmPolicy": "IRC DM Policy",
7247
+ "channels.irc.nickserv.enabled": "IRC NickServ Enabled",
7248
+ "channels.irc.nickserv.service": "IRC NickServ Service",
7249
+ "channels.irc.nickserv.password": "IRC NickServ Password",
7250
+ "channels.irc.nickserv.passwordFile": "IRC NickServ Password File",
7251
+ "channels.irc.nickserv.register": "IRC NickServ Register",
7252
+ "channels.irc.nickserv.registerEmail": "IRC NickServ Register Email"
7419
7253
  };
7420
- const GROUP_ORDER = {
7421
- wizard: 20,
7422
- update: 25,
7423
- diagnostics: 27,
7424
- gateway: 30,
7425
- nodeHost: 35,
7426
- agents: 40,
7427
- tools: 50,
7428
- bindings: 55,
7429
- audio: 60,
7430
- models: 70,
7431
- messages: 80,
7432
- commands: 85,
7433
- session: 90,
7434
- cron: 100,
7435
- hooks: 110,
7436
- ui: 120,
7437
- browser: 130,
7438
- talk: 140,
7439
- channels: 150,
7440
- skills: 200,
7441
- plugins: 205,
7442
- discovery: 210,
7443
- presence: 220,
7444
- voicewake: 230,
7445
- logging: 900
7446
- };
7447
- const FIELD_LABELS = {
7448
- "meta.lastTouchedVersion": "Config Last Touched Version",
7449
- "meta.lastTouchedAt": "Config Last Touched At",
7450
- "update.channel": "Update Channel",
7451
- "update.checkOnStart": "Update Check on Start",
7452
- "diagnostics.enabled": "Diagnostics Enabled",
7453
- "diagnostics.flags": "Diagnostics Flags",
7454
- "diagnostics.otel.enabled": "OpenTelemetry Enabled",
7455
- "diagnostics.otel.endpoint": "OpenTelemetry Endpoint",
7456
- "diagnostics.otel.protocol": "OpenTelemetry Protocol",
7457
- "diagnostics.otel.headers": "OpenTelemetry Headers",
7458
- "diagnostics.otel.serviceName": "OpenTelemetry Service Name",
7459
- "diagnostics.otel.traces": "OpenTelemetry Traces Enabled",
7460
- "diagnostics.otel.metrics": "OpenTelemetry Metrics Enabled",
7461
- "diagnostics.otel.logs": "OpenTelemetry Logs Enabled",
7462
- "diagnostics.otel.sampleRate": "OpenTelemetry Trace Sample Rate",
7463
- "diagnostics.otel.flushIntervalMs": "OpenTelemetry Flush Interval (ms)",
7464
- "diagnostics.cacheTrace.enabled": "Cache Trace Enabled",
7465
- "diagnostics.cacheTrace.filePath": "Cache Trace File Path",
7466
- "diagnostics.cacheTrace.includeMessages": "Cache Trace Include Messages",
7467
- "diagnostics.cacheTrace.includePrompt": "Cache Trace Include Prompt",
7468
- "diagnostics.cacheTrace.includeSystem": "Cache Trace Include System",
7469
- "agents.list.*.identity.avatar": "Identity Avatar",
7470
- "agents.list.*.skills": "Agent Skill Filter",
7471
- "gateway.remote.url": "Remote Gateway URL",
7472
- "gateway.remote.sshTarget": "Remote Gateway SSH Target",
7473
- "gateway.remote.sshIdentity": "Remote Gateway SSH Identity",
7474
- "gateway.remote.token": "Remote Gateway Token",
7475
- "gateway.remote.password": "Remote Gateway Password",
7476
- "gateway.remote.tlsFingerprint": "Remote Gateway TLS Fingerprint",
7477
- "gateway.auth.token": "Gateway Token",
7478
- "gateway.auth.password": "Gateway Password",
7479
- "tools.media.image.enabled": "Enable Image Understanding",
7480
- "tools.media.image.maxBytes": "Image Understanding Max Bytes",
7481
- "tools.media.image.maxChars": "Image Understanding Max Chars",
7482
- "tools.media.image.prompt": "Image Understanding Prompt",
7483
- "tools.media.image.timeoutSeconds": "Image Understanding Timeout (sec)",
7484
- "tools.media.image.attachments": "Image Understanding Attachment Policy",
7485
- "tools.media.image.models": "Image Understanding Models",
7486
- "tools.media.image.scope": "Image Understanding Scope",
7487
- "tools.media.models": "Media Understanding Shared Models",
7488
- "tools.media.concurrency": "Media Understanding Concurrency",
7489
- "tools.media.audio.enabled": "Enable Audio Understanding",
7490
- "tools.media.audio.maxBytes": "Audio Understanding Max Bytes",
7491
- "tools.media.audio.maxChars": "Audio Understanding Max Chars",
7492
- "tools.media.audio.prompt": "Audio Understanding Prompt",
7493
- "tools.media.audio.timeoutSeconds": "Audio Understanding Timeout (sec)",
7494
- "tools.media.audio.language": "Audio Understanding Language",
7495
- "tools.media.audio.attachments": "Audio Understanding Attachment Policy",
7496
- "tools.media.audio.models": "Audio Understanding Models",
7497
- "tools.media.audio.scope": "Audio Understanding Scope",
7498
- "tools.media.video.enabled": "Enable Video Understanding",
7499
- "tools.media.video.maxBytes": "Video Understanding Max Bytes",
7500
- "tools.media.video.maxChars": "Video Understanding Max Chars",
7501
- "tools.media.video.prompt": "Video Understanding Prompt",
7502
- "tools.media.video.timeoutSeconds": "Video Understanding Timeout (sec)",
7503
- "tools.media.video.attachments": "Video Understanding Attachment Policy",
7504
- "tools.media.video.models": "Video Understanding Models",
7505
- "tools.media.video.scope": "Video Understanding Scope",
7506
- "tools.links.enabled": "Enable Link Understanding",
7507
- "tools.links.maxLinks": "Link Understanding Max Links",
7508
- "tools.links.timeoutSeconds": "Link Understanding Timeout (sec)",
7509
- "tools.links.models": "Link Understanding Models",
7510
- "tools.links.scope": "Link Understanding Scope",
7511
- "tools.profile": "Tool Profile",
7512
- "tools.alsoAllow": "Tool Allowlist Additions",
7513
- "agents.list[].tools.profile": "Agent Tool Profile",
7514
- "agents.list[].tools.alsoAllow": "Agent Tool Allowlist Additions",
7515
- "tools.byProvider": "Tool Policy by Provider",
7516
- "agents.list[].tools.byProvider": "Agent Tool Policy by Provider",
7517
- "tools.exec.applyPatch.enabled": "Enable apply_patch",
7518
- "tools.exec.applyPatch.allowModels": "apply_patch Model Allowlist",
7519
- "tools.exec.notifyOnExit": "Exec Notify On Exit",
7520
- "tools.exec.approvalRunningNoticeMs": "Exec Approval Running Notice (ms)",
7521
- "tools.exec.host": "Exec Host",
7522
- "tools.exec.security": "Exec Security",
7523
- "tools.exec.ask": "Exec Ask",
7524
- "tools.exec.node": "Exec Node Binding",
7525
- "tools.exec.pathPrepend": "Exec PATH Prepend",
7526
- "tools.exec.safeBins": "Exec Safe Bins",
7527
- "tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
7528
- "tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
7529
- "tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
7530
- "tools.message.crossContext.marker.enabled": "Cross-Context Marker",
7531
- "tools.message.crossContext.marker.prefix": "Cross-Context Marker Prefix",
7532
- "tools.message.crossContext.marker.suffix": "Cross-Context Marker Suffix",
7533
- "tools.message.broadcast.enabled": "Enable Message Broadcast",
7534
- "tools.web.search.enabled": "Enable Web Search Tool",
7535
- "tools.web.search.provider": "Web Search Provider",
7536
- "tools.web.search.apiKey": "Brave Search API Key",
7537
- "tools.web.search.maxResults": "Web Search Max Results",
7538
- "tools.web.search.timeoutSeconds": "Web Search Timeout (sec)",
7539
- "tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)",
7540
- "tools.web.fetch.enabled": "Enable Web Fetch Tool",
7541
- "tools.web.fetch.maxChars": "Web Fetch Max Chars",
7542
- "tools.web.fetch.timeoutSeconds": "Web Fetch Timeout (sec)",
7543
- "tools.web.fetch.cacheTtlMinutes": "Web Fetch Cache TTL (min)",
7544
- "tools.web.fetch.maxRedirects": "Web Fetch Max Redirects",
7545
- "tools.web.fetch.userAgent": "Web Fetch User-Agent",
7546
- "gateway.controlUi.basePath": "Control UI Base Path",
7547
- "gateway.controlUi.root": "Control UI Assets Root",
7548
- "gateway.controlUi.allowedOrigins": "Control UI Allowed Origins",
7549
- "gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth",
7550
- "gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth",
7551
- "gateway.http.endpoints.chatCompletions.enabled": "OpenAI Chat Completions Endpoint",
7552
- "gateway.reload.mode": "Config Reload Mode",
7553
- "gateway.reload.debounceMs": "Config Reload Debounce (ms)",
7554
- "gateway.nodes.browser.mode": "Gateway Node Browser Mode",
7555
- "gateway.nodes.browser.node": "Gateway Node Browser Pin",
7556
- "gateway.nodes.allowCommands": "Gateway Node Allowlist (Extra Commands)",
7557
- "gateway.nodes.denyCommands": "Gateway Node Denylist",
7558
- "nodeHost.browserProxy.enabled": "Node Browser Proxy Enabled",
7559
- "nodeHost.browserProxy.allowProfiles": "Node Browser Proxy Allowed Profiles",
7560
- "skills.load.watch": "Watch Skills",
7561
- "skills.load.watchDebounceMs": "Skills Watch Debounce (ms)",
7562
- "agents.defaults.workspace": "Workspace",
7563
- "agents.defaults.repoRoot": "Repo Root",
7564
- "agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars",
7565
- "agents.defaults.envelopeTimezone": "Envelope Timezone",
7566
- "agents.defaults.envelopeTimestamp": "Envelope Timestamp",
7567
- "agents.defaults.envelopeElapsed": "Envelope Elapsed",
7568
- "agents.defaults.memorySearch": "Memory Search",
7569
- "agents.defaults.memorySearch.enabled": "Enable Memory Search",
7570
- "agents.defaults.memorySearch.sources": "Memory Search Sources",
7571
- "agents.defaults.memorySearch.extraPaths": "Extra Memory Paths",
7572
- "agents.defaults.memorySearch.experimental.sessionMemory": "Memory Search Session Index (Experimental)",
7573
- "agents.defaults.memorySearch.provider": "Memory Search Provider",
7574
- "agents.defaults.memorySearch.remote.baseUrl": "Remote Embedding Base URL",
7575
- "agents.defaults.memorySearch.remote.apiKey": "Remote Embedding API Key",
7576
- "agents.defaults.memorySearch.remote.headers": "Remote Embedding Headers",
7577
- "agents.defaults.memorySearch.remote.batch.concurrency": "Remote Batch Concurrency",
7578
- "agents.defaults.memorySearch.model": "Memory Search Model",
7579
- "agents.defaults.memorySearch.fallback": "Memory Search Fallback",
7580
- "agents.defaults.memorySearch.local.modelPath": "Local Embedding Model Path",
7581
- "agents.defaults.memorySearch.store.path": "Memory Search Index Path",
7582
- "agents.defaults.memorySearch.store.vector.enabled": "Memory Search Vector Index",
7583
- "agents.defaults.memorySearch.store.vector.extensionPath": "Memory Search Vector Extension Path",
7584
- "agents.defaults.memorySearch.chunking.tokens": "Memory Chunk Tokens",
7585
- "agents.defaults.memorySearch.chunking.overlap": "Memory Chunk Overlap Tokens",
7586
- "agents.defaults.memorySearch.sync.onSessionStart": "Index on Session Start",
7587
- "agents.defaults.memorySearch.sync.onSearch": "Index on Search (Lazy)",
7588
- "agents.defaults.memorySearch.sync.watch": "Watch Memory Files",
7589
- "agents.defaults.memorySearch.sync.watchDebounceMs": "Memory Watch Debounce (ms)",
7590
- "agents.defaults.memorySearch.sync.sessions.deltaBytes": "Session Delta Bytes",
7591
- "agents.defaults.memorySearch.sync.sessions.deltaMessages": "Session Delta Messages",
7592
- "agents.defaults.memorySearch.query.maxResults": "Memory Search Max Results",
7593
- "agents.defaults.memorySearch.query.minScore": "Memory Search Min Score",
7594
- "agents.defaults.memorySearch.query.hybrid.enabled": "Memory Search Hybrid",
7595
- "agents.defaults.memorySearch.query.hybrid.vectorWeight": "Memory Search Vector Weight",
7596
- "agents.defaults.memorySearch.query.hybrid.textWeight": "Memory Search Text Weight",
7597
- "agents.defaults.memorySearch.query.hybrid.candidateMultiplier": "Memory Search Hybrid Candidate Multiplier",
7598
- "agents.defaults.memorySearch.cache.enabled": "Memory Search Embedding Cache",
7599
- "agents.defaults.memorySearch.cache.maxEntries": "Memory Search Embedding Cache Max Entries",
7600
- memory: "Memory",
7601
- "memory.backend": "Memory Backend",
7602
- "memory.citations": "Memory Citations Mode",
7603
- "memory.qmd.command": "QMD Binary",
7604
- "memory.qmd.includeDefaultMemory": "QMD Include Default Memory",
7605
- "memory.qmd.paths": "QMD Extra Paths",
7606
- "memory.qmd.paths.path": "QMD Path",
7607
- "memory.qmd.paths.pattern": "QMD Path Pattern",
7608
- "memory.qmd.paths.name": "QMD Path Name",
7609
- "memory.qmd.sessions.enabled": "QMD Session Indexing",
7610
- "memory.qmd.sessions.exportDir": "QMD Session Export Directory",
7611
- "memory.qmd.sessions.retentionDays": "QMD Session Retention (days)",
7612
- "memory.qmd.update.interval": "QMD Update Interval",
7613
- "memory.qmd.update.debounceMs": "QMD Update Debounce (ms)",
7614
- "memory.qmd.update.onBoot": "QMD Update on Startup",
7615
- "memory.qmd.update.waitForBootSync": "QMD Wait for Boot Sync",
7616
- "memory.qmd.update.embedInterval": "QMD Embed Interval",
7617
- "memory.qmd.update.commandTimeoutMs": "QMD Command Timeout (ms)",
7618
- "memory.qmd.update.updateTimeoutMs": "QMD Update Timeout (ms)",
7619
- "memory.qmd.update.embedTimeoutMs": "QMD Embed Timeout (ms)",
7620
- "memory.qmd.limits.maxResults": "QMD Max Results",
7621
- "memory.qmd.limits.maxSnippetChars": "QMD Max Snippet Chars",
7622
- "memory.qmd.limits.maxInjectedChars": "QMD Max Injected Chars",
7623
- "memory.qmd.limits.timeoutMs": "QMD Search Timeout (ms)",
7624
- "memory.qmd.scope": "QMD Surface Scope",
7625
- "auth.profiles": "Auth Profiles",
7626
- "auth.order": "Auth Profile Order",
7627
- "auth.cooldowns.billingBackoffHours": "Billing Backoff (hours)",
7628
- "auth.cooldowns.billingBackoffHoursByProvider": "Billing Backoff Overrides",
7629
- "auth.cooldowns.billingMaxHours": "Billing Backoff Cap (hours)",
7630
- "auth.cooldowns.failureWindowHours": "Failover Window (hours)",
7631
- "agents.defaults.models": "Models",
7632
- "agents.defaults.model.primary": "Primary Model",
7633
- "agents.defaults.model.fallbacks": "Model Fallbacks",
7634
- "agents.defaults.imageModel.primary": "Image Model",
7635
- "agents.defaults.imageModel.fallbacks": "Image Model Fallbacks",
7636
- "agents.defaults.humanDelay.mode": "Human Delay Mode",
7637
- "agents.defaults.humanDelay.minMs": "Human Delay Min (ms)",
7638
- "agents.defaults.humanDelay.maxMs": "Human Delay Max (ms)",
7639
- "agents.defaults.cliBackends": "CLI Backends",
7640
- "commands.native": "Native Commands",
7641
- "commands.nativeSkills": "Native Skill Commands",
7642
- "commands.text": "Text Commands",
7643
- "commands.bash": "Allow Bash Chat Command",
7644
- "commands.bashForegroundMs": "Bash Foreground Window (ms)",
7645
- "commands.config": "Allow /config",
7646
- "commands.debug": "Allow /debug",
7647
- "commands.restart": "Allow Restart",
7648
- "commands.useAccessGroups": "Use Access Groups",
7649
- "commands.ownerAllowFrom": "Command Owners",
7650
- "commands.allowFrom": "Command Access Allowlist",
7651
- "ui.seamColor": "Accent Color",
7652
- "ui.assistant.name": "Assistant Name",
7653
- "ui.assistant.avatar": "Assistant Avatar",
7654
- "browser.evaluateEnabled": "Browser Evaluate Enabled",
7655
- "browser.snapshotDefaults": "Browser Snapshot Defaults",
7656
- "browser.snapshotDefaults.mode": "Browser Snapshot Mode",
7657
- "browser.remoteCdpTimeoutMs": "Remote CDP Timeout (ms)",
7658
- "browser.remoteCdpHandshakeTimeoutMs": "Remote CDP Handshake Timeout (ms)",
7659
- "session.dmScope": "DM Session Scope",
7660
- "session.agentToAgent.maxPingPongTurns": "Agent-to-Agent Ping-Pong Turns",
7661
- "messages.ackReaction": "Ack Reaction Emoji",
7662
- "messages.ackReactionScope": "Ack Reaction Scope",
7663
- "messages.inbound.debounceMs": "Inbound Message Debounce (ms)",
7664
- "talk.apiKey": "Talk API Key",
7665
- "channels.whatsapp": "WhatsApp",
7666
- "channels.telegram": "Telegram",
7667
- "channels.telegram.customCommands": "Telegram Custom Commands",
7668
- "channels.discord": "Discord",
7669
- "channels.slack": "Slack",
7670
- "channels.mattermost": "Mattermost",
7671
- "channels.signal": "Signal",
7672
- "channels.imessage": "iMessage",
7673
- "channels.bluebubbles": "BlueBubbles",
7674
- "channels.msteams": "MS Teams",
7675
- "channels.telegram.botToken": "Telegram Bot Token",
7676
- "channels.telegram.dmPolicy": "Telegram DM Policy",
7677
- "channels.telegram.streamMode": "Telegram Draft Stream Mode",
7678
- "channels.telegram.draftChunk.minChars": "Telegram Draft Chunk Min Chars",
7679
- "channels.telegram.draftChunk.maxChars": "Telegram Draft Chunk Max Chars",
7680
- "channels.telegram.draftChunk.breakPreference": "Telegram Draft Chunk Break Preference",
7681
- "channels.telegram.retry.attempts": "Telegram Retry Attempts",
7682
- "channels.telegram.retry.minDelayMs": "Telegram Retry Min Delay (ms)",
7683
- "channels.telegram.retry.maxDelayMs": "Telegram Retry Max Delay (ms)",
7684
- "channels.telegram.retry.jitter": "Telegram Retry Jitter",
7685
- "channels.telegram.network.autoSelectFamily": "Telegram autoSelectFamily",
7686
- "channels.telegram.timeoutSeconds": "Telegram API Timeout (seconds)",
7687
- "channels.telegram.capabilities.inlineButtons": "Telegram Inline Buttons",
7688
- "channels.whatsapp.dmPolicy": "WhatsApp DM Policy",
7689
- "channels.whatsapp.selfChatMode": "WhatsApp Self-Phone Mode",
7690
- "channels.whatsapp.debounceMs": "WhatsApp Message Debounce (ms)",
7691
- "channels.signal.dmPolicy": "Signal DM Policy",
7692
- "channels.imessage.dmPolicy": "iMessage DM Policy",
7693
- "channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
7694
- "channels.discord.dm.policy": "Discord DM Policy",
7695
- "channels.discord.retry.attempts": "Discord Retry Attempts",
7696
- "channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
7697
- "channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
7698
- "channels.discord.retry.jitter": "Discord Retry Jitter",
7699
- "channels.discord.maxLinesPerMessage": "Discord Max Lines Per Message",
7700
- "channels.discord.intents.presence": "Discord Presence Intent",
7701
- "channels.discord.intents.guildMembers": "Discord Guild Members Intent",
7702
- "channels.discord.pluralkit.enabled": "Discord PluralKit Enabled",
7703
- "channels.discord.pluralkit.token": "Discord PluralKit Token",
7704
- "channels.slack.dm.policy": "Slack DM Policy",
7705
- "channels.slack.allowBots": "Slack Allow Bot Messages",
7706
- "channels.discord.token": "Discord Bot Token",
7707
- "channels.slack.botToken": "Slack Bot Token",
7708
- "channels.slack.appToken": "Slack App Token",
7709
- "channels.slack.userToken": "Slack User Token",
7710
- "channels.slack.userTokenReadOnly": "Slack User Token Read Only",
7711
- "channels.slack.thread.historyScope": "Slack Thread History Scope",
7712
- "channels.slack.thread.inheritParent": "Slack Thread Parent Inheritance",
7713
- "channels.mattermost.botToken": "Mattermost Bot Token",
7714
- "channels.mattermost.baseUrl": "Mattermost Base URL",
7715
- "channels.mattermost.chatmode": "Mattermost Chat Mode",
7716
- "channels.mattermost.oncharPrefixes": "Mattermost Onchar Prefixes",
7717
- "channels.mattermost.requireMention": "Mattermost Require Mention",
7718
- "channels.signal.account": "Signal Account",
7719
- "channels.imessage.cliPath": "iMessage CLI Path",
7720
- "agents.list[].skills": "Agent Skill Filter",
7721
- "agents.list[].identity.avatar": "Agent Avatar",
7722
- "discovery.mdns.mode": "mDNS Discovery Mode",
7723
- "plugins.enabled": "Enable Plugins",
7724
- "plugins.allow": "Plugin Allowlist",
7725
- "plugins.deny": "Plugin Denylist",
7726
- "plugins.load.paths": "Plugin Load Paths",
7727
- "plugins.slots": "Plugin Slots",
7728
- "plugins.slots.memory": "Memory Plugin",
7729
- "plugins.entries": "Plugin Entries",
7730
- "plugins.entries.*.enabled": "Plugin Enabled",
7731
- "plugins.entries.*.config": "Plugin Config",
7732
- "plugins.installs": "Plugin Install Records",
7733
- "plugins.installs.*.source": "Plugin Install Source",
7734
- "plugins.installs.*.spec": "Plugin Install Spec",
7735
- "plugins.installs.*.sourcePath": "Plugin Install Source Path",
7736
- "plugins.installs.*.installPath": "Plugin Install Path",
7737
- "plugins.installs.*.version": "Plugin Install Version",
7738
- "plugins.installs.*.installedAt": "Plugin Install Time"
7254
+ const IRC_FIELD_HELP = {
7255
+ "channels.irc.configWrites": "Allow IRC to write config in response to channel events/commands (default: true).",
7256
+ "channels.irc.dmPolicy": "Direct message access control (\"pairing\" recommended). \"open\" requires channels.irc.allowFrom=[\"*\"].",
7257
+ "channels.irc.nickserv.enabled": "Enable NickServ identify/register after connect (defaults to enabled when password is configured).",
7258
+ "channels.irc.nickserv.service": "NickServ service nick (default: NickServ).",
7259
+ "channels.irc.nickserv.password": "NickServ password used for IDENTIFY/REGISTER (sensitive).",
7260
+ "channels.irc.nickserv.passwordFile": "Optional file path containing NickServ password.",
7261
+ "channels.irc.nickserv.register": "If true, send NickServ REGISTER on every connect. Use once for initial registration, then disable.",
7262
+ "channels.irc.nickserv.registerEmail": "Email used with NickServ REGISTER (required when register=true)."
7739
7263
  };
7264
+
7265
+ //#endregion
7266
+ //#region src/config/schema.help.ts
7740
7267
  const FIELD_HELP = {
7741
7268
  "meta.lastTouchedVersion": "Auto-set when OpenClaw writes the config.",
7742
7269
  "meta.lastTouchedAt": "ISO timestamp of the last config write (auto-set).",
@@ -7810,6 +7337,7 @@ const FIELD_HELP = {
7810
7337
  "channels.slack.allowBots": "Allow bot-authored messages to trigger Slack replies (default: false).",
7811
7338
  "channels.slack.thread.historyScope": "Scope for Slack thread history context (\"thread\" isolates per thread; \"channel\" reuses channel history).",
7812
7339
  "channels.slack.thread.inheritParent": "If true, Slack thread sessions inherit the parent channel transcript (default: false).",
7340
+ "channels.slack.thread.initialHistoryLimit": "Maximum number of existing Slack thread messages to fetch when starting a new thread session (default: 20, set to 0 to disable).",
7813
7341
  "channels.mattermost.botToken": "Bot token from Mattermost System Console -> Integrations -> Bot Accounts.",
7814
7342
  "channels.mattermost.baseUrl": "Base URL for your Mattermost server (e.g., https://chat.example.com).",
7815
7343
  "channels.mattermost.chatmode": "Reply to channel messages on mention (\"oncall\"), on trigger chars (\">\" or \"!\") (\"onchar\"), or on every message (\"onmessage\").",
@@ -7835,7 +7363,7 @@ const FIELD_HELP = {
7835
7363
  "agents.defaults.memorySearch.remote.baseUrl": "Custom base URL for remote embeddings (OpenAI-compatible proxies or Gemini overrides).",
7836
7364
  "agents.defaults.memorySearch.remote.apiKey": "Custom API key for the remote embedding provider.",
7837
7365
  "agents.defaults.memorySearch.remote.headers": "Extra headers for remote embeddings (merged; remote overrides OpenAI headers).",
7838
- "agents.defaults.memorySearch.remote.batch.enabled": "Enable batch API for memory embeddings (OpenAI/Gemini/Voyage; default: false).",
7366
+ "agents.defaults.memorySearch.remote.batch.enabled": "Enable batch API for memory embeddings (OpenAI/Gemini; default: true).",
7839
7367
  "agents.defaults.memorySearch.remote.batch.wait": "Wait for batch completion when indexing (default: true).",
7840
7368
  "agents.defaults.memorySearch.remote.batch.concurrency": "Max concurrent embedding batch jobs for memory indexing (default: 2).",
7841
7369
  "agents.defaults.memorySearch.remote.batch.pollIntervalMs": "Polling interval in ms for batch status (default: 2000).",
@@ -7915,7 +7443,6 @@ const FIELD_HELP = {
7915
7443
  "commands.restart": "Allow /restart and gateway restart tool actions (default: false).",
7916
7444
  "commands.useAccessGroups": "Enforce access-group allowlists/policies for commands.",
7917
7445
  "commands.ownerAllowFrom": "Explicit owner allowlist for owner-only tools/commands. Use channel-native IDs (optionally prefixed like \"whatsapp:+15551234567\"). '*' is ignored.",
7918
- "commands.allowFrom": "Per-provider allowlist restricting who can use slash commands. If set, overrides the channel's allowFrom for command authorization. Use '*' key for global default; provider-specific keys (e.g. 'discord') override the global. Example: { \"*\": [\"user1\"], \"discord\": [\"user:123\"] }.",
7919
7446
  "session.dmScope": "DM session scoping: \"main\" keeps continuity; \"per-peer\", \"per-channel-peer\", or \"per-account-channel-peer\" isolates DM history (recommended for shared inboxes/multi-account).",
7920
7447
  "session.identityLinks": "Map canonical identities to provider-prefixed peer IDs for DM session linking (example: telegram:123456).",
7921
7448
  "channels.telegram.configWrites": "Allow Telegram to write config in response to channel events/commands (default: true).",
@@ -7926,6 +7453,7 @@ const FIELD_HELP = {
7926
7453
  "channels.signal.configWrites": "Allow Signal to write config in response to channel events/commands (default: true).",
7927
7454
  "channels.imessage.configWrites": "Allow iMessage to write config in response to channel events/commands (default: true).",
7928
7455
  "channels.msteams.configWrites": "Allow Microsoft Teams to write config in response to channel events/commands (default: true).",
7456
+ ...IRC_FIELD_HELP,
7929
7457
  "channels.discord.commands.native": "Override native commands for Discord (bool or \"auto\").",
7930
7458
  "channels.discord.commands.nativeSkills": "Override native skill commands for Discord (bool or \"auto\").",
7931
7459
  "channels.telegram.commands.native": "Override native commands for Telegram (bool or \"auto\").",
@@ -7966,6 +7494,361 @@ const FIELD_HELP = {
7966
7494
  "channels.discord.pluralkit.token": "Optional PluralKit token for resolving private systems or members.",
7967
7495
  "channels.slack.dm.policy": "Direct message access control (\"pairing\" recommended). \"open\" requires channels.slack.dm.allowFrom=[\"*\"]."
7968
7496
  };
7497
+
7498
+ //#endregion
7499
+ //#region src/config/schema.labels.ts
7500
+ const FIELD_LABELS = {
7501
+ "meta.lastTouchedVersion": "Config Last Touched Version",
7502
+ "meta.lastTouchedAt": "Config Last Touched At",
7503
+ "update.channel": "Update Channel",
7504
+ "update.checkOnStart": "Update Check on Start",
7505
+ "diagnostics.enabled": "Diagnostics Enabled",
7506
+ "diagnostics.flags": "Diagnostics Flags",
7507
+ "diagnostics.otel.enabled": "OpenTelemetry Enabled",
7508
+ "diagnostics.otel.endpoint": "OpenTelemetry Endpoint",
7509
+ "diagnostics.otel.protocol": "OpenTelemetry Protocol",
7510
+ "diagnostics.otel.headers": "OpenTelemetry Headers",
7511
+ "diagnostics.otel.serviceName": "OpenTelemetry Service Name",
7512
+ "diagnostics.otel.traces": "OpenTelemetry Traces Enabled",
7513
+ "diagnostics.otel.metrics": "OpenTelemetry Metrics Enabled",
7514
+ "diagnostics.otel.logs": "OpenTelemetry Logs Enabled",
7515
+ "diagnostics.otel.sampleRate": "OpenTelemetry Trace Sample Rate",
7516
+ "diagnostics.otel.flushIntervalMs": "OpenTelemetry Flush Interval (ms)",
7517
+ "diagnostics.cacheTrace.enabled": "Cache Trace Enabled",
7518
+ "diagnostics.cacheTrace.filePath": "Cache Trace File Path",
7519
+ "diagnostics.cacheTrace.includeMessages": "Cache Trace Include Messages",
7520
+ "diagnostics.cacheTrace.includePrompt": "Cache Trace Include Prompt",
7521
+ "diagnostics.cacheTrace.includeSystem": "Cache Trace Include System",
7522
+ "agents.list.*.identity.avatar": "Identity Avatar",
7523
+ "agents.list.*.skills": "Agent Skill Filter",
7524
+ "gateway.remote.url": "Remote Gateway URL",
7525
+ "gateway.remote.sshTarget": "Remote Gateway SSH Target",
7526
+ "gateway.remote.sshIdentity": "Remote Gateway SSH Identity",
7527
+ "gateway.remote.token": "Remote Gateway Token",
7528
+ "gateway.remote.password": "Remote Gateway Password",
7529
+ "gateway.remote.tlsFingerprint": "Remote Gateway TLS Fingerprint",
7530
+ "gateway.auth.token": "Gateway Token",
7531
+ "gateway.auth.password": "Gateway Password",
7532
+ "tools.media.image.enabled": "Enable Image Understanding",
7533
+ "tools.media.image.maxBytes": "Image Understanding Max Bytes",
7534
+ "tools.media.image.maxChars": "Image Understanding Max Chars",
7535
+ "tools.media.image.prompt": "Image Understanding Prompt",
7536
+ "tools.media.image.timeoutSeconds": "Image Understanding Timeout (sec)",
7537
+ "tools.media.image.attachments": "Image Understanding Attachment Policy",
7538
+ "tools.media.image.models": "Image Understanding Models",
7539
+ "tools.media.image.scope": "Image Understanding Scope",
7540
+ "tools.media.models": "Media Understanding Shared Models",
7541
+ "tools.media.concurrency": "Media Understanding Concurrency",
7542
+ "tools.media.audio.enabled": "Enable Audio Understanding",
7543
+ "tools.media.audio.maxBytes": "Audio Understanding Max Bytes",
7544
+ "tools.media.audio.maxChars": "Audio Understanding Max Chars",
7545
+ "tools.media.audio.prompt": "Audio Understanding Prompt",
7546
+ "tools.media.audio.timeoutSeconds": "Audio Understanding Timeout (sec)",
7547
+ "tools.media.audio.language": "Audio Understanding Language",
7548
+ "tools.media.audio.attachments": "Audio Understanding Attachment Policy",
7549
+ "tools.media.audio.models": "Audio Understanding Models",
7550
+ "tools.media.audio.scope": "Audio Understanding Scope",
7551
+ "tools.media.video.enabled": "Enable Video Understanding",
7552
+ "tools.media.video.maxBytes": "Video Understanding Max Bytes",
7553
+ "tools.media.video.maxChars": "Video Understanding Max Chars",
7554
+ "tools.media.video.prompt": "Video Understanding Prompt",
7555
+ "tools.media.video.timeoutSeconds": "Video Understanding Timeout (sec)",
7556
+ "tools.media.video.attachments": "Video Understanding Attachment Policy",
7557
+ "tools.media.video.models": "Video Understanding Models",
7558
+ "tools.media.video.scope": "Video Understanding Scope",
7559
+ "tools.links.enabled": "Enable Link Understanding",
7560
+ "tools.links.maxLinks": "Link Understanding Max Links",
7561
+ "tools.links.timeoutSeconds": "Link Understanding Timeout (sec)",
7562
+ "tools.links.models": "Link Understanding Models",
7563
+ "tools.links.scope": "Link Understanding Scope",
7564
+ "tools.profile": "Tool Profile",
7565
+ "tools.alsoAllow": "Tool Allowlist Additions",
7566
+ "agents.list[].tools.profile": "Agent Tool Profile",
7567
+ "agents.list[].tools.alsoAllow": "Agent Tool Allowlist Additions",
7568
+ "tools.byProvider": "Tool Policy by Provider",
7569
+ "agents.list[].tools.byProvider": "Agent Tool Policy by Provider",
7570
+ "tools.exec.applyPatch.enabled": "Enable apply_patch",
7571
+ "tools.exec.applyPatch.allowModels": "apply_patch Model Allowlist",
7572
+ "tools.exec.notifyOnExit": "Exec Notify On Exit",
7573
+ "tools.exec.approvalRunningNoticeMs": "Exec Approval Running Notice (ms)",
7574
+ "tools.exec.host": "Exec Host",
7575
+ "tools.exec.security": "Exec Security",
7576
+ "tools.exec.ask": "Exec Ask",
7577
+ "tools.exec.node": "Exec Node Binding",
7578
+ "tools.exec.pathPrepend": "Exec PATH Prepend",
7579
+ "tools.exec.safeBins": "Exec Safe Bins",
7580
+ "tools.message.allowCrossContextSend": "Allow Cross-Context Messaging",
7581
+ "tools.message.crossContext.allowWithinProvider": "Allow Cross-Context (Same Provider)",
7582
+ "tools.message.crossContext.allowAcrossProviders": "Allow Cross-Context (Across Providers)",
7583
+ "tools.message.crossContext.marker.enabled": "Cross-Context Marker",
7584
+ "tools.message.crossContext.marker.prefix": "Cross-Context Marker Prefix",
7585
+ "tools.message.crossContext.marker.suffix": "Cross-Context Marker Suffix",
7586
+ "tools.message.broadcast.enabled": "Enable Message Broadcast",
7587
+ "tools.web.search.enabled": "Enable Web Search Tool",
7588
+ "tools.web.search.provider": "Web Search Provider",
7589
+ "tools.web.search.apiKey": "Brave Search API Key",
7590
+ "tools.web.search.maxResults": "Web Search Max Results",
7591
+ "tools.web.search.timeoutSeconds": "Web Search Timeout (sec)",
7592
+ "tools.web.search.cacheTtlMinutes": "Web Search Cache TTL (min)",
7593
+ "tools.web.fetch.enabled": "Enable Web Fetch Tool",
7594
+ "tools.web.fetch.maxChars": "Web Fetch Max Chars",
7595
+ "tools.web.fetch.timeoutSeconds": "Web Fetch Timeout (sec)",
7596
+ "tools.web.fetch.cacheTtlMinutes": "Web Fetch Cache TTL (min)",
7597
+ "tools.web.fetch.maxRedirects": "Web Fetch Max Redirects",
7598
+ "tools.web.fetch.userAgent": "Web Fetch User-Agent",
7599
+ "gateway.controlUi.basePath": "Control UI Base Path",
7600
+ "gateway.controlUi.root": "Control UI Assets Root",
7601
+ "gateway.controlUi.allowedOrigins": "Control UI Allowed Origins",
7602
+ "gateway.controlUi.allowInsecureAuth": "Allow Insecure Control UI Auth",
7603
+ "gateway.controlUi.dangerouslyDisableDeviceAuth": "Dangerously Disable Control UI Device Auth",
7604
+ "gateway.http.endpoints.chatCompletions.enabled": "OpenAI Chat Completions Endpoint",
7605
+ "gateway.reload.mode": "Config Reload Mode",
7606
+ "gateway.reload.debounceMs": "Config Reload Debounce (ms)",
7607
+ "gateway.nodes.browser.mode": "Gateway Node Browser Mode",
7608
+ "gateway.nodes.browser.node": "Gateway Node Browser Pin",
7609
+ "gateway.nodes.allowCommands": "Gateway Node Allowlist (Extra Commands)",
7610
+ "gateway.nodes.denyCommands": "Gateway Node Denylist",
7611
+ "nodeHost.browserProxy.enabled": "Node Browser Proxy Enabled",
7612
+ "nodeHost.browserProxy.allowProfiles": "Node Browser Proxy Allowed Profiles",
7613
+ "skills.load.watch": "Watch Skills",
7614
+ "skills.load.watchDebounceMs": "Skills Watch Debounce (ms)",
7615
+ "agents.defaults.workspace": "Workspace",
7616
+ "agents.defaults.repoRoot": "Repo Root",
7617
+ "agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars",
7618
+ "agents.defaults.envelopeTimezone": "Envelope Timezone",
7619
+ "agents.defaults.envelopeTimestamp": "Envelope Timestamp",
7620
+ "agents.defaults.envelopeElapsed": "Envelope Elapsed",
7621
+ "agents.defaults.memorySearch": "Memory Search",
7622
+ "agents.defaults.memorySearch.enabled": "Enable Memory Search",
7623
+ "agents.defaults.memorySearch.sources": "Memory Search Sources",
7624
+ "agents.defaults.memorySearch.extraPaths": "Extra Memory Paths",
7625
+ "agents.defaults.memorySearch.experimental.sessionMemory": "Memory Search Session Index (Experimental)",
7626
+ "agents.defaults.memorySearch.provider": "Memory Search Provider",
7627
+ "agents.defaults.memorySearch.remote.baseUrl": "Remote Embedding Base URL",
7628
+ "agents.defaults.memorySearch.remote.apiKey": "Remote Embedding API Key",
7629
+ "agents.defaults.memorySearch.remote.headers": "Remote Embedding Headers",
7630
+ "agents.defaults.memorySearch.remote.batch.concurrency": "Remote Batch Concurrency",
7631
+ "agents.defaults.memorySearch.model": "Memory Search Model",
7632
+ "agents.defaults.memorySearch.fallback": "Memory Search Fallback",
7633
+ "agents.defaults.memorySearch.local.modelPath": "Local Embedding Model Path",
7634
+ "agents.defaults.memorySearch.store.path": "Memory Search Index Path",
7635
+ "agents.defaults.memorySearch.store.vector.enabled": "Memory Search Vector Index",
7636
+ "agents.defaults.memorySearch.store.vector.extensionPath": "Memory Search Vector Extension Path",
7637
+ "agents.defaults.memorySearch.chunking.tokens": "Memory Chunk Tokens",
7638
+ "agents.defaults.memorySearch.chunking.overlap": "Memory Chunk Overlap Tokens",
7639
+ "agents.defaults.memorySearch.sync.onSessionStart": "Index on Session Start",
7640
+ "agents.defaults.memorySearch.sync.onSearch": "Index on Search (Lazy)",
7641
+ "agents.defaults.memorySearch.sync.watch": "Watch Memory Files",
7642
+ "agents.defaults.memorySearch.sync.watchDebounceMs": "Memory Watch Debounce (ms)",
7643
+ "agents.defaults.memorySearch.sync.sessions.deltaBytes": "Session Delta Bytes",
7644
+ "agents.defaults.memorySearch.sync.sessions.deltaMessages": "Session Delta Messages",
7645
+ "agents.defaults.memorySearch.query.maxResults": "Memory Search Max Results",
7646
+ "agents.defaults.memorySearch.query.minScore": "Memory Search Min Score",
7647
+ "agents.defaults.memorySearch.query.hybrid.enabled": "Memory Search Hybrid",
7648
+ "agents.defaults.memorySearch.query.hybrid.vectorWeight": "Memory Search Vector Weight",
7649
+ "agents.defaults.memorySearch.query.hybrid.textWeight": "Memory Search Text Weight",
7650
+ "agents.defaults.memorySearch.query.hybrid.candidateMultiplier": "Memory Search Hybrid Candidate Multiplier",
7651
+ "agents.defaults.memorySearch.cache.enabled": "Memory Search Embedding Cache",
7652
+ "agents.defaults.memorySearch.cache.maxEntries": "Memory Search Embedding Cache Max Entries",
7653
+ memory: "Memory",
7654
+ "memory.backend": "Memory Backend",
7655
+ "memory.citations": "Memory Citations Mode",
7656
+ "memory.qmd.command": "QMD Binary",
7657
+ "memory.qmd.includeDefaultMemory": "QMD Include Default Memory",
7658
+ "memory.qmd.paths": "QMD Extra Paths",
7659
+ "memory.qmd.paths.path": "QMD Path",
7660
+ "memory.qmd.paths.pattern": "QMD Path Pattern",
7661
+ "memory.qmd.paths.name": "QMD Path Name",
7662
+ "memory.qmd.sessions.enabled": "QMD Session Indexing",
7663
+ "memory.qmd.sessions.exportDir": "QMD Session Export Directory",
7664
+ "memory.qmd.sessions.retentionDays": "QMD Session Retention (days)",
7665
+ "memory.qmd.update.interval": "QMD Update Interval",
7666
+ "memory.qmd.update.debounceMs": "QMD Update Debounce (ms)",
7667
+ "memory.qmd.update.onBoot": "QMD Update on Startup",
7668
+ "memory.qmd.update.waitForBootSync": "QMD Wait for Boot Sync",
7669
+ "memory.qmd.update.embedInterval": "QMD Embed Interval",
7670
+ "memory.qmd.update.commandTimeoutMs": "QMD Command Timeout (ms)",
7671
+ "memory.qmd.update.updateTimeoutMs": "QMD Update Timeout (ms)",
7672
+ "memory.qmd.update.embedTimeoutMs": "QMD Embed Timeout (ms)",
7673
+ "memory.qmd.limits.maxResults": "QMD Max Results",
7674
+ "memory.qmd.limits.maxSnippetChars": "QMD Max Snippet Chars",
7675
+ "memory.qmd.limits.maxInjectedChars": "QMD Max Injected Chars",
7676
+ "memory.qmd.limits.timeoutMs": "QMD Search Timeout (ms)",
7677
+ "memory.qmd.scope": "QMD Surface Scope",
7678
+ "auth.profiles": "Auth Profiles",
7679
+ "auth.order": "Auth Profile Order",
7680
+ "auth.cooldowns.billingBackoffHours": "Billing Backoff (hours)",
7681
+ "auth.cooldowns.billingBackoffHoursByProvider": "Billing Backoff Overrides",
7682
+ "auth.cooldowns.billingMaxHours": "Billing Backoff Cap (hours)",
7683
+ "auth.cooldowns.failureWindowHours": "Failover Window (hours)",
7684
+ "agents.defaults.models": "Models",
7685
+ "agents.defaults.model.primary": "Primary Model",
7686
+ "agents.defaults.model.fallbacks": "Model Fallbacks",
7687
+ "agents.defaults.imageModel.primary": "Image Model",
7688
+ "agents.defaults.imageModel.fallbacks": "Image Model Fallbacks",
7689
+ "agents.defaults.humanDelay.mode": "Human Delay Mode",
7690
+ "agents.defaults.humanDelay.minMs": "Human Delay Min (ms)",
7691
+ "agents.defaults.humanDelay.maxMs": "Human Delay Max (ms)",
7692
+ "agents.defaults.cliBackends": "CLI Backends",
7693
+ "commands.native": "Native Commands",
7694
+ "commands.nativeSkills": "Native Skill Commands",
7695
+ "commands.text": "Text Commands",
7696
+ "commands.bash": "Allow Bash Chat Command",
7697
+ "commands.bashForegroundMs": "Bash Foreground Window (ms)",
7698
+ "commands.config": "Allow /config",
7699
+ "commands.debug": "Allow /debug",
7700
+ "commands.restart": "Allow Restart",
7701
+ "commands.useAccessGroups": "Use Access Groups",
7702
+ "commands.ownerAllowFrom": "Command Owners",
7703
+ "ui.seamColor": "Accent Color",
7704
+ "ui.assistant.name": "Assistant Name",
7705
+ "ui.assistant.avatar": "Assistant Avatar",
7706
+ "browser.evaluateEnabled": "Browser Evaluate Enabled",
7707
+ "browser.snapshotDefaults": "Browser Snapshot Defaults",
7708
+ "browser.snapshotDefaults.mode": "Browser Snapshot Mode",
7709
+ "browser.remoteCdpTimeoutMs": "Remote CDP Timeout (ms)",
7710
+ "browser.remoteCdpHandshakeTimeoutMs": "Remote CDP Handshake Timeout (ms)",
7711
+ "session.dmScope": "DM Session Scope",
7712
+ "session.agentToAgent.maxPingPongTurns": "Agent-to-Agent Ping-Pong Turns",
7713
+ "messages.ackReaction": "Ack Reaction Emoji",
7714
+ "messages.ackReactionScope": "Ack Reaction Scope",
7715
+ "messages.inbound.debounceMs": "Inbound Message Debounce (ms)",
7716
+ "talk.apiKey": "Talk API Key",
7717
+ "channels.whatsapp": "WhatsApp",
7718
+ "channels.telegram": "Telegram",
7719
+ "channels.telegram.customCommands": "Telegram Custom Commands",
7720
+ "channels.discord": "Discord",
7721
+ "channels.slack": "Slack",
7722
+ "channels.mattermost": "Mattermost",
7723
+ "channels.signal": "Signal",
7724
+ "channels.imessage": "iMessage",
7725
+ "channels.bluebubbles": "BlueBubbles",
7726
+ "channels.msteams": "MS Teams",
7727
+ ...IRC_FIELD_LABELS,
7728
+ "channels.telegram.botToken": "Telegram Bot Token",
7729
+ "channels.telegram.dmPolicy": "Telegram DM Policy",
7730
+ "channels.telegram.streamMode": "Telegram Draft Stream Mode",
7731
+ "channels.telegram.draftChunk.minChars": "Telegram Draft Chunk Min Chars",
7732
+ "channels.telegram.draftChunk.maxChars": "Telegram Draft Chunk Max Chars",
7733
+ "channels.telegram.draftChunk.breakPreference": "Telegram Draft Chunk Break Preference",
7734
+ "channels.telegram.retry.attempts": "Telegram Retry Attempts",
7735
+ "channels.telegram.retry.minDelayMs": "Telegram Retry Min Delay (ms)",
7736
+ "channels.telegram.retry.maxDelayMs": "Telegram Retry Max Delay (ms)",
7737
+ "channels.telegram.retry.jitter": "Telegram Retry Jitter",
7738
+ "channels.telegram.network.autoSelectFamily": "Telegram autoSelectFamily",
7739
+ "channels.telegram.timeoutSeconds": "Telegram API Timeout (seconds)",
7740
+ "channels.telegram.capabilities.inlineButtons": "Telegram Inline Buttons",
7741
+ "channels.whatsapp.dmPolicy": "WhatsApp DM Policy",
7742
+ "channels.whatsapp.selfChatMode": "WhatsApp Self-Phone Mode",
7743
+ "channels.whatsapp.debounceMs": "WhatsApp Message Debounce (ms)",
7744
+ "channels.signal.dmPolicy": "Signal DM Policy",
7745
+ "channels.imessage.dmPolicy": "iMessage DM Policy",
7746
+ "channels.bluebubbles.dmPolicy": "BlueBubbles DM Policy",
7747
+ "channels.discord.dm.policy": "Discord DM Policy",
7748
+ "channels.discord.retry.attempts": "Discord Retry Attempts",
7749
+ "channels.discord.retry.minDelayMs": "Discord Retry Min Delay (ms)",
7750
+ "channels.discord.retry.maxDelayMs": "Discord Retry Max Delay (ms)",
7751
+ "channels.discord.retry.jitter": "Discord Retry Jitter",
7752
+ "channels.discord.maxLinesPerMessage": "Discord Max Lines Per Message",
7753
+ "channels.discord.intents.presence": "Discord Presence Intent",
7754
+ "channels.discord.intents.guildMembers": "Discord Guild Members Intent",
7755
+ "channels.discord.pluralkit.enabled": "Discord PluralKit Enabled",
7756
+ "channels.discord.pluralkit.token": "Discord PluralKit Token",
7757
+ "channels.slack.dm.policy": "Slack DM Policy",
7758
+ "channels.slack.allowBots": "Slack Allow Bot Messages",
7759
+ "channels.discord.token": "Discord Bot Token",
7760
+ "channels.slack.botToken": "Slack Bot Token",
7761
+ "channels.slack.appToken": "Slack App Token",
7762
+ "channels.slack.userToken": "Slack User Token",
7763
+ "channels.slack.userTokenReadOnly": "Slack User Token Read Only",
7764
+ "channels.slack.thread.historyScope": "Slack Thread History Scope",
7765
+ "channels.slack.thread.inheritParent": "Slack Thread Parent Inheritance",
7766
+ "channels.slack.thread.initialHistoryLimit": "Slack Thread Initial History Limit",
7767
+ "channels.mattermost.botToken": "Mattermost Bot Token",
7768
+ "channels.mattermost.baseUrl": "Mattermost Base URL",
7769
+ "channels.mattermost.chatmode": "Mattermost Chat Mode",
7770
+ "channels.mattermost.oncharPrefixes": "Mattermost Onchar Prefixes",
7771
+ "channels.mattermost.requireMention": "Mattermost Require Mention",
7772
+ "channels.signal.account": "Signal Account",
7773
+ "channels.imessage.cliPath": "iMessage CLI Path",
7774
+ "agents.list[].skills": "Agent Skill Filter",
7775
+ "agents.list[].identity.avatar": "Agent Avatar",
7776
+ "discovery.mdns.mode": "mDNS Discovery Mode",
7777
+ "plugins.enabled": "Enable Plugins",
7778
+ "plugins.allow": "Plugin Allowlist",
7779
+ "plugins.deny": "Plugin Denylist",
7780
+ "plugins.load.paths": "Plugin Load Paths",
7781
+ "plugins.slots": "Plugin Slots",
7782
+ "plugins.slots.memory": "Memory Plugin",
7783
+ "plugins.entries": "Plugin Entries",
7784
+ "plugins.entries.*.enabled": "Plugin Enabled",
7785
+ "plugins.entries.*.config": "Plugin Config",
7786
+ "plugins.installs": "Plugin Install Records",
7787
+ "plugins.installs.*.source": "Plugin Install Source",
7788
+ "plugins.installs.*.spec": "Plugin Install Spec",
7789
+ "plugins.installs.*.sourcePath": "Plugin Install Source Path",
7790
+ "plugins.installs.*.installPath": "Plugin Install Path",
7791
+ "plugins.installs.*.version": "Plugin Install Version",
7792
+ "plugins.installs.*.installedAt": "Plugin Install Time"
7793
+ };
7794
+
7795
+ //#endregion
7796
+ //#region src/config/schema.hints.ts
7797
+ const log$3 = createSubsystemLogger("config/schema");
7798
+ const GROUP_LABELS = {
7799
+ wizard: "Wizard",
7800
+ update: "Update",
7801
+ diagnostics: "Diagnostics",
7802
+ logging: "Logging",
7803
+ gateway: "Gateway",
7804
+ nodeHost: "Node Host",
7805
+ agents: "Agents",
7806
+ tools: "Tools",
7807
+ bindings: "Bindings",
7808
+ audio: "Audio",
7809
+ models: "Models",
7810
+ messages: "Messages",
7811
+ commands: "Commands",
7812
+ session: "Session",
7813
+ cron: "Cron",
7814
+ hooks: "Hooks",
7815
+ ui: "UI",
7816
+ browser: "Browser",
7817
+ talk: "Talk",
7818
+ channels: "Messaging Channels",
7819
+ skills: "Skills",
7820
+ plugins: "Plugins",
7821
+ discovery: "Discovery",
7822
+ presence: "Presence",
7823
+ voicewake: "Voice Wake"
7824
+ };
7825
+ const GROUP_ORDER = {
7826
+ wizard: 20,
7827
+ update: 25,
7828
+ diagnostics: 27,
7829
+ gateway: 30,
7830
+ nodeHost: 35,
7831
+ agents: 40,
7832
+ tools: 50,
7833
+ bindings: 55,
7834
+ audio: 60,
7835
+ models: 70,
7836
+ messages: 80,
7837
+ commands: 85,
7838
+ session: 90,
7839
+ cron: 100,
7840
+ hooks: 110,
7841
+ ui: 120,
7842
+ browser: 130,
7843
+ talk: 140,
7844
+ channels: 150,
7845
+ skills: 200,
7846
+ plugins: 205,
7847
+ discovery: 210,
7848
+ presence: 220,
7849
+ voicewake: 230,
7850
+ logging: 900
7851
+ };
7969
7852
  const FIELD_PLACEHOLDERS = {
7970
7853
  "gateway.remote.url": "ws://host:18789",
7971
7854
  "gateway.remote.tlsFingerprint": "sha256:ab12cd34…",
@@ -7976,15 +7859,430 @@ const FIELD_PLACEHOLDERS = {
7976
7859
  "channels.mattermost.baseUrl": "https://chat.example.com",
7977
7860
  "agents.list[].identity.avatar": "avatars/openclaw.png"
7978
7861
  };
7979
- const SENSITIVE_PATTERNS = [
7980
- /token/i,
7981
- /password/i,
7982
- /secret/i,
7983
- /api.?key/i
7984
- ];
7985
- function isSensitivePath(path) {
7986
- return SENSITIVE_PATTERNS.some((pattern) => pattern.test(path));
7862
+ /**
7863
+ * Non-sensitive field names that happen to match sensitive patterns.
7864
+ * These are explicitly excluded from redaction (plugin config) and
7865
+ * warnings about not being marked sensitive (base config).
7866
+ */
7867
+ const SENSITIVE_KEY_WHITELIST = new Set([
7868
+ "maxtokens",
7869
+ "maxoutputtokens",
7870
+ "maxinputtokens",
7871
+ "maxcompletiontokens",
7872
+ "contexttokens",
7873
+ "totaltokens",
7874
+ "tokencount",
7875
+ "tokenlimit",
7876
+ "tokenbudget",
7877
+ "passwordFile"
7878
+ ]);
7879
+ const SENSITIVE_PATTERNS = [
7880
+ /token$/i,
7881
+ /password/i,
7882
+ /secret/i,
7883
+ /api.?key/i
7884
+ ];
7885
+ function isSensitiveConfigPath(path) {
7886
+ return !Array.from(SENSITIVE_KEY_WHITELIST).some((suffix) => path.endsWith(suffix)) && SENSITIVE_PATTERNS.some((pattern) => pattern.test(path));
7887
+ }
7888
+ function buildBaseHints() {
7889
+ const hints = {};
7890
+ for (const [group, label] of Object.entries(GROUP_LABELS)) hints[group] = {
7891
+ label,
7892
+ group: label,
7893
+ order: GROUP_ORDER[group]
7894
+ };
7895
+ for (const [path, label] of Object.entries(FIELD_LABELS)) {
7896
+ const current = hints[path];
7897
+ hints[path] = current ? {
7898
+ ...current,
7899
+ label
7900
+ } : { label };
7901
+ }
7902
+ for (const [path, help] of Object.entries(FIELD_HELP)) {
7903
+ const current = hints[path];
7904
+ hints[path] = current ? {
7905
+ ...current,
7906
+ help
7907
+ } : { help };
7908
+ }
7909
+ for (const [path, placeholder] of Object.entries(FIELD_PLACEHOLDERS)) {
7910
+ const current = hints[path];
7911
+ hints[path] = current ? {
7912
+ ...current,
7913
+ placeholder
7914
+ } : { placeholder };
7915
+ }
7916
+ return hints;
7917
+ }
7918
+ function applySensitiveHints(hints, allowedKeys) {
7919
+ const next = { ...hints };
7920
+ for (const key of Object.keys(next)) {
7921
+ if (allowedKeys && !allowedKeys.has(key)) continue;
7922
+ if (next[key]?.sensitive !== void 0) continue;
7923
+ if (isSensitiveConfigPath(key)) next[key] = {
7924
+ ...next[key],
7925
+ sensitive: true
7926
+ };
7927
+ }
7928
+ return next;
7929
+ }
7930
+ function isUnwrappable(object) {
7931
+ return !!object && typeof object === "object" && "unwrap" in object && typeof object.unwrap === "function" && !(object instanceof z.ZodArray);
7932
+ }
7933
+ function mapSensitivePaths(schema, path, hints) {
7934
+ let next = { ...hints };
7935
+ let currentSchema = schema;
7936
+ let isSensitive = sensitive.has(currentSchema);
7937
+ while (isUnwrappable(currentSchema)) {
7938
+ currentSchema = currentSchema.unwrap();
7939
+ isSensitive ||= sensitive.has(currentSchema);
7940
+ }
7941
+ if (isSensitive) next[path] = {
7942
+ ...next[path],
7943
+ sensitive: true
7944
+ };
7945
+ else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$3.warn(`possibly sensitive key found: (${path})`);
7946
+ if (currentSchema instanceof z.ZodObject) {
7947
+ const shape = currentSchema.shape;
7948
+ for (const key in shape) {
7949
+ const nextPath = path ? `${path}.${key}` : key;
7950
+ next = mapSensitivePaths(shape[key], nextPath, next);
7951
+ }
7952
+ } else if (currentSchema instanceof z.ZodArray) {
7953
+ const nextPath = path ? `${path}[]` : "[]";
7954
+ next = mapSensitivePaths(currentSchema.element, nextPath, next);
7955
+ } else if (currentSchema instanceof z.ZodRecord) {
7956
+ const nextPath = path ? `${path}.*` : "*";
7957
+ next = mapSensitivePaths(currentSchema._def.valueType, nextPath, next);
7958
+ } else if (currentSchema instanceof z.ZodUnion || currentSchema instanceof z.ZodDiscriminatedUnion) for (const option of currentSchema.options) next = mapSensitivePaths(option, path, next);
7959
+ else if (currentSchema instanceof z.ZodIntersection) {
7960
+ next = mapSensitivePaths(currentSchema._def.left, path, next);
7961
+ next = mapSensitivePaths(currentSchema._def.right, path, next);
7962
+ }
7963
+ return next;
7964
+ }
7965
+
7966
+ //#endregion
7967
+ //#region src/config/redact-snapshot.ts
7968
+ const log$2 = createSubsystemLogger("config/redaction");
7969
+ const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
7970
+ function isSensitivePath(path) {
7971
+ if (path.endsWith("[]")) return isSensitiveConfigPath(path.slice(0, -2));
7972
+ else return isSensitiveConfigPath(path);
7973
+ }
7974
+ function isEnvVarPlaceholder(value) {
7975
+ return ENV_VAR_PLACEHOLDER_PATTERN.test(value.trim());
7976
+ }
7977
+ function isExtensionPath(path) {
7978
+ return path === "plugins" || path.startsWith("plugins.") || path === "channels" || path.startsWith("channels.");
7979
+ }
7980
+ function isExplicitlyNonSensitivePath(hints, paths) {
7981
+ if (!hints) return false;
7982
+ return paths.some((path) => hints[path]?.sensitive === false);
7983
+ }
7984
+ /**
7985
+ * Sentinel value used to replace sensitive config fields in gateway responses.
7986
+ * Write-side handlers (config.set, config.apply, config.patch) detect this
7987
+ * sentinel and restore the original value from the on-disk config, so a
7988
+ * round-trip through the Web UI does not corrupt credentials.
7989
+ */
7990
+ const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
7991
+ function buildRedactionLookup(hints) {
7992
+ let result = /* @__PURE__ */ new Set();
7993
+ for (const [path, hint] of Object.entries(hints)) {
7994
+ if (!hint.sensitive) continue;
7995
+ const parts = path.split(".");
7996
+ let joinedPath = parts.shift() ?? "";
7997
+ result.add(joinedPath);
7998
+ if (joinedPath.endsWith("[]")) result.add(joinedPath.slice(0, -2));
7999
+ for (const part of parts) {
8000
+ if (part.endsWith("[]")) result.add(`${joinedPath}.${part.slice(0, -2)}`);
8001
+ joinedPath = `${joinedPath}.${part}`;
8002
+ result.add(joinedPath);
8003
+ }
8004
+ }
8005
+ if (result.size !== 0) result.add("");
8006
+ return result;
8007
+ }
8008
+ /**
8009
+ * Deep-walk an object and replace string values at sensitive paths
8010
+ * with the redaction sentinel.
8011
+ */
8012
+ function redactObject(obj, hints) {
8013
+ if (hints) {
8014
+ const lookup = buildRedactionLookup(hints);
8015
+ return lookup.has("") ? redactObjectWithLookup(obj, lookup, "", [], hints) : redactObjectGuessing(obj, "", [], hints);
8016
+ } else return redactObjectGuessing(obj, "", []);
8017
+ }
8018
+ /**
8019
+ * Collect all sensitive string values from a config object.
8020
+ * Used for text-based redaction of the raw JSON5 source.
8021
+ */
8022
+ function collectSensitiveValues(obj, hints) {
8023
+ const result = [];
8024
+ if (hints) {
8025
+ const lookup = buildRedactionLookup(hints);
8026
+ if (lookup.has("")) redactObjectWithLookup(obj, lookup, "", result, hints);
8027
+ else redactObjectGuessing(obj, "", result, hints);
8028
+ } else redactObjectGuessing(obj, "", result);
8029
+ return result;
8030
+ }
8031
+ /**
8032
+ * Worker for redactObject() and collectSensitiveValues().
8033
+ * Used when there are ConfigUiHints available.
8034
+ */
8035
+ function redactObjectWithLookup(obj, lookup, prefix, values, hints) {
8036
+ if (obj === null || obj === void 0) return obj;
8037
+ if (Array.isArray(obj)) {
8038
+ const path = `${prefix}[]`;
8039
+ if (!lookup.has(path)) {
8040
+ if (!isExtensionPath(prefix)) return obj;
8041
+ return redactObjectGuessing(obj, prefix, values, hints);
8042
+ }
8043
+ return obj.map((item) => {
8044
+ if (typeof item === "string" && !isEnvVarPlaceholder(item)) {
8045
+ values.push(item);
8046
+ return REDACTED_SENTINEL;
8047
+ }
8048
+ return redactObjectWithLookup(item, lookup, path, values, hints);
8049
+ });
8050
+ }
8051
+ if (typeof obj === "object") {
8052
+ const result = {};
8053
+ for (const [key, value] of Object.entries(obj)) {
8054
+ const path = prefix ? `${prefix}.${key}` : key;
8055
+ const wildcardPath = prefix ? `${prefix}.*` : "*";
8056
+ let matched = false;
8057
+ for (const candidate of [path, wildcardPath]) {
8058
+ result[key] = value;
8059
+ if (lookup.has(candidate)) {
8060
+ matched = true;
8061
+ if (typeof value === "string" && !isEnvVarPlaceholder(value)) {
8062
+ result[key] = REDACTED_SENTINEL;
8063
+ values.push(value);
8064
+ } else if (typeof value === "object" && value !== null) result[key] = redactObjectWithLookup(value, lookup, candidate, values, hints);
8065
+ break;
8066
+ }
8067
+ }
8068
+ if (!matched && isExtensionPath(path)) {
8069
+ const markedNonSensitive = isExplicitlyNonSensitivePath(hints, [path, wildcardPath]);
8070
+ if (typeof value === "string" && !markedNonSensitive && isSensitivePath(path) && !isEnvVarPlaceholder(value)) {
8071
+ result[key] = REDACTED_SENTINEL;
8072
+ values.push(value);
8073
+ } else if (typeof value === "object" && value !== null) result[key] = redactObjectGuessing(value, path, values, hints);
8074
+ }
8075
+ }
8076
+ return result;
8077
+ }
8078
+ return obj;
8079
+ }
8080
+ /**
8081
+ * Worker for redactObject() and collectSensitiveValues().
8082
+ * Used when ConfigUiHints are NOT available.
8083
+ */
8084
+ function redactObjectGuessing(obj, prefix, values, hints) {
8085
+ if (obj === null || obj === void 0) return obj;
8086
+ if (Array.isArray(obj)) return obj.map((item) => {
8087
+ const path = `${prefix}[]`;
8088
+ if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && typeof item === "string" && !isEnvVarPlaceholder(item)) {
8089
+ values.push(item);
8090
+ return REDACTED_SENTINEL;
8091
+ }
8092
+ return redactObjectGuessing(item, path, values, hints);
8093
+ });
8094
+ if (typeof obj === "object") {
8095
+ const result = {};
8096
+ for (const [key, value] of Object.entries(obj)) {
8097
+ const dotPath = prefix ? `${prefix}.${key}` : key;
8098
+ if (!isExplicitlyNonSensitivePath(hints, [dotPath, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(dotPath) && typeof value === "string" && !isEnvVarPlaceholder(value)) {
8099
+ result[key] = REDACTED_SENTINEL;
8100
+ values.push(value);
8101
+ } else if (typeof value === "object" && value !== null) result[key] = redactObjectGuessing(value, dotPath, values, hints);
8102
+ else result[key] = value;
8103
+ }
8104
+ return result;
8105
+ }
8106
+ return obj;
8107
+ }
8108
+ /**
8109
+ * Replace known sensitive values in a raw JSON5 string with the sentinel.
8110
+ * Values are replaced longest-first to avoid partial matches.
8111
+ */
8112
+ function redactRawText(raw, config, hints) {
8113
+ const sensitiveValues = collectSensitiveValues(config, hints);
8114
+ sensitiveValues.sort((a, b) => b.length - a.length);
8115
+ let result = raw;
8116
+ for (const value of sensitiveValues) result = result.replaceAll(value, REDACTED_SENTINEL);
8117
+ return result;
8118
+ }
8119
+ /**
8120
+ * Returns a copy of the config snapshot with all sensitive fields
8121
+ * replaced by {@link REDACTED_SENTINEL}. The `hash` is preserved
8122
+ * (it tracks config identity, not content).
8123
+ *
8124
+ * Both `config` (the parsed object) and `raw` (the JSON5 source) are scrubbed
8125
+ * so no credential can leak through either path.
8126
+ *
8127
+ * When `uiHints` are provided, sensitivity is determined from the schema hints.
8128
+ * Without hints, falls back to regex-based detection via `isSensitivePath()`.
8129
+ */
8130
+ /**
8131
+ * Redact sensitive fields from a plain config object (not a full snapshot).
8132
+ * Used by write endpoints (config.set, config.patch, config.apply) to avoid
8133
+ * leaking credentials in their responses.
8134
+ */
8135
+ function redactConfigObject(value, uiHints) {
8136
+ return redactObject(value, uiHints);
8137
+ }
8138
+ function redactConfigSnapshot(snapshot, uiHints) {
8139
+ if (!snapshot.valid) return {
8140
+ ...snapshot,
8141
+ config: {},
8142
+ raw: null,
8143
+ parsed: null,
8144
+ resolved: {}
8145
+ };
8146
+ const redactedConfig = redactObject(snapshot.config, uiHints);
8147
+ const redactedRaw = snapshot.raw ? redactRawText(snapshot.raw, snapshot.config, uiHints) : null;
8148
+ const redactedParsed = snapshot.parsed ? redactObject(snapshot.parsed, uiHints) : snapshot.parsed;
8149
+ const redactedResolved = redactConfigObject(snapshot.resolved);
8150
+ return {
8151
+ ...snapshot,
8152
+ config: redactedConfig,
8153
+ raw: redactedRaw,
8154
+ parsed: redactedParsed,
8155
+ resolved: redactedResolved
8156
+ };
8157
+ }
8158
+ /**
8159
+ * Deep-walk `incoming` and replace any {@link REDACTED_SENTINEL} values
8160
+ * (on sensitive paths) with the corresponding value from `original`.
8161
+ *
8162
+ * This is called by config.set / config.apply / config.patch before writing,
8163
+ * so that credentials survive a Web UI round-trip unmodified.
8164
+ */
8165
+ function restoreRedactedValues(incoming, original, hints) {
8166
+ if (incoming === null || incoming === void 0) return {
8167
+ ok: false,
8168
+ error: "no input"
8169
+ };
8170
+ if (typeof incoming !== "object") return {
8171
+ ok: false,
8172
+ error: "input not an object"
8173
+ };
8174
+ try {
8175
+ if (hints) {
8176
+ const lookup = buildRedactionLookup(hints);
8177
+ if (lookup.has("")) return {
8178
+ ok: true,
8179
+ result: restoreRedactedValuesWithLookup(incoming, original, lookup, "", hints)
8180
+ };
8181
+ else return {
8182
+ ok: true,
8183
+ result: restoreRedactedValuesGuessing(incoming, original, "", hints)
8184
+ };
8185
+ } else return {
8186
+ ok: true,
8187
+ result: restoreRedactedValuesGuessing(incoming, original, "")
8188
+ };
8189
+ } catch (err) {
8190
+ if (err instanceof RedactionError) return {
8191
+ ok: false,
8192
+ humanReadableMessage: `Sentinel value "${REDACTED_SENTINEL}" in key ${err.key} is not valid as real data`
8193
+ };
8194
+ throw err;
8195
+ }
8196
+ }
8197
+ var RedactionError = class RedactionError extends Error {
8198
+ constructor(key) {
8199
+ super("internal error class---should never escape");
8200
+ this.key = key;
8201
+ this.name = "RedactionError";
8202
+ Object.setPrototypeOf(this, RedactionError.prototype);
8203
+ }
8204
+ };
8205
+ /**
8206
+ * Worker for restoreRedactedValues().
8207
+ * Used when there are ConfigUiHints available.
8208
+ */
8209
+ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hints) {
8210
+ if (incoming === null || incoming === void 0) return incoming;
8211
+ if (typeof incoming !== "object") return incoming;
8212
+ if (Array.isArray(incoming)) {
8213
+ const path = `${prefix}[]`;
8214
+ if (!lookup.has(path)) {
8215
+ if (!isExtensionPath(prefix)) return incoming;
8216
+ return restoreRedactedValuesGuessing(incoming, original, prefix, hints);
8217
+ }
8218
+ const origArr = Array.isArray(original) ? original : [];
8219
+ if (incoming.length < origArr.length) log$2.warn(`Redacted config array key ${path} has been truncated`);
8220
+ return incoming.map((item, i) => {
8221
+ if (item === REDACTED_SENTINEL) return origArr[i];
8222
+ return restoreRedactedValuesWithLookup(item, origArr[i], lookup, path, hints);
8223
+ });
8224
+ }
8225
+ const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
8226
+ const result = {};
8227
+ for (const [key, value] of Object.entries(incoming)) {
8228
+ result[key] = value;
8229
+ const path = prefix ? `${prefix}.${key}` : key;
8230
+ const wildcardPath = prefix ? `${prefix}.*` : "*";
8231
+ let matched = false;
8232
+ for (const candidate of [path, wildcardPath]) if (lookup.has(candidate)) {
8233
+ matched = true;
8234
+ if (value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
8235
+ else {
8236
+ log$2.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
8237
+ throw new RedactionError(candidate);
8238
+ }
8239
+ else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesWithLookup(value, orig[key], lookup, candidate, hints);
8240
+ break;
8241
+ }
8242
+ if (!matched && isExtensionPath(path)) {
8243
+ if (!isExplicitlyNonSensitivePath(hints, [path, wildcardPath]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
8244
+ else {
8245
+ log$2.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
8246
+ throw new RedactionError(path);
8247
+ }
8248
+ else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
8249
+ }
8250
+ }
8251
+ return result;
8252
+ }
8253
+ /**
8254
+ * Worker for restoreRedactedValues().
8255
+ * Used when ConfigUiHints are NOT available.
8256
+ */
8257
+ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
8258
+ if (incoming === null || incoming === void 0) return incoming;
8259
+ if (typeof incoming !== "object") return incoming;
8260
+ if (Array.isArray(incoming)) {
8261
+ const origArr = Array.isArray(original) ? original : [];
8262
+ return incoming.map((item, i) => {
8263
+ const path = `${prefix}[]`;
8264
+ if (incoming.length < origArr.length) log$2.warn(`Redacted config array key ${path} has been truncated`);
8265
+ if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && item === REDACTED_SENTINEL) return origArr[i];
8266
+ return restoreRedactedValuesGuessing(item, origArr[i], path, hints);
8267
+ });
8268
+ }
8269
+ const orig = original && typeof original === "object" && !Array.isArray(original) ? original : {};
8270
+ const result = {};
8271
+ for (const [key, value] of Object.entries(incoming)) {
8272
+ const path = prefix ? `${prefix}.${key}` : key;
8273
+ if (!isExplicitlyNonSensitivePath(hints, [path, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
8274
+ else {
8275
+ log$2.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
8276
+ throw new RedactionError(path);
8277
+ }
8278
+ else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
8279
+ else result[key] = value;
8280
+ }
8281
+ return result;
7987
8282
  }
8283
+
8284
+ //#endregion
8285
+ //#region src/config/schema.ts
7988
8286
  function cloneSchema(value) {
7989
8287
  if (typeof structuredClone === "function") return structuredClone(value);
7990
8288
  return JSON.parse(JSON.stringify(value));
@@ -8014,43 +8312,11 @@ function mergeObjectSchema(base, extension) {
8014
8312
  if (additional !== void 0) merged.additionalProperties = additional;
8015
8313
  return merged;
8016
8314
  }
8017
- function buildBaseHints() {
8018
- const hints = {};
8019
- for (const [group, label] of Object.entries(GROUP_LABELS)) hints[group] = {
8020
- label,
8021
- group: label,
8022
- order: GROUP_ORDER[group]
8023
- };
8024
- for (const [path, label] of Object.entries(FIELD_LABELS)) {
8025
- const current = hints[path];
8026
- hints[path] = current ? {
8027
- ...current,
8028
- label
8029
- } : { label };
8030
- }
8031
- for (const [path, help] of Object.entries(FIELD_HELP)) {
8032
- const current = hints[path];
8033
- hints[path] = current ? {
8034
- ...current,
8035
- help
8036
- } : { help };
8037
- }
8038
- for (const [path, placeholder] of Object.entries(FIELD_PLACEHOLDERS)) {
8039
- const current = hints[path];
8040
- hints[path] = current ? {
8041
- ...current,
8042
- placeholder
8043
- } : { placeholder };
8044
- }
8045
- return hints;
8046
- }
8047
- function applySensitiveHints(hints) {
8048
- const next = { ...hints };
8049
- for (const key of Object.keys(next)) if (isSensitivePath(key)) next[key] = {
8050
- ...next[key],
8051
- sensitive: true
8052
- };
8053
- return next;
8315
+ function collectExtensionHintKeys(hints, plugins, channels) {
8316
+ const pluginPrefixes = plugins.map((plugin) => plugin.id.trim()).filter(Boolean).map((id) => `plugins.entries.${id}`);
8317
+ const channelPrefixes = channels.map((channel) => channel.id.trim()).filter(Boolean).map((id) => `channels.${id}`);
8318
+ const prefixes = [...pluginPrefixes, ...channelPrefixes];
8319
+ return new Set(Object.keys(hints).filter((key) => prefixes.some((prefix) => key === prefix || key.startsWith(`${prefix}.`))));
8054
8320
  }
8055
8321
  function applyPluginHints(hints, plugins) {
8056
8322
  const next = { ...hints };
@@ -8200,7 +8466,7 @@ function buildBaseConfigSchema() {
8200
8466
  unrepresentable: "any"
8201
8467
  });
8202
8468
  schema.title = "OpenClawConfig";
8203
- const hints = applySensitiveHints(buildBaseHints());
8469
+ const hints = mapSensitivePaths(OpenClawSchema, "", buildBaseHints());
8204
8470
  const next = {
8205
8471
  schema: stripChannelSchema(schema),
8206
8472
  uiHints: hints,
@@ -8215,7 +8481,8 @@ function buildConfigSchema(params) {
8215
8481
  const plugins = params?.plugins ?? [];
8216
8482
  const channels = params?.channels ?? [];
8217
8483
  if (plugins.length === 0 && channels.length === 0) return base;
8218
- const mergedHints = applySensitiveHints(applyHeartbeatTargetHints(applyChannelHints(applyPluginHints(base.uiHints, plugins), channels), channels));
8484
+ const mergedWithoutSensitiveHints = applyHeartbeatTargetHints(applyChannelHints(applyPluginHints(base.uiHints, plugins), channels), channels);
8485
+ const mergedHints = applySensitiveHints(mergedWithoutSensitiveHints, collectExtensionHintKeys(mergedWithoutSensitiveHints, plugins, channels));
8219
8486
  const mergedSchema = applyChannelSchemas(applyPluginSchemas(base.schema, plugins), channels);
8220
8487
  return {
8221
8488
  ...base,
@@ -8250,45 +8517,49 @@ function requireConfigBaseHash(params, snapshot, respond) {
8250
8517
  }
8251
8518
  return true;
8252
8519
  }
8520
+ function loadSchemaWithPlugins() {
8521
+ const cfg = loadConfig();
8522
+ return buildConfigSchema({
8523
+ plugins: loadOpenClawPlugins({
8524
+ config: cfg,
8525
+ cache: true,
8526
+ workspaceDir: resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)),
8527
+ logger: {
8528
+ info: () => {},
8529
+ warn: () => {},
8530
+ error: () => {},
8531
+ debug: () => {}
8532
+ }
8533
+ }).plugins.map((plugin) => ({
8534
+ id: plugin.id,
8535
+ name: plugin.name,
8536
+ description: plugin.description,
8537
+ configUiHints: plugin.configUiHints,
8538
+ configSchema: plugin.configJsonSchema
8539
+ })),
8540
+ channels: listChannelPlugins().map((entry) => ({
8541
+ id: entry.id,
8542
+ label: entry.meta.label,
8543
+ description: entry.meta.blurb,
8544
+ configSchema: entry.configSchema?.schema,
8545
+ configUiHints: entry.configSchema?.uiHints
8546
+ }))
8547
+ });
8548
+ }
8253
8549
  const configHandlers = {
8254
8550
  "config.get": async ({ params, respond }) => {
8255
8551
  if (!validateConfigGetParams(params)) {
8256
8552
  respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid config.get params: ${formatValidationErrors(validateConfigGetParams.errors)}`));
8257
8553
  return;
8258
8554
  }
8259
- respond(true, redactConfigSnapshot(await readConfigFileSnapshot()), void 0);
8555
+ respond(true, redactConfigSnapshot(await readConfigFileSnapshot(), loadSchemaWithPlugins().uiHints), void 0);
8260
8556
  },
8261
8557
  "config.schema": ({ params, respond }) => {
8262
8558
  if (!validateConfigSchemaParams(params)) {
8263
8559
  respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid config.schema params: ${formatValidationErrors(validateConfigSchemaParams.errors)}`));
8264
8560
  return;
8265
8561
  }
8266
- const cfg = loadConfig();
8267
- respond(true, buildConfigSchema({
8268
- plugins: loadOpenClawPlugins({
8269
- config: cfg,
8270
- workspaceDir: resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)),
8271
- logger: {
8272
- info: () => {},
8273
- warn: () => {},
8274
- error: () => {},
8275
- debug: () => {}
8276
- }
8277
- }).plugins.map((plugin) => ({
8278
- id: plugin.id,
8279
- name: plugin.name,
8280
- description: plugin.description,
8281
- configUiHints: plugin.configUiHints,
8282
- configSchema: plugin.configJsonSchema
8283
- })),
8284
- channels: listChannelPlugins().map((entry) => ({
8285
- id: entry.id,
8286
- label: entry.meta.label,
8287
- description: entry.meta.blurb,
8288
- configSchema: entry.configSchema?.schema,
8289
- configUiHints: entry.configSchema?.uiHints
8290
- }))
8291
- }), void 0);
8562
+ respond(true, loadSchemaWithPlugins(), void 0);
8292
8563
  },
8293
8564
  "config.set": async ({ params, respond }) => {
8294
8565
  if (!validateConfigSetParams(params)) {
@@ -8307,23 +8578,22 @@ const configHandlers = {
8307
8578
  respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, parsedRes.error));
8308
8579
  return;
8309
8580
  }
8310
- const validated = validateConfigObjectWithPlugins(parsedRes.parsed);
8311
- if (!validated.ok) {
8312
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
8581
+ const schemaSet = loadSchemaWithPlugins();
8582
+ const restored = restoreRedactedValues(parsedRes.parsed, snapshot.config, schemaSet.uiHints);
8583
+ if (!restored.ok) {
8584
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restored.humanReadableMessage ?? "invalid config"));
8313
8585
  return;
8314
8586
  }
8315
- let restored;
8316
- try {
8317
- restored = restoreRedactedValues(validated.config, snapshot.config);
8318
- } catch (err) {
8319
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
8587
+ const validated = validateConfigObjectWithPlugins(restored.result);
8588
+ if (!validated.ok) {
8589
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
8320
8590
  return;
8321
8591
  }
8322
- await writeConfigFile(restored);
8592
+ await writeConfigFile(validated.config);
8323
8593
  respond(true, {
8324
8594
  ok: true,
8325
8595
  path: CONFIG_PATH,
8326
- config: redactConfigObject(restored)
8596
+ config: redactConfigObject(validated.config, schemaSet.uiHints)
8327
8597
  }, void 0);
8328
8598
  },
8329
8599
  "config.patch": async ({ params, respond }) => {
@@ -8352,14 +8622,13 @@ const configHandlers = {
8352
8622
  return;
8353
8623
  }
8354
8624
  const merged = applyMergePatch(snapshot.config, parsedRes.parsed);
8355
- let restoredMerge;
8356
- try {
8357
- restoredMerge = restoreRedactedValues(merged, snapshot.config);
8358
- } catch (err) {
8359
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
8625
+ const schemaPatch = loadSchemaWithPlugins();
8626
+ const restoredMerge = restoreRedactedValues(merged, snapshot.config, schemaPatch.uiHints);
8627
+ if (!restoredMerge.ok) {
8628
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restoredMerge.humanReadableMessage ?? "invalid config"));
8360
8629
  return;
8361
8630
  }
8362
- const validated = validateConfigObjectWithPlugins(applyLegacyMigrations(restoredMerge).next ?? restoredMerge);
8631
+ const validated = validateConfigObjectWithPlugins(applyLegacyMigrations(restoredMerge.result).next ?? restoredMerge.result);
8363
8632
  if (!validated.ok) {
8364
8633
  respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
8365
8634
  return;
@@ -8394,7 +8663,7 @@ const configHandlers = {
8394
8663
  respond(true, {
8395
8664
  ok: true,
8396
8665
  path: CONFIG_PATH,
8397
- config: redactConfigObject(validated.config),
8666
+ config: redactConfigObject(validated.config, schemaPatch.uiHints),
8398
8667
  restart,
8399
8668
  sentinel: {
8400
8669
  path: sentinelPath,
@@ -8419,19 +8688,18 @@ const configHandlers = {
8419
8688
  respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, parsedRes.error));
8420
8689
  return;
8421
8690
  }
8422
- const validated = validateConfigObjectWithPlugins(parsedRes.parsed);
8423
- if (!validated.ok) {
8424
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
8691
+ const schemaApply = loadSchemaWithPlugins();
8692
+ const restored = restoreRedactedValues(parsedRes.parsed, snapshot.config, schemaApply.uiHints);
8693
+ if (!restored.ok) {
8694
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, restored.humanReadableMessage ?? "invalid config"));
8425
8695
  return;
8426
8696
  }
8427
- let restoredApply;
8428
- try {
8429
- restoredApply = restoreRedactedValues(validated.config, snapshot.config);
8430
- } catch (err) {
8431
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(err instanceof Error ? err.message : err)));
8697
+ const validated = validateConfigObjectWithPlugins(restored.result);
8698
+ if (!validated.ok) {
8699
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid config", { details: { issues: validated.issues } }));
8432
8700
  return;
8433
8701
  }
8434
- await writeConfigFile(restoredApply);
8702
+ await writeConfigFile(validated.config);
8435
8703
  const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey?.trim() || void 0 : void 0;
8436
8704
  const note = typeof params.note === "string" ? params.note?.trim() || void 0 : void 0;
8437
8705
  const restartDelayMsRaw = params.restartDelayMs;
@@ -8461,7 +8729,7 @@ const configHandlers = {
8461
8729
  respond(true, {
8462
8730
  ok: true,
8463
8731
  path: CONFIG_PATH,
8464
- config: redactConfigObject(restoredApply),
8732
+ config: redactConfigObject(validated.config, schemaApply.uiHints),
8465
8733
  restart,
8466
8734
  sentinel: {
8467
8735
  path: sentinelPath,
@@ -8906,7 +9174,7 @@ async function verifyDeviceToken(params) {
8906
9174
  ok: false,
8907
9175
  reason: "token-revoked"
8908
9176
  };
8909
- if (entry.token !== params.token) return {
9177
+ if (!safeEqualSecret(params.token, entry.token)) return {
8910
9178
  ok: false,
8911
9179
  reason: "token-mismatch"
8912
9180
  };
@@ -9758,7 +10026,7 @@ const nodeHandlers = {
9758
10026
  const p = params;
9759
10027
  const payloadJSON = typeof p.payloadJSON === "string" ? p.payloadJSON : p.payload !== void 0 ? JSON.stringify(p.payload) : null;
9760
10028
  await respondUnavailableOnThrow(respond, async () => {
9761
- const { handleNodeEvent } = await import("./server-node-events-V_G9BRRw.js");
10029
+ const { handleNodeEvent } = await import("./server-node-events-BbHOZX3O.js");
9762
10030
  const nodeId = client?.connect?.device?.id ?? client?.connect?.client?.id ?? "node";
9763
10031
  await handleNodeEvent({
9764
10032
  deps: context.deps,
@@ -9826,8 +10094,13 @@ const sendHandlers = {
9826
10094
  return;
9827
10095
  }
9828
10096
  const to = request.to.trim();
9829
- const message = request.message.trim();
9830
- const mediaUrls = Array.isArray(request.mediaUrls) ? request.mediaUrls : void 0;
10097
+ const message = typeof request.message === "string" ? request.message.trim() : "";
10098
+ const mediaUrl = typeof request.mediaUrl === "string" && request.mediaUrl.trim().length > 0 ? request.mediaUrl.trim() : void 0;
10099
+ const mediaUrls = Array.isArray(request.mediaUrls) ? request.mediaUrls.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0) : void 0;
10100
+ if (!message && !mediaUrl && (mediaUrls?.length ?? 0) === 0) {
10101
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid send params: text or media is required"));
10102
+ return;
10103
+ }
9831
10104
  const channelInput = typeof request.channel === "string" ? request.channel : void 0;
9832
10105
  const normalizedChannel = channelInput ? normalizeChannelId(channelInput) : null;
9833
10106
  if (channelInput && !normalizedChannel) {
@@ -9859,7 +10132,7 @@ const sendHandlers = {
9859
10132
  const outboundDeps = context.deps ? createOutboundSendDeps(context.deps) : void 0;
9860
10133
  const mirrorPayloads = normalizeReplyPayloadsForDelivery([{
9861
10134
  text: message,
9862
- mediaUrl: request.mediaUrl,
10135
+ mediaUrl,
9863
10136
  mediaUrls
9864
10137
  }]);
9865
10138
  const mirrorText = mirrorPayloads.map((payload) => payload.text).filter(Boolean).join("\n");
@@ -9887,7 +10160,7 @@ const sendHandlers = {
9887
10160
  accountId,
9888
10161
  payloads: [{
9889
10162
  text: message,
9890
- mediaUrl: request.mediaUrl,
10163
+ mediaUrl,
9891
10164
  mediaUrls
9892
10165
  }],
9893
10166
  gifPlayback: request.gifPlayback,
@@ -10528,7 +10801,8 @@ const sessionsHandlers = {
10528
10801
  skillsSnapshot: entry?.skillsSnapshot,
10529
10802
  inputTokens: 0,
10530
10803
  outputTokens: 0,
10531
- totalTokens: 0
10804
+ totalTokens: 0,
10805
+ totalTokensFresh: true
10532
10806
  };
10533
10807
  store[primaryKey] = nextEntry;
10534
10808
  return nextEntry;
@@ -10672,6 +10946,7 @@ const sessionsHandlers = {
10672
10946
  delete entryToUpdate.inputTokens;
10673
10947
  delete entryToUpdate.outputTokens;
10674
10948
  delete entryToUpdate.totalTokens;
10949
+ delete entryToUpdate.totalTokensFresh;
10675
10950
  entryToUpdate.updatedAt = Date.now();
10676
10951
  });
10677
10952
  respond(true, {
@@ -10909,23 +11184,70 @@ const systemHandlers = {
10909
11184
 
10910
11185
  //#endregion
10911
11186
  //#region src/gateway/server-methods/talk.ts
10912
- const talkHandlers = { "talk.mode": ({ params, respond, context, client, isWebchatConnect }) => {
10913
- if (client && isWebchatConnect(client.connect) && !context.hasConnectedMobileNode()) {
10914
- respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "talk disabled: no connected iOS/Android nodes"));
10915
- return;
10916
- }
10917
- if (!validateTalkModeParams(params)) {
10918
- respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid talk.mode params: ${formatValidationErrors(validateTalkModeParams.errors)}`));
10919
- return;
11187
+ const ADMIN_SCOPE$2 = "operator.admin";
11188
+ const TALK_SECRETS_SCOPE = "operator.talk.secrets";
11189
+ function canReadTalkSecrets(client) {
11190
+ const scopes = Array.isArray(client?.connect?.scopes) ? client.connect.scopes : [];
11191
+ return scopes.includes(ADMIN_SCOPE$2) || scopes.includes(TALK_SECRETS_SCOPE);
11192
+ }
11193
+ function normalizeTalkConfigSection(value) {
11194
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
11195
+ const source = value;
11196
+ const talk = {};
11197
+ if (typeof source.voiceId === "string") talk.voiceId = source.voiceId;
11198
+ if (source.voiceAliases && typeof source.voiceAliases === "object" && !Array.isArray(source.voiceAliases)) {
11199
+ const aliases = {};
11200
+ for (const [alias, id] of Object.entries(source.voiceAliases)) {
11201
+ if (typeof id !== "string") continue;
11202
+ aliases[alias] = id;
11203
+ }
11204
+ if (Object.keys(aliases).length > 0) talk.voiceAliases = aliases;
11205
+ }
11206
+ if (typeof source.modelId === "string") talk.modelId = source.modelId;
11207
+ if (typeof source.outputFormat === "string") talk.outputFormat = source.outputFormat;
11208
+ if (typeof source.apiKey === "string") talk.apiKey = source.apiKey;
11209
+ if (typeof source.interruptOnSpeech === "boolean") talk.interruptOnSpeech = source.interruptOnSpeech;
11210
+ return Object.keys(talk).length > 0 ? talk : void 0;
11211
+ }
11212
+ const talkHandlers = {
11213
+ "talk.config": async ({ params, respond, client }) => {
11214
+ if (!validateTalkConfigParams(params)) {
11215
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid talk.config params: ${formatValidationErrors(validateTalkConfigParams.errors)}`));
11216
+ return;
11217
+ }
11218
+ const includeSecrets = Boolean(params.includeSecrets);
11219
+ if (includeSecrets && !canReadTalkSecrets(client)) {
11220
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `missing scope: ${TALK_SECRETS_SCOPE}`));
11221
+ return;
11222
+ }
11223
+ const snapshot = await readConfigFileSnapshot();
11224
+ const configPayload = {};
11225
+ const talk = normalizeTalkConfigSection(includeSecrets ? snapshot.config.talk : redactConfigObject(snapshot.config.talk));
11226
+ if (talk) configPayload.talk = talk;
11227
+ const sessionMainKey = snapshot.config.session?.mainKey;
11228
+ if (typeof sessionMainKey === "string") configPayload.session = { mainKey: sessionMainKey };
11229
+ const seamColor = snapshot.config.ui?.seamColor;
11230
+ if (typeof seamColor === "string") configPayload.ui = { seamColor };
11231
+ respond(true, { config: configPayload }, void 0);
11232
+ },
11233
+ "talk.mode": ({ params, respond, context, client, isWebchatConnect }) => {
11234
+ if (client && isWebchatConnect(client.connect) && !context.hasConnectedMobileNode()) {
11235
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "talk disabled: no connected iOS/Android nodes"));
11236
+ return;
11237
+ }
11238
+ if (!validateTalkModeParams(params)) {
11239
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid talk.mode params: ${formatValidationErrors(validateTalkModeParams.errors)}`));
11240
+ return;
11241
+ }
11242
+ const payload = {
11243
+ enabled: params.enabled,
11244
+ phase: params.phase ?? null,
11245
+ ts: Date.now()
11246
+ };
11247
+ context.broadcast("talk.mode", payload, { dropIfSlow: true });
11248
+ respond(true, payload, void 0);
10920
11249
  }
10921
- const payload = {
10922
- enabled: params.enabled,
10923
- phase: params.phase ?? null,
10924
- ts: Date.now()
10925
- };
10926
- context.broadcast("talk.mode", payload, { dropIfSlow: true });
10927
- respond(true, payload, void 0);
10928
- } };
11250
+ };
10929
11251
 
10930
11252
  //#endregion
10931
11253
  //#region src/gateway/server-methods/tts.ts
@@ -11258,7 +11580,7 @@ const usageHandlers = {
11258
11580
  const limit = typeof p.limit === "number" && Number.isFinite(p.limit) ? p.limit : 50;
11259
11581
  const includeContextWeight = p.includeContextWeight ?? false;
11260
11582
  const specificKey = typeof p.key === "string" ? p.key.trim() : null;
11261
- const { store } = loadCombinedSessionStoreForGateway(config);
11583
+ const { storePath, store } = loadCombinedSessionStoreForGateway(config);
11262
11584
  const now = Date.now();
11263
11585
  const mergedEntries = [];
11264
11586
  if (specificKey) {
@@ -11278,7 +11600,16 @@ const usageHandlers = {
11278
11600
  const resolvedStoreKey = storeMatch?.key ?? storeByIdMatch?.key ?? specificKey;
11279
11601
  const storeEntry = storeMatch?.entry ?? storeByIdMatch?.entry;
11280
11602
  const sessionId = storeEntry?.sessionId ?? keyRest;
11281
- const sessionFile = resolveSessionFilePath(sessionId, storeEntry, { agentId: agentIdFromKey });
11603
+ let sessionFile;
11604
+ try {
11605
+ sessionFile = resolveSessionFilePath(sessionId, storeEntry, resolveSessionFilePathOptions({
11606
+ storePath: storePath !== "(multiple)" ? storePath : void 0,
11607
+ agentId: agentIdFromKey
11608
+ }));
11609
+ } catch {
11610
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session reference: ${specificKey}`));
11611
+ return;
11612
+ }
11282
11613
  try {
11283
11614
  const stats = fs.statSync(sessionFile);
11284
11615
  if (stats.isFile()) mergedEntries.push({
@@ -11386,11 +11717,13 @@ const usageHandlers = {
11386
11717
  target.missingCostEntries += source.missingCostEntries;
11387
11718
  };
11388
11719
  for (const merged of limitedEntries) {
11720
+ const agentId = parseAgentSessionKey(merged.key)?.agentId;
11389
11721
  const usage = await loadSessionCostSummary({
11390
11722
  sessionId: merged.sessionId,
11391
11723
  sessionEntry: merged.storeEntry,
11392
11724
  sessionFile: merged.sessionFile,
11393
11725
  config,
11726
+ agentId,
11394
11727
  startMs,
11395
11728
  endMs
11396
11729
  });
@@ -11407,7 +11740,6 @@ const usageHandlers = {
11407
11740
  aggregateTotals.cacheWriteCost += usage.cacheWriteCost;
11408
11741
  aggregateTotals.missingCostEntries += usage.missingCostEntries;
11409
11742
  }
11410
- const agentId = parseAgentSessionKey(merged.key)?.agentId;
11411
11743
  const channel = merged.storeEntry?.channel ?? merged.storeEntry?.origin?.provider;
11412
11744
  const chatType = merged.storeEntry?.chatType ?? merged.storeEntry?.origin?.chatType;
11413
11745
  if (usage) {
@@ -11604,15 +11936,27 @@ const usageHandlers = {
11604
11936
  return;
11605
11937
  }
11606
11938
  const config = loadConfig();
11607
- const { entry } = loadSessionEntry(key);
11939
+ const { entry, storePath } = loadSessionEntry(key);
11608
11940
  const parsed = parseAgentSessionKey(key);
11609
11941
  const agentId = parsed?.agentId;
11610
11942
  const rawSessionId = parsed?.rest ?? key;
11943
+ const sessionId = entry?.sessionId ?? rawSessionId;
11944
+ let sessionFile;
11945
+ try {
11946
+ sessionFile = resolveSessionFilePath(sessionId, entry, resolveSessionFilePathOptions({
11947
+ storePath,
11948
+ agentId
11949
+ }));
11950
+ } catch {
11951
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
11952
+ return;
11953
+ }
11611
11954
  const timeseries = await loadSessionUsageTimeSeries({
11612
- sessionId: entry?.sessionId ?? rawSessionId,
11955
+ sessionId,
11613
11956
  sessionEntry: entry,
11614
- sessionFile: entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId }),
11957
+ sessionFile,
11615
11958
  config,
11959
+ agentId,
11616
11960
  maxPoints: 200
11617
11961
  });
11618
11962
  if (!timeseries) {
@@ -11629,18 +11973,28 @@ const usageHandlers = {
11629
11973
  }
11630
11974
  const limit = typeof params?.limit === "number" && Number.isFinite(params.limit) ? Math.min(params.limit, 1e3) : 200;
11631
11975
  const config = loadConfig();
11632
- const { entry } = loadSessionEntry(key);
11976
+ const { entry, storePath } = loadSessionEntry(key);
11633
11977
  const parsed = parseAgentSessionKey(key);
11634
11978
  const agentId = parsed?.agentId;
11635
11979
  const rawSessionId = parsed?.rest ?? key;
11636
11980
  const sessionId = entry?.sessionId ?? rawSessionId;
11637
- const sessionFile = entry?.sessionFile ?? resolveSessionFilePath(rawSessionId, entry, { agentId });
11638
- const { loadSessionLogs } = await import("./session-cost-usage-CcCEQNuc.js").then((n) => n.a);
11981
+ let sessionFile;
11982
+ try {
11983
+ sessionFile = resolveSessionFilePath(sessionId, entry, resolveSessionFilePathOptions({
11984
+ storePath,
11985
+ agentId
11986
+ }));
11987
+ } catch {
11988
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `Invalid session key: ${key}`));
11989
+ return;
11990
+ }
11991
+ const { loadSessionLogs } = await import("./session-cost-usage-B-tyjp76.js").then((n) => n.a);
11639
11992
  respond(true, { logs: await loadSessionLogs({
11640
11993
  sessionId,
11641
11994
  sessionEntry: entry,
11642
11995
  sessionFile,
11643
11996
  config,
11997
+ agentId,
11644
11998
  limit
11645
11999
  }) ?? [] }, void 0);
11646
12000
  }
@@ -12069,7 +12423,9 @@ const READ_METHODS = new Set([
12069
12423
  "last-heartbeat",
12070
12424
  "node.list",
12071
12425
  "node.describe",
12072
- "chat.history"
12426
+ "chat.history",
12427
+ "config.get",
12428
+ "talk.config"
12073
12429
  ]);
12074
12430
  const WRITE_METHODS = new Set([
12075
12431
  "send",
@@ -12457,6 +12813,7 @@ function normalizeHookMapping(mapping, index, transformsDir) {
12457
12813
  action,
12458
12814
  wakeMode,
12459
12815
  name: mapping.name,
12816
+ agentId: mapping.agentId?.trim() || void 0,
12460
12817
  sessionKey: mapping.sessionKey,
12461
12818
  messageTemplate: mapping.messageTemplate,
12462
12819
  textTemplate: mapping.textTemplate,
@@ -12495,6 +12852,7 @@ function buildActionFromMapping(mapping, ctx) {
12495
12852
  kind: "agent",
12496
12853
  message: renderTemplate(mapping.messageTemplate ?? "", ctx),
12497
12854
  name: renderOptional(mapping.name, ctx),
12855
+ agentId: mapping.agentId,
12498
12856
  wakeMode: mapping.wakeMode ?? "now",
12499
12857
  sessionKey: renderOptional(mapping.sessionKey, ctx),
12500
12858
  deliver: mapping.deliver,
@@ -12523,6 +12881,7 @@ function mergeAction(base, override, defaultAction) {
12523
12881
  message: typeof override.message === "string" ? override.message : baseAgent?.message ?? "",
12524
12882
  wakeMode: override.wakeMode === "next-heartbeat" ? "next-heartbeat" : baseAgent?.wakeMode ?? "now",
12525
12883
  name: override.name ?? baseAgent?.name,
12884
+ agentId: override.agentId ?? baseAgent?.agentId,
12526
12885
  sessionKey: override.sessionKey ?? baseAgent?.sessionKey,
12527
12886
  deliver: typeof override.deliver === "boolean" ? override.deliver : baseAgent?.deliver,
12528
12887
  allowUnsafeExternalContent: typeof override.allowUnsafeExternalContent === "boolean" ? override.allowUnsafeExternalContent : baseAgent?.allowUnsafeExternalContent,
@@ -12635,13 +12994,76 @@ function resolveHooksConfig(cfg) {
12635
12994
  const withSlash = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
12636
12995
  const trimmed = withSlash.length > 1 ? withSlash.replace(/\/+$/, "") : withSlash;
12637
12996
  if (trimmed === "/") throw new Error("hooks.path may not be '/'");
12997
+ const maxBodyBytes = cfg.hooks?.maxBodyBytes && cfg.hooks.maxBodyBytes > 0 ? cfg.hooks.maxBodyBytes : DEFAULT_HOOKS_MAX_BODY_BYTES;
12998
+ const mappings = resolveHookMappings(cfg.hooks);
12999
+ const defaultAgentId = resolveDefaultAgentId(cfg);
13000
+ const knownAgentIds = resolveKnownAgentIds(cfg, defaultAgentId);
13001
+ const allowedAgentIds = resolveAllowedAgentIds(cfg.hooks?.allowedAgentIds);
13002
+ const defaultSessionKey = resolveSessionKey$1(cfg.hooks?.defaultSessionKey);
13003
+ const allowedSessionKeyPrefixes = resolveAllowedSessionKeyPrefixes(cfg.hooks?.allowedSessionKeyPrefixes);
13004
+ if (defaultSessionKey && allowedSessionKeyPrefixes && !isSessionKeyAllowedByPrefix(defaultSessionKey, allowedSessionKeyPrefixes)) throw new Error("hooks.defaultSessionKey must match hooks.allowedSessionKeyPrefixes");
13005
+ if (!defaultSessionKey && allowedSessionKeyPrefixes && !isSessionKeyAllowedByPrefix("hook:example", allowedSessionKeyPrefixes)) throw new Error("hooks.allowedSessionKeyPrefixes must include 'hook:' when hooks.defaultSessionKey is unset");
12638
13006
  return {
12639
13007
  basePath: trimmed,
12640
13008
  token,
12641
- maxBodyBytes: cfg.hooks?.maxBodyBytes && cfg.hooks.maxBodyBytes > 0 ? cfg.hooks.maxBodyBytes : DEFAULT_HOOKS_MAX_BODY_BYTES,
12642
- mappings: resolveHookMappings(cfg.hooks)
13009
+ maxBodyBytes,
13010
+ mappings,
13011
+ agentPolicy: {
13012
+ defaultAgentId,
13013
+ knownAgentIds,
13014
+ allowedAgentIds
13015
+ },
13016
+ sessionPolicy: {
13017
+ defaultSessionKey,
13018
+ allowRequestSessionKey: cfg.hooks?.allowRequestSessionKey === true,
13019
+ allowedSessionKeyPrefixes
13020
+ }
12643
13021
  };
12644
13022
  }
13023
+ function resolveKnownAgentIds(cfg, defaultAgentId) {
13024
+ const known = new Set(listAgentIds(cfg));
13025
+ known.add(defaultAgentId);
13026
+ return known;
13027
+ }
13028
+ function resolveAllowedAgentIds(raw) {
13029
+ if (!Array.isArray(raw)) return;
13030
+ const allowed = /* @__PURE__ */ new Set();
13031
+ let hasWildcard = false;
13032
+ for (const entry of raw) {
13033
+ const trimmed = entry.trim();
13034
+ if (!trimmed) continue;
13035
+ if (trimmed === "*") {
13036
+ hasWildcard = true;
13037
+ break;
13038
+ }
13039
+ allowed.add(normalizeAgentId(trimmed));
13040
+ }
13041
+ if (hasWildcard) return;
13042
+ return allowed;
13043
+ }
13044
+ function resolveSessionKey$1(raw) {
13045
+ const value = raw?.trim();
13046
+ return value ? value : void 0;
13047
+ }
13048
+ function normalizeSessionKeyPrefix(raw) {
13049
+ const value = raw.trim().toLowerCase();
13050
+ return value ? value : void 0;
13051
+ }
13052
+ function resolveAllowedSessionKeyPrefixes(raw) {
13053
+ if (!Array.isArray(raw)) return;
13054
+ const set = /* @__PURE__ */ new Set();
13055
+ for (const prefix of raw) {
13056
+ const normalized = normalizeSessionKeyPrefix(prefix);
13057
+ if (!normalized) continue;
13058
+ set.add(normalized);
13059
+ }
13060
+ return set.size > 0 ? Array.from(set) : void 0;
13061
+ }
13062
+ function isSessionKeyAllowedByPrefix(sessionKey, prefixes) {
13063
+ const normalized = sessionKey.trim().toLowerCase();
13064
+ if (!normalized) return false;
13065
+ return prefixes.some((prefix) => normalized.startsWith(prefix));
13066
+ }
12645
13067
  function extractHookToken(req) {
12646
13068
  const auth = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
12647
13069
  if (auth.toLowerCase().startsWith("bearer ")) {
@@ -12723,20 +13145,71 @@ function normalizeWakePayload(payload) {
12723
13145
  }
12724
13146
  };
12725
13147
  }
12726
- const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin) => plugin.id)];
12727
- const getHookChannelSet = () => new Set(listHookChannelValues());
12728
- const getHookChannelError = () => `channel must be ${listHookChannelValues().join("|")}`;
12729
- function resolveHookChannel(raw) {
12730
- if (raw === void 0) return "last";
12731
- if (typeof raw !== "string") return null;
12732
- const normalized = normalizeMessageChannel(raw);
12733
- if (!normalized || !getHookChannelSet().has(normalized)) return null;
12734
- return normalized;
12735
- }
12736
- function resolveHookDeliver(raw) {
12737
- return raw !== false;
12738
- }
12739
- function normalizeAgentPayload(payload, opts) {
13148
+ const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin) => plugin.id)];
13149
+ const getHookChannelSet = () => new Set(listHookChannelValues());
13150
+ const getHookChannelError = () => `channel must be ${listHookChannelValues().join("|")}`;
13151
+ function resolveHookChannel(raw) {
13152
+ if (raw === void 0) return "last";
13153
+ if (typeof raw !== "string") return null;
13154
+ const normalized = normalizeMessageChannel(raw);
13155
+ if (!normalized || !getHookChannelSet().has(normalized)) return null;
13156
+ return normalized;
13157
+ }
13158
+ function resolveHookDeliver(raw) {
13159
+ return raw !== false;
13160
+ }
13161
+ function resolveHookTargetAgentId(hooksConfig, agentId) {
13162
+ const raw = agentId?.trim();
13163
+ if (!raw) return;
13164
+ const normalized = normalizeAgentId(raw);
13165
+ if (hooksConfig.agentPolicy.knownAgentIds.has(normalized)) return normalized;
13166
+ return hooksConfig.agentPolicy.defaultAgentId;
13167
+ }
13168
+ function isHookAgentAllowed(hooksConfig, agentId) {
13169
+ const raw = agentId?.trim();
13170
+ if (!raw) return true;
13171
+ const allowed = hooksConfig.agentPolicy.allowedAgentIds;
13172
+ if (allowed === void 0) return true;
13173
+ const resolved = resolveHookTargetAgentId(hooksConfig, raw);
13174
+ return resolved ? allowed.has(resolved) : false;
13175
+ }
13176
+ const getHookAgentPolicyError = () => "agentId is not allowed by hooks.allowedAgentIds";
13177
+ const getHookSessionKeyRequestPolicyError = () => "sessionKey is disabled for external /hooks/agent payloads; set hooks.allowRequestSessionKey=true to enable";
13178
+ const getHookSessionKeyPrefixError = (prefixes) => `sessionKey must start with one of: ${prefixes.join(", ")}`;
13179
+ function resolveHookSessionKey(params) {
13180
+ const requested = resolveSessionKey$1(params.sessionKey);
13181
+ if (requested) {
13182
+ if (params.source === "request" && !params.hooksConfig.sessionPolicy.allowRequestSessionKey) return {
13183
+ ok: false,
13184
+ error: getHookSessionKeyRequestPolicyError()
13185
+ };
13186
+ const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
13187
+ if (allowedPrefixes && !isSessionKeyAllowedByPrefix(requested, allowedPrefixes)) return {
13188
+ ok: false,
13189
+ error: getHookSessionKeyPrefixError(allowedPrefixes)
13190
+ };
13191
+ return {
13192
+ ok: true,
13193
+ value: requested
13194
+ };
13195
+ }
13196
+ const defaultSessionKey = params.hooksConfig.sessionPolicy.defaultSessionKey;
13197
+ if (defaultSessionKey) return {
13198
+ ok: true,
13199
+ value: defaultSessionKey
13200
+ };
13201
+ const generated = `hook:${(params.idFactory ?? randomUUID)()}`;
13202
+ const allowedPrefixes = params.hooksConfig.sessionPolicy.allowedSessionKeyPrefixes;
13203
+ if (allowedPrefixes && !isSessionKeyAllowedByPrefix(generated, allowedPrefixes)) return {
13204
+ ok: false,
13205
+ error: getHookSessionKeyPrefixError(allowedPrefixes)
13206
+ };
13207
+ return {
13208
+ ok: true,
13209
+ value: generated
13210
+ };
13211
+ }
13212
+ function normalizeAgentPayload(payload) {
12740
13213
  const message = typeof payload.message === "string" ? payload.message.trim() : "";
12741
13214
  if (!message) return {
12742
13215
  ok: false,
@@ -12744,10 +13217,11 @@ function normalizeAgentPayload(payload, opts) {
12744
13217
  };
12745
13218
  const nameRaw = payload.name;
12746
13219
  const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : "Hook";
13220
+ const agentIdRaw = payload.agentId;
13221
+ const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : void 0;
12747
13222
  const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now";
12748
13223
  const sessionKeyRaw = payload.sessionKey;
12749
- const idFactory = opts?.idFactory ?? randomUUID;
12750
- const sessionKey = typeof sessionKeyRaw === "string" && sessionKeyRaw.trim() ? sessionKeyRaw.trim() : `hook:${idFactory()}`;
13224
+ const sessionKey = typeof sessionKeyRaw === "string" && sessionKeyRaw.trim() ? sessionKeyRaw.trim() : void 0;
12751
13225
  const channel = resolveHookChannel(payload.channel);
12752
13226
  if (!channel) return {
12753
13227
  ok: false,
@@ -12770,6 +13244,7 @@ function normalizeAgentPayload(payload, opts) {
12770
13244
  value: {
12771
13245
  message,
12772
13246
  name,
13247
+ agentId,
12773
13248
  wakeMode,
12774
13249
  sessionKey,
12775
13250
  deliver,
@@ -12787,7 +13262,7 @@ function normalizeAgentPayload(payload, opts) {
12787
13262
  async function startBrowserControlServerIfEnabled() {
12788
13263
  if (isTruthyEnvValue(process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER)) return null;
12789
13264
  const override = process.env.OPENCLAW_BROWSER_CONTROL_MODULE?.trim();
12790
- const mod = override ? await import(override) : await import("./control-service-Djd_WI3_.js").then((n) => n.t);
13265
+ const mod = override ? await import(override) : await import("./control-service-COF59GQe.js").then((n) => n.t);
12791
13266
  const start = typeof mod.startBrowserControlServiceFromConfig === "function" ? mod.startBrowserControlServiceFromConfig : mod.startBrowserControlServerFromConfig;
12792
13267
  const stop = typeof mod.stopBrowserControlService === "function" ? mod.stopBrowserControlService : mod.stopBrowserControlServer;
12793
13268
  if (!start) return null;
@@ -12931,6 +13406,62 @@ async function resolveGatewayRuntimeConfig(params) {
12931
13406
  };
12932
13407
  }
12933
13408
 
13409
+ //#endregion
13410
+ //#region src/infra/fs-safe.ts
13411
+ var SafeOpenError = class extends Error {
13412
+ constructor(code, message) {
13413
+ super(message);
13414
+ this.code = code;
13415
+ this.name = "SafeOpenError";
13416
+ }
13417
+ };
13418
+ const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
13419
+ const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
13420
+ const isNodeError = (err) => Boolean(err && typeof err === "object" && "code" in err);
13421
+ const isNotFoundError = (err) => isNodeError(err) && typeof err.code === "string" && NOT_FOUND_CODES.has(err.code);
13422
+ const isSymlinkOpenError = (err) => isNodeError(err) && (err.code === "ELOOP" || err.code === "EINVAL" || err.code === "ENOTSUP");
13423
+ async function openFileWithinRoot(params) {
13424
+ let rootReal;
13425
+ try {
13426
+ rootReal = await fs$1.realpath(params.rootDir);
13427
+ } catch (err) {
13428
+ if (isNotFoundError(err)) throw new SafeOpenError("not-found", "root dir not found");
13429
+ throw err;
13430
+ }
13431
+ const rootWithSep = ensureTrailingSep(rootReal);
13432
+ const resolved = path.resolve(rootWithSep, params.relativePath);
13433
+ if (!resolved.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
13434
+ const supportsNoFollow = process.platform !== "win32" && "O_NOFOLLOW" in constants;
13435
+ const flags = constants.O_RDONLY | (supportsNoFollow ? constants.O_NOFOLLOW : 0);
13436
+ let handle;
13437
+ try {
13438
+ handle = await fs$1.open(resolved, flags);
13439
+ } catch (err) {
13440
+ if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
13441
+ if (isSymlinkOpenError(err)) throw new SafeOpenError("invalid-path", "symlink open blocked");
13442
+ throw err;
13443
+ }
13444
+ try {
13445
+ if ((await fs$1.lstat(resolved).catch(() => null))?.isSymbolicLink()) throw new SafeOpenError("invalid-path", "symlink not allowed");
13446
+ const realPath = await fs$1.realpath(resolved);
13447
+ if (!realPath.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
13448
+ const stat = await handle.stat();
13449
+ if (!stat.isFile()) throw new SafeOpenError("invalid-path", "not a file");
13450
+ const realStat = await fs$1.stat(realPath);
13451
+ if (stat.ino !== realStat.ino || stat.dev !== realStat.dev) throw new SafeOpenError("invalid-path", "path mismatch");
13452
+ return {
13453
+ handle,
13454
+ realPath,
13455
+ stat
13456
+ };
13457
+ } catch (err) {
13458
+ await handle.close().catch(() => {});
13459
+ if (err instanceof SafeOpenError) throw err;
13460
+ if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
13461
+ throw err;
13462
+ }
13463
+ }
13464
+
12934
13465
  //#endregion
12935
13466
  //#region src/canvas-host/a2ui.ts
12936
13467
  const A2UI_PATH = "/__openclaw__/a2ui";
@@ -12970,24 +13501,29 @@ function normalizeUrlPath$1(rawPath) {
12970
13501
  const normalized = path.posix.normalize(decoded);
12971
13502
  return normalized.startsWith("/") ? normalized : `/${normalized}`;
12972
13503
  }
12973
- async function resolveA2uiFilePath(rootReal, urlPath) {
13504
+ async function resolveA2uiFile(rootReal, urlPath) {
12974
13505
  const normalized = normalizeUrlPath$1(urlPath);
12975
13506
  const rel = normalized.replace(/^\/+/, "");
12976
13507
  if (rel.split("/").some((p) => p === "..")) return null;
12977
- let candidate = path.join(rootReal, rel);
12978
- if (normalized.endsWith("/")) candidate = path.join(candidate, "index.html");
13508
+ const tryOpen = async (relative) => {
13509
+ try {
13510
+ return await openFileWithinRoot({
13511
+ rootDir: rootReal,
13512
+ relativePath: relative
13513
+ });
13514
+ } catch (err) {
13515
+ if (err instanceof SafeOpenError) return null;
13516
+ throw err;
13517
+ }
13518
+ };
13519
+ if (normalized.endsWith("/")) return await tryOpen(path.posix.join(rel, "index.html"));
13520
+ const candidate = path.join(rootReal, rel);
12979
13521
  try {
12980
- if ((await fs$1.stat(candidate)).isDirectory()) candidate = path.join(candidate, "index.html");
13522
+ const st = await fs$1.lstat(candidate);
13523
+ if (st.isSymbolicLink()) return null;
13524
+ if (st.isDirectory()) return await tryOpen(path.posix.join(rel, "index.html"));
12981
13525
  } catch {}
12982
- const rootPrefix = rootReal.endsWith(path.sep) ? rootReal : `${rootReal}${path.sep}`;
12983
- try {
12984
- if ((await fs$1.lstat(candidate)).isSymbolicLink()) return null;
12985
- const real = await fs$1.realpath(candidate);
12986
- if (!real.startsWith(rootPrefix)) return null;
12987
- return real;
12988
- } catch {
12989
- return null;
12990
- }
13526
+ return await tryOpen(rel);
12991
13527
  }
12992
13528
  function injectCanvasLiveReload(html) {
12993
13529
  const snippet = `
@@ -13063,80 +13599,33 @@ async function handleA2uiHttpRequest(req, res) {
13063
13599
  res.end("A2UI assets not found");
13064
13600
  return true;
13065
13601
  }
13066
- const filePath = await resolveA2uiFilePath(a2uiRootReal, url.pathname.slice(basePath.length) || "/");
13067
- if (!filePath) {
13602
+ const result = await resolveA2uiFile(a2uiRootReal, url.pathname.slice(basePath.length) || "/");
13603
+ if (!result) {
13068
13604
  res.statusCode = 404;
13069
13605
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
13070
13606
  res.end("not found");
13071
13607
  return true;
13072
13608
  }
13073
- const lower = filePath.toLowerCase();
13074
- const mime = lower.endsWith(".html") || lower.endsWith(".htm") ? "text/html" : await detectMime({ filePath }) ?? "application/octet-stream";
13075
- res.setHeader("Cache-Control", "no-store");
13076
- if (mime === "text/html") {
13077
- const html = await fs$1.readFile(filePath, "utf8");
13078
- res.setHeader("Content-Type", "text/html; charset=utf-8");
13079
- res.end(injectCanvasLiveReload(html));
13080
- return true;
13081
- }
13082
- res.setHeader("Content-Type", mime);
13083
- res.end(await fs$1.readFile(filePath));
13084
- return true;
13085
- }
13086
-
13087
- //#endregion
13088
- //#region src/infra/fs-safe.ts
13089
- var SafeOpenError = class extends Error {
13090
- constructor(code, message) {
13091
- super(message);
13092
- this.code = code;
13093
- this.name = "SafeOpenError";
13094
- }
13095
- };
13096
- const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
13097
- const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
13098
- const isNodeError = (err) => Boolean(err && typeof err === "object" && "code" in err);
13099
- const isNotFoundError = (err) => isNodeError(err) && typeof err.code === "string" && NOT_FOUND_CODES.has(err.code);
13100
- const isSymlinkOpenError = (err) => isNodeError(err) && (err.code === "ELOOP" || err.code === "EINVAL" || err.code === "ENOTSUP");
13101
- async function openFileWithinRoot(params) {
13102
- let rootReal;
13103
- try {
13104
- rootReal = await fs$1.realpath(params.rootDir);
13105
- } catch (err) {
13106
- if (isNotFoundError(err)) throw new SafeOpenError("not-found", "root dir not found");
13107
- throw err;
13108
- }
13109
- const rootWithSep = ensureTrailingSep(rootReal);
13110
- const resolved = path.resolve(rootWithSep, params.relativePath);
13111
- if (!resolved.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
13112
- const supportsNoFollow = process.platform !== "win32" && "O_NOFOLLOW" in constants;
13113
- const flags = constants.O_RDONLY | (supportsNoFollow ? constants.O_NOFOLLOW : 0);
13114
- let handle;
13115
- try {
13116
- handle = await fs$1.open(resolved, flags);
13117
- } catch (err) {
13118
- if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
13119
- if (isSymlinkOpenError(err)) throw new SafeOpenError("invalid-path", "symlink open blocked");
13120
- throw err;
13121
- }
13122
13609
  try {
13123
- if ((await fs$1.lstat(resolved).catch(() => null))?.isSymbolicLink()) throw new SafeOpenError("invalid-path", "symlink not allowed");
13124
- const realPath = await fs$1.realpath(resolved);
13125
- if (!realPath.startsWith(rootWithSep)) throw new SafeOpenError("invalid-path", "path escapes root");
13126
- const stat = await handle.stat();
13127
- if (!stat.isFile()) throw new SafeOpenError("invalid-path", "not a file");
13128
- const realStat = await fs$1.stat(realPath);
13129
- if (stat.ino !== realStat.ino || stat.dev !== realStat.dev) throw new SafeOpenError("invalid-path", "path mismatch");
13130
- return {
13131
- handle,
13132
- realPath,
13133
- stat
13134
- };
13135
- } catch (err) {
13136
- await handle.close().catch(() => {});
13137
- if (err instanceof SafeOpenError) throw err;
13138
- if (isNotFoundError(err)) throw new SafeOpenError("not-found", "file not found");
13139
- throw err;
13610
+ const lower = result.realPath.toLowerCase();
13611
+ const mime = lower.endsWith(".html") || lower.endsWith(".htm") ? "text/html" : await detectMime({ filePath: result.realPath }) ?? "application/octet-stream";
13612
+ res.setHeader("Cache-Control", "no-store");
13613
+ if (req.method === "HEAD") {
13614
+ res.setHeader("Content-Type", mime === "text/html" ? "text/html; charset=utf-8" : mime);
13615
+ res.end();
13616
+ return true;
13617
+ }
13618
+ if (mime === "text/html") {
13619
+ const buf = await result.handle.readFile({ encoding: "utf8" });
13620
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
13621
+ res.end(injectCanvasLiveReload(buf));
13622
+ return true;
13623
+ }
13624
+ res.setHeader("Content-Type", mime);
13625
+ res.end(await result.handle.readFile());
13626
+ return true;
13627
+ } finally {
13628
+ await result.handle.close().catch(() => {});
13140
13629
  }
13141
13630
  }
13142
13631
 
@@ -13289,7 +13778,7 @@ async function prepareCanvasRoot(rootDir) {
13289
13778
  return rootReal;
13290
13779
  }
13291
13780
  function resolveDefaultCanvasRoot() {
13292
- const candidates = [path.join(STATE_DIR, "canvas")];
13781
+ const candidates = [path.join(resolveStateDir(), "canvas")];
13293
13782
  return candidates.find((dir) => {
13294
13783
  try {
13295
13784
  return fsSync.statSync(dir).isDirectory();
@@ -13833,6 +14322,20 @@ function sendUnauthorized(res) {
13833
14322
  type: "unauthorized"
13834
14323
  } });
13835
14324
  }
14325
+ function sendRateLimited(res, retryAfterMs) {
14326
+ if (retryAfterMs && retryAfterMs > 0) res.setHeader("Retry-After", String(Math.ceil(retryAfterMs / 1e3)));
14327
+ sendJson$1(res, 429, { error: {
14328
+ message: "Too many failed authentication attempts. Please try again later.",
14329
+ type: "rate_limited"
14330
+ } });
14331
+ }
14332
+ function sendGatewayAuthFailure(res, authResult) {
14333
+ if (authResult.rateLimited) {
14334
+ sendRateLimited(res, authResult.retryAfterMs);
14335
+ return;
14336
+ }
14337
+ sendUnauthorized(res);
14338
+ }
13836
14339
  function sendInvalidRequest(res, message) {
13837
14340
  sendJson$1(res, 400, { error: {
13838
14341
  message,
@@ -13992,16 +14495,18 @@ async function handleOpenAiHttpRequest(req, res, opts) {
13992
14495
  return true;
13993
14496
  }
13994
14497
  const token = getBearerToken(req);
13995
- if (!(await authorizeGatewayConnect({
14498
+ const authResult = await authorizeGatewayConnect({
13996
14499
  auth: opts.auth,
13997
14500
  connectAuth: {
13998
14501
  token,
13999
14502
  password: token
14000
14503
  },
14001
14504
  req,
14002
- trustedProxies: opts.trustedProxies
14003
- })).ok) {
14004
- sendUnauthorized(res);
14505
+ trustedProxies: opts.trustedProxies,
14506
+ rateLimiter: opts.rateLimiter
14507
+ });
14508
+ if (!authResult.ok) {
14509
+ sendGatewayAuthFailure(res, authResult);
14005
14510
  return true;
14006
14511
  }
14007
14512
  const body = await readJsonBodyOrError(req, res, opts.maxBodyBytes ?? 1024 * 1024);
@@ -14060,8 +14565,9 @@ async function handleOpenAiHttpRequest(req, res, opts) {
14060
14565
  }
14061
14566
  });
14062
14567
  } catch (err) {
14568
+ logWarn(`openai-compat: chat completion failed: ${String(err)}`);
14063
14569
  sendJson$1(res, 500, { error: {
14064
- message: String(err),
14570
+ message: "internal error",
14065
14571
  type: "api_error"
14066
14572
  } });
14067
14573
  }
@@ -14162,6 +14668,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
14162
14668
  });
14163
14669
  }
14164
14670
  } catch (err) {
14671
+ logWarn(`openai-compat: streaming chat completion failed: ${String(err)}`);
14165
14672
  if (closed) return;
14166
14673
  writeSse(res, {
14167
14674
  id: runId,
@@ -14170,7 +14677,7 @@ async function handleOpenAiHttpRequest(req, res, opts) {
14170
14677
  model,
14171
14678
  choices: [{
14172
14679
  index: 0,
14173
- delta: { content: `Error: ${String(err)}` },
14680
+ delta: { content: "Error: internal error" },
14174
14681
  finish_reason: "stop"
14175
14682
  }]
14176
14683
  });
@@ -14438,6 +14945,7 @@ const OutputTextDoneEventSchema = z.object({
14438
14945
  //#endregion
14439
14946
  //#region src/gateway/openresponses-http.ts
14440
14947
  const DEFAULT_BODY_BYTES$1 = 20 * 1024 * 1024;
14948
+ const DEFAULT_MAX_URL_PARTS = 8;
14441
14949
  function writeSseEvent(res, event) {
14442
14950
  res.write(`event: ${event.type}\n`);
14443
14951
  res.write(`data: ${JSON.stringify(event)}\n\n`);
@@ -14450,13 +14958,20 @@ function extractTextContent(content) {
14450
14958
  return "";
14451
14959
  }).filter(Boolean).join("\n");
14452
14960
  }
14961
+ function normalizeHostnameAllowlist(values) {
14962
+ if (!values || values.length === 0) return;
14963
+ const normalized = values.map((value) => value.trim()).filter((value) => value.length > 0);
14964
+ return normalized.length > 0 ? normalized : void 0;
14965
+ }
14453
14966
  function resolveResponsesLimits(config) {
14454
14967
  const files = config?.files;
14455
14968
  const images = config?.images;
14456
14969
  return {
14457
14970
  maxBodyBytes: config?.maxBodyBytes ?? DEFAULT_BODY_BYTES$1,
14971
+ maxUrlParts: typeof config?.maxUrlParts === "number" ? Math.max(0, Math.floor(config.maxUrlParts)) : DEFAULT_MAX_URL_PARTS,
14458
14972
  files: {
14459
14973
  allowUrl: files?.allowUrl ?? true,
14974
+ urlAllowlist: normalizeHostnameAllowlist(files?.urlAllowlist),
14460
14975
  allowedMimes: normalizeMimeList(files?.allowedMimes, DEFAULT_INPUT_FILE_MIMES),
14461
14976
  maxBytes: files?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES,
14462
14977
  maxChars: files?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS,
@@ -14470,6 +14985,7 @@ function resolveResponsesLimits(config) {
14470
14985
  },
14471
14986
  images: {
14472
14987
  allowUrl: images?.allowUrl ?? true,
14988
+ urlAllowlist: normalizeHostnameAllowlist(images?.urlAllowlist),
14473
14989
  allowedMimes: normalizeMimeList(images?.allowedMimes, DEFAULT_INPUT_IMAGE_MIMES),
14474
14990
  maxBytes: images?.maxBytes ?? DEFAULT_INPUT_IMAGE_MAX_BYTES,
14475
14991
  maxRedirects: images?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS,
@@ -14621,16 +15137,18 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14621
15137
  return true;
14622
15138
  }
14623
15139
  const token = getBearerToken(req);
14624
- if (!(await authorizeGatewayConnect({
15140
+ const authResult = await authorizeGatewayConnect({
14625
15141
  auth: opts.auth,
14626
15142
  connectAuth: {
14627
15143
  token,
14628
15144
  password: token
14629
15145
  },
14630
15146
  req,
14631
- trustedProxies: opts.trustedProxies
14632
- })).ok) {
14633
- sendUnauthorized(res);
15147
+ trustedProxies: opts.trustedProxies,
15148
+ rateLimiter: opts.rateLimiter
15149
+ });
15150
+ if (!authResult.ok) {
15151
+ sendGatewayAuthFailure(res, authResult);
14634
15152
  return true;
14635
15153
  }
14636
15154
  const limits = resolveResponsesLimits(opts.config);
@@ -14651,6 +15169,11 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14651
15169
  const user = payload.user;
14652
15170
  let images = [];
14653
15171
  let fileContexts = [];
15172
+ let urlParts = 0;
15173
+ const markUrlPart = () => {
15174
+ urlParts += 1;
15175
+ if (urlParts > limits.maxUrlParts) throw new Error(`Too many URL-based input sources: ${urlParts} (limit: ${limits.maxUrlParts})`);
15176
+ };
14654
15177
  try {
14655
15178
  if (Array.isArray(payload.input)) {
14656
15179
  for (const item of payload.input) if (item.type === "message" && typeof item.content !== "string") for (const part of item.content) {
@@ -14658,6 +15181,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14658
15181
  const source = part.source;
14659
15182
  const sourceType = source.type === "base64" || source.type === "url" ? source.type : void 0;
14660
15183
  if (!sourceType) throw new Error("input_image must have 'source.url' or 'source.data'");
15184
+ if (sourceType === "url") markUrlPart();
14661
15185
  const image = await extractImageContentFromSource({
14662
15186
  type: sourceType,
14663
15187
  url: source.url,
@@ -14671,6 +15195,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14671
15195
  const source = part.source;
14672
15196
  const sourceType = source.type === "base64" || source.type === "url" ? source.type : void 0;
14673
15197
  if (!sourceType) throw new Error("input_file must have 'source.url' or 'source.data'");
15198
+ if (sourceType === "url") markUrlPart();
14674
15199
  const file = await extractFileContentFromSource({
14675
15200
  source: {
14676
15201
  type: sourceType,
@@ -14688,8 +15213,9 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14688
15213
  }
14689
15214
  }
14690
15215
  } catch (err) {
15216
+ logWarn(`openresponses: request parsing failed: ${String(err)}`);
14691
15217
  sendJson$1(res, 400, { error: {
14692
- message: String(err),
15218
+ message: "invalid request",
14693
15219
  type: "invalid_request_error"
14694
15220
  } });
14695
15221
  return true;
@@ -14705,8 +15231,9 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14705
15231
  resolvedClientTools = toolChoiceResult.tools;
14706
15232
  toolChoicePrompt = toolChoiceResult.extraSystemPrompt;
14707
15233
  } catch (err) {
15234
+ logWarn(`openresponses: tool configuration failed: ${String(err)}`);
14708
15235
  sendJson$1(res, 400, { error: {
14709
- message: String(err),
15236
+ message: "invalid tool configuration",
14710
15237
  type: "invalid_request_error"
14711
15238
  } });
14712
15239
  return true;
@@ -14787,6 +15314,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14787
15314
  usage
14788
15315
  }));
14789
15316
  } catch (err) {
15317
+ logWarn(`openresponses: non-stream response failed: ${String(err)}`);
14790
15318
  sendJson$1(res, 500, createResponseResource({
14791
15319
  id: responseId,
14792
15320
  model,
@@ -14794,7 +15322,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
14794
15322
  output: [],
14795
15323
  error: {
14796
15324
  code: "api_error",
14797
- message: String(err)
15325
+ message: "internal error"
14798
15326
  }
14799
15327
  }));
14800
15328
  }
@@ -15025,6 +15553,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
15025
15553
  });
15026
15554
  }
15027
15555
  } catch (err) {
15556
+ logWarn(`openresponses: streaming response failed: ${String(err)}`);
15028
15557
  if (closed) return;
15029
15558
  finalUsage = finalUsage ?? createEmptyUsage();
15030
15559
  writeSseEvent(res, {
@@ -15036,7 +15565,7 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
15036
15565
  output: [],
15037
15566
  error: {
15038
15567
  code: "api_error",
15039
- message: String(err)
15568
+ message: "internal error"
15040
15569
  },
15041
15570
  usage: finalUsage
15042
15571
  })
@@ -15061,6 +15590,17 @@ async function handleOpenResponsesHttpRequest(req, res, opts) {
15061
15590
  //#region src/gateway/tools-invoke-http.ts
15062
15591
  const DEFAULT_BODY_BYTES = 2 * 1024 * 1024;
15063
15592
  const MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]);
15593
+ /**
15594
+ * Tools denied via HTTP /tools/invoke regardless of session policy.
15595
+ * Prevents RCE and privilege escalation from HTTP API surface.
15596
+ * Configurable via gateway.tools.{deny,allow} in openclaw.json.
15597
+ */
15598
+ const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [
15599
+ "sessions_spawn",
15600
+ "sessions_send",
15601
+ "gateway",
15602
+ "whatsapp_login"
15603
+ ];
15064
15604
  function resolveSessionKeyFromBody(body) {
15065
15605
  if (typeof body.sessionKey === "string" && body.sessionKey.trim()) return body.sessionKey.trim();
15066
15606
  }
@@ -15088,6 +15628,15 @@ function mergeActionIntoArgsIfSupported(params) {
15088
15628
  action
15089
15629
  };
15090
15630
  }
15631
+ function getErrorMessage(err) {
15632
+ if (err instanceof Error) return err.message || String(err);
15633
+ if (typeof err === "string") return err;
15634
+ return String(err);
15635
+ }
15636
+ function isToolInputError(err) {
15637
+ if (err instanceof ToolInputError) return true;
15638
+ return typeof err === "object" && err !== null && "name" in err && err.name === "ToolInputError";
15639
+ }
15091
15640
  async function handleToolsInvokeHttpRequest(req, res, opts) {
15092
15641
  if (new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`).pathname !== "/tools/invoke") return false;
15093
15642
  if (req.method !== "POST") {
@@ -15096,16 +15645,18 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
15096
15645
  }
15097
15646
  const cfg = loadConfig();
15098
15647
  const token = getBearerToken(req);
15099
- if (!(await authorizeGatewayConnect({
15648
+ const authResult = await authorizeGatewayConnect({
15100
15649
  auth: opts.auth,
15101
15650
  connectAuth: token ? {
15102
15651
  token,
15103
15652
  password: token
15104
15653
  } : null,
15105
15654
  req,
15106
- trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies
15107
- })).ok) {
15108
- sendUnauthorized(res);
15655
+ trustedProxies: opts.trustedProxies ?? cfg.gateway?.trustedProxies,
15656
+ rateLimiter: opts.rateLimiter
15657
+ });
15658
+ if (!authResult.ok) {
15659
+ sendGatewayAuthFailure(res, authResult);
15109
15660
  return true;
15110
15661
  }
15111
15662
  const bodyUnknown = await readJsonBodyOrError(req, res, opts.maxBodyBytes ?? DEFAULT_BODY_BYTES);
@@ -15199,7 +15750,11 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
15199
15750
  const agentFiltered = agentPolicyExpanded ? filterToolsByPolicy(globalProviderFiltered, agentPolicyExpanded) : globalProviderFiltered;
15200
15751
  const agentProviderFiltered = agentProviderExpanded ? filterToolsByPolicy(agentFiltered, agentProviderExpanded) : agentFiltered;
15201
15752
  const groupFiltered = groupPolicyExpanded ? filterToolsByPolicy(agentProviderFiltered, groupPolicyExpanded) : agentProviderFiltered;
15202
- const tool = (subagentPolicyExpanded ? filterToolsByPolicy(groupFiltered, subagentPolicyExpanded) : groupFiltered).find((t) => t.name === toolName);
15753
+ const subagentFiltered = subagentPolicyExpanded ? filterToolsByPolicy(groupFiltered, subagentPolicyExpanded) : groupFiltered;
15754
+ const gatewayToolsCfg = cfg.gateway?.tools;
15755
+ const gatewayDenyNames = DEFAULT_GATEWAY_HTTP_TOOL_DENY.filter((name) => !gatewayToolsCfg?.allow?.includes(name)).concat(Array.isArray(gatewayToolsCfg?.deny) ? gatewayToolsCfg.deny : []);
15756
+ const gatewayDenySet = new Set(gatewayDenyNames);
15757
+ const tool = subagentFiltered.filter((t) => !gatewayDenySet.has(t.name)).find((t) => t.name === toolName);
15203
15758
  if (!tool) {
15204
15759
  sendJson$1(res, 404, {
15205
15760
  ok: false,
@@ -15221,11 +15776,22 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
15221
15776
  result: await tool.execute?.(`http-${Date.now()}`, toolArgs)
15222
15777
  });
15223
15778
  } catch (err) {
15224
- sendJson$1(res, 400, {
15779
+ if (isToolInputError(err)) {
15780
+ sendJson$1(res, 400, {
15781
+ ok: false,
15782
+ error: {
15783
+ type: "tool_error",
15784
+ message: getErrorMessage(err) || "invalid tool arguments"
15785
+ }
15786
+ });
15787
+ return true;
15788
+ }
15789
+ logWarn(`tools-invoke: tool execution failed: ${String(err)}`);
15790
+ sendJson$1(res, 500, {
15225
15791
  ok: false,
15226
15792
  error: {
15227
15793
  type: "tool_error",
15228
- message: err instanceof Error ? err.message : String(err)
15794
+ message: "tool execution failed"
15229
15795
  }
15230
15796
  });
15231
15797
  }
@@ -15234,6 +15800,9 @@ async function handleToolsInvokeHttpRequest(req, res, opts) {
15234
15800
 
15235
15801
  //#endregion
15236
15802
  //#region src/gateway/server-http.ts
15803
+ const HOOK_AUTH_FAILURE_LIMIT = 20;
15804
+ const HOOK_AUTH_FAILURE_WINDOW_MS = 6e4;
15805
+ const HOOK_AUTH_FAILURE_TRACK_MAX = 2048;
15237
15806
  function sendJson(res, status, body) {
15238
15807
  res.statusCode = status;
15239
15808
  res.setHeader("Content-Type", "application/json; charset=utf-8");
@@ -15247,11 +15816,12 @@ function hasAuthorizedWsClientForIp(clients, clientIp) {
15247
15816
  return false;
15248
15817
  }
15249
15818
  async function authorizeCanvasRequest(params) {
15250
- const { req, auth, trustedProxies, clients } = params;
15251
- if (isLocalDirectRequest(req, trustedProxies)) return true;
15819
+ const { req, auth, trustedProxies, clients, rateLimiter } = params;
15820
+ if (isLocalDirectRequest(req, trustedProxies)) return { ok: true };
15821
+ let lastAuthFailure = null;
15252
15822
  const token = getBearerToken(req);
15253
15823
  if (token) {
15254
- if ((await authorizeGatewayConnect({
15824
+ const authResult = await authorizeGatewayConnect({
15255
15825
  auth: {
15256
15826
  ...auth,
15257
15827
  allowTailscale: false
@@ -15261,8 +15831,11 @@ async function authorizeCanvasRequest(params) {
15261
15831
  password: token
15262
15832
  },
15263
15833
  req,
15264
- trustedProxies
15265
- })).ok) return true;
15834
+ trustedProxies,
15835
+ rateLimiter
15836
+ });
15837
+ if (authResult.ok) return authResult;
15838
+ lastAuthFailure = authResult;
15266
15839
  }
15267
15840
  const clientIp = resolveGatewayClientIp({
15268
15841
  remoteAddr: req.socket?.remoteAddress ?? "",
@@ -15270,11 +15843,65 @@ async function authorizeCanvasRequest(params) {
15270
15843
  realIp: getHeader(req, "x-real-ip"),
15271
15844
  trustedProxies
15272
15845
  });
15273
- if (!clientIp) return false;
15274
- return hasAuthorizedWsClientForIp(clients, clientIp);
15846
+ if (!clientIp) return lastAuthFailure ?? {
15847
+ ok: false,
15848
+ reason: "unauthorized"
15849
+ };
15850
+ if (!isPrivateOrLoopbackAddress(clientIp)) return lastAuthFailure ?? {
15851
+ ok: false,
15852
+ reason: "unauthorized"
15853
+ };
15854
+ if (hasAuthorizedWsClientForIp(clients, clientIp)) return { ok: true };
15855
+ return lastAuthFailure ?? {
15856
+ ok: false,
15857
+ reason: "unauthorized"
15858
+ };
15859
+ }
15860
+ function writeUpgradeAuthFailure(socket, auth) {
15861
+ if (auth.rateLimited) {
15862
+ const retryAfterSeconds = auth.retryAfterMs && auth.retryAfterMs > 0 ? Math.ceil(auth.retryAfterMs / 1e3) : void 0;
15863
+ socket.write([
15864
+ "HTTP/1.1 429 Too Many Requests",
15865
+ retryAfterSeconds ? `Retry-After: ${retryAfterSeconds}` : void 0,
15866
+ "Content-Type: application/json; charset=utf-8",
15867
+ "Connection: close",
15868
+ "",
15869
+ JSON.stringify({ error: {
15870
+ message: "Too many failed authentication attempts. Please try again later.",
15871
+ type: "rate_limited"
15872
+ } })
15873
+ ].filter(Boolean).join("\r\n"));
15874
+ return;
15875
+ }
15876
+ socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
15275
15877
  }
15276
15878
  function createHooksRequestHandler(opts) {
15277
15879
  const { getHooksConfig, bindHost, port, logHooks, dispatchAgentHook, dispatchWakeHook } = opts;
15880
+ const hookAuthFailures = /* @__PURE__ */ new Map();
15881
+ const resolveHookClientKey = (req) => {
15882
+ return req.socket?.remoteAddress?.trim() || "unknown";
15883
+ };
15884
+ const recordHookAuthFailure = (clientKey, nowMs) => {
15885
+ if (!hookAuthFailures.has(clientKey) && hookAuthFailures.size >= HOOK_AUTH_FAILURE_TRACK_MAX) hookAuthFailures.clear();
15886
+ const current = hookAuthFailures.get(clientKey);
15887
+ const next = !current || nowMs - current.windowStartedAtMs >= HOOK_AUTH_FAILURE_WINDOW_MS ? {
15888
+ count: 1,
15889
+ windowStartedAtMs: nowMs
15890
+ } : {
15891
+ count: current.count + 1,
15892
+ windowStartedAtMs: current.windowStartedAtMs
15893
+ };
15894
+ hookAuthFailures.set(clientKey, next);
15895
+ if (next.count <= HOOK_AUTH_FAILURE_LIMIT) return { throttled: false };
15896
+ const retryAfterMs = Math.max(1, next.windowStartedAtMs + HOOK_AUTH_FAILURE_WINDOW_MS - nowMs);
15897
+ return {
15898
+ throttled: true,
15899
+ retryAfterSeconds: Math.ceil(retryAfterMs / 1e3)
15900
+ };
15901
+ };
15902
+ const clearHookAuthFailure = (clientKey) => {
15903
+ hookAuthFailures.delete(clientKey);
15904
+ };
15278
15905
  return async (req, res) => {
15279
15906
  const hooksConfig = getHooksConfig();
15280
15907
  if (!hooksConfig) return false;
@@ -15288,12 +15915,24 @@ function createHooksRequestHandler(opts) {
15288
15915
  return true;
15289
15916
  }
15290
15917
  const token = extractHookToken(req);
15291
- if (!token || token !== hooksConfig.token) {
15918
+ const clientKey = resolveHookClientKey(req);
15919
+ if (!safeEqualSecret(token, hooksConfig.token)) {
15920
+ const throttle = recordHookAuthFailure(clientKey, Date.now());
15921
+ if (throttle.throttled) {
15922
+ const retryAfter = throttle.retryAfterSeconds ?? 1;
15923
+ res.statusCode = 429;
15924
+ res.setHeader("Retry-After", String(retryAfter));
15925
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
15926
+ res.end("Too Many Requests");
15927
+ logHooks.warn(`hook auth throttled for ${clientKey}; retry-after=${retryAfter}s`);
15928
+ return true;
15929
+ }
15292
15930
  res.statusCode = 401;
15293
15931
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
15294
15932
  res.end("Unauthorized");
15295
15933
  return true;
15296
15934
  }
15935
+ clearHookAuthFailure(clientKey);
15297
15936
  if (req.method !== "POST") {
15298
15937
  res.statusCode = 405;
15299
15938
  res.setHeader("Allow", "POST");
@@ -15343,9 +15982,32 @@ function createHooksRequestHandler(opts) {
15343
15982
  });
15344
15983
  return true;
15345
15984
  }
15985
+ if (!isHookAgentAllowed(hooksConfig, normalized.value.agentId)) {
15986
+ sendJson(res, 400, {
15987
+ ok: false,
15988
+ error: getHookAgentPolicyError()
15989
+ });
15990
+ return true;
15991
+ }
15992
+ const sessionKey = resolveHookSessionKey({
15993
+ hooksConfig,
15994
+ source: "request",
15995
+ sessionKey: normalized.value.sessionKey
15996
+ });
15997
+ if (!sessionKey.ok) {
15998
+ sendJson(res, 400, {
15999
+ ok: false,
16000
+ error: sessionKey.error
16001
+ });
16002
+ return true;
16003
+ }
15346
16004
  sendJson(res, 202, {
15347
16005
  ok: true,
15348
- runId: dispatchAgentHook(normalized.value)
16006
+ runId: dispatchAgentHook({
16007
+ ...normalized.value,
16008
+ sessionKey: sessionKey.value,
16009
+ agentId: resolveHookTargetAgentId(hooksConfig, normalized.value.agentId)
16010
+ })
15349
16011
  });
15350
16012
  return true;
15351
16013
  }
@@ -15388,13 +16050,33 @@ function createHooksRequestHandler(opts) {
15388
16050
  });
15389
16051
  return true;
15390
16052
  }
16053
+ if (!isHookAgentAllowed(hooksConfig, mapped.action.agentId)) {
16054
+ sendJson(res, 400, {
16055
+ ok: false,
16056
+ error: getHookAgentPolicyError()
16057
+ });
16058
+ return true;
16059
+ }
16060
+ const sessionKey = resolveHookSessionKey({
16061
+ hooksConfig,
16062
+ source: "mapping",
16063
+ sessionKey: mapped.action.sessionKey
16064
+ });
16065
+ if (!sessionKey.ok) {
16066
+ sendJson(res, 400, {
16067
+ ok: false,
16068
+ error: sessionKey.error
16069
+ });
16070
+ return true;
16071
+ }
15391
16072
  sendJson(res, 202, {
15392
16073
  ok: true,
15393
16074
  runId: dispatchAgentHook({
15394
16075
  message: mapped.action.message,
15395
16076
  name: mapped.action.name ?? "Hook",
16077
+ agentId: resolveHookTargetAgentId(hooksConfig, mapped.action.agentId),
15396
16078
  wakeMode: mapped.action.wakeMode,
15397
- sessionKey: mapped.action.sessionKey ?? "",
16079
+ sessionKey: sessionKey.value,
15398
16080
  deliver: resolveHookDeliver(mapped.action.deliver),
15399
16081
  channel,
15400
16082
  to: mapped.action.to,
@@ -15421,7 +16103,7 @@ function createHooksRequestHandler(opts) {
15421
16103
  };
15422
16104
  }
15423
16105
  function createGatewayHttpServer(opts) {
15424
- const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth } = opts;
16106
+ const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth, rateLimiter } = opts;
15425
16107
  const httpServer = opts.tlsOptions ? createServer$1(opts.tlsOptions, (req, res) => {
15426
16108
  handleRequest(req, res);
15427
16109
  }) : createServer((req, res) => {
@@ -15432,35 +16114,60 @@ function createGatewayHttpServer(opts) {
15432
16114
  try {
15433
16115
  const configSnapshot = loadConfig();
15434
16116
  const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
16117
+ const requestPath = new URL(req.url ?? "/", "http://localhost").pathname;
15435
16118
  if (await handleHooksRequest(req, res)) return;
15436
16119
  if (await handleToolsInvokeHttpRequest(req, res, {
15437
16120
  auth: resolvedAuth,
15438
- trustedProxies
16121
+ trustedProxies,
16122
+ rateLimiter
15439
16123
  })) return;
15440
16124
  if (await handleSlackHttpRequest(req, res)) return;
15441
- if (handlePluginRequest && await handlePluginRequest(req, res)) return;
16125
+ if (handlePluginRequest) {
16126
+ if (requestPath.startsWith("/api/channels/")) {
16127
+ const token = getBearerToken(req);
16128
+ const authResult = await authorizeGatewayConnect({
16129
+ auth: resolvedAuth,
16130
+ connectAuth: token ? {
16131
+ token,
16132
+ password: token
16133
+ } : null,
16134
+ req,
16135
+ trustedProxies,
16136
+ rateLimiter
16137
+ });
16138
+ if (!authResult.ok) {
16139
+ sendGatewayAuthFailure(res, authResult);
16140
+ return;
16141
+ }
16142
+ }
16143
+ if (await handlePluginRequest(req, res)) return;
16144
+ }
15442
16145
  if (openResponsesEnabled) {
15443
16146
  if (await handleOpenResponsesHttpRequest(req, res, {
15444
16147
  auth: resolvedAuth,
15445
16148
  config: openResponsesConfig,
15446
- trustedProxies
16149
+ trustedProxies,
16150
+ rateLimiter
15447
16151
  })) return;
15448
16152
  }
15449
16153
  if (openAiChatCompletionsEnabled) {
15450
16154
  if (await handleOpenAiHttpRequest(req, res, {
15451
16155
  auth: resolvedAuth,
15452
- trustedProxies
16156
+ trustedProxies,
16157
+ rateLimiter
15453
16158
  })) return;
15454
16159
  }
15455
16160
  if (canvasHost) {
15456
- if (isCanvasPath(new URL(req.url ?? "/", "http://localhost").pathname)) {
15457
- if (!await authorizeCanvasRequest({
16161
+ if (isCanvasPath(requestPath)) {
16162
+ const ok = await authorizeCanvasRequest({
15458
16163
  req,
15459
16164
  auth: resolvedAuth,
15460
16165
  trustedProxies,
15461
- clients
15462
- })) {
15463
- sendUnauthorized(res);
16166
+ clients,
16167
+ rateLimiter
16168
+ });
16169
+ if (!ok.ok) {
16170
+ sendGatewayAuthFailure(res, ok);
15464
16171
  return;
15465
16172
  }
15466
16173
  }
@@ -15490,18 +16197,20 @@ function createGatewayHttpServer(opts) {
15490
16197
  return httpServer;
15491
16198
  }
15492
16199
  function attachGatewayUpgradeHandler(opts) {
15493
- const { httpServer, wss, canvasHost, clients, resolvedAuth } = opts;
16200
+ const { httpServer, wss, canvasHost, clients, resolvedAuth, rateLimiter } = opts;
15494
16201
  httpServer.on("upgrade", (req, socket, head) => {
15495
16202
  (async () => {
15496
16203
  if (canvasHost) {
15497
16204
  if (new URL(req.url ?? "/", "http://localhost").pathname === CANVAS_WS_PATH) {
15498
- if (!await authorizeCanvasRequest({
16205
+ const ok = await authorizeCanvasRequest({
15499
16206
  req,
15500
16207
  auth: resolvedAuth,
15501
16208
  trustedProxies: loadConfig().gateway?.trustedProxies ?? [],
15502
- clients
15503
- })) {
15504
- socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
16209
+ clients,
16210
+ rateLimiter
16211
+ });
16212
+ if (!ok.ok) {
16213
+ writeUpgradeAuthFailure(socket, ok);
15505
16214
  socket.destroy();
15506
16215
  return;
15507
16216
  }
@@ -15527,12 +16236,13 @@ function createGatewayHooksRequestHandler(params) {
15527
16236
  if (value.mode === "now") requestHeartbeatNow({ reason: "hook:wake" });
15528
16237
  };
15529
16238
  const dispatchAgentHook = (value) => {
15530
- const sessionKey = value.sessionKey.trim() ? value.sessionKey.trim() : `hook:${randomUUID()}`;
16239
+ const sessionKey = value.sessionKey.trim();
15531
16240
  const mainSessionKey = resolveMainSessionKeyFromConfig();
15532
16241
  const jobId = randomUUID();
15533
16242
  const now = Date.now();
15534
16243
  const job = {
15535
16244
  id: jobId,
16245
+ agentId: value.agentId,
15536
16246
  name: value.name,
15537
16247
  enabled: true,
15538
16248
  createdAtMs: now,
@@ -15860,6 +16570,7 @@ async function createGatewayRuntimeState(params) {
15860
16570
  handleHooksRequest,
15861
16571
  handlePluginRequest,
15862
16572
  resolvedAuth: params.resolvedAuth,
16573
+ rateLimiter: params.rateLimiter,
15863
16574
  tlsOptions: params.gatewayTls?.enabled ? params.gatewayTls.tlsOptions : void 0
15864
16575
  });
15865
16576
  try {
@@ -15886,7 +16597,8 @@ async function createGatewayRuntimeState(params) {
15886
16597
  wss,
15887
16598
  canvasHost,
15888
16599
  clients,
15889
- resolvedAuth: params.resolvedAuth
16600
+ resolvedAuth: params.resolvedAuth,
16601
+ rateLimiter: params.rateLimiter
15890
16602
  });
15891
16603
  const agentRunSeq = /* @__PURE__ */ new Map();
15892
16604
  const dedupe = /* @__PURE__ */ new Map();
@@ -15980,11 +16692,15 @@ function logGatewayStartup(params) {
15980
16692
  * console.log(`Loaded ${count} hook handlers`);
15981
16693
  * ```
15982
16694
  */
15983
- async function loadInternalHooks(cfg, workspaceDir) {
16695
+ async function loadInternalHooks(cfg, workspaceDir, opts) {
15984
16696
  if (!cfg.hooks?.internal?.enabled) return 0;
15985
16697
  let loadedCount = 0;
15986
16698
  try {
15987
- const eligible = loadWorkspaceHookEntries(workspaceDir, { config: cfg }).filter((entry) => shouldIncludeHook({
16699
+ const eligible = loadWorkspaceHookEntries(workspaceDir, {
16700
+ config: cfg,
16701
+ managedHooksDir: opts?.managedHooksDir,
16702
+ bundledHooksDir: opts?.bundledHooksDir
16703
+ }).filter((entry) => shouldIncludeHook({
15988
16704
  entry,
15989
16705
  config: cfg
15990
16706
  }));
@@ -16408,6 +17124,8 @@ function formatGatewayAuthFailureMessage(params) {
16408
17124
  case "tailscale_proxy_missing": return "unauthorized: tailscale proxy headers missing (use Tailscale Serve or gateway token/password)";
16409
17125
  case "tailscale_whois_failed": return "unauthorized: tailscale identity check failed (use Tailscale Serve auth or gateway token/password)";
16410
17126
  case "tailscale_user_mismatch": return "unauthorized: tailscale identity mismatch (use Tailscale Serve auth or gateway token/password)";
17127
+ case "rate_limited": return "unauthorized: too many failed authentication attempts (retry later)";
17128
+ case "device_token_mismatch": return "unauthorized: device token mismatch (rotate/reissue device token)";
16411
17129
  default: break;
16412
17130
  }
16413
17131
  if (authMode === "token" && authProvided === "none") return `unauthorized: gateway token missing (${tokenHint})`;
@@ -16415,7 +17133,7 @@ function formatGatewayAuthFailureMessage(params) {
16415
17133
  return "unauthorized";
16416
17134
  }
16417
17135
  function attachGatewayWsMessageHandler(params) {
16418
- const { socket, upgradeReq, connId, remoteAddr, forwardedFor, realIp, requestHost, requestOrigin, requestUserAgent, canvasHostUrl, connectNonce, resolvedAuth, gatewayMethods, events, extraHandlers, buildRequestContext, send, close, isClosed, clearHandshakeTimer, getClient, setClient, setHandshakeState, setCloseCause, setLastFrameMeta, logGateway, logHealth, logWsControl } = params;
17136
+ const { socket, upgradeReq, connId, remoteAddr, forwardedFor, realIp, requestHost, requestOrigin, requestUserAgent, canvasHostUrl, connectNonce, resolvedAuth, rateLimiter, gatewayMethods, events, extraHandlers, buildRequestContext, send, close, isClosed, clearHandshakeTimer, getClient, setClient, setHandshakeState, setCloseCause, setLastFrameMeta, logGateway, logHealth, logWsControl } = params;
16419
17137
  const configSnapshot = loadConfig();
16420
17138
  const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
16421
17139
  const clientIp = resolveGatewayClientIp({
@@ -16518,8 +17236,7 @@ function attachGatewayWsMessageHandler(params) {
16518
17236
  close(1008, "invalid role");
16519
17237
  return;
16520
17238
  }
16521
- const requestedScopes = Array.isArray(connectParams.scopes) ? connectParams.scopes : [];
16522
- const scopes = requestedScopes.length > 0 ? requestedScopes : role === "operator" ? ["operator.admin"] : [];
17239
+ const scopes = Array.isArray(connectParams.scopes) ? connectParams.scopes : [];
16523
17240
  connectParams.role = role;
16524
17241
  connectParams.scopes = scopes;
16525
17242
  const isControlUi = connectParams.client.id === GATEWAY_CLIENT_IDS.CONTROL_UI;
@@ -16561,12 +17278,26 @@ function attachGatewayWsMessageHandler(params) {
16561
17278
  const disableControlUiDeviceAuth = isControlUi && configSnapshot.gateway?.controlUi?.dangerouslyDisableDeviceAuth === true;
16562
17279
  const allowControlUiBypass = allowInsecureControlUi || disableControlUiDeviceAuth;
16563
17280
  const device = disableControlUiDeviceAuth ? null : deviceRaw;
16564
- const authResult = await authorizeGatewayConnect({
17281
+ const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
17282
+ let authResult = await authorizeGatewayConnect({
16565
17283
  auth: resolvedAuth,
16566
17284
  connectAuth: connectParams.auth,
16567
17285
  req: upgradeReq,
16568
- trustedProxies
17286
+ trustedProxies,
17287
+ rateLimiter: hasDeviceTokenCandidate ? void 0 : rateLimiter,
17288
+ clientIp,
17289
+ rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET
16569
17290
  });
17291
+ if (hasDeviceTokenCandidate && authResult.ok && rateLimiter && (authResult.method === "token" || authResult.method === "password")) {
17292
+ const sharedRateCheck = rateLimiter.check(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
17293
+ if (!sharedRateCheck.allowed) authResult = {
17294
+ ok: false,
17295
+ reason: "rate_limited",
17296
+ rateLimited: true,
17297
+ retryAfterMs: sharedRateCheck.retryAfterMs
17298
+ };
17299
+ else rateLimiter.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
17300
+ }
16570
17301
  let authOk = authResult.ok;
16571
17302
  let authMethod = authResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
16572
17303
  const sharedAuthResult = hasSharedAuth ? await authorizeGatewayConnect({
@@ -16576,23 +17307,24 @@ function attachGatewayWsMessageHandler(params) {
16576
17307
  },
16577
17308
  connectAuth: connectParams.auth,
16578
17309
  req: upgradeReq,
16579
- trustedProxies
17310
+ trustedProxies,
17311
+ rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET
16580
17312
  }) : null;
16581
17313
  const sharedAuthOk = sharedAuthResult?.ok === true && (sharedAuthResult.method === "token" || sharedAuthResult.method === "password");
16582
- const rejectUnauthorized = () => {
17314
+ const rejectUnauthorized = (failedAuth) => {
16583
17315
  setHandshakeState("failed");
16584
- logWsControl.warn(`unauthorized conn=${connId} remote=${remoteAddr ?? "?"} client=${clientLabel} ${connectParams.client.mode} v${connectParams.client.version} reason=${authResult.reason ?? "unknown"}`);
17316
+ logWsControl.warn(`unauthorized conn=${connId} remote=${remoteAddr ?? "?"} client=${clientLabel} ${connectParams.client.mode} v${connectParams.client.version} reason=${failedAuth.reason ?? "unknown"}`);
16585
17317
  const authProvided = connectParams.auth?.token ? "token" : connectParams.auth?.password ? "password" : "none";
16586
17318
  const authMessage = formatGatewayAuthFailureMessage({
16587
17319
  authMode: resolvedAuth.mode,
16588
17320
  authProvided,
16589
- reason: authResult.reason,
17321
+ reason: failedAuth.reason,
16590
17322
  client: connectParams.client
16591
17323
  });
16592
17324
  setCloseCause("unauthorized", {
16593
17325
  authMode: resolvedAuth.mode,
16594
17326
  authProvided,
16595
- authReason: authResult.reason,
17327
+ authReason: failedAuth.reason,
16596
17328
  allowTailscale: resolvedAuth.allowTailscale,
16597
17329
  client: connectParams.client.id,
16598
17330
  clientDisplayName: connectParams.client.displayName,
@@ -16629,7 +17361,7 @@ function attachGatewayWsMessageHandler(params) {
16629
17361
  }
16630
17362
  if (!canSkipDevice) {
16631
17363
  if (!authOk && hasSharedAuth) {
16632
- rejectUnauthorized();
17364
+ rejectUnauthorized(authResult);
16633
17365
  return;
16634
17366
  }
16635
17367
  setHandshakeState("failed");
@@ -16723,7 +17455,7 @@ function attachGatewayWsMessageHandler(params) {
16723
17455
  clientId: connectParams.client.id,
16724
17456
  clientMode: connectParams.client.mode,
16725
17457
  role,
16726
- scopes: requestedScopes,
17458
+ scopes,
16727
17459
  signedAtMs: signedAt,
16728
17460
  token: connectParams.auth?.token ?? null,
16729
17461
  nonce: providedNonce || void 0,
@@ -16736,7 +17468,7 @@ function attachGatewayWsMessageHandler(params) {
16736
17468
  clientId: connectParams.client.id,
16737
17469
  clientMode: connectParams.client.mode,
16738
17470
  role,
16739
- scopes: requestedScopes,
17471
+ scopes,
16740
17472
  signedAtMs: signedAt,
16741
17473
  token: connectParams.auth?.token ?? null,
16742
17474
  version: "v1"
@@ -16792,7 +17524,16 @@ function attachGatewayWsMessageHandler(params) {
16792
17524
  }
16793
17525
  }
16794
17526
  if (!authOk && connectParams.auth?.token && device) {
16795
- if ((await verifyDeviceToken({
17527
+ if (rateLimiter) {
17528
+ const deviceRateCheck = rateLimiter.check(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
17529
+ if (!deviceRateCheck.allowed) authResult = {
17530
+ ok: false,
17531
+ reason: "rate_limited",
17532
+ rateLimited: true,
17533
+ retryAfterMs: deviceRateCheck.retryAfterMs
17534
+ };
17535
+ }
17536
+ if (!authResult.rateLimited) if ((await verifyDeviceToken({
16796
17537
  deviceId: device.id,
16797
17538
  token: connectParams.auth.token,
16798
17539
  role,
@@ -16800,10 +17541,17 @@ function attachGatewayWsMessageHandler(params) {
16800
17541
  })).ok) {
16801
17542
  authOk = true;
16802
17543
  authMethod = "device-token";
17544
+ rateLimiter?.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
17545
+ } else {
17546
+ authResult = {
17547
+ ok: false,
17548
+ reason: "device_token_mismatch"
17549
+ };
17550
+ rateLimiter?.recordFailure(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
16803
17551
  }
16804
17552
  }
16805
17553
  if (!authOk) {
16806
- rejectUnauthorized();
17554
+ rejectUnauthorized(authResult);
16807
17555
  return;
16808
17556
  }
16809
17557
  if (device && devicePublicKey && !(allowControlUiBypass && sharedAuthOk)) {
@@ -17070,8 +17818,18 @@ function attachGatewayWsMessageHandler(params) {
17070
17818
 
17071
17819
  //#endregion
17072
17820
  //#region src/gateway/server/ws-connection.ts
17821
+ const LOG_HEADER_MAX_LEN = 300;
17822
+ const LOG_HEADER_CONTROL_REGEX = /[\u0000-\u001f\u007f-\u009f]/g;
17823
+ const LOG_HEADER_FORMAT_REGEX = /\p{Cf}/gu;
17824
+ const sanitizeLogValue = (value) => {
17825
+ if (!value) return;
17826
+ const cleaned = value.replace(LOG_HEADER_CONTROL_REGEX, " ").replace(LOG_HEADER_FORMAT_REGEX, " ").replace(/\s+/g, " ").trim();
17827
+ if (!cleaned) return;
17828
+ if (cleaned.length <= LOG_HEADER_MAX_LEN) return cleaned;
17829
+ return truncateUtf16Safe(cleaned, LOG_HEADER_MAX_LEN);
17830
+ };
17073
17831
  function attachGatewayWsConnectionHandler(params) {
17074
- const { wss, clients, port, gatewayHost, canvasHostEnabled, canvasHostServerPort, resolvedAuth, gatewayMethods, events, logGateway, logHealth, logWsControl, extraHandlers, broadcast, buildRequestContext } = params;
17832
+ const { wss, clients, port, gatewayHost, canvasHostEnabled, canvasHostServerPort, resolvedAuth, rateLimiter, gatewayMethods, events, logGateway, logHealth, logWsControl, extraHandlers, broadcast, buildRequestContext } = params;
17075
17833
  wss.on("connection", (socket, upgradeReq) => {
17076
17834
  let client = null;
17077
17835
  let closed = false;
@@ -17145,6 +17903,11 @@ function attachGatewayWsConnectionHandler(params) {
17145
17903
  const isNoisySwiftPmHelperClose = (userAgent, remote) => Boolean(userAgent?.toLowerCase().includes("swiftpm-testing-helper") && isLoopbackAddress(remote));
17146
17904
  socket.once("close", (code, reason) => {
17147
17905
  const durationMs = Date.now() - openedAt;
17906
+ const logForwardedFor = sanitizeLogValue(forwardedFor);
17907
+ const logOrigin = sanitizeLogValue(requestOrigin);
17908
+ const logHost = sanitizeLogValue(requestHost);
17909
+ const logUserAgent = sanitizeLogValue(requestUserAgent);
17910
+ const logReason = sanitizeLogValue(reason?.toString());
17148
17911
  const closeContext = {
17149
17912
  cause: closeCause,
17150
17913
  handshake: handshakeState,
@@ -17152,14 +17915,14 @@ function attachGatewayWsConnectionHandler(params) {
17152
17915
  lastFrameType,
17153
17916
  lastFrameMethod,
17154
17917
  lastFrameId,
17155
- host: requestHost,
17156
- origin: requestOrigin,
17157
- userAgent: requestUserAgent,
17158
- forwardedFor,
17918
+ host: logHost,
17919
+ origin: logOrigin,
17920
+ userAgent: logUserAgent,
17921
+ forwardedFor: logForwardedFor,
17159
17922
  ...closeMeta
17160
17923
  };
17161
- if (!client) (isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) ? logWsControl.debug : logWsControl.warn)(`closed before connect conn=${connId} remote=${remoteAddr ?? "?"} fwd=${forwardedFor ?? "n/a"} origin=${requestOrigin ?? "n/a"} host=${requestHost ?? "n/a"} ua=${requestUserAgent ?? "n/a"} code=${code ?? "n/a"} reason=${reason?.toString() || "n/a"}`, closeContext);
17162
- if (client && isWebchatClient(client.connect.client)) logWsControl.info(`webchat disconnected code=${code} reason=${reason?.toString() || "n/a"} conn=${connId}`);
17924
+ if (!client) (isNoisySwiftPmHelperClose(requestUserAgent, remoteAddr) ? logWsControl.debug : logWsControl.warn)(`closed before connect conn=${connId} remote=${remoteAddr ?? "?"} fwd=${logForwardedFor || "n/a"} origin=${logOrigin || "n/a"} host=${logHost || "n/a"} ua=${logUserAgent || "n/a"} code=${code ?? "n/a"} reason=${logReason || "n/a"}`, closeContext);
17925
+ if (client && isWebchatClient(client.connect.client)) logWsControl.info(`webchat disconnected code=${code} reason=${logReason || "n/a"} conn=${connId}`);
17163
17926
  if (client?.presenceKey) {
17164
17927
  upsertPresence(client.presenceKey, { reason: "disconnect" });
17165
17928
  incrementPresenceVersion();
@@ -17179,7 +17942,7 @@ function attachGatewayWsConnectionHandler(params) {
17179
17942
  logWs("out", "close", {
17180
17943
  connId,
17181
17944
  code,
17182
- reason: reason?.toString(),
17945
+ reason: logReason,
17183
17946
  durationMs,
17184
17947
  cause: closeCause,
17185
17948
  handshake: handshakeState,
@@ -17211,6 +17974,7 @@ function attachGatewayWsConnectionHandler(params) {
17211
17974
  canvasHostUrl,
17212
17975
  connectNonce,
17213
17976
  resolvedAuth,
17977
+ rateLimiter,
17214
17978
  gatewayMethods,
17215
17979
  events,
17216
17980
  extraHandlers,
@@ -17247,6 +18011,7 @@ function attachGatewayWsHandlers(params) {
17247
18011
  canvasHostEnabled: params.canvasHostEnabled,
17248
18012
  canvasHostServerPort: params.canvasHostServerPort,
17249
18013
  resolvedAuth: params.resolvedAuth,
18014
+ rateLimiter: params.rateLimiter,
17250
18015
  gatewayMethods: params.gatewayMethods,
17251
18016
  events: params.events,
17252
18017
  logGateway: params.logGateway,
@@ -17346,6 +18111,8 @@ async function startGatewayServer(port = 18789, opts = {}) {
17346
18111
  const { bindHost, controlUiEnabled, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, controlUiBasePath, controlUiRoot: controlUiRootOverride, resolvedAuth, tailscaleConfig, tailscaleMode } = runtimeConfig;
17347
18112
  let hooksConfig = runtimeConfig.hooksConfig;
17348
18113
  const canvasHostEnabled = runtimeConfig.canvasHostEnabled;
18114
+ const rateLimitConfig = cfgAtStart.gateway?.auth?.rateLimit;
18115
+ const authRateLimiter = rateLimitConfig ? createAuthRateLimiter(rateLimitConfig) : void 0;
17349
18116
  let controlUiRootState;
17350
18117
  if (controlUiRootOverride) {
17351
18118
  const resolvedOverride = resolveControlUiRootOverrideSync(controlUiRootOverride);
@@ -17395,6 +18162,7 @@ async function startGatewayServer(port = 18789, opts = {}) {
17395
18162
  openResponsesEnabled,
17396
18163
  openResponsesConfig,
17397
18164
  resolvedAuth,
18165
+ rateLimiter: authRateLimiter,
17398
18166
  gatewayTls,
17399
18167
  hooksConfig: () => hooksConfig,
17400
18168
  pluginRegistry,
@@ -17502,6 +18270,7 @@ async function startGatewayServer(port = 18789, opts = {}) {
17502
18270
  canvasHostEnabled: Boolean(canvasHost),
17503
18271
  canvasHostServerPort,
17504
18272
  resolvedAuth,
18273
+ rateLimiter: authRateLimiter,
17505
18274
  gatewayMethods,
17506
18275
  events: GATEWAY_EVENTS,
17507
18276
  logGateway: log,
@@ -17585,6 +18354,12 @@ async function startGatewayServer(port = 18789, opts = {}) {
17585
18354
  logChannels,
17586
18355
  logBrowser
17587
18356
  }));
18357
+ {
18358
+ const hookRunner = getGlobalHookRunner();
18359
+ if (hookRunner?.hasHooks("gateway_start")) hookRunner.runGatewayStart({ port }, { port }).catch((err) => {
18360
+ log.warn(`gateway_start hook failed: ${String(err)}`);
18361
+ });
18362
+ }
17588
18363
  const { applyHotReload, requestGatewayRestart } = createGatewayReloadHandlers({
17589
18364
  deps,
17590
18365
  broadcast,
@@ -17647,12 +18422,21 @@ async function startGatewayServer(port = 18789, opts = {}) {
17647
18422
  httpServers
17648
18423
  });
17649
18424
  return { close: async (opts) => {
18425
+ {
18426
+ const hookRunner = getGlobalHookRunner();
18427
+ if (hookRunner?.hasHooks("gateway_stop")) try {
18428
+ await hookRunner.runGatewayStop({ reason: opts?.reason ?? "gateway stopping" }, { port });
18429
+ } catch (err) {
18430
+ log.warn(`gateway_stop hook failed: ${String(err)}`);
18431
+ }
18432
+ }
17650
18433
  if (diagnosticsEnabled) stopDiagnosticHeartbeat();
17651
18434
  if (skillsRefreshTimer) {
17652
18435
  clearTimeout(skillsRefreshTimer);
17653
18436
  skillsRefreshTimer = null;
17654
18437
  }
17655
18438
  skillsChangeUnsub();
18439
+ authRateLimiter?.dispose();
17656
18440
  await close(opts);
17657
18441
  } };
17658
18442
  }
@@ -17752,6 +18536,8 @@ async function runGatewayLoop(params) {
17752
18536
  process.removeListener("SIGINT", onSigint);
17753
18537
  process.removeListener("SIGUSR1", onSigusr1);
17754
18538
  };
18539
+ const DRAIN_TIMEOUT_MS = 3e4;
18540
+ const SHUTDOWN_TIMEOUT_MS = 5e3;
17755
18541
  const request = (action, signal) => {
17756
18542
  if (shuttingDown) {
17757
18543
  gatewayLog$1.info(`received ${signal} during shutdown; ignoring`);
@@ -17760,13 +18546,23 @@ async function runGatewayLoop(params) {
17760
18546
  shuttingDown = true;
17761
18547
  const isRestart = action === "restart";
17762
18548
  gatewayLog$1.info(`received ${signal}; ${isRestart ? "restarting" : "shutting down"}`);
18549
+ const forceExitMs = isRestart ? DRAIN_TIMEOUT_MS + SHUTDOWN_TIMEOUT_MS : SHUTDOWN_TIMEOUT_MS;
17763
18550
  const forceExitTimer = setTimeout(() => {
17764
18551
  gatewayLog$1.error("shutdown timed out; exiting without full cleanup");
17765
18552
  cleanupSignals();
17766
18553
  params.runtime.exit(0);
17767
- }, 5e3);
18554
+ }, forceExitMs);
17768
18555
  (async () => {
17769
18556
  try {
18557
+ if (isRestart) {
18558
+ const activeTasks = getActiveTaskCount();
18559
+ if (activeTasks > 0) {
18560
+ gatewayLog$1.info(`draining ${activeTasks} active task(s) before restart (timeout ${DRAIN_TIMEOUT_MS}ms)`);
18561
+ const { drained } = await waitForActiveTasks(DRAIN_TIMEOUT_MS);
18562
+ if (drained) gatewayLog$1.info("all active tasks drained");
18563
+ else gatewayLog$1.warn("drain timeout reached; proceeding with restart");
18564
+ }
18565
+ }
17770
18566
  await server?.close({
17771
18567
  reason: isRestart ? "gateway restarting" : "gateway stopping",
17772
18568
  restartExpectedMs: isRestart ? 1500 : null