diff --git a/.gitignore b/.gitignore index 13613a58..80f86335 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ edict/frontend/node_modules/ # Backups *.bak* data +.helloagents/ diff --git a/dashboard/dist/assets/index-BVCpkIeJ.js b/dashboard/dist/assets/index-BVCpkIeJ.js new file mode 100644 index 00000000..d2440a30 --- /dev/null +++ b/dashboard/dist/assets/index-BVCpkIeJ.js @@ -0,0 +1,89 @@ +(function(){const h=document.createElement("link").relList;if(h&&h.supports&&h.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))y(S);new MutationObserver(S=>{for(const I of S)if(I.type==="childList")for(const F of I.addedNodes)F.tagName==="LINK"&&F.rel==="modulepreload"&&y(F)}).observe(document,{childList:!0,subtree:!0});function u(S){const I={};return S.integrity&&(I.integrity=S.integrity),S.referrerPolicy&&(I.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?I.credentials="include":S.crossOrigin==="anonymous"?I.credentials="omit":I.credentials="same-origin",I}function y(S){if(S.ep)return;S.ep=!0;const I=u(S);fetch(S.href,I)}})();function to(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var Hi={exports:{}},Br={},Vi={exports:{}},xe={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Hu;function ff(){if(Hu)return xe;Hu=1;var o=Symbol.for("react.element"),h=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),y=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),I=Symbol.for("react.provider"),F=Symbol.for("react.context"),k=Symbol.for("react.forward_ref"),b=Symbol.for("react.suspense"),_=Symbol.for("react.memo"),T=Symbol.for("react.lazy"),f=Symbol.iterator;function w(g){return g===null||typeof g!="object"?null:(g=f&&g[f]||g["@@iterator"],typeof g=="function"?g:null)}var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},P=Object.assign,L={};function U(g,R,me){this.props=g,this.context=R,this.refs=L,this.updater=me||m}U.prototype.isReactComponent={},U.prototype.setState=function(g,R){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,R,"setState")},U.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function B(){}B.prototype=U.prototype;function W(g,R,me){this.props=g,this.context=R,this.refs=L,this.updater=me||m}var ne=W.prototype=new B;ne.constructor=W,P(ne,U.prototype),ne.isPureReactComponent=!0;var E=Array.isArray,fe=Object.prototype.hasOwnProperty,oe={current:null},pe={key:!0,ref:!0,__self:!0,__source:!0};function z(g,R,me){var ve,d={},M=null,V=null;if(R!=null)for(ve in R.ref!==void 0&&(V=R.ref),R.key!==void 0&&(M=""+R.key),R)fe.call(R,ve)&&!pe.hasOwnProperty(ve)&&(d[ve]=R[ve]);var ye=arguments.length-2;if(ye===1)d.children=me;else if(1>>1,R=H[g];if(0>>1;gS(d,Q))MS(V,d)?(H[g]=V,H[M]=Q,g=M):(H[g]=d,H[ve]=Q,g=ve);else if(MS(V,Q))H[g]=V,H[M]=Q,g=M;else break e}}return le}function S(H,le){var Q=H.sortIndex-le.sortIndex;return Q!==0?Q:H.id-le.id}if(typeof performance=="object"&&typeof performance.now=="function"){var I=performance;o.unstable_now=function(){return I.now()}}else{var F=Date,k=F.now();o.unstable_now=function(){return F.now()-k}}var b=[],_=[],T=1,f=null,w=3,m=!1,P=!1,L=!1,U=typeof setTimeout=="function"?setTimeout:null,B=typeof clearTimeout=="function"?clearTimeout:null,W=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function ne(H){for(var le=u(_);le!==null;){if(le.callback===null)y(_);else if(le.startTime<=H)y(_),le.sortIndex=le.expirationTime,h(b,le);else break;le=u(_)}}function E(H){if(L=!1,ne(H),!P)if(u(b)!==null)P=!0,ze(fe);else{var le=u(_);le!==null&&ke(E,le.startTime-H)}}function fe(H,le){P=!1,L&&(L=!1,B(z),z=-1),m=!0;var Q=w;try{for(ne(le),f=u(b);f!==null&&(!(f.expirationTime>le)||H&&!re());){var g=f.callback;if(typeof g=="function"){f.callback=null,w=f.priorityLevel;var R=g(f.expirationTime<=le);le=o.unstable_now(),typeof R=="function"?f.callback=R:f===u(b)&&y(b),ne(le)}else y(b);f=u(b)}if(f!==null)var me=!0;else{var ve=u(_);ve!==null&&ke(E,ve.startTime-le),me=!1}return me}finally{f=null,w=Q,m=!1}}var oe=!1,pe=null,z=-1,Ee=5,J=-1;function re(){return!(o.unstable_now()-JH||125g?(H.sortIndex=Q,h(_,H),u(b)===null&&H===u(_)&&(L?(B(z),z=-1):L=!0,ke(E,Q-g))):(H.sortIndex=R,h(b,H),P||m||(P=!0,ze(fe))),H},o.unstable_shouldYield=re,o.unstable_wrapCallback=function(H){var le=w;return function(){var Q=w;w=le;try{return H.apply(this,arguments)}finally{w=Q}}}})(Gi)),Gi}var Yu;function vf(){return Yu||(Yu=1,Ki.exports=hf()),Ki.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Xu;function gf(){if(Xu)return it;Xu=1;var o=Fr(),h=vf();function u(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),b=Object.prototype.hasOwnProperty,_=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,T={},f={};function w(e){return b.call(f,e)?!0:b.call(T,e)?!1:_.test(e)?f[e]=!0:(T[e]=!0,!1)}function m(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function P(e,t,n,r){if(t===null||typeof t>"u"||m(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function L(e,t,n,r,s,i,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=a}var U={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){U[e]=new L(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];U[t]=new L(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){U[e]=new L(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){U[e]=new L(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){U[e]=new L(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){U[e]=new L(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){U[e]=new L(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){U[e]=new L(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){U[e]=new L(e,5,!1,e.toLowerCase(),null,!1,!1)});var B=/[\-:]([a-z])/g;function W(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(B,W);U[t]=new L(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(B,W);U[t]=new L(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(B,W);U[t]=new L(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){U[e]=new L(e,1,!1,e.toLowerCase(),null,!1,!1)}),U.xlinkHref=new L("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){U[e]=new L(e,1,!1,e.toLowerCase(),null,!0,!0)});function ne(e,t,n,r){var s=U.hasOwnProperty(t)?U[t]:null;(s!==null?s.type!==0:r||!(2c||s[a]!==i[c]){var p=` +`+s[a].replace(" at new "," at ");return e.displayName&&p.includes("")&&(p=p.replace("",e.displayName)),p}while(1<=a&&0<=c);break}}}finally{me=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?R(e):""}function d(e){switch(e.tag){case 5:return R(e.type);case 16:return R("Lazy");case 13:return R("Suspense");case 19:return R("SuspenseList");case 0:case 2:case 15:return e=ve(e.type,!1),e;case 11:return e=ve(e.type.render,!1),e;case 1:return e=ve(e.type,!0),e;default:return""}}function M(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case pe:return"Fragment";case oe:return"Portal";case Ee:return"Profiler";case z:return"StrictMode";case ae:return"Suspense";case G:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case re:return(e.displayName||"Context")+".Consumer";case J:return(e._context.displayName||"Context")+".Provider";case je:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case _e:return t=e.displayName||null,t!==null?t:M(e.type)||"Memo";case ze:t=e._payload,e=e._init;try{return M(e(t))}catch{}}return null}function V(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return M(t);case 8:return t===z?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ye(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ge(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Me(e){var t=ge(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(a){r=""+a,i.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Pt(e){e._valueTracker||(e._valueTracker=Me(e))}function qn(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ge(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function C(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function X(e,t){var n=t.checked;return Q({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function ie(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ye(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Be(e,t){t=t.checked,t!=null&&ne(e,"checked",t,!1)}function dt(e,t){Be(e,t);var n=ye(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?_n(e,t.type,n):t.hasOwnProperty("defaultValue")&&_n(e,t.type,ye(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Cn(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function _n(e,t,n){(t!=="number"||C(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var cn=Array.isArray;function bt(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=Wt.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function tr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var nr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},hc=["Webkit","ms","Moz","O"];Object.keys(nr).forEach(function(e){hc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),nr[t]=nr[e]})});function lo(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||nr.hasOwnProperty(e)&&nr[e]?(""+t).trim():t+"px"}function so(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=lo(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var vc=Q({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ns(e,t){if(t){if(vc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(u(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(u(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(u(61))}if(t.style!=null&&typeof t.style!="object")throw Error(u(62))}}function rs(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var ls=null;function ss(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var is=null,En=null,zn=null;function io(e){if(e=Nr(e)){if(typeof is!="function")throw Error(u(280));var t=e.stateNode;t&&(t=pl(t),is(e.stateNode,e.type,t))}}function oo(e){En?zn?zn.push(e):zn=[e]:En=e}function ao(){if(En){var e=En,t=zn;if(zn=En=null,io(e),t)for(e=0;e>>=0,e===0?32:31-(Ec(e)/zc|0)|0}var Yr=64,Xr=4194304;function ir(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function qr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,i=e.pingedLanes,a=n&268435455;if(a!==0){var c=a&~s;c!==0?r=ir(c):(i&=a,i!==0&&(r=ir(i)))}else a=n&~s,a!==0?r=ir(a):i!==0&&(r=ir(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&s)===0&&(s=r&-r,i=t&-t,s>=i||s===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function or(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-xt(t),e[t]=n}function bc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=hr),Ao=" ",$o=!1;function Bo(e,t){switch(e){case"keyup":return id.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Oo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Pn=!1;function ad(e,t){switch(e){case"compositionend":return Oo(t);case"keypress":return t.which!==32?null:($o=!0,Ao);case"textInput":return e=t.data,e===Ao&&$o?null:e;default:return null}}function ud(e,t){if(Pn)return e==="compositionend"||!Ns&&Bo(e,t)?(e=Po(),nl=ys=Kt=null,Pn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Ko(n)}}function Yo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Yo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(){for(var e=window,t=C();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=C(e.document)}return t}function Es(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function yd(e){var t=Xo(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Yo(n.ownerDocument.documentElement,n)){if(r!==null&&Es(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,i=Math.min(r.start,s);r=r.end===void 0?i:Math.min(r.end,s),!e.extend&&i>r&&(s=r,r=i,i=s),s=Go(n,i);var a=Go(n,r);s&&a&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,bn=null,zs=null,xr=null,Ts=!1;function qo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ts||bn==null||bn!==C(r)||(r=bn,"selectionStart"in r&&Es(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),xr&&yr(xr,r)||(xr=r,r=cl(zs,"onSelect"),0An||(e.current=Fs[An],Fs[An]=null,An--)}function Te(e,t){An++,Fs[An]=e.current,e.current=t}var qt={},Ye=Xt(qt),tt=Xt(!1),mn=qt;function $n(e,t){var n=e.type.contextTypes;if(!n)return qt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},i;for(i in n)s[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function nt(e){return e=e.childContextTypes,e!=null}function ml(){Pe(tt),Pe(Ye)}function fa(e,t,n){if(Ye.current!==qt)throw Error(u(168));Te(Ye,t),Te(tt,n)}function pa(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(u(108,V(e)||"Unknown",s));return Q({},n,r)}function hl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||qt,mn=Ye.current,Te(Ye,e),Te(tt,tt.current),!0}function ma(e,t,n){var r=e.stateNode;if(!r)throw Error(u(169));n?(e=pa(e,t,mn),r.__reactInternalMemoizedMergedChildContext=e,Pe(tt),Pe(Ye),Te(Ye,e)):Pe(tt),Te(tt,n)}var Dt=null,vl=!1,Ws=!1;function ha(e){Dt===null?Dt=[e]:Dt.push(e)}function Ld(e){vl=!0,ha(e)}function Zt(){if(!Ws&&Dt!==null){Ws=!0;var e=0,t=Ce;try{var n=Dt;for(Ce=1;e>=a,s-=a,Mt=1<<32-xt(t)+s|n<de?(Qe=ce,ce=null):Qe=ce.sibling;var Ne=D(x,ce,j[de],O);if(Ne===null){ce===null&&(ce=Qe);break}e&&ce&&Ne.alternate===null&&t(x,ce),v=i(Ne,v,de),ue===null?te=Ne:ue.sibling=Ne,ue=Ne,ce=Qe}if(de===j.length)return n(x,ce),be&&vn(x,de),te;if(ce===null){for(;dede?(Qe=ce,ce=null):Qe=ce.sibling;var an=D(x,ce,Ne.value,O);if(an===null){ce===null&&(ce=Qe);break}e&&ce&&an.alternate===null&&t(x,ce),v=i(an,v,de),ue===null?te=an:ue.sibling=an,ue=an,ce=Qe}if(Ne.done)return n(x,ce),be&&vn(x,de),te;if(ce===null){for(;!Ne.done;de++,Ne=j.next())Ne=$(x,Ne.value,O),Ne!==null&&(v=i(Ne,v,de),ue===null?te=Ne:ue.sibling=Ne,ue=Ne);return be&&vn(x,de),te}for(ce=r(x,ce);!Ne.done;de++,Ne=j.next())Ne=K(ce,x,de,Ne.value,O),Ne!==null&&(e&&Ne.alternate!==null&&ce.delete(Ne.key===null?de:Ne.key),v=i(Ne,v,de),ue===null?te=Ne:ue.sibling=Ne,ue=Ne);return e&&ce.forEach(function(df){return t(x,df)}),be&&vn(x,de),te}function $e(x,v,j,O){if(typeof j=="object"&&j!==null&&j.type===pe&&j.key===null&&(j=j.props.children),typeof j=="object"&&j!==null){switch(j.$$typeof){case fe:e:{for(var te=j.key,ue=v;ue!==null;){if(ue.key===te){if(te=j.type,te===pe){if(ue.tag===7){n(x,ue.sibling),v=s(ue,j.props.children),v.return=x,x=v;break e}}else if(ue.elementType===te||typeof te=="object"&&te!==null&&te.$$typeof===ze&&ja(te)===ue.type){n(x,ue.sibling),v=s(ue,j.props),v.ref=Cr(x,ue,j),v.return=x,x=v;break e}n(x,ue);break}else t(x,ue);ue=ue.sibling}j.type===pe?(v=Nn(j.props.children,x.mode,O,j.key),v.return=x,x=v):(O=Hl(j.type,j.key,j.props,null,x.mode,O),O.ref=Cr(x,v,j),O.return=x,x=O)}return a(x);case oe:e:{for(ue=j.key;v!==null;){if(v.key===ue)if(v.tag===4&&v.stateNode.containerInfo===j.containerInfo&&v.stateNode.implementation===j.implementation){n(x,v.sibling),v=s(v,j.children||[]),v.return=x,x=v;break e}else{n(x,v);break}else t(x,v);v=v.sibling}v=Bi(j,x.mode,O),v.return=x,x=v}return a(x);case ze:return ue=j._init,$e(x,v,ue(j._payload),O)}if(cn(j))return q(x,v,j,O);if(le(j))return ee(x,v,j,O);kl(x,j)}return typeof j=="string"&&j!==""||typeof j=="number"?(j=""+j,v!==null&&v.tag===6?(n(x,v.sibling),v=s(v,j),v.return=x,x=v):(n(x,v),v=$i(j,x.mode,O),v.return=x,x=v),a(x)):n(x,v)}return $e}var Wn=Sa(!0),wa=Sa(!1),jl=Xt(null),Sl=null,Un=null,Gs=null;function Ys(){Gs=Un=Sl=null}function Xs(e){var t=jl.current;Pe(jl),e._currentValue=t}function qs(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Hn(e,t){Sl=e,Gs=Un=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(rt=!0),e.firstContext=null)}function mt(e){var t=e._currentValue;if(Gs!==e)if(e={context:e,memoizedValue:t,next:null},Un===null){if(Sl===null)throw Error(u(308));Un=e,Sl.dependencies={lanes:0,firstContext:e}}else Un=Un.next=e;return t}var gn=null;function Zs(e){gn===null?gn=[e]:gn.push(e)}function Na(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,Zs(t)):(n.next=s.next,s.next=n),t.interleaved=n,$t(e,r)}function $t(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var Jt=!1;function Js(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ca(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Bt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function en(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(Se&2)!==0){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,$t(e,n)}return s=r.interleaved,s===null?(t.next=t,Zs(r)):(t.next=s.next,s.next=t),r.interleaved=t,$t(e,n)}function wl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ps(e,n)}}function _a(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?s=i=a:i=i.next=a,n=n.next}while(n!==null);i===null?s=i=t:i=i.next=t}else s=i=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Nl(e,t,n,r){var s=e.updateQueue;Jt=!1;var i=s.firstBaseUpdate,a=s.lastBaseUpdate,c=s.shared.pending;if(c!==null){s.shared.pending=null;var p=c,N=p.next;p.next=null,a===null?i=N:a.next=N,a=p;var A=e.alternate;A!==null&&(A=A.updateQueue,c=A.lastBaseUpdate,c!==a&&(c===null?A.firstBaseUpdate=N:c.next=N,A.lastBaseUpdate=p))}if(i!==null){var $=s.baseState;a=0,A=N=p=null,c=i;do{var D=c.lane,K=c.eventTime;if((r&D)===D){A!==null&&(A=A.next={eventTime:K,lane:0,tag:c.tag,payload:c.payload,callback:c.callback,next:null});e:{var q=e,ee=c;switch(D=t,K=n,ee.tag){case 1:if(q=ee.payload,typeof q=="function"){$=q.call(K,$,D);break e}$=q;break e;case 3:q.flags=q.flags&-65537|128;case 0:if(q=ee.payload,D=typeof q=="function"?q.call(K,$,D):q,D==null)break e;$=Q({},$,D);break e;case 2:Jt=!0}}c.callback!==null&&c.lane!==0&&(e.flags|=64,D=s.effects,D===null?s.effects=[c]:D.push(c))}else K={eventTime:K,lane:D,tag:c.tag,payload:c.payload,callback:c.callback,next:null},A===null?(N=A=K,p=$):A=A.next=K,a|=D;if(c=c.next,c===null){if(c=s.shared.pending,c===null)break;D=c,c=D.next,D.next=null,s.lastBaseUpdate=D,s.shared.pending=null}}while(!0);if(A===null&&(p=$),s.baseState=p,s.firstBaseUpdate=N,s.lastBaseUpdate=A,t=s.shared.interleaved,t!==null){s=t;do a|=s.lane,s=s.next;while(s!==t)}else i===null&&(s.shared.lanes=0);kn|=a,e.lanes=a,e.memoizedState=$}}function Ea(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=li.transition;li.transition={};try{e(!1),t()}finally{Ce=n,li.transition=r}}function Qa(){return ht().memoizedState}function Id(e,t,n){var r=ln(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Ka(e))Ga(t,n);else if(n=Na(e,t,n,r),n!==null){var s=et();Ct(n,e,r,s),Ya(n,t,r)}}function Dd(e,t,n){var r=ln(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ka(e))Ga(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var a=t.lastRenderedState,c=i(a,n);if(s.hasEagerState=!0,s.eagerState=c,kt(c,a)){var p=t.interleaved;p===null?(s.next=s,Zs(t)):(s.next=p.next,p.next=s),t.interleaved=s;return}}catch{}finally{}n=Na(e,t,s,r),n!==null&&(s=et(),Ct(n,e,r,s),Ya(n,t,r))}}function Ka(e){var t=e.alternate;return e===Ie||t!==null&&t===Ie}function Ga(e,t){Tr=El=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ya(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ps(e,n)}}var Ll={readContext:mt,useCallback:Xe,useContext:Xe,useEffect:Xe,useImperativeHandle:Xe,useInsertionEffect:Xe,useLayoutEffect:Xe,useMemo:Xe,useReducer:Xe,useRef:Xe,useState:Xe,useDebugValue:Xe,useDeferredValue:Xe,useTransition:Xe,useMutableSource:Xe,useSyncExternalStore:Xe,useId:Xe,unstable_isNewReconciler:!1},Md={readContext:mt,useCallback:function(e,t){return Tt().memoizedState=[e,t===void 0?null:t],e},useContext:mt,useEffect:$a,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,zl(4194308,4,Fa.bind(null,t,e),n)},useLayoutEffect:function(e,t){return zl(4194308,4,e,t)},useInsertionEffect:function(e,t){return zl(4,2,e,t)},useMemo:function(e,t){var n=Tt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Tt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Id.bind(null,Ie,e),[r.memoizedState,e]},useRef:function(e){var t=Tt();return e={current:e},t.memoizedState=e},useState:Ma,useDebugValue:di,useDeferredValue:function(e){return Tt().memoizedState=e},useTransition:function(){var e=Ma(!1),t=e[0];return e=Rd.bind(null,e[1]),Tt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Ie,s=Tt();if(be){if(n===void 0)throw Error(u(407));n=n()}else{if(n=t(),Ve===null)throw Error(u(349));(xn&30)!==0||Pa(r,t,n)}s.memoizedState=n;var i={value:n,getSnapshot:t};return s.queue=i,$a(Ra.bind(null,r,i,e),[e]),r.flags|=2048,br(9,ba.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Tt(),t=Ve.identifierPrefix;if(be){var n=At,r=Mt;n=(r&~(1<<32-xt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Lr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[Et]=t,e[wr]=r,hu(e,t,!1,!1),t.stateNode=e;e:{switch(a=rs(n,r),n){case"dialog":Le("cancel",e),Le("close",e),s=r;break;case"iframe":case"object":case"embed":Le("load",e),s=r;break;case"video":case"audio":for(s=0;sYn&&(t.flags|=128,r=!0,Rr(i,!1),t.lanes=4194304)}else{if(!r)if(e=Cl(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Rr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!a.alternate&&!be)return qe(t),null}else 2*Ae()-i.renderingStartTime>Yn&&n!==1073741824&&(t.flags|=128,r=!0,Rr(i,!1),t.lanes=4194304);i.isBackwards?(a.sibling=t.child,t.child=a):(n=i.last,n!==null?n.sibling=a:t.child=a,i.last=a)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=Ae(),t.sibling=null,n=Re.current,Te(Re,r?n&1|2:n&1),t):(qe(t),null);case 22:case 23:return Di(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(ct&1073741824)!==0&&(qe(t),t.subtreeFlags&6&&(t.flags|=8192)):qe(t),null;case 24:return null;case 25:return null}throw Error(u(156,t.tag))}function Hd(e,t){switch(Hs(t),t.tag){case 1:return nt(t.type)&&ml(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Vn(),Pe(tt),Pe(Ye),ri(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return ti(t),null;case 13:if(Pe(Re),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(u(340));Fn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Pe(Re),null;case 4:return Vn(),null;case 10:return Xs(t.type._context),null;case 22:case 23:return Di(),null;case 24:return null;default:return null}}var Il=!1,Ze=!1,Vd=typeof WeakSet=="function"?WeakSet:Set,Y=null;function Kn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){De(e,t,r)}else n.current=null}function wi(e,t,n){try{n()}catch(r){De(e,t,r)}}var yu=!1;function Qd(e,t){if(Ds=el,e=Xo(),Es(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var a=0,c=-1,p=-1,N=0,A=0,$=e,D=null;t:for(;;){for(var K;$!==n||s!==0&&$.nodeType!==3||(c=a+s),$!==i||r!==0&&$.nodeType!==3||(p=a+r),$.nodeType===3&&(a+=$.nodeValue.length),(K=$.firstChild)!==null;)D=$,$=K;for(;;){if($===e)break t;if(D===n&&++N===s&&(c=a),D===i&&++A===r&&(p=a),(K=$.nextSibling)!==null)break;$=D,D=$.parentNode}$=K}n=c===-1||p===-1?null:{start:c,end:p}}else n=null}n=n||{start:0,end:0}}else n=null;for(Ms={focusedElem:e,selectionRange:n},el=!1,Y=t;Y!==null;)if(t=Y,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,Y=e;else for(;Y!==null;){t=Y;try{var q=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(q!==null){var ee=q.memoizedProps,$e=q.memoizedState,x=t.stateNode,v=x.getSnapshotBeforeUpdate(t.elementType===t.type?ee:St(t.type,ee),$e);x.__reactInternalSnapshotBeforeUpdate=v}break;case 3:var j=t.stateNode.containerInfo;j.nodeType===1?j.textContent="":j.nodeType===9&&j.documentElement&&j.removeChild(j.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch(O){De(t,t.return,O)}if(e=t.sibling,e!==null){e.return=t.return,Y=e;break}Y=t.return}return q=yu,yu=!1,q}function Ir(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var i=s.destroy;s.destroy=void 0,i!==void 0&&wi(t,n,i)}s=s.next}while(s!==r)}}function Dl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Ni(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function xu(e){var t=e.alternate;t!==null&&(e.alternate=null,xu(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Et],delete t[wr],delete t[Os],delete t[zd],delete t[Td])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function ku(e){return e.tag===5||e.tag===3||e.tag===4}function ju(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||ku(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ci(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=fl));else if(r!==4&&(e=e.child,e!==null))for(Ci(e,t,n),e=e.sibling;e!==null;)Ci(e,t,n),e=e.sibling}function _i(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(_i(e,t,n),e=e.sibling;e!==null;)_i(e,t,n),e=e.sibling}var Ke=null,wt=!1;function tn(e,t,n){for(n=n.child;n!==null;)Su(e,t,n),n=n.sibling}function Su(e,t,n){if(_t&&typeof _t.onCommitFiberUnmount=="function")try{_t.onCommitFiberUnmount(Gr,n)}catch{}switch(n.tag){case 5:Ze||Kn(n,t);case 6:var r=Ke,s=wt;Ke=null,tn(e,t,n),Ke=r,wt=s,Ke!==null&&(wt?(e=Ke,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ke.removeChild(n.stateNode));break;case 18:Ke!==null&&(wt?(e=Ke,n=n.stateNode,e.nodeType===8?Bs(e.parentNode,n):e.nodeType===1&&Bs(e,n),fr(e)):Bs(Ke,n.stateNode));break;case 4:r=Ke,s=wt,Ke=n.stateNode.containerInfo,wt=!0,tn(e,t,n),Ke=r,wt=s;break;case 0:case 11:case 14:case 15:if(!Ze&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var i=s,a=i.destroy;i=i.tag,a!==void 0&&((i&2)!==0||(i&4)!==0)&&wi(n,t,a),s=s.next}while(s!==r)}tn(e,t,n);break;case 1:if(!Ze&&(Kn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(c){De(n,t,c)}tn(e,t,n);break;case 21:tn(e,t,n);break;case 22:n.mode&1?(Ze=(r=Ze)||n.memoizedState!==null,tn(e,t,n),Ze=r):tn(e,t,n);break;default:tn(e,t,n)}}function wu(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Vd),t.forEach(function(r){var s=tf.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function Nt(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=a),r&=~i}if(r=s,r=Ae()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Gd(r/1960))-r,10e?16:e,rn===null)var r=!1;else{if(e=rn,rn=null,Ol=0,(Se&6)!==0)throw Error(u(331));var s=Se;for(Se|=4,Y=e.current;Y!==null;){var i=Y,a=i.child;if((Y.flags&16)!==0){var c=i.deletions;if(c!==null){for(var p=0;pAe()-Ti?Sn(e,0):zi|=n),st(e,t)}function Mu(e,t){t===0&&((e.mode&1)===0?t=1:(t=Xr,Xr<<=1,(Xr&130023424)===0&&(Xr=4194304)));var n=et();e=$t(e,t),e!==null&&(or(e,t,n),st(e,n))}function ef(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Mu(e,n)}function tf(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(u(314))}r!==null&&r.delete(t),Mu(e,n)}var Au;Au=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||tt.current)rt=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return rt=!1,Wd(e,t,n);rt=(e.flags&131072)!==0}else rt=!1,be&&(t.flags&1048576)!==0&&va(t,yl,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Rl(e,t),e=t.pendingProps;var s=$n(t,Ye.current);Hn(t,n),s=ii(null,t,r,e,s,n);var i=oi();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,nt(r)?(i=!0,hl(t)):i=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,Js(t),s.updater=Pl,t.stateNode=s,s._reactInternals=t,pi(t,r,e,n),t=gi(null,t,r,!0,i,n)):(t.tag=0,be&&i&&Us(t),Je(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Rl(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=rf(r),e=St(r,e),s){case 0:t=vi(null,t,r,e,n);break e;case 1:t=uu(null,t,r,e,n);break e;case 11:t=lu(null,t,r,e,n);break e;case 14:t=su(null,t,r,St(r.type,e),n);break e}throw Error(u(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:St(r,s),vi(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:St(r,s),uu(e,t,r,s,n);case 3:e:{if(cu(t),e===null)throw Error(u(387));r=t.pendingProps,i=t.memoizedState,s=i.element,Ca(e,t),Nl(t,r,null,n);var a=t.memoizedState;if(r=a.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){s=Qn(Error(u(423)),t),t=du(e,t,r,n,s);break e}else if(r!==s){s=Qn(Error(u(424)),t),t=du(e,t,r,n,s);break e}else for(ut=Yt(t.stateNode.containerInfo.firstChild),at=t,be=!0,jt=null,n=wa(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Fn(),r===s){t=Ot(e,t,n);break e}Je(e,t,r,n)}t=t.child}return t;case 5:return za(t),e===null&&Qs(t),r=t.type,s=t.pendingProps,i=e!==null?e.memoizedProps:null,a=s.children,As(r,s)?a=null:i!==null&&As(r,i)&&(t.flags|=32),au(e,t),Je(e,t,a,n),t.child;case 6:return e===null&&Qs(t),null;case 13:return fu(e,t,n);case 4:return ei(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Wn(t,null,r,n):Je(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:St(r,s),lu(e,t,r,s,n);case 7:return Je(e,t,t.pendingProps,n),t.child;case 8:return Je(e,t,t.pendingProps.children,n),t.child;case 12:return Je(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,i=t.memoizedProps,a=s.value,Te(jl,r._currentValue),r._currentValue=a,i!==null)if(kt(i.value,a)){if(i.children===s.children&&!tt.current){t=Ot(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var c=i.dependencies;if(c!==null){a=i.child;for(var p=c.firstContext;p!==null;){if(p.context===r){if(i.tag===1){p=Bt(-1,n&-n),p.tag=2;var N=i.updateQueue;if(N!==null){N=N.shared;var A=N.pending;A===null?p.next=p:(p.next=A.next,A.next=p),N.pending=p}}i.lanes|=n,p=i.alternate,p!==null&&(p.lanes|=n),qs(i.return,n,t),c.lanes|=n;break}p=p.next}}else if(i.tag===10)a=i.type===t.type?null:i.child;else if(i.tag===18){if(a=i.return,a===null)throw Error(u(341));a.lanes|=n,c=a.alternate,c!==null&&(c.lanes|=n),qs(a,n,t),a=i.sibling}else a=i.child;if(a!==null)a.return=i;else for(a=i;a!==null;){if(a===t){a=null;break}if(i=a.sibling,i!==null){i.return=a.return,a=i;break}a=a.return}i=a}Je(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,Hn(t,n),s=mt(s),r=r(s),t.flags|=1,Je(e,t,r,n),t.child;case 14:return r=t.type,s=St(r,t.pendingProps),s=St(r.type,s),su(e,t,r,s,n);case 15:return iu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:St(r,s),Rl(e,t),t.tag=1,nt(r)?(e=!0,hl(t)):e=!1,Hn(t,n),qa(t,r,s),pi(t,r,s,n),gi(null,t,r,!0,e,n);case 19:return mu(e,t,n);case 22:return ou(e,t,n)}throw Error(u(156,t.tag))};function $u(e,t){return go(e,t)}function nf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function gt(e,t,n,r){return new nf(e,t,n,r)}function Ai(e){return e=e.prototype,!(!e||!e.isReactComponent)}function rf(e){if(typeof e=="function")return Ai(e)?1:0;if(e!=null){if(e=e.$$typeof,e===je)return 11;if(e===_e)return 14}return 2}function on(e,t){var n=e.alternate;return n===null?(n=gt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Hl(e,t,n,r,s,i){var a=2;if(r=e,typeof e=="function")Ai(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case pe:return Nn(n.children,s,i,t);case z:a=8,s|=8;break;case Ee:return e=gt(12,n,t,s|2),e.elementType=Ee,e.lanes=i,e;case ae:return e=gt(13,n,t,s),e.elementType=ae,e.lanes=i,e;case G:return e=gt(19,n,t,s),e.elementType=G,e.lanes=i,e;case ke:return Vl(n,s,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case J:a=10;break e;case re:a=9;break e;case je:a=11;break e;case _e:a=14;break e;case ze:a=16,r=null;break e}throw Error(u(130,e==null?e:typeof e,""))}return t=gt(a,n,t,s),t.elementType=e,t.type=r,t.lanes=i,t}function Nn(e,t,n,r){return e=gt(7,e,r,t),e.lanes=n,e}function Vl(e,t,n,r){return e=gt(22,e,r,t),e.elementType=ke,e.lanes=n,e.stateNode={isHidden:!1},e}function $i(e,t,n){return e=gt(6,e,null,t),e.lanes=n,e}function Bi(e,t,n){return t=gt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function lf(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=fs(0),this.expirationTimes=fs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=fs(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function Oi(e,t,n,r,s,i,a,c,p){return e=new lf(e,t,n,c,p),t===1?(t=1,i===!0&&(t|=8)):t=0,i=gt(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Js(i),e}function sf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(h){console.error(h)}}return o(),Qi.exports=gf(),Qi.exports}var Zu;function xf(){if(Zu)return Zl;Zu=1;var o=yf();return Zl.createRoot=o.createRoot,Zl.hydrateRoot=o.hydrateRoot,Zl}var kf=xf();const jf=to(kf),Sf={},Ju=o=>{let h;const u=new Set,y=(T,f)=>{const w=typeof T=="function"?T(h):T;if(!Object.is(w,h)){const m=h;h=f??(typeof w!="object"||w===null)?w:Object.assign({},h,w),u.forEach(P=>P(h,m))}},S=()=>h,b={setState:y,getState:S,getInitialState:()=>_,subscribe:T=>(u.add(T),()=>u.delete(T)),destroy:()=>{(Sf?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),u.clear()}},_=h=o(y,S,b);return b},wf=o=>o?Ju(o):Ju;var Yi={exports:{}},Xi={},qi={exports:{}},Zi={};/** + * @license React + * use-sync-external-store-shim.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var ec;function Nf(){if(ec)return Zi;ec=1;var o=Fr();function h(f,w){return f===w&&(f!==0||1/f===1/w)||f!==f&&w!==w}var u=typeof Object.is=="function"?Object.is:h,y=o.useState,S=o.useEffect,I=o.useLayoutEffect,F=o.useDebugValue;function k(f,w){var m=w(),P=y({inst:{value:m,getSnapshot:w}}),L=P[0].inst,U=P[1];return I(function(){L.value=m,L.getSnapshot=w,b(L)&&U({inst:L})},[f,m,w]),S(function(){return b(L)&&U({inst:L}),f(function(){b(L)&&U({inst:L})})},[f]),F(m),m}function b(f){var w=f.getSnapshot;f=f.value;try{var m=w();return!u(f,m)}catch{return!0}}function _(f,w){return w()}var T=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?_:k;return Zi.useSyncExternalStore=o.useSyncExternalStore!==void 0?o.useSyncExternalStore:T,Zi}var tc;function Cf(){return tc||(tc=1,qi.exports=Nf()),qi.exports}/** + * @license React + * use-sync-external-store-shim/with-selector.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var nc;function _f(){if(nc)return Xi;nc=1;var o=Fr(),h=Cf();function u(_,T){return _===T&&(_!==0||1/_===1/T)||_!==_&&T!==T}var y=typeof Object.is=="function"?Object.is:u,S=h.useSyncExternalStore,I=o.useRef,F=o.useEffect,k=o.useMemo,b=o.useDebugValue;return Xi.useSyncExternalStoreWithSelector=function(_,T,f,w,m){var P=I(null);if(P.current===null){var L={hasValue:!1,value:null};P.current=L}else L=P.current;P=k(function(){function B(oe){if(!W){if(W=!0,ne=oe,oe=w(oe),m!==void 0&&L.hasValue){var pe=L.value;if(m(pe,oe))return E=pe}return E=oe}if(pe=E,y(ne,oe))return pe;var z=w(oe);return m!==void 0&&m(pe,z)?(ne=oe,pe):(ne=oe,E=z)}var W=!1,ne,E,fe=f===void 0?null:f;return[function(){return B(T())},fe===null?void 0:function(){return B(fe())}]},[T,f,w,m]);var U=S(_,P[0],P[1]);return F(function(){L.hasValue=!0,L.value=U},[U]),b(U),U},Xi}var rc;function Ef(){return rc||(rc=1,Yi.exports=_f()),Yi.exports}var zf=Ef();const Tf=to(zf),uc={},{useDebugValue:Lf}=ac,{useSyncExternalStoreWithSelector:Pf}=Tf;let lc=!1;const bf=o=>o;function Rf(o,h=bf,u){(uc?"production":void 0)!=="production"&&u&&!lc&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),lc=!0);const y=Pf(o.subscribe,o.getState,o.getServerState||o.getInitialState,h,u);return Lf(y),y}const sc=o=>{(uc?"production":void 0)!=="production"&&typeof o!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const h=typeof o=="function"?wf(o):o,u=(y,S)=>Rf(h,y,S);return Object.assign(u,h),u},If=o=>o?sc(o):sc,we="";async function yt(o){const h=await fetch(o,{cache:"no-store"});if(!h.ok)throw new Error(String(h.status));return h.json()}async function Fe(o,h){return(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)})).json()}const he={liveStatus:()=>yt(`${we}/api/live-status`),agentConfig:()=>yt(`${we}/api/agent-config`),modelChangeLog:()=>yt(`${we}/api/model-change-log`).catch(()=>[]),officialsStats:()=>yt(`${we}/api/officials-stats`),morningBrief:()=>yt(`${we}/api/morning-brief`),morningConfig:()=>yt(`${we}/api/morning-config`),agentsStatus:()=>yt(`${we}/api/agents-status`),taskActivity:o=>yt(`${we}/api/task-activity/${encodeURIComponent(o)}`),schedulerState:o=>yt(`${we}/api/scheduler-state/${encodeURIComponent(o)}`),schedulerMetrics:o=>yt(o?`${we}/api/scheduler-metrics/${encodeURIComponent(o)}`:`${we}/api/scheduler-metrics`),skillContent:(o,h)=>yt(`${we}/api/skill-content/${encodeURIComponent(o)}/${encodeURIComponent(h)}`),setModel:(o,h)=>Fe(`${we}/api/set-model`,{agentId:o,model:h}),agentWake:o=>Fe(`${we}/api/agent-wake`,{agentId:o}),taskAction:(o,h,u)=>Fe(`${we}/api/task-action`,{taskId:o,action:h,reason:u}),reviewAction:(o,h,u)=>Fe(`${we}/api/review-action`,{taskId:o,action:h,comment:u}),advanceState:(o,h)=>Fe(`${we}/api/advance-state`,{taskId:o,comment:h}),archiveTask:(o,h)=>Fe(`${we}/api/archive-task`,{taskId:o,archived:h}),archiveAllDone:()=>Fe(`${we}/api/archive-task`,{archiveAllDone:!0}),schedulerScan:(o=180)=>Fe(`${we}/api/scheduler-scan`,{thresholdSec:o}),schedulerRetry:(o,h)=>Fe(`${we}/api/scheduler-retry`,{taskId:o,reason:h}),schedulerEscalate:(o,h)=>Fe(`${we}/api/scheduler-escalate`,{taskId:o,reason:h}),schedulerRollback:(o,h)=>Fe(`${we}/api/scheduler-rollback`,{taskId:o,reason:h}),schedulerAction:(o,h,u,y,S)=>Fe(`${we}/api/scheduler-action`,{taskId:o,action:h,reason:u,expectedVersion:y,recoveryTarget:S}),refreshMorning:()=>Fe(`${we}/api/morning-brief/refresh`,{}),saveMorningConfig:o=>Fe(`${we}/api/morning-config`,o),addSkill:(o,h,u,y)=>Fe(`${we}/api/add-skill`,{agentId:o,skillName:h,description:u,trigger:y}),addRemoteSkill:(o,h,u,y)=>Fe(`${we}/api/add-remote-skill`,{agentId:o,skillName:h,sourceUrl:u,description:y||""}),remoteSkillsList:()=>yt(`${we}/api/remote-skills-list`),updateRemoteSkill:(o,h)=>Fe(`${we}/api/update-remote-skill`,{agentId:o,skillName:h}),removeRemoteSkill:(o,h)=>Fe(`${we}/api/remove-remote-skill`,{agentId:o,skillName:h}),createTask:o=>Fe(`${we}/api/create-task`,o),courtDiscuss:o=>Fe(`${we}/api/court-discuss`,o)},cc=[{key:"Inbox",dept:"皇上",icon:"👑",action:"下旨"},{key:"Taizi",dept:"太子",icon:"🤴",action:"分拣"},{key:"Zhongshu",dept:"中书省",icon:"📜",action:"起草"},{key:"Menxia",dept:"门下省",icon:"🔍",action:"审议"},{key:"Assigned",dept:"尚书省",icon:"📮",action:"派发"},{key:"Doing",dept:"六部",icon:"⚙️",action:"执行"},{key:"Review",dept:"尚书省",icon:"🔎",action:"汇总"},{key:"Done",dept:"回奏",icon:"✅",action:"完成"}],Df={Inbox:0,Pending:0,Taizi:1,Zhongshu:2,Menxia:3,Assigned:4,Doing:5,Review:6,Done:7,Blocked:5,Cancelled:5,Next:4},Mf={太子:"#e8a040",中书省:"#a07aff",门下省:"#6a9eff",尚书省:"#6aef9a",礼部:"#f5c842",户部:"#ff9a6a",兵部:"#ff5270",刑部:"#cc4444",工部:"#44aaff",吏部:"#9b59b6",皇上:"#ffd700",回奏:"#2ecc8a"},Wr={Inbox:"收件",Pending:"待处理",Taizi:"太子分拣",Zhongshu:"中书起草",Menxia:"门下审议",Assigned:"已派发",Doing:"执行中",Review:"待审查",Done:"已完成",Blocked:"阻塞",Cancelled:"已取消",Next:"待执行"};function Jl(o){return Mf[o]||"#6a9eff"}function no(o){const h=o.review_round||0;return o.state==="Menxia"&&h>1?`门下审议(第${h}轮)`:o.state==="Zhongshu"&&h>0?`中书修订(第${h}轮)`:Wr[o.state]||o.state}function un(o){return/^JJC-/i.test(o.id||"")}function ts(o){return o.archived||["Done","Cancelled"].includes(o.state)}function ro(o){const h=Df[o.state]??4;return cc.map((u,y)=>({...u,status:y({liveStatus:null,agentConfig:null,changeLog:[],officialsData:null,agentsStatusData:null,morningBrief:null,subConfig:null,activeTab:"edicts",edictFilter:"active",sessFilter:"all",tplCatFilter:"全部",selectedOfficial:null,modalTaskId:null,countdown:5,toasts:[],setActiveTab:u=>{o({activeTab:u});const y=h();["models","skills","sessions"].includes(u)&&!y.agentConfig&&y.loadAgentConfig(),u==="officials"&&!y.officialsData&&y.loadOfficials(),u==="monitor"&&y.loadAgentsStatus(),u==="morning"&&!y.morningBrief&&y.loadMorning()},setEdictFilter:u=>o({edictFilter:u}),setSessFilter:u=>o({sessFilter:u}),setTplCatFilter:u=>o({tplCatFilter:u}),setSelectedOfficial:u=>o({selectedOfficial:u}),setModalTaskId:u=>o({modalTaskId:u}),setCountdown:u=>o({countdown:u}),toast:(u,y="ok")=>{const S=++Ff;o(I=>({toasts:[...I.toasts,{id:S,msg:u,type:y}]})),setTimeout(()=>{o(I=>({toasts:I.toasts.filter(F=>F.id!==S)}))},3e3)},loadLive:async()=>{try{const u=await he.liveStatus();o({liveStatus:u}),h().officialsData||he.officialsStats().then(S=>o({officialsData:S})).catch(()=>{})}catch{}},loadAgentConfig:async()=>{try{const u=await he.agentConfig(),y=await he.modelChangeLog();o({agentConfig:u,changeLog:y})}catch{}},loadOfficials:async()=>{try{const u=await he.officialsStats();o({officialsData:u})}catch{}},loadAgentsStatus:async()=>{try{const u=await he.agentsStatus();o({agentsStatusData:u})}catch{o({agentsStatusData:null})}},loadMorning:async()=>{try{const[u,y]=await Promise.all([he.morningBrief(),he.morningConfig()]);o({morningBrief:u,subConfig:y})}catch{}},loadSubConfig:async()=>{try{const u=await he.morningConfig();o({subConfig:u})}catch{}},loadAll:async()=>{const u=h();await u.loadLive();const y=u.activeTab;["models","skills"].includes(y)&&await u.loadAgentConfig()}}));let Or=null;function Wf(){Or||(se.getState().loadAll(),Or=setInterval(()=>{const o=se.getState(),h=o.countdown-1;h<=0?(o.setCountdown(5),o.loadAll()):o.setCountdown(h)},1e3))}function Uf(){Or&&(clearInterval(Or),Or=null)}function Hf(o){if(!o)return"";try{const h=new Date(o.includes("T")?o:o.replace(" ","T")+"Z");if(isNaN(h.getTime()))return"";const u=Date.now()-h.getTime(),y=Math.floor(u/6e4);if(y<1)return"刚刚";if(y<60)return y+"分钟前";const S=Math.floor(y/60);return S<24?S+"小时前":Math.floor(S/24)+"天前"}catch{return""}}const ic={Doing:0,Review:1,Assigned:2,Menxia:3,Zhongshu:4,Taizi:5,Inbox:6,Blocked:7,Next:8,Done:9,Cancelled:10};function Vf({task:o}){const h=ro(o);return l.jsx("div",{className:"ec-pipe",children:h.map((u,y)=>l.jsxs("span",{style:{display:"contents"},children:[l.jsxs("div",{className:`ep-node ${u.status}`,children:[l.jsx("div",{className:"ep-icon",children:u.icon}),l.jsx("div",{className:"ep-name",children:u.dept})]}),yB.setModalTaskId),u=se(B=>B.toast),y=se(B=>B.loadAll),S=o.heartbeat||{status:"unknown",label:"⚪"},I="st-"+(o.state||""),F="dt-"+(o.org||"").replace(/\s/g,""),k=cc.find((B,W)=>ro(o)[W].status==="active"),b=o.todos||[],_=b.filter(B=>B.status==="completed").length,T=b.length,f=!["Done","Blocked","Cancelled"].includes(o.state),w=["Blocked","Cancelled"].includes(o.state),m=ts(o),P=o.block&&o.block!=="无"&&o.block!=="-",L=async(B,W)=>{if(W.stopPropagation(),B==="stop"||B==="cancel"){const ne=prompt(B==="stop"?"请输入叫停原因:":"请输入取消原因:");if(ne===null)return;try{const E=await he.taskAction(o.id,B,ne);E.ok?(u(E.message||"操作成功"),y()):u(E.error||"操作失败","err")}catch{u("服务器连接失败","err")}}else if(B==="resume")try{const ne=await he.taskAction(o.id,"resume","恢复执行");ne.ok?(u(ne.message||"已恢复"),y()):u(ne.error||"操作失败","err")}catch{u("服务器连接失败","err")}},U=async B=>{B.stopPropagation();try{const W=await he.archiveTask(o.id,!o.archived);W.ok?(u(W.message||"操作成功"),y()):u(W.error||"操作失败","err")}catch{u("服务器连接失败","err")}};return l.jsxs("div",{className:`edict-card${m?" archived":""}`,onClick:()=>h(o.id),children:[l.jsx(Vf,{task:o}),l.jsx("div",{className:"ec-id",children:o.id}),l.jsx("div",{className:"ec-title",children:o.title||"(无标题)"}),l.jsxs("div",{className:"ec-meta",children:[l.jsx("span",{className:`tag ${I}`,children:no(o)}),o.org&&l.jsx("span",{className:`tag ${F}`,children:o.org}),k&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["当前: ",l.jsxs("b",{style:{color:Jl(k.dept)},children:[k.dept," · ",k.action]})]})]}),o.now&&o.now!=="-"&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:6},children:o.now.substring(0,80)}),(o.review_round||0)>0&&l.jsxs("div",{style:{fontSize:11,marginBottom:6},children:[Array.from({length:o.review_round||0},(B,W)=>l.jsx("span",{style:{display:"inline-block",width:14,height:14,borderRadius:"50%",background:W<(o.review_round||0)-1?"#1a3a6a22":"var(--acc)22",border:`1px solid ${W<(o.review_round||0)-1?"#2a4a8a":"var(--acc)"}`,fontSize:9,textAlign:"center",lineHeight:"13px",marginRight:2,color:W<(o.review_round||0)-1?"#4a6aaa":"var(--acc)"},children:W+1},W)),l.jsxs("span",{style:{color:"var(--muted)",fontSize:10},children:["第 ",o.review_round," 轮磋商"]})]}),T>0&&l.jsxs("div",{className:"ec-todo-bar",children:[l.jsxs("span",{children:["📋 ",_,"/",T]}),l.jsx("div",{className:"ec-todo-track",children:l.jsx("div",{className:"ec-todo-fill",style:{width:`${Math.round(_/T*100)}%`}})}),l.jsx("span",{children:_===T?"✅ 全部完成":"🔄 进行中"})]}),l.jsxs("div",{className:"ec-footer",children:[l.jsx("span",{className:`hb ${S.status}`,children:S.label}),P&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)",background:"#200a10"},children:["🚫 ",o.block]}),o.eta&&o.eta!=="-"&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["📅 ",o.eta]})]}),l.jsxs("div",{className:"ec-actions",onClick:B=>B.stopPropagation(),children:[f&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"mini-act",onClick:B=>L("stop",B),children:"⏸ 叫停"}),l.jsx("button",{className:"mini-act danger",onClick:B=>L("cancel",B),children:"🚫 取消"})]}),w&&l.jsx("button",{className:"mini-act",onClick:B=>L("resume",B),children:"▶ 恢复"}),m&&!o.archived&&l.jsx("button",{className:"mini-act",onClick:U,children:"📦 归档"}),o.archived&&l.jsx("button",{className:"mini-act",onClick:U,children:"📤 取消归档"})]})]})}function Kf(){const o=se(m=>m.liveStatus),h=se(m=>m.edictFilter),u=se(m=>m.setEdictFilter),y=se(m=>m.toast),S=se(m=>m.loadAll),F=((o==null?void 0:o.tasks)||[]).filter(un),k=F.filter(m=>!ts(m)),b=F.filter(m=>ts(m));let _;h==="active"?_=k:h==="archived"?_=b:_=F,_.sort((m,P)=>(ic[m.state]??9)-(ic[P.state]??9));const T=F.filter(m=>!m.archived&&["Done","Cancelled"].includes(m.state)),f=async()=>{if(confirm("将所有已完成/已取消的旨意移入归档?"))try{const m=await he.archiveAllDone();m.ok?(y(`📦 ${m.count||0} 道旨意已归档`),S()):y(m.error||"批量归档失败","err")}catch{y("服务器连接失败","err")}},w=async()=>{try{const m=await he.schedulerScan();m.ok?y(`🧭 太子巡检完成:${m.count||0} 个动作`):y(m.error||"巡检失败","err"),S()}catch{y("服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{className:"archive-bar",children:[l.jsx("span",{className:"ab-label",children:"筛选:"}),["active","archived","all"].map(m=>l.jsx("button",{className:`ab-btn ${h===m?"active":""}`,onClick:()=>u(m),children:m==="active"?"活跃":m==="archived"?"归档":"全部"},m)),T.length>0&&l.jsx("button",{className:"ab-btn",onClick:f,children:"📦 一键归档"}),l.jsxs("span",{className:"ab-count",children:["活跃 ",k.length," · 归档 ",b.length," · 共 ",F.length]}),l.jsx("button",{className:"ab-scan",onClick:w,children:"🧭 太子巡检"})]}),l.jsx("div",{className:"edict-grid",children:_.length===0?l.jsxs("div",{className:"empty",style:{gridColumn:"1/-1"},children:["暂无旨意",l.jsx("br",{}),l.jsx("small",{style:{fontSize:11,marginTop:6,display:"block",color:"var(--muted)"},children:"通过飞书向太子发送任务,太子分拣后转中书省处理"})]}):_.map(m=>l.jsx(Qf,{task:m},m.id))})]})}function Gf(){var ne;const o=se(E=>E.liveStatus),h=se(E=>E.agentsStatusData),u=se(E=>E.officialsData),y=se(E=>E.loadAgentsStatus),S=se(E=>E.setModalTaskId),I=se(E=>E.toast);Z.useEffect(()=>{y()},[y]);const k=((o==null?void 0:o.tasks)||[]).filter(E=>un(E)&&E.state!=="Done"&&E.state!=="Next"),b={};u!=null&&u.officials&&u.officials.forEach(E=>{b[E.id]=E});const _=async E=>{try{const fe=await he.agentWake(E);I(fe.message||"唤醒指令已发出"),setTimeout(()=>y(),3e4)}catch{I("唤醒失败","err")}},T=async()=>{if(!h)return;const E=h.agents.filter(fe=>fe.id!=="main"&&fe.status!=="running"&&fe.status!=="unconfigured");if(!E.length){I("所有 Agent 均已在线");return}I(`正在唤醒 ${E.length} 个 Agent...`);for(const fe of E)try{await he.agentWake(fe.id)}catch{}I(`${E.length} 个唤醒指令已发出,30秒后刷新状态`),setTimeout(()=>y(),3e4)},f=h,w=((ne=f==null?void 0:f.agents)==null?void 0:ne.filter(E=>E.id!=="main"))||[],m=w.filter(E=>E.status==="running").length,P=w.filter(E=>E.status==="idle").length,L=w.filter(E=>E.status==="offline").length,U=w.filter(E=>E.status==="unconfigured").length,B=f==null?void 0:f.gateway,W=B!=null&&B.probe?"ok":B!=null&&B.alive?"warn":"err";return l.jsxs("div",{children:[f&&f.ok&&l.jsxs("div",{className:"as-panel",children:[l.jsxs("div",{className:"as-header",children:[l.jsx("span",{className:"as-title",children:"🔌 Agent 在线状态"}),l.jsxs("span",{className:`as-gw ${W}`,children:["Gateway: ",(B==null?void 0:B.status)||"未知"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>y(),style:{marginLeft:8},children:"🔄 刷新"}),L+U>0&&l.jsx("button",{className:"btn-refresh",onClick:T,style:{marginLeft:4,borderColor:"var(--warn)",color:"var(--warn)"},children:"⚡ 全部唤醒"})]}),l.jsx("div",{className:"as-grid",children:w.map(E=>{const fe=E.status!=="running"&&E.status!=="unconfigured"&&(B==null?void 0:B.alive);return l.jsxs("div",{className:"as-card",title:`${E.role} · ${E.statusLabel}`,children:[l.jsx("div",{className:`as-dot ${E.status}`}),l.jsx("div",{style:{fontSize:22},children:E.emoji}),l.jsx("div",{style:{fontSize:12,fontWeight:700},children:E.label}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:E.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:E.statusLabel}),E.lastActive?l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["⏰ ",E.lastActive]}):l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"无活动记录"}),fe&&l.jsx("button",{className:"as-wake-btn",onClick:oe=>{oe.stopPropagation(),_(E.id)},children:"⚡ 唤醒"})]},E.id)})}),l.jsxs("div",{className:"as-summary",children:[l.jsxs("span",{children:[l.jsx("span",{className:"as-dot running",style:{position:"static",width:8,height:8}})," ",m," 运行中"]}),l.jsxs("span",{children:[l.jsx("span",{className:"as-dot idle",style:{position:"static",width:8,height:8}})," ",P," 待命"]}),L>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot offline",style:{position:"static",width:8,height:8}})," ",L," 离线"]}),U>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot unconfigured",style:{position:"static",width:8,height:8}})," ",U," 未配置"]}),l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["检测于 ",(f.checkedAt||"").substring(11,19)]})]})]}),l.jsx("div",{className:"duty-grid",children:$f.map(E=>{const fe=k.filter(ae=>ae.org===E.label),oe=fe.some(ae=>ae.state==="Doing"),pe=fe.some(ae=>ae.state==="Blocked"),z=b[E.id],Ee=(z==null?void 0:z.heartbeat)||{status:"idle"},J=pe?"blocked":oe?"busy":Ee.status==="active"?"active":"idle",re=pe?"⚠️ 阻塞":oe?"⚙️ 执行中":Ee.status==="active"?"🟢 活跃":"⚪ 候命",je=pe?"blocked-card":oe?"active-card":"";return l.jsxs("div",{className:`duty-card ${je}`,children:[l.jsxs("div",{className:"dc-hdr",children:[l.jsx("span",{className:"dc-emoji",children:E.emoji}),l.jsxs("div",{className:"dc-info",children:[l.jsx("div",{className:"dc-name",children:E.label}),l.jsxs("div",{className:"dc-role",children:[E.role," · ",E.rank]})]}),l.jsxs("div",{className:"dc-status",children:[l.jsx("span",{className:`dc-dot ${J}`}),l.jsx("span",{children:re})]})]}),l.jsx("div",{className:"dc-body",children:fe.length>0?fe.map(ae=>l.jsxs("div",{className:"dc-task",onClick:()=>S(ae.id),children:[l.jsx("div",{className:"dc-task-id",children:ae.id}),l.jsx("div",{className:"dc-task-title",children:ae.title||"(无标题)"}),ae.now&&ae.now!=="-"&&l.jsx("div",{className:"dc-task-now",children:ae.now.substring(0,70)}),l.jsxs("div",{className:"dc-task-meta",children:[l.jsx("span",{className:`tag st-${ae.state}`,children:no(ae)}),ae.block&&ae.block!=="无"&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)"},children:["🚫",ae.block]})]})]},ae.id)):l.jsxs("div",{className:"dc-idle",children:[l.jsx("span",{style:{fontSize:20},children:"🪭"}),l.jsx("span",{children:"候命中"})]})}),l.jsxs("div",{className:"dc-footer",children:[l.jsxs("span",{className:"dc-model",children:["🤖 ",(z==null?void 0:z.model_short)||"待配置"]}),(z==null?void 0:z.last_active)&&l.jsxs("span",{className:"dc-la",children:["⏰ ",z.last_active]})]})]},E.id)})})]})}const Yf=["🥇","🥈","🥉"];function Xf(){var f;const o=se(w=>w.officialsData),h=se(w=>w.selectedOfficial),u=se(w=>w.setSelectedOfficial),y=se(w=>w.loadOfficials),S=se(w=>w.setModalTaskId);if(Z.useEffect(()=>{y()},[y]),!(o!=null&&o.officials))return l.jsx("div",{className:"empty",children:"⚠️ 请确保本地服务器已启动"});const I=o.officials,F=o.totals||{tasks_done:0,cost_cny:0},k=Math.max(...I.map(w=>w.tokens_in+w.tokens_out+w.cache_read+w.cache_write),1),b=I.filter(w=>{var m;return((m=w.heartbeat)==null?void 0:m.status)==="active"}),_=I.find(w=>{var m;return w.id===(h||((m=I[0])==null?void 0:m.id))}),T=(_==null?void 0:_.id)||((f=I[0])==null?void 0:f.id);return l.jsxs("div",{children:[b.length>0&&l.jsxs("div",{className:"off-activity",children:[l.jsx("span",{children:"🟢 当前活跃:"}),b.map(w=>l.jsxs("span",{style:{fontSize:12},children:[w.emoji," ",w.role]},w.id)),l.jsx("span",{style:{color:"var(--muted)",fontSize:11,marginLeft:"auto"},children:"其余官员待命"})]}),l.jsxs("div",{className:"off-kpi",children:[l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"var(--acc)"},children:I.length}),l.jsx("div",{className:"kpi-l",children:"在职官员"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"#f5c842"},children:F.tasks_done||0}),l.jsx("div",{className:"kpi-l",children:"累计完成旨意"})]}),l.jsxs("div",{className:"kpi",children:[l.jsxs("div",{className:"kpi-v",style:{color:(F.cost_cny||0)>20?"var(--warn)":"var(--ok)"},children:["¥",F.cost_cny||0]}),l.jsx("div",{className:"kpi-l",children:"累计费用(含缓存)"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{fontSize:16,paddingTop:4},children:o.top_official||"—"}),l.jsx("div",{className:"kpi-l",children:"功绩最高"})]})]}),l.jsxs("div",{className:"off-layout",children:[l.jsxs("div",{className:"off-ranklist",children:[l.jsx("div",{className:"orl-hdr",children:"功绩排行"}),I.map(w=>{const m=w.heartbeat||{status:"idle"};return l.jsxs("div",{className:`orl-item${T===w.id?" selected":""}`,onClick:()=>u(w.id),children:[l.jsx("span",{style:{minWidth:24,textAlign:"center"},children:w.merit_rank<=3?Yf[w.merit_rank-1]:"#"+w.merit_rank}),l.jsx("span",{children:w.emoji}),l.jsxs("span",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700},children:w.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:w.label})]}),l.jsxs("span",{style:{fontSize:11},children:[w.merit_score,"分"]}),l.jsx("span",{className:`dc-dot ${m.status}`,style:{width:8,height:8}})]},w.id)})]}),l.jsx("div",{className:"off-detail",children:_?l.jsx(qf,{official:_,maxTk:k,onOpenTask:S}):l.jsx("div",{className:"empty",children:"选择左侧官员查看详情"})})]})]})}function qf({official:o,maxTk:h,onOpenTask:u}){const y=o.heartbeat||{status:"idle",label:"⚪ 待命"},S=o.tokens_in+o.tokens_out+o.cache_read+o.cache_write,I=o.participated_edicts||[],F=[{l:"输入",v:o.tokens_in,color:"#6a9eff"},{l:"输出",v:o.tokens_out,color:"#a07aff"},{l:"缓存读",v:o.cache_read,color:"#2ecc8a"},{l:"缓存写",v:o.cache_write,color:"#f5c842"}];return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:16,alignItems:"center",marginBottom:20},children:[l.jsx("div",{style:{fontSize:40},children:o.emoji}),l.jsxs("div",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:18,fontWeight:800},children:o.role}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[o.label," · ",l.jsx("span",{style:{color:"var(--acc)"},children:o.model_short||o.model})]}),l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginTop:2},children:["🏅 ",o.rank," · 功绩分 ",o.merit_score]})]}),l.jsxs("div",{style:{textAlign:"right"},children:[l.jsx("div",{className:`hb ${y.status}`,style:{marginBottom:4},children:y.label}),o.last_active&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["活跃 ",o.last_active]}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:2},children:[o.sessions," 个会话 · ",o.messages," 条消息"]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"功绩统计"}),l.jsxs("div",{style:{display:"flex",gap:16},children:[l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--ok)"},children:o.tasks_done}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"完成旨意"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--warn)"},children:o.tasks_active}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"执行中"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--acc)"},children:o.flow_participations}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"流转参与"})]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"Token 消耗"}),F.map(k=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:11,marginBottom:2},children:[l.jsx("span",{style:{color:"var(--muted)"},children:k.l}),l.jsx("span",{children:k.v.toLocaleString()})]}),l.jsx("div",{style:{height:6,background:"#0e1320",borderRadius:3},children:l.jsx("div",{style:{height:"100%",width:`${h>0?Math.round(k.v/h*100):0}%`,background:k.color,borderRadius:3}})})]},k.l))]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"累计费用"}),l.jsxs("div",{style:{display:"flex",gap:10},children:[l.jsxs("span",{style:{fontSize:12,color:o.cost_cny>10?"var(--danger)":o.cost_cny>3?"var(--warn)":"var(--ok)"},children:[l.jsxs("b",{children:["¥",o.cost_cny]})," 人民币"]}),l.jsxs("span",{style:{fontSize:12},children:[l.jsxs("b",{children:["$",o.cost_usd]})," 美元"]}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["总计 ",S.toLocaleString()," tokens"]})]})]}),l.jsxs("div",{children:[l.jsxs("div",{className:"sec-title",children:["参与旨意(",I.length," 道)"]}),I.length===0?l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无旨意记录"}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:I.map(k=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",padding:"6px 8px",borderRadius:6,cursor:"pointer",border:"1px solid var(--line)"},onClick:()=>u(k.id),children:[l.jsx("span",{style:{fontSize:10,color:"var(--acc)",fontWeight:700},children:k.id}),l.jsx("span",{style:{flex:1,fontSize:12},children:k.title.substring(0,35)}),l.jsx("span",{className:`tag st-${k.state}`,style:{fontSize:10},children:Wr[k.state]||k.state})]},k.id))})]})]})}const Zf=[{id:"anthropic/claude-sonnet-4-6",l:"Claude Sonnet 4.6",p:"Anthropic"},{id:"anthropic/claude-opus-4-5",l:"Claude Opus 4.5",p:"Anthropic"},{id:"anthropic/claude-haiku-3-5",l:"Claude Haiku 3.5",p:"Anthropic"},{id:"openai/gpt-4o",l:"GPT-4o",p:"OpenAI"},{id:"openai/gpt-4o-mini",l:"GPT-4o Mini",p:"OpenAI"},{id:"google/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Google"},{id:"copilot/claude-sonnet-4",l:"Claude Sonnet 4",p:"Copilot"},{id:"copilot/claude-opus-4.5",l:"Claude Opus 4.5",p:"Copilot"},{id:"copilot/gpt-4o",l:"GPT-4o",p:"Copilot"},{id:"copilot/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Copilot"}];function Jf(){var w;const o=se(m=>m.agentConfig),h=se(m=>m.changeLog),u=se(m=>m.loadAgentConfig),y=se(m=>m.toast),[S,I]=Z.useState({}),[F,k]=Z.useState({});if(Z.useEffect(()=>{u()},[u]),Z.useEffect(()=>{if(o!=null&&o.agents){const m={};o.agents.forEach(P=>{m[P.id]=P.model}),I(m)}},[o]),!(o!=null&&o.agents))return l.jsx("div",{className:"empty",style:{gridColumn:"1/-1"},children:"⚠️ 请先启动本地服务器"});const b=(w=o.knownModels)!=null&&w.length?o.knownModels.map(m=>({id:m.id,l:m.label,p:m.provider})):Zf,_=(m,P)=>{I(L=>({...L,[m]:P}))},T=m=>{const P=o.agents.find(L=>L.id===m);P&&I(L=>({...L,[m]:P.model}))},f=async m=>{const P=S[m];if(P){k(L=>({...L,[m]:{cls:"pending",text:"⟳ 提交中…"}}));try{const L=await he.setModel(m,P);L.ok?(k(U=>({...U,[m]:{cls:"ok",text:"✅ 已提交,Gateway 重启中(约5秒)"}})),y(m+" 模型已更改","ok"),setTimeout(()=>u(),5500)):k(U=>({...U,[m]:{cls:"err",text:"❌ "+(L.error||"错误")}}))}catch{k(L=>({...L,[m]:{cls:"err",text:"❌ 无法连接服务器"}}))}}};return l.jsxs("div",{children:[l.jsx("div",{className:"model-grid",children:o.agents.map(m=>{const P=S[m.id]||m.model,L=P!==m.model,U=F[m.id];return l.jsxs("div",{className:"mc-card",children:[l.jsxs("div",{className:"mc-top",children:[l.jsx("span",{className:"mc-emoji",children:m.emoji||"🏛️"}),l.jsxs("div",{children:[l.jsxs("div",{className:"mc-name",children:[m.label," ",l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:m.id})]}),l.jsx("div",{className:"mc-role",children:m.role})]})]}),l.jsxs("div",{className:"mc-cur",children:["当前: ",l.jsx("b",{children:m.model})]}),l.jsx("select",{className:"msel",value:P,onChange:B=>_(m.id,B.target.value),children:b.map(B=>l.jsxs("option",{value:B.id,children:[B.l," (",B.p,")"]},B.id))}),l.jsxs("div",{className:"mc-btns",children:[l.jsx("button",{className:"btn btn-p",disabled:!L,onClick:()=>f(m.id),children:"应用"}),l.jsx("button",{className:"btn btn-g",onClick:()=>T(m.id),children:"重置"})]}),U&&l.jsx("div",{className:`mc-st ${U.cls}`,children:U.text})]},m.id)})}),l.jsxs("div",{style:{marginTop:24},children:[l.jsx("div",{className:"sec-title",children:"变更日志"}),l.jsx("div",{className:"cl-list",children:h!=null&&h.length?[...h].reverse().slice(0,15).map((m,P)=>l.jsxs("div",{className:"cl-row",children:[l.jsx("span",{className:"cl-t",children:(m.at||"").substring(0,16).replace("T"," ")}),l.jsx("span",{className:"cl-a",children:m.agentId}),l.jsxs("span",{className:"cl-c",children:[l.jsx("b",{children:m.oldModel})," → ",l.jsx("b",{children:m.newModel}),m.rolledBack&&l.jsx("span",{style:{color:"var(--danger)",fontSize:10,border:"1px solid #ff527044",padding:"1px 5px",borderRadius:3,marginLeft:4},children:"⚠ 已回滚"})]})]},P)):l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无变更"})})]})]})}const ep=[{label:"obra/superpowers",emoji:"⚡",stars:"66.9k",desc:"完整开发工作流技能集",skills:[{name:"brainstorming",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"},{name:"test-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/test-driven-development/SKILL.md"},{name:"systematic-debugging",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/systematic-debugging/SKILL.md"},{name:"subagent-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/subagent-driven-development/SKILL.md"},{name:"writing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/writing-plans/SKILL.md"},{name:"executing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/executing-plans/SKILL.md"},{name:"requesting-code-review",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/requesting-code-review/SKILL.md"},{name:"root-cause-tracing",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/root-cause-tracing/SKILL.md"},{name:"verification-before-completion",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/verification-before-completion/SKILL.md"},{name:"dispatching-parallel-agents",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/dispatching-parallel-agents/SKILL.md"}]},{label:"anthropics/skills",emoji:"🏛️",stars:"官方",desc:"Anthropic 官方技能库",skills:[{name:"docx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md"},{name:"pdf",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md"},{name:"xlsx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md"},{name:"pptx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md"},{name:"mcp-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md"},{name:"frontend-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md"},{name:"web-artifacts-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md"},{name:"webapp-testing",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md"},{name:"algorithmic-art",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md"},{name:"canvas-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md"}]},{label:"ComposioHQ/awesome-claude-skills",emoji:"🌐",stars:"39.2k",desc:"100+ 社区精选技能",skills:[{name:"github-integration",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/github-integration/SKILL.md"},{name:"data-analysis",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/data-analysis/SKILL.md"},{name:"code-review",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/code-review/SKILL.md"}]}];function tp(){const o=se(d=>d.agentConfig),h=se(d=>d.loadAgentConfig),u=se(d=>d.toast),[y,S]=Z.useState(null),[I,F]=Z.useState(null),[k,b]=Z.useState({name:"",desc:"",trigger:""}),[_,T]=Z.useState(!1),[f,w]=Z.useState("local"),[m,P]=Z.useState([]),[L,U]=Z.useState(!1),[B,W]=Z.useState(!1),[ne,E]=Z.useState({agentId:"",skillName:"",sourceUrl:"",description:""}),[fe,oe]=Z.useState(!1),[pe,z]=Z.useState(null),[Ee,J]=Z.useState(null),[re,je]=Z.useState(null),[ae,G]=Z.useState("");Z.useEffect(()=>{h()},[h]),Z.useEffect(()=>{f==="remote"&&_e()},[f]);const _e=async()=>{U(!0);try{const d=await he.remoteSkillsList();d.ok&&P(d.remoteSkills||[])}catch{u("远程技能列表加载失败","err")}U(!1)},ze=async(d,M)=>{S({agentId:d,name:M,content:"⟳ 加载中…",path:""});try{const V=await he.skillContent(d,M);V.ok?S({agentId:d,name:M,content:V.content||"",path:V.path||""}):S({agentId:d,name:M,content:"❌ "+(V.error||"无法读取"),path:""})}catch{S({agentId:d,name:M,content:"❌ 服务器连接失败",path:""})}},ke=(d,M)=>{F({agentId:d,agentLabel:M}),b({name:"",desc:"",trigger:""})},H=async d=>{if(d.preventDefault(),!(!I||!k.name)){T(!0);try{const M=await he.addSkill(I.agentId,k.name,k.desc,k.trigger);M.ok?(u(`✅ 技能 ${k.name} 已添加到 ${I.agentLabel}`,"ok"),F(null),h()):u(M.error||"添加失败","err")}catch{u("服务器连接失败","err")}T(!1)}},le=async d=>{d.preventDefault();const{agentId:M,skillName:V,sourceUrl:ye,description:ge}=ne;if(!(!M||!V||!ye)){oe(!0);try{const Me=await he.addRemoteSkill(M,V,ye,ge);Me.ok?(u(`✅ 远程技能 ${V} 已添加到 ${M}`,"ok"),W(!1),E({agentId:"",skillName:"",sourceUrl:"",description:""}),_e(),h()):u(Me.error||"添加失败","err")}catch{u("服务器连接失败","err")}oe(!1)}},Q=async d=>{const M=`${d.agentId}/${d.skillName}`;z(M);try{const V=await he.updateRemoteSkill(d.agentId,d.skillName);V.ok?(u(`✅ 技能 ${d.skillName} 已更新`,"ok"),_e()):u(V.error||"更新失败","err")}catch{u("服务器连接失败","err")}z(null)},g=async d=>{const M=`${d.agentId}/${d.skillName}`;J(M);try{const V=await he.removeRemoteSkill(d.agentId,d.skillName);V.ok?(u(`🗑️ 技能 ${d.skillName} 已移除`,"ok"),_e(),h()):u(V.error||"移除失败","err")}catch{u("服务器连接失败","err")}J(null)},R=async(d,M)=>{if(!ae){u("请先选择目标 Agent","err");return}try{const V=await he.addRemoteSkill(ae,M,d,"");V.ok?(u(`✅ ${M} → ${ae}`,"ok"),_e(),h()):u(V.error||"导入失败","err")}catch{u("服务器连接失败","err")}};if(!(o!=null&&o.agents))return l.jsx("div",{className:"empty",children:"无法加载"});const me=l.jsx("div",{children:l.jsx("div",{className:"skills-grid",children:o.agents.map(d=>l.jsxs("div",{className:"sk-card",children:[l.jsxs("div",{className:"sk-hdr",children:[l.jsx("span",{className:"sk-emoji",children:d.emoji||"🏛️"}),l.jsx("span",{className:"sk-name",children:d.label}),l.jsxs("span",{className:"sk-cnt",children:[(d.skills||[]).length," 技能"]})]}),l.jsx("div",{className:"sk-list",children:(d.skills||[]).length?(d.skills||[]).map(M=>l.jsxs("div",{className:"sk-item",onClick:()=>ze(d.id,M.name),children:[l.jsxs("span",{className:"si-name",children:["📦 ",M.name]}),l.jsx("span",{className:"si-desc",children:M.description||"无描述"}),l.jsx("span",{className:"si-arrow",children:"›"})]},M.name)):l.jsx("div",{className:"sk-empty",children:"暂无 Skills"})}),l.jsx("div",{className:"sk-add",onClick:()=>ke(d.id,d.label),children:"+ 添加技能"})]},d.id))})}),ve=l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:10,marginBottom:20,flexWrap:"wrap",alignItems:"center"},children:[l.jsx("button",{style:{padding:"8px 18px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600,fontSize:13},onClick:()=>{W(!0),je(null)},children:"+ 添加远程 Skill"}),l.jsx("button",{style:{padding:"8px 14px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:8,cursor:"pointer",fontSize:12},onClick:_e,children:"⟳ 刷新列表"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:4},children:["共 ",m.length," 个远程技能"]})]}),l.jsxs("div",{style:{marginBottom:24},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700,color:"var(--muted)",letterSpacing:".06em",marginBottom:10},children:"🌐 社区技能源 — 一键导入"}),l.jsx("div",{style:{display:"flex",gap:10,flexWrap:"wrap"},children:ep.map(d=>l.jsxs("div",{onClick:()=>je((re==null?void 0:re.label)===d.label?null:d),style:{padding:"8px 14px",background:(re==null?void 0:re.label)===d.label?"#0d1f45":"var(--panel)",border:`1px solid ${(re==null?void 0:re.label)===d.label?"var(--acc)":"var(--line)"}`,borderRadius:10,cursor:"pointer",fontSize:12,transition:"all .15s"},children:[l.jsx("span",{style:{marginRight:6},children:d.emoji}),l.jsx("b",{style:{color:"var(--text)"},children:d.label}),l.jsxs("span",{style:{marginLeft:6,color:"#f0b429",fontSize:11},children:["★ ",d.stars]}),l.jsx("span",{style:{marginLeft:8,color:"var(--muted)"},children:d.desc})]},d.label))}),re&&l.jsxs("div",{style:{marginTop:14,background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:16},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:14},children:[l.jsx("span",{style:{fontSize:12,fontWeight:600},children:"目标 Agent:"}),l.jsxs("select",{value:ae,onChange:d=>G(d.target.value),style:{padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(d=>l.jsxs("option",{value:d.id,children:[d.emoji," ",d.label," (",d.id,")"]},d.id))]})]}),l.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:8},children:re.skills.map(d=>{const M=m.some(V=>V.skillName===d.name&&V.agentId===ae);return l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"8px 12px",background:"var(--panel2)",borderRadius:8,border:"1px solid var(--line)"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{fontSize:12,fontWeight:600},children:["📦 ",d.name]}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)",wordBreak:"break-all",maxWidth:180},children:d.url.split("/").slice(-2).join("/")})]}),M?l.jsx("span",{style:{fontSize:10,color:"#4caf88",fontWeight:600},children:"✓ 已导入"}):l.jsx("button",{onClick:()=>R(d.url,d.name),style:{padding:"4px 10px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:6,cursor:"pointer",fontSize:11,whiteSpace:"nowrap"},children:"导入"})]},d.name)})})]})]}),L?l.jsx("div",{style:{textAlign:"center",padding:"40px 0",color:"var(--muted)",fontSize:13},children:"⟳ 加载中…"}):m.length===0?l.jsxs("div",{style:{textAlign:"center",padding:"40px",background:"var(--panel)",borderRadius:12,border:"1px dashed var(--line)"},children:[l.jsx("div",{style:{fontSize:32,marginBottom:10},children:"🌐"}),l.jsx("div",{style:{fontSize:14,color:"var(--muted)"},children:"尚无远程技能"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginTop:6},children:"从社区技能源快速导入,或手动添加 URL"})]}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:10},children:m.map(d=>{var Me;const M=`${d.agentId}/${d.skillName}`,V=pe===M,ye=Ee===M,ge=o.agents.find(Pt=>Pt.id===d.agentId);return l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:"14px 18px",display:"grid",gridTemplateColumns:"1fr auto",gap:12,alignItems:"center"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:6},children:[l.jsxs("span",{style:{fontSize:14,fontWeight:700},children:["📦 ",d.skillName]}),l.jsx("span",{style:{fontSize:10,padding:"2px 8px",borderRadius:999,background:d.status==="valid"?"#0d3322":"#3d1111",color:d.status==="valid"?"#4caf88":"#ff5270",fontWeight:600},children:d.status==="valid"?"✓ 有效":"✗ 文件丢失"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",background:"var(--panel2)",padding:"2px 8px",borderRadius:6},children:[ge==null?void 0:ge.emoji," ",(ge==null?void 0:ge.label)||d.agentId]})]}),d.description&&l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:4},children:d.description}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",display:"flex",gap:16,flexWrap:"wrap"},children:[l.jsxs("span",{children:["🔗 ",l.jsx("a",{href:d.sourceUrl,target:"_blank",rel:"noreferrer",style:{color:"var(--acc)",textDecoration:"none"},children:d.sourceUrl.length>60?d.sourceUrl.slice(0,60)+"…":d.sourceUrl})]}),l.jsxs("span",{children:["📅 ",d.lastUpdated?d.lastUpdated.slice(0,10):(Me=d.addedAt)==null?void 0:Me.slice(0,10)]})]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{onClick:()=>ze(d.agentId,d.skillName),style:{padding:"6px 12px",background:"transparent",color:"var(--muted)",border:"1px solid var(--line)",borderRadius:6,cursor:"pointer",fontSize:11},children:"查看"}),l.jsx("button",{onClick:()=>Q(d),disabled:V,style:{padding:"6px 12px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:6,cursor:"pointer",fontSize:11},children:V?"⟳":"更新"}),l.jsx("button",{onClick:()=>g(d),disabled:ye,style:{padding:"6px 12px",background:"transparent",color:"#ff5270",border:"1px solid #ff5270",borderRadius:6,cursor:"pointer",fontSize:11},children:ye?"⟳":"删除"})]})]},M)})})]});return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:4,marginBottom:20,borderBottom:"1px solid var(--line)",paddingBottom:0},children:[{key:"local",label:"🏛️ 本地技能",count:o.agents.reduce((d,M)=>{var V;return d+(((V=M.skills)==null?void 0:V.length)||0)},0)},{key:"remote",label:"🌐 远程技能",count:m.length}].map(d=>l.jsxs("div",{onClick:()=>w(d.key),style:{padding:"8px 18px",cursor:"pointer",fontSize:13,borderRadius:"8px 8px 0 0",fontWeight:f===d.key?700:400,background:f===d.key?"var(--panel)":"transparent",color:f===d.key?"var(--text)":"var(--muted)",border:f===d.key?"1px solid var(--line)":"1px solid transparent",borderBottom:f===d.key?"1px solid var(--panel)":"1px solid transparent",position:"relative",bottom:-1,transition:"all .15s"},children:[d.label,d.count>0&&l.jsx("span",{style:{marginLeft:6,fontSize:10,padding:"1px 6px",borderRadius:999,background:"#1a2040",color:"var(--acc)"},children:d.count})]},d.key))}),f==="local"?me:ve,y&&l.jsx("div",{className:"modal-bg open",onClick:()=>S(null),children:l.jsxs("div",{className:"modal",onClick:d=>d.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>S(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:y.agentId.toUpperCase()}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:16},children:["📦 ",y.name]}),l.jsxs("div",{className:"sk-modal-body",children:[l.jsx("div",{className:"sk-md",style:{whiteSpace:"pre-wrap",fontSize:12,lineHeight:1.7},children:y.content}),y.path&&l.jsxs("div",{className:"sk-path",style:{fontSize:10,color:"var(--muted)",marginTop:12},children:["📂 ",y.path]})]})]})]})}),I&&l.jsx("div",{className:"modal-bg open",onClick:()=>F(null),children:l.jsxs("div",{className:"modal",onClick:d=>d.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>F(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsxs("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:["为 ",I.agentLabel," 添加技能"]}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"+ 新增 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:14,marginBottom:18,fontSize:12,lineHeight:1.7,color:"var(--muted)"},children:[l.jsx("b",{style:{color:"var(--text)"},children:"📋 Skill 规范说明"}),l.jsx("br",{}),"• 技能名称使用",l.jsx("b",{style:{color:"var(--text)"},children:"小写英文 + 连字符"}),l.jsx("br",{}),"• 创建后会生成模板文件 SKILL.md",l.jsx("br",{}),"• 技能会在 agent 收到相关任务时",l.jsx("b",{style:{color:"var(--text)"},children:"自动激活"})]}),l.jsxs("form",{onSubmit:H,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 data-analysis, code-review",value:k.name,onChange:d=>b(M=>({...M,name:d.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"技能描述"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:k.desc,onChange:d=>b(M=>({...M,desc:d.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"触发条件(可选)"}),l.jsx("input",{type:"text",placeholder:"何时激活此技能",value:k.trigger,onChange:d=>b(M=>({...M,trigger:d.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>F(null),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:_,style:{padding:"8px 20px",fontSize:13,background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:_?"⟳ 创建中…":"📦 创建技能"})]})]})]})]})}),B&&l.jsx("div",{className:"modal-bg open",onClick:()=>W(!1),children:l.jsxs("div",{className:"modal",style:{maxWidth:520},onClick:d=>d.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>W(!1),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"#a07aff",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"远程技能管理"}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"🌐 添加远程 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:12,marginBottom:18,fontSize:11,color:"var(--muted)",lineHeight:1.7},children:["支持 GitHub Raw URL,如:",l.jsx("br",{}),l.jsx("code",{style:{color:"var(--acc)",fontSize:10},children:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"})]}),l.jsxs("form",{onSubmit:le,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["目标 Agent ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsxs("select",{required:!0,value:ne.agentId,onChange:d=>E(M=>({...M,agentId:d.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(d=>l.jsxs("option",{value:d.id,children:[d.emoji," ",d.label," (",d.id,")"]},d.id))]})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 brainstorming, code-review",value:ne.skillName,onChange:d=>E(M=>({...M,skillName:d.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["源 URL ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"url",required:!0,placeholder:"https://raw.githubusercontent.com/...",value:ne.sourceUrl,onChange:d=>E(M=>({...M,sourceUrl:d.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"描述(可选)"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:ne.description,onChange:d=>E(M=>({...M,description:d.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>W(!1),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:fe,style:{padding:"8px 20px",fontSize:13,background:"#a07aff",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:fe?"⟳ 下载中…":"🌐 添加远程技能"})]})]})]})]})})]})}function np(){const o=se(y=>y.agentConfig),h={},u={};return o!=null&&o.agents&&o.agents.forEach(y=>{h[y.id]=y.emoji||"🏛️",u[y.id]=y.label||y.id}),{emojiMap:h,labelMap:u}}function es(o){const h=(o.id||"").match(/^OC-(\w+)-/);return h?h[1]:(o.org||"").replace(/省|部/g,"").toLowerCase()}function dc(o,h){let u=o.title||"";if(u==="heartbeat 会话")return"💓 心跳检测";const y=u.match(/^agent:(\w+):(\w+)/);if(y){const S=h[y[1]]||y[1];return y[2]==="main"?S+" · 主会话":y[2]==="subagent"?S+" · 子任务执行":y[2]==="cron"?S+" · 定时任务":S+" · "+y[2]}return u.replace(/ 会话$/,"")||o.id}function fc(o){const h=o.now||"";return h.includes("feishu/direct")?{icon:"💬",text:"飞书对话"}:h.includes("feishu")?{icon:"💬",text:"飞书"}:h.includes("webchat")?{icon:"🌐",text:"WebChat"}:h.includes("cron")?{icon:"⏰",text:"定时"}:h.includes("direct")?{icon:"📨",text:"直连"}:{icon:"🔗",text:"会话"}}function rp(o){const h=o.activity||[];for(let u=h.length-1;u>=0;u--){const y=h[u];if(y.kind==="assistant"){let S=y.text||"";if(S.startsWith("NO_REPLY")||S.startsWith("Reasoning:"))continue;return S=S.replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").replace(/^#+\s/gm,"").trim(),S.substring(0,120)+(S.length>120?"…":"")}}return""}function lp(){const o=se(f=>f.liveStatus),h=se(f=>f.sessFilter),u=se(f=>f.setSessFilter),{emojiMap:y,labelMap:S}=np(),[I,F]=Z.useState(null),b=((o==null?void 0:o.tasks)||[]).filter(f=>!un(f));let _=b;h==="active"?_=b.filter(f=>!["Done","Cancelled"].includes(f.state)):h!=="all"&&(_=b.filter(f=>es(f)===h));const T=[...new Set(b.map(es))];return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:[{key:"all",label:`全部 (${b.length})`},{key:"active",label:"活跃"},...T.slice(0,8).map(f=>({key:f,label:S[f]||f}))].map(f=>l.jsx("span",{className:`sess-filter${h===f.key?" active":""}`,onClick:()=>u(f.key),children:f.label},f.key))}),l.jsx("div",{className:"sess-grid",children:_.length?_.map(f=>{const w=es(f),m=y[w]||"🏛️",P=S[w]||f.org||w,L=f.heartbeat||{status:"unknown",label:""},U=fc(f),B=dc(f,S),W=rp(f),E=(f.sourceMeta||{}).totalTokens,fe=f.eta||"",oe=L.status==="active"?"🟢":L.status==="warn"?"🟡":L.status==="stalled"?"🔴":"⚪",pe=f.state||"Unknown";return l.jsxs("div",{className:"sess-card",onClick:()=>F(f),children:[l.jsxs("div",{className:"sc-top",children:[l.jsx("span",{className:"sc-emoji",children:m}),l.jsx("div",{style:{flex:1,minWidth:0},children:l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{className:"sc-agent",children:P}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)",background:"var(--panel2)",padding:"2px 6px",borderRadius:4},children:[U.icon," ",U.text]})]})}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{title:L.label||"",children:oe}),l.jsx("span",{className:`tag st-${pe}`,style:{fontSize:10},children:Wr[pe]||pe})]})]}),l.jsx("div",{className:"sc-title",children:B}),W&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:8,borderLeft:"2px solid var(--line)",paddingLeft:8,maxHeight:40,overflow:"hidden"},children:W}),l.jsxs("div",{className:"sc-meta",children:[E?l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["🪙 ",E.toLocaleString()," tokens"]}):null,fe?l.jsx("span",{className:"sc-time",children:Hf(fe)}):null]})]},f.id)}):l.jsx("div",{style:{fontSize:13,color:"var(--muted)",padding:24,textAlign:"center",gridColumn:"1/-1"},children:"暂无小任务/会话数据"})}),I&&l.jsx(sp,{task:I,labelMap:S,emojiMap:y,onClose:()=>F(null)})]})}function sp({task:o,labelMap:h,emojiMap:u,onClose:y}){const S=es(o),I=u[S]||"🏛️",F=dc(o,h),k=fc(o),b=o.heartbeat||{label:""},_=o.sourceMeta||{},T=o.activity||[],f=o.state||"Unknown",w=_.totalTokens,m=_.inputTokens,P=_.outputTokens;return l.jsx("div",{className:"modal-bg open",onClick:y,children:l.jsxs("div",{className:"modal",onClick:L=>L.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:y,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[I," ",F]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${f}`,children:Wr[f]||f}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:[k.icon," ",k.text]}),b.label&&l.jsx("span",{style:{fontSize:11},children:b.label})]}),l.jsxs("div",{style:{display:"flex",gap:14,marginBottom:18,flexWrap:"wrap"},children:[w!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700,color:"var(--acc)"},children:w.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"总 Tokens"})]}),m!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:m.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输入"})]}),P!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:P.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输出"})]})]}),l.jsxs("div",{style:{fontSize:12,fontWeight:700,marginBottom:8},children:["📋 最近活动 ",l.jsxs("span",{style:{fontWeight:400,color:"var(--muted)"},children:["(",T.length," 条)"]})]}),l.jsx("div",{style:{maxHeight:350,overflowY:"auto",border:"1px solid var(--line)",borderRadius:10,background:"var(--panel2)"},children:T.length?T.slice(-15).reverse().map((L,U)=>{const B=L.kind||"",W=B==="assistant"?"🤖":B==="tool"?"🔧":B==="user"?"👤":"📝",ne=B==="assistant"?"回复":B==="tool"?"工具":B==="user"?"用户":"事件";let E=(L.text||"").replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").trim();E.length>200&&(E=E.substring(0,200)+"…");const fe=(L.at||"").substring(11,19);return l.jsxs("div",{style:{padding:"8px 12px",borderBottom:"1px solid var(--line)",fontSize:12,lineHeight:1.5},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[l.jsx("span",{children:W}),l.jsx("span",{style:{fontWeight:600,fontSize:11},children:ne}),l.jsx("span",{style:{color:"var(--muted)",fontSize:10,marginLeft:"auto"},children:fe})]}),l.jsx("div",{style:{color:"var(--muted)"},children:E})]},U)}):l.jsx("div",{style:{padding:16,color:"var(--muted)",fontSize:12,textAlign:"center"},children:"暂无活动记录"})}),o.output&&o.output!=="-"&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:12,wordBreak:"break-all",borderTop:"1px solid var(--line)",paddingTop:8},children:["📂 ",o.output]})]})]})})}function ip(){const o=se(_=>_.liveStatus),[h,u]=Z.useState("all"),[y,S]=Z.useState(null),I=se(_=>_.toast);let k=((o==null?void 0:o.tasks)||[]).filter(_=>un(_)&&["Done","Cancelled"].includes(_.state));h!=="all"&&(k=k.filter(_=>_.state===h));const b=_=>{const T=_.flow_log||[];let f=`# 📜 奏折 · ${_.title} + +`;if(f+=`- **任务编号**: ${_.id} +`,f+=`- **状态**: ${_.state} +`,f+=`- **负责部门**: ${_.org} +`,T.length){const w=T[0].at?T[0].at.substring(0,19).replace("T"," "):"未知",m=T[T.length-1].at?T[T.length-1].at.substring(0,19).replace("T"," "):"未知";f+=`- **开始时间**: ${w} +`,f+=`- **完成时间**: ${m} +`}f+=` +## 流转记录 + +`;for(const w of T)f+=`- **${w.from}** → **${w.to}** + ${w.remark} + _${(w.at||"").substring(0,19)}_ + +`;_.output&&_.output!=="-"&&(f+=`## 产出物 + +\`${_.output}\` +`),navigator.clipboard.writeText(f).then(()=>I("✅ 奏折已复制为 Markdown","ok"),()=>I("复制失败","err"))};return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:8,marginBottom:16,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:12,color:"var(--muted)"},children:"筛选:"}),[{key:"all",label:"全部"},{key:"Done",label:"✅ 已完成"},{key:"Cancelled",label:"🚫 已取消"}].map(_=>l.jsx("span",{className:`sess-filter${h===_.key?" active":""}`,onClick:()=>u(_.key),children:_.label},_.key))]}),l.jsx("div",{className:"mem-list",children:k.length?k.map(_=>{const T=_.flow_log||[],f=[...new Set(T.map(L=>L.from).concat(T.map(L=>L.to)).filter(L=>L&&L!=="皇上"))],w=T.length?(T[0].at||"").substring(0,16).replace("T"," "):"",m=T.length?(T[T.length-1].at||"").substring(0,16).replace("T"," "):"",P=_.state==="Done"?"✅":"🚫";return l.jsxs("div",{className:"mem-card",onClick:()=>S(_),children:[l.jsx("div",{className:"mem-icon",children:"📜"}),l.jsxs("div",{className:"mem-info",children:[l.jsxs("div",{className:"mem-title",children:[P," ",_.title||_.id]}),l.jsxs("div",{className:"mem-sub",children:[_.id," · ",_.org||""," · 流转 ",T.length," 步"]}),l.jsx("div",{className:"mem-tags",children:f.slice(0,5).map(L=>l.jsx("span",{className:"mem-tag",children:L},L))})]}),l.jsxs("div",{className:"mem-right",children:[l.jsx("span",{className:"mem-date",children:w}),m!==w&&l.jsx("span",{className:"mem-date",children:m})]})]},_.id)}):l.jsx("div",{className:"mem-empty",children:"暂无奏折 — 任务完成后自动生成"})}),y&&l.jsx(op,{task:y,onClose:()=>S(null),onExport:b})]})}function op({task:o,onClose:h,onExport:u}){const y=o.flow_log||[],S=o.state||"Unknown",I=S==="Done"?"✅":S==="Cancelled"?"🚫":"🔄",F=[...new Set(y.map(m=>m.from).concat(y.map(m=>m.to)).filter(m=>m&&m!=="皇上"))],k=[],b=[],_=[],T=[],f=[];for(const m of y)m.from==="皇上"?k.push(m):m.to==="中书省"||m.from==="中书省"?b.push(m):m.to==="门下省"||m.from==="门下省"?_.push(m):m.remark&&(m.remark.includes("完成")||m.remark.includes("回奏"))?f.push(m):T.push(m);const w=(m,P,L)=>L.length?l.jsxs("div",{style:{marginBottom:18},children:[l.jsxs("div",{style:{fontSize:13,fontWeight:700,marginBottom:10},children:[P," ",m]}),l.jsx("div",{className:"md-timeline",children:L.map((U,B)=>{var ne,E;const W=(ne=U.remark)!=null&&ne.includes("✅")?"green":(E=U.remark)!=null&&E.includes("驳")?"red":"";return l.jsxs("div",{className:"md-tl-item",children:[l.jsx("div",{className:`md-tl-dot ${W}`}),l.jsxs("div",{style:{display:"flex",gap:6,alignItems:"baseline"},children:[l.jsx("span",{className:"md-tl-from",children:U.from}),l.jsxs("span",{className:"md-tl-to",children:["→ ",U.to]})]}),l.jsx("div",{className:"md-tl-remark",children:U.remark}),l.jsx("div",{className:"md-tl-time",children:(U.at||"").substring(0,19).replace("T"," ")})]},B)})})]}):null;return l.jsx("div",{className:"modal-bg open",onClick:h,children:l.jsxs("div",{className:"modal",onClick:m=>m.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:h,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[I," ",o.title||o.id]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${S}`,children:Wr[S]||S}),l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:o.org}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["流转 ",y.length," 步"]}),F.map(m=>l.jsx("span",{className:"mem-tag",children:m},m))]}),o.now&&l.jsx("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:"10px 14px",marginBottom:18,fontSize:12,color:"var(--muted)"},children:o.now}),w("圣旨原文","👑",k),w("中书规划","📋",b),w("门下审议","🔍",_),w("六部执行","⚔️",T),w("汇总回奏","📨",f),o.output&&o.output!=="-"&&l.jsxs("div",{style:{marginTop:12,paddingTop:12,borderTop:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,marginBottom:4},children:"📦 产出物"}),l.jsx("code",{style:{fontSize:11,wordBreak:"break-all"},children:o.output})]}),l.jsx("div",{style:{display:"flex",gap:8,marginTop:16,justifyContent:"flex-end"},children:l.jsx("button",{className:"btn btn-g",onClick:()=>u(o),style:{fontSize:12,padding:"6px 16px"},children:"📋 复制奏折"})})]})]})})}const ap=["中书省","尚书省","礼部","户部","兵部","刑部","工部","吏部"],up=[{id:"taizi",label:"太子",emoji:"🤴"},{id:"zhongshu",label:"中书省",emoji:"📜"},{id:"menxia",label:"门下省",emoji:"🔍"},{id:"shangshu",label:"尚书省",emoji:"📮"},{id:"hubu",label:"户部",emoji:"💰"},{id:"bingbu",label:"兵部",emoji:"⚔️"}];function cp(){var Pt,qn;const o=se(C=>C.tplCatFilter),h=se(C=>C.setTplCatFilter),u=se(C=>C.toast),y=se(C=>C.loadAll),[S,I]=Z.useState(null),[F,k]=Z.useState({}),[b,_]=Z.useState(""),[T,f]=Z.useState(""),[w,m]=Z.useState(""),[P,L]=Z.useState("normal"),[U,B]=Z.useState(""),[W,ne]=Z.useState(["taizi","zhongshu","menxia"]),[E,fe]=Z.useState(""),[oe,pe]=Z.useState(!1),[z,Ee]=Z.useState(null),[J,re]=Z.useState(!1),[je,ae]=Z.useState("");Z.useEffect(()=>{if(!J||!E)return;const C=setInterval(async()=>{try{const X=await he.courtDiscuss({action:"status",sessionId:E});Ee(X)}catch{}},1200);return()=>clearInterval(C)},[J,E]);let G=Bf;o!=="全部"&&(G=G.filter(C=>C.cat===o));const _e=C=>{const X={};C.params.forEach(ie=>{X[ie.key]=ie.default||""}),k(X),I(C),_("")},ze=C=>{let X=C.command;for(const ie of C.params)X=X.replace(new RegExp("\\{"+ie.key+"\\}","g"),F[ie.key]||ie.default||"");return X},ke=()=>{S&&_(ze(S))},H=async()=>{try{const C=await he.agentsStatus();if(C.ok&&C.gateway&&!C.gateway.alive&&(u("⚠️ Gateway 未启动,任务将无法派发!","err"),!confirm("Gateway 未启动,继续?")))return!1}catch{}return!0},le=async C=>{if(C.preventDefault(),!S)return;const X=ze(S);if(!X.trim()){u("请填写必填参数","err");return}if(await H()&&confirm(`确认下旨? + +${X.substring(0,200)}${X.length>200?"…":""}`))try{const ie={};for(const dt of S.params)ie[dt.key]=F[dt.key]||dt.default||"";const Be=await he.createTask({title:X,org:"中书省",targetDept:S.depts[0]||"",priority:"normal",templateId:S.id,params:ie});Be.ok?(u(`📜 ${Be.taskId} 旨意已下达`,"ok"),I(null),y()):u(Be.error||"下旨失败","err")}catch{u("⚠️ 服务器连接失败","err")}},Q=C=>{ne(X=>X.includes(C)?X.length<=1?X:X.filter(ie=>ie!==C):[...X,C])},g=async C=>{C.preventDefault();const X=U.trim();if(X.length<10){u("议题至少 10 个字","err");return}if(W.length<2){u("至少选择 2 位大臣参与议政","err");return}if(await H()&&confirm(`开始御前议政? + +议题:${X.substring(0,120)}${X.length>120?"…":""}`)){re(!0),pe(!0),fe(""),Ee(null);try{const ie=await he.courtDiscuss({action:"start",topic:X,participants:W,emperorNote:je.trim()});Ee(ie),fe(ie.sessionId||""),ie.ok?u("🧠 首轮议政已启动,正在轮番发言","ok"):u(ie.error||"议政失败","err")}catch{u("⚠️ 服务器连接失败","err")}finally{pe(!1)}}},R=async()=>{if(E)try{const C=await he.courtDiscuss({action:"status",sessionId:E});Ee(C)}catch{}},me=async()=>{if(!E){u("请先开始议政","err");return}pe(!0);try{const C=await he.courtDiscuss({action:"next",sessionId:E,emperorNote:je.trim()});Ee(C),C.ok?u("已启动下一轮讨论","ok"):u(C.error||"继续讨论失败","err")}catch{u("⚠️ 服务器连接失败","err")}finally{pe(!1)}},ve=async()=>{var C,X;if(!E){u("请先开始议政","err");return}if(confirm("确认皇上拍板结束,并将综合旨意复制到自由下旨区?")){pe(!0);try{const ie=await he.courtDiscuss({action:"finalize",sessionId:E,emperorNote:je.trim()});if(Ee(ie),ie.ok){const Be=V(ie);Be&&f(Be),(C=ie.final)!=null&&C.recommended_target_dept&&m(ie.final.recommended_target_dept),(X=ie.final)!=null&&X.recommended_priority&&L(ie.final.recommended_priority),re(!1),ae(""),u("✅ 已形成综合旨意,已复制到自由下旨区,请修改后下达","ok")}else u(ie.error||"拍板失败","err")}catch{u("⚠️ 服务器连接失败","err")}finally{pe(!1)}}},d=C=>C.replace(/[^\p{L}\p{N}]+/gu,"").toLowerCase(),M=(C,X)=>{const ie=d(C),Be=d(X);if(!Be||X.trim().length<20)return!0;if(!ie)return!1;if(ie===Be||ie.includes(Be)||Be.includes(ie))return!0;let dt=0;for(const Cn of Be)ie.includes(Cn)&&(dt+=1);return dt/Math.max(Be.length,1)>.85},V=C=>{var Zn,Jn,er;const X=(C.topic||"").trim(),ie=C.final,Be=((ie==null?void 0:ie.recommended_edict)||"").trim(),dt=((ie==null?void 0:ie.clarified_goal)||"").trim(),Cn=(((Zn=C.assessment)==null?void 0:Zn.draft_direction)||"").trim(),_n=((er=(Jn=(C.emperorNotes||[]).slice(-1)[0])==null?void 0:Jn.text)==null?void 0:er.trim())||"";if(Be&&!M(X,Be))return Be;const cn=C.rounds||0,bt=(C.discussion||[]).filter(Rt=>(Rt.round||0)===cn&&(Rt.reply||"").trim()).slice(-3).map(Rt=>{const Wt=(Rt.reply||"").replace(/\s+/g," ").trim(),Hr=Wt.length>90?`${Wt.slice(0,90)}...`:Wt;return`${Rt.agentLabel}:${Hr}`}),dn=[`请太子牵头办理:${dt||Cn||X||"落实本次议政结论"}。`];return bt.length>0&&dn.push(`议政要点:${bt.join(";")}。`),_n&&dn.push(`皇上最终拍板:${_n}。`),dn.push("请形成执行方案、风险清单与里程碑,并按节点回报。"),dn.join(` +`)},ye=()=>{var X,ie;if(!z){u("没有可用的议政结论","err");return}const C=V(z);if(!C){u("没有可用的建议旨意","err");return}f(C),(X=z==null?void 0:z.final)!=null&&X.recommended_target_dept&&m(z.final.recommended_target_dept),(ie=z==null?void 0:z.final)!=null&&ie.recommended_priority&&L(z.final.recommended_priority),re(!1),u("已将议政结论填入自由下旨区","ok")},ge=async()=>{if(!E){u("请先开始议政","err");return}if(confirm("确认终止该话题,不进入办理流程?")){pe(!0);try{const C=await he.courtDiscuss({action:"terminate",sessionId:E,emperorNote:je.trim()});Ee(C),C.ok?u("🛑 话题已终止","ok"):u(C.error||"终止失败","err")}catch{u("⚠️ 服务器连接失败","err")}finally{pe(!1)}}},Me=async C=>{C.preventDefault();const X=T.trim();if(!X){u("请先输入旨意内容","err");return}if(X.length<10){u("旨意至少 10 个字,避免被判定为闲聊","err");return}if(await H()&&confirm(`确认下旨给太子? + +${X.substring(0,200)}${X.length>200?"…":""}`))try{const ie=await he.createTask({title:X,org:"中书省",targetDept:w||"",priority:P,templateId:"manual-free",params:{source:"free-edict"}});ie.ok?(u(`📜 ${ie.taskId} 旨意已下达`,"ok"),f(""),m(""),L("normal"),y()):u(ie.error||"下旨失败","err")}catch{u("⚠️ 服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:14,marginBottom:16},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:6},children:"御前议政(先讨论再下旨)"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:10},children:"适合想法尚未成熟时,先让太子与诸臣讨论澄清,再生成可执行旨意。"}),l.jsxs("form",{onSubmit:g,children:[l.jsx("textarea",{className:"tpl-input",style:{minHeight:88,resize:"vertical",marginBottom:10},placeholder:"例如:目前天下要闻标题是英文,想在看板中展示中文标题,但点击后保持英文原文。请先讨论清楚方案和边界。",value:U,onChange:C=>B(C.target.value),disabled:oe}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:10},children:[l.jsx("button",{type:"submit",className:"tpl-go",style:{flex:"0 0 170px"},disabled:oe,children:oe?"⏳ 议政中...":"🧠 开始首轮议政"}),E&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",alignSelf:"center"},children:["会话: ",E]})]}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:8},children:up.map(C=>{const X=W.includes(C.id);return l.jsxs("button",{type:"button",className:"tpl-cat",onClick:()=>Q(C.id),style:{cursor:"pointer",borderColor:X?"var(--acc)":"var(--line)",color:X?"var(--text)":"var(--muted)",background:X?"var(--acc-soft)":"transparent"},disabled:oe,children:[C.emoji," ",C.label]},C.id)})})]}),z&&l.jsxs("div",{style:{borderTop:"1px dashed var(--line)",marginTop:10,paddingTop:10,fontSize:12},children:[l.jsxs("div",{style:{marginBottom:8},children:["当前状态:",l.jsx("strong",{style:{marginLeft:6},children:z.status==="handoffed"?"已交办太子":z.status==="terminated"?"已终止":z.status==="done"?"讨论已结束":"讨论进行中"}),typeof z.rounds=="number"&&l.jsxs("span",{style:{marginLeft:10},children:["轮次:",z.rounds]})]}),z.message&&l.jsx("div",{style:{color:"var(--muted)",marginBottom:8},children:z.message}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>{re(!0),R()},children:"🪟 打开议政窗口"}),l.jsx("button",{type:"button",className:"tpl-go",onClick:R,children:"刷新状态"})]})]})]}),l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:14,marginBottom:16},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:6},children:"自由下旨(不走模板)"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:10},children:"直接输入任务指令,系统会创建旨意并先派发给太子分拣。"}),l.jsxs("form",{onSubmit:Me,children:[l.jsx("textarea",{className:"tpl-input",style:{minHeight:92,resize:"vertical",marginBottom:10},placeholder:"例如:请对 edict 项目做一次端到端测试审查,重点检查任务卡住时的自动重试与回滚链路。",value:T,onChange:C=>f(C.target.value)}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:10},children:[l.jsxs("select",{className:"tpl-input",style:{flex:"1 1 220px"},value:w,onChange:C=>m(C.target.value),children:[l.jsx("option",{value:"",children:"建议执行部门(可选)"}),ap.map(C=>l.jsx("option",{value:C,children:C},C))]}),l.jsxs("select",{className:"tpl-input",style:{flex:"0 0 160px"},value:P,onChange:C=>L(C.target.value),children:[l.jsx("option",{value:"low",children:"低"}),l.jsx("option",{value:"normal",children:"普通"}),l.jsx("option",{value:"high",children:"高"}),l.jsx("option",{value:"critical",children:"紧急"})]}),l.jsx("button",{type:"submit",className:"tpl-go",style:{flex:"0 0 130px"},children:"📜 下旨"})]})]})]}),l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:Of.map(C=>l.jsxs("span",{className:`tpl-cat${o===C.name?" active":""}`,onClick:()=>h(C.name),children:[C.icon," ",C.name]},C.name))}),l.jsx("div",{className:"tpl-grid",children:G.map(C=>l.jsxs("div",{className:"tpl-card",children:[l.jsxs("div",{className:"tpl-top",children:[l.jsx("span",{className:"tpl-icon",children:C.icon}),l.jsx("span",{className:"tpl-name",children:C.name})]}),l.jsx("div",{className:"tpl-desc",children:C.desc}),l.jsxs("div",{className:"tpl-footer",children:[C.depts.map(X=>l.jsx("span",{className:"tpl-dept",children:X},X)),l.jsxs("span",{className:"tpl-est",children:[C.est," · ",C.cost]}),l.jsx("button",{className:"tpl-go",onClick:()=>_e(C),children:"下旨"})]})]},C.id))}),J&&l.jsx("div",{className:"modal-bg open",onClick:()=>re(!1),children:l.jsxs("div",{className:"modal",onClick:C=>C.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>re(!1),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"御前议政窗口"}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:"🪟 轮番议政 · 皇上拍板"}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:12},children:["当前会话:",E||"未开始"," ",z!=null&&z.status?`| 状态:${z.status}`:""]}),oe&&l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:10},children:"⏳ 议政处理中,请稍候…"}),(z==null?void 0:z.roundRunning)&&((Pt=z==null?void 0:z.speakingNow)==null?void 0:Pt.agentLabel)&&l.jsxs("div",{style:{fontSize:12,color:"var(--acc)",marginBottom:10},children:["🔄 正在发言:第",z.speakingNow.round||"?","轮 · 第",z.speakingNow.turn||"?","位/",z.speakingNow.totalTurns||"?"," ·",z.speakingNow.agentLabel]}),(z==null?void 0:z.topic)&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:10,whiteSpace:"pre-wrap",lineHeight:1.55,marginBottom:10},children:["议题:",z.topic]}),(z==null?void 0:z.assessment)&&!(z!=null&&z.final)&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:10,marginBottom:10,fontSize:12},children:[l.jsxs("div",{style:{fontWeight:700,marginBottom:4},children:[z.assessment.moderatorLabel," 本轮建议:",z.assessment.recommend_stop?"可结束讨论":"建议继续讨论"]}),z.assessment.reason&&l.jsxs("div",{style:{color:"var(--muted)",marginBottom:4},children:["原因:",z.assessment.reason]}),z.assessment.question_to_emperor&&l.jsxs("div",{children:["请皇上拍板:",z.assessment.question_to_emperor]})]}),(z==null?void 0:z.final)&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:10,marginBottom:10,fontSize:12},children:[l.jsx("div",{style:{fontWeight:700,marginBottom:6},children:z.final.ready_for_edict?"✅ 已形成办理结论":"🟡 结论暂不建议办理"}),z.final.clarified_goal&&l.jsxs("div",{style:{color:"var(--muted)",marginBottom:6},children:["目标澄清:",z.final.clarified_goal]}),l.jsx("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:8,padding:8,whiteSpace:"pre-wrap",lineHeight:1.5},children:z.final.recommended_edict||"(暂无草案)"})]}),(z==null?void 0:z.linkedTaskId)&&l.jsxs("div",{style:{fontSize:12,color:"var(--acc)",marginBottom:10},children:["已交由太子办理:",z.linkedTaskId]}),l.jsxs("div",{style:{border:"1px solid var(--line)",borderRadius:8,maxHeight:360,overflow:"auto",padding:10,background:"var(--panel2)"},children:[((z==null?void 0:z.discussion)||[]).length===0&&l.jsx("div",{style:{color:"var(--muted)",fontSize:12},children:"暂无讨论记录"}),((z==null?void 0:z.discussion)||[]).map((C,X)=>l.jsxs("div",{style:{border:C.status==="speaking"?"1px solid #4da3ff66":C.error?"1px solid #ff7d7d66":"1px solid var(--line)",borderRadius:8,padding:8,marginBottom:8,background:C.status==="speaking"?"#4da3ff12":C.error?"#ff7d7d12":"var(--panel)"},children:[l.jsxs("div",{style:{fontWeight:700,marginBottom:4},children:["第",C.round,"轮 · 第",C.turn||"?","位/",C.totalTurns||"?"," · ",C.agentLabel,C.status==="speaking"&&l.jsx("span",{style:{marginLeft:8,color:"#4da3ff"},children:"(发言中)"}),C.error&&l.jsx("span",{style:{marginLeft:8,color:"#ff7d7d"},children:"(发言降级)"})]}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.5,fontSize:12},children:C.reply||(C.status==="speaking"?"……":"(暂无内容)")})]},`${C.round}-${C.agentId}-${X}`))]}),l.jsxs("div",{style:{marginTop:12,marginBottom:10},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700,marginBottom:6},children:"皇上发言(本轮批示)"}),l.jsx("textarea",{className:"tpl-input",style:{minHeight:56,resize:"vertical"},placeholder:"例如:请收敛到可执行方案;若仍有模型兼容问题,直接给出是否终止建议。",value:je,onChange:C=>ae(C.target.value),disabled:oe}),((z==null?void 0:z.emperorNotes)||[]).length>0&&l.jsxs("div",{style:{marginTop:6,fontSize:12,color:"var(--muted)"},children:["最近批示:",(qn=((z==null?void 0:z.emperorNotes)||[]).slice(-1)[0])==null?void 0:qn.text]})]}),l.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:R,disabled:oe,children:"刷新"}),(z==null?void 0:z.status)==="ongoing"&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:me,disabled:oe||!!(z!=null&&z.roundRunning),children:"继续一轮"}),l.jsx("button",{type:"button",className:"tpl-go",onClick:ve,disabled:oe||!!(z!=null&&z.roundRunning),children:"拍板并生成旨意"}),l.jsx("button",{type:"button",className:"btn btn-g",style:{borderColor:"#ff7d7d",color:"#ff7d7d"},onClick:ge,disabled:oe,children:"终止话题"})]}),(z==null?void 0:z.status)==="done"&&!(z!=null&&z.linkedTaskId)&&l.jsxs(l.Fragment,{children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:ye,children:"采用到自由下旨"}),l.jsx("button",{type:"button",className:"btn btn-g",style:{borderColor:"#ff7d7d",color:"#ff7d7d"},onClick:ge,disabled:oe,children:"终止话题"})]})]})]})]})}),S&&l.jsx("div",{className:"modal-bg open",onClick:()=>I(null),children:l.jsxs("div",{className:"modal",onClick:C=>C.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>I(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"圣旨模板"}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[S.icon," ",S.name]}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:18},children:S.desc}),l.jsxs("div",{style:{display:"flex",gap:6,marginBottom:18,flexWrap:"wrap"},children:[S.depts.map(C=>l.jsx("span",{className:"tpl-dept",children:C},C)),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:"auto"},children:[S.est," · ",S.cost]})]}),l.jsxs("form",{className:"tpl-form",onSubmit:le,children:[S.params.map(C=>l.jsxs("div",{className:"tpl-field",children:[l.jsxs("label",{className:"tpl-label",children:[C.label,C.required&&l.jsx("span",{style:{color:"#ff5270"},children:" *"})]}),C.type==="textarea"?l.jsx("textarea",{className:"tpl-input",style:{minHeight:80,resize:"vertical"},required:C.required,value:F[C.key]||"",onChange:X=>k(ie=>({...ie,[C.key]:X.target.value}))}):C.type==="select"?l.jsx("select",{className:"tpl-input",value:F[C.key]||C.default||"",onChange:X=>k(ie=>({...ie,[C.key]:X.target.value})),children:(C.options||[]).map(X=>l.jsx("option",{children:X},X))}):l.jsx("input",{className:"tpl-input",type:"text",required:C.required,value:F[C.key]||"",onChange:X=>k(ie=>({...ie,[C.key]:X.target.value}))})]},C.key)),b&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:12,marginBottom:14,fontSize:12,color:"var(--muted)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,color:"var(--text)",marginBottom:6},children:"📜 将发送给中书省的旨意:"}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.6},children:b})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:ke,style:{padding:"8px 16px",fontSize:12},children:"👁 预览旨意"}),l.jsx("button",{type:"submit",className:"tpl-go",style:{padding:"8px 20px",fontSize:13},children:"📜 下旨"})]})]})]})]})})]})}const pc={政治:{icon:"🏛️",color:"#6a9eff",desc:"全球政治动态"},军事:{icon:"⚔️",color:"#ff5270",desc:"军事与冲突"},经济:{icon:"💹",color:"#2ecc8a",desc:"经济与市场"},AI大模型:{icon:"🤖",color:"#a07aff",desc:"AI与大模型进展"}},Ji=["政治","军事","经济","AI大模型"];function dp(){const o=se(J=>J.morningBrief),h=se(J=>J.subConfig),u=se(J=>J.loadMorning),y=se(J=>J.loadSubConfig),S=se(J=>J.toast),[I,F]=Z.useState(!1),[k,b]=Z.useState(null),[_,T]=Z.useState(!1),[f,w]=Z.useState("⟳ 立即采集"),m=Z.useRef(null);Z.useEffect(()=>{u()},[u]),Z.useEffect(()=>{h&&b(JSON.parse(JSON.stringify(h)))},[h]),Z.useEffect(()=>()=>{m.current&&clearInterval(m.current)},[]);const P=async()=>{T(!0),w("⟳ 采集中…");let J=null;try{J=(o==null?void 0:o.generated_at)||null}catch{}try{await he.refreshMorning(),S("采集已触发,自动检测更新中…","ok");let re=0;m.current&&clearInterval(m.current),m.current=setInterval(async()=>{if(re++,re>24){clearInterval(m.current),m.current=null,T(!1),w("⟳ 立即采集"),S("采集超时,请重试","err");return}try{const je=await he.morningBrief();je.generated_at&&je.generated_at!==J?(clearInterval(m.current),m.current=null,T(!1),w("⟳ 立即采集"),u(),S("✅ 天下要闻已更新","ok")):w(`⟳ 采集中… (${re*5}s)`)}catch{}},5e3)}catch{S("触发失败","err"),T(!1),w("⟳ 立即采集")}},L=J=>{if(!k)return;const re=[...k.categories||[]],je=re.find(ae=>ae.name===J);je?je.enabled=!je.enabled:re.push({name:J,enabled:!0}),b({...k,categories:re})},U=J=>{if(!k||!J)return;const re=[...k.keywords||[]];re.includes(J)||re.push(J),b({...k,keywords:re})},B=J=>{if(!k)return;const re=[...k.keywords||[]];re.splice(J,1),b({...k,keywords:re})},W=(J,re,je)=>{if(!k||!J||!re){S("请填写源名称和URL","err");return}const ae=[...k.custom_feeds||[]];ae.push({name:J,url:re,category:je}),b({...k,custom_feeds:ae})},ne=J=>{if(!k)return;const re=[...k.custom_feeds||[]];re.splice(J,1),b({...k,custom_feeds:re})},E=async()=>{if(k)try{const J=await he.saveMorningConfig(k);J.ok?(S("订阅配置已保存","ok"),y()):S(J.error||"保存失败","err")}catch{S("服务器连接失败","err")}},fe=k?new Set((k.categories||[]).filter(J=>J.enabled).map(J=>J.name)):new Set(Ji),oe=((k==null?void 0:k.keywords)||[]).map(J=>J.toLowerCase()),pe=(o==null?void 0:o.categories)||{},z=o!=null&&o.date?o.date.replace(/(\d{4})(\d{2})(\d{2})/,"$1年$2月$3日"):"",Ee=Object.values(pe).flat().length;return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16},children:[l.jsxs("div",{children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:4},children:"🌅 天下要闻"}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[z&&`${z} | `,(o==null?void 0:o.generated_at)&&`采集于 ${o.generated_at} | `,"共 ",Ee," 条要闻"]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{className:"btn btn-g",onClick:()=>F(!I),style:{fontSize:12,padding:"6px 14px"},children:"⚙ 订阅配置"}),l.jsx("button",{className:"tpl-go",disabled:_,onClick:P,style:{fontSize:12,padding:"6px 14px"},children:f})]})]}),I&&k&&l.jsx(fp,{config:k,enabledSet:fe,onToggleCat:L,onAddKeyword:U,onRemoveKeyword:B,onAddFeed:W,onRemoveFeed:ne,onSave:E,onSetWebhook:J=>b({...k,feishu_webhook:J})}),Object.keys(pe).length?l.jsx("div",{className:"mb-cats",children:Object.entries(pe).map(([J,re])=>{if(!fe.has(J))return null;const je=pc[J]||{icon:"📰",color:"var(--acc)"},ae=re.map(G=>{const _e=((G.title||"")+(G.summary||"")).toLowerCase(),ze=oe.filter(ke=>_e.includes(ke)).length;return{...G,_kwHits:ze}}).sort((G,_e)=>_e._kwHits-G._kwHits);return l.jsxs("div",{className:"mb-cat",children:[l.jsxs("div",{className:"mb-cat-hdr",children:[l.jsx("span",{className:"mb-cat-icon",children:je.icon}),l.jsx("span",{className:"mb-cat-name",style:{color:je.color},children:J}),l.jsxs("span",{className:"mb-cat-cnt",children:[ae.length," 条"]})]}),l.jsx("div",{className:"mb-news-list",children:ae.length?ae.map((G,_e)=>{const ze=!!(G.image&&G.image.startsWith("http"));return l.jsxs("div",{className:"mb-card",onClick:()=>window.open(G.link,"_blank"),children:[l.jsx("div",{className:"mb-img",children:ze?l.jsx("img",{src:G.image,onError:ke=>{ke.target.style.display="none"},loading:"lazy",alt:""}):l.jsx("span",{children:je.icon})}),l.jsxs("div",{className:"mb-info",children:[l.jsxs("div",{className:"mb-headline",children:[G.title,G._kwHits>0&&l.jsx("span",{style:{fontSize:9,padding:"1px 5px",borderRadius:999,background:"#a07aff22",color:"#a07aff",border:"1px solid #a07aff44",marginLeft:4},children:"⭐ 关注"})]}),l.jsx("div",{className:"mb-summary",children:G.summary||G.desc||""}),l.jsxs("div",{className:"mb-meta",children:[l.jsxs("span",{className:"mb-source",children:["📡 ",G.source||""]}),G.pub_date&&l.jsx("span",{className:"mb-time",children:G.pub_date.substring(0,16)})]})]})]},_e)}):l.jsx("div",{className:"mb-empty",style:{padding:16},children:"暂无新闻"})})]},J)})}):l.jsx("div",{className:"mb-empty",children:"暂无数据,点击右上角「立即采集」获取今日简报"})]})}function fp({config:o,enabledSet:h,onToggleCat:u,onAddKeyword:y,onRemoveKeyword:S,onAddFeed:I,onRemoveFeed:F,onSave:k,onSetWebhook:b}){const[_,T]=Z.useState(""),[f,w]=Z.useState(""),[m,P]=Z.useState(""),[L,U]=Z.useState(Ji[0]),B=[...Ji];return(o.categories||[]).forEach(W=>{B.includes(W.name)||B.push(W.name)}),l.jsxs("div",{className:"sub-config",style:{marginBottom:20,padding:16,background:"var(--panel2)",borderRadius:12,border:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:12},children:"⚙ 订阅配置"}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"订阅分类"}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:B.map(W=>{const ne=pc[W]||{icon:"📰"},E=h.has(W);return l.jsxs("div",{className:`sub-cat ${E?"active":""}`,onClick:()=>u(W),style:{cursor:"pointer",padding:"6px 12px",borderRadius:8,border:`1px solid ${E?"var(--acc)":"var(--line)"}`,display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{children:ne.icon}),l.jsx("span",{style:{fontSize:12},children:W}),E&&l.jsx("span",{style:{fontSize:10,color:"var(--ok)"},children:"✓"})]},W)})})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"关注关键词"}),l.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap",marginBottom:6},children:(o.keywords||[]).map((W,ne)=>l.jsxs("span",{className:"sub-kw",style:{fontSize:11,padding:"2px 8px",borderRadius:4,background:"var(--bg)",border:"1px solid var(--line)"},children:[W,l.jsx("span",{style:{cursor:"pointer",marginLeft:4,color:"var(--danger)"},onClick:()=>S(ne),children:"✕"})]},ne))}),l.jsxs("div",{style:{display:"flex",gap:6},children:[l.jsx("input",{type:"text",value:_,onChange:W=>T(W.target.value),placeholder:"输入关键词",onKeyDown:W=>{W.key==="Enter"&&(y(_.trim()),T(""))},style:{flex:1,padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}}),l.jsx("button",{className:"btn btn-g",onClick:()=>{y(_.trim()),T("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"自定义信息源"}),(o.custom_feeds||[]).map((W,ne)=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:4,fontSize:11},children:[l.jsx("span",{style:{fontWeight:600},children:W.name}),l.jsx("span",{style:{color:"var(--muted)",flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:W.url}),l.jsx("span",{style:{color:"var(--acc)"},children:W.category}),l.jsx("span",{style:{cursor:"pointer",color:"var(--danger)"},onClick:()=>F(ne),children:"✕"})]},ne)),l.jsxs("div",{style:{display:"flex",gap:6,marginTop:6},children:[l.jsx("input",{placeholder:"源名称",value:f,onChange:W=>w(W.target.value),style:{width:100,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("input",{placeholder:"RSS / URL",value:m,onChange:W=>P(W.target.value),style:{flex:1,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("select",{value:L,onChange:W=>U(W.target.value),style:{padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"},children:B.map(W=>l.jsx("option",{value:W,children:W},W))}),l.jsx("button",{className:"btn btn-g",onClick:()=>{I(f,m,L),w(""),P("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:6},children:"飞书 Webhook"}),l.jsx("input",{type:"text",value:o.feishu_webhook||"",onChange:W=>b(W.target.value),placeholder:"https://open.feishu.cn/open-apis/bot/v2/hook/...",style:{width:"100%",padding:"8px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsx("div",{style:{display:"flex",justifyContent:"flex-end"},children:l.jsx("button",{className:"tpl-go",onClick:k,style:{fontSize:12,padding:"6px 16px"},children:"💾 保存配置"})})]})}const mc={main:"太子",zhongshu:"中书省",menxia:"门下省",shangshu:"尚书省",libu:"礼部",hubu:"户部",bingbu:"兵部",xingbu:"刑部",gongbu:"工部",libu_hr:"吏部",zaochao:"钦天监"},pp={Taizi:"中书省起草",Zhongshu:"门下省审议",Menxia:"尚书省派发",Assigned:"开始执行",Doing:"进入审查",Review:"完成"};function oc(o){const h=Math.max(0,o);if(h<60)return`${h}秒`;if(h<3600)return`${Math.floor(h/60)}分${h%60}秒`;const u=Math.floor(h/3600),y=Math.floor(h%3600/60);return`${u}小时${y}分`}function eo(o){if(!o)return"";if(typeof o=="number"){const h=new Date(o);return`${String(h.getHours()).padStart(2,"0")}:${String(h.getMinutes()).padStart(2,"0")}:${String(h.getSeconds()).padStart(2,"0")}`}return typeof o=="string"&&o.length>=19?o.substring(11,19):String(o).substring(0,8)}function mp(){var H,le,Q,g,R,me,ve;const o=se(d=>d.modalTaskId),h=se(d=>d.setModalTaskId),u=se(d=>d.liveStatus),y=se(d=>d.loadAll),S=se(d=>d.toast),[I,F]=Z.useState(null),[k,b]=Z.useState(null),_=Z.useRef(null),T=Z.useRef(null),f=((H=u==null?void 0:u.tasks)==null?void 0:H.find(d=>d.id===o))||null,w=Z.useCallback(async()=>{if(o)try{const d=await he.taskActivity(o);F(d)}catch{F(null)}},[o]),m=Z.useCallback(async()=>{if(o)try{const d=await he.schedulerState(o);b(d)}catch{b(null)}},[o]);if(Z.useEffect(()=>!o||!f?void 0:(w(),m(),["Done","Cancelled"].includes(f.state)||(_.current=setInterval(()=>{w(),m()},4e3)),()=>{_.current&&(clearInterval(_.current),_.current=null)}),[o,f==null?void 0:f.state,w,m]),Z.useEffect(()=>{T.current&&(T.current.scrollTop=T.current.scrollHeight)},[(le=I==null?void 0:I.activity)==null?void 0:le.length]),!o||!f)return null;const P=()=>h(null),L=ro(f),U=L.find(d=>d.status==="active"),B=f.heartbeat||{status:"unknown",label:"⚪ 无数据"},W=f.flow_log||[],ne=f.todos||[],E=ne.filter(d=>d.status==="completed").length,fe=ne.length,oe=!["Done","Blocked","Cancelled"].includes(f.state),pe=["Blocked","Cancelled"].includes(f.state),z=async(d,M)=>{try{const V=await he.taskAction(f.id,d,M);V.ok?(S(V.message||"操作成功","ok"),y(),P()):S(V.error||"操作失败","err")}catch{S("服务器连接失败","err")}},Ee=async d=>{const M={approve:"准奏",reject:"封驳"},V=prompt(`${M[d]} ${f.id} + +请输入批注(可留空):`);if(V!==null)try{const ye=await he.reviewAction(f.id,d,V||"");ye.ok?(S(`✅ ${f.id} 已${M[d]}`,"ok"),y(),P()):S(ye.error||"操作失败","err")}catch{S("服务器连接失败","err")}},J=async()=>{const d=pp[f.state]||"下一步",M=prompt(`⏩ 手动推进 ${f.id} +当前: ${f.state} → 下一步: ${d} + +请输入说明(可留空):`);if(M!==null)try{const V=await he.advanceState(f.id,M||"");V.ok?(S(`⏩ ${V.message}`,"ok"),y(),P()):S(V.error||"推进失败","err")}catch{S("服务器连接失败","err")}},re=async d=>{if(d==="scan"){try{const ge=await he.schedulerScan(180);ge.ok?S(`🔍 扫描完成:${ge.count||0} 个动作`,"ok"):S(ge.error||"扫描失败","err"),m()}catch{S("服务器连接失败","err")}return}const V=prompt(`请输入${{retry:"重试",escalate:"升级",rollback:"回滚"}[d]}原因(可留空):`);if(V===null)return;const ye={retry:he.schedulerRetry,escalate:he.schedulerEscalate,rollback:he.schedulerRollback};try{const ge=await ye[d](f.id,V);ge.ok?S(ge.message||"操作成功","ok"):S(ge.error||"操作失败","err"),m(),y()}catch{S("服务器连接失败","err")}},je=()=>{const d=prompt("请输入叫停原因(可留空):");d!==null&&z("stop",d)},ae=()=>{if(!confirm(`确定要取消 ${f.id} 吗?`))return;const d=prompt("请输入取消原因(可留空):");d!==null&&z("cancel",d)},G=k==null?void 0:k.scheduler,_e=(k==null?void 0:k.stalledSec)||0,ze=(k==null?void 0:k.stateAgeSec)||0,ke=(k==null?void 0:k.decision)||(G==null?void 0:G.decisionPacket)||null;return l.jsx("div",{className:"modal-bg open",onClick:P,children:l.jsxs("div",{className:"modal",onClick:d=>d.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:P,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{className:"modal-id",children:f.id}),l.jsx("div",{className:"modal-title",children:f.title||"(无标题)"}),U&&l.jsxs("div",{className:"cur-stage",children:[l.jsx("div",{className:"cs-icon",children:U.icon}),l.jsxs("div",{className:"cs-info",children:[l.jsx("div",{className:"cs-dept",style:{color:Jl(U.dept)},children:U.dept}),l.jsxs("div",{className:"cs-action",children:["当前阶段:",U.action]})]}),l.jsx("span",{className:`hb ${B.status} cs-hb`,children:B.label})]}),l.jsx("div",{className:"m-pipe",children:L.map((d,M)=>l.jsxs("div",{className:"mp-stage",children:[l.jsxs("div",{className:`mp-node ${d.status}`,children:[d.status==="done"&&l.jsx("div",{className:"mp-done-tick",children:"✓"}),l.jsx("div",{className:"mp-icon",children:d.icon}),l.jsx("div",{className:"mp-dept",style:d.status==="active"?{color:"var(--acc)"}:d.status==="done"?{color:"var(--ok)"}:{},children:d.dept}),l.jsx("div",{className:"mp-action",children:d.action})]}),Mz("resume","恢复执行"),children:"▶️ 恢复执行"}),["Review","Menxia"].includes(f.state)&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"btn-action",style:{background:"#2ecc8a22",color:"#2ecc8a",border:"1px solid #2ecc8a44"},onClick:()=>Ee("approve"),children:"✅ 准奏"}),l.jsx("button",{className:"btn-action",style:{background:"#ff527022",color:"#ff5270",border:"1px solid #ff527044"},onClick:()=>Ee("reject"),children:"🚫 封驳"})]}),["Pending","Taizi","Zhongshu","Menxia","Assigned","Doing","Review","Next"].includes(f.state)&&l.jsx("button",{className:"btn-action",style:{background:"#7c5cfc18",color:"#7c5cfc",border:"1px solid #7c5cfc44"},onClick:J,children:"⏩ 推进到下一步"})]}),l.jsxs("div",{className:"sched-section",children:[l.jsxs("div",{className:"sched-head",children:[l.jsx("span",{className:"sched-title",children:"🧭 太子调度"}),l.jsx("span",{className:"sched-status",children:G?`${G.enabled===!1?"已禁用":"运行中"} · 阈值 ${G.stallThresholdSec||180}s`:"加载中..."})]}),l.jsxs("div",{className:"sched-grid",children:[l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"停滞时长"}),l.jsx("div",{className:"v",children:oc(_e)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"状态驻留"}),l.jsx("div",{className:"v",children:oc(ze)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"重试次数"}),l.jsx("div",{className:"v",children:(G==null?void 0:G.retryCount)||0})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"升级级别"}),l.jsx("div",{className:"v",children:G!=null&&G.escalationLevel?G.escalationLevel===1?"门下省":"尚书省":"无"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"派发状态"}),l.jsx("div",{className:"v",children:(G==null?void 0:G.lastDispatchStatus)||"idle"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"控制态"}),l.jsx("div",{className:"v",children:(k==null?void 0:k.controlState)||(G==null?void 0:G.controlState)||"-"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"写回状态"}),l.jsx("div",{className:"v",children:((Q=k==null?void 0:k.writeback)==null?void 0:Q.status)||((g=G==null?void 0:G.writeback)==null?void 0:g.status)||"idle"})]})]}),ke?l.jsxs("div",{className:"sched-line",style:{marginTop:10,display:"block",border:"1px solid #ffb74d44",borderRadius:10,padding:"10px 12px",background:"#ffb74d12"},children:[l.jsx("div",{style:{color:"#ffb74d",fontWeight:700,marginBottom:6},children:"🧾 拍板事项"}),l.jsx("div",{style:{marginBottom:8},children:ke.question}),(ke.options||[]).map((d,M)=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("strong",{children:[M+1,". ",d.label,ke.recommended===d.id?"(推荐)":""]}),l.jsx("div",{style:{color:"var(--muted)",fontSize:12},children:d.impact})]},`${d.id}-${M}`)),(ke.evidence||[]).length>0&&l.jsx("div",{style:{marginTop:8,color:"var(--muted)",fontSize:12},children:(ke.evidence||[]).slice(0,4).map((d,M)=>l.jsxs("div",{children:["- ",d]},M))})]}):l.jsx("div",{className:"sched-line",style:{marginTop:10,display:"block",border:"1px dashed #8a94a64d",borderRadius:10,padding:"8px 12px",color:"var(--muted)"},children:"当前无拍板事项(仅在门下省/审查节点停滞时触发)"}),G&&l.jsxs("div",{className:"sched-line",children:[G.lastProgressAt&&l.jsxs("span",{children:["最近进展 ",(G.lastProgressAt||"").replace("T"," ").substring(0,19)]}),G.lastDispatchAt&&l.jsxs("span",{children:["最近派发 ",(G.lastDispatchAt||"").replace("T"," ").substring(0,19)]}),l.jsxs("span",{children:["自动回滚 ",G.autoRollback===!1?"关闭":"开启"]}),G.lastDispatchAgent&&l.jsxs("span",{children:["目标 ",G.lastDispatchAgent]}),((R=k==null?void 0:k.lease)==null?void 0:R.ownerRunId)&&l.jsxs("span",{children:["租约 ",k.lease.ownerRunId]}),((me=k==null?void 0:k.lastAction)==null?void 0:me.reasonCode)&&l.jsxs("span",{children:["原因码 ",k.lastAction.reasonCode]}),((ve=k==null?void 0:k.writeback)==null?void 0:ve.lastError)&&l.jsxs("span",{children:["写回错误 ",k.writeback.lastError]})]}),l.jsxs("div",{className:"sched-actions",children:[l.jsx("button",{className:"sched-btn",onClick:()=>re("retry"),children:"🔁 重试派发"}),l.jsx("button",{className:"sched-btn warn",onClick:()=>re("escalate"),children:"📣 升级协调"}),l.jsx("button",{className:"sched-btn danger",onClick:()=>re("rollback"),children:"↩️ 回滚稳定点"}),l.jsx("button",{className:"sched-btn",onClick:()=>re("scan"),children:"🔍 立即扫描"})]})]}),fe>0&&l.jsx(hp,{todos:ne,todoDone:E,todoTotal:fe}),l.jsx("div",{className:"m-section",children:l.jsxs("div",{className:"m-rows",children:[l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"状态"}),l.jsxs("div",{className:"mr-val",children:[l.jsx("span",{className:`tag st-${f.state}`,children:no(f)}),(f.review_round||0)>0&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:8},children:["共磋商 ",f.review_round," 轮"]})]})]}),l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"执行部门"}),l.jsx("div",{className:"mr-val",children:l.jsx("span",{className:`tag dt-${(f.org||"").replace(/\s/g,"")}`,children:f.org||"—"})})]}),f.eta&&f.eta!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"预计完成"}),l.jsx("div",{className:"mr-val",children:f.eta})]}),f.block&&f.block!=="无"&&f.block!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",style:{color:"var(--danger)"},children:"阻塞项"}),l.jsx("div",{className:"mr-val",style:{color:"var(--danger)"},children:f.block})]}),f.now&&f.now!=="-"&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"当前进展"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:f.now})]}),f.ac&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"验收标准"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:f.ac})]})]})}),W.length>0&&l.jsxs("div",{className:"m-section",children:[l.jsxs("div",{className:"m-sec-label",children:["流转日志(",W.length," 条)"]}),l.jsx("div",{className:"fl-timeline",children:W.map((d,M)=>{const V=Jl(d.from||"");return l.jsxs("div",{className:"fl-item",children:[l.jsx("div",{className:"fl-time",children:d.at?d.at.substring(11,16):""}),l.jsx("div",{className:"fl-dot",style:{background:V}}),l.jsxs("div",{className:"fl-content",children:[l.jsxs("div",{className:"fl-who",children:[l.jsx("span",{className:"from",style:{color:V},children:d.from}),l.jsx("span",{style:{color:"var(--muted)"},children:" → "}),l.jsx("span",{className:"to",style:{color:Jl(d.to||"")},children:d.to})]}),l.jsx("div",{className:"fl-rem",children:d.remark})]})]},M)})})]}),f.output&&f.output!=="-"&&f.output!==""&&l.jsxs("div",{className:"m-section",children:[l.jsx("div",{className:"m-sec-label",children:"产出物"}),l.jsx("code",{children:f.output})]}),l.jsx(vp,{data:I,isDone:["Done","Cancelled"].includes(f.state),logRef:T})]})]})})}function hp({todos:o,todoDone:h,todoTotal:u}){return l.jsxs("div",{className:"todo-section",children:[l.jsxs("div",{className:"todo-header",children:[l.jsxs("div",{className:"m-sec-label",style:{marginBottom:0,border:"none",padding:0},children:["子任务清单(",h,"/",u,")"]}),l.jsxs("div",{className:"todo-progress",children:[l.jsx("div",{className:"todo-bar",children:l.jsx("div",{className:"todo-bar-fill",style:{width:`${Math.round(h/u*100)}%`}})}),l.jsxs("span",{children:[Math.round(h/u*100),"%"]})]})]}),l.jsx("div",{className:"todo-list",children:o.map(y=>{const S=y.status==="completed"?"✅":y.status==="in-progress"?"🔄":"⬜",I=y.status==="completed"?"已完成":y.status==="in-progress"?"进行中":"待开始",F=y.status==="completed"?"s-done":y.status==="in-progress"?"s-progress":"s-notstarted",k=y.status==="completed"?"done":"";return l.jsxs("div",{className:`todo-item ${k}`,children:[l.jsxs("div",{className:"t-row",children:[l.jsx("span",{className:"t-icon",children:S}),l.jsxs("span",{className:"t-id",children:["#",y.id]}),l.jsx("span",{className:"t-title",children:y.title}),l.jsx("span",{className:`t-status ${F}`,children:I})]}),y.detail&&l.jsx("div",{className:"todo-detail",children:y.detail})]},y.id)})})]})}function vp({data:o,isDone:h,logRef:u}){if(!o)return null;const y=o.activity||[],S=(()=>{if(!y.length)return!1;const P=y[y.length-1];if(!P.at)return!1;const L=typeof P.at=="number"?P.at:new Date(P.at).getTime();return Date.now()-L<3e5})(),I=[];o.agentLabel&&I.push(o.agentLabel),o.relatedAgents&&o.relatedAgents.length>1&&I.push(`${o.relatedAgents.length}个 Agent`),o.lastActive&&I.push(`最后活跃: ${o.lastActive}`);const F=o.phaseDurations||[],k=Math.max(...F.map(P=>P.durationSec||1),1),b={皇上:"#eab308",太子:"#f97316",中书省:"#3b82f6",门下省:"#8b5cf6",尚书省:"#10b981",六部:"#06b6d4",礼部:"#ec4899",户部:"#f59e0b",兵部:"#ef4444",刑部:"#6366f1",工部:"#14b8a6",吏部:"#d946ef"},_=o.todosSummary,T=o.resourceSummary,f=y.filter(P=>P.kind==="flow"),w=y.filter(P=>P.kind!=="flow"),m=new Map;return w.forEach(P=>{const L=P.agent||"unknown";m.has(L)||m.set(L,[]),m.get(L).push(P)}),l.jsxs("div",{className:"la-section",children:[l.jsxs("div",{className:"la-header",children:[l.jsxs("span",{className:"la-title",children:[l.jsx("span",{className:`la-dot${S?"":" idle"}`}),h?"执行回顾":"实时动态"]}),l.jsx("span",{className:"la-agent",children:I.join(" · ")||"加载中..."})]}),F.length>0&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:6},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"⏱ 阶段耗时"}),o.totalDuration&&l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["总耗时 ",o.totalDuration]})]}),F.map((P,L)=>{const U=Math.max(5,Math.round((P.durationSec||1)/k*100)),B=b[P.phase]||"#6b7280";return l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,margin:"2px 0",fontSize:11},children:[l.jsx("span",{style:{minWidth:48,color:"var(--muted)",textAlign:"right"},children:P.phase}),l.jsx("div",{style:{flex:1,height:14,background:"var(--panel)",borderRadius:3,overflow:"hidden"},children:l.jsx("div",{style:{width:`${U}%`,height:"100%",background:B,borderRadius:3,opacity:P.ongoing?.6:.85}})}),l.jsxs("span",{style:{minWidth:60,fontSize:10,color:"var(--muted)"},children:[P.durationText,P.ongoing&&l.jsx("span",{style:{fontSize:9,color:"#60a5fa"},children:" ●进行中"})]})]},L)})]}),_&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📊 执行进度"}),l.jsxs("span",{style:{fontSize:20,fontWeight:700,color:_.percent>=100?"#22c55e":_.percent>=50?"#60a5fa":"var(--text)"},children:[_.percent,"%"]}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["✅",_.completed," 🔄",_.inProgress," ⬜",_.notStarted," / 共",_.total,"项"]})]}),l.jsxs("div",{style:{height:8,background:"var(--panel)",borderRadius:4,overflow:"hidden",display:"flex"},children:[l.jsx("div",{style:{width:`${_.total?_.completed/_.total*100:0}%`,background:"#22c55e",transition:"width .3s"}}),l.jsx("div",{style:{width:`${_.total?_.inProgress/_.total*100:0}%`,background:"#3b82f6",transition:"width .3s"}})]})]}),T&&(T.totalTokens||T.totalCost)&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)",display:"flex",gap:12,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📈 资源消耗"}),T.totalTokens!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["🔢 ",T.totalTokens.toLocaleString()," tokens"]}),T.totalCost!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["💰 $",T.totalCost.toFixed(4)]}),T.totalElapsedSec!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⏳ ",T.totalElapsedSec>=60?`${Math.floor(T.totalElapsedSec/60)}分`:"",T.totalElapsedSec%60,"秒"]})]}),l.jsxs("div",{className:"la-log",ref:u,children:[f.length>0&&l.jsx("div",{className:"la-flow-wrap",children:f.map((P,L)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"📋"}),l.jsxs("span",{className:"la-body",children:[l.jsx("b",{children:P.from})," → ",l.jsx("b",{children:P.to})," ",P.remark||""]}),l.jsx("span",{className:"la-time",children:eo(P.at)})]},`flow-${L}`))}),m.size>0?l.jsx("div",{className:"la-groups",children:Array.from(m.entries()).map(([P,L])=>{const U=mc[P]||P||"未标识",B=L[L.length-1],W=B!=null&&B.at?eo(B.at):"--:--:--";return l.jsxs("div",{className:"la-group",children:[l.jsxs("div",{className:"la-group-hd",children:[l.jsx("span",{className:"name",children:U}),l.jsxs("span",{children:["最近更新 ",W]})]}),l.jsx("div",{className:"la-group-bd",children:L.map((ne,E)=>l.jsx(gp,{entry:ne},E))})]},P)})}):!f.length&&l.jsx("div",{className:"la-empty",children:o.message||o.error||"Agent 尚未上报进展(等待 Agent 调用 progress 命令)"})]})]})}function gp({entry:o}){var y,S,I;const h=eo(o.at),u=o.agent?l.jsx("span",{style:{fontSize:9,color:"var(--muted)",background:"var(--panel)",padding:"1px 4px",borderRadius:3,marginRight:4},children:mc[o.agent]||o.agent}):null;if(o.kind==="progress")return l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🔄"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("b",{children:"当前进展:"}),o.text]}),l.jsx("span",{className:"la-time",children:h})]});if(o.kind==="todos"){const F=o.items||[],k=new Map;return o.diff&&((o.diff.changed||[]).forEach(b=>k.set(b.id,{type:"changed",from:b.from,to:b.to})),(o.diff.added||[]).forEach(b=>k.set(b.id,{type:"added"}))),l.jsxs("div",{className:"la-entry",style:{flexDirection:"column",alignItems:"flex-start",gap:2},children:[l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginBottom:2},children:[u,"📝 执行计划"]}),F.map(b=>{const _=b.status==="completed"?"✅":b.status==="in-progress"?"🔄":"⬜",T=k.get(String(b.id)),f=b.status==="completed"?{opacity:.5,textDecoration:"line-through"}:b.status==="in-progress"?{color:"#60a5fa",fontWeight:"bold"}:{};return l.jsxs("div",{style:f,children:[_," ",b.title,T&&T.type==="changed"&&T.to==="completed"&&l.jsx("span",{style:{color:"#22c55e",fontSize:9,marginLeft:4},children:"✨刚完成"}),T&&T.type==="changed"&&T.to!=="completed"&&l.jsxs("span",{style:{color:"#f59e0b",fontSize:9,marginLeft:4},children:["↻",T.from,"→",T.to]}),T&&T.type==="added"&&l.jsx("span",{style:{color:"#3b82f6",fontSize:9,marginLeft:4},children:"🆕新增"})]},b.id)}),(S=(y=o.diff)==null?void 0:y.removed)==null?void 0:S.map(b=>l.jsxs("div",{style:{opacity:.4,textDecoration:"line-through"},children:["🗑 ",b.title]},b.id))]})}if(o.kind==="assistant")return l.jsxs(l.Fragment,{children:[o.thinking&&l.jsxs("div",{className:"la-entry la-thinking",children:[l.jsx("span",{className:"la-icon",children:"💭"}),l.jsxs("span",{className:"la-body",children:[u,o.thinking]}),l.jsx("span",{className:"la-time",children:h})]}),(I=o.tools)==null?void 0:I.map((F,k)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"🔧"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:F.name}),l.jsx("span",{className:"la-trunc",children:F.input_preview||""})]}),l.jsx("span",{className:"la-time",children:h})]},k)),o.text&&l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🤖"}),l.jsxs("span",{className:"la-body",children:[u,o.text]}),l.jsx("span",{className:"la-time",children:h})]})]});if(o.kind==="tool_result"){const F=o.exitCode===0||o.exitCode===null||o.exitCode===void 0;return l.jsxs("div",{className:`la-entry la-tool-result ${F?"ok":"err"}`,children:[l.jsx("span",{className:"la-icon",children:F?"✅":"❌"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:o.tool||""}),o.output?o.output.substring(0,150):""]}),l.jsx("span",{className:"la-time",children:h})]})}return o.kind==="user"?l.jsxs("div",{className:"la-entry la-user",children:[l.jsx("span",{className:"la-icon",children:"📥"}),l.jsxs("span",{className:"la-body",children:[u,o.text||""]}),l.jsx("span",{className:"la-time",children:h})]}):null}function yp(){const o=se(h=>h.toasts);return o.length?l.jsx("div",{className:"toaster",children:o.map(h=>l.jsx("div",{className:`toast ${h.type}`,children:h.msg},h.id))}):null}function xp(){const o=se(P=>P.liveStatus),[h,u]=Z.useState(!1),[y,S]=Z.useState(!1);Z.useEffect(()=>{const P=localStorage.getItem("openclaw_court_date"),L=new Date().toISOString().substring(0,10);if(!JSON.parse(localStorage.getItem("openclaw_court_pref")||'{"enabled":true}').enabled||P===L)return;localStorage.setItem("openclaw_court_date",L),u(!0);const B=setTimeout(()=>I(),3500);return()=>clearTimeout(B)},[]);const I=()=>{S(!0),setTimeout(()=>u(!1),500)};if(!h)return null;const k=((o==null?void 0:o.tasks)||[]).filter(un),b=k.filter(P=>!["Done","Cancelled"].includes(P.state)).length,_=k.filter(P=>P.state==="Done").length,T=k.filter(P=>P.state!=="Done"&&P.state!=="Cancelled"&&P.eta&&new Date(P.eta.replace(" ","T"))0&&` · ⚠ 超期 ${T} 件`]}),l.jsx("div",{className:"crm-date in",children:m}),l.jsx("div",{className:"crm-skip",children:"点击任意处跳过"})]})}function kp(){const o=se(f=>f.activeTab),h=se(f=>f.setActiveTab),u=se(f=>f.liveStatus),y=se(f=>f.countdown),S=se(f=>f.loadAll);Z.useEffect(()=>(Wf(),()=>Uf()),[]);const I=(u==null?void 0:u.tasks)||[],F=I.filter(un),k=F.filter(f=>!ts(f)),b=u==null?void 0:u.syncStatus,_=b==null?void 0:b.ok,T=f=>f==="edicts"?String(k.length):f==="sessions"?String(I.filter(w=>!un(w)).length):f==="memorials"?String(F.filter(w=>["Done","Cancelled"].includes(w.state)).length):f==="monitor"?I.filter(m=>un(m)&&m.state==="Doing").length+"活跃":"";return l.jsxs("div",{className:"wrap",children:[l.jsxs("div",{className:"hdr",children:[l.jsxs("div",{children:[l.jsx("div",{className:"logo",children:"三省六部 · 总控台"}),l.jsx("div",{className:"sub-text",children:"OpenClaw Sansheng-Liubu Dashboard"})]}),l.jsxs("div",{className:"hdr-r",children:[l.jsx("span",{className:`chip ${_?"ok":_===!1?"err":""}`,children:_?"✅ 同步正常":_===!1?"❌ 服务器未启动":"⏳ 连接中…"}),l.jsxs("span",{className:"chip",children:[k.length," 道旨意"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>S(),children:"⟳ 刷新"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⟳ ",y,"s"]})]})]}),l.jsx("div",{className:"tabs",children:Af.map(f=>l.jsxs("div",{className:`tab ${o===f.key?"active":""}`,onClick:()=>h(f.key),children:[f.icon," ",f.label,T(f.key)&&l.jsx("span",{className:"tbadge",children:T(f.key)})]},f.key))}),o==="edicts"&&l.jsx(Kf,{}),o==="monitor"&&l.jsx(Gf,{}),o==="officials"&&l.jsx(Xf,{}),o==="models"&&l.jsx(Jf,{}),o==="skills"&&l.jsx(tp,{}),o==="sessions"&&l.jsx(lp,{}),o==="memorials"&&l.jsx(ip,{}),o==="templates"&&l.jsx(cp,{}),o==="morning"&&l.jsx(dp,{}),l.jsx(mp,{}),l.jsx(yp,{}),l.jsx(xp,{})]})}jf.createRoot(document.getElementById("root")).render(l.jsx(ac.StrictMode,{children:l.jsx(kp,{})})); diff --git a/dashboard/dist/assets/index-CqMbmm5B.js b/dashboard/dist/assets/index-CqMbmm5B.js deleted file mode 100644 index d0a0215b..00000000 --- a/dashboard/dist/assets/index-CqMbmm5B.js +++ /dev/null @@ -1,84 +0,0 @@ -(function(){const p=document.createElement("link").relList;if(p&&p.supports&&p.supports("modulepreload"))return;for(const S of document.querySelectorAll('link[rel="modulepreload"]'))k(S);new MutationObserver(S=>{for(const D of S)if(D.type==="childList")for(const F of D.addedNodes)F.tagName==="LINK"&&F.rel==="modulepreload"&&k(F)}).observe(document,{childList:!0,subtree:!0});function u(S){const D={};return S.integrity&&(D.integrity=S.integrity),S.referrerPolicy&&(D.referrerPolicy=S.referrerPolicy),S.crossOrigin==="use-credentials"?D.credentials="include":S.crossOrigin==="anonymous"?D.credentials="omit":D.credentials="same-origin",D}function k(S){if(S.ep)return;S.ep=!0;const D=u(S);fetch(S.href,D)}})();function Qi(o){return o&&o.__esModule&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o}var Mi={exports:{}},Cr={},Ai={exports:{}},de={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Hu;function df(){if(Hu)return de;Hu=1;var o=Symbol.for("react.element"),p=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),k=Symbol.for("react.strict_mode"),S=Symbol.for("react.profiler"),D=Symbol.for("react.provider"),F=Symbol.for("react.context"),_=Symbol.for("react.forward_ref"),R=Symbol.for("react.suspense"),C=Symbol.for("react.memo"),z=Symbol.for("react.lazy"),d=Symbol.iterator;function w(v){return v===null||typeof v!="object"?null:(v=d&&v[d]||v["@@iterator"],typeof v=="function"?v:null)}var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,g={};function M(v,L,ae){this.props=v,this.context=L,this.refs=g,this.updater=ae||m}M.prototype.isReactComponent={},M.prototype.setState=function(v,L){if(typeof v!="object"&&typeof v!="function"&&v!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,v,L,"setState")},M.prototype.forceUpdate=function(v){this.updater.enqueueForceUpdate(this,v,"forceUpdate")};function P(){}P.prototype=M.prototype;function B(v,L,ae){this.props=v,this.context=L,this.refs=g,this.updater=ae||m}var Z=B.prototype=new P;Z.constructor=B,T(Z,M.prototype),Z.isPureReactComponent=!0;var I=Array.isArray,oe=Object.prototype.hasOwnProperty,ue={current:null},pe={key:!0,ref:!0,__self:!0,__source:!0};function me(v,L,ae){var ce,x={},U=null,ee=null;if(L!=null)for(ce in L.ref!==void 0&&(ee=L.ref),L.key!==void 0&&(U=""+L.key),L)oe.call(L,ce)&&!pe.hasOwnProperty(ce)&&(x[ce]=L[ce]);var he=arguments.length-2;if(he===1)x.children=ae;else if(1>>1,L=E[v];if(0>>1;vS(x,W))US(ee,x)?(E[v]=ee,E[U]=W,v=U):(E[v]=x,E[ce]=W,v=ce);else if(US(ee,W))E[v]=ee,E[U]=W,v=U;else break e}}return Q}function S(E,Q){var W=E.sortIndex-Q.sortIndex;return W!==0?W:E.id-Q.id}if(typeof performance=="object"&&typeof performance.now=="function"){var D=performance;o.unstable_now=function(){return D.now()}}else{var F=Date,_=F.now();o.unstable_now=function(){return F.now()-_}}var R=[],C=[],z=1,d=null,w=3,m=!1,T=!1,g=!1,M=typeof setTimeout=="function"?setTimeout:null,P=typeof clearTimeout=="function"?clearTimeout:null,B=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Z(E){for(var Q=u(C);Q!==null;){if(Q.callback===null)k(C);else if(Q.startTime<=E)k(C),Q.sortIndex=Q.expirationTime,p(R,Q);else break;Q=u(C)}}function I(E){if(g=!1,Z(E),!T)if(u(R)!==null)T=!0,_e(oe);else{var Q=u(C);Q!==null&&xe(I,Q.startTime-E)}}function oe(E,Q){T=!1,g&&(g=!1,P(me),me=-1),m=!0;var W=w;try{for(Z(Q),d=u(R);d!==null&&(!(d.expirationTime>Q)||E&&!le());){var v=d.callback;if(typeof v=="function"){d.callback=null,w=d.priorityLevel;var L=v(d.expirationTime<=Q);Q=o.unstable_now(),typeof L=="function"?d.callback=L:d===u(R)&&k(R),Z(Q)}else k(R);d=u(R)}if(d!==null)var ae=!0;else{var ce=u(C);ce!==null&&xe(I,ce.startTime-Q),ae=!1}return ae}finally{d=null,w=W,m=!1}}var ue=!1,pe=null,me=-1,be=5,X=-1;function le(){return!(o.unstable_now()-XE||125v?(E.sortIndex=W,p(C,E),u(R)===null&&E===u(C)&&(g?(P(me),me=-1):g=!0,xe(I,W-v))):(E.sortIndex=L,p(R,E),T||m||(T=!0,_e(oe))),E},o.unstable_shouldYield=le,o.unstable_wrapCallback=function(E){var Q=w;return function(){var W=w;w=Q;try{return E.apply(this,arguments)}finally{w=W}}}})($i)),$i}var Yu;function hf(){return Yu||(Yu=1,bi.exports=mf()),bi.exports}/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Xu;function vf(){if(Xu)return nt;Xu=1;var o=_r(),p=hf();function u(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),R=Object.prototype.hasOwnProperty,C=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,z={},d={};function w(e){return R.call(d,e)?!0:R.call(z,e)?!1:C.test(e)?d[e]=!0:(z[e]=!0,!1)}function m(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function T(e,t,n,r){if(t===null||typeof t>"u"||m(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function g(e,t,n,r,s,i,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=s,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=a}var M={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){M[e]=new g(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];M[t]=new g(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){M[e]=new g(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){M[e]=new g(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){M[e]=new g(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){M[e]=new g(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){M[e]=new g(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){M[e]=new g(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){M[e]=new g(e,5,!1,e.toLowerCase(),null,!1,!1)});var P=/[\-:]([a-z])/g;function B(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(P,B);M[t]=new g(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){M[e]=new g(e,1,!1,e.toLowerCase(),null,!1,!1)}),M.xlinkHref=new g("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){M[e]=new g(e,1,!1,e.toLowerCase(),null,!0,!0)});function Z(e,t,n,r){var s=M.hasOwnProperty(t)?M[t]:null;(s!==null?s.type!==0:r||!(2c||s[a]!==i[c]){var f=` -`+s[a].replace(" at new "," at ");return e.displayName&&f.includes("")&&(f=f.replace("",e.displayName)),f}while(1<=a&&0<=c);break}}}finally{ae=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?L(e):""}function x(e){switch(e.tag){case 5:return L(e.type);case 16:return L("Lazy");case 13:return L("Suspense");case 19:return L("SuspenseList");case 0:case 2:case 15:return e=ce(e.type,!1),e;case 11:return e=ce(e.type.render,!1),e;case 1:return e=ce(e.type,!0),e;default:return""}}function U(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case pe:return"Fragment";case ue:return"Portal";case be:return"Profiler";case me:return"StrictMode";case se:return"Suspense";case J:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case le:return(e.displayName||"Context")+".Consumer";case X:return(e._context.displayName||"Context")+".Provider";case ke:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Ne:return t=e.displayName||null,t!==null?t:U(e.type)||"Memo";case _e:t=e._payload,e=e._init;try{return U(e(t))}catch{}}return null}function ee(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return U(t);case 8:return t===me?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function he(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function ve(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Me(e){var t=ve(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var s=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return s.call(this)},set:function(a){r=""+a,i.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function mn(e){e._valueTracker||(e._valueTracker=Me(e))}function Yi(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=ve(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function Tr(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Wl(e,t){var n=t.checked;return W({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function Xi(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=he(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Zi(e,t){t=t.checked,t!=null&&Z(e,"checked",t,!1)}function Ul(e,t){Zi(e,t);var n=he(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Hl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Hl(e,t.type,he(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function qi(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Hl(e,t,n){(t!=="number"||Tr(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Fn=Array.isArray;function hn(e,t,n,r){if(e=e.options,t){t={};for(var s=0;s"+t.valueOf().toString()+"",t=Lr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Bn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Wn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},mc=["Webkit","ms","Moz","O"];Object.keys(Wn).forEach(function(e){mc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wn[t]=Wn[e]})});function lo(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Wn.hasOwnProperty(e)&&Wn[e]?(""+t).trim():t+"px"}function so(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,s=lo(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,s):e[n]=s}}var hc=W({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Kl(e,t){if(t){if(hc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(u(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(u(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(u(61))}if(t.style!=null&&typeof t.style!="object")throw Error(u(62))}}function Gl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Yl=null;function Xl(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Zl=null,vn=null,gn=null;function io(e){if(e=cr(e)){if(typeof Zl!="function")throw Error(u(280));var t=e.stateNode;t&&(t=Jr(t),Zl(e.stateNode,e.type,t))}}function oo(e){vn?gn?gn.push(e):gn=[e]:vn=e}function ao(){if(vn){var e=vn,t=gn;if(gn=vn=null,io(e),t)for(e=0;e>>=0,e===0?32:31-(Ec(e)/_c|0)|0}var Mr=64,Ar=4194304;function Qn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Or(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,s=e.suspendedLanes,i=e.pingedLanes,a=n&268435455;if(a!==0){var c=a&~s;c!==0?r=Qn(c):(i&=a,i!==0&&(r=Qn(i)))}else a=n&~s,a!==0?r=Qn(a):i!==0&&(r=Qn(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&s)===0&&(s=r&-r,i=t&-t,s>=i||s===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Kn(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-pt(t),e[t]=n}function Rc(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=tr),Oo=" ",bo=!1;function $o(e,t){switch(e){case"keyup":return sd.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Fo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var kn=!1;function od(e,t){switch(e){case"compositionend":return Fo(t);case"keypress":return t.which!==32?null:(bo=!0,Oo);case"textInput":return e=t.data,e===Oo&&bo?null:e;default:return null}}function ad(e,t){if(kn)return e==="compositionend"||!hs&&$o(e,t)?(e=Ro(),Wr=us=bt=null,kn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Ko(n)}}function Yo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Yo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(){for(var e=window,t=Tr();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Tr(e.document)}return t}function ys(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function gd(e){var t=Xo(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&Yo(n.ownerDocument.documentElement,n)){if(r!==null&&ys(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var s=n.textContent.length,i=Math.min(r.start,s);r=r.end===void 0?i:Math.min(r.end,s),!e.extend&&i>r&&(s=r,r=i,i=s),s=Go(n,i);var a=Go(n,r);s&&a&&(e.rangeCount!==1||e.anchorNode!==s.node||e.anchorOffset!==s.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(s.node,s.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,jn=null,xs=null,sr=null,ks=!1;function Zo(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;ks||jn==null||jn!==Tr(r)||(r=jn,"selectionStart"in r&&ys(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),sr&&lr(sr,r)||(sr=r,r=Xr(xs,"onSelect"),0En||(e.current=Ps[En],Ps[En]=null,En--)}function we(e,t){En++,Ps[En]=e.current,e.current=t}var Wt={},Ve=Bt(Wt),Ze=Bt(!1),nn=Wt;function _n(e,t){var n=e.type.contextTypes;if(!n)return Wt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var s={},i;for(i in n)s[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=s),s}function qe(e){return e=e.childContextTypes,e!=null}function el(){Ee(Ze),Ee(Ve)}function fa(e,t,n){if(Ve.current!==Wt)throw Error(u(168));we(Ve,t),we(Ze,n)}function pa(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var s in r)if(!(s in t))throw Error(u(108,ee(e)||"Unknown",s));return W({},n,r)}function tl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Wt,nn=Ve.current,we(Ve,e),we(Ze,Ze.current),!0}function ma(e,t,n){var r=e.stateNode;if(!r)throw Error(u(169));n?(e=pa(e,t,nn),r.__reactInternalMemoizedMergedChildContext=e,Ee(Ze),Ee(Ve),we(Ve,e)):Ee(Ze),we(Ze,n)}var _t=null,nl=!1,Is=!1;function ha(e){_t===null?_t=[e]:_t.push(e)}function Td(e){nl=!0,ha(e)}function Ut(){if(!Is&&_t!==null){Is=!0;var e=0,t=je;try{var n=_t;for(je=1;e>=a,s-=a,zt=1<<32-pt(t)+s|n<ie?(Be=re,re=null):Be=re.sibling;var ye=A(y,re,j[ie],$);if(ye===null){re===null&&(re=Be);break}e&&re&&ye.alternate===null&&t(y,re),h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye,re=Be}if(ie===j.length)return n(y,re),ze&&ln(y,ie),Y;if(re===null){for(;ieie?(Be=re,re=null):Be=re.sibling;var qt=A(y,re,ye.value,$);if(qt===null){re===null&&(re=Be);break}e&&re&&qt.alternate===null&&t(y,re),h=i(qt,h,ie),ne===null?Y=qt:ne.sibling=qt,ne=qt,re=Be}if(ye.done)return n(y,re),ze&&ln(y,ie),Y;if(re===null){for(;!ye.done;ie++,ye=j.next())ye=b(y,ye.value,$),ye!==null&&(h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye);return ze&&ln(y,ie),Y}for(re=r(y,re);!ye.done;ie++,ye=j.next())ye=H(re,y,ie,ye.value,$),ye!==null&&(e&&ye.alternate!==null&&re.delete(ye.key===null?ie:ye.key),h=i(ye,h,ie),ne===null?Y=ye:ne.sibling=ye,ne=ye);return e&&re.forEach(function(cf){return t(y,cf)}),ze&&ln(y,ie),Y}function Ie(y,h,j,$){if(typeof j=="object"&&j!==null&&j.type===pe&&j.key===null&&(j=j.props.children),typeof j=="object"&&j!==null){switch(j.$$typeof){case oe:e:{for(var Y=j.key,ne=h;ne!==null;){if(ne.key===Y){if(Y=j.type,Y===pe){if(ne.tag===7){n(y,ne.sibling),h=s(ne,j.props.children),h.return=y,y=h;break e}}else if(ne.elementType===Y||typeof Y=="object"&&Y!==null&&Y.$$typeof===_e&&ja(Y)===ne.type){n(y,ne.sibling),h=s(ne,j.props),h.ref=dr(y,ne,j),h.return=y,y=h;break e}n(y,ne);break}else t(y,ne);ne=ne.sibling}j.type===pe?(h=pn(j.props.children,y.mode,$,j.key),h.return=y,y=h):($=Ll(j.type,j.key,j.props,null,y.mode,$),$.ref=dr(y,h,j),$.return=y,y=$)}return a(y);case ue:e:{for(ne=j.key;h!==null;){if(h.key===ne)if(h.tag===4&&h.stateNode.containerInfo===j.containerInfo&&h.stateNode.implementation===j.implementation){n(y,h.sibling),h=s(h,j.children||[]),h.return=y,y=h;break e}else{n(y,h);break}else t(y,h);h=h.sibling}h=Li(j,y.mode,$),h.return=y,y=h}return a(y);case _e:return ne=j._init,Ie(y,h,ne(j._payload),$)}if(Fn(j))return K(y,h,j,$);if(Q(j))return G(y,h,j,$);il(y,j)}return typeof j=="string"&&j!==""||typeof j=="number"?(j=""+j,h!==null&&h.tag===6?(n(y,h.sibling),h=s(h,j),h.return=y,y=h):(n(y,h),h=Ti(j,y.mode,$),h.return=y,y=h),a(y)):n(y,h)}return Ie}var Rn=Sa(!0),wa=Sa(!1),ol=Bt(null),al=null,Pn=null,$s=null;function Fs(){$s=Pn=al=null}function Bs(e){var t=ol.current;Ee(ol),e._currentValue=t}function Ws(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function In(e,t){al=e,$s=Pn=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Je=!0),e.firstContext=null)}function ut(e){var t=e._currentValue;if($s!==e)if(e={context:e,memoizedValue:t,next:null},Pn===null){if(al===null)throw Error(u(308));Pn=e,al.dependencies={lanes:0,firstContext:e}}else Pn=Pn.next=e;return t}var sn=null;function Us(e){sn===null?sn=[e]:sn.push(e)}function Na(e,t,n,r){var s=t.interleaved;return s===null?(n.next=n,Us(t)):(n.next=s.next,s.next=n),t.interleaved=n,Lt(e,r)}function Lt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var Ht=!1;function Hs(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ca(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Rt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Vt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(ge&2)!==0){var s=r.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),r.pending=t,Lt(e,n)}return s=r.interleaved,s===null?(t.next=t,Us(r)):(t.next=s.next,s.next=t),r.interleaved=t,Lt(e,n)}function ul(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}function Ea(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var s=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?s=i=a:i=i.next=a,n=n.next}while(n!==null);i===null?s=i=t:i=i.next=t}else s=i=t;n={baseState:r.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function cl(e,t,n,r){var s=e.updateQueue;Ht=!1;var i=s.firstBaseUpdate,a=s.lastBaseUpdate,c=s.shared.pending;if(c!==null){s.shared.pending=null;var f=c,N=f.next;f.next=null,a===null?i=N:a.next=N,a=f;var O=e.alternate;O!==null&&(O=O.updateQueue,c=O.lastBaseUpdate,c!==a&&(c===null?O.firstBaseUpdate=N:c.next=N,O.lastBaseUpdate=f))}if(i!==null){var b=s.baseState;a=0,O=N=f=null,c=i;do{var A=c.lane,H=c.eventTime;if((r&A)===A){O!==null&&(O=O.next={eventTime:H,lane:0,tag:c.tag,payload:c.payload,callback:c.callback,next:null});e:{var K=e,G=c;switch(A=t,H=n,G.tag){case 1:if(K=G.payload,typeof K=="function"){b=K.call(H,b,A);break e}b=K;break e;case 3:K.flags=K.flags&-65537|128;case 0:if(K=G.payload,A=typeof K=="function"?K.call(H,b,A):K,A==null)break e;b=W({},b,A);break e;case 2:Ht=!0}}c.callback!==null&&c.lane!==0&&(e.flags|=64,A=s.effects,A===null?s.effects=[c]:A.push(c))}else H={eventTime:H,lane:A,tag:c.tag,payload:c.payload,callback:c.callback,next:null},O===null?(N=O=H,f=b):O=O.next=H,a|=A;if(c=c.next,c===null){if(c=s.shared.pending,c===null)break;A=c,c=A.next,A.next=null,s.lastBaseUpdate=A,s.shared.pending=null}}while(!0);if(O===null&&(f=b),s.baseState=f,s.firstBaseUpdate=N,s.lastBaseUpdate=O,t=s.shared.interleaved,t!==null){s=t;do a|=s.lane,s=s.next;while(s!==t)}else i===null&&(s.shared.lanes=0);un|=a,e.lanes=a,e.memoizedState=b}}function _a(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Ys.transition;Ys.transition={};try{e(!1),t()}finally{je=n,Ys.transition=r}}function Qa(){return ct().memoizedState}function Id(e,t,n){var r=Yt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},Ka(e))Ga(t,n);else if(n=Na(e,t,n,r),n!==null){var s=Xe();xt(n,e,r,s),Ya(n,t,r)}}function Dd(e,t,n){var r=Yt(e),s={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(Ka(e))Ga(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var a=t.lastRenderedState,c=i(a,n);if(s.hasEagerState=!0,s.eagerState=c,mt(c,a)){var f=t.interleaved;f===null?(s.next=s,Us(t)):(s.next=f.next,f.next=s),t.interleaved=s;return}}catch{}finally{}n=Na(e,t,s,r),n!==null&&(s=Xe(),xt(n,e,r,s),Ya(n,t,r))}}function Ka(e){var t=e.alternate;return e===Le||t!==null&&t===Le}function Ga(e,t){hr=pl=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function Ya(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ls(e,n)}}var vl={readContext:ut,useCallback:Qe,useContext:Qe,useEffect:Qe,useImperativeHandle:Qe,useInsertionEffect:Qe,useLayoutEffect:Qe,useMemo:Qe,useReducer:Qe,useRef:Qe,useState:Qe,useDebugValue:Qe,useDeferredValue:Qe,useTransition:Qe,useMutableSource:Qe,useSyncExternalStore:Qe,useId:Qe,unstable_isNewReconciler:!1},Md={readContext:ut,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:ut,useEffect:ba,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,ml(4194308,4,Ba.bind(null,t,e),n)},useLayoutEffect:function(e,t){return ml(4194308,4,e,t)},useInsertionEffect:function(e,t){return ml(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=Id.bind(null,Le,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:Aa,useDebugValue:ni,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=Aa(!1),t=e[0];return e=Pd.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=Le,s=Nt();if(ze){if(n===void 0)throw Error(u(407));n=n()}else{if(n=t(),Fe===null)throw Error(u(349));(an&30)!==0||Ra(r,t,n)}s.memoizedState=n;var i={value:n,getSnapshot:t};return s.queue=i,ba(Ia.bind(null,r,i,e),[e]),r.flags|=2048,yr(9,Pa.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Nt(),t=Fe.identifierPrefix;if(ze){var n=Tt,r=zt;n=(r&~(1<<32-pt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=vr++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[St]=t,e[ur]=r,hu(e,t,!1,!1),t.stateNode=e;e:{switch(a=Gl(n,r),n){case"dialog":Ce("cancel",e),Ce("close",e),s=r;break;case"iframe":case"object":case"embed":Ce("load",e),s=r;break;case"video":case"audio":for(s=0;sbn&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304)}else{if(!r)if(e=dl(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),xr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!a.alternate&&!ze)return Ke(t),null}else 2*Pe()-i.renderingStartTime>bn&&n!==1073741824&&(t.flags|=128,r=!0,xr(i,!1),t.lanes=4194304);i.isBackwards?(a.sibling=t.child,t.child=a):(n=i.last,n!==null?n.sibling=a:t.child=a,i.last=a)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=Pe(),t.sibling=null,n=Te.current,we(Te,r?n&1|2:n&1),t):(Ke(t),null);case 22:case 23:return Ei(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(it&1073741824)!==0&&(Ke(t),t.subtreeFlags&6&&(t.flags|=8192)):Ke(t),null;case 24:return null;case 25:return null}throw Error(u(156,t.tag))}function Ud(e,t){switch(Ms(t),t.tag){case 1:return qe(t.type)&&el(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Dn(),Ee(Ze),Ee(Ve),Gs(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Qs(t),null;case 13:if(Ee(Te),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(u(340));Ln()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Ee(Te),null;case 4:return Dn(),null;case 10:return Bs(t.type._context),null;case 22:case 23:return Ei(),null;case 24:return null;default:return null}}var kl=!1,Ge=!1,Hd=typeof WeakSet=="function"?WeakSet:Set,V=null;function An(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){Re(e,t,r)}else n.current=null}function mi(e,t,n){try{n()}catch(r){Re(e,t,r)}}var yu=!1;function Vd(e,t){if(Es=Fr,e=Xo(),ys(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var s=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var a=0,c=-1,f=-1,N=0,O=0,b=e,A=null;t:for(;;){for(var H;b!==n||s!==0&&b.nodeType!==3||(c=a+s),b!==i||r!==0&&b.nodeType!==3||(f=a+r),b.nodeType===3&&(a+=b.nodeValue.length),(H=b.firstChild)!==null;)A=b,b=H;for(;;){if(b===e)break t;if(A===n&&++N===s&&(c=a),A===i&&++O===r&&(f=a),(H=b.nextSibling)!==null)break;b=A,A=b.parentNode}b=H}n=c===-1||f===-1?null:{start:c,end:f}}else n=null}n=n||{start:0,end:0}}else n=null;for(_s={focusedElem:e,selectionRange:n},Fr=!1,V=t;V!==null;)if(t=V,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,V=e;else for(;V!==null;){t=V;try{var K=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(K!==null){var G=K.memoizedProps,Ie=K.memoizedState,y=t.stateNode,h=y.getSnapshotBeforeUpdate(t.elementType===t.type?G:vt(t.type,G),Ie);y.__reactInternalSnapshotBeforeUpdate=h}break;case 3:var j=t.stateNode.containerInfo;j.nodeType===1?j.textContent="":j.nodeType===9&&j.documentElement&&j.removeChild(j.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch($){Re(t,t.return,$)}if(e=t.sibling,e!==null){e.return=t.return,V=e;break}V=t.return}return K=yu,yu=!1,K}function kr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var s=r=r.next;do{if((s.tag&e)===e){var i=s.destroy;s.destroy=void 0,i!==void 0&&mi(t,n,i)}s=s.next}while(s!==r)}}function jl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function hi(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function xu(e){var t=e.alternate;t!==null&&(e.alternate=null,xu(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[St],delete t[ur],delete t[Rs],delete t[_d],delete t[zd])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function ku(e){return e.tag===5||e.tag===3||e.tag===4}function ju(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||ku(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function vi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=qr));else if(r!==4&&(e=e.child,e!==null))for(vi(e,t,n),e=e.sibling;e!==null;)vi(e,t,n),e=e.sibling}function gi(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(gi(e,t,n),e=e.sibling;e!==null;)gi(e,t,n),e=e.sibling}var Ue=null,gt=!1;function Qt(e,t,n){for(n=n.child;n!==null;)Su(e,t,n),n=n.sibling}function Su(e,t,n){if(jt&&typeof jt.onCommitFiberUnmount=="function")try{jt.onCommitFiberUnmount(Dr,n)}catch{}switch(n.tag){case 5:Ge||An(n,t);case 6:var r=Ue,s=gt;Ue=null,Qt(e,t,n),Ue=r,gt=s,Ue!==null&&(gt?(e=Ue,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ue.removeChild(n.stateNode));break;case 18:Ue!==null&&(gt?(e=Ue,n=n.stateNode,e.nodeType===8?Ls(e.parentNode,n):e.nodeType===1&&Ls(e,n),qn(e)):Ls(Ue,n.stateNode));break;case 4:r=Ue,s=gt,Ue=n.stateNode.containerInfo,gt=!0,Qt(e,t,n),Ue=r,gt=s;break;case 0:case 11:case 14:case 15:if(!Ge&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){s=r=r.next;do{var i=s,a=i.destroy;i=i.tag,a!==void 0&&((i&2)!==0||(i&4)!==0)&&mi(n,t,a),s=s.next}while(s!==r)}Qt(e,t,n);break;case 1:if(!Ge&&(An(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(c){Re(n,t,c)}Qt(e,t,n);break;case 21:Qt(e,t,n);break;case 22:n.mode&1?(Ge=(r=Ge)||n.memoizedState!==null,Qt(e,t,n),Ge=r):Qt(e,t,n);break;default:Qt(e,t,n)}}function wu(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new Hd),t.forEach(function(r){var s=ef.bind(null,e,r);n.has(r)||(n.add(r),r.then(s,s))})}}function yt(e,t){var n=t.deletions;if(n!==null)for(var r=0;rs&&(s=a),r&=~i}if(r=s,r=Pe()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Kd(r/1960))-r,10e?16:e,Gt===null)var r=!1;else{if(e=Gt,Gt=null,El=0,(ge&6)!==0)throw Error(u(331));var s=ge;for(ge|=4,V=e.current;V!==null;){var i=V,a=i.child;if((V.flags&16)!==0){var c=i.deletions;if(c!==null){for(var f=0;fPe()-ki?dn(e,0):xi|=n),tt(e,t)}function Au(e,t){t===0&&((e.mode&1)===0?t=1:(t=Ar,Ar<<=1,(Ar&130023424)===0&&(Ar=4194304)));var n=Xe();e=Lt(e,t),e!==null&&(Kn(e,t,n),tt(e,n))}function Jd(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Au(e,n)}function ef(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,s=e.memoizedState;s!==null&&(n=s.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(u(314))}r!==null&&r.delete(t),Au(e,n)}var Ou;Ou=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Ze.current)Je=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Je=!1,Bd(e,t,n);Je=(e.flags&131072)!==0}else Je=!1,ze&&(t.flags&1048576)!==0&&va(t,ll,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;xl(e,t),e=t.pendingProps;var s=_n(t,Ve.current);In(t,n),s=Zs(null,t,r,e,s,n);var i=qs();return t.flags|=1,typeof s=="object"&&s!==null&&typeof s.render=="function"&&s.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,qe(r)?(i=!0,tl(t)):i=!1,t.memoizedState=s.state!==null&&s.state!==void 0?s.state:null,Hs(t),s.updater=gl,t.stateNode=s,s._reactInternals=t,li(t,r,e,n),t=ai(null,t,r,!0,i,n)):(t.tag=0,ze&&i&&Ds(t),Ye(null,t,s,n),t=t.child),t;case 16:r=t.elementType;e:{switch(xl(e,t),e=t.pendingProps,s=r._init,r=s(r._payload),t.type=r,s=t.tag=nf(r),e=vt(r,e),s){case 0:t=oi(null,t,r,e,n);break e;case 1:t=uu(null,t,r,e,n);break e;case 11:t=lu(null,t,r,e,n);break e;case 14:t=su(null,t,r,vt(r.type,e),n);break e}throw Error(u(306,r,""))}return t;case 0:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),oi(e,t,r,s,n);case 1:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),uu(e,t,r,s,n);case 3:e:{if(cu(t),e===null)throw Error(u(387));r=t.pendingProps,i=t.memoizedState,s=i.element,Ca(e,t),cl(t,r,null,n);var a=t.memoizedState;if(r=a.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){s=Mn(Error(u(423)),t),t=du(e,t,r,n,s);break e}else if(r!==s){s=Mn(Error(u(424)),t),t=du(e,t,r,n,s);break e}else for(st=Ft(t.stateNode.containerInfo.firstChild),lt=t,ze=!0,ht=null,n=wa(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Ln(),r===s){t=Pt(e,t,n);break e}Ye(e,t,r,n)}t=t.child}return t;case 5:return za(t),e===null&&Os(t),r=t.type,s=t.pendingProps,i=e!==null?e.memoizedProps:null,a=s.children,zs(r,s)?a=null:i!==null&&zs(r,i)&&(t.flags|=32),au(e,t),Ye(e,t,a,n),t.child;case 6:return e===null&&Os(t),null;case 13:return fu(e,t,n);case 4:return Vs(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Rn(t,null,r,n):Ye(e,t,r,n),t.child;case 11:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),lu(e,t,r,s,n);case 7:return Ye(e,t,t.pendingProps,n),t.child;case 8:return Ye(e,t,t.pendingProps.children,n),t.child;case 12:return Ye(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,s=t.pendingProps,i=t.memoizedProps,a=s.value,we(ol,r._currentValue),r._currentValue=a,i!==null)if(mt(i.value,a)){if(i.children===s.children&&!Ze.current){t=Pt(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var c=i.dependencies;if(c!==null){a=i.child;for(var f=c.firstContext;f!==null;){if(f.context===r){if(i.tag===1){f=Rt(-1,n&-n),f.tag=2;var N=i.updateQueue;if(N!==null){N=N.shared;var O=N.pending;O===null?f.next=f:(f.next=O.next,O.next=f),N.pending=f}}i.lanes|=n,f=i.alternate,f!==null&&(f.lanes|=n),Ws(i.return,n,t),c.lanes|=n;break}f=f.next}}else if(i.tag===10)a=i.type===t.type?null:i.child;else if(i.tag===18){if(a=i.return,a===null)throw Error(u(341));a.lanes|=n,c=a.alternate,c!==null&&(c.lanes|=n),Ws(a,n,t),a=i.sibling}else a=i.child;if(a!==null)a.return=i;else for(a=i;a!==null;){if(a===t){a=null;break}if(i=a.sibling,i!==null){i.return=a.return,a=i;break}a=a.return}i=a}Ye(e,t,s.children,n),t=t.child}return t;case 9:return s=t.type,r=t.pendingProps.children,In(t,n),s=ut(s),r=r(s),t.flags|=1,Ye(e,t,r,n),t.child;case 14:return r=t.type,s=vt(r,t.pendingProps),s=vt(r.type,s),su(e,t,r,s,n);case 15:return iu(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,s=t.pendingProps,s=t.elementType===r?s:vt(r,s),xl(e,t),t.tag=1,qe(r)?(e=!0,tl(t)):e=!1,In(t,n),Za(t,r,s),li(t,r,s,n),ai(null,t,r,!0,e,n);case 19:return mu(e,t,n);case 22:return ou(e,t,n)}throw Error(u(156,t.tag))};function bu(e,t){return go(e,t)}function tf(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ft(e,t,n,r){return new tf(e,t,n,r)}function zi(e){return e=e.prototype,!(!e||!e.isReactComponent)}function nf(e){if(typeof e=="function")return zi(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ke)return 11;if(e===Ne)return 14}return 2}function Zt(e,t){var n=e.alternate;return n===null?(n=ft(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Ll(e,t,n,r,s,i){var a=2;if(r=e,typeof e=="function")zi(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case pe:return pn(n.children,s,i,t);case me:a=8,s|=8;break;case be:return e=ft(12,n,t,s|2),e.elementType=be,e.lanes=i,e;case se:return e=ft(13,n,t,s),e.elementType=se,e.lanes=i,e;case J:return e=ft(19,n,t,s),e.elementType=J,e.lanes=i,e;case xe:return Rl(n,s,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case X:a=10;break e;case le:a=9;break e;case ke:a=11;break e;case Ne:a=14;break e;case _e:a=16,r=null;break e}throw Error(u(130,e==null?e:typeof e,""))}return t=ft(a,n,t,s),t.elementType=e,t.type=r,t.lanes=i,t}function pn(e,t,n,r){return e=ft(7,e,r,t),e.lanes=n,e}function Rl(e,t,n,r){return e=ft(22,e,r,t),e.elementType=xe,e.lanes=n,e.stateNode={isHidden:!1},e}function Ti(e,t,n){return e=ft(6,e,null,t),e.lanes=n,e}function Li(e,t,n){return t=ft(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function rf(e,t,n,r,s){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=rs(0),this.expirationTimes=rs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=rs(0),this.identifierPrefix=r,this.onRecoverableError=s,this.mutableSourceEagerHydrationData=null}function Ri(e,t,n,r,s,i,a,c,f){return e=new rf(e,t,n,c,f),t===1?(t=1,i===!0&&(t|=8)):t=0,i=ft(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Hs(i),e}function lf(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(o)}catch(p){console.error(p)}}return o(),Oi.exports=vf(),Oi.exports}var qu;function yf(){if(qu)return bl;qu=1;var o=gf();return bl.createRoot=o.createRoot,bl.hydrateRoot=o.hydrateRoot,bl}var xf=yf();const kf=Qi(xf),jf={},Ju=o=>{let p;const u=new Set,k=(z,d)=>{const w=typeof z=="function"?z(p):z;if(!Object.is(w,p)){const m=p;p=d??(typeof w!="object"||w===null)?w:Object.assign({},p,w),u.forEach(T=>T(p,m))}},S=()=>p,R={setState:k,getState:S,getInitialState:()=>C,subscribe:z=>(u.add(z),()=>u.delete(z)),destroy:()=>{(jf?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),u.clear()}},C=p=o(k,S,R);return R},Sf=o=>o?Ju(o):Ju;var Fi={exports:{}},Bi={},Wi={exports:{}},Ui={};/** - * @license React - * use-sync-external-store-shim.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var ec;function wf(){if(ec)return Ui;ec=1;var o=_r();function p(d,w){return d===w&&(d!==0||1/d===1/w)||d!==d&&w!==w}var u=typeof Object.is=="function"?Object.is:p,k=o.useState,S=o.useEffect,D=o.useLayoutEffect,F=o.useDebugValue;function _(d,w){var m=w(),T=k({inst:{value:m,getSnapshot:w}}),g=T[0].inst,M=T[1];return D(function(){g.value=m,g.getSnapshot=w,R(g)&&M({inst:g})},[d,m,w]),S(function(){return R(g)&&M({inst:g}),d(function(){R(g)&&M({inst:g})})},[d]),F(m),m}function R(d){var w=d.getSnapshot;d=d.value;try{var m=w();return!u(d,m)}catch{return!0}}function C(d,w){return w()}var z=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?C:_;return Ui.useSyncExternalStore=o.useSyncExternalStore!==void 0?o.useSyncExternalStore:z,Ui}var tc;function Nf(){return tc||(tc=1,Wi.exports=wf()),Wi.exports}/** - * @license React - * use-sync-external-store-shim/with-selector.production.js - * - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var nc;function Cf(){if(nc)return Bi;nc=1;var o=_r(),p=Nf();function u(C,z){return C===z&&(C!==0||1/C===1/z)||C!==C&&z!==z}var k=typeof Object.is=="function"?Object.is:u,S=p.useSyncExternalStore,D=o.useRef,F=o.useEffect,_=o.useMemo,R=o.useDebugValue;return Bi.useSyncExternalStoreWithSelector=function(C,z,d,w,m){var T=D(null);if(T.current===null){var g={hasValue:!1,value:null};T.current=g}else g=T.current;T=_(function(){function P(ue){if(!B){if(B=!0,Z=ue,ue=w(ue),m!==void 0&&g.hasValue){var pe=g.value;if(m(pe,ue))return I=pe}return I=ue}if(pe=I,k(Z,ue))return pe;var me=w(ue);return m!==void 0&&m(pe,me)?(Z=ue,pe):(Z=ue,I=me)}var B=!1,Z,I,oe=d===void 0?null:d;return[function(){return P(z())},oe===null?void 0:function(){return P(oe())}]},[z,d,w,m]);var M=S(C,T[0],T[1]);return F(function(){g.hasValue=!0,g.value=M},[M]),R(M),M},Bi}var rc;function Ef(){return rc||(rc=1,Fi.exports=Cf()),Fi.exports}var _f=Ef();const zf=Qi(_f),ac={},{useDebugValue:Tf}=oc,{useSyncExternalStoreWithSelector:Lf}=zf;let lc=!1;const Rf=o=>o;function Pf(o,p=Rf,u){(ac?"production":void 0)!=="production"&&u&&!lc&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),lc=!0);const k=Lf(o.subscribe,o.getState,o.getServerState||o.getInitialState,p,u);return Tf(k),k}const sc=o=>{(ac?"production":void 0)!=="production"&&typeof o!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const p=typeof o=="function"?Sf(o):o,u=(k,S)=>Pf(p,k,S);return Object.assign(u,p),u},If=o=>o?sc(o):sc,Se="";async function kt(o){const p=await fetch(o,{cache:"no-store"});if(!p.ok)throw new Error(String(p.status));return p.json()}async function We(o,p){return(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p)})).json()}const fe={liveStatus:()=>kt(`${Se}/api/live-status`),agentConfig:()=>kt(`${Se}/api/agent-config`),modelChangeLog:()=>kt(`${Se}/api/model-change-log`).catch(()=>[]),officialsStats:()=>kt(`${Se}/api/officials-stats`),morningBrief:()=>kt(`${Se}/api/morning-brief`),morningConfig:()=>kt(`${Se}/api/morning-config`),agentsStatus:()=>kt(`${Se}/api/agents-status`),taskActivity:o=>kt(`${Se}/api/task-activity/${encodeURIComponent(o)}`),schedulerState:o=>kt(`${Se}/api/scheduler-state/${encodeURIComponent(o)}`),skillContent:(o,p)=>kt(`${Se}/api/skill-content/${encodeURIComponent(o)}/${encodeURIComponent(p)}`),setModel:(o,p)=>We(`${Se}/api/set-model`,{agentId:o,model:p}),agentWake:o=>We(`${Se}/api/agent-wake`,{agentId:o}),taskAction:(o,p,u)=>We(`${Se}/api/task-action`,{taskId:o,action:p,reason:u}),reviewAction:(o,p,u)=>We(`${Se}/api/review-action`,{taskId:o,action:p,comment:u}),advanceState:(o,p)=>We(`${Se}/api/advance-state`,{taskId:o,comment:p}),archiveTask:(o,p)=>We(`${Se}/api/archive-task`,{taskId:o,archived:p}),archiveAllDone:()=>We(`${Se}/api/archive-task`,{archiveAllDone:!0}),schedulerScan:(o=180)=>We(`${Se}/api/scheduler-scan`,{thresholdSec:o}),schedulerRetry:(o,p)=>We(`${Se}/api/scheduler-retry`,{taskId:o,reason:p}),schedulerEscalate:(o,p)=>We(`${Se}/api/scheduler-escalate`,{taskId:o,reason:p}),schedulerRollback:(o,p)=>We(`${Se}/api/scheduler-rollback`,{taskId:o,reason:p}),refreshMorning:()=>We(`${Se}/api/morning-brief/refresh`,{}),saveMorningConfig:o=>We(`${Se}/api/morning-config`,o),addSkill:(o,p,u,k)=>We(`${Se}/api/add-skill`,{agentId:o,skillName:p,description:u,trigger:k}),addRemoteSkill:(o,p,u,k)=>We(`${Se}/api/add-remote-skill`,{agentId:o,skillName:p,sourceUrl:u,description:k||""}),remoteSkillsList:()=>kt(`${Se}/api/remote-skills-list`),updateRemoteSkill:(o,p)=>We(`${Se}/api/update-remote-skill`,{agentId:o,skillName:p}),removeRemoteSkill:(o,p)=>We(`${Se}/api/remove-remote-skill`,{agentId:o,skillName:p}),createTask:o=>We(`${Se}/api/create-task`,o)},uc=[{key:"Inbox",dept:"皇上",icon:"👑",action:"下旨"},{key:"Taizi",dept:"太子",icon:"🤴",action:"分拣"},{key:"Zhongshu",dept:"中书省",icon:"📜",action:"起草"},{key:"Menxia",dept:"门下省",icon:"🔍",action:"审议"},{key:"Assigned",dept:"尚书省",icon:"📮",action:"派发"},{key:"Doing",dept:"六部",icon:"⚙️",action:"执行"},{key:"Review",dept:"尚书省",icon:"🔎",action:"汇总"},{key:"Done",dept:"回奏",icon:"✅",action:"完成"}],Df={Inbox:0,Pending:0,Taizi:1,Zhongshu:2,Menxia:3,Assigned:4,Doing:5,Review:6,Done:7,Blocked:5,Cancelled:5,Next:4},Mf={太子:"#e8a040",中书省:"#a07aff",门下省:"#6a9eff",尚书省:"#6aef9a",礼部:"#f5c842",户部:"#ff9a6a",兵部:"#ff5270",刑部:"#cc4444",工部:"#44aaff",吏部:"#9b59b6",皇上:"#ffd700",回奏:"#2ecc8a"},zr={Inbox:"收件",Pending:"待处理",Taizi:"太子分拣",Zhongshu:"中书起草",Menxia:"门下审议",Assigned:"已派发",Doing:"执行中",Review:"待审查",Done:"已完成",Blocked:"阻塞",Cancelled:"已取消",Next:"待执行"};function $l(o){return Mf[o]||"#6a9eff"}function Ki(o){const p=o.review_round||0;return o.state==="Menxia"&&p>1?`门下审议(第${p}轮)`:o.state==="Zhongshu"&&p>0?`中书修订(第${p}轮)`:zr[o.state]||o.state}function Jt(o){return/^JJC-/i.test(o.id||"")}function Bl(o){return o.archived||["Done","Cancelled"].includes(o.state)}function Gi(o){const p=Df[o.state]??4;return uc.map((u,k)=>({...u,status:k({liveStatus:null,agentConfig:null,changeLog:[],officialsData:null,agentsStatusData:null,morningBrief:null,subConfig:null,activeTab:"edicts",edictFilter:"active",sessFilter:"all",tplCatFilter:"全部",selectedOfficial:null,modalTaskId:null,countdown:5,toasts:[],setActiveTab:u=>{o({activeTab:u});const k=p();["models","skills","sessions"].includes(u)&&!k.agentConfig&&k.loadAgentConfig(),u==="officials"&&!k.officialsData&&k.loadOfficials(),u==="monitor"&&k.loadAgentsStatus(),u==="morning"&&!k.morningBrief&&k.loadMorning()},setEdictFilter:u=>o({edictFilter:u}),setSessFilter:u=>o({sessFilter:u}),setTplCatFilter:u=>o({tplCatFilter:u}),setSelectedOfficial:u=>o({selectedOfficial:u}),setModalTaskId:u=>o({modalTaskId:u}),setCountdown:u=>o({countdown:u}),toast:(u,k="ok")=>{const S=++Ff;o(D=>({toasts:[...D.toasts,{id:S,msg:u,type:k}]})),setTimeout(()=>{o(D=>({toasts:D.toasts.filter(F=>F.id!==S)}))},3e3)},loadLive:async()=>{try{const u=await fe.liveStatus();o({liveStatus:u}),p().officialsData||fe.officialsStats().then(S=>o({officialsData:S})).catch(()=>{})}catch{}},loadAgentConfig:async()=>{try{const u=await fe.agentConfig(),k=await fe.modelChangeLog();o({agentConfig:u,changeLog:k})}catch{}},loadOfficials:async()=>{try{const u=await fe.officialsStats();o({officialsData:u})}catch{}},loadAgentsStatus:async()=>{try{const u=await fe.agentsStatus();o({agentsStatusData:u})}catch{o({agentsStatusData:null})}},loadMorning:async()=>{try{const[u,k]=await Promise.all([fe.morningBrief(),fe.morningConfig()]);o({morningBrief:u,subConfig:k})}catch{}},loadSubConfig:async()=>{try{const u=await fe.morningConfig();o({subConfig:u})}catch{}},loadAll:async()=>{const u=p();await u.loadLive();const k=u.activeTab;["models","skills"].includes(k)&&await u.loadAgentConfig()}}));let Er=null;function Bf(){Er||(q.getState().loadAll(),Er=setInterval(()=>{const o=q.getState(),p=o.countdown-1;p<=0?(o.setCountdown(5),o.loadAll()):o.setCountdown(p)},1e3))}function Wf(){Er&&(clearInterval(Er),Er=null)}function Uf(o){if(!o)return"";try{const p=new Date(o.includes("T")?o:o.replace(" ","T")+"Z");if(isNaN(p.getTime()))return"";const u=Date.now()-p.getTime(),k=Math.floor(u/6e4);if(k<1)return"刚刚";if(k<60)return k+"分钟前";const S=Math.floor(k/60);return S<24?S+"小时前":Math.floor(S/24)+"天前"}catch{return""}}const ic={Doing:0,Review:1,Assigned:2,Menxia:3,Zhongshu:4,Taizi:5,Inbox:6,Blocked:7,Next:8,Done:9,Cancelled:10};function Hf({task:o}){const p=Gi(o);return l.jsx("div",{className:"ec-pipe",children:p.map((u,k)=>l.jsxs("span",{style:{display:"contents"},children:[l.jsxs("div",{className:`ep-node ${u.status}`,children:[l.jsx("div",{className:"ep-icon",children:u.icon}),l.jsx("div",{className:"ep-name",children:u.dept})]}),kP.setModalTaskId),u=q(P=>P.toast),k=q(P=>P.loadAll),S=o.heartbeat||{status:"unknown",label:"⚪"},D="st-"+(o.state||""),F="dt-"+(o.org||"").replace(/\s/g,""),_=uc.find((P,B)=>Gi(o)[B].status==="active"),R=o.todos||[],C=R.filter(P=>P.status==="completed").length,z=R.length,d=!["Done","Blocked","Cancelled"].includes(o.state),w=["Blocked","Cancelled"].includes(o.state),m=Bl(o),T=o.block&&o.block!=="无"&&o.block!=="-",g=async(P,B)=>{if(B.stopPropagation(),P==="stop"||P==="cancel"){const Z=prompt(P==="stop"?"请输入叫停原因:":"请输入取消原因:");if(Z===null)return;try{const I=await fe.taskAction(o.id,P,Z);I.ok?(u(I.message||"操作成功"),k()):u(I.error||"操作失败","err")}catch{u("服务器连接失败","err")}}else if(P==="resume")try{const Z=await fe.taskAction(o.id,"resume","恢复执行");Z.ok?(u(Z.message||"已恢复"),k()):u(Z.error||"操作失败","err")}catch{u("服务器连接失败","err")}},M=async P=>{P.stopPropagation();try{const B=await fe.archiveTask(o.id,!o.archived);B.ok?(u(B.message||"操作成功"),k()):u(B.error||"操作失败","err")}catch{u("服务器连接失败","err")}};return l.jsxs("div",{className:`edict-card${m?" archived":""}`,onClick:()=>p(o.id),children:[l.jsx(Hf,{task:o}),l.jsx("div",{className:"ec-id",children:o.id}),l.jsx("div",{className:"ec-title",children:o.title||"(无标题)"}),l.jsxs("div",{className:"ec-meta",children:[l.jsx("span",{className:`tag ${D}`,children:Ki(o)}),o.org&&l.jsx("span",{className:`tag ${F}`,children:o.org}),_&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["当前: ",l.jsxs("b",{style:{color:$l(_.dept)},children:[_.dept," · ",_.action]})]})]}),o.now&&o.now!=="-"&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:6},children:o.now.substring(0,80)}),(o.review_round||0)>0&&l.jsxs("div",{style:{fontSize:11,marginBottom:6},children:[Array.from({length:o.review_round||0},(P,B)=>l.jsx("span",{style:{display:"inline-block",width:14,height:14,borderRadius:"50%",background:B<(o.review_round||0)-1?"#1a3a6a22":"var(--acc)22",border:`1px solid ${B<(o.review_round||0)-1?"#2a4a8a":"var(--acc)"}`,fontSize:9,textAlign:"center",lineHeight:"13px",marginRight:2,color:B<(o.review_round||0)-1?"#4a6aaa":"var(--acc)"},children:B+1},B)),l.jsxs("span",{style:{color:"var(--muted)",fontSize:10},children:["第 ",o.review_round," 轮磋商"]})]}),z>0&&l.jsxs("div",{className:"ec-todo-bar",children:[l.jsxs("span",{children:["📋 ",C,"/",z]}),l.jsx("div",{className:"ec-todo-track",children:l.jsx("div",{className:"ec-todo-fill",style:{width:`${Math.round(C/z*100)}%`}})}),l.jsx("span",{children:C===z?"✅ 全部完成":"🔄 进行中"})]}),l.jsxs("div",{className:"ec-footer",children:[l.jsx("span",{className:`hb ${S.status}`,children:S.label}),T&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)",background:"#200a10"},children:["🚫 ",o.block]}),o.eta&&o.eta!=="-"&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["📅 ",o.eta]})]}),l.jsxs("div",{className:"ec-actions",onClick:P=>P.stopPropagation(),children:[d&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"mini-act",onClick:P=>g("stop",P),children:"⏸ 叫停"}),l.jsx("button",{className:"mini-act danger",onClick:P=>g("cancel",P),children:"🚫 取消"})]}),w&&l.jsx("button",{className:"mini-act",onClick:P=>g("resume",P),children:"▶ 恢复"}),m&&!o.archived&&l.jsx("button",{className:"mini-act",onClick:M,children:"📦 归档"}),o.archived&&l.jsx("button",{className:"mini-act",onClick:M,children:"📤 取消归档"})]})]})}function Qf(){const o=q(m=>m.liveStatus),p=q(m=>m.edictFilter),u=q(m=>m.setEdictFilter),k=q(m=>m.toast),S=q(m=>m.loadAll),F=((o==null?void 0:o.tasks)||[]).filter(Jt),_=F.filter(m=>!Bl(m)),R=F.filter(m=>Bl(m));let C;p==="active"?C=_:p==="archived"?C=R:C=F,C.sort((m,T)=>(ic[m.state]??9)-(ic[T.state]??9));const z=F.filter(m=>!m.archived&&["Done","Cancelled"].includes(m.state)),d=async()=>{if(confirm("将所有已完成/已取消的旨意移入归档?"))try{const m=await fe.archiveAllDone();m.ok?(k(`📦 ${m.count||0} 道旨意已归档`),S()):k(m.error||"批量归档失败","err")}catch{k("服务器连接失败","err")}},w=async()=>{try{const m=await fe.schedulerScan();m.ok?k(`🧭 太子巡检完成:${m.count||0} 个动作`):k(m.error||"巡检失败","err"),S()}catch{k("服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsxs("div",{className:"archive-bar",children:[l.jsx("span",{className:"ab-label",children:"筛选:"}),["active","archived","all"].map(m=>l.jsx("button",{className:`ab-btn ${p===m?"active":""}`,onClick:()=>u(m),children:m==="active"?"活跃":m==="archived"?"归档":"全部"},m)),z.length>0&&l.jsx("button",{className:"ab-btn",onClick:d,children:"📦 一键归档"}),l.jsxs("span",{className:"ab-count",children:["活跃 ",_.length," · 归档 ",R.length," · 共 ",F.length]}),l.jsx("button",{className:"ab-scan",onClick:w,children:"🧭 太子巡检"})]}),l.jsx("div",{className:"edict-grid",children:C.length===0?l.jsxs("div",{className:"empty",style:{gridColumn:"1/-1"},children:["暂无旨意",l.jsx("br",{}),l.jsx("small",{style:{fontSize:11,marginTop:6,display:"block",color:"var(--muted)"},children:"通过飞书向太子发送任务,太子分拣后转中书省处理"})]}):C.map(m=>l.jsx(Vf,{task:m},m.id))})]})}function Kf(){var Z;const o=q(I=>I.liveStatus),p=q(I=>I.agentsStatusData),u=q(I=>I.officialsData),k=q(I=>I.loadAgentsStatus),S=q(I=>I.setModalTaskId),D=q(I=>I.toast);te.useEffect(()=>{k()},[k]);const _=((o==null?void 0:o.tasks)||[]).filter(I=>Jt(I)&&I.state!=="Done"&&I.state!=="Next"),R={};u!=null&&u.officials&&u.officials.forEach(I=>{R[I.id]=I});const C=async I=>{try{const oe=await fe.agentWake(I);D(oe.message||"唤醒指令已发出"),setTimeout(()=>k(),3e4)}catch{D("唤醒失败","err")}},z=async()=>{if(!p)return;const I=p.agents.filter(oe=>oe.id!=="main"&&oe.status!=="running"&&oe.status!=="unconfigured");if(!I.length){D("所有 Agent 均已在线");return}D(`正在唤醒 ${I.length} 个 Agent...`);for(const oe of I)try{await fe.agentWake(oe.id)}catch{}D(`${I.length} 个唤醒指令已发出,30秒后刷新状态`),setTimeout(()=>k(),3e4)},d=p,w=((Z=d==null?void 0:d.agents)==null?void 0:Z.filter(I=>I.id!=="main"))||[],m=w.filter(I=>I.status==="running").length,T=w.filter(I=>I.status==="idle").length,g=w.filter(I=>I.status==="offline").length,M=w.filter(I=>I.status==="unconfigured").length,P=d==null?void 0:d.gateway,B=P!=null&&P.probe?"ok":P!=null&&P.alive?"warn":"err";return l.jsxs("div",{children:[d&&d.ok&&l.jsxs("div",{className:"as-panel",children:[l.jsxs("div",{className:"as-header",children:[l.jsx("span",{className:"as-title",children:"🔌 Agent 在线状态"}),l.jsxs("span",{className:`as-gw ${B}`,children:["Gateway: ",(P==null?void 0:P.status)||"未知"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>k(),style:{marginLeft:8},children:"🔄 刷新"}),g+M>0&&l.jsx("button",{className:"btn-refresh",onClick:z,style:{marginLeft:4,borderColor:"var(--warn)",color:"var(--warn)"},children:"⚡ 全部唤醒"})]}),l.jsx("div",{className:"as-grid",children:w.map(I=>{const oe=I.status!=="running"&&I.status!=="unconfigured"&&(P==null?void 0:P.alive);return l.jsxs("div",{className:"as-card",title:`${I.role} · ${I.statusLabel}`,children:[l.jsx("div",{className:`as-dot ${I.status}`}),l.jsx("div",{style:{fontSize:22},children:I.emoji}),l.jsx("div",{style:{fontSize:12,fontWeight:700},children:I.label}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:I.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:I.statusLabel}),I.lastActive?l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["⏰ ",I.lastActive]}):l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"无活动记录"}),oe&&l.jsx("button",{className:"as-wake-btn",onClick:ue=>{ue.stopPropagation(),C(I.id)},children:"⚡ 唤醒"})]},I.id)})}),l.jsxs("div",{className:"as-summary",children:[l.jsxs("span",{children:[l.jsx("span",{className:"as-dot running",style:{position:"static",width:8,height:8}})," ",m," 运行中"]}),l.jsxs("span",{children:[l.jsx("span",{className:"as-dot idle",style:{position:"static",width:8,height:8}})," ",T," 待命"]}),g>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot offline",style:{position:"static",width:8,height:8}})," ",g," 离线"]}),M>0&&l.jsxs("span",{children:[l.jsx("span",{className:"as-dot unconfigured",style:{position:"static",width:8,height:8}})," ",M," 未配置"]}),l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["检测于 ",(d.checkedAt||"").substring(11,19)]})]})]}),l.jsx("div",{className:"duty-grid",children:Of.map(I=>{const oe=_.filter(se=>se.org===I.label),ue=oe.some(se=>se.state==="Doing"),pe=oe.some(se=>se.state==="Blocked"),me=R[I.id],be=(me==null?void 0:me.heartbeat)||{status:"idle"},X=pe?"blocked":ue?"busy":be.status==="active"?"active":"idle",le=pe?"⚠️ 阻塞":ue?"⚙️ 执行中":be.status==="active"?"🟢 活跃":"⚪ 候命",ke=pe?"blocked-card":ue?"active-card":"";return l.jsxs("div",{className:`duty-card ${ke}`,children:[l.jsxs("div",{className:"dc-hdr",children:[l.jsx("span",{className:"dc-emoji",children:I.emoji}),l.jsxs("div",{className:"dc-info",children:[l.jsx("div",{className:"dc-name",children:I.label}),l.jsxs("div",{className:"dc-role",children:[I.role," · ",I.rank]})]}),l.jsxs("div",{className:"dc-status",children:[l.jsx("span",{className:`dc-dot ${X}`}),l.jsx("span",{children:le})]})]}),l.jsx("div",{className:"dc-body",children:oe.length>0?oe.map(se=>l.jsxs("div",{className:"dc-task",onClick:()=>S(se.id),children:[l.jsx("div",{className:"dc-task-id",children:se.id}),l.jsx("div",{className:"dc-task-title",children:se.title||"(无标题)"}),se.now&&se.now!=="-"&&l.jsx("div",{className:"dc-task-now",children:se.now.substring(0,70)}),l.jsxs("div",{className:"dc-task-meta",children:[l.jsx("span",{className:`tag st-${se.state}`,children:Ki(se)}),se.block&&se.block!=="无"&&l.jsxs("span",{className:"tag",style:{borderColor:"#ff527044",color:"var(--danger)"},children:["🚫",se.block]})]})]},se.id)):l.jsxs("div",{className:"dc-idle",children:[l.jsx("span",{style:{fontSize:20},children:"🪭"}),l.jsx("span",{children:"候命中"})]})}),l.jsxs("div",{className:"dc-footer",children:[l.jsxs("span",{className:"dc-model",children:["🤖 ",(me==null?void 0:me.model_short)||"待配置"]}),(me==null?void 0:me.last_active)&&l.jsxs("span",{className:"dc-la",children:["⏰ ",me.last_active]})]})]},I.id)})})]})}const Gf=["🥇","🥈","🥉"];function Yf(){var d;const o=q(w=>w.officialsData),p=q(w=>w.selectedOfficial),u=q(w=>w.setSelectedOfficial),k=q(w=>w.loadOfficials),S=q(w=>w.setModalTaskId);if(te.useEffect(()=>{k()},[k]),!(o!=null&&o.officials))return l.jsx("div",{className:"empty",children:"⚠️ 请确保本地服务器已启动"});const D=o.officials,F=o.totals||{tasks_done:0,cost_cny:0},_=Math.max(...D.map(w=>w.tokens_in+w.tokens_out+w.cache_read+w.cache_write),1),R=D.filter(w=>{var m;return((m=w.heartbeat)==null?void 0:m.status)==="active"}),C=D.find(w=>{var m;return w.id===(p||((m=D[0])==null?void 0:m.id))}),z=(C==null?void 0:C.id)||((d=D[0])==null?void 0:d.id);return l.jsxs("div",{children:[R.length>0&&l.jsxs("div",{className:"off-activity",children:[l.jsx("span",{children:"🟢 当前活跃:"}),R.map(w=>l.jsxs("span",{style:{fontSize:12},children:[w.emoji," ",w.role]},w.id)),l.jsx("span",{style:{color:"var(--muted)",fontSize:11,marginLeft:"auto"},children:"其余官员待命"})]}),l.jsxs("div",{className:"off-kpi",children:[l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"var(--acc)"},children:D.length}),l.jsx("div",{className:"kpi-l",children:"在职官员"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{color:"#f5c842"},children:F.tasks_done||0}),l.jsx("div",{className:"kpi-l",children:"累计完成旨意"})]}),l.jsxs("div",{className:"kpi",children:[l.jsxs("div",{className:"kpi-v",style:{color:(F.cost_cny||0)>20?"var(--warn)":"var(--ok)"},children:["¥",F.cost_cny||0]}),l.jsx("div",{className:"kpi-l",children:"累计费用(含缓存)"})]}),l.jsxs("div",{className:"kpi",children:[l.jsx("div",{className:"kpi-v",style:{fontSize:16,paddingTop:4},children:o.top_official||"—"}),l.jsx("div",{className:"kpi-l",children:"功绩最高"})]})]}),l.jsxs("div",{className:"off-layout",children:[l.jsxs("div",{className:"off-ranklist",children:[l.jsx("div",{className:"orl-hdr",children:"功绩排行"}),D.map(w=>{const m=w.heartbeat||{status:"idle"};return l.jsxs("div",{className:`orl-item${z===w.id?" selected":""}`,onClick:()=>u(w.id),children:[l.jsx("span",{style:{minWidth:24,textAlign:"center"},children:w.merit_rank<=3?Gf[w.merit_rank-1]:"#"+w.merit_rank}),l.jsx("span",{children:w.emoji}),l.jsxs("span",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700},children:w.role}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:w.label})]}),l.jsxs("span",{style:{fontSize:11},children:[w.merit_score,"分"]}),l.jsx("span",{className:`dc-dot ${m.status}`,style:{width:8,height:8}})]},w.id)})]}),l.jsx("div",{className:"off-detail",children:C?l.jsx(Xf,{official:C,maxTk:_,onOpenTask:S}):l.jsx("div",{className:"empty",children:"选择左侧官员查看详情"})})]})]})}function Xf({official:o,maxTk:p,onOpenTask:u}){const k=o.heartbeat||{status:"idle",label:"⚪ 待命"},S=o.tokens_in+o.tokens_out+o.cache_read+o.cache_write,D=o.participated_edicts||[],F=[{l:"输入",v:o.tokens_in,color:"#6a9eff"},{l:"输出",v:o.tokens_out,color:"#a07aff"},{l:"缓存读",v:o.cache_read,color:"#2ecc8a"},{l:"缓存写",v:o.cache_write,color:"#f5c842"}];return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:16,alignItems:"center",marginBottom:20},children:[l.jsx("div",{style:{fontSize:40},children:o.emoji}),l.jsxs("div",{style:{flex:1},children:[l.jsx("div",{style:{fontSize:18,fontWeight:800},children:o.role}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[o.label," · ",l.jsx("span",{style:{color:"var(--acc)"},children:o.model_short||o.model})]}),l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginTop:2},children:["🏅 ",o.rank," · 功绩分 ",o.merit_score]})]}),l.jsxs("div",{style:{textAlign:"right"},children:[l.jsx("div",{className:`hb ${k.status}`,style:{marginBottom:4},children:k.label}),o.last_active&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)"},children:["活跃 ",o.last_active]}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:2},children:[o.sessions," 个会话 · ",o.messages," 条消息"]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"功绩统计"}),l.jsxs("div",{style:{display:"flex",gap:16},children:[l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--ok)"},children:o.tasks_done}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"完成旨意"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--warn)"},children:o.tasks_active}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"执行中"})]}),l.jsxs("div",{style:{textAlign:"center"},children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,color:"var(--acc)"},children:o.flow_participations}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)"},children:"流转参与"})]})]})]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"Token 消耗"}),F.map(_=>l.jsxs("div",{style:{marginBottom:6},children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",fontSize:11,marginBottom:2},children:[l.jsx("span",{style:{color:"var(--muted)"},children:_.l}),l.jsx("span",{children:_.v.toLocaleString()})]}),l.jsx("div",{style:{height:6,background:"#0e1320",borderRadius:3},children:l.jsx("div",{style:{height:"100%",width:`${p>0?Math.round(_.v/p*100):0}%`,background:_.color,borderRadius:3}})})]},_.l))]}),l.jsxs("div",{style:{marginBottom:18},children:[l.jsx("div",{className:"sec-title",children:"累计费用"}),l.jsxs("div",{style:{display:"flex",gap:10},children:[l.jsxs("span",{style:{fontSize:12,color:o.cost_cny>10?"var(--danger)":o.cost_cny>3?"var(--warn)":"var(--ok)"},children:[l.jsxs("b",{children:["¥",o.cost_cny]})," 人民币"]}),l.jsxs("span",{style:{fontSize:12},children:[l.jsxs("b",{children:["$",o.cost_usd]})," 美元"]}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["总计 ",S.toLocaleString()," tokens"]})]})]}),l.jsxs("div",{children:[l.jsxs("div",{className:"sec-title",children:["参与旨意(",D.length," 道)"]}),D.length===0?l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无旨意记录"}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:D.map(_=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",padding:"6px 8px",borderRadius:6,cursor:"pointer",border:"1px solid var(--line)"},onClick:()=>u(_.id),children:[l.jsx("span",{style:{fontSize:10,color:"var(--acc)",fontWeight:700},children:_.id}),l.jsx("span",{style:{flex:1,fontSize:12},children:_.title.substring(0,35)}),l.jsx("span",{className:`tag st-${_.state}`,style:{fontSize:10},children:zr[_.state]||_.state})]},_.id))})]})]})}const Zf=[{id:"anthropic/claude-sonnet-4-6",l:"Claude Sonnet 4.6",p:"Anthropic"},{id:"anthropic/claude-opus-4-5",l:"Claude Opus 4.5",p:"Anthropic"},{id:"anthropic/claude-haiku-3-5",l:"Claude Haiku 3.5",p:"Anthropic"},{id:"openai/gpt-4o",l:"GPT-4o",p:"OpenAI"},{id:"openai/gpt-4o-mini",l:"GPT-4o Mini",p:"OpenAI"},{id:"google/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Google"},{id:"copilot/claude-sonnet-4",l:"Claude Sonnet 4",p:"Copilot"},{id:"copilot/claude-opus-4.5",l:"Claude Opus 4.5",p:"Copilot"},{id:"copilot/gpt-4o",l:"GPT-4o",p:"Copilot"},{id:"copilot/gemini-2.5-pro",l:"Gemini 2.5 Pro",p:"Copilot"}];function qf(){var w;const o=q(m=>m.agentConfig),p=q(m=>m.changeLog),u=q(m=>m.loadAgentConfig),k=q(m=>m.toast),[S,D]=te.useState({}),[F,_]=te.useState({});if(te.useEffect(()=>{u()},[u]),te.useEffect(()=>{if(o!=null&&o.agents){const m={};o.agents.forEach(T=>{m[T.id]=T.model}),D(m)}},[o]),!(o!=null&&o.agents))return l.jsx("div",{className:"empty",style:{gridColumn:"1/-1"},children:"⚠️ 请先启动本地服务器"});const R=(w=o.knownModels)!=null&&w.length?o.knownModels.map(m=>({id:m.id,l:m.label,p:m.provider})):Zf,C=(m,T)=>{D(g=>({...g,[m]:T}))},z=m=>{const T=o.agents.find(g=>g.id===m);T&&D(g=>({...g,[m]:T.model}))},d=async m=>{const T=S[m];if(T){_(g=>({...g,[m]:{cls:"pending",text:"⟳ 提交中…"}}));try{const g=await fe.setModel(m,T);g.ok?(_(M=>({...M,[m]:{cls:"ok",text:"✅ 已提交,Gateway 重启中(约5秒)"}})),k(m+" 模型已更改","ok"),setTimeout(()=>u(),5500)):_(M=>({...M,[m]:{cls:"err",text:"❌ "+(g.error||"错误")}}))}catch{_(g=>({...g,[m]:{cls:"err",text:"❌ 无法连接服务器"}}))}}};return l.jsxs("div",{children:[l.jsx("div",{className:"model-grid",children:o.agents.map(m=>{const T=S[m.id]||m.model,g=T!==m.model,M=F[m.id];return l.jsxs("div",{className:"mc-card",children:[l.jsxs("div",{className:"mc-top",children:[l.jsx("span",{className:"mc-emoji",children:m.emoji||"🏛️"}),l.jsxs("div",{children:[l.jsxs("div",{className:"mc-name",children:[m.label," ",l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:m.id})]}),l.jsx("div",{className:"mc-role",children:m.role})]})]}),l.jsxs("div",{className:"mc-cur",children:["当前: ",l.jsx("b",{children:m.model})]}),l.jsx("select",{className:"msel",value:T,onChange:P=>C(m.id,P.target.value),children:R.map(P=>l.jsxs("option",{value:P.id,children:[P.l," (",P.p,")"]},P.id))}),l.jsxs("div",{className:"mc-btns",children:[l.jsx("button",{className:"btn btn-p",disabled:!g,onClick:()=>d(m.id),children:"应用"}),l.jsx("button",{className:"btn btn-g",onClick:()=>z(m.id),children:"重置"})]}),M&&l.jsx("div",{className:`mc-st ${M.cls}`,children:M.text})]},m.id)})}),l.jsxs("div",{style:{marginTop:24},children:[l.jsx("div",{className:"sec-title",children:"变更日志"}),l.jsx("div",{className:"cl-list",children:p!=null&&p.length?[...p].reverse().slice(0,15).map((m,T)=>l.jsxs("div",{className:"cl-row",children:[l.jsx("span",{className:"cl-t",children:(m.at||"").substring(0,16).replace("T"," ")}),l.jsx("span",{className:"cl-a",children:m.agentId}),l.jsxs("span",{className:"cl-c",children:[l.jsx("b",{children:m.oldModel})," → ",l.jsx("b",{children:m.newModel}),m.rolledBack&&l.jsx("span",{style:{color:"var(--danger)",fontSize:10,border:"1px solid #ff527044",padding:"1px 5px",borderRadius:3,marginLeft:4},children:"⚠ 已回滚"})]})]},T)):l.jsx("div",{style:{fontSize:12,color:"var(--muted)",padding:"8px 0"},children:"暂无变更"})})]})]})}const Jf=[{label:"obra/superpowers",emoji:"⚡",stars:"66.9k",desc:"完整开发工作流技能集",skills:[{name:"brainstorming",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"},{name:"test-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/test-driven-development/SKILL.md"},{name:"systematic-debugging",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/systematic-debugging/SKILL.md"},{name:"subagent-driven-development",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/subagent-driven-development/SKILL.md"},{name:"writing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/writing-plans/SKILL.md"},{name:"executing-plans",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/executing-plans/SKILL.md"},{name:"requesting-code-review",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/requesting-code-review/SKILL.md"},{name:"root-cause-tracing",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/root-cause-tracing/SKILL.md"},{name:"verification-before-completion",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/verification-before-completion/SKILL.md"},{name:"dispatching-parallel-agents",url:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/dispatching-parallel-agents/SKILL.md"}]},{label:"anthropics/skills",emoji:"🏛️",stars:"官方",desc:"Anthropic 官方技能库",skills:[{name:"docx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md"},{name:"pdf",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md"},{name:"xlsx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md"},{name:"pptx",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md"},{name:"mcp-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md"},{name:"frontend-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md"},{name:"web-artifacts-builder",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md"},{name:"webapp-testing",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md"},{name:"algorithmic-art",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md"},{name:"canvas-design",url:"https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md"}]},{label:"ComposioHQ/awesome-claude-skills",emoji:"🌐",stars:"39.2k",desc:"100+ 社区精选技能",skills:[{name:"github-integration",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/github-integration/SKILL.md"},{name:"data-analysis",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/data-analysis/SKILL.md"},{name:"code-review",url:"https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/code-review/SKILL.md"}]}];function ep(){const o=q(x=>x.agentConfig),p=q(x=>x.loadAgentConfig),u=q(x=>x.toast),[k,S]=te.useState(null),[D,F]=te.useState(null),[_,R]=te.useState({name:"",desc:"",trigger:""}),[C,z]=te.useState(!1),[d,w]=te.useState("local"),[m,T]=te.useState([]),[g,M]=te.useState(!1),[P,B]=te.useState(!1),[Z,I]=te.useState({agentId:"",skillName:"",sourceUrl:"",description:""}),[oe,ue]=te.useState(!1),[pe,me]=te.useState(null),[be,X]=te.useState(null),[le,ke]=te.useState(null),[se,J]=te.useState("");te.useEffect(()=>{p()},[p]),te.useEffect(()=>{d==="remote"&&Ne()},[d]);const Ne=async()=>{M(!0);try{const x=await fe.remoteSkillsList();x.ok&&T(x.remoteSkills||[])}catch{u("远程技能列表加载失败","err")}M(!1)},_e=async(x,U)=>{S({agentId:x,name:U,content:"⟳ 加载中…",path:""});try{const ee=await fe.skillContent(x,U);ee.ok?S({agentId:x,name:U,content:ee.content||"",path:ee.path||""}):S({agentId:x,name:U,content:"❌ "+(ee.error||"无法读取"),path:""})}catch{S({agentId:x,name:U,content:"❌ 服务器连接失败",path:""})}},xe=(x,U)=>{F({agentId:x,agentLabel:U}),R({name:"",desc:"",trigger:""})},E=async x=>{if(x.preventDefault(),!(!D||!_.name)){z(!0);try{const U=await fe.addSkill(D.agentId,_.name,_.desc,_.trigger);U.ok?(u(`✅ 技能 ${_.name} 已添加到 ${D.agentLabel}`,"ok"),F(null),p()):u(U.error||"添加失败","err")}catch{u("服务器连接失败","err")}z(!1)}},Q=async x=>{x.preventDefault();const{agentId:U,skillName:ee,sourceUrl:he,description:ve}=Z;if(!(!U||!ee||!he)){ue(!0);try{const Me=await fe.addRemoteSkill(U,ee,he,ve);Me.ok?(u(`✅ 远程技能 ${ee} 已添加到 ${U}`,"ok"),B(!1),I({agentId:"",skillName:"",sourceUrl:"",description:""}),Ne(),p()):u(Me.error||"添加失败","err")}catch{u("服务器连接失败","err")}ue(!1)}},W=async x=>{const U=`${x.agentId}/${x.skillName}`;me(U);try{const ee=await fe.updateRemoteSkill(x.agentId,x.skillName);ee.ok?(u(`✅ 技能 ${x.skillName} 已更新`,"ok"),Ne()):u(ee.error||"更新失败","err")}catch{u("服务器连接失败","err")}me(null)},v=async x=>{const U=`${x.agentId}/${x.skillName}`;X(U);try{const ee=await fe.removeRemoteSkill(x.agentId,x.skillName);ee.ok?(u(`🗑️ 技能 ${x.skillName} 已移除`,"ok"),Ne(),p()):u(ee.error||"移除失败","err")}catch{u("服务器连接失败","err")}X(null)},L=async(x,U)=>{if(!se){u("请先选择目标 Agent","err");return}try{const ee=await fe.addRemoteSkill(se,U,x,"");ee.ok?(u(`✅ ${U} → ${se}`,"ok"),Ne(),p()):u(ee.error||"导入失败","err")}catch{u("服务器连接失败","err")}};if(!(o!=null&&o.agents))return l.jsx("div",{className:"empty",children:"无法加载"});const ae=l.jsx("div",{children:l.jsx("div",{className:"skills-grid",children:o.agents.map(x=>l.jsxs("div",{className:"sk-card",children:[l.jsxs("div",{className:"sk-hdr",children:[l.jsx("span",{className:"sk-emoji",children:x.emoji||"🏛️"}),l.jsx("span",{className:"sk-name",children:x.label}),l.jsxs("span",{className:"sk-cnt",children:[(x.skills||[]).length," 技能"]})]}),l.jsx("div",{className:"sk-list",children:(x.skills||[]).length?(x.skills||[]).map(U=>l.jsxs("div",{className:"sk-item",onClick:()=>_e(x.id,U.name),children:[l.jsxs("span",{className:"si-name",children:["📦 ",U.name]}),l.jsx("span",{className:"si-desc",children:U.description||"无描述"}),l.jsx("span",{className:"si-arrow",children:"›"})]},U.name)):l.jsx("div",{className:"sk-empty",children:"暂无 Skills"})}),l.jsx("div",{className:"sk-add",onClick:()=>xe(x.id,x.label),children:"+ 添加技能"})]},x.id))})}),ce=l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:10,marginBottom:20,flexWrap:"wrap",alignItems:"center"},children:[l.jsx("button",{style:{padding:"8px 18px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600,fontSize:13},onClick:()=>{B(!0),ke(null)},children:"+ 添加远程 Skill"}),l.jsx("button",{style:{padding:"8px 14px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:8,cursor:"pointer",fontSize:12},onClick:Ne,children:"⟳ 刷新列表"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:4},children:["共 ",m.length," 个远程技能"]})]}),l.jsxs("div",{style:{marginBottom:24},children:[l.jsx("div",{style:{fontSize:12,fontWeight:700,color:"var(--muted)",letterSpacing:".06em",marginBottom:10},children:"🌐 社区技能源 — 一键导入"}),l.jsx("div",{style:{display:"flex",gap:10,flexWrap:"wrap"},children:Jf.map(x=>l.jsxs("div",{onClick:()=>ke((le==null?void 0:le.label)===x.label?null:x),style:{padding:"8px 14px",background:(le==null?void 0:le.label)===x.label?"#0d1f45":"var(--panel)",border:`1px solid ${(le==null?void 0:le.label)===x.label?"var(--acc)":"var(--line)"}`,borderRadius:10,cursor:"pointer",fontSize:12,transition:"all .15s"},children:[l.jsx("span",{style:{marginRight:6},children:x.emoji}),l.jsx("b",{style:{color:"var(--text)"},children:x.label}),l.jsxs("span",{style:{marginLeft:6,color:"#f0b429",fontSize:11},children:["★ ",x.stars]}),l.jsx("span",{style:{marginLeft:8,color:"var(--muted)"},children:x.desc})]},x.label))}),le&&l.jsxs("div",{style:{marginTop:14,background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:16},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,marginBottom:14},children:[l.jsx("span",{style:{fontSize:12,fontWeight:600},children:"目标 Agent:"}),l.jsxs("select",{value:se,onChange:x=>J(x.target.value),style:{padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(x=>l.jsxs("option",{value:x.id,children:[x.emoji," ",x.label," (",x.id,")"]},x.id))]})]}),l.jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(260px, 1fr))",gap:8},children:le.skills.map(x=>{const U=m.some(ee=>ee.skillName===x.name&&ee.agentId===se);return l.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"8px 12px",background:"var(--panel2)",borderRadius:8,border:"1px solid var(--line)"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{fontSize:12,fontWeight:600},children:["📦 ",x.name]}),l.jsx("div",{style:{fontSize:10,color:"var(--muted)",wordBreak:"break-all",maxWidth:180},children:x.url.split("/").slice(-2).join("/")})]}),U?l.jsx("span",{style:{fontSize:10,color:"#4caf88",fontWeight:600},children:"✓ 已导入"}):l.jsx("button",{onClick:()=>L(x.url,x.name),style:{padding:"4px 10px",background:"var(--acc)",color:"#fff",border:"none",borderRadius:6,cursor:"pointer",fontSize:11,whiteSpace:"nowrap"},children:"导入"})]},x.name)})})]})]}),g?l.jsx("div",{style:{textAlign:"center",padding:"40px 0",color:"var(--muted)",fontSize:13},children:"⟳ 加载中…"}):m.length===0?l.jsxs("div",{style:{textAlign:"center",padding:"40px",background:"var(--panel)",borderRadius:12,border:"1px dashed var(--line)"},children:[l.jsx("div",{style:{fontSize:32,marginBottom:10},children:"🌐"}),l.jsx("div",{style:{fontSize:14,color:"var(--muted)"},children:"尚无远程技能"}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginTop:6},children:"从社区技能源快速导入,或手动添加 URL"})]}):l.jsx("div",{style:{display:"flex",flexDirection:"column",gap:10},children:m.map(x=>{var Me;const U=`${x.agentId}/${x.skillName}`,ee=pe===U,he=be===U,ve=o.agents.find(mn=>mn.id===x.agentId);return l.jsxs("div",{style:{background:"var(--panel)",border:"1px solid var(--line)",borderRadius:12,padding:"14px 18px",display:"grid",gridTemplateColumns:"1fr auto",gap:12,alignItems:"center"},children:[l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginBottom:6},children:[l.jsxs("span",{style:{fontSize:14,fontWeight:700},children:["📦 ",x.skillName]}),l.jsx("span",{style:{fontSize:10,padding:"2px 8px",borderRadius:999,background:x.status==="valid"?"#0d3322":"#3d1111",color:x.status==="valid"?"#4caf88":"#ff5270",fontWeight:600},children:x.status==="valid"?"✓ 有效":"✗ 文件丢失"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",background:"var(--panel2)",padding:"2px 8px",borderRadius:6},children:[ve==null?void 0:ve.emoji," ",(ve==null?void 0:ve.label)||x.agentId]})]}),x.description&&l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:4},children:x.description}),l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",display:"flex",gap:16,flexWrap:"wrap"},children:[l.jsxs("span",{children:["🔗 ",l.jsx("a",{href:x.sourceUrl,target:"_blank",rel:"noreferrer",style:{color:"var(--acc)",textDecoration:"none"},children:x.sourceUrl.length>60?x.sourceUrl.slice(0,60)+"…":x.sourceUrl})]}),l.jsxs("span",{children:["📅 ",x.lastUpdated?x.lastUpdated.slice(0,10):(Me=x.addedAt)==null?void 0:Me.slice(0,10)]})]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{onClick:()=>_e(x.agentId,x.skillName),style:{padding:"6px 12px",background:"transparent",color:"var(--muted)",border:"1px solid var(--line)",borderRadius:6,cursor:"pointer",fontSize:11},children:"查看"}),l.jsx("button",{onClick:()=>W(x),disabled:ee,style:{padding:"6px 12px",background:"transparent",color:"var(--acc)",border:"1px solid var(--acc)",borderRadius:6,cursor:"pointer",fontSize:11},children:ee?"⟳":"更新"}),l.jsx("button",{onClick:()=>v(x),disabled:he,style:{padding:"6px 12px",background:"transparent",color:"#ff5270",border:"1px solid #ff5270",borderRadius:6,cursor:"pointer",fontSize:11},children:he?"⟳":"删除"})]})]},U)})})]});return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:4,marginBottom:20,borderBottom:"1px solid var(--line)",paddingBottom:0},children:[{key:"local",label:"🏛️ 本地技能",count:o.agents.reduce((x,U)=>{var ee;return x+(((ee=U.skills)==null?void 0:ee.length)||0)},0)},{key:"remote",label:"🌐 远程技能",count:m.length}].map(x=>l.jsxs("div",{onClick:()=>w(x.key),style:{padding:"8px 18px",cursor:"pointer",fontSize:13,borderRadius:"8px 8px 0 0",fontWeight:d===x.key?700:400,background:d===x.key?"var(--panel)":"transparent",color:d===x.key?"var(--text)":"var(--muted)",border:d===x.key?"1px solid var(--line)":"1px solid transparent",borderBottom:d===x.key?"1px solid var(--panel)":"1px solid transparent",position:"relative",bottom:-1,transition:"all .15s"},children:[x.label,x.count>0&&l.jsx("span",{style:{marginLeft:6,fontSize:10,padding:"1px 6px",borderRadius:999,background:"#1a2040",color:"var(--acc)"},children:x.count})]},x.key))}),d==="local"?ae:ce,k&&l.jsx("div",{className:"modal-bg open",onClick:()=>S(null),children:l.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>S(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:k.agentId.toUpperCase()}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:16},children:["📦 ",k.name]}),l.jsxs("div",{className:"sk-modal-body",children:[l.jsx("div",{className:"sk-md",style:{whiteSpace:"pre-wrap",fontSize:12,lineHeight:1.7},children:k.content}),k.path&&l.jsxs("div",{className:"sk-path",style:{fontSize:10,color:"var(--muted)",marginTop:12},children:["📂 ",k.path]})]})]})]})}),D&&l.jsx("div",{className:"modal-bg open",onClick:()=>F(null),children:l.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>F(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsxs("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:["为 ",D.agentLabel," 添加技能"]}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"+ 新增 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:14,marginBottom:18,fontSize:12,lineHeight:1.7,color:"var(--muted)"},children:[l.jsx("b",{style:{color:"var(--text)"},children:"📋 Skill 规范说明"}),l.jsx("br",{}),"• 技能名称使用",l.jsx("b",{style:{color:"var(--text)"},children:"小写英文 + 连字符"}),l.jsx("br",{}),"• 创建后会生成模板文件 SKILL.md",l.jsx("br",{}),"• 技能会在 agent 收到相关任务时",l.jsx("b",{style:{color:"var(--text)"},children:"自动激活"})]}),l.jsxs("form",{onSubmit:E,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 data-analysis, code-review",value:_.name,onChange:x=>R(U=>({...U,name:x.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"技能描述"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:_.desc,onChange:x=>R(U=>({...U,desc:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"触发条件(可选)"}),l.jsx("input",{type:"text",placeholder:"何时激活此技能",value:_.trigger,onChange:x=>R(U=>({...U,trigger:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>F(null),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:C,style:{padding:"8px 20px",fontSize:13,background:"var(--acc)",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:C?"⟳ 创建中…":"📦 创建技能"})]})]})]})]})}),P&&l.jsx("div",{className:"modal-bg open",onClick:()=>B(!1),children:l.jsxs("div",{className:"modal",style:{maxWidth:520},onClick:x=>x.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>B(!1),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"#a07aff",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"远程技能管理"}),l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:18},children:"🌐 添加远程 Skill"}),l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:10,padding:12,marginBottom:18,fontSize:11,color:"var(--muted)",lineHeight:1.7},children:["支持 GitHub Raw URL,如:",l.jsx("br",{}),l.jsx("code",{style:{color:"var(--acc)",fontSize:10},children:"https://raw.githubusercontent.com/obra/superpowers/refs/heads/main/skills/brainstorming/SKILL.md"})]}),l.jsxs("form",{onSubmit:Q,style:{display:"flex",flexDirection:"column",gap:14},children:[l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["目标 Agent ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsxs("select",{required:!0,value:Z.agentId,onChange:x=>I(U=>({...U,agentId:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13},children:[l.jsx("option",{value:"",children:"— 选择 Agent —"}),o.agents.map(x=>l.jsxs("option",{value:x.id,children:[x.emoji," ",x.label," (",x.id,")"]},x.id))]})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["技能名称 ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"text",required:!0,placeholder:"如 brainstorming, code-review",value:Z.skillName,onChange:x=>I(U=>({...U,skillName:x.target.value.toLowerCase().replace(/[^a-z0-9-]/g,"")})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{children:[l.jsxs("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:["源 URL ",l.jsx("span",{style:{color:"#ff5270"},children:"*"})]}),l.jsx("input",{type:"url",required:!0,placeholder:"https://raw.githubusercontent.com/...",value:Z.sourceUrl,onChange:x=>I(U=>({...U,sourceUrl:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsxs("div",{children:[l.jsx("label",{style:{fontSize:12,fontWeight:600,display:"block",marginBottom:6},children:"描述(可选)"}),l.jsx("input",{type:"text",placeholder:"一句话说明用途",value:Z.description,onChange:x=>I(U=>({...U,description:x.target.value})),style:{width:"100%",padding:"10px 12px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:8,color:"var(--text)",fontSize:13,outline:"none"}})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end",marginTop:4},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:()=>B(!1),style:{padding:"8px 20px"},children:"取消"}),l.jsx("button",{type:"submit",disabled:oe,style:{padding:"8px 20px",fontSize:13,background:"#a07aff",color:"#fff",border:"none",borderRadius:8,cursor:"pointer",fontWeight:600},children:oe?"⟳ 下载中…":"🌐 添加远程技能"})]})]})]})]})})]})}function tp(){const o=q(k=>k.agentConfig),p={},u={};return o!=null&&o.agents&&o.agents.forEach(k=>{p[k.id]=k.emoji||"🏛️",u[k.id]=k.label||k.id}),{emojiMap:p,labelMap:u}}function Fl(o){const p=(o.id||"").match(/^OC-(\w+)-/);return p?p[1]:(o.org||"").replace(/省|部/g,"").toLowerCase()}function cc(o,p){let u=o.title||"";if(u==="heartbeat 会话")return"💓 心跳检测";const k=u.match(/^agent:(\w+):(\w+)/);if(k){const S=p[k[1]]||k[1];return k[2]==="main"?S+" · 主会话":k[2]==="subagent"?S+" · 子任务执行":k[2]==="cron"?S+" · 定时任务":S+" · "+k[2]}return u.replace(/ 会话$/,"")||o.id}function dc(o){const p=o.now||"";return p.includes("feishu/direct")?{icon:"💬",text:"飞书对话"}:p.includes("feishu")?{icon:"💬",text:"飞书"}:p.includes("webchat")?{icon:"🌐",text:"WebChat"}:p.includes("cron")?{icon:"⏰",text:"定时"}:p.includes("direct")?{icon:"📨",text:"直连"}:{icon:"🔗",text:"会话"}}function np(o){const p=o.activity||[];for(let u=p.length-1;u>=0;u--){const k=p[u];if(k.kind==="assistant"){let S=k.text||"";if(S.startsWith("NO_REPLY")||S.startsWith("Reasoning:"))continue;return S=S.replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").replace(/^#+\s/gm,"").trim(),S.substring(0,120)+(S.length>120?"…":"")}}return""}function rp(){const o=q(d=>d.liveStatus),p=q(d=>d.sessFilter),u=q(d=>d.setSessFilter),{emojiMap:k,labelMap:S}=tp(),[D,F]=te.useState(null),R=((o==null?void 0:o.tasks)||[]).filter(d=>!Jt(d));let C=R;p==="active"?C=R.filter(d=>!["Done","Cancelled"].includes(d.state)):p!=="all"&&(C=R.filter(d=>Fl(d)===p));const z=[...new Set(R.map(Fl))];return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:[{key:"all",label:`全部 (${R.length})`},{key:"active",label:"活跃"},...z.slice(0,8).map(d=>({key:d,label:S[d]||d}))].map(d=>l.jsx("span",{className:`sess-filter${p===d.key?" active":""}`,onClick:()=>u(d.key),children:d.label},d.key))}),l.jsx("div",{className:"sess-grid",children:C.length?C.map(d=>{const w=Fl(d),m=k[w]||"🏛️",T=S[w]||d.org||w,g=d.heartbeat||{status:"unknown",label:""},M=dc(d),P=cc(d,S),B=np(d),I=(d.sourceMeta||{}).totalTokens,oe=d.eta||"",ue=g.status==="active"?"🟢":g.status==="warn"?"🟡":g.status==="stalled"?"🔴":"⚪",pe=d.state||"Unknown";return l.jsxs("div",{className:"sess-card",onClick:()=>F(d),children:[l.jsxs("div",{className:"sc-top",children:[l.jsx("span",{className:"sc-emoji",children:m}),l.jsx("div",{style:{flex:1,minWidth:0},children:l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{className:"sc-agent",children:T}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)",background:"var(--panel2)",padding:"2px 6px",borderRadius:4},children:[M.icon," ",M.text]})]})}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{title:g.label||"",children:ue}),l.jsx("span",{className:`tag st-${pe}`,style:{fontSize:10},children:zr[pe]||pe})]})]}),l.jsx("div",{className:"sc-title",children:P}),B&&l.jsx("div",{style:{fontSize:11,color:"var(--muted)",lineHeight:1.5,marginBottom:8,borderLeft:"2px solid var(--line)",paddingLeft:8,maxHeight:40,overflow:"hidden"},children:B}),l.jsxs("div",{className:"sc-meta",children:[I?l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["🪙 ",I.toLocaleString()," tokens"]}):null,oe?l.jsx("span",{className:"sc-time",children:Uf(oe)}):null]})]},d.id)}):l.jsx("div",{style:{fontSize:13,color:"var(--muted)",padding:24,textAlign:"center",gridColumn:"1/-1"},children:"暂无小任务/会话数据"})}),D&&l.jsx(lp,{task:D,labelMap:S,emojiMap:k,onClose:()=>F(null)})]})}function lp({task:o,labelMap:p,emojiMap:u,onClose:k}){const S=Fl(o),D=u[S]||"🏛️",F=cc(o,p),_=dc(o),R=o.heartbeat||{label:""},C=o.sourceMeta||{},z=o.activity||[],d=o.state||"Unknown",w=C.totalTokens,m=C.inputTokens,T=C.outputTokens;return l.jsx("div",{className:"modal-bg open",onClick:k,children:l.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:k,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[D," ",F]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${d}`,children:zr[d]||d}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:[_.icon," ",_.text]}),R.label&&l.jsx("span",{style:{fontSize:11},children:R.label})]}),l.jsxs("div",{style:{display:"flex",gap:14,marginBottom:18,flexWrap:"wrap"},children:[w!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700,color:"var(--acc)"},children:w.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"总 Tokens"})]}),m!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:m.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输入"})]}),T!=null&&l.jsxs("div",{style:{background:"var(--panel2)",padding:"10px 16px",borderRadius:8,fontSize:12},children:[l.jsx("div",{style:{fontSize:16,fontWeight:700},children:T.toLocaleString()}),l.jsx("div",{style:{color:"var(--muted)",fontSize:10},children:"输出"})]})]}),l.jsxs("div",{style:{fontSize:12,fontWeight:700,marginBottom:8},children:["📋 最近活动 ",l.jsxs("span",{style:{fontWeight:400,color:"var(--muted)"},children:["(",z.length," 条)"]})]}),l.jsx("div",{style:{maxHeight:350,overflowY:"auto",border:"1px solid var(--line)",borderRadius:10,background:"var(--panel2)"},children:z.length?z.slice(-15).reverse().map((g,M)=>{const P=g.kind||"",B=P==="assistant"?"🤖":P==="tool"?"🔧":P==="user"?"👤":"📝",Z=P==="assistant"?"回复":P==="tool"?"工具":P==="user"?"用户":"事件";let I=(g.text||"").replace(/\[\[.*?\]\]/g,"").replace(/\*\*/g,"").trim();I.length>200&&(I=I.substring(0,200)+"…");const oe=(g.at||"").substring(11,19);return l.jsxs("div",{style:{padding:"8px 12px",borderBottom:"1px solid var(--line)",fontSize:12,lineHeight:1.5},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[l.jsx("span",{children:B}),l.jsx("span",{style:{fontWeight:600,fontSize:11},children:Z}),l.jsx("span",{style:{color:"var(--muted)",fontSize:10,marginLeft:"auto"},children:oe})]}),l.jsx("div",{style:{color:"var(--muted)"},children:I})]},M)}):l.jsx("div",{style:{padding:16,color:"var(--muted)",fontSize:12,textAlign:"center"},children:"暂无活动记录"})}),o.output&&o.output!=="-"&&l.jsxs("div",{style:{fontSize:10,color:"var(--muted)",marginTop:12,wordBreak:"break-all",borderTop:"1px solid var(--line)",paddingTop:8},children:["📂 ",o.output]})]})]})})}function sp(){const o=q(C=>C.liveStatus),[p,u]=te.useState("all"),[k,S]=te.useState(null),D=q(C=>C.toast);let _=((o==null?void 0:o.tasks)||[]).filter(C=>Jt(C)&&["Done","Cancelled"].includes(C.state));p!=="all"&&(_=_.filter(C=>C.state===p));const R=C=>{const z=C.flow_log||[];let d=`# 📜 奏折 · ${C.title} - -`;if(d+=`- **任务编号**: ${C.id} -`,d+=`- **状态**: ${C.state} -`,d+=`- **负责部门**: ${C.org} -`,z.length){const w=z[0].at?z[0].at.substring(0,19).replace("T"," "):"未知",m=z[z.length-1].at?z[z.length-1].at.substring(0,19).replace("T"," "):"未知";d+=`- **开始时间**: ${w} -`,d+=`- **完成时间**: ${m} -`}d+=` -## 流转记录 - -`;for(const w of z)d+=`- **${w.from}** → **${w.to}** - ${w.remark} - _${(w.at||"").substring(0,19)}_ - -`;C.output&&C.output!=="-"&&(d+=`## 产出物 - -\`${C.output}\` -`),navigator.clipboard.writeText(d).then(()=>D("✅ 奏折已复制为 Markdown","ok"),()=>D("复制失败","err"))};return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",gap:8,marginBottom:16,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:12,color:"var(--muted)"},children:"筛选:"}),[{key:"all",label:"全部"},{key:"Done",label:"✅ 已完成"},{key:"Cancelled",label:"🚫 已取消"}].map(C=>l.jsx("span",{className:`sess-filter${p===C.key?" active":""}`,onClick:()=>u(C.key),children:C.label},C.key))]}),l.jsx("div",{className:"mem-list",children:_.length?_.map(C=>{const z=C.flow_log||[],d=[...new Set(z.map(g=>g.from).concat(z.map(g=>g.to)).filter(g=>g&&g!=="皇上"))],w=z.length?(z[0].at||"").substring(0,16).replace("T"," "):"",m=z.length?(z[z.length-1].at||"").substring(0,16).replace("T"," "):"",T=C.state==="Done"?"✅":"🚫";return l.jsxs("div",{className:"mem-card",onClick:()=>S(C),children:[l.jsx("div",{className:"mem-icon",children:"📜"}),l.jsxs("div",{className:"mem-info",children:[l.jsxs("div",{className:"mem-title",children:[T," ",C.title||C.id]}),l.jsxs("div",{className:"mem-sub",children:[C.id," · ",C.org||""," · 流转 ",z.length," 步"]}),l.jsx("div",{className:"mem-tags",children:d.slice(0,5).map(g=>l.jsx("span",{className:"mem-tag",children:g},g))})]}),l.jsxs("div",{className:"mem-right",children:[l.jsx("span",{className:"mem-date",children:w}),m!==w&&l.jsx("span",{className:"mem-date",children:m})]})]},C.id)}):l.jsx("div",{className:"mem-empty",children:"暂无奏折 — 任务完成后自动生成"})}),k&&l.jsx(ip,{task:k,onClose:()=>S(null),onExport:R})]})}function ip({task:o,onClose:p,onExport:u}){const k=o.flow_log||[],S=o.state||"Unknown",D=S==="Done"?"✅":S==="Cancelled"?"🚫":"🔄",F=[...new Set(k.map(m=>m.from).concat(k.map(m=>m.to)).filter(m=>m&&m!=="皇上"))],_=[],R=[],C=[],z=[],d=[];for(const m of k)m.from==="皇上"?_.push(m):m.to==="中书省"||m.from==="中书省"?R.push(m):m.to==="门下省"||m.from==="门下省"?C.push(m):m.remark&&(m.remark.includes("完成")||m.remark.includes("回奏"))?d.push(m):z.push(m);const w=(m,T,g)=>g.length?l.jsxs("div",{style:{marginBottom:18},children:[l.jsxs("div",{style:{fontSize:13,fontWeight:700,marginBottom:10},children:[T," ",m]}),l.jsx("div",{className:"md-timeline",children:g.map((M,P)=>{var Z,I;const B=(Z=M.remark)!=null&&Z.includes("✅")?"green":(I=M.remark)!=null&&I.includes("驳")?"red":"";return l.jsxs("div",{className:"md-tl-item",children:[l.jsx("div",{className:`md-tl-dot ${B}`}),l.jsxs("div",{style:{display:"flex",gap:6,alignItems:"baseline"},children:[l.jsx("span",{className:"md-tl-from",children:M.from}),l.jsxs("span",{className:"md-tl-to",children:["→ ",M.to]})]}),l.jsx("div",{className:"md-tl-remark",children:M.remark}),l.jsx("div",{className:"md-tl-time",children:(M.at||"").substring(0,19).replace("T"," ")})]},P)})})]}):null;return l.jsx("div",{className:"modal-bg open",onClick:p,children:l.jsxs("div",{className:"modal",onClick:m=>m.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:p,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:o.id}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[D," ",o.title||o.id]}),l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:18,flexWrap:"wrap"},children:[l.jsx("span",{className:`tag st-${S}`,children:zr[S]||S}),l.jsx("span",{style:{fontSize:11,color:"var(--muted)"},children:o.org}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["流转 ",k.length," 步"]}),F.map(m=>l.jsx("span",{className:"mem-tag",children:m},m))]}),o.now&&l.jsx("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:"10px 14px",marginBottom:18,fontSize:12,color:"var(--muted)"},children:o.now}),w("圣旨原文","👑",_),w("中书规划","📋",R),w("门下审议","🔍",C),w("六部执行","⚔️",z),w("汇总回奏","📨",d),o.output&&o.output!=="-"&&l.jsxs("div",{style:{marginTop:12,paddingTop:12,borderTop:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,marginBottom:4},children:"📦 产出物"}),l.jsx("code",{style:{fontSize:11,wordBreak:"break-all"},children:o.output})]}),l.jsx("div",{style:{display:"flex",gap:8,marginTop:16,justifyContent:"flex-end"},children:l.jsx("button",{className:"btn btn-g",onClick:()=>u(o),style:{fontSize:12,padding:"6px 16px"},children:"📋 复制奏折"})})]})]})})}function op(){const o=q(g=>g.tplCatFilter),p=q(g=>g.setTplCatFilter),u=q(g=>g.toast),k=q(g=>g.loadAll),[S,D]=te.useState(null),[F,_]=te.useState({}),[R,C]=te.useState("");let z=bf;o!=="全部"&&(z=z.filter(g=>g.cat===o));const d=g=>{const M={};g.params.forEach(P=>{M[P.key]=P.default||""}),_(M),D(g),C("")},w=g=>{let M=g.command;for(const P of g.params)M=M.replace(new RegExp("\\{"+P.key+"\\}","g"),F[P.key]||P.default||"");return M},m=()=>{S&&C(w(S))},T=async g=>{if(g.preventDefault(),!S)return;const M=w(S);if(!M.trim()){u("请填写必填参数","err");return}try{const P=await fe.agentsStatus();if(P.ok&&P.gateway&&!P.gateway.alive&&(u("⚠️ Gateway 未启动,任务将无法派发!","err"),!confirm("Gateway 未启动,继续?")))return}catch{}if(confirm(`确认下旨? - -${M.substring(0,200)}${M.length>200?"…":""}`))try{const P={};for(const Z of S.params)P[Z.key]=F[Z.key]||Z.default||"";const B=await fe.createTask({title:M.substring(0,120),org:"中书省",targetDept:S.depts[0]||"",priority:"normal",templateId:S.id,params:P});B.ok?(u(`📜 ${B.taskId} 旨意已下达`,"ok"),D(null),k()):u(B.error||"下旨失败","err")}catch{u("⚠️ 服务器连接失败","err")}};return l.jsxs("div",{children:[l.jsx("div",{style:{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"},children:$f.map(g=>l.jsxs("span",{className:`tpl-cat${o===g.name?" active":""}`,onClick:()=>p(g.name),children:[g.icon," ",g.name]},g.name))}),l.jsx("div",{className:"tpl-grid",children:z.map(g=>l.jsxs("div",{className:"tpl-card",children:[l.jsxs("div",{className:"tpl-top",children:[l.jsx("span",{className:"tpl-icon",children:g.icon}),l.jsx("span",{className:"tpl-name",children:g.name})]}),l.jsx("div",{className:"tpl-desc",children:g.desc}),l.jsxs("div",{className:"tpl-footer",children:[g.depts.map(M=>l.jsx("span",{className:"tpl-dept",children:M},M)),l.jsxs("span",{className:"tpl-est",children:[g.est," · ",g.cost]}),l.jsx("button",{className:"tpl-go",onClick:()=>d(g),children:"下旨"})]})]},g.id))}),S&&l.jsx("div",{className:"modal-bg open",onClick:()=>D(null),children:l.jsxs("div",{className:"modal",onClick:g=>g.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:()=>D(null),children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{style:{fontSize:11,color:"var(--acc)",fontWeight:700,letterSpacing:".04em",marginBottom:4},children:"圣旨模板"}),l.jsxs("div",{style:{fontSize:20,fontWeight:800,marginBottom:6},children:[S.icon," ",S.name]}),l.jsx("div",{style:{fontSize:12,color:"var(--muted)",marginBottom:18},children:S.desc}),l.jsxs("div",{style:{display:"flex",gap:6,marginBottom:18,flexWrap:"wrap"},children:[S.depts.map(g=>l.jsx("span",{className:"tpl-dept",children:g},g)),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:"auto"},children:[S.est," · ",S.cost]})]}),l.jsxs("form",{className:"tpl-form",onSubmit:T,children:[S.params.map(g=>l.jsxs("div",{className:"tpl-field",children:[l.jsxs("label",{className:"tpl-label",children:[g.label,g.required&&l.jsx("span",{style:{color:"#ff5270"},children:" *"})]}),g.type==="textarea"?l.jsx("textarea",{className:"tpl-input",style:{minHeight:80,resize:"vertical"},required:g.required,value:F[g.key]||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value}))}):g.type==="select"?l.jsx("select",{className:"tpl-input",value:F[g.key]||g.default||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value})),children:(g.options||[]).map(M=>l.jsx("option",{children:M},M))}):l.jsx("input",{className:"tpl-input",type:"text",required:g.required,value:F[g.key]||"",onChange:M=>_(P=>({...P,[g.key]:M.target.value}))})]},g.key)),R&&l.jsxs("div",{style:{background:"var(--panel2)",border:"1px solid var(--line)",borderRadius:8,padding:12,marginBottom:14,fontSize:12,color:"var(--muted)"},children:[l.jsx("div",{style:{fontSize:11,fontWeight:600,color:"var(--text)",marginBottom:6},children:"📜 将发送给中书省的旨意:"}),l.jsx("div",{style:{whiteSpace:"pre-wrap",lineHeight:1.6},children:R})]}),l.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[l.jsx("button",{type:"button",className:"btn btn-g",onClick:m,style:{padding:"8px 16px",fontSize:12},children:"👁 预览旨意"}),l.jsx("button",{type:"submit",className:"tpl-go",style:{padding:"8px 20px",fontSize:13},children:"📜 下旨"})]})]})]})]})})]})}const fc={政治:{icon:"🏛️",color:"#6a9eff",desc:"全球政治动态"},军事:{icon:"⚔️",color:"#ff5270",desc:"军事与冲突"},经济:{icon:"💹",color:"#2ecc8a",desc:"经济与市场"},AI大模型:{icon:"🤖",color:"#a07aff",desc:"AI与大模型进展"}},Hi=["政治","军事","经济","AI大模型"];function ap(){const o=q(X=>X.morningBrief),p=q(X=>X.subConfig),u=q(X=>X.loadMorning),k=q(X=>X.loadSubConfig),S=q(X=>X.toast),[D,F]=te.useState(!1),[_,R]=te.useState(null),[C,z]=te.useState(!1),[d,w]=te.useState("⟳ 立即采集"),m=te.useRef(null);te.useEffect(()=>{u()},[u]),te.useEffect(()=>{p&&R(JSON.parse(JSON.stringify(p)))},[p]),te.useEffect(()=>()=>{m.current&&clearInterval(m.current)},[]);const T=async()=>{z(!0),w("⟳ 采集中…");let X=null;try{X=(o==null?void 0:o.generated_at)||null}catch{}try{await fe.refreshMorning(),S("采集已触发,自动检测更新中…","ok");let le=0;m.current&&clearInterval(m.current),m.current=setInterval(async()=>{if(le++,le>24){clearInterval(m.current),m.current=null,z(!1),w("⟳ 立即采集"),S("采集超时,请重试","err");return}try{const ke=await fe.morningBrief();ke.generated_at&&ke.generated_at!==X?(clearInterval(m.current),m.current=null,z(!1),w("⟳ 立即采集"),u(),S("✅ 天下要闻已更新","ok")):w(`⟳ 采集中… (${le*5}s)`)}catch{}},5e3)}catch{S("触发失败","err"),z(!1),w("⟳ 立即采集")}},g=X=>{if(!_)return;const le=[..._.categories||[]],ke=le.find(se=>se.name===X);ke?ke.enabled=!ke.enabled:le.push({name:X,enabled:!0}),R({..._,categories:le})},M=X=>{if(!_||!X)return;const le=[..._.keywords||[]];le.includes(X)||le.push(X),R({..._,keywords:le})},P=X=>{if(!_)return;const le=[..._.keywords||[]];le.splice(X,1),R({..._,keywords:le})},B=(X,le,ke)=>{if(!_||!X||!le){S("请填写源名称和URL","err");return}const se=[..._.custom_feeds||[]];se.push({name:X,url:le,category:ke}),R({..._,custom_feeds:se})},Z=X=>{if(!_)return;const le=[..._.custom_feeds||[]];le.splice(X,1),R({..._,custom_feeds:le})},I=async()=>{if(_)try{const X=await fe.saveMorningConfig(_);X.ok?(S("订阅配置已保存","ok"),k()):S(X.error||"保存失败","err")}catch{S("服务器连接失败","err")}},oe=_?new Set((_.categories||[]).filter(X=>X.enabled).map(X=>X.name)):new Set(Hi),ue=((_==null?void 0:_.keywords)||[]).map(X=>X.toLowerCase()),pe=(o==null?void 0:o.categories)||{},me=o!=null&&o.date?o.date.replace(/(\d{4})(\d{2})(\d{2})/,"$1年$2月$3日"):"",be=Object.values(pe).flat().length;return l.jsxs("div",{children:[l.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:16},children:[l.jsxs("div",{children:[l.jsx("div",{style:{fontSize:20,fontWeight:800,marginBottom:4},children:"🌅 天下要闻"}),l.jsxs("div",{style:{fontSize:12,color:"var(--muted)"},children:[me&&`${me} | `,(o==null?void 0:o.generated_at)&&`采集于 ${o.generated_at} | `,"共 ",be," 条要闻"]})]}),l.jsxs("div",{style:{display:"flex",gap:8},children:[l.jsx("button",{className:"btn btn-g",onClick:()=>F(!D),style:{fontSize:12,padding:"6px 14px"},children:"⚙ 订阅配置"}),l.jsx("button",{className:"tpl-go",disabled:C,onClick:T,style:{fontSize:12,padding:"6px 14px"},children:d})]})]}),D&&_&&l.jsx(up,{config:_,enabledSet:oe,onToggleCat:g,onAddKeyword:M,onRemoveKeyword:P,onAddFeed:B,onRemoveFeed:Z,onSave:I,onSetWebhook:X=>R({..._,feishu_webhook:X})}),Object.keys(pe).length?l.jsx("div",{className:"mb-cats",children:Object.entries(pe).map(([X,le])=>{if(!oe.has(X))return null;const ke=fc[X]||{icon:"📰",color:"var(--acc)"},se=le.map(J=>{const Ne=((J.title||"")+(J.summary||"")).toLowerCase(),_e=ue.filter(xe=>Ne.includes(xe)).length;return{...J,_kwHits:_e}}).sort((J,Ne)=>Ne._kwHits-J._kwHits);return l.jsxs("div",{className:"mb-cat",children:[l.jsxs("div",{className:"mb-cat-hdr",children:[l.jsx("span",{className:"mb-cat-icon",children:ke.icon}),l.jsx("span",{className:"mb-cat-name",style:{color:ke.color},children:X}),l.jsxs("span",{className:"mb-cat-cnt",children:[se.length," 条"]})]}),l.jsx("div",{className:"mb-news-list",children:se.length?se.map((J,Ne)=>{const _e=!!(J.image&&J.image.startsWith("http"));return l.jsxs("div",{className:"mb-card",onClick:()=>window.open(J.link,"_blank"),children:[l.jsx("div",{className:"mb-img",children:_e?l.jsx("img",{src:J.image,onError:xe=>{xe.target.style.display="none"},loading:"lazy",alt:""}):l.jsx("span",{children:ke.icon})}),l.jsxs("div",{className:"mb-info",children:[l.jsxs("div",{className:"mb-headline",children:[J.title,J._kwHits>0&&l.jsx("span",{style:{fontSize:9,padding:"1px 5px",borderRadius:999,background:"#a07aff22",color:"#a07aff",border:"1px solid #a07aff44",marginLeft:4},children:"⭐ 关注"})]}),l.jsx("div",{className:"mb-summary",children:J.summary||J.desc||""}),l.jsxs("div",{className:"mb-meta",children:[l.jsxs("span",{className:"mb-source",children:["📡 ",J.source||""]}),J.pub_date&&l.jsx("span",{className:"mb-time",children:J.pub_date.substring(0,16)})]})]})]},Ne)}):l.jsx("div",{className:"mb-empty",style:{padding:16},children:"暂无新闻"})})]},X)})}):l.jsx("div",{className:"mb-empty",children:"暂无数据,点击右上角「立即采集」获取今日简报"})]})}function up({config:o,enabledSet:p,onToggleCat:u,onAddKeyword:k,onRemoveKeyword:S,onAddFeed:D,onRemoveFeed:F,onSave:_,onSetWebhook:R}){const[C,z]=te.useState(""),[d,w]=te.useState(""),[m,T]=te.useState(""),[g,M]=te.useState(Hi[0]),P=[...Hi];return(o.categories||[]).forEach(B=>{P.includes(B.name)||P.push(B.name)}),l.jsxs("div",{className:"sub-config",style:{marginBottom:20,padding:16,background:"var(--panel2)",borderRadius:12,border:"1px solid var(--line)"},children:[l.jsx("div",{style:{fontSize:14,fontWeight:700,marginBottom:12},children:"⚙ 订阅配置"}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"订阅分类"}),l.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:P.map(B=>{const Z=fc[B]||{icon:"📰"},I=p.has(B);return l.jsxs("div",{className:`sub-cat ${I?"active":""}`,onClick:()=>u(B),style:{cursor:"pointer",padding:"6px 12px",borderRadius:8,border:`1px solid ${I?"var(--acc)":"var(--line)"}`,display:"flex",alignItems:"center",gap:6},children:[l.jsx("span",{children:Z.icon}),l.jsx("span",{style:{fontSize:12},children:B}),I&&l.jsx("span",{style:{fontSize:10,color:"var(--ok)"},children:"✓"})]},B)})})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"关注关键词"}),l.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap",marginBottom:6},children:(o.keywords||[]).map((B,Z)=>l.jsxs("span",{className:"sub-kw",style:{fontSize:11,padding:"2px 8px",borderRadius:4,background:"var(--bg)",border:"1px solid var(--line)"},children:[B,l.jsx("span",{style:{cursor:"pointer",marginLeft:4,color:"var(--danger)"},onClick:()=>S(Z),children:"✕"})]},Z))}),l.jsxs("div",{style:{display:"flex",gap:6},children:[l.jsx("input",{type:"text",value:C,onChange:B=>z(B.target.value),placeholder:"输入关键词",onKeyDown:B=>{B.key==="Enter"&&(k(C.trim()),z(""))},style:{flex:1,padding:"6px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}}),l.jsx("button",{className:"btn btn-g",onClick:()=>{k(C.trim()),z("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:8},children:"自定义信息源"}),(o.custom_feeds||[]).map((B,Z)=>l.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:4,fontSize:11},children:[l.jsx("span",{style:{fontWeight:600},children:B.name}),l.jsx("span",{style:{color:"var(--muted)",flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:B.url}),l.jsx("span",{style:{color:"var(--acc)"},children:B.category}),l.jsx("span",{style:{cursor:"pointer",color:"var(--danger)"},onClick:()=>F(Z),children:"✕"})]},Z)),l.jsxs("div",{style:{display:"flex",gap:6,marginTop:6},children:[l.jsx("input",{placeholder:"源名称",value:d,onChange:B=>w(B.target.value),style:{width:100,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("input",{placeholder:"RSS / URL",value:m,onChange:B=>T(B.target.value),style:{flex:1,padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"}}),l.jsx("select",{value:g,onChange:B=>M(B.target.value),style:{padding:"6px 8px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:11,outline:"none"},children:P.map(B=>l.jsx("option",{value:B,children:B},B))}),l.jsx("button",{className:"btn btn-g",onClick:()=>{D(d,m,g),w(""),T("")},style:{fontSize:11,padding:"4px 12px"},children:"添加"})]})]}),l.jsxs("div",{style:{marginBottom:14},children:[l.jsx("div",{style:{fontSize:12,fontWeight:600,marginBottom:6},children:"飞书 Webhook"}),l.jsx("input",{type:"text",value:o.feishu_webhook||"",onChange:B=>R(B.target.value),placeholder:"https://open.feishu.cn/open-apis/bot/v2/hook/...",style:{width:"100%",padding:"8px 10px",background:"var(--bg)",border:"1px solid var(--line)",borderRadius:6,color:"var(--text)",fontSize:12,outline:"none"}})]}),l.jsx("div",{style:{display:"flex",justifyContent:"flex-end"},children:l.jsx("button",{className:"tpl-go",onClick:_,style:{fontSize:12,padding:"6px 16px"},children:"💾 保存配置"})})]})}const pc={main:"太子",zhongshu:"中书省",menxia:"门下省",shangshu:"尚书省",libu:"礼部",hubu:"户部",bingbu:"兵部",xingbu:"刑部",gongbu:"工部",libu_hr:"吏部",zaochao:"钦天监"},cp={Taizi:"中书省起草",Zhongshu:"门下省审议",Menxia:"尚书省派发",Assigned:"开始执行",Doing:"进入审查",Review:"完成"};function dp(o){const p=Math.max(0,o);if(p<60)return`${p}秒`;if(p<3600)return`${Math.floor(p/60)}分${p%60}秒`;const u=Math.floor(p/3600),k=Math.floor(p%3600/60);return`${u}小时${k}分`}function Vi(o){if(!o)return"";if(typeof o=="number"){const p=new Date(o);return`${String(p.getHours()).padStart(2,"0")}:${String(p.getMinutes()).padStart(2,"0")}:${String(p.getSeconds()).padStart(2,"0")}`}return typeof o=="string"&&o.length>=19?o.substring(11,19):String(o).substring(0,8)}function fp(){var _e,xe;const o=q(E=>E.modalTaskId),p=q(E=>E.setModalTaskId),u=q(E=>E.liveStatus),k=q(E=>E.loadAll),S=q(E=>E.toast),[D,F]=te.useState(null),[_,R]=te.useState(null),C=te.useRef(null),z=te.useRef(null),d=((_e=u==null?void 0:u.tasks)==null?void 0:_e.find(E=>E.id===o))||null,w=te.useCallback(async()=>{if(o)try{const E=await fe.taskActivity(o);F(E)}catch{F(null)}},[o]),m=te.useCallback(async()=>{if(o)try{const E=await fe.schedulerState(o);R(E)}catch{R(null)}},[o]);if(te.useEffect(()=>!o||!d?void 0:(w(),m(),["Done","Cancelled"].includes(d.state)||(C.current=setInterval(()=>{w(),m()},4e3)),()=>{C.current&&(clearInterval(C.current),C.current=null)}),[o,d==null?void 0:d.state,w,m]),te.useEffect(()=>{z.current&&(z.current.scrollTop=z.current.scrollHeight)},[(xe=D==null?void 0:D.activity)==null?void 0:xe.length]),!o||!d)return null;const T=()=>p(null),g=Gi(d),M=g.find(E=>E.status==="active"),P=d.heartbeat||{status:"unknown",label:"⚪ 无数据"},B=d.flow_log||[],Z=d.todos||[],I=Z.filter(E=>E.status==="completed").length,oe=Z.length,ue=!["Done","Blocked","Cancelled"].includes(d.state),pe=["Blocked","Cancelled"].includes(d.state),me=async(E,Q)=>{try{const W=await fe.taskAction(d.id,E,Q);W.ok?(S(W.message||"操作成功","ok"),k(),T()):S(W.error||"操作失败","err")}catch{S("服务器连接失败","err")}},be=async E=>{const Q={approve:"准奏",reject:"封驳"},W=prompt(`${Q[E]} ${d.id} - -请输入批注(可留空):`);if(W!==null)try{const v=await fe.reviewAction(d.id,E,W||"");v.ok?(S(`✅ ${d.id} 已${Q[E]}`,"ok"),k(),T()):S(v.error||"操作失败","err")}catch{S("服务器连接失败","err")}},X=async()=>{const E=cp[d.state]||"下一步",Q=prompt(`⏩ 手动推进 ${d.id} -当前: ${d.state} → 下一步: ${E} - -请输入说明(可留空):`);if(Q!==null)try{const W=await fe.advanceState(d.id,Q||"");W.ok?(S(`⏩ ${W.message}`,"ok"),k(),T()):S(W.error||"推进失败","err")}catch{S("服务器连接失败","err")}},le=async E=>{if(E==="scan"){try{const L=await fe.schedulerScan(180);L.ok?S(`🔍 扫描完成:${L.count||0} 个动作`,"ok"):S(L.error||"扫描失败","err"),m()}catch{S("服务器连接失败","err")}return}const W=prompt(`请输入${{retry:"重试",escalate:"升级",rollback:"回滚"}[E]}原因(可留空):`);if(W===null)return;const v={retry:fe.schedulerRetry,escalate:fe.schedulerEscalate,rollback:fe.schedulerRollback};try{const L=await v[E](d.id,W);L.ok?S(L.message||"操作成功","ok"):S(L.error||"操作失败","err"),m(),k()}catch{S("服务器连接失败","err")}},ke=()=>{const E=prompt("请输入叫停原因(可留空):");E!==null&&me("stop",E)},se=()=>{if(!confirm(`确定要取消 ${d.id} 吗?`))return;const E=prompt("请输入取消原因(可留空):");E!==null&&me("cancel",E)},J=_==null?void 0:_.scheduler,Ne=(_==null?void 0:_.stalledSec)||0;return l.jsx("div",{className:"modal-bg open",onClick:T,children:l.jsxs("div",{className:"modal",onClick:E=>E.stopPropagation(),children:[l.jsx("button",{className:"modal-close",onClick:T,children:"✕"}),l.jsxs("div",{className:"modal-body",children:[l.jsx("div",{className:"modal-id",children:d.id}),l.jsx("div",{className:"modal-title",children:d.title||"(无标题)"}),M&&l.jsxs("div",{className:"cur-stage",children:[l.jsx("div",{className:"cs-icon",children:M.icon}),l.jsxs("div",{className:"cs-info",children:[l.jsx("div",{className:"cs-dept",style:{color:$l(M.dept)},children:M.dept}),l.jsxs("div",{className:"cs-action",children:["当前阶段:",M.action]})]}),l.jsx("span",{className:`hb ${P.status} cs-hb`,children:P.label})]}),l.jsx("div",{className:"m-pipe",children:g.map((E,Q)=>l.jsxs("div",{className:"mp-stage",children:[l.jsxs("div",{className:`mp-node ${E.status}`,children:[E.status==="done"&&l.jsx("div",{className:"mp-done-tick",children:"✓"}),l.jsx("div",{className:"mp-icon",children:E.icon}),l.jsx("div",{className:"mp-dept",style:E.status==="active"?{color:"var(--acc)"}:E.status==="done"?{color:"var(--ok)"}:{},children:E.dept}),l.jsx("div",{className:"mp-action",children:E.action})]}),Qme("resume","恢复执行"),children:"▶️ 恢复执行"}),["Review","Menxia"].includes(d.state)&&l.jsxs(l.Fragment,{children:[l.jsx("button",{className:"btn-action",style:{background:"#2ecc8a22",color:"#2ecc8a",border:"1px solid #2ecc8a44"},onClick:()=>be("approve"),children:"✅ 准奏"}),l.jsx("button",{className:"btn-action",style:{background:"#ff527022",color:"#ff5270",border:"1px solid #ff527044"},onClick:()=>be("reject"),children:"🚫 封驳"})]}),["Pending","Taizi","Zhongshu","Menxia","Assigned","Doing","Review","Next"].includes(d.state)&&l.jsx("button",{className:"btn-action",style:{background:"#7c5cfc18",color:"#7c5cfc",border:"1px solid #7c5cfc44"},onClick:X,children:"⏩ 推进到下一步"})]}),l.jsxs("div",{className:"sched-section",children:[l.jsxs("div",{className:"sched-head",children:[l.jsx("span",{className:"sched-title",children:"🧭 太子调度"}),l.jsx("span",{className:"sched-status",children:J?`${J.enabled===!1?"已禁用":"运行中"} · 阈值 ${J.stallThresholdSec||180}s`:"加载中..."})]}),l.jsxs("div",{className:"sched-grid",children:[l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"停滞时长"}),l.jsx("div",{className:"v",children:dp(Ne)})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"重试次数"}),l.jsx("div",{className:"v",children:(J==null?void 0:J.retryCount)||0})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"升级级别"}),l.jsx("div",{className:"v",children:J!=null&&J.escalationLevel?J.escalationLevel===1?"门下省":"尚书省":"无"})]}),l.jsxs("div",{className:"sched-kpi",children:[l.jsx("div",{className:"k",children:"派发状态"}),l.jsx("div",{className:"v",children:(J==null?void 0:J.lastDispatchStatus)||"idle"})]})]}),J&&l.jsxs("div",{className:"sched-line",children:[J.lastProgressAt&&l.jsxs("span",{children:["最近进展 ",(J.lastProgressAt||"").replace("T"," ").substring(0,19)]}),J.lastDispatchAt&&l.jsxs("span",{children:["最近派发 ",(J.lastDispatchAt||"").replace("T"," ").substring(0,19)]}),l.jsxs("span",{children:["自动回滚 ",J.autoRollback===!1?"关闭":"开启"]}),J.lastDispatchAgent&&l.jsxs("span",{children:["目标 ",J.lastDispatchAgent]})]}),l.jsxs("div",{className:"sched-actions",children:[l.jsx("button",{className:"sched-btn",onClick:()=>le("retry"),children:"🔁 重试派发"}),l.jsx("button",{className:"sched-btn warn",onClick:()=>le("escalate"),children:"📣 升级协调"}),l.jsx("button",{className:"sched-btn danger",onClick:()=>le("rollback"),children:"↩️ 回滚稳定点"}),l.jsx("button",{className:"sched-btn",onClick:()=>le("scan"),children:"🔍 立即扫描"})]})]}),oe>0&&l.jsx(pp,{todos:Z,todoDone:I,todoTotal:oe}),l.jsx("div",{className:"m-section",children:l.jsxs("div",{className:"m-rows",children:[l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"状态"}),l.jsxs("div",{className:"mr-val",children:[l.jsx("span",{className:`tag st-${d.state}`,children:Ki(d)}),(d.review_round||0)>0&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)",marginLeft:8},children:["共磋商 ",d.review_round," 轮"]})]})]}),l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"执行部门"}),l.jsx("div",{className:"mr-val",children:l.jsx("span",{className:`tag dt-${(d.org||"").replace(/\s/g,"")}`,children:d.org||"—"})})]}),d.eta&&d.eta!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",children:"预计完成"}),l.jsx("div",{className:"mr-val",children:d.eta})]}),d.block&&d.block!=="无"&&d.block!=="-"&&l.jsxs("div",{className:"m-row",children:[l.jsx("div",{className:"mr-label",style:{color:"var(--danger)"},children:"阻塞项"}),l.jsx("div",{className:"mr-val",style:{color:"var(--danger)"},children:d.block})]}),d.now&&d.now!=="-"&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"当前进展"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:d.now})]}),d.ac&&l.jsxs("div",{className:"m-row",style:{gridColumn:"1/-1"},children:[l.jsx("div",{className:"mr-label",children:"验收标准"}),l.jsx("div",{className:"mr-val",style:{fontWeight:400,fontSize:12},children:d.ac})]})]})}),B.length>0&&l.jsxs("div",{className:"m-section",children:[l.jsxs("div",{className:"m-sec-label",children:["流转日志(",B.length," 条)"]}),l.jsx("div",{className:"fl-timeline",children:B.map((E,Q)=>{const W=$l(E.from||"");return l.jsxs("div",{className:"fl-item",children:[l.jsx("div",{className:"fl-time",children:E.at?E.at.substring(11,16):""}),l.jsx("div",{className:"fl-dot",style:{background:W}}),l.jsxs("div",{className:"fl-content",children:[l.jsxs("div",{className:"fl-who",children:[l.jsx("span",{className:"from",style:{color:W},children:E.from}),l.jsx("span",{style:{color:"var(--muted)"},children:" → "}),l.jsx("span",{className:"to",style:{color:$l(E.to||"")},children:E.to})]}),l.jsx("div",{className:"fl-rem",children:E.remark})]})]},Q)})})]}),d.output&&d.output!=="-"&&d.output!==""&&l.jsxs("div",{className:"m-section",children:[l.jsx("div",{className:"m-sec-label",children:"产出物"}),l.jsx("code",{children:d.output})]}),l.jsx(mp,{data:D,isDone:["Done","Cancelled"].includes(d.state),logRef:z})]})]})})}function pp({todos:o,todoDone:p,todoTotal:u}){return l.jsxs("div",{className:"todo-section",children:[l.jsxs("div",{className:"todo-header",children:[l.jsxs("div",{className:"m-sec-label",style:{marginBottom:0,border:"none",padding:0},children:["子任务清单(",p,"/",u,")"]}),l.jsxs("div",{className:"todo-progress",children:[l.jsx("div",{className:"todo-bar",children:l.jsx("div",{className:"todo-bar-fill",style:{width:`${Math.round(p/u*100)}%`}})}),l.jsxs("span",{children:[Math.round(p/u*100),"%"]})]})]}),l.jsx("div",{className:"todo-list",children:o.map(k=>{const S=k.status==="completed"?"✅":k.status==="in-progress"?"🔄":"⬜",D=k.status==="completed"?"已完成":k.status==="in-progress"?"进行中":"待开始",F=k.status==="completed"?"s-done":k.status==="in-progress"?"s-progress":"s-notstarted",_=k.status==="completed"?"done":"";return l.jsxs("div",{className:`todo-item ${_}`,children:[l.jsxs("div",{className:"t-row",children:[l.jsx("span",{className:"t-icon",children:S}),l.jsxs("span",{className:"t-id",children:["#",k.id]}),l.jsx("span",{className:"t-title",children:k.title}),l.jsx("span",{className:`t-status ${F}`,children:D})]}),k.detail&&l.jsx("div",{className:"todo-detail",children:k.detail})]},k.id)})})]})}function mp({data:o,isDone:p,logRef:u}){if(!o)return null;const k=o.activity||[],S=(()=>{if(!k.length)return!1;const T=k[k.length-1];if(!T.at)return!1;const g=typeof T.at=="number"?T.at:new Date(T.at).getTime();return Date.now()-g<3e5})(),D=[];o.agentLabel&&D.push(o.agentLabel),o.relatedAgents&&o.relatedAgents.length>1&&D.push(`${o.relatedAgents.length}个 Agent`),o.lastActive&&D.push(`最后活跃: ${o.lastActive}`);const F=o.phaseDurations||[],_=Math.max(...F.map(T=>T.durationSec||1),1),R={皇上:"#eab308",太子:"#f97316",中书省:"#3b82f6",门下省:"#8b5cf6",尚书省:"#10b981",六部:"#06b6d4",礼部:"#ec4899",户部:"#f59e0b",兵部:"#ef4444",刑部:"#6366f1",工部:"#14b8a6",吏部:"#d946ef"},C=o.todosSummary,z=o.resourceSummary,d=k.filter(T=>T.kind==="flow"),w=k.filter(T=>T.kind!=="flow"),m=new Map;return w.forEach(T=>{const g=T.agent||"unknown";m.has(g)||m.set(g,[]),m.get(g).push(T)}),l.jsxs("div",{className:"la-section",children:[l.jsxs("div",{className:"la-header",children:[l.jsxs("span",{className:"la-title",children:[l.jsx("span",{className:`la-dot${S?"":" idle"}`}),p?"执行回顾":"实时动态"]}),l.jsx("span",{className:"la-agent",children:D.join(" · ")||"加载中..."})]}),F.length>0&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:6},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"⏱ 阶段耗时"}),o.totalDuration&&l.jsxs("span",{style:{marginLeft:"auto",fontSize:10,color:"var(--muted)"},children:["总耗时 ",o.totalDuration]})]}),F.map((T,g)=>{const M=Math.max(5,Math.round((T.durationSec||1)/_*100)),P=R[T.phase]||"#6b7280";return l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,margin:"2px 0",fontSize:11},children:[l.jsx("span",{style:{minWidth:48,color:"var(--muted)",textAlign:"right"},children:T.phase}),l.jsx("div",{style:{flex:1,height:14,background:"var(--panel)",borderRadius:3,overflow:"hidden"},children:l.jsx("div",{style:{width:`${M}%`,height:"100%",background:P,borderRadius:3,opacity:T.ongoing?.6:.85}})}),l.jsxs("span",{style:{minWidth:60,fontSize:10,color:"var(--muted)"},children:[T.durationText,T.ongoing&&l.jsx("span",{style:{fontSize:9,color:"#60a5fa"},children:" ●进行中"})]})]},g)})]}),C&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)"},children:[l.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📊 执行进度"}),l.jsxs("span",{style:{fontSize:20,fontWeight:700,color:C.percent>=100?"#22c55e":C.percent>=50?"#60a5fa":"var(--text)"},children:[C.percent,"%"]}),l.jsxs("span",{style:{fontSize:10,color:"var(--muted)"},children:["✅",C.completed," 🔄",C.inProgress," ⬜",C.notStarted," / 共",C.total,"项"]})]}),l.jsxs("div",{style:{height:8,background:"var(--panel)",borderRadius:4,overflow:"hidden",display:"flex"},children:[l.jsx("div",{style:{width:`${C.total?C.completed/C.total*100:0}%`,background:"#22c55e",transition:"width .3s"}}),l.jsx("div",{style:{width:`${C.total?C.inProgress/C.total*100:0}%`,background:"#3b82f6",transition:"width .3s"}})]})]}),z&&(z.totalTokens||z.totalCost)&&l.jsxs("div",{style:{padding:"4px 0 8px",borderBottom:"1px solid var(--line)",display:"flex",gap:12,alignItems:"center"},children:[l.jsx("span",{style:{fontSize:11,fontWeight:600},children:"📈 资源消耗"}),z.totalTokens!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["🔢 ",z.totalTokens.toLocaleString()," tokens"]}),z.totalCost!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["💰 $",z.totalCost.toFixed(4)]}),z.totalElapsedSec!=null&&l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⏳ ",z.totalElapsedSec>=60?`${Math.floor(z.totalElapsedSec/60)}分`:"",z.totalElapsedSec%60,"秒"]})]}),l.jsxs("div",{className:"la-log",ref:u,children:[d.length>0&&l.jsx("div",{className:"la-flow-wrap",children:d.map((T,g)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"📋"}),l.jsxs("span",{className:"la-body",children:[l.jsx("b",{children:T.from})," → ",l.jsx("b",{children:T.to})," ",T.remark||""]}),l.jsx("span",{className:"la-time",children:Vi(T.at)})]},`flow-${g}`))}),m.size>0?l.jsx("div",{className:"la-groups",children:Array.from(m.entries()).map(([T,g])=>{const M=pc[T]||T||"未标识",P=g[g.length-1],B=P!=null&&P.at?Vi(P.at):"--:--:--";return l.jsxs("div",{className:"la-group",children:[l.jsxs("div",{className:"la-group-hd",children:[l.jsx("span",{className:"name",children:M}),l.jsxs("span",{children:["最近更新 ",B]})]}),l.jsx("div",{className:"la-group-bd",children:g.map((Z,I)=>l.jsx(hp,{entry:Z},I))})]},T)})}):!d.length&&l.jsx("div",{className:"la-empty",children:o.message||o.error||"Agent 尚未上报进展(等待 Agent 调用 progress 命令)"})]})]})}function hp({entry:o}){var k,S,D;const p=Vi(o.at),u=o.agent?l.jsx("span",{style:{fontSize:9,color:"var(--muted)",background:"var(--panel)",padding:"1px 4px",borderRadius:3,marginRight:4},children:pc[o.agent]||o.agent}):null;if(o.kind==="progress")return l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🔄"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("b",{children:"当前进展:"}),o.text]}),l.jsx("span",{className:"la-time",children:p})]});if(o.kind==="todos"){const F=o.items||[],_=new Map;return o.diff&&((o.diff.changed||[]).forEach(R=>_.set(R.id,{type:"changed",from:R.from,to:R.to})),(o.diff.added||[]).forEach(R=>_.set(R.id,{type:"added"}))),l.jsxs("div",{className:"la-entry",style:{flexDirection:"column",alignItems:"flex-start",gap:2},children:[l.jsxs("div",{style:{fontSize:11,color:"var(--muted)",marginBottom:2},children:[u,"📝 执行计划"]}),F.map(R=>{const C=R.status==="completed"?"✅":R.status==="in-progress"?"🔄":"⬜",z=_.get(String(R.id)),d=R.status==="completed"?{opacity:.5,textDecoration:"line-through"}:R.status==="in-progress"?{color:"#60a5fa",fontWeight:"bold"}:{};return l.jsxs("div",{style:d,children:[C," ",R.title,z&&z.type==="changed"&&z.to==="completed"&&l.jsx("span",{style:{color:"#22c55e",fontSize:9,marginLeft:4},children:"✨刚完成"}),z&&z.type==="changed"&&z.to!=="completed"&&l.jsxs("span",{style:{color:"#f59e0b",fontSize:9,marginLeft:4},children:["↻",z.from,"→",z.to]}),z&&z.type==="added"&&l.jsx("span",{style:{color:"#3b82f6",fontSize:9,marginLeft:4},children:"🆕新增"})]},R.id)}),(S=(k=o.diff)==null?void 0:k.removed)==null?void 0:S.map(R=>l.jsxs("div",{style:{opacity:.4,textDecoration:"line-through"},children:["🗑 ",R.title]},R.id))]})}if(o.kind==="assistant")return l.jsxs(l.Fragment,{children:[o.thinking&&l.jsxs("div",{className:"la-entry la-thinking",children:[l.jsx("span",{className:"la-icon",children:"💭"}),l.jsxs("span",{className:"la-body",children:[u,o.thinking]}),l.jsx("span",{className:"la-time",children:p})]}),(D=o.tools)==null?void 0:D.map((F,_)=>l.jsxs("div",{className:"la-entry la-tool",children:[l.jsx("span",{className:"la-icon",children:"🔧"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:F.name}),l.jsx("span",{className:"la-trunc",children:F.input_preview||""})]}),l.jsx("span",{className:"la-time",children:p})]},_)),o.text&&l.jsxs("div",{className:"la-entry la-assistant",children:[l.jsx("span",{className:"la-icon",children:"🤖"}),l.jsxs("span",{className:"la-body",children:[u,o.text]}),l.jsx("span",{className:"la-time",children:p})]})]});if(o.kind==="tool_result"){const F=o.exitCode===0||o.exitCode===null||o.exitCode===void 0;return l.jsxs("div",{className:`la-entry la-tool-result ${F?"ok":"err"}`,children:[l.jsx("span",{className:"la-icon",children:F?"✅":"❌"}),l.jsxs("span",{className:"la-body",children:[u,l.jsx("span",{className:"la-tool-name",children:o.tool||""}),o.output?o.output.substring(0,150):""]}),l.jsx("span",{className:"la-time",children:p})]})}return o.kind==="user"?l.jsxs("div",{className:"la-entry la-user",children:[l.jsx("span",{className:"la-icon",children:"📥"}),l.jsxs("span",{className:"la-body",children:[u,o.text||""]}),l.jsx("span",{className:"la-time",children:p})]}):null}function vp(){const o=q(p=>p.toasts);return o.length?l.jsx("div",{className:"toaster",children:o.map(p=>l.jsx("div",{className:`toast ${p.type}`,children:p.msg},p.id))}):null}function gp(){const o=q(T=>T.liveStatus),[p,u]=te.useState(!1),[k,S]=te.useState(!1);te.useEffect(()=>{const T=localStorage.getItem("openclaw_court_date"),g=new Date().toISOString().substring(0,10);if(!JSON.parse(localStorage.getItem("openclaw_court_pref")||'{"enabled":true}').enabled||T===g)return;localStorage.setItem("openclaw_court_date",g),u(!0);const P=setTimeout(()=>D(),3500);return()=>clearTimeout(P)},[]);const D=()=>{S(!0),setTimeout(()=>u(!1),500)};if(!p)return null;const _=((o==null?void 0:o.tasks)||[]).filter(Jt),R=_.filter(T=>!["Done","Cancelled"].includes(T.state)).length,C=_.filter(T=>T.state==="Done").length,z=_.filter(T=>T.state!=="Done"&&T.state!=="Cancelled"&&T.eta&&new Date(T.eta.replace(" ","T"))0&&` · ⚠ 超期 ${z} 件`]}),l.jsx("div",{className:"crm-date in",children:m}),l.jsx("div",{className:"crm-skip",children:"点击任意处跳过"})]})}function yp(){const o=q(d=>d.activeTab),p=q(d=>d.setActiveTab),u=q(d=>d.liveStatus),k=q(d=>d.countdown),S=q(d=>d.loadAll);te.useEffect(()=>(Bf(),()=>Wf()),[]);const D=(u==null?void 0:u.tasks)||[],F=D.filter(Jt),_=F.filter(d=>!Bl(d)),R=u==null?void 0:u.syncStatus,C=R==null?void 0:R.ok,z=d=>d==="edicts"?String(_.length):d==="sessions"?String(D.filter(w=>!Jt(w)).length):d==="memorials"?String(F.filter(w=>["Done","Cancelled"].includes(w.state)).length):d==="monitor"?D.filter(m=>Jt(m)&&m.state==="Doing").length+"活跃":"";return l.jsxs("div",{className:"wrap",children:[l.jsxs("div",{className:"hdr",children:[l.jsxs("div",{children:[l.jsx("div",{className:"logo",children:"三省六部 · 总控台"}),l.jsx("div",{className:"sub-text",children:"OpenClaw Sansheng-Liubu Dashboard"})]}),l.jsxs("div",{className:"hdr-r",children:[l.jsx("span",{className:`chip ${C?"ok":C===!1?"err":""}`,children:C?"✅ 同步正常":C===!1?"❌ 服务器未启动":"⏳ 连接中…"}),l.jsxs("span",{className:"chip",children:[_.length," 道旨意"]}),l.jsx("button",{className:"btn-refresh",onClick:()=>S(),children:"⟳ 刷新"}),l.jsxs("span",{style:{fontSize:11,color:"var(--muted)"},children:["⟳ ",k,"s"]})]})]}),l.jsx("div",{className:"tabs",children:Af.map(d=>l.jsxs("div",{className:`tab ${o===d.key?"active":""}`,onClick:()=>p(d.key),children:[d.icon," ",d.label,z(d.key)&&l.jsx("span",{className:"tbadge",children:z(d.key)})]},d.key))}),o==="edicts"&&l.jsx(Qf,{}),o==="monitor"&&l.jsx(Kf,{}),o==="officials"&&l.jsx(Yf,{}),o==="models"&&l.jsx(qf,{}),o==="skills"&&l.jsx(ep,{}),o==="sessions"&&l.jsx(rp,{}),o==="memorials"&&l.jsx(sp,{}),o==="templates"&&l.jsx(op,{}),o==="morning"&&l.jsx(ap,{}),l.jsx(fp,{}),l.jsx(vp,{}),l.jsx(gp,{})]})}kf.createRoot(document.getElementById("root")).render(l.jsx(oc.StrictMode,{children:l.jsx(yp,{})})); diff --git a/dashboard/dist/index.html b/dashboard/dist/index.html index 457c14dd..a9286da0 100644 --- a/dashboard/dist/index.html +++ b/dashboard/dist/index.html @@ -5,7 +5,7 @@ 三省六部 · Edict Dashboard - + diff --git a/dashboard/server.py b/dashboard/server.py index 57fd64b2..6ea81843 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -11,10 +11,11 @@ GET /api/model-change-log → data/model_change_log.json GET /api/last-result → data/last_model_change_result.json """ -import json, pathlib, subprocess, sys, threading, argparse, datetime, logging, re, os +import json, pathlib, subprocess, sys, threading, argparse, datetime, logging, re, os, shlex, uuid, difflib from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse from urllib.request import Request, urlopen +from urllib.error import HTTPError # 引入文件锁工具,确保与其他脚本并发安全 scripts_dir = str(pathlib.Path(__file__).parent.parent / 'scripts') @@ -105,43 +106,71 @@ def handle_task_action(task_id, action, reason): return {'ok': False, 'error': f'任务 {task_id} 不存在'} old_state = task.get('state', '') - _ensure_scheduler(task) + sched = _ensure_scheduler(task) _scheduler_snapshot(task, f'task-action-before-{action}') + run_id = _new_run_id() + _acquire_lease(task, stage=old_state, role='manual', owner_run_id=run_id, ttl_sec=180, force_takeover=True) if action == 'stop': - task['state'] = 'Blocked' - task['block'] = reason or '皇上叫停' - task['now'] = f'⏸️ 已暂停:{reason}' + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_stop', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state='Blocked', + now_text=f'⏸️ 已暂停:{reason}', + block_text=reason or '皇上叫停', + flow_from='皇上', + flow_remark=f'⏸️ 叫停:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 叫停失败: {commit.get("blockedBy")}'} + task['_prev_state'] = old_state elif action == 'cancel': - task['state'] = 'Cancelled' - task['block'] = reason or '皇上取消' - task['now'] = f'🚫 已取消:{reason}' + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_cancel', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state='Cancelled', + now_text=f'🚫 已取消:{reason}', + block_text=reason or '皇上取消', + flow_from='皇上', + flow_remark=f'🚫 取消:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 取消失败: {commit.get("blockedBy")}'} + task['_prev_state'] = old_state elif action == 'resume': - # Resume to previous active state or Doing - task['state'] = task.get('_prev_state', 'Doing') - task['block'] = '无' - task['now'] = f'▶️ 已恢复执行' - - if action in ('stop', 'cancel'): - task['_prev_state'] = old_state # Save for resume - - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': '皇上', - 'to': task.get('org', ''), - 'remark': f'{"⏸️ 叫停" if action == "stop" else "🚫 取消" if action == "cancel" else "▶️ 恢复"}:{reason}' - }) - - if action == 'resume': - _scheduler_mark_progress(task, f'恢复到 {task.get("state", "Doing")}') - else: - _scheduler_add_flow(task, f'皇上{action}:{reason or "无"}') - - task['updatedAt'] = now_iso() + resume_state = task.get('_prev_state', 'Doing') + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_resume', + owner_run_id=run_id, + expected_version=task.get('_scheduler', {}).get('stateVersion'), + to_state=resume_state, + now_text='▶️ 已恢复执行', + block_text='无', + flow_from='皇上', + flow_remark=f'▶️ 恢复:{reason or "无"}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 恢复失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'恢复到 {task.get("state", "Doing")}', reason_code='manual_resume') + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) save_tasks(tasks) if action == 'resume' and task.get('state') not in _TERMINAL_STATES: - dispatch_for_state(task_id, task, task.get('state'), trigger='resume') + dispatch_for_state(task_id, task, task.get('state'), trigger='resume', owner_run_id=run_id) label = {'stop': '已叫停', 'cancel': '已取消', 'resume': '已恢复'}[action] return {'ok': True, 'message': f'{task_id} {label}'} @@ -542,8 +571,6 @@ def handle_create_task(title, org='中书省', official='中书令', priority='n title = re.split(r'\n*```', title, maxsplit=1)[0].strip() # 清理常见前缀: "传旨:" "下旨:" 等 title = re.sub(r'^(传旨|下旨)[::\uff1a]\s*', '', title) - if len(title) > 100: - title = title[:100] + '…' # 标题质量校验:防止闲聊被误建为旨意 if len(title) < _MIN_TITLE_LEN: return {'ok': False, 'error': f'标题过短({len(title)}<{_MIN_TITLE_LEN}字),不像是旨意'} @@ -608,44 +635,64 @@ def handle_review_action(task_id, action, comment=''): if task.get('state') not in ('Review', 'Menxia'): return {'ok': False, 'error': f'任务 {task_id} 当前状态为 {task.get("state")},无法御批'} - _ensure_scheduler(task) + sched = _ensure_scheduler(task) _scheduler_snapshot(task, f'review-before-{action}') + run_id = _new_run_id() + _acquire_lease(task, stage=task.get('state', ''), role='manual-review', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') if action == 'approve': if task['state'] == 'Menxia': - task['state'] = 'Assigned' - task['now'] = '门下省准奏,移交尚书省派发' + next_state = 'Assigned' + next_now = '门下省准奏,移交尚书省派发' remark = f'✅ 准奏:{comment or "门下省审议通过"}' to_dept = '尚书省' else: # Review - task['state'] = 'Done' - task['now'] = '御批通过,任务完成' + next_state = 'Done' + next_now = '御批通过,任务完成' remark = f'✅ 御批准奏:{comment or "审查通过"}' to_dept = '皇上' elif action == 'reject': round_num = (task.get('review_round') or 0) + 1 task['review_round'] = round_num - task['state'] = 'Zhongshu' - task['now'] = f'封驳退回中书省修订(第{round_num}轮)' + next_state = 'Zhongshu' + next_now = f'封驳退回中书省修订(第{round_num}轮)' remark = f'🚫 封驳:{comment or "需要修改"}' to_dept = '中书省' else: return {'ok': False, 'error': f'未知操作: {action}'} - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': '门下省' if task.get('state') != 'Done' else '皇上', - 'to': to_dept, - 'remark': remark - }) - _scheduler_mark_progress(task, f'审议动作 {action} -> {task.get("state")}') - task['updatedAt'] = now_iso() + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_review_decision', + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=next_now, + block_text='无', + flow_from='门下省' if next_state != 'Done' else '皇上', + flow_to=to_dept, + flow_remark=remark, + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'{task_id} 审批提交失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'审议动作 {action} -> {task.get("state")}', reason_code='manual_review_decision') + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) + if next_state != 'Done': + cds = sched.setdefault('cooldowns', {}) + cds.pop('noDispatchUntil', None) + cds.pop('noEscalateUntil', None) + _release_lease(task, run_id) save_tasks(tasks) # 🚀 审批后自动派发对应 Agent new_state = task['state'] if new_state not in ('Done',): - dispatch_for_state(task_id, task, new_state) + dispatch_for_state(task_id, task, new_state, owner_run_id=run_id) label = '已准奏' if action == 'approve' else '已封驳' dispatched = ' (已自动派发 Agent)' if new_state != 'Done' else '' @@ -856,6 +903,989 @@ def do_wake(): return {'ok': True, 'message': f'{agent_id} 唤醒指令已发出,约10-30秒后生效'} +def _agent_label(agent_id): + for dept in _AGENT_DEPTS: + if dept.get('id') == agent_id: + return dept.get('label') or agent_id + return agent_id + + +def _extract_json_obj(text): + if not text: + return None + s = text.strip() + if s.startswith('```'): + s = re.sub(r'^```[a-zA-Z]*\s*', '', s) + s = re.sub(r'\s*```$', '', s) + start = s.find('{') + end = s.rfind('}') + if start < 0 or end <= start: + return None + candidate = s[start:end + 1] + try: + obj = json.loads(candidate) + return obj if isinstance(obj, dict) else None + except Exception: + return None + + +def _looks_like_provider_tool_error(text): + s = (text or '').strip() + if not s: + return False + if 'Invalid JSON payload' in s and 'patternProperties' in s: + return True + if 'Unknown name "patternProperties"' in s: + return True + return False + + +def _run_agent_sync(agent_id, message, timeout_sec=120): + if not _SAFE_NAME_RE.match(agent_id): + raise ValueError(f'agent_id 非法: {agent_id}') + model_name = _get_agent_model(agent_id) + if _is_gemini_model(model_name): + return _run_aihub_gemini_content(model_name, message, timeout_sec=timeout_sec) + if _is_aihub_model(model_name): + return _run_aihub_openai_chat(model_name, message, timeout_sec=timeout_sec) + + cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', message, '--timeout', str(timeout_sec)] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout_sec + 15) + output = (result.stdout or '').strip() + err = (result.stderr or '').strip() + if result.returncode != 0: + raise RuntimeError(err[:400] or output[:400] or f'agent {agent_id} failed') + text = output or err + if _looks_like_provider_tool_error(text): + raise RuntimeError(text[:400]) + return text[:12000] + + +def _get_agent_model(agent_id): + cfg = read_json(DATA / 'agent_config.json', {}) + for ag in cfg.get('agents', []) if isinstance(cfg, dict) else []: + if isinstance(ag, dict) and ag.get('id') == agent_id: + return str(ag.get('model') or '').strip() + return '' + + +def _is_aihub_model(model_name): + return bool(model_name) and str(model_name).startswith('aihub/') + + +def _is_gemini_model(model_name): + return bool(model_name) and str(model_name).startswith('aihub/gemini-') + + +def _get_aihub_openai_provider(): + cfg = read_json(OCLAW_HOME / 'openclaw.json', {}) + models = cfg.get('models', {}) if isinstance(cfg, dict) else {} + providers = models.get('providers', {}) if isinstance(models, dict) else {} + aihub = providers.get('aihub', {}) if isinstance(providers, dict) else {} + if not isinstance(aihub, dict): + return None + base_url = str(aihub.get('baseUrl') or '').strip().rstrip('/') + api_key = str(aihub.get('apiKey') or '').strip() + api_type = str(aihub.get('api') or '').strip() + if not base_url or not api_key: + return None + if api_type and api_type != 'openai-completions': + return None + return { + 'baseUrl': base_url, + 'apiKey': api_key, + } + + +def _aihub_base_root(base_url): + root = (base_url or '').strip().rstrip('/') + if root.endswith('/v1'): + root = root[:-3] + return root.rstrip('/') + + +def _urlopen_json(req, timeout_sec): + with urlopen(req, timeout=max(20, int(timeout_sec) + 10)) as resp: + text = resp.read().decode('utf-8', 'ignore') + try: + return json.loads(text) + except Exception: + raise RuntimeError('provider 返回非 JSON 响应') + + +def _http_error_message(e): + body = '' + try: + body = (e.read() or b'').decode('utf-8', 'ignore') + except Exception: + body = '' + return body[:320] or str(e) + + +def _extract_text_from_gemini_response(obj): + candidates = obj.get('candidates') + if not isinstance(candidates, list): + return '' + for c in candidates: + if not isinstance(c, dict): + continue + content = c.get('content') + parts = content.get('parts') if isinstance(content, dict) else None + if not isinstance(parts, list): + continue + chunks = [] + for p in parts: + if isinstance(p, dict) and isinstance(p.get('text'), str): + chunks.append(p.get('text')) + txt = ''.join(chunks).strip() + if txt: + return txt + return '' + + +def _run_aihub_gemini_content(model_name, message, timeout_sec=120): + provider = _get_aihub_openai_provider() + if not provider: + raise RuntimeError('未找到 aihub provider 配置') + model_id = model_name.split('/', 1)[1] if '/' in model_name else model_name + root = _aihub_base_root(provider['baseUrl']) + payload = { + # gemini 原生协议字段 + 'contents': [{'parts': [{'text': message}]}], + # aihub 兼容层需要这两个字段,否则 gemini-3-flash-preview 可能报参数缺失 + 'model': model_id, + 'messages': [{'role': 'user', 'content': message}], + } + headers = { + 'Content-Type': 'application/json', + } + last_error = 'gemini 请求失败' + for ver in ('v1beta', 'v1'): + endpoint = f'{root}/{ver}/models/{model_id}:generateContent?key={provider["apiKey"]}' + req = Request( + endpoint, + data=json.dumps(payload).encode('utf-8'), + headers=headers, + method='POST', + ) + try: + obj = _urlopen_json(req, timeout_sec=timeout_sec) + text = _extract_text_from_gemini_response(obj) + if text: + return text[:12000] + if isinstance(obj, dict) and obj.get('error'): + last_error = str(obj.get('error')) + else: + last_error = 'gemini 返回缺少文本内容' + except HTTPError as e: + msg = _http_error_message(e) + last_error = msg + # 非 404/400 直接终止,避免无意义重试 + if e.code not in (400, 404): + break + except Exception as e: + last_error = str(e) + break + raise RuntimeError(last_error[:320]) + + +def _run_aihub_openai_chat(model_name, message, timeout_sec=120): + provider = _get_aihub_openai_provider() + if not provider: + raise RuntimeError('未找到 aihub openai-completions provider 配置') + model_id = model_name.split('/', 1)[1] if '/' in model_name else model_name + endpoint = provider['baseUrl'] + '/chat/completions' + payload = { + 'model': model_id, + 'messages': [{'role': 'user', 'content': message}], + 'temperature': 0.2, + 'stream': False, + } + req = Request( + endpoint, + data=json.dumps(payload).encode('utf-8'), + headers={ + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {provider["apiKey"]}', + }, + method='POST', + ) + try: + with urlopen(req, timeout=max(20, int(timeout_sec) + 10)) as resp: + text = resp.read().decode('utf-8', 'ignore') + except HTTPError as e: + body = '' + try: + body = (e.read() or b'').decode('utf-8', 'ignore') + except Exception: + body = '' + raise RuntimeError(body[:260] or str(e)) + except Exception as e: + raise RuntimeError(str(e)) + try: + obj = json.loads(text) + except Exception: + raise RuntimeError('aihub 返回非 JSON 响应') + choices = obj.get('choices') + if isinstance(choices, list) and choices: + msg = choices[0].get('message', {}) if isinstance(choices[0], dict) else {} + content = msg.get('content') + if isinstance(content, str) and content.strip(): + return content.strip()[:12000] + out = str(obj.get('output_text') or '').strip() + if out: + return out[:12000] + raise RuntimeError('aihub 响应缺少可用内容') + + +def _load_court_session(session_id): + items = atomic_json_read(DATA / 'court_discussions.json', []) + if not isinstance(items, list): + return None + for item in items: + if isinstance(item, dict) and item.get('id') == session_id: + return item + return None + + +def _upsert_court_session(session): + target = DATA / 'court_discussions.json' + sid = session.get('id') + if not sid: + return + + def updater(items): + if not isinstance(items, list): + items = [] + out = [] + replaced = False + for item in items: + if isinstance(item, dict) and item.get('id') == sid: + out.append(session) + replaced = True + else: + out.append(item) + if not replaced: + out.insert(0, session) + out.sort(key=lambda x: x.get('updatedAt', '') if isinstance(x, dict) else '', reverse=True) + return out[:120] + + atomic_json_update(target, updater, []) + + +def _update_court_session(session_id, updater): + target = DATA / 'court_discussions.json' + holder = {'session': None} + + def _apply(items): + if not isinstance(items, list): + items = [] + out = [] + for item in items: + if isinstance(item, dict) and item.get('id') == session_id: + session = dict(item) + updater(session) + holder['session'] = session + out.append(session) + else: + out.append(item) + out.sort(key=lambda x: x.get('updatedAt', '') if isinstance(x, dict) else '', reverse=True) + return out[:120] + + atomic_json_update(target, _apply, []) + return holder['session'] + + +def _pick_moderator(selected): + if 'menxia' in selected: + return 'menxia' + if 'taizi' in selected: + return 'taizi' + return selected[0] + + +def _friendly_agent_error(err_text): + text = (err_text or '').strip() + if 'patternProperties' in text and 'Invalid JSON payload' in text: + return ( + '模型工具协议不兼容(patternProperties)。' + '建议切换该大臣模型,或先排除此大臣后继续议政。' + ) + if not text: + return '未知错误' + return text[:240] + + +def _append_emperor_note(session, note): + n = (note or '').strip() + if not n: + return + notes = session.setdefault('emperorNotes', []) + notes.append({'at': now_iso(), 'text': n[:600]}) + if len(notes) > 30: + session['emperorNotes'] = notes[-30:] + + +def _normalize_compare_text(text): + if not text: + return '' + return re.sub(r'[\W_]+', '', str(text), flags=re.UNICODE).lower() + + +def _needs_edict_fallback(topic, recommended_edict): + title = (recommended_edict or '').strip() + if len(title) < 20: + return True + topic_norm = _normalize_compare_text(topic) + title_norm = _normalize_compare_text(title) + if not title_norm: + return True + if topic_norm and (title_norm == topic_norm or title_norm in topic_norm or topic_norm in title_norm): + return True + if topic_norm: + ratio = difflib.SequenceMatcher(None, topic_norm[:240], title_norm[:240]).ratio() + if ratio >= 0.82: + return True + return False + + +def _build_fallback_edict_text(session, final_obj): + topic = str(session.get('topic') or '').strip() + clarified_goal = str(final_obj.get('clarified_goal') or '').strip() + latest_assessment = (session.get('assessments') or [])[-1] if session.get('assessments') else {} + draft_direction = str((latest_assessment or {}).get('draft_direction') or '').strip() + latest_note = '' + emperor_notes = session.get('emperorNotes') or [] + if emperor_notes: + latest_note = str(emperor_notes[-1].get('text') or '').strip() + + round_no = int(session.get('rounds') or 0) + highlights = [] + for x in (session.get('discussion') or []): + if int(x.get('round') or 0) != round_no: + continue + label = str(x.get('agentLabel') or '').strip() + reply = str(x.get('reply') or '').strip() + if not label or not reply: + continue + reply_line = re.sub(r'\s+', ' ', reply).strip() + if len(reply_line) > 90: + reply_line = reply_line[:90] + '...' + highlights.append(f'{label}:{reply_line}') + if len(highlights) > 3: + highlights = highlights[:3] + + goal = clarified_goal or draft_direction or topic + lines = [f'请太子牵头办理:{goal}。'] + if highlights: + lines.append('议政要点:' + ';'.join(highlights) + '。') + if latest_note: + lines.append(f'皇上最终拍板:{latest_note}。') + lines.append('请形成执行方案、风险清单与里程碑,并按节点回报。') + return '\n'.join(lines).strip() + + +def _build_court_response(session, message=''): + assessment = None + assessments = session.get('assessments') or [] + if assessments: + assessment = assessments[-1] + return { + 'ok': True, + 'sessionId': session.get('id'), + 'status': session.get('status', 'ongoing'), + 'topic': session.get('topic', ''), + 'participants': session.get('participants', []), + 'rounds': int(session.get('rounds') or 0), + 'moderator': { + 'id': session.get('moderatorId', ''), + 'label': _agent_label(session.get('moderatorId', '')), + }, + 'assessment': assessment, + 'suggestedAction': session.get('suggestedAction', 'next'), + 'linkedTaskId': session.get('linkedTaskId', ''), + 'roundRunning': bool(session.get('roundRunning', False)), + 'currentRound': int(session.get('currentRound') or 0), + 'speakingNow': session.get('speakingNow') or {}, + 'emperorNotes': session.get('emperorNotes', [])[-10:], + 'discussion': (session.get('discussion') or [])[-80:], + 'final': session.get('final'), + 'message': message or session.get('message', ''), + } + + +def _run_court_round(session): + topic = session.get('topic', '') + selected = session.get('participants', []) + round_no = int(session.get('rounds') or 0) + 1 + transcript = session.setdefault('discussion', []) + round_entries = [] + emperor_notes = session.get('emperorNotes') or [] + latest_note = emperor_notes[-1].get('text', '') if emperor_notes else '' + + for idx, aid in enumerate(selected): + label = _agent_label(aid) + recent = transcript[-6:] + recent_text = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:400]}' + for x in recent + ]) if recent else '暂无' + prompt = ( + f'你正在参与御前议政讨论,角色是「{label}」。\n' + f'议题:{topic}\n' + f'当前第 {round_no} 轮,你是本轮第 {idx + 1}/{len(selected)} 位发言。\n\n' + f'最近讨论摘要:\n{recent_text}\n\n' + f'皇上最新批示:{latest_note or "暂无"}\n\n' + f'请输出四段(中文、简洁):\n' + f'【你认为最关键的澄清点】\n' + f'【你看到的主要风险】\n' + f'【你建议皇上现在做的决定】\n' + f'【可直接执行的修改建议】' + ) + reply = '' + error_text = '' + try: + reply = _run_agent_sync(aid, prompt, timeout_sec=120) + except Exception as e: + error_text = _friendly_agent_error(str(e)) + reply = ( + f'【系统降级】{label} 本轮发言失败:{error_text}\n' + f'【建议】请皇上选择“继续一轮”或调整参与大臣后再议。' + ) + round_entries.append({ + 'round': round_no, + 'turn': idx + 1, + 'totalTurns': len(selected), + 'agentId': aid, + 'agentLabel': label, + 'reply': reply[:4000], + 'error': bool(error_text), + 'at': now_iso(), + }) + + transcript.extend(round_entries) + session['rounds'] = round_no + + moderator_id = session.get('moderatorId') or _pick_moderator(selected) + moderator_label = _agent_label(moderator_id) + recent_round_text = '\n\n'.join([ + f'[{x["agentLabel"]}] {x["reply"][:1200]}' + for x in round_entries + ]) + assess_prompt = ( + f'你现在是本轮议政主持人({moderator_label})。\n' + f'议题:{topic}\n' + f'第 {round_no} 轮各方意见如下:\n{recent_round_text}\n\n' + f'请只输出 JSON(不要代码块):\n' + f'{{\n' + f' "recommend_stop": true,\n' + f' "reason": "为何建议结束/继续",\n' + f' "question_to_emperor": "请皇上拍板的问题",\n' + f' "focus_next_round": ["若继续,下一轮重点1", "重点2"],\n' + f' "draft_direction": "若现在结束,旨意草案应强调什么"\n' + f'}}' + ) + assess_raw = '' + assess = {} + try: + assess_raw = _run_agent_sync(moderator_id, assess_prompt, timeout_sec=120) + assess = _extract_json_obj(assess_raw) or {} + except Exception as e: + err_msg = _friendly_agent_error(str(e)) + has_error_entry = any(bool(x.get('error')) for x in round_entries) + assess = { + 'recommend_stop': bool(has_error_entry), + 'reason': f'主持评估降级:{err_msg}', + 'question_to_emperor': '是否继续下一轮讨论,或直接终止该话题?', + 'focus_next_round': ['先排查失败大臣模型兼容性', '收敛为可执行目标'], + 'draft_direction': '若无共识建议先终止,若有可执行路径则交由太子办理', + } + assess_raw = str(e)[:2000] + assessment = { + 'round': round_no, + 'moderatorId': moderator_id, + 'moderatorLabel': moderator_label, + 'recommend_stop': bool(assess.get('recommend_stop', False)), + 'reason': str(assess.get('reason') or '').strip(), + 'question_to_emperor': str(assess.get('question_to_emperor') or '').strip(), + 'focus_next_round': assess.get('focus_next_round') if isinstance(assess.get('focus_next_round'), list) else [], + 'draft_direction': str(assess.get('draft_direction') or '').strip(), + 'raw': assess_raw[:2000], + 'at': now_iso(), + } + session.setdefault('assessments', []).append(assessment) + session['suggestedAction'] = 'finalize' if assessment['recommend_stop'] else 'next' + session['status'] = 'ongoing' + session['updatedAt'] = now_iso() + session['message'] = ( + f'第 {round_no} 轮结束,{moderator_label}建议' + f'{"可请皇上决定结束讨论" if assessment["recommend_stop"] else "继续讨论一轮"}' + ) + return round_entries, assessment + + +def _start_court_round_async(session_id): + session = _load_court_session(session_id) + if not session: + return {'ok': False, 'error': f'讨论会话 {session_id} 不存在'} + if session.get('roundRunning'): + return {'ok': True, 'message': f'第{int(session.get("currentRound") or 0)}轮仍在进行中'} + if session.get('status') in ('terminated', 'handoffed'): + return {'ok': False, 'error': f'会话状态为 {session.get("status")},不可继续讨论'} + + next_round = int(session.get('rounds') or 0) + 1 + run_id = _new_run_id() + + _update_court_session( + session_id, + lambda s: ( + s.update({ + 'roundRunning': True, + 'currentRound': next_round, + 'runningRunId': run_id, + 'status': 'ongoing', + 'updatedAt': now_iso(), + 'message': f'第 {next_round} 轮议政已开始,正在轮番发言', + }) + ), + ) + + def _worker(): + try: + latest = _load_court_session(session_id) or {} + selected = list(latest.get('participants') or []) + topic = (latest.get('topic') or '').strip() + if not selected or not topic: + raise RuntimeError('会话数据不完整') + + for idx, aid in enumerate(selected): + current = _load_court_session(session_id) or {} + if current.get('status') in ('terminated', 'handoffed'): + _update_court_session( + session_id, + lambda s: s.update({ + 'roundRunning': False, + 'updatedAt': now_iso(), + 'message': f'第 {next_round} 轮已停止(会话状态: {s.get("status")})', + }), + ) + return + + transcript = current.get('discussion') or [] + recent = transcript[-6:] + recent_text = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:400]}' + for x in recent + ]) if recent else '暂无' + emperor_notes = current.get('emperorNotes') or [] + latest_note = emperor_notes[-1].get('text', '') if emperor_notes else '' + label = _agent_label(aid) + entry_id = f'cd-entry-{uuid.uuid4().hex[:12]}' + + def _append_pending_entry(s): + s.setdefault('discussion', []).append({ + 'entryId': entry_id, + 'round': next_round, + 'turn': idx + 1, + 'totalTurns': len(selected), + 'agentId': aid, + 'agentLabel': label, + 'reply': '', + 'status': 'speaking', + 'error': False, + 'at': now_iso(), + }) + s['speakingNow'] = { + 'round': next_round, + 'turn': idx + 1, + 'totalTurns': len(selected), + 'agentId': aid, + 'agentLabel': label, + } + s['updatedAt'] = now_iso() + s['message'] = f'第 {next_round} 轮进行中:{label} 正在发言({idx + 1}/{len(selected)})' + + _update_court_session(session_id, _append_pending_entry) + + prompt = ( + f'你正在参与御前议政讨论,角色是「{label}」。\n' + f'议题:{topic}\n' + f'当前第 {next_round} 轮,你是本轮第 {idx + 1}/{len(selected)} 位发言。\n\n' + f'最近讨论摘要:\n{recent_text}\n\n' + f'皇上最新批示:{latest_note or "暂无"}\n\n' + f'请输出四段(中文、简洁):\n' + f'【你认为最关键的澄清点】\n' + f'【你看到的主要风险】\n' + f'【你建议皇上现在做的决定】\n' + f'【可直接执行的修改建议】' + ) + + reply = '' + error_text = '' + try: + reply = _run_agent_sync(aid, prompt, timeout_sec=60) + except Exception as e: + error_text = _friendly_agent_error(str(e)) + reply = ( + f'【系统降级】{label} 本轮发言失败:{error_text}\n' + f'【建议】请皇上选择“继续一轮”或调整参与大臣后再议。' + ) + + def _update_entry_done(s): + updated = False + for item in s.get('discussion', []): + if item.get('entryId') == entry_id: + item['reply'] = reply[:4000] + item['error'] = bool(error_text) + item['status'] = 'error' if error_text else 'done' + updated = True + break + if not updated: + s.setdefault('discussion', []).append({ + 'entryId': entry_id, + 'round': next_round, + 'turn': idx + 1, + 'totalTurns': len(selected), + 'agentId': aid, + 'agentLabel': label, + 'reply': reply[:4000], + 'error': bool(error_text), + 'status': 'error' if error_text else 'done', + 'at': now_iso(), + }) + s['updatedAt'] = now_iso() + s['message'] = f'第 {next_round} 轮进行中:{label} 已发言({idx + 1}/{len(selected)})' + + _update_court_session(session_id, _update_entry_done) + + final_session = _load_court_session(session_id) or {} + round_entries = [ + x for x in (final_session.get('discussion') or []) + if int(x.get('round') or 0) == next_round and (x.get('status') in ('done', 'error') or x.get('reply')) + ] + moderator_id = final_session.get('moderatorId') or _pick_moderator(selected) + moderator_label = _agent_label(moderator_id) + recent_round_text = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:1200]}' + for x in round_entries + ]) + assess_prompt = ( + f'你现在是本轮议政主持人({moderator_label})。\n' + f'议题:{topic}\n' + f'第 {next_round} 轮各方意见如下:\n{recent_round_text}\n\n' + f'请只输出 JSON(不要代码块):\n' + f'{{\n' + f' "recommend_stop": true,\n' + f' "reason": "为何建议结束/继续",\n' + f' "question_to_emperor": "请皇上拍板的问题",\n' + f' "focus_next_round": ["若继续,下一轮重点1", "重点2"],\n' + f' "draft_direction": "若现在结束,旨意草案应强调什么"\n' + f'}}' + ) + + assess_raw = '' + assess = {} + try: + assess_raw = _run_agent_sync(moderator_id, assess_prompt, timeout_sec=120) + assess = _extract_json_obj(assess_raw) or {} + except Exception as e: + err_msg = _friendly_agent_error(str(e)) + has_error_entry = any(bool(x.get('error')) for x in round_entries) + assess = { + 'recommend_stop': bool(has_error_entry), + 'reason': f'主持评估降级:{err_msg}', + 'question_to_emperor': '是否继续下一轮讨论,或直接终止该话题?', + 'focus_next_round': ['先排查失败大臣模型兼容性', '收敛为可执行目标'], + 'draft_direction': '若无共识建议先终止,若有可执行路径则交由太子办理', + } + assess_raw = str(e)[:2000] + + assessment = { + 'round': next_round, + 'moderatorId': moderator_id, + 'moderatorLabel': moderator_label, + 'recommend_stop': bool(assess.get('recommend_stop', False)), + 'reason': str(assess.get('reason') or '').strip(), + 'question_to_emperor': str(assess.get('question_to_emperor') or '').strip(), + 'focus_next_round': assess.get('focus_next_round') if isinstance(assess.get('focus_next_round'), list) else [], + 'draft_direction': str(assess.get('draft_direction') or '').strip(), + 'raw': assess_raw[:2000], + 'at': now_iso(), + } + + def _finish_round(s): + s.setdefault('assessments', []).append(assessment) + s['rounds'] = next_round + s['suggestedAction'] = 'finalize' if assessment.get('recommend_stop') else 'next' + s['roundRunning'] = False + s['speakingNow'] = {} + s['updatedAt'] = now_iso() + s['message'] = ( + f'第 {next_round} 轮结束,{moderator_label}建议' + f'{"可请皇上决定结束讨论" if assessment.get("recommend_stop") else "继续讨论一轮"}' + ) + + _update_court_session(session_id, _finish_round) + except Exception as e: + _update_court_session( + session_id, + lambda s: s.update({ + 'roundRunning': False, + 'speakingNow': {}, + 'updatedAt': now_iso(), + 'message': f'议政轮次执行失败:{_friendly_agent_error(str(e))}', + }), + ) + + threading.Thread(target=_worker, daemon=True).start() + return {'ok': True, 'message': f'第 {next_round} 轮已启动'} + + +def _finalize_court_session(session, force=False): + topic = session.get('topic', '') + transcript = session.get('discussion') or [] + if not transcript: + return {'ok': False, 'error': '暂无讨论内容,无法生成结论'} + + moderator_id = session.get('moderatorId') or _pick_moderator(session.get('participants') or ['taizi']) + discuss_pack = '\n\n'.join([ + f'[{x.get("agentLabel", "")}] {(x.get("reply", "") or "")[:1200]}' + for x in transcript[-24:] + ]) + assess_pack = '\n'.join([ + f'第{a.get("round")}轮建议: {a.get("reason", "")}' + for a in (session.get('assessments') or [])[-6:] + ]) + emperor_notes = session.get('emperorNotes') or [] + emperor_pack = '\n'.join([f'- {(x.get("text") or "")[:200]}' for x in emperor_notes[-6:]]) or '暂无' + synth_prompt = ( + f'请作为太子秘书处,基于御前讨论输出最终可执行结论。\n' + f'议题:{topic}\n' + f'主持审议摘要:\n{assess_pack or "暂无"}\n\n' + f'皇上批示:\n{emperor_pack}\n\n' + f'讨论记录:\n{discuss_pack}\n\n' + f'请只输出 JSON(不要代码块):\n' + f'{{\n' + f' "ready_for_edict": true,\n' + f' "clarified_goal": "一句话目标",\n' + f' "risks": ["风险1","风险2"],\n' + f' "questions_to_emperor": ["若仍有未定项,在此列出"],\n' + f' "recommended_edict": "可直接下旨的完整文本",\n' + f' "recommended_target_dept": "中书省",\n' + f' "recommended_priority": "normal"\n' + f'}}' + ) + synth_raw = '' + synth = {} + try: + synth_raw = _run_agent_sync(moderator_id, synth_prompt, timeout_sec=120) + synth = _extract_json_obj(synth_raw) or {} + except Exception as e: + err_msg = _friendly_agent_error(str(e)) + synth_raw = str(e)[:4000] + has_error_entry = any(bool(x.get('error')) for x in transcript[-24:]) + synth = { + 'ready_for_edict': False if has_error_entry else True, + 'clarified_goal': topic[:80], + 'risks': [f'总结阶段降级:{err_msg}'], + 'questions_to_emperor': ['是否允许在降级结论下交由太子办理?'], + 'recommended_edict': f'请太子先组织可行性评估:{topic}', + 'recommended_target_dept': '中书省', + 'recommended_priority': 'normal', + } + final = { + 'ready_for_edict': bool(synth.get('ready_for_edict', False)), + 'clarified_goal': str(synth.get('clarified_goal') or '').strip(), + 'risks': synth.get('risks') if isinstance(synth.get('risks'), list) else [], + 'questions_to_emperor': ( + synth.get('questions_to_emperor') if isinstance(synth.get('questions_to_emperor'), list) else [] + ), + 'recommended_edict': str(synth.get('recommended_edict') or topic).strip(), + 'recommended_target_dept': str(synth.get('recommended_target_dept') or '中书省').strip(), + 'recommended_priority': str(synth.get('recommended_priority') or 'normal').strip(), + 'forceFinalized': bool(force), + 'raw': synth_raw[:4000], + } + if _needs_edict_fallback(topic, final.get('recommended_edict', '')): + final['recommended_edict'] = _build_fallback_edict_text(session, final) + if final['recommended_priority'] not in ('low', 'normal', 'high', 'critical'): + final['recommended_priority'] = 'normal' + if final['recommended_target_dept'] not in ('中书省', '尚书省', '礼部', '户部', '兵部', '刑部', '工部', '吏部'): + final['recommended_target_dept'] = '中书省' + + session['final'] = final + session['status'] = 'done' + session['suggestedAction'] = 'finalize' + session['updatedAt'] = now_iso() + session['finalizedAt'] = now_iso() + session['message'] = '议政讨论已结束,可直接下旨' + return {'ok': True, 'final': final} + + +def _handoff_court_session(session, force=False): + if session.get('status') == 'terminated': + return {'ok': False, 'error': '话题已终止,无法交办'} + if session.get('roundRunning'): + return { + 'ok': False, + 'error': f'第{int(session.get("currentRound") or 0)}轮发言进行中,请等待本轮结束后再交办', + } + if session.get('linkedTaskId'): + return {'ok': True, 'taskId': session.get('linkedTaskId', ''), 'reused': True} + if not session.get('final'): + finalized = _finalize_court_session(session, force=bool(force)) + if not finalized.get('ok'): + return finalized + + final = session.get('final') or {} + if not bool(final.get('ready_for_edict')) and not force: + return {'ok': False, 'error': '当前结论未达到可下旨状态,如需强制交办请传 force=true'} + + title = str(final.get('recommended_edict') or session.get('topic') or '').strip() + if not title: + return {'ok': False, 'error': '结论缺少可交办内容'} + target_dept = str(final.get('recommended_target_dept') or '').strip() + priority = str(final.get('recommended_priority') or 'normal').strip() + create = handle_create_task( + title=title, + org='中书省', + official='中书令', + priority=priority, + template_id='court-discuss', + params={'source': 'court-discuss', 'sessionId': session.get('id', '')}, + target_dept=target_dept, + ) + if not create.get('ok'): + return {'ok': False, 'error': create.get('error') or '交办失败'} + + session['status'] = 'handoffed' + session['linkedTaskId'] = create.get('taskId', '') + session['handoffAt'] = now_iso() + session['updatedAt'] = now_iso() + session['message'] = f'已交由太子办理:{session["linkedTaskId"]}' + return {'ok': True, 'taskId': session.get('linkedTaskId', '')} + + +def handle_court_discuss(action='start', topic='', participants=None, session_id='', force=False, emperor_note=''): + # emperor_note 用于皇上在每轮拍板前补充要求 + action = (action or 'start').strip().lower() + if action in ('start', 'next', 'finalize', 'handoff') and not _check_gateway_alive(): + return {'ok': False, 'error': 'Gateway 未启动,请先运行 openclaw gateway start'} + if action == 'start': + topic = (topic or '').strip() + if len(topic) < 10: + return {'ok': False, 'error': '议题至少 10 个字'} + + allowed = {dept.get('id') for dept in _AGENT_DEPTS} + selected = [] + for aid in (participants or []): + if isinstance(aid, str): + x = aid.strip() + if x and x in allowed and x not in selected: + selected.append(x) + if not selected: + selected = ['taizi', 'zhongshu', 'menxia'] + if len(selected) > 6: + selected = selected[:6] + if len(selected) < 2: + return {'ok': False, 'error': '至少选择 2 位大臣参与讨论'} + for aid in selected: + if not _check_agent_workspace(aid): + return {'ok': False, 'error': f'Agent {aid} 工作空间不存在,请先配置'} + + session = { + 'id': f"CD-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}", + 'topic': topic, + 'participants': selected, + 'moderatorId': _pick_moderator(selected), + 'status': 'ongoing', + 'rounds': 0, + 'currentRound': 0, + 'roundRunning': False, + 'speakingNow': {}, + 'discussion': [], + 'assessments': [], + 'final': None, + 'emperorNotes': [], + 'createdAt': now_iso(), + 'updatedAt': now_iso(), + 'message': '议政会话已创建,等待发言', + } + _append_emperor_note(session, emperor_note) + _upsert_court_session(session) + started = _start_court_round_async(session.get('id', '')) + if not started.get('ok'): + return started + latest = _load_court_session(session.get('id', '')) or session + return _build_court_response(latest, latest.get('message', '')) + + if not session_id: + return {'ok': False, 'error': 'sessionId required'} + session = _load_court_session(session_id) + if not session: + return {'ok': False, 'error': f'讨论会话 {session_id} 不存在'} + note = (emperor_note or '').strip() + if note: + updated = _update_court_session( + session_id, + lambda s: ( + _append_emperor_note(s, note), + s.update({'updatedAt': now_iso()}), + ), + ) + if updated: + session = updated + + if action == 'status': + latest = _load_court_session(session_id) or session + return _build_court_response(latest, latest.get('message', '会话状态已返回')) + + if action == 'next': + if session.get('status') in ('done', 'handoffed', 'terminated'): + return _build_court_response(session, '会话已结束,无需继续') + if session.get('roundRunning'): + return _build_court_response(session, f'第{int(session.get("currentRound") or 0)}轮仍在进行中') + started = _start_court_round_async(session_id) + if not started.get('ok'): + return started + latest = _load_court_session(session_id) or session + return _build_court_response(latest, latest.get('message', '')) + + if action == 'finalize': + if session.get('status') == 'handoffed': + return _build_court_response(session, f'该话题已交办:{session.get("linkedTaskId")}') + if session.get('status') == 'done' and session.get('final'): + return _build_court_response(session, '议政已结束,请在自由下旨区修改后下达') + if session.get('roundRunning'): + return {'ok': False, 'error': f'第{int(session.get("currentRound") or 0)}轮发言进行中,请稍后再形成结论'} + finalized = _finalize_court_session(session, force=bool(force)) + if not finalized.get('ok'): + return finalized + session['message'] = '皇上已拍板:请先修改旨意后再下达' + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + if action == 'handoff': + handoff = _handoff_court_session(session, force=bool(force)) + if not handoff.get('ok'): + return {'ok': False, 'error': handoff.get('error') or '交办失败'} + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + if action == 'terminate': + if session.get('linkedTaskId'): + return _build_court_response(session, f'该话题已交办:{session.get("linkedTaskId")},不可终止') + session['status'] = 'terminated' + session['terminatedAt'] = now_iso() + session['updatedAt'] = now_iso() + session['suggestedAction'] = 'terminate' + session['message'] = '皇上裁决:该话题不进入办理流程,已终止' + _upsert_court_session(session) + return _build_court_response(session, session.get('message', '')) + + return {'ok': False, 'error': f'unsupported action: {action}'} + + # ══ Agent 实时活动读取 ══ # 状态 → agent_id 映射 @@ -874,8 +1904,48 @@ def do_wake(): '刑部': 'xingbu', '工部': 'gongbu', '吏部': 'libu_hr', '中书省': 'zhongshu', '门下省': 'menxia', '尚书省': 'shangshu', } +_EXECUTION_DEPTS = {'礼部', '户部', '兵部', '刑部', '工部', '吏部'} _TERMINAL_STATES = {'Done', 'Cancelled'} +_DIAG_MAX_LOG = 300 +_FLOW_DEDUPE_WINDOW_SEC = 60 + +_CONTROL_STATE_BY_STATE = { + 'Pending': 'Pending', + 'Taizi': 'Taizi', + 'Zhongshu': 'Zhongshu', + 'Menxia': 'WaitingDecision', + 'Assigned': 'Assigned', + 'Next': 'Assigned', + 'Doing': 'Doing', + 'Review': 'WaitingDecision', + 'Done': 'Completed', + 'Cancelled': 'Cancelled', + 'Blocked': 'Blocked', +} + +_CONTROL_ACTION_ALLOWLIST = { + 'Pending': {'dispatch', 'advance', 'noop'}, + 'Taizi': {'dispatch', 'advance', 'retry', 'noop'}, + 'Zhongshu': {'dispatch', 'advance', 'retry', 'escalate', 'noop'}, + 'Assigned': {'dispatch', 'retry', 'escalate', 'advance', 'rollback', 'wait_human', 'noop'}, + 'Doing': {'dispatch', 'retry', 'writeback_retry', 'advance', 'wait_human', 'noop'}, + 'ExecutionOutputReady': {'writeback_retry', 'wait_human', 'noop'}, + 'WritebackPending': {'writeback_retry', 'wait_human', 'noop'}, + 'RetryableFailure': {'retry', 'wait_human', 'rollback', 'noop'}, + 'WaitingDecision': {'wait_human', 'manual_decide', 'noop'}, + 'EscalationCandidate': {'escalate', 'wait_human', 'noop'}, + 'Blocked': {'manual_decide', 'noop'}, + 'Completed': {'manual_decide', 'noop'}, + 'Cancelled': {'manual_decide', 'noop'}, +} + +_COOLDOWN_SECONDS = { + 'post_dispatch_escalate': 90, + 'post_dispatch_dispatch': 90, + 'post_doing_retry': 120, + 'post_human_decision_reassign': 180, +} def _parse_iso(ts): @@ -887,6 +1957,178 @@ def _parse_iso(ts): return None +def _new_run_id(): + return f'run-{uuid.uuid4().hex[:10]}' + + +def _lease_expired(lease, now_dt=None): + if not isinstance(lease, dict): + return True + ttl_sec = int(lease.get('ttlSec') or 0) + hb = _parse_iso(lease.get('heartbeatAt') or lease.get('acquiredAt')) + if ttl_sec <= 0 or not hb: + return True + now_dt = now_dt or datetime.datetime.now(datetime.timezone.utc) + return (now_dt - hb).total_seconds() > ttl_sec + + +def _sync_control_state(task): + sched = task.setdefault('_scheduler', {}) + if not isinstance(sched, dict): + sched = {} + task['_scheduler'] = sched + state = task.get('state', '') + writeback = sched.get('writeback') or {} + wb_status = writeback.get('status', '') + if wb_status == 'WritebackPending': + control_state = 'WritebackPending' + elif wb_status == 'ExecutionOutputReady': + control_state = 'ExecutionOutputReady' + else: + control_state = _CONTROL_STATE_BY_STATE.get(state, 'Assigned') + sched['controlState'] = control_state + return control_state + + +def _append_diagnostic( + task, + event_type, + reason_code, + details='', + action='', + dedupe_key='', + suppress_window_sec=60, +): + diag = task.setdefault('diagnostic_log', []) + if dedupe_key: + now_dt = datetime.datetime.now(datetime.timezone.utc) + for item in reversed(diag[-30:]): + if item.get('dedupeKey') != dedupe_key: + continue + prev_dt = _parse_iso(item.get('at')) + if prev_dt and (now_dt - prev_dt).total_seconds() <= suppress_window_sec: + return False + break + diag.append({ + 'at': now_iso(), + 'eventType': event_type, + 'action': action, + 'reasonCode': reason_code, + 'details': details, + 'dedupeKey': dedupe_key, + }) + if len(diag) > _DIAG_MAX_LOG: + task['diagnostic_log'] = diag[-_DIAG_MAX_LOG:] + return True + + +def _set_cooldown(task, key, seconds): + if seconds <= 0: + return + sched = _ensure_scheduler(task) + cds = sched.setdefault('cooldowns', {}) + until_dt = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(seconds=int(seconds)) + cds[key] = until_dt.isoformat().replace('+00:00', 'Z') + + +def _cooldown_remaining(task, key): + sched = _ensure_scheduler(task) + cds = sched.get('cooldowns') or {} + until_dt = _parse_iso(cds.get(key)) + if not until_dt: + return 0 + left = (until_dt - datetime.datetime.now(datetime.timezone.utc)).total_seconds() + return max(0, int(left)) + + +def _cooldown_block_for_action(task, action): + blocks = [] + if action in ('dispatch', 'retry'): + left = _cooldown_remaining(task, 'noDispatchUntil') + if left > 0: + blocks.append(('dispatchCooldown', left)) + if action == 'retry' and task.get('state') == 'Doing': + left = _cooldown_remaining(task, 'noRetryUntil') + if left > 0: + blocks.append(('retryCooldown', left)) + if action == 'escalate': + left = _cooldown_remaining(task, 'noEscalateUntil') + if left > 0: + blocks.append(('escalateCooldown', left)) + left2 = _cooldown_remaining(task, 'noReassignUntil') + if left2 > 0: + blocks.append(('reassignCooldown', left2)) + return blocks + + +def _action_allowed(task, action): + sched = _ensure_scheduler(task) + control_state = _sync_control_state(task) + allowed = _CONTROL_ACTION_ALLOWLIST.get(control_state, {'noop'}) + if action not in allowed: + return { + 'ok': False, + 'blockedBy': 'stateGuard', + 'controlState': control_state, + 'allowedActions': sorted(allowed), + } + cd_blocks = _cooldown_block_for_action(task, action) + if cd_blocks: + code, left = cd_blocks[0] + return { + 'ok': False, + 'blockedBy': code, + 'cooldownSec': left, + 'controlState': control_state, + 'allowedActions': sorted(allowed), + } + return {'ok': True, 'controlState': control_state, 'allowedActions': sorted(allowed)} + + +def _acquire_lease(task, stage, role, owner_run_id, ttl_sec=180, force_takeover=False): + sched = _ensure_scheduler(task) + lease = sched.setdefault('lease', {}) + now_ts = now_iso() + current_owner = lease.get('ownerRunId', '') + expired = _lease_expired(lease) + if force_takeover or not current_owner or expired: + lease.update({ + 'stage': stage, + 'role': role, + 'ownerRunId': owner_run_id, + 'acquiredAt': now_ts, + 'heartbeatAt': now_ts, + 'ttlSec': int(ttl_sec), + }) + return {'ok': True, 'takenOver': bool(current_owner and current_owner != owner_run_id)} + if current_owner == owner_run_id: + lease['heartbeatAt'] = now_ts + lease['ttlSec'] = int(ttl_sec) + return {'ok': True, 'takenOver': False} + return {'ok': False, 'blockedBy': 'leaseBusy', 'ownerRunId': current_owner} + + +def _renew_lease(task, owner_run_id, ttl_sec=None): + sched = _ensure_scheduler(task) + lease = sched.setdefault('lease', {}) + if lease.get('ownerRunId') != owner_run_id: + return False + lease['heartbeatAt'] = now_iso() + if ttl_sec: + lease['ttlSec'] = int(ttl_sec) + return True + + +def _release_lease(task, owner_run_id): + sched = _ensure_scheduler(task) + lease = sched.get('lease') or {} + if lease.get('ownerRunId') != owner_run_id: + return False + lease['ownerRunId'] = '' + lease['releasedAt'] = now_iso() + return True + + def _ensure_scheduler(task): sched = task.setdefault('_scheduler', {}) if not isinstance(sched, dict): @@ -897,13 +2139,48 @@ def _ensure_scheduler(task): sched.setdefault('maxRetry', 1) sched.setdefault('retryCount', 0) sched.setdefault('escalationLevel', 0) + sched.setdefault('maxStateAgeSec', 900) sched.setdefault('autoRollback', True) + sched.setdefault('autoAdvance', True) if not sched.get('lastProgressAt'): sched['lastProgressAt'] = task.get('updatedAt') or now_iso() + cur_state = task.get('state', '') + if not sched.get('stateSince'): + sched['stateSince'] = task.get('updatedAt') or now_iso() + if sched.get('stateName') != cur_state: + sched['stateName'] = cur_state + sched['stateSince'] = task.get('updatedAt') or now_iso() if 'stallSince' not in sched: sched['stallSince'] = None + if 'awaitingEmperorDecision' not in sched: + sched['awaitingEmperorDecision'] = False if 'lastDispatchStatus' not in sched: sched['lastDispatchStatus'] = 'idle' + sched.setdefault('stateVersion', 0) + sched.setdefault('lastCommit', {}) + sched.setdefault('lastAction', {}) + sched.setdefault('cooldowns', {}) + lease = sched.setdefault('lease', {}) + if not isinstance(lease, dict): + lease = {} + sched['lease'] = lease + lease.setdefault('stage', '') + lease.setdefault('role', '') + lease.setdefault('ownerRunId', '') + lease.setdefault('acquiredAt', '') + lease.setdefault('heartbeatAt', '') + lease.setdefault('ttlSec', 180) + writeback = sched.setdefault('writeback', {}) + if not isinstance(writeback, dict): + writeback = {} + sched['writeback'] = writeback + writeback.setdefault('status', 'idle') + writeback.setdefault('retryCount', 0) + writeback.setdefault('maxRetry', 2) + writeback.setdefault('firstOutputAt', '') + writeback.setdefault('lastCommittedAt', '') + writeback.setdefault('lastError', '') + writeback.setdefault('lastDispatchOutput', '') if 'snapshot' not in sched: sched['snapshot'] = { 'state': task.get('state', ''), @@ -912,16 +2189,34 @@ def _ensure_scheduler(task): 'savedAt': now_iso(), 'note': 'init', } + _sync_control_state(task) return sched -def _scheduler_add_flow(task, remark, to=''): - task.setdefault('flow_log', []).append({ +def _scheduler_add_flow(task, remark, to='', reason_code=''): + flow_log = task.setdefault('flow_log', []) + now_dt = datetime.datetime.now(datetime.timezone.utc) + entry = { 'at': now_iso(), 'from': '太子调度', 'to': to or task.get('org', ''), 'remark': f'🧭 {remark}' - }) + } + if reason_code: + entry['reasonCode'] = reason_code + + if flow_log: + last = flow_log[-1] + last_dt = _parse_iso(last.get('at')) + if ( + last.get('from') == entry['from'] + and last.get('to') == entry['to'] + and last.get('remark') == entry['remark'] + and ((now_dt - last_dt).total_seconds() <= _FLOW_DEDUPE_WINDOW_SEC if last_dt else False) + ): + return False + flow_log.append(entry) + return True def _scheduler_snapshot(task, note=''): @@ -935,15 +2230,236 @@ def _scheduler_snapshot(task, note=''): } -def _scheduler_mark_progress(task, note=''): +def _scheduler_mark_progress(task, note='', reason_code='progress_update'): sched = _ensure_scheduler(task) sched['lastProgressAt'] = now_iso() sched['stallSince'] = None + sched['awaitingEmperorDecision'] = False + sched['decisionPacket'] = None sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['lastEscalatedAt'] = None if note: - _scheduler_add_flow(task, f'进展确认:{note}') + _scheduler_add_flow(task, f'进展确认:{note}', reason_code=reason_code) + + +def _scheduler_mark_state_change(task, new_state, reason_code='state_change'): + sched = _ensure_scheduler(task) + sched['stateName'] = new_state + sched['stateSince'] = now_iso() + sched['awaitingEmperorDecision'] = False + sched['decisionPacket'] = None + if new_state == 'Doing': + _set_cooldown(task, 'noRetryUntil', _COOLDOWN_SECONDS['post_doing_retry']) + _sync_control_state(task) + sched['lastAction'] = { + 'action': 'state_change', + 'reasonCode': reason_code, + 'at': now_iso(), + } + + +def commit_state_change( + task, + action, + reason_code, + owner_run_id='', + expected_version=None, + to_state=None, + to_org=None, + now_text=None, + block_text=None, + flow_remark='', + flow_from='太子调度', + flow_to='', + force=False, +): + sched = _ensure_scheduler(task) + current_state = task.get('state', '') + current_version = int(sched.get('stateVersion') or 0) + + if expected_version is not None and int(expected_version) != current_version: + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='version_conflict', + details=f'expected={expected_version}, current={current_version}', + dedupe_key=f'{task.get("id")}:version:{action}:{expected_version}:{current_version}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': 'versionConflict', + 'currentVersion': current_version, + 'expectedVersion': expected_version, + } + return {'ok': False, 'committed': False, 'blockedBy': 'versionConflict', 'currentVersion': current_version} + + if owner_run_id and not force: + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if not lease_owner or lease_owner != owner_run_id or _lease_expired(lease): + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='stale_owner', + details=f'owner={owner_run_id}, leaseOwner={lease_owner}', + dedupe_key=f'{task.get("id")}:owner:{action}:{owner_run_id}:{lease_owner}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': 'staleOwner', + 'ownerRunId': owner_run_id, + 'leaseOwner': lease_owner, + } + return {'ok': False, 'committed': False, 'blockedBy': 'staleOwner', 'leaseOwner': lease_owner} + _renew_lease(task, owner_run_id) + + if not force: + allowed = _action_allowed(task, action) + if not allowed.get('ok'): + _append_diagnostic( + task, + event_type='state_commit_blocked', + action=action, + reason_code='action_blocked', + details=str(allowed), + dedupe_key=f'{task.get("id")}:blocked:{action}:{allowed.get("blockedBy")}', + ) + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'blocked', + 'blockedBy': allowed.get('blockedBy'), + 'controlState': allowed.get('controlState'), + } + return {'ok': False, 'committed': False, 'blockedBy': allowed.get('blockedBy')} + + state_changed = False + prev_state = current_state + if to_state is not None and to_state != current_state: + task['state'] = to_state + state_changed = True + if to_org is not None: + task['org'] = to_org + else: + task['org'] = _derive_org_for_state(task, to_state, task.get('org', '')) + # 进入非执行态时清理 writeback 阶段残留,避免 controlState 被旧状态污染 + if to_state != 'Doing': + wb = sched.setdefault('writeback', {}) + wb['status'] = 'idle' + wb['lastError'] = '' + _scheduler_mark_state_change(task, to_state, reason_code=reason_code) + elif to_org is not None: + task['org'] = to_org + + if now_text is not None: + task['now'] = now_text + if block_text is not None: + task['block'] = block_text + + if flow_remark: + task.setdefault('flow_log', []).append({ + 'at': now_iso(), + 'from': flow_from, + 'to': flow_to or task.get('org', ''), + 'remark': flow_remark, + 'reasonCode': reason_code, + }) + + sched['stateVersion'] = current_version + 1 + sched['lastAction'] = {'action': action, 'reasonCode': reason_code, 'at': now_iso()} + sched['lastCommit'] = { + 'at': now_iso(), + 'action': action, + 'reasonCode': reason_code, + 'result': 'committed', + 'ownerRunId': owner_run_id, + 'fromState': prev_state, + 'toState': task.get('state', ''), + 'stateChanged': state_changed, + 'version': sched['stateVersion'], + } + _sync_control_state(task) + task['updatedAt'] = now_iso() + return {'ok': True, 'committed': True, 'stateChanged': state_changed, 'stateVersion': sched['stateVersion']} + + +def _build_decision_packet(task, state, stalled_sec=0, state_age_sec=0): + title = (task.get('title') or '').strip() + now_text = (task.get('now') or '').strip() + progress_log = task.get('progress_log') or [] + flow_log = task.get('flow_log') or [] + latest_progress = (progress_log[-1].get('text') if progress_log else '') or now_text + latest_flow = (flow_log[-1].get('remark') if flow_log else '') + + if state == 'Menxia': + question = '请拍板:是否准奏并移交尚书省执行?' + options = [ + { + 'id': 'approve', + 'label': '准奏推进', + 'impact': '状态变更为 Assigned,尚书省开始派发六部执行(推荐)', + }, + { + 'id': 'reject', + 'label': '封驳退回', + 'impact': '状态回到 Zhongshu,要求中书省补充/修改方案后再审', + }, + ] + recommended = 'approve' + elif state == 'Review': + question = '请拍板:是否验收通过并结案?' + options = [ + { + 'id': 'approve', + 'label': '验收通过', + 'impact': '状态变更为 Done,任务归档结束(推荐)', + }, + { + 'id': 'reject', + 'label': '退回整改', + 'impact': '状态回到 Zhongshu,按封驳意见继续修订', + }, + ] + recommended = 'approve' + else: + question = '请拍板:是否继续当前推进策略?' + options = [ + {'id': 'approve', 'label': '继续推进', 'impact': '按当前流程继续自动调度(推荐)'}, + {'id': 'reject', 'label': '人工干预', 'impact': '改为人工指定下一步或回滚'}, + ] + recommended = 'approve' + + evidence = [ + f'任务: {task.get("id", "")}', + f'状态: {_STATE_LABELS.get(state, state)}', + f'停滞: {int(stalled_sec)}秒', + f'驻留: {int(state_age_sec)}秒', + ] + if title: + evidence.append(f'旨意: {title[:120]}') + if latest_progress: + evidence.append(f'最近进展: {latest_progress[:200]}') + if latest_flow: + evidence.append(f'最近流转: {latest_flow[:200]}') + + return { + 'state': state, + 'question': question, + 'options': options, + 'recommended': recommended, + 'evidence': evidence, + 'generatedAt': now_iso(), + } def _update_task_scheduler(task_id, updater): @@ -953,11 +2469,82 @@ def _update_task_scheduler(task_id, updater): return False sched = _ensure_scheduler(task) updater(task, sched) + _sync_control_state(task) task['updatedAt'] = now_iso() save_tasks(tasks) return True +def _retry_writeback_for_task(task_id, owner_run_id=''): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + + sched = _ensure_scheduler(task) + wb = sched.setdefault('writeback', {}) + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if owner_run_id and lease_owner and owner_run_id != lease_owner: + _append_diagnostic( + task, + event_type='writeback_retry_blocked', + action='writeback_retry', + reason_code='stale_owner', + details=f'owner={owner_run_id}, leaseOwner={lease_owner}', + dedupe_key=f'{task_id}:writeback_retry:stale_owner', + ) + save_tasks(tasks) + return {'ok': False, 'blockedBy': 'staleOwner'} + + output = wb.get('lastDispatchOutput', '') + if not output: + wb['status'] = 'ExecutionOutputReady' + wb['lastError'] = 'missing_dispatch_output' + _append_diagnostic( + task, + event_type='writeback_retry_blocked', + action='writeback_retry', + reason_code='missing_dispatch_output', + details=f'task={task_id}', + dedupe_key=f'{task_id}:writeback_retry:missing_output', + ) + save_tasks(tasks) + return {'ok': False, 'blockedBy': 'missingDispatchOutput'} + + bridge = _bridge_apply_kanban_commands(task_id, output) + if bridge.get('attempted', 0) > 0 and bridge.get('applied', 0) >= bridge.get('attempted', 0): + wb['status'] = 'idle' + wb['retryCount'] = 0 + wb['lastCommittedAt'] = now_iso() + wb['lastError'] = '' + _scheduler_add_flow(task, '写回重试成功,提交已落板', reason_code='writeback_retry_success') + _release_lease(task, owner_run_id or lease_owner) + save_tasks(tasks) + return {'ok': True, 'committed': True, 'attempted': bridge.get('attempted', 0), 'applied': bridge.get('applied', 0)} + + wb['status'] = 'WritebackPending' + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['lastError'] = '; '.join((bridge.get('errors') or [])[:2]) or 'writeback_retry_failed' + _append_diagnostic( + task, + event_type='writeback_retry_failed', + action='writeback_retry', + reason_code='writeback_retry_failed', + details=wb['lastError'], + dedupe_key=f'{task_id}:writeback_retry:failed', + ) + _scheduler_add_flow(task, '写回重试失败,等待下一次提交重试', reason_code='writeback_retry_failed') + save_tasks(tasks) + return { + 'ok': False, + 'committed': False, + 'attempted': bridge.get('attempted', 0), + 'applied': bridge.get('applied', 0), + 'error': wb.get('lastError', ''), + } + + def get_scheduler_state(task_id): tasks = load_tasks() task = next((t for t in tasks if t.get('id') == task_id), None) @@ -969,17 +2556,276 @@ def get_scheduler_state(task_id): stalled_sec = 0 if last_progress: stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) + state_since = _parse_iso(sched.get('stateSince') or task.get('updatedAt')) + state_age_sec = 0 + if state_since: + state_age_sec = max(0, int((now_dt - state_since).total_seconds())) + state_age_limit = int(sched.get('maxStateAgeSec') or 0) return { 'ok': True, 'taskId': task_id, 'state': task.get('state', ''), 'org': task.get('org', ''), 'scheduler': sched, + 'controlState': sched.get('controlState'), + 'lease': sched.get('lease'), + 'lastAction': sched.get('lastAction'), + 'writeback': sched.get('writeback'), + 'decision': sched.get('decisionPacket'), 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + 'stateAgeLimitSec': state_age_limit, + 'checkedAt': now_iso(), + } + + +def get_scheduler_metrics(task_id=''): + tasks = load_tasks() + if task_id: + tasks = [t for t in tasks if t.get('id') == task_id] + if not tasks: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + + task_metrics = [] + total_dispatch_attempts = 0 + total_unique_steps = 0 + total_invalid_control = 0 + total_control_actions = 0 + writeback_lags = [] + + now_dt = datetime.datetime.now(datetime.timezone.utc) + for task in tasks: + sched = _ensure_scheduler(task) + progress_log = task.get('progress_log') or [] + diagnostic_log = task.get('diagnostic_log') or [] + flow_log = task.get('flow_log') or [] + + dispatch_attempts = int(sched.get('dispatchAttempts') or 0) + unique_execution_steps = len({ + (p.get('agent', ''), p.get('text', ''), p.get('state', '')) + for p in progress_log if p.get('text') + }) + unique_execution_steps = max(unique_execution_steps, 1 if dispatch_attempts > 0 else 0) + amplification_ratio = round(dispatch_attempts / unique_execution_steps, 2) if unique_execution_steps else 0.0 + + control_actions = sum( + 1 for f in flow_log + if isinstance(f, dict) and str(f.get('from', '')).startswith('太子调度') + ) + invalid_control = sum( + 1 for d in diagnostic_log + if d.get('eventType') in ('state_commit_blocked', 'control_blocked') + ) + invalid_ratio = round(invalid_control / control_actions, 3) if control_actions else 0.0 + + wb = sched.get('writeback') or {} + first_output = _parse_iso(wb.get('firstOutputAt')) + committed_at = _parse_iso(wb.get('lastCommittedAt')) + writeback_lag_sec = None + if first_output: + end_dt = committed_at or now_dt + writeback_lag_sec = max(0, int((end_dt - first_output).total_seconds())) + writeback_lags.append(writeback_lag_sec) + + task_metrics.append({ + 'taskId': task.get('id', ''), + 'state': task.get('state', ''), + 'dispatchAttempts': dispatch_attempts, + 'uniqueExecutionSteps': unique_execution_steps, + 'dispatchAmplificationRatio': amplification_ratio, + 'controlActions': control_actions, + 'invalidControlActions': invalid_control, + 'invalidControlRatio': invalid_ratio, + 'writebackLagSec': writeback_lag_sec, + 'writebackStatus': wb.get('status', 'idle'), + }) + + total_dispatch_attempts += dispatch_attempts + total_unique_steps += unique_execution_steps + total_invalid_control += invalid_control + total_control_actions += control_actions + + global_amp = round(total_dispatch_attempts / total_unique_steps, 2) if total_unique_steps else 0.0 + global_invalid = round(total_invalid_control / total_control_actions, 3) if total_control_actions else 0.0 + avg_writeback_lag = ( + round(sum(writeback_lags) / len(writeback_lags), 2) if writeback_lags else None + ) + + return { + 'ok': True, + 'taskId': task_id or '', + 'metrics': task_metrics, + 'summary': { + 'taskCount': len(task_metrics), + 'dispatchAttempts': total_dispatch_attempts, + 'uniqueExecutionSteps': total_unique_steps, + 'dispatchAmplificationRatio': global_amp, + 'controlActions': total_control_actions, + 'invalidControlActions': total_invalid_control, + 'invalidControlRatio': global_invalid, + 'avgWritebackLagSec': avg_writeback_lag, + }, 'checkedAt': now_iso(), } +def handle_scheduler_action(task_id, action, reason='', expected_version=None, owner_run_id='', recovery_target=''): + action = (action or '').strip() + if action == 'retry': + return handle_scheduler_retry(task_id, reason) + if action == 'escalate': + return handle_scheduler_escalate(task_id, reason) + if action == 'rollback': + return handle_scheduler_rollback(task_id, reason) + if action in ('wait_human', 'manual_decide'): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + _ensure_scheduler(task) + sched = task.get('_scheduler') or {} + run_id = owner_run_id or _new_run_id() + _acquire_lease(task, stage=task.get('state', ''), role='manual', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + target = (recovery_target or '').strip() + reason_code = 'manual_human_decision' + to_state = None + to_org = None + now_text = task.get('now', '') + block_text = task.get('block', '') + trigger_dispatch_state = '' + trigger_writeback_retry = False + + if target == 'continue_execution': + reason_code = 'human_continue_execution' + to_state = 'Doing' + to_org = _derive_org_for_state(task, 'Doing', task.get('org', '')) + now_text = '👑 皇上裁决:继续执行' + block_text = '无' + trigger_dispatch_state = 'Doing' + elif target == 'continue_writeback': + reason_code = 'human_continue_writeback' + now_text = '👑 皇上裁决:继续提交写回' + block_text = '无' + trigger_writeback_retry = True + elif target == 'reassign': + reason_code = 'human_reassign' + to_state = 'Assigned' + to_org = '尚书省' + now_text = '👑 皇上裁决:改派尚书省重新派发' + block_text = '无' + trigger_dispatch_state = 'Assigned' + elif target == 'terminate': + reason_code = 'human_terminate' + to_state = 'Cancelled' + to_org = task.get('org', '') + now_text = f'👑 皇上裁决:终止任务({reason or "人工终止"})' + block_text = reason or '皇上终止' + + commit = commit_state_change( + task, + action='manual_decide', + reason_code=reason_code, + owner_run_id=run_id, + expected_version=expected_version if expected_version is not None else sched.get('stateVersion'), + to_state=to_state, + to_org=to_org, + now_text=now_text, + block_text=block_text, + flow_from='皇上', + flow_remark=f'👑 人工裁决:{reason or "无"}(目标:{target or "仅记录"})', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'提交失败: {commit.get("blockedBy")}'} + if trigger_writeback_retry: + wb = sched.setdefault('writeback', {}) + wb['status'] = 'WritebackPending' + wb['lastError'] = wb.get('lastError') or 'human_resume_writeback' + if trigger_dispatch_state: + cds = sched.setdefault('cooldowns', {}) + cds.pop('noDispatchUntil', None) + cds.pop('noEscalateUntil', None) + _release_lease(task, run_id) + _set_cooldown(task, 'noReassignUntil', _COOLDOWN_SECONDS['post_human_decision_reassign']) + save_tasks(tasks) + if trigger_dispatch_state: + dispatch_for_state(task_id, task, trigger_dispatch_state, trigger='human-decision', owner_run_id=run_id) + if trigger_writeback_retry: + _retry_writeback_for_task(task_id, owner_run_id=run_id) + return {'ok': True, 'message': f'{task_id} 已记录人工裁决', 'recoveryTarget': target or 'record_only'} + return {'ok': False, 'error': f'不支持的 action: {action}'} + + +def handle_scheduler_commit(payload): + task_id = (payload.get('taskId') or '').strip() + action = (payload.get('action') or '').strip() + if not task_id or not action: + return {'ok': False, 'error': 'taskId/action required'} + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'error': f'任务 {task_id} 不存在'} + sched = _ensure_scheduler(task) + sched = task.get('_scheduler') or {} + owner_run_id = (payload.get('ownerRunId') or '').strip() or _new_run_id() + expected_version = payload.get('expectedVersion') + to_state = payload.get('toState') + to_org = payload.get('toOrg') + reason_code = (payload.get('reasonCode') or '').strip() or f'manual_commit_{action}' + context = payload.get('context') if isinstance(payload.get('context'), dict) else {} + flow_remark = context.get('flowRemark', f'🔒 统一提交:{action}') + flow_from = context.get('flowFrom', '太子调度') + flow_to = context.get('flowTo', '') + force = bool(payload.get('force', False)) + lease = sched.get('lease') or {} + lease_owner = lease.get('ownerRunId') or '' + if force: + _acquire_lease( + task, + stage=task.get('state', ''), + role='manual-commit', + owner_run_id=owner_run_id, + ttl_sec=180, + force_takeover=True, + ) + elif not lease_owner: + _acquire_lease( + task, + stage=task.get('state', ''), + role='manual-commit', + owner_run_id=owner_run_id, + ttl_sec=180, + force_takeover=False, + ) + elif lease_owner == owner_run_id: + _renew_lease(task, owner_run_id, ttl_sec=180) + + commit = commit_state_change( + task, + action=action, + reason_code=reason_code, + owner_run_id=owner_run_id, + expected_version=expected_version if expected_version is not None else sched.get('stateVersion'), + to_state=to_state, + to_org=to_org, + now_text=context.get('nowText'), + block_text=context.get('blockText'), + flow_remark=flow_remark, + flow_from=flow_from, + flow_to=flow_to, + force=force, + ) + save_tasks(tasks) + return { + 'ok': bool(commit.get('ok')), + 'committed': bool(commit.get('committed')), + 'blockedBy': commit.get('blockedBy'), + 'currentVersion': task.get('_scheduler', {}).get('stateVersion'), + 'taskId': task_id, + } + + def handle_scheduler_retry(task_id, reason=''): tasks = load_tasks() task = next((t for t in tasks if t.get('id') == task_id), None) @@ -990,14 +2836,41 @@ def handle_scheduler_retry(task_id, reason=''): return {'ok': False, 'error': f'任务 {task_id} 当前状态 {state} 不支持重试'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=state, role='scheduler', owner_run_id=run_id, ttl_sec=180) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='retry', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:retry:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + + commit = commit_state_change( + task, + action='retry', + reason_code='manual_retry', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'🔁 手动重试:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 重试被拒绝: {commit.get("blockedBy")}'} + sched['retryCount'] = int(sched.get('retryCount') or 0) + 1 sched['lastRetryAt'] = now_iso() sched['lastDispatchTrigger'] = 'taizi-retry' - _scheduler_add_flow(task, f'触发重试第{sched["retryCount"]}次:{reason or "超时未推进"}') + _scheduler_add_flow(task, f'触发重试第{sched["retryCount"]}次:{reason or "超时未推进"}', reason_code='manual_retry') task['updatedAt'] = now_iso() save_tasks(tasks) - dispatch_for_state(task_id, task, state, trigger='taizi-retry') + dispatch_for_state(task_id, task, state, trigger='taizi-retry', owner_run_id=run_id) return {'ok': True, 'message': f'{task_id} 已触发重试派发', 'retryCount': sched['retryCount']} @@ -1011,6 +2884,33 @@ def handle_scheduler_escalate(task_id, reason=''): return {'ok': False, 'error': f'任务 {task_id} 已结束,无需升级'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=state, role='scheduler', owner_run_id=run_id, ttl_sec=180) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='escalate', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:escalate:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + + commit = commit_state_change( + task, + action='escalate', + reason_code='manual_escalate', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'⬆️ 手动升级:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 升级被拒绝: {commit.get("blockedBy")}'} + current_level = int(sched.get('escalationLevel') or 0) next_level = min(current_level + 1, 2) target = 'menxia' if next_level == 1 else 'shangshu' @@ -1018,7 +2918,7 @@ def handle_scheduler_escalate(task_id, reason=''): sched['escalationLevel'] = next_level sched['lastEscalatedAt'] = now_iso() - _scheduler_add_flow(task, f'升级到{target_label}协调:{reason or "任务停滞"}', to=target_label) + _scheduler_add_flow(task, f'升级到{target_label}协调:{reason or "任务停滞"}', to=target_label, reason_code='manual_escalate') task['updatedAt'] = now_iso() save_tasks(tasks) @@ -1041,21 +2941,48 @@ def handle_scheduler_rollback(task_id, reason=''): if not task: return {'ok': False, 'error': f'任务 {task_id} 不存在'} sched = _ensure_scheduler(task) + run_id = _new_run_id() + lease_result = _acquire_lease(task, stage=task.get('state', ''), role='scheduler', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='rollback', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:rollback:lease_busy', + ) + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 当前被其它流程持有租约'} + snapshot = sched.get('snapshot') or {} snap_state = snapshot.get('state') if not snap_state: return {'ok': False, 'error': f'任务 {task_id} 无可用回滚快照'} old_state = task.get('state', '') - task['state'] = snap_state - task['org'] = snapshot.get('org', task.get('org', '')) - task['now'] = f'↩️ 太子调度自动回滚:{reason or "恢复到上个稳定节点"}' - task['block'] = '无' + commit = commit_state_change( + task, + action='rollback', + reason_code='manual_rollback', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + to_state=snap_state, + to_org=snapshot.get('org', task.get('org', '')), + now_text=f'↩️ 太子调度自动回滚:{reason or "恢复到上个稳定节点"}', + block_text='无', + flow_remark=f'↩️ 手动回滚:{old_state} → {snap_state},原因:{reason or "人工触发"}', + flow_from='皇上', + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 回滚被拒绝: {commit.get("blockedBy")}'} + sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['stallSince'] = None sched['lastProgressAt'] = now_iso() - _scheduler_add_flow(task, f'执行回滚:{old_state} → {snap_state},原因:{reason or "停滞恢复"}') + _scheduler_add_flow(task, f'执行回滚:{old_state} → {snap_state},原因:{reason or "停滞恢复"}', reason_code='manual_rollback') task['updatedAt'] = now_iso() save_tasks(tasks) @@ -1065,13 +2992,135 @@ def handle_scheduler_rollback(task_id, reason=''): return {'ok': True, 'message': f'{task_id} 已回滚到 {snap_state}'} +def decide_next_action(task, threshold_sec=180): + sched = _ensure_scheduler(task) + state = task.get('state', '') + task_threshold = int(sched.get('stallThresholdSec') or threshold_sec) + now_dt = datetime.datetime.now(datetime.timezone.utc) + last_progress = _parse_iso(sched.get('lastProgressAt') or task.get('updatedAt')) + if not last_progress: + last_progress = now_dt + stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) + state_since = _parse_iso(sched.get('stateSince') or task.get('updatedAt')) + state_age_sec = max(0, int((now_dt - state_since).total_seconds())) if state_since else 0 + state_age_limit = max(task_threshold, int(sched.get('maxStateAgeSec') or (task_threshold * 4))) + age_overdue = ( + sched.get('autoAdvance', True) + and state in _AUTO_ADVANCE_SAFE_STATES + and state_age_sec >= state_age_limit + ) + + if state in _TERMINAL_STATES or task.get('archived') or state == 'Blocked': + return {'action': 'noop', 'reasonCode': 'state_terminal_or_blocked'} + + writeback = sched.get('writeback') or {} + wb_status = writeback.get('status') + if wb_status == 'WritebackPending': + retry_count = int(writeback.get('retryCount') or 0) + max_retry = int(writeback.get('maxRetry') or 2) + if retry_count < max_retry: + allow = _action_allowed(task, 'writeback_retry') + if allow.get('ok'): + return { + 'action': 'writeback_retry', + 'reasonCode': 'writeback_retry_budget', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return { + 'action': 'await-decision', + 'reasonCode': 'writeback_pending_need_human', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if stalled_sec < task_threshold and not age_overdue: + return {'action': 'noop', 'reasonCode': 'below_threshold'} + + if age_overdue and stalled_sec < task_threshold: + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, _, _ = flow + return { + 'action': 'auto-advance', + 'reasonCode': 'state_age_overdue', + 'toState': next_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if state in _RISK_DECISION_STATES: + return { + 'action': 'await-decision', + 'reasonCode': 'risk_state_stalled', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + retry_count = int(sched.get('retryCount') or 0) + max_retry = max(0, int(sched.get('maxRetry') or 1)) + if retry_count < max_retry: + allow = _action_allowed(task, 'retry') + if allow.get('ok'): + return { + 'action': 'retry', + 'reasonCode': 'stall_retry_budget', + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return {'action': 'noop', 'reasonCode': f'blocked_{allow.get("blockedBy", "retry")}'} + + level = int(sched.get('escalationLevel') or 0) + if level < 2: + allow = _action_allowed(task, 'escalate') + if allow.get('ok'): + next_level = level + 1 + target = 'menxia' if next_level == 1 else 'shangshu' + target_label = '门下省' if next_level == 1 else '尚书省' + return { + 'action': 'escalate', + 'reasonCode': 'stall_need_escalation', + 'to': target, + 'toLabel': target_label, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + return {'action': 'noop', 'reasonCode': f'blocked_{allow.get("blockedBy", "escalate")}'} + + if sched.get('autoAdvance', True) and state in _AUTO_ADVANCE_SAFE_STATES: + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, _, _ = flow + return { + 'action': 'auto-advance', + 'reasonCode': 'stall_auto_advance', + 'toState': next_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + if sched.get('autoRollback', True): + snapshot = sched.get('snapshot') or {} + snap_state = snapshot.get('state') + if snap_state and snap_state != state: + return { + 'action': 'rollback', + 'reasonCode': 'stall_auto_rollback', + 'toState': snap_state, + 'stalledSec': stalled_sec, + 'stateAgeSec': state_age_sec, + } + + return {'action': 'wait_human', 'reasonCode': 'no_safe_auto_action'} + + def handle_scheduler_scan(threshold_sec=180): threshold_sec = max(30, int(threshold_sec or 180)) tasks = load_tasks() - now_dt = datetime.datetime.now(datetime.timezone.utc) - pending_retries = [] + pending_dispatches = [] pending_escalates = [] - pending_rollbacks = [] + pending_auto_advances = [] + pending_writeback_retries = [] actions = [] changed = False @@ -1084,69 +3133,220 @@ def handle_scheduler_scan(threshold_sec=180): continue sched = _ensure_scheduler(task) - task_threshold = int(sched.get('stallThresholdSec') or threshold_sec) - last_progress = _parse_iso(sched.get('lastProgressAt') or task.get('updatedAt')) - if not last_progress: - continue - stalled_sec = max(0, int((now_dt - last_progress).total_seconds())) - if stalled_sec < task_threshold: + decision = decide_next_action(task, threshold_sec) + action = decision.get('action', 'noop') + if action in ('noop', 'wait_human'): continue - - if not sched.get('stallSince'): - sched['stallSince'] = now_iso() + run_id = _new_run_id() + version = sched.get('stateVersion') + lease_result = _acquire_lease( + task, + stage=state, + role='scheduler', + owner_run_id=run_id, + ttl_sec=180, + force_takeover=(action in ('rollback',)), + ) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action=action, + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:{action}:lease_busy', + ) changed = True + continue - retry_count = int(sched.get('retryCount') or 0) - max_retry = max(0, int(sched.get('maxRetry') or 1)) - level = int(sched.get('escalationLevel') or 0) - - if retry_count < max_retry: - sched['retryCount'] = retry_count + 1 + if action == 'retry': + commit = commit_state_change( + task, + action='retry', + reason_code=decision.get('reasonCode', 'stall_retry_budget'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'🔁 自动重试:停滞{decision.get("stalledSec", 0)}秒', + ) + if not commit.get('committed'): + changed = True + continue + sched['retryCount'] = int(sched.get('retryCount') or 0) + 1 sched['lastRetryAt'] = now_iso() sched['lastDispatchTrigger'] = 'taizi-scan-retry' - _scheduler_add_flow(task, f'停滞{stalled_sec}秒,触发自动重试第{sched["retryCount"]}次') - pending_retries.append((task_id, state)) - actions.append({'taskId': task_id, 'action': 'retry', 'stalledSec': stalled_sec}) + pending_dispatches.append((task_id, state, run_id, 'taizi-scan-retry')) + actions.append({ + 'taskId': task_id, + 'action': 'retry', + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) changed = True continue - if level < 2: - next_level = level + 1 - target = 'menxia' if next_level == 1 else 'shangshu' - target_label = '门下省' if next_level == 1 else '尚书省' + if action == 'escalate': + next_level = min(int(sched.get('escalationLevel') or 0) + 1, 2) + target = decision.get('to') or ('menxia' if next_level == 1 else 'shangshu') + target_label = decision.get('toLabel') or ('门下省' if next_level == 1 else '尚书省') + commit = commit_state_change( + task, + action='escalate', + reason_code=decision.get('reasonCode', 'stall_need_escalation'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'⬆️ 自动升级:停滞{decision.get("stalledSec", 0)}秒,升级至{target_label}协调', + ) + if not commit.get('committed'): + changed = True + continue sched['escalationLevel'] = next_level sched['lastEscalatedAt'] = now_iso() - _scheduler_add_flow(task, f'停滞{stalled_sec}秒,升级至{target_label}协调', to=target_label) - pending_escalates.append((task_id, state, target, target_label, stalled_sec)) - actions.append({'taskId': task_id, 'action': 'escalate', 'to': target_label, 'stalledSec': stalled_sec}) + pending_escalates.append((task_id, state, target, target_label, decision.get('stalledSec', 0))) + actions.append({ + 'taskId': task_id, + 'action': 'escalate', + 'to': target_label, + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) changed = True continue - if sched.get('autoRollback', True): + if action == 'auto-advance': + flow = _STATE_FLOW.get(state) + if flow: + next_state, _, to_dept, _ = flow + _scheduler_snapshot(task, f'auto-advance-before-{state}') + commit = commit_state_change( + task, + action='advance', + reason_code=decision.get('reasonCode', 'auto_advance'), + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=( + f'⏩ 太子调度自动推进:{_STATE_LABELS.get(state, state)}' + f' → {_STATE_LABELS.get(next_state, next_state)}' + ), + block_text='无', + flow_remark=( + f'⏩ 自动推进:{_STATE_LABELS.get(state, state)} → ' + f'{_STATE_LABELS.get(next_state, next_state)}' + ), + flow_to=to_dept, + ) + if not commit.get('committed'): + changed = True + continue + _scheduler_mark_progress(task, f'自动推进 {state} -> {next_state}', reason_code='auto_advance') + pending_auto_advances.append((task_id, next_state)) + actions.append({ + 'taskId': task_id, + 'action': 'auto-advance', + 'fromState': state, + 'toState': next_state, + 'reasonCode': decision.get('reasonCode'), + 'stalledSec': decision.get('stalledSec'), + 'stateAgeSec': decision.get('stateAgeSec'), + }) + changed = True + continue + + if action == 'writeback_retry': + commit = commit_state_change( + task, + action='writeback_retry', + reason_code=decision.get('reasonCode', 'writeback_retry_budget'), + owner_run_id=run_id, + expected_version=version, + flow_remark=f'🧩 自动提交重试:停滞{decision.get("stalledSec", 0)}秒', + ) + if not commit.get('committed'): + changed = True + continue + wb = sched.setdefault('writeback', {}) + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['status'] = 'WritebackPending' + wb['lastError'] = wb.get('lastError') or 'writeback_pending_retry' + _scheduler_add_flow( + task, + f'写回失败,触发提交重试第{wb["retryCount"]}次', + reason_code=decision.get('reasonCode', 'writeback_retry_budget'), + ) + pending_writeback_retries.append((task_id, run_id)) + actions.append({ + 'taskId': task_id, + 'action': 'writeback_retry', + 'stalledSec': decision.get('stalledSec'), + 'reasonCode': decision.get('reasonCode'), + }) + changed = True + continue + + if action == 'await-decision': + stalled_sec = int(decision.get('stalledSec') or 0) + state_age_sec = int(decision.get('stateAgeSec') or 0) + sched['decisionPacket'] = _build_decision_packet(task, state, stalled_sec, state_age_sec) + if not sched.get('awaitingEmperorDecision'): + sched['awaitingEmperorDecision'] = True + task['block'] = '风险节点停滞,等待皇上裁决' + task['now'] = f'⚠️ 风险节点{_STATE_LABELS.get(state, state)}停滞,等待皇上裁决' + _scheduler_add_flow( + task, + f'风险节点停滞{stalled_sec}秒,暂停自动推进并等待皇上裁决', + reason_code=decision.get('reasonCode', 'await_decision') + ) + actions.append({ + 'taskId': task_id, + 'action': 'await-decision', + 'state': state, + 'stalledSec': stalled_sec, + 'question': sched['decisionPacket'].get('question'), + 'reasonCode': decision.get('reasonCode'), + }) + task['updatedAt'] = now_iso() + changed = True + continue + + if action == 'rollback' and sched.get('autoRollback', True): snapshot = sched.get('snapshot') or {} snap_state = snapshot.get('state') if snap_state and snap_state != state: old_state = state - task['state'] = snap_state - task['org'] = snapshot.get('org', task.get('org', '')) - task['now'] = '↩️ 太子调度自动回滚到稳定节点' - task['block'] = '无' + commit = commit_state_change( + task, + action='rollback', + reason_code=decision.get('reasonCode', 'auto_rollback'), + owner_run_id=run_id, + expected_version=version, + to_state=snap_state, + to_org=snapshot.get('org', task.get('org', '')), + now_text='↩️ 太子调度自动回滚到稳定节点', + block_text='无', + flow_remark=f'↩️ 连续停滞,自动回滚:{old_state} → {snap_state}', + ) + if not commit.get('committed'): + changed = True + continue sched['retryCount'] = 0 sched['escalationLevel'] = 0 sched['stallSince'] = None sched['lastProgressAt'] = now_iso() - _scheduler_add_flow(task, f'连续停滞,自动回滚:{old_state} → {snap_state}') - pending_rollbacks.append((task_id, snap_state)) + pending_dispatches.append((task_id, snap_state, run_id, 'taizi-auto-rollback')) actions.append({'taskId': task_id, 'action': 'rollback', 'toState': snap_state}) changed = True if changed: save_tasks(tasks) - for task_id, state in pending_retries: + for task_id, state, owner_run_id, trigger in pending_dispatches: retry_task = next((t for t in tasks if t.get('id') == task_id), None) if retry_task: - dispatch_for_state(task_id, retry_task, state, trigger='taizi-scan-retry') + dispatch_for_state(task_id, retry_task, state, trigger=trigger, owner_run_id=owner_run_id) + + for task_id, owner_run_id in pending_writeback_retries: + _retry_writeback_for_task(task_id, owner_run_id=owner_run_id) for task_id, state, target, target_label, stalled_sec in pending_escalates: msg = ( @@ -1159,10 +3359,10 @@ def handle_scheduler_scan(threshold_sec=180): ) wake_agent(target, msg) - for task_id, state in pending_rollbacks: - rollback_task = next((t for t in tasks if t.get('id') == task_id), None) - if rollback_task and state not in _TERMINAL_STATES: - dispatch_for_state(task_id, rollback_task, state, trigger='taizi-auto-rollback') + for task_id, state in pending_auto_advances: + adv_task = next((t for t in tasks if t.get('id') == task_id), None) + if adv_task and state not in _TERMINAL_STATES: + dispatch_for_state(task_id, adv_task, state, trigger='taizi-auto-advance') return { 'ok': True, @@ -1676,6 +3876,18 @@ def get_task_activity(task_id): 'from': fl.get('from', ''), 'to': fl.get('to', ''), 'remark': fl.get('remark', ''), + 'reasonCode': fl.get('reasonCode', ''), + }) + + diagnostic_log = task.get('diagnostic_log', []) + for dg in diagnostic_log: + activity.append({ + 'at': dg.get('at', ''), + 'kind': 'diagnostic', + 'action': dg.get('action', ''), + 'reasonCode': dg.get('reasonCode', ''), + 'text': dg.get('details', ''), + 'eventType': dg.get('eventType', ''), }) progress_log = task.get('progress_log', []) @@ -1881,10 +4093,289 @@ def get_task_activity(task_id): 'Pending': '待处理', 'Taizi': '太子', 'Zhongshu': '中书省', 'Menxia': '门下省', 'Assigned': '尚书省', 'Next': '待执行', 'Doing': '执行中', 'Review': '审查', 'Done': '完成', } +_AUTO_ADVANCE_SAFE_STATES = {'Taizi', 'Zhongshu', 'Assigned', 'Next'} +_RISK_DECISION_STATES = {'Menxia', 'Review'} + + +def _derive_org_for_state(task, state, current_org=''): + """根据目标状态推导 org,避免 state/org 不一致导致后续派发失败。""" + fixed = { + 'Taizi': '太子', + 'Zhongshu': '中书省', + 'Menxia': '门下省', + 'Assigned': '尚书省', + 'Review': '尚书省', + } + if state in fixed: + return fixed[state] + + if state in ('Doing', 'Next'): + target_dept = (task.get('targetDept') or '').strip() + if target_dept in _ORG_AGENT_MAP: + return target_dept + if current_org in _ORG_AGENT_MAP and current_org not in ('中书省', '门下省', '尚书省', '太子'): + return current_org + return current_org or target_dept or '尚书省' + + return current_org or task.get('org', '') + + +def _extract_kanban_commands_from_text(text): + """从 agent 文本中提取 kanban_update.py 命令参数(仅白名单子命令)。""" + if not text: + return [] + allowed = {'progress', 'flow', 'state', 'todo', 'done', 'block'} + out = [] + for raw in text.splitlines(): + line = raw.strip() + if not line or 'kanban_update.py' not in line: + continue + line = line.strip('`').lstrip('-').strip() + if not line.startswith('python3'): + continue + try: + parts = shlex.split(line) + except Exception: + continue + script_idx = -1 + for i, tok in enumerate(parts): + if tok.endswith('kanban_update.py'): + script_idx = i + break + if script_idx < 0: + continue + args = parts[script_idx + 1:] + if not args: + continue + subcmd = args[0] + if subcmd not in allowed: + continue + out.append(args) + return out + + +def _bridge_apply_kanban_commands(task_id, text): + """在本机代执行 agent 输出中的 kanban_update.py 命令,解决 agent 沙箱无法写看板。""" + commands = _extract_kanban_commands_from_text(text) + if not commands: + return {'applied': 0, 'attempted': 0, 'errors': [], 'deptDispatches': []} + + script_path = str(SCRIPTS / 'kanban_update.py') + applied = 0 + errors = [] + attempted = 0 + dept_dispatches = [] + dept_seen = set() + + for args in commands[:6]: + subcmd = args[0] + # 所有允许子命令都要求第二个参数是 task_id,避免误执行到其它任务 + if len(args) < 2 or args[1] != task_id: + continue + if subcmd == 'flow' and len(args) >= 4: + from_dept = (args[2] or '').strip() + to_dept = (args[3] or '').strip() + remark = (args[4] or '').strip() if len(args) >= 5 else '' + if from_dept == '尚书省' and to_dept in _EXECUTION_DEPTS: + standby = ('待命' in remark) or ('排障' in remark and '派发' not in remark) + if not standby and to_dept not in dept_seen: + dept_seen.add(to_dept) + dept_dispatches.append(to_dept) + attempted += 1 + cmd = ['python3', script_path] + args + try: + result = subprocess.run(cmd, capture_output=True, text=True, timeout=20) + if result.returncode == 0: + applied += 1 + else: + stderr = (result.stderr or result.stdout or '').strip() + errors.append(f'{subcmd}: {stderr[:160]}') + except Exception as e: + errors.append(f'{subcmd}: {str(e)[:160]}') + + return { + 'applied': applied, + 'attempted': attempted, + 'errors': errors, + 'deptDispatches': dept_dispatches, + } + + +def _pick_execution_dept(task, dept_dispatches): + target_dept = (task.get('targetDept') or '').strip() + if target_dept in _EXECUTION_DEPTS: + return target_dept + for dept in dept_dispatches or []: + if dept in _EXECUTION_DEPTS: + return dept + return '' + + +def _should_auto_handoff_to_zhongshu(text): + if not text: + return False + content = text.replace('\n', ' ') + patterns = [ + r'(转交|移交|转呈|交由|交付|送交).{0,6}中书省', + r'中书省.{0,8}(起草|拟定|撰写).{0,4}方案', + r'中书省.{0,6}确认', + ] + return any(re.search(p, content) for p in patterns) + + +def _should_auto_handoff_to_menxia(text): + if not text: + return False + content = text.replace('\n', ' ') + patterns = [ + r'(提交|送交|转交|移交|请交).{0,8}门下省', + r'门下省.{0,8}(审议|审核|复核|裁决)', + r'(请|待).{0,4}门下省.{0,4}(准奏|审议)', + ] + return any(re.search(p, content) for p in patterns) + + +def _auto_handoff_to_zhongshu(task_id, reason_text=''): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'reason': 'task_not_found'} + cur_state = task.get('state', '') + if cur_state != 'Taizi': + return {'ok': False, 'reason': f'state={cur_state}'} + + _ensure_scheduler(task) + _scheduler_snapshot(task, 'taizi-auto-handoff-before') + run_id = _new_run_id() + _acquire_lease(task, stage=cur_state, role='taizi-auto-handoff', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') + remark = reason_text or '太子已转交中书省起草执行方案' + commit = commit_state_change( + task, + action='advance', + reason_code='taizi_auto_handoff', + owner_run_id=run_id, + expected_version=version, + to_state='Zhongshu', + to_org='中书省', + now_text='中书省起草执行方案', + block_text='无', + flow_from='太子', + flow_to='中书省', + flow_remark=remark, + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'reason': f'commit_blocked:{commit.get("blockedBy")}'} + _scheduler_mark_progress(task, '太子自动转交中书省', reason_code='taizi_auto_handoff') + cds = sched.setdefault('cooldowns', {}) + cds.pop('noDispatchUntil', None) + cds.pop('noEscalateUntil', None) + _release_lease(task, run_id) + save_tasks(tasks) + dispatch_for_state(task_id, task, 'Zhongshu', trigger='taizi-auto-handoff') + return {'ok': True} + + +def _auto_handoff_to_menxia(task_id, reason_text=''): + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'reason': 'task_not_found'} + cur_state = task.get('state', '') + if cur_state != 'Zhongshu': + return {'ok': False, 'reason': f'state={cur_state}'} + + sched = _ensure_scheduler(task) + _scheduler_snapshot(task, 'zhongshu-auto-handoff-before') + run_id = _new_run_id() + _acquire_lease(task, stage=cur_state, role='zhongshu-auto-handoff', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') + remark = reason_text or '中书省已提交门下省审议' + commit = commit_state_change( + task, + action='advance', + reason_code='zhongshu_auto_handoff', + owner_run_id=run_id, + expected_version=version, + to_state='Menxia', + to_org='门下省', + now_text='门下省审议方案', + block_text='无', + flow_from='中书省', + flow_to='门下省', + flow_remark=remark, + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'reason': f'commit_blocked:{commit.get("blockedBy")}'} + _scheduler_mark_progress(task, '中书省自动提交门下省审议', reason_code='zhongshu_auto_handoff') + cds = sched.setdefault('cooldowns', {}) + cds.pop('noDispatchUntil', None) + cds.pop('noEscalateUntil', None) + _release_lease(task, run_id) + save_tasks(tasks) + dispatch_for_state(task_id, task, 'Menxia', trigger='zhongshu-auto-handoff') + return {'ok': True} + + +def _auto_handoff_to_execution(task_id, preferred_dept='', trigger='shangshu-auto-handoff'): + """尚书省派发后自动切换到六部执行态,并自动派发对应执行 Agent。""" + tasks = load_tasks() + task = next((t for t in tasks if t.get('id') == task_id), None) + if not task: + return {'ok': False, 'reason': 'task_not_found'} + + cur_state = task.get('state', '') + if cur_state not in ('Assigned', 'Next'): + return {'ok': False, 'reason': f'state={cur_state}'} + + dept = (preferred_dept or '').strip() + if dept not in _EXECUTION_DEPTS: + return {'ok': False, 'reason': f'dept={dept}'} + + sched = _ensure_scheduler(task) + _scheduler_snapshot(task, f'{trigger}-before-{cur_state}') + run_id = _new_run_id() + _acquire_lease(task, stage=cur_state, role='auto-handoff', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') + task['targetDept'] = dept + commit = commit_state_change( + task, + action='advance', + reason_code='shangshu_auto_handoff', + owner_run_id=run_id, + expected_version=version, + to_state='Doing', + to_org=dept, + now_text=f'尚书省已派发,{dept}执行中', + block_text='无', + flow_remark=f'尚书省派发完成,自动切换到{dept}执行', + flow_to=dept, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'reason': f'commit_blocked:{commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'自动切换执行:{cur_state} -> Doing ({dept})', reason_code='shangshu_auto_handoff') + cds = sched.setdefault('cooldowns', {}) + cds.pop('noDispatchUntil', None) + cds.pop('noEscalateUntil', None) + _release_lease(task, run_id) + save_tasks(tasks) + dispatch_for_state(task_id, task, 'Doing', trigger=trigger, owner_run_id=run_id) + return {'ok': True, 'dept': dept} -def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): + +def dispatch_for_state(task_id, task, new_state, trigger='state-transition', owner_run_id=''): """推进/审批后自动派发对应 Agent(后台异步,不阻塞响应)。""" + tasks = load_tasks() + persisted = next((t for t in tasks if t.get('id') == task_id), None) + if persisted: + task = persisted + agent_id = _STATE_AGENT_MAP.get(new_state) if agent_id is None and new_state in ('Doing', 'Next'): org = task.get('org', '') @@ -1893,18 +4384,86 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): log.info(f'ℹ️ {task_id} 新状态 {new_state} 无对应 Agent,跳过自动派发') return - _update_task_scheduler(task_id, lambda t, s: ( - s.update({ - 'lastDispatchAt': now_iso(), - 'lastDispatchStatus': 'queued', - 'lastDispatchAgent': agent_id, - 'lastDispatchTrigger': trigger, - }), - _scheduler_add_flow(t, f'已入队派发:{new_state} → {agent_id}({trigger})', to=_STATE_LABELS.get(new_state, new_state)) - )) + sched = _ensure_scheduler(task) + action_ok = _action_allowed(task, 'dispatch') + if not action_ok.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='dispatch_blocked', + details=str(action_ok), + dedupe_key=f'{task_id}:dispatch:blocked:{action_ok.get("blockedBy")}', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + run_id = owner_run_id or _new_run_id() + lease_result = _acquire_lease( + task, + stage=new_state, + role=agent_id, + owner_run_id=run_id, + ttl_sec=300, + force_takeover=False, + ) + if not lease_result.get('ok'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='lease_busy', + details=str(lease_result), + dedupe_key=f'{task_id}:dispatch:lease_busy', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + commit = commit_state_change( + task, + action='dispatch', + reason_code='dispatch_queued', + owner_run_id=run_id, + expected_version=sched.get('stateVersion'), + flow_remark=f'🚀 已入队派发:{new_state} → {agent_id}({trigger})', + flow_to=_STATE_LABELS.get(new_state, new_state), + ) + if not commit.get('committed'): + _append_diagnostic( + task, + event_type='control_blocked', + action='dispatch', + reason_code='commit_blocked', + details=str(commit), + dedupe_key=f'{task_id}:dispatch:commit:{commit.get("blockedBy")}', + ) + if persisted: + task['updatedAt'] = now_iso() + save_tasks(tasks) + return + + sched.update({ + 'lastDispatchAt': now_iso(), + 'lastDispatchStatus': 'queued', + 'lastDispatchAgent': agent_id, + 'lastDispatchTrigger': trigger, + 'dispatchRunId': run_id, + 'dispatchAttempts': int(sched.get('dispatchAttempts') or 0) + 1, + }) + _set_cooldown(task, 'noEscalateUntil', _COOLDOWN_SECONDS['post_dispatch_escalate']) + _set_cooldown(task, 'noDispatchUntil', _COOLDOWN_SECONDS['post_dispatch_dispatch']) + task['updatedAt'] = now_iso() + if persisted: + save_tasks(tasks) title = task.get('title', '(无标题)') target_dept = task.get('targetDept', '') + kanban_cmd = 'python3 "scripts/kanban_update.py"' + kanban_cmd_fallback = f'python3 "{OCLAW_HOME / "workspace-main" / "scripts" / "kanban_update.py"}"' # 根据 agent_id 构造针对性消息 _msgs = { @@ -1912,21 +4471,21 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): f'📜 皇上旨意需要你处理\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。直接用 kanban_update.py 更新状态。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请立即转交中书省起草执行方案。' ), 'zhongshu': ( f'📜 旨意已到中书省,请起草方案\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务记录,请勿重复创建。直接用 kanban_update.py state 更新状态。\n' + f'⚠️ 看板已有此任务记录,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请立即起草执行方案,走完完整三省流程(中书起草→门下审议→尚书派发→六部执行)。' ), 'menxia': ( f'📋 中书省方案提交审议\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请审议中书省方案,给出准奏或封驳意见。' ), 'shangshu': ( @@ -1934,47 +4493,213 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'): f'任务ID: {task_id}\n' f'旨意: {title}\n' f'{"建议派发部门: " + target_dept if target_dept else ""}\n' - f'⚠️ 看板已有此任务,请勿重复创建。\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' f'请分析方案并派发给六部执行。' ), + 'gongbu': ( + f'🔧 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'xingbu': ( + f'⚖️ 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'libu': ( + f'📝 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'hubu': ( + f'💰 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'bingbu': ( + f'⚔️ 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), + 'libu_hr': ( + f'👔 六部执行任务\n' + f'任务ID: {task_id}\n' + f'旨意: {title}\n' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。\n' + f'请开始执行并持续回写 progress/todo,完成后回传尚书省。' + ), } msg = _msgs.get(agent_id, ( f'📌 请处理任务\n' f'任务ID: {task_id}\n' f'旨意: {title}\n' - f'⚠️ 看板已有此任务,请勿重复创建。直接用 kanban_update.py 更新状态。' + f'⚠️ 看板已有此任务,请勿重复创建。优先用:{kanban_cmd}(若失败再用:{kanban_cmd_fallback})。' )) def _do_dispatch(): try: if not _check_gateway_alive(): log.warning(f'⚠️ {task_id} 自动派发跳过: Gateway 未启动') - _update_task_scheduler(task_id, lambda t, s: s.update({ - 'lastDispatchAt': now_iso(), - 'lastDispatchStatus': 'gateway-offline', - 'lastDispatchAgent': agent_id, - 'lastDispatchTrigger': trigger, - })) + def _mark_gateway_offline(t, s): + s.update({ + 'lastDispatchAt': now_iso(), + 'lastDispatchStatus': 'gateway-offline', + 'lastDispatchAgent': agent_id, + 'lastDispatchTrigger': trigger, + }) + s['controlState'] = 'RetryableFailure' + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='gateway_offline', + details=f'task={task_id}, agent={agent_id}', + dedupe_key=f'{task_id}:dispatch:gateway_offline', + ) + _update_task_scheduler(task_id, _mark_gateway_offline) return - cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', msg, - '--deliver', '--channel', 'feishu', '--timeout', '300'] + # 默认走本地 direct 调用,避免硬编码 feishu 导致无渠道环境派发失败。 + # 如需强制渠道投递,可设置 EDICT_DISPATCH_CHANNEL=feishu|telegram|signal... + cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', msg, '--timeout', '300'] + dispatch_channel = (os.environ.get('EDICT_DISPATCH_CHANNEL') or '').strip() + if dispatch_channel: + cmd.extend(['--deliver', '--channel', dispatch_channel]) max_retries = 2 err = '' for attempt in range(1, max_retries + 1): log.info(f'🔄 自动派发 {task_id} → {agent_id} (第{attempt}次)...') result = subprocess.run(cmd, capture_output=True, text=True, timeout=310) + dispatch_text = ((result.stdout or '') + '\n' + (result.stderr or '')).strip() + if result.returncode == 0 and _looks_like_provider_tool_error(dispatch_text): + model_name = _get_agent_model(agent_id) + if _is_gemini_model(model_name): + try: + dispatch_text = _run_aihub_gemini_content(model_name, msg, timeout_sec=120) + log.info(f'🧩 {task_id} {agent_id} 命中 schema 不兼容,已改用 Gemini 协议兜底') + except Exception as e: + err = f'gemini-fallback-failed: {str(e)[:180]}' + log.warning(f'⚠️ {task_id} Gemini 协议兜底失败(第{attempt}次): {err}') + if attempt < max_retries: + import time + time.sleep(5) + continue + else: + err = _friendly_agent_error(dispatch_text) + log.warning(f'⚠️ {task_id} 派发响应异常(第{attempt}次): {err}') + if attempt < max_retries: + import time + time.sleep(5) + continue if result.returncode == 0: log.info(f'✅ {task_id} 自动派发成功 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + bridge = _bridge_apply_kanban_commands(task_id, dispatch_text) + if ( + agent_id == 'taizi' + and new_state == 'Taizi' + and not bridge.get('attempted') + and _should_auto_handoff_to_zhongshu(dispatch_text) + ): + auto = _auto_handoff_to_zhongshu(task_id, '太子指令:转交中书省起草执行方案') + if auto.get('ok'): + log.info(f'🚦 {task_id} 太子已转交中书省,自动推进到 Zhongshu') + if ( + agent_id == 'zhongshu' + and new_state == 'Zhongshu' + and not bridge.get('attempted') + and _should_auto_handoff_to_menxia(dispatch_text) + ): + auto = _auto_handoff_to_menxia(task_id, '中书省方案已提交门下省审议') + if auto.get('ok'): + log.info(f'🚦 {task_id} 中书省已提交门下省,自动推进到 Menxia') + handoff = {'ok': False} + handoff_dept = '' + if agent_id == 'shangshu' and new_state in ('Assigned', 'Next'): + handoff_dept = _pick_execution_dept(task, bridge.get('deptDispatches', [])) + if handoff_dept: + handoff = _auto_handoff_to_execution( + task_id, + preferred_dept=handoff_dept, + trigger='shangshu-auto-handoff' + ) + if handoff.get('ok'): + log.info(f'🚦 {task_id} 尚书省派发完成,自动切换到 {handoff_dept} 执行') + if bridge.get('applied', 0) > 0: + log.info( + f'🧩 {task_id} 桥接执行看板命令: ' + f'{bridge.get("applied", 0)}/{bridge.get("attempted", 0)}' + ) + elif bridge.get('attempted', 0) > 0 and bridge.get('errors'): + log.warning(f'⚠️ {task_id} 桥接执行失败: {" | ".join(bridge.get("errors", [])[:2])}') + def _mark_dispatch_success(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'success', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': '', - }), - _scheduler_add_flow(t, f'派发成功:{agent_id}({trigger})', to=t.get('org', '')) - )) + }) + wb = s.setdefault('writeback', {}) + wb.setdefault('retryCount', 0) + wb.setdefault('maxRetry', 2) + wb['lastDispatchOutput'] = dispatch_text[:12000] + if not wb.get('firstOutputAt'): + wb['firstOutputAt'] = now_iso() + if bridge.get('attempted', 0) <= 0: + if new_state in ('Doing', 'Assigned', 'Next'): + wb['status'] = 'ExecutionOutputReady' + wb['lastError'] = 'no_bridge_command_detected' + else: + wb['status'] = 'idle' + wb['lastError'] = '' + elif bridge.get('applied', 0) >= bridge.get('attempted', 0): + wb['status'] = 'idle' + wb['lastCommittedAt'] = now_iso() + wb['lastError'] = '' + wb['retryCount'] = 0 + else: + wb['status'] = 'WritebackPending' + wb['retryCount'] = int(wb.get('retryCount') or 0) + 1 + wb['lastError'] = '; '.join((bridge.get('errors') or [])[:2]) or 'writeback_failed' + _append_diagnostic( + t, + event_type='writeback_pending', + action='writeback_retry', + reason_code='writeback_failed', + details=wb['lastError'], + dedupe_key=f'{task_id}:writeback_pending', + ) + _sync_control_state(t) + _set_cooldown(t, 'noEscalateUntil', _COOLDOWN_SECONDS['post_dispatch_escalate']) + _set_cooldown(t, 'noDispatchUntil', _COOLDOWN_SECONDS['post_dispatch_dispatch']) + _release_lease(t, run_id) + _scheduler_add_flow( + t, + ( + f'派发成功:{agent_id}({trigger})' + + ( + f';桥接执行 {bridge.get("applied", 0)}/{bridge.get("attempted", 0)}' + if bridge.get('attempted', 0) > 0 else '' + ) + + ( + f';自动切换执行:{handoff_dept}' + if handoff.get('ok') and handoff_dept else '' + ) + ), + to=t.get('org', ''), + reason_code='dispatch_success', + ) + _update_task_scheduler(task_id, _mark_dispatch_success) return err = result.stderr[:200] if result.stderr else result.stdout[:200] log.warning(f'⚠️ {task_id} 自动派发失败(第{attempt}次): {err}') @@ -1982,40 +4707,70 @@ def _do_dispatch(): import time time.sleep(5) log.error(f'❌ {task_id} 自动派发最终失败 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_failed(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'failed', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': err, - }), - _scheduler_add_flow(t, f'派发失败:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_failed', + details=err, + dedupe_key=f'{task_id}:dispatch_failed', + ) + _scheduler_add_flow(t, f'派发失败:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_failed') + _update_task_scheduler(task_id, _mark_dispatch_failed) except subprocess.TimeoutExpired: log.error(f'❌ {task_id} 自动派发超时 → {agent_id}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_timeout(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'timeout', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': 'timeout', - }), - _scheduler_add_flow(t, f'派发超时:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_timeout', + details=f'task={task_id}, agent={agent_id}', + dedupe_key=f'{task_id}:dispatch_timeout', + ) + _scheduler_add_flow(t, f'派发超时:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_timeout') + _update_task_scheduler(task_id, _mark_dispatch_timeout) except Exception as e: log.warning(f'⚠️ {task_id} 自动派发异常: {e}') - _update_task_scheduler(task_id, lambda t, s: ( + def _mark_dispatch_error(t, s): s.update({ 'lastDispatchAt': now_iso(), 'lastDispatchStatus': 'error', 'lastDispatchAgent': agent_id, 'lastDispatchTrigger': trigger, 'lastDispatchError': str(e)[:200], - }), - _scheduler_add_flow(t, f'派发异常:{agent_id}({trigger})', to=t.get('org', '')) - )) + 'controlState': 'RetryableFailure', + }) + _release_lease(t, run_id) + _append_diagnostic( + t, + event_type='dispatch_failed', + action='dispatch', + reason_code='dispatch_error', + details=str(e)[:200], + dedupe_key=f'{task_id}:dispatch_error', + ) + _scheduler_add_flow(t, f'派发异常:{agent_id}({trigger})', to=t.get('org', ''), reason_code='dispatch_error') + _update_task_scheduler(task_id, _mark_dispatch_error) threading.Thread(target=_do_dispatch, daemon=True).start() log.info(f'🚀 {task_id} 推进后自动派发 → {agent_id}') @@ -2032,24 +4787,35 @@ def handle_advance_state(task_id, comment=''): return {'ok': False, 'error': f'任务 {task_id} 状态为 {cur},无法推进'} _ensure_scheduler(task) _scheduler_snapshot(task, f'advance-before-{cur}') + run_id = _new_run_id() + _acquire_lease(task, stage=cur, role='manual-advance', owner_run_id=run_id, ttl_sec=180, force_takeover=True) + version = task.get('_scheduler', {}).get('stateVersion') next_state, from_dept, to_dept, default_remark = _STATE_FLOW[cur] remark = comment or default_remark - task['state'] = next_state - task['now'] = f'⬇️ 手动推进:{remark}' - task.setdefault('flow_log', []).append({ - 'at': now_iso(), - 'from': from_dept, - 'to': to_dept, - 'remark': f'⬇️ 手动推进:{remark}' - }) - _scheduler_mark_progress(task, f'手动推进 {cur} -> {next_state}') - task['updatedAt'] = now_iso() + commit = commit_state_change( + task, + action='manual_decide', + reason_code='manual_advance', + owner_run_id=run_id, + expected_version=version, + to_state=next_state, + to_org=_derive_org_for_state(task, next_state, task.get('org', '')), + now_text=f'⬇️ 手动推进:{remark}', + flow_from=from_dept, + flow_to=to_dept, + flow_remark=f'⬇️ 手动推进:{remark}', + force=True, + ) + if not commit.get('committed'): + save_tasks(tasks) + return {'ok': False, 'error': f'任务 {task_id} 推进失败: {commit.get("blockedBy")}'} + _scheduler_mark_progress(task, f'手动推进 {cur} -> {next_state}', reason_code='manual_advance') save_tasks(tasks) # 🚀 推进后自动派发对应 Agent(Done 状态无需派发) if next_state != 'Done': - dispatch_for_state(task_id, task, next_state) + dispatch_for_state(task_id, task, next_state, owner_run_id=run_id) from_label = _STATE_LABELS.get(cur, cur) to_label = _STATE_LABELS.get(next_state, next_state) @@ -2179,6 +4945,14 @@ def do_GET(self): self.send_json({'ok': False, 'error': 'task_id required'}, 400) else: self.send_json(get_scheduler_state(task_id)) + elif p == '/api/scheduler-metrics': + self.send_json(get_scheduler_metrics()) + elif p.startswith('/api/scheduler-metrics/'): + task_id = p.replace('/api/scheduler-metrics/', '') + if not task_id: + self.send_json({'ok': False, 'error': 'task_id required'}, 400) + else: + self.send_json(get_scheduler_metrics(task_id)) elif p == '/api/agents-status': self.send_json(get_agents_status()) elif p.startswith('/api/agent-activity/'): @@ -2253,13 +5027,30 @@ def do_POST(self): self.send_json({'ok': False, 'error': f'repair flow order failed: {e}'}, 500) return + if p == '/api/scheduler-action': + task_id = body.get('taskId', '').strip() + action = body.get('action', '').strip() + reason = body.get('reason', '').strip() + expected_version = body.get('expectedVersion') + owner_run_id = body.get('ownerRunId', '').strip() + recovery_target = body.get('recoveryTarget', '').strip() + if not task_id or not action: + self.send_json({'ok': False, 'error': 'taskId/action required'}, 400) + return + self.send_json(handle_scheduler_action(task_id, action, reason, expected_version, owner_run_id, recovery_target)) + return + + if p == '/api/scheduler-commit': + self.send_json(handle_scheduler_commit(body)) + return + if p == '/api/scheduler-retry': task_id = body.get('taskId', '').strip() reason = body.get('reason', '').strip() if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_retry(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'retry', reason)) return if p == '/api/scheduler-escalate': @@ -2268,7 +5059,7 @@ def do_POST(self): if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_escalate(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'escalate', reason)) return if p == '/api/scheduler-rollback': @@ -2277,7 +5068,7 @@ def do_POST(self): if not task_id: self.send_json({'ok': False, 'error': 'taskId required'}, 400) return - self.send_json(handle_scheduler_rollback(task_id, reason)) + self.send_json(handle_scheduler_action(task_id, 'rollback', reason)) return if p == '/api/morning-brief/refresh': @@ -2402,6 +5193,30 @@ def do_refresh(): self.send_json(result) return + if p == '/api/court-discuss': + action = body.get('action', 'start') + topic = body.get('topic', '').strip() + participants = body.get('participants', []) + session_id = body.get('sessionId', '').strip() + force = bool(body.get('force', False)) + emperor_note = body.get('emperorNote', '').strip() + if action == 'start' and not topic: + self.send_json({'ok': False, 'error': 'topic required'}, 400) + return + if action in ('next', 'status', 'finalize', 'handoff', 'terminate') and not session_id: + self.send_json({'ok': False, 'error': 'sessionId required'}, 400) + return + result = handle_court_discuss( + action=action, + topic=topic, + participants=participants, + session_id=session_id, + force=force, + emperor_note=emperor_note, + ) + self.send_json(result) + return + if p == '/api/review-action': task_id = body.get('taskId', '').strip() action = body.get('action', '').strip() # approve, reject diff --git a/docs/task-dispatch-architecture.md b/docs/task-dispatch-architecture.md index e5981e97..ac03048a 100644 --- a/docs/task-dispatch-architecture.md +++ b/docs/task-dispatch-architecture.md @@ -26,6 +26,34 @@ --- +## 🔄 2026-03-13 稳定性改造补充(控制层) + +为解决重复派发、Doing 阶段误升级、执行产出与状态提交脱节等问题,调度层新增以下硬约束: + +1. **单写提交入口(commit gate)** + - 主状态、`_scheduler.controlState`、调度关键字段写入统一走 `commit_state_change()`。 + - 提交时校验 `owner_run_id + state_version + action whitelist`。 + - 版本冲突或 owner 过期仅记录诊断事件,不覆盖已有状态。 + +2. **任务阶段租约锁(lease lock)** + - 维度:`task_id + stage + executor_role`。 + - 字段:`ownerRunId/acquiredAt/heartbeatAt/ttlSec`。 + - 未持锁流程禁止派发与提交;TTL 超时可接管,防止死锁。 + +3. **状态动作白名单** + - `Doing/WritebackPending/WaitingDecision` 禁止自动改派与自动升级。 + - 自动调度在每个 decision tick 内仅允许一个动作生效。 + +4. **写回闭环分层** + - 执行输出与状态提交分离:`ExecutionOutputReady` / `WritebackPending`。 + - 仅 writeback 成功才算状态前进;提交失败进入提交重试链路。 + +5. **事件分层与可观测指标** + - 事件分为 `flow/progress/diagnostic`,并增加 `reason_code`。 + - 核心指标:调度放大比、无效控制比、写回脱节时长。 + +--- + ## 📚 第一部分:业务架构 ### 1.1 帝国制度:分权制衡的设计哲学 diff --git a/edict/frontend/src/api.ts b/edict/frontend/src/api.ts index 7e6c776d..44291bc2 100644 --- a/edict/frontend/src/api.ts +++ b/edict/frontend/src/api.ts @@ -39,6 +39,12 @@ export const api = { fetchJ(`${API_BASE}/api/task-activity/${encodeURIComponent(id)}`), schedulerState: (id: string) => fetchJ(`${API_BASE}/api/scheduler-state/${encodeURIComponent(id)}`), + schedulerMetrics: (id?: string) => + fetchJ( + id + ? `${API_BASE}/api/scheduler-metrics/${encodeURIComponent(id)}` + : `${API_BASE}/api/scheduler-metrics` + ), // 技能内容 skillContent: (agentId: string, skillName: string) => @@ -72,6 +78,20 @@ export const api = { postJ(`${API_BASE}/api/scheduler-escalate`, { taskId, reason }), schedulerRollback: (taskId: string, reason: string) => postJ(`${API_BASE}/api/scheduler-rollback`, { taskId, reason }), + schedulerAction: ( + taskId: string, + action: string, + reason: string, + expectedVersion?: number, + recoveryTarget?: 'continue_execution' | 'continue_writeback' | 'reassign' | 'terminate' + ) => + postJ(`${API_BASE}/api/scheduler-action`, { + taskId, + action, + reason, + expectedVersion, + recoveryTarget, + }), refreshMorning: () => postJ(`${API_BASE}/api/morning-brief/refresh`, {}), saveMorningConfig: (config: SubConfig) => @@ -93,6 +113,8 @@ export const api = { createTask: (data: CreateTaskPayload) => postJ(`${API_BASE}/api/create-task`, data), + courtDiscuss: (data: CourtDiscussPayload) => + postJ(`${API_BASE}/api/court-discuss`, data), }; // ── Types ── @@ -336,20 +358,107 @@ export interface TaskActivityData { export interface SchedulerInfo { retryCount?: number; escalationLevel?: number; + stateVersion?: number; lastDispatchStatus?: string; stallThresholdSec?: number; enabled?: boolean; lastProgressAt?: string; lastDispatchAt?: string; lastDispatchAgent?: string; + dispatchAttempts?: number; autoRollback?: boolean; + autoAdvance?: boolean; + stateSince?: string; + maxStateAgeSec?: number; + controlState?: string; + cooldowns?: Record; + lease?: LeaseInfo; + writeback?: WritebackInfo; + lastAction?: { action?: string; reasonCode?: string; at?: string }; + lastCommit?: { action?: string; reasonCode?: string; result?: string; blockedBy?: string; version?: number }; + awaitingEmperorDecision?: boolean; + decisionPacket?: DecisionPacket | null; +} + +export interface LeaseInfo { + stage?: string; + role?: string; + ownerRunId?: string; + acquiredAt?: string; + heartbeatAt?: string; + ttlSec?: number; +} + +export interface WritebackInfo { + status?: string; + retryCount?: number; + maxRetry?: number; + firstOutputAt?: string; + lastCommittedAt?: string; + lastError?: string; +} + +export interface DecisionOption { + id: string; + label: string; + impact: string; +} + +export interface DecisionPacket { + state: string; + question: string; + options: DecisionOption[]; + recommended?: string; + evidence?: string[]; + generatedAt?: string; } export interface SchedulerStateData { ok: boolean; error?: string; + taskId?: string; + state?: string; + org?: string; scheduler?: SchedulerInfo; + controlState?: string; + lease?: LeaseInfo; + lastAction?: { action?: string; reasonCode?: string; at?: string }; + writeback?: WritebackInfo; + decision?: DecisionPacket | null; stalledSec?: number; + stateAgeSec?: number; + stateAgeLimitSec?: number; +} + +export interface SchedulerTaskMetric { + taskId: string; + state: string; + dispatchAttempts: number; + uniqueExecutionSteps: number; + dispatchAmplificationRatio: number; + controlActions: number; + invalidControlActions: number; + invalidControlRatio: number; + writebackLagSec: number | null; + writebackStatus: string; +} + +export interface SchedulerMetricsData { + ok: boolean; + error?: string; + taskId?: string; + metrics?: SchedulerTaskMetric[]; + summary?: { + taskCount: number; + dispatchAttempts: number; + uniqueExecutionSteps: number; + dispatchAmplificationRatio: number; + controlActions: number; + invalidControlActions: number; + invalidControlRatio: number; + avgWritebackLagSec: number | null; + }; + checkedAt?: string; } export interface SkillContentResult { @@ -378,6 +487,77 @@ export interface CreateTaskPayload { params?: Record; } +export interface CourtDiscussPayload { + action: 'start' | 'next' | 'finalize' | 'status' | 'handoff' | 'terminate'; + topic?: string; + participants?: string[]; + sessionId?: string; + force?: boolean; + emperorNote?: string; +} + +export interface CourtDiscussEntry { + entryId?: string; + round: number; + turn?: number; + totalTurns?: number; + agentId: string; + agentLabel: string; + reply: string; + error?: boolean; + status?: 'speaking' | 'done' | 'error'; + at: string; +} + +export interface CourtDiscussFinal { + ready_for_edict: boolean; + clarified_goal: string; + risks: string[]; + questions_to_emperor: string[]; + recommended_edict: string; + recommended_target_dept: string; + recommended_priority: 'low' | 'normal' | 'high' | 'critical'; + raw?: string; +} + +export interface CourtDiscussAssessment { + round: number; + moderatorId: string; + moderatorLabel: string; + recommend_stop: boolean; + reason: string; + question_to_emperor: string; + focus_next_round: string[]; + draft_direction: string; + raw?: string; + at?: string; +} + +export interface CourtDiscussResult extends ActionResult { + sessionId?: string; + status?: 'ongoing' | 'done' | 'handoffed' | 'terminated'; + roundRunning?: boolean; + currentRound?: number; + speakingNow?: { + round?: number; + turn?: number; + totalTurns?: number; + agentId?: string; + agentLabel?: string; + }; + topic?: string; + participants?: string[]; + rounds?: number; + moderator?: { id: string; label: string }; + assessment?: CourtDiscussAssessment; + suggestedAction?: 'next' | 'finalize' | 'terminate'; + linkedTaskId?: string; + emperorNotes?: Array<{ at: string; text: string }>; + discussion?: CourtDiscussEntry[]; + partial?: CourtDiscussEntry[]; + final?: CourtDiscussFinal; +} + export interface RemoteSkillItem { skillName: string; agentId: string; diff --git a/edict/frontend/src/components/TaskModal.tsx b/edict/frontend/src/components/TaskModal.tsx index a5fdb848..0b3dda55 100644 --- a/edict/frontend/src/components/TaskModal.tsx +++ b/edict/frontend/src/components/TaskModal.tsx @@ -224,6 +224,8 @@ export default function TaskModal() { // Scheduler state const sched = schedData?.scheduler; const stalledSec = schedData?.stalledSec || 0; + const stateAgeSec = schedData?.stateAgeSec || 0; + const decision = schedData?.decision || sched?.decisionPacket || null; return (
@@ -296,16 +298,45 @@ export default function TaskModal() {
停滞时长
{fmtStalled(stalledSec)}
+
状态驻留
{fmtStalled(stateAgeSec)}
重试次数
{sched?.retryCount || 0}
升级级别
{!sched?.escalationLevel ? '无' : sched.escalationLevel === 1 ? '门下省' : '尚书省'}
派发状态
{sched?.lastDispatchStatus || 'idle'}
+
控制态
{schedData?.controlState || sched?.controlState || '-'}
+
写回状态
{schedData?.writeback?.status || sched?.writeback?.status || 'idle'}
+ {decision ? ( +
+
🧾 拍板事项
+
{decision.question}
+ {(decision.options || []).map((opt, idx) => ( +
+ {idx + 1}. {opt.label}{decision.recommended === opt.id ? '(推荐)' : ''} +
{opt.impact}
+
+ ))} + {(decision.evidence || []).length > 0 && ( +
+ {(decision.evidence || []).slice(0, 4).map((ev, i) => ( +
- {ev}
+ ))} +
+ )} +
+ ) : ( +
+ 当前无拍板事项(仅在门下省/审查节点停滞时触发) +
+ )} {sched && (
{sched.lastProgressAt && 最近进展 {(sched.lastProgressAt || '').replace('T', ' ').substring(0, 19)}} {sched.lastDispatchAt && 最近派发 {(sched.lastDispatchAt || '').replace('T', ' ').substring(0, 19)}} 自动回滚 {sched.autoRollback === false ? '关闭' : '开启'} {sched.lastDispatchAgent && 目标 {sched.lastDispatchAgent}} + {schedData?.lease?.ownerRunId && 租约 {schedData.lease.ownerRunId}} + {schedData?.lastAction?.reasonCode && 原因码 {schedData.lastAction.reasonCode}} + {schedData?.writeback?.lastError && 写回错误 {schedData.writeback.lastError}}
)}
diff --git a/edict/frontend/src/components/TemplatePanel.tsx b/edict/frontend/src/components/TemplatePanel.tsx index 92e32228..ad65491c 100644 --- a/edict/frontend/src/components/TemplatePanel.tsx +++ b/edict/frontend/src/components/TemplatePanel.tsx @@ -1,7 +1,17 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useStore, TEMPLATES, TPL_CATS } from '../store'; import type { Template } from '../store'; -import { api } from '../api'; +import { api, type CourtDiscussResult } from '../api'; + +const FREE_TARGET_DEPTS = ['中书省', '尚书省', '礼部', '户部', '兵部', '刑部', '工部', '吏部']; +const DISCUSS_AGENT_OPTIONS = [ + { id: 'taizi', label: '太子', emoji: '🤴' }, + { id: 'zhongshu', label: '中书省', emoji: '📜' }, + { id: 'menxia', label: '门下省', emoji: '🔍' }, + { id: 'shangshu', label: '尚书省', emoji: '📮' }, + { id: 'hubu', label: '户部', emoji: '💰' }, + { id: 'bingbu', label: '兵部', emoji: '⚔️' }, +]; export default function TemplatePanel() { const tplCatFilter = useStore((s) => s.tplCatFilter); @@ -12,6 +22,29 @@ export default function TemplatePanel() { const [formTpl, setFormTpl] = useState