agentix-cli 0.18.0 → 0.19.0

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 (171) hide show
  1. package/README.md +75 -27
  2. package/dist/{agent-RDVGL2TW.js → agent-57MG4LBM.js} +2 -2
  3. package/dist/{audit-Z47AQFFD.js → audit-JU3HWISB.js} +2 -2
  4. package/dist/{board-dashboard-KX2CNOSY.js → board-dashboard-HXDOYGIG.js} +2237 -69
  5. package/dist/board-dashboard-HXDOYGIG.js.map +1 -0
  6. package/dist/chunk-23AUU4S2.js +102 -0
  7. package/dist/chunk-23AUU4S2.js.map +1 -0
  8. package/dist/chunk-4MREHZP5.js +111 -0
  9. package/dist/chunk-4MREHZP5.js.map +1 -0
  10. package/dist/chunk-4YCH6IZV.js +2 -0
  11. package/dist/chunk-6B74TO2J.js +743 -0
  12. package/dist/chunk-6B74TO2J.js.map +1 -0
  13. package/dist/chunk-7P5QRX5W.js +108 -0
  14. package/dist/chunk-7P5QRX5W.js.map +1 -0
  15. package/dist/chunk-A6BKSZQA.js +3 -0
  16. package/dist/chunk-A6BKSZQA.js.map +1 -0
  17. package/dist/{chunk-6PPIFDOQ.js → chunk-CNNLREHD.js} +67 -22
  18. package/dist/{chunk-6PPIFDOQ.js.map → chunk-CNNLREHD.js.map} +1 -1
  19. package/dist/{chunk-J4WKMKTP.js → chunk-DLVOHCTA.js} +2 -2
  20. package/dist/chunk-E4ZZILAK.js +15 -0
  21. package/dist/chunk-E4ZZILAK.js.map +1 -0
  22. package/dist/{chunk-TRQSP67X.js → chunk-IKBHHTIT.js} +2 -2
  23. package/dist/chunk-JTJIPNXY.js +2 -0
  24. package/dist/chunk-JTJIPNXY.js.map +1 -0
  25. package/dist/chunk-MBOLXKA2.js +6 -0
  26. package/dist/chunk-MBOLXKA2.js.map +1 -0
  27. package/dist/{inbox-O5UB3YQN.js → chunk-MTRWXRFF.js} +69 -4
  28. package/dist/chunk-MTRWXRFF.js.map +1 -0
  29. package/dist/{processes-7W27G3J3.js → chunk-NOF4HSGK.js} +3 -3
  30. package/dist/{processes-7W27G3J3.js.map → chunk-NOF4HSGK.js.map} +1 -1
  31. package/dist/chunk-QW2OYR3N.js +7 -0
  32. package/dist/chunk-QW2OYR3N.js.map +1 -0
  33. package/dist/chunk-SV3I4CI7.js +440 -0
  34. package/dist/chunk-SV3I4CI7.js.map +1 -0
  35. package/dist/{chunk-JXEPJ3YD.js → chunk-TC3BSUP7.js} +5 -5
  36. package/dist/chunk-VWTBHLMX.js +1 -0
  37. package/dist/chunk-WFHIIVLL.js +719 -0
  38. package/dist/chunk-WFHIIVLL.js.map +1 -0
  39. package/dist/chunk-WPPUOTY6.js +6 -0
  40. package/dist/chunk-WPPUOTY6.js.map +1 -0
  41. package/dist/cli.js +1 -1
  42. package/dist/cli.js.map +1 -1
  43. package/dist/{compaction-S5H6DTOR.js → compaction-BTUJJ3LJ.js} +4 -4
  44. package/dist/config-QXWKEQON.js +2 -0
  45. package/dist/config-mutator-53XGR2NT.js +2 -0
  46. package/dist/conflict-detector-D7VIYPDI.js +2 -0
  47. package/dist/conflict-detector-D7VIYPDI.js.map +1 -0
  48. package/dist/{context-planner-2RGHJHSO.js → context-planner-I7ID7XF3.js} +3 -3
  49. package/dist/debug-N3B5NVJU.js +2 -0
  50. package/dist/{discover-OATU7SHI.js → discover-XJXQXXIO.js} +2 -2
  51. package/dist/doctor-SEOUFHSL.js +2 -0
  52. package/dist/{editor-chat-FUBQLMAQ.js → editor-chat-RVLCKKB7.js} +2 -2
  53. package/dist/{heal-YP7QI7D3.js → heal-YEQYWX2L.js} +2 -2
  54. package/dist/inbox-BIN2S5XN.js +2 -0
  55. package/dist/index.d.ts +830 -100
  56. package/dist/index.js +2 -2
  57. package/dist/index.js.map +1 -1
  58. package/dist/{ingest-whatsapp-C3O5UPBX.js → ingest-whatsapp-LZE62INA.js} +2 -2
  59. package/dist/loader-5CA3O5GM.js +2 -0
  60. package/dist/loader-6IHUCPVO.js +2 -0
  61. package/dist/memory-extract-KRCHD3OP.js +2 -0
  62. package/dist/migrate-v2-QO7SLKIM.js +4 -0
  63. package/dist/migrate-v2-QO7SLKIM.js.map +1 -0
  64. package/dist/processes-RJ6RDP32.js +2 -0
  65. package/dist/program-SASDC2GV.js +2 -0
  66. package/dist/{providers-WCQSKTNP.js → providers-WVCMIW67.js} +2 -2
  67. package/dist/query-HZYYRCLU.js +2 -0
  68. package/dist/recipes-EPLE2SNT.js +2 -0
  69. package/dist/registry-HM4ELSXS.js +2 -0
  70. package/dist/rotation-memo-GMTZPMNE.js +13 -0
  71. package/dist/rotation-memo-GMTZPMNE.js.map +1 -0
  72. package/dist/scheduler-XVFIK4XH.js +2 -0
  73. package/dist/{slack-D27BE37Q.js → slack-6P7ZJ32Y.js} +2 -2
  74. package/dist/store-BAPDZHXE.js +2 -0
  75. package/dist/{subagent-EPOT3QDM.js → subagent-WV7QKQ3L.js} +2 -2
  76. package/dist/{task-store-3NUWLYRY.js → task-store-RHISEU6V.js} +2 -2
  77. package/dist/{telegram-UWV67KWG.js → telegram-PIYDRM46.js} +2 -2
  78. package/dist/timers-5LPZN55J.js +2 -0
  79. package/dist/types-VECTMYTO.js +2 -0
  80. package/dist/types-VECTMYTO.js.map +1 -0
  81. package/dist/types-ZRLHHCGB.js +2 -0
  82. package/dist/types-ZRLHHCGB.js.map +1 -0
  83. package/dist/web/activity-graph.global.js +654 -0
  84. package/dist/web/activity-graph.global.js.map +1 -0
  85. package/dist/web/workflow-editor.global.js +1 -1
  86. package/dist/web/workflow-editor.global.js.map +1 -1
  87. package/dist/{whatsapp-KJ2NRKKQ.js → whatsapp-PFRDMUMT.js} +2 -2
  88. package/dist/{whatsapp-state-3ZVQ6MQD.js → whatsapp-state-XMTCSRXU.js} +2 -2
  89. package/dist/wiki-53W5NLZV.js +2 -0
  90. package/dist/wiki-53W5NLZV.js.map +1 -0
  91. package/dist/workspace-setup-FPGN5VNY.js +2 -0
  92. package/dist/workspace-setup-FPGN5VNY.js.map +1 -0
  93. package/package.json +3 -2
  94. package/dist/board-dashboard-KX2CNOSY.js.map +0 -1
  95. package/dist/chunk-34NG7UDX.js +0 -11
  96. package/dist/chunk-34NG7UDX.js.map +0 -1
  97. package/dist/chunk-ASSJ4Z6Z.js +0 -164
  98. package/dist/chunk-ASSJ4Z6Z.js.map +0 -1
  99. package/dist/chunk-BLUXL6HR.js +0 -854
  100. package/dist/chunk-BLUXL6HR.js.map +0 -1
  101. package/dist/chunk-BTAEYL3P.js +0 -72
  102. package/dist/chunk-BTAEYL3P.js.map +0 -1
  103. package/dist/chunk-DSCMCVTV.js +0 -7
  104. package/dist/chunk-DSCMCVTV.js.map +0 -1
  105. package/dist/chunk-HPDHNIA3.js +0 -2
  106. package/dist/chunk-IM376SD6.js +0 -13
  107. package/dist/chunk-IM376SD6.js.map +0 -1
  108. package/dist/chunk-LEIYC3BF.js +0 -104
  109. package/dist/chunk-LEIYC3BF.js.map +0 -1
  110. package/dist/chunk-LY7JGDV7.js +0 -6
  111. package/dist/chunk-LY7JGDV7.js.map +0 -1
  112. package/dist/chunk-U364GNQX.js +0 -719
  113. package/dist/chunk-U364GNQX.js.map +0 -1
  114. package/dist/chunk-WZEYII2S.js +0 -6
  115. package/dist/chunk-WZEYII2S.js.map +0 -1
  116. package/dist/config-UOHEG4BW.js +0 -2
  117. package/dist/config-mutator-KSEBWKJH.js +0 -2
  118. package/dist/debug-TGAP7E7W.js +0 -2
  119. package/dist/inbox-O5UB3YQN.js.map +0 -1
  120. package/dist/loader-BGWA7IRY.js +0 -2
  121. package/dist/loader-LBHKSXBK.js +0 -2
  122. package/dist/memory-extract-KKPKAOPH.js +0 -2
  123. package/dist/program-2MDELQLX.js +0 -2
  124. package/dist/query-FZAL7AQD.js +0 -2
  125. package/dist/recipes-LZ56TA3X.js +0 -2
  126. package/dist/registry-ONJSILH4.js +0 -2
  127. package/dist/scheduler-XRM2RJIV.js +0 -2
  128. package/dist/store-4UCO56K2.js +0 -2
  129. package/dist/timers-QB4KYNAE.js +0 -2
  130. package/dist/types-ECMEC5BZ.js +0 -2
  131. package/dist/types-TTB6DJUE.js +0 -2
  132. package/dist/usage-dashboard-FQGPPABD.js +0 -803
  133. package/dist/usage-dashboard-FQGPPABD.js.map +0 -1
  134. package/dist/wiki-OUYKM7MB.js +0 -2
  135. package/dist/workspace-setup-6GDBEORT.js +0 -2
  136. /package/dist/{agent-RDVGL2TW.js.map → agent-57MG4LBM.js.map} +0 -0
  137. /package/dist/{audit-Z47AQFFD.js.map → audit-JU3HWISB.js.map} +0 -0
  138. /package/dist/{chunk-HPDHNIA3.js.map → chunk-4YCH6IZV.js.map} +0 -0
  139. /package/dist/{chunk-J4WKMKTP.js.map → chunk-DLVOHCTA.js.map} +0 -0
  140. /package/dist/{chunk-TRQSP67X.js.map → chunk-IKBHHTIT.js.map} +0 -0
  141. /package/dist/{chunk-JXEPJ3YD.js.map → chunk-TC3BSUP7.js.map} +0 -0
  142. /package/dist/{config-UOHEG4BW.js.map → chunk-VWTBHLMX.js.map} +0 -0
  143. /package/dist/{compaction-S5H6DTOR.js.map → compaction-BTUJJ3LJ.js.map} +0 -0
  144. /package/dist/{config-mutator-KSEBWKJH.js.map → config-QXWKEQON.js.map} +0 -0
  145. /package/dist/{debug-TGAP7E7W.js.map → config-mutator-53XGR2NT.js.map} +0 -0
  146. /package/dist/{context-planner-2RGHJHSO.js.map → context-planner-I7ID7XF3.js.map} +0 -0
  147. /package/dist/{heal-YP7QI7D3.js.map → debug-N3B5NVJU.js.map} +0 -0
  148. /package/dist/{discover-OATU7SHI.js.map → discover-XJXQXXIO.js.map} +0 -0
  149. /package/dist/{loader-BGWA7IRY.js.map → doctor-SEOUFHSL.js.map} +0 -0
  150. /package/dist/{editor-chat-FUBQLMAQ.js.map → editor-chat-RVLCKKB7.js.map} +0 -0
  151. /package/dist/{loader-LBHKSXBK.js.map → heal-YEQYWX2L.js.map} +0 -0
  152. /package/dist/{memory-extract-KKPKAOPH.js.map → inbox-BIN2S5XN.js.map} +0 -0
  153. /package/dist/{ingest-whatsapp-C3O5UPBX.js.map → ingest-whatsapp-LZE62INA.js.map} +0 -0
  154. /package/dist/{program-2MDELQLX.js.map → loader-5CA3O5GM.js.map} +0 -0
  155. /package/dist/{providers-WCQSKTNP.js.map → loader-6IHUCPVO.js.map} +0 -0
  156. /package/dist/{query-FZAL7AQD.js.map → memory-extract-KRCHD3OP.js.map} +0 -0
  157. /package/dist/{recipes-LZ56TA3X.js.map → processes-RJ6RDP32.js.map} +0 -0
  158. /package/dist/{registry-ONJSILH4.js.map → program-SASDC2GV.js.map} +0 -0
  159. /package/dist/{scheduler-XRM2RJIV.js.map → providers-WVCMIW67.js.map} +0 -0
  160. /package/dist/{store-4UCO56K2.js.map → query-HZYYRCLU.js.map} +0 -0
  161. /package/dist/{task-store-3NUWLYRY.js.map → recipes-EPLE2SNT.js.map} +0 -0
  162. /package/dist/{timers-QB4KYNAE.js.map → registry-HM4ELSXS.js.map} +0 -0
  163. /package/dist/{types-ECMEC5BZ.js.map → scheduler-XVFIK4XH.js.map} +0 -0
  164. /package/dist/{slack-D27BE37Q.js.map → slack-6P7ZJ32Y.js.map} +0 -0
  165. /package/dist/{types-TTB6DJUE.js.map → store-BAPDZHXE.js.map} +0 -0
  166. /package/dist/{subagent-EPOT3QDM.js.map → subagent-WV7QKQ3L.js.map} +0 -0
  167. /package/dist/{wiki-OUYKM7MB.js.map → task-store-RHISEU6V.js.map} +0 -0
  168. /package/dist/{telegram-UWV67KWG.js.map → telegram-PIYDRM46.js.map} +0 -0
  169. /package/dist/{workspace-setup-6GDBEORT.js.map → timers-5LPZN55J.js.map} +0 -0
  170. /package/dist/{whatsapp-KJ2NRKKQ.js.map → whatsapp-PFRDMUMT.js.map} +0 -0
  171. /package/dist/{whatsapp-state-3ZVQ6MQD.js.map → whatsapp-state-XMTCSRXU.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import{a as S,b as Fe,c as Ue,d as We,e as D,f as K,g as le}from"./chunk-ASSJ4Z6Z.js";import{a as E,b as C,g as Be,h as je,i as R,j as N,k as V,l as ze,m as qe,n as Ge}from"./chunk-6PPIFDOQ.js";import{a as se,b as ie,c as De,d as He,e as Oe,i as Ne}from"./chunk-IM376SD6.js";import"./chunk-DSCMCVTV.js";import"./chunk-J4WKMKTP.js";import{a as Le,b as Pe,e as $}from"./chunk-LY7JGDV7.js";import"./chunk-N67JZAXU.js";import{f as Re}from"./chunk-CDNWCYF7.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import{a as Ie}from"./chunk-5F3J55LG.js";import{b as Me}from"./chunk-WZEYII2S.js";import"./chunk-HPDHNIA3.js";import{createServer as xn}from"http";import{appendFileSync as yn,existsSync as wn,mkdirSync as kn}from"fs";import{readFile as Sn}from"fs/promises";import{fileURLToPath as En}from"url";import{dirname as _n,resolve as re}from"path";var Ke='/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */\n!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?e(Object(i),!0).forEach(function(t){var e,n;e=o,t=i[n=t],n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(t){Object.defineProperty(o,t,Object.getOwnPropertyDescriptor(i,t))})}return o}function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,o=arguments[e];for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(t[n]=o[n])}return t}).apply(this,arguments)}function i(t,e){if(null==t)return{};var n,o=function(t,e){if(null==t)return{};for(var n,o={},i=Object.keys(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols)for(var i=Object.getOwnPropertySymbols(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n]);return o}function r(t){return function(t){if(Array.isArray(t))return l(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return l(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var y=t(/(?:Trident.*rv[ :]?11\\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1};function h(t,e,n){t.addEventListener(e,n,!y&&d)}function f(t,e,n){t.removeEventListener(e,n,!y&&d)}function p(t,e){if(e&&(">"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function O(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d=t!==window&&t.parentNode&&t!==O()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth);if((e||n)&&t!==window&&(i=i||t.parentNode,!y))do{if(i&&i.getBoundingClientRect&&("none"!==R(i,"transform")||n&&"static"!==R(i,"position"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,"border-top-width")),l-=h.left+parseInt(R(i,"border-left-width")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);return o&&t!==window&&(o=(e=v(i||t))&&e.a,t=e&&e.d,e&&(s=(a/=t)+(u/=t),c=(l/=o)+(d/=o))),{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=M(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!("top"===n||"left"===n?r<=i:i<=r))return o;if(o===O())break;o=M(o,!1)}return!1}function B(t,e,n,o){for(var i=0,r=0,a=t.children;r<a.length;){if("none"!==a[r].style.display&&a[r]!==Ft.ghost&&(o||a[r]!==Ft.dragged)&&P(a[r],n.draggable,t,!1)){if(i===e)return a[r];i++}r++}return null}function F(t,e){for(var n=t.lastElementChild;n&&(n===Ft.ghost||"none"===R(n,"display")||e&&!p(n,e));)n=n.previousElementSibling;return n||null}function j(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)"TEMPLATE"===t.nodeName.toUpperCase()||t===Ft.clone||e&&!p(t,e)||n++;return n}function E(t){var e=0,n=0,o=O();if(t)do{var i=v(t),r=i.a,i=i.d}while(e+=t.scrollLeft*r,n+=t.scrollTop*i,t!==o&&(t=t.parentNode));return[e,n]}function M(t,e){if(!t||!t.getBoundingClientRect)return O();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return O();if(o||e)return n;o=!0}}}while(n=n.parentNode);return O()}function D(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function S(e,n){return function(){var t;g||(1===(t=arguments).length?e.call(this,t[0]):e.apply(this,t),g=setTimeout(function(){g=void 0},n))}}function H(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function _(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function C(t,e){R(t,"position","absolute"),R(t,"top",e.top),R(t,"left",e.left),R(t,"width",e.width),R(t,"height",e.height)}function T(t){R(t,"position",""),R(t,"top",""),R(t,"left",""),R(t,"width",""),R(t,"height","")}function L(n,o,i){var r={};return Array.from(n.children).forEach(function(t){var e;P(t,o.draggable,n,!1)&&!t.animated&&t!==i&&(e=X(t),r.left=Math.min(null!==(t=r.left)&&void 0!==t?t:1/0,e.left),r.top=Math.min(null!==(t=r.top)&&void 0!==t?t:1/0,e.top),r.right=Math.max(null!==(t=r.right)&&void 0!==t?t:-1/0,e.right),r.bottom=Math.max(null!==(t=r.bottom)&&void 0!==t?t:-1/0,e.bottom))}),r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}var K="Sortable"+(new Date).getTime();function x(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){var e,n;"none"!==R(t,"display")&&t!==Ft.ghost&&(o.push({target:t,rect:X(t)}),e=I({},o[o.length-1].rect),!t.thisAnimationDuration||(n=v(t,!0))&&(e.top-=n.f,e.left-=n.e),t.fromRect=e)})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void("function"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&D(r,i)&&!D(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(t=l,s=r,r=a,a=c.options,e=Math.sqrt(Math.pow(s.top-t.top,2)+Math.pow(s.left-t.left,2))/Math.sqrt(Math.pow(s.top-r.top,2)+Math.pow(s.left-r.left,2))*a.animation),D(i,o)||(n.prevFromRect=o,n.prevToRect=i,e=e||c.options.animation,c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){"function"==typeof t&&t()},d):"function"==typeof t&&t(),o=[]},animate:function(t,e,n,o){var i,r;o&&(R(t,"transition",""),R(t,"transform",""),i=(r=v(this.el))&&r.a,r=r&&r.d,i=(e.left-n.left)/(i||1),r=(e.top-n.top)/(r||1),t.animatingX=!!i,t.animatingY=!!r,R(t,"transform","translate3d("+i+"px,"+r+"px,0)"),this.forRepaintDummy=t.offsetWidth,R(t,"transition","transform "+o+"ms"+(this.options.easing?" "+this.options.easing:"")),R(t,"transform","translate3d(0,0,0)"),"number"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,"transition",""),R(t,"transform",""),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o))}}}var A=[],N={initializeByDefault:!0},W={mount:function(e){for(var t in N)!N.hasOwnProperty(t)||t in e||(e[t]=N[t]);A.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),A.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";A.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in A.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&(void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r))}},getEventProperties:function(e,n){var o={};return A.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return A.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function z(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[K]){var g,m=e.options,t="on"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||y||w?(g=document.createEvent("Event")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var v,b=I(I({},p),W.getEventProperties(o,e));for(v in b)g[v]=b[v];n&&n.dispatchEvent(g),m[t]&&m[t].call(e,g)}}function G(t,e){var n=(o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{}).evt,o=i(o,U);W.pluginEvent.bind(Ft)(t,e,I({dragEl:V,parentEl:Z,ghostEl:$,rootEl:Q,nextEl:J,lastDownEl:tt,cloneEl:et,cloneHidden:nt,dragStarted:gt,putSortable:st,activeSortable:Ft.active,originalEvent:n,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at,hideGhostForTarget:Rt,unhideGhostForTarget:Xt,cloneNowHidden:function(){nt=!0},cloneNowShown:function(){nt=!1},dispatchSortableEvent:function(t){q({sortable:e,name:t,originalEvent:n})}},o))}var U=["evt"];function q(t){z(I({putSortable:st,cloneEl:et,targetEl:V,rootEl:Q,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at},t))}var V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,yt,wt=!1,Et=!1,Dt=[],St=!1,_t=!1,Ct=[],Tt=!1,xt=[],Ot="undefined"!=typeof document,Mt=n,At=w||y?"cssFloat":"float",Nt=Ot&&!c&&!n&&"draggable"in document.createElement("div"),It=function(){if(Ot){if(y)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Pt=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=B(t,0,e),r=B(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){e="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==e?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[At]||r&&"none"===n[At]&&o<s+t)?"vertical":"horizontal"},kt=function(t){function l(r,a){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==r&&(a||i))return!0;if(null==r||!1===r)return!1;if(a&&"clone"===r)return r;if("function"==typeof r)return l(r(t,e,n,o),a)(t,e,n,o);e=(a?t:e).options.group.name;return!0===r||"string"==typeof r&&r===e||r.join&&-1<r.indexOf(e)}}var e={},n=t.group;n&&"object"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=l(n.pull,!0),e.checkPut=l(n.put),e.revertClone=n.revertClone,t.group=e},Rt=function(){!It&&$&&R($,"display","none")},Xt=function(){!It&&$&&R($,"display","")};Ot&&!c&&document.addEventListener("click",function(t){if(Et)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),Et=!1},!0);function Yt(t){if(V){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,Dt.some(function(t){var e=t[K].options.emptyInsertThreshold;if(e&&!F(t)){var n=X(t),o=i>=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}$&&(r?(r.e+=a-(dt||0),r.f+=l-(ht||0)):r={a:1,b:0,c:0,d:1,e:a,f:l},r="matrix(".concat(r.a,",").concat(r.b,",").concat(r.c,",").concat(r.d,",").concat(r.e,",").concat(r.f,")"),R($,"webkitTransform",r),R($,"mozTransform",r),R($,"msTransform",r),R($,"transform",r),dt=a,ht=l,ut=i),t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!$){var t=this.options.fallbackOnBody?document.body:Q,e=X(V,!0,Mt,!0,t),n=this.options;if(Mt){for(yt=t;"static"===R(yt,"position")&&"none"===R(yt,"transform")&&yt!==document;)yt=yt.parentNode;yt!==document.body&&yt!==document.documentElement?(yt===document&&(yt=O()),e.top+=yt.scrollTop,e.left+=yt.scrollLeft):yt=O(),Ct=E(yt)}k($=V.cloneNode(!0),n.ghostClass,!1),k($,n.fallbackClass,!0),k($,n.dragClass,!0),R($,"transition",""),R($,"transform",""),R($,"box-sizing","border-box"),R($,"margin",0),R($,"top",e.top),R($,"left",e.left),R($,"width",e.width),R($,"height",e.height),R($,"opacity","0.8"),R($,"position",Mt?"absolute":"fixed"),R($,"zIndex","100000"),R($,"pointerEvents","none"),Ft.ghost=$,t.appendChild($),R($,"transform-origin",ft/parseInt($.style.width)*100+"% "+pt/parseInt($.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;G("dragStart",this,{evt:t}),Ft.eventCanceled?this._onDrop():(G("setupClone",this),Ft.eventCanceled||((et=_(V)).removeAttribute("id"),et.draggable=!1,et.style["will-change"]="",this._hideClone(),k(et,this.options.chosenClass,!1),Ft.clone=et),n.cloneId=Kt(function(){G("clone",n),Ft.eventCanceled||(n.options.removeCloneOnHide||Q.insertBefore(et,V),n._hideClone(),q({sortable:n,name:"clone"}))}),e||k(V,i.dragClass,!0),e?(Et=!0,n._loopId=setInterval(n._emulateDragOver,50)):(f(document,"mouseup",n._onDrop),f(document,"touchend",n._onDrop),f(document,"touchcancel",n._onDrop),o&&(o.effectAllowed="move",i.setData&&i.setData.call(n,o,V)),h(document,"drop",n),R(V,"transform","translateZ(0)")),wt=!0,n._dragStartId=Kt(n._dragStarted.bind(n,e,t)),h(document,"selectstart",n),gt=!0,u&&R(document.body,"user-select","none"))},_onDragOver:function(n){var o,i,r,t,e,a=this.el,l=n.target,s=this.options,c=s.group,u=Ft.active,d=lt===c,h=s.sort,f=st||u,p=this,g=!1;if(!Tt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),l=P(l,s.draggable,a,!0),O("dragOver"),Ft.eventCanceled)return g;if(V.contains(n.target)||l.animated&&l.animatingX&&l.animatingY||p._ignoreWhileAnimating===l)return A(!1);if(Et=!1,u&&!s.disabled&&(d?h||(i=Z!==Q):st===this||(this.lastPutMode=lt.checkPull(this,u,V,n))&&c.checkPut(this,u,V,n))){if(r="vertical"===this._getDirection(n,l),o=X(V),O("dragOverValid"),Ft.eventCanceled)return g;if(i)return Z=Q,M(),this._hideClone(),O("revert"),Ft.eventCanceled||(J?Q.insertBefore(V,J):Q.appendChild(V)),A(!0);var m=F(a,s.draggable);if(m&&(S=n,c=r,x=X(F((D=this).el,D.options.draggable)),D=L(D.el,D.options,$),!(c?S.clientX>D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY<C.top&&t.clientX<C.right:t.clientY<_.top-10||t.clientY<C.bottom&&t.clientX<C.left)){var v=B(a,0,s,!0);if(v===V)return A(!1);if(E=X(l=v),!1!==jt(Q,a,V,o,l,E,n,!1))return M(),a.insertBefore(V,v),Z=a,N(),A(!0)}else if(l.parentNode===a){var b,y,w,E=X(l),D=V.parentNode!==a,S=(S=V.animated&&V.toRect||o,x=l.animated&&l.toRect||E,_=(e=r)?S.left:S.top,t=e?S.right:S.bottom,C=e?S.width:S.height,v=e?x.left:x.top,S=e?x.right:x.bottom,x=e?x.width:x.height,!(_===v||t===S||_+C/2===v+x/2)),_=r?"top":"left",C=Y(l,"top","top")||Y(V,"top","top"),v=C?C.scrollTop:void 0;if(mt!==l&&(y=E[_],St=!1,_t=!S&&s.invertSwap||D),0!==(b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,t=o?n.top:n.left,o=o?n.bottom:n.right,n=!1;if(!a)if(l&&bt<c*i){if(St=!St&&(1===vt?t+c*r/2<s:s<o-c*r/2)?!0:St)n=!0;else if(1===vt?s<t+bt:o-bt<s)return-vt}else if(t+c*(1-i)/2<s&&s<o-c*(1-i)/2)return function(t){return j(V)<j(t)?1:-1}(e);if((n=n||a)&&(s<t+c*r/2||o-c*r/2<s))return t+c/2<s?1:-1;return 0}(n,l,E,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,_t,mt===l)))for(var T=j(V);(w=Z.children[T-=b])&&("none"===R(w,"display")||w===$););if(0===b||w===l)return A(!1);vt=b;var x=(mt=l).nextElementSibling,D=!1,S=jt(Q,a,V,o,l,E,n,D=1===b);if(!1!==S)return 1!==S&&-1!==S||(D=1===S),Tt=!0,setTimeout(Lt,30),M(),D&&!x?a.appendChild(V):l.parentNode.insertBefore(V,D?x:l),C&&H(C,0,v-C.scrollTop),Z=V.parentNode,void 0===y||_t||(bt=Math.abs(y-X(l)[_])),N(),A(!0)}}else{if(m===V)return A(!1);if((l=m&&a===n.target?m:l)&&(E=X(l)),!1!==jt(Q,a,V,o,l,E,n,!!l))return M(),m&&m.nextSibling?a.insertBefore(V,m.nextSibling):a.appendChild(V),Z=a,N(),A(!0)}if(a.contains(V))return A(!1)}return!1}function O(t,e){G(t,p,I({evt:n,isOwner:d,axis:r?"vertical":"horizontal",revert:i,dragRect:o,targetRect:E,canSort:h,fromSortable:f,target:l,completed:A,onMove:function(t,e){return jt(Q,a,V,o,t,X(t),n,e)},changed:N},e))}function M(){O("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function A(t){return O("dragOverCompleted",{insertion:t}),t&&(d?u._hideClone():u._showClone(p),p!==f&&(k(V,(st||u).options.ghostClass,!1),k(V,s.ghostClass,!0)),st!==p&&p!==Ft.active?st=p:p===Ft.active&&st&&(st=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll(function(){O("dragOverAnimationComplete"),p._ignoreWhileAnimating=null}),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===V&&!V.animated||l===a&&!l.animated)&&(mt=null),s.dragoverBubble||n.rootEl||l===document||(V.parentNode[K]._isOutsideThisEl(n.target),t||Yt(n)),!s.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),g=!0}function N(){it=j(V),at=j(V,s.draggable),q({sortable:p,name:"change",toEl:a,newIndex:it,newDraggableIndex:at,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){f(document,"mousemove",this._onTouchMove),f(document,"touchmove",this._onTouchMove),f(document,"pointermove",this._onTouchMove),f(document,"dragover",Yt),f(document,"mousemove",Yt),f(document,"touchmove",Yt)},_offUpEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._onDrop),f(t,"touchend",this._onDrop),f(t,"pointerup",this._onDrop),f(t,"touchcancel",this._onDrop),f(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;it=j(V),at=j(V,n.draggable),G("drop",this,{evt:t}),Z=V&&V.parentNode,it=j(V),at=j(V,n.draggable),Ft.eventCanceled||(St=_t=wt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Wt(this.cloneId),Wt(this._dragStartId),this.nativeDraggable&&(f(document,"drop",this),f(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),u&&R(document.body,"user-select",""),R(V,"transform",""),t&&(gt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),$&&$.parentNode&&$.parentNode.removeChild($),(Q===Z||st&&"clone"!==st.lastPutMode)&&et&&et.parentNode&&et.parentNode.removeChild(et),V&&(this.nativeDraggable&&f(V,"dragend",this),Ht(V),V.style["will-change"]="",gt&&!wt&&k(V,(st||this).options.ghostClass,!1),k(V,this.options.chosenClass,!1),q({sortable:this,name:"unchoose",toEl:Z,newIndex:null,newDraggableIndex:null,originalEvent:t}),Q!==Z?(0<=it&&(q({rootEl:Z,name:"add",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"remove",toEl:Z,originalEvent:t}),q({rootEl:Z,name:"sort",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),st&&st.save()):it!==ot&&0<=it&&(q({sortable:this,name:"update",toEl:Z,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),Ft.active&&(null!=it&&-1!==it||(it=ot,at=rt),q({sortable:this,name:"end",toEl:Z,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){G("nulling",this),Q=V=Z=$=J=et=tt=nt=ct=ut=gt=it=at=ot=rt=mt=vt=st=lt=Ft.dragged=Ft.ghost=Ft.clone=Ft.active=null,xt.forEach(function(t){t.checked=!0}),xt.length=dt=ht=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":V&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||function(t){var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;for(;n--;)o+=e.charCodeAt(n);return o.toString(36)}(t));return e},sort:function(t,e){var n={},o=this.el;this.toArray().forEach(function(t,e){e=o.children[e];P(e,this.options.draggable,o,!1)&&(n[t]=e)},this),e&&this.captureAnimationState(),t.forEach(function(t){n[t]&&(o.removeChild(n[t]),o.appendChild(n[t]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=W.modifyOption(this,t,e);n[t]=void 0!==o?o:e,"group"===t&&kt(n)},destroy:function(){G("destroy",this);var t=this.el;t[K]=null,f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart),f(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Dt.splice(Dt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){nt||(G("hideClone",this),Ft.eventCanceled||(R(et,"display","none"),this.options.removeCloneOnHide&&et.parentNode&&et.parentNode.removeChild(et),nt=!0))},_showClone:function(t){"clone"===t.lastPutMode?nt&&(G("showClone",this),Ft.eventCanceled||(V.parentNode!=Q||this.options.group.revertClone?J?Q.insertBefore(et,J):Q.appendChild(et):Q.insertBefore(et,V),this.options.group.revertClone&&this.animate(V,et),R(et,"display",""),nt=!1)):this._hideClone()}},Ot&&h(document,"touchmove",function(t){(Ft.active||wt)&&t.cancelable&&t.preventDefault()}),Ft.utils={on:h,off:f,css:R,find:b,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:S,closest:P,toggleClass:k,clone:_,index:j,nextTick:Kt,cancelNextTick:Wt,detectDirection:Pt,getChild:B},Ft.get=function(t){return t[K]},Ft.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];(e=e[0].constructor===Array?e[0]:e).forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(t));t.utils&&(Ft.utils=I(I({},Ft.utils),t.utils)),W.mount(t)})},Ft.create=function(t,e){return new Ft(t,e)};var zt,Gt,Ut,qt,Vt,Zt,$t=[],Qt=!(Ft.version="1.15.2");function Jt(){$t.forEach(function(t){clearInterval(t.pid)}),$t=[]}function te(){clearInterval(Zt)}var ee,ne=S(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=O(),u=!1;Gt!==e&&(Gt=e,Jt(),zt=t.scroll,i=t.scrollFn,!0===zt&&(zt=M(e,!0)));var d=0,h=zt;do{var f=h,p=X(f),g=p.top,m=p.bottom,v=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,p=f.scrollTop,D=f===c?(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX||"visible"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY||"visible"===C.overflowY)):(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY)),T=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(v-r)<=l&&!!T),p=D&&(Math.abs(m-a)<=l&&p+w<_)-(Math.abs(g-a)<=l&&!!p);if(!$t[d])for(var x=0;x<=d;x++)$t[x]||($t[x]={});$t[d].vx==T&&$t[d].vy==p&&$t[d].el===f||($t[d].el=f,$t[d].vx=T,$t[d].vy=p,clearInterval($t[d].pid),0==T&&0==p||(u=!0,$t[d].pid=setInterval(function(){o&&0===this.layer&&Ft.active._onTouchMove(Vt);var t=$t[this.layer].vy?$t[this.layer].vy*s:0,e=$t[this.layer].vx?$t[this.layer].vx*s:0;"function"==typeof i&&"continue"!==i.call(Ft.dragged.parentNode[K],e,t,n,Vt,$t[this.layer].el)||H($t[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=M(h,!1)));Qt=u}},30),c=function(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,t=t.unhideGhostForTarget;e&&(i=n||i,a(),e=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,e=document.elementFromPoint(e.clientX,e.clientY),t(),i&&!i.el.contains(e)&&(r("spill"),this.onSpill({dragEl:o,putSortable:n})))};function oe(){}function ie(){}oe.prototype={startIndex:null,dragStart:function(t){t=t.oldDraggableIndex;this.startIndex=t},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();t=B(this.sortable.el,this.startIndex,this.options);t?this.sortable.el.insertBefore(e,t):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:c},a(oe,{pluginName:"revertOnSpill"}),ie.prototype={onSpill:function(t){var e=t.dragEl,t=t.putSortable||this.sortable;t.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),t.animateAll()},drop:c},a(ie,{pluginName:"removeOnSpill"});var re,ae,le,se,ce,ue=[],de=[],he=!1,fe=!1,pe=!1;function ge(n,o){de.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)})}function me(){ue.forEach(function(t){t!==le&&t.parentNode&&t.parentNode.removeChild(t)})}return Ft.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){t=t.originalEvent;this.sortable.nativeDraggable?h(document,"dragover",this._handleAutoScroll):this.options.supportPointer?h(document,"pointermove",this._handleFallbackAutoScroll):t.touches?h(document,"touchmove",this._handleFallbackAutoScroll):h(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){t=t.originalEvent;this.options.dragOverBubble||t.rootEl||this._handleAutoScroll(t)},drop:function(){this.sortable.nativeDraggable?f(document,"dragover",this._handleAutoScroll):(f(document,"pointermove",this._handleFallbackAutoScroll),f(document,"touchmove",this._handleFallbackAutoScroll),f(document,"mousemove",this._handleFallbackAutoScroll)),te(),Jt(),clearTimeout(g),g=void 0},nulling:function(){Vt=Gt=zt=Qt=Zt=Ut=qt=null,$t.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o,i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(r,a);Vt=e,n||this.options.forceAutoScrollFallback||w||y||u?(ne(e,this.options,t,n),o=M(t,!0),!Qt||Zt&&r===Ut&&a===qt||(Zt&&te(),Zt=setInterval(function(){var t=M(document.elementFromPoint(r,a),!0);t!==o&&(o=t,Jt()),ne(e,i.options,t,n)},10),Ut=r,qt=a)):this.options.bubbleScroll&&M(t,!0)!==O()?ne(e,this.options,M(t,!1),!1):Jt()}},a(t,{pluginName:"scroll",initializeByDefault:!0})}),Ft.mount(ie,oe),Ft.mount(new function(){function t(){this.defaults={swapClass:"sortable-swap-highlight"}}return t.prototype={dragStart:function(t){t=t.dragEl;ee=t},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;i.options.swap&&(t=this.sortable.el,i=this.options,n&&n!==t&&(t=ee,ee=!1!==o(n)?(k(n,i.swapClass,!0),n):null,t&&t!==ee&&k(t,i.swapClass,!1)),r(),e(!0),a())},drop:function(t){var e,n,o=t.activeSortable,i=t.putSortable,r=t.dragEl,a=i||this.sortable,l=this.options;ee&&k(ee,l.swapClass,!1),ee&&(l.swap||i&&i.options.swap)&&r!==ee&&(a.captureAnimationState(),a!==o&&o.captureAnimationState(),n=ee,t=(e=r).parentNode,l=n.parentNode,t&&l&&!t.isEqualNode(n)&&!l.isEqualNode(e)&&(i=j(e),r=j(n),t.isEqualNode(l)&&i<r&&r++,t.insertBefore(n,t.children[i]),l.insertBefore(e,l.children[r])),a.animateAll(),a!==o&&o.animateAll())},nulling:function(){ee=null}},a(t,{pluginName:"swap",eventProperties:function(){return{swapItem:ee}}})}),Ft.mount(new function(){function t(o){for(var t in this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.avoidImplicitDeselect||(o.options.supportPointer?h(document,"pointerup",this._deselectMultiDrag):(h(document,"mouseup",this._deselectMultiDrag),h(document,"touchend",this._deselectMultiDrag))),h(document,"keydown",this._checkKeyDown),h(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(t,e){var n="";ue.length&&ae===o?ue.forEach(function(t,e){n+=(e?", ":"")+t.textContent}):n=e.textContent,t.setData("Text",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){t=t.dragEl;le=t},delayEnded:function(){this.isMultiDrag=~ue.indexOf(le)},setupClone:function(t){var e=t.sortable,t=t.cancel;if(this.isMultiDrag){for(var n=0;n<ue.length;n++)de.push(_(ue[n])),de[n].sortableIndex=ue[n].sortableIndex,de[n].draggable=!1,de[n].style["will-change"]="",k(de[n],this.options.selectedClass,!1),ue[n]===le&&k(de[n],this.options.chosenClass,!1);e._hideClone(),t()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,t=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||ue.length&&ae===e&&(ge(!0,n),o("clone"),t()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,t=t.cancel;this.isMultiDrag&&(ge(!1,n),de.forEach(function(t){R(t,"display","")}),e(),ce=!1,t())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),t=t.cancel;this.isMultiDrag&&(de.forEach(function(t){R(t,"display","none"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),ce=!0,t())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ae&&ae.multiDrag._deselectMultiDrag(),ue.forEach(function(t){t.sortableIndex=j(t)}),ue=ue.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),pe=!0},dragStarted:function(t){var e,n=this,t=t.sortable;this.isMultiDrag&&(this.options.sort&&(t.captureAnimationState(),this.options.animation&&(ue.forEach(function(t){t!==le&&R(t,"position","absolute")}),e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&C(t,e)}),he=fe=!0)),t.animateAll(function(){he=fe=!1,n.options.animation&&ue.forEach(function(t){T(t)}),n.options.sort&&me()}))},dragOver:function(t){var e=t.target,n=t.completed,t=t.cancel;fe&&~ue.indexOf(e)&&(n(!1),t())},revert:function(t){var n,o,e=t.fromSortable,i=t.rootEl,r=t.sortable,a=t.dragRect;1<ue.length&&(ue.forEach(function(t){r.addAnimationState({target:t,rect:fe?X(t):a}),T(t),t.fromRect=a,e.removeAnimationState(t)}),fe=!1,n=!this.options.removeCloneOnHide,o=i,ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)}))},dragOverCompleted:function(t){var e,n=t.sortable,o=t.isOwner,i=t.insertion,r=t.activeSortable,a=t.parentEl,l=t.putSortable,t=this.options;i&&(o&&r._hideClone(),he=!1,t.animation&&1<ue.length&&(fe||!o&&!r.options.sort&&!l)&&(e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&(C(t,e),a.appendChild(t))}),fe=!0),o||(fe||me(),1<ue.length?(o=ce,r._showClone(n),r.options.animation&&!ce&&o&&de.forEach(function(t){r.addAnimationState({target:t,rect:se}),t.fromRect=se,t.thisAnimationDuration=null})):r._showClone(n)))},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,t=t.activeSortable;ue.forEach(function(t){t.thisAnimationDuration=null}),t.options.animation&&!n&&t.multiDrag.isMultiDrag&&(se=a({},e),e=v(le,!0),se.top-=e.f,se.left-=e.e)},dragOverAnimationComplete:function(){fe&&(fe=!1,me())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c,u,d,h=this.options,f=o.children;if(!pe)if(h.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(le,h.selectedClass,!~ue.indexOf(le)),~ue.indexOf(le))ue.splice(ue.indexOf(le),1),re=null,z({sortable:i,rootEl:n,name:"deselect",targetEl:le,originalEvent:e});else{if(ue.push(le),z({sortable:i,rootEl:n,name:"select",targetEl:le,originalEvent:e}),e.shiftKey&&re&&i.el.contains(re)){var p=j(re),t=j(le);if(~p&&~t&&p!==t)for(var g,m=p<t?(g=p,t):(g=t,p+1);g<m;g++)~ue.indexOf(f[g])||(k(f[g],h.selectedClass,!0),ue.push(f[g]),z({sortable:i,rootEl:n,name:"select",targetEl:f[g],originalEvent:e}))}else re=le;ae=s}pe&&this.isMultiDrag&&(fe=!1,(o[K].options.sort||o!==n)&&1<ue.length&&(c=X(le),u=j(le,":not(."+this.options.selectedClass+")"),!he&&h.animation&&(le.thisAnimationDuration=null),s.captureAnimationState(),he||(h.animation&&(le.fromRect=c,ue.forEach(function(t){var e;t.thisAnimationDuration=null,t!==le&&(e=fe?X(t):c,t.fromRect=e,s.addAnimationState({target:t,rect:e}))})),me(),ue.forEach(function(t){f[u]?o.insertBefore(t,f[u]):o.appendChild(t),u++}),a===j(le)&&(d=!1,ue.forEach(function(t){t.sortableIndex!==j(t)&&(d=!0)}),d&&(r("update"),r("sort")))),ue.forEach(function(t){T(t)}),s.animateAll()),ae=s),(n===o||l&&"clone"!==l.lastPutMode)&&de.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=pe=!1,de.length=0},destroyGlobal:function(){this._deselectMultiDrag(),f(document,"pointerup",this._deselectMultiDrag),f(document,"mouseup",this._deselectMultiDrag),f(document,"touchend",this._deselectMultiDrag),f(document,"keydown",this._checkKeyDown),f(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==pe&&pe||ae!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;ue.length;){var e=ue[0];k(e,this.options.selectedClass,!1),ue.shift(),z({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:e,originalEvent:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:"multiDrag",utils:{select:function(t){var e=t.parentNode[K];e&&e.options.multiDrag&&!~ue.indexOf(t)&&(ae&&ae!==e&&(ae.multiDrag._deselectMultiDrag(),ae=e),k(t,e.options.selectedClass,!0),ue.push(t))},deselect:function(t){var e=t.parentNode[K],n=ue.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),ue.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return ue.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=fe&&t!==le?-1:fe?j(t,":not(."+n.options.selectedClass+")"):j(t),i.push({multiDragElement:t,index:e})}),{items:r(ue),clones:[].concat(de),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return"ctrl"===(t=t.toLowerCase())?t="Control":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Ft});';import{existsSync as be,readFileSync as Bt,writeFileSync as tt,rmSync as Yn,mkdirSync as jt,copyFileSync as zt}from"fs";import{resolve as ne}from"path";import{existsSync as M,readFileSync as ue,writeFileSync as me,mkdirSync as Y,readdirSync as pe,rmSync as Mt,statSync as I}from"fs";import{resolve as Je,relative as It,dirname as he,join as z,extname as J,basename as ge}from"path";var A=200*1024,Xe=new Set([".md",".markdown",".txt"]),G=new Set([".md",".markdown",".txt",".json",".jsonc",".yaml",".yml",".toml",".sh",".bash",".zsh",".py",".js",".mjs",".ts",".tsx",".jsx",".css",".html",".lock",".cfg",".ini",".env",".gitignore"]),Lt=6,Pt=new Set(["CLAUDE.md","SOUL.md","IDENTITY.md","PERSONA.md","AGENT.md","BOOTSTRAP.md"]);function H(t,e){if(!t)throw new Error("Agent workspace is not configured.");let a=Je(t);if(M(a)||Y(a,{recursive:!0}),!e||e.startsWith("/")||e.match(/^[a-zA-Z]:/))throw new Error(`Path must be relative to the workspace: ${e}`);let n=Je(a,e);if(It(a,n).startsWith(".."))throw new Error(`Path escapes the workspace: ${e}`);return n}function Ve(t){let e=J(t).toLowerCase();if(!Xe.has(e))throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${e}).`)}function de(t,e){let a=t.split(/\r?\n/).find(n=>n.trim().startsWith("#"));return a&&a.replace(/^#+\s*/,"").trim().slice(0,120)||e}function F(t){let e=H(t,"."),a=[],n=new Set,o=[];try{o=pe(e)}catch{}for(let i of Pt){let l=z(e,i);if(M(l)&&I(l).isFile()){let d=I(l).size,u=d>0&&d<A?ce(l):"";a.push({path:i,title:de(u,i),size:d,exists:!0}),n.add(i)}else a.push({path:i,title:i,size:0,exists:!1}),n.add(i)}for(let i of o){if(n.has(i))continue;let l=z(e,i);try{let d=I(l);if(!d.isFile()||!Xe.has(J(i).toLowerCase()))continue;let u=d.size>0&&d.size<A?ce(l):"";a.push({path:i,title:de(u,i),size:d.size,exists:!0})}catch{}}let r=[],s=z(e,".claude","skills");if(M(s))try{for(let i of pe(s)){let l=z(s,i),d;try{d=I(l)}catch{continue}if(!d.isDirectory())continue;let u=z(l,"SKILL.md");if(!M(u))continue;let p=I(u),f=p.size<A?ce(u):"";r.push({slug:i,title:de(f,i),size:p.size,path:`.claude/skills/${i}/SKILL.md`})}}catch{}return r.sort((i,l)=>i.slug.localeCompare(l.slug)),{identity:a,skills:r,workspace:e}}function ce(t){try{return ue(t,"utf-8")}catch{return""}}function Q(t,e){let a=H(t,e);if(Ve(a),!M(a))return{path:e,content:""};if(I(a).size>A)throw new Error(`File is too large to edit in the admin panel (> ${A/1024} KB).`);return{path:e,content:ue(a,"utf-8")}}function Z(t,e,a){let n=H(t,e);Ve(n);let o=Buffer.from(a,"utf-8");if(o.length>A)throw new Error(`File too large (${o.length} > ${A} bytes).`);return Y(he(n),{recursive:!0}),me(n,o),{path:e,bytes:o.length}}function ee(t,e,a={}){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.");let n=`.claude/skills/${e}/SKILL.md`,o=H(t,n);if(M(o))throw new Error(`Skill "${e}" already exists.`);Y(he(o),{recursive:!0});let r=a.content?.trim()?a.content:Dt(e,a.title);return me(o,r),{slug:e,path:n}}function te(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=H(t,`.claude/skills/${e}`);if(!M(a))throw new Error(`Skill "${e}" not found.`);if(!I(a).isDirectory())throw new Error(`Skill path is not a directory: ${e}`);return Mt(a,{recursive:!0,force:!0}),{slug:e}}function Ye(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=H(t,`.claude/skills/${e}`);if(!M(a))throw new Error(`Skill "${e}" not found.`);let n=[];return o(a,"",0,n),n.sort((r,s)=>r.kind!==s.kind?r.kind==="dir"?-1:1:r.path.localeCompare(s.path)),n;function o(r,s,i,l){if(i>Lt)return;let d=[];try{d=pe(r)}catch{return}for(let u of d){if(u==="node_modules"||u===".git")continue;let p=z(r,u),f=s?`${s}/${u}`:u,b;try{b=I(p)}catch{continue}if(b.isDirectory())l.push({path:`${f}/`,kind:"dir",size:0,editable:!1}),o(p,f,i+1,l);else if(b.isFile()){let c=J(u).toLowerCase(),m=G.has(c)||G.has(u.toLowerCase());l.push({path:f,kind:"file",size:b.size,editable:m&&b.size<A})}}}}function Qe(t,e,a){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!a||a.includes(".."))throw new Error(`Invalid file path: ${a}`);let n=H(t,`.claude/skills/${e}/${a}`);if(!M(n)||!I(n).isFile())throw new Error(`File not found: ${a}`);let o=J(n).toLowerCase(),r=ge(n).toLowerCase();if(!G.has(o)&&!G.has(r))throw new Error(`File type not editable: ${a}`);if(I(n).size>A)throw new Error(`File too large to edit (> ${A/1024} KB): ${a}`);return{path:a,content:ue(n,"utf-8")}}function Ze(t,e,a,n){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!a||a.includes(".."))throw new Error(`Invalid file path: ${a}`);let o=H(t,`.claude/skills/${e}/${a}`),r=J(o).toLowerCase(),s=ge(o).toLowerCase();if(!G.has(r)&&!G.has(s))throw new Error(`File type not editable: ${a}`);let i=Buffer.from(n,"utf-8");if(i.length>A)throw new Error(`File too large (${i.length} > ${A} bytes).`);return Y(he(o),{recursive:!0}),me(o,i),{path:a,bytes:i.length}}function ae(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=H(t,`.claude/skills/${e}`);if(!M(a))throw new Error(`Skill "${e}" not found.`);let n=o=>M(z(a,o));return n("pnpm-lock.yaml")?{manager:"pnpm",file:"pnpm-lock.yaml",command:"pnpm install"}:n("yarn.lock")?{manager:"yarn",file:"yarn.lock",command:"yarn install"}:n("package.json")?{manager:"npm",file:"package.json",command:"npm install"}:n("requirements.txt")?{manager:"pip",file:"requirements.txt",command:"pip install -r requirements.txt"}:n("Gemfile")?{manager:"bundle",file:"Gemfile",command:"bundle install"}:{manager:null,file:null,command:null}}function Dt(t,e){return`# ${e?.trim()||t}
1
+ import{a as wt}from"./chunk-MTRWXRFF.js";import{a as kt}from"./chunk-NOF4HSGK.js";import{a as S,b as _t,c as Et,d as St,e as U,f as ce,g as De,h as Tt}from"./chunk-WFHIIVLL.js";import{a as A,b as R,g as ft,h as vt,i as j,j as z,k as ve,l as bt,m as xt,n as yt}from"./chunk-CNNLREHD.js";import{a as Ie,b as Le,f as pt,g as ut,h as mt,p as ht}from"./chunk-E4ZZILAK.js";import"./chunk-EW27SCCC.js";import"./chunk-QW2OYR3N.js";import"./chunk-DLVOHCTA.js";import{a as dt,b as ct,e as B}from"./chunk-MBOLXKA2.js";import"./chunk-N67JZAXU.js";import{f as gt}from"./chunk-CDNWCYF7.js";import"./chunk-AFRHKA7B.js";import{a as lt}from"./chunk-5F3J55LG.js";import"./chunk-4MREHZP5.js";import{b as it}from"./chunk-7P5QRX5W.js";import"./chunk-JTJIPNXY.js";import"./chunk-64P4QHSZ.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";import{createServer as So}from"http";import{appendFileSync as To,existsSync as Da,mkdirSync as Co}from"fs";import{readFile as Ao}from"fs/promises";import{fileURLToPath as $o}from"url";import{dirname as Mo,resolve as ge}from"path";var Ct='/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */\n!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?e(Object(i),!0).forEach(function(t){var e,n;e=o,t=i[n=t],n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(t){Object.defineProperty(o,t,Object.getOwnPropertyDescriptor(i,t))})}return o}function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,o=arguments[e];for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(t[n]=o[n])}return t}).apply(this,arguments)}function i(t,e){if(null==t)return{};var n,o=function(t,e){if(null==t)return{};for(var n,o={},i=Object.keys(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols)for(var i=Object.getOwnPropertySymbols(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n]);return o}function r(t){return function(t){if(Array.isArray(t))return l(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return l(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var y=t(/(?:Trident.*rv[ :]?11\\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1};function h(t,e,n){t.addEventListener(e,n,!y&&d)}function f(t,e,n){t.removeEventListener(e,n,!y&&d)}function p(t,e){if(e&&(">"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function O(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d=t!==window&&t.parentNode&&t!==O()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth);if((e||n)&&t!==window&&(i=i||t.parentNode,!y))do{if(i&&i.getBoundingClientRect&&("none"!==R(i,"transform")||n&&"static"!==R(i,"position"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,"border-top-width")),l-=h.left+parseInt(R(i,"border-left-width")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);return o&&t!==window&&(o=(e=v(i||t))&&e.a,t=e&&e.d,e&&(s=(a/=t)+(u/=t),c=(l/=o)+(d/=o))),{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=M(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!("top"===n||"left"===n?r<=i:i<=r))return o;if(o===O())break;o=M(o,!1)}return!1}function B(t,e,n,o){for(var i=0,r=0,a=t.children;r<a.length;){if("none"!==a[r].style.display&&a[r]!==Ft.ghost&&(o||a[r]!==Ft.dragged)&&P(a[r],n.draggable,t,!1)){if(i===e)return a[r];i++}r++}return null}function F(t,e){for(var n=t.lastElementChild;n&&(n===Ft.ghost||"none"===R(n,"display")||e&&!p(n,e));)n=n.previousElementSibling;return n||null}function j(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)"TEMPLATE"===t.nodeName.toUpperCase()||t===Ft.clone||e&&!p(t,e)||n++;return n}function E(t){var e=0,n=0,o=O();if(t)do{var i=v(t),r=i.a,i=i.d}while(e+=t.scrollLeft*r,n+=t.scrollTop*i,t!==o&&(t=t.parentNode));return[e,n]}function M(t,e){if(!t||!t.getBoundingClientRect)return O();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return O();if(o||e)return n;o=!0}}}while(n=n.parentNode);return O()}function D(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function S(e,n){return function(){var t;g||(1===(t=arguments).length?e.call(this,t[0]):e.apply(this,t),g=setTimeout(function(){g=void 0},n))}}function H(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function _(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function C(t,e){R(t,"position","absolute"),R(t,"top",e.top),R(t,"left",e.left),R(t,"width",e.width),R(t,"height",e.height)}function T(t){R(t,"position",""),R(t,"top",""),R(t,"left",""),R(t,"width",""),R(t,"height","")}function L(n,o,i){var r={};return Array.from(n.children).forEach(function(t){var e;P(t,o.draggable,n,!1)&&!t.animated&&t!==i&&(e=X(t),r.left=Math.min(null!==(t=r.left)&&void 0!==t?t:1/0,e.left),r.top=Math.min(null!==(t=r.top)&&void 0!==t?t:1/0,e.top),r.right=Math.max(null!==(t=r.right)&&void 0!==t?t:-1/0,e.right),r.bottom=Math.max(null!==(t=r.bottom)&&void 0!==t?t:-1/0,e.bottom))}),r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}var K="Sortable"+(new Date).getTime();function x(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){var e,n;"none"!==R(t,"display")&&t!==Ft.ghost&&(o.push({target:t,rect:X(t)}),e=I({},o[o.length-1].rect),!t.thisAnimationDuration||(n=v(t,!0))&&(e.top-=n.f,e.left-=n.e),t.fromRect=e)})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void("function"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&D(r,i)&&!D(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(t=l,s=r,r=a,a=c.options,e=Math.sqrt(Math.pow(s.top-t.top,2)+Math.pow(s.left-t.left,2))/Math.sqrt(Math.pow(s.top-r.top,2)+Math.pow(s.left-r.left,2))*a.animation),D(i,o)||(n.prevFromRect=o,n.prevToRect=i,e=e||c.options.animation,c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){"function"==typeof t&&t()},d):"function"==typeof t&&t(),o=[]},animate:function(t,e,n,o){var i,r;o&&(R(t,"transition",""),R(t,"transform",""),i=(r=v(this.el))&&r.a,r=r&&r.d,i=(e.left-n.left)/(i||1),r=(e.top-n.top)/(r||1),t.animatingX=!!i,t.animatingY=!!r,R(t,"transform","translate3d("+i+"px,"+r+"px,0)"),this.forRepaintDummy=t.offsetWidth,R(t,"transition","transform "+o+"ms"+(this.options.easing?" "+this.options.easing:"")),R(t,"transform","translate3d(0,0,0)"),"number"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,"transition",""),R(t,"transform",""),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o))}}}var A=[],N={initializeByDefault:!0},W={mount:function(e){for(var t in N)!N.hasOwnProperty(t)||t in e||(e[t]=N[t]);A.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),A.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";A.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in A.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&(void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r))}},getEventProperties:function(e,n){var o={};return A.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return A.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function z(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[K]){var g,m=e.options,t="on"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||y||w?(g=document.createEvent("Event")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var v,b=I(I({},p),W.getEventProperties(o,e));for(v in b)g[v]=b[v];n&&n.dispatchEvent(g),m[t]&&m[t].call(e,g)}}function G(t,e){var n=(o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{}).evt,o=i(o,U);W.pluginEvent.bind(Ft)(t,e,I({dragEl:V,parentEl:Z,ghostEl:$,rootEl:Q,nextEl:J,lastDownEl:tt,cloneEl:et,cloneHidden:nt,dragStarted:gt,putSortable:st,activeSortable:Ft.active,originalEvent:n,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at,hideGhostForTarget:Rt,unhideGhostForTarget:Xt,cloneNowHidden:function(){nt=!0},cloneNowShown:function(){nt=!1},dispatchSortableEvent:function(t){q({sortable:e,name:t,originalEvent:n})}},o))}var U=["evt"];function q(t){z(I({putSortable:st,cloneEl:et,targetEl:V,rootEl:Q,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at},t))}var V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,yt,wt=!1,Et=!1,Dt=[],St=!1,_t=!1,Ct=[],Tt=!1,xt=[],Ot="undefined"!=typeof document,Mt=n,At=w||y?"cssFloat":"float",Nt=Ot&&!c&&!n&&"draggable"in document.createElement("div"),It=function(){if(Ot){if(y)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Pt=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=B(t,0,e),r=B(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){e="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==e?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[At]||r&&"none"===n[At]&&o<s+t)?"vertical":"horizontal"},kt=function(t){function l(r,a){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==r&&(a||i))return!0;if(null==r||!1===r)return!1;if(a&&"clone"===r)return r;if("function"==typeof r)return l(r(t,e,n,o),a)(t,e,n,o);e=(a?t:e).options.group.name;return!0===r||"string"==typeof r&&r===e||r.join&&-1<r.indexOf(e)}}var e={},n=t.group;n&&"object"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=l(n.pull,!0),e.checkPut=l(n.put),e.revertClone=n.revertClone,t.group=e},Rt=function(){!It&&$&&R($,"display","none")},Xt=function(){!It&&$&&R($,"display","")};Ot&&!c&&document.addEventListener("click",function(t){if(Et)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),Et=!1},!0);function Yt(t){if(V){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,Dt.some(function(t){var e=t[K].options.emptyInsertThreshold;if(e&&!F(t)){var n=X(t),o=i>=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}$&&(r?(r.e+=a-(dt||0),r.f+=l-(ht||0)):r={a:1,b:0,c:0,d:1,e:a,f:l},r="matrix(".concat(r.a,",").concat(r.b,",").concat(r.c,",").concat(r.d,",").concat(r.e,",").concat(r.f,")"),R($,"webkitTransform",r),R($,"mozTransform",r),R($,"msTransform",r),R($,"transform",r),dt=a,ht=l,ut=i),t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!$){var t=this.options.fallbackOnBody?document.body:Q,e=X(V,!0,Mt,!0,t),n=this.options;if(Mt){for(yt=t;"static"===R(yt,"position")&&"none"===R(yt,"transform")&&yt!==document;)yt=yt.parentNode;yt!==document.body&&yt!==document.documentElement?(yt===document&&(yt=O()),e.top+=yt.scrollTop,e.left+=yt.scrollLeft):yt=O(),Ct=E(yt)}k($=V.cloneNode(!0),n.ghostClass,!1),k($,n.fallbackClass,!0),k($,n.dragClass,!0),R($,"transition",""),R($,"transform",""),R($,"box-sizing","border-box"),R($,"margin",0),R($,"top",e.top),R($,"left",e.left),R($,"width",e.width),R($,"height",e.height),R($,"opacity","0.8"),R($,"position",Mt?"absolute":"fixed"),R($,"zIndex","100000"),R($,"pointerEvents","none"),Ft.ghost=$,t.appendChild($),R($,"transform-origin",ft/parseInt($.style.width)*100+"% "+pt/parseInt($.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;G("dragStart",this,{evt:t}),Ft.eventCanceled?this._onDrop():(G("setupClone",this),Ft.eventCanceled||((et=_(V)).removeAttribute("id"),et.draggable=!1,et.style["will-change"]="",this._hideClone(),k(et,this.options.chosenClass,!1),Ft.clone=et),n.cloneId=Kt(function(){G("clone",n),Ft.eventCanceled||(n.options.removeCloneOnHide||Q.insertBefore(et,V),n._hideClone(),q({sortable:n,name:"clone"}))}),e||k(V,i.dragClass,!0),e?(Et=!0,n._loopId=setInterval(n._emulateDragOver,50)):(f(document,"mouseup",n._onDrop),f(document,"touchend",n._onDrop),f(document,"touchcancel",n._onDrop),o&&(o.effectAllowed="move",i.setData&&i.setData.call(n,o,V)),h(document,"drop",n),R(V,"transform","translateZ(0)")),wt=!0,n._dragStartId=Kt(n._dragStarted.bind(n,e,t)),h(document,"selectstart",n),gt=!0,u&&R(document.body,"user-select","none"))},_onDragOver:function(n){var o,i,r,t,e,a=this.el,l=n.target,s=this.options,c=s.group,u=Ft.active,d=lt===c,h=s.sort,f=st||u,p=this,g=!1;if(!Tt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),l=P(l,s.draggable,a,!0),O("dragOver"),Ft.eventCanceled)return g;if(V.contains(n.target)||l.animated&&l.animatingX&&l.animatingY||p._ignoreWhileAnimating===l)return A(!1);if(Et=!1,u&&!s.disabled&&(d?h||(i=Z!==Q):st===this||(this.lastPutMode=lt.checkPull(this,u,V,n))&&c.checkPut(this,u,V,n))){if(r="vertical"===this._getDirection(n,l),o=X(V),O("dragOverValid"),Ft.eventCanceled)return g;if(i)return Z=Q,M(),this._hideClone(),O("revert"),Ft.eventCanceled||(J?Q.insertBefore(V,J):Q.appendChild(V)),A(!0);var m=F(a,s.draggable);if(m&&(S=n,c=r,x=X(F((D=this).el,D.options.draggable)),D=L(D.el,D.options,$),!(c?S.clientX>D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY<C.top&&t.clientX<C.right:t.clientY<_.top-10||t.clientY<C.bottom&&t.clientX<C.left)){var v=B(a,0,s,!0);if(v===V)return A(!1);if(E=X(l=v),!1!==jt(Q,a,V,o,l,E,n,!1))return M(),a.insertBefore(V,v),Z=a,N(),A(!0)}else if(l.parentNode===a){var b,y,w,E=X(l),D=V.parentNode!==a,S=(S=V.animated&&V.toRect||o,x=l.animated&&l.toRect||E,_=(e=r)?S.left:S.top,t=e?S.right:S.bottom,C=e?S.width:S.height,v=e?x.left:x.top,S=e?x.right:x.bottom,x=e?x.width:x.height,!(_===v||t===S||_+C/2===v+x/2)),_=r?"top":"left",C=Y(l,"top","top")||Y(V,"top","top"),v=C?C.scrollTop:void 0;if(mt!==l&&(y=E[_],St=!1,_t=!S&&s.invertSwap||D),0!==(b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,t=o?n.top:n.left,o=o?n.bottom:n.right,n=!1;if(!a)if(l&&bt<c*i){if(St=!St&&(1===vt?t+c*r/2<s:s<o-c*r/2)?!0:St)n=!0;else if(1===vt?s<t+bt:o-bt<s)return-vt}else if(t+c*(1-i)/2<s&&s<o-c*(1-i)/2)return function(t){return j(V)<j(t)?1:-1}(e);if((n=n||a)&&(s<t+c*r/2||o-c*r/2<s))return t+c/2<s?1:-1;return 0}(n,l,E,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,_t,mt===l)))for(var T=j(V);(w=Z.children[T-=b])&&("none"===R(w,"display")||w===$););if(0===b||w===l)return A(!1);vt=b;var x=(mt=l).nextElementSibling,D=!1,S=jt(Q,a,V,o,l,E,n,D=1===b);if(!1!==S)return 1!==S&&-1!==S||(D=1===S),Tt=!0,setTimeout(Lt,30),M(),D&&!x?a.appendChild(V):l.parentNode.insertBefore(V,D?x:l),C&&H(C,0,v-C.scrollTop),Z=V.parentNode,void 0===y||_t||(bt=Math.abs(y-X(l)[_])),N(),A(!0)}}else{if(m===V)return A(!1);if((l=m&&a===n.target?m:l)&&(E=X(l)),!1!==jt(Q,a,V,o,l,E,n,!!l))return M(),m&&m.nextSibling?a.insertBefore(V,m.nextSibling):a.appendChild(V),Z=a,N(),A(!0)}if(a.contains(V))return A(!1)}return!1}function O(t,e){G(t,p,I({evt:n,isOwner:d,axis:r?"vertical":"horizontal",revert:i,dragRect:o,targetRect:E,canSort:h,fromSortable:f,target:l,completed:A,onMove:function(t,e){return jt(Q,a,V,o,t,X(t),n,e)},changed:N},e))}function M(){O("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function A(t){return O("dragOverCompleted",{insertion:t}),t&&(d?u._hideClone():u._showClone(p),p!==f&&(k(V,(st||u).options.ghostClass,!1),k(V,s.ghostClass,!0)),st!==p&&p!==Ft.active?st=p:p===Ft.active&&st&&(st=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll(function(){O("dragOverAnimationComplete"),p._ignoreWhileAnimating=null}),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===V&&!V.animated||l===a&&!l.animated)&&(mt=null),s.dragoverBubble||n.rootEl||l===document||(V.parentNode[K]._isOutsideThisEl(n.target),t||Yt(n)),!s.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),g=!0}function N(){it=j(V),at=j(V,s.draggable),q({sortable:p,name:"change",toEl:a,newIndex:it,newDraggableIndex:at,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){f(document,"mousemove",this._onTouchMove),f(document,"touchmove",this._onTouchMove),f(document,"pointermove",this._onTouchMove),f(document,"dragover",Yt),f(document,"mousemove",Yt),f(document,"touchmove",Yt)},_offUpEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._onDrop),f(t,"touchend",this._onDrop),f(t,"pointerup",this._onDrop),f(t,"touchcancel",this._onDrop),f(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;it=j(V),at=j(V,n.draggable),G("drop",this,{evt:t}),Z=V&&V.parentNode,it=j(V),at=j(V,n.draggable),Ft.eventCanceled||(St=_t=wt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Wt(this.cloneId),Wt(this._dragStartId),this.nativeDraggable&&(f(document,"drop",this),f(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),u&&R(document.body,"user-select",""),R(V,"transform",""),t&&(gt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),$&&$.parentNode&&$.parentNode.removeChild($),(Q===Z||st&&"clone"!==st.lastPutMode)&&et&&et.parentNode&&et.parentNode.removeChild(et),V&&(this.nativeDraggable&&f(V,"dragend",this),Ht(V),V.style["will-change"]="",gt&&!wt&&k(V,(st||this).options.ghostClass,!1),k(V,this.options.chosenClass,!1),q({sortable:this,name:"unchoose",toEl:Z,newIndex:null,newDraggableIndex:null,originalEvent:t}),Q!==Z?(0<=it&&(q({rootEl:Z,name:"add",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"remove",toEl:Z,originalEvent:t}),q({rootEl:Z,name:"sort",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),st&&st.save()):it!==ot&&0<=it&&(q({sortable:this,name:"update",toEl:Z,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),Ft.active&&(null!=it&&-1!==it||(it=ot,at=rt),q({sortable:this,name:"end",toEl:Z,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){G("nulling",this),Q=V=Z=$=J=et=tt=nt=ct=ut=gt=it=at=ot=rt=mt=vt=st=lt=Ft.dragged=Ft.ghost=Ft.clone=Ft.active=null,xt.forEach(function(t){t.checked=!0}),xt.length=dt=ht=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":V&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||function(t){var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;for(;n--;)o+=e.charCodeAt(n);return o.toString(36)}(t));return e},sort:function(t,e){var n={},o=this.el;this.toArray().forEach(function(t,e){e=o.children[e];P(e,this.options.draggable,o,!1)&&(n[t]=e)},this),e&&this.captureAnimationState(),t.forEach(function(t){n[t]&&(o.removeChild(n[t]),o.appendChild(n[t]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=W.modifyOption(this,t,e);n[t]=void 0!==o?o:e,"group"===t&&kt(n)},destroy:function(){G("destroy",this);var t=this.el;t[K]=null,f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart),f(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Dt.splice(Dt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){nt||(G("hideClone",this),Ft.eventCanceled||(R(et,"display","none"),this.options.removeCloneOnHide&&et.parentNode&&et.parentNode.removeChild(et),nt=!0))},_showClone:function(t){"clone"===t.lastPutMode?nt&&(G("showClone",this),Ft.eventCanceled||(V.parentNode!=Q||this.options.group.revertClone?J?Q.insertBefore(et,J):Q.appendChild(et):Q.insertBefore(et,V),this.options.group.revertClone&&this.animate(V,et),R(et,"display",""),nt=!1)):this._hideClone()}},Ot&&h(document,"touchmove",function(t){(Ft.active||wt)&&t.cancelable&&t.preventDefault()}),Ft.utils={on:h,off:f,css:R,find:b,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:S,closest:P,toggleClass:k,clone:_,index:j,nextTick:Kt,cancelNextTick:Wt,detectDirection:Pt,getChild:B},Ft.get=function(t){return t[K]},Ft.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];(e=e[0].constructor===Array?e[0]:e).forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(t));t.utils&&(Ft.utils=I(I({},Ft.utils),t.utils)),W.mount(t)})},Ft.create=function(t,e){return new Ft(t,e)};var zt,Gt,Ut,qt,Vt,Zt,$t=[],Qt=!(Ft.version="1.15.2");function Jt(){$t.forEach(function(t){clearInterval(t.pid)}),$t=[]}function te(){clearInterval(Zt)}var ee,ne=S(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=O(),u=!1;Gt!==e&&(Gt=e,Jt(),zt=t.scroll,i=t.scrollFn,!0===zt&&(zt=M(e,!0)));var d=0,h=zt;do{var f=h,p=X(f),g=p.top,m=p.bottom,v=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,p=f.scrollTop,D=f===c?(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX||"visible"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY||"visible"===C.overflowY)):(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY)),T=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(v-r)<=l&&!!T),p=D&&(Math.abs(m-a)<=l&&p+w<_)-(Math.abs(g-a)<=l&&!!p);if(!$t[d])for(var x=0;x<=d;x++)$t[x]||($t[x]={});$t[d].vx==T&&$t[d].vy==p&&$t[d].el===f||($t[d].el=f,$t[d].vx=T,$t[d].vy=p,clearInterval($t[d].pid),0==T&&0==p||(u=!0,$t[d].pid=setInterval(function(){o&&0===this.layer&&Ft.active._onTouchMove(Vt);var t=$t[this.layer].vy?$t[this.layer].vy*s:0,e=$t[this.layer].vx?$t[this.layer].vx*s:0;"function"==typeof i&&"continue"!==i.call(Ft.dragged.parentNode[K],e,t,n,Vt,$t[this.layer].el)||H($t[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=M(h,!1)));Qt=u}},30),c=function(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,t=t.unhideGhostForTarget;e&&(i=n||i,a(),e=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,e=document.elementFromPoint(e.clientX,e.clientY),t(),i&&!i.el.contains(e)&&(r("spill"),this.onSpill({dragEl:o,putSortable:n})))};function oe(){}function ie(){}oe.prototype={startIndex:null,dragStart:function(t){t=t.oldDraggableIndex;this.startIndex=t},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();t=B(this.sortable.el,this.startIndex,this.options);t?this.sortable.el.insertBefore(e,t):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:c},a(oe,{pluginName:"revertOnSpill"}),ie.prototype={onSpill:function(t){var e=t.dragEl,t=t.putSortable||this.sortable;t.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),t.animateAll()},drop:c},a(ie,{pluginName:"removeOnSpill"});var re,ae,le,se,ce,ue=[],de=[],he=!1,fe=!1,pe=!1;function ge(n,o){de.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)})}function me(){ue.forEach(function(t){t!==le&&t.parentNode&&t.parentNode.removeChild(t)})}return Ft.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){t=t.originalEvent;this.sortable.nativeDraggable?h(document,"dragover",this._handleAutoScroll):this.options.supportPointer?h(document,"pointermove",this._handleFallbackAutoScroll):t.touches?h(document,"touchmove",this._handleFallbackAutoScroll):h(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){t=t.originalEvent;this.options.dragOverBubble||t.rootEl||this._handleAutoScroll(t)},drop:function(){this.sortable.nativeDraggable?f(document,"dragover",this._handleAutoScroll):(f(document,"pointermove",this._handleFallbackAutoScroll),f(document,"touchmove",this._handleFallbackAutoScroll),f(document,"mousemove",this._handleFallbackAutoScroll)),te(),Jt(),clearTimeout(g),g=void 0},nulling:function(){Vt=Gt=zt=Qt=Zt=Ut=qt=null,$t.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o,i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(r,a);Vt=e,n||this.options.forceAutoScrollFallback||w||y||u?(ne(e,this.options,t,n),o=M(t,!0),!Qt||Zt&&r===Ut&&a===qt||(Zt&&te(),Zt=setInterval(function(){var t=M(document.elementFromPoint(r,a),!0);t!==o&&(o=t,Jt()),ne(e,i.options,t,n)},10),Ut=r,qt=a)):this.options.bubbleScroll&&M(t,!0)!==O()?ne(e,this.options,M(t,!1),!1):Jt()}},a(t,{pluginName:"scroll",initializeByDefault:!0})}),Ft.mount(ie,oe),Ft.mount(new function(){function t(){this.defaults={swapClass:"sortable-swap-highlight"}}return t.prototype={dragStart:function(t){t=t.dragEl;ee=t},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;i.options.swap&&(t=this.sortable.el,i=this.options,n&&n!==t&&(t=ee,ee=!1!==o(n)?(k(n,i.swapClass,!0),n):null,t&&t!==ee&&k(t,i.swapClass,!1)),r(),e(!0),a())},drop:function(t){var e,n,o=t.activeSortable,i=t.putSortable,r=t.dragEl,a=i||this.sortable,l=this.options;ee&&k(ee,l.swapClass,!1),ee&&(l.swap||i&&i.options.swap)&&r!==ee&&(a.captureAnimationState(),a!==o&&o.captureAnimationState(),n=ee,t=(e=r).parentNode,l=n.parentNode,t&&l&&!t.isEqualNode(n)&&!l.isEqualNode(e)&&(i=j(e),r=j(n),t.isEqualNode(l)&&i<r&&r++,t.insertBefore(n,t.children[i]),l.insertBefore(e,l.children[r])),a.animateAll(),a!==o&&o.animateAll())},nulling:function(){ee=null}},a(t,{pluginName:"swap",eventProperties:function(){return{swapItem:ee}}})}),Ft.mount(new function(){function t(o){for(var t in this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.avoidImplicitDeselect||(o.options.supportPointer?h(document,"pointerup",this._deselectMultiDrag):(h(document,"mouseup",this._deselectMultiDrag),h(document,"touchend",this._deselectMultiDrag))),h(document,"keydown",this._checkKeyDown),h(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(t,e){var n="";ue.length&&ae===o?ue.forEach(function(t,e){n+=(e?", ":"")+t.textContent}):n=e.textContent,t.setData("Text",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){t=t.dragEl;le=t},delayEnded:function(){this.isMultiDrag=~ue.indexOf(le)},setupClone:function(t){var e=t.sortable,t=t.cancel;if(this.isMultiDrag){for(var n=0;n<ue.length;n++)de.push(_(ue[n])),de[n].sortableIndex=ue[n].sortableIndex,de[n].draggable=!1,de[n].style["will-change"]="",k(de[n],this.options.selectedClass,!1),ue[n]===le&&k(de[n],this.options.chosenClass,!1);e._hideClone(),t()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,t=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||ue.length&&ae===e&&(ge(!0,n),o("clone"),t()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,t=t.cancel;this.isMultiDrag&&(ge(!1,n),de.forEach(function(t){R(t,"display","")}),e(),ce=!1,t())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),t=t.cancel;this.isMultiDrag&&(de.forEach(function(t){R(t,"display","none"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),ce=!0,t())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ae&&ae.multiDrag._deselectMultiDrag(),ue.forEach(function(t){t.sortableIndex=j(t)}),ue=ue.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),pe=!0},dragStarted:function(t){var e,n=this,t=t.sortable;this.isMultiDrag&&(this.options.sort&&(t.captureAnimationState(),this.options.animation&&(ue.forEach(function(t){t!==le&&R(t,"position","absolute")}),e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&C(t,e)}),he=fe=!0)),t.animateAll(function(){he=fe=!1,n.options.animation&&ue.forEach(function(t){T(t)}),n.options.sort&&me()}))},dragOver:function(t){var e=t.target,n=t.completed,t=t.cancel;fe&&~ue.indexOf(e)&&(n(!1),t())},revert:function(t){var n,o,e=t.fromSortable,i=t.rootEl,r=t.sortable,a=t.dragRect;1<ue.length&&(ue.forEach(function(t){r.addAnimationState({target:t,rect:fe?X(t):a}),T(t),t.fromRect=a,e.removeAnimationState(t)}),fe=!1,n=!this.options.removeCloneOnHide,o=i,ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)}))},dragOverCompleted:function(t){var e,n=t.sortable,o=t.isOwner,i=t.insertion,r=t.activeSortable,a=t.parentEl,l=t.putSortable,t=this.options;i&&(o&&r._hideClone(),he=!1,t.animation&&1<ue.length&&(fe||!o&&!r.options.sort&&!l)&&(e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&(C(t,e),a.appendChild(t))}),fe=!0),o||(fe||me(),1<ue.length?(o=ce,r._showClone(n),r.options.animation&&!ce&&o&&de.forEach(function(t){r.addAnimationState({target:t,rect:se}),t.fromRect=se,t.thisAnimationDuration=null})):r._showClone(n)))},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,t=t.activeSortable;ue.forEach(function(t){t.thisAnimationDuration=null}),t.options.animation&&!n&&t.multiDrag.isMultiDrag&&(se=a({},e),e=v(le,!0),se.top-=e.f,se.left-=e.e)},dragOverAnimationComplete:function(){fe&&(fe=!1,me())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c,u,d,h=this.options,f=o.children;if(!pe)if(h.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(le,h.selectedClass,!~ue.indexOf(le)),~ue.indexOf(le))ue.splice(ue.indexOf(le),1),re=null,z({sortable:i,rootEl:n,name:"deselect",targetEl:le,originalEvent:e});else{if(ue.push(le),z({sortable:i,rootEl:n,name:"select",targetEl:le,originalEvent:e}),e.shiftKey&&re&&i.el.contains(re)){var p=j(re),t=j(le);if(~p&&~t&&p!==t)for(var g,m=p<t?(g=p,t):(g=t,p+1);g<m;g++)~ue.indexOf(f[g])||(k(f[g],h.selectedClass,!0),ue.push(f[g]),z({sortable:i,rootEl:n,name:"select",targetEl:f[g],originalEvent:e}))}else re=le;ae=s}pe&&this.isMultiDrag&&(fe=!1,(o[K].options.sort||o!==n)&&1<ue.length&&(c=X(le),u=j(le,":not(."+this.options.selectedClass+")"),!he&&h.animation&&(le.thisAnimationDuration=null),s.captureAnimationState(),he||(h.animation&&(le.fromRect=c,ue.forEach(function(t){var e;t.thisAnimationDuration=null,t!==le&&(e=fe?X(t):c,t.fromRect=e,s.addAnimationState({target:t,rect:e}))})),me(),ue.forEach(function(t){f[u]?o.insertBefore(t,f[u]):o.appendChild(t),u++}),a===j(le)&&(d=!1,ue.forEach(function(t){t.sortableIndex!==j(t)&&(d=!0)}),d&&(r("update"),r("sort")))),ue.forEach(function(t){T(t)}),s.animateAll()),ae=s),(n===o||l&&"clone"!==l.lastPutMode)&&de.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=pe=!1,de.length=0},destroyGlobal:function(){this._deselectMultiDrag(),f(document,"pointerup",this._deselectMultiDrag),f(document,"mouseup",this._deselectMultiDrag),f(document,"touchend",this._deselectMultiDrag),f(document,"keydown",this._checkKeyDown),f(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==pe&&pe||ae!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;ue.length;){var e=ue[0];k(e,this.options.selectedClass,!1),ue.shift(),z({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:e,originalEvent:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:"multiDrag",utils:{select:function(t){var e=t.parentNode[K];e&&e.options.multiDrag&&!~ue.indexOf(t)&&(ae&&ae!==e&&(ae.multiDrag._deselectMultiDrag(),ae=e),k(t,e.options.selectedClass,!0),ue.push(t))},deselect:function(t){var e=t.parentNode[K],n=ue.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),ue.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return ue.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=fe&&t!==le?-1:fe?j(t,":not(."+n.options.selectedClass+")"):j(t),i.push({multiDragElement:t,index:e})}),{items:r(ue),clones:[].concat(de),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return"ctrl"===(t=t.toLowerCase())?t="Control":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Ft});';import{existsSync as Se,readFileSync as Ot,writeFileSync as Ht,rmSync as ns,mkdirSync as Xa,copyFileSync as Va,readdirSync as Ya}from"fs";import{resolve as oe}from"path";import{existsSync as q,readFileSync as He,writeFileSync as Ne,mkdirSync as be,readdirSync as Oe,rmSync as ja,statSync as F}from"fs";import{resolve as At,relative as za,dirname as Be,join as Q,extname as pe,basename as je}from"path";var O=200*1024,$t=new Set([".md",".markdown",".txt"]),ne=new Set([".md",".markdown",".txt",".json",".jsonc",".yaml",".yml",".toml",".sh",".bash",".zsh",".py",".js",".mjs",".ts",".tsx",".jsx",".css",".html",".lock",".cfg",".ini",".env",".gitignore"]),qa=6,Fa=new Set(["CLAUDE.md","SOUL.md","IDENTITY.md","PERSONA.md","AGENT.md","BOOTSTRAP.md"]);function K(t,e){if(!t)throw new Error("Agent workspace is not configured.");let a=At(t);if(q(a)||be(a,{recursive:!0}),!e||e.startsWith("/")||e.match(/^[a-zA-Z]:/))throw new Error(`Path must be relative to the workspace: ${e}`);let n=At(a,e);if(za(a,n).startsWith(".."))throw new Error(`Path escapes the workspace: ${e}`);return n}function Mt(t){let e=pe(t).toLowerCase();if(!$t.has(e))throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${e}).`)}function Pe(t,e){let a=t.split(/\r?\n/).find(n=>n.trim().startsWith("#"));return a&&a.replace(/^#+\s*/,"").trim().slice(0,120)||e}function re(t){let e=K(t,"."),a=[],n=new Set,r=[];try{r=Oe(e)}catch{}for(let i of Fa){let l=Q(e,i);if(q(l)&&F(l).isFile()){let d=F(l).size,c=d>0&&d<O?Re(l):"";a.push({path:i,title:Pe(c,i),size:d,exists:!0}),n.add(i)}else a.push({path:i,title:i,size:0,exists:!1}),n.add(i)}for(let i of r){if(n.has(i))continue;let l=Q(e,i);try{let d=F(l);if(!d.isFile()||!$t.has(pe(i).toLowerCase()))continue;let c=d.size>0&&d.size<O?Re(l):"";a.push({path:i,title:Pe(c,i),size:d.size,exists:!0})}catch{}}let o=[],s=Q(e,".claude","skills");if(q(s))try{for(let i of Oe(s)){let l=Q(s,i),d;try{d=F(l)}catch{continue}if(!d.isDirectory())continue;let c=Q(l,"SKILL.md");if(!q(c))continue;let u=F(c),h=u.size<O?Re(c):"";o.push({slug:i,title:Pe(h,i),size:u.size,path:`.claude/skills/${i}/SKILL.md`})}}catch{}return o.sort((i,l)=>i.slug.localeCompare(l.slug)),{identity:a,skills:o,workspace:e}}function Re(t){try{return He(t,"utf-8")}catch{return""}}function xe(t,e){let a=K(t,e);if(Mt(a),!q(a))return{path:e,content:""};if(F(a).size>O)throw new Error(`File is too large to edit in the admin panel (> ${O/1024} KB).`);return{path:e,content:He(a,"utf-8")}}function ye(t,e,a){let n=K(t,e);Mt(n);let r=Buffer.from(a,"utf-8");if(r.length>O)throw new Error(`File too large (${r.length} > ${O} bytes).`);return be(Be(n),{recursive:!0}),Ne(n,r),{path:e,bytes:r.length}}function we(t,e,a={}){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.");let n=`.claude/skills/${e}/SKILL.md`,r=K(t,n);if(q(r))throw new Error(`Skill "${e}" already exists.`);be(Be(r),{recursive:!0});let o=a.content?.trim()?a.content:Ga(e,a.title);return Ne(r,o),{slug:e,path:n}}function ke(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=K(t,`.claude/skills/${e}`);if(!q(a))throw new Error(`Skill "${e}" not found.`);if(!F(a).isDirectory())throw new Error(`Skill path is not a directory: ${e}`);return ja(a,{recursive:!0,force:!0}),{slug:e}}function It(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=K(t,`.claude/skills/${e}`);if(!q(a))throw new Error(`Skill "${e}" not found.`);let n=[];return r(a,"",0,n),n.sort((o,s)=>o.kind!==s.kind?o.kind==="dir"?-1:1:o.path.localeCompare(s.path)),n;function r(o,s,i,l){if(i>qa)return;let d=[];try{d=Oe(o)}catch{return}for(let c of d){if(c==="node_modules"||c===".git")continue;let u=Q(o,c),h=s?`${s}/${c}`:c,x;try{x=F(u)}catch{continue}if(x.isDirectory())l.push({path:`${h}/`,kind:"dir",size:0,editable:!1}),r(u,h,i+1,l);else if(x.isFile()){let p=pe(c).toLowerCase(),m=ne.has(p)||ne.has(c.toLowerCase());l.push({path:h,kind:"file",size:x.size,editable:m&&x.size<O})}}}}function Lt(t,e,a){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!a||a.includes(".."))throw new Error(`Invalid file path: ${a}`);let n=K(t,`.claude/skills/${e}/${a}`);if(!q(n)||!F(n).isFile())throw new Error(`File not found: ${a}`);let r=pe(n).toLowerCase(),o=je(n).toLowerCase();if(!ne.has(r)&&!ne.has(o))throw new Error(`File type not editable: ${a}`);if(F(n).size>O)throw new Error(`File too large to edit (> ${O/1024} KB): ${a}`);return{path:a,content:He(n,"utf-8")}}function Dt(t,e,a,n){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!a||a.includes(".."))throw new Error(`Invalid file path: ${a}`);let r=K(t,`.claude/skills/${e}/${a}`),o=pe(r).toLowerCase(),s=je(r).toLowerCase();if(!ne.has(o)&&!ne.has(s))throw new Error(`File type not editable: ${a}`);let i=Buffer.from(n,"utf-8");if(i.length>O)throw new Error(`File too large (${i.length} > ${O} bytes).`);return be(Be(r),{recursive:!0}),Ne(r,i),{path:a,bytes:i.length}}function _e(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let a=K(t,`.claude/skills/${e}`);if(!q(a))throw new Error(`Skill "${e}" not found.`);let n=r=>q(Q(a,r));return n("pnpm-lock.yaml")?{manager:"pnpm",file:"pnpm-lock.yaml",command:"pnpm install"}:n("yarn.lock")?{manager:"yarn",file:"yarn.lock",command:"yarn install"}:n("package.json")?{manager:"npm",file:"package.json",command:"npm install"}:n("requirements.txt")?{manager:"pip",file:"requirements.txt",command:"pip install -r requirements.txt"}:n("Gemfile")?{manager:"bundle",file:"Gemfile",command:"bundle install"}:{manager:null,file:null,command:null}}function Ga(t,e){return`# ${e?.trim()||t}
2
2
 
3
3
  Describe what this skill does and when the agent should use it. Keep it under ~5 lines \u2014 the agent reads this to decide whether to invoke the skill.
4
4
 
@@ -11,12 +11,12 @@ Document the arguments, commands, or files the skill expects. Example:
11
11
 
12
12
  \`\`\`bash
13
13
  # Example invocation
14
- claude-code ${ge(t)} --arg value
14
+ claude-code ${je(t)} --arg value
15
15
  \`\`\`
16
- `}var q={agents:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4M8 16h.01M16 16h.01"/></svg>',channels:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>',schedules:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>',webhooks:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 12a3 3 0 1 0 3-3m-8 11a3 3 0 1 0 2.6-1.5L10 12m7 3a3 3 0 1 0-2-5.5"/></svg>',mesh:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="6" cy="18" r="2"/><circle cx="18" cy="18" r="2"/><circle cx="12" cy="12" r="2"/><path d="M7.4 7.4l3.2 3.2M13.4 13.4l3.2 3.2M16.6 7.4l-3.2 3.2M10.6 13.4l-3.2 3.2"/></svg>',tokens:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-8 8a4 4 0 1 1-6 6m6-6l5-5 3 3-5 5m-3-3l3 3"/></svg>',advanced:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 4h8l4 4v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M9 13l2 2 4-4"/></svg>'};function et(t={}){let e=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",a=Be({kicker:"Settings",title:"Your team's control room",lead:`Manage the agents, the places they answer from, and who's allowed to reach them. Everything here writes to <span class="mono muted">agentx.json</span> on your machine \u2014 nothing leaves.`})+je([{kind:"off",num:"\u2014",label:"Agents online"},{kind:"off",num:"\u2014",label:"Channels connected"},{kind:"off",num:"0",label:"Needs attention"},{kind:"off",num:"\u2014",label:"Active tokens"}]).replace('<div class="ax-health-strip">','<div class="ax-health-strip" id="ax-health">');return E({title:"AgentX \xB7 Settings",activeTab:"admin",subtitle:"Settings",peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:a+Ot+ze,css:Rt,scripts:`${e}<script>${qe}
17
- ${Ge}
18
- ${Ht}
19
- ${Nt}</script>`})}var Ht=`
16
+ `}var G={agents:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="10" rx="2"/><circle cx="12" cy="5" r="2"/><path d="M12 7v4M8 16h.01M16 16h.01"/></svg>',channels:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>',schedules:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>',webhooks:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 12a3 3 0 1 0 3-3m-8 11a3 3 0 1 0 2.6-1.5L10 12m7 3a3 3 0 1 0-2-5.5"/></svg>',mesh:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="6" cy="18" r="2"/><circle cx="18" cy="18" r="2"/><circle cx="12" cy="12" r="2"/><path d="M7.4 7.4l3.2 3.2M13.4 13.4l3.2 3.2M16.6 7.4l-3.2 3.2M10.6 13.4l-3.2 3.2"/></svg>',tokens:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-8 8a4 4 0 1 1-6 6m6-6l5-5 3 3-5 5m-3-3l3 3"/></svg>',advanced:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 4h8l4 4v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M9 13l2 2 4-4"/></svg>'};function Pt(t={}){let e=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",a=ft({kicker:"Settings",title:"Your team's control room",lead:`Manage the agents, the places they answer from, and who's allowed to reach them. Everything here writes to <span class="mono muted">agentx.json</span> on your machine \u2014 nothing leaves.`})+vt([{kind:"off",num:"\u2014",label:"Agents online"},{kind:"off",num:"\u2014",label:"Channels connected"},{kind:"off",num:"0",label:"Needs attention"},{kind:"off",num:"\u2014",label:"Active tokens"}]).replace('<div class="ax-health-strip">','<div class="ax-health-strip" id="ax-health">');return A({title:"AgentX \xB7 Settings",activeTab:"admin",subtitle:"Settings",peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:a+Ua+bt,css:Ka,scripts:`${e}<script>${xt}
17
+ ${yt}
18
+ ${Wa}
19
+ ${Ja}</script>`})}var Wa=`
20
20
  (function(){
21
21
  function fmt(n) { return n == null ? '\u2014' : String(n); }
22
22
  async function refresh() {
@@ -77,7 +77,7 @@ ${Nt}</script>`})}var Ht=`
77
77
  refresh();
78
78
  // Refresh again after the main admin script has had a chance to mutate config.
79
79
  document.addEventListener('ax-config-saved', refresh);
80
- })();`,Ot=`
80
+ })();`,Ua=`
81
81
  <div id="peer-banner" class="peer-banner">
82
82
  <span class="label">Managing</span>
83
83
  <span class="name" id="peer-banner-name">\u2014</span>
@@ -91,6 +91,9 @@ ${Nt}</script>`})}var Ht=`
91
91
  <button data-tab="crons">Schedules</button>
92
92
  <button data-tab="webhooks">Webhooks</button>
93
93
  <button data-tab="mesh">Mesh</button>
94
+ <button data-tab="team">Team</button>
95
+ <button data-tab="business">Business</button>
96
+ <button data-tab="boards-cfg">Boards</button>
94
97
  <button data-tab="tokens">Tokens</button>
95
98
  <button data-tab="advanced">Advanced</button>
96
99
  </nav>
@@ -218,8 +221,8 @@ ${Nt}</script>`})}var Ht=`
218
221
  <div id="global-msg" class="msg"></div>
219
222
 
220
223
  <section id="tab-agents" class="tab active">
221
- ${N({icon:q.agents,title:"Your agents",lead:"Each agent is a role with its own trigger words and workspace. Delete removes the config only \u2014 the folder on disk stays."})}
222
- ${R({persistKey:"agents",bodyHtml:`<b>What's an agent, exactly?</b> Think of it as a specialised teammate. A sales agent handles pricing questions, a support agent answers tickets, a reports agent sends a weekly summary. You decide what they do and what to say to wake them up (their "trigger words", like <code>@support</code>).`})}
224
+ ${z({icon:G.agents,title:"Your agents",lead:"Each agent is a role with its own trigger words and workspace. Delete removes the config only \u2014 the folder on disk stays."})}
225
+ ${j({persistKey:"agents",bodyHtml:`<b>What's an agent, exactly?</b> Think of it as a specialised teammate. A sales agent handles pricing questions, a support agent answers tickets, a reports agent sends a weekly summary. You decide what they do and what to say to wake them up (their "trigger words", like <code>@support</code>).`})}
223
226
  <div id="agent-list" class="ax-stack"></div>
224
227
  <div class="add-form">
225
228
  <h3>Add a new agent</h3>
@@ -241,13 +244,40 @@ ${Nt}</script>`})}var Ht=`
241
244
  </section>
242
245
 
243
246
  <section id="tab-channels" class="tab">
244
- ${N({icon:q.channels,title:"Where your agents show up",lead:"Connect a chat app, and your agents can answer from there. Set up once \u2014 they'll keep working in the background."})}
245
- ${R({persistKey:"channels",bodyHtml:`<b>How does this work?</b> We give each channel a small "bridge" that forwards messages to AgentX. For chat apps (Telegram, Slack, Discord) you'll paste a bot token \u2014 we keep the value in your <code>.env</code> file and never send it over the network. For webhook-style sources (GitHub, GitLab) we hand you a URL to paste into their dashboard.`})}
247
+ ${z({icon:G.channels,title:"Where your agents show up",lead:"Connect a chat app, and your agents can answer from there. Set up once \u2014 they'll keep working in the background."})}
248
+ ${j({persistKey:"channels",bodyHtml:`<b>How does this work?</b> We give each channel a small "bridge" that forwards messages to AgentX. For chat apps (Telegram, Slack, Discord) you'll paste a bot token \u2014 we keep the value in your <code>.env</code> file and never send it over the network. For webhook-style sources (GitHub, GitLab) we hand you a URL to paste into their dashboard.`})}
249
+
250
+ <details class="add-form" style="margin-top:14px;margin-bottom:18px" id="notif-section">
251
+ <summary class="primary">\u2699 Notifications routing</summary>
252
+ <div style="margin-top:10px">
253
+ <p style="font-size:11px;color:var(--ax-muted);margin:0 0 10px">Where AgentX pings you when a task finishes, errors, or runs long. Mirrors <code>agentx notifications</code>.</p>
254
+ <div id="notif-current" style="font-size:12px;margin-bottom:10px;padding:8px 10px;background:var(--ax-surface);border-radius:4px;color:var(--ax-muted)">\u2014</div>
255
+ <label>Channel<span class="hint">(telegram | whatsapp | slack | discord \u2014 leave blank to clear)</span></label>
256
+ <input id="notif-channel" placeholder="telegram" />
257
+ <label>Chat id</label>
258
+ <input id="notif-chat-id" placeholder="-1001234567890" />
259
+ <label>Account id <span class="hint">(optional \u2014 when the channel is multi-account, e.g. multiple Telegram bots)</span></label>
260
+ <input id="notif-account-id" placeholder="default" />
261
+ <label>Long-task threshold (seconds) <span class="hint">(0 disables long-task pings)</span></label>
262
+ <input id="notif-threshold" type="number" min="0" max="3600" />
263
+ <fieldset style="margin-top:8px;border:1px solid var(--ax-border);border-radius:4px;padding:8px 10px">
264
+ <legend style="font-size:11px;color:var(--ax-muted);padding:0 4px">Events to ping on</legend>
265
+ <label class="ax-inline" style="display:inline-flex;gap:6px;font-size:12px;margin-right:14px"><input type="checkbox" id="notif-on-complete" /> task complete</label>
266
+ <label class="ax-inline" style="display:inline-flex;gap:6px;font-size:12px;margin-right:14px"><input type="checkbox" id="notif-on-error" /> task error</label>
267
+ <label class="ax-inline" style="display:inline-flex;gap:6px;font-size:12px"><input type="checkbox" id="notif-on-queued" /> task queued</label>
268
+ </fieldset>
269
+ <div class="actions" style="margin-top:10px">
270
+ <button class="primary" onclick="saveNotifications()">Save notifications</button>
271
+ <button class="ghost" onclick="clearNotificationsDestination()">Clear destination</button>
272
+ <div id="notif-msg" class="msg"></div>
273
+ </div>
274
+ </div>
275
+ </details>
246
276
 
247
- <div id="ch-chatapps-label">${V({label:"Chat apps"})}</div>
277
+ <div id="ch-chatapps-label">${ve({label:"Chat apps"})}</div>
248
278
  <div class="ax-connectors" id="ch-chatapps"></div>
249
279
 
250
- <div id="ch-devtools-label">${V({label:"Developer tools",rightHtml:'<span class="ax-pill">Send events from GitHub, GitLab, Sentry, etc.</span>'})}</div>
280
+ <div id="ch-devtools-label">${ve({label:"Developer tools",rightHtml:'<span class="ax-pill">Send events from GitHub, GitLab, Sentry, etc.</span>'})}</div>
251
281
  <div class="ax-connectors" id="ch-devtools"></div>
252
282
 
253
283
  <!-- Active-channel configuration panes. One shows at a time; clicking
@@ -259,13 +289,14 @@ ${Nt}</script>`})}var Ht=`
259
289
  <div id="ch-slack" hidden><div id="slack-section" class="section-block"></div></div>
260
290
  <div id="ch-discord" hidden></div>
261
291
  <div id="ch-gitlab" hidden></div>
292
+ <div id="ch-webrtc" hidden></div>
262
293
  <div id="ch-github" hidden></div>
263
294
  </div>
264
295
  </section>
265
296
 
266
297
  <section id="tab-crons" class="tab">
267
- ${N({icon:q.schedules,title:"Schedules",lead:"Make an agent do something on a timer \u2014 no one has to ask. Daily summaries, weekly reports, hourly health checks."})}
268
- ${R({persistKey:"crons",bodyHtml:"<b>What's a good schedule?</b> Anything recurring. A <i>Reports agent</i> sending Monday-morning numbers, a <i>Health agent</i> pinging you at 3am if a server's down, a <i>Standup agent</i> reminding the team. Pick a time below."})}
298
+ ${z({icon:G.schedules,title:"Schedules",lead:"Make an agent do something on a timer \u2014 no one has to ask. Daily summaries, weekly reports, hourly health checks."})}
299
+ ${j({persistKey:"crons",bodyHtml:"<b>What's a good schedule?</b> Anything recurring. A <i>Reports agent</i> sending Monday-morning numbers, a <i>Health agent</i> pinging you at 3am if a server's down, a <i>Standup agent</i> reminding the team. Pick a time below."})}
269
300
  <div id="cron-list" class="ax-stack" style="margin-bottom:22px"></div>
270
301
 
271
302
  <div class="ax-builder">
@@ -362,8 +393,8 @@ ${Nt}</script>`})}var Ht=`
362
393
  </section>
363
394
 
364
395
  <section id="tab-webhooks" class="tab">
365
- ${N({icon:q.webhooks,title:"Webhooks",lead:"A URL you give to another service. When something happens there \u2014 a PR is merged, an error fires \u2014 they POST to that URL, and we forward it to your agent as a readable message."})}
366
- ${R({persistKey:"webhooks",bodyHtml:`<b>Not sure you need this?</b> If a service you use has a "webhooks" setting (GitHub, Stripe, Sentry, Typeform\u2026), you probably do. It's how those services tell AgentX when something's worth looking at.`})}
396
+ ${z({icon:G.webhooks,title:"Webhooks",lead:"A URL you give to another service. When something happens there \u2014 a PR is merged, an error fires \u2014 they POST to that URL, and we forward it to your agent as a readable message."})}
397
+ ${j({persistKey:"webhooks",bodyHtml:`<b>Not sure you need this?</b> If a service you use has a "webhooks" setting (GitHub, Stripe, Sentry, Typeform\u2026), you probably do. It's how those services tell AgentX when something's worth looking at.`})}
367
398
  <div id="webhook-list" class="ax-stack"></div>
368
399
  <div class="add-form">
369
400
  <h3>Add a webhook</h3>
@@ -382,10 +413,10 @@ ${Nt}</script>`})}var Ht=`
382
413
  </section>
383
414
 
384
415
  <section id="tab-mesh" class="tab">
385
- ${N({icon:q.mesh,title:"Mesh",lead:"Link this AgentX to other machines so they share work. Good for teams running AgentX on multiple laptops or a mix of laptop + server."})}
416
+ ${z({icon:G.mesh,title:"Mesh",lead:"Link this AgentX to other machines so they share work. Good for teams running AgentX on multiple laptops or a mix of laptop + server."})}
386
417
  <div id="mesh-hero" class="ax-mesh-hero"></div>
387
- ${R({persistKey:"mesh",bodyHtml:"<b>Do I need mesh?</b> Only if you're running AgentX in more than one place. Solo users on one laptop can skip this entirely."})}
388
- ${V({label:"Connected peers"})}
418
+ ${j({persistKey:"mesh",bodyHtml:"<b>Do I need mesh?</b> Only if you're running AgentX in more than one place. Solo users on one laptop can skip this entirely."})}
419
+ ${ve({label:"Connected peers"})}
389
420
  <div id="mesh-peers" class="ax-stack"></div>
390
421
  <div class="add-form">
391
422
  <h3>Add a peer</h3>
@@ -397,11 +428,161 @@ ${Nt}</script>`})}var Ht=`
397
428
  <input id="m-token" type="password" />
398
429
  <div class="actions"><button class="primary" onclick="addMeshPeer()">Add peer</button><div id="m-msg" class="msg"></div></div>
399
430
  </div>
431
+
432
+ <details class="add-form" style="margin-top:16px">
433
+ <summary class="primary">\u23F1 Health-check cadence</summary>
434
+ <div style="margin-top:10px">
435
+ <p style="font-size:11px;color:var(--ax-muted);margin:0 0 10px">How often AgentX pings each peer to check if it's alive. Mirrors <code>agentx mesh health</code>. Lower interval = faster peer-down detection (more wake noise on flaky links); higher = quieter on battery-constrained mobile peers.</p>
436
+ <div class="rowf">
437
+ <div><label>Interval (seconds, 5..3600)<span class="hint">(default 60)</span></label><input id="mh-interval" type="number" min="5" max="3600" /></div>
438
+ <div><label>Timeout (seconds, 1..60)<span class="hint">(default 10)</span></label><input id="mh-timeout" type="number" min="1" max="60" /></div>
439
+ </div>
440
+ <div class="actions"><button class="primary" onclick="saveMeshHealth()">Save</button><div id="mh-msg" class="msg"></div></div>
441
+ </div>
442
+ </details>
443
+ </section>
444
+
445
+ <section id="tab-team" class="tab">
446
+ ${z({icon:G.tokens,title:"Actors & Roles",lead:"Humans (Actors) and groups of humans (Roles) that workflow user-tasks can be assigned to. Mirrors <code>agentx actor</code> and <code>agentx role</code>."})}
447
+ ${j({persistKey:"team",bodyHtml:"<b>Actors</b> are people. They carry one or more channel handles (Telegram, WhatsApp, Slack, Discord, email) so user-tasks land in a place they actually read. <b>Roles</b> are named groups \u2014 assigning a userTask to a role lets the engine pick a member by strategy (first-available, round-robin, all). Assignees in workflow YAML are referenced as <code>actor:&lt;id&gt;</code> or <code>role:&lt;id&gt;</code>."})}
448
+
449
+ <div class="ax-stack" style="margin-top:14px">
450
+ <h3 style="margin:0 0 6px;font-size:13px">Actors</h3>
451
+ <div id="actors-list"></div>
452
+ <details class="add-form" style="margin-top:10px">
453
+ <summary class="primary">+ Add actor</summary>
454
+ <div style="margin-top:10px">
455
+ <label>Actor id<span class="hint">(must start with <code>actor:</code>, e.g. <code>actor:anis</code>)</span></label>
456
+ <input id="ac-id" placeholder="actor:anis" />
457
+ <label>Display name</label>
458
+ <input id="ac-name" placeholder="Anis Marrouchi" />
459
+ <label>Email <span class="hint">(optional)</span></label>
460
+ <input id="ac-email" placeholder="anis@noqta.tn" />
461
+ <label>Channel handles<span class="hint">(at least one \u2014 channel:handle, comma-separated, mark preferred with *)</span></label>
462
+ <input id="ac-channels" placeholder="telegram:marrouchi*, whatsapp:21612345678" />
463
+ <label>Timezone <span class="hint">(optional, e.g. <code>Africa/Tunis</code>)</span></label>
464
+ <input id="ac-timezone" placeholder="Africa/Tunis" />
465
+ <div class="actions"><button class="primary" onclick="upsertActor()">Save actor</button><div id="ac-msg" class="msg"></div></div>
466
+ </div>
467
+ </details>
468
+ </div>
469
+
470
+ <div class="ax-stack" style="margin-top:24px">
471
+ <h3 style="margin:0 0 6px;font-size:13px">Roles</h3>
472
+ <div id="roles-list"></div>
473
+ <details class="add-form" style="margin-top:10px">
474
+ <summary class="primary">+ Add role</summary>
475
+ <div style="margin-top:10px">
476
+ <label>Role id<span class="hint">(must start with <code>role:</code>, e.g. <code>role:on-call</code>)</span></label>
477
+ <input id="rl-id" placeholder="role:on-call" />
478
+ <label>Display name</label>
479
+ <input id="rl-name" placeholder="On-call engineers" />
480
+ <label>Assignment strategy</label>
481
+ <select id="rl-strategy">
482
+ <option value="first-available">first-available \u2014 first listed member sees the task</option>
483
+ <option value="round-robin">round-robin \u2014 rotate across members</option>
484
+ <option value="all">all \u2014 every member sees the task</option>
485
+ </select>
486
+ <div class="actions"><button class="primary" onclick="upsertRole()">Save role</button><div id="rl-msg" class="msg"></div></div>
487
+ </div>
488
+ </details>
489
+ </div>
490
+ </section>
491
+
492
+ <section id="tab-business" class="tab">
493
+ ${z({icon:G.tokens,title:"Business layer",lead:"Org chart, projects, and contact map \u2014 the data that drives PM gating and activity-graph attribution. Mirrors <code>agentx business</code>."})}
494
+ ${j({persistKey:"business",bodyHtml:'<b>Three concepts.</b> The <b>org chart</b> is who reports to whom (<code>reportsTo</code>). <b>Projects</b> map an id to a PM and client (drives the PM gate and lets the activity graph attribute work to the right client). The <b>contact map</b> tells the activity graph which client a Telegram/WhatsApp chat belongs to \u2014 without it, free-text channels fall into the catch-all "internal" bucket. Editing here writes to <code>agentx.json</code>; restart the daemon (or POST /reload) for the change to take effect.'})}
495
+
496
+ <div class="ax-stack" style="margin-top:14px">
497
+ <h3 style="margin:0 0 6px;font-size:13px">Org chart</h3>
498
+ <div id="business-org-list"></div>
499
+ <details class="add-form" style="margin-top:10px">
500
+ <summary class="primary">+ Add or update org-chart entry</summary>
501
+ <div style="margin-top:10px">
502
+ <label>Agent id<span class="hint">(must match a configured agent \u2014 e.g. <code>devops-agent</code>)</span></label>
503
+ <input id="bo-agentId" placeholder="devops-agent" />
504
+ <label>Role title</label>
505
+ <input id="bo-role" placeholder="DevOps" />
506
+ <label>Reports to <span class="hint">(another agent id; leave blank for top-level)</span></label>
507
+ <input id="bo-reportsTo" placeholder="coo-agent" />
508
+ <label>Schedule <span class="hint">(start\u2013end \xB7 days, e.g. <code>09:00 17:00 mon,tue,wed,thu,fri</code>)</span></label>
509
+ <div style="display:flex;gap:6px"><input id="bo-start" placeholder="09:00" style="width:80px" /><input id="bo-end" placeholder="17:00" style="width:80px" /><input id="bo-days" placeholder="mon,tue,wed,thu,fri" /></div>
510
+ <div class="actions"><button class="primary" onclick="upsertOrgEntry()">Save entry</button><div id="bo-msg" class="msg"></div></div>
511
+ </div>
512
+ </details>
513
+ </div>
514
+
515
+ <div class="ax-stack" style="margin-top:24px">
516
+ <h3 style="margin:0 0 6px;font-size:13px">Projects</h3>
517
+ <div id="business-project-list"></div>
518
+ <details class="add-form" style="margin-top:10px">
519
+ <summary class="primary">+ Add or update project</summary>
520
+ <div style="margin-top:10px">
521
+ <label>Project id<span class="hint">(<code>owner/repo</code> for GitLab/GitHub; stable string for internal projects)</span></label>
522
+ <input id="bp-id" placeholder="mtgl/system" />
523
+ <label>PM <span class="hint">(agentId \u2014 drives the PM gate)</span></label>
524
+ <input id="bp-pm" placeholder="pm-mtgl" />
525
+ <label>Client <span class="hint">(used by the activity graph to attribute traffic)</span></label>
526
+ <input id="bp-client" placeholder="mtgl" />
527
+ <div class="actions"><button class="primary" onclick="upsertProject()">Save project</button><div id="bp-msg" class="msg"></div></div>
528
+ </div>
529
+ </details>
530
+ </div>
531
+
532
+ <div class="ax-stack" style="margin-top:24px">
533
+ <h3 style="margin:0 0 6px;font-size:13px">Contact map</h3>
534
+ <div id="business-contact-list"></div>
535
+ <details class="add-form" style="margin-top:10px">
536
+ <summary class="primary">+ Add contact mapping</summary>
537
+ <div style="margin-top:10px">
538
+ <label>Channel <span class="hint">(telegram | whatsapp | slack | discord \u2014 optional)</span></label>
539
+ <input id="bc-channel" placeholder="telegram" />
540
+ <label>Chat id <span class="hint">(e.g. <code>-100\u2026</code>, JID \u2014 pick at least one of these three)</span></label>
541
+ <input id="bc-chatId" placeholder="-1003861455814" />
542
+ <label>Username</label>
543
+ <input id="bc-username" placeholder="anis" />
544
+ <label>Sender id</label>
545
+ <input id="bc-senderId" placeholder="8500203323" />
546
+ <label>Client <span class="hint">(required)</span></label>
547
+ <input id="bc-client" placeholder="noqta" />
548
+ <label>Project <span class="hint">(optional \u2014 defaults to <code>&lt;client&gt;/_chat</code>)</span></label>
549
+ <input id="bc-project" placeholder="noqta/internal" />
550
+ <label>Display name <span class="hint">(initiator pill override)</span></label>
551
+ <input id="bc-displayName" placeholder="Anis Marrouchi" />
552
+ <div class="actions"><button class="primary" onclick="upsertContact()">Save mapping</button><div id="bc-msg" class="msg"></div></div>
553
+ </div>
554
+ </details>
555
+ </div>
556
+ </section>
557
+
558
+ <section id="tab-boards-cfg" class="tab">
559
+ ${z({icon:G.tokens,title:"Kanban boards",lead:"Configure the boards rendered on the home page. Mirrors <code>agentx board add/edit/remove</code> + <code>agentx board column</code>. Source is GitLab today; backlog/wiki sources land when the schema unlocks them."})}
560
+ ${j({persistKey:"boards-cfg",bodyHtml:'<b>Two layers.</b> A board points to one or more GitLab projects (<code>source.projects</code>) and optionally filters by a primary tool label. Inside each board, <b>columns</b> map drag-drop actions to scoped-labels (e.g. dropping a card on "Doing" adds <code>Status::Doing</code>). The default flow is Open \u2192 To Do \u2192 Doing \u2192 On Hold \u2192 Review \u2192 Closed; override per-board with the column controls below.'})}
561
+
562
+ <div class="ax-stack" style="margin-top:14px">
563
+ <h3 style="margin:0 0 6px;font-size:13px">Boards</h3>
564
+ <div id="boards-cfg-list"></div>
565
+ <details class="add-form" style="margin-top:10px">
566
+ <summary class="primary">+ Add or update board</summary>
567
+ <div style="margin-top:10px">
568
+ <label>Board id<span class="hint">(unique slug, e.g. <code>mtgl</code>)</span></label>
569
+ <input id="bd-id" placeholder="mtgl" />
570
+ <label>Display name</label>
571
+ <input id="bd-name" placeholder="MTGL System" />
572
+ <label>GitLab project paths<span class="hint">(comma-separated, e.g. <code>mtgl/system,mtgl/website</code>)</span></label>
573
+ <input id="bd-projects" placeholder="mtgl/system" />
574
+ <label>Primary tool label <span class="hint">(optional \u2014 ANDed into every query, e.g. <code>Tool::Claude</code>)</span></label>
575
+ <input id="bd-label" placeholder="Tool::Claude" />
576
+ <div style="display:flex;gap:6px"><label style="flex:1">Open-window days<input id="bd-days" type="number" min="1" max="365" value="30" /></label><label style="flex:1">Closed-window days<input id="bd-closed-days" type="number" min="1" max="365" value="30" /></label></div>
577
+ <div class="actions"><button class="primary" onclick="upsertBoardCfg()">Save board</button><div id="bd-msg" class="msg"></div></div>
578
+ </div>
579
+ </details>
580
+ </div>
400
581
  </section>
401
582
 
402
583
  <section id="tab-tokens" class="tab">
403
- ${N({icon:q.tokens,title:"Access tokens",lead:"Passwords, but for software. Anything that wants to talk to AgentX from outside (a Slack bridge, another AgentX machine, a script) uses one."})}
404
- ${R({persistKey:"tokens",bodyHtml:"<b>Tokens get scopes.</b> A scope is what the token is allowed to do. Give the narrowest one that works \u2014 a Slack bridge only needs to send messages, it doesn't need to edit settings. You can't recover a token after you close this page, so copy it immediately."})}
584
+ ${z({icon:G.tokens,title:"Access tokens",lead:"Passwords, but for software. Anything that wants to talk to AgentX from outside (a Slack bridge, another AgentX machine, a script) uses one."})}
585
+ ${j({persistKey:"tokens",bodyHtml:"<b>Tokens get scopes.</b> A scope is what the token is allowed to do. Give the narrowest one that works \u2014 a Slack bridge only needs to send messages, it doesn't need to edit settings. You can't recover a token after you close this page, so copy it immediately."})}
405
586
  <div id="token-list" class="ax-stack"></div>
406
587
  <div class="add-form">
407
588
  <h3>Mint a new token</h3>
@@ -420,8 +601,8 @@ ${Nt}</script>`})}var Ht=`
420
601
  </section>
421
602
 
422
603
  <section id="tab-advanced" class="tab">
423
- ${N({icon:q.advanced,title:"Raw configuration",lead:"Everything on the other tabs writes to this file. Edit here for bulk changes, diffs, or fields we haven't put in the UI yet. Saving creates a timestamped backup."})}
424
- ${R({persistKey:"advanced",bodyHtml:`<b>Safe to look, careful editing.</b> We've pretty-printed and annotated each section so you can read it. Click any <span class="mono" style="color:var(--ax-muted)">\u25B8</span> to fold. Switch to <b>Edit</b> to change values \u2014 invalid JSON is refused before saving.`})}
604
+ ${z({icon:G.advanced,title:"Raw configuration",lead:"Everything on the other tabs writes to this file. Edit here for bulk changes, diffs, or fields we haven't put in the UI yet. Saving creates a timestamped backup."})}
605
+ ${j({persistKey:"advanced",bodyHtml:`<b>Safe to look, careful editing.</b> We've pretty-printed and annotated each section so you can read it. Click any <span class="mono" style="color:var(--ax-muted)">\u25B8</span> to fold. Switch to <b>Edit</b> to change values \u2014 invalid JSON is refused before saving.`})}
425
606
 
426
607
  <div class="ax-jv-toolbar">
427
608
  <input class="ax-jv-search" id="jv-search" placeholder="Search keys & values\u2026" />
@@ -447,7 +628,7 @@ ${Nt}</script>`})}var Ht=`
447
628
  <div class="hint-block">The schema is documented at <code>docs/reference/config-schema.md</code>. Invalid JSON is refused; schema-level errors only surface after the daemon tries to use the new config.</div>
448
629
  </section>
449
630
  </main>
450
- `,Rt=`
631
+ `,Ka=`
451
632
 
452
633
  /* --- Inner tabs (Agents / Channels / Schedules / \u2026) --- */
453
634
  nav.tabs{display:flex;gap:4px;padding:14px 24px 0;background:var(--ax-bg-elev);
@@ -655,7 +836,7 @@ textarea.raw{min-height:480px;font-family:var(--ax-mono);font-size:12px;line-hei
655
836
  .peer-banner button{background:transparent;border:1px solid currentColor;color:var(--ax-accent);
656
837
  padding:4px 10px;border-radius:4px;font:inherit;font-size:11px;cursor:pointer}
657
838
  .peer-banner button:hover{background:color-mix(in oklch,var(--ax-accent) 12%,transparent)}
658
- `,Nt=`
839
+ `,Ja=`
659
840
  const $ = (id) => document.getElementById(id);
660
841
  let state = null;
661
842
 
@@ -703,6 +884,14 @@ async function refresh() {
703
884
  renderCrons();
704
885
  renderWebhooks();
705
886
  renderMesh();
887
+ renderTeam();
888
+ wireTeamHandlers();
889
+ renderBusiness();
890
+ wireBusinessHandlers();
891
+ renderBoardsCfg();
892
+ wireBoardsCfgHandlers();
893
+ renderNotifications();
894
+ wireWebhookTriggerHandlers();
706
895
  initScheduleBuilder();
707
896
  initScheduleExpertAgentSelect();
708
897
  } catch (e) {
@@ -762,6 +951,35 @@ function renderWebhooks() {
762
951
  '</div>' +
763
952
  '<div style="font-size:11px;color:var(--ax-muted);margin-top:6px">' + escapeHtml(meta.hint) + '</div>';
764
953
 
954
+ // Triggers + defaultWorkflow editor \u2014 collapsed by default. Maps a
955
+ // platform event-type (e.g. "issues.opened") to a workflow id; the
956
+ // defaultWorkflow runs when no specific trigger matches.
957
+ const triggers = (w.triggers && typeof w.triggers === 'object') ? w.triggers : {};
958
+ const triggerRows = Object.keys(triggers).map(function(evt){
959
+ return '<div style="display:flex;gap:6px;align-items:center;margin-top:4px"><code style="flex:1;font-size:11px">' + escapeHtml(evt) + ' \u2192 ' + escapeHtml(triggers[evt]) + '</code><a href="#" data-act="wh-trig-rm" data-id="' + escapeHtml(w.id) + '" data-event="' + escapeHtml(evt) + '" style="color:var(--ax-muted);font-size:11px">\xD7</a></div>';
960
+ }).join('');
961
+ const trigBlock = document.createElement('details');
962
+ trigBlock.style.marginTop = '8px';
963
+ trigBlock.innerHTML =
964
+ '<summary style="font-size:11px;cursor:pointer;color:var(--ax-accent,#3a7bd5)">Routing \u2014 event-type triggers + default workflow</summary>' +
965
+ '<div style="margin-top:8px;padding:8px 10px;background:var(--ax-surface);border-radius:4px">' +
966
+ '<div style="font-size:11px;color:var(--ax-muted);margin-bottom:4px">Event-type \u2192 workflow id (e.g. <code>issues.opened</code> \u2192 <code>triage-bug</code>):</div>' +
967
+ (triggerRows || '<div style="font-size:11px;color:var(--ax-muted);font-style:italic">no triggers \u2014 every event uses the default workflow below</div>') +
968
+ '<div style="display:flex;gap:6px;margin-top:6px">' +
969
+ '<input data-wh-trig-event placeholder="event-type" style="flex:1;font-size:11px" />' +
970
+ '<input data-wh-trig-wf placeholder="workflow id" style="flex:1;font-size:11px" />' +
971
+ '<button data-act="wh-trig-add" data-id="' + escapeHtml(w.id) + '" class="ax-btn" style="padding:3px 9px;font-size:11px">Add</button>' +
972
+ '</div>' +
973
+ '<div style="margin-top:10px;padding-top:8px;border-top:1px dashed var(--ax-border)">' +
974
+ '<div style="font-size:11px;color:var(--ax-muted);margin-bottom:4px">Default workflow (fires when no trigger matches):</div>' +
975
+ '<div style="display:flex;gap:6px">' +
976
+ '<input data-wh-default value="' + escapeHtml(w.defaultWorkflow || '') + '" placeholder="(none)" style="flex:1;font-size:11px" />' +
977
+ '<button data-act="wh-default-save" data-id="' + escapeHtml(w.id) + '" class="ax-btn" style="padding:3px 9px;font-size:11px">Save</button>' +
978
+ '</div>' +
979
+ '</div>' +
980
+ '</div>';
981
+ card.appendChild(trigBlock);
982
+
765
983
  card.querySelector('[data-copy]').addEventListener('click', (e) => {
766
984
  const u = e.currentTarget.dataset.copy;
767
985
  navigator.clipboard.writeText(u).catch(() => {});
@@ -807,7 +1025,10 @@ async function toggleWebhook(id, enabled) {
807
1025
  }
808
1026
 
809
1027
  function renderMesh() {
810
- const m = state.mesh || { enabled: false, peers: [] };
1028
+ const m = state.mesh || { enabled: false, peers: [], healthCheck: { interval: 60, timeout: 10 } };
1029
+ const hc = m.healthCheck || { interval: 60, timeout: 10 };
1030
+ if ($('mh-interval')) $('mh-interval').value = hc.interval ?? 60;
1031
+ if ($('mh-timeout')) $('mh-timeout').value = hc.timeout ?? 10;
811
1032
 
812
1033
  // Hero card: status message + enable toggle + SVG network viz.
813
1034
  const hero = $('mesh-hero');
@@ -925,6 +1146,511 @@ function initialsOf(name, id) {
925
1146
  return (parts[0] || id || '?').slice(0, 2).toUpperCase();
926
1147
  }
927
1148
 
1149
+ // ---------------- Actors & Roles tab ----------------
1150
+ //
1151
+ // Inline onclick handlers can't pass quoted strings cleanly here \u2014 the
1152
+ // whole admin page is generated via a TypeScript template literal, so
1153
+ // escaped single-quotes in the JS source collapse and break the JS
1154
+ // parser. We attach a single delegated click handler instead and route
1155
+ // by data-act / data-id attributes.
1156
+
1157
+ function renderTeam() {
1158
+ const actors = state.actors || [];
1159
+ const roles = state.roles || [];
1160
+
1161
+ const al = $('actors-list');
1162
+ if (al) {
1163
+ if (!actors.length) {
1164
+ al.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:24px 18px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:var(--ax-radius-lg);color:var(--ax-muted)">No actors yet. Add one below or via <code>agentx actor add</code>.</div>';
1165
+ } else {
1166
+ al.innerHTML = actors.map(a => {
1167
+ const channels = (a.channels || []).map(c =>
1168
+ '<span class="ax-pill" style="font-size:10px;padding:2px 6px">' +
1169
+ escapeHtml(c.channel) + ':' + escapeHtml(String(c.handle).slice(0, 30)) +
1170
+ (c.preferredForTasks ? ' \u2605' : '') + '</span>'
1171
+ ).join(' ');
1172
+ return '<div class="ax-row-card" style="display:flex;align-items:center;gap:10px;padding:10px 14px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:6px">' +
1173
+ '<div style="flex:1;min-width:0">' +
1174
+ '<div style="display:flex;align-items:baseline;gap:8px;flex-wrap:wrap"><b>' + escapeHtml(a.name) + '</b><code style="font-size:10px;color:var(--ax-muted)">' + escapeHtml(a.id) + '</code>' +
1175
+ (a.email ? '<span style="font-size:11px;color:var(--ax-muted)">' + escapeHtml(a.email) + '</span>' : '') +
1176
+ (a.timezone ? '<span style="font-size:11px;color:var(--ax-muted)">' + escapeHtml(a.timezone) + '</span>' : '') +
1177
+ '</div>' +
1178
+ '<div style="margin-top:4px;display:flex;gap:4px;flex-wrap:wrap">' + channels + '</div>' +
1179
+ '</div>' +
1180
+ '<button class="ghost danger" data-act="delete-actor" data-id="' + escapeHtml(a.id) + '">Delete</button>' +
1181
+ '</div>';
1182
+ }).join('');
1183
+ }
1184
+ }
1185
+
1186
+ const rl = $('roles-list');
1187
+ if (rl) {
1188
+ if (!roles.length) {
1189
+ rl.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:24px 18px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:var(--ax-radius-lg);color:var(--ax-muted)">No roles yet. Add one below or via <code>agentx role create</code>.</div>';
1190
+ } else {
1191
+ rl.innerHTML = roles.map(r => {
1192
+ const memberPills = (r.members || []).map(m => {
1193
+ const ref = m.actor || m.role || '';
1194
+ return '<span class="ax-pill" style="font-size:10px;padding:2px 6px">' + escapeHtml(ref) +
1195
+ ' <a href="#" data-act="revoke" data-role="' + escapeHtml(r.id) + '" data-member="' + escapeHtml(ref) + '" style="color:var(--ax-muted);text-decoration:none">\xD7</a></span>';
1196
+ }).join(' ');
1197
+ const memberOptions = '<option value="">\u2014 add actor or role \u2014</option>' +
1198
+ actors.filter(a => !(r.members || []).some(m => m.actor === a.id)).map(a => '<option value="' + escapeHtml(a.id) + '">' + escapeHtml(a.id) + ' (' + escapeHtml(a.name) + ')</option>').join('') +
1199
+ roles.filter(rr => rr.id !== r.id && !(r.members || []).some(m => m.role === rr.id)).map(rr => '<option value="' + escapeHtml(rr.id) + '">' + escapeHtml(rr.id) + ' (' + escapeHtml(rr.name) + ')</option>').join('');
1200
+ return '<div class="ax-row-card" style="padding:10px 14px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:6px">' +
1201
+ '<div style="display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;justify-content:space-between">' +
1202
+ '<div><b>' + escapeHtml(r.name) + '</b> <code style="font-size:10px;color:var(--ax-muted)">' + escapeHtml(r.id) + '</code> <span style="font-size:11px;color:var(--ax-muted)">strategy: ' + escapeHtml(r.assignmentStrategy) + '</span></div>' +
1203
+ '<button class="ghost danger" data-act="delete-role" data-id="' + escapeHtml(r.id) + '">Delete</button>' +
1204
+ '</div>' +
1205
+ '<div style="margin-top:6px;display:flex;gap:4px;flex-wrap:wrap;align-items:center">' + (memberPills || '<i style="font-size:11px;color:var(--ax-muted)">no members yet</i>') +
1206
+ '<select data-act="grant" data-role="' + escapeHtml(r.id) + '" style="margin-left:auto;font-size:11px;padding:2px 6px">' + memberOptions + '</select>' +
1207
+ '</div>' +
1208
+ '</div>';
1209
+ }).join('');
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ // Single delegated click + change handler for the Team tab. Wired once
1215
+ // at page-init time below renderTeam in the existing DOMContentLoaded
1216
+ // flow \u2014 see the wireTeamHandlers call.
1217
+ function wireTeamHandlers() {
1218
+ if (window.__teamWired) return;
1219
+ window.__teamWired = true;
1220
+ document.addEventListener('click', async (ev) => {
1221
+ const t = ev.target.closest('[data-act]');
1222
+ if (!t) return;
1223
+ const act = t.getAttribute('data-act');
1224
+ if (act === 'delete-actor') {
1225
+ const id = t.getAttribute('data-id');
1226
+ if (!confirm('Delete actor ' + id + '?')) return;
1227
+ try { await req('DELETE', '/api/admin/actors', { id }); await load(); }
1228
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1229
+ } else if (act === 'delete-role') {
1230
+ const id = t.getAttribute('data-id');
1231
+ if (!confirm('Delete role ' + id + '?')) return;
1232
+ try { await req('DELETE', '/api/admin/roles', { id }); await load(); }
1233
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1234
+ } else if (act === 'revoke') {
1235
+ ev.preventDefault();
1236
+ const role = t.getAttribute('data-role');
1237
+ const member = t.getAttribute('data-member');
1238
+ try { await req('POST', '/api/admin/roles/revoke', { role, member }); await load(); }
1239
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1240
+ }
1241
+ });
1242
+ document.addEventListener('change', async (ev) => {
1243
+ const t = ev.target.closest('select[data-act="grant"]');
1244
+ if (!t) return;
1245
+ const role = t.getAttribute('data-role');
1246
+ const member = t.value;
1247
+ if (!member) return;
1248
+ try { await req('POST', '/api/admin/roles/grant', { role, member }); await load(); }
1249
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1250
+ });
1251
+ }
1252
+
1253
+ // Form-submit shims kept on window for the inline onclick="upsertActor()"
1254
+ // handlers in the add-form summary blocks.
1255
+
1256
+ window.upsertActor = async function() {
1257
+ const id = $('ac-id').value.trim();
1258
+ const name = $('ac-name').value.trim();
1259
+ const email = $('ac-email').value.trim();
1260
+ const channelsRaw = $('ac-channels').value.trim();
1261
+ const timezone = $('ac-timezone').value.trim();
1262
+ const channels = channelsRaw.split(',').map(s => s.trim()).filter(Boolean).map(s => {
1263
+ const preferredForTasks = s.endsWith('*');
1264
+ const clean = preferredForTasks ? s.slice(0, -1) : s;
1265
+ const sep = clean.indexOf(':');
1266
+ return { channel: clean.slice(0, sep), handle: clean.slice(sep + 1), preferredForTasks };
1267
+ }).filter(c => c.channel && c.handle);
1268
+ try {
1269
+ await req('POST', '/api/admin/actors', { id, name, email: email || undefined, channels, timezone: timezone || undefined });
1270
+ $('ac-id').value = ''; $('ac-name').value = ''; $('ac-email').value = ''; $('ac-channels').value = ''; $('ac-timezone').value = '';
1271
+ showMsg($('ac-msg'), 'ok', 'Actor saved');
1272
+ await load();
1273
+ } catch (e) { showMsg($('ac-msg'), 'err', e.message); }
1274
+ }
1275
+
1276
+ window.upsertRole = async function() {
1277
+ const id = $('rl-id').value.trim();
1278
+ const name = $('rl-name').value.trim();
1279
+ const assignmentStrategy = $('rl-strategy').value;
1280
+ try {
1281
+ await req('POST', '/api/admin/roles', { id, name, assignmentStrategy });
1282
+ $('rl-id').value = ''; $('rl-name').value = '';
1283
+ showMsg($('rl-msg'), 'ok', 'Role saved');
1284
+ await load();
1285
+ } catch (e) { showMsg($('rl-msg'), 'err', e.message); }
1286
+ }
1287
+
1288
+ // ---------------- Business tab ----------------
1289
+
1290
+ function renderBusiness() {
1291
+ const b = state.business || {};
1292
+ const orgChart = b.orgChart || {};
1293
+ const projects = b.projects || [];
1294
+ const contactMap = b.contactMap || [];
1295
+
1296
+ const ol = $('business-org-list');
1297
+ if (ol) {
1298
+ const entries = Object.entries(orgChart);
1299
+ if (entries.length === 0) {
1300
+ ol.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:18px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:6px;color:var(--ax-muted);font-size:12px">no org-chart entries</div>';
1301
+ } else {
1302
+ ol.innerHTML = entries.map(function(kv){
1303
+ const id = kv[0]; const e = kv[1];
1304
+ const reports = e.reportsTo ? '<span style="font-size:11px;color:var(--ax-muted)">\u2192 ' + escapeHtml(e.reportsTo) + '</span>' : '';
1305
+ const sched = e.schedule ? '<span style="font-size:11px;color:var(--ax-muted)">' + escapeHtml((e.schedule.days || []).join(',')) + ' ' + escapeHtml(e.schedule.start || '') + '\u2013' + escapeHtml(e.schedule.end || '') + '</span>' : '';
1306
+ return '<div class="ax-row-card" style="display:flex;align-items:center;gap:10px;padding:8px 12px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:5px">' +
1307
+ '<div style="flex:1;min-width:0;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap">' +
1308
+ '<code style="font-size:12px">' + escapeHtml(id) + '</code>' +
1309
+ '<b>' + escapeHtml(e.role || '') + '</b>' + reports + sched +
1310
+ '</div>' +
1311
+ '<button class="ghost danger" data-act="biz-org-rm" data-id="' + escapeHtml(id) + '">Remove</button>' +
1312
+ '</div>';
1313
+ }).join('');
1314
+ }
1315
+ }
1316
+
1317
+ const pl = $('business-project-list');
1318
+ if (pl) {
1319
+ if (projects.length === 0) {
1320
+ pl.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:18px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:6px;color:var(--ax-muted);font-size:12px">no projects</div>';
1321
+ } else {
1322
+ pl.innerHTML = projects.map(function(p){
1323
+ const pm = p.pm ? '<span style="font-size:11px;color:var(--ax-muted)">pm=' + escapeHtml(p.pm) + '</span>' : '';
1324
+ const client = p.client ? '<span style="font-size:11px;color:var(--ax-muted)">client=' + escapeHtml(p.client) + '</span>' : '';
1325
+ return '<div class="ax-row-card" style="display:flex;align-items:center;gap:10px;padding:8px 12px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:5px">' +
1326
+ '<div style="flex:1;min-width:0;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap"><code style="font-size:12px">' + escapeHtml(p.id) + '</code>' + pm + client + '</div>' +
1327
+ '<button class="ghost danger" data-act="biz-proj-rm" data-id="' + escapeHtml(p.id) + '">Remove</button>' +
1328
+ '</div>';
1329
+ }).join('');
1330
+ }
1331
+ }
1332
+
1333
+ const cl = $('business-contact-list');
1334
+ if (cl) {
1335
+ if (contactMap.length === 0) {
1336
+ cl.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:18px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:6px;color:var(--ax-muted);font-size:12px">no contact mappings</div>';
1337
+ } else {
1338
+ cl.innerHTML = contactMap.map(function(c, idx){
1339
+ const key = c.chatId ? 'chatId=' + c.chatId : c.username ? 'username=' + c.username : c.senderId ? 'senderId=' + c.senderId : '?';
1340
+ const dn = c.displayName ? '<span style="font-size:11px;color:var(--ax-muted)">(' + escapeHtml(c.displayName) + ')</span>' : '';
1341
+ return '<div class="ax-row-card" style="display:flex;align-items:center;gap:10px;padding:8px 12px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:5px">' +
1342
+ '<div style="flex:1;min-width:0;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap">' +
1343
+ (c.channel ? '<span class="ax-pill" style="font-size:10px;padding:2px 6px">' + escapeHtml(c.channel) + '</span>' : '') +
1344
+ '<code style="font-size:12px">' + escapeHtml(key) + '</code>' +
1345
+ '<span style="font-size:11px;color:var(--ax-muted)">\u2192 ' + escapeHtml(c.client) + (c.project ? '/' + escapeHtml(c.project) : '') + '</span>' + dn +
1346
+ '</div>' +
1347
+ '<button class="ghost danger" data-act="biz-contact-rm" data-idx="' + idx + '">Remove</button>' +
1348
+ '</div>';
1349
+ }).join('');
1350
+ }
1351
+ }
1352
+ }
1353
+
1354
+ function wireBusinessHandlers() {
1355
+ if (window.__bizWired) return;
1356
+ window.__bizWired = true;
1357
+ document.addEventListener('click', async (ev) => {
1358
+ const t = ev.target.closest('[data-act^="biz-"]');
1359
+ if (!t) return;
1360
+ const act = t.getAttribute('data-act');
1361
+ if (act === 'biz-org-rm') {
1362
+ const agentId = t.getAttribute('data-id');
1363
+ if (!confirm('Remove org-chart entry for ' + agentId + '?')) return;
1364
+ try { await req('DELETE', '/api/admin/business/orgchart', { agentId }); await load(); }
1365
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1366
+ } else if (act === 'biz-proj-rm') {
1367
+ const id = t.getAttribute('data-id');
1368
+ if (!confirm('Remove project ' + id + '?')) return;
1369
+ try { await req('DELETE', '/api/admin/business/project', { id }); await load(); }
1370
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1371
+ } else if (act === 'biz-contact-rm') {
1372
+ const idx = parseInt(t.getAttribute('data-idx') || '-1', 10);
1373
+ const list = (state.business && state.business.contactMap) || [];
1374
+ const c = list[idx];
1375
+ if (!c) return;
1376
+ const filters = {};
1377
+ if (c.channel) filters.channel = c.channel;
1378
+ if (c.chatId) filters.chatId = c.chatId;
1379
+ else if (c.username) filters.username = c.username;
1380
+ else if (c.senderId) filters.senderId = c.senderId;
1381
+ try { await req('DELETE', '/api/admin/business/contact', filters); await load(); }
1382
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1383
+ }
1384
+ });
1385
+ }
1386
+
1387
+ window.upsertOrgEntry = async function() {
1388
+ const agentId = $('bo-agentId').value.trim();
1389
+ const role = $('bo-role').value.trim();
1390
+ const reportsTo = $('bo-reportsTo').value.trim();
1391
+ const start = $('bo-start').value.trim() || '09:00';
1392
+ const end = $('bo-end').value.trim() || '17:00';
1393
+ const days = ($('bo-days').value.trim() || 'mon,tue,wed,thu,fri').split(',').map(s => s.trim()).filter(Boolean);
1394
+ try {
1395
+ await req('POST', '/api/admin/business/orgchart', { agentId, role, reportsTo, start, end, days });
1396
+ $('bo-agentId').value = ''; $('bo-role').value = ''; $('bo-reportsTo').value = '';
1397
+ showMsg($('bo-msg'), 'ok', 'Entry saved');
1398
+ await load();
1399
+ } catch (e) { showMsg($('bo-msg'), 'err', e.message); }
1400
+ }
1401
+
1402
+ window.upsertProject = async function() {
1403
+ const id = $('bp-id').value.trim();
1404
+ const pm = $('bp-pm').value.trim();
1405
+ const client = $('bp-client').value.trim();
1406
+ try {
1407
+ await req('POST', '/api/admin/business/project', { id, pm, client });
1408
+ $('bp-id').value = ''; $('bp-pm').value = ''; $('bp-client').value = '';
1409
+ showMsg($('bp-msg'), 'ok', 'Project saved');
1410
+ await load();
1411
+ } catch (e) { showMsg($('bp-msg'), 'err', e.message); }
1412
+ }
1413
+
1414
+ // ---------------- Boards-cfg tab ----------------
1415
+
1416
+ function renderBoardsCfg() {
1417
+ const boards = state.boards || [];
1418
+ const list = $('boards-cfg-list');
1419
+ if (!list) return;
1420
+ if (boards.length === 0) {
1421
+ list.innerHTML = '<div class="ax-empty-card" style="text-align:center;padding:24px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:6px;color:var(--ax-muted);font-size:12px">no boards configured</div>';
1422
+ return;
1423
+ }
1424
+ list.innerHTML = boards.map(function(b){
1425
+ const projects = ((b.source && b.source.projects) || []).map(p => '<span class="ax-pill" style="font-size:10px;padding:2px 6px">' + escapeHtml(p) + '</span>').join(' ');
1426
+ const cols = (b.columns || []).map(function(c){
1427
+ const map = c.kind === 'scoped-label' ? 'scoped=' + (c.scopedLabel || '')
1428
+ : c.kind === 'label' ? 'label=' + (c.mapsToLabel || '')
1429
+ : c.kind === 'open-backlog' ? 'prefix=' + (c.scopedPrefix || 'Status')
1430
+ : c.kind;
1431
+ return '<span style="display:inline-flex;align-items:center;gap:4px;font-size:10px;padding:2px 6px;border:1px solid var(--ax-border);border-radius:3px;margin-right:4px;margin-bottom:4px"><b>' + escapeHtml(c.title) + '</b><span style="color:var(--ax-muted)">(' + escapeHtml(map) + ')</span><a href="#" data-act="bd-col-rm" data-board="' + escapeHtml(b.id) + '" data-column="' + escapeHtml(c.id) + '" style="color:var(--ax-muted);text-decoration:none">\xD7</a></span>';
1432
+ }).join('');
1433
+ return '<div class="ax-row-card" style="padding:10px 14px;border:1px solid var(--ax-border);border-radius:6px;margin-bottom:8px">' +
1434
+ '<div style="display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;justify-content:space-between">' +
1435
+ '<div><b>' + escapeHtml(b.name) + '</b> <code style="font-size:10px;color:var(--ax-muted)">' + escapeHtml(b.id) + '</code></div>' +
1436
+ '<div style="display:flex;gap:4px">' +
1437
+ '<button class="ghost" data-act="bd-edit" data-id="' + escapeHtml(b.id) + '">Edit</button>' +
1438
+ '<button class="ghost danger" data-act="bd-rm" data-id="' + escapeHtml(b.id) + '">Remove</button>' +
1439
+ '</div>' +
1440
+ '</div>' +
1441
+ '<div style="margin-top:6px">' + projects +
1442
+ (b.primaryToolLabel ? '<span class="ax-pill" style="font-size:10px;padding:2px 6px;margin-left:4px">label: ' + escapeHtml(b.primaryToolLabel) + '</span>' : '') +
1443
+ '<span class="ax-pill" style="font-size:10px;padding:2px 6px;margin-left:4px">open=' + (b.timeRangeDays || 30) + 'd / closed=' + (b.closedWindowDays || 30) + 'd</span>' +
1444
+ '</div>' +
1445
+ '<div style="margin-top:8px;padding-top:8px;border-top:1px dashed var(--ax-border)">' +
1446
+ '<div style="font-size:11px;color:var(--ax-muted);margin-bottom:4px">columns:</div>' +
1447
+ (cols || '<i style="font-size:11px;color:var(--ax-muted)">no custom columns (board uses GitLab default flow)</i>') +
1448
+ '<details style="margin-top:6px"><summary style="font-size:11px;cursor:pointer;color:var(--ax-accent,#3a7bd5)">+ add column</summary>' +
1449
+ '<div style="margin-top:6px;display:grid;grid-template-columns:1fr 1fr 1fr 1fr auto;gap:6px;align-items:end">' +
1450
+ '<label>Id<input data-bd-col="id" data-board="' + escapeHtml(b.id) + '" placeholder="doing" /></label>' +
1451
+ '<label>Title<input data-bd-col="title" data-board="' + escapeHtml(b.id) + '" placeholder="Doing" /></label>' +
1452
+ '<label>Kind<select data-bd-col="kind" data-board="' + escapeHtml(b.id) + '"><option value="scoped-label">scoped-label</option><option value="label">label</option><option value="open-backlog">open-backlog</option><option value="closed">closed</option></select></label>' +
1453
+ '<label>Scoped/label value<input data-bd-col="value" data-board="' + escapeHtml(b.id) + '" placeholder="Status::Doing" /></label>' +
1454
+ '<button data-act="bd-col-add" data-board="' + escapeHtml(b.id) + '">Add</button>' +
1455
+ '</div>' +
1456
+ '</details>' +
1457
+ '</div>' +
1458
+ '</div>';
1459
+ }).join('');
1460
+ }
1461
+
1462
+ function wireBoardsCfgHandlers() {
1463
+ if (window.__boardsCfgWired) return;
1464
+ window.__boardsCfgWired = true;
1465
+ document.addEventListener('click', async (ev) => {
1466
+ const t = ev.target.closest('[data-act^="bd-"]');
1467
+ if (!t) return;
1468
+ const act = t.getAttribute('data-act');
1469
+ if (act === 'bd-rm') {
1470
+ const id = t.getAttribute('data-id');
1471
+ if (!confirm('Remove board ' + id + '?')) return;
1472
+ try { await req('DELETE', '/api/admin/boards', { id }); await load(); }
1473
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1474
+ } else if (act === 'bd-edit') {
1475
+ // Pre-fill the add form so the operator can re-save.
1476
+ const id = t.getAttribute('data-id');
1477
+ const b = (state.boards || []).find(x => x.id === id);
1478
+ if (!b) return;
1479
+ $('bd-id').value = b.id;
1480
+ $('bd-name').value = b.name || '';
1481
+ $('bd-projects').value = ((b.source && b.source.projects) || []).join(',');
1482
+ $('bd-label').value = b.primaryToolLabel || '';
1483
+ $('bd-days').value = b.timeRangeDays || 30;
1484
+ $('bd-closed-days').value = b.closedWindowDays || 30;
1485
+ // Open the details and scroll to it
1486
+ const details = document.querySelector('#tab-boards-cfg .add-form');
1487
+ if (details) { details.open = true; details.scrollIntoView({ behavior: 'smooth' }); }
1488
+ } else if (act === 'bd-col-rm') {
1489
+ ev.preventDefault();
1490
+ const boardId = t.getAttribute('data-board');
1491
+ const columnId = t.getAttribute('data-column');
1492
+ try { await req('DELETE', '/api/admin/boards/columns', { boardId, columnId }); await load(); }
1493
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1494
+ } else if (act === 'bd-col-add') {
1495
+ const boardId = t.getAttribute('data-board');
1496
+ const card = t.closest('.ax-row-card');
1497
+ if (!card) return;
1498
+ const id = card.querySelector('[data-bd-col="id"]').value.trim();
1499
+ const title = card.querySelector('[data-bd-col="title"]').value.trim();
1500
+ const kind = card.querySelector('[data-bd-col="kind"]').value;
1501
+ const value = card.querySelector('[data-bd-col="value"]').value.trim();
1502
+ if (!id || !title) { showMsg($('global-msg'), 'err', 'column id and title required'); return; }
1503
+ const payload = { boardId, columnId: id, title, kind };
1504
+ if (kind === 'scoped-label') payload.scopedLabel = value;
1505
+ else if (kind === 'label') payload.mapsToLabel = value;
1506
+ try { await req('POST', '/api/admin/boards/columns', payload); await load(); }
1507
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1508
+ }
1509
+ });
1510
+ }
1511
+
1512
+ // ---------------- Notifications + Webhook triggers ----------------
1513
+
1514
+ function renderNotifications() {
1515
+ const n = state.notifications || {};
1516
+ const cur = $('notif-current');
1517
+ if (cur) {
1518
+ if (n.destination) {
1519
+ const acct = n.destination.accountId ? ' (account=' + escapeHtml(n.destination.accountId) + ')' : '';
1520
+ cur.innerHTML = '<b>Routing to</b> <code>' + escapeHtml(n.destination.channel) + ':' + escapeHtml(n.destination.chatId) + '</code>' + acct;
1521
+ } else {
1522
+ cur.innerHTML = '<i>No destination set \u2014 notifications go to the daemon log only.</i>';
1523
+ }
1524
+ }
1525
+ if ($('notif-channel') && n.destination) {
1526
+ $('notif-channel').value = n.destination.channel || '';
1527
+ $('notif-chat-id').value = n.destination.chatId || '';
1528
+ $('notif-account-id').value = n.destination.accountId || '';
1529
+ }
1530
+ if ($('notif-threshold')) $('notif-threshold').value = n.longTaskThreshold ?? 30;
1531
+ if ($('notif-on-complete')) $('notif-on-complete').checked = n.on?.taskComplete !== false;
1532
+ if ($('notif-on-error')) $('notif-on-error').checked = n.on?.taskError !== false;
1533
+ if ($('notif-on-queued')) $('notif-on-queued').checked = !!n.on?.taskQueued;
1534
+ }
1535
+
1536
+ window.saveNotifications = async function() {
1537
+ const channel = $('notif-channel').value.trim();
1538
+ const chatId = $('notif-chat-id').value.trim();
1539
+ const accountId = $('notif-account-id').value.trim();
1540
+ const threshold = parseInt($('notif-threshold').value, 10);
1541
+ const body = {
1542
+ on: {
1543
+ taskComplete: $('notif-on-complete').checked,
1544
+ taskError: $('notif-on-error').checked,
1545
+ taskQueued: $('notif-on-queued').checked,
1546
+ },
1547
+ longTaskThreshold: Number.isFinite(threshold) ? threshold : 30,
1548
+ };
1549
+ if (channel && chatId) {
1550
+ body.destination = { channel, chatId, ...(accountId ? { accountId } : {}) };
1551
+ }
1552
+ try {
1553
+ await req('POST', '/api/admin/notifications', body);
1554
+ showMsg($('notif-msg'), 'ok', 'Saved');
1555
+ await load();
1556
+ } catch (e) { showMsg($('notif-msg'), 'err', e.message); }
1557
+ }
1558
+
1559
+ window.clearNotificationsDestination = async function() {
1560
+ try {
1561
+ await req('POST', '/api/admin/notifications', { destination: null });
1562
+ showMsg($('notif-msg'), 'ok', 'Destination cleared');
1563
+ await load();
1564
+ } catch (e) { showMsg($('notif-msg'), 'err', e.message); }
1565
+ }
1566
+
1567
+ function wireWebhookTriggerHandlers() {
1568
+ if (window.__whTrigWired) return;
1569
+ window.__whTrigWired = true;
1570
+ document.addEventListener('click', async (ev) => {
1571
+ const t = ev.target.closest('[data-act^="wh-"]');
1572
+ if (!t) return;
1573
+ const id = t.getAttribute('data-id');
1574
+ if (t.getAttribute('data-act') === 'wh-trig-rm') {
1575
+ ev.preventDefault();
1576
+ const evt = t.getAttribute('data-event');
1577
+ const wh = state.webhooks.find(w => w.id === id);
1578
+ if (!wh) return;
1579
+ const next = { ...(wh.triggers || {}) };
1580
+ delete next[evt];
1581
+ try { await req('POST', '/api/admin/webhooks/triggers', { id, triggers: next }); await load(); }
1582
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1583
+ } else if (t.getAttribute('data-act') === 'wh-trig-add') {
1584
+ const card = t.closest('.ax-row-card');
1585
+ if (!card) return;
1586
+ const evt = card.querySelector('[data-wh-trig-event]').value.trim();
1587
+ const wf = card.querySelector('[data-wh-trig-wf]').value.trim();
1588
+ if (!evt || !wf) { showMsg($('global-msg'), 'err', 'event-type + workflow id required'); return; }
1589
+ const wh = state.webhooks.find(w => w.id === id);
1590
+ const next = { ...((wh && wh.triggers) || {}), [evt]: wf };
1591
+ try { await req('POST', '/api/admin/webhooks/triggers', { id, triggers: next }); await load(); }
1592
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1593
+ } else if (t.getAttribute('data-act') === 'wh-default-save') {
1594
+ const card = t.closest('.ax-row-card');
1595
+ if (!card) return;
1596
+ const defaultWorkflow = card.querySelector('[data-wh-default]').value.trim();
1597
+ try { await req('POST', '/api/admin/webhooks/triggers', { id, defaultWorkflow }); await load(); }
1598
+ catch (e) { showMsg($('global-msg'), 'err', e.message); }
1599
+ }
1600
+ });
1601
+ }
1602
+
1603
+ window.saveMeshHealth = async function() {
1604
+ const interval = parseInt($('mh-interval').value, 10);
1605
+ const timeout = parseInt($('mh-timeout').value, 10);
1606
+ const body = {};
1607
+ if (Number.isFinite(interval)) body.interval = interval;
1608
+ if (Number.isFinite(timeout)) body.timeout = timeout;
1609
+ if (Object.keys(body).length === 0) {
1610
+ showMsg($('mh-msg'), 'err', 'set interval and/or timeout');
1611
+ return;
1612
+ }
1613
+ try {
1614
+ await req('POST', '/api/admin/mesh/health', body);
1615
+ showMsg($('mh-msg'), 'ok', 'Saved');
1616
+ await load();
1617
+ } catch (e) { showMsg($('mh-msg'), 'err', e.message); }
1618
+ }
1619
+
1620
+ window.upsertBoardCfg = async function() {
1621
+ const id = $('bd-id').value.trim();
1622
+ const name = $('bd-name').value.trim();
1623
+ const projects = $('bd-projects').value.trim();
1624
+ const primaryToolLabel = $('bd-label').value.trim();
1625
+ const timeRangeDays = parseInt($('bd-days').value, 10) || 30;
1626
+ const closedWindowDays = parseInt($('bd-closed-days').value, 10) || 30;
1627
+ try {
1628
+ await req('POST', '/api/admin/boards', { id, name, projects, primaryToolLabel, timeRangeDays, closedWindowDays });
1629
+ showMsg($('bd-msg'), 'ok', 'Board saved');
1630
+ await load();
1631
+ } catch (e) { showMsg($('bd-msg'), 'err', e.message); }
1632
+ }
1633
+
1634
+ window.upsertContact = async function() {
1635
+ const channel = $('bc-channel').value.trim();
1636
+ const chatId = $('bc-chatId').value.trim();
1637
+ const username = $('bc-username').value.trim();
1638
+ const senderId = $('bc-senderId').value.trim();
1639
+ const client = $('bc-client').value.trim();
1640
+ const project = $('bc-project').value.trim();
1641
+ const displayName = $('bc-displayName').value.trim();
1642
+ if (!chatId && !username && !senderId) {
1643
+ showMsg($('bc-msg'), 'err', 'one of chat-id / username / sender-id required');
1644
+ return;
1645
+ }
1646
+ try {
1647
+ await req('POST', '/api/admin/business/contact', { channel, chatId, username, senderId, client, project, displayName });
1648
+ ['bc-channel','bc-chatId','bc-username','bc-senderId','bc-client','bc-project','bc-displayName'].forEach(id => { $(id).value = ''; });
1649
+ showMsg($('bc-msg'), 'ok', 'Mapping saved');
1650
+ await load();
1651
+ } catch (e) { showMsg($('bc-msg'), 'err', e.message); }
1652
+ }
1653
+
928
1654
  /** Pick a stable avatar variant from the string hash. Gives each agent a
929
1655
  * distinct-looking square without the operator having to configure one. */
930
1656
  function avatarVariant(key) {
@@ -1095,6 +1821,7 @@ const CHANNEL_DEFS = [
1095
1821
  { id: 'discord', icon: 'DC', label: 'Discord' },
1096
1822
  { id: 'gitlab', icon: 'GL', label: 'GitLab' },
1097
1823
  { id: 'github', icon: 'GH', label: 'GitHub' },
1824
+ { id: 'webrtc', icon: '\u260E', label: 'Calls (WebRTC)' },
1098
1825
  ];
1099
1826
 
1100
1827
  /** Per-connector card metadata \u2014 logo color, long description, setup time.
@@ -1106,6 +1833,7 @@ const CONNECTOR_META = {
1106
1833
  discord: { group: 'chat', brand: '#5865F2', desc: 'Works in servers, threads, and DMs. Create a Discord bot, grant Read/Send Messages, paste the token.' },
1107
1834
  gitlab: { group: 'dev', brand: '#FC6D26', desc: 'Pings your agent on MRs, issues, and pipeline events. Works with self-hosted too.' },
1108
1835
  github: { group: 'dev', brand: '#24292e', desc: 'Pings your agent on PRs, issues, and CI runs. Configure per-repo.' },
1836
+ webrtc: { group: 'chat', brand: '#10B981', desc: 'Voice calls with a transcribing bot. Live on the /call page; history of recent calls below.' },
1109
1837
  };
1110
1838
 
1111
1839
  function renderChannelsGrid() {
@@ -1179,7 +1907,7 @@ function channelSubtext(id, status) {
1179
1907
  * the matching connector card. */
1180
1908
  function showChannelPane(id) {
1181
1909
  state.__activeChannel = id;
1182
- const panes = ['telegram','whatsapp','slack','discord','gitlab','github'];
1910
+ const panes = ['telegram','whatsapp','slack','discord','gitlab','github','webrtc'];
1183
1911
  for (const p of panes) {
1184
1912
  const el = document.getElementById('ch-' + p);
1185
1913
  if (el) el.hidden = (p !== id);
@@ -1202,6 +1930,7 @@ function showChannelPane(id) {
1202
1930
  else if (id === 'gitlab') renderGitLabPane();
1203
1931
  else if (id === 'github') renderGitHubPane();
1204
1932
  else if (id === 'whatsapp') renderWhatsAppPane();
1933
+ else if (id === 'webrtc') renderWebRtcPane();
1205
1934
  }
1206
1935
 
1207
1936
  function channelStatus(id) {
@@ -1385,6 +2114,52 @@ function jumpToWebhooks() {
1385
2114
  if (src) src.value = 'github';
1386
2115
  }
1387
2116
 
2117
+ function renderWebRtcPane() {
2118
+ const pane = $('ch-webrtc');
2119
+ if (!pane) return;
2120
+ pane.innerHTML =
2121
+ '<div class="ax-section-card"><div class="ax-section-card__head">' +
2122
+ '<div class="ax-section-card__icon" style="background:#10B981;color:#fff">\u260E</div>' +
2123
+ '<div class="ax-section-card__info"><h3>Calls (WebRTC)</h3><div class="ax-section-card__sub">Live calls happen on <a href="/call" target="_blank">/call</a>. The transcribing bot can be invited per call from there. This panel shows active + recent sessions.</div></div></div>' +
2124
+ '<div class="ax-stack" style="margin-top:14px">' +
2125
+ '<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">' +
2126
+ '<h3 style="margin:0;font-size:13px">Active calls</h3>' +
2127
+ '<button class="ax-btn" onclick="loadWebRtcHistory()">Refresh</button>' +
2128
+ '</div>' +
2129
+ '<div id="webrtc-active" style="border:1px solid var(--ax-border);border-radius:6px;padding:8px;font-family:&quot;IBM Plex Mono&quot;,monospace;font-size:11px">click refresh</div>' +
2130
+ '<h3 style="margin:14px 0 8px;font-size:13px">Recent sessions <span style="font-size:11px;color:var(--ax-muted);font-weight:normal">(in-process ring buffer, last ~50)</span></h3>' +
2131
+ '<div id="webrtc-history" style="border:1px solid var(--ax-border);border-radius:6px;padding:8px;font-family:&quot;IBM Plex Mono&quot;,monospace;font-size:11px;max-height:280px;overflow-y:auto">click refresh</div>' +
2132
+ '</div>';
2133
+ loadWebRtcHistory();
2134
+ }
2135
+
2136
+ window.loadWebRtcHistory = async function() {
2137
+ try {
2138
+ const data = await req('GET', '/api/admin/channels/webrtc/history');
2139
+ const active = data.active || [];
2140
+ const history = data.history || [];
2141
+ const fmtTs = function(ms) { const d = new Date(ms); return (d.getMonth()+1) + '/' + d.getDate() + ' ' + String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0') + ':' + String(d.getSeconds()).padStart(2,'0'); };
2142
+ const fmtDur = function(s) { if (s < 60) return s + 's'; const m = Math.floor(s/60); return m + 'm ' + (s % 60) + 's'; };
2143
+ $('webrtc-active').innerHTML = active.length
2144
+ ? active.map(function(b){ return '<div>\xB7 call=' + escapeHtml(b.callId.slice(0, 12)) + ' agent=<b>' + escapeHtml(b.agentId) + '</b> target=' + escapeHtml(b.target) + ' uptime=' + fmtDur(b.uptimeSec) + '</div>'; }).join('')
2145
+ : '<i style="color:var(--ax-muted)">no active calls</i>';
2146
+ $('webrtc-history').innerHTML = history.length
2147
+ ? '<div style="display:grid;grid-template-columns:130px 110px 110px 1fr 70px 60px;gap:6px;font-size:10px;color:var(--ax-muted);text-transform:uppercase;letter-spacing:0.04em;padding-bottom:4px;border-bottom:1px solid var(--ax-border);margin-bottom:4px"><div>started</div><div>agent</div><div>target</div><div>callId</div><div>duration</div><div>chunks</div></div>' +
2148
+ history.map(function(h){ return '<div style="display:grid;grid-template-columns:130px 110px 110px 1fr 70px 60px;gap:6px;padding:3px 0">' +
2149
+ '<div>' + escapeHtml(fmtTs(h.startedAt)) + '</div>' +
2150
+ '<div><b>' + escapeHtml(h.agentId) + '</b></div>' +
2151
+ '<div>' + escapeHtml(h.target) + '</div>' +
2152
+ '<div style="color:var(--ax-muted)">' + escapeHtml(h.callId.slice(0, 24)) + '</div>' +
2153
+ '<div>' + escapeHtml(fmtDur(h.durationSec)) + '</div>' +
2154
+ '<div>' + h.transcriptChunks + '</div>' +
2155
+ '</div>'; }).join('')
2156
+ : '<i style="color:var(--ax-muted)">no completed sessions in this daemon&#39;s memory yet</i>';
2157
+ } catch (e) {
2158
+ $('webrtc-active').innerHTML = '<i style="color:var(--ax-err)">failed: ' + e.message + '</i>';
2159
+ $('webrtc-history').innerHTML = '';
2160
+ }
2161
+ }
2162
+
1388
2163
  function renderWhatsAppPane() {
1389
2164
  const w = state.whatsapp || {};
1390
2165
  const enabled = !!w.enabled;
@@ -1406,10 +2181,57 @@ function renderWhatsAppPane() {
1406
2181
  '<div id="wa-connected" style="display:none;padding:14px;background:color-mix(in oklch,var(--ax-accent) 10%,transparent);border:1px solid color-mix(in oklch,var(--ax-accent) 35%,transparent);border-radius:6px;color:var(--ax-accent);font-size:13px">\u2713 Paired \u2014 the daemon is signed in.</div>' +
1407
2182
  '<div id="wa-disabled" style="display:none;padding:14px;font-size:12px;color:var(--ax-muted);line-height:1.6">WhatsApp is currently disabled in <code>agentx.json</code>. Enable it there (or via the Advanced tab) and restart the daemon; the QR will appear once Baileys needs it.</div>' +
1408
2183
  '<div class="hint-block" style="margin-top:12px">Routes, phone numbers, and allow-lists live under <code>channels.whatsapp</code> in the <b>Advanced</b> tab. Requires <code>@whiskeysockets/baileys</code> installed in the daemon&rsquo;s dir.</div>' +
1409
- '</div>';
2184
+ '</div>' +
2185
+ '<details class="add-form" style="margin-top:14px"><summary class="primary">\u{1F4E5} Wiki ingest from WhatsApp</summary>' +
2186
+ '<div style="margin-top:10px">' +
2187
+ '<p style="font-size:11px;color:var(--ax-muted);margin:0 0 10px">Sweep observed WhatsApp chats/contacts into an agent&#39;s wiki. Mirrors <code>agentx whatsapp ingest-all/list-chats/list-contacts</code>.</p>' +
2188
+ '<div style="display:flex;gap:8px;margin-bottom:10px">' +
2189
+ '<button class="ax-btn" onclick="loadWhatsAppLists()">Reload chats + contacts</button>' +
2190
+ '<label class="ax-inline" style="display:inline-flex;gap:6px;align-items:center;font-size:12px"><input type="checkbox" id="wa-ingest-dry" checked /> dry-run</label>' +
2191
+ '<label class="ax-inline" style="display:inline-flex;gap:6px;align-items:center;font-size:12px"><input type="checkbox" id="wa-ingest-force" /> force (override <code>ingest.enabled=false</code>)</label>' +
2192
+ '</div>' +
2193
+ '<div style="display:flex;gap:8px;margin-bottom:10px">' +
2194
+ '<input id="wa-ingest-agent" placeholder="agent id (defaults to channels.whatsapp.defaultAgent)" style="flex:1" />' +
2195
+ '<button class="ax-btn ax-btn--primary" onclick="runWhatsAppIngest()">Sweep now</button>' +
2196
+ '</div>' +
2197
+ '<div id="wa-ingest-msg" class="msg" style="margin-bottom:10px"></div>' +
2198
+ '<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">' +
2199
+ '<div><h4 style="font-size:11px;color:var(--ax-muted);margin:0 0 4px;text-transform:uppercase;letter-spacing:0.04em">Chats</h4><div id="wa-chats" style="max-height:200px;overflow-y:auto;border:1px solid var(--ax-border);border-radius:4px;padding:6px;font-family:&quot;IBM Plex Mono&quot;,monospace;font-size:11px">click reload</div></div>' +
2200
+ '<div><h4 style="font-size:11px;color:var(--ax-muted);margin:0 0 4px;text-transform:uppercase;letter-spacing:0.04em">Contacts</h4><div id="wa-contacts" style="max-height:200px;overflow-y:auto;border:1px solid var(--ax-border);border-radius:4px;padding:6px;font-family:&quot;IBM Plex Mono&quot;,monospace;font-size:11px">click reload</div></div>' +
2201
+ '</div>' +
2202
+ '</div>' +
2203
+ '</details>';
1410
2204
  startWhatsAppPolling();
1411
2205
  }
1412
2206
 
2207
+ window.loadWhatsAppLists = async function() {
2208
+ try {
2209
+ const [chats, contacts] = await Promise.all([
2210
+ req('GET', '/api/admin/channels/whatsapp/chats').catch(() => ({chats:[]})),
2211
+ req('GET', '/api/admin/channels/whatsapp/contacts').catch(() => ({contacts:[]})),
2212
+ ]);
2213
+ const cl = $('wa-chats');
2214
+ const cs = chats.chats || [];
2215
+ cl.innerHTML = cs.length ? cs.slice(0, 100).map(c => '<div>' + escapeHtml(c.name || c.jid || '?') + ' <span style="color:var(--ax-muted)">' + (c.kind || '') + '</span></div>').join('') : '<i style="color:var(--ax-muted)">empty</i>';
2216
+ const xl = $('wa-contacts');
2217
+ const xs = contacts.contacts || [];
2218
+ xl.innerHTML = xs.length ? xs.slice(0, 100).map(c => '<div>' + escapeHtml(c.name || c.jid || '?') + '</div>').join('') : '<i style="color:var(--ax-muted)">empty</i>';
2219
+ } catch (e) { showMsg($('wa-ingest-msg'), 'err', e.message); }
2220
+ }
2221
+
2222
+ window.runWhatsAppIngest = async function() {
2223
+ const agent = $('wa-ingest-agent').value.trim();
2224
+ const dryRun = $('wa-ingest-dry').checked;
2225
+ const force = $('wa-ingest-force').checked;
2226
+ const body = { dryRun, force };
2227
+ if (agent) body.agent = agent;
2228
+ showMsg($('wa-ingest-msg'), 'info', 'sweeping\u2026');
2229
+ try {
2230
+ const r = await req('POST', '/api/admin/channels/whatsapp/ingest', body);
2231
+ showMsg($('wa-ingest-msg'), 'ok', 'Done \u2014 ' + (r.summary || JSON.stringify(r).slice(0, 120)));
2232
+ } catch (e) { showMsg($('wa-ingest-msg'), 'err', e.message); }
2233
+ }
2234
+
1413
2235
  const _wa = { timer: null, lastQRSeen: '' };
1414
2236
  function startWhatsAppPolling() {
1415
2237
  stopWhatsAppPolling();
@@ -2519,11 +3341,11 @@ for (const btn of document.querySelectorAll('nav.tabs button')) {
2519
3341
  })();
2520
3342
 
2521
3343
  refresh();
2522
- `;function at(t,e,a=[],n){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(et({peers:a,localToken:n}))}async function nt(t,e){let a=xe();e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(a))}async function ot(t,e,a){try{if(t.method==="GET"&&a==="/api/admin/channels/whatsapp/qr.svg"){await ta(e,"/whatsapp/qr.svg");return}let n=t.method==="GET"?void 0:await qt(t),o={"GET /api/admin/state":()=>Gt(),"POST /api/admin/agents":()=>Xt(n),"PATCH /api/admin/agents":()=>Vt(n),"DELETE /api/admin/agents":()=>Yt(n),"POST /api/admin/agents/access":()=>Ft(n),"POST /api/admin/channels/telegram":()=>Qt(n),"DELETE /api/admin/channels/telegram":()=>ya(n),"POST /api/admin/channels/telegram/toggle":()=>wa(n),"POST /api/admin/crons":()=>ka(n),"DELETE /api/admin/crons":()=>Sa(n),"POST /api/admin/config/raw":()=>Ea(n),"GET /api/admin/tokens":()=>Ut(),"POST /api/admin/tokens":()=>Wt(n),"DELETE /api/admin/tokens":()=>Kt(n),"POST /api/admin/agents/test":()=>Jt(n),"PATCH /api/admin/channels/telegram":()=>Zt(n),"POST /api/admin/channels/slack":()=>ma(n),"POST /api/admin/channels/slack/toggle":()=>ha(n),"POST /api/admin/channels/discord":()=>ga(n),"POST /api/admin/channels/discord/toggle":()=>fa(n),"POST /api/admin/channels/gitlab":()=>va(n),"POST /api/admin/channels/gitlab/toggle":()=>ba(n),"GET /api/admin/channels/whatsapp/state":()=>ea("/whatsapp/state"),"POST /api/admin/crons/preview":()=>xa(n),"POST /api/admin/webhooks":()=>ia(n),"PATCH /api/admin/webhooks":()=>la(n),"DELETE /api/admin/webhooks":()=>ua(n),"POST /api/admin/mesh/peers":()=>da(n),"DELETE /api/admin/mesh/peers":()=>ca(n),"POST /api/admin/mesh/toggle":()=>pa(n),"GET /api/admin/files":()=>aa(t),"GET /api/admin/files/read":()=>na(t),"PUT /api/admin/files":()=>oa(n),"POST /api/admin/files/skill":()=>ra(n),"DELETE /api/admin/files/skill":()=>sa(n)},r=`${t.method} ${a}`,s=o[r];if(!s){fe(e,404,{error:`unknown admin endpoint: ${r}`});return}let i=await s();fe(e,200,i)}catch(n){fe(e,400,{error:n.message||"admin op failed"})}}function fe(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function qt(t){return new Promise((e,a)=>{let n="";t.on("data",o=>{n+=o}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(o){a(o)}}),t.on("error",a)})}function xe(){let t=ne(process.cwd(),"agentx.json");return be(t)?JSON.parse(Bt(t,"utf-8")):null}function Gt(){let t=xe();if(!t)return{exists:!1};let e=Object.entries(t.agents||{}).map(([f,b])=>({id:f,name:b.name,tier:b.tier,model:b.model,mentions:b.mentions||[],workspace:b.workspace,access:b.access||"private",systemPrompt:b.systemPrompt||"",maxConcurrent:b.maxConcurrent??1,maxExecutionMinutes:b.maxExecutionMinutes??20,permissionMode:b.permissionMode||"default"})),a=t.channels?.telegram?.accounts||{},n={enabled:!!t.channels?.telegram?.enabled,accounts:Object.entries(a).map(([f,b])=>({id:f,botUsername:b.botUsername||"",agentBinding:b.agentBinding||"",botTokenRef:b.botToken||""}))},o={enabled:!!t.channels?.slack?.enabled,botTokenRef:t.channels?.slack?.botToken||"",appTokenRef:t.channels?.slack?.appToken||"",agentBinding:t.channels?.slack?.agentBinding||""},r={enabled:!!t.channels?.discord?.enabled,tokenRef:t.channels?.discord?.token||"",agentBinding:t.channels?.discord?.agentBinding||""},s={enabled:!!t.channels?.gitlab?.enabled,host:t.channels?.gitlab?.host||"",webhookPort:t.channels?.gitlab?.webhookPort||18810,tokenRef:t.channels?.gitlab?.token||"",routeCount:Array.isArray(t.channels?.gitlab?.routes)?t.channels.gitlab.routes.length:0,agentMappingCount:Array.isArray(t.channels?.gitlab?.agentMappings)?t.channels.gitlab.agentMappings.length:0},i={enabled:!!t.channels?.whatsapp?.enabled,sessionDir:t.channels?.whatsapp?.sessionDir||".agentx/whatsapp-sessions",routeCount:Array.isArray(t.channels?.whatsapp?.routes)?t.channels.whatsapp.routes.length:0},l=Object.entries(t.crons||{}).map(([f,b])=>({id:f,schedule:b.schedule,agent:b.agent,prompt:(b.prompt||"").slice(0,200),enabled:b.enabled!==!1})),d=Array.isArray(t.webhooks)?t.webhooks.map(f=>({id:f.id,source:f.source,agentId:f.agentId,secretEnv:f.secretEnv||"",description:f.description||"",enabled:f.enabled!==!1})):[],u={enabled:!!t.mesh?.enabled,discovery:t.mesh?.discovery||"static",peers:(t.mesh?.peers||[]).map(f=>({url:f.url,name:f.name,hasToken:!!f.token}))},p=t.dashboard?.daemonUrl||"http://localhost:18800";return{exists:!0,agents:e,telegram:n,slack:o,discord:r,gitlab:s,whatsapp:i,crons:l,webhooks:d,mesh:u,daemonUrl:p,nodeName:t.node?.name}}function Ft(t){let e=String(t?.id||"").trim(),a=t?.access==="public"?"public":"private";if(!e)throw new Error("Agent id is required.");let{summary:n}=S(o=>{if(!o.agents?.[e])throw new Error(`Agent "${e}" not found.`);return o.agents[e].access=a,`agent "${e}" is now ${a}`});return{summary:n}}function Ut(){return new D().list().map(t=>({id:t.id,name:t.name,prefix:t.prefix,scopes:t.scopes,createdAt:t.createdAt,expiresAt:t.expiresAt,revokedAt:t.revokedAt,lastUsedAt:t.lastUsedAt}))}function Wt(t){let e=String(t?.name||"").trim(),a=Array.isArray(t?.scopes)?t.scopes.map(String):[],n=t?.expiresInDays?Number(t.expiresInDays):void 0,{token:o,record:r}=new D().create({name:e,scopes:a,expiresInDays:n});return{summary:`created token "${e}"`,token:o,record:r}}function Kt(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Token id is required.");let a=new D().revoke(e);if(!a)throw new Error(`Token "${e}" not found.`);return{summary:`revoked token ${e} (${a.name})`}}async function Jt(t){let e=String(t?.agent||t?.id||"").trim(),a=String(t?.message||"").trim(),n=String(t?.chatId||"").trim()||`admin-${Date.now()}`;if(!e)throw new Error("Agent id is required.");if(!a)throw new Error("Message is required.");let o="http://127.0.0.1:18800",r;try{let u=$();o=u.dashboard.daemonUrl.replace(/\/+$/,"")||o,r=u.dashboard.token}catch{}let s={"Content-Type":"application/json"};r&&(s.Authorization=`Bearer ${r}`);let i=await fetch(`${o}/task`,{method:"POST",headers:s,body:JSON.stringify({agent:e,message:a,context:{channel:"test-drive",sender:"admin",chatId:n}})}),l=await i.text(),d;try{d=JSON.parse(l)}catch{d={content:l}}if(!i.ok)throw new Error(d?.error||`daemon HTTP ${i.status}`);return{chatId:n,response:d}}function Xt(t){let e=String(t?.id||"").trim(),a=String(t?.name||"").trim(),n=t?.tier==="sdk"?"sdk":"claude-code",o=it(t?.triggerWords),r=t?.model?String(t.model).trim():void 0,s=t?.personality?String(t.personality).trim():void 0;if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Agent id must be lowercase (letters, digits, -, _).");if(!a)throw new Error("Agent name is required.");if(o.length===0)throw new Error("At least one trigger word is required.");let i=ne(process.cwd(),"agents",e),{summary:l}=S(u=>{if(u.agents=u.agents||{},u.agents[e])throw new Error(`Agent "${e}" already exists.`);let p={name:a,workspace:i,tier:n,mentions:o,maxConcurrent:2,permissionMode:"default"};return r&&(p.model=r),s&&(p.systemPrompt=s.slice(0,400)),u.agents[e]=p,`added agent "${e}"`}),d=ne(i,"CLAUDE.md");if(!be(d))try{jt(i,{recursive:!0}),tt(d,`# ${a}
3344
+ `;function Nt(t,e,a=[],n){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Pt({peers:a,localToken:n}))}async function Bt(t,e){let a=Fe();e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(a))}async function jt(t,e,a){try{if(t.method==="GET"&&a==="/api/admin/channels/whatsapp/qr.svg"){await un(e,"/whatsapp/qr.svg");return}let n=t.method==="GET"?void 0:await Qa(t),r={"GET /api/admin/state":()=>Za(),"POST /api/admin/agents":()=>on(n),"PATCH /api/admin/agents":()=>sn(n),"DELETE /api/admin/agents":()=>ln(n),"POST /api/admin/agents/access":()=>en(n),"POST /api/admin/channels/telegram":()=>dn(n),"DELETE /api/admin/channels/telegram":()=>In(n),"POST /api/admin/channels/telegram/toggle":()=>Ln(n),"POST /api/admin/crons":()=>Dn(n),"DELETE /api/admin/crons":()=>Pn(n),"POST /api/admin/config/raw":()=>Rn(n),"GET /api/admin/tokens":()=>tn(),"POST /api/admin/tokens":()=>an(n),"DELETE /api/admin/tokens":()=>nn(n),"POST /api/admin/agents/test":()=>rn(n),"PATCH /api/admin/channels/telegram":()=>cn(n),"POST /api/admin/channels/slack":()=>En(n),"POST /api/admin/channels/slack/toggle":()=>Sn(n),"POST /api/admin/channels/discord":()=>Tn(n),"POST /api/admin/channels/discord/toggle":()=>Cn(n),"POST /api/admin/channels/gitlab":()=>An(n),"POST /api/admin/channels/gitlab/toggle":()=>$n(n),"GET /api/admin/channels/whatsapp/state":()=>Ee("/whatsapp/state"),"GET /api/admin/channels/webrtc/history":()=>Ee("/webrtc/history"),"GET /api/admin/channels/whatsapp/chats":()=>Ee("/whatsapp/chats"),"GET /api/admin/channels/whatsapp/contacts":()=>Ee("/whatsapp/contacts"),"POST /api/admin/channels/whatsapp/ingest":()=>pn("/whatsapp/ingest",n),"POST /api/admin/crons/preview":()=>Mn(n),"POST /api/admin/webhooks":()=>bn(n),"PATCH /api/admin/webhooks":()=>xn(n),"DELETE /api/admin/webhooks":()=>_n(n),"POST /api/admin/mesh/peers":()=>yn(n),"DELETE /api/admin/mesh/peers":()=>wn(n),"POST /api/admin/mesh/toggle":()=>kn(n),"GET /api/admin/files":()=>mn(t),"GET /api/admin/files/read":()=>gn(t),"PUT /api/admin/files":()=>hn(n),"POST /api/admin/files/skill":()=>fn(n),"DELETE /api/admin/files/skill":()=>vn(n),"POST /api/admin/actors":()=>On(n),"DELETE /api/admin/actors":()=>Hn(n),"POST /api/admin/roles":()=>Nn(n),"DELETE /api/admin/roles":()=>Bn(n),"POST /api/admin/roles/grant":()=>jn(n),"POST /api/admin/roles/revoke":()=>zn(n),"POST /api/admin/business/orgchart":()=>qn(n),"DELETE /api/admin/business/orgchart":()=>Fn(n),"POST /api/admin/business/project":()=>Gn(n),"DELETE /api/admin/business/project":()=>Wn(n),"POST /api/admin/business/contact":()=>Un(n),"DELETE /api/admin/business/contact":()=>Kn(n),"POST /api/admin/boards":()=>Jn(n),"DELETE /api/admin/boards":()=>Xn(n),"POST /api/admin/boards/columns":()=>Vn(n),"DELETE /api/admin/boards/columns":()=>Yn(n),"POST /api/admin/notifications":()=>Zn(n),"POST /api/admin/webhooks/triggers":()=>er(n),"POST /api/admin/mesh/health":()=>Qn(n)},o=`${t.method} ${a}`,s=r[o];if(!s){ze(e,404,{error:`unknown admin endpoint: ${o}`});return}let i=await s();ze(e,200,i)}catch(n){ze(e,400,{error:n.message||"admin op failed"})}}function ze(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function Qa(t){return new Promise((e,a)=>{let n="";t.on("data",r=>{n+=r}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(r){a(r)}}),t.on("error",a)})}function Rt(t){let e=oe(process.cwd(),t);if(!Se(e))return[];let a=[];for(let n of Ya(e,{withFileTypes:!0}))if(!(!n.isFile()||!n.name.endsWith(".json")))try{a.push(JSON.parse(Ot(oe(e,n.name),"utf-8")))}catch{}return a}function Fe(){let t=oe(process.cwd(),"agentx.json");return Se(t)?JSON.parse(Ot(t,"utf-8")):null}function Za(){let t=Fe();if(!t)return{exists:!1};let e=Object.entries(t.agents||{}).map(([g,v])=>({id:g,name:v.name,tier:v.tier,model:v.model,mentions:v.mentions||[],workspace:v.workspace,access:v.access||"private",systemPrompt:v.systemPrompt||"",maxConcurrent:v.maxConcurrent??1,maxExecutionMinutes:v.maxExecutionMinutes??20,permissionMode:v.permissionMode||"default"})),a=t.channels?.telegram?.accounts||{},n={enabled:!!t.channels?.telegram?.enabled,accounts:Object.entries(a).map(([g,v])=>({id:g,botUsername:v.botUsername||"",agentBinding:v.agentBinding||"",botTokenRef:v.botToken||""}))},r={enabled:!!t.channels?.slack?.enabled,botTokenRef:t.channels?.slack?.botToken||"",appTokenRef:t.channels?.slack?.appToken||"",agentBinding:t.channels?.slack?.agentBinding||""},o={enabled:!!t.channels?.discord?.enabled,tokenRef:t.channels?.discord?.token||"",agentBinding:t.channels?.discord?.agentBinding||""},s={enabled:!!t.channels?.gitlab?.enabled,host:t.channels?.gitlab?.host||"",webhookPort:t.channels?.gitlab?.webhookPort||18810,tokenRef:t.channels?.gitlab?.token||"",routeCount:Array.isArray(t.channels?.gitlab?.routes)?t.channels.gitlab.routes.length:0,agentMappingCount:Array.isArray(t.channels?.gitlab?.agentMappings)?t.channels.gitlab.agentMappings.length:0},i={enabled:!!t.channels?.whatsapp?.enabled,sessionDir:t.channels?.whatsapp?.sessionDir||".agentx/whatsapp-sessions",routeCount:Array.isArray(t.channels?.whatsapp?.routes)?t.channels.whatsapp.routes.length:0},l=Object.entries(t.crons||{}).map(([g,v])=>({id:g,schedule:v.schedule,agent:v.agent,prompt:(v.prompt||"").slice(0,200),enabled:v.enabled!==!1})),d=Array.isArray(t.webhooks)?t.webhooks.map(g=>({id:g.id,source:g.source,agentId:g.agentId,secretEnv:g.secretEnv||"",description:g.description||"",enabled:g.enabled!==!1,triggers:g.triggers&&typeof g.triggers=="object"?g.triggers:{},defaultWorkflow:g.defaultWorkflow||""})):[],c={enabled:!!t.mesh?.enabled,discovery:t.mesh?.discovery||"static",peers:(t.mesh?.peers||[]).map(g=>({url:g.url,name:g.name,hasToken:!!g.token})),healthCheck:{interval:t.mesh?.healthCheck?.interval??60,timeout:t.mesh?.healthCheck?.timeout??10}},u=t.dashboard?.daemonUrl||"http://localhost:18800",h=Rt(".agentx/actors").map(g=>({id:g.id,name:g.name,email:g.email,channels:Array.isArray(g.channels)?g.channels.map(v=>({channel:v.channel,handle:v.handle,preferredForTasks:!!v.preferredForTasks})):[],timezone:g.timezone})).sort((g,v)=>g.id.localeCompare(v.id)),x=Rt(".agentx/roles").map(g=>({id:g.id,name:g.name,members:Array.isArray(g.members)?g.members:[],assignmentStrategy:g.assignmentStrategy||"first-available"})).sort((g,v)=>g.id.localeCompare(v.id)),p=t.business||{},m={enabled:!!p.enabled,timezone:p.timezone||"UTC",orgChart:p.orgChart||{},projects:Array.isArray(p.projects)?p.projects:[],contactMap:Array.isArray(p.contactMap)?p.contactMap:[]},f=t.notifications||{},w={longTaskThreshold:f.longTaskThreshold??30,destination:f.destination||null,on:{taskComplete:f.on?.taskComplete!==!1,taskError:f.on?.taskError!==!1,taskQueued:!!f.on?.taskQueued}},b=(Array.isArray(t.boards)?t.boards:[]).map(g=>({id:g.id,name:g.name,source:g.source||{type:"gitlab",projects:[]},primaryToolLabel:g.primaryToolLabel||"",timeRangeDays:g.timeRangeDays??30,closedWindowDays:g.closedWindowDays??30,columns:Array.isArray(g.columns)?g.columns:[]}));return{exists:!0,agents:e,telegram:n,slack:r,discord:o,gitlab:s,whatsapp:i,crons:l,webhooks:d,mesh:c,daemonUrl:u,nodeName:t.node?.name,actors:h,roles:x,business:m,boards:b,notifications:w}}function en(t){let e=String(t?.id||"").trim(),a=t?.access==="public"?"public":"private";if(!e)throw new Error("Agent id is required.");let{summary:n}=S(r=>{if(!r.agents?.[e])throw new Error(`Agent "${e}" not found.`);return r.agents[e].access=a,`agent "${e}" is now ${a}`});return{summary:n}}function tn(){return new U().list().map(t=>({id:t.id,name:t.name,prefix:t.prefix,scopes:t.scopes,createdAt:t.createdAt,expiresAt:t.expiresAt,revokedAt:t.revokedAt,lastUsedAt:t.lastUsedAt}))}function an(t){let e=String(t?.name||"").trim(),a=Array.isArray(t?.scopes)?t.scopes.map(String):[],n=t?.expiresInDays?Number(t.expiresInDays):void 0,{token:r,record:o}=new U().create({name:e,scopes:a,expiresInDays:n});return{summary:`created token "${e}"`,token:r,record:o}}function nn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Token id is required.");let a=new U().revoke(e);if(!a)throw new Error(`Token "${e}" not found.`);return{summary:`revoked token ${e} (${a.name})`}}async function rn(t){let e=String(t?.agent||t?.id||"").trim(),a=String(t?.message||"").trim(),n=String(t?.chatId||"").trim()||`admin-${Date.now()}`;if(!e)throw new Error("Agent id is required.");if(!a)throw new Error("Message is required.");let r="http://127.0.0.1:18800",o;try{let c=B();r=c.dashboard.daemonUrl.replace(/\/+$/,"")||r,o=c.dashboard.token}catch{}let s={"Content-Type":"application/json"};o&&(s.Authorization=`Bearer ${o}`);let i=await fetch(`${r}/task`,{method:"POST",headers:s,body:JSON.stringify({agent:e,message:a,context:{channel:"test-drive",sender:"admin",chatId:n}})}),l=await i.text(),d;try{d=JSON.parse(l)}catch{d={content:l}}if(!i.ok)throw new Error(d?.error||`daemon HTTP ${i.status}`);return{chatId:n,response:d}}function on(t){let e=String(t?.id||"").trim(),a=String(t?.name||"").trim(),n=t?.tier==="sdk"?"sdk":"claude-code",r=qt(t?.triggerWords),o=t?.model?String(t.model).trim():void 0,s=t?.personality?String(t.personality).trim():void 0;if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Agent id must be lowercase (letters, digits, -, _).");if(!a)throw new Error("Agent name is required.");if(r.length===0)throw new Error("At least one trigger word is required.");let i=oe(process.cwd(),"agents",e),{summary:l}=S(c=>{if(c.agents=c.agents||{},c.agents[e])throw new Error(`Agent "${e}" already exists.`);let u={name:a,workspace:i,tier:n,mentions:r,maxConcurrent:2,permissionMode:"default"};return o&&(u.model=o),s&&(u.systemPrompt=s.slice(0,400)),c.agents[e]=u,`added agent "${e}"`}),d=oe(i,"CLAUDE.md");if(!Se(d))try{Xa(i,{recursive:!0}),Ht(d,`# ${a}
2523
3345
 
2524
3346
  ${s||"You are "+a+"."}
2525
- `)}catch{}return{summary:l}}function Vt(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Agent id is required.");let a=t?.patch||{},{summary:n}=S(o=>{if(!o.agents?.[e])throw new Error(`Agent "${e}" not found.`);let r=o.agents[e];return typeof a.name=="string"&&a.name.trim()&&(r.name=a.name.trim()),typeof a.model=="string"&&(r.model=a.model.trim()||void 0),typeof a.tier=="string"&&["claude-code","sdk","orchestrator"].includes(a.tier)&&(r.tier=a.tier),typeof a.systemPrompt=="string"&&(r.systemPrompt=a.systemPrompt.trim()||void 0),Array.isArray(a.mentions)?r.mentions=a.mentions.map(s=>String(s).trim()).filter(Boolean):typeof a.triggerWords=="string"&&(r.mentions=it(a.triggerWords)),typeof a.maxConcurrent=="number"&&a.maxConcurrent>=1&&(r.maxConcurrent=a.maxConcurrent),typeof a.maxExecutionMinutes=="number"&&a.maxExecutionMinutes>=1&&a.maxExecutionMinutes<=240&&(r.maxExecutionMinutes=a.maxExecutionMinutes),typeof a.permissionMode=="string"&&(r.permissionMode=a.permissionMode),(a.access==="public"||a.access==="private")&&(r.access=a.access),`updated agent "${e}"`});return{summary:n}}function Yt(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Agent id is required.");let{summary:a}=S(n=>{if(!n.agents||!n.agents[e])throw new Error(`Agent "${e}" not found.`);if(delete n.agents[e],n.channels?.telegram?.accounts)for(let o of Object.keys(n.channels.telegram.accounts))n.channels.telegram.accounts[o]?.agentBinding===e&&(n.channels.telegram.accounts[o].agentBinding="");return`removed agent "${e}"`});return{summary:a}}function Qt(t){let e=String(t?.id||"").trim(),a=String(t?.agentBinding||"").trim(),n=String(t?.botUsername||"").trim(),o=String(t?.botTokenEnv||"").trim();if(!e)throw new Error("Account id is required.");if(!a)throw new Error("Choose an agent to bind this account to.");if(!o)throw new Error("Bot token env-var name is required (e.g. TG_SUPPORT_BOT_TOKEN).");let{summary:r}=S(s=>{if(s.channels=s.channels||{},s.channels.telegram=s.channels.telegram||{enabled:!0,accounts:{},policy:{dm:"pair",group:"mention-required"}},s.channels.telegram.enabled=!0,s.channels.telegram.accounts=s.channels.telegram.accounts||{},s.channels.telegram.accounts[e])throw new Error(`Telegram account "${e}" already exists.`);if(!s.agents?.[a])throw new Error(`Unknown agent "${a}".`);return s.channels.telegram.accounts[e]={botToken:"${"+o+"}",botUsername:n,agentBinding:a},`added telegram account "${e}"`});return{summary:r,hint:`Add ${o}=<bot-token> to your .env file.`}}function Zt(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Account id is required.");let a=t?.patch||{},{summary:n}=S(o=>{let r=o.channels?.telegram?.accounts?.[e];if(!r)throw new Error(`Account "${e}" not found.`);if(typeof a.agentBinding=="string"&&a.agentBinding.trim()){if(!o.agents?.[a.agentBinding])throw new Error(`Unknown agent "${a.agentBinding}".`);r.agentBinding=a.agentBinding.trim()}return typeof a.botUsername=="string"&&(r.botUsername=a.botUsername.trim()),typeof a.botTokenEnv=="string"&&a.botTokenEnv.trim()&&(r.botToken="${"+a.botTokenEnv.trim()+"}"),`updated telegram account "${e}"`});return{summary:n}}async function ea(t){let{url:e,headers:a}=rt(),n=await fetch(e+t,{headers:a}),o=await n.text();if(!n.ok)throw new Error(`daemon ${n.status}: ${o.slice(0,200)}`);try{return JSON.parse(o)}catch{return{raw:o}}}async function ta(t,e){try{let{url:a,headers:n}=rt(),o=await fetch(a+e,{headers:n}),r=await o.text();t.writeHead(o.status,{"Content-Type":o.headers.get("content-type")||"image/svg+xml","Cache-Control":"no-store"}),t.end(r)}catch(a){t.writeHead(502,{"Content-Type":"text/plain; charset=utf-8"}),t.end("daemon unreachable: "+(a?.message||"unknown"))}}function rt(){let t="http://127.0.0.1:18800",e={};try{let a=$();t=a.dashboard.daemonUrl?.replace(/\/+$/,"")||t,a.dashboard.token&&(e.Authorization=`Bearer ${a.dashboard.token}`)}catch{}return{url:t,headers:e}}function X(t){let a=xe()?.agents?.[t];if(!a)throw new Error(`Unknown agent "${t}".`);if(!a.workspace)throw new Error(`Agent "${t}" has no workspace configured.`);return a.workspace}function ve(t,e){try{return new URL(t.url||"/","http://localhost").searchParams.get(e)||""}catch{return""}}function aa(t){let e=ve(t,"agent").trim();if(!e)throw new Error("agent= query param is required.");let a=F(X(e));return{agentId:e,...a}}function na(t){let e=ve(t,"agent").trim(),a=ve(t,"path").trim();if(!e||!a)throw new Error("agent= and path= query params are required.");return Q(X(e),a)}function oa(t){let e=String(t?.agent||"").trim(),a=String(t?.path||"").trim(),n=typeof t?.content=="string"?t.content:"";if(!e||!a)throw new Error("agent and path are required.");let o=Z(X(e),a,n);return{summary:`saved ${a} (${o.bytes} bytes)`,...o}}function ra(t){let e=String(t?.agent||"").trim(),a=String(t?.slug||"").trim(),n=t?.title?String(t.title):void 0,o=t?.content?String(t.content):void 0;if(!e)throw new Error("agent is required.");let r=ee(X(e),a,{title:n,content:o});return{summary:`added skill "${a}"`,...r}}function sa(t){let e=String(t?.agent||"").trim(),a=String(t?.slug||"").trim();if(!e||!a)throw new Error("agent and slug are required.");let n=te(X(e),a);return{summary:`removed skill "${a}"`,...n}}var st=["gitlab","github","sentry","stripe","discord","slack","custom"];function ia(t){let e=String(t?.id||"").trim(),a=String(t?.source||"").trim(),n=String(t?.agentId||"").trim(),o=String(t?.secretEnv||"").trim(),r=String(t?.description||"").trim();if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Webhook id must be lowercase (letters, digits, -, _).");if(!st.includes(a))throw new Error(`Unknown source: ${a}`);if(!n)throw new Error("Pick an agent to bind the webhook to.");let{summary:s}=S(i=>{if(!i.agents?.[n])throw new Error(`Unknown agent "${n}".`);if(i.webhooks=Array.isArray(i.webhooks)?i.webhooks:[],i.webhooks.find(d=>d.id===e))throw new Error(`Webhook "${e}" already exists.`);let l={id:e,source:a,agentId:n,enabled:!0};return o&&(l.secretEnv=o),r&&(l.description=r),i.webhooks.push(l),`added webhook "${e}" (${a} \u2192 ${n})`});return{summary:s}}function la(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Webhook id is required.");let a=t?.patch||{},{summary:n}=S(o=>{let r=(o.webhooks||[]).find(s=>s.id===e);if(!r)throw new Error(`Webhook "${e}" not found.`);if(typeof a.source=="string"&&st.includes(a.source)&&(r.source=a.source),typeof a.agentId=="string"&&a.agentId.trim()){if(!o.agents?.[a.agentId])throw new Error(`Unknown agent "${a.agentId}".`);r.agentId=a.agentId.trim()}return typeof a.secretEnv=="string"&&(r.secretEnv=a.secretEnv.trim()||void 0),typeof a.description=="string"&&(r.description=a.description.trim()||void 0),typeof a.enabled=="boolean"&&(r.enabled=a.enabled),`updated webhook "${e}"`});return{summary:n}}function da(t){let e=String(t?.url||"").trim().replace(/\/+$/,""),a=String(t?.name||"").trim(),n=String(t?.token||"").trim()||void 0;if(!e||!/^https?:\/\//.test(e))throw new Error("Peer URL must start with http:// or https://");if(!a)throw new Error("Peer name is required.");let{summary:o}=S(r=>{if(r.mesh=r.mesh||{enabled:!0,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},r.mesh.enabled=!0,r.mesh.peers=Array.isArray(r.mesh.peers)?r.mesh.peers:[],r.mesh.peers.find(i=>i.url===e))throw new Error(`Peer at ${e} already registered.`);let s={url:e,name:a};return n&&(s.token=n),r.mesh.peers.push(s),`added mesh peer "${a}"`});return{summary:o}}function ca(t){let e=String(t?.url||"").trim().replace(/\/+$/,"");if(!e)throw new Error("Peer URL is required.");let{summary:a}=S(n=>{let o=(n.mesh?.peers||[]).length;if(n.mesh=n.mesh||{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},n.mesh.peers=(n.mesh.peers||[]).filter(r=>r.url.replace(/\/+$/,"")!==e),n.mesh.peers.length===o)throw new Error(`Peer at ${e} not found.`);return`removed mesh peer ${e}`});return{summary:a}}function pa(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.mesh=n.mesh||{enabled:e,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},n.mesh.enabled=e,`mesh ${e?"enabled":"disabled"}`));return{summary:a}}function ua(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Webhook id is required.");let{summary:a}=S(n=>{let o=(n.webhooks||[]).length;if(n.webhooks=(n.webhooks||[]).filter(r=>r.id!==e),n.webhooks.length===o)throw new Error(`Webhook "${e}" not found.`);return`removed webhook "${e}"`});return{summary:a}}function ma(t){let e=String(t?.botTokenEnv||"").trim(),a=String(t?.appTokenEnv||"").trim(),n=String(t?.agentBinding||"").trim();if(!e)throw new Error("Bot token env-var name is required (e.g. SLACK_BOT_TOKEN).");if(!a)throw new Error("App-level token env-var name is required (e.g. SLACK_APP_TOKEN).");let{summary:o}=S(r=>{if(r.channels=r.channels||{},r.channels.slack=r.channels.slack||{enabled:!1},r.channels.slack.enabled=!0,r.channels.slack.botToken="${"+e+"}",r.channels.slack.appToken="${"+a+"}",n){if(!r.agents?.[n])throw new Error(`Unknown agent "${n}".`);r.channels.slack.agentBinding=n}return"configured slack connector"});return{summary:o,hint:`Add ${e}=xoxb-\u2026 and ${a}=xapp-\u2026 to your .env.`}}function ha(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.slack=n.channels.slack||{enabled:!1},n.channels.slack.enabled=e,`slack ${e?"enabled":"disabled"}`));return{summary:a}}function ga(t){let e=String(t?.tokenEnv||"").trim(),a=String(t?.agentBinding||"").trim();if(!e)throw new Error("Bot token env-var name is required (e.g. DISCORD_BOT_TOKEN).");let{summary:n}=S(o=>{if(o.channels=o.channels||{},o.channels.discord=o.channels.discord||{enabled:!1},o.channels.discord.enabled=!0,o.channels.discord.token="${"+e+"}",a){if(!o.agents?.[a])throw new Error(`Unknown agent "${a}".`);o.channels.discord.agentBinding=a}return"configured discord connector"});return{summary:n,hint:`Add ${e}=<bot-token> to your .env.`}}function fa(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.discord=n.channels.discord||{enabled:!1},n.channels.discord.enabled=e,`discord ${e?"enabled":"disabled"}`));return{summary:a}}function va(t){let e=String(t?.host||"").trim().replace(/\/+$/,""),a=String(t?.tokenEnv||"").trim(),n=parseInt(String(t?.webhookPort||"18810"),10);if(!e||!/^https?:\/\//.test(e))throw new Error("Host must start with http:// or https://");if(!a)throw new Error("Admin token env-var name is required (e.g. GITLAB_TOKEN).");if(!Number.isFinite(n)||n<1||n>65535)throw new Error("Webhook port must be between 1 and 65535.");let{summary:o}=S(r=>(r.channels=r.channels||{},r.channels.gitlab=r.channels.gitlab||{enabled:!1,routes:[],agentMappings:[]},r.channels.gitlab.enabled=!0,r.channels.gitlab.host=e,r.channels.gitlab.token="${"+a+"}",r.channels.gitlab.webhookPort=n,"configured gitlab connector"));return{summary:o,hint:`Add ${a}=<personal-access-token> to your .env. Per-project routes + per-agent tokens stay in the Advanced JSON editor.`}}function ba(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.gitlab=n.channels.gitlab||{enabled:!1,routes:[],agentMappings:[]},n.channels.gitlab.enabled=e,`gitlab ${e?"enabled":"disabled"}`));return{summary:a}}async function xa(t){let e=String(t?.schedule||"").trim();if(!e)throw new Error("schedule is required");let{getNextCronDate:a}=await import("./scheduler-XRM2RJIV.js"),n;try{n=(await import("cronstrue")).default.toString(e,{use24HourTimeFormat:!1})}catch(i){throw new Error(`Invalid cron: ${i.message||"unknown"}`)}let o=Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC",r=[],s=new Date;try{for(let i=0;i<3;i++)s=a(e,s,o),r.push(s.toISOString())}catch(i){throw new Error(`Cron preview failed: ${i.message}`)}return{human:n,next:r,timezone:o}}function ya(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Account id is required.");let{summary:a}=S(n=>{if(!n.channels?.telegram?.accounts?.[e])throw new Error(`Account "${e}" not found.`);return delete n.channels.telegram.accounts[e],`removed telegram account "${e}"`});return{summary:a}}function wa(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.telegram=n.channels.telegram||{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},n.channels.telegram.enabled=e,`telegram ${e?"enabled":"disabled"}`));return{summary:a}}function ka(t){let e=String(t?.id||"").trim(),a=String(t?.schedule||"").trim(),n=String(t?.agent||"").trim(),o=String(t?.prompt||"").trim();if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Cron id must be lowercase (letters, digits, -, _).");if(!a)throw new Error("Cron expression is required (e.g. '0 9 * * 1' for Mondays at 9am).");if(!n)throw new Error("Pick an agent to run this schedule.");if(!o)throw new Error("A prompt is required (what should the agent do?).");let{summary:r}=S(s=>{if(s.crons=s.crons||{},s.crons[e])throw new Error(`Cron "${e}" already exists.`);if(!s.agents?.[n])throw new Error(`Unknown agent "${n}".`);return s.crons[e]={schedule:a,agent:n,prompt:o,enabled:!0},`added cron "${e}"`});return{summary:r}}function Sa(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Cron id is required.");let{summary:a}=S(n=>{if(!n.crons?.[e])throw new Error(`Cron "${e}" not found.`);return delete n.crons[e],`removed cron "${e}"`});return{summary:a}}function Ea(t){let e=String(t?.raw||"");if(!e.trim())throw new Error("Config body is empty.");let a;try{a=JSON.parse(e)}catch(r){throw new Error(`Invalid JSON: ${r.message}`)}let n=ne(process.cwd(),"agentx.json"),o=`${n}.bak.${Date.now()}`;if(be(n))try{zt(n,o)}catch{}tt(n,JSON.stringify(a,null,2)+`
2526
- `,"utf-8");try{let s=$().dashboard.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800";fetch(`${s}/reload`,{method:"POST"}).catch(()=>null)}catch{}return{summary:"agentx.json replaced",backupPath:o}}function it(t){return String(t||"").split(/[,\s]+/).map(e=>e.trim()).filter(Boolean).filter((e,a,n)=>n.indexOf(e)===a)}import{resolve as Ca}from"path";function lt(t={}){let e=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",a=`<div id="msg" class="msg"></div>
3347
+ `)}catch{}return{summary:l}}function sn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Agent id is required.");let a=t?.patch||{},{summary:n}=S(r=>{if(!r.agents?.[e])throw new Error(`Agent "${e}" not found.`);let o=r.agents[e];return typeof a.name=="string"&&a.name.trim()&&(o.name=a.name.trim()),typeof a.model=="string"&&(o.model=a.model.trim()||void 0),typeof a.tier=="string"&&["claude-code","sdk","orchestrator"].includes(a.tier)&&(o.tier=a.tier),typeof a.systemPrompt=="string"&&(o.systemPrompt=a.systemPrompt.trim()||void 0),Array.isArray(a.mentions)?o.mentions=a.mentions.map(s=>String(s).trim()).filter(Boolean):typeof a.triggerWords=="string"&&(o.mentions=qt(a.triggerWords)),typeof a.maxConcurrent=="number"&&a.maxConcurrent>=1&&(o.maxConcurrent=a.maxConcurrent),typeof a.maxExecutionMinutes=="number"&&a.maxExecutionMinutes>=1&&a.maxExecutionMinutes<=240&&(o.maxExecutionMinutes=a.maxExecutionMinutes),typeof a.permissionMode=="string"&&(o.permissionMode=a.permissionMode),(a.access==="public"||a.access==="private")&&(o.access=a.access),`updated agent "${e}"`});return{summary:n}}function ln(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Agent id is required.");let{summary:a}=S(n=>{if(!n.agents||!n.agents[e])throw new Error(`Agent "${e}" not found.`);if(delete n.agents[e],n.channels?.telegram?.accounts)for(let r of Object.keys(n.channels.telegram.accounts))n.channels.telegram.accounts[r]?.agentBinding===e&&(n.channels.telegram.accounts[r].agentBinding="");return`removed agent "${e}"`});return{summary:a}}function dn(t){let e=String(t?.id||"").trim(),a=String(t?.agentBinding||"").trim(),n=String(t?.botUsername||"").trim(),r=String(t?.botTokenEnv||"").trim();if(!e)throw new Error("Account id is required.");if(!a)throw new Error("Choose an agent to bind this account to.");if(!r)throw new Error("Bot token env-var name is required (e.g. TG_SUPPORT_BOT_TOKEN).");let{summary:o}=S(s=>{if(s.channels=s.channels||{},s.channels.telegram=s.channels.telegram||{enabled:!0,accounts:{},policy:{dm:"pair",group:"mention-required"}},s.channels.telegram.enabled=!0,s.channels.telegram.accounts=s.channels.telegram.accounts||{},s.channels.telegram.accounts[e])throw new Error(`Telegram account "${e}" already exists.`);if(!s.agents?.[a])throw new Error(`Unknown agent "${a}".`);return s.channels.telegram.accounts[e]={botToken:"${"+r+"}",botUsername:n,agentBinding:a},`added telegram account "${e}"`});return{summary:o,hint:`Add ${r}=<bot-token> to your .env file.`}}function cn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Account id is required.");let a=t?.patch||{},{summary:n}=S(r=>{let o=r.channels?.telegram?.accounts?.[e];if(!o)throw new Error(`Account "${e}" not found.`);if(typeof a.agentBinding=="string"&&a.agentBinding.trim()){if(!r.agents?.[a.agentBinding])throw new Error(`Unknown agent "${a.agentBinding}".`);o.agentBinding=a.agentBinding.trim()}return typeof a.botUsername=="string"&&(o.botUsername=a.botUsername.trim()),typeof a.botTokenEnv=="string"&&a.botTokenEnv.trim()&&(o.botToken="${"+a.botTokenEnv.trim()+"}"),`updated telegram account "${e}"`});return{summary:n}}async function Ee(t){let{url:e,headers:a}=Ge(),n=await fetch(e+t,{headers:a}),r=await n.text();if(!n.ok)throw new Error(`daemon ${n.status}: ${r.slice(0,200)}`);try{return JSON.parse(r)}catch{return{raw:r}}}async function pn(t,e){let{url:a,headers:n}=Ge(),r=await fetch(a+t,{method:"POST",headers:{...n,"Content-Type":"application/json"},body:JSON.stringify(e||{})}),o=await r.text();if(!r.ok)throw new Error(`daemon ${r.status}: ${o.slice(0,200)}`);try{return JSON.parse(o)}catch{return{raw:o}}}async function un(t,e){try{let{url:a,headers:n}=Ge(),r=await fetch(a+e,{headers:n}),o=await r.text();t.writeHead(r.status,{"Content-Type":r.headers.get("content-type")||"image/svg+xml","Cache-Control":"no-store"}),t.end(o)}catch(a){t.writeHead(502,{"Content-Type":"text/plain; charset=utf-8"}),t.end("daemon unreachable: "+(a?.message||"unknown"))}}function Ge(){let t="http://127.0.0.1:18800",e={};try{let a=B();t=a.dashboard.daemonUrl?.replace(/\/+$/,"")||t,a.dashboard.token&&(e.Authorization=`Bearer ${a.dashboard.token}`)}catch{}return{url:t,headers:e}}function ue(t){let a=Fe()?.agents?.[t];if(!a)throw new Error(`Unknown agent "${t}".`);if(!a.workspace)throw new Error(`Agent "${t}" has no workspace configured.`);return a.workspace}function qe(t,e){try{return new URL(t.url||"/","http://localhost").searchParams.get(e)||""}catch{return""}}function mn(t){let e=qe(t,"agent").trim();if(!e)throw new Error("agent= query param is required.");let a=re(ue(e));return{agentId:e,...a}}function gn(t){let e=qe(t,"agent").trim(),a=qe(t,"path").trim();if(!e||!a)throw new Error("agent= and path= query params are required.");return xe(ue(e),a)}function hn(t){let e=String(t?.agent||"").trim(),a=String(t?.path||"").trim(),n=typeof t?.content=="string"?t.content:"";if(!e||!a)throw new Error("agent and path are required.");let r=ye(ue(e),a,n);return{summary:`saved ${a} (${r.bytes} bytes)`,...r}}function fn(t){let e=String(t?.agent||"").trim(),a=String(t?.slug||"").trim(),n=t?.title?String(t.title):void 0,r=t?.content?String(t.content):void 0;if(!e)throw new Error("agent is required.");let o=we(ue(e),a,{title:n,content:r});return{summary:`added skill "${a}"`,...o}}function vn(t){let e=String(t?.agent||"").trim(),a=String(t?.slug||"").trim();if(!e||!a)throw new Error("agent and slug are required.");let n=ke(ue(e),a);return{summary:`removed skill "${a}"`,...n}}var zt=["gitlab","github","sentry","stripe","discord","slack","custom"];function bn(t){let e=String(t?.id||"").trim(),a=String(t?.source||"").trim(),n=String(t?.agentId||"").trim(),r=String(t?.secretEnv||"").trim(),o=String(t?.description||"").trim();if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Webhook id must be lowercase (letters, digits, -, _).");if(!zt.includes(a))throw new Error(`Unknown source: ${a}`);if(!n)throw new Error("Pick an agent to bind the webhook to.");let{summary:s}=S(i=>{if(!i.agents?.[n])throw new Error(`Unknown agent "${n}".`);if(i.webhooks=Array.isArray(i.webhooks)?i.webhooks:[],i.webhooks.find(d=>d.id===e))throw new Error(`Webhook "${e}" already exists.`);let l={id:e,source:a,agentId:n,enabled:!0};return r&&(l.secretEnv=r),o&&(l.description=o),i.webhooks.push(l),`added webhook "${e}" (${a} \u2192 ${n})`});return{summary:s}}function xn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Webhook id is required.");let a=t?.patch||{},{summary:n}=S(r=>{let o=(r.webhooks||[]).find(s=>s.id===e);if(!o)throw new Error(`Webhook "${e}" not found.`);if(typeof a.source=="string"&&zt.includes(a.source)&&(o.source=a.source),typeof a.agentId=="string"&&a.agentId.trim()){if(!r.agents?.[a.agentId])throw new Error(`Unknown agent "${a.agentId}".`);o.agentId=a.agentId.trim()}return typeof a.secretEnv=="string"&&(o.secretEnv=a.secretEnv.trim()||void 0),typeof a.description=="string"&&(o.description=a.description.trim()||void 0),typeof a.enabled=="boolean"&&(o.enabled=a.enabled),`updated webhook "${e}"`});return{summary:n}}function yn(t){let e=String(t?.url||"").trim().replace(/\/+$/,""),a=String(t?.name||"").trim(),n=String(t?.token||"").trim()||void 0;if(!e||!/^https?:\/\//.test(e))throw new Error("Peer URL must start with http:// or https://");if(!a)throw new Error("Peer name is required.");let{summary:r}=S(o=>{if(o.mesh=o.mesh||{enabled:!0,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},o.mesh.enabled=!0,o.mesh.peers=Array.isArray(o.mesh.peers)?o.mesh.peers:[],o.mesh.peers.find(i=>i.url===e))throw new Error(`Peer at ${e} already registered.`);let s={url:e,name:a};return n&&(s.token=n),o.mesh.peers.push(s),`added mesh peer "${a}"`});return{summary:r}}function wn(t){let e=String(t?.url||"").trim().replace(/\/+$/,"");if(!e)throw new Error("Peer URL is required.");let{summary:a}=S(n=>{let r=(n.mesh?.peers||[]).length;if(n.mesh=n.mesh||{enabled:!1,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},n.mesh.peers=(n.mesh.peers||[]).filter(o=>o.url.replace(/\/+$/,"")!==e),n.mesh.peers.length===r)throw new Error(`Peer at ${e} not found.`);return`removed mesh peer ${e}`});return{summary:a}}function kn(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.mesh=n.mesh||{enabled:e,peers:[],discovery:"static",healthCheck:{interval:60,timeout:10}},n.mesh.enabled=e,`mesh ${e?"enabled":"disabled"}`));return{summary:a}}function _n(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Webhook id is required.");let{summary:a}=S(n=>{let r=(n.webhooks||[]).length;if(n.webhooks=(n.webhooks||[]).filter(o=>o.id!==e),n.webhooks.length===r)throw new Error(`Webhook "${e}" not found.`);return`removed webhook "${e}"`});return{summary:a}}function En(t){let e=String(t?.botTokenEnv||"").trim(),a=String(t?.appTokenEnv||"").trim(),n=String(t?.agentBinding||"").trim();if(!e)throw new Error("Bot token env-var name is required (e.g. SLACK_BOT_TOKEN).");if(!a)throw new Error("App-level token env-var name is required (e.g. SLACK_APP_TOKEN).");let{summary:r}=S(o=>{if(o.channels=o.channels||{},o.channels.slack=o.channels.slack||{enabled:!1},o.channels.slack.enabled=!0,o.channels.slack.botToken="${"+e+"}",o.channels.slack.appToken="${"+a+"}",n){if(!o.agents?.[n])throw new Error(`Unknown agent "${n}".`);o.channels.slack.agentBinding=n}return"configured slack connector"});return{summary:r,hint:`Add ${e}=xoxb-\u2026 and ${a}=xapp-\u2026 to your .env.`}}function Sn(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.slack=n.channels.slack||{enabled:!1},n.channels.slack.enabled=e,`slack ${e?"enabled":"disabled"}`));return{summary:a}}function Tn(t){let e=String(t?.tokenEnv||"").trim(),a=String(t?.agentBinding||"").trim();if(!e)throw new Error("Bot token env-var name is required (e.g. DISCORD_BOT_TOKEN).");let{summary:n}=S(r=>{if(r.channels=r.channels||{},r.channels.discord=r.channels.discord||{enabled:!1},r.channels.discord.enabled=!0,r.channels.discord.token="${"+e+"}",a){if(!r.agents?.[a])throw new Error(`Unknown agent "${a}".`);r.channels.discord.agentBinding=a}return"configured discord connector"});return{summary:n,hint:`Add ${e}=<bot-token> to your .env.`}}function Cn(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.discord=n.channels.discord||{enabled:!1},n.channels.discord.enabled=e,`discord ${e?"enabled":"disabled"}`));return{summary:a}}function An(t){let e=String(t?.host||"").trim().replace(/\/+$/,""),a=String(t?.tokenEnv||"").trim(),n=parseInt(String(t?.webhookPort||"18810"),10);if(!e||!/^https?:\/\//.test(e))throw new Error("Host must start with http:// or https://");if(!a)throw new Error("Admin token env-var name is required (e.g. GITLAB_TOKEN).");if(!Number.isFinite(n)||n<1||n>65535)throw new Error("Webhook port must be between 1 and 65535.");let{summary:r}=S(o=>(o.channels=o.channels||{},o.channels.gitlab=o.channels.gitlab||{enabled:!1,routes:[],agentMappings:[]},o.channels.gitlab.enabled=!0,o.channels.gitlab.host=e,o.channels.gitlab.token="${"+a+"}",o.channels.gitlab.webhookPort=n,"configured gitlab connector"));return{summary:r,hint:`Add ${a}=<personal-access-token> to your .env. Per-project routes + per-agent tokens stay in the Advanced JSON editor.`}}function $n(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.gitlab=n.channels.gitlab||{enabled:!1,routes:[],agentMappings:[]},n.channels.gitlab.enabled=e,`gitlab ${e?"enabled":"disabled"}`));return{summary:a}}async function Mn(t){let e=String(t?.schedule||"").trim();if(!e)throw new Error("schedule is required");let{getNextCronDate:a}=await import("./scheduler-XVFIK4XH.js"),n;try{n=(await import("cronstrue")).default.toString(e,{use24HourTimeFormat:!1})}catch(i){throw new Error(`Invalid cron: ${i.message||"unknown"}`)}let r=Intl.DateTimeFormat().resolvedOptions().timeZone||"UTC",o=[],s=new Date;try{for(let i=0;i<3;i++)s=a(e,s,r),o.push(s.toISOString())}catch(i){throw new Error(`Cron preview failed: ${i.message}`)}return{human:n,next:o,timezone:r}}function In(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Account id is required.");let{summary:a}=S(n=>{if(!n.channels?.telegram?.accounts?.[e])throw new Error(`Account "${e}" not found.`);return delete n.channels.telegram.accounts[e],`removed telegram account "${e}"`});return{summary:a}}function Ln(t){let e=!!t?.enabled,{summary:a}=S(n=>(n.channels=n.channels||{},n.channels.telegram=n.channels.telegram||{enabled:!1,accounts:{},policy:{dm:"pair",group:"mention-required"}},n.channels.telegram.enabled=e,`telegram ${e?"enabled":"disabled"}`));return{summary:a}}function Dn(t){let e=String(t?.id||"").trim(),a=String(t?.schedule||"").trim(),n=String(t?.agent||"").trim(),r=String(t?.prompt||"").trim();if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Cron id must be lowercase (letters, digits, -, _).");if(!a)throw new Error("Cron expression is required (e.g. '0 9 * * 1' for Mondays at 9am).");if(!n)throw new Error("Pick an agent to run this schedule.");if(!r)throw new Error("A prompt is required (what should the agent do?).");let{summary:o}=S(s=>{if(s.crons=s.crons||{},s.crons[e])throw new Error(`Cron "${e}" already exists.`);if(!s.agents?.[n])throw new Error(`Unknown agent "${n}".`);return s.crons[e]={schedule:a,agent:n,prompt:r,enabled:!0},`added cron "${e}"`});return{summary:o}}function Pn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("Cron id is required.");let{summary:a}=S(n=>{if(!n.crons?.[e])throw new Error(`Cron "${e}" not found.`);return delete n.crons[e],`removed cron "${e}"`});return{summary:a}}function Rn(t){let e=String(t?.raw||"");if(!e.trim())throw new Error("Config body is empty.");let a;try{a=JSON.parse(e)}catch(o){throw new Error(`Invalid JSON: ${o.message}`)}let n=oe(process.cwd(),"agentx.json"),r=`${n}.bak.${Date.now()}`;if(Se(n))try{Va(n,r)}catch{}Ht(n,JSON.stringify(a,null,2)+`
3348
+ `,"utf-8");try{let s=B().dashboard.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800";fetch(`${s}/reload`,{method:"POST"}).catch(()=>null)}catch{}return{summary:"agentx.json replaced",backupPath:r}}function qt(t){return String(t||"").split(/[,\s]+/).map(e=>e.trim()).filter(Boolean).filter((e,a,n)=>n.indexOf(e)===a)}async function se(){let{ActorStore:t}=await import("./store-BAPDZHXE.js");return new t}async function On(t){let e=String(t?.id||"").trim(),a=String(t?.name||"").trim();if(!e.startsWith("actor:"))throw new Error("id must start with 'actor:'");if(!a)throw new Error("name required");let n=Array.isArray(t?.channels)?t.channels:[];if(n.length===0)throw new Error("at least one channel handle required");let o=(await se()).saveActor({id:e,name:a,email:t?.email||void 0,channels:n.map(s=>({channel:s.channel,handle:String(s.handle||"").trim(),preferredForTasks:!!s.preferredForTasks})).filter(s=>s.handle),timezone:t?.timezone||void 0});return{summary:`Actor ${o.id} saved`,actor:o}}async function Hn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("id required");if(!(await se()).deleteActor(e))throw new Error(`actor ${e} not found`);return{summary:`Actor ${e} deleted`}}async function Nn(t){let e=String(t?.id||"").trim(),a=String(t?.name||"").trim();if(!e.startsWith("role:"))throw new Error("id must start with 'role:'");if(!a)throw new Error("name required");let n=t?.assignmentStrategy||"first-available",r=await se(),o=r.getRole(e),s=r.saveRole({id:e,name:a,members:Array.isArray(t?.members)?t.members:o?.members||[],assignmentStrategy:n,rotationCursor:o?.rotationCursor??0});return{summary:`Role ${s.id} saved`,role:s}}async function Bn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("id required");if(!(await se()).deleteRole(e))throw new Error(`role ${e} not found`);return{summary:`Role ${e} deleted`}}async function jn(t){let e=String(t?.role||"").trim(),a=String(t?.member||"").trim();if(!e.startsWith("role:"))throw new Error("role required");if(!a.startsWith("actor:")&&!a.startsWith("role:"))throw new Error("member must be actor:<id> or role:<id>");let n=await se(),r=n.getRole(e);if(!r)throw new Error(`role ${e} not found`);let o=a.startsWith("actor:")?{actor:a}:{role:a};if(r.members.some(l=>("actor"in l?l.actor:l.role)===a))return{summary:`${a} already in ${e}`,role:r};let i=n.saveRole({...r,members:[...r.members,o]});return{summary:`${a} granted to ${e}`,role:i}}async function zn(t){let e=String(t?.role||"").trim(),a=String(t?.member||"").trim();if(!e.startsWith("role:"))throw new Error("role required");let n=await se(),r=n.getRole(e);if(!r)throw new Error(`role ${e} not found`);let o=r.members.filter(i=>("actor"in i?i.actor:i.role)!==a);if(o.length===r.members.length)return{summary:`${a} not in ${e}`,role:r};let s=n.saveRole({...r,members:o});return{summary:`${a} revoked from ${e}`,role:s}}async function qn(t){let e=String(t?.agentId||"").trim(),a=String(t?.role||"").trim();if(!e)throw new Error("agentId required");if(!a)throw new Error("role required");let n=Array.isArray(t?.days)&&t.days.length>0?t.days:["mon","tue","wed","thu","fri"],r=String(t?.start||"09:00"),o=String(t?.end||"17:00"),s=t?.reportsTo?String(t.reportsTo).trim():"",i=typeof t?.utilizationTarget=="number"?t.utilizationTarget:.8,{summary:l}=S(d=>(d.business=d.business||{},d.business.orgChart=d.business.orgChart||{},d.business.orgChart[e]={role:a,...s?{reportsTo:s}:{},schedule:{days:n,start:r,end:o},utilizationTarget:i},`orgChart "${e}" upserted`));return{summary:l}}async function Fn(t){let e=String(t?.agentId||"").trim();if(!e)throw new Error("agentId required");let{summary:a}=S(n=>{if(!n.business?.orgChart||!(e in n.business.orgChart))throw new Error(`no orgChart entry for "${e}"`);return delete n.business.orgChart[e],`orgChart "${e}" removed`});return{summary:a}}async function Gn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("project id required");let a=t?.pm?String(t.pm).trim():"",n=t?.client?String(t.client).trim():"",{summary:r}=S(o=>{o.business=o.business||{},o.business.projects=o.business.projects||[];let s=o.business.projects.findIndex(l=>l.id===e),i={id:e,...a?{pm:a}:{},...n?{client:n}:{}};return s>=0?o.business.projects[s]={...o.business.projects[s],...i}:o.business.projects.push(i),s>=0?`project "${e}" updated`:`project "${e}" added`});return{summary:r}}async function Wn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("project id required");let{summary:a}=S(n=>{let r=n.business?.projects||[],o=r.length;if(n.business.projects=r.filter(s=>s.id!==e),n.business.projects.length===o)throw new Error(`no project "${e}"`);return`project "${e}" removed`});return{summary:a}}async function Un(t){let e=String(t?.client||"").trim();if(!e)throw new Error("client required");if(!t?.chatId&&!t?.username&&!t?.senderId)throw new Error("one of chatId / username / senderId required");let{summary:a}=S(n=>{n.business=n.business||{},n.business.contactMap=n.business.contactMap||[];let r={client:e};t?.channel&&(r.channel=String(t.channel)),t?.chatId&&(r.chatId=String(t.chatId)),t?.username&&(r.username=String(t.username)),t?.senderId&&(r.senderId=String(t.senderId)),t?.project&&(r.project=String(t.project)),t?.displayName&&(r.displayName=String(t.displayName)),n.business.contactMap.push(r);let o=r.chatId||r.username||r.senderId;return`contact ${r.channel?r.channel+"/":""}${o} \u2192 ${e} added`});return{summary:a}}async function Kn(t){let a=["channel","chatId","username","senderId"].filter(r=>t?.[r]);if(a.length===0)throw new Error("at least one filter (channel/chatId/username/senderId) required");let{summary:n}=S(r=>{let o=r.business?.contactMap||[],s=o.length;if(r.business.contactMap=o.filter(i=>{for(let l of a)if(i[l]!==t[l])return!0;return!1}),r.business.contactMap.length===s)throw new Error("no matching contact entry");return`${s-r.business.contactMap.length} contact entry/entries removed`});return{summary:n}}async function Jn(t){let e=String(t?.id||"").trim(),a=String(t?.name||"").trim(),n=String(t?.projects||"").split(/[,\n]/).map(l=>l.trim()).filter(Boolean);if(!e)throw new Error("id required");if(!a)throw new Error("name required");if(n.length===0)throw new Error("at least one project path required");let r=t?.primaryToolLabel?String(t.primaryToolLabel).trim():"",o=Number.isFinite(Number(t?.timeRangeDays))?Number(t.timeRangeDays):30,s=Number.isFinite(Number(t?.closedWindowDays))?Number(t.closedWindowDays):30,{summary:i}=S(l=>{l.boards=Array.isArray(l.boards)?l.boards:[];let d=l.boards.findIndex(u=>u.id===e),c={id:e,name:a,source:{type:"gitlab",projects:n},timeRangeDays:o,closedWindowDays:s};if(r&&(c.primaryToolLabel=r),d>=0){let u=l.boards[d];return l.boards[d]={...u,...c,columns:u.columns,labels:u.labels},`board "${e}" updated`}return l.boards.push(c),`board "${e}" added`});return{summary:i}}async function Xn(t){let e=String(t?.id||"").trim();if(!e)throw new Error("id required");let{summary:a}=S(n=>{let r=Array.isArray(n.boards)?n.boards:[],o=r.length;if(n.boards=r.filter(s=>s.id!==e),n.boards.length===o)throw new Error(`board "${e}" not found`);return`board "${e}" removed`});return{summary:a}}async function Vn(t){let e=String(t?.boardId||"").trim(),a=String(t?.columnId||"").trim().toLowerCase().replace(/[^a-z0-9_-]/g,"-"),n=String(t?.title||"").trim(),r=String(t?.kind||"scoped-label");if(!e)throw new Error("boardId required");if(!a)throw new Error("columnId required");if(!n)throw new Error("title required");if(!["open-backlog","scoped-label","closed","label"].includes(r))throw new Error("kind must be open-backlog | scoped-label | closed | label");let{summary:o}=S(s=>{let l=(Array.isArray(s.boards)?s.boards:[]).find(u=>u.id===e);if(!l)throw new Error(`board "${e}" not found`);l.columns=Array.isArray(l.columns)?l.columns:[];let d={id:a,title:n,kind:r,scopedPrefix:t?.scopedPrefix||"Status"};if(r==="scoped-label"){if(!t?.scopedLabel)throw new Error("scopedLabel required for kind=scoped-label");d.scopedLabel=String(t.scopedLabel)}if(r==="label"){if(!t?.mapsToLabel)throw new Error("mapsToLabel required for kind=label");d.mapsToLabel=String(t.mapsToLabel)}t?.accent&&(d.accent=String(t.accent));let c=l.columns.findIndex(u=>u.id===a);return c>=0?(l.columns[c]={...l.columns[c],...d},`column "${a}" updated`):(l.columns.push(d),`column "${a}" added to board "${e}"`)});return{summary:o}}async function Yn(t){let e=String(t?.boardId||"").trim(),a=String(t?.columnId||"").trim();if(!e||!a)throw new Error("boardId and columnId required");let{summary:n}=S(r=>{let s=(Array.isArray(r.boards)?r.boards:[]).find(l=>l.id===e);if(!s)throw new Error(`board "${e}" not found`);let i=(s.columns||[]).length;if(s.columns=(s.columns||[]).filter(l=>l.id!==a),s.columns.length===i)throw new Error(`column "${a}" not found`);return`column "${a}" removed from board "${e}"`});return{summary:n}}async function Qn(t){let e=t?.interval!==void 0?Number(t.interval):void 0,a=t?.timeout!==void 0?Number(t.timeout):void 0;if(e!==void 0&&(!Number.isFinite(e)||e<5||e>3600))throw new Error("interval must be 5..3600 seconds");if(a!==void 0&&(!Number.isFinite(a)||a<1||a>60))throw new Error("timeout must be 1..60 seconds");if(e===void 0&&a===void 0)throw new Error("nothing to update");let{summary:n}=S(r=>(r.mesh=r.mesh||{},r.mesh.healthCheck=r.mesh.healthCheck||{},e!==void 0&&(r.mesh.healthCheck.interval=e),a!==void 0&&(r.mesh.healthCheck.timeout=a),`mesh.healthCheck updated (interval=${r.mesh.healthCheck.interval}s, timeout=${r.mesh.healthCheck.timeout}s)`));return{summary:n}}async function Zn(t){let{summary:e}=S(a=>{a.notifications=a.notifications||{};let n=[];if(t&&typeof t=="object"){if("destination"in t)if(!t.destination)delete a.notifications.destination,n.push("destination cleared");else if(t.destination.channel&&t.destination.chatId)a.notifications.destination={channel:String(t.destination.channel),chatId:String(t.destination.chatId),...t.destination.accountId?{accountId:String(t.destination.accountId)}:{}},n.push(`destination=${t.destination.channel}:${t.destination.chatId}`);else throw new Error("destination requires channel + chatId");if("on"in t&&t.on&&typeof t.on=="object"){a.notifications.on=a.notifications.on||{};for(let r of["taskComplete","taskError","taskQueued"])r in t.on&&(a.notifications.on[r]=!!t.on[r]);n.push("on="+JSON.stringify(a.notifications.on))}if("longTaskThreshold"in t){let r=Number(t.longTaskThreshold);if(!Number.isFinite(r)||r<0)throw new Error("longTaskThreshold must be a non-negative number");a.notifications.longTaskThreshold=r,n.push(`threshold=${r}s`)}}if(n.length===0)throw new Error("nothing to update");return`notifications updated (${n.join(", ")})`});return{summary:e}}async function er(t){let e=String(t?.id||"").trim();if(!e)throw new Error("webhook id required");let{summary:a}=S(n=>{let o=(Array.isArray(n.webhooks)?n.webhooks:[]).find(s=>s.id===e);if(!o)throw new Error(`webhook "${e}" not found`);if("triggers"in t){if(t.triggers===null||typeof t.triggers=="object"&&Object.keys(t.triggers).length===0)delete o.triggers;else if(typeof t.triggers=="object"){let s={};for(let[i,l]of Object.entries(t.triggers))typeof l=="string"&&l.trim()&&(s[String(i)]=l.trim());Object.keys(s).length===0?delete o.triggers:o.triggers=s}}return"defaultWorkflow"in t&&(t.defaultWorkflow?o.defaultWorkflow=String(t.defaultWorkflow).trim():delete o.defaultWorkflow),`webhook "${e}" triggers/defaultWorkflow updated`});return{summary:a}}import{resolve as nr}from"path";function Ft(t={}){let e=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",a=`<div id="msg" class="msg"></div>
2527
3349
 
2528
3350
  <div id="disabled-banner" class="banner warn" style="display:none">
2529
3351
  <b>Graph is disabled.</b> Set <code class="mono">graph.enabled: true</code>
@@ -2578,7 +3400,7 @@ ${s||"You are "+a+"."}
2578
3400
  <h2>Recent classifications</h2>
2579
3401
  <p class="muted">Last 25 log rows, newest first \u2014 approved / pending / rejected.</p>
2580
3402
  <div id="recent"></div>
2581
- </section>`;return E({title:"AgentX \xB7 Intent Graph",activeTab:"graph",subtitle:"Intent Graph",peers:t.peers,currentPeerId:t.currentPeerId,body:a,css:_a,scripts:`${e}<script>${Ta}</script>`})}var _a=`
3403
+ </section>`;return A({title:"AgentX \xB7 Intent Graph",activeTab:"graph",subtitle:"Intent Graph",peers:t.peers,currentPeerId:t.currentPeerId,body:a,css:tr,scripts:`${e}<script>${ar}</script>`})}var tr=`
2582
3404
  main { max-width: 1200px; margin: 0 auto; padding: 24px 20px 60px; }
2583
3405
  h2 { margin: 0 0 12px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
2584
3406
  .grid { display: grid; gap: 20px; grid-template-columns: 1fr; }
@@ -2682,7 +3504,7 @@ details {
2682
3504
  border-radius: 4px; padding: 8px 12px; margin-top: 8px;
2683
3505
  }
2684
3506
  details summary { cursor: pointer; font-size: 11px; color: var(--ax-muted); user-select: none; }
2685
- details[open] summary { margin-bottom: 8px; }`,Ta=`
3507
+ details[open] summary { margin-bottom: 8px; }`,ar=`
2686
3508
  const $ = (id) => document.getElementById(id);
2687
3509
  const state = { schema: null, nodes: [], pending: [], recent: [] };
2688
3510
 
@@ -2902,7 +3724,1087 @@ $('btn-save-schema').addEventListener('click', async () => {
2902
3724
  $('btn-reload-schema').addEventListener('click', refresh);
2903
3725
 
2904
3726
  refresh();
2905
- `;function ct(t,e,a=[],n){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(lt({peers:a,localToken:n}))}async function pt(t,e,a){try{let n=t.method==="GET"?void 0:await Oa(t),o=Aa(),r={"GET /api/admin/graph/state":()=>$a(o),"POST /api/admin/graph/classifications/approve":()=>Ma(o,n),"POST /api/admin/graph/classifications/reject":()=>Ia(o,n),"POST /api/admin/graph/nodes":()=>La(o,n),"PATCH /api/admin/graph/nodes":()=>Pa(o,n),"DELETE /api/admin/graph/nodes":()=>Da(o,n),"PUT /api/admin/graph/schema":()=>Ha(o,n)},s=`${t.method} ${a}`,i=r[s];if(!i){ye(e,404,{error:`unknown graph endpoint: ${s}`});return}let l=await i();ye(e,200,l)}catch(n){ye(e,400,{error:n?.message||"graph op failed"})}}function Aa(){let t=$(),e=Ca(process.cwd(),t.graph.baseDir);return new Me({baseDir:e})}function $a(t){let e=$(),a=t.loadSchema(),n=t.loadNodes().nodes,o=t.readRecentClassifications(100),r=t.loadIndex().entries,s=new Map;for(let l of o)s.has(l.msgHash)||s.set(l.msgHash,l);let i=[];for(let l of s.values())l.status==="pending"&&!r[l.msgHash]&&i.push(l);return{enabled:e.graph.enabled,schema:a,nodes:n,pending:i,recent:o.slice(0,25),counts:{nodes:n.length,pending:i.length,fingerprints:Object.keys(r).length}}}function Ma(t,e){let a=String(e?.msgHash||"").trim();if(!a)throw new Error("msgHash is required");let n=ut(t,a);return Array.isArray(e?.path)&&e.path.length>0&&(n.path=e.path.map(o=>String(o))),e?.proposedAxes&&typeof e.proposedAxes=="object"&&(n.proposedAxes=we(e.proposedAxes)),t.approveClassification(n),{summary:`Approved ${n.msgHash}`}}function Ia(t,e){let a=String(e?.msgHash||"").trim();if(!a)throw new Error("msgHash is required");let n=ut(t,a);return t.rejectClassification(n),{summary:`Rejected ${n.msgHash}`}}function ut(t,e){let n=t.readRecentClassifications(500).find(o=>o.msgHash===e);if(!n)throw new Error(`No classification found for ${e}`);return n}function La(t,e){let a=dt(String(e?.id||"")),n=String(e?.level||"").trim(),o=e?.parentId?dt(String(e.parentId)):null,r=we({[a]:e?.axes||{}})[a]||{};if(!a)throw new Error("id is required");if(!n)throw new Error("level is required");let s={id:a,level:n,parentId:o,axes:r,createdAt:new Date().toISOString(),createdBy:"admin-ui"};return t.addNode(s),{node:s}}function Pa(t,e){let a=String(e?.id||"").trim();if(!a)throw new Error("id is required");let n=we({[a]:e?.axes||{}})[a]||{};return{node:t.updateNodeAxes(a,n)}}function Da(t,e){let a=String(e?.id||"").trim();if(!a)throw new Error("id is required");return t.deleteNode(a),{summary:`Deleted ${a}`}}function Ha(t,e){if(!e?.schema||typeof e.schema!="object")throw new Error("schema object is required");return t.saveSchema(e.schema),{summary:"Schema saved"}}function we(t){let e={};for(let[a,n]of Object.entries(t||{})){if(!n||typeof n!="object")continue;let o={};for(let[r,s]of Object.entries(n))typeof s=="string"&&(o[r]=s);e[a]=o}return e}function dt(t){return t.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}function ye(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function Oa(t){return new Promise((e,a)=>{let n="";t.on("data",o=>{n+=o}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(o){a(o)}}),t.on("error",a)})}import{existsSync as ja,readFileSync as za}from"fs";import{resolve as ht}from"path";function mt(t){let e=t.agentId,a=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",n=`<script>window.AGENT_ID = ${JSON.stringify(e)};</script>`,o=Ra.replace(/__AGENT_ID__/g,C(e));return E({title:`AgentX \xB7 ${C(e)}`,activeTab:"admin",subtitle:`Agent \xB7 ${C(e)}`,peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:o,css:Na,headExtras:'<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">',scripts:`${n}${a}<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script><script>${Ba}</script>`})}var Ra=`
3727
+ `;function Wt(t,e,a=[],n){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Ft({peers:a,localToken:n}))}async function Ut(t,e,a){try{let n=t.method==="GET"?void 0:await ur(t),r=rr(),o={"GET /api/admin/graph/state":()=>or(r),"POST /api/admin/graph/classifications/approve":()=>sr(r,n),"POST /api/admin/graph/classifications/reject":()=>ir(r,n),"POST /api/admin/graph/nodes":()=>lr(r,n),"PATCH /api/admin/graph/nodes":()=>dr(r,n),"DELETE /api/admin/graph/nodes":()=>cr(r,n),"PUT /api/admin/graph/schema":()=>pr(r,n)},s=`${t.method} ${a}`,i=o[s];if(!i){We(e,404,{error:`unknown graph endpoint: ${s}`});return}let l=await i();We(e,200,l)}catch(n){We(e,400,{error:n?.message||"graph op failed"})}}function rr(){let t=B(),e=nr(process.cwd(),t.graph.baseDir);return new it({baseDir:e})}function or(t){let e=B(),a=t.loadSchema(),n=t.loadNodes().nodes,r=t.readRecentClassifications(100),o=t.loadIndex().entries,s=new Map;for(let l of r)s.has(l.msgHash)||s.set(l.msgHash,l);let i=[];for(let l of s.values())l.status==="pending"&&!o[l.msgHash]&&i.push(l);return{enabled:e.graph.enabled,schema:a,nodes:n,pending:i,recent:r.slice(0,25),counts:{nodes:n.length,pending:i.length,fingerprints:Object.keys(o).length}}}function sr(t,e){let a=String(e?.msgHash||"").trim();if(!a)throw new Error("msgHash is required");let n=Kt(t,a);return Array.isArray(e?.path)&&e.path.length>0&&(n.path=e.path.map(r=>String(r))),e?.proposedAxes&&typeof e.proposedAxes=="object"&&(n.proposedAxes=Ue(e.proposedAxes)),t.approveClassification(n),{summary:`Approved ${n.msgHash}`}}function ir(t,e){let a=String(e?.msgHash||"").trim();if(!a)throw new Error("msgHash is required");let n=Kt(t,a);return t.rejectClassification(n),{summary:`Rejected ${n.msgHash}`}}function Kt(t,e){let n=t.readRecentClassifications(500).find(r=>r.msgHash===e);if(!n)throw new Error(`No classification found for ${e}`);return n}function lr(t,e){let a=Gt(String(e?.id||"")),n=String(e?.level||"").trim(),r=e?.parentId?Gt(String(e.parentId)):null,o=Ue({[a]:e?.axes||{}})[a]||{};if(!a)throw new Error("id is required");if(!n)throw new Error("level is required");let s={id:a,level:n,parentId:r,axes:o,createdAt:new Date().toISOString(),createdBy:"admin-ui"};return t.addNode(s),{node:s}}function dr(t,e){let a=String(e?.id||"").trim();if(!a)throw new Error("id is required");let n=Ue({[a]:e?.axes||{}})[a]||{};return{node:t.updateNodeAxes(a,n)}}function cr(t,e){let a=String(e?.id||"").trim();if(!a)throw new Error("id is required");return t.deleteNode(a),{summary:`Deleted ${a}`}}function pr(t,e){if(!e?.schema||typeof e.schema!="object")throw new Error("schema object is required");return t.saveSchema(e.schema),{summary:"Schema saved"}}function Ue(t){let e={};for(let[a,n]of Object.entries(t||{})){if(!n||typeof n!="object")continue;let r={};for(let[o,s]of Object.entries(n))typeof s=="string"&&(r[o]=s);e[a]=r}return e}function Gt(t){return t.trim().toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")}function We(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function ur(t){return new Promise((e,a)=>{let n="";t.on("data",r=>{n+=r}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(r){a(r)}}),t.on("error",a)})}import{resolve as hr}from"path";import{existsSync as fr}from"fs";import vr from"better-sqlite3";function Jt(t={}){let e=`<div class="ax-obs">
3728
+ <header class="ax-obs__head">
3729
+ <h1>Health</h1>
3730
+ <p class="ax-obs__sub">Platform-health view: what each agent ran, what failed, and how the router decided. <span class="ax-obs__sub-link">Spend lives on the <a href="/admin/cost">Cost</a> page.</span></p>
3731
+ <nav class="ax-obs__tabs" role="tablist">
3732
+ <button class="ax-obs__tab is-active" data-tab="overview" role="tab">Overview</button>
3733
+ <button class="ax-obs__tab" data-tab="activity" role="tab">Activity</button>
3734
+ <button class="ax-obs__tab" data-tab="errors" role="tab">Errors</button>
3735
+ <button class="ax-obs__tab" data-tab="routing" role="tab">Routing</button>
3736
+ <button class="ax-obs__tab" data-tab="rotations" role="tab">Rotations</button>
3737
+ <button class="ax-obs__tab" data-tab="logs" role="tab">Logs</button>
3738
+ <button class="ax-obs__tab" data-tab="doctor" role="tab">Doctor</button>
3739
+ </nav>
3740
+ <div class="ax-obs__filters" id="obs-filters">
3741
+ <label>Agent
3742
+ <input id="obs-agent" type="text" placeholder="filter by agent id" />
3743
+ </label>
3744
+ <label class="js-tech-only">Limit
3745
+ <input id="obs-limit" type="number" min="1" max="500" value="50" />
3746
+ </label>
3747
+ <button id="obs-refresh" type="button">Refresh</button>
3748
+ </div>
3749
+ </header>
3750
+
3751
+ <section class="ax-obs__pane is-active" data-pane="overview">
3752
+ <div class="ax-obs__kpis" id="obs-overview-kpis"></div>
3753
+ <div class="ax-obs__split">
3754
+ <div class="ax-obs__card">
3755
+ <h3>Top agents today</h3>
3756
+ <table class="ax-obs__table">
3757
+ <thead><tr><th>Agent</th><th class="num">Tasks</th><th class="num">Errors</th><th class="num">Avg time</th><th>Last active</th></tr></thead>
3758
+ <tbody id="obs-overview-agents"></tbody>
3759
+ </table>
3760
+ <p class="ax-obs__empty" id="obs-overview-agents-empty" hidden>No tasks yet today.</p>
3761
+ </div>
3762
+ <div class="ax-obs__card">
3763
+ <h3>Channels today</h3>
3764
+ <table class="ax-obs__table">
3765
+ <thead><tr><th>Channel</th><th class="num">Tasks</th><th class="num">Errors</th></tr></thead>
3766
+ <tbody id="obs-overview-channels"></tbody>
3767
+ </table>
3768
+ <p class="ax-obs__empty" id="obs-overview-channels-empty" hidden>No traffic yet today.</p>
3769
+ </div>
3770
+ </div>
3771
+ <div class="ax-obs__card">
3772
+ <h3>Idle agents <span class="ax-obs__hint">(no activity in the last 24h)</span></h3>
3773
+ <p id="obs-overview-idle" class="ax-obs__idle"></p>
3774
+ </div>
3775
+ <div class="ax-obs__card">
3776
+ <h3>Recent failures (last 24h) <span class="ax-obs__hint">\u2014 top 5</span></h3>
3777
+ <ul id="obs-overview-errors" class="ax-obs__error-list"></ul>
3778
+ <p class="ax-obs__empty" id="obs-overview-errors-empty" hidden>No failures in the last 24 hours. \u2713</p>
3779
+ </div>
3780
+ </section>
3781
+
3782
+ <section class="ax-obs__pane" data-pane="activity">
3783
+ <div class="ax-obs__card">
3784
+ <h3>Last 24 hours <span class="ax-obs__hint">\u2014 hourly</span></h3>
3785
+ <div id="obs-activity-hourly" class="ax-obs__chart"></div>
3786
+ <p class="ax-obs__empty" id="obs-activity-hourly-empty" hidden>No activity in the last 24 hours.</p>
3787
+ </div>
3788
+ <div class="ax-obs__card">
3789
+ <h3>Last 7 days <span class="ax-obs__hint">\u2014 daily</span></h3>
3790
+ <div id="obs-activity-daily" class="ax-obs__chart"></div>
3791
+ </div>
3792
+ <div class="ax-obs__card">
3793
+ <h3>Per-agent activity (7-day window)</h3>
3794
+ <table class="ax-obs__table">
3795
+ <thead><tr><th>Agent</th><th class="num">Tasks 24h</th><th class="num">Tasks 7d</th><th class="num">Errors 7d</th><th class="num">Avg time</th><th>Last active</th></tr></thead>
3796
+ <tbody id="obs-activity-agents"></tbody>
3797
+ </table>
3798
+ <p class="ax-obs__empty" id="obs-activity-agents-empty" hidden>(no rows)</p>
3799
+ </div>
3800
+ </section>
3801
+
3802
+ <section class="ax-obs__pane" data-pane="errors">
3803
+ <div class="ax-obs__table-wrap"><table class="ax-obs__table">
3804
+ <thead><tr><th>When</th><th>Agent</th><th>Channel</th><th>What happened</th><th>Message</th></tr></thead>
3805
+ <tbody id="obs-errors"></tbody>
3806
+ </table></div>
3807
+ <p class="ax-obs__empty" id="obs-errors-empty" hidden>No errors found.</p>
3808
+ </section>
3809
+
3810
+ <section class="ax-obs__pane" data-pane="routing">
3811
+ <p class="ax-obs__hint">Technical: every routing decision the daemon made \u2014 which stage decided which agent gets a message. Useful for debugging "why didn't agent X pick up this?".</p>
3812
+ <div class="ax-obs__table-wrap"><table class="ax-obs__table">
3813
+ <thead><tr><th>at</th><th>channel</th><th>chat_id</th><th>kind</th><th>stage</th><th>agent</th><th>reason</th></tr></thead>
3814
+ <tbody id="obs-routing"></tbody>
3815
+ </table></div>
3816
+ <p class="ax-obs__empty" id="obs-routing-empty" hidden>(no rows)</p>
3817
+ </section>
3818
+
3819
+ <section class="ax-obs__pane" data-pane="rotations">
3820
+ <p class="ax-obs__hint">Technical: each time an agent's Claude session was rotated \u2014 <code>stale</code> (idle), <code>tier-2</code> (input crossed billing threshold), or <code>max-turns</code> (per-session cap).</p>
3821
+ <div class="ax-obs__table-wrap"><table class="ax-obs__table">
3822
+ <thead><tr><th>at</th><th>agent</th><th>channel</th><th>reason</th><th>last turn input tokens</th></tr></thead>
3823
+ <tbody id="obs-rotations"></tbody>
3824
+ </table></div>
3825
+ <p class="ax-obs__empty" id="obs-rotations-empty" hidden>(no rows)</p>
3826
+ </section>
3827
+
3828
+ <section class="ax-obs__pane" data-pane="logs">
3829
+ <p class="ax-obs__hint">Live tail of the daemon's stdout \u2014 same source as <code>agentx daemon logs -f</code>. Streams via SSE; falls back to /tmp/agentx-daemon.log when the daemon isn't under systemd.</p>
3830
+ <div class="ax-obs__logs-toolbar">
3831
+ <input id="obs-logs-filter" type="text" placeholder="grep (case-insensitive)" />
3832
+ <label class="ax-obs__inline"><input id="obs-logs-autoscroll" type="checkbox" checked /> auto-scroll</label>
3833
+ <button id="obs-logs-pause" type="button">Pause</button>
3834
+ <button id="obs-logs-clear" type="button">Clear</button>
3835
+ <span id="obs-logs-status" class="ax-obs__hint">connecting\u2026</span>
3836
+ </div>
3837
+ <pre id="obs-logs-body" class="ax-obs__logs-body"></pre>
3838
+ </section>
3839
+
3840
+ <section class="ax-obs__pane" data-pane="doctor">
3841
+ <p class="ax-obs__hint">Preflight checks \u2014 same as <code>agentx doctor</code>. Read-only. Re-run any time.</p>
3842
+ <div class="ax-obs__doctor-toolbar">
3843
+ <button id="obs-doctor-run" type="button">Re-run checks</button>
3844
+ <label class="ax-obs__inline"><input id="obs-doctor-running" type="checkbox" checked /> probe running daemon</label>
3845
+ <span id="obs-doctor-summary" class="ax-obs__hint">\u2014</span>
3846
+ </div>
3847
+ <div id="obs-doctor-body" class="ax-obs__doctor-body"></div>
3848
+ </section>
3849
+ </div>`;return A({title:"AgentX \xB7 Health",activeTab:"health",subtitle:"Health",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:mr,scripts:`<script>${gr}</script>`})}var mr=`
3850
+ .ax-obs { padding: 16px 24px; }
3851
+ .ax-obs__head { margin-bottom: 16px; }
3852
+ .ax-obs__head h1 { margin: 0 0 4px; font-size: 18px; }
3853
+ .ax-obs__sub { color: var(--ax-muted); font-size: 13px; margin: 0 0 12px; }
3854
+ .ax-obs__sub code { font-size: 12px; padding: 1px 5px; background: var(--ax-surface-2); border-radius: 4px; }
3855
+ .ax-obs__tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--ax-border); margin-bottom: 12px; align-items: center; }
3856
+ .ax-obs__tab {
3857
+ background: transparent; border: 0; padding: 8px 14px; cursor: pointer;
3858
+ font: inherit; color: var(--ax-muted); border-bottom: 2px solid transparent;
3859
+ }
3860
+ .ax-obs__tab:hover { color: var(--ax-text); }
3861
+ .ax-obs__tab.is-active { color: var(--ax-text); border-bottom-color: var(--ax-accent, #3a7bd5); }
3862
+ .ax-obs__filters { display: flex; gap: 12px; align-items: end; flex-wrap: wrap; padding: 8px 0; }
3863
+ .ax-obs__filters label { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--ax-muted); }
3864
+ .ax-obs__filters input {
3865
+ font: inherit; padding: 6px 8px; border: 1px solid var(--ax-border);
3866
+ border-radius: 6px; background: var(--ax-bg); color: var(--ax-text); width: 160px;
3867
+ }
3868
+ .ax-obs__filters input[type="number"] { width: 80px; }
3869
+ .ax-obs__filters button {
3870
+ padding: 6px 14px; border: 1px solid var(--ax-border); background: var(--ax-bg);
3871
+ color: var(--ax-text); border-radius: 6px; cursor: pointer; font: inherit;
3872
+ }
3873
+ .ax-obs__filters button:hover { background: var(--ax-surface-2); }
3874
+ .ax-obs__pane { display: none; }
3875
+ .ax-obs__pane.is-active { display: block; }
3876
+ .ax-obs__hint { color: var(--ax-muted); font-size: 11px; font-weight: normal; }
3877
+ .ax-obs__table-wrap { overflow-x: auto; max-height: calc(100vh - 280px); overflow-y: auto; border: 1px solid var(--ax-border); border-radius: 6px; }
3878
+ .ax-obs__table { width: 100%; border-collapse: collapse; font-size: 12px; }
3879
+ .ax-obs__table thead th {
3880
+ position: sticky; top: 0; background: var(--ax-bg); padding: 8px 10px;
3881
+ text-align: left; border-bottom: 1px solid var(--ax-border);
3882
+ font-weight: 600; font-size: 11px; color: var(--ax-muted); text-transform: uppercase; letter-spacing: 0.04em;
3883
+ }
3884
+ .ax-obs__table td { padding: 6px 10px; border-bottom: 1px solid var(--ax-border); vertical-align: top; }
3885
+ .ax-obs__table tr:hover td { background: var(--ax-surface-2); }
3886
+ .ax-obs__table .num { text-align: right; font-variant-numeric: tabular-nums; }
3887
+ .ax-obs__table .small { color: var(--ax-muted); font-size: 11px; }
3888
+
3889
+ /* KPI cards */
3890
+ .ax-obs__kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 16px; }
3891
+ .ax-obs__kpi { padding: 14px 16px; border: 1px solid var(--ax-border); border-radius: 8px; background: var(--ax-bg); }
3892
+ .ax-obs__kpi-label { color: var(--ax-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 6px; }
3893
+ .ax-obs__kpi-value { font-size: 22px; font-weight: 600; line-height: 1.1; }
3894
+ .ax-obs__kpi-sub { color: var(--ax-muted); font-size: 11px; margin-top: 4px; }
3895
+ .ax-obs__kpi--alert .ax-obs__kpi-value { color: #cf222e; }
3896
+ .ax-obs__kpi--good .ax-obs__kpi-value { color: #2da44e; }
3897
+ .ax-obs__delta { font-size: 11px; }
3898
+ .ax-obs__delta--up { color: #2da44e; }
3899
+ .ax-obs__delta--down { color: #cf222e; }
3900
+
3901
+ .ax-obs__card { border: 1px solid var(--ax-border); border-radius: 8px; padding: 14px 16px; margin-bottom: 12px; background: var(--ax-bg); }
3902
+ .ax-obs__card h3 { margin: 0 0 10px; font-size: 13px; font-weight: 600; }
3903
+ .ax-obs__split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
3904
+ @media (max-width: 900px) { .ax-obs__split { grid-template-columns: 1fr; } }
3905
+ .ax-obs__idle { margin: 0; font-size: 12px; color: var(--ax-muted); }
3906
+ .ax-obs__idle .ax-obs__pill { margin: 2px 4px 2px 0; }
3907
+ .ax-obs__error-list { list-style: none; margin: 0; padding: 0; }
3908
+ .ax-obs__error-list li {
3909
+ padding: 8px 10px; border-radius: 6px; background: var(--ax-surface-2);
3910
+ margin-bottom: 6px; font-size: 12px; display: grid; gap: 4px;
3911
+ }
3912
+ .ax-obs__error-plain { font-weight: 600; color: #cf222e; }
3913
+ .ax-obs__error-meta { color: var(--ax-muted); font-size: 11px; }
3914
+ .ax-obs__error-message { color: var(--ax-muted); font-size: 11px; font-family: monospace; word-break: break-word; }
3915
+
3916
+ /* Bar charts */
3917
+ .ax-obs__chart { display: flex; align-items: flex-end; gap: 2px; height: 120px; padding: 4px 0; }
3918
+ .ax-obs__bar { flex: 1; min-width: 4px; background: var(--ax-accent, #3a7bd5); border-radius: 2px 2px 0 0; opacity: 0.8; position: relative; cursor: default; transition: opacity 0.1s; }
3919
+ .ax-obs__bar:hover { opacity: 1; }
3920
+ .ax-obs__bar--with-error { background: linear-gradient(to top, #cf222e var(--err-pct), var(--ax-accent, #3a7bd5) var(--err-pct)); }
3921
+ .ax-obs__bar[data-tooltip]:hover::after {
3922
+ content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%;
3923
+ transform: translateX(-50%); white-space: nowrap; padding: 4px 8px;
3924
+ background: var(--ax-text); color: var(--ax-bg); border-radius: 4px;
3925
+ font-size: 11px; pointer-events: none; z-index: 10;
3926
+ }
3927
+ .ax-obs__chart--cost .ax-obs__bar { background: #6b7280; }
3928
+
3929
+ .ax-obs__pill {
3930
+ display: inline-block; padding: 1px 7px; border-radius: 10px;
3931
+ font-size: 10px; font-weight: 600; text-transform: uppercase;
3932
+ background: var(--ax-surface-2); color: var(--ax-text);
3933
+ }
3934
+ .ax-obs__pill--match { background: rgba(46, 160, 67, 0.15); color: #2da44e; }
3935
+ .ax-obs__pill--drop { background: rgba(207, 34, 46, 0.15); color: #cf222e; }
3936
+ .ax-obs__pill--tier-2 { background: rgba(218, 119, 6, 0.15); color: #bf8700; }
3937
+ .ax-obs__pill--stale { background: rgba(99, 110, 123, 0.15); color: #57606a; }
3938
+ .ax-obs__pill--max-turns { background: rgba(99, 110, 123, 0.15); color: #57606a; }
3939
+ .ax-obs__pill--idle { background: rgba(218, 119, 6, 0.15); color: #bf8700; }
3940
+ .ax-obs__empty { color: var(--ax-muted); font-style: italic; padding: 18px 0; margin: 0; }
3941
+ .ax-obs__logs-toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
3942
+ .ax-obs__logs-toolbar input[type="text"] { font-family: 'IBM Plex Mono', monospace; font-size: 12px; padding: 5px 9px; min-width: 220px; background: var(--ax-bg-elev); border: 1px solid var(--ax-border); border-radius: 4px; color: var(--ax-fg); }
3943
+ .ax-obs__logs-toolbar button { font-size: 11px; padding: 5px 11px; border-radius: 4px; border: 1px solid var(--ax-border); background: var(--ax-bg); color: var(--ax-fg); cursor: pointer; }
3944
+ .ax-obs__logs-toolbar button:hover { background: var(--ax-surface); }
3945
+ .ax-obs__inline { font-size: 11px; color: var(--ax-muted); display: inline-flex; align-items: center; gap: 4px; }
3946
+ .ax-obs__logs-body { font-family: 'IBM Plex Mono', monospace; font-size: 11px; line-height: 1.45; height: calc(100vh - 320px); min-height: 360px; overflow-y: auto; background: var(--ax-bg-elev); border: 1px solid var(--ax-border); border-radius: 4px; padding: 10px 12px; margin: 0; white-space: pre-wrap; word-break: break-word; }
3947
+ .ax-obs__logs-body .err { color: var(--ax-err, #e74c3c); }
3948
+ .ax-obs__logs-body .warn { color: #d4ac0d; }
3949
+ .ax-obs__logs-body .info { color: var(--ax-fg); }
3950
+ .ax-obs__logs-body .dim { color: var(--ax-muted); }
3951
+ .ax-obs__doctor-toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
3952
+ .ax-obs__doctor-toolbar button { font-size: 11px; padding: 5px 11px; border-radius: 4px; border: 1px solid var(--ax-border); background: var(--ax-bg); color: var(--ax-fg); cursor: pointer; }
3953
+ .ax-obs__doctor-toolbar button:hover { background: var(--ax-surface); }
3954
+ .ax-obs__doctor-body { display: flex; flex-direction: column; gap: 10px; }
3955
+ .ax-obs__doctor-group { background: var(--ax-bg-elev); border: 1px solid var(--ax-border); border-radius: 6px; padding: 10px 14px; }
3956
+ .ax-obs__doctor-group h4 { font-family: 'IBM Plex Sans', sans-serif; font-size: 11px; font-weight: 600; margin: 0 0 8px; color: var(--ax-muted); text-transform: uppercase; letter-spacing: 0.04em; }
3957
+ .ax-obs__doctor-row { display: grid; grid-template-columns: 60px 1fr; gap: 10px; padding: 5px 0; border-top: 1px solid var(--ax-border); align-items: baseline; }
3958
+ .ax-obs__doctor-row:first-child { border-top: 0; }
3959
+ .ax-obs__doctor-sev { font-size: 10px; text-transform: uppercase; font-weight: 600; letter-spacing: 0.04em; padding: 2px 6px; border-radius: 3px; text-align: center; }
3960
+ .ax-obs__doctor-sev.ok { color: #2ecc71; background: rgba(46,204,113,0.12); }
3961
+ .ax-obs__doctor-sev.warn { color: #d4ac0d; background: rgba(212,172,13,0.14); }
3962
+ .ax-obs__doctor-sev.fail { color: var(--ax-err, #e74c3c); background: rgba(231,76,60,0.14); }
3963
+ .ax-obs__doctor-title { font-size: 12px; font-weight: 500; }
3964
+ .ax-obs__doctor-detail { font-size: 11px; color: var(--ax-muted); margin-top: 2px; font-family: 'IBM Plex Mono', monospace; word-break: break-word; }
3965
+ .ax-obs__doctor-fix { font-size: 11px; color: var(--ax-accent, #3a7bd5); margin-top: 2px; }
3966
+ `,gr=`
3967
+ (function(){
3968
+ var panes = document.querySelectorAll('.ax-obs__pane');
3969
+ var tabs = document.querySelectorAll('.ax-obs__tab');
3970
+ var current = 'overview';
3971
+
3972
+ tabs.forEach(function(t){
3973
+ t.addEventListener('click', function(){
3974
+ tabs.forEach(function(x){ x.classList.toggle('is-active', x === t); });
3975
+ panes.forEach(function(p){ p.classList.toggle('is-active', p.dataset.pane === t.dataset.tab); });
3976
+ current = t.dataset.tab;
3977
+ load(current);
3978
+ });
3979
+ });
3980
+
3981
+ function fmtTs(s){
3982
+ if (!s) return '';
3983
+ return String(s).replace('T', ' ').replace('Z', '').slice(0, 19);
3984
+ }
3985
+ function esc(s){
3986
+ if (s == null) return '';
3987
+ return String(s).replace(/[&<>"']/g, function(c){
3988
+ return c === '&' ? '&amp;' : c === '<' ? '&lt;' : c === '>' ? '&gt;' : c === '"' ? '&quot;' : '&#39;';
3989
+ });
3990
+ }
3991
+ function fmtMoney(n){
3992
+ if (n == null) return '$0.00';
3993
+ if (n < 0.01) return '$' + n.toFixed(4);
3994
+ if (n < 1) return '$' + n.toFixed(3);
3995
+ return '$' + n.toFixed(2);
3996
+ }
3997
+ function fmtMs(n){
3998
+ if (!n) return '\u2013';
3999
+ if (n < 1000) return Math.round(n) + 'ms';
4000
+ if (n < 60000) return (n / 1000).toFixed(1) + 's';
4001
+ return Math.round(n / 60000) + 'm';
4002
+ }
4003
+ function fmtNum(n){
4004
+ if (n == null) return '\u2013';
4005
+ return Number(n).toLocaleString();
4006
+ }
4007
+ function fmtRel(iso){
4008
+ if (!iso) return '\u2013';
4009
+ var d = new Date(iso).getTime();
4010
+ if (isNaN(d)) return '\u2013';
4011
+ var diff = (Date.now() - d) / 1000;
4012
+ if (diff < 60) return Math.floor(diff) + 's ago';
4013
+ if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
4014
+ if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
4015
+ return Math.floor(diff / 86400) + 'd ago';
4016
+ }
4017
+ function delta(n, label){
4018
+ if (n === 0 || n == null) return '<span class="ax-obs__delta">no change</span>';
4019
+ var arrow = n > 0 ? '\u25B2' : '\u25BC';
4020
+ var cls = n > 0 ? 'ax-obs__delta--up' : 'ax-obs__delta--down';
4021
+ return '<span class="ax-obs__delta ' + cls + '">' + arrow + ' ' + Math.abs(n).toLocaleString() + ' ' + (label || '') + '</span>';
4022
+ }
4023
+ function deltaMoney(n){
4024
+ if (Math.abs(n) < 0.001) return '<span class="ax-obs__delta">no change</span>';
4025
+ var arrow = n > 0 ? '\u25B2' : '\u25BC';
4026
+ var cls = n > 0 ? 'ax-obs__delta--down' : 'ax-obs__delta--up'; // more cost = bad
4027
+ return '<span class="ax-obs__delta ' + cls + '">' + arrow + ' ' + fmtMoney(Math.abs(n)) + ' vs yesterday</span>';
4028
+ }
4029
+
4030
+ async function load(tab){
4031
+ if (tab === 'logs') return ensureLogsStream();
4032
+ if (logsState.es) closeLogsStream();
4033
+ if (tab === 'doctor') return loadDoctor();
4034
+ var agent = document.getElementById('obs-agent').value || '';
4035
+ var limit = document.getElementById('obs-limit').value || 50;
4036
+
4037
+ var url, qs = (agent ? '?agent=' + encodeURIComponent(agent) : '');
4038
+ if (tab === 'overview' || tab === 'activity') {
4039
+ url = '/api/admin/observability/' + tab + qs;
4040
+ } else {
4041
+ qs = '?limit=' + encodeURIComponent(limit) + (agent ? '&agent=' + encodeURIComponent(agent) : '');
4042
+ url = '/api/admin/observability/' + tab + qs;
4043
+ }
4044
+
4045
+ try {
4046
+ var r = await fetch(url, { credentials: 'same-origin' });
4047
+ var data = await r.json();
4048
+ render(tab, data);
4049
+ } catch (e) {
4050
+ console.error('observability fetch failed', e);
4051
+ }
4052
+ }
4053
+
4054
+ // ----- Logs tab: SSE-driven live tail -----
4055
+ var logsState = { es: null, paused: false, buffer: [], maxLines: 2000 };
4056
+ function classifyLine(s){
4057
+ var l = s.toLowerCase();
4058
+ if (l.indexOf('error') >= 0 || l.indexOf('fail') >= 0) return 'err';
4059
+ if (l.indexOf('warn') >= 0) return 'warn';
4060
+ if (l.indexOf('debug') >= 0 || l.indexOf('[graph]') >= 0) return 'dim';
4061
+ return 'info';
4062
+ }
4063
+ function renderLogsLine(line){
4064
+ if (!line) return;
4065
+ var body = document.getElementById('obs-logs-body');
4066
+ var filter = (document.getElementById('obs-logs-filter').value || '').toLowerCase();
4067
+ if (filter && line.toLowerCase().indexOf(filter) === -1) return;
4068
+ var span = document.createElement('span');
4069
+ span.className = classifyLine(line);
4070
+ span.textContent = line + '\\n';
4071
+ body.appendChild(span);
4072
+ while (body.children.length > logsState.maxLines) body.removeChild(body.firstChild);
4073
+ if (document.getElementById('obs-logs-autoscroll').checked) {
4074
+ body.scrollTop = body.scrollHeight;
4075
+ }
4076
+ }
4077
+ function ensureLogsStream(){
4078
+ if (logsState.es || logsState.paused) {
4079
+ document.getElementById('obs-logs-status').textContent = logsState.paused ? 'paused' : 'live';
4080
+ return;
4081
+ }
4082
+ document.getElementById('obs-logs-status').textContent = 'connecting\u2026';
4083
+ var es = new EventSource('/api/admin/logs/stream', { withCredentials: true });
4084
+ logsState.es = es;
4085
+ es.addEventListener('line', function(ev){ renderLogsLine(ev.data); });
4086
+ es.addEventListener('open', function(){ document.getElementById('obs-logs-status').textContent = 'live'; });
4087
+ es.addEventListener('error', function(){
4088
+ document.getElementById('obs-logs-status').textContent = 'disconnected (retrying)';
4089
+ });
4090
+ }
4091
+ function closeLogsStream(){
4092
+ if (logsState.es) { try { logsState.es.close(); } catch (e) {} logsState.es = null; }
4093
+ }
4094
+ // ----- Doctor tab: preflight checks (mirror of agentx doctor) -----
4095
+ async function loadDoctor(){
4096
+ var running = document.getElementById('obs-doctor-running').checked;
4097
+ document.getElementById('obs-doctor-summary').textContent = 'running\u2026';
4098
+ try {
4099
+ var r = await fetch('/api/admin/doctor?running=' + (running ? 'true' : 'false'), { credentials: 'same-origin' });
4100
+ var data = await r.json();
4101
+ var s = data.summary || {};
4102
+ document.getElementById('obs-doctor-summary').textContent =
4103
+ 'ok=' + (s.ok || 0) + ' warn=' + (s.warnings || 0) + ' fail=' + (s.errors || 0);
4104
+ var grouped = {};
4105
+ for (var i = 0; i < (data.checks || []).length; i++) {
4106
+ var c = data.checks[i];
4107
+ (grouped[c.group] = grouped[c.group] || []).push(c);
4108
+ }
4109
+ var html = '';
4110
+ Object.keys(grouped).forEach(function(g){
4111
+ html += '<div class="ax-obs__doctor-group"><h4>' + esc(g) + '</h4>';
4112
+ grouped[g].forEach(function(c){
4113
+ html += '<div class="ax-obs__doctor-row">' +
4114
+ '<span class="ax-obs__doctor-sev ' + esc(c.severity) + '">' + esc(c.severity) + '</span>' +
4115
+ '<div>' +
4116
+ '<div class="ax-obs__doctor-title">' + esc(c.title) + '</div>' +
4117
+ (c.detail ? '<div class="ax-obs__doctor-detail">' + esc(c.detail) + '</div>' : '') +
4118
+ (c.fix ? '<div class="ax-obs__doctor-fix">\u2192 ' + esc(c.fix) + '</div>' : '') +
4119
+ '</div>' +
4120
+ '</div>';
4121
+ });
4122
+ html += '</div>';
4123
+ });
4124
+ document.getElementById('obs-doctor-body').innerHTML = html || '<p class="ax-obs__empty">no checks</p>';
4125
+ } catch (e) {
4126
+ document.getElementById('obs-doctor-summary').textContent = 'failed: ' + e.message;
4127
+ }
4128
+ }
4129
+
4130
+ function wireLogsToolbar(){
4131
+ var pauseBtn = document.getElementById('obs-logs-pause');
4132
+ var clearBtn = document.getElementById('obs-logs-clear');
4133
+ if (!pauseBtn || pauseBtn.dataset.wired) return;
4134
+ pauseBtn.dataset.wired = '1';
4135
+ pauseBtn.addEventListener('click', function(){
4136
+ logsState.paused = !logsState.paused;
4137
+ pauseBtn.textContent = logsState.paused ? 'Resume' : 'Pause';
4138
+ if (logsState.paused) closeLogsStream();
4139
+ else ensureLogsStream();
4140
+ document.getElementById('obs-logs-status').textContent = logsState.paused ? 'paused' : 'connecting\u2026';
4141
+ });
4142
+ clearBtn.addEventListener('click', function(){
4143
+ document.getElementById('obs-logs-body').innerHTML = '';
4144
+ });
4145
+ }
4146
+
4147
+ function render(tab, data){
4148
+ if (tab === 'overview') return renderOverview(data);
4149
+ if (tab === 'activity') return renderActivity(data);
4150
+ if (tab === 'errors') return renderErrors(data);
4151
+ if (tab === 'routing') return renderRouting(data);
4152
+ if (tab === 'rotations') return renderRotations(data);
4153
+ }
4154
+
4155
+ function renderOverview(data){
4156
+ if (!data) return;
4157
+ var k = data.kpis || {};
4158
+ var errAlert = k.errorsToday > 0 ? ' ax-obs__kpi--alert' : ' ax-obs__kpi--good';
4159
+ document.getElementById('obs-overview-kpis').innerHTML = [
4160
+ kpi('Tasks today', fmtNum(k.tasksToday), delta(k.tasksDelta, 'vs yesterday')),
4161
+ kpi('Errors today', fmtNum(k.errorsToday) + ' <span class="ax-obs__kpi-sub">(' + (k.errorRate * 100).toFixed(1) + '% error rate)</span>', '', errAlert),
4162
+ kpi('Avg response', fmtMs(k.avgDurationMs), 'p50 ' + fmtMs(k.p50DurationMs) + ' \xB7 p95 ' + fmtMs(k.p95DurationMs)),
4163
+ kpi('Spend today', fmtMoney(k.spendToday), deltaMoney(k.spendDelta)),
4164
+ ].join('');
4165
+
4166
+ var pa = data.perAgent || [];
4167
+ if (pa.length) {
4168
+ document.getElementById('obs-overview-agents-empty').hidden = true;
4169
+ document.getElementById('obs-overview-agents').innerHTML = pa.map(function(r){
4170
+ return '<tr>'
4171
+ + '<td>' + esc(r.agent_id) + '</td>'
4172
+ + '<td class="num">' + fmtNum(r.tasks) + '</td>'
4173
+ + '<td class="num" ' + (r.errors > 0 ? 'style="color:#cf222e;font-weight:600"' : '') + '>' + fmtNum(r.errors) + '</td>'
4174
+ + '<td class="num">' + fmtMs(r.avg_duration_ms) + '</td>'
4175
+ + '<td class="small">' + fmtRel(r.last_active) + '</td>'
4176
+ + '</tr>';
4177
+ }).join('');
4178
+ } else {
4179
+ document.getElementById('obs-overview-agents-empty').hidden = false;
4180
+ document.getElementById('obs-overview-agents').innerHTML = '';
4181
+ }
4182
+
4183
+ var pc = data.perChannel || [];
4184
+ if (pc.length) {
4185
+ document.getElementById('obs-overview-channels-empty').hidden = true;
4186
+ document.getElementById('obs-overview-channels').innerHTML = pc.map(function(r){
4187
+ return '<tr>'
4188
+ + '<td>' + esc(r.channel) + '</td>'
4189
+ + '<td class="num">' + fmtNum(r.tasks) + '</td>'
4190
+ + '<td class="num" ' + (r.errors > 0 ? 'style="color:#cf222e;font-weight:600"' : '') + '>' + fmtNum(r.errors) + '</td>'
4191
+ + '</tr>';
4192
+ }).join('');
4193
+ } else {
4194
+ document.getElementById('obs-overview-channels-empty').hidden = false;
4195
+ document.getElementById('obs-overview-channels').innerHTML = '';
4196
+ }
4197
+
4198
+ var idle = data.idleAgents || [];
4199
+ document.getElementById('obs-overview-idle').innerHTML = idle.length
4200
+ ? idle.map(function(a){ return '<span class="ax-obs__pill ax-obs__pill--idle">' + esc(a) + '</span>'; }).join('')
4201
+ : '<span class="ax-obs__pill ax-obs__pill--match">All recently-active agents posted a task in the last 24h \u2713</span>';
4202
+
4203
+ var er = data.recentErrors || [];
4204
+ var ulEmpty = document.getElementById('obs-overview-errors-empty');
4205
+ var ulList = document.getElementById('obs-overview-errors');
4206
+ if (er.length === 0) {
4207
+ ulEmpty.hidden = false;
4208
+ ulList.innerHTML = '';
4209
+ } else {
4210
+ ulEmpty.hidden = true;
4211
+ ulList.innerHTML = er.map(function(r){
4212
+ return '<li>'
4213
+ + '<div class="ax-obs__error-plain">' + esc(r.plain) + '</div>'
4214
+ + '<div class="ax-obs__error-meta">' + fmtRel(r.started_at) + ' \xB7 ' + esc(r.agent_id) + ' \xB7 ' + esc(r.channel) + '</div>'
4215
+ + (r.message_preview ? '<div class="ax-obs__error-message">' + esc(r.message_preview.slice(0, 200)) + '</div>' : '')
4216
+ + '</li>';
4217
+ }).join('');
4218
+ }
4219
+ }
4220
+
4221
+ function renderActivity(data){
4222
+ if (!data) return;
4223
+ var hourly = data.hourly || [];
4224
+ if (hourly.length === 0) {
4225
+ document.getElementById('obs-activity-hourly-empty').hidden = false;
4226
+ document.getElementById('obs-activity-hourly').innerHTML = '';
4227
+ } else {
4228
+ document.getElementById('obs-activity-hourly-empty').hidden = true;
4229
+ document.getElementById('obs-activity-hourly').innerHTML = renderBarChart(hourly, 'hour', 'tasks', 'errors');
4230
+ }
4231
+ document.getElementById('obs-activity-daily').innerHTML = renderBarChart(data.daily || [], 'day', 'tasks', 'errors');
4232
+
4233
+ var rows = data.perAgent || [];
4234
+ if (rows.length === 0) {
4235
+ document.getElementById('obs-activity-agents-empty').hidden = false;
4236
+ document.getElementById('obs-activity-agents').innerHTML = '';
4237
+ } else {
4238
+ document.getElementById('obs-activity-agents-empty').hidden = true;
4239
+ document.getElementById('obs-activity-agents').innerHTML = rows.map(function(r){
4240
+ return '<tr>'
4241
+ + '<td>' + esc(r.agent_id) + '</td>'
4242
+ + '<td class="num">' + fmtNum(r.tasks_24h) + '</td>'
4243
+ + '<td class="num">' + fmtNum(r.tasks_7d) + '</td>'
4244
+ + '<td class="num" ' + (r.errors_7d > 0 ? 'style="color:#cf222e;font-weight:600"' : '') + '>' + fmtNum(r.errors_7d) + '</td>'
4245
+ + '<td class="num">' + fmtMs(r.avg_duration_ms) + '</td>'
4246
+ + '<td class="small">' + fmtRel(r.last_active) + '</td>'
4247
+ + '</tr>';
4248
+ }).join('');
4249
+ }
4250
+ }
4251
+
4252
+ function renderErrors(data){
4253
+ var rows = data && data.rows || [];
4254
+ var body = document.getElementById('obs-errors');
4255
+ var empty = document.getElementById('obs-errors-empty');
4256
+ if (!rows.length) { body.innerHTML = ''; empty.hidden = false; return; }
4257
+ empty.hidden = true;
4258
+ body.innerHTML = rows.map(function(r){
4259
+ return '<tr>'
4260
+ + '<td>' + fmtTs(r.started_at) + '</td>'
4261
+ + '<td>' + esc(r.agent_id) + '</td>'
4262
+ + '<td>' + esc(r.channel) + '</td>'
4263
+ + '<td><span style="color:#cf222e;font-weight:600">' + esc(r.plain) + '</span></td>'
4264
+ + '<td class="small">' + esc((r.message_preview || '').slice(0, 60)) + '</td>'
4265
+ + '</tr>';
4266
+ }).join('');
4267
+ }
4268
+
4269
+ function renderRouting(data){
4270
+ var rows = data && data.rows || [];
4271
+ var body = document.getElementById('obs-routing');
4272
+ var empty = document.getElementById('obs-routing-empty');
4273
+ if (!rows.length) { body.innerHTML = ''; empty.hidden = false; return; }
4274
+ empty.hidden = true;
4275
+ body.innerHTML = rows.map(function(r){
4276
+ return '<tr>'
4277
+ + '<td>' + fmtTs(r.at) + '</td>'
4278
+ + '<td>' + esc(r.channel) + '</td>'
4279
+ + '<td class="small">' + esc(r.chat_id) + (r.account_id ? ' / ' + esc(r.account_id) : '') + '</td>'
4280
+ + '<td><span class="ax-obs__pill ax-obs__pill--' + esc(r.kind) + '">' + esc(r.kind || '?') + '</span></td>'
4281
+ + '<td>' + esc(r.deciding_stage) + '</td>'
4282
+ + '<td>' + esc(r.agent_id || '-') + '</td>'
4283
+ + '<td class="small">' + esc(r.reason || '') + '</td>'
4284
+ + '</tr>';
4285
+ }).join('');
4286
+ }
4287
+
4288
+ function renderRotations(data){
4289
+ var rows = data && data.rows || [];
4290
+ var body = document.getElementById('obs-rotations');
4291
+ var empty = document.getElementById('obs-rotations-empty');
4292
+ if (!rows.length) { body.innerHTML = ''; empty.hidden = false; return; }
4293
+ empty.hidden = true;
4294
+ body.innerHTML = rows.map(function(r){
4295
+ return '<tr>'
4296
+ + '<td>' + fmtTs(r.rotated_at) + '</td>'
4297
+ + '<td>' + esc(r.agent_id) + '</td>'
4298
+ + '<td>' + esc(r.channel) + '</td>'
4299
+ + '<td><span class="ax-obs__pill ax-obs__pill--' + esc(r.reason) + '">' + esc(r.reason) + '</span></td>'
4300
+ + '<td class="num">' + (r.last_turn_input_tokens != null ? r.last_turn_input_tokens.toLocaleString() : '-') + '</td>'
4301
+ + '</tr>';
4302
+ }).join('');
4303
+ }
4304
+
4305
+ function kpi(label, value, sub, extraCls){
4306
+ return '<div class="ax-obs__kpi' + (extraCls || '') + '">'
4307
+ + '<div class="ax-obs__kpi-label">' + label + '</div>'
4308
+ + '<div class="ax-obs__kpi-value">' + value + '</div>'
4309
+ + '<div class="ax-obs__kpi-sub">' + (sub || '') + '</div>'
4310
+ + '</div>';
4311
+ }
4312
+
4313
+ function renderBarChart(rows, xKey, valueKey, errorKey, money){
4314
+ if (!rows.length) return '<p class="ax-obs__empty">(no data)</p>';
4315
+ var max = 0;
4316
+ for (var i = 0; i < rows.length; i++) {
4317
+ if (rows[i][valueKey] > max) max = rows[i][valueKey];
4318
+ }
4319
+ if (max === 0) return '<p class="ax-obs__empty">(no activity)</p>';
4320
+ return rows.map(function(r){
4321
+ var v = r[valueKey] || 0;
4322
+ var pct = (v / max) * 100;
4323
+ var label = money ? fmtMoney(v) : fmtNum(v);
4324
+ var tip = esc(r[xKey]) + ': ' + label + (errorKey && r[errorKey] ? ' (' + r[errorKey] + ' errors)' : '');
4325
+ var errPct = errorKey && v > 0 ? Math.round((r[errorKey] / v) * 100) + '%' : '0%';
4326
+ var cls = errorKey && r[errorKey] > 0 ? 'ax-obs__bar ax-obs__bar--with-error' : 'ax-obs__bar';
4327
+ return '<div class="' + cls + '" style="height:' + pct + '%;--err-pct:' + errPct + '" data-tooltip="' + tip + '"></div>';
4328
+ }).join('');
4329
+ }
4330
+
4331
+ document.getElementById('obs-refresh').addEventListener('click', function(){ load(current); });
4332
+ document.getElementById('obs-agent').addEventListener('change', function(){ load(current); });
4333
+ var lim = document.getElementById('obs-limit');
4334
+ if (lim) lim.addEventListener('change', function(){ load(current); });
4335
+
4336
+ document.getElementById('obs-doctor-run').addEventListener('click', loadDoctor);
4337
+ document.getElementById('obs-doctor-running').addEventListener('change', loadDoctor);
4338
+ wireLogsToolbar();
4339
+ document.getElementById('obs-logs-filter').addEventListener('input', function(){
4340
+ // re-render existing buffered lines through the filter
4341
+ var body = document.getElementById('obs-logs-body');
4342
+ var filter = (this.value || '').toLowerCase();
4343
+ Array.from(body.children).forEach(function(s){
4344
+ s.style.display = (filter && s.textContent.toLowerCase().indexOf(filter) === -1) ? 'none' : '';
4345
+ });
4346
+ });
4347
+
4348
+ load(current);
4349
+ })();
4350
+ `;var Xt={opus:{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},sonnet:{input:3,output:15,cacheRead:.3,cacheCreate:3.75},haiku:{input:.25,output:1.25,cacheRead:.025,cacheCreate:.3125}};function Vt(t){let e=(t||"").toLowerCase();return e.includes("opus")?"opus":e.includes("haiku")?"haiku":"sonnet"}function Ke(t){let e=Xt[Vt(t.model)],a=t.input_tokens/1e6*e.input+t.output_tokens/1e6*e.output+t.cache_read_tokens/1e6*e.cacheRead+t.cache_create_tokens/1e6*e.cacheCreate,n=(t.tier2_input_tokens??0)/1e6*e.input*.5+(t.tier2_output_tokens??0)/1e6*e.output*.5+(t.tier2_cache_read_tokens??0)/1e6*e.cacheRead*.5+(t.tier2_cache_create_tokens??0)/1e6*e.cacheCreate*.5;return a+n}function br(){let t=hr(process.cwd(),".agentx/db.sqlite");if(!fr(t))return null;try{let e=new vr(t,{readonly:!0,fileMustExist:!0});return{db:e,close:()=>e.close()}}catch{return null}}function Yt(t,e,a=[]){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Jt({peers:a}))}function xr(t){let e=new URL(t.url||"/","http://_"),a=parseInt(e.searchParams.get("limit")||"50",10),n=Number.isFinite(a)?Math.max(1,Math.min(500,a)):50,r=e.searchParams.get("agent")||void 0;return{limit:n,agent:r&&r.length>0?r:void 0}}function J(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}async function Qt(t,e,a){if(!a.startsWith("/api/admin/observability/"))return!1;let n=a.replace(/^\/api\/admin\/observability\//,"").split("?")[0],r=br();if(!r)return J(e,503,{error:"operational SQLite not available",rows:[]}),!0;try{let{limit:o,agent:s}=xr(t);if(n==="routing"){let i=[],l={limit:o};s&&(i.push("agent_id = @agent"),l.agent=s);let d=`
4351
+ SELECT at, channel, chat_id, account_id, kind, deciding_stage, agent_id, reason
4352
+ FROM route_traces
4353
+ ${i.length?"WHERE "+i.join(" AND "):""}
4354
+ ORDER BY at DESC LIMIT @limit
4355
+ `,c=r.db.prepare(d).all(l);return J(e,200,{rows:c}),!0}if(n==="rotations"){let i=[],l={limit:o};s&&(i.push("agent_id = @agent"),l.agent=s);let d=`
4356
+ SELECT rotated_at, agent_id, channel, reason, last_turn_input_tokens
4357
+ FROM rotations
4358
+ ${i.length?"WHERE "+i.join(" AND "):""}
4359
+ ORDER BY rotated_at DESC LIMIT @limit
4360
+ `,c=r.db.prepare(d).all(l);return J(e,200,{rows:c}),!0}if(n==="errors"){let i=["status = 'error'"],l={limit:o};s&&(i.push("agent_id = @agent"),l.agent=s);let d=`
4361
+ SELECT started_at, agent_id, channel, message_preview, error
4362
+ FROM task_history
4363
+ WHERE ${i.join(" AND ")}
4364
+ ORDER BY started_at DESC LIMIT @limit
4365
+ `,u=r.db.prepare(d).all(l).map(h=>({...h,plain:Zt(h.error)}));return J(e,200,{rows:u}),!0}return n==="summary"||n==="overview"?(J(e,200,yr(r.db)),!0):n==="activity"?(J(e,200,wr(r.db,{agent:s})),!0):n==="cost"?(J(e,200,kr(r.db)),!0):(J(e,404,{error:"unknown observability slice",rows:[]}),!0)}catch(o){return J(e,500,{error:o?.message??String(o),rows:[]}),!0}finally{r.close()}}function Zt(t){let e=String(t||"");return e?/timed out|SIGTERM|exit 143|maxExecutionMinutes/i.test(e)?"Agent ran past its time limit":/ECONNREFUSED|fetch failed|getaddrinfo|ENOTFOUND/i.test(e)?"Couldn't reach a service over the network":/401|403|Unauthorized|Forbidden|invalid token|missing scope/i.test(e)?"Authentication or permission denied":/rate limit|429|too many requests/i.test(e)?"Hit a rate limit (too many calls)":/quota|insufficient_quota/i.test(e)?"Provider account out of quota or credits":/tier-?2|tier two|200K|200,000/i.test(e)?"Hit Anthropic tier-2 billing threshold":/JSON|parse|unexpected token/i.test(e)?"Got a response in the wrong shape":/permission denied|EACCES|EPERM/i.test(e)?"File permission denied":/ENOENT|no such file/i.test(e)?"Expected file is missing":/abort|cancel/i.test(e)?"Task was cancelled before it finished":e.split(`
4366
+ `)[0].slice(0,80)+(e.length>80?"\u2026":""):"unknown failure"}function yr(t){let e=new Date,a=new Date(e.getFullYear(),e.getMonth(),e.getDate()).toISOString(),n=new Date(e.getTime()-864e5),r=new Date(n.getFullYear(),n.getMonth(),n.getDate()).toISOString(),o=new Date(e.getTime()-864e5).toISOString(),s=t.prepare("SELECT COUNT(*) AS n FROM task_history WHERE started_at >= ?").get(a).n,i=t.prepare("SELECT COUNT(*) AS n FROM task_history WHERE started_at >= ? AND started_at < ?").get(r,a).n,l=t.prepare("SELECT COUNT(*) AS n FROM task_history WHERE status='error' AND started_at >= ?").get(a).n,d=t.prepare("SELECT AVG(duration_ms) AS d FROM task_history WHERE status='ok' AND started_at >= ?").get(a).d??0,c=t.prepare("SELECT duration_ms FROM task_history WHERE status='ok' AND started_at >= ? AND duration_ms IS NOT NULL ORDER BY duration_ms ASC").all(a),u=T=>c.length?c[Math.min(c.length-1,Math.floor(c.length*T))].duration_ms:0,h=u(.5),x=u(.95),m=t.prepare("SELECT * FROM usage_daily WHERE day = date('now')").all().reduce((T,M)=>T+Ke(M),0),f=t.prepare("SELECT * FROM usage_daily WHERE day = date('now', '-1 day')").all().reduce((T,M)=>T+Ke(M),0),w=t.prepare(`
4367
+ SELECT agent_id,
4368
+ COUNT(*) AS tasks,
4369
+ SUM(CASE WHEN status='error' THEN 1 ELSE 0 END) AS errors,
4370
+ ROUND(AVG(duration_ms)) AS avg_duration_ms,
4371
+ MAX(started_at) AS last_active
4372
+ FROM task_history
4373
+ WHERE started_at >= ?
4374
+ GROUP BY agent_id
4375
+ ORDER BY tasks DESC LIMIT 10
4376
+ `).all(a),b=t.prepare(`
4377
+ SELECT COALESCE(channel, 'unknown') AS channel,
4378
+ COUNT(*) AS tasks,
4379
+ SUM(CASE WHEN status='error' THEN 1 ELSE 0 END) AS errors
4380
+ FROM task_history
4381
+ WHERE started_at >= ?
4382
+ GROUP BY channel
4383
+ ORDER BY tasks DESC
4384
+ `).all(a),g=t.prepare("SELECT DISTINCT agent_id FROM usage_daily WHERE day >= date('now', '-7 day')").all().map(T=>T.agent_id),v=new Set(t.prepare("SELECT DISTINCT agent_id FROM task_history WHERE started_at >= ?").all(o).map(T=>T.agent_id)),C=g.filter(T=>!v.has(T)),_=t.prepare(`
4385
+ SELECT started_at, agent_id, channel, message_preview, error
4386
+ FROM task_history
4387
+ WHERE status='error' AND started_at >= ?
4388
+ ORDER BY started_at DESC LIMIT 5
4389
+ `).all(o).map(T=>({...T,plain:Zt(T.error)}));return{kpis:{tasksToday:s,tasksDelta:s-i,errorsToday:l,errorRate:s>0?l/s:0,avgDurationMs:Math.round(d),p50DurationMs:h,p95DurationMs:x,spendToday:m,spendDelta:m-f,idleAgentCount:C.length},perAgent:w,perChannel:b,idleAgents:C,recentErrors:_}}function wr(t,e){let a=new Date(Date.now()-864e5).toISOString(),n=new Date(Date.now()-7*864e5).toISOString(),r=e.agent?"AND agent_id = @agent":"",o=e.agent?{agent:e.agent}:{},s=t.prepare(`
4390
+ SELECT strftime('%Y-%m-%dT%H:00', started_at) AS hour,
4391
+ COUNT(*) AS tasks,
4392
+ SUM(CASE WHEN status='error' THEN 1 ELSE 0 END) AS errors
4393
+ FROM task_history
4394
+ WHERE started_at >= @since ${r}
4395
+ GROUP BY hour
4396
+ ORDER BY hour
4397
+ `).all({since:a,...o}),i=t.prepare(`
4398
+ SELECT date(started_at) AS day,
4399
+ COUNT(*) AS tasks,
4400
+ SUM(CASE WHEN status='error' THEN 1 ELSE 0 END) AS errors
4401
+ FROM task_history
4402
+ WHERE started_at >= @since ${r}
4403
+ GROUP BY day
4404
+ ORDER BY day
4405
+ `).all({since:n,...o}),l=t.prepare(`
4406
+ SELECT agent_id,
4407
+ COUNT(*) AS tasks_7d,
4408
+ SUM(CASE WHEN started_at >= @last24 THEN 1 ELSE 0 END) AS tasks_24h,
4409
+ SUM(CASE WHEN status='error' THEN 1 ELSE 0 END) AS errors_7d,
4410
+ ROUND(AVG(duration_ms)) AS avg_duration_ms,
4411
+ MAX(started_at) AS last_active
4412
+ FROM task_history
4413
+ WHERE started_at >= @since
4414
+ GROUP BY agent_id
4415
+ ORDER BY tasks_24h DESC, tasks_7d DESC
4416
+ `).all({since:n,last24:a});return{hourly:s,daily:i,perAgent:l}}function kr(t){let e=t.prepare(`
4417
+ SELECT day, agent_id, model,
4418
+ input_tokens, output_tokens, cache_read_tokens, cache_create_tokens,
4419
+ tier2_input_tokens, tier2_output_tokens, tier2_cache_read_tokens, tier2_cache_create_tokens,
4420
+ tasks
4421
+ FROM usage_daily
4422
+ WHERE day >= date('now', '-30 day')
4423
+ ORDER BY day
4424
+ `).all(),a=new Date().toISOString().slice(0,10),n=new Date(Date.now()-864e5).toISOString().slice(0,10),r=new Date(Date.now()-7*864e5).toISOString().slice(0,10),o=0,s=0,i=0,l=0,d=0,c=0,u={},h={};for(let m of e){let f=Ke(m);l+=f,m.day>=r&&(i+=f),m.day===a&&(o+=f),m.day===n&&(s+=f),u[m.day]=(u[m.day]||0)+f;let w=Vt(m.model),b=Xt[w],g=(m.tier2_input_tokens||0)/1e6*b.input*.5+(m.tier2_output_tokens||0)/1e6*b.output*.5+(m.tier2_cache_read_tokens||0)/1e6*b.cacheRead*.5+(m.tier2_cache_create_tokens||0)/1e6*b.cacheCreate*.5;m.day===a&&(d+=g),m.day>=r&&(c+=g);let v=h[m.agent_id]||(h[m.agent_id]={spend:0,tasks:0,tier2:0});v.spend+=f,v.tasks+=m.tasks||0,v.tier2+=g}let x=Object.entries(h).map(([m,f])=>({agent_id:m,spend:f.spend,tasks:f.tasks,tier2:f.tier2})).sort((m,f)=>f.spend-m.spend),p=Object.entries(u).map(([m,f])=>({day:m,spend:f})).sort((m,f)=>m.day<f.day?-1:1);return{kpis:{spendToday:o,spendYesterday:s,spend7d:i,spend30d:l,tier2Today:d,tier2_7d:c,tier2PctToday:o>0?d/o:0},trend:p,perAgent:x}}import{resolve as _r}from"path";import{existsSync as Er}from"fs";import Sr from"better-sqlite3";function Tr(){let t=_r(process.cwd(),".agentx/intent/ledger.sqlite");if(!Er(t))return null;try{let e=new Sr(t,{readonly:!0,fileMustExist:!0});return{db:e,close:()=>e.close()}}catch{return null}}function Z(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function Cr(t){if(!t)return;let e=String(t).trim().match(/^(\d+)\s*(s|m|h|d|w)?$/i);if(!e)return;let a=parseInt(e[1],10),n=(e[2]||"h").toLowerCase(),r=n==="s"?1e3:n==="m"?6e4:n==="h"?36e5:n==="d"?864e5:n==="w"?7*864e5:0;return Date.now()-a*r}async function ea(t,e,a){if(!a.startsWith("/api/admin/ledger/"))return!1;let n=a.replace(/^\/api\/admin\/ledger\//,"").split("?")[0],r=Tr();if(!r)return Z(e,503,{error:"intent ledger not available",rows:[]}),!0;try{let o=new URL(t.url||"/","http://_"),s=Cr(o.searchParams.get("since")),i=o.searchParams.get("source")||"",l=o.searchParams.get("project")||"",d=parseInt(o.searchParams.get("limit")||"50",10),c=Number.isFinite(d)?Math.max(1,Math.min(500,d)):50;if(n==="stats"){let u=s!==void 0?"WHERE ts >= @since":"",h=s!==void 0?"WHERE decided_at >= @since":"",x=r.db.prepare(`SELECT source, COUNT(*) AS n FROM intent_events ${u} GROUP BY source ORDER BY n DESC`).all({since:s}),p=r.db.prepare(`SELECT COUNT(*) AS n FROM intent_decisions ${h}`).get({since:s}).n,m=r.db.prepare(`SELECT outcome, COUNT(*) AS n FROM intent_decisions ${h} GROUP BY outcome ORDER BY n DESC`).all({since:s}),f=r.db.prepare(`SELECT COUNT(*) AS n FROM intent_divergences ${u}`).get({since:s}).n,w=r.db.prepare(`SELECT source, COUNT(*) AS n FROM intent_divergences ${u} GROUP BY source ORDER BY n DESC`).all({since:s}),b=r.db.prepare(`
4425
+ SELECT COUNT(*) AS n FROM intent_decisions d
4426
+ LEFT JOIN intent_resolutions r
4427
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
4428
+ WHERE d.outcome = 'dispatched' AND r.decision_event_id IS NULL
4429
+ `).get().n;return Z(e,200,{since:s??null,eventsBySource:x,totalDecisions:p,decisionsByOutcome:m,totalDivergences:f,divergencesBySource:w,inFlight:b}),!0}if(n==="events"){let u=[],h={limit:c};s!==void 0&&(u.push("ts >= @since"),h.since=s),i&&(u.push("source = @source"),h.source=i),l&&(u.push("project = @project"),h.project=l);let x=`
4430
+ SELECT id, ts, source, source_event_id, project, subject, intent
4431
+ FROM intent_events
4432
+ ${u.length?"WHERE "+u.join(" AND "):""}
4433
+ ORDER BY ts DESC LIMIT @limit
4434
+ `;return Z(e,200,{rows:r.db.prepare(x).all(h)}),!0}if(n==="divergences"){let u=[],h={limit:c};s!==void 0&&(u.push("ts >= @since"),h.since=s),i&&(u.push("source = @source"),h.source=i);let x=`
4435
+ SELECT ts, source, decided_by,
4436
+ legacy_agent_id, legacy_outcome, legacy_reason,
4437
+ ledger_agent_id, ledger_outcome, ledger_reason,
4438
+ event_id
4439
+ FROM intent_divergences
4440
+ ${u.length?"WHERE "+u.join(" AND "):""}
4441
+ ORDER BY ts DESC LIMIT @limit
4442
+ `;return Z(e,200,{rows:r.db.prepare(x).all(h)}),!0}if(n==="active"){let u=["d.outcome = 'dispatched'","r.decision_event_id IS NULL"],h={limit:c};i&&(u.push("e.source = @source"),h.source=i);let x=`
4443
+ SELECT e.id AS event_id, e.ts, e.source, e.project, e.subject, e.intent,
4444
+ d.decided_by, d.decided_at, d.agent_id, d.reason
4445
+ FROM intent_decisions d
4446
+ JOIN intent_events e ON e.id = d.event_id
4447
+ LEFT JOIN intent_resolutions r
4448
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
4449
+ WHERE ${u.join(" AND ")}
4450
+ ORDER BY d.decided_at DESC LIMIT @limit
4451
+ `;return Z(e,200,{rows:r.db.prepare(x).all(h)}),!0}return Z(e,404,{error:"unknown ledger slice",rows:[]}),!0}catch(o){return Z(e,500,{error:o?.message??String(o),rows:[]}),!0}finally{r.close()}}function ta(t={}){return A({title:"AgentX \xB7 Ledger",activeTab:"graph",subtitle:"Ledger",body:`<div class="ax-led">
4452
+ <header class="ax-led__head">
4453
+ <h1>Intent ledger</h1>
4454
+ <p class="ax-led__sub">Read-only view over <code>.agentx/intent/ledger.sqlite</code> \u2014 same source as <code>agentx ledger</code>. Replay and lineage live in the CLI for now.</p>
4455
+ <nav class="ax-led__tabs" role="tablist">
4456
+ <button class="ax-led__tab is-active" data-tab="stats" role="tab">Stats</button>
4457
+ <button class="ax-led__tab" data-tab="events" role="tab">Events</button>
4458
+ <button class="ax-led__tab" data-tab="divergences" role="tab">Divergences</button>
4459
+ <button class="ax-led__tab" data-tab="active" role="tab">In-flight</button>
4460
+ </nav>
4461
+ <div class="ax-led__filters">
4462
+ <label>Since
4463
+ <select id="led-since">
4464
+ <option value="">all-time</option>
4465
+ <option value="1h">last 1h</option>
4466
+ <option value="24h" selected>last 24h</option>
4467
+ <option value="7d">last 7d</option>
4468
+ <option value="30d">last 30d</option>
4469
+ </select>
4470
+ </label>
4471
+ <label>Source
4472
+ <input id="led-source" type="text" placeholder="telegram, gitlab, mesh, \u2026" />
4473
+ </label>
4474
+ <label>Limit
4475
+ <input id="led-limit" type="number" min="1" max="500" value="50" />
4476
+ </label>
4477
+ <button id="led-refresh" type="button">Refresh</button>
4478
+ </div>
4479
+ </header>
4480
+
4481
+ <section class="ax-led__pane is-active" data-pane="stats">
4482
+ <div class="ax-led__kpis" id="led-stats-kpis"></div>
4483
+ <div class="ax-led__split">
4484
+ <div class="ax-led__card"><h3>Events by source</h3><table class="ax-led__table"><thead><tr><th>source</th><th class="num">count</th></tr></thead><tbody id="led-stats-events"></tbody></table></div>
4485
+ <div class="ax-led__card"><h3>Decisions by outcome</h3><table class="ax-led__table"><thead><tr><th>outcome</th><th class="num">count</th></tr></thead><tbody id="led-stats-decisions"></tbody></table></div>
4486
+ <div class="ax-led__card"><h3>Divergences by source</h3><table class="ax-led__table"><thead><tr><th>source</th><th class="num">count</th></tr></thead><tbody id="led-stats-divergences"></tbody></table></div>
4487
+ </div>
4488
+ </section>
4489
+
4490
+ <section class="ax-led__pane" data-pane="events">
4491
+ <div class="ax-led__table-wrap"><table class="ax-led__table">
4492
+ <thead><tr><th>at</th><th>source</th><th>intent</th><th>project</th><th>subject</th><th class="mono small">id</th></tr></thead>
4493
+ <tbody id="led-events"></tbody>
4494
+ </table></div>
4495
+ <p class="ax-led__empty" id="led-events-empty" hidden>(no rows)</p>
4496
+ </section>
4497
+
4498
+ <section class="ax-led__pane" data-pane="divergences">
4499
+ <p class="ax-led__hint">When the legacy router and the ledger disagree on what to dispatch \u2014 agentx logs both decisions and the difference here. A growing count means the new ledger is observing edge cases the old code path missed (or vice versa); investigate.</p>
4500
+ <div class="ax-led__table-wrap"><table class="ax-led__table">
4501
+ <thead><tr><th>at</th><th>source</th><th>decided by</th><th>legacy \u2192 agent</th><th>ledger \u2192 agent</th><th>reason</th></tr></thead>
4502
+ <tbody id="led-divergences"></tbody>
4503
+ </table></div>
4504
+ <p class="ax-led__empty" id="led-divergences-empty" hidden>(no rows)</p>
4505
+ </section>
4506
+
4507
+ <section class="ax-led__pane" data-pane="active">
4508
+ <p class="ax-led__hint">Decisions where the dispatch landed but no resolution row exists yet. Long-lived rows here are the operationally interesting ones \u2014 they're the running tasks.</p>
4509
+ <div class="ax-led__table-wrap"><table class="ax-led__table">
4510
+ <thead><tr><th>started</th><th>source</th><th>agent</th><th>intent</th><th>subject</th><th>reason</th></tr></thead>
4511
+ <tbody id="led-active"></tbody>
4512
+ </table></div>
4513
+ <p class="ax-led__empty" id="led-active-empty" hidden>(no rows)</p>
4514
+ </section>
4515
+ </div>`,css:`
4516
+ .ax-led{padding:24px 28px;max-width:1240px;margin:0 auto}
4517
+ .ax-led__head h1{font-family:'IBM Plex Sans',sans-serif;font-weight:600;font-size:20px;letter-spacing:-0.01em;margin:0 0 6px}
4518
+ .ax-led__sub{color:var(--ax-muted);font-size:12px;margin:0 0 12px}
4519
+ .ax-led__tabs{display:flex;gap:2px;border-bottom:1px solid var(--ax-border);margin-bottom:12px;align-items:center}
4520
+ .ax-led__tab{font:inherit;font-size:12px;background:none;border:0;border-bottom:2px solid transparent;color:var(--ax-muted);padding:8px 12px;cursor:pointer}
4521
+ .ax-led__tab:hover{color:var(--ax-text)}
4522
+ .ax-led__tab.is-active{color:var(--ax-text);border-bottom-color:var(--ax-accent,#3a7bd5)}
4523
+ .ax-led__filters{display:flex;align-items:center;gap:14px;margin-bottom:12px;flex-wrap:wrap}
4524
+ .ax-led__filters label{font-size:11px;color:var(--ax-muted);display:inline-flex;align-items:center;gap:6px}
4525
+ .ax-led__filters input,.ax-led__filters select{font-family:'IBM Plex Mono',monospace;font-size:12px;padding:4px 8px;background:var(--ax-bg-elev);border:1px solid var(--ax-border);border-radius:4px;color:var(--ax-fg)}
4526
+ .ax-led__filters button{font-size:11px;padding:5px 11px;border-radius:4px;border:1px solid var(--ax-border);background:var(--ax-bg);color:var(--ax-fg);cursor:pointer}
4527
+ .ax-led__filters button:hover{background:var(--ax-surface)}
4528
+ .ax-led__pane{display:none}
4529
+ .ax-led__pane.is-active{display:block}
4530
+ .ax-led__kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:14px}
4531
+ .ax-led__kpi{background:var(--ax-bg-elev);border:1px solid var(--ax-border);border-radius:6px;padding:10px 12px}
4532
+ .ax-led__kpi-label{font-size:10px;text-transform:uppercase;letter-spacing:0.04em;color:var(--ax-muted);font-weight:600;margin-bottom:4px}
4533
+ .ax-led__kpi-value{font-family:'IBM Plex Mono',monospace;font-size:18px;font-weight:600}
4534
+ .ax-led__split{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}
4535
+ .ax-led__card{background:var(--ax-bg-elev);border:1px solid var(--ax-border);border-radius:6px;padding:12px 14px}
4536
+ .ax-led__card h3{font-family:'IBM Plex Sans',sans-serif;font-size:12px;font-weight:600;margin:0 0 8px;color:var(--ax-fg);text-transform:uppercase;letter-spacing:0.04em}
4537
+ .ax-led__table{width:100%;border-collapse:collapse;font-size:11px;font-family:'IBM Plex Mono',monospace}
4538
+ .ax-led__table thead th{text-align:left;padding:5px 7px;border-bottom:1px solid var(--ax-border);font-weight:500;color:var(--ax-muted);font-size:10px;text-transform:uppercase;letter-spacing:0.04em}
4539
+ .ax-led__table tbody td{padding:5px 7px;border-bottom:1px solid var(--ax-border);vertical-align:top}
4540
+ .ax-led__table tr:hover td{background:var(--ax-surface)}
4541
+ .ax-led__table .num{text-align:right;font-variant-numeric:tabular-nums}
4542
+ .ax-led__table .small{color:var(--ax-muted);font-size:10px}
4543
+ .ax-led__table .mono{font-family:'IBM Plex Mono',monospace}
4544
+ .ax-led__table-wrap{overflow-x:auto;max-height:calc(100vh - 280px);overflow-y:auto;border:1px solid var(--ax-border);border-radius:6px}
4545
+ .ax-led__empty{color:var(--ax-muted);font-style:italic;padding:18px 0;margin:0}
4546
+ .ax-led__hint{color:var(--ax-muted);font-size:11px;margin:0 0 8px}
4547
+ .ax-led__pill{display:inline-block;padding:1px 6px;border-radius:3px;font-size:10px;background:var(--ax-bg);border:1px solid var(--ax-border)}
4548
+ .ax-led__pill--err{color:var(--ax-err,#e74c3c);border-color:rgba(231,76,60,0.5)}
4549
+ .ax-led__pill--ok{color:#2ecc71}
4550
+ `,scripts:`<script>
4551
+ (function(){
4552
+ var current = 'stats';
4553
+ function $(id){ return document.getElementById(id); }
4554
+ function esc(s){ if (s == null) return ''; return String(s).replace(/[&<>"']/g, function(c){ return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[c]; }); }
4555
+ function fmtTs(ms){ if (!ms) return ''; var d = new Date(Number(ms)); var pad = function(n){ return n < 10 ? '0' + n : n; }; return d.getMonth()+1 + '/' + d.getDate() + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()); }
4556
+ function fmtNum(n){ return (n||0).toLocaleString(); }
4557
+ function kpi(label, value){ return '<div class="ax-led__kpi"><div class="ax-led__kpi-label">' + esc(label) + '</div><div class="ax-led__kpi-value">' + value + '</div></div>'; }
4558
+
4559
+ document.querySelectorAll('.ax-led__tab').forEach(function(t){
4560
+ t.addEventListener('click', function(){
4561
+ document.querySelectorAll('.ax-led__tab').forEach(function(x){ x.classList.toggle('is-active', x === t); });
4562
+ document.querySelectorAll('.ax-led__pane').forEach(function(p){ p.classList.toggle('is-active', p.dataset.pane === t.dataset.tab); });
4563
+ current = t.dataset.tab;
4564
+ load();
4565
+ });
4566
+ });
4567
+ $('led-refresh').addEventListener('click', load);
4568
+ $('led-since').addEventListener('change', load);
4569
+ $('led-source').addEventListener('change', load);
4570
+ $('led-limit').addEventListener('change', load);
4571
+
4572
+ async function load(){
4573
+ var since = $('led-since').value;
4574
+ var source = $('led-source').value.trim();
4575
+ var limit = $('led-limit').value || 50;
4576
+ var qs = '?limit=' + encodeURIComponent(limit);
4577
+ if (since) qs += '&since=' + encodeURIComponent(since);
4578
+ if (source) qs += '&source=' + encodeURIComponent(source);
4579
+ try {
4580
+ var r = await fetch('/api/admin/ledger/' + current + qs, { credentials: 'same-origin' });
4581
+ var data = await r.json();
4582
+ render(current, data);
4583
+ } catch (e) {
4584
+ console.error('ledger fetch failed', e);
4585
+ }
4586
+ }
4587
+ function render(tab, data){
4588
+ if (tab === 'stats') return renderStats(data);
4589
+ if (tab === 'events') return renderEvents(data);
4590
+ if (tab === 'divergences') return renderDivergences(data);
4591
+ if (tab === 'active') return renderActive(data);
4592
+ }
4593
+ function renderStats(d){
4594
+ $('led-stats-kpis').innerHTML = [
4595
+ kpi('Decisions', fmtNum(d.totalDecisions)),
4596
+ kpi('Divergences', fmtNum(d.totalDivergences)),
4597
+ kpi('In-flight', fmtNum(d.inFlight)),
4598
+ ].join('');
4599
+ $('led-stats-events').innerHTML = (d.eventsBySource || []).map(function(r){
4600
+ return '<tr><td>' + esc(r.source) + '</td><td class="num">' + fmtNum(r.n) + '</td></tr>';
4601
+ }).join('') || '<tr><td colspan="2" class="small">(none)</td></tr>';
4602
+ $('led-stats-decisions').innerHTML = (d.decisionsByOutcome || []).map(function(r){
4603
+ return '<tr><td>' + esc(r.outcome) + '</td><td class="num">' + fmtNum(r.n) + '</td></tr>';
4604
+ }).join('') || '<tr><td colspan="2" class="small">(none)</td></tr>';
4605
+ $('led-stats-divergences').innerHTML = (d.divergencesBySource || []).map(function(r){
4606
+ return '<tr><td>' + esc(r.source) + '</td><td class="num">' + fmtNum(r.n) + '</td></tr>';
4607
+ }).join('') || '<tr><td colspan="2" class="small">(none)</td></tr>';
4608
+ }
4609
+ function renderEvents(d){
4610
+ var rows = d.rows || [];
4611
+ $('led-events-empty').hidden = rows.length > 0;
4612
+ $('led-events').innerHTML = rows.map(function(r){
4613
+ return '<tr>' +
4614
+ '<td>' + esc(fmtTs(r.ts)) + '</td>' +
4615
+ '<td><span class="ax-led__pill">' + esc(r.source) + '</span></td>' +
4616
+ '<td>' + esc(r.intent || '') + '</td>' +
4617
+ '<td>' + esc(r.project || '') + '</td>' +
4618
+ '<td class="small">' + esc((r.subject || '').slice(0, 80)) + '</td>' +
4619
+ '<td class="small mono">' + esc((r.id || '').slice(0, 16)) + '</td>' +
4620
+ '</tr>';
4621
+ }).join('');
4622
+ }
4623
+ function renderDivergences(d){
4624
+ var rows = d.rows || [];
4625
+ $('led-divergences-empty').hidden = rows.length > 0;
4626
+ $('led-divergences').innerHTML = rows.map(function(r){
4627
+ var legacy = (r.legacy_outcome || '') + (r.legacy_agent_id ? ' \u2192 ' + r.legacy_agent_id : '');
4628
+ var ledger = (r.ledger_outcome || '') + (r.ledger_agent_id ? ' \u2192 ' + r.ledger_agent_id : '');
4629
+ var reason = (r.legacy_reason || r.ledger_reason || '').slice(0, 100);
4630
+ return '<tr>' +
4631
+ '<td>' + esc(fmtTs(r.ts)) + '</td>' +
4632
+ '<td><span class="ax-led__pill">' + esc(r.source) + '</span></td>' +
4633
+ '<td>' + esc(r.decided_by || '') + '</td>' +
4634
+ '<td>' + esc(legacy) + '</td>' +
4635
+ '<td>' + esc(ledger) + '</td>' +
4636
+ '<td class="small">' + esc(reason) + '</td>' +
4637
+ '</tr>';
4638
+ }).join('');
4639
+ }
4640
+ function renderActive(d){
4641
+ var rows = d.rows || [];
4642
+ $('led-active-empty').hidden = rows.length > 0;
4643
+ $('led-active').innerHTML = rows.map(function(r){
4644
+ return '<tr>' +
4645
+ '<td>' + esc(fmtTs(r.decided_at)) + '</td>' +
4646
+ '<td><span class="ax-led__pill">' + esc(r.source) + '</span></td>' +
4647
+ '<td>' + esc(r.agent_id || '') + '</td>' +
4648
+ '<td>' + esc(r.intent || '') + '</td>' +
4649
+ '<td class="small">' + esc((r.subject || '').slice(0, 80)) + '</td>' +
4650
+ '<td class="small">' + esc((r.reason || '').slice(0, 80)) + '</td>' +
4651
+ '</tr>';
4652
+ }).join('');
4653
+ }
4654
+ load();
4655
+ })();
4656
+ </script>`,peers:t.peers,currentPeerId:t.currentPeerId})}function aa(t={}){return A({title:"AgentX \xB7 Cost",activeTab:"cost",subtitle:"Cost",body:`<div class="ax-cost">
4657
+ <header class="ax-cost__head">
4658
+ <h1>Cost</h1>
4659
+ <p class="ax-cost__sub">Anthropic spend across all agents over the last 30 days. Rates: Opus $15/$75 per M, Sonnet $3/$15, Haiku $0.25/$1.25 (input/output).</p>
4660
+ </header>
4661
+
4662
+ <section class="ax-cost__pane">
4663
+ <div class="ax-obs__kpis" id="cost-kpis"></div>
4664
+ <div class="ax-obs__card">
4665
+ <h3>30-day spend trend</h3>
4666
+ <div id="cost-trend" class="ax-obs__chart ax-obs__chart--cost"></div>
4667
+ </div>
4668
+ <div class="ax-obs__card">
4669
+ <h3>Per-agent cost (30 days)</h3>
4670
+ <table class="ax-obs__table">
4671
+ <thead><tr><th>Agent</th><th class="num">Tasks</th><th class="num">Spend</th><th class="num">Tier-2 surcharge</th></tr></thead>
4672
+ <tbody id="cost-agents"></tbody>
4673
+ </table>
4674
+ <p class="ax-obs__empty" id="cost-agents-empty" hidden>No usage in the last 30 days.</p>
4675
+ </div>
4676
+ </section>
4677
+ </div>`,css:`
4678
+ .ax-cost{padding:24px 28px;max-width:1100px;margin:0 auto}
4679
+ .ax-cost__head h1{font-family:'IBM Plex Sans',sans-serif;font-weight:600;font-size:20px;letter-spacing:-0.01em;margin:0 0 6px}
4680
+ .ax-cost__sub{color:var(--ax-muted);font-size:12px;margin:0 0 18px}
4681
+ .ax-cost__pane{display:flex;flex-direction:column;gap:16px}
4682
+ .ax-obs__kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}
4683
+ .ax-obs__kpi{background:var(--ax-bg-elev);border:1px solid var(--ax-border);border-radius:8px;padding:12px 14px}
4684
+ .ax-obs__kpi-label{font-size:10px;text-transform:uppercase;letter-spacing:0.04em;color:var(--ax-muted);font-weight:600;margin-bottom:6px}
4685
+ .ax-obs__kpi-value{font-family:'IBM Plex Mono',monospace;font-size:20px;font-weight:600}
4686
+ .ax-obs__kpi-sub{font-size:11px;color:var(--ax-muted);margin-top:4px}
4687
+ .ax-obs__delta{font-size:11px;color:var(--ax-muted)}
4688
+ .ax-obs__delta--up{color:var(--ax-success)}
4689
+ .ax-obs__delta--down{color:var(--ax-danger)}
4690
+ .ax-obs__card{background:var(--ax-bg-elev);border:1px solid var(--ax-border);border-radius:8px;padding:14px 16px}
4691
+ .ax-obs__card h3{font-family:'IBM Plex Sans',sans-serif;font-size:13px;font-weight:600;margin:0 0 10px;color:var(--ax-fg)}
4692
+ .ax-obs__table{width:100%;border-collapse:collapse;font-size:12px;font-family:'IBM Plex Mono',monospace}
4693
+ .ax-obs__table thead th{text-align:left;padding:6px 8px;border-bottom:1px solid var(--ax-border);font-weight:500;color:var(--ax-muted);font-size:11px;text-transform:uppercase;letter-spacing:0.04em}
4694
+ .ax-obs__table tbody td{padding:6px 8px;border-bottom:1px solid var(--ax-border)}
4695
+ .ax-obs__table .num{text-align:right;font-variant-numeric:tabular-nums}
4696
+ .ax-obs__table .small{font-size:11px;color:var(--ax-muted)}
4697
+ .ax-obs__empty{color:var(--ax-muted);font-size:12px;font-style:italic;margin:8px 0 0}
4698
+ .ax-obs__chart{display:flex;align-items:flex-end;gap:2px;height:120px;padding:8px 0}
4699
+ .ax-obs__chart--cost .bar{background:var(--ax-accent,#3a7bd5)}
4700
+ .ax-obs__chart .bar{flex:1;background:var(--ax-fg);opacity:0.4;min-width:4px;position:relative;border-radius:2px 2px 0 0}
4701
+ .ax-obs__chart .bar:hover{opacity:1}
4702
+ .ax-obs__chart .bar-label{position:absolute;bottom:-18px;left:50%;transform:translateX(-50%);font-size:9px;color:var(--ax-muted);white-space:nowrap}
4703
+ `,scripts:`<script>
4704
+ (function(){
4705
+ function fmtNum(n){ return (n||0).toLocaleString(); }
4706
+ function fmtMoney(n){ if (n == null || isNaN(n)) return '$0'; if (n < 0.01) return '$' + n.toFixed(4); if (n < 1) return '$' + n.toFixed(3); if (n < 100) return '$' + n.toFixed(2); return '$' + Math.round(n).toLocaleString(); }
4707
+ function esc(s){ return String(s||'').replace(/[&<>"']/g, function(c){ return ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]); }); }
4708
+ function kpi(label, value, sub){
4709
+ return '<div class="ax-obs__kpi"><div class="ax-obs__kpi-label">' + esc(label) + '</div><div class="ax-obs__kpi-value">' + value + '</div>' + (sub ? '<div class="ax-obs__kpi-sub">' + sub + '</div>' : '') + '</div>';
4710
+ }
4711
+ function deltaMoney(n){
4712
+ if (Math.abs(n) < 0.001) return '<span class="ax-obs__delta">no change</span>';
4713
+ var arrow = n > 0 ? '\\u25B2' : '\\u25BC';
4714
+ var cls = n > 0 ? 'ax-obs__delta--down' : 'ax-obs__delta--up';
4715
+ return '<span class="ax-obs__delta ' + cls + '">' + arrow + ' ' + fmtMoney(Math.abs(n)) + ' vs yesterday</span>';
4716
+ }
4717
+ function bars(rows, xKey, valueKey){
4718
+ if (!rows || rows.length === 0) return '<div style="color:var(--ax-muted);font-size:11px;padding:20px 0">No data yet.</div>';
4719
+ var max = 0;
4720
+ rows.forEach(function(r){ if ((r[valueKey] || 0) > max) max = r[valueKey]; });
4721
+ if (max === 0) return '<div style="color:var(--ax-muted);font-size:11px;padding:20px 0">All zero.</div>';
4722
+ return rows.map(function(r){
4723
+ var h = Math.max(2, Math.round(((r[valueKey] || 0) / max) * 100));
4724
+ var label = fmtMoney(r[valueKey] || 0);
4725
+ var x = String(r[xKey] || '').slice(5);
4726
+ return '<div class="bar" style="height:' + h + '%" title="' + esc(x + ': ' + label) + '"></div>';
4727
+ }).join('');
4728
+ }
4729
+
4730
+ async function load(){
4731
+ try {
4732
+ var r = await fetch('/api/admin/observability/cost', { credentials: 'same-origin' });
4733
+ var data = await r.json();
4734
+ render(data);
4735
+ } catch (e) {
4736
+ console.error('cost fetch failed', e);
4737
+ }
4738
+ }
4739
+ function render(data){
4740
+ if (!data) return;
4741
+ var k = data.kpis || {};
4742
+ document.getElementById('cost-kpis').innerHTML = [
4743
+ kpi('Today', fmtMoney(k.spendToday), deltaMoney(k.spendToday - (k.spendYesterday || 0))),
4744
+ kpi('Last 7 days', fmtMoney(k.spend7d), 'avg ' + fmtMoney(k.spend7d / 7) + '/day'),
4745
+ kpi('Last 30 days', fmtMoney(k.spend30d), 'avg ' + fmtMoney(k.spend30d / 30) + '/day'),
4746
+ kpi('Tier-2 surcharge today', fmtMoney(k.tier2Today), ((k.tier2PctToday || 0) * 100).toFixed(1) + '% of today\\u2019s spend'),
4747
+ ].join('');
4748
+ document.getElementById('cost-trend').innerHTML = bars(data.trend || [], 'day', 'spend');
4749
+
4750
+ var rows = data.perAgent || [];
4751
+ var emptyEl = document.getElementById('cost-agents-empty');
4752
+ var bodyEl = document.getElementById('cost-agents');
4753
+ if (rows.length === 0) {
4754
+ emptyEl.hidden = false;
4755
+ bodyEl.innerHTML = '';
4756
+ } else {
4757
+ emptyEl.hidden = true;
4758
+ bodyEl.innerHTML = rows.map(function(r){
4759
+ return '<tr>'
4760
+ + '<td>' + esc(r.agent_id) + '</td>'
4761
+ + '<td class="num">' + fmtNum(r.tasks) + '</td>'
4762
+ + '<td class="num">' + fmtMoney(r.spend) + '</td>'
4763
+ + '<td class="num small" ' + (r.tier2 > 0 ? 'style="color:#bf8700"' : '') + '>' + fmtMoney(r.tier2) + '</td>'
4764
+ + '</tr>';
4765
+ }).join('');
4766
+ }
4767
+ }
4768
+ load();
4769
+ setInterval(load, 30000);
4770
+ })();
4771
+ </script>`,peers:t.peers,currentPeerId:t.currentPeerId})}import{resolve as Xe,join as $r}from"path";import{existsSync as Ve,readdirSync as Mr,readFileSync as Ir}from"fs";import Lr from"better-sqlite3";function na(t={}){let e='<div id="ax-fleet-root"></div>';return A({title:"AgentX \xB7 Activity Graph",activeTab:"graph",subtitle:"Activity Graph",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:Ar,scripts:'<script src="/assets/activity-graph.global.js" defer></script>',noMain:!0})}var Ar=`
4772
+ html, body { height: 100%; }
4773
+ body { margin: 0; }
4774
+ #ax-fleet-root { height: calc(100vh - 48px); }
4775
+ #ax-fleet-root:empty::before {
4776
+ content: "Loading fleet snapshot\u2026";
4777
+ display: block;
4778
+ padding: 40px 24px;
4779
+ color: var(--ax-muted);
4780
+ font: 13px/1 -apple-system, BlinkMacSystemFont, sans-serif;
4781
+ text-align: center;
4782
+ }
4783
+ `;function Te(){let t=Xe(process.cwd(),".agentx/intent/ledger.sqlite");if(!Ve(t))return null;try{let e=new Lr(t,{readonly:!0,fileMustExist:!0});return{db:e,close:()=>e.close()}}catch{return null}}function X(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function sa(t,e,a=[]){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(na({peers:a}))}var ie={gitlab:{label:"GitLab",color:"#fc6d26"},github:{label:"GitHub",color:"#6e7681"},telegram:{label:"Telegram",color:"#0088cc"},whatsapp:{label:"WhatsApp",color:"#25d366"},slack:{label:"Slack",color:"#4a154b"},discord:{label:"Discord",color:"#5865f2"},mesh:{label:"Mesh (a2a)",color:"#bf8700"},a2a:{label:"Agent \u2192 Agent",color:"#bf8700"},cron:{label:"Cron",color:"#6b7280"},workflow:{label:"Workflow",color:"#9333ea"},api:{label:"API",color:"#3a7bd5"}};function Dr(t,e,a){if(t==="mesh"){let n=a?.context?.channel;if(typeof n=="string"&&n in ie)return n;if(e.startsWith("mesh.")){let r=e.slice(5);if(r==="a2a")return"a2a";if(r in ie)return r}return"mesh"}if(t==="workflow"){let n=a?.event?.payload,r=typeof n?.channel=="string"?n.channel:"";if(r&&r in ie)return r;let o=n?.issueEvent||n?.webhookEvent;if(o){if(o.user?.username||o.project?.path_with_namespace)return"gitlab";if(o.sender?.login)return"github"}if(e.startsWith("workflow.")){let s=e.slice(9).split("-")[0];if(s in ie)return s}return"workflow"}return t}function Pr(t){let e={mtgl:"#e07a3a",ksi:"#3a7bd5",noqta:"#10b981",hasanah:"#bc8cff","hasanah-lab":"#bc8cff",hexastack:"#06b6d4",hackathonat:"#ec775c",internal:"#6b7280",unmapped:"#d29922"};if(e[t])return e[t];let a=["#e07a3a","#3a7bd5","#10b981","#bc8cff","#ec775c","#bf8700","#5865f2","#cf222e"],n=0;for(let r=0;r<t.length;r++)n=n*31+t.charCodeAt(r)|0;return a[Math.abs(n)%a.length]}function Rr(t,e){if(!t)return!1;let a=e?.agents?.[t];return a&&typeof a.system=="boolean"?a.system:!!(t==="graph-agent"||t==="intent-classifier"||/-classifier$/.test(t))}function ia(t,e){if(!t)return"unmapped";if(e){let n=e.find(r=>r.id===t);if(n?.client)return n.client}let a=t.indexOf("/");return a>0?t.slice(0,a):t}function la(t){let e=new Map;if(!t)return e;let a=t.business;if(!a)return e;let n=a.projects??[],r=a.orgChart??{};for(let s of n){if(!s.pm)continue;let i=s.client||(s.id.includes("/")?s.id.slice(0,s.id.indexOf("/")):s.id);e.has(s.pm)||e.set(s.pm,i)}function o(s,i=new Set){if(e.has(s))return e.get(s);if(i.has(s))return;i.add(s);let l=r[s];if(!l?.reportsTo)return;let d=o(l.reportsTo,i);return d&&e.set(s,d),d}for(let s of Object.keys(r))o(s);return e}function da(t,e,a){if(!a.length)return;let n=String(e?.chatId??e?.message?.chat?.id??""),r=String(e?.sender?.username??e?.message?.from?.username??""),o=String(e?.sender?.id??e?.message?.from?.id??""),s=a.filter(i=>!i.channel||i.channel===t);for(let i of s)if(i.chatId&&n&&i.chatId===n)return i;for(let i of s)if(i.username&&r&&i.username===r)return i;for(let i of s)if(i.senderId&&o&&i.senderId===o)return i;for(let i of s)if(!i.chatId&&!i.username&&!i.senderId&&i.channel===t)return i}function Or(t,e,a){if(!a||typeof a!="object")return null;if(t==="mesh"){if(typeof a.senderAgentId=="string"&&a.senderAgentId.trim()){let r=a.senderAgentId.trim();return{id:r,name:r,avatar:L(r),kind:"a2a"}}let n=a.context&&typeof a.context=="object"?a.context:null;if(n){let r=typeof n.senderUsername=="string"?n.senderUsername:null,o=typeof n.sender=="string"?n.sender:null,s=typeof n.senderId=="string"?n.senderId:null;if(o&&o.startsWith("agent:")){let l=o.slice(6);if(l&&!Je(l))return{id:l,name:l,avatar:L(l),kind:"a2a"}}let i=o||r||s;if(i&&!Je(r||o||"")){let l=r||o||s,d=e==="mesh.github"?"github":e==="mesh.gitlab"?"gitlab":e==="mesh.a2a"?"a2a":"mesh";return{id:l,name:o||r||l,avatar:L(i),kind:d}}}return e==="mesh.task"||e==="mesh.a2a"?{id:"__a2a",name:"Internal (A2A)",avatar:"\u21BB",kind:"a2a"}:null}if(t==="cron"){let n=typeof a.jobId=="string"?a.jobId:null;if(n)return{id:`cron:${n}`,name:`Cron: ${n}`,avatar:"\u23F1",kind:"cron"}}if(t==="workflow"){let n=a.event?.payload;if(n&&typeof n=="object"){if(n.sender&&typeof n.sender=="object"){let s=n.sender,i=typeof s.username=="string"?s.username:null,l=typeof s.name=="string"?s.name:null,d=i||(s.id!=null?String(s.id):null)||l;if(d&&!Je(i||l||"")){let c=typeof n.channel=="string"?n.channel:"",u=ra(c);return{id:d,name:l||i||d,avatar:L(l||i||d),kind:u}}}let o=n.issueEvent||n.webhookEvent;if(o&&typeof o=="object"){let s=o.user;if(s&&typeof s=="object"&&(s.username||s.name)){let l=s.username||s.name,d=s.name||s.username;return{id:l,name:d,avatar:L(d),kind:"gitlab"}}let i=o.sender;if(i?.login)return{id:i.login,name:i.login,avatar:L(i.login),kind:"github"}}}let r=typeof a.workflowId=="string"&&a.workflowId||typeof a.runId=="string"&&a.runId||typeof a.workflow=="string"&&a.workflow||typeof a.event?.id=="string"&&a.event.id.split(":")[0];if(r)return{id:`workflow:${r}`,name:`Workflow: ${r}`,avatar:"\u25B6",kind:"workflow"}}if(a.sender&&typeof a.sender=="object"){let n=a.sender,r=typeof n.username=="string"?n.username:null,o=typeof n.name=="string"?n.name:null,s=r||(n.id!=null?String(n.id):null)||o;if(s){let i=o||r||s;return{id:s,name:i,avatar:L(i),kind:ra(t)}}}if(t==="gitlab"){let n=a.user||a.payload?.user;if(n&&typeof n=="object"&&(n.username||n.name)){let r=n.username||n.name,o=n.name||n.username;return{id:r,name:o,avatar:L(o),kind:"gitlab"}}}if(t==="github"){let n=a.sender||a.payload?.sender;if(n?.login)return{id:n.login,name:n.login,avatar:L(n.login),kind:"github"}}if(t==="telegram"){let n=a.message?.from||a.from;if(n?.username){let r=n.first_name||n.username;return{id:n.username,name:r,avatar:L(r),kind:"telegram"}}if(n?.id)return{id:String(n.id),name:n.first_name||`tg:${n.id}`,avatar:L(n.first_name||`tg${n.id}`),kind:"telegram"}}if(t==="whatsapp"){let n=a.from||a.payload?.from;if(n){let r=String(n),o=r.replace(/@.*$/,"");return{id:r,name:o,avatar:L(o),kind:"whatsapp"}}}return null}function ra(t){return t==="telegram"||t==="whatsapp"||t==="slack"||t==="discord"?t:"system"}function Je(t){if(!t)return!1;let e=t.toLowerCase();return e==="graph-classifier"||e==="graph-agent"||e.startsWith("system:")||e.startsWith("internal:")||e.endsWith("-system")}function L(t){let e=String(t||"?").trim().split(/\s+/).filter(Boolean);return e.length>=2?(e[0][0]+e[1][0]).toUpperCase():String(t||"?").slice(0,2).toUpperCase()}function ca(t,e){if(!e||typeof e!="object")return"";let a=[];if(typeof e.text=="string"&&a.push(e.text),e.message?.text&&a.push(e.message.text),e.message?.caption&&a.push(e.message.caption),e.object_attributes?.note&&a.push(e.object_attributes.note),e.object_attributes?.title){let n=e.object_attributes.title,r=e.object_attributes.description;a.push(r?`${n}
4784
+ ${r}`:n)}e.comment?.body&&a.push(e.comment.body),e.issue?.title&&a.push(e.issue.title+(e.issue.body?`
4785
+ ${e.issue.body}`:"")),e.pull_request?.title&&a.push(e.pull_request.title),typeof e.message=="string"&&a.push(e.message),typeof e.prompt=="string"&&a.push(e.prompt),typeof e.input=="string"&&a.push(e.input),e.payload&&typeof e.payload.text=="string"&&a.push(e.payload.text),e.payload&&typeof e.payload.message=="string"&&a.push(e.payload.message);for(let n of a)if(typeof n=="string"&&n.trim().length>0){let r=n.trim();return r.length>220?r.slice(0,217)+"\u2026":r}return""}function Hr(t){if(!t)return"(no subject)";let e;return(e=t.match(/^merge_request:(\d+)(?::note:(\d+))?/))?e[2]?`MR #${e[1]} \xB7 note ${e[2]}`:`MR #${e[1]}`:(e=t.match(/^issue:(\d+)(?::note:(\d+))?/))?e[2]?`Issue #${e[1]} \xB7 note ${e[2]}`:`Issue #${e[1]}`:(e=t.match(/^chat:(.+?):(.+)$/))?`${e[1]} \xB7 ${e[2].slice(0,40)}`:(e=t.match(/^mesh:agent:(.+)$/))?`\u2192 ${e[1]}`:(e=t.match(/^workflow:([^:]+):(.+)$/))?`${e[1]} \xB7 ${e[2].slice(0,30)}`:t.length>60?t.slice(0,57)+"\u2026":t}function Ye(t,e,a){let n=Date.now(),r=n-a*3600*1e3,o=t.prepare(`
4786
+ SELECT id, ts, source, project, subject, intent, raw_json
4787
+ FROM intent_events WHERE ts >= ? ORDER BY ts ASC
4788
+ `).all(r),s=new Map,i=new Map;if(o.length>0){let y=o.map($=>$.id),_=y.map(($,V)=>`@id${V}`).join(","),T=Object.fromEntries(y.map(($,V)=>[`id${V}`,$])),M=t.prepare(`
4789
+ SELECT event_id, decided_at, decided_by, agent_id, outcome, reason
4790
+ FROM intent_decisions WHERE event_id IN (${_}) ORDER BY decided_at ASC
4791
+ `).all(T);for(let $ of M)s.has($.event_id)||s.set($.event_id,[]),s.get($.event_id).push($);let I=t.prepare(`
4792
+ SELECT decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary
4793
+ FROM intent_resolutions WHERE decision_event_id IN (${_})
4794
+ `).all(T);for(let $ of I)i.set(`${$.decision_event_id}|${$.decision_decided_by}`,$)}let l=new Map,d=new Map,c=new Map,u=new Map,h=[],x=e?.business?.projects??[],p=e?.business?.contactMap??[],m=la(e);for(let y of o){let _=null;try{_=JSON.parse(y.raw_json)}catch{}let T=da(y.source,_,p),M=null,I=null;T?(M=T.client,I=T.project||`${T.client}/_chat`):y.project&&(M=ia(y.project,x),I=y.project);let $=Dr(y.source,y.intent||"",_),V=ie[$]||{label:$,color:"#6b7280"};c.has($)||c.set($,{id:$,label:V.label,color:V.color});let ae="__system",he,Y=Or(y.source,y.intent||"",_);if(Y){ae=Y.id,he=Y.kind;let N=T?.displayName||Y.name;(!u.has(Y.id)||T?.displayName)&&u.set(Y.id,{id:Y.id,name:N,avatar:L(N),kind:Y.kind})}else u.has("__system")||u.set("__system",{id:"__system",name:"Schedule",avatar:"\u23F1",kind:"system"});let Oa=ca(y.source,_);for(let N of s.get(y.id)??[]){if(N.outcome!=="dispatched"||!N.agent_id)continue;let Ha=`${y.id}|${N.decided_by}`,fe=i.get(Ha),ot=N.decided_at,st=fe?.resolved_at??null,$e=st===null,Na=fe?.duration_ms??($e?n-ot:0),Ba=$e?"active":fe?.status==="error"||fe?.status==="fail"?"error":"completed",P,de;if(M)P=M,de=I;else{let Me=m.get(N.agent_id);Me?(P=Me,de=`${Me}/_${y.source}`):(P="unmapped",de=`unmapped/_${y.source}`)}l.has(P)||l.set(P,{id:P,name:P,color:Pr(P),projects:[]}),d.has(P)||d.set(P,new Set),d.get(P).add(de),h.push({id:`${y.id}|${N.decided_by}`,agentId:N.agent_id,clientId:P,projectId:de,channelId:$,initiatorId:ae,initiatorKind:he,subject:Hr(y.subject),intent:y.intent||"",startedAt:ot,resolvedAt:st,duration:Na,active:$e,outcome:Ba,tokens:0,inputPreview:Oa,system:Rr(N.agent_id,e)})}}let f=[...l.values()].map(y=>({...y,projects:[...d.get(y.id)||[]].sort()})).sort((y,_)=>y.id.localeCompare(_.id)),w=[],b=new Set;if(e)for(let[y,_]of Object.entries(e.agents||{})){let T={id:y,name:_.name||y,tier:_.tier==="claude-code"?"lead":"worker",model:_.model||"",role:_.systemPrompt?_.systemPrompt.split(/[.\n]/)[0].slice(0,60):_.tier||""};w.push(T),b.add(y)}for(let y of h)b.has(y.agentId)||(w.push({id:y.agentId,name:y.agentId,tier:"worker",model:"",role:""}),b.add(y.agentId));w.sort((y,_)=>y.id.localeCompare(_.id));let g=[],v=new Set;for(let[y,_]of Object.entries(ie))g.push({id:y,label:_.label,color:_.color}),v.add(y);for(let y of c.values())v.has(y.id)||g.push(y);let C=[...u.values()];return C.find(y=>y.id==="__system")||C.push({id:"__system",name:"Schedule",avatar:"\u23F1",kind:"system"}),{now:n,windowH:a,clients:f,agents:w,channels:g,initiators:C,dispatches:h}}function pa(t){let e=Te();if(!e)return null;try{return Ye(e.db,ee,t)}finally{e.close()}}function ua(t){let e=new Map,a=new Map,n=new Map,r=new Map,o=[],s=0,i=0;for(let{nodeId:l,snap:d}of t){d.now>s&&(s=d.now),d.windowH>i&&(i=d.windowH);for(let c of d.clients){let u=e.get(c.id);if(!u)e.set(c.id,{...c,projects:[...c.projects]});else{let h=Array.from(new Set([...u.projects,...c.projects])).sort();e.set(c.id,{...u,projects:h})}}for(let c of d.agents)a.has(c.id)||a.set(c.id,c);for(let c of d.channels)n.has(c.id)||n.set(c.id,c);for(let c of d.initiators)r.has(c.id)||r.set(c.id,c);for(let c of d.dispatches)o.push({...c,nodeId:l,id:`${l}::${c.id}`})}return{now:s||Date.now(),windowH:i||6,clients:[...e.values()].sort((l,d)=>l.id.localeCompare(d.id)),agents:[...a.values()].sort((l,d)=>l.id.localeCompare(d.id)),channels:[...n.values()],initiators:[...r.values()],dispatches:o.sort((l,d)=>d.startedAt-l.startedAt)}}var ee=null;function ma(t){ee=t}async function ga(t,e,a){if(a!=="/api/admin/activity-graph"&&!a.startsWith("/api/admin/activity-graph?"))return!1;let n=Te();if(!n)return X(e,503,{error:"intent ledger not available",clients:[],agents:[],channels:[],initiators:[],dispatches:[]}),!0;try{let r=new URL(t.url||"/","http://_"),o=fa(parseInt(r.searchParams.get("hours")||"6",10));X(e,200,Ye(n.db,ee,o))}catch(r){X(e,500,{error:r?.message??String(r)})}finally{n.close()}return!0}var Nr=5e3;async function ha(t,e,a){if(a!=="/api/admin/activity-graph/stream"&&!a.startsWith("/api/admin/activity-graph/stream?"))return!1;e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),e.write(`: connected
4795
+
4796
+ `);let n=new URL(t.url||"/","http://_"),r=fa(parseInt(n.searchParams.get("hours")||"6",10)),o=!1,s=()=>{o=!0};t.on("close",s),t.on("error",s),e.on("close",s),e.on("error",s);let i=setInterval(()=>{if(!o)try{e.write(`: hb
4797
+
4798
+ `)}catch{s()}},25e3);try{for(;!o;){let l=Te();if(l)try{let c=Ye(l.db,ee,r);e.write(`event: snapshot
4799
+ `),e.write(`data: ${JSON.stringify(c)}
4800
+
4801
+ `)}catch(c){e.write(`event: error
4802
+ data: ${JSON.stringify({error:c?.message??String(c)})}
4803
+
4804
+ `)}finally{l.close()}else e.write(`event: error
4805
+ data: ${JSON.stringify({error:"ledger not available"})}
4806
+
4807
+ `);let d=Date.now();for(;!o&&Date.now()-d<Nr;)await new Promise(c=>setTimeout(c,200))}}finally{clearInterval(i);try{e.end()}catch{}}return!0}function fa(t){return!Number.isFinite(t)||t<1?6:Math.min(720,Math.max(1,t))}function Br(t,e,a=5e3){let n=new Date(e).toISOString().slice(0,10),r=Xe(process.cwd(),".agentx/task-history",t,n);if(!Ve(r)){let o=new Date(e-864e5).toISOString().slice(0,10),s=Xe(process.cwd(),".agentx/task-history",t,o);return Ve(s)?oa(s,e,a):null}return oa(r,e,a)}function oa(t,e,a){let n=null,r=1/0;for(let o of Mr(t)){if(!o.endsWith(".json"))continue;let s;try{s=JSON.parse(Ir($r(t,o),"utf-8"))}catch{continue}let i=s.startedAt,l;if(typeof i=="number")l=i;else if(typeof i=="string"){let c=Date.parse(i);if(!Number.isFinite(c))continue;l=c}else continue;let d=Math.abs(l-e);d<r&&d<=a&&(n=s,r=d)}return n}async function va(t,e,a){let n=a.match(/^\/api\/admin\/activity-graph\/dispatch\/([^/?]+)(?:\?.*)?$/);if(!n)return!1;let r=decodeURIComponent(n[1]),o=r.indexOf("|");if(o<0)return X(e,400,{error:"invalid dispatch id"}),!0;let s=r.slice(0,o),i=r.slice(o+1),l=Te();if(!l)return X(e,503,{error:"ledger not available"}),!0;try{let d=l.db.prepare("SELECT id, ts, source, project, subject, intent, raw_json FROM intent_events WHERE id = ?").get(s);if(!d)return X(e,404,{error:"event not found"}),!0;let c=l.db.prepare("SELECT decided_at, decided_by, agent_id, outcome, reason FROM intent_decisions WHERE event_id = ? AND decided_by = ?").get(s,i);if(!c)return X(e,404,{error:"decision not found"}),!0;let u=l.db.prepare("SELECT resolved_at, status, duration_ms, result_summary FROM intent_resolutions WHERE decision_event_id = ? AND decision_decided_by = ?").get(s,i),h=null;try{h=JSON.parse(d.raw_json)}catch{}let x=ca(d.source,h),p=h&&typeof h=="object"&&(h.text||h.message?.text||h.message?.caption||h.object_attributes?.note||h.object_attributes?.description||h.comment?.body||h.issue?.body||(typeof h.message=="string"?h.message:null)||(typeof h.prompt=="string"?h.prompt:null)||null)||x,m=null,f=null;if(c.agent_id){let y=Br(c.agent_id,c.decided_at);if(y){typeof y.responseText=="string"&&(m=y.responseText);let _=y.transcript;Array.isArray(_)&&(f=_.length)}}let w=ee?.business?.projects??[],b=ee?.business?.contactMap??[],g=la(ee),v={client:"unmapped",project:`unmapped/_${d.source}`,via:"fallback"},C=da(d.source,h,b);if(C)v={client:C.client,project:C.project||`${C.client}/_chat`,via:`contactMap (channel=${C.channel??"*"}${C.username?` username=${C.username}`:""}${C.chatId?` chatId=${C.chatId}`:""})`};else if(d.project)v={client:ia(d.project,w),project:d.project,via:`project namespace prefix (${d.project})`};else if(c.agent_id){let y=g.get(c.agent_id);y&&(v={client:y,project:`${y}/_${d.source}`,via:`orgChart fallback (agent ${c.agent_id} reports up to a PM whose project belongs to ${y})`})}X(e,200,{id:r,eventId:s,source:d.source,subject:d.subject,intent:d.intent,project:d.project,decidedBy:c.decided_by,agentId:c.agent_id,outcome:c.outcome,reason:c.reason,startedAt:c.decided_at,resolvedAt:u?.resolved_at??null,durationMs:u?.duration_ms??null,input:p,response:m,transcriptLen:f,resolutionStatus:u?.status??null,resultSummary:u?.result_summary??null,attribution:v})}catch(d){X(e,500,{error:d?.message??String(d)})}finally{l.close()}return!0}import{existsSync as Fr,readFileSync as Gr}from"fs";import{resolve as xa}from"path";function ba(t){let e=t.agentId,a=t.localToken?`<script>window.AX_LOCAL_TOKEN = ${JSON.stringify(t.localToken)};</script>`:"",n=`<script>window.AGENT_ID = ${JSON.stringify(e)};</script>`,r=jr.replace(/__AGENT_ID__/g,R(e));return A({title:`AgentX \xB7 ${R(e)}`,activeTab:"admin",subtitle:`Agent \xB7 ${R(e)}`,peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:r,css:zr,headExtras:'<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">',scripts:`${n}${a}<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script><script>${qr}</script>`})}var jr=`
2906
4808
  <!-- Breadcrumb -->
2907
4809
  <div class="ax-crumb">
2908
4810
  <a href="/admin">Settings</a>
@@ -2986,6 +4888,10 @@ refresh();
2986
4888
  Handovers
2987
4889
  <span class="ax-rail__count" id="rail-handovers-count">0</span>
2988
4890
  </a>
4891
+ <a data-tab="capability">
4892
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>
4893
+ Capability
4894
+ </a>
2989
4895
 
2990
4896
  <div class="ax-rail__sep"></div>
2991
4897
  <div class="ax-rail__label">Observe</div>
@@ -3056,6 +4962,9 @@ refresh();
3056
4962
  <option value="private">Private (only this node)</option>
3057
4963
  <option value="public">Public API</option>
3058
4964
  </select>
4965
+ <div id="public-api-hint" style="display:none;margin-top:6px;padding:8px 10px;background:var(--ax-surface);border-radius:4px;font-size:11px;color:var(--ax-muted)">
4966
+ Public API enabled. External callers POST to <code id="public-api-url" style="font-family:'IBM Plex Mono',monospace"></code> with a token scoped <code>agent:<span id="public-api-scope"></span></code> or <code>agent:*</code>. Mint one in the <a href="/admin#tokens">Tokens tab</a>.
4967
+ </div>
3059
4968
  </div>
3060
4969
  </div>
3061
4970
  </div>
@@ -3307,6 +5216,93 @@ refresh();
3307
5216
  </div>
3308
5217
  </section>
3309
5218
 
5219
+ <!-- Capability \u2014 mirrors agentx agent capability -->
5220
+ <section id="tab-capability" class="ax-panel__tab">
5221
+ <div class="ax-panel">
5222
+ <div class="ax-panel__head">
5223
+ <div>
5224
+ <h2>Capability flags</h2>
5225
+ <p class="ax-lead" style="margin:0">Phase 5 typed dispatch, Phase 8 delegation depth, and per-agent context-engine overrides. Mirrors <code>agentx agent capability</code>. <b>Restart the daemon</b> after changes for them to take effect.</p>
5226
+ </div>
5227
+ </div>
5228
+ <div class="ax-panel__body">
5229
+ <label class="ax-field">
5230
+ <span class="ax-field__label">Allowed intents <span class="ax-hint">(comma-separated allow-list \u2014 leave blank for permissive default)</span></span>
5231
+ <input id="cap-intents" type="text" placeholder="issue.opened, merge_request.opened" />
5232
+ <span class="ax-hint">Set to limit this agent to specific intents. The org-chart canHandle() check rejects anything not on the list.</span>
5233
+ </label>
5234
+ <label class="ax-field">
5235
+ <span class="ax-field__label">Max delegation depth <span class="ax-hint">(0..50; lower for agents at the bottom of a chain)</span></span>
5236
+ <input id="cap-mdd" type="number" min="0" max="50" />
5237
+ <span class="ax-hint">Caps cascade chains where A \u2192 B \u2192 A. Default 5.</span>
5238
+ </label>
5239
+ <label class="ax-field">
5240
+ <span class="ax-field__label">Context references</span>
5241
+ <select id="cap-cref">
5242
+ <option value="false">off \u2014 skip the [Verified References] block</option>
5243
+ <option value="true">on \u2014 render deterministic references in the prompt</option>
5244
+ </select>
5245
+ <span class="ax-hint">Turn on for agents (PMs, devops) that need cited facts. Requires a <code>references/</code> registry in the workspace.</span>
5246
+ </label>
5247
+ <label class="ax-field">
5248
+ <span class="ax-field__label">Context strategy <span class="ax-hint">(per-agent override; blank = global default)</span></span>
5249
+ <select id="cap-cstrat">
5250
+ <option value="">(global default)</option>
5251
+ <option value="layered">layered \u2014 full context, larger prompt</option>
5252
+ <option value="planner">planner \u2014 smaller prompt, more tool-driven exploration</option>
5253
+ </select>
5254
+ </label>
5255
+ <label class="ax-field">
5256
+ <span class="ax-field__label">Max execution minutes <span class="ax-hint">(1..240; SIGTERM after this)</span></span>
5257
+ <input id="cap-mxm" type="number" min="1" max="240" />
5258
+ <span class="ax-hint">Wall-clock cap on a single Claude Code invocation. Default 20m. Bump for devops/coder agents that do long investigations.</span>
5259
+ </label>
5260
+ <div class="ax-form-actions" style="margin-top:14px">
5261
+ <button class="ax-btn ax-btn--primary" id="cap-save">Save capability</button>
5262
+ <span id="cap-msg" class="ax-hint" style="margin-left:10px"></span>
5263
+ </div>
5264
+ </div>
5265
+ </div>
5266
+
5267
+ <!-- MCP servers \u2014 synced to <workspace>/.mcp.json by agent-mcp.ts at boot -->
5268
+ <div class="ax-panel" style="margin-top:14px">
5269
+ <div class="ax-panel__head">
5270
+ <div>
5271
+ <h2>MCP servers</h2>
5272
+ <p class="ax-lead" style="margin:0">Each entry becomes a server in the agent's <code>.mcp.json</code>. Operator-edited entries in that file are respected (the sync layer marks them operator-owned and skips them); this tab only writes to <code>agentx.json</code>.</p>
5273
+ </div>
5274
+ </div>
5275
+ <div class="ax-panel__body">
5276
+ <div id="mcp-list" style="display:flex;flex-direction:column;gap:6px;margin-bottom:12px"></div>
5277
+ <details>
5278
+ <summary style="cursor:pointer;font-size:12px;color:var(--ax-accent,#3a7bd5)">+ Add or update server</summary>
5279
+ <div style="margin-top:10px;display:flex;flex-direction:column;gap:8px">
5280
+ <label class="ax-field">
5281
+ <span class="ax-field__label">Name <span class="ax-hint">(identifier; will key the entry in agent.mcp)</span></span>
5282
+ <input id="mcp-name" type="text" placeholder="filesystem, gitlab, brave-search\u2026" />
5283
+ </label>
5284
+ <label class="ax-field">
5285
+ <span class="ax-field__label">Command</span>
5286
+ <input id="mcp-command" type="text" placeholder="npx" />
5287
+ </label>
5288
+ <label class="ax-field">
5289
+ <span class="ax-field__label">Args <span class="ax-hint">(comma-separated)</span></span>
5290
+ <input id="mcp-args" type="text" placeholder="-y, @modelcontextprotocol/server-filesystem, /Users/me/code" />
5291
+ </label>
5292
+ <label class="ax-field">
5293
+ <span class="ax-field__label">Env <span class="ax-hint">(KEY=value, comma-separated; optional)</span></span>
5294
+ <input id="mcp-env" type="text" placeholder="GITLAB_TOKEN=glpat-\u2026, GITLAB_HOST=https://gitlab.example.com" />
5295
+ </label>
5296
+ <div class="ax-form-actions">
5297
+ <button class="ax-btn ax-btn--primary" id="mcp-save">Save MCP server</button>
5298
+ <span id="mcp-msg" class="ax-hint" style="margin-left:10px"></span>
5299
+ </div>
5300
+ </div>
5301
+ </details>
5302
+ </div>
5303
+ </div>
5304
+ </section>
5305
+
3310
5306
  <!-- Activity -->
3311
5307
  <section id="tab-activity" class="ax-panel__tab">
3312
5308
  <div class="ax-panel">
@@ -3395,7 +5391,7 @@ refresh();
3395
5391
  </div>
3396
5392
  </div>
3397
5393
  </div>
3398
- `,Na=`
5394
+ `,zr=`
3399
5395
  /* Agent-specific layout + legacy styles for bits the migration left in
3400
5396
  * place (EasyMDE theming, skills tree, plain-editor). Shared chrome + tab
3401
5397
  * primitives live in components.css.ts. */
@@ -3514,7 +5510,7 @@ textarea.inp.mono { font-family: var(--ax-mono); }
3514
5510
 
3515
5511
  /* Agent-specific button variant used in rail context (no ax-btn wrapper) */
3516
5512
  .ax-btn.lg { padding: 9px 16px; font-size: 13px; }
3517
- `,Ba=`
5513
+ `,qr=`
3518
5514
  const $ = (id) => document.getElementById(id);
3519
5515
  const state = { agent: null, activeSkill: null, activeSkillFile: null, identityPath: null };
3520
5516
 
@@ -3675,6 +5671,23 @@ async function loadAgent(){
3675
5671
  $('f-tier').value = a.tier || 'claude-code';
3676
5672
  $('f-model').value = a.model || '';
3677
5673
  $('f-access').value = a.access || 'private';
5674
+ // Public-API hint \u2014 show endpoint + scope when access is public.
5675
+ const hintEl = document.getElementById('public-api-hint');
5676
+ if (hintEl) {
5677
+ const isPublic = a.access === 'public';
5678
+ hintEl.style.display = isPublic ? '' : 'none';
5679
+ if (isPublic) {
5680
+ const daemonUrl = (state && state.daemonUrl) || (location.origin || '').replace(':4202', ':18800');
5681
+ const urlEl = document.getElementById('public-api-url');
5682
+ const scopeEl = document.getElementById('public-api-scope');
5683
+ if (urlEl) urlEl.textContent = daemonUrl + '/api/public/agents/' + AGENT_ID + '/messages';
5684
+ if (scopeEl) scopeEl.textContent = AGENT_ID;
5685
+ }
5686
+ }
5687
+ // Also re-render when the operator flips the dropdown.
5688
+ $('f-access').addEventListener('change', () => {
5689
+ if (hintEl) hintEl.style.display = $('f-access').value === 'public' ? '' : 'none';
5690
+ }, { once: true });
3678
5691
  setOpt('maxConcurrent', a.maxConcurrent ?? 1);
3679
5692
  setOpt('maxExecutionMinutes', a.maxExecutionMinutes ?? 20);
3680
5693
  setOpt('permissionMode', a.permissionMode || 'default');
@@ -3686,9 +5699,110 @@ async function loadAgent(){
3686
5699
  // Stats placeholders; loadActivity() fills real numbers once the tab is visited.
3687
5700
  const handling = (a.runningTasks?.length) || 0;
3688
5701
  $('stat-handling').textContent = String(handling);
5702
+
5703
+ // Capability tab \u2014 Phase 5/8 fields. Mirrors the agentx agent capability CLI.
5704
+ const intentsEl = $('cap-intents');
5705
+ if (intentsEl) {
5706
+ intentsEl.value = Array.isArray(a.intents) ? a.intents.join(', ') : '';
5707
+ $('cap-mdd').value = a.maxDelegationDepth ?? 5;
5708
+ $('cap-cref').value = a.contextReferences ? 'true' : 'false';
5709
+ $('cap-cstrat').value = a.contextStrategy || '';
5710
+ $('cap-mxm').value = a.maxExecutionMinutes ?? 20;
5711
+ }
5712
+ // MCP servers list
5713
+ const mcpListEl = document.getElementById('mcp-list');
5714
+ if (mcpListEl) {
5715
+ const mcp = a.mcp || {};
5716
+ const names = Object.keys(mcp);
5717
+ if (names.length === 0) {
5718
+ mcpListEl.innerHTML = '<div style="font-size:11px;color:var(--ax-muted);font-style:italic;padding:8px;border:1px dashed var(--ax-border);border-radius:4px">no MCP servers configured</div>';
5719
+ } else {
5720
+ mcpListEl.innerHTML = names.map(function(n){
5721
+ const s = mcp[n];
5722
+ const args = (s.args || []).map(function(x){ return esc(x); }).join(' ');
5723
+ const envCount = s.env ? Object.keys(s.env).length : 0;
5724
+ return '<div style="display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--ax-border);border-radius:4px">' +
5725
+ '<div style="flex:1;min-width:0">' +
5726
+ '<div><b>' + esc(n) + '</b> <code style="font-size:11px;color:var(--ax-muted);margin-left:4px">' + esc(s.command) + ' ' + args + '</code></div>' +
5727
+ (envCount ? '<div style="font-size:11px;color:var(--ax-muted);margin-top:2px">env: ' + envCount + ' var(s)</div>' : '') +
5728
+ '</div>' +
5729
+ '<button class="ax-btn ax-btn--danger" data-mcp-rm="' + esc(n) + '">Remove</button>' +
5730
+ '</div>';
5731
+ }).join('');
5732
+ mcpListEl.querySelectorAll('[data-mcp-rm]').forEach(function(b){
5733
+ b.addEventListener('click', async function(){
5734
+ const n = b.getAttribute('data-mcp-rm');
5735
+ if (!confirm('Remove MCP server "' + n + '"?')) return;
5736
+ try {
5737
+ await req('DELETE', '/api/admin/agent/' + AGENT_ID + '/mcp', { name: n });
5738
+ await loadAgent();
5739
+ } catch (e) { showMsg('err', e.message); }
5740
+ });
5741
+ });
5742
+ }
5743
+ }
3689
5744
  } catch (e) { showMsg('err', e.message); }
3690
5745
  }
3691
5746
 
5747
+ // MCP save \u2014 adds or updates one server entry on the agent.
5748
+ (function(){
5749
+ const btn = document.getElementById('mcp-save');
5750
+ if (!btn) return;
5751
+ btn.addEventListener('click', async () => {
5752
+ const name = document.getElementById('mcp-name').value.trim();
5753
+ const command = document.getElementById('mcp-command').value.trim();
5754
+ const argsRaw = document.getElementById('mcp-args').value.trim();
5755
+ const envRaw = document.getElementById('mcp-env').value.trim();
5756
+ const args = argsRaw ? argsRaw.split(',').map(function(s){ return s.trim(); }).filter(Boolean) : [];
5757
+ const env = {};
5758
+ if (envRaw) {
5759
+ envRaw.split(',').map(function(s){ return s.trim(); }).filter(Boolean).forEach(function(pair){
5760
+ const eq = pair.indexOf('=');
5761
+ if (eq > 0) env[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
5762
+ });
5763
+ }
5764
+ const body = { name, command, args };
5765
+ if (Object.keys(env).length > 0) body.env = env;
5766
+ const msg = document.getElementById('mcp-msg');
5767
+ try {
5768
+ await req('POST', '/api/admin/agent/' + AGENT_ID + '/mcp', body);
5769
+ if (msg) { msg.textContent = '\u2713 saved \u2014 agent picks it up at next daemon boot'; msg.style.color = 'var(--ax-success)'; }
5770
+ ['mcp-name','mcp-command','mcp-args','mcp-env'].forEach(function(id){ document.getElementById(id).value = ''; });
5771
+ await loadAgent();
5772
+ } catch (e) {
5773
+ if (msg) { msg.textContent = e.message; msg.style.color = 'var(--ax-err)'; }
5774
+ }
5775
+ });
5776
+ })();
5777
+
5778
+ // Capability save \u2014 POST to the same agent-config write path the
5779
+ // CLI uses, so both surfaces persist identically. (See agentx agent
5780
+ // capability in the CLI for the cli mirror.)
5781
+ (function(){
5782
+ const capSaveBtn = document.getElementById('cap-save');
5783
+ if (!capSaveBtn) return;
5784
+ capSaveBtn.addEventListener('click', async () => {
5785
+ const intentsRaw = document.getElementById('cap-intents').value.trim();
5786
+ const intents = intentsRaw ? intentsRaw.split(',').map(function(s){ return s.trim(); }).filter(Boolean) : [];
5787
+ const body = {
5788
+ intents: intents,
5789
+ maxDelegationDepth: parseInt(document.getElementById('cap-mdd').value, 10) || 5,
5790
+ contextReferences: document.getElementById('cap-cref').value === 'true',
5791
+ maxExecutionMinutes: parseInt(document.getElementById('cap-mxm').value, 10) || 20,
5792
+ };
5793
+ const cstrat = document.getElementById('cap-cstrat').value;
5794
+ if (cstrat) body.contextStrategy = cstrat;
5795
+ const msg = document.getElementById('cap-msg');
5796
+ try {
5797
+ await req('PATCH', '/api/admin/agent/' + AGENT_ID + '/capability', body);
5798
+ if (msg) { msg.textContent = '\u2713 saved \u2014 restart the daemon for changes to take effect'; msg.style.color = 'var(--ax-success)'; }
5799
+ await loadAgent();
5800
+ } catch (e) {
5801
+ if (msg) { msg.textContent = e.message; msg.style.color = 'var(--ax-err)'; }
5802
+ }
5803
+ });
5804
+ })();
5805
+
3692
5806
  $('btn-save-meta').addEventListener('click', async () => {
3693
5807
  const body = {
3694
5808
  name: $('f-name').value.trim(),
@@ -4220,9 +6334,9 @@ document.addEventListener('keydown', (e) => {
4220
6334
  /* ===== Boot ===== */
4221
6335
  initOptsToggles();
4222
6336
  loadAgent();
4223
- `;function gt(t,e,a,n=[],o){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(mt({agentId:a,peers:n,localToken:o}))}async function ft(t,e,a){try{let n=t.method==="GET"?void 0:await sn(t),o=new URL(t.url||"/","http://localhost"),r=Object.fromEntries(o.searchParams),s=a.match(/^\/api\/admin\/agent\/([a-z0-9][a-z0-9_-]*)(?:\/(.+))?$/);if(!s){oe(e,404,{error:`unknown agent endpoint: ${a}`});return}let i=s[1],l=s[2]||"",d=`${t.method} ${l}`,p={"GET ":()=>bt(i),"PATCH ":()=>qa(i,n),"GET identity":()=>Ga(i),"GET identity/file":()=>Fa(i,String(r.path||"")),"PUT identity/file":()=>Ua(i,n),"GET skills":()=>Wa(i),"POST skills":()=>Ka(i,n),"DELETE skills":()=>Ja(i,n),"GET skills/tree":()=>Xa(i,String(r.slug||"")),"GET skills/file":()=>Va(i,String(r.slug||""),String(r.path||"")),"PUT skills/file":()=>Ya(i,n),"GET skills/deps":()=>Qa(i,String(r.slug||"")),"POST skills/install":()=>Za(i,n,t),"POST skills/deps":()=>en(i,n,t),"GET channels":()=>tn(i),"GET activity":()=>an(i,t),"GET handovers":()=>nn(i),"POST handovers":()=>on(i,n),"DELETE handovers":()=>rn(n)}[d];if(!p){oe(e,404,{error:`unknown agent sub-endpoint: ${d}`});return}let f=await p();oe(e,200,f)}catch(n){oe(e,400,{error:n?.message||"agent op failed"})}}function vt(){let t=ht(process.cwd(),"agentx.json");return ja(t)?JSON.parse(za(t,"utf-8")):null}function T(t){let e=vt();if(!e)throw new Error("agentx.json not found");let a=e.agents?.[t];if(!a)throw new Error(`Agent "${t}" not found`);return{def:a,cfg:e}}function bt(t){let{def:e,cfg:a}=T(t),n=e.workspace?F(e.workspace):null;return{id:t,name:e.name||t,tier:e.tier,model:e.model,workspace:e.workspace,systemPrompt:e.systemPrompt||"",mentions:e.mentions||[],access:e.access||"private",maxConcurrent:e.maxConcurrent??1,maxExecutionMinutes:e.maxExecutionMinutes??20,permissionMode:e.permissionMode||"default",overview:n,defaultWorker:a.node?.defaultAgent,draftAgent:a.dashboard?.draftAgent}}function qa(t,e){let a={};for(let o of["name","tier","model","systemPrompt","mentions","access","maxConcurrent","maxExecutionMinutes","permissionMode"])e?.[o]!==void 0&&(a[o]=e[o]);let{summary:n}=S(o=>{if(!o.agents?.[t])throw new Error(`Agent "${t}" not found`);return o.agents[t]={...o.agents[t],...a},`agent "${t}" updated (${Object.keys(a).join(", ")})`});return{summary:n,agent:bt(t)}}function Ga(t){let{def:e}=T(t);return F(e.workspace)}function Fa(t,e){if(!e)throw new Error("path query param required");let{def:a}=T(t);return Q(a.workspace,e)}function Ua(t,e){let a=String(e?.path||"");if(!a)throw new Error("path required");let{def:n}=T(t);return Z(n.workspace,a,String(e?.content??""))}function Wa(t){let{def:e}=T(t),a=F(e.workspace);return{skills:a.skills,workspace:a.workspace}}function Ka(t,e){let a=String(e?.slug||"").trim();if(!a)throw new Error("slug required");let{def:n}=T(t);return ee(n.workspace,a,{title:e?.title,content:e?.content})}function Ja(t,e){let a=String(e?.slug||"").trim();if(!a)throw new Error("slug required");let{def:n}=T(t);return te(n.workspace,a)}function Xa(t,e){if(!e)throw new Error("slug query param required");let{def:a}=T(t),n=Ye(a.workspace,e),o=ae(a.workspace,e);return{slug:e,tree:n,deps:o}}function Va(t,e,a){if(!e||!a)throw new Error("slug and path query params required");let{def:n}=T(t);return Qe(n.workspace,e,a)}function Ya(t,e){let a=String(e?.slug||""),n=String(e?.path||"");if(!a||!n)throw new Error("slug and path required");let{def:o}=T(t);return Ze(o.workspace,a,n,String(e?.content??""))}function Qa(t,e){if(!e)throw new Error("slug query param required");let{def:a}=T(t);return ae(a.workspace,e)}async function Za(t,e,a){let n=String(e?.package||"").trim();if(!n||!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)?$/.test(n))throw new Error("package must be in 'owner/repo' or 'owner/repo/skill' form");let o=xt(e?.worker),{def:r}=T(t),s=[`Install the skills.sh package "${n}" into agent "${t}".`,`Workspace: ${r.workspace}`,`Run: cd ${r.workspace} && npx -y skills add ${n}`,`If that fails, fall back to: agentx skill install ${n}`,`Skills should land under ${r.workspace}/.claude/skills/. Report what was installed and any errors.`].join(`
4224
- `);return yt(a,o,s,`install-${n}:${Date.now()}`)}async function en(t,e,a){let n=String(e?.slug||"").trim();if(!n)throw new Error("slug required");let o=xt(e?.worker),{def:r}=T(t),s=ae(r.workspace,n);if(!s.manager)throw new Error(`No dependency manifest detected in .claude/skills/${n}. Expected one of: package.json, requirements.txt, Gemfile.`);let i=ht(r.workspace,".claude","skills",n),l=[`Install dependencies for skill "${n}" in agent "${t}".`,`cd to: ${i}`,`Detected manifest: ${s.file}`,`Run: ${s.command}`,"Stream the install output. On success, summarise the packages installed.","On failure, report the exit code and the last 20 lines of output."].join(`
4225
- `);return yt(a,o,l,`deps-${t}-${n}:${Date.now()}`)}function xt(t){if(t&&typeof t=="string")return t;let e=$();return e.dashboard?.draftAgent||e.node?.defaultAgent||Object.keys(e.agents).find(a=>a.includes("devops"))||Object.keys(e.agents)[0]}async function yt(t,e,a,n){let o=$(),r=(o.dashboard.daemonUrl||"http://localhost:18800").replace(/\/+$/,""),s={"Content-Type":"application/json"},i=t.headers.authorization;i?s.Authorization=String(i):o.dashboard.token&&(s.Authorization=`Bearer ${o.dashboard.token}`);let l=await fetch(`${r}/task`,{method:"POST",headers:s,body:JSON.stringify({agent:e,message:a,context:{channel:"admin",sender:"admin-ui",chatId:n}})}),d=await l.text(),u;try{u=JSON.parse(d)}catch{u={content:d}}if(!l.ok)throw new Error(u?.error||`daemon /task HTTP ${l.status}`);return{worker:e,taskId:u?.taskId,content:u?.content,duration:u?.duration,error:u?.error}}function tn(t){let e=vt(),a=[],n=e?.channels?.telegram?.accounts||{};for(let[r,s]of Object.entries(n))s?.agentBinding===t&&a.push({channel:"telegram",account:r,detail:s?.botUsername||r});e?.channels?.slack?.agentBinding===t&&a.push({channel:"slack",detail:"bound"}),e?.channels?.discord?.agentBinding===t&&a.push({channel:"discord",detail:"bound"});let o=e?.channels?.gitlab?.agentMappings||[];for(let r of o)r?.agentId===t&&a.push({channel:"gitlab",detail:(r?.gitlabUsernames||[]).join(", ")||"mapped"});return{bindings:a}}async function an(t,e){try{let n=($().dashboard.daemonUrl||"http://localhost:18800").replace(/\/+$/,""),o=await fetch(`${n}/agents`);if(!o.ok)throw new Error(`daemon HTTP ${o.status}`);let s=(await o.json()).find(i=>i.id===t);return{activeTasks:s?.activeTasks??0,totalTasks:s?.totalTasks??0,errors:s?.errors??0,lastActive:s?.lastActive??null,runningTasks:s?.runningTasks??[],lastSummary:s?.lastSummary??null}}catch(a){return{error:a?.message,activeTasks:0,totalTasks:0,errors:0,runningTasks:[]}}}function ke(){return new Ie}function nn(t){let e=ke(),{incoming:a,outgoing:n}=e.listByAgent(t);return{incoming:a,outgoing:n}}function on(t,e){let a=String(e?.channel||"").trim(),n=String(e?.chatId||"").trim(),o=String(e?.toAgent||"").trim();if(!a||!n||!o)throw new Error("channel, chatId, toAgent required");let r=String(e?.fromAgent||t||"").trim()||t,s=e?.accountId?String(e.accountId):void 0,i=e?.summary?String(e.summary):void 0,l=e?.expiresAt?String(e.expiresAt):void 0,d=ke().set({channel:a,chatId:n,accountId:s,fromAgent:r,toAgent:o,summary:i,expiresAt:l,createdBy:"admin-ui"});return{summary:`Handed off ${a}:${n} from ${r} to ${o}`,record:d}}function rn(t){let e=String(t?.channel||"").trim(),a=String(t?.chatId||"").trim();if(!e||!a)throw new Error("channel and chatId required");let n=t?.accountId?String(t.accountId):void 0;return{summary:ke().remove(e,a,n)?"Released":"No active handover for that chat"}}function oe(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function sn(t){return new Promise((e,a)=>{let n="";t.on("data",o=>{n+=o}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(o){a(o)}}),t.on("error",a)})}var L={brand:"AgentX",subtitle:"Team AI ops",nodes:"machines",agentsCount:"agents",activeNow:"handling now",errorsCount:"failed",stats:{active:"Handling",total:"Today",errors:"Failed"},idle:"idle",runningNoPreview:"working \xB7 preparing reply",recentActivities:"Recent activities \u2192",noAgentsOnNode:"No agents set up on this machine.",unreachable:"This machine is unreachable.",neverRan:"not used yet",tierLabels:{"claude-code":"Claude Code",sdk:"SDK",orchestrator:"Orchestrator"},taskModalTitle:"Task activity",taskModalConnecting:"connecting\u2026",taskModalLive:"live",taskModalFinished:"finished",taskModalArchived:"archived",taskModalLoadFailed:"couldn't load",taskModalFinalResponse:"Final reply",historyPanelTitle:"Recent activities",historyEmpty:"No activity yet.",historyLoading:"loading\u2026"};var wt=[{term:"Agent",definition:'An AI worker with its own role, instructions, and channels it responds on. Think of each agent as a job title \u2014 "support", "devops", "sales".'},{term:"Trigger word",alias:"mention",definition:"A word that activates an agent. When the trigger appears in a message (e.g. @support), AgentX routes the message to that agent."},{term:"Channel",definition:"Where messages come from \u2014 Telegram, WhatsApp, Discord, GitLab comments, a scheduled cron, or an HTTP webhook."},{term:"AI engine",alias:"tier",definition:"Which backend answers: Claude Code (reasoning + tools + files), an SDK provider (OpenAI, Anthropic API, Ollama), or an Orchestrator that coordinates sub-agents."},{term:"Agent folder",alias:"workspace",definition:"The directory on disk where the agent lives. It holds the agent's personality (CLAUDE.md), its knowledge (wiki), and the tools it can use."},{term:"Team network",alias:"mesh",definition:'When you run AgentX on more than one machine, the machines find each other and share work. Each machine is a "node"; together they are your team network.'},{term:"Status check-in",alias:"heartbeat",definition:"A lightweight ping between machines so each one knows the others are alive and responsive."},{term:"Board",definition:"A Kanban view of tasks (issues, tickets) pulled from GitLab. Drag cards between columns to move work forward \u2014 the labels update upstream automatically."},{term:"Task",definition:"A single conversation or job an agent handles, from trigger message to final reply. Every task is logged and replayable."},{term:"Schedule",alias:"cron",definition:'A recurring job \u2014 e.g. "every Monday at 9am" \u2014 that nudges an agent without a human trigger.'},{term:"Status label",alias:"scopedLabel",definition:"A GitLab label that moves an issue between Kanban columns (e.g. Status::Doing). Labels are mutually exclusive by prefix."}];function Se(t={}){let e=`<script>window.UI_LABELS = ${JSON.stringify(L)};</script>`,a=`<span id="ts" class="ax-mono" title="Last update">\u2014</span>
6337
+ `;function ya(t,e,a,n=[],r){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(ba({agentId:a,peers:n,localToken:r}))}async function wa(t,e,a){try{let n=t.method==="GET"?void 0:await mo(t),r=new URL(t.url||"/","http://localhost"),o=Object.fromEntries(r.searchParams),s=a.match(/^\/api\/admin\/agent\/([a-z0-9][a-z0-9_-]*)(?:\/(.+))?$/);if(!s){Ce(e,404,{error:`unknown agent endpoint: ${a}`});return}let i=s[1],l=s[2]||"",d=`${t.method} ${l}`,u={"GET ":()=>me(i),"PATCH ":()=>Wr(i,n),"PATCH capability":()=>Jr(i,n),"POST mcp":()=>Ur(i,n),"DELETE mcp":()=>Kr(i,n),"GET identity":()=>Xr(i),"GET identity/file":()=>Vr(i,String(o.path||"")),"PUT identity/file":()=>Yr(i,n),"GET skills":()=>Qr(i),"POST skills":()=>Zr(i,n),"DELETE skills":()=>eo(i,n),"GET skills/tree":()=>to(i,String(o.slug||"")),"GET skills/file":()=>ao(i,String(o.slug||""),String(o.path||"")),"PUT skills/file":()=>no(i,n),"GET skills/deps":()=>ro(i,String(o.slug||"")),"POST skills/install":()=>oo(i,n,t),"POST skills/deps":()=>so(i,n,t),"GET channels":()=>io(i),"GET activity":()=>lo(i,t),"GET handovers":()=>co(i),"POST handovers":()=>po(i,n),"DELETE handovers":()=>uo(n)}[d];if(!u){Ce(e,404,{error:`unknown agent sub-endpoint: ${d}`});return}let h=await u();Ce(e,200,h)}catch(n){Ce(e,400,{error:n?.message||"agent op failed"})}}function ka(){let t=xa(process.cwd(),"agentx.json");return Fr(t)?JSON.parse(Gr(t,"utf-8")):null}function D(t){let e=ka();if(!e)throw new Error("agentx.json not found");let a=e.agents?.[t];if(!a)throw new Error(`Agent "${t}" not found`);return{def:a,cfg:e}}function me(t){let{def:e,cfg:a}=D(t),n=e.workspace?re(e.workspace):null;return{id:t,name:e.name||t,tier:e.tier,model:e.model,workspace:e.workspace,systemPrompt:e.systemPrompt||"",mentions:e.mentions||[],access:e.access||"private",maxConcurrent:e.maxConcurrent??1,maxExecutionMinutes:e.maxExecutionMinutes??20,permissionMode:e.permissionMode||"default",intents:Array.isArray(e.intents)?e.intents:[],maxDelegationDepth:e.maxDelegationDepth??5,contextReferences:!!e.contextReferences,contextStrategy:e.contextStrategy||"",mcp:e.mcp&&typeof e.mcp=="object"?e.mcp:{},overview:n,defaultWorker:a.node?.defaultAgent,draftAgent:a.dashboard?.draftAgent}}function Wr(t,e){let a={};for(let r of["name","tier","model","systemPrompt","mentions","access","maxConcurrent","maxExecutionMinutes","permissionMode"])e?.[r]!==void 0&&(a[r]=e[r]);let{summary:n}=S(r=>{if(!r.agents?.[t])throw new Error(`Agent "${t}" not found`);return r.agents[t]={...r.agents[t],...a},`agent "${t}" updated (${Object.keys(a).join(", ")})`});return{summary:n,agent:me(t)}}function Ur(t,e){let a=String(e?.name||"").trim(),n=String(e?.command||"").trim();if(!a)throw new Error("name required");if(!/^[a-zA-Z0-9_-]+$/.test(a))throw new Error("name must be identifier-safe (alnum, _, -)");if(!n)throw new Error("command required");let r=Array.isArray(e?.args)?e.args.map(i=>String(i)):[],o;if(e?.env&&typeof e.env=="object"){o={};for(let[i,l]of Object.entries(e.env))typeof l=="string"&&(o[i]=l);Object.keys(o).length===0&&(o=void 0)}let{summary:s}=S(i=>{if(!i.agents?.[t])throw new Error(`Agent "${t}" not found`);return i.agents[t].mcp=i.agents[t].mcp||{},i.agents[t].mcp[a]={command:n,args:r,...o?{env:o}:{}},`agent "${t}" mcp.${a} upserted`});return{summary:s,agent:me(t)}}function Kr(t,e){let a=String(e?.name||"").trim();if(!a)throw new Error("name required");let{summary:n}=S(r=>{if(!r.agents?.[t])throw new Error(`Agent "${t}" not found`);if(!r.agents[t].mcp||!(a in r.agents[t].mcp))throw new Error(`no mcp.${a} on "${t}"`);return delete r.agents[t].mcp[a],Object.keys(r.agents[t].mcp).length===0&&delete r.agents[t].mcp,`agent "${t}" mcp.${a} removed`});return{summary:n,agent:me(t)}}function Jr(t,e){let a={};if(Array.isArray(e?.intents)&&(a.intents=e.intents.map(r=>String(r)).filter(Boolean)),e?.maxDelegationDepth!==void 0){let r=Number(e.maxDelegationDepth);if(!Number.isFinite(r)||r<0||r>50)throw new Error("maxDelegationDepth must be 0..50");a.maxDelegationDepth=r}if(e?.contextReferences!==void 0&&(a.contextReferences=!!e.contextReferences),e?.contextStrategy!==void 0){if(e.contextStrategy&&!["layered","planner"].includes(e.contextStrategy))throw new Error("contextStrategy must be layered|planner|empty");e.contextStrategy?a.contextStrategy=e.contextStrategy:a.contextStrategy=void 0}if(e?.maxExecutionMinutes!==void 0){let r=Number(e.maxExecutionMinutes);if(!Number.isFinite(r)||r<1||r>240)throw new Error("maxExecutionMinutes must be 1..240");a.maxExecutionMinutes=r}if(Object.keys(a).length===0)throw new Error("nothing to update");let{summary:n}=S(r=>{if(!r.agents?.[t])throw new Error(`Agent "${t}" not found`);let o={...r.agents[t]};for(let[s,i]of Object.entries(a))i===void 0?delete o[s]:o[s]=i;return r.agents[t]=o,`agent "${t}" capability updated (${Object.keys(a).join(", ")})`});return{summary:n,agent:me(t)}}function Xr(t){let{def:e}=D(t);return re(e.workspace)}function Vr(t,e){if(!e)throw new Error("path query param required");let{def:a}=D(t);return xe(a.workspace,e)}function Yr(t,e){let a=String(e?.path||"");if(!a)throw new Error("path required");let{def:n}=D(t);return ye(n.workspace,a,String(e?.content??""))}function Qr(t){let{def:e}=D(t),a=re(e.workspace);return{skills:a.skills,workspace:a.workspace}}function Zr(t,e){let a=String(e?.slug||"").trim();if(!a)throw new Error("slug required");let{def:n}=D(t);return we(n.workspace,a,{title:e?.title,content:e?.content})}function eo(t,e){let a=String(e?.slug||"").trim();if(!a)throw new Error("slug required");let{def:n}=D(t);return ke(n.workspace,a)}function to(t,e){if(!e)throw new Error("slug query param required");let{def:a}=D(t),n=It(a.workspace,e),r=_e(a.workspace,e);return{slug:e,tree:n,deps:r}}function ao(t,e,a){if(!e||!a)throw new Error("slug and path query params required");let{def:n}=D(t);return Lt(n.workspace,e,a)}function no(t,e){let a=String(e?.slug||""),n=String(e?.path||"");if(!a||!n)throw new Error("slug and path required");let{def:r}=D(t);return Dt(r.workspace,a,n,String(e?.content??""))}function ro(t,e){if(!e)throw new Error("slug query param required");let{def:a}=D(t);return _e(a.workspace,e)}async function oo(t,e,a){let n=String(e?.package||"").trim();if(!n||!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)?$/.test(n))throw new Error("package must be in 'owner/repo' or 'owner/repo/skill' form");let r=_a(e?.worker),{def:o}=D(t),s=[`Install the skills.sh package "${n}" into agent "${t}".`,`Workspace: ${o.workspace}`,`Run: cd ${o.workspace} && npx -y skills add ${n}`,`If that fails, fall back to: agentx skill install ${n}`,`Skills should land under ${o.workspace}/.claude/skills/. Report what was installed and any errors.`].join(`
6338
+ `);return Ea(a,r,s,`install-${n}:${Date.now()}`)}async function so(t,e,a){let n=String(e?.slug||"").trim();if(!n)throw new Error("slug required");let r=_a(e?.worker),{def:o}=D(t),s=_e(o.workspace,n);if(!s.manager)throw new Error(`No dependency manifest detected in .claude/skills/${n}. Expected one of: package.json, requirements.txt, Gemfile.`);let i=xa(o.workspace,".claude","skills",n),l=[`Install dependencies for skill "${n}" in agent "${t}".`,`cd to: ${i}`,`Detected manifest: ${s.file}`,`Run: ${s.command}`,"Stream the install output. On success, summarise the packages installed.","On failure, report the exit code and the last 20 lines of output."].join(`
6339
+ `);return Ea(a,r,l,`deps-${t}-${n}:${Date.now()}`)}function _a(t){if(t&&typeof t=="string")return t;let e=B();return e.dashboard?.draftAgent||e.node?.defaultAgent||Object.keys(e.agents).find(a=>a.includes("devops"))||Object.keys(e.agents)[0]}async function Ea(t,e,a,n){let r=B(),o=(r.dashboard.daemonUrl||"http://localhost:18800").replace(/\/+$/,""),s={"Content-Type":"application/json"},i=t.headers.authorization;i?s.Authorization=String(i):r.dashboard.token&&(s.Authorization=`Bearer ${r.dashboard.token}`);let l=await fetch(`${o}/task`,{method:"POST",headers:s,body:JSON.stringify({agent:e,message:a,context:{channel:"admin",sender:"admin-ui",chatId:n}})}),d=await l.text(),c;try{c=JSON.parse(d)}catch{c={content:d}}if(!l.ok)throw new Error(c?.error||`daemon /task HTTP ${l.status}`);return{worker:e,taskId:c?.taskId,content:c?.content,duration:c?.duration,error:c?.error}}function io(t){let e=ka(),a=[],n=e?.channels?.telegram?.accounts||{};for(let[o,s]of Object.entries(n))s?.agentBinding===t&&a.push({channel:"telegram",account:o,detail:s?.botUsername||o});e?.channels?.slack?.agentBinding===t&&a.push({channel:"slack",detail:"bound"}),e?.channels?.discord?.agentBinding===t&&a.push({channel:"discord",detail:"bound"});let r=e?.channels?.gitlab?.agentMappings||[];for(let o of r)o?.agentId===t&&a.push({channel:"gitlab",detail:(o?.gitlabUsernames||[]).join(", ")||"mapped"});return{bindings:a}}async function lo(t,e){try{let n=(B().dashboard.daemonUrl||"http://localhost:18800").replace(/\/+$/,""),r=await fetch(`${n}/agents`);if(!r.ok)throw new Error(`daemon HTTP ${r.status}`);let s=(await r.json()).find(i=>i.id===t);return{activeTasks:s?.activeTasks??0,totalTasks:s?.totalTasks??0,errors:s?.errors??0,lastActive:s?.lastActive??null,runningTasks:s?.runningTasks??[],lastSummary:s?.lastSummary??null}}catch(a){return{error:a?.message,activeTasks:0,totalTasks:0,errors:0,runningTasks:[]}}}function Qe(){return new lt}function co(t){let e=Qe(),{incoming:a,outgoing:n}=e.listByAgent(t);return{incoming:a,outgoing:n}}function po(t,e){let a=String(e?.channel||"").trim(),n=String(e?.chatId||"").trim(),r=String(e?.toAgent||"").trim();if(!a||!n||!r)throw new Error("channel, chatId, toAgent required");let o=String(e?.fromAgent||t||"").trim()||t,s=e?.accountId?String(e.accountId):void 0,i=e?.summary?String(e.summary):void 0,l=e?.expiresAt?String(e.expiresAt):void 0,d=Qe().set({channel:a,chatId:n,accountId:s,fromAgent:o,toAgent:r,summary:i,expiresAt:l,createdBy:"admin-ui"});return{summary:`Handed off ${a}:${n} from ${o} to ${r}`,record:d}}function uo(t){let e=String(t?.channel||"").trim(),a=String(t?.chatId||"").trim();if(!e||!a)throw new Error("channel and chatId required");let n=t?.accountId?String(t.accountId):void 0;return{summary:Qe().remove(e,a,n)?"Released":"No active handover for that chat"}}function Ce(t,e,a){t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a))}function mo(t){return new Promise((e,a)=>{let n="";t.on("data",r=>{n+=r}),t.on("end",()=>{try{e(n?JSON.parse(n):{})}catch(r){a(r)}}),t.on("error",a)})}var W={brand:"AgentX",subtitle:"Team AI ops",nodes:"machines",agentsCount:"agents",activeNow:"handling now",errorsCount:"failed",stats:{active:"Handling",total:"Today",errors:"Failed"},idle:"idle",runningNoPreview:"working \xB7 preparing reply",recentActivities:"Recent activities \u2192",noAgentsOnNode:"No agents set up on this machine.",unreachable:"This machine is unreachable.",neverRan:"not used yet",tierLabels:{"claude-code":"Claude Code",sdk:"SDK",orchestrator:"Orchestrator"},taskModalTitle:"Task activity",taskModalConnecting:"connecting\u2026",taskModalLive:"live",taskModalFinished:"finished",taskModalArchived:"archived",taskModalLoadFailed:"couldn't load",taskModalFinalResponse:"Final reply",historyPanelTitle:"Recent activities",historyEmpty:"No activity yet.",historyLoading:"loading\u2026"};var Sa=[{term:"Agent",definition:'An AI worker with its own role, instructions, and channels it responds on. Think of each agent as a job title \u2014 "support", "devops", "sales".'},{term:"Trigger word",alias:"mention",definition:"A word that activates an agent. When the trigger appears in a message (e.g. @support), AgentX routes the message to that agent."},{term:"Channel",definition:"Where messages come from \u2014 Telegram, WhatsApp, Discord, GitLab comments, a scheduled cron, or an HTTP webhook."},{term:"AI engine",alias:"tier",definition:"Which backend answers: Claude Code (reasoning + tools + files), an SDK provider (OpenAI, Anthropic API, Ollama), or an Orchestrator that coordinates sub-agents."},{term:"Agent folder",alias:"workspace",definition:"The directory on disk where the agent lives. It holds the agent's personality (CLAUDE.md), its knowledge (wiki), and the tools it can use."},{term:"Team network",alias:"mesh",definition:'When you run AgentX on more than one machine, the machines find each other and share work. Each machine is a "node"; together they are your team network.'},{term:"Status check-in",alias:"heartbeat",definition:"A lightweight ping between machines so each one knows the others are alive and responsive."},{term:"Board",definition:"A Kanban view of tasks (issues, tickets) pulled from GitLab. Drag cards between columns to move work forward \u2014 the labels update upstream automatically."},{term:"Task",definition:"A single conversation or job an agent handles, from trigger message to final reply. Every task is logged and replayable."},{term:"Schedule",alias:"cron",definition:'A recurring job \u2014 e.g. "every Monday at 9am" \u2014 that nudges an agent without a human trigger.'},{term:"Status label",alias:"scopedLabel",definition:"A GitLab label that moves an issue between Kanban columns (e.g. Status::Doing). Labels are mutually exclusive by prefix."}];function Ze(t={}){let e=`<script>window.UI_LABELS = ${JSON.stringify(W)};</script>`,a=`<span id="ts" class="ax-mono" title="Last update">\u2014</span>
4226
6340
  <span id="conn-dot" class="ax-dot ax-dot--ok ax-dot--pulse" title="connected"></span>
4227
6341
  <span id="conn-label">live</span>`,n=`<div class="ax-app-live">
4228
6342
  <section id="statstrip" class="ax-statstrip"></section>
@@ -4230,7 +6344,7 @@ loadAgent();
4230
6344
  </div>
4231
6345
  <aside id="history-panel" class="history-panel hidden" aria-hidden="true">
4232
6346
  <header>
4233
- <h2 id="history-panel-title">${C(L.historyPanelTitle)}</h2>
6347
+ <h2 id="history-panel-title">${R(W.historyPanelTitle)}</h2>
4234
6348
  <span class="history-panel-source" id="history-panel-source"></span>
4235
6349
  <button class="history-panel-close" id="history-panel-close" aria-label="Close">\xD7</button>
4236
6350
  </header>
@@ -4241,13 +6355,13 @@ loadAgent();
4241
6355
  <div class="task-modal-card" role="dialog" aria-modal="true">
4242
6356
  <header>
4243
6357
  <span class="task-modal-channel" id="task-modal-channel"></span>
4244
- <h2 id="task-modal-title">${C(L.taskModalTitle)}</h2>
4245
- <span class="task-modal-status" id="task-modal-status">${C(L.taskModalConnecting)}</span>
6358
+ <h2 id="task-modal-title">${R(W.taskModalTitle)}</h2>
6359
+ <span class="task-modal-status" id="task-modal-status">${R(W.taskModalConnecting)}</span>
4246
6360
  <button class="task-modal-close" id="task-modal-close" aria-label="Close">\xD7</button>
4247
6361
  </header>
4248
6362
  <div id="task-modal-output" class="task-modal-output"></div>
4249
6363
  </div>
4250
- </div>`;return E({title:`${L.brand} \xB7 ${L.subtitle}`,activeTab:"live",subtitle:L.subtitle,peers:t.peers,currentPeerId:t.currentPeerId,customHeader:void 0,noMain:!0,body:n,css:ln,scripts:e+`<script>${dn}</script>`,headExtras:`<script>
6364
+ </div>`;return A({title:`${W.brand} \xB7 ${W.subtitle}`,activeTab:"live",subtitle:W.subtitle,peers:t.peers,currentPeerId:t.currentPeerId,customHeader:void 0,noMain:!0,body:n,css:go,scripts:e+`<script>${ho}</script>`,headExtras:`<script>
4251
6365
  // rightExtras for the topbar are injected at runtime \u2014 they reference
4252
6366
  // IDs (#ts, #conn-dot) that the script below addresses directly.
4253
6367
  document.addEventListener('DOMContentLoaded', () => {
@@ -4258,7 +6372,7 @@ loadAgent();
4258
6372
  extras.innerHTML = ${JSON.stringify(a)};
4259
6373
  right.insertBefore(extras, right.firstChild);
4260
6374
  });
4261
- </script>`})}var ln=`
6375
+ </script>`})}var go=`
4262
6376
  /* --- Page frame --- */
4263
6377
  .ax-app-live { min-height: 100vh; display: flex; flex-direction: column; }
4264
6378
 
@@ -4527,7 +6641,7 @@ loadAgent();
4527
6641
  border-top-color: var(--ax-accent); border-radius: 50%;
4528
6642
  animation: ax-spin 800ms linear infinite; display: inline-block;
4529
6643
  }
4530
- @keyframes ax-spin { to { transform: rotate(360deg); } }`,dn=`
6644
+ @keyframes ax-spin { to { transform: rotate(360deg); } }`,ho=`
4531
6645
  'use strict';
4532
6646
 
4533
6647
  const ui = { nodes: new Map(), summary: { nodes: 0, agents: 0, busy: 0, errors: 0 } };
@@ -5203,7 +7317,7 @@ document.getElementById('grid').addEventListener('click', (e) => {
5203
7317
  }
5204
7318
  });
5205
7319
 
5206
- `;function kt(t={}){let e=`
7320
+ `;function Ta(t={}){let e=`
5207
7321
  <select id="boardPicker" title="Switch board"></select>
5208
7322
  <div class="search">
5209
7323
  <span class="icon">\u2315</span>
@@ -5304,7 +7418,7 @@ document.getElementById('grid').addEventListener('click', (e) => {
5304
7418
  </footer>
5305
7419
  </div>
5306
7420
  </div>
5307
- <script src="/sortable.min.js"></script>`;return E({title:"AgentX \xB7 Boards",activeTab:"boards",subtitle:"Boards",subheader:e,peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:a,css:cn,scripts:`<script>${pn}</script>`,headExtras:`<script>
7421
+ <script src="/sortable.min.js"></script>`;return A({title:"AgentX \xB7 Boards",activeTab:"boards",subtitle:"Boards",subheader:e,peers:t.peers,currentPeerId:t.currentPeerId,noMain:!0,body:a,css:fo,scripts:`<script>${vo}</script>`,headExtras:`<script>
5308
7422
  // The boards page has its own connection dot + wants body height:100%
5309
7423
  // for the full-viewport kanban layout. Inject the dot into the topbar
5310
7424
  // and override body height at runtime so the shared shell doesn't
@@ -5320,7 +7434,7 @@ document.getElementById('grid').addEventListener('click', (e) => {
5320
7434
  right.insertBefore(dot, right.firstChild);
5321
7435
  }
5322
7436
  });
5323
- </script>`})}var cn=`
7437
+ </script>`})}var fo=`
5324
7438
  /* Kanban subheader \u2014 replaces the legacy toolbar that used to live in <header>. */
5325
7439
  .ax-subheader select, .ax-subheader input, .ax-subheader button {
5326
7440
  background: var(--col); color: var(--text); border: 1px solid var(--border);
@@ -5573,7 +7687,7 @@ header a.link:hover { color: var(--accent); border-color: var(--accent); }
5573
7687
  .draft-box .draft-row .hint { color: var(--muted); font-size: 12px; flex: 1; }
5574
7688
  .draft-box .draft-row .hint.err { color: var(--red); }
5575
7689
  .draft-box .draft-row .hint.ok { color: var(--green); }
5576
- `,pn=`
7690
+ `,vo=`
5577
7691
  'use strict';
5578
7692
  const state = {
5579
7693
  boards: [], current: null, items: {},
@@ -6632,9 +8746,9 @@ function appendEvents(events) {
6632
8746
  }
6633
8747
 
6634
8748
  loadBoards().catch(e => toast('Init failed: ' + e.message));
6635
- `;function St(t={}){let a=`<h1>Plain-English glossary</h1>
8749
+ `;function Ca(t={}){let a=`<h1>Plain-English glossary</h1>
6636
8750
  <p class="lead">What the terms on the dashboard mean. Schema keys (shown in the pill on the right of each term) are what you'd write in <code>agentx.json</code> \u2014 the dashboard just relabels them for readability.</p>
6637
- ${wt.map(n=>{let o=n.alias?`<span class="alias" title="Schema key">${C(n.alias)}</span>`:"";return`<article class="term"><h3>${C(n.term)}${o}</h3><p>${C(n.definition)}</p></article>`}).join("")}`;return E({title:`${L.brand} \xB7 Glossary`,activeTab:"glossary",subtitle:"Glossary",peers:t.peers,currentPeerId:t.currentPeerId,body:a,css:un})}var un=`
8751
+ ${Sa.map(n=>{let r=n.alias?`<span class="alias" title="Schema key">${R(n.alias)}</span>`:"";return`<article class="term"><h3>${R(n.term)}${r}</h3><p>${R(n.definition)}</p></article>`}).join("")}`;return A({title:`${W.brand} \xB7 Glossary`,activeTab:"glossary",subtitle:"Glossary",peers:t.peers,currentPeerId:t.currentPeerId,body:a,css:bo})}var bo=`
6638
8752
  main { max-width: 760px; margin: 0 auto; padding: 32px 24px 80px; }
6639
8753
  h1 { font-size: 22px; margin: 0 0 6px; font-weight: 600; letter-spacing: -0.01em; }
6640
8754
  .lead {
@@ -6664,7 +8778,7 @@ article.term .alias {
6664
8778
  }
6665
8779
  article.term p {
6666
8780
  margin: 0; color: var(--ax-text-2); font-size: 13px; line-height: 1.6;
6667
- }`;function Et(t={}){let e=`<div class="ax-wf__root">
8781
+ }`;function Aa(t={}){let e=`<div class="ax-wf__root">
6668
8782
  <aside class="ax-wf__list">
6669
8783
  <header>
6670
8784
  <h2>Workflows</h2>
@@ -6702,11 +8816,16 @@ article.term p {
6702
8816
  <footer>
6703
8817
  <span id="wf-run-status" class="ax-wf__status-pill">\u2014</span>
6704
8818
  <span id="wf-run-live" class="hint">\u2014</span>
8819
+ <span id="wf-run-actions" class="ax-wf__run-actions">
8820
+ <button data-run-action="pause" type="button" title="Pause this run">Pause</button>
8821
+ <button data-run-action="resume" type="button" title="Resume a paused run">Resume</button>
8822
+ <button data-run-action="cancel" type="button" title="Cancel this run" class="danger">Cancel</button>
8823
+ </span>
6705
8824
  </footer>
6706
8825
  </aside>
6707
8826
  </div>
6708
8827
 
6709
- <div id="wf-toast" class="ax-wf__toast" hidden></div>`;return E({title:"AgentX \xB7 Workflows",activeTab:"workflows",subtitle:"Workflows",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:mn,scripts:`<script>${hn}</script>`})}var mn=`
8828
+ <div id="wf-toast" class="ax-wf__toast" hidden></div>`;return A({title:"AgentX \xB7 Workflows",activeTab:"workflows",subtitle:"Workflows",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:xo,scripts:`<script>${yo}</script>`})}var xo=`
6710
8829
  .ax-wf__root {
6711
8830
  display: grid;
6712
8831
  grid-template-columns: 340px 1fr;
@@ -6900,6 +9019,10 @@ article.term p {
6900
9019
  .ax-wf__status-pill.running { background: color-mix(in oklch, var(--ax-accent) 15%, transparent); color: var(--ax-accent); }
6901
9020
  .ax-wf__status-pill.completed { color: var(--ax-muted); background: var(--ax-surface); }
6902
9021
  .ax-wf__status-pill.failed { color: var(--ax-err); background: color-mix(in oklch, var(--ax-err) 15%, transparent); }
9022
+ .ax-wf__run-actions { display: flex; gap: 4px; margin-left: auto; }
9023
+ .ax-wf__run-actions button { font-size: 11px; padding: 3px 9px; border-radius: 4px; border: 1px solid var(--ax-border); background: var(--ax-bg); color: var(--ax-fg); cursor: pointer; }
9024
+ .ax-wf__run-actions button:hover { background: var(--ax-surface); }
9025
+ .ax-wf__run-actions button.danger:hover { background: color-mix(in oklch, var(--ax-err) 15%, transparent); border-color: var(--ax-err); color: var(--ax-err); }
6903
9026
 
6904
9027
  .ax-wf__icon {
6905
9028
  background: none; border: none; color: var(--ax-muted);
@@ -6923,7 +9046,7 @@ article.term p {
6923
9046
  font-size: 12px; z-index: 50;
6924
9047
  }
6925
9048
  .hint { color: var(--ax-muted); font-size: 11px; }
6926
- `,hn=`
9049
+ `,yo=`
6927
9050
  (() => {
6928
9051
  const $ = (sel) => document.querySelector(sel)
6929
9052
  const $$ = (sel) => Array.from(document.querySelectorAll(sel))
@@ -7074,6 +9197,7 @@ article.term p {
7074
9197
  // --- Run drawer ------------------------------------------------------
7075
9198
 
7076
9199
  async function openRun(runId) {
9200
+ state.activeRunId = runId
7077
9201
  const res = await fetchJSON("/api/workflows/runs/" + encodeURIComponent(runId))
7078
9202
  paintRun(res.run || res)
7079
9203
  connectRunStream(runId)
@@ -7083,6 +9207,7 @@ article.term p {
7083
9207
  }
7084
9208
 
7085
9209
  function closeRun() {
9210
+ state.activeRunId = null
7086
9211
  if (state.sse) { state.sse.close(); state.sse = null }
7087
9212
  const panel = $("#wf-run-panel")
7088
9213
  panel.classList.add("hidden")
@@ -7194,6 +9319,32 @@ article.term p {
7194
9319
 
7195
9320
  // --- Bindings --------------------------------------------------------
7196
9321
 
9322
+ // Run lifecycle action \u2014 pause / resume / cancel via setStatus on the
9323
+ // backend RunStore, mirroring \`agentx workflow pause/resume/cancel\`.
9324
+ // Opens a tiny confirm dialog for cancel since it's destructive.
9325
+ async function runAction(action) {
9326
+ if (!state.activeRunId) return
9327
+ if (action === "cancel" && !confirm("Cancel this run? In-flight work will stop.")) return
9328
+ const statusByAction = { pause: "paused", resume: "running", cancel: "canceled" }
9329
+ const status = statusByAction[action]
9330
+ if (!status) return
9331
+ try {
9332
+ const r = await fetch("/api/workflows/runs/" + encodeURIComponent(state.activeRunId) + "/status", {
9333
+ method: "POST",
9334
+ headers: { "Content-Type": "application/json", "X-Requested-With": "agentx-board" },
9335
+ credentials: "same-origin",
9336
+ body: JSON.stringify({ status }),
9337
+ })
9338
+ if (!r.ok) throw new Error("HTTP " + r.status + ": " + (await r.text()).slice(0, 120))
9339
+ toast(action + "d")
9340
+ refresh()
9341
+ // Refresh the open run panel so the status pill updates immediately.
9342
+ if (state.activeRunId) openRun(state.activeRunId)
9343
+ } catch (e) {
9344
+ toast("action failed: " + e.message)
9345
+ }
9346
+ }
9347
+
7197
9348
  document.addEventListener("DOMContentLoaded", () => {
7198
9349
  $("#wf-refresh").addEventListener("click", refresh)
7199
9350
  $("#wf-run-close").addEventListener("click", closeRun)
@@ -7201,6 +9352,9 @@ article.term p {
7201
9352
  state.filter = e.target.value
7202
9353
  renderList()
7203
9354
  })
9355
+ document.querySelectorAll('#wf-run-actions [data-run-action]').forEach((b) => {
9356
+ b.addEventListener('click', () => runAction(b.getAttribute('data-run-action')))
9357
+ })
7204
9358
  document.addEventListener("keydown", (e) => {
7205
9359
  if (e.key === "r" && !e.metaKey && document.activeElement?.tagName !== "INPUT") refresh()
7206
9360
  if (e.key === "Escape") closeRun()
@@ -7210,7 +9364,7 @@ article.term p {
7210
9364
  setInterval(refresh, 15000)
7211
9365
  })
7212
9366
  })();
7213
- `;function _t(t={}){let e='<div id="wfe-root" style="height:100%"></div>';return E({title:"AgentX \xB7 Workflow editor",activeTab:"workflows",subtitle:"Workflow editor",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:gn,scripts:'<script src="/assets/workflow-editor.global.js" defer></script>',noMain:!0})}var gn=`
9367
+ `;function $a(t={}){let e='<div id="wfe-root" style="height:100%"></div>';return A({title:"AgentX \xB7 Workflow editor",activeTab:"workflows",subtitle:"Workflow editor",peers:t.peers,currentPeerId:t.currentPeerId,body:e,css:wo,scripts:'<script src="/assets/workflow-editor.global.js" defer></script>',noMain:!0})}var wo=`
7214
9368
  /* Preload the fonts the redesign's CSS references. Placed in the shell so
7215
9369
  * they start fetching while the React bundle is still parsing. */
7216
9370
  @import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Instrument+Serif&display=swap");
@@ -7231,19 +9385,25 @@ body { margin: 0; overflow: hidden; }
7231
9385
  font: 13px/1 -apple-system, BlinkMacSystemFont, sans-serif;
7232
9386
  text-align: center;
7233
9387
  }
7234
- `;function Ct(t,e,a){let n=t.url||"";if(!n.startsWith("/api/workflows"))return!1;let o=t.method||"GET";if(n==="/api/workflows"){if(o==="GET")return a.requireScope(t,e,["dashboard:read"])?y(e,200,{workflows:a.store.list()}):!0;if(o==="POST")return a.requireScope(t,e,["dashboard:write"])?U(t,e,s=>Tt(e,a,s,{mode:"create"})):!0}if(n.startsWith("/api/workflows/runs")&&o==="GET")return a.requireScope(t,e,["dashboard:read"])?vn(t,e,a,n):!0;let r=n.match(/^\/api\/workflows\/([^\/?]+)(\/layout|\/validate)?(\?.*)?$/);if(r){let s=decodeURIComponent(r[1]),i=r[2];if(i==="/layout"){if(o==="GET")return a.requireScope(t,e,["dashboard:read"])?y(e,200,{layout:a.layouts.get(s)}):!0;if(o==="PUT")return a.requireScope(t,e,["dashboard:write"])?U(t,e,l=>{try{let d=a.layouts.save(s,l);return y(e,200,{layout:d})}catch(d){return y(e,400,{error:"invalid layout",message:d.message})}}):!0}if(i==="/validate"&&o==="POST")return a.requireScope(t,e,["dashboard:read"])?U(t,e,l=>{let d=se.safeParse(l);if(!d.success)return y(e,200,{ok:!1,issues:d.error.issues.map(p=>({path:p.path.join("."),message:p.message}))});let u=ie(d.data).map(p=>({path:"",message:p}));return y(e,200,{ok:u.length===0,issues:u,workflow:d.data})}):!0;if(!i){if(o==="GET"){if(!a.requireScope(t,e,["dashboard:read"]))return!0;let l=a.store.get(s);return l?y(e,200,{workflow:l}):y(e,404,{error:"workflow not found"})}if(o==="PUT")return a.requireScope(t,e,["dashboard:write"])?U(t,e,l=>Tt(e,a,l,{mode:"update",id:s})):!0;if(o==="DELETE"){if(!a.requireScope(t,e,["dashboard:write"]))return!0;let l=a.store.delete(s);return a.layouts.delete(s),y(e,l?200:404,l?{ok:!0}:{error:"workflow not found"})}}}if(n.startsWith("/api/workflows/tasks")){if(!a.tasks||!a.dispatcher)return y(e,501,{error:"task engine not enabled"});let s=n.replace(/^\/api\/workflows\/tasks/,"");if(s===""||s.startsWith("?")){if(o!=="GET")return y(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:read"]))return!0;let d=new URL(n,"http://_").searchParams.get("actor")||void 0,u=d?a.tasks.listForActor(d):a.tasks.listOpen();return y(e,200,{tasks:u})}let i=s.match(/^\/([^\/?]+)(\/submit)?$/);if(i){let l=decodeURIComponent(i[1]);if(i[2])return o!=="POST"?y(e,405,{error:"method not allowed"}):a.requireScope(t,e,["dashboard:write"])?U(t,e,async d=>{let u=Re.safeParse(d&&typeof d=="object"&&"submission"in d?d.submission:d);if(!u.success)return y(e,400,{error:"invalid submission",issues:u.error.issues.map(b=>({path:b.path.join("."),message:b.message}))});let p=typeof d?.submittedBy=="string"?d.submittedBy:"anonymous",f=await a.dispatcher.submitTask(l,u.data,p);return f.ok?y(e,200,{ok:!0,runId:f.runId}):y(e,400,{error:f.error,fieldErrors:f.fieldErrors})}):!0;if(o==="GET"){if(!a.requireScope(t,e,["dashboard:read"]))return!0;let d=a.tasks.get(l);return d?y(e,200,{task:d}):y(e,404,{error:"task not found"})}}}if(n.startsWith("/api/workflows/signal/")){if(o!=="POST")return y(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:write"]))return!0;if(!a.dispatcher)return y(e,501,{error:"dispatcher not enabled"});let s=decodeURIComponent(n.replace(/^\/api\/workflows\/signal\//,"").split("?")[0]);return s?U(t,e,i=>{let l=i,d=a.dispatcher.emitSignal({name:s,scope:l?.scope,workflowId:l?.workflowId,payload:l?.payload});return y(e,200,{ok:!0,emission:d})}):y(e,400,{error:"signal name required"})}return y(e,404,{error:"unknown workflows endpoint",path:n})}var fn=["mesh"];function Tt(t,e,a,n){let o=se.safeParse(a);if(!o.success)return y(t,400,{error:"invalid workflow",issues:o.error.issues.map(l=>({path:l.path.join("."),message:l.message}))});let r=o.data,s=ie(r);if(s.length>0)return y(t,400,{error:"lint errors",issues:s.map(l=>({path:"",message:l}))});if(n.mode==="update"&&r.id!==n.id)return y(t,400,{error:"workflow id does not match path"});if(n.mode==="create"&&e.store.get(r.id))return y(t,409,{error:`workflow "${r.id}" already exists`});if(n.mode==="update"&&a&&typeof a=="object"){let l=a,d=e.store.get(n.id);if(d)for(let u of fn)!(u in l)&&u in d&&(r[u]=d[u])}let i=e.store.save(r);return e.layouts.sync(i.id,i.nodes.map(l=>l.id)),y(t,200,{workflow:i})}function vn(t,e,a,n){let o=new URL(n,"http://_").searchParams,r=n.replace(/^\/api\/workflows\/runs/,"");if(r===""||r.startsWith("?")){let i=Math.max(1,Math.min(500,Number(o.get("limit")||50))),l=o.get("workflowId")||void 0;return y(e,200,{runs:a.runs.list({workflowId:l,limit:i})})}let s=r.match(/^\/([^\/?]+)(\/stream)?$/);if(s){let i=decodeURIComponent(s[1]);if(s[2])return bn(t,e,a,i);let l=a.runs.get(i);return l?y(e,200,{run:l}):y(e,404,{error:"run not found"})}return y(e,404,{error:"unknown runs endpoint"})}function bn(t,e,a,n){let o=a.runs.get(n);if(!o)return y(e,404,{error:"run not found"}),!0;e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),e.write(`data: ${JSON.stringify(o)}
9388
+ `;function Ia(t,e,a){let n=t.url||"";if(!n.startsWith("/api/workflows"))return!1;let r=t.method||"GET";if(n==="/api/workflows"){if(r==="GET")return a.requireScope(t,e,["dashboard:read"])?E(e,200,{workflows:a.store.list()}):!0;if(r==="POST")return a.requireScope(t,e,["dashboard:write"])?te(t,e,s=>Ma(e,a,s,{mode:"create"})):!0}if(n.startsWith("/api/workflows/runs"))return r==="GET"&&!a.requireScope(t,e,["dashboard:read"])?!0:_o(t,e,a,n);if(n.startsWith("/api/workflows/tasks")){if(!a.tasks||!a.dispatcher)return E(e,501,{error:"task engine not enabled"});let s=n.replace(/^\/api\/workflows\/tasks/,"");if(s==="/history"||s.startsWith("/history?")){if(r!=="GET")return E(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:read"]))return!0;let l=new URL(n,"http://_").searchParams,d=parseInt(l.get("limit")||"50",10),c=Number.isFinite(d)?Math.max(1,Math.min(500,d)):50,h=a.tasks.listArchived().filter(x=>!!x.submittedAt).sort((x,p)=>x.submittedAt<p.submittedAt?1:-1).slice(0,c).map(x=>{let p=Date.parse(x.createdAt),m=Date.parse(x.submittedAt),f=x.dueAt?Date.parse(x.dueAt):null,w=f!==null&&m>f;return{id:x.id,runId:x.runId,workflowId:x.workflowId,title:x.title,assignee:x.assignee,submittedBy:x.submittedBy,submittedAt:x.submittedAt,submittedAction:x.submittedAction,createdAt:x.createdAt,dueAt:x.dueAt??null,durationMs:Number.isFinite(m-p)?m-p:null,breachedSla:w,status:x.status}});return E(e,200,{rows:h})}if(s===""||s.startsWith("?")){if(r!=="GET")return E(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:read"]))return!0;let d=new URL(n,"http://_").searchParams.get("actor")||void 0,c=d?a.tasks.listForActor(d):a.tasks.listOpen();return E(e,200,{tasks:c})}let i=s.match(/^\/([^\/?]+)(\/submit)?$/);if(i){let l=decodeURIComponent(i[1]);if(i[2])return r!=="POST"?E(e,405,{error:"method not allowed"}):a.requireScope(t,e,["dashboard:write"])?te(t,e,async d=>{let c=gt.safeParse(d&&typeof d=="object"&&"submission"in d?d.submission:d);if(!c.success)return E(e,400,{error:"invalid submission",issues:c.error.issues.map(x=>({path:x.path.join("."),message:x.message}))});let u=typeof d?.submittedBy=="string"?d.submittedBy:"anonymous",h=await a.dispatcher.submitTask(l,c.data,u);return h.ok?E(e,200,{ok:!0,runId:h.runId}):E(e,400,{error:h.error,fieldErrors:h.fieldErrors})}):!0;if(r==="GET"){if(!a.requireScope(t,e,["dashboard:read"]))return!0;let d=a.tasks.get(l);return d?E(e,200,{task:d}):E(e,404,{error:"task not found"})}}}if(n.startsWith("/api/workflows/signal/")){if(r!=="POST")return E(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:write"]))return!0;if(!a.dispatcher)return E(e,501,{error:"dispatcher not enabled"});let s=decodeURIComponent(n.replace(/^\/api\/workflows\/signal\//,"").split("?")[0]);return s?te(t,e,i=>{let l=i,d=a.dispatcher.emitSignal({name:s,scope:l?.scope,workflowId:l?.workflowId,payload:l?.payload});return E(e,200,{ok:!0,emission:d})}):E(e,400,{error:"signal name required"})}let o=n.match(/^\/api\/workflows\/([^\/?]+)(\/layout|\/validate)?(\?.*)?$/);if(o){let s=decodeURIComponent(o[1]),i=o[2];if(i==="/layout"){if(r==="GET")return a.requireScope(t,e,["dashboard:read"])?E(e,200,{layout:a.layouts.get(s)}):!0;if(r==="PUT")return a.requireScope(t,e,["dashboard:write"])?te(t,e,l=>{try{let d=a.layouts.save(s,l);return E(e,200,{layout:d})}catch(d){return E(e,400,{error:"invalid layout",message:d.message})}}):!0}if(i==="/validate"&&r==="POST")return a.requireScope(t,e,["dashboard:read"])?te(t,e,l=>{let d=Ie.safeParse(l);if(!d.success)return E(e,200,{ok:!1,issues:d.error.issues.map(u=>({path:u.path.join("."),message:u.message}))});let c=Le(d.data).map(u=>({path:"",message:u}));return E(e,200,{ok:c.length===0,issues:c,workflow:d.data})}):!0;if(!i){if(r==="GET"){if(!a.requireScope(t,e,["dashboard:read"]))return!0;let l=a.store.get(s);return l?E(e,200,{workflow:l}):E(e,404,{error:"workflow not found"})}if(r==="PUT")return a.requireScope(t,e,["dashboard:write"])?te(t,e,l=>Ma(e,a,l,{mode:"update",id:s})):!0;if(r==="DELETE"){if(!a.requireScope(t,e,["dashboard:write"]))return!0;let l=a.store.delete(s);return a.layouts.delete(s),E(e,l?200:404,l?{ok:!0}:{error:"workflow not found"})}}}return E(e,404,{error:"unknown workflows endpoint",path:n})}var ko=["mesh"];function Ma(t,e,a,n){let r=Ie.safeParse(a);if(!r.success)return E(t,400,{error:"invalid workflow",issues:r.error.issues.map(l=>({path:l.path.join("."),message:l.message}))});let o=r.data,s=Le(o);if(s.length>0)return E(t,400,{error:"lint errors",issues:s.map(l=>({path:"",message:l}))});if(n.mode==="update"&&o.id!==n.id)return E(t,400,{error:"workflow id does not match path"});if(n.mode==="create"&&e.store.get(o.id))return E(t,409,{error:`workflow "${o.id}" already exists`});if(n.mode==="update"&&a&&typeof a=="object"){let l=a,d=e.store.get(n.id);if(d)for(let c of ko)!(c in l)&&c in d&&(o[c]=d[c])}let i=e.store.save(o);return e.layouts.sync(i.id,i.nodes.map(l=>l.id)),E(t,200,{workflow:i})}function _o(t,e,a,n){let r=new URL(n,"http://_").searchParams,o=n.replace(/^\/api\/workflows\/runs/,"");if(o===""||o.startsWith("?")){let l=Math.max(1,Math.min(500,Number(r.get("limit")||50))),d=r.get("workflowId")||void 0;return E(e,200,{runs:a.runs.list({workflowId:d,limit:l})})}let s=o.match(/^\/([^\/?]+)\/status$/);if(s){if((t.method||"GET").toUpperCase()!=="POST")return E(e,405,{error:"method not allowed"});if(!a.requireScope(t,e,["dashboard:write"]))return!0;let l=decodeURIComponent(s[1]);return te(t,e,d=>{let c=d?.status;return c!=="paused"&&c!=="running"&&c!=="canceled"?E(e,400,{error:"status must be paused|running|canceled"}):a.runs.setStatus(l,c)?E(e,200,{ok:!0,runId:l,status:c}):E(e,404,{error:"run not found"})})}let i=o.match(/^\/([^\/?]+)(\/stream)?$/);if(i){let l=decodeURIComponent(i[1]);if(i[2])return Eo(t,e,a,l);let d=a.runs.get(l);return d?E(e,200,{run:d}):E(e,404,{error:"run not found"})}return E(e,404,{error:"unknown runs endpoint"})}function Eo(t,e,a,n){let r=a.runs.get(n);if(!r)return E(e,404,{error:"run not found"}),!0;e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),e.write(`data: ${JSON.stringify(r)}
7235
9389
 
7236
- `);let r=!1,s=()=>{r=!0,l?.(),clearInterval(u);try{e.end()}catch{}};t.on("close",s);let i=p=>{if(!r)try{e.write(`data: ${JSON.stringify(p)}
9390
+ `);let o=!1,s=()=>{o=!0,l?.(),clearInterval(c);try{e.end()}catch{}};t.on("close",s);let i=u=>{if(!o)try{e.write(`data: ${JSON.stringify(u)}
7237
9391
 
7238
- `)}catch{s()}},l=a.runEvents?.subscribe(n,i),d=o.updatedAt,u=setInterval(()=>{if(r)return;let p=a.runs.get(n);p&&p.updatedAt!==d&&(d=p.updatedAt,i(p))},2e3);return!0}function U(t,e,a){let n="";return t.setEncoding("utf8"),t.on("data",o=>{n+=o}),t.on("end",()=>{let o;try{o=n?JSON.parse(n):{}}catch(r){y(e,400,{error:"invalid JSON body",message:r.message});return}Promise.resolve(a(o)).catch(r=>{try{y(e,500,{error:"handler threw",message:r?.message??String(r)})}catch{}})}),t.on("error",o=>y(e,400,{error:"body read failed",message:o.message})),!0}function y(t,e,a){return t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a)),!0}function dr(t){let e=t.dashboard,a=t.boards,n=e.port,o=e.bind,r=e.token,s=new Map;for(let b of a)try{s.set(b.id,$n(b,t))}catch(c){console.error(` Board "${b.id}" failed to initialize: ${c.message}`)}let i=t.node?.id||"local",l=t.workflows?.dir?re(process.cwd(),t.workflows.dir):void 0,d=new De(l?{baseDir:l}:void 0),u=new He(l?{baseDir:l,nodeId:i}:{nodeId:i}),p=new Oe(l?{baseDir:l}:void 0);xn(async(b,c)=>{try{await Tn(b,c,{boards:a,sources:s,token:r,config:t,workflowStore:d,workflowRuns:u,workflowLayouts:p})}catch(m){h(c,500,{error:m.message||"internal error"})}}).listen(n,o,()=>{let b=o==="0.0.0.0"?"localhost":o;a.length===0?(console.log(`
7239
- Live dashboard: http://${b}:${n}
9392
+ `)}catch{s()}},l=a.runEvents?.subscribe(n,i),d=r.updatedAt,c=setInterval(()=>{if(o)return;let u=a.runs.get(n);u&&u.updatedAt!==d&&(d=u.updatedAt,i(u))},2e3);return!0}function te(t,e,a){let n="";return t.setEncoding("utf8"),t.on("data",r=>{n+=r}),t.on("end",()=>{let r;try{r=n?JSON.parse(n):{}}catch(o){E(e,400,{error:"invalid JSON body",message:o.message});return}Promise.resolve(a(r)).catch(o=>{try{E(e,500,{error:"handler threw",message:o?.message??String(o)})}catch{}})}),t.on("error",r=>E(e,400,{error:"body read failed",message:r.message})),!0}function E(t,e,a){return t.writeHead(e,{"Content-Type":"application/json"}),t.end(JSON.stringify(a)),!0}function qi(t){let e=t.dashboard,a=t.boards,n=e.port,r=e.bind,o=e.token,s=new Map;for(let x of a)try{s.set(x.id,Po(x,t))}catch(p){console.error(` Board "${x.id}" failed to initialize: ${p.message}`)}let i=t.node?.id||"local",l=t.workflows?.dir?ge(process.cwd(),t.workflows.dir):void 0,d=new pt(l?{baseDir:l}:void 0),c=new ut(l?{baseDir:l,nodeId:i}:{nodeId:i}),u=new mt(l?{baseDir:l}:void 0);ma(t),So(async(x,p)=>{try{await Io(x,p,{boards:a,sources:s,token:o,config:t,workflowStore:d,workflowRuns:c,workflowLayouts:u})}catch(m){k(p,500,{error:m.message||"internal error"})}}).listen(n,r,()=>{let x=r==="0.0.0.0"?"localhost":r;a.length===0?(console.log(`
9393
+ Live dashboard: http://${x}:${n}
7240
9394
  `),console.log(" (no boards configured \u2014 '/' serves the live view; add a board with 'agentx board add')")):(console.log(`
7241
- Kanban dashboard: http://${b}:${n}
7242
- `),console.log(` Boards: ${a.map(c=>c.id).join(", ")}`)),o==="127.0.0.1"&&!r?console.log(" (bound to localhost; no auth token \u2014 safe default)"):o!=="127.0.0.1"&&!r&&console.log(" \u26A0 bound to a non-loopback address without a token \u2014 writes are UNAUTHENTICATED")})}async function Tn(t,e,a){let n=new URL(t.url||"/","http://localhost"),o=n.pathname,r=(t.method||"GET").toUpperCase();if(e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, PATCH, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r==="OPTIONS"){e.writeHead(204),e.end();return}if(r==="GET"&&o==="/"){let c=Fe();if(!c.configExists||c.agentCount===0){e.writeHead(302,{Location:"/setup"}),e.end();return}let m=B(a.config),x=a.boards.length===0?Se({peers:m}):kt({peers:m});e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(x);return}if(r==="GET"&&o==="/live"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Se({peers:B(a.config)}));return}if(r==="GET"&&o==="/glossary"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(St({peers:B(a.config)}));return}if(r==="GET"&&o==="/workflows"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Et({peers:B(a.config)}));return}if(r==="GET"&&o==="/workflows/editor"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(_t({peers:B(a.config)}));return}if(r==="GET"&&o==="/setup"){Ue(t,e);return}if(r==="POST"&&o==="/api/setup"){await We(t,e);return}if(r==="GET"&&o==="/admin"){at(t,e,B(a.config),a.token);return}if(r==="GET"&&o==="/admin/graph"){ct(t,e,B(a.config),a.token);return}let s=r==="GET"&&o.match(/^\/admin\/agents\/([a-z0-9][a-z0-9_-]*)$/);if(s){gt(t,e,s[1],B(a.config),a.token);return}if(o.startsWith("/api/admin/")){let c=String(t.headers["x-agentx-peer"]||"").trim()||n.searchParams.get("peer")||"";if(c&&c!=="primary"){let m=Gn(c,a.config);if(!m){h(e,404,{error:`unknown peer: ${c}`});return}await qn(t,e,m,o+(n.search||""));return}if(a.token){let m=t.headers.authorization||"",x=m.startsWith("Bearer ")?m.slice(7):"";if(x!==a.token){let w=new D,g=x?w.verify(x):null;if(!g||!K(g,"dashboard:write")){h(e,401,{error:"unauthorized"});return}}}}if(r==="GET"&&o==="/api/admin/config"){await nt(t,e);return}if(o.startsWith("/api/admin/graph/")){await pt(t,e,o);return}if(o.startsWith("/api/admin/agent/")){await ft(t,e,o);return}if(o.startsWith("/api/admin/")){await ot(t,e,o);return}if(r==="GET"&&o==="/sortable.min.js"){e.writeHead(200,{"Content-Type":"application/javascript; charset=utf-8","Cache-Control":"public, max-age=86400"}),e.end(Ke);return}if(r==="GET"&&o.startsWith("/assets/")){let c=o.slice(8);if(!/^[a-zA-Z0-9._-]+\.(js|map|css)$/.test(c)){h(e,400,{error:"invalid asset name"});return}let m=jn("web/"+c);try{let x=await zn(m),w=c.endsWith(".map")?"application/json; charset=utf-8":c.endsWith(".css")?"text/css; charset=utf-8":"application/javascript; charset=utf-8";e.writeHead(200,{"Content-Type":w,"Cache-Control":"public, max-age=60"}),e.end(x)}catch{h(e,404,{error:"asset not found",hint:"run: npm run build:web"})}return}if(r==="GET"&&o==="/api/live"){try{let c=await At(a.config);h(e,200,c)}catch(c){h(e,502,{error:c.message})}return}if(r==="GET"&&o==="/api/live/stream"){Ln(t,e,a.config);return}if(r==="GET"&&o==="/api/task/stream"){let c=new URL(t.url||"/","http://localhost"),m=c.searchParams.get("node"),x=c.searchParams.get("agent"),w=c.searchParams.get("task");if(!m||!x||!w){h(e,400,{error:"node, agent, task query params required"});return}await Pn(t,e,a,m,x,w);return}if(r==="GET"&&o==="/api/task/history"){let c=new URL(t.url||"/","http://localhost"),m=c.searchParams.get("node"),x=c.searchParams.get("agent"),w=c.searchParams.get("task"),g=c.searchParams.get("limit")||"50";if(!m||!x){h(e,400,{error:"node and agent query params required"});return}await On(e,a,m,x,w,g);return}if(r==="GET"&&o==="/api/agents"){try{let c={};a.config.dashboard.token&&(c.Authorization=`Bearer ${a.config.dashboard.token}`);let m=await fetch(a.config.dashboard.daemonUrl.replace(/\/+$/,"")+"/agents",{headers:c});if(!m.ok)throw new Error(`HTTP ${m.status}`);let x=await m.json();h(e,200,x)}catch(c){h(e,502,{error:c.message||"agents fetch failed"})}return}if(r==="POST"&&o==="/api/draft"){let c=await W(t);try{let m=await Nn(a.config,c);h(e,200,m)}catch(m){h(e,502,{error:m.message||"draft failed"})}return}let i=o.match(/^\/api\/public\/agents\/([^/]+)\/messages$/);if(i&&r==="POST"){let c=i[1];if(!Dn(t,e,[`agent:${c}`]))return;await Hn(t,e,a.config,c);return}if(o.startsWith("/api/")&&a.token){let c=t.headers.authorization||"",m=c.startsWith("Bearer ")?c.slice(7):"";if(m!==a.token){let x=new D,w=m?x.verify(m):null;if(!w||!K(w,"dashboard:write")){h(e,401,{error:"unauthorized"});return}}}if((r==="POST"||r==="PATCH"||r==="DELETE"||r==="PUT")&&o.startsWith("/api/")&&t.headers["x-requested-with"]!=="agentx-board"){h(e,400,{error:"missing X-Requested-With: agentx-board"});return}if(r==="GET"&&o==="/api/workflows/runs"){try{let c=new URL(t.url||"/","http://localhost"),m=Math.max(1,Math.min(500,Number(c.searchParams.get("limit")||50))),x=c.searchParams.get("workflowId")||void 0,w=a.workflowRuns.list({limit:m,workflowId:x}),g=`?limit=${m}${x?`&workflowId=${encodeURIComponent(x)}`:""}`,v=[{url:a.config.dashboard.daemonUrl,token:a.config.dashboard.token},...(a.config.dashboard.daemons||[]).map(_=>({url:_.url,token:_.token}))],k=await Promise.all(v.map(async _=>{let j={};_.token&&(j.Authorization=`Bearer ${_.token}`);try{let Ae=await fetch(`${_.url.replace(/\/+$/,"")}/api/workflows/runs${g}`,{headers:j});if(!Ae.ok)return[];let $e=await Ae.json();return Array.isArray($e.runs)?$e.runs:[]}catch{return[]}})),O=new Map;for(let _ of w)O.set(_.id,_);for(let _ of k)for(let j of _)O.set(j.id,j);let P=Array.from(O.values()).sort((_,j)=>_.updatedAt<j.updatedAt?1:-1).slice(0,m);h(e,200,{runs:P})}catch(c){h(e,502,{error:"runs fetch failed",message:c.message||String(c)})}return}let d=r==="GET"&&o.match(/^\/api\/workflows\/runs\/([^\/]+)$/);if(d){let c=decodeURIComponent(d[1]),m=a.workflowRuns.get(c);if(m){h(e,200,{run:m});return}let x=[{url:a.config.dashboard.daemonUrl,token:a.config.dashboard.token},...(a.config.dashboard.daemons||[]).map(w=>({url:w.url,token:w.token}))];for(let w of x)try{let g={};w.token&&(g.Authorization=`Bearer ${w.token}`);let v=await fetch(`${w.url.replace(/\/+$/,"")}/api/workflows/runs/${encodeURIComponent(c)}`,{headers:g});if(v.ok){h(e,200,await v.json());return}}catch{}h(e,404,{error:"run not found on any configured peer"});return}if(r==="POST"&&o==="/api/workflows/editor/chat"){try{let c=await W(t),m={"Content-Type":"application/json"};a.config.dashboard.token&&(m.Authorization=`Bearer ${a.config.dashboard.token}`);let x=a.config.dashboard.daemonUrl.replace(/\/+$/,""),w=await fetch(`${x}/api/workflows/editor/chat`,{method:"POST",headers:m,body:JSON.stringify(c??{})}),g=await w.json().catch(()=>({error:`HTTP ${w.status}`}));h(e,w.status,g)}catch(c){h(e,502,{error:"daemon unreachable",message:c.message||String(c)})}return}if(o.startsWith("/api/workflows")&&Ct(t,e,{store:a.workflowStore,runs:a.workflowRuns,layouts:a.workflowLayouts,requireScope:(m,x,w)=>{if(a.token)return!0;let g=le(m);if(!g)return!0;let v=new D().verify(g);if(!v)return h(x,401,{error:"invalid or revoked token"}),!1;for(let k of w)if(!K(v,k))return h(x,403,{error:`token missing scope: ${k}`,scopes:v.scopes}),!1;return!0}}))return;if(r==="GET"&&o==="/api/boards"){h(e,200,a.boards.map(c=>({id:c.id,name:c.name,source:c.source,primaryToolLabel:c.primaryToolLabel,labels:c.labels,columns:c.columns,timeRangeDays:c.timeRangeDays})));return}let u=o.match(/^\/api\/boards\/([a-z0-9_-]+)\/members$/);if(u&&r==="GET"){let c=a.sources.get(u[1]);if(!c){h(e,404,{error:"unknown board"});return}try{let m=typeof c.listMembers=="function"?await c.listMembers():[];h(e,200,m)}catch(m){h(e,502,{error:m.message})}return}let p=o.match(/^\/api\/boards\/([a-z0-9_-]+)\/milestones$/);if(p&&r==="GET"){let c=a.sources.get(p[1]);if(!c){h(e,404,{error:"unknown board"});return}try{let m=typeof c.listMilestones=="function"?await c.listMilestones():[];h(e,200,m)}catch(m){h(e,502,{error:m.message})}return}let f=o.match(/^\/api\/boards\/([a-z0-9_-]+)\/items\/(.+?)\/move$/),b=o.match(/^\/api\/boards\/([a-z0-9_-]+)\/items(?:\/(.+))?$/);if(f&&r==="PATCH"){let[,c,m]=f,x=decodeURIComponent(m),w=a.boards.find(P=>P.id===c);if(!w){h(e,404,{error:`unknown board: ${c}`});return}let g=a.sources.get(c);if(!g){h(e,503,{error:`board "${c}" source unavailable`});return}let v=await W(t),k=w.columns.find(P=>P.id===v.to),O=w.columns.find(P=>P.id===v.from);if(!k){h(e,400,{error:`unknown 'to' column: ${v.to}`});return}try{await An(g,x,O,k),Ee({actor:_e(t),action:"move",boardId:c,itemId:x,payload:{from:v.from,to:v.to}}),h(e,200,{ok:!0})}catch(P){h(e,502,{error:P.message||"transition failed"})}return}if(b){let[,c,m]=b,x=m?decodeURIComponent(m):void 0,w=a.boards.find(v=>v.id===c);if(!w){h(e,404,{error:`unknown board: ${c}`});return}let g=a.sources.get(c);if(!g){h(e,503,{error:`board "${c}" source unavailable`});return}if(r==="GET"&&!x){let{columns:v,totals:k}=await Cn(w,g,n.searchParams);h(e,200,{columns:v,totals:k});return}if(r==="GET"&&x){try{let v=typeof g.getItem=="function"?await g.getItem(x):null;if(!v){h(e,404,{error:"not found"});return}h(e,200,v)}catch(v){h(e,502,{error:v.message})}return}if(r==="PATCH"&&x){let v=await W(t);try{let k=typeof g.updateItem=="function"?await g.updateItem(x,v):null;if(!k){h(e,501,{error:"update unsupported"});return}Ee({actor:_e(t),action:"update",boardId:c,itemId:x,payload:v}),h(e,200,k)}catch(k){h(e,502,{error:k.message})}return}if(r==="POST"&&!x){let v=await W(t);if(!v.title){h(e,400,{error:"title required"});return}if(!g.capabilities.create||!g.create){h(e,400,{error:`source ${g.type} does not support create`});return}try{let k=await g.create({title:v.title,description:v.description,labels:v.labels,assigneeUsernames:v.assigneeUsernames,milestoneTitle:v.milestoneTitle,projectHint:v.projectHint});Ee({actor:_e(t),action:"create",boardId:c,itemId:k.id,payload:{title:v.title}}),h(e,201,k)}catch(k){h(e,502,{error:k.message})}return}}h(e,404,{error:"not found",path:o})}async function Cn(t,e,a){if(!e.capabilities.listAll||!e.listAll){let g={};for(let v of t.columns)g[v.id]=[];return{columns:g,totals:Object.fromEntries(t.columns.map(v=>[v.id,0]))}}let n=[];t.primaryToolLabel&&n.push(t.primaryToolLabel);let o=a.get("label")||void 0;o&&n.push(o);let r=a.get("q")||void 0,s=a.get("milestone")||void 0,i=a.get("assignee")||void 0,l=parseInt(a.get("days")||String(t.timeRangeDays),10),d=parseInt(a.get("closedDays")||String(t.closedWindowDays??30),10),u=t.columns.some(g=>g.kind!=="closed"),p=t.columns.some(g=>g.kind==="closed"),f=u?await e.listAll({sinceDays:l,labels:n.length?n:void 0,search:r,milestone:s,state:"opened"}):[],b=p?await e.listAll({sinceDays:d,labels:n.length?n:void 0,search:r,milestone:s,state:"closed"}):[],c={};for(let g of t.columns)c[g.id]=[];let m=t.columns.find(g=>g.kind==="closed");for(let g of b)m&&c[m.id].push({...g,stage:m.id});let x=t.columns.filter(g=>g.kind!=="closed");for(let g of f){let v=Le(g,x);if(!v)continue;let k=c[v];k&&k.push({...g,stage:v})}if(i)for(let g of Object.keys(c))c[g]=c[g].filter(v=>v.assignee===i);for(let g of Object.keys(c))c[g].sort((v,k)=>{let O=(v.priority??99)-(k.priority??99);return O!==0?O:(k.updatedAt||"").localeCompare(v.updatedAt||"")});let w=Object.fromEntries(Object.entries(c).map(([g,v])=>[g,v.length]));return{columns:c,totals:w}}async function An(t,e,a,n){let o=Pe(a,n),r=t;o.closeIssue&&typeof r.setState=="function"?await r.setState(e,"close"):o.reopen&&typeof r.setState=="function"&&await r.setState(e,"reopen"),(o.addLabels?.length||o.removeLabels?.length)&&typeof r.transitionMany=="function"?await r.transitionMany(e,o.addLabels||[],o.removeLabels||[]):o.addLabels?.length&&t.transition&&await t.transition(e,o.addLabels[0],o.removeLabels?.[0])}function $n(t,e){if(t.source.type==="gitlab"){let a=e.channels.gitlab;if(!a?.token)throw new Error("channels.gitlab.token is required for gitlab boards");let n={};for(let o of a.agentMappings)o.gitlabUsernames.length&&(n[o.agentId]=o.gitlabUsernames);return new Ne(a.host,a.token,t.source.projects,n,(...o)=>console.log("[board]",...o))}throw new Error(`unsupported board source: ${t.source.type}`)}function Ee(t){try{let e=re(process.cwd(),".agentx");wn(e)||kn(e,{recursive:!0});let a=JSON.stringify({ts:new Date().toISOString(),...t})+`
7243
- `;yn(re(e,"board-audit.jsonl"),a)}catch{}}function _e(t){let e=t.headers["x-forwarded-for"];return(Array.isArray(e)?e[0]:e?.split(",")[0])||t.socket.remoteAddress||"unknown"}async function Mn(t,e,a){let n={};e&&(n.Authorization=`Bearer ${e}`);let o={id:t,name:t,url:t,reachable:!1,agents:[]};try{let[r,s,i]=await Promise.all([fetch(t+"/health",{headers:n,signal:a}).catch(()=>null),fetch(t+"/agents",{headers:n,signal:a}).catch(()=>null),fetch(t+"/mesh",{headers:n,signal:a}).catch(()=>null)]);if(!s||!s.ok)return o.error=s?`HTTP ${s.status}`:"unreachable",o;let l=await s.json();if(o.agents=l.map(d=>({id:d.id,name:d.name,tier:d.tier,model:d.model,active:d.active||0,total:d.total||0,errors:d.errors||0,lastActive:d.lastActive,lastSummary:d.lastSummary,hourlyTasks:Array.isArray(d.hourlyTasks)?d.hourlyTasks:void 0,runningTasks:Array.isArray(d.runningTasks)?d.runningTasks:[]})),r&&r.ok){let d=await r.json();o.uptimeSec=d.uptime,o.name=d.node?.name||d.node?.id||t,o.id=d.node?.id||t,d.usage&&(o.usage=d.usage)}o.reachable=!0,o._peers=Array.isArray(i&&await In(i))?o._peers:void 0}catch(r){o.error=r.message||String(r)}return o}var Te=new WeakMap;async function In(t){if(Te.has(t))return Te.get(t);try{let e=t.ok?await t.json():null;return Te.set(t,e),e}catch{return null}}async function Ce(t,e,a){try{let n={};e&&(n.Authorization=`Bearer ${e}`);let o=await fetch(t+"/mesh",{headers:n,signal:a});return o.ok?(await o.json()).filter(s=>s&&s.peerUrl).map(s=>({url:String(s.peerUrl).replace(/\/+$/,""),name:s.peer||s.peerUrl})):[]}catch{return[]}}async function At(t){let e=t.dashboard,a=e.daemonUrl.replace(/\/+$/,""),n=e.token,o=new Map;o.set(a,{name:"primary",url:a,token:n});for(let i of e.daemons){let l=i.url.replace(/\/+$/,"");o.has(l)||o.set(l,{name:i.name,url:l,token:i.token})}let r=new AbortController,s=setTimeout(()=>r.abort(),3e3);try{let i=await Ce(a,n,r.signal);for(let d of i)o.has(d.url)||o.set(d.url,d);let l=await Promise.all([...o.values()].map(d=>Mn(d.url,d.token,r.signal)));return{ts:new Date().toISOString(),nodes:l}}finally{clearTimeout(s)}}function Ln(t,e,a){e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let n=!1,o=(i,l)=>{n||e.write(`event: ${i}
9395
+ Kanban dashboard: http://${x}:${n}
9396
+ `),console.log(` Boards: ${a.map(p=>p.id).join(", ")}`)),r==="127.0.0.1"&&!o?console.log(" (bound to localhost; no auth token \u2014 safe default)"):r!=="127.0.0.1"&&!o&&console.log(" \u26A0 bound to a non-loopback address without a token \u2014 writes are UNAUTHENTICATED")})}async function Io(t,e,a){let n=new URL(t.url||"/","http://localhost"),r=n.pathname,o=(t.method||"GET").toUpperCase();if(e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, PATCH, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),o==="OPTIONS"){e.writeHead(204),e.end();return}if(o==="GET"&&r==="/"){let p=_t();if(!p.configExists||p.agentCount===0){e.writeHead(302,{Location:"/setup"}),e.end();return}let m=H(a.config),f=a.boards.length===0?Ze({peers:m}):Ta({peers:m});e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(f);return}if(o==="GET"&&r==="/live"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Ze({peers:H(a.config)}));return}if(o==="GET"&&r==="/glossary"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Ca({peers:H(a.config)}));return}if(o==="GET"&&r==="/workflows"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(Aa({peers:H(a.config)}));return}if(o==="GET"&&r==="/workflows/editor"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end($a({peers:H(a.config)}));return}if(o==="GET"&&r==="/inbox"){let p=n.searchParams.get("actor")||void 0;e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(wt({actor:p}));return}if(o==="GET"&&r==="/processes"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(kt({}));return}if(o==="GET"&&r==="/graph"){e.writeHead(302,{Location:"/admin/graph"}),e.end();return}if(o==="GET"&&r==="/setup"){Et(t,e);return}if(o==="POST"&&r==="/api/setup"){await St(t,e);return}if(o==="GET"&&r==="/admin"){Nt(t,e,H(a.config),a.token);return}if(o==="GET"&&r==="/admin/graph"){Wt(t,e,H(a.config),a.token);return}if(o==="GET"&&(r==="/admin/health"||r==="/admin/observability")){if(r==="/admin/observability"){e.writeHead(302,{Location:"/admin/health"}),e.end();return}Yt(t,e,H(a.config));return}if(r.startsWith("/api/admin/")){let p=String(t.headers["x-agentx-peer"]||"").trim()||n.searchParams.get("peer")||"";if(p==="fleet"){if(r==="/api/admin/activity-graph"){await Jo(t,e,a);return}let m=r.match(/^\/api\/admin\/activity-graph\/dispatch\/(.+)$/);if(m){let f=decodeURIComponent(m[1]),w=f.indexOf("::");if(w>0){let b=f.slice(0,w),g=f.slice(w+2),v=a.config.node?.id||a.config.node?.name||"local";if(b===v)t.url=`/api/admin/activity-graph/dispatch/${encodeURIComponent(g)}`;else{let C=(a.config.dashboard?.daemons||[]).find(y=>(y.name||y.url)===b);if(C){let y=nt(C.url.replace(/\/+$/,""),a.config);if(y){await La(t,e,y,`/api/admin/activity-graph/dispatch/${encodeURIComponent(g)}`);return}}k(e,404,{error:`unknown node "${b}" in fleet dispatch id`});return}}}}else if(p&&p!=="primary"){let m=nt(p,a.config);if(!m){k(e,404,{error:`unknown peer: ${p}`});return}let f=(n.search||"").replace(/[?&]peer=[^&]*/g,b=>b.startsWith("?")?"?":"");await La(t,e,m,r+(f==="?"?"":f));return}}if(o==="GET"&&r.startsWith("/api/admin/observability/")&&await Qt(t,e,r))return;if(o==="GET"&&r==="/api/admin/logs/stream"){await Xo(t,e);return}if(o==="GET"&&r==="/api/admin/doctor"){try{let{runDoctorChecks:p}=await import("./doctor-SEOUFHSL.js"),f=new URL(t.url||"/","http://_").searchParams.get("running")!=="false",w=await p({running:f});k(e,200,w)}catch(p){k(e,500,{error:p?.message||String(p)})}return}if(o==="GET"&&r==="/admin/ledger"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(ta({peers:H(a.config)}));return}if(o==="GET"&&r.startsWith("/api/admin/ledger/")&&await ea(t,e,r))return;if(o==="GET"&&r==="/admin/cost"){e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(aa({peers:H(a.config)}));return}if(o==="GET"&&(r==="/admin/wiki"||r.startsWith("/admin/wiki/"))){if(r==="/admin/wiki"){e.writeHead(302,{Location:"/admin/wiki/"}),e.end();return}await Oo(a)(t,e);return}if(o==="GET"&&r==="/admin/activity-graph"){sa(t,e,H(a.config));return}if(o==="GET"&&r==="/api/admin/activity-graph"&&await ga(t,e,r)||o==="GET"&&r.startsWith("/api/admin/activity-graph/stream")&&await ha(t,e,r)||o==="GET"&&r.startsWith("/api/admin/activity-graph/dispatch/")&&await va(t,e,r))return;let s=o==="GET"&&r.match(/^\/admin\/agents\/([a-z0-9][a-z0-9_-]*)$/);if(s){ya(t,e,s[1],H(a.config),a.token);return}if(r.startsWith("/api/admin/")&&a.token){let p=t.headers.authorization||"",m=p.startsWith("Bearer ")?p.slice(7):"";if(m!==a.token){let f=new U,w=m?f.verify(m):null;if(!w||!ce(w,"dashboard:write")){k(e,401,{error:"unauthorized"});return}}}if(o==="GET"&&r==="/api/admin/config"){await Bt(t,e);return}if(r.startsWith("/api/admin/graph/")){await Ut(t,e,r);return}if(r.startsWith("/api/admin/agent/")){await wa(t,e,r);return}if(r.startsWith("/api/admin/")){await jt(t,e,r);return}if(o==="GET"&&r==="/sortable.min.js"){e.writeHead(200,{"Content-Type":"application/javascript; charset=utf-8","Cache-Control":"public, max-age=86400"}),e.end(Ct);return}if(o==="GET"&&r.startsWith("/assets/")){let p=r.slice(8);if(!/^[a-zA-Z0-9._-]+\.(js|map|css)$/.test(p)){k(e,400,{error:"invalid asset name"});return}let m=Uo("web/"+p);try{let f=await Ko(m),w=p.endsWith(".map")?"application/json; charset=utf-8":p.endsWith(".css")?"text/css; charset=utf-8":"application/javascript; charset=utf-8";e.writeHead(200,{"Content-Type":w,"Cache-Control":"public, max-age=60"}),e.end(f)}catch{k(e,404,{error:"asset not found",hint:"run: npm run build:web"})}return}if(o==="GET"&&r==="/api/live"){try{let p=await Pa(a.config);k(e,200,p)}catch(p){k(e,502,{error:p.message})}return}if(o==="GET"&&r==="/api/live/stream"){No(t,e,a.config);return}if(o==="GET"&&r==="/api/task/stream"){let p=new URL(t.url||"/","http://localhost"),m=p.searchParams.get("node"),f=p.searchParams.get("agent"),w=p.searchParams.get("task");if(!m||!f||!w){k(e,400,{error:"node, agent, task query params required"});return}await Bo(t,e,a,m,f,w);return}if(o==="GET"&&r==="/api/task/history"){let p=new URL(t.url||"/","http://localhost"),m=p.searchParams.get("node"),f=p.searchParams.get("agent"),w=p.searchParams.get("task"),b=p.searchParams.get("limit")||"50";if(!m||!f){k(e,400,{error:"node and agent query params required"});return}await qo(e,a,m,f,w,b);return}if(o==="GET"&&r==="/api/agents"){try{let p={};a.config.dashboard.token&&(p.Authorization=`Bearer ${a.config.dashboard.token}`);let m=await fetch(a.config.dashboard.daemonUrl.replace(/\/+$/,"")+"/agents",{headers:p});if(!m.ok)throw new Error(`HTTP ${m.status}`);let f=await m.json();k(e,200,f)}catch(p){k(e,502,{error:p.message||"agents fetch failed"})}return}if(o==="POST"&&r==="/api/draft"){let p=await le(t);try{let m=await Go(a.config,p);k(e,200,m)}catch(m){k(e,502,{error:m.message||"draft failed"})}return}let i=r.match(/^\/api\/public\/agents\/([^/]+)\/messages$/);if(i&&o==="POST"){let p=i[1];if(!jo(t,e,[`agent:${p}`]))return;await zo(t,e,a.config,p);return}if(r.startsWith("/api/")&&a.token){let p=t.headers.authorization||"",m=p.startsWith("Bearer ")?p.slice(7):"";if(m!==a.token){let f=new U,w=m?f.verify(m):null;if(!w||!ce(w,"dashboard:write")){k(e,401,{error:"unauthorized"});return}}}if((o==="POST"||o==="PATCH"||o==="DELETE"||o==="PUT")&&r.startsWith("/api/")&&t.headers["x-requested-with"]!=="agentx-board"){k(e,400,{error:"missing X-Requested-With: agentx-board"});return}if(o==="GET"&&r==="/events"){let m=new URL(t.url||"/","http://localhost").searchParams.toString();e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*"});let f=[{name:a.config.node.name||"local",url:a.config.dashboard.daemonUrl,token:a.config.dashboard.token},...(a.config.dashboard.daemons||[]).map(v=>({name:v.name,url:v.url,token:v.token}))],w={flag:!1},b=[];t.on("close",()=>{w.flag=!0;for(let v of b)try{v.abort()}catch{}});let g=setInterval(()=>{if(!w.flag)try{e.write(`: ping
9397
+
9398
+ `)}catch{}},15e3);t.on("close",()=>clearInterval(g));for(let v of f)(async()=>{let C={Accept:"text/event-stream"};v.token&&(C.Authorization=`Bearer ${v.token}`);let y=new AbortController;b.push(y);try{let _=await fetch(`${v.url.replace(/\/+$/,"")}/events${m?`?${m}`:""}`,{headers:C,signal:y.signal});if(!_.ok||!_.body)return;let T=_.body.getReader(),M=new TextDecoder,I="";for(;!w.flag;){let{done:$,value:V}=await T.read();if($)break;I+=M.decode(V,{stream:!0});let ae;for(;(ae=I.indexOf(`
9399
+
9400
+ `))>=0;){let he=I.slice(0,ae);if(I=I.slice(ae+2),w.flag)break;try{e.write(he+`
9401
+
9402
+ `)}catch{w.flag=!0}}}}catch{}})();return}if(o==="GET"&&r==="/api/workflows/runs"){try{let p=new URL(t.url||"/","http://localhost"),m=Math.max(1,Math.min(500,Number(p.searchParams.get("limit")||50))),f=p.searchParams.get("workflowId")||void 0,w=a.workflowRuns.list({limit:m,workflowId:f}),b=`?limit=${m}${f?`&workflowId=${encodeURIComponent(f)}`:""}`,g=[{url:a.config.dashboard.daemonUrl,token:a.config.dashboard.token},...(a.config.dashboard.daemons||[]).map(_=>({url:_.url,token:_.token}))],v=await Promise.all(g.map(async _=>{let T={};_.token&&(T.Authorization=`Bearer ${_.token}`);try{let M=await fetch(`${_.url.replace(/\/+$/,"")}/api/workflows/runs${b}`,{headers:T});if(!M.ok)return[];let I=await M.json();return Array.isArray(I.runs)?I.runs:[]}catch{return[]}})),C=new Map;for(let _ of w)C.set(_.id,_);for(let _ of v)for(let T of _)C.set(T.id,T);let y=Array.from(C.values()).sort((_,T)=>_.updatedAt<T.updatedAt?1:-1).slice(0,m);k(e,200,{runs:y})}catch(p){k(e,502,{error:"runs fetch failed",message:p.message||String(p)})}return}let d=o==="GET"&&r.match(/^\/api\/workflows\/runs\/([^\/]+)$/);if(d){let p=decodeURIComponent(d[1]),m=a.workflowRuns.get(p);if(m){k(e,200,{run:m});return}let f=[{url:a.config.dashboard.daemonUrl,token:a.config.dashboard.token},...(a.config.dashboard.daemons||[]).map(w=>({url:w.url,token:w.token}))];for(let w of f)try{let b={};w.token&&(b.Authorization=`Bearer ${w.token}`);let g=await fetch(`${w.url.replace(/\/+$/,"")}/api/workflows/runs/${encodeURIComponent(p)}`,{headers:b});if(g.ok){k(e,200,await g.json());return}}catch{}k(e,404,{error:"run not found on any configured peer"});return}if(o==="POST"&&r==="/api/workflows/editor/chat"){try{let p=await le(t),m={"Content-Type":"application/json"};a.config.dashboard.token&&(m.Authorization=`Bearer ${a.config.dashboard.token}`);let f=a.config.dashboard.daemonUrl.replace(/\/+$/,""),w=await fetch(`${f}/api/workflows/editor/chat`,{method:"POST",headers:m,body:JSON.stringify(p??{})}),b=await w.json().catch(()=>({error:`HTTP ${w.status}`}));k(e,w.status,b)}catch(p){k(e,502,{error:"daemon unreachable",message:p.message||String(p)})}return}if(r.startsWith("/api/workflows/tasks")&&(o==="GET"||o==="POST")){try{let p=a.config.dashboard.daemonUrl.replace(/\/+$/,""),m={"Content-Type":"application/json",...a.config.dashboard.token?{Authorization:`Bearer ${a.config.dashboard.token}`}:{}},f=o==="POST"?await new Promise(g=>{let v=[];t.on("data",C=>v.push(C)),t.on("end",()=>g(Buffer.concat(v).toString("utf-8")))}):void 0,w=await fetch(`${p}${t.url}`,{method:o,headers:m,body:f}),b=await w.text();e.writeHead(w.status,{"Content-Type":w.headers.get("content-type")||"application/json"}),e.end(b)}catch(p){k(e,502,{error:"tasks proxy failed",message:p?.message||String(p)})}return}if(r.startsWith("/api/workflows")&&Ia(t,e,{store:a.workflowStore,runs:a.workflowRuns,layouts:a.workflowLayouts,requireScope:(m,f,w)=>{if(a.token)return!0;let b=De(m);if(!b)return!0;let g=new U().verify(b);if(!g)return k(f,401,{error:"invalid or revoked token"}),!1;for(let v of w)if(!ce(g,v))return k(f,403,{error:`token missing scope: ${v}`,scopes:g.scopes}),!1;return!0}}))return;if(o==="GET"&&r==="/api/boards"){k(e,200,a.boards.map(p=>({id:p.id,name:p.name,source:p.source,primaryToolLabel:p.primaryToolLabel,labels:p.labels,columns:p.columns,timeRangeDays:p.timeRangeDays})));return}let c=r.match(/^\/api\/boards\/([a-z0-9_-]+)\/members$/);if(c&&o==="GET"){let p=a.sources.get(c[1]);if(!p){k(e,404,{error:"unknown board"});return}try{let m=typeof p.listMembers=="function"?await p.listMembers():[];k(e,200,m)}catch(m){k(e,502,{error:m.message})}return}let u=r.match(/^\/api\/boards\/([a-z0-9_-]+)\/milestones$/);if(u&&o==="GET"){let p=a.sources.get(u[1]);if(!p){k(e,404,{error:"unknown board"});return}try{let m=typeof p.listMilestones=="function"?await p.listMilestones():[];k(e,200,m)}catch(m){k(e,502,{error:m.message})}return}let h=r.match(/^\/api\/boards\/([a-z0-9_-]+)\/items\/(.+?)\/move$/),x=r.match(/^\/api\/boards\/([a-z0-9_-]+)\/items(?:\/(.+))?$/);if(h&&o==="PATCH"){let[,p,m]=h,f=decodeURIComponent(m),w=a.boards.find(y=>y.id===p);if(!w){k(e,404,{error:`unknown board: ${p}`});return}let b=a.sources.get(p);if(!b){k(e,503,{error:`board "${p}" source unavailable`});return}let g=await le(t),v=w.columns.find(y=>y.id===g.to),C=w.columns.find(y=>y.id===g.from);if(!v){k(e,400,{error:`unknown 'to' column: ${g.to}`});return}try{await Do(b,f,C,v),et({actor:tt(t),action:"move",boardId:p,itemId:f,payload:{from:g.from,to:g.to}}),k(e,200,{ok:!0})}catch(y){k(e,502,{error:y.message||"transition failed"})}return}if(x){let[,p,m]=x,f=m?decodeURIComponent(m):void 0,w=a.boards.find(g=>g.id===p);if(!w){k(e,404,{error:`unknown board: ${p}`});return}let b=a.sources.get(p);if(!b){k(e,503,{error:`board "${p}" source unavailable`});return}if(o==="GET"&&!f){let{columns:g,totals:v}=await Lo(w,b,n.searchParams);k(e,200,{columns:g,totals:v});return}if(o==="GET"&&f){try{let g=typeof b.getItem=="function"?await b.getItem(f):null;if(!g){k(e,404,{error:"not found"});return}k(e,200,g)}catch(g){k(e,502,{error:g.message})}return}if(o==="PATCH"&&f){let g=await le(t);try{let v=typeof b.updateItem=="function"?await b.updateItem(f,g):null;if(!v){k(e,501,{error:"update unsupported"});return}et({actor:tt(t),action:"update",boardId:p,itemId:f,payload:g}),k(e,200,v)}catch(v){k(e,502,{error:v.message})}return}if(o==="POST"&&!f){let g=await le(t);if(!g.title){k(e,400,{error:"title required"});return}if(!b.capabilities.create||!b.create){k(e,400,{error:`source ${b.type} does not support create`});return}try{let v=await b.create({title:g.title,description:g.description,labels:g.labels,assigneeUsernames:g.assigneeUsernames,milestoneTitle:g.milestoneTitle,projectHint:g.projectHint});et({actor:tt(t),action:"create",boardId:p,itemId:v.id,payload:{title:g.title}}),k(e,201,v)}catch(v){k(e,502,{error:v.message})}return}}k(e,404,{error:"not found",path:r})}async function Lo(t,e,a){if(!e.capabilities.listAll||!e.listAll){let b={};for(let g of t.columns)b[g.id]=[];return{columns:b,totals:Object.fromEntries(t.columns.map(g=>[g.id,0]))}}let n=[];t.primaryToolLabel&&n.push(t.primaryToolLabel);let r=a.get("label")||void 0;r&&n.push(r);let o=a.get("q")||void 0,s=a.get("milestone")||void 0,i=a.get("assignee")||void 0,l=parseInt(a.get("days")||String(t.timeRangeDays),10),d=parseInt(a.get("closedDays")||String(t.closedWindowDays??30),10),c=t.columns.some(b=>b.kind!=="closed"),u=t.columns.some(b=>b.kind==="closed"),h=c?await e.listAll({sinceDays:l,labels:n.length?n:void 0,search:o,milestone:s,state:"opened"}):[],x=u?await e.listAll({sinceDays:d,labels:n.length?n:void 0,search:o,milestone:s,state:"closed"}):[],p={};for(let b of t.columns)p[b.id]=[];let m=t.columns.find(b=>b.kind==="closed");for(let b of x)m&&p[m.id].push({...b,stage:m.id});let f=t.columns.filter(b=>b.kind!=="closed");for(let b of h){let g=dt(b,f);if(!g)continue;let v=p[g];v&&v.push({...b,stage:g})}if(i)for(let b of Object.keys(p))p[b]=p[b].filter(g=>g.assignee===i);for(let b of Object.keys(p))p[b].sort((g,v)=>{let C=(g.priority??99)-(v.priority??99);return C!==0?C:(v.updatedAt||"").localeCompare(g.updatedAt||"")});let w=Object.fromEntries(Object.entries(p).map(([b,g])=>[b,g.length]));return{columns:p,totals:w}}async function Do(t,e,a,n){let r=ct(a,n),o=t;r.closeIssue&&typeof o.setState=="function"?await o.setState(e,"close"):r.reopen&&typeof o.setState=="function"&&await o.setState(e,"reopen"),(r.addLabels?.length||r.removeLabels?.length)&&typeof o.transitionMany=="function"?await o.transitionMany(e,r.addLabels||[],r.removeLabels||[]):r.addLabels?.length&&t.transition&&await t.transition(e,r.addLabels[0],r.removeLabels?.[0])}function Po(t,e){if(t.source.type==="gitlab"){let a=e.channels.gitlab;if(!a?.token)throw new Error("channels.gitlab.token is required for gitlab boards");let n={};for(let r of a.agentMappings)r.gitlabUsernames.length&&(n[r.agentId]=r.gitlabUsernames);return new ht(a.host,a.token,t.source.projects,n,(...r)=>console.log("[board]",...r))}throw new Error(`unsupported board source: ${t.source.type}`)}function et(t){try{let e=ge(process.cwd(),".agentx");Da(e)||Co(e,{recursive:!0});let a=JSON.stringify({ts:new Date().toISOString(),...t})+`
9403
+ `;To(ge(e,"board-audit.jsonl"),a)}catch{}}function tt(t){let e=t.headers["x-forwarded-for"];return(Array.isArray(e)?e[0]:e?.split(",")[0])||t.socket.remoteAddress||"unknown"}async function Ro(t,e,a){let n={};e&&(n.Authorization=`Bearer ${e}`);let r={id:t,name:t,url:t,reachable:!1,agents:[]};try{let[o,s,i]=await Promise.all([fetch(t+"/health",{headers:n,signal:a}).catch(()=>null),fetch(t+"/agents",{headers:n,signal:a}).catch(()=>null),fetch(t+"/mesh",{headers:n,signal:a}).catch(()=>null)]);if(!s||!s.ok)return r.error=s?`HTTP ${s.status}`:"unreachable",r;let l=await s.json();if(r.agents=l.map(d=>({id:d.id,name:d.name,tier:d.tier,model:d.model,active:d.active||0,total:d.total||0,errors:d.errors||0,lastActive:d.lastActive,lastSummary:d.lastSummary,hourlyTasks:Array.isArray(d.hourlyTasks)?d.hourlyTasks:void 0,runningTasks:Array.isArray(d.runningTasks)?d.runningTasks:[]})),o&&o.ok){let d=await o.json();r.uptimeSec=d.uptime,r.name=d.node?.name||d.node?.id||t,r.id=d.node?.id||t,d.usage&&(r.usage=d.usage)}r.reachable=!0,r._peers=Array.isArray(i&&await Ho(i))?r._peers:void 0}catch(o){r.error=o.message||String(o)}return r}var Ae=null;function Oo(t){if(Ae)return Ae;let e=ge(process.cwd(),".agentx/wiki");return Ae=Tt({wikiDir:e,pathPrefix:"/admin/wiki",mode:"graph"}),Ae}var at=new WeakMap;async function Ho(t){if(at.has(t))return at.get(t);try{let e=t.ok?await t.json():null;return at.set(t,e),e}catch{return null}}async function rt(t,e,a){try{let n={};e&&(n.Authorization=`Bearer ${e}`);let r=await fetch(t+"/mesh",{headers:n,signal:a});return r.ok?(await r.json()).filter(s=>s&&s.peerUrl).map(s=>({url:String(s.peerUrl).replace(/\/+$/,""),name:s.peer||s.peerUrl})):[]}catch{return[]}}async function Pa(t){let e=t.dashboard,a=e.daemonUrl.replace(/\/+$/,""),n=e.token,r=new Map;r.set(a,{name:"primary",url:a,token:n});for(let i of e.daemons){let l=i.url.replace(/\/+$/,"");r.has(l)||r.set(l,{name:i.name,url:l,token:i.token})}let o=new AbortController,s=setTimeout(()=>o.abort(),3e3);try{let i=await rt(a,n,o.signal);for(let d of i)r.has(d.url)||r.set(d.url,d);let l=await Promise.all([...r.values()].map(d=>Ro(d.url,d.token,o.signal)));return{ts:new Date().toISOString(),nodes:l}}finally{clearTimeout(s)}}function No(t,e,a){e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let n=!1,r=(i,l)=>{n||e.write(`event: ${i}
7244
9404
  data: ${JSON.stringify(l)}
7245
9405
 
7246
- `)},r=async()=>{try{let i=await At(a);o("snapshot",i)}catch(i){o("error",{message:i.message})}};r();let s=setInterval(r,2e3);t.on("close",()=>{n=!0,clearInterval(s),e.end()})}async function Pn(t,e,a,n,o,r){let s=n.replace(/\/+$/,""),i=new Set;i.add(a.config.dashboard.daemonUrl.replace(/\/+$/,""));for(let c of a.config.dashboard.daemons)i.add(c.url.replace(/\/+$/,""));try{let c=await Ce(a.config.dashboard.daemonUrl.replace(/\/+$/,""),a.config.dashboard.token);for(let m of c)i.add(m.url.replace(/\/+$/,""))}catch{}if(!i.has(s)){h(e,403,{error:"node not in dashboard allowlist",target:s});return}let l=s===a.config.dashboard.daemonUrl.replace(/\/+$/,"")?a.config.dashboard.token:a.config.dashboard.daemons.find(c=>c.url.replace(/\/+$/,"")===s)?.token,d={Accept:"text/event-stream"};l&&(d.Authorization=`Bearer ${l}`);let u=new AbortController,p;try{p=await fetch(`${s}/agents/${encodeURIComponent(o)}/tasks/${encodeURIComponent(r)}/stream`,{headers:d,signal:u.signal})}catch(c){h(e,502,{error:c.message||"upstream connect failed"});return}if(!p.ok||!p.body){h(e,p.status||502,{error:`upstream HTTP ${p.status}`});return}e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*","X-Accel-Buffering":"no"});let f=p.body.getReader();(async()=>{try{for(;;){let{value:c,done:m}=await f.read();if(m)break;c&&e.write(c)}}catch{}finally{try{e.end()}catch{}}})(),t.on("close",()=>{u.abort();try{e.end()}catch{}})}function Dn(t,e,a){let n=le(t);if(!n)return h(e,401,{error:"missing token",hint:"pass Authorization: Bearer <token> (agentx token create)"}),null;let o=new D().verify(n);if(!o)return h(e,401,{error:"invalid or revoked token"}),null;for(let r of a)if(!K(o,r))return h(e,403,{error:`token missing scope: ${r}`,scopes:o.scopes}),null;return o}async function Hn(t,e,a,n){let o=a.agents?.[n];if(!o){h(e,404,{error:"unknown agent"});return}if(o.access!=="public"){h(e,403,{error:"agent is private",hint:`set agents.${n}.access = "public" to expose it`});return}let r;try{r=await W(t)}catch{h(e,400,{error:"invalid JSON body"});return}let s=r?.message;if(!s||typeof s!="string"){h(e,400,{error:"required: { message: string, context?: {...} }"});return}let i=a.dashboard.daemonUrl.replace(/\/+$/,""),l={"Content-Type":"application/json"};a.dashboard.token&&(l.Authorization=`Bearer ${a.dashboard.token}`);try{let d=await fetch(`${i}/task`,{method:"POST",headers:l,body:JSON.stringify({agent:n,message:s,context:{channel:"public-api",sender:r.context?.sender||"api",...r.context||{}}})}),u=await d.text();e.writeHead(d.status,{"Content-Type":d.headers.get("content-type")||"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),e.end(u)}catch(d){h(e,502,{error:d.message||"upstream call failed"})}}async function On(t,e,a,n,o,r){let s=a.replace(/\/+$/,""),i=new Set;i.add(e.config.dashboard.daemonUrl.replace(/\/+$/,""));for(let p of e.config.dashboard.daemons)i.add(p.url.replace(/\/+$/,""));try{let p=await Ce(e.config.dashboard.daemonUrl.replace(/\/+$/,""),e.config.dashboard.token);for(let f of p)i.add(f.url.replace(/\/+$/,""))}catch{}if(!i.has(s)){h(t,403,{error:"node not in dashboard allowlist",target:s});return}let l=s===e.config.dashboard.daemonUrl.replace(/\/+$/,"")?e.config.dashboard.token:e.config.dashboard.daemons.find(p=>p.url.replace(/\/+$/,"")===s)?.token,d={Accept:"application/json"};l&&(d.Authorization=`Bearer ${l}`);let u=o?`/agents/${encodeURIComponent(n)}/tasks/${encodeURIComponent(o)}`:`/agents/${encodeURIComponent(n)}/tasks?limit=${encodeURIComponent(r)}`;try{let p=await fetch(`${s}${u}`,{headers:d}),f=await p.text();t.writeHead(p.status,{"Content-Type":p.headers.get("content-type")||"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),t.end(f)}catch(p){h(t,502,{error:p.message||"upstream fetch failed"})}}var Rn=`
9406
+ `)},o=async()=>{try{let i=await Pa(a);r("snapshot",i)}catch(i){r("error",{message:i.message})}};o();let s=setInterval(o,2e3);t.on("close",()=>{n=!0,clearInterval(s),e.end()})}async function Bo(t,e,a,n,r,o){let s=n.replace(/\/+$/,""),i=new Set;i.add(a.config.dashboard.daemonUrl.replace(/\/+$/,""));for(let p of a.config.dashboard.daemons)i.add(p.url.replace(/\/+$/,""));try{let p=await rt(a.config.dashboard.daemonUrl.replace(/\/+$/,""),a.config.dashboard.token);for(let m of p)i.add(m.url.replace(/\/+$/,""))}catch{}if(!i.has(s)){k(e,403,{error:"node not in dashboard allowlist",target:s});return}let l=s===a.config.dashboard.daemonUrl.replace(/\/+$/,"")?a.config.dashboard.token:a.config.dashboard.daemons.find(p=>p.url.replace(/\/+$/,"")===s)?.token,d={Accept:"text/event-stream"};l&&(d.Authorization=`Bearer ${l}`);let c=new AbortController,u;try{u=await fetch(`${s}/agents/${encodeURIComponent(r)}/tasks/${encodeURIComponent(o)}/stream`,{headers:d,signal:c.signal})}catch(p){k(e,502,{error:p.message||"upstream connect failed"});return}if(!u.ok||!u.body){k(e,u.status||502,{error:`upstream HTTP ${u.status}`});return}e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","Access-Control-Allow-Origin":"*","X-Accel-Buffering":"no"});let h=u.body.getReader();(async()=>{try{for(;;){let{value:p,done:m}=await h.read();if(m)break;p&&e.write(p)}}catch{}finally{try{e.end()}catch{}}})(),t.on("close",()=>{c.abort();try{e.end()}catch{}})}function jo(t,e,a){let n=De(t);if(!n)return k(e,401,{error:"missing token",hint:"pass Authorization: Bearer <token> (agentx token create)"}),null;let r=new U().verify(n);if(!r)return k(e,401,{error:"invalid or revoked token"}),null;for(let o of a)if(!ce(r,o))return k(e,403,{error:`token missing scope: ${o}`,scopes:r.scopes}),null;return r}async function zo(t,e,a,n){let r=a.agents?.[n];if(!r){k(e,404,{error:"unknown agent"});return}if(r.access!=="public"){k(e,403,{error:"agent is private",hint:`set agents.${n}.access = "public" to expose it`});return}let o;try{o=await le(t)}catch{k(e,400,{error:"invalid JSON body"});return}let s=o?.message;if(!s||typeof s!="string"){k(e,400,{error:"required: { message: string, context?: {...} }"});return}let i=a.dashboard.daemonUrl.replace(/\/+$/,""),l={"Content-Type":"application/json"};a.dashboard.token&&(l.Authorization=`Bearer ${a.dashboard.token}`);try{let d=await fetch(`${i}/task`,{method:"POST",headers:l,body:JSON.stringify({agent:n,message:s,context:{channel:"public-api",sender:o.context?.sender||"api",...o.context||{}}})}),c=await d.text();e.writeHead(d.status,{"Content-Type":d.headers.get("content-type")||"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),e.end(c)}catch(d){k(e,502,{error:d.message||"upstream call failed"})}}async function qo(t,e,a,n,r,o){let s=a.replace(/\/+$/,""),i=new Set;i.add(e.config.dashboard.daemonUrl.replace(/\/+$/,""));for(let u of e.config.dashboard.daemons)i.add(u.url.replace(/\/+$/,""));try{let u=await rt(e.config.dashboard.daemonUrl.replace(/\/+$/,""),e.config.dashboard.token);for(let h of u)i.add(h.url.replace(/\/+$/,""))}catch{}if(!i.has(s)){k(t,403,{error:"node not in dashboard allowlist",target:s});return}let l=s===e.config.dashboard.daemonUrl.replace(/\/+$/,"")?e.config.dashboard.token:e.config.dashboard.daemons.find(u=>u.url.replace(/\/+$/,"")===s)?.token,d={Accept:"application/json"};l&&(d.Authorization=`Bearer ${l}`);let c=r?`/agents/${encodeURIComponent(n)}/tasks/${encodeURIComponent(r)}`:`/agents/${encodeURIComponent(n)}/tasks?limit=${encodeURIComponent(o)}`;try{let u=await fetch(`${s}${c}`,{headers:d}),h=await u.text();t.writeHead(u.status,{"Content-Type":u.headers.get("content-type")||"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),t.end(h)}catch(u){k(t,502,{error:u.message||"upstream fetch failed"})}}var Fo=`
7247
9407
  You are helping draft a new GitLab issue. Follow these conventions strictly.
7248
9408
 
7249
9409
  Title pattern:
@@ -7275,8 +9435,16 @@ Return ONLY a single JSON object on one line \u2014 no prose, no code fences, no
7275
9435
  Shape:
7276
9436
  { "title": string, "description": string, "labels": string[], "assigneeUsername": string | null, "kind": string }
7277
9437
  If you don't have enough information to pick a label confidently, omit it rather than guessing.
7278
- `;async function Nn(t,e){let a=(e?.rough||"").toString().trim();if(!a)throw new Error("rough description required");let n=e?.agent||t.dashboard.draftAgent;if(!n)throw new Error("no draft agent configured (set dashboard.draftAgent or pass agent in body)");let o=e?.context||{},r=["Rough description from the user:",'"""',a,'"""',"",`Board: ${o.boardName||"(unspecified)"}`,o.primaryLabel?`Primary board label (include in labels): ${o.primaryLabel}`:"",o.columnLabel?`Target column label (include in labels): ${o.columnLabel}`:"",o.project?`GitLab project: ${o.project}`:"",o.knownLabels?.length?`Known labels in this project (prefer picking from these when they fit):
7279
- - ${o.knownLabels.slice(0,60).join(`
7280
- - `)}`:"",o.members?.length?`Possible assignees (GitLab usernames): ${o.members.slice(0,20).join(", ")}`:"","","Produce the JSON draft now."].filter(Boolean).join(`
7281
- `),s=t.dashboard.daemonUrl.replace(/\/+$/,"")+"/task",i={"Content-Type":"application/json"};t.dashboard.token&&(i.Authorization=`Bearer ${t.dashboard.token}`);let l=await fetch(s,{method:"POST",headers:i,body:JSON.stringify({agent:n,message:r,context:{channel:"a2a",sender:"board-dashboard",systemPrompt:Rn}})});if(!l.ok)throw new Error(`daemon /task HTTP ${l.status}: ${await l.text().catch(()=>"")}`);let d=await l.json();if(d.error)throw new Error(String(d.error));let u=(d.content||"").toString(),p=Bn(u);if(!p||typeof p.title!="string")throw new Error("agent did not return a valid JSON draft. Raw: "+u.slice(0,400));let f=Array.isArray(p.labels)?p.labels.filter(b=>typeof b=="string"):[];return o.primaryLabel&&!f.includes(o.primaryLabel)&&f.unshift(o.primaryLabel),o.columnLabel&&!f.includes(o.columnLabel)&&f.unshift(o.columnLabel),{title:p.title.trim(),description:(p.description||"").toString(),labels:f,assigneeUsername:typeof p.assigneeUsername=="string"?p.assigneeUsername:void 0,kind:typeof p.kind=="string"?p.kind:void 0,raw:u}}function Bn(t){if(!t)return null;try{return JSON.parse(t)}catch{}let e=t.match(/```(?:json)?\s*([\s\S]*?)```/);if(e)try{return JSON.parse(e[1])}catch{}let a=t.indexOf("{");if(a===-1)return null;let n=0;for(let o=a;o<t.length;o++){let r=t[o];if(r==="{")n++;else if(r==="}"&&(n--,n===0))try{return JSON.parse(t.slice(a,o+1))}catch{return null}}return null}function jn(t){let e=_n(En(import.meta.url));return re(e,t)}async function zn(t){return Sn(t)}function h(t,e,a){t.writeHead(e,{"Content-Type":"application/json; charset=utf-8"}),t.end(JSON.stringify(a))}async function W(t){return new Promise((e,a)=>{let n=[];t.on("data",o=>n.push(o)),t.on("end",()=>{try{let o=Buffer.concat(n).toString("utf-8");e(o?JSON.parse(o):{})}catch(o){a(o)}}),t.on("error",a)})}function $t(t){return t.dashboardUrl?t.dashboardUrl.replace(/\/+$/,""):t.url.replace(/\/+$/,"").replace(/:(?:1[89][89]00|18810)$/,":4202")}function B(t,e){let a=e||t,n={id:"primary",name:a.node?.name||"this daemon",dashboardUrl:"",primary:!0},o=(a.dashboard?.daemons||[]).map(r=>({id:r.url.replace(/\/+$/,""),name:r.name,dashboardUrl:$t(r),tokenScope:r.dashboardToken||r.token?"proxy-ready":void 0}));return[n,...o]}async function qn(t,e,a,n){let o=a.url+n,r=(t.method||"GET").toUpperCase(),i=r!=="GET"&&r!=="HEAD"?await new Promise((p,f)=>{let b=[];t.on("data",c=>b.push(c)),t.on("end",()=>p(Buffer.concat(b))),t.on("error",f)}):void 0,l={},d=t.headers["content-type"];d&&(l["Content-Type"]=Array.isArray(d)?d[0]:d);let u=t.headers["x-requested-with"];u&&(l["X-Requested-With"]=Array.isArray(u)?u[0]:u),a.token&&(l.Authorization=`Bearer ${a.token}`),l["X-Agentx-Peer"]="primary";try{let p=await fetch(o,{method:r,headers:l,body:i&&i.length>0?i:void 0}),f=Buffer.from(await p.arrayBuffer());e.writeHead(p.status,{"Content-Type":p.headers.get("content-type")||"application/json","Cache-Control":"no-store","Access-Control-Allow-Origin":"*"}),e.end(f)}catch(p){h(e,502,{error:`proxy to peer failed: ${p?.message||p}`,target:o})}}function Gn(t,e){let a=t.replace(/\/+$/,""),n=(e.dashboard?.daemons||[]).find(o=>o.url.replace(/\/+$/,"")===a);return n?{url:$t(n),token:n.dashboardToken||n.token}:null}export{dr as startBoardDashboard};
7282
- //# sourceMappingURL=board-dashboard-KX2CNOSY.js.map
9438
+ `;async function Go(t,e){let a=(e?.rough||"").toString().trim();if(!a)throw new Error("rough description required");let n=e?.agent||t.dashboard.draftAgent;if(!n)throw new Error("no draft agent configured (set dashboard.draftAgent or pass agent in body)");let r=e?.context||{},o=["Rough description from the user:",'"""',a,'"""',"",`Board: ${r.boardName||"(unspecified)"}`,r.primaryLabel?`Primary board label (include in labels): ${r.primaryLabel}`:"",r.columnLabel?`Target column label (include in labels): ${r.columnLabel}`:"",r.project?`GitLab project: ${r.project}`:"",r.knownLabels?.length?`Known labels in this project (prefer picking from these when they fit):
9439
+ - ${r.knownLabels.slice(0,60).join(`
9440
+ - `)}`:"",r.members?.length?`Possible assignees (GitLab usernames): ${r.members.slice(0,20).join(", ")}`:"","","Produce the JSON draft now."].filter(Boolean).join(`
9441
+ `),s=t.dashboard.daemonUrl.replace(/\/+$/,"")+"/task",i={"Content-Type":"application/json"};t.dashboard.token&&(i.Authorization=`Bearer ${t.dashboard.token}`);let l=await fetch(s,{method:"POST",headers:i,body:JSON.stringify({agent:n,message:o,context:{channel:"a2a",sender:"board-dashboard",systemPrompt:Fo}})});if(!l.ok)throw new Error(`daemon /task HTTP ${l.status}: ${await l.text().catch(()=>"")}`);let d=await l.json();if(d.error)throw new Error(String(d.error));let c=(d.content||"").toString(),u=Wo(c);if(!u||typeof u.title!="string")throw new Error("agent did not return a valid JSON draft. Raw: "+c.slice(0,400));let h=Array.isArray(u.labels)?u.labels.filter(x=>typeof x=="string"):[];return r.primaryLabel&&!h.includes(r.primaryLabel)&&h.unshift(r.primaryLabel),r.columnLabel&&!h.includes(r.columnLabel)&&h.unshift(r.columnLabel),{title:u.title.trim(),description:(u.description||"").toString(),labels:h,assigneeUsername:typeof u.assigneeUsername=="string"?u.assigneeUsername:void 0,kind:typeof u.kind=="string"?u.kind:void 0,raw:c}}function Wo(t){if(!t)return null;try{return JSON.parse(t)}catch{}let e=t.match(/```(?:json)?\s*([\s\S]*?)```/);if(e)try{return JSON.parse(e[1])}catch{}let a=t.indexOf("{");if(a===-1)return null;let n=0;for(let r=a;r<t.length;r++){let o=t[r];if(o==="{")n++;else if(o==="}"&&(n--,n===0))try{return JSON.parse(t.slice(a,r+1))}catch{return null}}return null}function Uo(t){let e=Mo($o(import.meta.url));return ge(e,t)}async function Ko(t){return Ao(t)}function k(t,e,a){t.writeHead(e,{"Content-Type":"application/json; charset=utf-8"}),t.end(JSON.stringify(a))}async function le(t){return new Promise((e,a)=>{let n=[];t.on("data",r=>n.push(r)),t.on("end",()=>{try{let r=Buffer.concat(n).toString("utf-8");e(r?JSON.parse(r):{})}catch(r){a(r)}}),t.on("error",a)})}function Ra(t){return t.dashboardUrl?t.dashboardUrl.replace(/\/+$/,""):t.url.replace(/\/+$/,"").replace(/:(?:1[89][89]00|18810)$/,":4202")}function H(t,e){let a=e||t,n={id:"primary",name:a.node?.name||"this daemon",dashboardUrl:"",primary:!0},r=(a.dashboard?.daemons||[]).map(s=>({id:s.url.replace(/\/+$/,""),name:s.name,dashboardUrl:Ra(s),tokenScope:s.dashboardToken||s.token?"proxy-ready":void 0})),o=r.length>0?[{id:"fleet",name:"Fleet (all peers)",dashboardUrl:"",tokenScope:"merged across nodes"}]:[];return[n,...r,...o]}async function Jo(t,e,a){let n=new URL(t.url||"/","http://_"),r=parseInt(n.searchParams.get("hours")||"6",10)||6,o=a.config.node?.id||a.config.node?.name||"local",s=[],i=pa(r);i&&s.push({nodeId:o,snap:i});let l=(a.config.dashboard?.daemons||[]).filter(d=>d.url);await Promise.all(l.map(async d=>{let c=nt(d.url.replace(/\/+$/,""),a.config);if(c)try{let u=new AbortController,h=setTimeout(()=>u.abort(),8e3);try{let x=await fetch(`${c.url}/api/admin/activity-graph?hours=${r}`,{headers:{Accept:"application/json","X-Agentx-Peer":"primary",...c.token?{Authorization:`Bearer ${c.token}`}:{}},signal:u.signal});if(!x.ok)return;let p=await x.json();s.push({nodeId:d.name||d.url,snap:p})}finally{clearTimeout(h)}}catch{}})),k(e,200,ua(s))}async function Xo(t,e){let{spawn:a,execSync:n}=await import("child_process"),r,o;try{n("which journalctl",{stdio:"pipe"}),r="journalctl",o=["-u","agentx","--no-pager","-n","200","-f","-o","cat"]}catch{let u=process.env.HOME||"",h=[u&&`${u}/.agentx/logs/daemon-stdout.log`,"/tmp/agentx-daemon.log"].filter(Boolean),x=h.find(p=>Da(p));if(!x){e.writeHead(503,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"no log source \u2014 install journalctl, run the daemon under launchd, or run it foreground writing to /tmp/agentx-daemon.log",searched:h}));return}r="tail",o=["-F","-n","200",x]}e.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),e.write(`: source=${r}
9442
+
9443
+ `);let s=a(r,o,{stdio:["ignore","pipe","pipe"]}),i="",l=u=>{i+=u.toString("utf8");let h;for(;(h=i.indexOf(`
9444
+ `))>=0;){let x=i.slice(0,h).replace(/\r$/,"");i=i.slice(h+1);try{e.write(`event: line
9445
+ data: ${x.replace(/\n/g," ")}
9446
+
9447
+ `)}catch{}}};s.stdout?.on("data",l),s.stderr?.on("data",l);let d=setInterval(()=>{try{e.write(`: hb
9448
+
9449
+ `)}catch{}},15e3),c=()=>{clearInterval(d);try{s.kill()}catch{}try{e.end()}catch{}};t.on("close",c),s.on("exit",c),s.on("error",c)}async function La(t,e,a,n){let r=a.url+n,o=(t.method||"GET").toUpperCase(),i=o!=="GET"&&o!=="HEAD"?await new Promise((u,h)=>{let x=[];t.on("data",p=>x.push(p)),t.on("end",()=>u(Buffer.concat(x))),t.on("error",h)}):void 0,l={},d=t.headers["content-type"];d&&(l["Content-Type"]=Array.isArray(d)?d[0]:d);let c=t.headers["x-requested-with"];c&&(l["X-Requested-With"]=Array.isArray(c)?c[0]:c),a.token&&(l.Authorization=`Bearer ${a.token}`),l["X-Agentx-Peer"]="primary";try{let u=await fetch(r,{method:o,headers:l,body:i&&i.length>0?i:void 0}),h=Buffer.from(await u.arrayBuffer());e.writeHead(u.status,{"Content-Type":u.headers.get("content-type")||"application/json","Cache-Control":"no-store","Access-Control-Allow-Origin":"*"}),e.end(h)}catch(u){k(e,502,{error:`proxy to peer failed: ${u?.message||u}`,target:r})}}function nt(t,e){let a=t.replace(/\/+$/,""),n=(e.dashboard?.daemons||[]).find(r=>r.url.replace(/\/+$/,"")===a);return n?{url:Ra(n),token:n.dashboardToken||n.token}:null}export{qi as startBoardDashboard};
9450
+ //# sourceMappingURL=board-dashboard-HXDOYGIG.js.map