diff --git a/main.js b/main.js index 106a36c..a28cb5f 100644 --- a/main.js +++ b/main.js @@ -1,10 +1,10 @@ -"use strict";var Zi=Object.create;var ws=Object.defineProperty;var er=Object.getOwnPropertyDescriptor;var tr=Object.getOwnPropertyNames;var sr=Object.getPrototypeOf,ar=Object.prototype.hasOwnProperty;var Ye=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),nr=(r,t)=>{for(var e in t)ws(r,e,{get:t[e],enumerable:!0})},Za=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of tr(t))!ar.call(r,a)&&a!==e&&ws(r,a,{get:()=>t[a],enumerable:!(s=er(t,a))||s.enumerable});return r};var Ke=(r,t,e)=>(e=r!=null?Zi(sr(r)):{},Za(t||!r||!r.__esModule?ws(e,"default",{value:r,enumerable:!0}):e,r)),ir=r=>Za(ws({},"__esModule",{value:!0}),r);var ft=Ye((wc,Fn)=>{"use strict";var Ln=["nodebuffer","arraybuffer","fragments"],Mn=typeof Blob<"u";Mn&&Ln.push("blob");Fn.exports={BINARY_TYPES:Ln,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:Mn,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var os=Ye((kc,Hs)=>{"use strict";var{EMPTY_BUFFER:Rr}=ft(),_a=Buffer[Symbol.species];function Ir(r,t){if(r.length===0)return Rr;if(r.length===1)return r[0];let e=Buffer.allocUnsafe(t),s=0;for(let a=0;a{"use strict";var Bn=Symbol("kDone"),Ea=Symbol("kRun"),Da=class{constructor(t){this[Bn]=()=>{this.pending--,this[Ea]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Ea]()}[Ea](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[Bn])}}};Un.exports=Da});var qt=Ye((Sc,Wn)=>{"use strict";var ls=require("zlib"),jn=os(),Mr=$n(),{kStatusCode:Hn}=ft(),Fr=Buffer[Symbol.species],Or=Buffer.from([0,0,255,255]),Ws=Symbol("permessage-deflate"),gt=Symbol("total-length"),jt=Symbol("callback"),kt=Symbol("buffers"),Ht=Symbol("error"),qs,Pa=class{constructor(t){if(this._options=t||{},this._threshold=this._options.threshold!==void 0?this._options.threshold:1024,this._maxPayload=this._options.maxPayload|0,this._isServer=!!this._options.isServer,this._deflate=null,this._inflate=null,this.params=null,!qs){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;qs=new Mr(e)}}static get extensionName(){return"permessage-deflate"}offer(){let t={};return this._options.serverNoContextTakeover&&(t.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(t.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(t.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?t.client_max_window_bits=this._options.clientMaxWindowBits:this._options.clientMaxWindowBits==null&&(t.client_max_window_bits=!0),t}accept(t){return t=this.normalizeParams(t),this.params=this._isServer?this.acceptAsServer(t):this.acceptAsClient(t),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){let t=this._deflate[jt];this._deflate.close(),this._deflate=null,t&&t(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(t){let e=this._options,s=t.find(a=>!(e.serverNoContextTakeover===!1&&a.server_no_context_takeover||a.server_max_window_bits&&(e.serverMaxWindowBits===!1||typeof e.serverMaxWindowBits=="number"&&e.serverMaxWindowBits>a.server_max_window_bits)||typeof e.clientMaxWindowBits=="number"&&!a.client_max_window_bits));if(!s)throw new Error("None of the extension offers can be accepted");return e.serverNoContextTakeover&&(s.server_no_context_takeover=!0),e.clientNoContextTakeover&&(s.client_no_context_takeover=!0),typeof e.serverMaxWindowBits=="number"&&(s.server_max_window_bits=e.serverMaxWindowBits),typeof e.clientMaxWindowBits=="number"?s.client_max_window_bits=e.clientMaxWindowBits:(s.client_max_window_bits===!0||e.clientMaxWindowBits===!1)&&delete s.client_max_window_bits,s}acceptAsClient(t){let e=t[0];if(this._options.clientNoContextTakeover===!1&&e.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(!e.client_max_window_bits)typeof this._options.clientMaxWindowBits=="number"&&(e.client_max_window_bits=this._options.clientMaxWindowBits);else if(this._options.clientMaxWindowBits===!1||typeof this._options.clientMaxWindowBits=="number"&&e.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"');return e}normalizeParams(t){return t.forEach(e=>{Object.keys(e).forEach(s=>{let a=e[s];if(a.length>1)throw new Error(`Parameter "${s}" must have only a single value`);if(a=a[0],s==="client_max_window_bits"){if(a!==!0){let n=+a;if(!Number.isInteger(n)||n<8||n>15)throw new TypeError(`Invalid value for parameter "${s}": ${a}`);a=n}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${s}": ${a}`)}else if(s==="server_max_window_bits"){let n=+a;if(!Number.isInteger(n)||n<8||n>15)throw new TypeError(`Invalid value for parameter "${s}": ${a}`);a=n}else if(s==="client_no_context_takeover"||s==="server_no_context_takeover"){if(a!==!0)throw new TypeError(`Invalid value for parameter "${s}": ${a}`)}else throw new Error(`Unknown parameter "${s}"`);e[s]=a})}),t}decompress(t,e,s){qs.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){qs.add(a=>{this._compress(t,e,(n,i)=>{a(),s(n,i)})})}_decompress(t,e,s){let a=this._isServer?"client":"server";if(!this._inflate){let n=`${a}_max_window_bits`,i=typeof this.params[n]!="number"?ls.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=ls.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[Ws]=this,this._inflate[gt]=0,this._inflate[kt]=[],this._inflate.on("error",Br),this._inflate.on("data",qn)}this._inflate[jt]=s,this._inflate.write(t),e&&this._inflate.write(Or),this._inflate.flush(()=>{let n=this._inflate[Ht];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=jn.concat(this._inflate[kt],this._inflate[gt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[gt]=0,this._inflate[kt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._inflate.reset()),s(null,i)})}_compress(t,e,s){let a=this._isServer?"server":"client";if(!this._deflate){let n=`${a}_max_window_bits`,i=typeof this.params[n]!="number"?ls.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=ls.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[gt]=0,this._deflate[kt]=[],this._deflate.on("data",Nr)}this._deflate[jt]=s,this._deflate.write(t),this._deflate.flush(ls.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=jn.concat(this._deflate[kt],this._deflate[gt]);e&&(n=new Fr(n.buffer,n.byteOffset,n.length-4)),this._deflate[jt]=null,this._deflate[gt]=0,this._deflate[kt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};Wn.exports=Pa;function Nr(r){this[kt].push(r),this[gt]+=r.length}function qn(r){if(this[gt]+=r.length,this[Ws]._maxPayload<1||this[gt]<=this[Ws]._maxPayload){this[kt].push(r);return}this[Ht]=new RangeError("Max payload size exceeded"),this[Ht].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[Ht][Hn]=1009,this.removeListener("data",qn),this.reset()}function Br(r){if(this[Ws]._inflate=null,this[Ht]){this[jt](this[Ht]);return}r[Hn]=1007,this[jt](r)}});var Wt=Ye((Cc,zs)=>{"use strict";var{isUtf8:zn}=require("buffer"),{hasBlob:Ur}=ft(),$r=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function jr(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Ra(r){let t=r.length,e=0;for(;e=t||(r[e+1]&192)!==128||(r[e+2]&192)!==128||r[e]===224&&(r[e+1]&224)===128||r[e]===237&&(r[e+1]&224)===160)return!1;e+=3}else if((r[e]&248)===240){if(e+3>=t||(r[e+1]&192)!==128||(r[e+2]&192)!==128||(r[e+3]&192)!==128||r[e]===240&&(r[e+1]&240)===128||r[e]===244&&r[e+1]>143||r[e]>244)return!1;e+=4}else return!1;return!0}function Hr(r){return Ur&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}zs.exports={isBlob:Hr,isValidStatusCode:jr,isValidUTF8:Ra,tokenChars:$r};if(zn)zs.exports.isValidUTF8=function(r){return r.length<24?Ra(r):zn(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");zs.exports.isValidUTF8=function(t){return t.length<32?Ra(t):r(t)}}catch{}});var Oa=Ye((Tc,Qn)=>{"use strict";var{Writable:qr}=require("stream"),Gn=qt(),{BINARY_TYPES:Wr,EMPTY_BUFFER:Vn,kStatusCode:zr,kWebSocket:Gr}=ft(),{concat:Ia,toArrayBuffer:Vr,unmask:Yr}=os(),{isValidStatusCode:Kr,isValidUTF8:Yn}=Wt(),Gs=Buffer[Symbol.species],at=0,Kn=1,Jn=2,Xn=3,La=4,Ma=5,Vs=6,Fa=class extends qr{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Wr[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Gr]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._errored=!1,this._loop=!1,this._state=at}_write(t,e,s){if(this._opcode===8&&this._state==at)return s();this._bufferedBytes+=t.length,this._buffers.push(t),this.startLoop(s)}consume(t){if(this._bufferedBytes-=t,t===this._buffers[0].length)return this._buffers.shift();if(t=s.length?e.set(this._buffers.shift(),a):(e.set(new Uint8Array(s.buffer,s.byteOffset,t),a),this._buffers[0]=new Gs(s.buffer,s.byteOffset+t,s.length-t)),t-=s.length}while(t>0);return e}startLoop(t){this._loop=!0;do switch(this._state){case at:this.getInfo(t);break;case Kn:this.getPayloadLength16(t);break;case Jn:this.getPayloadLength64(t);break;case Xn:this.getMask();break;case La:this.getData(t);break;case Ma:case Vs:this._loop=!1;return}while(this._loop);this._errored||t()}getInfo(t){if(this._bufferedBytes<2){this._loop=!1;return}let e=this.consume(2);if((e[0]&48)!==0){let a=this.createError(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");t(a);return}let s=(e[0]&64)===64;if(s&&!this._extensions[Gn.extensionName]){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(this._fin=(e[0]&128)===128,this._opcode=e[0]&15,this._payloadLength=e[1]&127,this._opcode===0){if(s){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(!this._fragmented){let a=this.createError(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}this._opcode=this._fragmented}else if(this._opcode===1||this._opcode===2){if(this._fragmented){let a=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}this._compressed=s}else if(this._opcode>7&&this._opcode<11){if(!this._fin){let a=this.createError(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");t(a);return}if(s){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(this._payloadLength>125||this._opcode===8&&this._payloadLength===1){let a=this.createError(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");t(a);return}}else{let a=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}if(!this._fin&&!this._fragmented&&(this._fragmented=this._opcode),this._masked=(e[1]&128)===128,this._isServer){if(!this._masked){let a=this.createError(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK");t(a);return}}else if(this._masked){let a=this.createError(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");t(a);return}this._payloadLength===126?this._state=Kn:this._payloadLength===127?this._state=Jn:this.haveLength(t)}getPayloadLength16(t){if(this._bufferedBytes<2){this._loop=!1;return}this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength(t)}getPayloadLength64(t){if(this._bufferedBytes<8){this._loop=!1;return}let e=this.consume(8),s=e.readUInt32BE(0);if(s>Math.pow(2,21)-1){let a=this.createError(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH");t(a);return}this._payloadLength=s*Math.pow(2,32)+e.readUInt32BE(4),this.haveLength(t)}haveLength(t){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0)){let e=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");t(e);return}this._masked?this._state=Xn:this._state=La}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=La}getData(t){let e=Vn;if(this._payloadLength){if(this._bufferedBytes7){this.controlMessage(e,t);return}if(this._compressed){this._state=Ma,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Gn.extensionName].decompress(t,this._fin,(a,n)=>{if(a)return e(a);if(n.length){if(this._messageLength+=n.length,this._messageLength>this._maxPayload&&this._maxPayload>0){let i=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(i);return}this._fragments.push(n)}this.dataMessage(e),this._state===at&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=at;return}let e=this._messageLength,s=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],this._opcode===2){let a;this._binaryType==="nodebuffer"?a=Ia(s,e):this._binaryType==="arraybuffer"?a=Vr(Ia(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit("message",a,!0),this._state=at,this.startLoop(t)}))}else{let a=Ia(s,e);if(!this._skipUTF8Validation&&!Yn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===Ma||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit("message",a,!1),this._state=at,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Vn),this.end();else{let s=t.readUInt16BE(0);if(!Kr(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new Gs(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!Yn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");e(n);return}this._loop=!1,this.emit("conclude",s,a),this.end()}this._state=at;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=at,this.startLoop(e)}))}createError(t,e,s,a,n){this._loop=!1,this._errored=!0;let i=new t(s?`Invalid WebSocket frame: ${e}`:e);return Error.captureStackTrace(i,this.createError),i.code=n,i[zr]=a,i}};Qn.exports=Fa});var Ua=Ye((Ac,ti)=>{"use strict";var{Duplex:_c}=require("stream"),{randomFillSync:Jr}=require("crypto"),Zn=qt(),{EMPTY_BUFFER:Xr,kWebSocket:Qr,NOOP:Zr}=ft(),{isBlob:zt,isValidStatusCode:eo}=Wt(),{mask:ei,toBuffer:Pt}=os(),nt=Symbol("kByteLength"),to=Buffer.alloc(4),Ys=8*1024,Rt,Gt=Ys,rt=0,so=1,ao=2,Na=class r{constructor(t,e,s){this._extensions=e||{},s&&(this._generateMask=s,this._maskBuffer=Buffer.alloc(4)),this._socket=t,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._queue=[],this._state=rt,this.onerror=Zr,this[Qr]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||to,e.generateMask?e.generateMask(s):(Gt===Ys&&(Rt===void 0&&(Rt=Buffer.alloc(Ys)),Jr(Rt,0,Ys),Gt=0),s[0]=Rt[Gt++],s[1]=Rt[Gt++],s[2]=Rt[Gt++],s[3]=Rt[Gt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[nt]!==void 0?o=e[nt]:(t=Buffer.from(t),o=t.length):(o=t.length,a=e.mask&&e.readOnly&&!i);let c=o;o>=65536?(n+=8,c=127):o>125&&(n+=2,c=126);let l=Buffer.allocUnsafe(a?o+n:n);return l[0]=e.fin?e.opcode|128:e.opcode,e.rsv1&&(l[0]|=64),l[1]=c,c===126?l.writeUInt16BE(o,2):c===127&&(l[2]=l[3]=0,l.writeUIntBE(o,4,6)),e.mask?(l[1]|=128,l[n-4]=s[0],l[n-3]=s[1],l[n-2]=s[2],l[n-1]=s[3],i?[l,t]:a?(ei(t,s,l,n,o),[l]):(ei(t,s,t,0,o),[l,t])):[l,t]}close(t,e,s,a){let n;if(t===void 0)n=Xr;else{if(typeof t!="number"||!eo(t))throw new TypeError("First argument must be a valid error code number");if(e===void 0||!e.length)n=Buffer.allocUnsafe(2),n.writeUInt16BE(t,0);else{let o=Buffer.byteLength(e);if(o>123)throw new RangeError("The message must not be greater than 123 bytes");n=Buffer.allocUnsafe(2+o),n.writeUInt16BE(t,0),typeof e=="string"?n.write(e,2):n.set(e,2)}}let i={[nt]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==rt?this.enqueue([this.dispatch,n,!1,i,a]):this.sendFrame(r.frame(n,i),a)}ping(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):zt(t)?(a=t.size,n=!1):(t=Pt(t),a=t.length,n=Pt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[nt]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};zt(t)?this._state!==rt?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==rt?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}pong(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):zt(t)?(a=t.size,n=!1):(t=Pt(t),a=t.length,n=Pt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[nt]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};zt(t)?this._state!==rt?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==rt?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}send(t,e,s){let a=this._extensions[Zn.extensionName],n=e.binary?2:1,i=e.compress,o,c;typeof t=="string"?(o=Buffer.byteLength(t),c=!1):zt(t)?(o=t.size,c=!1):(t=Pt(t),o=t.length,c=Pt.readOnly),this._firstFragment?(this._firstFragment=!1,i&&a&&a.params[a._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(i=o>=a._threshold),this._compress=i):(i=!1,n=0),e.fin&&(this._firstFragment=!0);let l={[nt]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:c,rsv1:i};zt(t)?this._state!==rt?this.enqueue([this.getBlobData,t,this._compress,l,s]):this.getBlobData(t,this._compress,l,s):this._state!==rt?this.enqueue([this.dispatch,t,this._compress,l,s]):this.dispatch(t,this._compress,l,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[nt],this._state=ao,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(Ba,this,o,a);return}this._bufferedBytes-=s[nt];let i=Pt(n);e?this.dispatch(i,e,s,a):(this._state=rt,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(no,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(r.frame(t,s),a);return}let n=this._extensions[Zn.extensionName];this._bufferedBytes+=s[nt],this._state=so,n.compress(t,s.fin,(i,o)=>{if(this._socket.destroyed){let c=new Error("The socket was closed while data was being compressed");Ba(this,c,a);return}this._bufferedBytes-=s[nt],this._state=rt,s.readOnly=!1,this.sendFrame(r.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===rt&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][nt],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][nt],this._queue.push(t)}sendFrame(t,e){t.length===2?(this._socket.cork(),this._socket.write(t[0]),this._socket.write(t[1],e),this._socket.uncork()):this._socket.write(t[0],e)}};ti.exports=Na;function Ba(r,t,e){typeof e=="function"&&e(t);for(let s=0;s{"use strict";var{kForOnEventAttribute:cs,kListener:$a}=ft(),si=Symbol("kCode"),ai=Symbol("kData"),ni=Symbol("kError"),ii=Symbol("kMessage"),ri=Symbol("kReason"),Vt=Symbol("kTarget"),oi=Symbol("kType"),li=Symbol("kWasClean"),yt=class{constructor(t){this[Vt]=null,this[oi]=t}get target(){return this[Vt]}get type(){return this[oi]}};Object.defineProperty(yt.prototype,"target",{enumerable:!0});Object.defineProperty(yt.prototype,"type",{enumerable:!0});var It=class extends yt{constructor(t,e={}){super(t),this[si]=e.code===void 0?0:e.code,this[ri]=e.reason===void 0?"":e.reason,this[li]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[si]}get reason(){return this[ri]}get wasClean(){return this[li]}};Object.defineProperty(It.prototype,"code",{enumerable:!0});Object.defineProperty(It.prototype,"reason",{enumerable:!0});Object.defineProperty(It.prototype,"wasClean",{enumerable:!0});var Yt=class extends yt{constructor(t,e={}){super(t),this[ni]=e.error===void 0?null:e.error,this[ii]=e.message===void 0?"":e.message}get error(){return this[ni]}get message(){return this[ii]}};Object.defineProperty(Yt.prototype,"error",{enumerable:!0});Object.defineProperty(Yt.prototype,"message",{enumerable:!0});var ds=class extends yt{constructor(t,e={}){super(t),this[ai]=e.data===void 0?null:e.data}get data(){return this[ai]}};Object.defineProperty(ds.prototype,"data",{enumerable:!0});var io={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[cs]&&a[$a]===t&&!a[cs])return;let s;if(r==="message")s=function(n,i){let o=new ds("message",{data:i?n:n.toString()});o[Vt]=this,Ks(t,this,o)};else if(r==="close")s=function(n,i){let o=new It("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Vt]=this,Ks(t,this,o)};else if(r==="error")s=function(n){let i=new Yt("error",{error:n,message:n.message});i[Vt]=this,Ks(t,this,i)};else if(r==="open")s=function(){let n=new yt("open");n[Vt]=this,Ks(t,this,n)};else return;s[cs]=!!e[cs],s[$a]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[$a]===t&&!e[cs]){this.removeListener(r,e);break}}};ci.exports={CloseEvent:It,ErrorEvent:Yt,Event:yt,EventTarget:io,MessageEvent:ds};function Ks(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var Js=Ye((Dc,hi)=>{"use strict";var{tokenChars:hs}=Wt();function ht(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function ro(r){let t=Object.create(null),e=Object.create(null),s=!1,a=!1,n=!1,i,o,c=-1,l=-1,d=-1,h=0;for(;h{let e=r[t];return Array.isArray(e)||(e=[e]),e.map(s=>[t].concat(Object.keys(s).map(a=>{let n=s[a];return Array.isArray(n)||(n=[n]),n.map(i=>i===!0?a:`${a}=${i}`).join("; ")})).join("; ")).join(", ")}).join(", ")}hi.exports={format:oo,parse:ro}});var ea=Ye((Ic,Si)=>{"use strict";var lo=require("events"),co=require("https"),ho=require("http"),mi=require("net"),uo=require("tls"),{randomBytes:po,createHash:mo}=require("crypto"),{Duplex:Pc,Readable:Rc}=require("stream"),{URL:ja}=require("url"),xt=qt(),fo=Oa(),go=Ua(),{isBlob:yo}=Wt(),{BINARY_TYPES:ui,CLOSE_TIMEOUT:vo,EMPTY_BUFFER:Xs,GUID:bo,kForOnEventAttribute:Ha,kListener:wo,kStatusCode:ko,kWebSocket:Ie,NOOP:fi}=ft(),{EventTarget:{addEventListener:xo,removeEventListener:So}}=di(),{format:Co,parse:To}=Js(),{toBuffer:_o}=os(),gi=Symbol("kAborted"),qa=[8,13],vt=["CONNECTING","OPEN","CLOSING","CLOSED"],Ao=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,be=class r extends lo{constructor(t,e,s){super(),this._binaryType=ui[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Xs,this._closeTimer=null,this._errorEmitted=!1,this._extensions={},this._paused=!1,this._protocol="",this._readyState=r.CONNECTING,this._receiver=null,this._sender=null,this._socket=null,t!==null?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,e===void 0?e=[]:Array.isArray(e)||(typeof e=="object"&&e!==null?(s=e,e=[]):e=[e]),yi(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){ui.includes(t)&&(this._binaryType=t,this._receiver&&(this._receiver._binaryType=t))}get bufferedAmount(){return this._socket?this._socket._writableState.length+this._sender._bufferedBytes:this._bufferedAmount}get extensions(){return Object.keys(this._extensions).join()}get isPaused(){return this._paused}get onclose(){return null}get onerror(){return null}get onopen(){return null}get onmessage(){return null}get protocol(){return this._protocol}get readyState(){return this._readyState}get url(){return this._url}setSocket(t,e,s){let a=new fo({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new go(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[Ie]=this,n[Ie]=this,t[Ie]=this,a.on("conclude",Po),a.on("drain",Ro),a.on("error",Io),a.on("message",Lo),a.on("ping",Mo),a.on("pong",Fo),n.onerror=Oo,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",wi),t.on("data",Zs),t.on("end",ki),t.on("error",xi),this._readyState=r.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[xt.extensionName]&&this._extensions[xt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(t,e){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Qe(this,this._req,"WebSocket was closed before the connection was established");return}if(this.readyState===r.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=r.CLOSING,this._sender.close(t,e,!this._isServer,s=>{s||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),bi(this)}}pause(){this.readyState===r.CONNECTING||this.readyState===r.CLOSED||(this._paused=!0,this._socket.pause())}ping(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){Wa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Xs,e,s)}pong(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){Wa(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Xs,e,s)}resume(){this.readyState===r.CONNECTING||this.readyState===r.CLOSED||(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof e=="function"&&(s=e,e={}),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){Wa(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[xt.extensionName]||(a.compress=!1),this._sender.send(t||Xs,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Qe(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(be,"CONNECTING",{enumerable:!0,value:vt.indexOf("CONNECTING")});Object.defineProperty(be.prototype,"CONNECTING",{enumerable:!0,value:vt.indexOf("CONNECTING")});Object.defineProperty(be,"OPEN",{enumerable:!0,value:vt.indexOf("OPEN")});Object.defineProperty(be.prototype,"OPEN",{enumerable:!0,value:vt.indexOf("OPEN")});Object.defineProperty(be,"CLOSING",{enumerable:!0,value:vt.indexOf("CLOSING")});Object.defineProperty(be.prototype,"CLOSING",{enumerable:!0,value:vt.indexOf("CLOSING")});Object.defineProperty(be,"CLOSED",{enumerable:!0,value:vt.indexOf("CLOSED")});Object.defineProperty(be.prototype,"CLOSED",{enumerable:!0,value:vt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(be.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(be.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[Ha])return t[wo];return null},set(t){for(let e of this.listeners(r))if(e[Ha]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[Ha]:!0})}})});be.prototype.addEventListener=xo;be.prototype.removeEventListener=So;Si.exports=be;function yi(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:vo,protocolVersion:qa[1],maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...s,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:"GET",host:void 0,path:void 0,port:void 0};if(r._autoPong=a.autoPong,r._closeTimeout=a.closeTimeout,!qa.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${qa.join(", ")})`);let n;if(t instanceof ja)n=t;else try{n=new ja(t)}catch{throw new SyntaxError(`Invalid URL: ${t}`)}n.protocol==="http:"?n.protocol="ws:":n.protocol==="https:"&&(n.protocol="wss:"),r._url=n.href;let i=n.protocol==="wss:",o=n.protocol==="ws+unix:",c;if(n.protocol!=="ws:"&&!i&&!o?c=`The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`:o&&!n.pathname?c="The URL's pathname is empty":n.hash&&(c="The URL contains a fragment identifier"),c){let m=new SyntaxError(c);if(r._redirects===0)throw m;Qs(r,m);return}let l=i?443:80,d=po(16).toString("base64"),h=i?co.request:ho.request,u=new Set,p;if(a.createConnection=a.createConnection||(i?Do:Eo),a.defaultPort=a.defaultPort||l,a.port=n.port||l,a.host=n.hostname.startsWith("[")?n.hostname.slice(1,-1):n.hostname,a.headers={...a.headers,"Sec-WebSocket-Version":a.protocolVersion,"Sec-WebSocket-Key":d,Connection:"Upgrade",Upgrade:"websocket"},a.path=n.pathname+n.search,a.timeout=a.handshakeTimeout,a.perMessageDeflate&&(p=new xt({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=Co({[xt.extensionName]:p.offer()})),e.length){for(let m of e){if(typeof m!="string"||!Ao.test(m)||u.has(m))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(m)}a.headers["Sec-WebSocket-Protocol"]=e.join(",")}if(a.origin&&(a.protocolVersion<13?a.headers["Sec-WebSocket-Origin"]=a.origin:a.headers.Origin=a.origin),(n.username||n.password)&&(a.auth=`${n.username}:${n.password}`),o){let m=a.path.split(":");a.socketPath=m[0],a.path=m[1]}let f;if(a.followRedirects){if(r._redirects===0){r._originalIpc=o,r._originalSecure=i,r._originalHostOrSocketPath=o?a.socketPath:n.host;let m=s&&s.headers;if(s={...s,headers:{}},m)for(let[y,k]of Object.entries(m))s.headers[y.toLowerCase()]=k}else if(r.listenerCount("redirect")===0){let m=o?r._originalIpc?a.socketPath===r._originalHostOrSocketPath:!1:r._originalIpc?!1:n.host===r._originalHostOrSocketPath;(!m||r._originalSecure&&!i)&&(delete a.headers.authorization,delete a.headers.cookie,m||delete a.headers.host,a.auth=void 0)}a.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(a.auth).toString("base64")),f=r._req=h(a),r._redirects&&r.emit("redirect",r.url,f)}else f=r._req=h(a);a.timeout&&f.on("timeout",()=>{Qe(r,f,"Opening handshake has timed out")}),f.on("error",m=>{f===null||f[gi]||(f=r._req=null,Qs(r,m))}),f.on("response",m=>{let y=m.headers.location,k=m.statusCode;if(y&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){Qe(r,f,"Maximum redirects exceeded");return}f.abort();let b;try{b=new ja(y,t)}catch{let g=new SyntaxError(`Invalid URL: ${y}`);Qs(r,g);return}yi(r,b,e,s)}else r.emit("unexpected-response",f,m)||Qe(r,f,`Unexpected server response: ${m.statusCode}`)}),f.on("upgrade",(m,y,k)=>{if(r.emit("upgrade",m),r.readyState!==be.CONNECTING)return;f=r._req=null;let b=m.headers.upgrade;if(b===void 0||b.toLowerCase()!=="websocket"){Qe(r,y,"Invalid Upgrade header");return}let v=mo("sha1").update(d+bo).digest("base64");if(m.headers["sec-websocket-accept"]!==v){Qe(r,y,"Invalid Sec-WebSocket-Accept header");return}let g=m.headers["sec-websocket-protocol"],x;if(g!==void 0?u.size?u.has(g)||(x="Server sent an invalid subprotocol"):x="Server sent a subprotocol but none was requested":u.size&&(x="Server sent no subprotocol"),x){Qe(r,y,x);return}g&&(r._protocol=g);let T=m.headers["sec-websocket-extensions"];if(T!==void 0){if(!p){Qe(r,y,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let _;try{_=To(T)}catch{Qe(r,y,"Invalid Sec-WebSocket-Extensions header");return}let R=Object.keys(_);if(R.length!==1||R[0]!==xt.extensionName){Qe(r,y,"Server indicated an extension that was not requested");return}try{p.accept(_[xt.extensionName])}catch{Qe(r,y,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[xt.extensionName]=p}r.setSocket(y,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(f,r):f.end()}function Qs(r,t){r._readyState=be.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function Eo(r){return r.path=r.socketPath,mi.connect(r)}function Do(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=mi.isIP(r.host)?"":r.host),uo.connect(r)}function Qe(r,t,e){r._readyState=be.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Qe),t.setHeader?(t[gi]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Qs,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function Wa(r,t,e){if(t){let s=yo(t)?t.size:_o(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${vt[r.readyState]})`);process.nextTick(e,s)}}function Po(r,t){let e=this[Ie];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[Ie]!==void 0&&(e._socket.removeListener("data",Zs),process.nextTick(vi,e._socket),r===1005?e.close():e.close(r,t))}function Ro(){let r=this[Ie];r.isPaused||r._socket.resume()}function Io(r){let t=this[Ie];t._socket[Ie]!==void 0&&(t._socket.removeListener("data",Zs),process.nextTick(vi,t._socket),t.close(r[ko])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function pi(){this[Ie].emitClose()}function Lo(r,t){this[Ie].emit("message",r,t)}function Mo(r){let t=this[Ie];t._autoPong&&t.pong(r,!this._isServer,fi),t.emit("ping",r)}function Fo(r){this[Ie].emit("pong",r)}function vi(r){r.resume()}function Oo(r){let t=this[Ie];t.readyState!==be.CLOSED&&(t.readyState===be.OPEN&&(t._readyState=be.CLOSING,bi(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function bi(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function wi(){let r=this[Ie];if(this.removeListener("close",wi),this.removeListener("data",Zs),this.removeListener("end",ki),r._readyState=be.CLOSING,!this._readableState.endEmitted&&!r._closeFrameReceived&&!r._receiver._writableState.errorEmitted&&this._readableState.length!==0){let t=this.read(this._readableState.length);r._receiver.write(t)}r._receiver.end(),this[Ie]=void 0,clearTimeout(r._closeTimer),r._receiver._writableState.finished||r._receiver._writableState.errorEmitted?r.emitClose():(r._receiver.on("error",pi),r._receiver.on("finish",pi))}function Zs(r){this[Ie]._receiver.write(r)||this.pause()}function ki(){let r=this[Ie];r._readyState=be.CLOSING,r._receiver.end(),this.end()}function xi(){let r=this[Ie];this.removeListener("error",xi),this.on("error",fi),r&&(r._readyState=be.CLOSING,this.destroy())}});var Ai=Ye((Mc,_i)=>{"use strict";var Lc=ea(),{Duplex:No}=require("stream");function Ci(r){r.emit("close")}function Bo(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Ti(r){this.removeListener("error",Ti),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function Uo(r,t){let e=!0,s=new No({...t,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return r.on("message",function(n,i){let o=!i&&s._readableState.objectMode?n.toString():n;s.push(o)||r.pause()}),r.once("error",function(n){s.destroyed||(e=!1,s.destroy(n))}),r.once("close",function(){s.destroyed||s.push(null)}),s._destroy=function(a,n){if(r.readyState===r.CLOSED){n(a),process.nextTick(Ci,s);return}let i=!1;r.once("error",function(c){i=!0,n(c)}),r.once("close",function(){i||n(a),process.nextTick(Ci,s)}),e&&r.terminate()},s._final=function(a){if(r.readyState===r.CONNECTING){r.once("open",function(){s._final(a)});return}r._socket!==null&&(r._socket._writableState.finished?(a(),s._readableState.endEmitted&&s.destroy()):(r._socket.once("finish",function(){a()}),r.close()))},s._read=function(){r.isPaused&&r.resume()},s._write=function(a,n,i){if(r.readyState===r.CONNECTING){r.once("open",function(){s._write(a,n,i)});return}r.send(a,i)},s.on("end",Bo),s.on("error",Ti),s}_i.exports=Uo});var za=Ye((Fc,Ei)=>{"use strict";var{tokenChars:$o}=Wt();function jo(r){let t=new Set,e=-1,s=-1,a=0;for(a;a{"use strict";var Ho=require("events"),ta=require("http"),{Duplex:Oc}=require("stream"),{createHash:qo}=require("crypto"),Di=Js(),Lt=qt(),Wo=za(),zo=ea(),{CLOSE_TIMEOUT:Go,GUID:Vo,kWebSocket:Yo}=ft(),Ko=/^[+/0-9A-Za-z]{22}==$/,Pi=0,Ri=1,Li=2,Ga=class extends Ho{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Go,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:zo,...t},t.port==null&&!t.server&&!t.noServer||t.port!=null&&(t.server||t.noServer)||t.server&&t.noServer)throw new TypeError('One and only one of the "port", "server", or "noServer" options must be specified');if(t.port!=null?(this._server=ta.createServer((s,a)=>{let n=ta.STATUS_CODES[426];a.writeHead(426,{"Content-Length":n.length,"Content-Type":"text/plain"}),a.end(n)}),this._server.listen(t.port,t.host,t.backlog,e)):t.server&&(this._server=t.server),this._server){let s=this.emit.bind(this,"connection");this._removeListeners=Jo(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(a,n,i)=>{this.handleUpgrade(a,n,i,s)}})}t.perMessageDeflate===!0&&(t.perMessageDeflate={}),t.clientTracking&&(this.clients=new Set,this._shouldEmitClose=!1),this.options=t,this._state=Pi}address(){if(this.options.noServer)throw new Error('The server is operating in "noServer" mode');return this._server?this._server.address():null}close(t){if(this._state===Li){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(us,this);return}if(t&&this.once("close",t),this._state!==Ri)if(this._state=Ri,this.options.noServer||this.options.server)this._server&&(this._removeListeners(),this._removeListeners=this._server=null),this.clients?this.clients.size?this._shouldEmitClose=!0:process.nextTick(us,this):process.nextTick(us,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{us(this)})}}shouldHandle(t){if(this.options.path){let e=t.url.indexOf("?");if((e!==-1?t.url.slice(0,e):t.url)!==this.options.path)return!1}return!0}handleUpgrade(t,e,s,a){e.on("error",Ii);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){Mt(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){Mt(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Ko.test(n)){Mt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){Mt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){ps(e,400);return}let c=t.headers["sec-websocket-protocol"],l=new Set;if(c!==void 0)try{l=Wo.parse(c)}catch{Mt(this,t,e,400,"Invalid Sec-WebSocket-Protocol header");return}let d=t.headers["sec-websocket-extensions"],h={};if(this.options.perMessageDeflate&&d!==void 0){let u=new Lt({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=Di.parse(d);p[Lt.extensionName]&&(u.accept(p[Lt.extensionName]),h[Lt.extensionName]=u)}catch{Mt(this,t,e,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let u={origin:t.headers[`${o===8?"sec-websocket-origin":"origin"}`],secure:!!(t.socket.authorized||t.socket.encrypted),req:t};if(this.options.verifyClient.length===2){this.options.verifyClient(u,(p,f,m,y)=>{if(!p)return ps(e,f||401,m,y);this.completeUpgrade(h,n,l,t,e,s,a)});return}if(!this.options.verifyClient(u))return ps(e,401)}this.completeUpgrade(h,n,l,t,e,s,a)}completeUpgrade(t,e,s,a,n,i,o){if(!n.readable||!n.writable)return n.destroy();if(n[Yo])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>Pi)return ps(n,503);let l=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${qo("sha1").update(e+Vo).digest("base64")}`],d=new this.options.WebSocket(null,void 0,this.options);if(s.size){let h=this.options.handleProtocols?this.options.handleProtocols(s,a):s.values().next().value;h&&(l.push(`Sec-WebSocket-Protocol: ${h}`),d._protocol=h)}if(t[Lt.extensionName]){let h=t[Lt.extensionName].params,u=Di.format({[Lt.extensionName]:[h]});l.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",l,a),n.write(l.concat(`\r +"use strict";var Zi=Object.create;var ws=Object.defineProperty;var er=Object.getOwnPropertyDescriptor;var tr=Object.getOwnPropertyNames;var sr=Object.getPrototypeOf,ar=Object.prototype.hasOwnProperty;var Ye=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),nr=(r,t)=>{for(var e in t)ws(r,e,{get:t[e],enumerable:!0})},Za=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of tr(t))!ar.call(r,a)&&a!==e&&ws(r,a,{get:()=>t[a],enumerable:!(s=er(t,a))||s.enumerable});return r};var Ke=(r,t,e)=>(e=r!=null?Zi(sr(r)):{},Za(t||!r||!r.__esModule?ws(e,"default",{value:r,enumerable:!0}):e,r)),ir=r=>Za(ws({},"__esModule",{value:!0}),r);var mt=Ye((xc,Fn)=>{"use strict";var Ln=["nodebuffer","arraybuffer","fragments"],Mn=typeof Blob<"u";Mn&&Ln.push("blob");Fn.exports={BINARY_TYPES:Ln,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:Mn,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var os=Ye((Sc,Hs)=>{"use strict";var{EMPTY_BUFFER:Rr}=mt(),Aa=Buffer[Symbol.species];function Ir(r,t){if(r.length===0)return Rr;if(r.length===1)return r[0];let e=Buffer.allocUnsafe(t),s=0;for(let a=0;a{"use strict";var Bn=Symbol("kDone"),Pa=Symbol("kRun"),Da=class{constructor(t){this[Bn]=()=>{this.pending--,this[Pa]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Pa]()}[Pa](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[Bn])}}};Un.exports=Da});var qt=Ye((Tc,Wn)=>{"use strict";var ls=require("zlib"),jn=os(),Mr=$n(),{kStatusCode:Hn}=mt(),Fr=Buffer[Symbol.species],Or=Buffer.from([0,0,255,255]),Ws=Symbol("permessage-deflate"),gt=Symbol("total-length"),jt=Symbol("callback"),kt=Symbol("buffers"),Ht=Symbol("error"),qs,Ra=class{constructor(t){if(this._options=t||{},this._threshold=this._options.threshold!==void 0?this._options.threshold:1024,this._maxPayload=this._options.maxPayload|0,this._isServer=!!this._options.isServer,this._deflate=null,this._inflate=null,this.params=null,!qs){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;qs=new Mr(e)}}static get extensionName(){return"permessage-deflate"}offer(){let t={};return this._options.serverNoContextTakeover&&(t.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(t.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(t.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?t.client_max_window_bits=this._options.clientMaxWindowBits:this._options.clientMaxWindowBits==null&&(t.client_max_window_bits=!0),t}accept(t){return t=this.normalizeParams(t),this.params=this._isServer?this.acceptAsServer(t):this.acceptAsClient(t),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){let t=this._deflate[jt];this._deflate.close(),this._deflate=null,t&&t(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(t){let e=this._options,s=t.find(a=>!(e.serverNoContextTakeover===!1&&a.server_no_context_takeover||a.server_max_window_bits&&(e.serverMaxWindowBits===!1||typeof e.serverMaxWindowBits=="number"&&e.serverMaxWindowBits>a.server_max_window_bits)||typeof e.clientMaxWindowBits=="number"&&!a.client_max_window_bits));if(!s)throw new Error("None of the extension offers can be accepted");return e.serverNoContextTakeover&&(s.server_no_context_takeover=!0),e.clientNoContextTakeover&&(s.client_no_context_takeover=!0),typeof e.serverMaxWindowBits=="number"&&(s.server_max_window_bits=e.serverMaxWindowBits),typeof e.clientMaxWindowBits=="number"?s.client_max_window_bits=e.clientMaxWindowBits:(s.client_max_window_bits===!0||e.clientMaxWindowBits===!1)&&delete s.client_max_window_bits,s}acceptAsClient(t){let e=t[0];if(this._options.clientNoContextTakeover===!1&&e.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(!e.client_max_window_bits)typeof this._options.clientMaxWindowBits=="number"&&(e.client_max_window_bits=this._options.clientMaxWindowBits);else if(this._options.clientMaxWindowBits===!1||typeof this._options.clientMaxWindowBits=="number"&&e.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"');return e}normalizeParams(t){return t.forEach(e=>{Object.keys(e).forEach(s=>{let a=e[s];if(a.length>1)throw new Error(`Parameter "${s}" must have only a single value`);if(a=a[0],s==="client_max_window_bits"){if(a!==!0){let n=+a;if(!Number.isInteger(n)||n<8||n>15)throw new TypeError(`Invalid value for parameter "${s}": ${a}`);a=n}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${s}": ${a}`)}else if(s==="server_max_window_bits"){let n=+a;if(!Number.isInteger(n)||n<8||n>15)throw new TypeError(`Invalid value for parameter "${s}": ${a}`);a=n}else if(s==="client_no_context_takeover"||s==="server_no_context_takeover"){if(a!==!0)throw new TypeError(`Invalid value for parameter "${s}": ${a}`)}else throw new Error(`Unknown parameter "${s}"`);e[s]=a})}),t}decompress(t,e,s){qs.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){qs.add(a=>{this._compress(t,e,(n,i)=>{a(),s(n,i)})})}_decompress(t,e,s){let a=this._isServer?"client":"server";if(!this._inflate){let n=`${a}_max_window_bits`,i=typeof this.params[n]!="number"?ls.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=ls.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[Ws]=this,this._inflate[gt]=0,this._inflate[kt]=[],this._inflate.on("error",Br),this._inflate.on("data",qn)}this._inflate[jt]=s,this._inflate.write(t),e&&this._inflate.write(Or),this._inflate.flush(()=>{let n=this._inflate[Ht];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=jn.concat(this._inflate[kt],this._inflate[gt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[gt]=0,this._inflate[kt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._inflate.reset()),s(null,i)})}_compress(t,e,s){let a=this._isServer?"server":"client";if(!this._deflate){let n=`${a}_max_window_bits`,i=typeof this.params[n]!="number"?ls.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=ls.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[gt]=0,this._deflate[kt]=[],this._deflate.on("data",Nr)}this._deflate[jt]=s,this._deflate.write(t),this._deflate.flush(ls.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=jn.concat(this._deflate[kt],this._deflate[gt]);e&&(n=new Fr(n.buffer,n.byteOffset,n.length-4)),this._deflate[jt]=null,this._deflate[gt]=0,this._deflate[kt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};Wn.exports=Ra;function Nr(r){this[kt].push(r),this[gt]+=r.length}function qn(r){if(this[gt]+=r.length,this[Ws]._maxPayload<1||this[gt]<=this[Ws]._maxPayload){this[kt].push(r);return}this[Ht]=new RangeError("Max payload size exceeded"),this[Ht].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[Ht][Hn]=1009,this.removeListener("data",qn),this.reset()}function Br(r){if(this[Ws]._inflate=null,this[Ht]){this[jt](this[Ht]);return}r[Hn]=1007,this[jt](r)}});var Wt=Ye((_c,zs)=>{"use strict";var{isUtf8:zn}=require("buffer"),{hasBlob:Ur}=mt(),$r=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function jr(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Ia(r){let t=r.length,e=0;for(;e=t||(r[e+1]&192)!==128||(r[e+2]&192)!==128||r[e]===224&&(r[e+1]&224)===128||r[e]===237&&(r[e+1]&224)===160)return!1;e+=3}else if((r[e]&248)===240){if(e+3>=t||(r[e+1]&192)!==128||(r[e+2]&192)!==128||(r[e+3]&192)!==128||r[e]===240&&(r[e+1]&240)===128||r[e]===244&&r[e+1]>143||r[e]>244)return!1;e+=4}else return!1;return!0}function Hr(r){return Ur&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}zs.exports={isBlob:Hr,isValidStatusCode:jr,isValidUTF8:Ia,tokenChars:$r};if(zn)zs.exports.isValidUTF8=function(r){return r.length<24?Ia(r):zn(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");zs.exports.isValidUTF8=function(t){return t.length<32?Ia(t):r(t)}}catch{}});var Na=Ye((Ac,Qn)=>{"use strict";var{Writable:qr}=require("stream"),Gn=qt(),{BINARY_TYPES:Wr,EMPTY_BUFFER:Vn,kStatusCode:zr,kWebSocket:Gr}=mt(),{concat:La,toArrayBuffer:Vr,unmask:Yr}=os(),{isValidStatusCode:Kr,isValidUTF8:Yn}=Wt(),Gs=Buffer[Symbol.species],at=0,Kn=1,Jn=2,Xn=3,Ma=4,Fa=5,Vs=6,Oa=class extends qr{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Wr[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Gr]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._errored=!1,this._loop=!1,this._state=at}_write(t,e,s){if(this._opcode===8&&this._state==at)return s();this._bufferedBytes+=t.length,this._buffers.push(t),this.startLoop(s)}consume(t){if(this._bufferedBytes-=t,t===this._buffers[0].length)return this._buffers.shift();if(t=s.length?e.set(this._buffers.shift(),a):(e.set(new Uint8Array(s.buffer,s.byteOffset,t),a),this._buffers[0]=new Gs(s.buffer,s.byteOffset+t,s.length-t)),t-=s.length}while(t>0);return e}startLoop(t){this._loop=!0;do switch(this._state){case at:this.getInfo(t);break;case Kn:this.getPayloadLength16(t);break;case Jn:this.getPayloadLength64(t);break;case Xn:this.getMask();break;case Ma:this.getData(t);break;case Fa:case Vs:this._loop=!1;return}while(this._loop);this._errored||t()}getInfo(t){if(this._bufferedBytes<2){this._loop=!1;return}let e=this.consume(2);if((e[0]&48)!==0){let a=this.createError(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");t(a);return}let s=(e[0]&64)===64;if(s&&!this._extensions[Gn.extensionName]){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(this._fin=(e[0]&128)===128,this._opcode=e[0]&15,this._payloadLength=e[1]&127,this._opcode===0){if(s){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(!this._fragmented){let a=this.createError(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}this._opcode=this._fragmented}else if(this._opcode===1||this._opcode===2){if(this._fragmented){let a=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}this._compressed=s}else if(this._opcode>7&&this._opcode<11){if(!this._fin){let a=this.createError(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");t(a);return}if(s){let a=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");t(a);return}if(this._payloadLength>125||this._opcode===8&&this._payloadLength===1){let a=this.createError(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");t(a);return}}else{let a=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");t(a);return}if(!this._fin&&!this._fragmented&&(this._fragmented=this._opcode),this._masked=(e[1]&128)===128,this._isServer){if(!this._masked){let a=this.createError(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK");t(a);return}}else if(this._masked){let a=this.createError(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");t(a);return}this._payloadLength===126?this._state=Kn:this._payloadLength===127?this._state=Jn:this.haveLength(t)}getPayloadLength16(t){if(this._bufferedBytes<2){this._loop=!1;return}this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength(t)}getPayloadLength64(t){if(this._bufferedBytes<8){this._loop=!1;return}let e=this.consume(8),s=e.readUInt32BE(0);if(s>Math.pow(2,21)-1){let a=this.createError(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH");t(a);return}this._payloadLength=s*Math.pow(2,32)+e.readUInt32BE(4),this.haveLength(t)}haveLength(t){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0)){let e=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");t(e);return}this._masked?this._state=Xn:this._state=Ma}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=Ma}getData(t){let e=Vn;if(this._payloadLength){if(this._bufferedBytes7){this.controlMessage(e,t);return}if(this._compressed){this._state=Fa,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Gn.extensionName].decompress(t,this._fin,(a,n)=>{if(a)return e(a);if(n.length){if(this._messageLength+=n.length,this._messageLength>this._maxPayload&&this._maxPayload>0){let i=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(i);return}this._fragments.push(n)}this.dataMessage(e),this._state===at&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=at;return}let e=this._messageLength,s=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],this._opcode===2){let a;this._binaryType==="nodebuffer"?a=La(s,e):this._binaryType==="arraybuffer"?a=Vr(La(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit("message",a,!0),this._state=at,this.startLoop(t)}))}else{let a=La(s,e);if(!this._skipUTF8Validation&&!Yn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===Fa||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit("message",a,!1),this._state=at,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Vn),this.end();else{let s=t.readUInt16BE(0);if(!Kr(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new Gs(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!Yn(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");e(n);return}this._loop=!1,this.emit("conclude",s,a),this.end()}this._state=at;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=at):(this._state=Vs,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=at,this.startLoop(e)}))}createError(t,e,s,a,n){this._loop=!1,this._errored=!0;let i=new t(s?`Invalid WebSocket frame: ${e}`:e);return Error.captureStackTrace(i,this.createError),i.code=n,i[zr]=a,i}};Qn.exports=Oa});var $a=Ye((Pc,ti)=>{"use strict";var{Duplex:Ec}=require("stream"),{randomFillSync:Jr}=require("crypto"),Zn=qt(),{EMPTY_BUFFER:Xr,kWebSocket:Qr,NOOP:Zr}=mt(),{isBlob:zt,isValidStatusCode:eo}=Wt(),{mask:ei,toBuffer:Dt}=os(),nt=Symbol("kByteLength"),to=Buffer.alloc(4),Ys=8*1024,Rt,Gt=Ys,it=0,so=1,ao=2,Ba=class r{constructor(t,e,s){this._extensions=e||{},s&&(this._generateMask=s,this._maskBuffer=Buffer.alloc(4)),this._socket=t,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._queue=[],this._state=it,this.onerror=Zr,this[Qr]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||to,e.generateMask?e.generateMask(s):(Gt===Ys&&(Rt===void 0&&(Rt=Buffer.alloc(Ys)),Jr(Rt,0,Ys),Gt=0),s[0]=Rt[Gt++],s[1]=Rt[Gt++],s[2]=Rt[Gt++],s[3]=Rt[Gt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[nt]!==void 0?o=e[nt]:(t=Buffer.from(t),o=t.length):(o=t.length,a=e.mask&&e.readOnly&&!i);let c=o;o>=65536?(n+=8,c=127):o>125&&(n+=2,c=126);let l=Buffer.allocUnsafe(a?o+n:n);return l[0]=e.fin?e.opcode|128:e.opcode,e.rsv1&&(l[0]|=64),l[1]=c,c===126?l.writeUInt16BE(o,2):c===127&&(l[2]=l[3]=0,l.writeUIntBE(o,4,6)),e.mask?(l[1]|=128,l[n-4]=s[0],l[n-3]=s[1],l[n-2]=s[2],l[n-1]=s[3],i?[l,t]:a?(ei(t,s,l,n,o),[l]):(ei(t,s,t,0,o),[l,t])):[l,t]}close(t,e,s,a){let n;if(t===void 0)n=Xr;else{if(typeof t!="number"||!eo(t))throw new TypeError("First argument must be a valid error code number");if(e===void 0||!e.length)n=Buffer.allocUnsafe(2),n.writeUInt16BE(t,0);else{let o=Buffer.byteLength(e);if(o>123)throw new RangeError("The message must not be greater than 123 bytes");n=Buffer.allocUnsafe(2+o),n.writeUInt16BE(t,0),typeof e=="string"?n.write(e,2):n.set(e,2)}}let i={[nt]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==it?this.enqueue([this.dispatch,n,!1,i,a]):this.sendFrame(r.frame(n,i),a)}ping(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):zt(t)?(a=t.size,n=!1):(t=Dt(t),a=t.length,n=Dt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[nt]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};zt(t)?this._state!==it?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==it?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}pong(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):zt(t)?(a=t.size,n=!1):(t=Dt(t),a=t.length,n=Dt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[nt]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};zt(t)?this._state!==it?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==it?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}send(t,e,s){let a=this._extensions[Zn.extensionName],n=e.binary?2:1,i=e.compress,o,c;typeof t=="string"?(o=Buffer.byteLength(t),c=!1):zt(t)?(o=t.size,c=!1):(t=Dt(t),o=t.length,c=Dt.readOnly),this._firstFragment?(this._firstFragment=!1,i&&a&&a.params[a._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(i=o>=a._threshold),this._compress=i):(i=!1,n=0),e.fin&&(this._firstFragment=!0);let l={[nt]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:c,rsv1:i};zt(t)?this._state!==it?this.enqueue([this.getBlobData,t,this._compress,l,s]):this.getBlobData(t,this._compress,l,s):this._state!==it?this.enqueue([this.dispatch,t,this._compress,l,s]):this.dispatch(t,this._compress,l,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[nt],this._state=ao,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(Ua,this,o,a);return}this._bufferedBytes-=s[nt];let i=Dt(n);e?this.dispatch(i,e,s,a):(this._state=it,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(no,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(r.frame(t,s),a);return}let n=this._extensions[Zn.extensionName];this._bufferedBytes+=s[nt],this._state=so,n.compress(t,s.fin,(i,o)=>{if(this._socket.destroyed){let c=new Error("The socket was closed while data was being compressed");Ua(this,c,a);return}this._bufferedBytes-=s[nt],this._state=it,s.readOnly=!1,this.sendFrame(r.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===it&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][nt],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][nt],this._queue.push(t)}sendFrame(t,e){t.length===2?(this._socket.cork(),this._socket.write(t[0]),this._socket.write(t[1],e),this._socket.uncork()):this._socket.write(t[0],e)}};ti.exports=Ba;function Ua(r,t,e){typeof e=="function"&&e(t);for(let s=0;s{"use strict";var{kForOnEventAttribute:cs,kListener:ja}=mt(),si=Symbol("kCode"),ai=Symbol("kData"),ni=Symbol("kError"),ii=Symbol("kMessage"),ri=Symbol("kReason"),Vt=Symbol("kTarget"),oi=Symbol("kType"),li=Symbol("kWasClean"),yt=class{constructor(t){this[Vt]=null,this[oi]=t}get target(){return this[Vt]}get type(){return this[oi]}};Object.defineProperty(yt.prototype,"target",{enumerable:!0});Object.defineProperty(yt.prototype,"type",{enumerable:!0});var It=class extends yt{constructor(t,e={}){super(t),this[si]=e.code===void 0?0:e.code,this[ri]=e.reason===void 0?"":e.reason,this[li]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[si]}get reason(){return this[ri]}get wasClean(){return this[li]}};Object.defineProperty(It.prototype,"code",{enumerable:!0});Object.defineProperty(It.prototype,"reason",{enumerable:!0});Object.defineProperty(It.prototype,"wasClean",{enumerable:!0});var Yt=class extends yt{constructor(t,e={}){super(t),this[ni]=e.error===void 0?null:e.error,this[ii]=e.message===void 0?"":e.message}get error(){return this[ni]}get message(){return this[ii]}};Object.defineProperty(Yt.prototype,"error",{enumerable:!0});Object.defineProperty(Yt.prototype,"message",{enumerable:!0});var ds=class extends yt{constructor(t,e={}){super(t),this[ai]=e.data===void 0?null:e.data}get data(){return this[ai]}};Object.defineProperty(ds.prototype,"data",{enumerable:!0});var io={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[cs]&&a[ja]===t&&!a[cs])return;let s;if(r==="message")s=function(n,i){let o=new ds("message",{data:i?n:n.toString()});o[Vt]=this,Ks(t,this,o)};else if(r==="close")s=function(n,i){let o=new It("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Vt]=this,Ks(t,this,o)};else if(r==="error")s=function(n){let i=new Yt("error",{error:n,message:n.message});i[Vt]=this,Ks(t,this,i)};else if(r==="open")s=function(){let n=new yt("open");n[Vt]=this,Ks(t,this,n)};else return;s[cs]=!!e[cs],s[ja]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[ja]===t&&!e[cs]){this.removeListener(r,e);break}}};ci.exports={CloseEvent:It,ErrorEvent:Yt,Event:yt,EventTarget:io,MessageEvent:ds};function Ks(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var Js=Ye((Rc,hi)=>{"use strict";var{tokenChars:hs}=Wt();function dt(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function ro(r){let t=Object.create(null),e=Object.create(null),s=!1,a=!1,n=!1,i,o,c=-1,l=-1,d=-1,h=0;for(;h{let e=r[t];return Array.isArray(e)||(e=[e]),e.map(s=>[t].concat(Object.keys(s).map(a=>{let n=s[a];return Array.isArray(n)||(n=[n]),n.map(i=>i===!0?a:`${a}=${i}`).join("; ")})).join("; ")).join(", ")}).join(", ")}hi.exports={format:oo,parse:ro}});var ea=Ye((Mc,Si)=>{"use strict";var lo=require("events"),co=require("https"),ho=require("http"),fi=require("net"),uo=require("tls"),{randomBytes:po,createHash:fo}=require("crypto"),{Duplex:Ic,Readable:Lc}=require("stream"),{URL:Ha}=require("url"),xt=qt(),mo=Na(),go=$a(),{isBlob:yo}=Wt(),{BINARY_TYPES:ui,CLOSE_TIMEOUT:vo,EMPTY_BUFFER:Xs,GUID:bo,kForOnEventAttribute:qa,kListener:wo,kStatusCode:ko,kWebSocket:Ie,NOOP:mi}=mt(),{EventTarget:{addEventListener:xo,removeEventListener:So}}=di(),{format:Co,parse:To}=Js(),{toBuffer:_o}=os(),gi=Symbol("kAborted"),Wa=[8,13],vt=["CONNECTING","OPEN","CLOSING","CLOSED"],Ao=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,be=class r extends lo{constructor(t,e,s){super(),this._binaryType=ui[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Xs,this._closeTimer=null,this._errorEmitted=!1,this._extensions={},this._paused=!1,this._protocol="",this._readyState=r.CONNECTING,this._receiver=null,this._sender=null,this._socket=null,t!==null?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,e===void 0?e=[]:Array.isArray(e)||(typeof e=="object"&&e!==null?(s=e,e=[]):e=[e]),yi(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){ui.includes(t)&&(this._binaryType=t,this._receiver&&(this._receiver._binaryType=t))}get bufferedAmount(){return this._socket?this._socket._writableState.length+this._sender._bufferedBytes:this._bufferedAmount}get extensions(){return Object.keys(this._extensions).join()}get isPaused(){return this._paused}get onclose(){return null}get onerror(){return null}get onopen(){return null}get onmessage(){return null}get protocol(){return this._protocol}get readyState(){return this._readyState}get url(){return this._url}setSocket(t,e,s){let a=new mo({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new go(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[Ie]=this,n[Ie]=this,t[Ie]=this,a.on("conclude",Do),a.on("drain",Ro),a.on("error",Io),a.on("message",Lo),a.on("ping",Mo),a.on("pong",Fo),n.onerror=Oo,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",wi),t.on("data",Zs),t.on("end",ki),t.on("error",xi),this._readyState=r.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[xt.extensionName]&&this._extensions[xt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=r.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(t,e){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Qe(this,this._req,"WebSocket was closed before the connection was established");return}if(this.readyState===r.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=r.CLOSING,this._sender.close(t,e,!this._isServer,s=>{s||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),bi(this)}}pause(){this.readyState===r.CONNECTING||this.readyState===r.CLOSED||(this._paused=!0,this._socket.pause())}ping(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){za(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Xs,e,s)}pong(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof t=="function"?(s=t,t=e=void 0):typeof e=="function"&&(s=e,e=void 0),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){za(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Xs,e,s)}resume(){this.readyState===r.CONNECTING||this.readyState===r.CLOSED||(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(t,e,s){if(this.readyState===r.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof e=="function"&&(s=e,e={}),typeof t=="number"&&(t=t.toString()),this.readyState!==r.OPEN){za(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[xt.extensionName]||(a.compress=!1),this._sender.send(t||Xs,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Qe(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(be,"CONNECTING",{enumerable:!0,value:vt.indexOf("CONNECTING")});Object.defineProperty(be.prototype,"CONNECTING",{enumerable:!0,value:vt.indexOf("CONNECTING")});Object.defineProperty(be,"OPEN",{enumerable:!0,value:vt.indexOf("OPEN")});Object.defineProperty(be.prototype,"OPEN",{enumerable:!0,value:vt.indexOf("OPEN")});Object.defineProperty(be,"CLOSING",{enumerable:!0,value:vt.indexOf("CLOSING")});Object.defineProperty(be.prototype,"CLOSING",{enumerable:!0,value:vt.indexOf("CLOSING")});Object.defineProperty(be,"CLOSED",{enumerable:!0,value:vt.indexOf("CLOSED")});Object.defineProperty(be.prototype,"CLOSED",{enumerable:!0,value:vt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(be.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(be.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[qa])return t[wo];return null},set(t){for(let e of this.listeners(r))if(e[qa]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[qa]:!0})}})});be.prototype.addEventListener=xo;be.prototype.removeEventListener=So;Si.exports=be;function yi(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:vo,protocolVersion:Wa[1],maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...s,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:"GET",host:void 0,path:void 0,port:void 0};if(r._autoPong=a.autoPong,r._closeTimeout=a.closeTimeout,!Wa.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${Wa.join(", ")})`);let n;if(t instanceof Ha)n=t;else try{n=new Ha(t)}catch{throw new SyntaxError(`Invalid URL: ${t}`)}n.protocol==="http:"?n.protocol="ws:":n.protocol==="https:"&&(n.protocol="wss:"),r._url=n.href;let i=n.protocol==="wss:",o=n.protocol==="ws+unix:",c;if(n.protocol!=="ws:"&&!i&&!o?c=`The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`:o&&!n.pathname?c="The URL's pathname is empty":n.hash&&(c="The URL contains a fragment identifier"),c){let f=new SyntaxError(c);if(r._redirects===0)throw f;Qs(r,f);return}let l=i?443:80,d=po(16).toString("base64"),h=i?co.request:ho.request,u=new Set,p;if(a.createConnection=a.createConnection||(i?Po:Eo),a.defaultPort=a.defaultPort||l,a.port=n.port||l,a.host=n.hostname.startsWith("[")?n.hostname.slice(1,-1):n.hostname,a.headers={...a.headers,"Sec-WebSocket-Version":a.protocolVersion,"Sec-WebSocket-Key":d,Connection:"Upgrade",Upgrade:"websocket"},a.path=n.pathname+n.search,a.timeout=a.handshakeTimeout,a.perMessageDeflate&&(p=new xt({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=Co({[xt.extensionName]:p.offer()})),e.length){for(let f of e){if(typeof f!="string"||!Ao.test(f)||u.has(f))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(f)}a.headers["Sec-WebSocket-Protocol"]=e.join(",")}if(a.origin&&(a.protocolVersion<13?a.headers["Sec-WebSocket-Origin"]=a.origin:a.headers.Origin=a.origin),(n.username||n.password)&&(a.auth=`${n.username}:${n.password}`),o){let f=a.path.split(":");a.socketPath=f[0],a.path=f[1]}let m;if(a.followRedirects){if(r._redirects===0){r._originalIpc=o,r._originalSecure=i,r._originalHostOrSocketPath=o?a.socketPath:n.host;let f=s&&s.headers;if(s={...s,headers:{}},f)for(let[y,k]of Object.entries(f))s.headers[y.toLowerCase()]=k}else if(r.listenerCount("redirect")===0){let f=o?r._originalIpc?a.socketPath===r._originalHostOrSocketPath:!1:r._originalIpc?!1:n.host===r._originalHostOrSocketPath;(!f||r._originalSecure&&!i)&&(delete a.headers.authorization,delete a.headers.cookie,f||delete a.headers.host,a.auth=void 0)}a.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(a.auth).toString("base64")),m=r._req=h(a),r._redirects&&r.emit("redirect",r.url,m)}else m=r._req=h(a);a.timeout&&m.on("timeout",()=>{Qe(r,m,"Opening handshake has timed out")}),m.on("error",f=>{m===null||m[gi]||(m=r._req=null,Qs(r,f))}),m.on("response",f=>{let y=f.headers.location,k=f.statusCode;if(y&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){Qe(r,m,"Maximum redirects exceeded");return}m.abort();let b;try{b=new Ha(y,t)}catch{let g=new SyntaxError(`Invalid URL: ${y}`);Qs(r,g);return}yi(r,b,e,s)}else r.emit("unexpected-response",m,f)||Qe(r,m,`Unexpected server response: ${f.statusCode}`)}),m.on("upgrade",(f,y,k)=>{if(r.emit("upgrade",f),r.readyState!==be.CONNECTING)return;m=r._req=null;let b=f.headers.upgrade;if(b===void 0||b.toLowerCase()!=="websocket"){Qe(r,y,"Invalid Upgrade header");return}let v=fo("sha1").update(d+bo).digest("base64");if(f.headers["sec-websocket-accept"]!==v){Qe(r,y,"Invalid Sec-WebSocket-Accept header");return}let g=f.headers["sec-websocket-protocol"],x;if(g!==void 0?u.size?u.has(g)||(x="Server sent an invalid subprotocol"):x="Server sent a subprotocol but none was requested":u.size&&(x="Server sent no subprotocol"),x){Qe(r,y,x);return}g&&(r._protocol=g);let T=f.headers["sec-websocket-extensions"];if(T!==void 0){if(!p){Qe(r,y,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let _;try{_=To(T)}catch{Qe(r,y,"Invalid Sec-WebSocket-Extensions header");return}let R=Object.keys(_);if(R.length!==1||R[0]!==xt.extensionName){Qe(r,y,"Server indicated an extension that was not requested");return}try{p.accept(_[xt.extensionName])}catch{Qe(r,y,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[xt.extensionName]=p}r.setSocket(y,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(m,r):m.end()}function Qs(r,t){r._readyState=be.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function Eo(r){return r.path=r.socketPath,fi.connect(r)}function Po(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=fi.isIP(r.host)?"":r.host),uo.connect(r)}function Qe(r,t,e){r._readyState=be.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Qe),t.setHeader?(t[gi]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Qs,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function za(r,t,e){if(t){let s=yo(t)?t.size:_o(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${vt[r.readyState]})`);process.nextTick(e,s)}}function Do(r,t){let e=this[Ie];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[Ie]!==void 0&&(e._socket.removeListener("data",Zs),process.nextTick(vi,e._socket),r===1005?e.close():e.close(r,t))}function Ro(){let r=this[Ie];r.isPaused||r._socket.resume()}function Io(r){let t=this[Ie];t._socket[Ie]!==void 0&&(t._socket.removeListener("data",Zs),process.nextTick(vi,t._socket),t.close(r[ko])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function pi(){this[Ie].emitClose()}function Lo(r,t){this[Ie].emit("message",r,t)}function Mo(r){let t=this[Ie];t._autoPong&&t.pong(r,!this._isServer,mi),t.emit("ping",r)}function Fo(r){this[Ie].emit("pong",r)}function vi(r){r.resume()}function Oo(r){let t=this[Ie];t.readyState!==be.CLOSED&&(t.readyState===be.OPEN&&(t._readyState=be.CLOSING,bi(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function bi(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function wi(){let r=this[Ie];if(this.removeListener("close",wi),this.removeListener("data",Zs),this.removeListener("end",ki),r._readyState=be.CLOSING,!this._readableState.endEmitted&&!r._closeFrameReceived&&!r._receiver._writableState.errorEmitted&&this._readableState.length!==0){let t=this.read(this._readableState.length);r._receiver.write(t)}r._receiver.end(),this[Ie]=void 0,clearTimeout(r._closeTimer),r._receiver._writableState.finished||r._receiver._writableState.errorEmitted?r.emitClose():(r._receiver.on("error",pi),r._receiver.on("finish",pi))}function Zs(r){this[Ie]._receiver.write(r)||this.pause()}function ki(){let r=this[Ie];r._readyState=be.CLOSING,r._receiver.end(),this.end()}function xi(){let r=this[Ie];this.removeListener("error",xi),this.on("error",mi),r&&(r._readyState=be.CLOSING,this.destroy())}});var Ai=Ye((Oc,_i)=>{"use strict";var Fc=ea(),{Duplex:No}=require("stream");function Ci(r){r.emit("close")}function Bo(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Ti(r){this.removeListener("error",Ti),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function Uo(r,t){let e=!0,s=new No({...t,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return r.on("message",function(n,i){let o=!i&&s._readableState.objectMode?n.toString():n;s.push(o)||r.pause()}),r.once("error",function(n){s.destroyed||(e=!1,s.destroy(n))}),r.once("close",function(){s.destroyed||s.push(null)}),s._destroy=function(a,n){if(r.readyState===r.CLOSED){n(a),process.nextTick(Ci,s);return}let i=!1;r.once("error",function(c){i=!0,n(c)}),r.once("close",function(){i||n(a),process.nextTick(Ci,s)}),e&&r.terminate()},s._final=function(a){if(r.readyState===r.CONNECTING){r.once("open",function(){s._final(a)});return}r._socket!==null&&(r._socket._writableState.finished?(a(),s._readableState.endEmitted&&s.destroy()):(r._socket.once("finish",function(){a()}),r.close()))},s._read=function(){r.isPaused&&r.resume()},s._write=function(a,n,i){if(r.readyState===r.CONNECTING){r.once("open",function(){s._write(a,n,i)});return}r.send(a,i)},s.on("end",Bo),s.on("error",Ti),s}_i.exports=Uo});var Ga=Ye((Nc,Ei)=>{"use strict";var{tokenChars:$o}=Wt();function jo(r){let t=new Set,e=-1,s=-1,a=0;for(a;a{"use strict";var Ho=require("events"),ta=require("http"),{Duplex:Bc}=require("stream"),{createHash:qo}=require("crypto"),Pi=Js(),Lt=qt(),Wo=Ga(),zo=ea(),{CLOSE_TIMEOUT:Go,GUID:Vo,kWebSocket:Yo}=mt(),Ko=/^[+/0-9A-Za-z]{22}==$/,Di=0,Ri=1,Li=2,Va=class extends Ho{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Go,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:zo,...t},t.port==null&&!t.server&&!t.noServer||t.port!=null&&(t.server||t.noServer)||t.server&&t.noServer)throw new TypeError('One and only one of the "port", "server", or "noServer" options must be specified');if(t.port!=null?(this._server=ta.createServer((s,a)=>{let n=ta.STATUS_CODES[426];a.writeHead(426,{"Content-Length":n.length,"Content-Type":"text/plain"}),a.end(n)}),this._server.listen(t.port,t.host,t.backlog,e)):t.server&&(this._server=t.server),this._server){let s=this.emit.bind(this,"connection");this._removeListeners=Jo(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(a,n,i)=>{this.handleUpgrade(a,n,i,s)}})}t.perMessageDeflate===!0&&(t.perMessageDeflate={}),t.clientTracking&&(this.clients=new Set,this._shouldEmitClose=!1),this.options=t,this._state=Di}address(){if(this.options.noServer)throw new Error('The server is operating in "noServer" mode');return this._server?this._server.address():null}close(t){if(this._state===Li){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(us,this);return}if(t&&this.once("close",t),this._state!==Ri)if(this._state=Ri,this.options.noServer||this.options.server)this._server&&(this._removeListeners(),this._removeListeners=this._server=null),this.clients?this.clients.size?this._shouldEmitClose=!0:process.nextTick(us,this):process.nextTick(us,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{us(this)})}}shouldHandle(t){if(this.options.path){let e=t.url.indexOf("?");if((e!==-1?t.url.slice(0,e):t.url)!==this.options.path)return!1}return!0}handleUpgrade(t,e,s,a){e.on("error",Ii);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){Mt(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){Mt(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Ko.test(n)){Mt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){Mt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){ps(e,400);return}let c=t.headers["sec-websocket-protocol"],l=new Set;if(c!==void 0)try{l=Wo.parse(c)}catch{Mt(this,t,e,400,"Invalid Sec-WebSocket-Protocol header");return}let d=t.headers["sec-websocket-extensions"],h={};if(this.options.perMessageDeflate&&d!==void 0){let u=new Lt({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=Pi.parse(d);p[Lt.extensionName]&&(u.accept(p[Lt.extensionName]),h[Lt.extensionName]=u)}catch{Mt(this,t,e,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let u={origin:t.headers[`${o===8?"sec-websocket-origin":"origin"}`],secure:!!(t.socket.authorized||t.socket.encrypted),req:t};if(this.options.verifyClient.length===2){this.options.verifyClient(u,(p,m,f,y)=>{if(!p)return ps(e,m||401,f,y);this.completeUpgrade(h,n,l,t,e,s,a)});return}if(!this.options.verifyClient(u))return ps(e,401)}this.completeUpgrade(h,n,l,t,e,s,a)}completeUpgrade(t,e,s,a,n,i,o){if(!n.readable||!n.writable)return n.destroy();if(n[Yo])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>Di)return ps(n,503);let l=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${qo("sha1").update(e+Vo).digest("base64")}`],d=new this.options.WebSocket(null,void 0,this.options);if(s.size){let h=this.options.handleProtocols?this.options.handleProtocols(s,a):s.values().next().value;h&&(l.push(`Sec-WebSocket-Protocol: ${h}`),d._protocol=h)}if(t[Lt.extensionName]){let h=t[Lt.extensionName].params,u=Pi.format({[Lt.extensionName]:[h]});l.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",l,a),n.write(l.concat(`\r `).join(`\r -`)),n.removeListener("error",Ii),d.setSocket(n,i,{allowSynchronousEvents:this.options.allowSynchronousEvents,maxPayload:this.options.maxPayload,skipUTF8Validation:this.options.skipUTF8Validation}),this.clients&&(this.clients.add(d),d.on("close",()=>{this.clients.delete(d),this._shouldEmitClose&&!this.clients.size&&process.nextTick(us,this)})),o(d,a)}};Mi.exports=Ga;function Jo(r,t){for(let e of Object.keys(t))r.on(e,t[e]);return function(){for(let s of Object.keys(t))r.removeListener(s,t[s])}}function us(r){r._state=Li,r.emit("close")}function Ii(){this.destroy()}function ps(r,t,e,s){e=e||ta.STATUS_CODES[t],s={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(e),...s},r.once("finish",r.destroy),r.end(`HTTP/1.1 ${t} ${ta.STATUS_CODES[t]}\r +`)),n.removeListener("error",Ii),d.setSocket(n,i,{allowSynchronousEvents:this.options.allowSynchronousEvents,maxPayload:this.options.maxPayload,skipUTF8Validation:this.options.skipUTF8Validation}),this.clients&&(this.clients.add(d),d.on("close",()=>{this.clients.delete(d),this._shouldEmitClose&&!this.clients.size&&process.nextTick(us,this)})),o(d,a)}};Mi.exports=Va;function Jo(r,t){for(let e of Object.keys(t))r.on(e,t[e]);return function(){for(let s of Object.keys(t))r.removeListener(s,t[s])}}function us(r){r._state=Li,r.emit("close")}function Ii(){this.destroy()}function ps(r,t,e,s){e=e||ta.STATUS_CODES[t],s={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(e),...s},r.once("finish",r.destroy),r.end(`HTTP/1.1 ${t} ${ta.STATUS_CODES[t]}\r `+Object.keys(s).map(a=>`${a}: ${s[a]}`).join(`\r `)+`\r \r -`+e)}function Mt(r,t,e,s,a,n){if(r.listenerCount("wsClientError")){let i=new Error(a);Error.captureStackTrace(i,Mt),r.emit("wsClientError",i,e,t)}else ps(e,s,a,n)}});var fl={};nr(fl,{default:()=>oa});module.exports=ir(fl);var Ji=require("fs"),Se=require("obsidian");var Ct="agent-fleet-agents";var wt="agent-fleet-dashboard",st="agent-fleet-chat",ct={fleetFolder:"_fleet",claudeCliPath:"claude",defaultModel:"default",awsRegion:"us-east-1",maxConcurrentRuns:2,runLogRetentionDays:30,catchUpMissedTasks:!0,notificationLevel:"all",showStatusBar:!0,mcpApiKeys:{},mcpTokens:{},channelCredentials:{},maxConcurrentChannelSessions:5,channelIdleTimeoutMinutes:15,channelRateLimitPerConversation:20,channelRateLimitWindowMinutes:5,chatWatchdogMinutes:10,defaultFileHashes:{}},en=["agents","skills","tasks","runs","memory","channels"];var C=require("obsidian");var da=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`--- +`+e)}function Mt(r,t,e,s,a,n){if(r.listenerCount("wsClientError")){let i=new Error(a);Error.captureStackTrace(i,Mt),r.emit("wsClientError",i,e,t)}else ps(e,s,a,n)}});var yl={};nr(yl,{default:()=>la});module.exports=ir(yl);var Ji=require("fs"),Se=require("obsidian");var Ct="agent-fleet-agents";var wt="agent-fleet-dashboard",st="agent-fleet-chat",lt={fleetFolder:"_fleet",claudeCliPath:"claude",defaultModel:"default",awsRegion:"us-east-1",maxConcurrentRuns:2,runLogRetentionDays:30,catchUpMissedTasks:!0,notificationLevel:"all",showStatusBar:!0,mcpApiKeys:{},mcpTokens:{},channelCredentials:{},maxConcurrentChannelSessions:5,channelIdleTimeoutMinutes:15,channelRateLimitPerConversation:20,channelRateLimitWindowMinutes:5,chatWatchdogMinutes:10,defaultFileHashes:{}},en=["agents","skills","tasks","runs","memory","channels"];var C=require("obsidian");var ha=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`--- {} --- @@ -11664,14 +11664,14 @@ ${e} --- ${s} -`}function me(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Ot(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}var ut=require("child_process"),sn=require("fs"),Qt=require("os"),Tt=require("path");function ha(){return(0,Qt.homedir)()}function Zt(){return(0,Tt.join)((0,Qt.homedir)(),".claude")}function es(){return(0,Tt.join)((0,Qt.homedir)(),".claude.json")}function an(){if(process.platform==="darwin")return"/bin/zsh";for(let r of["/bin/bash","/bin/zsh","/bin/sh"])if((0,sn.existsSync)(r))return r;return"/bin/sh"}function rr(r){return`'${r.replace(/'/g,"'\\''")}'`}function pt(r,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,ut.spawn)(r,t,s);let a=an(),n=[r,...t].map(rr).join(" ");return(0,ut.spawn)(a,["-l","-c",n],s)}function ua(r,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,ut.spawn)(r,[],{...e,shell:!0});let s=an();return(0,ut.spawn)(s,["-l","-c",r],e)}function nn(r){try{require("electron").shell.openExternal(r)}catch{switch(process.platform){case"darwin":(0,ut.spawn)("open",[r],{stdio:"ignore"});break;case"win32":(0,ut.spawn)("cmd.exe",["/c","start","",r.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,ut.spawn)("xdg-open",[r],{stdio:"ignore"});break}}}function fe(r){return r.split(/\r?\n/)}function rn(r){let t=(0,Qt.homedir)();if(process.platform==="win32")return[r,(0,Tt.join)(process.env.APPDATA??"","Claude","claude.exe"),(0,Tt.join)(process.env.LOCALAPPDATA??"","Claude","claude.exe"),(0,Tt.join)(t,".local","bin","claude.exe"),"claude.exe","claude"].filter(s=>!!s&&tn(s));let e=[r,(0,Tt.join)(t,".local","bin","claude")];return process.platform==="darwin"&&e.push("/opt/homebrew/bin/claude"),e.push("/usr/local/bin/claude","/usr/bin/claude","claude"),e.filter(s=>!!s&&tn(s))}function tn(r){return!r||/[\n\r\0]/.test(r)?!1:r.startsWith("/")?/^[\w/.@+-]+$/.test(r):r.startsWith("~")?/^~[\w/.@+-]*$/.test(r):/^[a-zA-Z]:[\\/]/.test(r)?/^[a-zA-Z]:[\\/][\w\\/. @+-]+$/.test(r):r.startsWith("\\\\")?/^\\\\[\w\\/. @+-]+$/.test(r):!r.includes("/")&&!r.includes("\\")?/^[\w.@+-]+$/.test(r):!1}function pa(r){return!!(r.includes("/")||r.includes("\\"))}function xs(r){return typeof r=="object"&&r!==null}function M(r){return typeof r=="string"?r:void 0}function Je(r,t){return typeof r=="boolean"?r:t}function $e(r,t){return typeof r=="number"&&Number.isFinite(r)?r:t}function ve(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}function on(r){return(0,C.normalizePath)(r.replace(/\.md$/,".permissions.json"))}function ln(r){let t=0;for(let e=0;ee.path.startsWith(`${this.getFleetRoot()}/`));for(let e of t)await this.loadFile(e);return this.validateReferences(),this.getSnapshot()}async loadFile(t){let e=typeof t=="string"?this.vault.getAbstractFileByPath(t):t;if(!(e instanceof C.TFile)||e.extension!=="md")return;if(this.isInsideAgentFolder(e.path)){await this.reloadFolderAgentContaining(e.path);return}if(this.isInsideSkillFolder(e.path)){await this.reloadFolderSkillContaining(e.path);return}this.clearStoredFile(e.path);let s=`${this.getSubfolder("channels")}/`;if(e.path.startsWith(s)){if(!e.path.slice(s.length).includes("/")){let o=await this.vault.cachedRead(e),c=this.parseChannelFile(e.path,o);c&&this.channels.set(e.path,c)}return}let a=await this.vault.cachedRead(e),n=this.parseFile(e.path,a);if(n)if("taskId"in n)this.tasks.set(e.path,n);else if("model"in n){if(!n.isFolder){let i=on(e.path),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFile)try{let c=await this.vault.cachedRead(o),l=JSON.parse(c);n.permissionRules={allow:ve(l.allow),deny:ve(l.deny)}}catch{}}this.agents.set(e.path,n)}else this.skills.set(e.path,n)}async reloadFolderAgentContaining(t){let e=`${this.getSubfolder("agents")}/`,a=t.slice(e.length).split("/")[0];if(!a)return;let n=(0,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let c=this.vault.getAbstractFileByPath(i);if(!(c instanceof C.TFile))return;let l=await this.loadFolderAgent(n,c);l&&this.agents.set(i,l)}isInsideAgentFolder(t){let e=`${this.getSubfolder("agents")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}isInsideSkillFolder(t){let e=`${this.getSubfolder("skills")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}async reloadFolderSkillContaining(t){let e=`${this.getSubfolder("skills")}/`,a=t.slice(e.length).split("/")[0];if(!a)return;let n=(0,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/skill.md`);if(this.skills.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let c=this.vault.getAbstractFileByPath(i);if(!(c instanceof C.TFile))return;let l=await this.loadFolderSkill(n,c);l&&this.skills.set(i,l)}async loadFolderSkills(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("skills"));if(t instanceof C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderSkill(e.path,a);n&&this.skills.set(s,n)}}async loadFolderSkill(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=ne(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async c=>{let l=(0,C.normalizePath)(`${t}/${c}`),d=this.vault.getAbstractFileByPath(l);if(!(d instanceof C.TFile))return"";let h=await this.vault.cachedRead(d);return ne(h).body};return{filePath:e.path,name:i,description:M(a.description),tags:ve(a.tags),body:n,toolsBody:await o("tools.md"),referencesBody:await o("references.md"),examplesBody:await o("examples.md"),isFolder:!0}}async loadFolderAgents(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("agents"));if(t instanceof C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderAgent(e.path,a);n&&this.agents.set(s,n)}}async loadFolderAgent(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=ne(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},c=(0,C.normalizePath)(`${t}/config.md`),l=this.vault.getAbstractFileByPath(c);if(l instanceof C.TFile){let B=await this.vault.cachedRead(l);o=ne(B).frontmatter}let d={allow:[],deny:[]},h=(0,C.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof C.TFile)try{let B=await this.vault.cachedRead(u),I=JSON.parse(B);d={allow:ve(I.allow),deny:ve(I.deny)}}catch{}if(d.allow.length===0&&d.deny.length===0){let B=ve(o.allowed_tools),I=ve(o.blocked_tools);(B.length>0||I.length>0)&&(d={allow:B,deny:I},this.warnedLegacyPerms.has(i)||(this.warnedLegacyPerms.add(i),console.warn(`Agent Fleet: "${i}" still uses legacy allowed_tools/blocked_tools in config.md. Permission rules now live in permissions.json. Open this agent in Edit and Save to migrate.`)))}let p="",f=(0,C.normalizePath)(`${t}/SKILLS.md`),m=this.vault.getAbstractFileByPath(f);if(m instanceof C.TFile){let B=await this.vault.cachedRead(m);p=ne(B).body}let y="",k=(0,C.normalizePath)(`${t}/CONTEXT.md`),b=this.vault.getAbstractFileByPath(k);if(b instanceof C.TFile){let B=await this.vault.cachedRead(b);y=ne(B).body}let v=!1,g="",x="",T=!0,_="",R=(0,C.normalizePath)(`${t}/HEARTBEAT.md`),A=this.vault.getAbstractFileByPath(R);if(A instanceof C.TFile){let B=await this.vault.cachedRead(A),I=ne(B);v=Je(I.frontmatter.enabled,!1),g=M(I.frontmatter.schedule)??"",T=Je(I.frontmatter.notify,!0),_=M(I.frontmatter.channel)??"",x=I.body}let S=M(a.model),L=M(o.model);S&&L&&S!==L&&(this.warnedFolderAgentModelConflict.has(i)||(this.warnedFolderAgentModelConflict.add(i),console.warn(`Agent Fleet: "${i}" has conflicting model fields \u2014 agent.md says "${S}", config.md says "${L}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let E=L??S??this.settings.defaultModel;return{filePath:e.path,name:i,description:M(a.description),model:E,adapter:M(o.adapter)??"claude-code",permissionMode:M(o.permission_mode)??"bypassPermissions",effort:M(o.effort),maxRetries:$e(o.max_retries,1),skills:ve(a.skills),mcpServers:ve(a.mcp_servers),cwd:M(o.cwd)||M(a.cwd),enabled:Je(a.enabled,!0),timeout:$e(o.timeout,$e(a.timeout,300)),approvalRequired:ve(o.approval_required),memory:Je(o.memory,Je(a.memory,!1)),memoryMaxEntries:$e(o.memory_max_entries,100),autoCompactThreshold:$e(o.auto_compact_threshold??a.auto_compact_threshold,85),tags:ve(a.tags),avatar:M(a.avatar)??"",body:n,contextBody:y,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:v,heartbeatSchedule:g,heartbeatBody:x,heartbeatNotify:T,heartbeatChannel:_,wikiKeeper:this.parseWikiKeeperConfig(o.wiki_keeper??a.wiki_keeper),wikiReferences:this.parseWikiReferences(o.wiki_references??a.wiki_references)}}parseWikiReferences(t){if(!Array.isArray(t))return;let e=[];for(let s of t)if(typeof s=="string"&&s.trim())e.push({agent:s.trim()});else if(s&&typeof s=="object"){let a=s.agent;typeof a=="string"&&a.trim()&&e.push({agent:a.trim()})}return e.length>0?e:void 0}parseWikiKeeperConfig(t){if(!t||typeof t!="object")return;let e=t;return{scopeRoot:M(e.scope_root)??"",inboxPath:M(e.inbox_path)??"_sources/inbox",archivePath:M(e.archive_path)??"_sources/archive",failedPath:M(e.failed_path)??"_sources/failed",topicsRoot:M(e.topics_root)??"_topics",indexPath:M(e.index_path)??"index.md",logPath:M(e.log_path)??"log.md",watchedFolders:ve(e.watched_folders),excludePatterns:ve(e.exclude_patterns),watchedSince:M(e.watched_since)??"",fileSubstantiveAnswers:Je(e.file_substantive_answers,!0),obsidianUrlScheme:Je(e.obsidian_url_scheme,!0),maxTokensPerIngest:$e(e.max_tokens_per_ingest,6e4),maxTokensPerRefresh:$e(e.max_tokens_per_refresh,3e4),indexSplitThreshold:$e(e.index_split_threshold,100),dedupSimilarityThreshold:$e(e.dedup_similarity_threshold,.82),summaryStaleDays:$e(e.summary_stale_days,30),stateFile:M(e.state_file)??".wiki-keeper-state.json"}}removeFile(t){this.clearStoredFile(t)}getSnapshot(){return{agents:Array.from(this.agents.values()).sort((t,e)=>t.name.localeCompare(e.name)),skills:Array.from(this.skills.values()).sort((t,e)=>t.name.localeCompare(e.name)),tasks:Array.from(this.tasks.values()).sort((t,e)=>t.taskId.localeCompare(e.taskId)),channels:Array.from(this.channels.values()).sort((t,e)=>t.name.localeCompare(e.name)),validationIssues:Array.from(this.validationIssues.values()).flat()}}getAgentByName(t){return Array.from(this.agents.values()).find(e=>e.name===t)}getSkillByName(t){return Array.from(this.skills.values()).find(e=>e.name===t)}getTaskById(t){return Array.from(this.tasks.values()).find(e=>e.taskId===t)}getTasksForAgent(t){return Array.from(this.tasks.values()).filter(e=>e.agent===t)}getChannelByName(t){return Array.from(this.channels.values()).find(e=>e.name===t)}getChannelsForAgent(t){return Array.from(this.channels.values()).filter(e=>e.defaultAgent===t)}getRunsRoot(){return this.getSubfolder("runs")}getMemoryPath(t){return(0,C.normalizePath)(`${this.getSubfolder("memory")}/${me(t)}.md`)}getConversationsDir(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,C.normalizePath)(`${s}/conversations`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,C.normalizePath)(`${e}/${t.name}-conversations`)}getLegacyChatPath(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,C.normalizePath)(`${s}/chat.json`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,C.normalizePath)(`${e}/${t.name}-chat.json`)}getConversationPath(t,e){if(e==="default")return this.getLegacyChatPath(t);let s=this.getConversationsDir(t),a=me(e)||"conversation";return(0,C.normalizePath)(`${s}/${a}.json`)}async listConversations(t){let e=[],s=this.getLegacyChatPath(t),a=this.vault.getAbstractFileByPath(s);if(a instanceof C.TFile){let o=await this.readConversationMeta(a,"default",!0);o&&e.push(o)}let n=this.getConversationsDir(t),i=this.vault.getAbstractFileByPath(n);if(i instanceof C.TFolder)for(let o of i.children){if(!(o instanceof C.TFile)||o.extension!=="json")continue;let c=o.basename,l=await this.readConversationMeta(o,c,!1);l&&e.push(l)}return e.sort((o,c)=>c.lastActive.localeCompare(o.lastActive)),e}async readConversationMeta(t,e,s){try{let a=await this.vault.cachedRead(t),n=JSON.parse(a);return{id:e,name:n.name?.trim()||(s?"Main chat":"Untitled"),lastActive:n.lastActive??new Date(t.stat.mtime).toISOString(),messageCount:Array.isArray(n.messages)?n.messages.length:0,isLegacy:s}}catch{return null}}async createConversation(t,e,s){if(e==="default")throw new Error('Cannot create a conversation with the reserved id "default"');let a=this.getConversationPath(t,e);if(this.vault.getAbstractFileByPath(a)instanceof C.TFile)return;let i=a.replace(/\/[^/]+$/,"");await this.ensureFolder(i);let o=new Date().toISOString(),c={sessionId:null,messages:[],lastActive:o,createdAt:o,name:s.trim()};await this.vault.create(a,JSON.stringify(c,null,2))}async renameConversation(t,e,s){let a=this.getConversationPath(t,e),n=this.vault.getAbstractFileByPath(a);if(!(n instanceof C.TFile))return;let i=await this.vault.cachedRead(n),o;try{o=JSON.parse(i)}catch{return}o.name=s.trim(),await this.vault.modify(n,JSON.stringify(o,null,2))}async deleteConversation(t,e){if(e==="default")throw new Error("The default (legacy) conversation can't be deleted from the registry; use the chat view's New Chat instead.");let s=this.getConversationPath(t,e),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFile&&await this.vault.delete(a)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof C.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ne(a);return{filePath:e,agent:M(n.agent)??t,lastUpdated:M(n.last_updated),body:i}}async appendMemory(t,e){if(e.length===0)return;let s=this.getMemoryPath(t),a=this.vault.getAbstractFileByPath(s),n=new Date().toISOString(),i=e.map(o=>`- ${o.trim()}`).join(` +`}function fe(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Ot(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}var ut=require("child_process"),sn=require("fs"),Qt=require("os"),Tt=require("path");function ua(){return(0,Qt.homedir)()}function Zt(){return(0,Tt.join)((0,Qt.homedir)(),".claude")}function es(){return(0,Tt.join)((0,Qt.homedir)(),".claude.json")}function an(){if(process.platform==="darwin")return"/bin/zsh";for(let r of["/bin/bash","/bin/zsh","/bin/sh"])if((0,sn.existsSync)(r))return r;return"/bin/sh"}function rr(r){return`'${r.replace(/'/g,"'\\''")}'`}function pt(r,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,ut.spawn)(r,t,s);let a=an(),n=[r,...t].map(rr).join(" ");return(0,ut.spawn)(a,["-l","-c",n],s)}function pa(r,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,ut.spawn)(r,[],{...e,shell:!0});let s=an();return(0,ut.spawn)(s,["-l","-c",r],e)}function nn(r){try{require("electron").shell.openExternal(r)}catch{switch(process.platform){case"darwin":(0,ut.spawn)("open",[r],{stdio:"ignore"});break;case"win32":(0,ut.spawn)("cmd.exe",["/c","start","",r.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,ut.spawn)("xdg-open",[r],{stdio:"ignore"});break}}}function me(r){return r.split(/\r?\n/)}function rn(r){let t=(0,Qt.homedir)();if(process.platform==="win32")return[r,(0,Tt.join)(process.env.APPDATA??"","Claude","claude.exe"),(0,Tt.join)(process.env.LOCALAPPDATA??"","Claude","claude.exe"),(0,Tt.join)(t,".local","bin","claude.exe"),"claude.exe","claude"].filter(s=>!!s&&tn(s));let e=[r,(0,Tt.join)(t,".local","bin","claude")];return process.platform==="darwin"&&e.push("/opt/homebrew/bin/claude"),e.push("/usr/local/bin/claude","/usr/bin/claude","claude"),e.filter(s=>!!s&&tn(s))}function tn(r){return!r||/[\n\r\0]/.test(r)?!1:r.startsWith("/")?/^[\w/.@+-]+$/.test(r):r.startsWith("~")?/^~[\w/.@+-]*$/.test(r):/^[a-zA-Z]:[\\/]/.test(r)?/^[a-zA-Z]:[\\/][\w\\/. @+-]+$/.test(r):r.startsWith("\\\\")?/^\\\\[\w\\/. @+-]+$/.test(r):!r.includes("/")&&!r.includes("\\")?/^[\w.@+-]+$/.test(r):!1}function fa(r){return!!(r.includes("/")||r.includes("\\"))}function xs(r){return typeof r=="object"&&r!==null}function M(r){return typeof r=="string"?r:void 0}function Je(r,t){return typeof r=="boolean"?r:t}function $e(r,t){return typeof r=="number"&&Number.isFinite(r)?r:t}function ve(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}function on(r){return(0,C.normalizePath)(r.replace(/\.md$/,".permissions.json"))}function ln(r){let t=0;for(let e=0;ee.path.startsWith(`${this.getFleetRoot()}/`));for(let e of t)await this.loadFile(e);return this.validateReferences(),this.getSnapshot()}async loadFile(t){let e=typeof t=="string"?this.vault.getAbstractFileByPath(t):t;if(!(e instanceof C.TFile)||e.extension!=="md")return;if(this.isInsideAgentFolder(e.path)){await this.reloadFolderAgentContaining(e.path);return}if(this.isInsideSkillFolder(e.path)){await this.reloadFolderSkillContaining(e.path);return}this.clearStoredFile(e.path);let s=`${this.getSubfolder("channels")}/`;if(e.path.startsWith(s)){if(!e.path.slice(s.length).includes("/")){let o=await this.vault.cachedRead(e),c=this.parseChannelFile(e.path,o);c&&this.channels.set(e.path,c)}return}let a=await this.vault.cachedRead(e),n=this.parseFile(e.path,a);if(n)if("taskId"in n)this.tasks.set(e.path,n);else if("model"in n){if(!n.isFolder){let i=on(e.path),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFile)try{let c=await this.vault.cachedRead(o),l=JSON.parse(c);n.permissionRules={allow:ve(l.allow),deny:ve(l.deny)}}catch{}}this.agents.set(e.path,n)}else this.skills.set(e.path,n)}async reloadFolderAgentContaining(t){let e=`${this.getSubfolder("agents")}/`,a=t.slice(e.length).split("/")[0];if(!a)return;let n=(0,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let c=this.vault.getAbstractFileByPath(i);if(!(c instanceof C.TFile))return;let l=await this.loadFolderAgent(n,c);l&&this.agents.set(i,l)}isInsideAgentFolder(t){let e=`${this.getSubfolder("agents")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}isInsideSkillFolder(t){let e=`${this.getSubfolder("skills")}/`;return t.startsWith(e)?t.slice(e.length).includes("/"):!1}async reloadFolderSkillContaining(t){let e=`${this.getSubfolder("skills")}/`,a=t.slice(e.length).split("/")[0];if(!a)return;let n=(0,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/skill.md`);if(this.skills.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let c=this.vault.getAbstractFileByPath(i);if(!(c instanceof C.TFile))return;let l=await this.loadFolderSkill(n,c);l&&this.skills.set(i,l)}async loadFolderSkills(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("skills"));if(t instanceof C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderSkill(e.path,a);n&&this.skills.set(s,n)}}async loadFolderSkill(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=ne(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async c=>{let l=(0,C.normalizePath)(`${t}/${c}`),d=this.vault.getAbstractFileByPath(l);if(!(d instanceof C.TFile))return"";let h=await this.vault.cachedRead(d);return ne(h).body};return{filePath:e.path,name:i,description:M(a.description),tags:ve(a.tags),body:n,toolsBody:await o("tools.md"),referencesBody:await o("references.md"),examplesBody:await o("examples.md"),isFolder:!0}}async loadFolderAgents(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("agents"));if(t instanceof C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderAgent(e.path,a);n&&this.agents.set(s,n)}}async loadFolderAgent(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=ne(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},c=(0,C.normalizePath)(`${t}/config.md`),l=this.vault.getAbstractFileByPath(c);if(l instanceof C.TFile){let B=await this.vault.cachedRead(l);o=ne(B).frontmatter}let d={allow:[],deny:[]},h=(0,C.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof C.TFile)try{let B=await this.vault.cachedRead(u),I=JSON.parse(B);d={allow:ve(I.allow),deny:ve(I.deny)}}catch{}if(d.allow.length===0&&d.deny.length===0){let B=ve(o.allowed_tools),I=ve(o.blocked_tools);(B.length>0||I.length>0)&&(d={allow:B,deny:I},this.warnedLegacyPerms.has(i)||(this.warnedLegacyPerms.add(i),console.warn(`Agent Fleet: "${i}" still uses legacy allowed_tools/blocked_tools in config.md. Permission rules now live in permissions.json. Open this agent in Edit and Save to migrate.`)))}let p="",m=(0,C.normalizePath)(`${t}/SKILLS.md`),f=this.vault.getAbstractFileByPath(m);if(f instanceof C.TFile){let B=await this.vault.cachedRead(f);p=ne(B).body}let y="",k=(0,C.normalizePath)(`${t}/CONTEXT.md`),b=this.vault.getAbstractFileByPath(k);if(b instanceof C.TFile){let B=await this.vault.cachedRead(b);y=ne(B).body}let v=!1,g="",x="",T=!0,_="",R=(0,C.normalizePath)(`${t}/HEARTBEAT.md`),A=this.vault.getAbstractFileByPath(R);if(A instanceof C.TFile){let B=await this.vault.cachedRead(A),I=ne(B);v=Je(I.frontmatter.enabled,!1),g=M(I.frontmatter.schedule)??"",T=Je(I.frontmatter.notify,!0),_=M(I.frontmatter.channel)??"",x=I.body}let S=M(a.model),L=M(o.model);S&&L&&S!==L&&(this.warnedFolderAgentModelConflict.has(i)||(this.warnedFolderAgentModelConflict.add(i),console.warn(`Agent Fleet: "${i}" has conflicting model fields \u2014 agent.md says "${S}", config.md says "${L}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let E=L??S??this.settings.defaultModel;return{filePath:e.path,name:i,description:M(a.description),model:E,adapter:M(o.adapter)??"claude-code",permissionMode:M(o.permission_mode)??"bypassPermissions",effort:M(o.effort),maxRetries:$e(o.max_retries,1),skills:ve(a.skills),mcpServers:ve(a.mcp_servers),cwd:M(o.cwd)||M(a.cwd),enabled:Je(a.enabled,!0),timeout:$e(o.timeout,$e(a.timeout,300)),approvalRequired:ve(o.approval_required),memory:Je(o.memory,Je(a.memory,!1)),memoryMaxEntries:$e(o.memory_max_entries,100),autoCompactThreshold:$e(o.auto_compact_threshold??a.auto_compact_threshold,85),tags:ve(a.tags),avatar:M(a.avatar)??"",body:n,contextBody:y,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:v,heartbeatSchedule:g,heartbeatBody:x,heartbeatNotify:T,heartbeatChannel:_,wikiKeeper:this.parseWikiKeeperConfig(o.wiki_keeper??a.wiki_keeper),wikiReferences:this.parseWikiReferences(o.wiki_references??a.wiki_references)}}parseWikiReferences(t){if(!Array.isArray(t))return;let e=[];for(let s of t)if(typeof s=="string"&&s.trim())e.push({agent:s.trim()});else if(s&&typeof s=="object"){let a=s.agent;typeof a=="string"&&a.trim()&&e.push({agent:a.trim()})}return e.length>0?e:void 0}parseWikiKeeperConfig(t){if(!t||typeof t!="object")return;let e=t;return{scopeRoot:M(e.scope_root)??"",inboxPath:M(e.inbox_path)??"_sources/inbox",archivePath:M(e.archive_path)??"_sources/archive",failedPath:M(e.failed_path)??"_sources/failed",topicsRoot:M(e.topics_root)??"_topics",indexPath:M(e.index_path)??"index.md",logPath:M(e.log_path)??"log.md",watchedFolders:ve(e.watched_folders),excludePatterns:ve(e.exclude_patterns),watchedSince:M(e.watched_since)??"",fileSubstantiveAnswers:Je(e.file_substantive_answers,!0),obsidianUrlScheme:Je(e.obsidian_url_scheme,!0),maxTokensPerIngest:$e(e.max_tokens_per_ingest,6e4),maxTokensPerRefresh:$e(e.max_tokens_per_refresh,3e4),indexSplitThreshold:$e(e.index_split_threshold,100),dedupSimilarityThreshold:$e(e.dedup_similarity_threshold,.82),summaryStaleDays:$e(e.summary_stale_days,30),stateFile:M(e.state_file)??".wiki-keeper-state.json"}}removeFile(t){this.clearStoredFile(t)}getSnapshot(){return{agents:Array.from(this.agents.values()).sort((t,e)=>t.name.localeCompare(e.name)),skills:Array.from(this.skills.values()).sort((t,e)=>t.name.localeCompare(e.name)),tasks:Array.from(this.tasks.values()).sort((t,e)=>t.taskId.localeCompare(e.taskId)),channels:Array.from(this.channels.values()).sort((t,e)=>t.name.localeCompare(e.name)),validationIssues:Array.from(this.validationIssues.values()).flat()}}getAgentByName(t){return Array.from(this.agents.values()).find(e=>e.name===t)}getSkillByName(t){return Array.from(this.skills.values()).find(e=>e.name===t)}getTaskById(t){return Array.from(this.tasks.values()).find(e=>e.taskId===t)}getTasksForAgent(t){return Array.from(this.tasks.values()).filter(e=>e.agent===t)}getChannelByName(t){return Array.from(this.channels.values()).find(e=>e.name===t)}getChannelsForAgent(t){return Array.from(this.channels.values()).filter(e=>e.defaultAgent===t)}getRunsRoot(){return this.getSubfolder("runs")}getMemoryPath(t){return(0,C.normalizePath)(`${this.getSubfolder("memory")}/${fe(t)}.md`)}getConversationsDir(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,C.normalizePath)(`${s}/conversations`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,C.normalizePath)(`${e}/${t.name}-conversations`)}getLegacyChatPath(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,C.normalizePath)(`${s}/chat.json`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,C.normalizePath)(`${e}/${t.name}-chat.json`)}getConversationPath(t,e){if(e==="default")return this.getLegacyChatPath(t);let s=this.getConversationsDir(t),a=fe(e)||"conversation";return(0,C.normalizePath)(`${s}/${a}.json`)}async listConversations(t){let e=[],s=this.getLegacyChatPath(t),a=this.vault.getAbstractFileByPath(s);if(a instanceof C.TFile){let o=await this.readConversationMeta(a,"default",!0);o&&e.push(o)}let n=this.getConversationsDir(t),i=this.vault.getAbstractFileByPath(n);if(i instanceof C.TFolder)for(let o of i.children){if(!(o instanceof C.TFile)||o.extension!=="json")continue;let c=o.basename,l=await this.readConversationMeta(o,c,!1);l&&e.push(l)}return e.sort((o,c)=>c.lastActive.localeCompare(o.lastActive)),e}async readConversationMeta(t,e,s){try{let a=await this.vault.cachedRead(t),n=JSON.parse(a);return{id:e,name:n.name?.trim()||(s?"Main chat":"Untitled"),lastActive:n.lastActive??new Date(t.stat.mtime).toISOString(),messageCount:Array.isArray(n.messages)?n.messages.length:0,isLegacy:s}}catch{return null}}async createConversation(t,e,s){if(e==="default")throw new Error('Cannot create a conversation with the reserved id "default"');let a=this.getConversationPath(t,e);if(this.vault.getAbstractFileByPath(a)instanceof C.TFile)return;let i=a.replace(/\/[^/]+$/,"");await this.ensureFolder(i);let o=new Date().toISOString(),c={sessionId:null,messages:[],lastActive:o,createdAt:o,name:s.trim()};await this.vault.create(a,JSON.stringify(c,null,2))}async renameConversation(t,e,s){let a=this.getConversationPath(t,e),n=this.vault.getAbstractFileByPath(a);if(!(n instanceof C.TFile))return;let i=await this.vault.cachedRead(n),o;try{o=JSON.parse(i)}catch{return}o.name=s.trim(),await this.vault.modify(n,JSON.stringify(o,null,2))}async deleteConversation(t,e){if(e==="default")throw new Error("The default (legacy) conversation can't be deleted from the registry; use the chat view's New Chat instead.");let s=this.getConversationPath(t,e),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFile&&await this.vault.delete(a)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof C.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ne(a);return{filePath:e,agent:M(n.agent)??t,lastUpdated:M(n.last_updated),body:i}}async appendMemory(t,e){if(e.length===0)return;let s=this.getMemoryPath(t),a=this.vault.getAbstractFileByPath(s),n=new Date().toISOString(),i=e.map(o=>`- ${o.trim()}`).join(` `);if(a instanceof C.TFile){let c=`${(await this.getMemory(t))?.body.trim()||"## Learned Context"} ${i}`.trim();await this.vault.modify(a,J({agent:t,last_updated:n},c));return}await this.createFileIfMissing(s,J({agent:t,last_updated:n},`## Learned Context -${i}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof C.TFolder))return[];let s=[];this.collectMarkdownChildren(e,s),s.sort((i,o)=>o.path.localeCompare(i.path));let a=s.slice(0,t),n=[];for(let i of a){let o=await this.readRunLog(i);o&&n.push(o)}return n.sort((i,o)=>o.started.localeCompare(i.started))}async listRunsSince(t){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof C.TFolder))return[];let s=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,a=[];for(let i of e.children)i instanceof C.TFolder&&(i.nameo.started.localeCompare(i.started))}async readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:a}=ne(e),n=a.match(/## Prompt\n([\s\S]*?)(?:\n## Result\n|\n## Output\n|$)/),i=a.match(/## Result\n([\s\S]*?)(?:\n## Output\n|$)/),o=a.match(/## Output\n([\s\S]*?)(?:\n## Tools Used\n|$)/),c=a.match(/## Tools Used\n([\s\S]*?)(?:\n## STDERR\n|$)/);return{filePath:t.path,runId:M(s.run_id)??t.basename,agent:M(s.agent)??"unknown",task:M(s.task)??"unknown",status:M(s.status)??"failure",started:M(s.started)??new Date(t.stat.ctime).toISOString(),completed:M(s.completed),durationSeconds:$e(s.duration_seconds,0),tokensUsed:typeof s.tokens_used=="number"?s.tokens_used:void 0,costUsd:typeof s.cost_usd=="number"?s.cost_usd:void 0,model:M(s.model)??ct.defaultModel,modelSource:(()=>{let l=M(s.model_source);if(l==="task"||l==="agent"||l==="settings"||l==="cli-default")return l})(),concreteModel:M(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:ve(s.tags),prompt:n?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:i?.[1]?.trim()||void 0,toolsUsed:c?.[1]?fe(c[1]).map(l=>l.replace(/^- /,"").trim()).filter(Boolean):[],approvals:this.parseApprovals(s.approvals)}}async writeRunLog(t){let e=new Date(t.started),s=(0,C.normalizePath)(`${this.getRunsRoot()}/${e.toISOString().slice(0,10)}`);await this.ensureFolder(s);let a=`${e.toISOString().slice(11,19).replace(/:/g,"")}-${me(t.agent)}-${me(t.task)}.md`,n=(0,C.normalizePath)(`${s}/${a}`),i=J({run_id:t.runId,agent:t.agent,task:t.task,status:t.status,started:t.started,completed:t.completed,duration_seconds:t.durationSeconds,tokens_used:t.tokensUsed,cost_usd:t.costUsd,model:t.model,model_source:t.modelSource,resolved_concrete_model:t.concreteModel,exit_code:t.exitCode,tags:t.tags,approvals:t.approvals},["## Prompt","",t.prompt.trim(),"",...t.finalResult&&t.finalResult.trim()?["## Result","",t.finalResult.trim(),""]:[],"## Output","",t.output.trim()||"(no output)","","## Tools Used","",...t.toolsUsed.length>0?t.toolsUsed.map(c=>`- ${c}`):["- none"],...t.stderr?["","## STDERR","",t.stderr.trim()]:[]].join(` -`)),o=this.vault.getAbstractFileByPath(n);return o instanceof C.TFile?await this.vault.modify(o,i):await this.vault.create(n,i),n}async updateTaskRunMetadata(t,e){let s=this.vault.getAbstractFileByPath(t.filePath);if(!(s instanceof C.TFile))return;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ne(a),o={...n,last_run:e.lastRun??t.lastRun,next_run:e.nextRun??t.nextRun,run_count:e.runCount??t.runCount};await this.vault.modify(s,J(o,i)),await this.loadFile(s)}async setApprovalDecision(t,e,s){let a=this.vault.getAbstractFileByPath(t);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n),c=(this.parseApprovals(i.approvals)??[]).map(l=>l.tool===e?{...l,status:s,resolvedAt:new Date().toISOString()}:l);await this.vault.modify(a,J({...i,approvals:c},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),me(t)),s=`--- -name: ${me(t)} +${i}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof C.TFolder))return[];let s=[];this.collectMarkdownChildren(e,s),s.sort((i,o)=>o.path.localeCompare(i.path));let a=s.slice(0,t),n=[];for(let i of a){let o=await this.readRunLog(i);o&&n.push(o)}return n.sort((i,o)=>o.started.localeCompare(i.started))}async listRunsSince(t){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof C.TFolder))return[];let s=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,a=[];for(let i of e.children)i instanceof C.TFolder&&(i.nameo.started.localeCompare(i.started))}async readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:a}=ne(e),n=a.match(/## Prompt\n([\s\S]*?)(?:\n## Result\n|\n## Output\n|$)/),i=a.match(/## Result\n([\s\S]*?)(?:\n## Output\n|$)/),o=a.match(/## Output\n([\s\S]*?)(?:\n## Tools Used\n|$)/),c=a.match(/## Tools Used\n([\s\S]*?)(?:\n## STDERR\n|$)/);return{filePath:t.path,runId:M(s.run_id)??t.basename,agent:M(s.agent)??"unknown",task:M(s.task)??"unknown",status:M(s.status)??"failure",started:M(s.started)??new Date(t.stat.ctime).toISOString(),completed:M(s.completed),durationSeconds:$e(s.duration_seconds,0),tokensUsed:typeof s.tokens_used=="number"?s.tokens_used:void 0,costUsd:typeof s.cost_usd=="number"?s.cost_usd:void 0,model:M(s.model)??lt.defaultModel,modelSource:(()=>{let l=M(s.model_source);if(l==="task"||l==="agent"||l==="settings"||l==="cli-default")return l})(),concreteModel:M(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:ve(s.tags),prompt:n?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:i?.[1]?.trim()||void 0,toolsUsed:c?.[1]?me(c[1]).map(l=>l.replace(/^- /,"").trim()).filter(Boolean):[],approvals:this.parseApprovals(s.approvals)}}async writeRunLog(t){let e=new Date(t.started),s=(0,C.normalizePath)(`${this.getRunsRoot()}/${e.toISOString().slice(0,10)}`);await this.ensureFolder(s);let a=`${e.toISOString().slice(11,19).replace(/:/g,"")}-${fe(t.agent)}-${fe(t.task)}.md`,n=(0,C.normalizePath)(`${s}/${a}`),i=J({run_id:t.runId,agent:t.agent,task:t.task,status:t.status,started:t.started,completed:t.completed,duration_seconds:t.durationSeconds,tokens_used:t.tokensUsed,cost_usd:t.costUsd,model:t.model,model_source:t.modelSource,resolved_concrete_model:t.concreteModel,exit_code:t.exitCode,tags:t.tags,approvals:t.approvals},["## Prompt","",t.prompt.trim(),"",...t.finalResult&&t.finalResult.trim()?["## Result","",t.finalResult.trim(),""]:[],"## Output","",t.output.trim()||"(no output)","","## Tools Used","",...t.toolsUsed.length>0?t.toolsUsed.map(c=>`- ${c}`):["- none"],...t.stderr?["","## STDERR","",t.stderr.trim()]:[]].join(` +`)),o=this.vault.getAbstractFileByPath(n);return o instanceof C.TFile?await this.vault.modify(o,i):await this.vault.create(n,i),n}async updateTaskRunMetadata(t,e){let s=this.vault.getAbstractFileByPath(t.filePath);if(!(s instanceof C.TFile))return;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ne(a),o={...n,last_run:e.lastRun??t.lastRun,next_run:e.nextRun??t.nextRun,run_count:e.runCount??t.runCount};await this.vault.modify(s,J(o,i)),await this.loadFile(s)}async setApprovalDecision(t,e,s){let a=this.vault.getAbstractFileByPath(t);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n),c=(this.parseApprovals(i.approvals)??[]).map(l=>l.tool===e?{...l,status:s,resolvedAt:new Date().toISOString()}:l);await this.vault.modify(a,J({...i,approvals:c},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),fe(t)),s=`--- +name: ${fe(t)} description: enabled: true skills: [] @@ -11679,15 +11679,15 @@ tags: [] --- Agent instructions go here. -`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=me(t.name),s=(0,C.normalizePath)(`${this.getSubfolder("agents")}/${e}`);await this.ensureFolder(s);let a={name:t.name,description:t.description||void 0,avatar:t.avatar||void 0,enabled:t.enabled??!0,tags:t.tags,skills:t.skills,mcp_servers:t.mcpServers?.length?t.mcpServers:void 0};t.model&&t.model!=="default"&&(a.model=t.model);let n=(0,C.normalizePath)(`${s}/agent.md`);await this.vault.create(n,J(a,t.systemPrompt||""));let i={model:t.model||"default",adapter:t.adapter||"claude-code",timeout:t.timeout,max_retries:1,cwd:t.cwd||"",permission_mode:t.permissionMode||"bypassPermissions",effort:t.effort||void 0,approval_required:t.approvalRequired,memory:t.memory,memory_max_entries:t.memoryMaxEntries};typeof t.autoCompactThreshold=="number"&&(i.auto_compact_threshold=t.autoCompactThreshold),t.wikiReferences&&t.wikiReferences.length>0&&(i.wiki_references=t.wikiReferences.map(h=>({agent:h})));let o=(0,C.normalizePath)(`${s}/config.md`);await this.vault.create(o,J(i,""));let c=(0,C.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(c,J({},t.skillsBody||""));let l=(0,C.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(l,J({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,C.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+` -`)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),me(t)),s=`--- -name: ${me(t)} +`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=fe(t.name),s=(0,C.normalizePath)(`${this.getSubfolder("agents")}/${e}`);await this.ensureFolder(s);let a={name:t.name,description:t.description||void 0,avatar:t.avatar||void 0,enabled:t.enabled??!0,tags:t.tags,skills:t.skills,mcp_servers:t.mcpServers?.length?t.mcpServers:void 0};t.model&&t.model!=="default"&&(a.model=t.model);let n=(0,C.normalizePath)(`${s}/agent.md`);await this.vault.create(n,J(a,t.systemPrompt||""));let i={model:t.model||"default",adapter:t.adapter||"claude-code",timeout:t.timeout,max_retries:1,cwd:t.cwd||"",permission_mode:t.permissionMode||"bypassPermissions",effort:t.effort||void 0,approval_required:t.approvalRequired,memory:t.memory,memory_max_entries:t.memoryMaxEntries};typeof t.autoCompactThreshold=="number"&&(i.auto_compact_threshold=t.autoCompactThreshold),t.wikiReferences&&t.wikiReferences.length>0&&(i.wiki_references=t.wikiReferences.map(h=>({agent:h})));let o=(0,C.normalizePath)(`${s}/config.md`);await this.vault.create(o,J(i,""));let c=(0,C.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(c,J({},t.skillsBody||""));let l=(0,C.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(l,J({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,C.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+` +`)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),fe(t)),s=`--- +name: ${fe(t)} description: tags: [] --- Skill instructions go here. -`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,C.normalizePath)(`${this.getSubfolder("skills")}/${me(t.name)}`);await this.ensureFolder(e);let s={name:t.name,description:t.description||void 0,tags:t.tags.length>0?t.tags:void 0},a=(0,C.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(a,J(s,t.body||"Skill instructions go here.")),t.toolsBody){let n=(0,C.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(n,`# Tools +`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,C.normalizePath)(`${this.getSubfolder("skills")}/${fe(t.name)}`);await this.ensureFolder(e);let s={name:t.name,description:t.description||void 0,tags:t.tags.length>0?t.tags:void 0},a=(0,C.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(a,J(s,t.body||"Skill instructions go here.")),t.toolsBody){let n=(0,C.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(n,`# Tools ${t.toolsBody}`)}if(t.referencesBody){let n=(0,C.normalizePath)(`${e}/references.md`);await this.createFileIfMissing(n,`# References @@ -11707,7 +11707,7 @@ ${e.referencesBody}`))}if(e.examplesBody!==void 0){let i=(0,C.normalizePath)(`${ ${e.examplesBody}`):await this.vault.create(i,`# Examples -${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);e.description!==void 0&&(i.description=e.description||void 0),e.tags!==void 0&&(i.tags=e.tags.length>0?e.tags:void 0);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,c))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,C.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}else await this.trashFile(e.filePath)}async deleteTask(t){let e=this.getTaskById(t);e&&await this.trashFile(e.filePath)}async updateChannel(t,e){let s=this.getChannelByName(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);e.default_agent!==void 0&&(i.default_agent=e.default_agent,delete i.agent),e.allowed_agents!==void 0&&(i.allowed_agents=e.allowed_agents),e.enabled!==void 0&&(i.enabled=e.enabled),e.credential_ref!==void 0&&(i.credential_ref=e.credential_ref),e.allowed_users!==void 0&&(i.allowed_users=e.allowed_users),e.per_user_sessions!==void 0&&(i.per_user_sessions=e.per_user_sessions),e.channel_context!==void 0&&(i.channel_context=e.channel_context||void 0),e.tags!==void 0&&(i.tags=e.tags),e.type!==void 0&&(i.type=e.type),e.transport!==void 0&&(i.transport=e.transport);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,c))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,C.normalizePath)(`${this.getSubfolder("channels")}/${me(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,C.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,C.normalizePath)(`${a}/HEARTBEAT.md`),i=this.vault.getAbstractFileByPath(n);if(i instanceof C.TFile){let o=await this.vault.cachedRead(i),{frontmatter:c,body:l}=ne(o);e.enabled!==void 0&&(c.enabled=e.enabled),e.schedule!==void 0&&(c.schedule=e.schedule||void 0),e.notify!==void 0&&(c.notify=e.notify),e.channel!==void 0&&(c.channel=e.channel||void 0);let d=e.body!==void 0?e.body:l;await this.vault.modify(i,J(c,d))}else{let o={enabled:e.enabled??!1};e.schedule&&(o.schedule=e.schedule),e.notify!==void 0&&(o.notify=e.notify),e.channel&&(o.channel=e.channel);let c=e.body??"";await this.vault.create(n,J(o,c))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let i=(0,C.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFolder){let c=[];this.collectMarkdownChildren(o,c);for(let l of c)s.push(l.path);await this.vault.trash(o,!0)}}else await this.trashFile(a.filePath),s.push(a.filePath);let n=this.getMemoryPath(t);if(this.vault.getAbstractFileByPath(n)&&(await this.trashFile(n),s.push(n)),e){let i=this.getTasksForAgent(t);for(let o of i)await this.trashFile(o.filePath),s.push(o.filePath)}return{trashedFiles:s}}async trashFile(t){let e=this.vault.getAbstractFileByPath(t);e&&await this.vault.trash(e,!0)}async ensureFolder(t){if(!this.vault.getAbstractFileByPath(t))try{await this.vault.createFolder(t)}catch(e){if(!(e instanceof Error?e.message:String(e)).includes("Folder already exists"))throw e}}async getAvailablePath(t,e){let s=0;for(;;){let a=s===0?"":`-${s+1}`,n=(0,C.normalizePath)(`${t}/${e}${a}.md`);if(!this.vault.getAbstractFileByPath(n))return n;s+=1}}async createFileIfMissing(t,e){if(!this.vault.getAbstractFileByPath(t))try{await this.vault.create(t,e)}catch(s){if(!(s instanceof Error?s.message:String(s)).includes("File already exists"))throw s}}collectMarkdownChildren(t,e){for(let s of t.children)s instanceof C.TFile&&s.extension==="md"&&e.push(s),s instanceof C.TFolder&&this.collectMarkdownChildren(s,e)}clearStoredFile(t){this.agents.delete(t),this.skills.delete(t),this.tasks.delete(t),this.channels.delete(t),this.validationIssues.delete(t)}setIssue(t,e){let s=this.validationIssues.get(t)??[];s.push({path:t,message:e}),this.validationIssues.set(t,s)}parseFile(t,e){return t.startsWith(`${this.getSubfolder("agents")}/`)?this.parseAgent(t,e):t.startsWith(`${this.getSubfolder("skills")}/`)?this.parseSkill(t,e):t.startsWith(`${this.getSubfolder("tasks")}/`)?this.parseTask(t,e):null}parseAgent(t,e){let{frontmatter:s,body:a}=ne(e);if(!xs(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=M(s.name),i=M(s.model)??this.settings.defaultModel;if(!n||!i)return this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null;let o=ve(s.allowed_tools),c=ve(s.blocked_tools);return(o.length>0||c.length>0)&&!this.warnedLegacyPerms.has(n)&&(this.warnedLegacyPerms.add(n),console.warn(`Agent Fleet: "${n}" still uses legacy allowed_tools/blocked_tools in its frontmatter. Permission rules now live in .permissions.json beside the .md. Open Edit and Save to migrate.`)),{filePath:t,name:n,description:M(s.description),model:i,adapter:M(s.adapter)??"claude-code",permissionMode:M(s.permission_mode)??"bypassPermissions",effort:M(s.effort),maxRetries:$e(s.max_retries,1),skills:ve(s.skills),mcpServers:ve(s.mcp_servers),cwd:M(s.cwd),enabled:Je(s.enabled,!0),timeout:$e(s.timeout,300),approvalRequired:ve(s.approval_required),memory:Je(s.memory,!1),memoryMaxEntries:$e(s.memory_max_entries,100),autoCompactThreshold:$e(s.auto_compact_threshold,85),tags:ve(s.tags),avatar:M(s.avatar)??"",body:a,contextBody:"",skillsBody:"",env:this.parseEnvMap(s.env),permissionRules:{allow:o,deny:c},isFolder:!1,heartbeatEnabled:!1,heartbeatSchedule:"",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""}}parseSkill(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.name);return n?{filePath:t,name:n,description:M(s.description),tags:ve(s.tags),body:a,toolsBody:"",referencesBody:"",examplesBody:"",isFolder:!1}:(this.setIssue(t,"Skill requires string field `name`."),null)}parseTask(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.task_id),i=M(s.agent),o=M(s.type);if(!n||!i||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!M(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!M(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let c=M(s.priority),d=c&&["low","medium","high","critical"].includes(c)?c:"medium";return{filePath:t,taskId:n,agent:i,schedule:M(s.schedule),runAt:M(s.run_at),type:o,priority:d,enabled:Je(s.enabled,!0),created:M(s.created)??new Date().toISOString(),lastRun:M(s.last_run),nextRun:M(s.next_run),runCount:$e(s.run_count,0),catchUp:Je(s.catch_up,this.settings.catchUpMissedTasks),effort:M(s.effort),model:M(s.model),tags:ve(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let i=M(s.type),o=["slack","telegram"];if(!i||!o.includes(i))return this.setIssue(t,`Channel \`${n}\` requires \`type\` to be one of: ${o.join(", ")}.`),null;let c=i,l=M(s.default_agent)??M(s.agent);if(!l)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=ve(s.allowed_agents),h=M(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=xs(s.transport)?s.transport:{};return{filePath:t,name:n,type:c,defaultAgent:l,allowedAgents:d,enabled:Je(s.enabled,!0),credentialRef:h,allowedUsers:ve(s.allowed_users),perUserSessions:Je(s.per_user_sessions,!0),channelContext:M(s.channel_context)??"",transport:u,tags:ve(s.tags),body:a}}validateReferences(){let t=new Set;for(let i of this.skills.values())t.has(i.name)&&this.setIssue(i.filePath,`Duplicate skill name \`${i.name}\`.`),t.add(i.name);let e=new Set;for(let i of this.agents.values()){e.has(i.name)&&this.setIssue(i.filePath,`Duplicate agent name \`${i.name}\`.`),e.add(i.name);for(let o of i.skills)t.has(o)||this.setIssue(i.filePath,`Agent references missing skill \`${o}\`.`)}for(let i of this.tasks.values())e.has(i.agent)||this.setIssue(i.filePath,`Task references missing agent \`${i.agent}\`.`);let s=new Set,a=new Map;for(let i of this.agents.values())a.set(i.name,i);let n=this.channelCredentialGetter?.()??this.settings.channelCredentials??{};for(let i of this.channels.values()){s.has(i.name)&&this.setIssue(i.filePath,`Duplicate channel name \`${i.name}\`.`),s.add(i.name);let o=a.get(i.defaultAgent);o?o.approvalRequired.length>0&&this.setIssue(i.filePath,`Channel \`${i.name}\` cannot bind to agent \`${o.name}\` because the agent has \`approval_required\` set (${o.approvalRequired.join(", ")}). Clear the approval list on the agent or pick a different agent.`):this.setIssue(i.filePath,`Channel \`${i.name}\` references missing agent \`${i.defaultAgent}\`.`);let c=n[i.credentialRef];c?c.type!==i.type&&this.setIssue(i.filePath,`Channel \`${i.name}\` is type \`${i.type}\` but credential \`${i.credentialRef}\` is type \`${c.type}\`.`):this.setIssue(i.filePath,`Channel \`${i.name}\` references missing credential \`${i.credentialRef}\`. Add it under Settings \u2192 Channel Credentials.`)}}parseEnvMap(t){if(!xs(t))return{};let e={};for(let[s,a]of Object.entries(t))typeof a=="string"?e[s]=a:(typeof a=="number"||typeof a=="boolean")&&(e[s]=String(a));return e}parseApprovals(t){if(Array.isArray(t))return t.flatMap(e=>{if(!xs(e)||!M(e.tool))return[];let s=M(e.tool);return s?[{tool:s,command:M(e.command),reason:M(e.reason),status:M(e.status)??"pending",resolvedAt:M(e.resolvedAt),note:M(e.note)}]:[]})}};var _t=require("obsidian"),Ss=class extends _t.Modal{constructor(e,s,a){super(e);this.info=s;this.onConfirm=a}deleteTasks=!0;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-confirm-delete-modal");let s=e.createDiv({cls:"af-delete-header"}),a=s.createSpan({cls:"af-delete-header-icon"});(0,_t.setIcon)(a,"alert-triangle"),s.createEl("h3",{text:`Delete agent "${this.info.agentName}"?`});let n=e.createDiv({cls:"af-delete-summary"});n.createDiv({text:"This action will:"});let i=n.createEl("ul",{cls:"af-delete-impact-list"});i.createEl("li",{text:"Move the agent definition to trash"}),this.info.hasMemory&&i.createEl("li",{text:"Move the agent's memory file to trash"}),this.info.taskCount>0&&i.createEl("li").setText(`${this.info.taskCount} task${this.info.taskCount!==1?"s":""} reference this agent`),this.info.runCount>0&&i.createEl("li",{cls:"af-delete-preserved"}).setText(`${this.info.runCount} run log${this.info.runCount!==1?"s":""} will be preserved`),e.createDiv({cls:"af-delete-note",text:"Files are moved to your system trash and can be recovered."}),this.info.taskCount>0&&new _t.Setting(e).setName("Also delete associated tasks").setDesc(`Delete ${this.info.taskCount} task${this.info.taskCount!==1?"s":""} that reference this agent`).addToggle(h=>{h.setValue(this.deleteTasks),h.onChange(u=>{this.deleteTasks=u})});let o=e.createDiv({cls:"af-delete-actions"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{cls:"af-delete-confirm-btn",text:"Delete Agent"}),d=l.createSpan({cls:"af-delete-btn-icon"});(0,_t.setIcon)(d,"trash-2"),l.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var N=require("obsidian");var cn=[{value:"opus",label:"opus \u2014 latest Opus"},{value:"sonnet",label:"sonnet \u2014 latest Sonnet"},{value:"haiku",label:"haiku \u2014 latest Haiku"},{value:"opusplan",label:"opusplan \u2014 plan-mode Opus"}],Cs="__custom__";function or(r){let t=r.trim();return!t||t==="default"||t==="subscription"?"inherit":cn.some(e=>e.value===t)?"alias":"custom"}function At(r,t){r.empty(),r.addClass("af-model-picker");let e=or(t.value),s=r.createEl("select",{cls:"af-form-select af-mp-select"}),a=t.allowInherit?t.inheritPlaceholder??"Inherit from agent":"Default (let Claude Code pick)";s.createEl("option",{text:a,attr:{value:""}});let n=s.createEl("optgroup",{attr:{label:"Aliases (any backend)"}});for(let o of cn)n.createEl("option",{text:o.label,attr:{value:o.value}});s.createEl("option",{text:"Custom\u2026",attr:{value:Cs}});let i=r.createEl("input",{cls:"af-form-input af-mp-custom-input",attr:{type:"text",placeholder:"e.g. claude-opus-4-7 \xB7 us.anthropic.claude-opus-4-7 \xB7 claude-opus-4-7@20251101",spellcheck:"false"}});e==="inherit"?(s.value="",i.value="",i.style.display="none"):e==="alias"?(s.value=t.value.trim(),i.value="",i.style.display="none"):(s.value=Cs,i.value=t.value.trim(),i.style.display=""),s.addEventListener("change",()=>{s.value===Cs?(i.style.display="",i.focus(),t.onChange(i.value.trim())):(i.style.display="none",t.onChange(s.value))}),i.addEventListener("input",()=>{s.value===Cs&&t.onChange(i.value.trim())})}var le=require("obsidian");function lr(){let r=new Date,t=r.getFullYear(),e=String(r.getMonth()+1).padStart(2,"0"),s=String(r.getDate()).padStart(2,"0");return`${t}-${e}-${s}`}var cr="0 3 * * *",dr="0 9 * * 0";function ma(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:lr(),heartbeatChannel:"",fileSubstantiveAnswers:!0,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,maxTokensPerRefresh:3e4,indexSplitThreshold:100,dedupSimilarityThreshold:.82,summaryStaleDays:30,failedPath:"_sources/failed",stateFile:".wiki-keeper-state.json"}}var un=ma();function fa(r){let t=me(r).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function hr(r,t){let e=t||"the whole vault",s={name:r,description:`Cultivates ${e} as a self-maintaining wiki. Ingests new sources, extracts durable claims from watched folders, answers questions with citations, and periodically lints.`,avatar:"library",enabled:!0,skills:["wiki-ingest","wiki-query","wiki-lint","wiki-refresh"],tags:t?["wiki-keeper",`scope:${me(t)}`]:["wiki-keeper"]},a=`You are the Wiki Keeper for the \`${e}\` scope. Maintain it as a persistent, interlinked knowledge base (Karpathy's "LLM wiki" pattern). +${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);e.description!==void 0&&(i.description=e.description||void 0),e.tags!==void 0&&(i.tags=e.tags.length>0?e.tags:void 0);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,c))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,C.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}else await this.trashFile(e.filePath)}async deleteTask(t){let e=this.getTaskById(t);e&&await this.trashFile(e.filePath)}async updateChannel(t,e){let s=this.getChannelByName(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);e.default_agent!==void 0&&(i.default_agent=e.default_agent,delete i.agent),e.allowed_agents!==void 0&&(i.allowed_agents=e.allowed_agents),e.enabled!==void 0&&(i.enabled=e.enabled),e.credential_ref!==void 0&&(i.credential_ref=e.credential_ref),e.allowed_users!==void 0&&(i.allowed_users=e.allowed_users),e.per_user_sessions!==void 0&&(i.per_user_sessions=e.per_user_sessions),e.channel_context!==void 0&&(i.channel_context=e.channel_context||void 0),e.tags!==void 0&&(i.tags=e.tags),e.type!==void 0&&(i.type=e.type),e.transport!==void 0&&(i.transport=e.transport);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,c))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,C.normalizePath)(`${this.getSubfolder("channels")}/${fe(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,C.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,C.normalizePath)(`${a}/HEARTBEAT.md`),i=this.vault.getAbstractFileByPath(n);if(i instanceof C.TFile){let o=await this.vault.cachedRead(i),{frontmatter:c,body:l}=ne(o);e.enabled!==void 0&&(c.enabled=e.enabled),e.schedule!==void 0&&(c.schedule=e.schedule||void 0),e.notify!==void 0&&(c.notify=e.notify),e.channel!==void 0&&(c.channel=e.channel||void 0);let d=e.body!==void 0?e.body:l;await this.vault.modify(i,J(c,d))}else{let o={enabled:e.enabled??!1};e.schedule&&(o.schedule=e.schedule),e.notify!==void 0&&(o.notify=e.notify),e.channel&&(o.channel=e.channel);let c=e.body??"";await this.vault.create(n,J(o,c))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let i=(0,C.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFolder){let c=[];this.collectMarkdownChildren(o,c);for(let l of c)s.push(l.path);await this.vault.trash(o,!0)}}else await this.trashFile(a.filePath),s.push(a.filePath);let n=this.getMemoryPath(t);if(this.vault.getAbstractFileByPath(n)&&(await this.trashFile(n),s.push(n)),e){let i=this.getTasksForAgent(t);for(let o of i)await this.trashFile(o.filePath),s.push(o.filePath)}return{trashedFiles:s}}async trashFile(t){let e=this.vault.getAbstractFileByPath(t);e&&await this.vault.trash(e,!0)}async ensureFolder(t){if(!this.vault.getAbstractFileByPath(t))try{await this.vault.createFolder(t)}catch(e){if(!(e instanceof Error?e.message:String(e)).includes("Folder already exists"))throw e}}async getAvailablePath(t,e){let s=0;for(;;){let a=s===0?"":`-${s+1}`,n=(0,C.normalizePath)(`${t}/${e}${a}.md`);if(!this.vault.getAbstractFileByPath(n))return n;s+=1}}async createFileIfMissing(t,e){if(!this.vault.getAbstractFileByPath(t))try{await this.vault.create(t,e)}catch(s){if(!(s instanceof Error?s.message:String(s)).includes("File already exists"))throw s}}collectMarkdownChildren(t,e){for(let s of t.children)s instanceof C.TFile&&s.extension==="md"&&e.push(s),s instanceof C.TFolder&&this.collectMarkdownChildren(s,e)}clearStoredFile(t){this.agents.delete(t),this.skills.delete(t),this.tasks.delete(t),this.channels.delete(t),this.validationIssues.delete(t)}setIssue(t,e){let s=this.validationIssues.get(t)??[];s.push({path:t,message:e}),this.validationIssues.set(t,s)}parseFile(t,e){return t.startsWith(`${this.getSubfolder("agents")}/`)?this.parseAgent(t,e):t.startsWith(`${this.getSubfolder("skills")}/`)?this.parseSkill(t,e):t.startsWith(`${this.getSubfolder("tasks")}/`)?this.parseTask(t,e):null}parseAgent(t,e){let{frontmatter:s,body:a}=ne(e);if(!xs(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=M(s.name),i=M(s.model)??this.settings.defaultModel;if(!n||!i)return this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null;let o=ve(s.allowed_tools),c=ve(s.blocked_tools);return(o.length>0||c.length>0)&&!this.warnedLegacyPerms.has(n)&&(this.warnedLegacyPerms.add(n),console.warn(`Agent Fleet: "${n}" still uses legacy allowed_tools/blocked_tools in its frontmatter. Permission rules now live in .permissions.json beside the .md. Open Edit and Save to migrate.`)),{filePath:t,name:n,description:M(s.description),model:i,adapter:M(s.adapter)??"claude-code",permissionMode:M(s.permission_mode)??"bypassPermissions",effort:M(s.effort),maxRetries:$e(s.max_retries,1),skills:ve(s.skills),mcpServers:ve(s.mcp_servers),cwd:M(s.cwd),enabled:Je(s.enabled,!0),timeout:$e(s.timeout,300),approvalRequired:ve(s.approval_required),memory:Je(s.memory,!1),memoryMaxEntries:$e(s.memory_max_entries,100),autoCompactThreshold:$e(s.auto_compact_threshold,85),tags:ve(s.tags),avatar:M(s.avatar)??"",body:a,contextBody:"",skillsBody:"",env:this.parseEnvMap(s.env),permissionRules:{allow:o,deny:c},isFolder:!1,heartbeatEnabled:!1,heartbeatSchedule:"",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""}}parseSkill(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.name);return n?{filePath:t,name:n,description:M(s.description),tags:ve(s.tags),body:a,toolsBody:"",referencesBody:"",examplesBody:"",isFolder:!1}:(this.setIssue(t,"Skill requires string field `name`."),null)}parseTask(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.task_id),i=M(s.agent),o=M(s.type);if(!n||!i||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!M(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!M(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let c=M(s.priority),d=c&&["low","medium","high","critical"].includes(c)?c:"medium";return{filePath:t,taskId:n,agent:i,schedule:M(s.schedule),runAt:M(s.run_at),type:o,priority:d,enabled:Je(s.enabled,!0),created:M(s.created)??new Date().toISOString(),lastRun:M(s.last_run),nextRun:M(s.next_run),runCount:$e(s.run_count,0),catchUp:Je(s.catch_up,this.settings.catchUpMissedTasks),effort:M(s.effort),model:M(s.model),tags:ve(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=ne(e),n=M(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let i=M(s.type),o=["slack","telegram"];if(!i||!o.includes(i))return this.setIssue(t,`Channel \`${n}\` requires \`type\` to be one of: ${o.join(", ")}.`),null;let c=i,l=M(s.default_agent)??M(s.agent);if(!l)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=ve(s.allowed_agents),h=M(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=xs(s.transport)?s.transport:{};return{filePath:t,name:n,type:c,defaultAgent:l,allowedAgents:d,enabled:Je(s.enabled,!0),credentialRef:h,allowedUsers:ve(s.allowed_users),perUserSessions:Je(s.per_user_sessions,!0),channelContext:M(s.channel_context)??"",transport:u,tags:ve(s.tags),body:a}}validateReferences(){let t=new Set;for(let i of this.skills.values())t.has(i.name)&&this.setIssue(i.filePath,`Duplicate skill name \`${i.name}\`.`),t.add(i.name);let e=new Set;for(let i of this.agents.values()){e.has(i.name)&&this.setIssue(i.filePath,`Duplicate agent name \`${i.name}\`.`),e.add(i.name);for(let o of i.skills)t.has(o)||this.setIssue(i.filePath,`Agent references missing skill \`${o}\`.`)}for(let i of this.tasks.values())e.has(i.agent)||this.setIssue(i.filePath,`Task references missing agent \`${i.agent}\`.`);let s=new Set,a=new Map;for(let i of this.agents.values())a.set(i.name,i);let n=this.channelCredentialGetter?.()??this.settings.channelCredentials??{};for(let i of this.channels.values()){s.has(i.name)&&this.setIssue(i.filePath,`Duplicate channel name \`${i.name}\`.`),s.add(i.name);let o=a.get(i.defaultAgent);o?o.approvalRequired.length>0&&this.setIssue(i.filePath,`Channel \`${i.name}\` cannot bind to agent \`${o.name}\` because the agent has \`approval_required\` set (${o.approvalRequired.join(", ")}). Clear the approval list on the agent or pick a different agent.`):this.setIssue(i.filePath,`Channel \`${i.name}\` references missing agent \`${i.defaultAgent}\`.`);let c=n[i.credentialRef];c?c.type!==i.type&&this.setIssue(i.filePath,`Channel \`${i.name}\` is type \`${i.type}\` but credential \`${i.credentialRef}\` is type \`${c.type}\`.`):this.setIssue(i.filePath,`Channel \`${i.name}\` references missing credential \`${i.credentialRef}\`. Add it under Settings \u2192 Channel Credentials.`)}}parseEnvMap(t){if(!xs(t))return{};let e={};for(let[s,a]of Object.entries(t))typeof a=="string"?e[s]=a:(typeof a=="number"||typeof a=="boolean")&&(e[s]=String(a));return e}parseApprovals(t){if(Array.isArray(t))return t.flatMap(e=>{if(!xs(e)||!M(e.tool))return[];let s=M(e.tool);return s?[{tool:s,command:M(e.command),reason:M(e.reason),status:M(e.status)??"pending",resolvedAt:M(e.resolvedAt),note:M(e.note)}]:[]})}};var _t=require("obsidian"),Ss=class extends _t.Modal{constructor(e,s,a){super(e);this.info=s;this.onConfirm=a}deleteTasks=!0;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-confirm-delete-modal");let s=e.createDiv({cls:"af-delete-header"}),a=s.createSpan({cls:"af-delete-header-icon"});(0,_t.setIcon)(a,"alert-triangle"),s.createEl("h3",{text:`Delete agent "${this.info.agentName}"?`});let n=e.createDiv({cls:"af-delete-summary"});n.createDiv({text:"This action will:"});let i=n.createEl("ul",{cls:"af-delete-impact-list"});i.createEl("li",{text:"Move the agent definition to trash"}),this.info.hasMemory&&i.createEl("li",{text:"Move the agent's memory file to trash"}),this.info.taskCount>0&&i.createEl("li").setText(`${this.info.taskCount} task${this.info.taskCount!==1?"s":""} reference this agent`),this.info.runCount>0&&i.createEl("li",{cls:"af-delete-preserved"}).setText(`${this.info.runCount} run log${this.info.runCount!==1?"s":""} will be preserved`),e.createDiv({cls:"af-delete-note",text:"Files are moved to your system trash and can be recovered."}),this.info.taskCount>0&&new _t.Setting(e).setName("Also delete associated tasks").setDesc(`Delete ${this.info.taskCount} task${this.info.taskCount!==1?"s":""} that reference this agent`).addToggle(h=>{h.setValue(this.deleteTasks),h.onChange(u=>{this.deleteTasks=u})});let o=e.createDiv({cls:"af-delete-actions"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{cls:"af-delete-confirm-btn",text:"Delete Agent"}),d=l.createSpan({cls:"af-delete-btn-icon"});(0,_t.setIcon)(d,"trash-2"),l.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var N=require("obsidian");var cn=[{value:"opus",label:"opus \u2014 latest Opus"},{value:"sonnet",label:"sonnet \u2014 latest Sonnet"},{value:"haiku",label:"haiku \u2014 latest Haiku"},{value:"opusplan",label:"opusplan \u2014 plan-mode Opus"}],Cs="__custom__";function or(r){let t=r.trim();return!t||t==="default"||t==="subscription"?"inherit":cn.some(e=>e.value===t)?"alias":"custom"}function At(r,t){r.empty(),r.addClass("af-model-picker");let e=or(t.value),s=r.createEl("select",{cls:"af-form-select af-mp-select"}),a=t.allowInherit?t.inheritPlaceholder??"Inherit from agent":"Default (let Claude Code pick)";s.createEl("option",{text:a,attr:{value:""}});let n=s.createEl("optgroup",{attr:{label:"Aliases (any backend)"}});for(let o of cn)n.createEl("option",{text:o.label,attr:{value:o.value}});s.createEl("option",{text:"Custom\u2026",attr:{value:Cs}});let i=r.createEl("input",{cls:"af-form-input af-mp-custom-input",attr:{type:"text",placeholder:"e.g. claude-opus-4-7 \xB7 us.anthropic.claude-opus-4-7 \xB7 claude-opus-4-7@20251101",spellcheck:"false"}});e==="inherit"?(s.value="",i.value="",i.style.display="none"):e==="alias"?(s.value=t.value.trim(),i.value="",i.style.display="none"):(s.value=Cs,i.value=t.value.trim(),i.style.display=""),s.addEventListener("change",()=>{s.value===Cs?(i.style.display="",i.focus(),t.onChange(i.value.trim())):(i.style.display="none",t.onChange(s.value))}),i.addEventListener("input",()=>{s.value===Cs&&t.onChange(i.value.trim())})}var le=require("obsidian");function lr(){let r=new Date,t=r.getFullYear(),e=String(r.getMonth()+1).padStart(2,"0"),s=String(r.getDate()).padStart(2,"0");return`${t}-${e}-${s}`}var cr="0 3 * * *",dr="0 9 * * 0";function ma(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:lr(),heartbeatChannel:"",fileSubstantiveAnswers:!0,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,maxTokensPerRefresh:3e4,indexSplitThreshold:100,dedupSimilarityThreshold:.82,summaryStaleDays:30,failedPath:"_sources/failed",stateFile:".wiki-keeper-state.json"}}var un=ma();function ga(r){let t=fe(r).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function hr(r,t){let e=t||"the whole vault",s={name:r,description:`Cultivates ${e} as a self-maintaining wiki. Ingests new sources, extracts durable claims from watched folders, answers questions with citations, and periodically lints.`,avatar:"library",enabled:!0,skills:["wiki-ingest","wiki-query","wiki-lint","wiki-refresh"],tags:t?["wiki-keeper",`scope:${fe(t)}`]:["wiki-keeper"]},a=`You are the Wiki Keeper for the \`${e}\` scope. Maintain it as a persistent, interlinked knowledge base (Karpathy's "LLM wiki" pattern). ## Scope isolation @@ -11757,8 +11757,8 @@ Use \`memory\` for procedural learning ("user prefers concept pages under sub-fo Lint runs on its own schedule via the sibling \`*-lint\` task. Change the schedule by editing this file's \`schedule:\` frontmatter directly, or via the agent editor in the dashboard. -`)}function pn(r){let e={task_id:`${r}-lint`,agent:r,type:"recurring",schedule:dr,priority:"low",enabled:!0,created:new Date().toISOString(),run_count:0,catch_up:!1,tags:["wiki-keeper","lint"]};return J(e,"Run the `wiki-lint` skill on the current scope.\n\nWrite the report as a new `## Lint YYYY-MM-DD` section inside the fenced block in log.md.\n")}function ga(r,t){return(0,le.normalizePath)(`${r}/tasks/${t}-lint.md`)}var mr="# Wiki Schema\n\n## Page types\n\n- **Entity pages** \u2014 people, orgs, products. Frontmatter: `type: entity`.\n- **Concept pages** \u2014 ideas, techniques, patterns. `type: concept`.\n- **Event pages** \u2014 meetings, releases, decisions. `type: event, date: YYYY-MM-DD`.\n- **Summary pages** (inbox-mode only) \u2014 one per ingested inbox source. `type: summary, source: `.\n- **Synthesis pages** \u2014 filed answers from `wiki-query` substantive responses. `type: synthesis, question: , refreshed: `. Live under `_topics/syntheses/`.\n\nTopic pages of type entity/concept/event SHOULD also carry:\n- `summary_refreshed: ` \u2014 last time `wiki-refresh` regenerated this page's summary block. Empty string on creation.\n- `claims_at_refresh: ` \u2014 how many claims existed at last refresh. Drives the staleness threshold.\n\n## Page anatomy\n\nEvery entity/concept/event page has this structure:\n\n```markdown\n---\ntype: entity\nname: Vendor X\nsummary_refreshed: 2026-04-26\nclaims_at_refresh: 14\n---\n\n\n## Summary\n\n- 3-7 bullet synthesis maintained by wiki-refresh.\n- Forward-links to other topics with [[wikilinks]].\n\n\n## Claims\n\n- 2026-04-18 [meeting]: from [[meetings/2026-04-18-vendor-sync|vendor-sync]]: Vendor X raised prices 15%\n- 2026-04-12 [doc]: from [[summaries/2026-04-12-renewal-brief|renewal-brief]]: contract auto-renews 2026-12-01\n\n## Contradictions\n\n(only present when contradictions exist)\n```\n\nThe fenced summary block is auto-managed by `wiki-refresh`. The `## Claims` section is append-only history written by `wiki-ingest` and `wiki-query` compounding. `## Contradictions` is created on demand. **Anything outside these structures is user-authored** and must be preserved.\n\n## Source-type tags\n\nEvery dated bullet in `## Claims` carries a tag indicating the source type:\n\n- `[doc]` \u2014 PDF, DOCX, XLSX, or other document\n- `[meeting]` \u2014 meeting note or transcript\n- `[email]` \u2014 email forward\n- `[note]` \u2014 markdown daily note or in-vault note\n- `[web]` \u2014 web clipping or URL drop\n- `[synthesis]` \u2014 bullet emitted by `wiki-query` compounding (links to a synthesis page)\n- `[other]` \u2014 fallback\n\nTags help `wiki-query` weight evidence quality and help `wiki-lint` identify drift.\n\n## Naming\n\n- Slug-case filenames: `vendor-x.md`, not `Vendor X.md`.\n- Group by type under `_topics//` when there are >5 pages of a type.\n\n## Links\n\n- Every entity/concept page MUST have \u22651 inbound link from `index.md` (or a sub-MOC) or a sibling.\n- Summary pages MUST forward-link to every entity/concept they mention.\n- Watched-mode extractions append dated entries to topic pages; forward-link to the source file path so readers can find the raw note.\n- Synthesis pages forward-link to every cited topic and earn their place that way (lint exempts them from orphan flagging if they have any outbound links to topics).\n\n## Index split\n\nWhen the topic count exceeds the `index_split_threshold` config value (default 100) or any single type exceeds 30 pages, `index.md` becomes a hub of hubs and per-type sub-MOCs live at `/index/.md`. After split, new topic-page entries go into the matching sub-MOC, not the root index.\n\n## Conflict resolution\n\n- New claim contradicts existing? Add a `## Contradictions` section with a dated entry. Do NOT overwrite.\n- Flag in `log.md` for user review.\n- `wiki-refresh` reflects unresolved contradictions in the summary so query-time readers see them.\n\n## Failed sources\n\nFiles that fail ingest 3 times in a row are quarantined to `/` (default `_sources/failed/`) with a sidecar `.error.md`. Lint surfaces the quarantine count weekly.\n",fr=["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],gr=["Bash(rm -rf *)","Bash(git push *)","Bash(rm -rf /*)","Bash(mv * /*)","Bash(cp -r * /*)"];async function mn(r,t){let e=(0,le.normalizePath)(`${t}/permissions.json`),s=r.getAbstractFileByPath(e),a={allow:[],deny:[]};if(s instanceof le.TFile)try{let o=await r.cachedRead(s),c=JSON.parse(o),l=Array.isArray(c.allow)?c.allow.filter(h=>typeof h=="string"):[],d=Array.isArray(c.deny)?c.deny.filter(h=>typeof h=="string"):[];a={allow:l,deny:d}}catch{}let n={allow:dn(a.allow,fr),deny:dn(a.deny,gr)},i=JSON.stringify(n,null,2)+` -`;return s instanceof le.TFile?await r.modify(s,i):await r.create(e,i),n}function dn(r,t){let e=new Set,s=[];for(let a of r)e.has(a)||(e.add(a),s.push(a));for(let a of t)e.has(a)||(e.add(a),s.push(a));return s}async function fn(r,t,e){let s=fa(e.scopeSlug||e.scopeRoot),a=(0,le.normalizePath)(`${t}/agents/${s}`);if(await Nt(r,a),r.getAbstractFileByPath((0,le.normalizePath)(`${a}/agent.md`)))throw new Error(`Wiki Keeper agent already exists at ${a}. Delete it first or choose a different scope slug.`);await r.create((0,le.normalizePath)(`${a}/agent.md`),hr(s,e.scopeRoot)),await r.create((0,le.normalizePath)(`${a}/config.md`),ur(e)),await r.create((0,le.normalizePath)(`${a}/HEARTBEAT.md`),pr(e)),await r.create((0,le.normalizePath)(`${a}/CONTEXT.md`),mr),await mn(r,a);let i=ga(t,s);r.getAbstractFileByPath(i)||(await Nt(r,(0,le.normalizePath)(`${t}/tasks`)),await r.create(i,pn(s)));let o=e.scopeRoot.trim(),c=o?`${o}/`:"";return await Nt(r,(0,le.normalizePath)(`${c}${e.inboxPath}`)),await Nt(r,(0,le.normalizePath)(`${c}${e.topicsRoot}`)),await hn(r,(0,le.normalizePath)(`${c}${e.indexPath}`),`# Index +`)}function pn(r){let e={task_id:`${r}-lint`,agent:r,type:"recurring",schedule:dr,priority:"low",enabled:!0,created:new Date().toISOString(),run_count:0,catch_up:!1,tags:["wiki-keeper","lint"]};return J(e,"Run the `wiki-lint` skill on the current scope.\n\nWrite the report as a new `## Lint YYYY-MM-DD` section inside the fenced block in log.md.\n")}function ya(r,t){return(0,le.normalizePath)(`${r}/tasks/${t}-lint.md`)}var fr="# Wiki Schema\n\n## Page types\n\n- **Entity pages** \u2014 people, orgs, products. Frontmatter: `type: entity`.\n- **Concept pages** \u2014 ideas, techniques, patterns. `type: concept`.\n- **Event pages** \u2014 meetings, releases, decisions. `type: event, date: YYYY-MM-DD`.\n- **Summary pages** (inbox-mode only) \u2014 one per ingested inbox source. `type: summary, source: `.\n- **Synthesis pages** \u2014 filed answers from `wiki-query` substantive responses. `type: synthesis, question: , refreshed: `. Live under `_topics/syntheses/`.\n\nTopic pages of type entity/concept/event SHOULD also carry:\n- `summary_refreshed: ` \u2014 last time `wiki-refresh` regenerated this page's summary block. Empty string on creation.\n- `claims_at_refresh: ` \u2014 how many claims existed at last refresh. Drives the staleness threshold.\n\n## Page anatomy\n\nEvery entity/concept/event page has this structure:\n\n```markdown\n---\ntype: entity\nname: Vendor X\nsummary_refreshed: 2026-04-26\nclaims_at_refresh: 14\n---\n\n\n## Summary\n\n- 3-7 bullet synthesis maintained by wiki-refresh.\n- Forward-links to other topics with [[wikilinks]].\n\n\n## Claims\n\n- 2026-04-18 [meeting]: from [[meetings/2026-04-18-vendor-sync|vendor-sync]]: Vendor X raised prices 15%\n- 2026-04-12 [doc]: from [[summaries/2026-04-12-renewal-brief|renewal-brief]]: contract auto-renews 2026-12-01\n\n## Contradictions\n\n(only present when contradictions exist)\n```\n\nThe fenced summary block is auto-managed by `wiki-refresh`. The `## Claims` section is append-only history written by `wiki-ingest` and `wiki-query` compounding. `## Contradictions` is created on demand. **Anything outside these structures is user-authored** and must be preserved.\n\n## Source-type tags\n\nEvery dated bullet in `## Claims` carries a tag indicating the source type:\n\n- `[doc]` \u2014 PDF, DOCX, XLSX, or other document\n- `[meeting]` \u2014 meeting note or transcript\n- `[email]` \u2014 email forward\n- `[note]` \u2014 markdown daily note or in-vault note\n- `[web]` \u2014 web clipping or URL drop\n- `[synthesis]` \u2014 bullet emitted by `wiki-query` compounding (links to a synthesis page)\n- `[other]` \u2014 fallback\n\nTags help `wiki-query` weight evidence quality and help `wiki-lint` identify drift.\n\n## Naming\n\n- Slug-case filenames: `vendor-x.md`, not `Vendor X.md`.\n- Group by type under `_topics//` when there are >5 pages of a type.\n\n## Links\n\n- Every entity/concept page MUST have \u22651 inbound link from `index.md` (or a sub-MOC) or a sibling.\n- Summary pages MUST forward-link to every entity/concept they mention.\n- Watched-mode extractions append dated entries to topic pages; forward-link to the source file path so readers can find the raw note.\n- Synthesis pages forward-link to every cited topic and earn their place that way (lint exempts them from orphan flagging if they have any outbound links to topics).\n\n## Index split\n\nWhen the topic count exceeds the `index_split_threshold` config value (default 100) or any single type exceeds 30 pages, `index.md` becomes a hub of hubs and per-type sub-MOCs live at `/index/.md`. After split, new topic-page entries go into the matching sub-MOC, not the root index.\n\n## Conflict resolution\n\n- New claim contradicts existing? Add a `## Contradictions` section with a dated entry. Do NOT overwrite.\n- Flag in `log.md` for user review.\n- `wiki-refresh` reflects unresolved contradictions in the summary so query-time readers see them.\n\n## Failed sources\n\nFiles that fail ingest 3 times in a row are quarantined to `/` (default `_sources/failed/`) with a sidecar `.error.md`. Lint surfaces the quarantine count weekly.\n",mr=["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],gr=["Bash(rm -rf *)","Bash(git push *)","Bash(rm -rf /*)","Bash(mv * /*)","Bash(cp -r * /*)"];async function fn(r,t){let e=(0,le.normalizePath)(`${t}/permissions.json`),s=r.getAbstractFileByPath(e),a={allow:[],deny:[]};if(s instanceof le.TFile)try{let o=await r.cachedRead(s),c=JSON.parse(o),l=Array.isArray(c.allow)?c.allow.filter(h=>typeof h=="string"):[],d=Array.isArray(c.deny)?c.deny.filter(h=>typeof h=="string"):[];a={allow:l,deny:d}}catch{}let n={allow:dn(a.allow,mr),deny:dn(a.deny,gr)},i=JSON.stringify(n,null,2)+` +`;return s instanceof le.TFile?await r.modify(s,i):await r.create(e,i),n}function dn(r,t){let e=new Set,s=[];for(let a of r)e.has(a)||(e.add(a),s.push(a));for(let a of t)e.has(a)||(e.add(a),s.push(a));return s}async function mn(r,t,e){let s=ga(e.scopeSlug||e.scopeRoot),a=(0,le.normalizePath)(`${t}/agents/${s}`);if(await Nt(r,a),r.getAbstractFileByPath((0,le.normalizePath)(`${a}/agent.md`)))throw new Error(`Wiki Keeper agent already exists at ${a}. Delete it first or choose a different scope slug.`);await r.create((0,le.normalizePath)(`${a}/agent.md`),hr(s,e.scopeRoot)),await r.create((0,le.normalizePath)(`${a}/config.md`),ur(e)),await r.create((0,le.normalizePath)(`${a}/HEARTBEAT.md`),pr(e)),await r.create((0,le.normalizePath)(`${a}/CONTEXT.md`),fr),await fn(r,a);let i=ya(t,s);r.getAbstractFileByPath(i)||(await Nt(r,(0,le.normalizePath)(`${t}/tasks`)),await r.create(i,pn(s)));let o=e.scopeRoot.trim(),c=o?`${o}/`:"";return await Nt(r,(0,le.normalizePath)(`${c}${e.inboxPath}`)),await Nt(r,(0,le.normalizePath)(`${c}${e.topicsRoot}`)),await hn(r,(0,le.normalizePath)(`${c}${e.indexPath}`),`# Index @@ -11766,13 +11766,13 @@ Change the schedule by editing this file's \`schedule:\` frontmatter directly, o -`),{path:a,name:s}}async function Nt(r,t){if(r.getAbstractFileByPath(t))return;let e=t.split("/"),s="";for(let a of e)if(s=s?`${s}/${a}`:a,!r.getAbstractFileByPath(s))try{await r.createFolder(s)}catch(n){if(!(n instanceof Error?n.message:String(n)).includes("already exists"))throw n}}async function hn(r,t,e){if(r.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await Nt(r,s),await r.create(t,e)}async function gn(r,t,e,s){let a=(0,le.normalizePath)(`${t}/agents/${e}`),n=(0,le.normalizePath)(`${a}/config.md`),i=r.getAbstractFileByPath(n);if(!(i instanceof le.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await r.cachedRead(i),{frontmatter:c,body:l}=ne(o),d=c.wiki_keeper??{};d.watched_folders=s.watchedFolders,d.exclude_patterns=s.excludePatterns,s.watchedSince?d.watched_since=s.watchedSince:delete d.watched_since,delete d.ingest_schedule,delete d.lint_schedule,delete d.lint_day,d.file_substantive_answers=s.fileSubstantiveAnswers,d.obsidian_url_scheme=s.obsidianUrlScheme,d.max_tokens_per_ingest=s.maxTokensPerIngest,d.max_tokens_per_refresh=s.maxTokensPerRefresh,d.index_split_threshold=s.indexSplitThreshold,d.dedup_similarity_threshold=s.dedupSimilarityThreshold,d.summary_stale_days=s.summaryStaleDays,(typeof d.failed_path!="string"||!d.failed_path)&&(d.failed_path="_sources/failed"),c.wiki_keeper=d,delete c.allowed_tools,delete c.blocked_tools,await r.modify(i,J(c,l)),await mn(r,a);let h=(0,le.normalizePath)(`${a}/HEARTBEAT.md`),u=r.getAbstractFileByPath(h);if(u instanceof le.TFile){let m=await r.cachedRead(u),{frontmatter:y,body:k}=ne(m);s.heartbeatChannel?y.channel=s.heartbeatChannel:delete y.channel,await r.modify(u,J(y,k))}let p=ga(t,e);r.getAbstractFileByPath(p)instanceof le.TFile||(await Nt(r,(0,le.normalizePath)(`${t}/tasks`)),await r.create(p,pn(e)))}async function yn(r,t,e){let s=(0,le.normalizePath)(`${t}/agents/${e}`),a=r.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof le.TFolder))throw new Error(`Expected folder at ${s}`);await r.adapter.rmdir(s,!0);let n=ga(t,e),i=r.getAbstractFileByPath(n);i instanceof le.TFile&&await r.delete(i)}var Ts=class extends N.PluginSettingTab{constructor(e){super(e.app,e);this.plugin=e}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2",{text:"Agent Fleet Settings"}),new N.Setting(e).setName("Fleet folder").addText(n=>n.setValue(this.plugin.settings.fleetFolder).onChange(async i=>{this.plugin.settings.fleetFolder=i.trim()||ct.fleetFolder,await this.plugin.saveSettings()})),new N.Setting(e).setName("Claude CLI path").addText(n=>n.setValue(this.plugin.settings.claudeCliPath).onChange(async i=>{this.plugin.settings.claudeCliPath=i.trim()||ct.claudeCliPath,await this.plugin.saveSettings()}));let a=new N.Setting(e).setName("Default model").setDesc("Fallback for agents that don\u2019t set their own. Aliases (opus/sonnet/haiku) work on any backend; use Custom for pinned IDs or Bedrock/Vertex/Foundry.").controlEl.createDiv();At(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||ct.defaultModel,await this.plugin.saveSettings()}}),new N.Setting(e).setName("AWS region").addText(n=>n.setValue(this.plugin.settings.awsRegion).onChange(async i=>{this.plugin.settings.awsRegion=i.trim()||ct.awsRegion,await this.plugin.saveSettings()})),new N.Setting(e).setName("Max concurrent runs").addSlider(n=>n.setLimits(1,10,1).setValue(this.plugin.settings.maxConcurrentRuns).setDynamicTooltip().onChange(async i=>{this.plugin.settings.maxConcurrentRuns=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Run log retention").setDesc("Days to keep run logs before auto-prune.").addSlider(n=>n.setLimits(1,365,1).setValue(this.plugin.settings.runLogRetentionDays).setDynamicTooltip().onChange(async i=>{this.plugin.settings.runLogRetentionDays=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Catch up missed tasks").addToggle(n=>n.setValue(this.plugin.settings.catchUpMissedTasks).onChange(async i=>{this.plugin.settings.catchUpMissedTasks=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Notification level").addDropdown(n=>n.addOption("all","All").addOption("failures-only","Failures only").addOption("none","None").setValue(this.plugin.settings.notificationLevel).onChange(async i=>{this.plugin.settings.notificationLevel=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Status bar").addToggle(n=>n.setValue(this.plugin.settings.showStatusBar).onChange(async i=>{this.plugin.settings.showStatusBar=i,await this.plugin.saveSettings(),this.plugin.refreshStatusBar()})),new N.Setting(e).setName("Chat watchdog timeout (minutes)").setDesc("How long a chat session waits for any output from the Claude CLI before killing the subprocess and surfacing a timeout error. Raise this if your agents run long tools (e.g. large web fetches, builds) and you're seeing spurious 'no response' errors.").addSlider(n=>n.setLimits(1,60,1).setValue(this.plugin.settings.chatWatchdogMinutes).setDynamicTooltip().onChange(async i=>{this.plugin.settings.chatWatchdogMinutes=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Verify Claude CLI").setDesc("Checks that the configured binary is reachable.").addButton(n=>n.setButtonText("Verify").onClick(async()=>{let i=await this.plugin.verifyClaudeCli();new N.Notice(i?"Claude CLI detected.":"Claude CLI check failed. See console for details.")})),this.renderWikiKeepersSection(e),this.renderChannelsSection(e)}renderChannelsSection(e){e.createEl("h3",{text:"Channels"});let s=e.createDiv({cls:"af-settings-warning"});s.style.padding="12px",s.style.margin="8px 0 16px 0",s.style.border="1px solid var(--background-modifier-border)",s.style.borderRadius="6px",s.style.background="var(--background-secondary)",s.createEl("strong",{text:"Credential storage: "}),s.createSpan({text:"Channel credentials are stored in this plugin's data.json inside your vault's .obsidian folder. If you sync your .obsidian folder across devices, credentials will sync with it. Do not commit this file to a public git repository."}),new N.Setting(e).setName("Max concurrent channel sessions").setDesc("Hard cap on live claude subprocesses across all channels. Oldest idle session is hibernated when exceeded.").addSlider(l=>l.setLimits(1,20,1).setValue(this.plugin.settings.maxConcurrentChannelSessions).setDynamicTooltip().onChange(async d=>{this.plugin.settings.maxConcurrentChannelSessions=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Idle timeout (minutes)").setDesc("Channel sessions with no activity for this long get their subprocess hibernated. State is preserved and the next message resumes transparently.").addSlider(l=>l.setLimits(1,120,1).setValue(this.plugin.settings.channelIdleTimeoutMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelIdleTimeoutMinutes=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Rate limit per conversation").setDesc("Maximum messages allowed per external conversation within the rolling window.").addSlider(l=>l.setLimits(1,100,1).setValue(this.plugin.settings.channelRateLimitPerConversation).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitPerConversation=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Rate limit window (minutes)").addSlider(l=>l.setLimits(1,60,1).setValue(this.plugin.settings.channelRateLimitWindowMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitWindowMinutes=d,await this.plugin.saveSettings()})),e.createEl("h4",{text:"Channel credentials"});let a=e.createDiv({cls:"af-channel-credentials"});this.renderCredentialList(a);let n=e.createDiv({cls:"af-channel-credential-add"});n.style.marginTop="12px",n.style.padding="12px",n.style.border="1px dashed var(--background-modifier-border)",n.style.borderRadius="6px",n.createEl("strong",{text:"Add a channel credential"});let i={ref:"",type:"slack",botToken:"",appToken:""};new N.Setting(n).setName("Reference name").setDesc("Used by `credential_ref` in _fleet/channels/*.md files.").addText(l=>l.setPlaceholder("my-creds").onChange(d=>{i.ref=d.trim()})),new N.Setting(n).setName("Type").addDropdown(l=>l.addOption("slack","Slack").addOption("telegram","Telegram").setValue("slack").onChange(d=>{i.type=d,o.style.display=d==="slack"?"":"none",c.style.display=d==="telegram"?"":"none"}));let o=n.createDiv();new N.Setting(o).setName("Bot token (xoxb-...)").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xoxb-...").onChange(d=>{i.botToken=d.trim()})}),new N.Setting(o).setName("App-level token (xapp-...)").setDesc("Generated in your Slack app's Basic Information \u2192 App-Level Tokens.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xapp-...").onChange(d=>{i.appToken=d.trim()})});let c=n.createDiv();c.style.display="none",new N.Setting(c).setName("Bot token").setDesc("From @BotFather on Telegram.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("123456:ABC-DEF1234...").onChange(d=>{i.botToken=d.trim()})}),new N.Setting(n).addButton(l=>l.setButtonText("Add credential").setCta().onClick(async()=>{if(!i.ref||!i.botToken){new N.Notice("Fill in the reference name and bot token.");return}let d;if(i.type==="telegram")d={type:"telegram",botToken:i.botToken};else{if(!i.appToken){new N.Notice("Slack requires both bot token and app-level token.");return}d={type:"slack",botToken:i.botToken,appToken:i.appToken}}this.plugin.channelCredentials.set(i.ref,d),new N.Notice(`Added credential \`${i.ref}\`.`),this.display()}))}renderCredentialList(e){let s=this.plugin.channelCredentials.list();if(s.length===0){e.createDiv({text:"No channel credentials configured yet.",cls:"af-muted"}).style.color="var(--text-muted)";return}for(let{ref:a,entry:n}of s){let i=e.createDiv({cls:"af-channel-credential-row"});i.style.display="flex",i.style.justifyContent="space-between",i.style.alignItems="center",i.style.padding="8px 12px",i.style.border="1px solid var(--background-modifier-border)",i.style.borderRadius="6px",i.style.marginBottom="6px";let o=i.createDiv();o.createEl("strong",{text:a}),o.createEl("span",{text:` \xB7 ${n.type} \xB7 ${vr(yr(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let c=i.createEl("button",{text:"Remove"});c.onclick=()=>{this.plugin.channelCredentials.delete(a),new N.Notice(`Removed credential \`${a}\`.`),this.display()}}}renderWikiKeepersSection(e){e.createEl("h3",{text:"Wiki Keepers"});let s=e.createEl("p",{cls:"af-settings-hint",text:"Per-scope wiki agents. Each runs its own inbox + watched folders + topics + heartbeat. All fields on the Add form are optional \u2014 click Create with everything blank to get a whole-vault keeper with defaults."});s.style.color="var(--af-text-secondary)",s.style.fontSize="12px";let n=this.plugin.runtime.getSnapshot().agents.filter(o=>o.wikiKeeper!==void 0),i=e.createDiv({cls:"af-wk-list"});if(n.length===0)i.createDiv({cls:"af-wk-empty",text:"No Wiki Keepers yet. Click Add to create one."});else for(let o of n){let c=i.createDiv({cls:"af-wk-row"}),l=c.createDiv({cls:"af-wk-row-left"});l.createSpan({cls:"af-wk-name",text:o.name});let d=o.wikiKeeper.scopeRoot||"(whole vault)";l.createSpan({cls:"af-wk-scope",text:`scope: ${d}`});let h=c.createDiv({cls:"af-wk-row-actions"}),u=h.createEl("button",{text:"Edit",cls:"af-wk-row-btn"});u.onclick=()=>{new va(this.plugin,o,()=>{this.scheduleRerender()}).open()};let p=h.createEl("button",{text:"Delete",cls:"af-wk-row-btn af-wk-row-btn-danger"});p.onclick=async()=>{if(confirm(`Delete Wiki Keeper "${o.name}"? +`),{path:a,name:s}}async function Nt(r,t){if(r.getAbstractFileByPath(t))return;let e=t.split("/"),s="";for(let a of e)if(s=s?`${s}/${a}`:a,!r.getAbstractFileByPath(s))try{await r.createFolder(s)}catch(n){if(!(n instanceof Error?n.message:String(n)).includes("already exists"))throw n}}async function hn(r,t,e){if(r.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await Nt(r,s),await r.create(t,e)}async function gn(r,t,e,s){let a=(0,le.normalizePath)(`${t}/agents/${e}`),n=(0,le.normalizePath)(`${a}/config.md`),i=r.getAbstractFileByPath(n);if(!(i instanceof le.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await r.cachedRead(i),{frontmatter:c,body:l}=ne(o),d=c.wiki_keeper??{};d.watched_folders=s.watchedFolders,d.exclude_patterns=s.excludePatterns,s.watchedSince?d.watched_since=s.watchedSince:delete d.watched_since,delete d.ingest_schedule,delete d.lint_schedule,delete d.lint_day,d.file_substantive_answers=s.fileSubstantiveAnswers,d.obsidian_url_scheme=s.obsidianUrlScheme,d.max_tokens_per_ingest=s.maxTokensPerIngest,d.max_tokens_per_refresh=s.maxTokensPerRefresh,d.index_split_threshold=s.indexSplitThreshold,d.dedup_similarity_threshold=s.dedupSimilarityThreshold,d.summary_stale_days=s.summaryStaleDays,(typeof d.failed_path!="string"||!d.failed_path)&&(d.failed_path="_sources/failed"),c.wiki_keeper=d,delete c.allowed_tools,delete c.blocked_tools,await r.modify(i,J(c,l)),await fn(r,a);let h=(0,le.normalizePath)(`${a}/HEARTBEAT.md`),u=r.getAbstractFileByPath(h);if(u instanceof le.TFile){let f=await r.cachedRead(u),{frontmatter:y,body:k}=ne(f);s.heartbeatChannel?y.channel=s.heartbeatChannel:delete y.channel,await r.modify(u,J(y,k))}let p=ya(t,e);r.getAbstractFileByPath(p)instanceof le.TFile||(await Nt(r,(0,le.normalizePath)(`${t}/tasks`)),await r.create(p,pn(e)))}async function yn(r,t,e){let s=(0,le.normalizePath)(`${t}/agents/${e}`),a=r.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof le.TFolder))throw new Error(`Expected folder at ${s}`);await r.adapter.rmdir(s,!0);let n=ya(t,e),i=r.getAbstractFileByPath(n);i instanceof le.TFile&&await r.delete(i)}var Ts=class extends N.PluginSettingTab{constructor(e){super(e.app,e);this.plugin=e}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2",{text:"Agent Fleet Settings"}),new N.Setting(e).setName("Fleet folder").addText(n=>n.setValue(this.plugin.settings.fleetFolder).onChange(async i=>{this.plugin.settings.fleetFolder=i.trim()||lt.fleetFolder,await this.plugin.saveSettings()})),new N.Setting(e).setName("Claude CLI path").addText(n=>n.setValue(this.plugin.settings.claudeCliPath).onChange(async i=>{this.plugin.settings.claudeCliPath=i.trim()||lt.claudeCliPath,await this.plugin.saveSettings()}));let a=new N.Setting(e).setName("Default model").setDesc("Fallback for agents that don\u2019t set their own. Aliases (opus/sonnet/haiku) work on any backend; use Custom for pinned IDs or Bedrock/Vertex/Foundry.").controlEl.createDiv();At(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||lt.defaultModel,await this.plugin.saveSettings()}}),new N.Setting(e).setName("AWS region").addText(n=>n.setValue(this.plugin.settings.awsRegion).onChange(async i=>{this.plugin.settings.awsRegion=i.trim()||lt.awsRegion,await this.plugin.saveSettings()})),new N.Setting(e).setName("Max concurrent runs").addSlider(n=>n.setLimits(1,10,1).setValue(this.plugin.settings.maxConcurrentRuns).setDynamicTooltip().onChange(async i=>{this.plugin.settings.maxConcurrentRuns=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Run log retention").setDesc("Days to keep run logs before auto-prune.").addSlider(n=>n.setLimits(1,365,1).setValue(this.plugin.settings.runLogRetentionDays).setDynamicTooltip().onChange(async i=>{this.plugin.settings.runLogRetentionDays=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Catch up missed tasks").addToggle(n=>n.setValue(this.plugin.settings.catchUpMissedTasks).onChange(async i=>{this.plugin.settings.catchUpMissedTasks=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Notification level").addDropdown(n=>n.addOption("all","All").addOption("failures-only","Failures only").addOption("none","None").setValue(this.plugin.settings.notificationLevel).onChange(async i=>{this.plugin.settings.notificationLevel=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Status bar").addToggle(n=>n.setValue(this.plugin.settings.showStatusBar).onChange(async i=>{this.plugin.settings.showStatusBar=i,await this.plugin.saveSettings(),this.plugin.refreshStatusBar()})),new N.Setting(e).setName("Chat watchdog timeout (minutes)").setDesc("How long a chat session waits for any output from the Claude CLI before killing the subprocess and surfacing a timeout error. Raise this if your agents run long tools (e.g. large web fetches, builds) and you're seeing spurious 'no response' errors.").addSlider(n=>n.setLimits(1,60,1).setValue(this.plugin.settings.chatWatchdogMinutes).setDynamicTooltip().onChange(async i=>{this.plugin.settings.chatWatchdogMinutes=i,await this.plugin.saveSettings()})),new N.Setting(e).setName("Verify Claude CLI").setDesc("Checks that the configured binary is reachable.").addButton(n=>n.setButtonText("Verify").onClick(async()=>{let i=await this.plugin.verifyClaudeCli();new N.Notice(i?"Claude CLI detected.":"Claude CLI check failed. See console for details.")})),this.renderWikiKeepersSection(e),this.renderChannelsSection(e)}renderChannelsSection(e){e.createEl("h3",{text:"Channels"});let s=e.createDiv({cls:"af-settings-warning"});s.style.padding="12px",s.style.margin="8px 0 16px 0",s.style.border="1px solid var(--background-modifier-border)",s.style.borderRadius="6px",s.style.background="var(--background-secondary)",s.createEl("strong",{text:"Credential storage: "}),s.createSpan({text:"Channel credentials are stored in this plugin's data.json inside your vault's .obsidian folder. If you sync your .obsidian folder across devices, credentials will sync with it. Do not commit this file to a public git repository."}),new N.Setting(e).setName("Max concurrent channel sessions").setDesc("Hard cap on live claude subprocesses across all channels. Oldest idle session is hibernated when exceeded.").addSlider(l=>l.setLimits(1,20,1).setValue(this.plugin.settings.maxConcurrentChannelSessions).setDynamicTooltip().onChange(async d=>{this.plugin.settings.maxConcurrentChannelSessions=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Idle timeout (minutes)").setDesc("Channel sessions with no activity for this long get their subprocess hibernated. State is preserved and the next message resumes transparently.").addSlider(l=>l.setLimits(1,120,1).setValue(this.plugin.settings.channelIdleTimeoutMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelIdleTimeoutMinutes=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Rate limit per conversation").setDesc("Maximum messages allowed per external conversation within the rolling window.").addSlider(l=>l.setLimits(1,100,1).setValue(this.plugin.settings.channelRateLimitPerConversation).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitPerConversation=d,await this.plugin.saveSettings()})),new N.Setting(e).setName("Rate limit window (minutes)").addSlider(l=>l.setLimits(1,60,1).setValue(this.plugin.settings.channelRateLimitWindowMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitWindowMinutes=d,await this.plugin.saveSettings()})),e.createEl("h4",{text:"Channel credentials"});let a=e.createDiv({cls:"af-channel-credentials"});this.renderCredentialList(a);let n=e.createDiv({cls:"af-channel-credential-add"});n.style.marginTop="12px",n.style.padding="12px",n.style.border="1px dashed var(--background-modifier-border)",n.style.borderRadius="6px",n.createEl("strong",{text:"Add a channel credential"});let i={ref:"",type:"slack",botToken:"",appToken:""};new N.Setting(n).setName("Reference name").setDesc("Used by `credential_ref` in _fleet/channels/*.md files.").addText(l=>l.setPlaceholder("my-creds").onChange(d=>{i.ref=d.trim()})),new N.Setting(n).setName("Type").addDropdown(l=>l.addOption("slack","Slack").addOption("telegram","Telegram").setValue("slack").onChange(d=>{i.type=d,o.style.display=d==="slack"?"":"none",c.style.display=d==="telegram"?"":"none"}));let o=n.createDiv();new N.Setting(o).setName("Bot token (xoxb-...)").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xoxb-...").onChange(d=>{i.botToken=d.trim()})}),new N.Setting(o).setName("App-level token (xapp-...)").setDesc("Generated in your Slack app's Basic Information \u2192 App-Level Tokens.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xapp-...").onChange(d=>{i.appToken=d.trim()})});let c=n.createDiv();c.style.display="none",new N.Setting(c).setName("Bot token").setDesc("From @BotFather on Telegram.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("123456:ABC-DEF1234...").onChange(d=>{i.botToken=d.trim()})}),new N.Setting(n).addButton(l=>l.setButtonText("Add credential").setCta().onClick(async()=>{if(!i.ref||!i.botToken){new N.Notice("Fill in the reference name and bot token.");return}let d;if(i.type==="telegram")d={type:"telegram",botToken:i.botToken};else{if(!i.appToken){new N.Notice("Slack requires both bot token and app-level token.");return}d={type:"slack",botToken:i.botToken,appToken:i.appToken}}this.plugin.channelCredentials.set(i.ref,d),new N.Notice(`Added credential \`${i.ref}\`.`),this.display()}))}renderCredentialList(e){let s=this.plugin.channelCredentials.list();if(s.length===0){e.createDiv({text:"No channel credentials configured yet.",cls:"af-muted"}).style.color="var(--text-muted)";return}for(let{ref:a,entry:n}of s){let i=e.createDiv({cls:"af-channel-credential-row"});i.style.display="flex",i.style.justifyContent="space-between",i.style.alignItems="center",i.style.padding="8px 12px",i.style.border="1px solid var(--background-modifier-border)",i.style.borderRadius="6px",i.style.marginBottom="6px";let o=i.createDiv();o.createEl("strong",{text:a}),o.createEl("span",{text:` \xB7 ${n.type} \xB7 ${vr(yr(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let c=i.createEl("button",{text:"Remove"});c.onclick=()=>{this.plugin.channelCredentials.delete(a),new N.Notice(`Removed credential \`${a}\`.`),this.display()}}}renderWikiKeepersSection(e){e.createEl("h3",{text:"Wiki Keepers"});let s=e.createEl("p",{cls:"af-settings-hint",text:"Per-scope wiki agents. Each runs its own inbox + watched folders + topics + heartbeat. All fields on the Add form are optional \u2014 click Create with everything blank to get a whole-vault keeper with defaults."});s.style.color="var(--af-text-secondary)",s.style.fontSize="12px";let n=this.plugin.runtime.getSnapshot().agents.filter(o=>o.wikiKeeper!==void 0),i=e.createDiv({cls:"af-wk-list"});if(n.length===0)i.createDiv({cls:"af-wk-empty",text:"No Wiki Keepers yet. Click Add to create one."});else for(let o of n){let c=i.createDiv({cls:"af-wk-row"}),l=c.createDiv({cls:"af-wk-row-left"});l.createSpan({cls:"af-wk-name",text:o.name});let d=o.wikiKeeper.scopeRoot||"(whole vault)";l.createSpan({cls:"af-wk-scope",text:`scope: ${d}`});let h=c.createDiv({cls:"af-wk-row-actions"}),u=h.createEl("button",{text:"Edit",cls:"af-wk-row-btn"});u.onclick=()=>{new ba(this.plugin,o,()=>{this.scheduleRerender()}).open()};let p=h.createEl("button",{text:"Delete",cls:"af-wk-row-btn af-wk-row-btn-danger"});p.onclick=async()=>{if(confirm(`Delete Wiki Keeper "${o.name}"? This removes the agent folder at _fleet/agents/${o.name}/. -Your scope's inbox, topics, index, and log are NOT deleted.`))try{await yn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),await this.plugin.refreshFromVault(),new N.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(m){let y=m instanceof Error?m.message:String(m);new N.Notice(`Failed to delete: ${y}`)}}}new N.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new ya(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},ya=class extends N.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=ma()}input;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:"Add Wiki Keeper"});let s=e.createDiv({cls:"af-wk-banner"});s.createEl("strong",{text:"All fields are optional. "}),s.createSpan({text:"Click Create with everything blank and you'll get a whole-vault keeper with sensible defaults (inbox at _sources/inbox, topics at topics/, nightly ingest at 3am, Sunday lint)."}),e.createEl("h3",{text:"Scope",cls:"af-wk-section-h"}),new N.Setting(e).setName("Scope folder").setDesc("Optional. Vault-relative path. Empty = whole vault.").addText(d=>d.setPlaceholder("(empty = whole vault)").setValue(this.input.scopeRoot).onChange(h=>{this.input.scopeRoot=h.trim(),this.renderPreview()})),new N.Setting(e).setName("Slug").setDesc("Optional. Agent name suffix. Default: derived from scope folder name, or 'wiki-keeper' for whole-vault.").addText(d=>d.setPlaceholder("(auto)").setValue(this.input.scopeSlug).onChange(h=>{this.input.scopeSlug=h.trim(),this.renderPreview()})),this.renderPreview(),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new N.Setting(e).setName("Watched folders").setDesc("Optional. Comma- or newline-separated vault-relative paths. Read-only \u2014 claims are extracted but files are never moved. Leave empty for inbox-only mode (you drop files into _sources/inbox/ and they get archived after processing).").addTextArea(d=>d.setPlaceholder("(empty = inbox-only)").setValue(this.input.watchedFolders.join(", ")).onChange(h=>{this.input.watchedFolders=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new N.Setting(e).setName("Exclude patterns").setDesc("Optional. Glob patterns to skip. Leave empty to process everything under watched + inbox.").addTextArea(d=>d.setPlaceholder("(empty = no excludes)").setValue(this.input.excludePatterns.join(", ")).onChange(h=>{this.input.excludePatterns=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new N.Setting(e).setName("Watch files modified since").setDesc("Watched mode only \u2014 files whose last modification date is before this are skipped. Defaults to today so an established vault doesn't flood the keeper on first run. Clear the field to process everything.").addText(d=>{d.inputEl.type="date",d.setValue(this.input.watchedSince).onChange(h=>{this.input.watchedSince=h.trim()})});let a=this.plugin.runtime.getSnapshot().channels.map(d=>d.name);new N.Setting(e).setName("Heartbeat channel").setDesc("Optional. Slack/Telegram channel for ingest + lint summaries. Leave as (none) to disable.").addDropdown(d=>{d.addOption("","(none)");for(let h of a)d.addOption(h,h);d.setValue(this.input.heartbeatChannel).onChange(h=>{this.input.heartbeatChannel=h})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new N.Setting(e).setName("Max tokens per ingest").setDesc("Hard cap on token spend per run. Unprocessed files resume next cycle.").addText(d=>d.setValue(String(this.input.maxTokensPerIngest)).onChange(h=>{let u=parseInt(h,10);!isNaN(u)&&u>0&&(this.input.maxTokensPerIngest=u)})),new N.Setting(e).setName("File substantive answers").setDesc("If on, wiki-query files long answers under _topics/syntheses/. Off = answers live only in chat.").addToggle(d=>d.setValue(this.input.fileSubstantiveAnswers).onChange(h=>{this.input.fileSubstantiveAnswers=h})),new N.Setting(e).setName("Obsidian URL scheme for citations").setDesc("If on, external-channel (Slack/Telegram) replies rewrite [[wikilinks]] as obsidian:// URLs.").addToggle(d=>d.setValue(this.input.obsidianUrlScheme).onChange(h=>{this.input.obsidianUrlScheme=h}));let n=e.createEl("h3",{text:"Paths (expert)",cls:"af-wk-section-h"});n.title="Paths are relative to scope_root. Defaults work for almost everyone. Override only if your vault layout demands it \u2014 these cannot be changed after creation.";let i=(d,h,u)=>{new N.Setting(e).setName(d).setDesc(h).addText(p=>p.setPlaceholder(String(this.input[u]??"")).setValue(String(this.input[u]??"")).onChange(f=>{this.input[u]=f.trim()||un[u]}))};i("Inbox path","Where one-off source drops land.","inboxPath"),i("Archive path","Where processed inbox files are moved after summarization.","archivePath"),i("Topics root","Folder under scope_root that holds the generated topic pages.","topicsRoot"),i("Index path","Relative file path for the curated index.","indexPath"),i("Log path","Relative file path for the keeper's activity log.","logPath"),i("State file","Hidden JSON file that tracks watched-source mtimes.","stateFile");let o=e.createDiv({cls:"af-wk-modal-footer"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{text:"Create",cls:"mod-cta"});l.onclick=async()=>{l.setText("Creating\u2026"),l.disabled=!0;try{let{name:d}=await fn(this.app.vault,this.plugin.settings.fleetFolder,this.input);await this.plugin.refreshFromVault(),this.close(),new N.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new N.Notice(`Failed to create Wiki Keeper: ${h}`),l.setText("Create"),l.disabled=!1}}}renderPreview(){let e=this.contentEl.querySelector(".af-wk-name-preview");e||(e=this.contentEl.createDiv({cls:"af-wk-name-preview"}));let s=this.input.scopeSlug||this.input.scopeRoot;e.setText(`\u2192 Will create agent: ${fa(s)}`)}onClose(){this.contentEl.empty()}},va=class extends N.Modal{constructor(e,s,a){super(e.app);this.plugin=e;this.agent=s;this.onSaved=a;let n=s.wikiKeeper;this.edit={watchedFolders:[...n.watchedFolders],excludePatterns:[...n.excludePatterns],watchedSince:n.watchedSince,heartbeatChannel:s.heartbeatChannel??"",fileSubstantiveAnswers:n.fileSubstantiveAnswers,obsidianUrlScheme:n.obsidianUrlScheme,maxTokensPerIngest:n.maxTokensPerIngest,maxTokensPerRefresh:n.maxTokensPerRefresh,indexSplitThreshold:n.indexSplitThreshold,dedupSimilarityThreshold:n.dedupSimilarityThreshold,summaryStaleDays:n.summaryStaleDays}}edit;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:`Edit ${this.agent.name}`});let s=e.createDiv({cls:"af-wk-banner"}),a=this.agent.wikiKeeper.scopeRoot||"(whole vault)";s.createEl("strong",{text:"Scope: "}),s.createSpan({text:a}),s.createEl("br"),s.createSpan({text:"Scope and paths are fixed after creation \u2014 changing them would orphan existing topic pages. To move a Wiki Keeper, delete this one and create a new one at the new scope."}),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new N.Setting(e).setName("Watched folders").setDesc("Comma- or newline-separated vault-relative paths.").addTextArea(l=>l.setValue(this.edit.watchedFolders.join(", ")).onChange(d=>{this.edit.watchedFolders=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new N.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(l=>l.setValue(this.edit.excludePatterns.join(", ")).onChange(d=>{this.edit.excludePatterns=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new N.Setting(e).setName("Watch files modified since").setDesc("Watched mode skips files older than this date. Clear to process everything.").addText(l=>{l.inputEl.type="date",l.setValue(this.edit.watchedSince).onChange(d=>{this.edit.watchedSince=d.trim()})});let n=this.plugin.runtime.getSnapshot().channels.map(l=>l.name);new N.Setting(e).setName("Heartbeat channel").addDropdown(l=>{l.addOption("","(none)");for(let d of n)l.addOption(d,d);l.setValue(this.edit.heartbeatChannel).onChange(d=>{this.edit.heartbeatChannel=d})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new N.Setting(e).setName("Max tokens per ingest").addText(l=>l.setValue(String(this.edit.maxTokensPerIngest)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerIngest=h)})),new N.Setting(e).setName("Max tokens per refresh").setDesc("Separate budget for the synthesis-refresh phase that regenerates topic-page summaries.").addText(l=>l.setValue(String(this.edit.maxTokensPerRefresh)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerRefresh=h)})),new N.Setting(e).setName("Index split threshold").setDesc("Topic-page count above which index.md splits into per-type sub-MOCs.").addText(l=>l.setValue(String(this.edit.indexSplitThreshold)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.indexSplitThreshold=h)})),new N.Setting(e).setName("Summary stale (days)").setDesc("Lint flags topic-page summaries older than this and chains wiki-refresh.").addText(l=>l.setValue(String(this.edit.summaryStaleDays)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.summaryStaleDays=h)})),new N.Setting(e).setName("Dedup similarity threshold").setDesc("Levenshtein-ratio above which lint proposes merging two same-type pages (0.0\u20131.0).").addText(l=>l.setValue(String(this.edit.dedupSimilarityThreshold)).onChange(d=>{let h=parseFloat(d);!isNaN(h)&&h>0&&h<=1&&(this.edit.dedupSimilarityThreshold=h)})),new N.Setting(e).setName("File substantive answers").setDesc("Compounding: wiki-query files long answers under _topics/syntheses/ and bullets each cited topic page. Default ON.").addToggle(l=>l.setValue(this.edit.fileSubstantiveAnswers).onChange(d=>{this.edit.fileSubstantiveAnswers=d})),new N.Setting(e).setName("Obsidian URL scheme for citations").setDesc("Rewrite [[wikilinks]] as obsidian:// URLs when replying via Slack/Telegram.").addToggle(l=>l.setValue(this.edit.obsidianUrlScheme).onChange(d=>{this.edit.obsidianUrlScheme=d}));let i=e.createDiv({cls:"af-wk-modal-footer"}),o=i.createEl("button",{text:"Cancel"});o.onclick=()=>this.close();let c=i.createEl("button",{text:"Save",cls:"mod-cta"});c.onclick=async()=>{c.setText("Saving\u2026"),c.disabled=!0;try{await gn(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),await this.plugin.refreshFromVault(),this.close(),new N.Notice("Saved."),this.onSaved()}catch(l){let d=l instanceof Error?l.message:String(l);new N.Notice(`Failed to save: ${d}`),c.setText("Save"),c.disabled=!1}}}onClose(){this.contentEl.empty()}};function yr(r){return r.type==="slack",r.botToken}function vr(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var Dn=require("crypto");function We(r,t,e,s,a,n,i,o){return We.fromTZ(We.tp(r,t,e,s,a,n,i),o)}We.fromTZISO=(r,t,e)=>We.fromTZ(br(r,t),e);We.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=ba(r.tz,e),a=new Date(e.getTime()-s),n=ba(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=ba(r.tz,i);if(o-n===0)return i;if(!t&&o-n>0)return i;if(t)throw new Error("Invalid date passed to fromTZ()");return a}};We.toTZ=function(r,t){let e=r.toLocaleString("en-US",{timeZone:t}).replace(/[\u202f]/," "),s=new Date(e);return{y:s.getFullYear(),m:s.getMonth()+1,d:s.getDate(),h:s.getHours(),i:s.getMinutes(),s:s.getSeconds(),tz:t}};We.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function ba(r,t=new Date){let e=t.toLocaleString("en-US",{timeZone:r,timeZoneName:"shortOffset"}).split(" ").slice(-1)[0],s=t.toLocaleString("en-US").replace(/[\u202f]/," ");return Date.parse(`${s} GMT`)-Date.parse(`${s} ${e}`)}function br(r,t){let e=new Date(Date.parse(r));if(isNaN(e))throw new Error("minitz: Invalid ISO8601 passed to parser.");let s=r.substring(9);return r.includes("Z")||s.includes("-")||s.includes("+")?We.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):We.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}We.minitz=We;function wr(r){if(r===void 0&&(r={}),delete r.name,r.legacyMode=r.legacyMode===void 0?!0:r.legacyMode,r.paused=r.paused===void 0?!1:r.paused,r.maxRuns=r.maxRuns===void 0?1/0:r.maxRuns,r.catch=r.catch===void 0?!1:r.catch,r.interval=r.interval===void 0?0:parseInt(r.interval,10),r.utcOffset=r.utcOffset===void 0?void 0:parseInt(r.utcOffset,10),r.unref=r.unref===void 0?!1:r.unref,r.startAt&&(r.startAt=new Te(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new Te(r.stopAt,r.timezone)),r.interval!==null){if(isNaN(r.interval))throw new Error("CronOptions: Supplied value for interval is not a number");if(r.interval<0)throw new Error("CronOptions: Supplied value for interval can not be negative")}if(r.utcOffset!==void 0){if(isNaN(r.utcOffset))throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");if(r.utcOffset<-870||r.utcOffset>870)throw new Error("CronOptions: utcOffset out of bounds.");if(r.utcOffset!==void 0&&r.timezone)throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.")}if(r.unref!==!0&&r.unref!==!1)throw new Error("CronOptions: Unref should be either true, false or undefined(false).");return r}var wa=32,ss=31|wa,bn=[1,2,4,8,16];function Ge(r,t){this.pattern=r,this.timezone=t,this.second=Array(60).fill(0),this.minute=Array(60).fill(0),this.hour=Array(24).fill(0),this.day=Array(31).fill(0),this.month=Array(12).fill(0),this.dayOfWeek=Array(7).fill(0),this.lastDayOfMonth=!1,this.starDOM=!1,this.starDOW=!1,this.parse()}Ge.prototype.parse=function(){if(!(typeof this.pattern=="string"||this.pattern.constructor===String))throw new TypeError("CronPattern: Pattern has to be of type string.");this.pattern.indexOf("@")>=0&&(this.pattern=this.handleNicknames(this.pattern).trim());let r=this.pattern.replace(/\s+/g," ").split(" ");if(r.length<5||r.length>6)throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exactly five or six space separated parts are required.");if(r.length===5&&r.unshift("0"),r[3].indexOf("L")>=0&&(r[3]=r[3].replace("L",""),this.lastDayOfMonth=!0),r[3]=="*"&&(this.starDOM=!0),r[4].length>=3&&(r[4]=this.replaceAlphaMonths(r[4])),r[5].length>=3&&(r[5]=this.replaceAlphaDays(r[5])),r[5]=="*"&&(this.starDOW=!0),this.pattern.indexOf("?")>=0){let t=new Te(new Date,this.timezone).getDate(!0);r[0]=r[0].replace("?",t.getSeconds()),r[1]=r[1].replace("?",t.getMinutes()),r[2]=r[2].replace("?",t.getHours()),this.starDOM||(r[3]=r[3].replace("?",t.getDate())),r[4]=r[4].replace("?",t.getMonth()+1),this.starDOW||(r[5]=r[5].replace("?",t.getDay()))}this.throwAtIllegalCharacters(r),this.partToArray("second",r[0],0,1),this.partToArray("minute",r[1],0,1),this.partToArray("hour",r[2],0,1),this.partToArray("day",r[3],-1,1),this.partToArray("month",r[4],-1,1),this.partToArray("dayOfWeek",r[5],0,ss),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};Ge.prototype.partToArray=function(r,t,e,s){let a=this[r],n=r==="day"&&this.lastDayOfMonth;if(t===""&&!n)throw new TypeError("CronPattern: configuration entry "+r+" ("+t+") is empty, check for trailing spaces.");if(t==="*")return a.fill(s);let i=t.split(",");if(i.length>1)for(let o=0;o6)&&t!=="L")throw new RangeError("CronPattern: Invalid value for dayOfWeek: "+t);this.setNthWeekdayOfMonth(t,e);return}if(r==="second"||r==="minute"){if(t<0||t>=60)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="hour"){if(t<0||t>=24)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="day"){if(t<0||t>=31)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="month"&&(t<0||t>=12))throw new RangeError("CronPattern: Invalid value for "+r+": "+t);this[r][t]=e};Ge.prototype.handleRangeWithStepping=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].match(/^(\d+)-(\d+)\/(\d+)$/);if(n===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+r+"'");let[,i,o,c]=n;if(i=parseInt(i,10)+e,o=parseInt(o,10)+e,c=parseInt(c,10),isNaN(i))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(c))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(c===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(c>this[t].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[t].length+")");if(i>o)throw new TypeError("CronPattern: From value is larger than to value: '"+r+"'");for(let l=i;l<=o;l+=c)this.setPart(t,l,a[1]||s)};Ge.prototype.extractNth=function(r,t){let e=r,s;if(e.includes("#")){if(t!=="dayOfWeek")throw new Error("CronPattern: nth (#) only allowed in day-of-week field");s=e.split("#")[1],e=e.split("#")[0]}return[e,s]};Ge.prototype.handleRange=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].split("-");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal range: '"+r+"'");let i=parseInt(n[0],10)+e,o=parseInt(n[1],10)+e;if(isNaN(i))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(i>o)throw new TypeError("CronPattern: From value is larger than to value: '"+r+"'");for(let c=i;c<=o;c++)this.setPart(t,c,a[1]||s)};Ge.prototype.handleStepping=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].split("/");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+r+"'");let i=0;n[0]!=="*"&&(i=parseInt(n[0],10)+e);let o=parseInt(n[1],10);if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(o===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(o>this[t].length)throw new TypeError("CronPattern: Syntax error, max steps for part is ("+this[t].length+")");for(let c=i;c0)this.dayOfWeek[r]=this.dayOfWeek[r]|bn[t-1];else if(t===ss)this.dayOfWeek[r]=ss;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var wn=[31,28,31,30,31,30,31,31,30,31,30,31],mt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function Te(r,t){if(this.tz=t,r&&r instanceof Date)if(!isNaN(r))this.fromDate(r);else throw new TypeError("CronDate: Invalid date passed to CronDate constructor");else if(r===void 0)this.fromDate(new Date);else if(r&&typeof r=="string")this.fromString(r);else if(r instanceof Te)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}Te.prototype.isNthWeekdayOfMonth=function(r,t,e,s){let n=new Date(Date.UTC(r,t,e)).getUTCDay(),i=0;for(let o=1;o<=e;o++)new Date(Date.UTC(r,t,o)).getUTCDay()===n&&i++;if(s&ss&&bn[i-1]&s)return!0;if(s&wa){let o=new Date(Date.UTC(r,t+1,0)).getUTCDate();for(let c=e+1;c<=o;c++)if(new Date(Date.UTC(r,t,c)).getUTCDay()===n)return!1;return!0}return!1};Te.prototype.fromDate=function(r){if(this.tz!==void 0)if(typeof this.tz=="number")this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes()+this.tz,this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),this.apply();else{let t=We.toTZ(r,this.tz);this.ms=r.getMilliseconds(),this.second=t.s,this.minute=t.i,this.hour=t.h,this.day=t.d,this.month=t.m-1,this.year=t.y}else this.ms=r.getMilliseconds(),this.second=r.getSeconds(),this.minute=r.getMinutes(),this.hour=r.getHours(),this.day=r.getDate(),this.month=r.getMonth(),this.year=r.getFullYear()};Te.prototype.fromCronDate=function(r){this.tz=r.tz,this.year=r.year,this.month=r.month,this.day=r.day,this.hour=r.hour,this.minute=r.minute,this.second=r.second,this.ms=r.ms};Te.prototype.apply=function(){if(this.month>11||this.day>wn[this.month]||this.hour>59||this.minute>59||this.second>59||this.hour<0||this.minute<0||this.second<0){let r=new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms));return this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes(),this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),!0}else return!1};Te.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=We.fromTZISO(r);this.ms=t.getUTCMilliseconds(),this.second=t.getUTCSeconds(),this.minute=t.getUTCMinutes(),this.hour=t.getUTCHours(),this.day=t.getUTCDate(),this.month=t.getUTCMonth(),this.year=t.getUTCFullYear(),this.apply()}else return this.fromDate(We.fromTZISO(r,this.tz))};Te.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=wn[this.month]:n=new Date(Date.UTC(this.year,this.month+1,0,0,0,0,0)).getUTCDate());let i=!e.starDOW&&t=="day"?new Date(Date.UTC(this.year,this.month,1,0,0,0,0)).getUTCDay():void 0;for(let o=this[t]+s;o1){let a=e+1;for(;a=mt.length?this:this.year>=3e3?null:this.recurse(r,t,e)};Te.prototype.increment=function(r,t,e){return this.second+=t.interval>1&&e?t.interval:1,this.ms=0,this.apply(),this.recurse(r,t,0)};Te.prototype.getDate=function(r){return r||this.tz===void 0?new Date(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms):typeof this.tz=="number"?new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute-this.tz,this.second,this.ms)):We(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};Te.prototype.getTime=function(){return this.getDate().getTime()};function _s(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function kr(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var vn=30*1e3,as=[];function ce(r,t,e){if(!(this instanceof ce))return new ce(r,t,e);let s,a;if(_s(t))a=t;else if(typeof t=="object")s=t;else if(t!==void 0)throw new Error("Cron: Invalid argument passed for optionsIn. Should be one of function, or object (options).");if(_s(e))a=e;else if(typeof e=="object")s=e;else if(e!==void 0)throw new Error("Cron: Invalid argument passed for funcIn. Should be one of function, or object (options).");if(this.name=s?s.name:void 0,this.options=wr(s),this._states={kill:!1,blocking:!1,previousRun:void 0,currentRun:void 0,once:void 0,currentTimeout:void 0,maxRuns:s?s.maxRuns:void 0,paused:s?s.paused:!1,pattern:void 0},r&&(r instanceof Date||typeof r=="string"&&r.indexOf(":")>0)?this._states.once=new Te(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new Ge(r,this.options.timezone),this.name){if(as.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");as.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}ce.prototype.nextRun=function(r){let t=this._next(r);return t?t.getDate():null};ce.prototype.nextRuns=function(r,t){r>this._states.maxRuns&&(r=this._states.maxRuns);let e=[],s=t||this._states.currentRun;for(;r--&&(s=this.nextRun(s));)e.push(s);return e};ce.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};ce.prototype.isRunning=function(){let r=this.nextRun(this._states.currentRun),t=!this._states.paused,e=this.fn!==void 0,s=!this._states.kill;return t&&e&&s&&r!==null};ce.prototype.isStopped=function(){return this._states.kill};ce.prototype.isBusy=function(){return this._states.blocking};ce.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};ce.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};ce.prototype.msToNext=function(r){r=r||new Date;let t=this._next(r);return t?t.getTime()-r.getTime():null};ce.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let r=as.indexOf(this);r>=0&&as.splice(r,1)};ce.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};ce.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};ce.prototype.schedule=function(r){if(r&&this.fn)throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");r&&(this.fn=r);let t=this.msToNext(),e=this.nextRun(this._states.currentRun);return t==null||isNaN(t)||e===null?this:(t>vn&&(t=vn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&kr(this._states.currentTimeout),this)};ce.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new Te(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){_s(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new Te(r,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};ce.prototype.trigger=async function(){await this._trigger()};ce.prototype._checkTrigger=function(r){let t=new Date,e=!this._states.paused&&t.getTime()>=r,s=this._states.blocking&&this.options.protect;e&&!s?(this._states.maxRuns--,this._trigger()):e&&s&&_s(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};ce.prototype._next=function(r){let t=!!(r||this._states.currentRun),e=!1;!r&&this.options.startAt&&this.options.interval&&([r,t]=this._calculatePreviousRun(r,t),e=!r),r=new Te(r,this.options.timezone||this.options.utcOffset),this.options.startAt&&r&&r.getTime()=this.options.stopAt.getTime()?null:s};ce.prototype._calculatePreviousRun=function(r,t){let e=new Te(void 0,this.options.timezone||this.options.utcOffset);if(this.options.startAt.getTime()<=e.getTime()){r=this.options.startAt;let s=r.getTime()+this.options.interval*1e3;for(;s<=e.getTime();)r=new Te(r,this.options.timezone||this.options.utcOffset).increment(this._states.pattern,this.options,!0),s=r.getTime()+this.options.interval*1e3;t=!0}return[r,t]};ce.Cron=ce;ce.scheduledJobs=as;var Pn=require("obsidian");var Sn=require("crypto");var kn=new Set(["","default","subscription"]);function Bt(r,t,e){let s=ka(r?.model);if(s)return{value:xa(s),source:"task"};let a=ka(t.model);if(a)return{value:xa(a),source:"agent"};let n=ka(e.defaultModel);return n?{value:xa(n),source:"settings"}:{value:"",source:"cli-default"}}function As(r){return!kn.has(r.trim())}function ka(r){if(!r)return"";let t=r.trim();return t||""}function xa(r){return kn.has(r)?"":r}function Es(r,t){let e=r.wikiReferences;if(!e||e.length===0)return null;let s=[];for(let n of e){let i=t.getAgentByName(n.agent);if(!i||!i.wikiKeeper)continue;let o=i.wikiKeeper,c=o.scopeRoot?`${o.scopeRoot.replace(/\/+$/,"")}/`:"";s.push({agentName:i.name,scopeRoot:o.scopeRoot||"(whole vault)",topicsPath:`${c}${o.topicsRoot}`,inboxPath:`${c}${o.inboxPath}`,indexPath:`${c}${o.indexPath}`})}if(s.length===0)return null;let a=["## Wiki Access"];a.push("You have read access to the following wikis maintained by other agents. Use the `wiki-query` skill in consumer mode when the user asks something a wiki may already cover."),a.push("");for(let n of s)a.push(`### Wiki: \`${n.agentName}\``),a.push(`- scope root: \`${n.scopeRoot}\``),a.push(`- topics: \`${n.topicsPath}/\``),a.push(`- index: \`${n.indexPath}\``),a.push(`- inbox: \`${n.inboxPath}/\``),a.push("");return a.push("### Rules"),a.push("- **Cite every factual claim** from a wiki using `[[/]]`. If a claim has no page, say the wiki doesn't cover it \u2014 do not fabricate."),a.push("- **When the user shares something durable** that isn't yet in a wiki (a decision, a new entity mention, a competitor change, a meeting outcome), write a short markdown file to the relevant wiki's inbox at `/YYYY-MM-DD-.md` with a one-line note + the source. The wiki keeper files it canonically on its next ingest."),a.push("- **Do NOT write to `/` directly.** The wiki keeper is the canonical curator of topic pages. Use the inbox as your deposit point."),a.push("- **When the question spans multiple wikis**, be explicit about which scope each cited page belongs to."),a.join(` -`)}var Xe=require("fs"),Sa=require("path");function Ds(r,t){let e=t.permissionRules.allow.length>0||t.permissionRules.deny.length>0,s=t.permissionMode?.trim(),a=!!s&&s!=="default",n=(t.mcpServers?.length??0)>0;if(!(e||a||n))return null;let o=(0,Sa.join)(r,".claude"),c=(0,Sa.join)(o,"settings.local.json");(0,Xe.existsSync)(o)||(0,Xe.mkdirSync)(o,{recursive:!0});let l=null;if((0,Xe.existsSync)(c))try{l=(0,Xe.readFileSync)(c,"utf-8")}catch{l=null}let d={};if(a&&(d.defaultMode=s),e||n){let h=[...t.permissionRules.allow];if(n)for(let u of t.mcpServers??[]){let p=u.replace(/[\s.]+/g,"_");h.push(`mcp__${p}`)}d.permissions={allow:h,deny:t.permissionRules.deny}}return(0,Xe.writeFileSync)(c,JSON.stringify(d,null,2)+` +Your scope's inbox, topics, index, and log are NOT deleted.`))try{await yn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),await this.plugin.refreshFromVault(),new N.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(f){let y=f instanceof Error?f.message:String(f);new N.Notice(`Failed to delete: ${y}`)}}}new N.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new va(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},va=class extends N.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=ma()}input;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:"Add Wiki Keeper"});let s=e.createDiv({cls:"af-wk-banner"});s.createEl("strong",{text:"All fields are optional. "}),s.createSpan({text:"Click Create with everything blank and you'll get a whole-vault keeper with sensible defaults (inbox at _sources/inbox, topics at topics/, nightly ingest at 3am, Sunday lint)."}),e.createEl("h3",{text:"Scope",cls:"af-wk-section-h"}),new N.Setting(e).setName("Scope folder").setDesc("Optional. Vault-relative path. Empty = whole vault.").addText(d=>d.setPlaceholder("(empty = whole vault)").setValue(this.input.scopeRoot).onChange(h=>{this.input.scopeRoot=h.trim(),this.renderPreview()})),new N.Setting(e).setName("Slug").setDesc("Optional. Agent name suffix. Default: derived from scope folder name, or 'wiki-keeper' for whole-vault.").addText(d=>d.setPlaceholder("(auto)").setValue(this.input.scopeSlug).onChange(h=>{this.input.scopeSlug=h.trim(),this.renderPreview()})),this.renderPreview(),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new N.Setting(e).setName("Watched folders").setDesc("Optional. Comma- or newline-separated vault-relative paths. Read-only \u2014 claims are extracted but files are never moved. Leave empty for inbox-only mode (you drop files into _sources/inbox/ and they get archived after processing).").addTextArea(d=>d.setPlaceholder("(empty = inbox-only)").setValue(this.input.watchedFolders.join(", ")).onChange(h=>{this.input.watchedFolders=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new N.Setting(e).setName("Exclude patterns").setDesc("Optional. Glob patterns to skip. Leave empty to process everything under watched + inbox.").addTextArea(d=>d.setPlaceholder("(empty = no excludes)").setValue(this.input.excludePatterns.join(", ")).onChange(h=>{this.input.excludePatterns=h.split(/[,\n]/).map(u=>u.trim()).filter(Boolean)})),new N.Setting(e).setName("Watch files modified since").setDesc("Watched mode only \u2014 files whose last modification date is before this are skipped. Defaults to today so an established vault doesn't flood the keeper on first run. Clear the field to process everything.").addText(d=>{d.inputEl.type="date",d.setValue(this.input.watchedSince).onChange(h=>{this.input.watchedSince=h.trim()})});let a=this.plugin.runtime.getSnapshot().channels.map(d=>d.name);new N.Setting(e).setName("Heartbeat channel").setDesc("Optional. Slack/Telegram channel for ingest + lint summaries. Leave as (none) to disable.").addDropdown(d=>{d.addOption("","(none)");for(let h of a)d.addOption(h,h);d.setValue(this.input.heartbeatChannel).onChange(h=>{this.input.heartbeatChannel=h})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new N.Setting(e).setName("Max tokens per ingest").setDesc("Hard cap on token spend per run. Unprocessed files resume next cycle.").addText(d=>d.setValue(String(this.input.maxTokensPerIngest)).onChange(h=>{let u=parseInt(h,10);!isNaN(u)&&u>0&&(this.input.maxTokensPerIngest=u)})),new N.Setting(e).setName("File substantive answers").setDesc("If on, wiki-query files long answers under _topics/syntheses/. Off = answers live only in chat.").addToggle(d=>d.setValue(this.input.fileSubstantiveAnswers).onChange(h=>{this.input.fileSubstantiveAnswers=h})),new N.Setting(e).setName("Obsidian URL scheme for citations").setDesc("If on, external-channel (Slack/Telegram) replies rewrite [[wikilinks]] as obsidian:// URLs.").addToggle(d=>d.setValue(this.input.obsidianUrlScheme).onChange(h=>{this.input.obsidianUrlScheme=h}));let n=e.createEl("h3",{text:"Paths (expert)",cls:"af-wk-section-h"});n.title="Paths are relative to scope_root. Defaults work for almost everyone. Override only if your vault layout demands it \u2014 these cannot be changed after creation.";let i=(d,h,u)=>{new N.Setting(e).setName(d).setDesc(h).addText(p=>p.setPlaceholder(String(this.input[u]??"")).setValue(String(this.input[u]??"")).onChange(m=>{this.input[u]=m.trim()||un[u]}))};i("Inbox path","Where one-off source drops land.","inboxPath"),i("Archive path","Where processed inbox files are moved after summarization.","archivePath"),i("Topics root","Folder under scope_root that holds the generated topic pages.","topicsRoot"),i("Index path","Relative file path for the curated index.","indexPath"),i("Log path","Relative file path for the keeper's activity log.","logPath"),i("State file","Hidden JSON file that tracks watched-source mtimes.","stateFile");let o=e.createDiv({cls:"af-wk-modal-footer"}),c=o.createEl("button",{text:"Cancel"});c.onclick=()=>this.close();let l=o.createEl("button",{text:"Create",cls:"mod-cta"});l.onclick=async()=>{l.setText("Creating\u2026"),l.disabled=!0;try{let{name:d}=await mn(this.app.vault,this.plugin.settings.fleetFolder,this.input);await this.plugin.refreshFromVault(),this.close(),new N.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new N.Notice(`Failed to create Wiki Keeper: ${h}`),l.setText("Create"),l.disabled=!1}}}renderPreview(){let e=this.contentEl.querySelector(".af-wk-name-preview");e||(e=this.contentEl.createDiv({cls:"af-wk-name-preview"}));let s=this.input.scopeSlug||this.input.scopeRoot;e.setText(`\u2192 Will create agent: ${ga(s)}`)}onClose(){this.contentEl.empty()}},ba=class extends N.Modal{constructor(e,s,a){super(e.app);this.plugin=e;this.agent=s;this.onSaved=a;let n=s.wikiKeeper;this.edit={watchedFolders:[...n.watchedFolders],excludePatterns:[...n.excludePatterns],watchedSince:n.watchedSince,heartbeatChannel:s.heartbeatChannel??"",fileSubstantiveAnswers:n.fileSubstantiveAnswers,obsidianUrlScheme:n.obsidianUrlScheme,maxTokensPerIngest:n.maxTokensPerIngest,maxTokensPerRefresh:n.maxTokensPerRefresh,indexSplitThreshold:n.indexSplitThreshold,dedupSimilarityThreshold:n.dedupSimilarityThreshold,summaryStaleDays:n.summaryStaleDays}}edit;onOpen(){let{contentEl:e}=this;e.empty(),e.addClass("af-wk-modal"),e.createEl("h2",{text:`Edit ${this.agent.name}`});let s=e.createDiv({cls:"af-wk-banner"}),a=this.agent.wikiKeeper.scopeRoot||"(whole vault)";s.createEl("strong",{text:"Scope: "}),s.createSpan({text:a}),s.createEl("br"),s.createSpan({text:"Scope and paths are fixed after creation \u2014 changing them would orphan existing topic pages. To move a Wiki Keeper, delete this one and create a new one at the new scope."}),e.createEl("h3",{text:"Sources",cls:"af-wk-section-h"}),new N.Setting(e).setName("Watched folders").setDesc("Comma- or newline-separated vault-relative paths.").addTextArea(l=>l.setValue(this.edit.watchedFolders.join(", ")).onChange(d=>{this.edit.watchedFolders=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new N.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(l=>l.setValue(this.edit.excludePatterns.join(", ")).onChange(d=>{this.edit.excludePatterns=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new N.Setting(e).setName("Watch files modified since").setDesc("Watched mode skips files older than this date. Clear to process everything.").addText(l=>{l.inputEl.type="date",l.setValue(this.edit.watchedSince).onChange(d=>{this.edit.watchedSince=d.trim()})});let n=this.plugin.runtime.getSnapshot().channels.map(l=>l.name);new N.Setting(e).setName("Heartbeat channel").addDropdown(l=>{l.addOption("","(none)");for(let d of n)l.addOption(d,d);l.setValue(this.edit.heartbeatChannel).onChange(d=>{this.edit.heartbeatChannel=d})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new N.Setting(e).setName("Max tokens per ingest").addText(l=>l.setValue(String(this.edit.maxTokensPerIngest)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerIngest=h)})),new N.Setting(e).setName("Max tokens per refresh").setDesc("Separate budget for the synthesis-refresh phase that regenerates topic-page summaries.").addText(l=>l.setValue(String(this.edit.maxTokensPerRefresh)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerRefresh=h)})),new N.Setting(e).setName("Index split threshold").setDesc("Topic-page count above which index.md splits into per-type sub-MOCs.").addText(l=>l.setValue(String(this.edit.indexSplitThreshold)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.indexSplitThreshold=h)})),new N.Setting(e).setName("Summary stale (days)").setDesc("Lint flags topic-page summaries older than this and chains wiki-refresh.").addText(l=>l.setValue(String(this.edit.summaryStaleDays)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.summaryStaleDays=h)})),new N.Setting(e).setName("Dedup similarity threshold").setDesc("Levenshtein-ratio above which lint proposes merging two same-type pages (0.0\u20131.0).").addText(l=>l.setValue(String(this.edit.dedupSimilarityThreshold)).onChange(d=>{let h=parseFloat(d);!isNaN(h)&&h>0&&h<=1&&(this.edit.dedupSimilarityThreshold=h)})),new N.Setting(e).setName("File substantive answers").setDesc("Compounding: wiki-query files long answers under _topics/syntheses/ and bullets each cited topic page. Default ON.").addToggle(l=>l.setValue(this.edit.fileSubstantiveAnswers).onChange(d=>{this.edit.fileSubstantiveAnswers=d})),new N.Setting(e).setName("Obsidian URL scheme for citations").setDesc("Rewrite [[wikilinks]] as obsidian:// URLs when replying via Slack/Telegram.").addToggle(l=>l.setValue(this.edit.obsidianUrlScheme).onChange(d=>{this.edit.obsidianUrlScheme=d}));let i=e.createDiv({cls:"af-wk-modal-footer"}),o=i.createEl("button",{text:"Cancel"});o.onclick=()=>this.close();let c=i.createEl("button",{text:"Save",cls:"mod-cta"});c.onclick=async()=>{c.setText("Saving\u2026"),c.disabled=!0;try{await gn(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),await this.plugin.refreshFromVault(),this.close(),new N.Notice("Saved."),this.onSaved()}catch(l){let d=l instanceof Error?l.message:String(l);new N.Notice(`Failed to save: ${d}`),c.setText("Save"),c.disabled=!1}}}onClose(){this.contentEl.empty()}};function yr(r){return r.type==="slack",r.botToken}function vr(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var Pn=require("crypto");function We(r,t,e,s,a,n,i,o){return We.fromTZ(We.tp(r,t,e,s,a,n,i),o)}We.fromTZISO=(r,t,e)=>We.fromTZ(br(r,t),e);We.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=wa(r.tz,e),a=new Date(e.getTime()-s),n=wa(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=wa(r.tz,i);if(o-n===0)return i;if(!t&&o-n>0)return i;if(t)throw new Error("Invalid date passed to fromTZ()");return a}};We.toTZ=function(r,t){let e=r.toLocaleString("en-US",{timeZone:t}).replace(/[\u202f]/," "),s=new Date(e);return{y:s.getFullYear(),m:s.getMonth()+1,d:s.getDate(),h:s.getHours(),i:s.getMinutes(),s:s.getSeconds(),tz:t}};We.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function wa(r,t=new Date){let e=t.toLocaleString("en-US",{timeZone:r,timeZoneName:"shortOffset"}).split(" ").slice(-1)[0],s=t.toLocaleString("en-US").replace(/[\u202f]/," ");return Date.parse(`${s} GMT`)-Date.parse(`${s} ${e}`)}function br(r,t){let e=new Date(Date.parse(r));if(isNaN(e))throw new Error("minitz: Invalid ISO8601 passed to parser.");let s=r.substring(9);return r.includes("Z")||s.includes("-")||s.includes("+")?We.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):We.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}We.minitz=We;function wr(r){if(r===void 0&&(r={}),delete r.name,r.legacyMode=r.legacyMode===void 0?!0:r.legacyMode,r.paused=r.paused===void 0?!1:r.paused,r.maxRuns=r.maxRuns===void 0?1/0:r.maxRuns,r.catch=r.catch===void 0?!1:r.catch,r.interval=r.interval===void 0?0:parseInt(r.interval,10),r.utcOffset=r.utcOffset===void 0?void 0:parseInt(r.utcOffset,10),r.unref=r.unref===void 0?!1:r.unref,r.startAt&&(r.startAt=new Te(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new Te(r.stopAt,r.timezone)),r.interval!==null){if(isNaN(r.interval))throw new Error("CronOptions: Supplied value for interval is not a number");if(r.interval<0)throw new Error("CronOptions: Supplied value for interval can not be negative")}if(r.utcOffset!==void 0){if(isNaN(r.utcOffset))throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");if(r.utcOffset<-870||r.utcOffset>870)throw new Error("CronOptions: utcOffset out of bounds.");if(r.utcOffset!==void 0&&r.timezone)throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.")}if(r.unref!==!0&&r.unref!==!1)throw new Error("CronOptions: Unref should be either true, false or undefined(false).");return r}var ka=32,ss=31|ka,bn=[1,2,4,8,16];function Ge(r,t){this.pattern=r,this.timezone=t,this.second=Array(60).fill(0),this.minute=Array(60).fill(0),this.hour=Array(24).fill(0),this.day=Array(31).fill(0),this.month=Array(12).fill(0),this.dayOfWeek=Array(7).fill(0),this.lastDayOfMonth=!1,this.starDOM=!1,this.starDOW=!1,this.parse()}Ge.prototype.parse=function(){if(!(typeof this.pattern=="string"||this.pattern.constructor===String))throw new TypeError("CronPattern: Pattern has to be of type string.");this.pattern.indexOf("@")>=0&&(this.pattern=this.handleNicknames(this.pattern).trim());let r=this.pattern.replace(/\s+/g," ").split(" ");if(r.length<5||r.length>6)throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exactly five or six space separated parts are required.");if(r.length===5&&r.unshift("0"),r[3].indexOf("L")>=0&&(r[3]=r[3].replace("L",""),this.lastDayOfMonth=!0),r[3]=="*"&&(this.starDOM=!0),r[4].length>=3&&(r[4]=this.replaceAlphaMonths(r[4])),r[5].length>=3&&(r[5]=this.replaceAlphaDays(r[5])),r[5]=="*"&&(this.starDOW=!0),this.pattern.indexOf("?")>=0){let t=new Te(new Date,this.timezone).getDate(!0);r[0]=r[0].replace("?",t.getSeconds()),r[1]=r[1].replace("?",t.getMinutes()),r[2]=r[2].replace("?",t.getHours()),this.starDOM||(r[3]=r[3].replace("?",t.getDate())),r[4]=r[4].replace("?",t.getMonth()+1),this.starDOW||(r[5]=r[5].replace("?",t.getDay()))}this.throwAtIllegalCharacters(r),this.partToArray("second",r[0],0,1),this.partToArray("minute",r[1],0,1),this.partToArray("hour",r[2],0,1),this.partToArray("day",r[3],-1,1),this.partToArray("month",r[4],-1,1),this.partToArray("dayOfWeek",r[5],0,ss),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};Ge.prototype.partToArray=function(r,t,e,s){let a=this[r],n=r==="day"&&this.lastDayOfMonth;if(t===""&&!n)throw new TypeError("CronPattern: configuration entry "+r+" ("+t+") is empty, check for trailing spaces.");if(t==="*")return a.fill(s);let i=t.split(",");if(i.length>1)for(let o=0;o6)&&t!=="L")throw new RangeError("CronPattern: Invalid value for dayOfWeek: "+t);this.setNthWeekdayOfMonth(t,e);return}if(r==="second"||r==="minute"){if(t<0||t>=60)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="hour"){if(t<0||t>=24)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="day"){if(t<0||t>=31)throw new RangeError("CronPattern: Invalid value for "+r+": "+t)}else if(r==="month"&&(t<0||t>=12))throw new RangeError("CronPattern: Invalid value for "+r+": "+t);this[r][t]=e};Ge.prototype.handleRangeWithStepping=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].match(/^(\d+)-(\d+)\/(\d+)$/);if(n===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+r+"'");let[,i,o,c]=n;if(i=parseInt(i,10)+e,o=parseInt(o,10)+e,c=parseInt(c,10),isNaN(i))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(isNaN(c))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(c===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(c>this[t].length)throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part ("+this[t].length+")");if(i>o)throw new TypeError("CronPattern: From value is larger than to value: '"+r+"'");for(let l=i;l<=o;l+=c)this.setPart(t,l,a[1]||s)};Ge.prototype.extractNth=function(r,t){let e=r,s;if(e.includes("#")){if(t!=="dayOfWeek")throw new Error("CronPattern: nth (#) only allowed in day-of-week field");s=e.split("#")[1],e=e.split("#")[0]}return[e,s]};Ge.prototype.handleRange=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].split("-");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal range: '"+r+"'");let i=parseInt(n[0],10)+e,o=parseInt(n[1],10)+e;if(isNaN(i))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(i>o)throw new TypeError("CronPattern: From value is larger than to value: '"+r+"'");for(let c=i;c<=o;c++)this.setPart(t,c,a[1]||s)};Ge.prototype.handleStepping=function(r,t,e,s){let a=this.extractNth(r,t),n=a[0].split("/");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+r+"'");let i=0;n[0]!=="*"&&(i=parseInt(n[0],10)+e);let o=parseInt(n[1],10);if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(o===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(o>this[t].length)throw new TypeError("CronPattern: Syntax error, max steps for part is ("+this[t].length+")");for(let c=i;c0)this.dayOfWeek[r]=this.dayOfWeek[r]|bn[t-1];else if(t===ss)this.dayOfWeek[r]=ss;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var wn=[31,28,31,30,31,30,31,31,30,31,30,31],ft=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function Te(r,t){if(this.tz=t,r&&r instanceof Date)if(!isNaN(r))this.fromDate(r);else throw new TypeError("CronDate: Invalid date passed to CronDate constructor");else if(r===void 0)this.fromDate(new Date);else if(r&&typeof r=="string")this.fromString(r);else if(r instanceof Te)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}Te.prototype.isNthWeekdayOfMonth=function(r,t,e,s){let n=new Date(Date.UTC(r,t,e)).getUTCDay(),i=0;for(let o=1;o<=e;o++)new Date(Date.UTC(r,t,o)).getUTCDay()===n&&i++;if(s&ss&&bn[i-1]&s)return!0;if(s&ka){let o=new Date(Date.UTC(r,t+1,0)).getUTCDate();for(let c=e+1;c<=o;c++)if(new Date(Date.UTC(r,t,c)).getUTCDay()===n)return!1;return!0}return!1};Te.prototype.fromDate=function(r){if(this.tz!==void 0)if(typeof this.tz=="number")this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes()+this.tz,this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),this.apply();else{let t=We.toTZ(r,this.tz);this.ms=r.getMilliseconds(),this.second=t.s,this.minute=t.i,this.hour=t.h,this.day=t.d,this.month=t.m-1,this.year=t.y}else this.ms=r.getMilliseconds(),this.second=r.getSeconds(),this.minute=r.getMinutes(),this.hour=r.getHours(),this.day=r.getDate(),this.month=r.getMonth(),this.year=r.getFullYear()};Te.prototype.fromCronDate=function(r){this.tz=r.tz,this.year=r.year,this.month=r.month,this.day=r.day,this.hour=r.hour,this.minute=r.minute,this.second=r.second,this.ms=r.ms};Te.prototype.apply=function(){if(this.month>11||this.day>wn[this.month]||this.hour>59||this.minute>59||this.second>59||this.hour<0||this.minute<0||this.second<0){let r=new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms));return this.ms=r.getUTCMilliseconds(),this.second=r.getUTCSeconds(),this.minute=r.getUTCMinutes(),this.hour=r.getUTCHours(),this.day=r.getUTCDate(),this.month=r.getUTCMonth(),this.year=r.getUTCFullYear(),!0}else return!1};Te.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=We.fromTZISO(r);this.ms=t.getUTCMilliseconds(),this.second=t.getUTCSeconds(),this.minute=t.getUTCMinutes(),this.hour=t.getUTCHours(),this.day=t.getUTCDate(),this.month=t.getUTCMonth(),this.year=t.getUTCFullYear(),this.apply()}else return this.fromDate(We.fromTZISO(r,this.tz))};Te.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=wn[this.month]:n=new Date(Date.UTC(this.year,this.month+1,0,0,0,0,0)).getUTCDate());let i=!e.starDOW&&t=="day"?new Date(Date.UTC(this.year,this.month,1,0,0,0,0)).getUTCDay():void 0;for(let o=this[t]+s;o1){let a=e+1;for(;a=ft.length?this:this.year>=3e3?null:this.recurse(r,t,e)};Te.prototype.increment=function(r,t,e){return this.second+=t.interval>1&&e?t.interval:1,this.ms=0,this.apply(),this.recurse(r,t,0)};Te.prototype.getDate=function(r){return r||this.tz===void 0?new Date(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms):typeof this.tz=="number"?new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute-this.tz,this.second,this.ms)):We(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};Te.prototype.getTime=function(){return this.getDate().getTime()};function _s(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function kr(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var vn=30*1e3,as=[];function ce(r,t,e){if(!(this instanceof ce))return new ce(r,t,e);let s,a;if(_s(t))a=t;else if(typeof t=="object")s=t;else if(t!==void 0)throw new Error("Cron: Invalid argument passed for optionsIn. Should be one of function, or object (options).");if(_s(e))a=e;else if(typeof e=="object")s=e;else if(e!==void 0)throw new Error("Cron: Invalid argument passed for funcIn. Should be one of function, or object (options).");if(this.name=s?s.name:void 0,this.options=wr(s),this._states={kill:!1,blocking:!1,previousRun:void 0,currentRun:void 0,once:void 0,currentTimeout:void 0,maxRuns:s?s.maxRuns:void 0,paused:s?s.paused:!1,pattern:void 0},r&&(r instanceof Date||typeof r=="string"&&r.indexOf(":")>0)?this._states.once=new Te(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new Ge(r,this.options.timezone),this.name){if(as.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");as.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}ce.prototype.nextRun=function(r){let t=this._next(r);return t?t.getDate():null};ce.prototype.nextRuns=function(r,t){r>this._states.maxRuns&&(r=this._states.maxRuns);let e=[],s=t||this._states.currentRun;for(;r--&&(s=this.nextRun(s));)e.push(s);return e};ce.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};ce.prototype.isRunning=function(){let r=this.nextRun(this._states.currentRun),t=!this._states.paused,e=this.fn!==void 0,s=!this._states.kill;return t&&e&&s&&r!==null};ce.prototype.isStopped=function(){return this._states.kill};ce.prototype.isBusy=function(){return this._states.blocking};ce.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};ce.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};ce.prototype.msToNext=function(r){r=r||new Date;let t=this._next(r);return t?t.getTime()-r.getTime():null};ce.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let r=as.indexOf(this);r>=0&&as.splice(r,1)};ce.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};ce.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};ce.prototype.schedule=function(r){if(r&&this.fn)throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");r&&(this.fn=r);let t=this.msToNext(),e=this.nextRun(this._states.currentRun);return t==null||isNaN(t)||e===null?this:(t>vn&&(t=vn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&kr(this._states.currentTimeout),this)};ce.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new Te(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){_s(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new Te(r,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};ce.prototype.trigger=async function(){await this._trigger()};ce.prototype._checkTrigger=function(r){let t=new Date,e=!this._states.paused&&t.getTime()>=r,s=this._states.blocking&&this.options.protect;e&&!s?(this._states.maxRuns--,this._trigger()):e&&s&&_s(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};ce.prototype._next=function(r){let t=!!(r||this._states.currentRun),e=!1;!r&&this.options.startAt&&this.options.interval&&([r,t]=this._calculatePreviousRun(r,t),e=!r),r=new Te(r,this.options.timezone||this.options.utcOffset),this.options.startAt&&r&&r.getTime()=this.options.stopAt.getTime()?null:s};ce.prototype._calculatePreviousRun=function(r,t){let e=new Te(void 0,this.options.timezone||this.options.utcOffset);if(this.options.startAt.getTime()<=e.getTime()){r=this.options.startAt;let s=r.getTime()+this.options.interval*1e3;for(;s<=e.getTime();)r=new Te(r,this.options.timezone||this.options.utcOffset).increment(this._states.pattern,this.options,!0),s=r.getTime()+this.options.interval*1e3;t=!0}return[r,t]};ce.Cron=ce;ce.scheduledJobs=as;var Dn=require("obsidian");var Sn=require("crypto");var kn=new Set(["","default","subscription"]);function Bt(r,t,e){let s=xa(r?.model);if(s)return{value:Sa(s),source:"task"};let a=xa(t.model);if(a)return{value:Sa(a),source:"agent"};let n=xa(e.defaultModel);return n?{value:Sa(n),source:"settings"}:{value:"",source:"cli-default"}}function As(r){return!kn.has(r.trim())}function xa(r){if(!r)return"";let t=r.trim();return t||""}function Sa(r){return kn.has(r)?"":r}function Es(r,t){let e=r.wikiReferences;if(!e||e.length===0)return null;let s=[];for(let n of e){let i=t.getAgentByName(n.agent);if(!i||!i.wikiKeeper)continue;let o=i.wikiKeeper,c=o.scopeRoot?`${o.scopeRoot.replace(/\/+$/,"")}/`:"";s.push({agentName:i.name,scopeRoot:o.scopeRoot||"(whole vault)",topicsPath:`${c}${o.topicsRoot}`,inboxPath:`${c}${o.inboxPath}`,indexPath:`${c}${o.indexPath}`})}if(s.length===0)return null;let a=["## Wiki Access"];a.push("You have read access to the following wikis maintained by other agents. Use the `wiki-query` skill in consumer mode when the user asks something a wiki may already cover."),a.push("");for(let n of s)a.push(`### Wiki: \`${n.agentName}\``),a.push(`- scope root: \`${n.scopeRoot}\``),a.push(`- topics: \`${n.topicsPath}/\``),a.push(`- index: \`${n.indexPath}\``),a.push(`- inbox: \`${n.inboxPath}/\``),a.push("");return a.push("### Rules"),a.push("- **Cite every factual claim** from a wiki using `[[/]]`. If a claim has no page, say the wiki doesn't cover it \u2014 do not fabricate."),a.push("- **When the user shares something durable** that isn't yet in a wiki (a decision, a new entity mention, a competitor change, a meeting outcome), write a short markdown file to the relevant wiki's inbox at `/YYYY-MM-DD-.md` with a one-line note + the source. The wiki keeper files it canonically on its next ingest."),a.push("- **Do NOT write to `/` directly.** The wiki keeper is the canonical curator of topic pages. Use the inbox as your deposit point."),a.push("- **When the question spans multiple wikis**, be explicit about which scope each cited page belongs to."),a.join(` +`)}var Xe=require("fs"),Ca=require("path");function Ps(r,t){let e=t.permissionRules.allow.length>0||t.permissionRules.deny.length>0,s=t.permissionMode?.trim(),a=!!s&&s!=="default",n=(t.mcpServers?.length??0)>0;if(!(e||a||n))return null;let o=(0,Ca.join)(r,".claude"),c=(0,Ca.join)(o,"settings.local.json");(0,Xe.existsSync)(o)||(0,Xe.mkdirSync)(o,{recursive:!0});let l=null;if((0,Xe.existsSync)(c))try{l=(0,Xe.readFileSync)(c,"utf-8")}catch{l=null}let d={};if(a&&(d.defaultMode=s),e||n){let h=[...t.permissionRules.allow];if(n)for(let u of t.mcpServers??[]){let p=u.replace(/[\s.]+/g,"_");h.push(`mcp__${p}`)}d.permissions={allow:h,deny:t.permissionRules.deny}}return(0,Xe.writeFileSync)(c,JSON.stringify(d,null,2)+` `,"utf-8"),{path:c,backupContent:l}}function Et(r){if(r)try{r.backupContent!==null?(0,Xe.writeFileSync)(r.path,r.backupContent,"utf-8"):(0,Xe.existsSync)(r.path)&&(0,Xe.unlinkSync)(r.path)}catch{}}function Cn(r){if(typeof r=="string")return r;if(Array.isArray(r))return r.map(e=>typeof e=="string"?e:e&&typeof e=="object"&&"text"in e&&typeof e.text=="string"?e.text:"").filter(Boolean).join(` -`);if(r&&typeof r=="object"){for(let t of["output","result","text","message"])if(t in r)return Cn(r[t])}}function Ca(r,t=[]){if(Array.isArray(r)){for(let i of r)Ca(i,t);return t}if(!r||typeof r!="object")return t;let e=r,s=typeof e.tool_name=="string"&&e.tool_name||typeof e.tool=="string"&&e.tool||typeof e.name=="string"&&e.name,a=typeof e.command=="string"?e.command:typeof e.input=="string"?e.input:typeof e.cmd=="string"?e.cmd:void 0,n=typeof e.reason=="string"?e.reason:void 0;s&&["tool_use","tool","name","tool_name"].some(i=>i in e)&&t.push({tool:s,command:a,reason:n});for(let i of Object.values(e))Ca(i,t);return t}function Tn(r){if(!r||typeof r!="object")return;let t=r,e=t.usage;if(e&&typeof e=="object"){let a=typeof e.input_tokens=="number"?e.input_tokens:0,n=typeof e.output_tokens=="number"?e.output_tokens:0,i=typeof e.cache_creation_input_tokens=="number"?e.cache_creation_input_tokens:0,o=typeof e.cache_read_input_tokens=="number"?e.cache_read_input_tokens:0,c=a+n+i+o;if(c>0)return c}let s=t.modelUsage;if(s&&typeof s=="object"){let a=0;for(let n of Object.values(s)){if(!n||typeof n!="object")continue;let i=n;a+=typeof i.inputTokens=="number"?i.inputTokens:0,a+=typeof i.outputTokens=="number"?i.outputTokens:0,a+=typeof i.cacheReadInputTokens=="number"?i.cacheReadInputTokens:0,a+=typeof i.cacheCreationInputTokens=="number"?i.cacheCreationInputTokens:0}if(a>0)return a}for(let a of["tokens_used","total_tokens","totalTokens"])if(typeof t[a]=="number")return t[a];for(let a of Object.values(t)){let n=Tn(a);if(typeof n=="number")return n}}function _n(r){if(!r||typeof r!="object")return;let t=r;if(typeof t.total_cost_usd=="number")return t.total_cost_usd;for(let e of Object.values(t)){let s=_n(e);if(typeof s=="number")return s}}function xn(r){if(!r||typeof r!="object")return;let t=r;if(t.type==="result"&&typeof t.result=="string"&&t.result.trim())return t.result}function Ta(r){if(!r||typeof r!="object")return;let t=r;if(t.modelUsage&&typeof t.modelUsage=="object"){let s=Object.keys(t.modelUsage);if(s.length>0&&s[0])return s[0]}let e=t.message;if(e&&typeof e.model=="string"&&e.model)return e.model;if(typeof t.model=="string"&&t.model)return t.model;for(let s of Object.values(t)){let a=Ta(s);if(a)return a}}function An(r){let t=r.matchAll(/\[REMEMBER\]([\s\S]*?)\[\/REMEMBER\]/g);return Array.from(t).map(e=>e[1]?.trim()??"").filter(Boolean)}var Ps=class{constructor(t,e){this.settings=t;this.repository=e}runningProcesses=new Map;abortAgent(t){let e=this.runningProcesses.get(t);return e?(e.kill(),this.runningProcesses.delete(t),!0):!1}extractStreamContent(t){let e=t.type;if(e==="assistant"){let s=t.message;if(s?.content&&Array.isArray(s.content)){let a=[];for(let n of s.content)if(n.type==="text"&&typeof n.text=="string")a.push(n.text);else if(n.type==="tool_use"){let i=String(n.name??"tool"),o=n.input,c=o?.command??o?.content??"";a.push(` +`);if(r&&typeof r=="object"){for(let t of["output","result","text","message"])if(t in r)return Cn(r[t])}}function Ta(r,t=[]){if(Array.isArray(r)){for(let i of r)Ta(i,t);return t}if(!r||typeof r!="object")return t;let e=r,s=typeof e.tool_name=="string"&&e.tool_name||typeof e.tool=="string"&&e.tool||typeof e.name=="string"&&e.name,a=typeof e.command=="string"?e.command:typeof e.input=="string"?e.input:typeof e.cmd=="string"?e.cmd:void 0,n=typeof e.reason=="string"?e.reason:void 0;s&&["tool_use","tool","name","tool_name"].some(i=>i in e)&&t.push({tool:s,command:a,reason:n});for(let i of Object.values(e))Ta(i,t);return t}function Tn(r){if(!r||typeof r!="object")return;let t=r,e=t.usage;if(e&&typeof e=="object"){let a=typeof e.input_tokens=="number"?e.input_tokens:0,n=typeof e.output_tokens=="number"?e.output_tokens:0,i=typeof e.cache_creation_input_tokens=="number"?e.cache_creation_input_tokens:0,o=typeof e.cache_read_input_tokens=="number"?e.cache_read_input_tokens:0,c=a+n+i+o;if(c>0)return c}let s=t.modelUsage;if(s&&typeof s=="object"){let a=0;for(let n of Object.values(s)){if(!n||typeof n!="object")continue;let i=n;a+=typeof i.inputTokens=="number"?i.inputTokens:0,a+=typeof i.outputTokens=="number"?i.outputTokens:0,a+=typeof i.cacheReadInputTokens=="number"?i.cacheReadInputTokens:0,a+=typeof i.cacheCreationInputTokens=="number"?i.cacheCreationInputTokens:0}if(a>0)return a}for(let a of["tokens_used","total_tokens","totalTokens"])if(typeof t[a]=="number")return t[a];for(let a of Object.values(t)){let n=Tn(a);if(typeof n=="number")return n}}function _n(r){if(!r||typeof r!="object")return;let t=r;if(typeof t.total_cost_usd=="number")return t.total_cost_usd;for(let e of Object.values(t)){let s=_n(e);if(typeof s=="number")return s}}function xn(r){if(!r||typeof r!="object")return;let t=r;if(t.type==="result"&&typeof t.result=="string"&&t.result.trim())return t.result}function _a(r){if(!r||typeof r!="object")return;let t=r;if(t.modelUsage&&typeof t.modelUsage=="object"){let s=Object.keys(t.modelUsage);if(s.length>0&&s[0])return s[0]}let e=t.message;if(e&&typeof e.model=="string"&&e.model)return e.model;if(typeof t.model=="string"&&t.model)return t.model;for(let s of Object.values(t)){let a=_a(s);if(a)return a}}function An(r){let t=r.matchAll(/\[REMEMBER\]([\s\S]*?)\[\/REMEMBER\]/g);return Array.from(t).map(e=>e[1]?.trim()??"").filter(Boolean)}var Ds=class{constructor(t,e){this.settings=t;this.repository=e}runningProcesses=new Map;abortAgent(t){let e=this.runningProcesses.get(t);return e?(e.kill(),this.runningProcesses.delete(t),!0):!1}extractStreamContent(t){let e=t.type;if(e==="assistant"){let s=t.message;if(s?.content&&Array.isArray(s.content)){let a=[];for(let n of s.content)if(n.type==="text"&&typeof n.text=="string")a.push(n.text);else if(n.type==="tool_use"){let i=String(n.name??"tool"),o=n.input,c=o?.command??o?.content??"";a.push(` \u25B8 ${i}${c?`: ${String(c).slice(0,200)}`:""} `)}if(a.length>0)return a.join("")}}if(e==="result"){let s=typeof t.result=="string"?t.result:null;if(s)return` ${s}`}return null}async buildPrompt(t,e,s){let a=[t.body.trim()];for(let i of t.skills){let o=this.repository.getSkillByName(i);if(o){let c=[o.body.trim()];o.toolsBody.trim()&&c.push(`### Tools @@ -11787,11 +11787,11 @@ ${t.contextBody.trim()}`),t.memory){let i=await this.repository.getMemory(t.name ${i.body.trim()}`)}let n=Es(t,this.repository);return n&&a.push(n),a.push(`## Task ${(s??e.body).trim()}`),a.filter(Boolean).join(` -`)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),i=(0,Sn.randomUUID)(),o=Bt(e,t,this.settings),c=a!=null,l=["-p",n,"--output-format",c?"stream-json":"json"];c&&l.push("--verbose"),As(o.value)&&l.push("--model",o.value);let d=e.effort||t.effort;d&&l.push("--effort",d);let h=t.permissionMode?.trim();h&&h!=="default"?l.push("--permission-mode",h):l.push("--permission-mode","bypassPermissions");let u=t.cwd??this.repository.getVaultBasePath()??".",p=Ds(u,t),f=Date.now(),m=()=>Et(p);try{return await new Promise((y,k)=>{let b=pt(this.settings.claudeCliPath,l,{cwd:u,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.runningProcesses.set(t.name,b);let v="",g="",x=!1,T=setTimeout(()=>{x=!0,b.kill()},t.timeout*1e3);b.stdout.on("data",_=>{let R=_.toString();if(v+=R,c&&a)for(let A of fe(R)){let S=A.trim();if(S)try{let L=JSON.parse(S),E=this.extractStreamContent(L);E&&a(E)}catch{}}}),b.stderr.on("data",_=>{g+=_.toString()}),b.on("error",_=>{clearTimeout(T),k(_)}),b.on("close",_=>{clearTimeout(T),this.runningProcesses.delete(t.name);let R=v.trim(),A;if(c){let F=fe(R);for(let $=F.length-1;$>=0;$--){let X=F[$]?.trim();if(X)try{let z=JSON.parse(X);if(z&&typeof z=="object"){A=z;break}}catch{}}}else if(R.startsWith("{")||R.startsWith("["))try{A=JSON.parse(R)}catch{A=void 0}let S=Cn(A)??"";if(!S&&c){let F=[];for(let $ of fe(R)){let X=$.trim();if(X)try{let z=JSON.parse(X);if(z.type==="assistant"&&z.message?.content)for(let ie of z.message.content)ie.type==="text"&&ie.text&&F.push(ie.text);else z.type==="result"&&typeof z.result=="string"&&F.push(z.result)}catch{}}S=F.join(` -`).trim()}S||(S=g.trim()||"(no output)");let L=Ca(A),E=Tn(A),B=_n(A),I=Ta(A),q=xn(A);if((!I||!q)&&c)for(let F of fe(R)){let $=F.trim();if($)try{let X=JSON.parse($);if(!I){let z=Ta(X);z&&(I=z)}if(!q){let z=xn(X);z&&(q=z)}if(I&&q)break}catch{}}y({runId:i,prompt:n,exitCode:_,durationSeconds:Math.max(1,Math.round((Date.now()-f)/1e3)),stdout:v,stderr:g,outputText:S,rawJson:A,tokensUsed:E,costUsd:B,toolsUsed:L,timedOut:x,resolvedModel:o.value,modelSource:o.source,concreteModel:I,finalResult:q})})})}finally{m()}}};var ns=Ke(require("crypto")),En=Ke(require("https")),Rs=Ke(require("http")),ze=Ke(require("fs")),Dt=Ke(require("path"));var Is=class{constructor(t){this.settings=t}cache=null;loadPromise=null;progressListeners=[];authManager;setAuthManager(t){this.authManager=t}getCachedServers(){return this.cache}onProgress(t){return this.progressListeners.push(t),()=>{this.progressListeners=this.progressListeners.filter(e=>e!==t)}}emitProgress(t){for(let e of this.progressListeners)try{e(t)}catch{}}async getServers(t=!1){return this.cache!==null&&!t?this.cache:this.loadPromise&&!t?this.loadPromise:(this.loadPromise=this.loadServers().then(e=>(this.cache=e,this.loadPromise=null,e)),this.loadPromise)}invalidateCache(){this.cache=null,this.loadPromise=null}async toggleServerEnabled(t,e){let s=Dt.join(Zt(),"settings.local.json"),a={};try{let o=ze.readFileSync(s,"utf8");a=JSON.parse(o)}catch{}let n=a.disabledMcpjsonServers??[],i=this.toInternalName(t);if(e?(a.disabledMcpjsonServers=n.filter(o=>o!==i),this.enableServerInClaudeConfig(t)):n.includes(i)||(n.push(i),a.disabledMcpjsonServers=n),ze.writeFileSync(s,JSON.stringify(a,null,2)),this.cache){let o=this.cache.find(c=>c.name===t);o&&(o.enabled=e)}}toInternalName(t){return t.replace(/\./g,"_").replace(/\s+/g,"_")}async addServer(t){let e=["mcp","add","-s",t.scope??"user"];if(t.transport==="stdio"){if(t.envVars)for(let[s,a]of Object.entries(t.envVars))e.push("-e",`${s}=${a}`);e.push(t.name,"--"),t.command&&e.push(t.command),t.args&&e.push(...t.args)}else if(e.push("-t",t.transport,t.name),t.url&&e.push(t.url),t.headers)for(let[s,a]of Object.entries(t.headers))e.push("-H",`${s}: ${a}`);await this.runCliArgs(e),this.enableServerInClaudeConfig(t.name),this.invalidateCache()}async removeServer(t,e){let s=["mcp","remove"];e&&s.push("-s",e),s.push(t),await this.runCliArgs(s),this.authManager?.removeToken(t),this.invalidateCache()}async authenticateServer(t,e,s="http"){let a=await this.discoverOAuthMetadata(e);if(!a)throw new Error("Server does not support OAuth \u2014 no authorization metadata found.");let{metadata:n,resourceScopes:i,resource:o}=a;if(!n.registration_endpoint)throw new Error("Server does not support Dynamic Client Registration.");let c=await this.startOAuthCallbackServer(),l=`http://localhost:${c.port}/callback`;try{let d=await this.registerOAuthClient(n.registration_endpoint,l),h=ns.randomBytes(32).toString("base64url"),u=ns.createHash("sha256").update(h).digest("base64url"),p=ns.randomBytes(16).toString("hex"),f=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:l,state:p,resource:o}),m=i??n.scopes_supported;m?.length&&f.set("scope",m.join(" "));let y=new URL(n.authorization_endpoint);for(let[g,x]of f)y.searchParams.set(g,x);let k=y.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",k),nn(k);let b=await c.waitForCode(p,18e4),v=await this.exchangeOAuthCode(n.token_endpoint,b,l,d,h);this.injectTokenIntoClaudeConfig(t,v.access_token),this.authManager&&this.authManager.storeOAuthToken(t,{accessToken:v.access_token,refreshToken:v.refresh_token,expiresAt:v.expires_in?Date.now()+v.expires_in*1e3:void 0,tokenEndpoint:n.token_endpoint,clientId:d,resource:o}),this.clearNeedsAuthCache(t),this.enableServerInClaudeConfig(t),this.invalidateCache()}finally{c.close()}}async extractTokenFromCli(t){try{return(await this.runCliArgs(["mcp","get",t])).match(/Authorization:\s*Bearer\s+(\S+)/)?.[1]}catch{return}}async refreshProbeTokens(){if(!this.authManager)return;let t=this.authManager.getExpiringTokens();for(let[s,a]of t)if(a.refreshToken)try{let n=new URLSearchParams({grant_type:"refresh_token",refresh_token:a.refreshToken,client_id:a.clientId}).toString(),i=await this.oauthFetch(a.tokenEndpoint,"POST",n,"application/x-www-form-urlencoded");if(i.status===200){let o=JSON.parse(i.body),c=o.access_token;c&&(this.authManager.storeOAuthToken(s,{accessToken:c,refreshToken:o.refresh_token??a.refreshToken,expiresAt:typeof o.expires_in=="number"?Date.now()+o.expires_in*1e3:void 0,tokenEndpoint:a.tokenEndpoint,clientId:a.clientId,resource:a.resource}),this.injectTokenIntoClaudeConfig(s,c),this.clearNeedsAuthCache(s))}}catch(n){console.warn(`McpManager: failed to refresh token for ${s}:`,n)}let e=this.cache??[];for(let s of e)if((s.type==="http"||s.type==="sse")&&s.url&&!this.authManager.hasToken(s.name))try{let a=await this.extractTokenFromCli(s.name);a&&this.authManager.storeProbeToken(s.name,a)}catch{}}async discoverOAuthMetadata(t){let e=t.endsWith("/sse")?t.replace(/\/sse$/,"/mcp"):t,s=new URL(e),a;try{let h=await this.oauthFetch(e,"POST","{}","application/json");h.status===401&&(a=h.headers?.["www-authenticate"]?.match(/resource_metadata="([^"]+)"/)?.[1])}catch{}a||(a=`${s.origin}/.well-known/oauth-protected-resource${s.pathname}`);let n=s.origin,i,o=e;try{let h=await this.oauthFetch(a,"GET");if(h.status===200){let u=JSON.parse(h.body),p=u.authorization_servers;p?.[0]&&(n=p[0]),Array.isArray(u.scopes_supported)&&(i=u.scopes_supported),typeof u.resource=="string"&&(o=u.resource)}}catch{}let c=new URL(n),l=c.pathname==="/"?"":c.pathname,d=`${c.origin}/.well-known/oauth-authorization-server${l}`;try{let h=await this.oauthFetch(d,"GET");if(h.status===200)return{metadata:JSON.parse(h.body),resourceScopes:i,resource:o}}catch{}if(l){let h=`${c.origin}/.well-known/oauth-authorization-server`;try{let u=await this.oauthFetch(h,"GET");if(u.status===200)return{metadata:JSON.parse(u.body),resourceScopes:i,resource:o}}catch{}}return null}async registerOAuthClient(t,e){let s=JSON.stringify({client_name:"Agent Fleet Obsidian Plugin",redirect_uris:[e],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"}),a=await this.oauthFetch(t,"POST",s,"application/json");if(a.status!==200&&a.status!==201)throw new Error(`Client registration failed (HTTP ${a.status})`);let n=JSON.parse(a.body);if(!n.client_id)throw new Error("Client registration response missing client_id");return n.client_id}async startOAuthCallbackServer(){let t=null,e=null,s=Rs.createServer((n,i)=>{let o=new URL(n.url??"/","http://localhost");if(o.pathname!=="/callback"){i.writeHead(404),i.end();return}let c=o.searchParams.get("error");if(c){let h=o.searchParams.get("error_description")??c;i.writeHead(200,{"Content-Type":"text/html"}),i.end("

Authorization Failed

"+h+"

You can close this tab.

"),e?.(new Error(`OAuth denied: ${h}`));return}let l=o.searchParams.get("code"),d=o.searchParams.get("state");if(!l||!d){i.writeHead(400),i.end("Missing code or state");return}i.writeHead(200,{"Content-Type":"text/html"}),i.end("

Authenticated!

You can close this tab and return to Obsidian.

"),t?.(l,d)});return{port:await new Promise((n,i)=>{s.listen(0,"127.0.0.1",()=>{let o=s.address();if(!o||typeof o=="string"){i(new Error("Failed to bind callback server"));return}n(o.port)}),s.on("error",i)}),waitForCode:(n,i)=>new Promise((o,c)=>{let l=setTimeout(()=>{c(new Error("Authentication timed out \u2014 complete authorization in your browser and try again."))},i);t=(d,h)=>{clearTimeout(l),h!==n?c(new Error("OAuth state mismatch \u2014 possible CSRF attack")):o(d)},e=d=>{clearTimeout(l),c(d)}}),close:()=>{try{s.close()}catch{}}}}async exchangeOAuthCode(t,e,s,a,n){let i=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:s,client_id:a,code_verifier:n}).toString(),o=await this.oauthFetch(t,"POST",i,"application/x-www-form-urlencoded");if(o.status!==200)throw new Error(`Token exchange failed (HTTP ${o.status}): ${o.body}`);let c=JSON.parse(o.body);if(!c.access_token)throw new Error("Token response missing access_token");return c}readTokenFromClaudeConfig(t){let e=es();try{let s=ze.readFileSync(e,"utf8"),i=JSON.parse(s).mcpServers?.[t]?.headers,o=i?.Authorization??i?.authorization;if(o?.startsWith("Bearer "))return o.slice(7)}catch{}}injectTokenIntoClaudeConfig(t,e){let s=es();try{let a=ze.readFileSync(s,"utf8"),n=JSON.parse(a),i=n.mcpServers;if(i?.[t]){let o=i[t];(!o.headers||typeof o.headers!="object")&&(o.headers={}),o.headers.Authorization=`Bearer ${e}`,ze.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=Dt.join(Zt(),"mcp-needs-auth-cache.json");try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s),n=!1;t in a&&(delete a[t],n=!0);let i=`claude.ai ${t}`;i in a&&(delete a[i],n=!0),n&&ze.writeFileSync(e,JSON.stringify(a))}catch{}}oauthFetch(t,e,s,a){return new Promise((n,i)=>{let o=new URL(t),c=o.protocol==="https:",l={Accept:"application/json"};s&&(l["Content-Type"]=a??"application/json",l["Content-Length"]=String(Buffer.byteLength(s)));let d={hostname:o.hostname,port:o.port||(c?443:80),path:o.pathname+o.search,method:e,headers:l},u=(c?En:Rs).request(d,f=>{let m="";f.on("data",y=>{m+=y.toString()}),f.on("end",()=>{let y={};for(let[k,b]of Object.entries(f.headers))typeof b=="string"?y[k]=b:Array.isArray(b)&&(y[k]=b.join(", "));n({status:f.statusCode??0,body:m,headers:y})})});u.on("error",i);let p=setTimeout(()=>{u.destroy(),i(new Error("OAuth request timed out"))},15e3);u.on("close",()=>clearTimeout(p)),s&&u.write(s),u.end()})}async loadServers(){try{this.emitProgress({phase:"list",message:"Scanning MCP servers\u2026"});let t=await this.runCli("mcp list"),e=this.parseListOutput(t);if(e.length===0)return this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[];let s=[];for(let i=0;ii.enabled&&(i.status==="connected"||i.status==="needs-auth"&&(i.type==="http"||i.type==="sse")&&i.url));a.length>0&&(this.emitProgress({phase:"tools",message:`Probing ${a.length} server${a.length!==1?"s":""} for tools\u2026`}),await Promise.allSettled(a.map(async i=>{try{let o=[];if(i.type==="stdio"&&i.command){let c=await this.probeStdioServer(i.command,i.args);c.description&&!i.description&&(i.description=c.description),o=c.tools}else(i.type==="http"||i.type==="sse")&&i.url&&(o=await this.probeHttpServer(i));o.length>0&&(i.toolDetails=o,i.tools=o.map(c=>c.name),i.status==="needs-auth"&&(i.status="connected"))}catch(o){console.warn(`McpManager: probe failed for ${i.name}:`,o)}})));let n=s.reduce((i,o)=>i+o.toolDetails.length,0);return this.emitProgress({phase:"done",serverCount:s.length,toolCount:n}),s}catch(t){return console.error("McpManager: failed to load servers",t),this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[]}}getPluginDescription(t){let e=t.replace(/^claude\.ai\s+/i,"").toLowerCase().replace(/\s+/g,"-"),s=Dt.join(Zt(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=ze.readFileSync(s,"utf8");return JSON.parse(a).description||void 0}catch{return}}probeStdioServer(t,e){return new Promise(s=>{let a=e?`${t} ${e}`:t,n=ua(a,{env:{...process.env}}),i="",o,c=[],l=!1,d=!1,h=!1,u=()=>{l||(l=!0,n.kill(),s({description:o,tools:c}))},p=setTimeout(u,1e4);n.stdout.on("data",f=>{i+=f.toString();let m=fe(i);i=m.pop()??"";for(let y of m){let k=y.trim();if(k){try{let b=JSON.parse(k);if(b.id===1&&b.result){o=b.result.instructions??b.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+` +`)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),i=(0,Sn.randomUUID)(),o=Bt(e,t,this.settings),c=a!=null,l=["-p",n,"--output-format",c?"stream-json":"json"];c&&l.push("--verbose"),As(o.value)&&l.push("--model",o.value);let d=e.effort||t.effort;d&&l.push("--effort",d);let h=t.permissionMode?.trim();h&&h!=="default"?l.push("--permission-mode",h):l.push("--permission-mode","bypassPermissions");let u=t.cwd??this.repository.getVaultBasePath()??".",p=Ps(u,t),m=Date.now(),f=()=>Et(p);try{return await new Promise((y,k)=>{let b=pt(this.settings.claudeCliPath,l,{cwd:u,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.runningProcesses.set(t.name,b);let v="",g="",x=!1,T=setTimeout(()=>{x=!0,b.kill()},t.timeout*1e3);b.stdout.on("data",_=>{let R=_.toString();if(v+=R,c&&a)for(let A of me(R)){let S=A.trim();if(S)try{let L=JSON.parse(S),E=this.extractStreamContent(L);E&&a(E)}catch{}}}),b.stderr.on("data",_=>{g+=_.toString()}),b.on("error",_=>{clearTimeout(T),k(_)}),b.on("close",_=>{clearTimeout(T),this.runningProcesses.delete(t.name);let R=v.trim(),A;if(c){let F=me(R);for(let $=F.length-1;$>=0;$--){let X=F[$]?.trim();if(X)try{let z=JSON.parse(X);if(z&&typeof z=="object"){A=z;break}}catch{}}}else if(R.startsWith("{")||R.startsWith("["))try{A=JSON.parse(R)}catch{A=void 0}let S=Cn(A)??"";if(!S&&c){let F=[];for(let $ of me(R)){let X=$.trim();if(X)try{let z=JSON.parse(X);if(z.type==="assistant"&&z.message?.content)for(let ie of z.message.content)ie.type==="text"&&ie.text&&F.push(ie.text);else z.type==="result"&&typeof z.result=="string"&&F.push(z.result)}catch{}}S=F.join(` +`).trim()}S||(S=g.trim()||"(no output)");let L=Ta(A),E=Tn(A),B=_n(A),I=_a(A),q=xn(A);if((!I||!q)&&c)for(let F of me(R)){let $=F.trim();if($)try{let X=JSON.parse($);if(!I){let z=_a(X);z&&(I=z)}if(!q){let z=xn(X);z&&(q=z)}if(I&&q)break}catch{}}y({runId:i,prompt:n,exitCode:_,durationSeconds:Math.max(1,Math.round((Date.now()-m)/1e3)),stdout:v,stderr:g,outputText:S,rawJson:A,tokensUsed:E,costUsd:B,toolsUsed:L,timedOut:x,resolvedModel:o.value,modelSource:o.source,concreteModel:I,finalResult:q})})})}finally{f()}}};var ns=Ke(require("crypto")),En=Ke(require("https")),Rs=Ke(require("http")),ze=Ke(require("fs")),Pt=Ke(require("path"));var Is=class{constructor(t){this.settings=t}cache=null;loadPromise=null;progressListeners=[];authManager;setAuthManager(t){this.authManager=t}getCachedServers(){return this.cache}onProgress(t){return this.progressListeners.push(t),()=>{this.progressListeners=this.progressListeners.filter(e=>e!==t)}}emitProgress(t){for(let e of this.progressListeners)try{e(t)}catch{}}async getServers(t=!1){return this.cache!==null&&!t?this.cache:this.loadPromise&&!t?this.loadPromise:(this.loadPromise=this.loadServers().then(e=>(this.cache=e,this.loadPromise=null,e)),this.loadPromise)}invalidateCache(){this.cache=null,this.loadPromise=null}async toggleServerEnabled(t,e){let s=Pt.join(Zt(),"settings.local.json"),a={};try{let o=ze.readFileSync(s,"utf8");a=JSON.parse(o)}catch{}let n=a.disabledMcpjsonServers??[],i=this.toInternalName(t);if(e?(a.disabledMcpjsonServers=n.filter(o=>o!==i),this.enableServerInClaudeConfig(t)):n.includes(i)||(n.push(i),a.disabledMcpjsonServers=n),ze.writeFileSync(s,JSON.stringify(a,null,2)),this.cache){let o=this.cache.find(c=>c.name===t);o&&(o.enabled=e)}}toInternalName(t){return t.replace(/\./g,"_").replace(/\s+/g,"_")}async addServer(t){let e=["mcp","add","-s",t.scope??"user"];if(t.transport==="stdio"){if(t.envVars)for(let[s,a]of Object.entries(t.envVars))e.push("-e",`${s}=${a}`);e.push(t.name,"--"),t.command&&e.push(t.command),t.args&&e.push(...t.args)}else if(e.push("-t",t.transport,t.name),t.url&&e.push(t.url),t.headers)for(let[s,a]of Object.entries(t.headers))e.push("-H",`${s}: ${a}`);await this.runCliArgs(e),this.enableServerInClaudeConfig(t.name),this.invalidateCache()}async removeServer(t,e){let s=["mcp","remove"];e&&s.push("-s",e),s.push(t),await this.runCliArgs(s),this.authManager?.removeToken(t),this.invalidateCache()}async authenticateServer(t,e,s="http"){let a=await this.discoverOAuthMetadata(e);if(!a)throw new Error("Server does not support OAuth \u2014 no authorization metadata found.");let{metadata:n,resourceScopes:i,resource:o}=a;if(!n.registration_endpoint)throw new Error("Server does not support Dynamic Client Registration.");let c=await this.startOAuthCallbackServer(),l=`http://localhost:${c.port}/callback`;try{let d=await this.registerOAuthClient(n.registration_endpoint,l),h=ns.randomBytes(32).toString("base64url"),u=ns.createHash("sha256").update(h).digest("base64url"),p=ns.randomBytes(16).toString("hex"),m=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:l,state:p,resource:o}),f=i??n.scopes_supported;f?.length&&m.set("scope",f.join(" "));let y=new URL(n.authorization_endpoint);for(let[g,x]of m)y.searchParams.set(g,x);let k=y.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",k),nn(k);let b=await c.waitForCode(p,18e4),v=await this.exchangeOAuthCode(n.token_endpoint,b,l,d,h);this.injectTokenIntoClaudeConfig(t,v.access_token),this.authManager&&this.authManager.storeOAuthToken(t,{accessToken:v.access_token,refreshToken:v.refresh_token,expiresAt:v.expires_in?Date.now()+v.expires_in*1e3:void 0,tokenEndpoint:n.token_endpoint,clientId:d,resource:o}),this.clearNeedsAuthCache(t),this.enableServerInClaudeConfig(t),this.invalidateCache()}finally{c.close()}}async extractTokenFromCli(t){try{return(await this.runCliArgs(["mcp","get",t])).match(/Authorization:\s*Bearer\s+(\S+)/)?.[1]}catch{return}}async refreshProbeTokens(){if(!this.authManager)return;let t=this.authManager.getExpiringTokens();for(let[s,a]of t)if(a.refreshToken)try{let n=new URLSearchParams({grant_type:"refresh_token",refresh_token:a.refreshToken,client_id:a.clientId}).toString(),i=await this.oauthFetch(a.tokenEndpoint,"POST",n,"application/x-www-form-urlencoded");if(i.status===200){let o=JSON.parse(i.body),c=o.access_token;c&&(this.authManager.storeOAuthToken(s,{accessToken:c,refreshToken:o.refresh_token??a.refreshToken,expiresAt:typeof o.expires_in=="number"?Date.now()+o.expires_in*1e3:void 0,tokenEndpoint:a.tokenEndpoint,clientId:a.clientId,resource:a.resource}),this.injectTokenIntoClaudeConfig(s,c),this.clearNeedsAuthCache(s))}}catch(n){console.warn(`McpManager: failed to refresh token for ${s}:`,n)}let e=this.cache??[];for(let s of e)if((s.type==="http"||s.type==="sse")&&s.url&&!this.authManager.hasToken(s.name))try{let a=await this.extractTokenFromCli(s.name);a&&this.authManager.storeProbeToken(s.name,a)}catch{}}async discoverOAuthMetadata(t){let e=t.endsWith("/sse")?t.replace(/\/sse$/,"/mcp"):t,s=new URL(e),a;try{let h=await this.oauthFetch(e,"POST","{}","application/json");h.status===401&&(a=h.headers?.["www-authenticate"]?.match(/resource_metadata="([^"]+)"/)?.[1])}catch{}a||(a=`${s.origin}/.well-known/oauth-protected-resource${s.pathname}`);let n=s.origin,i,o=e;try{let h=await this.oauthFetch(a,"GET");if(h.status===200){let u=JSON.parse(h.body),p=u.authorization_servers;p?.[0]&&(n=p[0]),Array.isArray(u.scopes_supported)&&(i=u.scopes_supported),typeof u.resource=="string"&&(o=u.resource)}}catch{}let c=new URL(n),l=c.pathname==="/"?"":c.pathname,d=`${c.origin}/.well-known/oauth-authorization-server${l}`;try{let h=await this.oauthFetch(d,"GET");if(h.status===200)return{metadata:JSON.parse(h.body),resourceScopes:i,resource:o}}catch{}if(l){let h=`${c.origin}/.well-known/oauth-authorization-server`;try{let u=await this.oauthFetch(h,"GET");if(u.status===200)return{metadata:JSON.parse(u.body),resourceScopes:i,resource:o}}catch{}}return null}async registerOAuthClient(t,e){let s=JSON.stringify({client_name:"Agent Fleet Obsidian Plugin",redirect_uris:[e],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"}),a=await this.oauthFetch(t,"POST",s,"application/json");if(a.status!==200&&a.status!==201)throw new Error(`Client registration failed (HTTP ${a.status})`);let n=JSON.parse(a.body);if(!n.client_id)throw new Error("Client registration response missing client_id");return n.client_id}async startOAuthCallbackServer(){let t=null,e=null,s=Rs.createServer((n,i)=>{let o=new URL(n.url??"/","http://localhost");if(o.pathname!=="/callback"){i.writeHead(404),i.end();return}let c=o.searchParams.get("error");if(c){let h=o.searchParams.get("error_description")??c;i.writeHead(200,{"Content-Type":"text/html"}),i.end("

Authorization Failed

"+h+"

You can close this tab.

"),e?.(new Error(`OAuth denied: ${h}`));return}let l=o.searchParams.get("code"),d=o.searchParams.get("state");if(!l||!d){i.writeHead(400),i.end("Missing code or state");return}i.writeHead(200,{"Content-Type":"text/html"}),i.end("

Authenticated!

You can close this tab and return to Obsidian.

"),t?.(l,d)});return{port:await new Promise((n,i)=>{s.listen(0,"127.0.0.1",()=>{let o=s.address();if(!o||typeof o=="string"){i(new Error("Failed to bind callback server"));return}n(o.port)}),s.on("error",i)}),waitForCode:(n,i)=>new Promise((o,c)=>{let l=setTimeout(()=>{c(new Error("Authentication timed out \u2014 complete authorization in your browser and try again."))},i);t=(d,h)=>{clearTimeout(l),h!==n?c(new Error("OAuth state mismatch \u2014 possible CSRF attack")):o(d)},e=d=>{clearTimeout(l),c(d)}}),close:()=>{try{s.close()}catch{}}}}async exchangeOAuthCode(t,e,s,a,n){let i=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:s,client_id:a,code_verifier:n}).toString(),o=await this.oauthFetch(t,"POST",i,"application/x-www-form-urlencoded");if(o.status!==200)throw new Error(`Token exchange failed (HTTP ${o.status}): ${o.body}`);let c=JSON.parse(o.body);if(!c.access_token)throw new Error("Token response missing access_token");return c}readTokenFromClaudeConfig(t){let e=es();try{let s=ze.readFileSync(e,"utf8"),i=JSON.parse(s).mcpServers?.[t]?.headers,o=i?.Authorization??i?.authorization;if(o?.startsWith("Bearer "))return o.slice(7)}catch{}}injectTokenIntoClaudeConfig(t,e){let s=es();try{let a=ze.readFileSync(s,"utf8"),n=JSON.parse(a),i=n.mcpServers;if(i?.[t]){let o=i[t];(!o.headers||typeof o.headers!="object")&&(o.headers={}),o.headers.Authorization=`Bearer ${e}`,ze.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=Pt.join(Zt(),"mcp-needs-auth-cache.json");try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s),n=!1;t in a&&(delete a[t],n=!0);let i=`claude.ai ${t}`;i in a&&(delete a[i],n=!0),n&&ze.writeFileSync(e,JSON.stringify(a))}catch{}}oauthFetch(t,e,s,a){return new Promise((n,i)=>{let o=new URL(t),c=o.protocol==="https:",l={Accept:"application/json"};s&&(l["Content-Type"]=a??"application/json",l["Content-Length"]=String(Buffer.byteLength(s)));let d={hostname:o.hostname,port:o.port||(c?443:80),path:o.pathname+o.search,method:e,headers:l},u=(c?En:Rs).request(d,m=>{let f="";m.on("data",y=>{f+=y.toString()}),m.on("end",()=>{let y={};for(let[k,b]of Object.entries(m.headers))typeof b=="string"?y[k]=b:Array.isArray(b)&&(y[k]=b.join(", "));n({status:m.statusCode??0,body:f,headers:y})})});u.on("error",i);let p=setTimeout(()=>{u.destroy(),i(new Error("OAuth request timed out"))},15e3);u.on("close",()=>clearTimeout(p)),s&&u.write(s),u.end()})}async loadServers(){try{this.emitProgress({phase:"list",message:"Scanning MCP servers\u2026"});let t=await this.runCli("mcp list"),e=this.parseListOutput(t);if(e.length===0)return this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[];let s=[];for(let i=0;ii.enabled&&(i.status==="connected"||i.status==="needs-auth"&&(i.type==="http"||i.type==="sse")&&i.url));a.length>0&&(this.emitProgress({phase:"tools",message:`Probing ${a.length} server${a.length!==1?"s":""} for tools\u2026`}),await Promise.allSettled(a.map(async i=>{try{let o=[];if(i.type==="stdio"&&i.command){let c=await this.probeStdioServer(i.command,i.args);c.description&&!i.description&&(i.description=c.description),o=c.tools}else(i.type==="http"||i.type==="sse")&&i.url&&(o=await this.probeHttpServer(i));o.length>0&&(i.toolDetails=o,i.tools=o.map(c=>c.name),i.status==="needs-auth"&&(i.status="connected"))}catch(o){console.warn(`McpManager: probe failed for ${i.name}:`,o)}})));let n=s.reduce((i,o)=>i+o.toolDetails.length,0);return this.emitProgress({phase:"done",serverCount:s.length,toolCount:n}),s}catch(t){return console.error("McpManager: failed to load servers",t),this.emitProgress({phase:"done",serverCount:0,toolCount:0}),[]}}getPluginDescription(t){let e=t.replace(/^claude\.ai\s+/i,"").toLowerCase().replace(/\s+/g,"-"),s=Pt.join(Zt(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=ze.readFileSync(s,"utf8");return JSON.parse(a).description||void 0}catch{return}}probeStdioServer(t,e){return new Promise(s=>{let a=e?`${t} ${e}`:t,n=pa(a,{env:{...process.env}}),i="",o,c=[],l=!1,d=!1,h=!1,u=()=>{l||(l=!0,n.kill(),s({description:o,tools:c}))},p=setTimeout(u,1e4);n.stdout.on("data",m=>{i+=m.toString();let f=me(i);i=f.pop()??"";for(let y of f){let k=y.trim();if(k){try{let b=JSON.parse(k);if(b.id===1&&b.result){o=b.result.instructions??b.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+` `),n.stdin.write(JSON.stringify({jsonrpc:"2.0",id:2,method:"tools/list"})+` `)}catch{clearTimeout(p),u();return}}else if(b.id===2&&b.result){for(let v of b.result.tools??[])c.push({name:v.name,description:v.description,inputSchema:v.inputSchema});h=!0}}catch{}d&&h&&(clearTimeout(p),u())}}}),n.on("error",()=>{clearTimeout(p),u()}),n.on("close",()=>{clearTimeout(p),u()});try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"agent-fleet",version:"1.0.0"}}})+` -`)}catch{clearTimeout(p),u()}})}async probeHttpServer(t){let e=await this.findServerToken(t);if(!e)return console.log(`McpManager: no auth token for ${t.name}, skipping tool probe`),[];let s=t.url.endsWith("/sse")?t.url.replace(/\/sse$/,"/mcp"):t.url;try{let n=(await this.httpRequest(s,e,{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-03-26",capabilities:{},clientInfo:{name:"agent-fleet",version:"1.0.0"}}}))?._sessionId;await this.httpRequest(s,e,{jsonrpc:"2.0",method:"notifications/initialized"},n);let i=await this.httpRequest(s,e,{jsonrpc:"2.0",id:2,method:"tools/list"},n),o=[],d=i?.result?.tools??[];for(let h of d)o.push({name:h.name,description:h.description,inputSchema:h.inputSchema});return o}catch(a){return console.warn(`McpManager: HTTP probe failed for ${t.name}:`,a),[]}}async findServerToken(t){if(this.authManager){let i=this.authManager.getToken(t.name);if(i)return i}let e=this.readTokenFromClaudeConfig(t.name);if(e)return this.authManager?.storeProbeToken(t.name,e),e;let s=t.name.replace(/^claude\.ai\s+/i,"").replace(/\s+/g,"_").toUpperCase(),a=[`${s}_API_KEY`,`${s}_API_KEY_MILO`,`${s}_TOKEN`];for(let i of a){let o=process.env[i];if(o)return o}let n=[Dt.join(ha(),".openclaw","workspace",".env"),Dt.join(ha(),".env")];for(let i of n)try{let o=ze.readFileSync(i,"utf8");for(let c of fe(o)){let l=c.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(l){let d=l[1],h=l[2].replace(/^["']|["']$/g,"");if(a.includes(d))return h}}}catch{}}httpRequest(t,e,s,a){return new Promise((n,i)=>{let o=JSON.stringify(s),c=new URL(t),l=c.protocol==="https:",d={"Content-Type":"application/json",Accept:"application/json, text/event-stream",Authorization:`Bearer ${e}`,"Content-Length":String(Buffer.byteLength(o))};a&&(d["mcp-session-id"]=a);let h={hostname:c.hostname,port:c.port||(l?443:80),path:c.pathname+c.search,method:"POST",headers:d},p=(l?En:Rs).request(h,m=>{let y="";m.on("data",k=>{y+=k.toString()}),m.on("end",()=>{let k=m.headers["mcp-session-id"];if((m.headers["content-type"]??"").includes("text/event-stream")){for(let v of fe(y))if(v.startsWith("data: "))try{let g=JSON.parse(v.slice(6));k&&(g._sessionId=k),n(g);return}catch{}n(null)}else try{let v=JSON.parse(y);k&&(v._sessionId=k),n(v)}catch{n(null)}})});p.on("error",i);let f=setTimeout(()=>{p.destroy(),n(null)},15e3);p.on("close",()=>clearTimeout(f)),p.write(o),p.end()})}runCli(t){return new Promise((e,s)=>{let a=`${this.settings.claudeCliPath} ${t}`,n=ua(a,{env:{...process.env}}),i="",o="";n.stdout.on("data",c=>{i+=c.toString()}),n.stderr.on("data",c=>{o+=c.toString()}),n.on("error",s),n.on("close",c=>{c!==0&&!i.trim()?s(new Error(o||`Process exited with code ${c}`)):e(i)})})}runCliArgs(t){return new Promise((e,s)=>{let a=pt(this.settings.claudeCliPath,t,{env:{...process.env}}),n="",i="";a.stdout.on("data",o=>{n+=o.toString()}),a.stderr.on("data",o=>{i+=o.toString()}),a.on("error",s),a.on("close",o=>{o!==0&&!n.trim()?s(new Error(i||`Process exited with code ${o}`)):e(n)})})}getDisabledServers(){let t=new Set,e=Dt.join(Zt(),"settings.local.json");try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s);for(let n of a.disabledMcpjsonServers??[])t.add(n)}catch{}try{let s=ze.readFileSync(es(),"utf8"),n=JSON.parse(s).projects;if(n){for(let i of Object.values(n))if(i&&Array.isArray(i.disabledMcpServers))for(let o of i.disabledMcpServers)t.add(o)}}catch{}return t}enableServerInClaudeConfig(t){let e=es();try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s),n=a.projects;if(!n)return;let i=!1;for(let o of Object.values(n))if(o&&Array.isArray(o.disabledMcpServers)){let c=o.disabledMcpServers.indexOf(t);c!==-1&&(o.disabledMcpServers.splice(c,1),i=!0)}i&&ze.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of fe(t)){let n=a.trim();if(!n||n.startsWith("Checking"))continue;let i=n.indexOf(": ");if(i===-1)continue;let o=n.slice(0,i).trim(),c=n.slice(i+2),l=c.lastIndexOf(" - ");if(l===-1)continue;let d=c.slice(0,l).trim(),h=c.slice(l+3).trim(),u="disconnected";h.includes("Connected")?u="connected":h.includes("authentication")?u="needs-auth":h.toLowerCase().includes("error")&&(u="error");let p="unknown",f,m;if(d.startsWith("http://")||d.startsWith("https://")){let b=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=b.endsWith("/sse")?"sse":"http",f=b}else d&&(p="stdio",m=d);let y=this.toInternalName(o),k=!s.has(y);e.push({name:o,type:p,status:u,scope:"unknown",enabled:k,url:f,command:m,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of fe(e)){let n=a.trim();if(n.startsWith("Type:")){let i=n.slice(5).trim().toLowerCase();i==="stdio"?s.type="stdio":i==="http"?s.type="http":i==="sse"&&(s.type="sse")}else if(n.startsWith("Scope:")){let i=n.slice(6).trim().toLowerCase();i.includes("user")?s.scope="user":i.includes("project")&&(s.scope="project")}else if(n.startsWith("Command:")){let i=n.slice(8).trim();i&&(s.command=i)}else if(n.startsWith("Args:")){let i=n.slice(5).trim();i&&(s.args=i)}}return s}};var xr={"every 5m":"*/5 * * * *","every 10m":"*/10 * * * *","every 15m":"*/15 * * * *","every 30m":"*/30 * * * *","every 1h":"0 * * * *","every 2h":"0 */2 * * *",hourly:"0 * * * *","daily at 9am":"0 9 * * *","daily at 6pm":"0 18 * * *","weekdays at 9am":"0 9 * * 1-5","weekly on monday":"0 9 * * 1","monthly on 1st":"0 9 1 * *"},Ls=class{constructor(t,e){this.maxConcurrentRuns=t;this.callbacks=e}jobs=new Map;activeRuns=0;queue=[];paused=!1;setMaxConcurrentRuns(t){this.maxConcurrentRuns=t}parseSchedule(t){return xr[t.toLowerCase()]??t}toLocalISOString(t){let e=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}T${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}async registerTask(t){if(this.unregisterTask(t.taskId),!(!t.enabled||t.type==="immediate"))try{if(t.type==="once"&&t.runAt){let e=new ce(new Date(t.runAt),{name:t.taskId,catch:!0},()=>{this.enqueue({task:t,reason:"scheduled"})});this.jobs.set(t.taskId,e),await this.callbacks.onTaskScheduled(t,t.runAt);return}if(t.type==="recurring"&&t.schedule){let e=this.parseSchedule(t.schedule),s=new ce(e,{name:t.taskId,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.enqueue({task:t,reason:"scheduled"})});this.jobs.set(t.taskId,s);let a=s.nextRun();await this.callbacks.onTaskScheduled(t,a?this.toLocalISOString(a):void 0)}}catch(e){console.error(`Agent Fleet: Failed to register task "${t.taskId}":`,e)}}unregisterTask(t){let e=this.jobs.get(t);e&&(e.stop(),this.jobs.delete(t))}async loadTasks(t){for(let e of t)await this.registerTask(e)}async handleStartupCatchUp(t){let e=Date.now();for(let s of t)!s.enabled||!s.catchUp||!s.nextRun||new Date(s.nextRun).getTime()t.pause())}resumeAll(){this.paused=!1,this.jobs.forEach(t=>t.resume()),this.processQueue()}shutdown(){this.paused=!0,this.jobs.forEach(t=>t.stop()),this.jobs.clear(),this.queue.length=0}getQueueSize(){return this.queue.length}async processQueue(){if(!this.paused)for(;this.activeRuns0;){let t=this.queue.shift();if(!t)return;this.activeRuns+=1,this.callbacks.onTaskTriggered(t).finally(async()=>{this.activeRuns-=1,await this.processQueue()})}}};var is=class r{constructor(t,e){this.repository=t;this.settings=e;this.executor=new Ps(e,t),this.mcpManager=new Is(e),this.scheduler=new Ls(e.maxConcurrentRuns,{onTaskTriggered:s=>this.runPendingTask(s),onTaskScheduled:(s,a)=>this.repository.updateTaskRunMetadata(s,{nextRun:a})})}scheduler;executor;mcpManager;snapshot={agents:[],skills:[],tasks:[],channels:[],validationIssues:[]};runtimeState=new Map;recentRuns=[];chartRuns=[];static CHART_WINDOW_DAYS=14;statusChangeListeners=new Set;runOutputListeners=new Map;runOutputBuffers=new Map;heartbeatJobs=new Map;heartbeatRegisteredAt=0;heartbeatsInFlight=new Set;heartbeatResultHandler;async initialize(){this.snapshot=await this.repository.loadAll(),await this.refreshRunCaches();let t=this.snapshot.tasks.filter(e=>this.repository.getAgentByName(e.agent)?.enabled!==!1);await this.scheduler.loadTasks(t),await this.scheduler.handleStartupCatchUp(t),this.registerHeartbeats(),this.emitStatusChange()}onHeartbeatResult(t){this.heartbeatResultHandler=t}async refreshFromVault(){this.snapshot=await this.repository.loadAll(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}getSnapshot(){return this.snapshot}getRecentRuns(){return this.recentRuns}getChartRuns(){return this.chartRuns}async refreshRunCaches(){this.recentRuns=await this.repository.listRecentRuns();let t=new Date;t.setDate(t.getDate()-(r.CHART_WINDOW_DAYS-1)),this.chartRuns=await this.repository.listRunsSince(t)}getAgentState(t){let e=this.runtimeState.get(t),s=this.snapshot.agents.find(a=>a.name===t);return s&&!s.enabled?{status:"disabled",lastRun:e?.lastRun,currentRunId:e?.currentRunId}:e??{status:"idle"}}getFleetStatus(){let t=new Date,e=o=>String(o).padStart(2,"0"),s=`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`,a=this.recentRuns.filter(o=>{let c=new Date(o.started);return`${c.getFullYear()}-${e(c.getMonth()+1)}-${e(c.getDate())}`===s}).length,n=Array.from(this.runtimeState.values()).filter(o=>o.status==="running").length,i=this.recentRuns.flatMap(o=>o.approvals??[]).filter(o=>o.status==="pending").length;return{running:n,pending:i,completedToday:a}}subscribe(t){return this.statusChangeListeners.add(t),()=>this.statusChangeListeners.delete(t)}onRunOutput(t,e){let s=this.runOutputListeners.get(t);s||(s=new Set,this.runOutputListeners.set(t,s)),s.add(e);let a=this.runOutputBuffers.get(t);return a&&e(a),()=>{this.runOutputListeners.get(t)?.delete(e)}}getRunOutputBuffer(t){return this.runOutputBuffers.get(t)??""}async handleVaultChange(t){await this.repository.loadFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}async handleVaultDelete(t){this.repository.removeFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}abortedAgents=new Set;abortAgentRun(t){let e=this.executor.abortAgent(t);return e&&(this.abortedAgents.add(t),this.runtimeState.set(t,{status:"idle"}),this.emitStatusChange()),e}wasAborted(t){return this.abortedAgents.has(t)}consumeAborted(t){let e=this.abortedAgents.has(t);return this.abortedAgents.delete(t),e}async runTaskNow(t,e){await this.scheduler.enqueue({task:{...t,type:"immediate"},reason:"manual",promptOverride:e})}async runAgentNow(t,e){let s=t.heartbeatBody.trim()&&e==="Run now and summarize the current state."?t.heartbeatBody.trim():e,a=s===t.heartbeatBody.trim()&&t.heartbeatBody.trim().length>0,n={filePath:"",taskId:a?`heartbeat-${Date.now()}`:`manual-${Date.now()}`,agent:t.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:a?[...t.tags,"heartbeat"]:t.tags,body:s};await this.scheduler.enqueue({task:n,reason:a?"heartbeat":"manual",promptOverride:s})}async resolveApproval(t,e,s){t.filePath&&(await this.repository.setApprovalDecision(t.filePath,e,s),await this.refreshRunCaches(),this.emitStatusChange())}async pruneOldRuns(){let t=Date.now()-this.settings.runLogRetentionDays*24*60*60*1e3,e=await this.repository.listRecentRuns(500);for(let s of e)s.filePath&&new Date(s.started).getTime()this.repository.getAgentByName(t.agent)?.enabled!==!1)),this.scheduler.resumeAll(),this.registerHeartbeats()}shutdown(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatsInFlight.clear(),this.scheduler.shutdown()}registerHeartbeats(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatRegisteredAt=Date.now();for(let t of this.snapshot.agents)if(!(!t.enabled||!t.heartbeatEnabled||!t.heartbeatSchedule.trim()||!t.heartbeatBody.trim()))try{let e=new ce(t.heartbeatSchedule,{name:`heartbeat:${t.name}`,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.runHeartbeat(t.name)});this.heartbeatJobs.set(t.name,e)}catch(e){console.error(`Agent Fleet: failed to register heartbeat for "${t.name}":`,e)}}async runHeartbeat(t){if(!(Date.now()-this.heartbeatRegisteredAt<1e4)&&!this.heartbeatsInFlight.has(t)){this.heartbeatsInFlight.add(t);try{let e=this.repository.getAgentByName(t);if(!e||!e.enabled||!e.heartbeatBody.trim())return;let s={filePath:"",taskId:`heartbeat-${Date.now()}`,agent:e.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:[...e.tags,"heartbeat"],body:e.heartbeatBody.trim()};await this.scheduler.enqueue({task:s,reason:"heartbeat",promptOverride:e.heartbeatBody.trim()})}finally{this.heartbeatsInFlight.delete(t)}}}getNextHeartbeat(t){let e=this.heartbeatJobs.get(t);return e?e.nextRun()??null:null}async runPendingTask({task:t,promptOverride:e}){let s=this.repository.getAgentByName(t.agent);if(!s||!s.enabled)return;let a=new Date().toISOString();this.runtimeState.set(s.name,{status:"running",currentTaskId:t.taskId,runStarted:a}),this.runOutputBuffers.set(s.name,""),this.emitStatusChange();try{let n=await this.executor.execute(s,t,e,u=>{let p=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,p+u);let f=this.runOutputListeners.get(s.name);if(f)for(let m of f)m(u)}),i=this.consumeAborted(s.name),o=i?[]:this.buildApprovals(s,n.toolsUsed),c=i?"cancelled":this.resolveRunStatus(n,o),l={runId:n.runId,agent:s.name,task:t.taskId,status:c,started:a,completed:new Date().toISOString(),durationSeconds:n.durationSeconds,tokensUsed:n.tokensUsed,costUsd:n.costUsd,model:n.resolvedModel||s.model,modelSource:n.modelSource,concreteModel:n.concreteModel,exitCode:n.exitCode,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:n.prompt,output:n.outputText,toolsUsed:n.toolsUsed.map(u=>`${u.tool}${u.command?`: ${u.command}`:""}`),finalResult:n.finalResult,stderr:n.stderr,approvals:o},d=await this.repository.writeRunLog(l);if(await this.repository.updateTaskRunMetadata(t,{lastRun:a,runCount:t.runCount+1}),s.memory){let u=An(n.outputText);try{await this.repository.appendMemory(s.name,u)}catch(p){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,p)}}if(t.tags.includes("heartbeat")&&!i&&s.heartbeatChannel&&l.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,l.output)}catch(u){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,u)}i&&(l.output="Task was manually stopped."),await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:i||c==="success"?"idle":c==="pending_approval"?"pending":"error",currentRunId:n.runId,lastRun:{...l,filePath:d}}),i||this.notify(l)}catch(n){let i=this.consumeAborted(s.name),o=i?"cancelled":"failure",c=Bt(t,s,this.settings),l={runId:(0,Dn.randomUUID)(),agent:s.name,task:t.taskId,status:o,started:a,completed:new Date().toISOString(),durationSeconds:Math.round((Date.now()-new Date(a).getTime())/1e3),model:c.value||s.model,modelSource:c.source,exitCode:i?-1:1,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:e??t.body,output:i?"Task was manually stopped.":n instanceof Error?n.message:String(n),toolsUsed:[]},d=await this.repository.writeRunLog(l);await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:i?"idle":"error",lastRun:{...l,filePath:d}}),i||this.notify(l)}finally{this.runOutputBuffers.delete(s.name),this.runOutputListeners.delete(s.name),this.emitStatusChange()}}buildApprovals(t,e){let s=e.filter(a=>t.approvalRequired.includes(a.tool)).map(a=>({tool:a.tool,command:a.command,reason:a.reason,status:"pending"}));return s.length>0?s:void 0}resolveRunStatus(t,e){return e?.length?"pending_approval":t.timedOut?"timeout":t.exitCode===0?"success":"failure"}notify(t){if(this.settings.notificationLevel==="none"||this.settings.notificationLevel==="failures-only"&&t.status==="success")return;let s=(fe(t.output).map(n=>n.trim()).find(n=>n&&!n.startsWith("{")&&!n.startsWith("["))??"").slice(0,120)||t.status,a=t.status==="success"?`\u2705 ${t.agent}: ${s}`:t.status==="pending_approval"?`\u{1F535} ${t.agent} needs approval: ${(t.approvals??[])[0]?.tool??"tool action"}`:`\u274C ${t.agent}: ${s}`;new Pn.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Ms=class{tokens=new Map;oauthTokens=new Map;storeProbeToken(t,e){this.tokens.set(t,e)}storeOAuthToken(t,e){this.oauthTokens.set(t,e),this.tokens.set(t,e.accessToken)}getToken(t){return this.tokens.get(t)}getOAuthToken(t){return this.oauthTokens.get(t)}hasToken(t){return this.tokens.has(t)}removeToken(t){this.tokens.delete(t),this.oauthTokens.delete(t)}getExpiringTokens(t=5*6e4){let e=new Map,s=Date.now();for(let[a,n]of this.oauthTokens)n.refreshToken&&n.expiresAt&&n.expiresAt-s0?t:r.WATCHDOG_FALLBACK_MINUTES)*60*1e3}armWatchdog(){this.clearWatchdog();let t=this.getWatchdogMs();this.watchdogTimer=setTimeout(()=>{if(this.watchdogTimer=null,!this.isStreaming)return;this.activeOnEvent?.({type:"error",content:"",errorMessage:`no response from the CLI for ${Math.round(t/6e4)} minutes \u2014 giving up`});let e=new Error("Watchdog timeout");this.handleProcessError(e);try{this.process?.kill()}catch{}},t)}clearWatchdog(){this.watchdogTimer&&(clearTimeout(this.watchdogTimer),this.watchdogTimer=null)}currentToolName;activityListeners=new Set;onActivityChange(t){return this.activityListeners.add(t),t(),()=>{this.activityListeners.delete(t)}}emitActivity(){for(let t of this.activityListeners)t()}setStreaming(t){this.isStreaming!==t&&(this.isStreaming=t,t||(this.currentToolName=void 0),this.emitActivity())}setCurrentTool(t){this.currentToolName!==t&&(this.currentToolName=t,this.emitActivity())}stats={costTotalUsd:0,turnCount:0};statsListeners=new Set;onStatsChange(t){return this.statsListeners.add(t),t({...this.stats}),()=>{this.statsListeners.delete(t)}}getStats(){return{...this.stats}}emitStats(){let t={...this.stats};for(let e of this.statsListeners)e(t)}getConversationName(){return this.conversationName}async setConversationName(t){this.conversationName=t,await this.persist()}refreshAgent(){let t=this.repository.getAgentByName(this.agent.name);t&&(this.agent=t)}get isThread(){return!!this.threadAnchorId}async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof je.TFile))return!1;try{let s=await this.vault.cachedRead(e);if(this.isThread){let n=JSON.parse(s);return n.messages?.length>0||n.sessionId?(this.messages=(n.messages??[]).map(i=>i.id?i:{...i,id:Rn(i)}),this.claudeSessionId=n.sessionId??null,this.threadAnchorIndex=n.anchorIndex,this.claudeSessionId&&(this.basePromptSent=!0),!0):!1}let a=JSON.parse(s);if(a.name&&(this.conversationName=a.name),a.messages?.length>0)return this.messages=a.messages.map(n=>n.id?n:{...n,id:Rn(n)}),this.claudeSessionId=a.sessionId??null,this.threadIndex=a.threads??{},this.claudeSessionId&&(this.basePromptSent=!0),!0}catch{}return!1}getThreadIndex(){return{...this.threadIndex}}async persist(){let t=new Date().toISOString(),e=this.getChatFilePath(),s;if(this.isThread){let n={anchorMessageId:this.threadAnchorId,anchorIndex:this.threadAnchorIndex??0,sessionId:this.claudeSessionId,messages:this.messages,createdAt:this.parentSession?.threadIndex[this.threadAnchorId]?.createdAt??t,lastActive:t};s=JSON.stringify(n,null,2)}else{let n={sessionId:this.claudeSessionId,messages:this.messages,lastActive:t,name:this.conversationName||void 0,threads:Object.keys(this.threadIndex).length>0?this.threadIndex:void 0};s=JSON.stringify(n,null,2)}let a=this.vault.getAbstractFileByPath(e);a instanceof je.TFile?await this.vault.modify(a,s):(await this.ensureParentFolders(e),await this.vault.create(e,s)),this.isThread&&this.parentSession&&this.threadAnchorId&&await this.parentSession.upsertThreadIndex(this.threadAnchorId,{path:e,createdAt:this.parentSession.threadIndex[this.threadAnchorId]?.createdAt??t,messageCount:this.messages.length,lastActive:t})}async upsertThreadIndex(t,e){this.threadIndex[t]=e,await this.persist()}async ensureParentFolders(t){let e=t.lastIndexOf("/");if(e<=0)return;let a=t.slice(0,e).split("/"),n="";for(let i of a)if(n=n?`${n}/${i}`:i,!this.vault.getAbstractFileByPath(n))try{await this.vault.createFolder(n)}catch(o){if(!(o instanceof Error?o.message:String(o)).includes("already exists"))throw o}}async clearPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);e instanceof je.TFile&&await this.vault.delete(e),this.messages=[],this.claudeSessionId=null,this.basePromptSent=!1}getChatFilePath(){if(this.threadAnchorId&&this.parentSession)return this.parentSession.getThreadFilePath(this.threadAnchorId);if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=me(this.conversationId)||"conversation";return(0,je.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.inAppConversationId&&this.inAppConversationId!=="default"){let e=me(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let a=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${a}/conversations/${e}.json`)}let s=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${s}/${this.agent.name}-conversations/${e}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${t}/${this.agent.name}-chat.json`)}getThreadFilePath(t){let e=this.getParentChatFilePath(),s=e.replace(/\/[^/]+$/,""),a=e.slice(s.length+1).replace(/\.json$/,"");return(0,je.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=me(this.conversationId)||"conversation";return(0,je.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.inAppConversationId&&this.inAppConversationId!=="default"){let e=me(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let a=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${a}/conversations/${e}.json`)}let s=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${s}/${this.agent.name}-conversations/${e}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${t}/${this.agent.name}-chat.json`)}async ensureProcess(){if(this.process&&this.isProcessAlive)return;this.refreshAgent();let t=["--input-format","stream-json","--output-format","stream-json","--verbose"];this.claudeSessionId&&(t.push("--resume",this.claudeSessionId),this.basePromptSent=!0);let e=Bt(null,this.agent,this.settings);As(e.value)&&t.push("--model",e.value);let s=this.agent.permissionMode?.trim();s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions"),this.agent.effort&&t.push("--effort",this.agent.effort);let a=this.agent.cwd??this.repository.getVaultBasePath()??".";this.settingsState=Ds(a,this.agent);let n=pt(this.settings.claudeCliPath,t,{cwd:a,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.process=n,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:i=>this.handleStdout(i),onStderr:()=>{},onError:i=>this.handleProcessError(i),onClose:()=>this.handleProcessClose()},n.stdout.on("data",this.processListeners.onStdout),n.stderr.on("data",this.processListeners.onStderr),n.on("error",this.processListeners.onError),n.on("close",this.processListeners.onClose)}detachProcessListeners(){this.process&&this.processListeners&&(this.process.stdout?.removeListener("data",this.processListeners.onStdout),this.process.stderr?.removeListener("data",this.processListeners.onStderr),this.process.removeListener("error",this.processListeners.onError),this.process.removeListener("close",this.processListeners.onClose)),this.processListeners=null}handleStdout(t){this.isStreaming&&this.armWatchdog(),this.stdoutBuffer+=t.toString();let e=fe(this.stdoutBuffer);this.stdoutBuffer=e.pop()??"";for(let s of e){let a=s.trim();if(a)try{let n=JSON.parse(a);this.handleEvent(n)}catch{}}}handleEvent(t){if(typeof t.session_id=="string"&&(this.claudeSessionId=t.session_id),this.updateStatsFromEvent(t),t.type==="system"&&t.subtype==="compact_boundary"){let s=t.compact_metadata,a=s&&typeof s.pre_tokens=="number"?s.pre_tokens:0,n=s&&typeof s.post_tokens=="number"?s.post_tokens:0;n>0&&(this.stats.contextTokensUsed=n),this.stats.lastCompact={preTokens:a,postTokens:n},this.emitStats(),this.needsCompactBeforeNextTurn=!1,this.activeOnEvent?.({type:"compacted",content:"",compact:{preTokens:a,postTokens:n}});return}if(t.type==="result"){if(t.is_error===!0||typeof t.api_error_status=="string"&&t.api_error_status){let s=this.describeResultError(t);this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd();return}let e=this.parseStreamEvent(t);if(e){if(e.type==="text"){let s=this.turnResponseText.length===0;this.turnResponseText+=e.content,this.setCurrentTool(void 0),s&&this.turnResponseText.length>0&&this.emitActivity()}else e.type==="tool_use"&&e.toolName&&(this.turnToolCalls.push({name:e.toolName,command:e.content||void 0}),this.setCurrentTool(e.toolName));this.activeOnEvent?.(e)}}get hasCurrentTurnText(){return this.turnResponseText.length>0}describeResultError(t){let e=[],s=typeof t.api_error_status=="string"?t.api_error_status:"",a=typeof t.subtype=="string"?t.subtype:"",n=typeof t.result=="string"?t.result:"";return s?e.push(`API ${s}`):a?e.push(a.replace(/_/g," ")):e.push("unknown error"),n&&e.push(`\u2014 ${n}`),e.join(" ")}updateStatsFromEvent(t){let e=!1,s=typeof t.model=="string"?t.model:void 0,a=t.message,n=a&&typeof a.model=="string"?a.model:void 0,i=s||n;if(i&&i!==this.stats.concreteModel&&(this.stats.concreteModel=i,e=!0),t.type==="rate_limit_event"){let o=t.rate_limit_info;o&&(this.stats.rateLimit={type:typeof o.rateLimitType=="string"?o.rateLimitType:"unknown",resetsAt:typeof o.resetsAt=="number"?o.resetsAt:void 0,status:typeof o.status=="string"?o.status:void 0,isUsingOverage:typeof o.isUsingOverage=="boolean"?o.isUsingOverage:void 0},e=!0)}if(t.type==="assistant"&&a){let o=a.usage;if(o){let c=typeof o.input_tokens=="number"?o.input_tokens:0,l=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0,d=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,h=c+l+d;h>0&&h!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=h,e=!0)}}if(t.type==="result"){let o=typeof t.total_cost_usd=="number"?t.total_cost_usd:0;o>0&&(this.stats.costTotalUsd+=o,e=!0);let c=t.modelUsage;if(c)for(let l of Object.values(c)){let d=l;typeof d.contextWindow=="number"&&d.contextWindow!==this.stats.contextWindow&&(this.stats.contextWindow=d.contextWindow,e=!0)}this.stats.turnCount+=1,e=!0}t.type==="result"&&this.evaluateAutoCompact(),e&&this.emitStats()}evaluateAutoCompact(){let t=this.agent.autoCompactThreshold??0;if(t<=0||t>=100)return;let e=this.stats.contextWindow,s=this.stats.contextTokensUsed;!e||!s||s/e*1000?[...this.turnToolCalls]:void 0});let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};if(this.activeOnEvent?.({type:"result",content:"",toolCalls:[...this.turnToolCalls]}),this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns--,this.pendingTurns<=0){this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),Et(this.settingsState),this.settingsState=null;let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,Et(this.settingsState),this.settingsState=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:Fs(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}async sendMessage(t,e,s,a){this.lastActiveAt=Date.now(),this.messages.push({id:Fs(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:a&&a.length>0?a:void 0});let n=s??t;this.basePromptSent||(n=`${await this.buildBasePrompt()} +`)}catch{clearTimeout(p),u()}})}async probeHttpServer(t){let e=await this.findServerToken(t);if(!e)return console.log(`McpManager: no auth token for ${t.name}, skipping tool probe`),[];let s=t.url.endsWith("/sse")?t.url.replace(/\/sse$/,"/mcp"):t.url;try{let n=(await this.httpRequest(s,e,{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-03-26",capabilities:{},clientInfo:{name:"agent-fleet",version:"1.0.0"}}}))?._sessionId;await this.httpRequest(s,e,{jsonrpc:"2.0",method:"notifications/initialized"},n);let i=await this.httpRequest(s,e,{jsonrpc:"2.0",id:2,method:"tools/list"},n),o=[],d=i?.result?.tools??[];for(let h of d)o.push({name:h.name,description:h.description,inputSchema:h.inputSchema});return o}catch(a){return console.warn(`McpManager: HTTP probe failed for ${t.name}:`,a),[]}}async findServerToken(t){if(this.authManager){let i=this.authManager.getToken(t.name);if(i)return i}let e=this.readTokenFromClaudeConfig(t.name);if(e)return this.authManager?.storeProbeToken(t.name,e),e;let s=t.name.replace(/^claude\.ai\s+/i,"").replace(/\s+/g,"_").toUpperCase(),a=[`${s}_API_KEY`,`${s}_API_KEY_MILO`,`${s}_TOKEN`];for(let i of a){let o=process.env[i];if(o)return o}let n=[Pt.join(ua(),".openclaw","workspace",".env"),Pt.join(ua(),".env")];for(let i of n)try{let o=ze.readFileSync(i,"utf8");for(let c of me(o)){let l=c.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(l){let d=l[1],h=l[2].replace(/^["']|["']$/g,"");if(a.includes(d))return h}}}catch{}}httpRequest(t,e,s,a){return new Promise((n,i)=>{let o=JSON.stringify(s),c=new URL(t),l=c.protocol==="https:",d={"Content-Type":"application/json",Accept:"application/json, text/event-stream",Authorization:`Bearer ${e}`,"Content-Length":String(Buffer.byteLength(o))};a&&(d["mcp-session-id"]=a);let h={hostname:c.hostname,port:c.port||(l?443:80),path:c.pathname+c.search,method:"POST",headers:d},p=(l?En:Rs).request(h,f=>{let y="";f.on("data",k=>{y+=k.toString()}),f.on("end",()=>{let k=f.headers["mcp-session-id"];if((f.headers["content-type"]??"").includes("text/event-stream")){for(let v of me(y))if(v.startsWith("data: "))try{let g=JSON.parse(v.slice(6));k&&(g._sessionId=k),n(g);return}catch{}n(null)}else try{let v=JSON.parse(y);k&&(v._sessionId=k),n(v)}catch{n(null)}})});p.on("error",i);let m=setTimeout(()=>{p.destroy(),n(null)},15e3);p.on("close",()=>clearTimeout(m)),p.write(o),p.end()})}runCli(t){return new Promise((e,s)=>{let a=`${this.settings.claudeCliPath} ${t}`,n=pa(a,{env:{...process.env}}),i="",o="";n.stdout.on("data",c=>{i+=c.toString()}),n.stderr.on("data",c=>{o+=c.toString()}),n.on("error",s),n.on("close",c=>{c!==0&&!i.trim()?s(new Error(o||`Process exited with code ${c}`)):e(i)})})}runCliArgs(t){return new Promise((e,s)=>{let a=pt(this.settings.claudeCliPath,t,{env:{...process.env}}),n="",i="";a.stdout.on("data",o=>{n+=o.toString()}),a.stderr.on("data",o=>{i+=o.toString()}),a.on("error",s),a.on("close",o=>{o!==0&&!n.trim()?s(new Error(i||`Process exited with code ${o}`)):e(n)})})}getDisabledServers(){let t=new Set,e=Pt.join(Zt(),"settings.local.json");try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s);for(let n of a.disabledMcpjsonServers??[])t.add(n)}catch{}try{let s=ze.readFileSync(es(),"utf8"),n=JSON.parse(s).projects;if(n){for(let i of Object.values(n))if(i&&Array.isArray(i.disabledMcpServers))for(let o of i.disabledMcpServers)t.add(o)}}catch{}return t}enableServerInClaudeConfig(t){let e=es();try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s),n=a.projects;if(!n)return;let i=!1;for(let o of Object.values(n))if(o&&Array.isArray(o.disabledMcpServers)){let c=o.disabledMcpServers.indexOf(t);c!==-1&&(o.disabledMcpServers.splice(c,1),i=!0)}i&&ze.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of me(t)){let n=a.trim();if(!n||n.startsWith("Checking"))continue;let i=n.indexOf(": ");if(i===-1)continue;let o=n.slice(0,i).trim(),c=n.slice(i+2),l=c.lastIndexOf(" - ");if(l===-1)continue;let d=c.slice(0,l).trim(),h=c.slice(l+3).trim(),u="disconnected";h.includes("Connected")?u="connected":h.includes("authentication")?u="needs-auth":h.toLowerCase().includes("error")&&(u="error");let p="unknown",m,f;if(d.startsWith("http://")||d.startsWith("https://")){let b=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=b.endsWith("/sse")?"sse":"http",m=b}else d&&(p="stdio",f=d);let y=this.toInternalName(o),k=!s.has(y);e.push({name:o,type:p,status:u,scope:"unknown",enabled:k,url:m,command:f,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of me(e)){let n=a.trim();if(n.startsWith("Type:")){let i=n.slice(5).trim().toLowerCase();i==="stdio"?s.type="stdio":i==="http"?s.type="http":i==="sse"&&(s.type="sse")}else if(n.startsWith("Scope:")){let i=n.slice(6).trim().toLowerCase();i.includes("user")?s.scope="user":i.includes("project")&&(s.scope="project")}else if(n.startsWith("Command:")){let i=n.slice(8).trim();i&&(s.command=i)}else if(n.startsWith("Args:")){let i=n.slice(5).trim();i&&(s.args=i)}}return s}};var xr={"every 5m":"*/5 * * * *","every 10m":"*/10 * * * *","every 15m":"*/15 * * * *","every 30m":"*/30 * * * *","every 1h":"0 * * * *","every 2h":"0 */2 * * *",hourly:"0 * * * *","daily at 9am":"0 9 * * *","daily at 6pm":"0 18 * * *","weekdays at 9am":"0 9 * * 1-5","weekly on monday":"0 9 * * 1","monthly on 1st":"0 9 1 * *"},Ls=class{constructor(t,e){this.maxConcurrentRuns=t;this.callbacks=e}jobs=new Map;activeRuns=0;queue=[];paused=!1;setMaxConcurrentRuns(t){this.maxConcurrentRuns=t}parseSchedule(t){return xr[t.toLowerCase()]??t}toLocalISOString(t){let e=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}T${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}`}async registerTask(t){if(this.unregisterTask(t.taskId),!(!t.enabled||t.type==="immediate"))try{if(t.type==="once"&&t.runAt){let e=new ce(new Date(t.runAt),{name:t.taskId,catch:!0},()=>{this.enqueue({task:t,reason:"scheduled"})});this.jobs.set(t.taskId,e),await this.callbacks.onTaskScheduled(t,t.runAt);return}if(t.type==="recurring"&&t.schedule){let e=this.parseSchedule(t.schedule),s=new ce(e,{name:t.taskId,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.enqueue({task:t,reason:"scheduled"})});this.jobs.set(t.taskId,s);let a=s.nextRun();await this.callbacks.onTaskScheduled(t,a?this.toLocalISOString(a):void 0)}}catch(e){console.error(`Agent Fleet: Failed to register task "${t.taskId}":`,e)}}unregisterTask(t){let e=this.jobs.get(t);e&&(e.stop(),this.jobs.delete(t))}async loadTasks(t){for(let e of t)await this.registerTask(e)}async handleStartupCatchUp(t){let e=Date.now();for(let s of t)!s.enabled||!s.catchUp||!s.nextRun||new Date(s.nextRun).getTime()t.pause())}resumeAll(){this.paused=!1,this.jobs.forEach(t=>t.resume()),this.processQueue()}shutdown(){this.paused=!0,this.jobs.forEach(t=>t.stop()),this.jobs.clear(),this.queue.length=0}getQueueSize(){return this.queue.length}async processQueue(){if(!this.paused)for(;this.activeRuns0;){let t=this.queue.shift();if(!t)return;this.activeRuns+=1,this.callbacks.onTaskTriggered(t).finally(async()=>{this.activeRuns-=1,await this.processQueue()})}}};var is=class r{constructor(t,e){this.repository=t;this.settings=e;this.executor=new Ds(e,t),this.mcpManager=new Is(e),this.scheduler=new Ls(e.maxConcurrentRuns,{onTaskTriggered:s=>this.runPendingTask(s),onTaskScheduled:(s,a)=>this.repository.updateTaskRunMetadata(s,{nextRun:a})})}scheduler;executor;mcpManager;snapshot={agents:[],skills:[],tasks:[],channels:[],validationIssues:[]};runtimeState=new Map;recentRuns=[];chartRuns=[];static CHART_WINDOW_DAYS=14;statusChangeListeners=new Set;runOutputListeners=new Map;runOutputBuffers=new Map;heartbeatJobs=new Map;heartbeatRegisteredAt=0;heartbeatsInFlight=new Set;heartbeatResultHandler;async initialize(){this.snapshot=await this.repository.loadAll(),await this.refreshRunCaches();let t=this.snapshot.tasks.filter(e=>this.repository.getAgentByName(e.agent)?.enabled!==!1);await this.scheduler.loadTasks(t),await this.scheduler.handleStartupCatchUp(t),this.registerHeartbeats(),this.emitStatusChange()}onHeartbeatResult(t){this.heartbeatResultHandler=t}async refreshFromVault(){this.snapshot=await this.repository.loadAll(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}getSnapshot(){return this.snapshot}getRecentRuns(){return this.recentRuns}getChartRuns(){return this.chartRuns}async refreshRunCaches(){this.recentRuns=await this.repository.listRecentRuns();let t=new Date;t.setDate(t.getDate()-(r.CHART_WINDOW_DAYS-1)),this.chartRuns=await this.repository.listRunsSince(t)}getAgentState(t){let e=this.runtimeState.get(t),s=this.snapshot.agents.find(a=>a.name===t);return s&&!s.enabled?{status:"disabled",lastRun:e?.lastRun,currentRunId:e?.currentRunId}:e??{status:"idle"}}getFleetStatus(){let t=new Date,e=o=>String(o).padStart(2,"0"),s=`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`,a=this.recentRuns.filter(o=>{let c=new Date(o.started);return`${c.getFullYear()}-${e(c.getMonth()+1)}-${e(c.getDate())}`===s}).length,n=Array.from(this.runtimeState.values()).filter(o=>o.status==="running").length,i=this.recentRuns.flatMap(o=>o.approvals??[]).filter(o=>o.status==="pending").length;return{running:n,pending:i,completedToday:a}}subscribe(t){return this.statusChangeListeners.add(t),()=>this.statusChangeListeners.delete(t)}onRunOutput(t,e){let s=this.runOutputListeners.get(t);s||(s=new Set,this.runOutputListeners.set(t,s)),s.add(e);let a=this.runOutputBuffers.get(t);return a&&e(a),()=>{this.runOutputListeners.get(t)?.delete(e)}}getRunOutputBuffer(t){return this.runOutputBuffers.get(t)??""}async handleVaultChange(t){await this.repository.loadFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}async handleVaultDelete(t){this.repository.removeFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),await this.refreshRunCaches(),this.emitStatusChange()}abortedAgents=new Set;abortAgentRun(t){let e=this.executor.abortAgent(t);return e&&(this.abortedAgents.add(t),this.runtimeState.set(t,{status:"idle"}),this.emitStatusChange()),e}wasAborted(t){return this.abortedAgents.has(t)}consumeAborted(t){let e=this.abortedAgents.has(t);return this.abortedAgents.delete(t),e}async runTaskNow(t,e){await this.scheduler.enqueue({task:{...t,type:"immediate"},reason:"manual",promptOverride:e})}async runAgentNow(t,e){let s=t.heartbeatBody.trim()&&e==="Run now and summarize the current state."?t.heartbeatBody.trim():e,a=s===t.heartbeatBody.trim()&&t.heartbeatBody.trim().length>0,n={filePath:"",taskId:a?`heartbeat-${Date.now()}`:`manual-${Date.now()}`,agent:t.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:a?[...t.tags,"heartbeat"]:t.tags,body:s};await this.scheduler.enqueue({task:n,reason:a?"heartbeat":"manual",promptOverride:s})}async resolveApproval(t,e,s){t.filePath&&(await this.repository.setApprovalDecision(t.filePath,e,s),await this.refreshRunCaches(),this.emitStatusChange())}async pruneOldRuns(){let t=Date.now()-this.settings.runLogRetentionDays*24*60*60*1e3,e=await this.repository.listRecentRuns(500);for(let s of e)s.filePath&&new Date(s.started).getTime()this.repository.getAgentByName(t.agent)?.enabled!==!1)),this.scheduler.resumeAll(),this.registerHeartbeats()}shutdown(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatsInFlight.clear(),this.scheduler.shutdown()}registerHeartbeats(){for(let[,t]of this.heartbeatJobs)t.stop();this.heartbeatJobs.clear(),this.heartbeatRegisteredAt=Date.now();for(let t of this.snapshot.agents)if(!(!t.enabled||!t.heartbeatEnabled||!t.heartbeatSchedule.trim()||!t.heartbeatBody.trim()))try{let e=new ce(t.heartbeatSchedule,{name:`heartbeat:${t.name}`,catch:!0,protect:!0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone},()=>{this.runHeartbeat(t.name)});this.heartbeatJobs.set(t.name,e)}catch(e){console.error(`Agent Fleet: failed to register heartbeat for "${t.name}":`,e)}}async runHeartbeat(t){if(!(Date.now()-this.heartbeatRegisteredAt<1e4)&&!this.heartbeatsInFlight.has(t)){this.heartbeatsInFlight.add(t);try{let e=this.repository.getAgentByName(t);if(!e||!e.enabled||!e.heartbeatBody.trim())return;let s={filePath:"",taskId:`heartbeat-${Date.now()}`,agent:e.name,type:"immediate",priority:"medium",enabled:!0,created:new Date().toISOString(),runCount:0,catchUp:!1,tags:[...e.tags,"heartbeat"],body:e.heartbeatBody.trim()};await this.scheduler.enqueue({task:s,reason:"heartbeat",promptOverride:e.heartbeatBody.trim()})}finally{this.heartbeatsInFlight.delete(t)}}}getNextHeartbeat(t){let e=this.heartbeatJobs.get(t);return e?e.nextRun()??null:null}async runPendingTask({task:t,promptOverride:e}){let s=this.repository.getAgentByName(t.agent);if(!s||!s.enabled)return;let a=new Date().toISOString();this.runtimeState.set(s.name,{status:"running",currentTaskId:t.taskId,runStarted:a}),this.runOutputBuffers.set(s.name,""),this.emitStatusChange();try{let n=await this.executor.execute(s,t,e,u=>{let p=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,p+u);let m=this.runOutputListeners.get(s.name);if(m)for(let f of m)f(u)}),i=this.consumeAborted(s.name),o=i?[]:this.buildApprovals(s,n.toolsUsed),c=i?"cancelled":this.resolveRunStatus(n,o),l={runId:n.runId,agent:s.name,task:t.taskId,status:c,started:a,completed:new Date().toISOString(),durationSeconds:n.durationSeconds,tokensUsed:n.tokensUsed,costUsd:n.costUsd,model:n.resolvedModel||s.model,modelSource:n.modelSource,concreteModel:n.concreteModel,exitCode:n.exitCode,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:n.prompt,output:n.outputText,toolsUsed:n.toolsUsed.map(u=>`${u.tool}${u.command?`: ${u.command}`:""}`),finalResult:n.finalResult,stderr:n.stderr,approvals:o},d=await this.repository.writeRunLog(l);if(await this.repository.updateTaskRunMetadata(t,{lastRun:a,runCount:t.runCount+1}),s.memory){let u=An(n.outputText);try{await this.repository.appendMemory(s.name,u)}catch(p){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,p)}}if(t.tags.includes("heartbeat")&&!i&&s.heartbeatChannel&&l.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,l.output)}catch(u){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,u)}i&&(l.output="Task was manually stopped."),await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:i||c==="success"?"idle":c==="pending_approval"?"pending":"error",currentRunId:n.runId,lastRun:{...l,filePath:d}}),i||this.notify(l)}catch(n){let i=this.consumeAborted(s.name),o=i?"cancelled":"failure",c=Bt(t,s,this.settings),l={runId:(0,Pn.randomUUID)(),agent:s.name,task:t.taskId,status:o,started:a,completed:new Date().toISOString(),durationSeconds:Math.round((Date.now()-new Date(a).getTime())/1e3),model:c.value||s.model,modelSource:c.source,exitCode:i?-1:1,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:e??t.body,output:i?"Task was manually stopped.":n instanceof Error?n.message:String(n),toolsUsed:[]},d=await this.repository.writeRunLog(l);await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:i?"idle":"error",lastRun:{...l,filePath:d}}),i||this.notify(l)}finally{this.runOutputBuffers.delete(s.name),this.runOutputListeners.delete(s.name),this.emitStatusChange()}}buildApprovals(t,e){let s=e.filter(a=>t.approvalRequired.includes(a.tool)).map(a=>({tool:a.tool,command:a.command,reason:a.reason,status:"pending"}));return s.length>0?s:void 0}resolveRunStatus(t,e){return e?.length?"pending_approval":t.timedOut?"timeout":t.exitCode===0?"success":"failure"}notify(t){if(this.settings.notificationLevel==="none"||this.settings.notificationLevel==="failures-only"&&t.status==="success")return;let s=(me(t.output).map(n=>n.trim()).find(n=>n&&!n.startsWith("{")&&!n.startsWith("["))??"").slice(0,120)||t.status,a=t.status==="success"?`\u2705 ${t.agent}: ${s}`:t.status==="pending_approval"?`\u{1F535} ${t.agent} needs approval: ${(t.approvals??[])[0]?.tool??"tool action"}`:`\u274C ${t.agent}: ${s}`;new Dn.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Ms=class{tokens=new Map;oauthTokens=new Map;storeProbeToken(t,e){this.tokens.set(t,e)}storeOAuthToken(t,e){this.oauthTokens.set(t,e),this.tokens.set(t,e.accessToken)}getToken(t){return this.tokens.get(t)}getOAuthToken(t){return this.oauthTokens.get(t)}hasToken(t){return this.tokens.has(t)}removeToken(t){this.tokens.delete(t),this.oauthTokens.delete(t)}getExpiringTokens(t=5*6e4){let e=new Map,s=Date.now();for(let[a,n]of this.oauthTokens)n.refreshToken&&n.expiresAt&&n.expiresAt-s0?t:r.WATCHDOG_FALLBACK_MINUTES)*60*1e3}armWatchdog(){this.clearWatchdog();let t=this.getWatchdogMs();this.watchdogTimer=setTimeout(()=>{if(this.watchdogTimer=null,!this.isStreaming)return;this.activeOnEvent?.({type:"error",content:"",errorMessage:`no response from the CLI for ${Math.round(t/6e4)} minutes \u2014 giving up`});let e=new Error("Watchdog timeout");this.handleProcessError(e);try{this.process?.kill()}catch{}},t)}clearWatchdog(){this.watchdogTimer&&(clearTimeout(this.watchdogTimer),this.watchdogTimer=null)}currentToolName;activityListeners=new Set;onActivityChange(t){return this.activityListeners.add(t),t(),()=>{this.activityListeners.delete(t)}}emitActivity(){for(let t of this.activityListeners)t()}setStreaming(t){this.isStreaming!==t&&(this.isStreaming=t,t||(this.currentToolName=void 0),this.emitActivity())}setCurrentTool(t){this.currentToolName!==t&&(this.currentToolName=t,this.emitActivity())}stats={costTotalUsd:0,turnCount:0};statsListeners=new Set;onStatsChange(t){return this.statsListeners.add(t),t({...this.stats}),()=>{this.statsListeners.delete(t)}}getStats(){return{...this.stats}}emitStats(){let t={...this.stats};for(let e of this.statsListeners)e(t)}getConversationName(){return this.conversationName}async setConversationName(t){this.conversationName=t,await this.persist()}refreshAgent(){let t=this.repository.getAgentByName(this.agent.name);t&&(this.agent=t)}get isThread(){return!!this.threadAnchorId}async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof je.TFile))return!1;try{let s=await this.vault.cachedRead(e);if(this.isThread){let n=JSON.parse(s);return n.messages?.length>0||n.sessionId?(this.messages=(n.messages??[]).map(i=>i.id?i:{...i,id:Rn(i)}),this.claudeSessionId=n.sessionId??null,this.threadAnchorIndex=n.anchorIndex,this.claudeSessionId&&(this.basePromptSent=!0),!0):!1}let a=JSON.parse(s);if(a.name&&(this.conversationName=a.name),a.messages?.length>0)return this.messages=a.messages.map(n=>n.id?n:{...n,id:Rn(n)}),this.claudeSessionId=a.sessionId??null,this.threadIndex=a.threads??{},this.claudeSessionId&&(this.basePromptSent=!0),!0}catch{}return!1}getThreadIndex(){return{...this.threadIndex}}async persist(){let t=new Date().toISOString(),e=this.getChatFilePath(),s;if(this.isThread){let n={anchorMessageId:this.threadAnchorId,anchorIndex:this.threadAnchorIndex??0,sessionId:this.claudeSessionId,messages:this.messages,createdAt:this.parentSession?.threadIndex[this.threadAnchorId]?.createdAt??t,lastActive:t};s=JSON.stringify(n,null,2)}else{let n={sessionId:this.claudeSessionId,messages:this.messages,lastActive:t,name:this.conversationName||void 0,threads:Object.keys(this.threadIndex).length>0?this.threadIndex:void 0};s=JSON.stringify(n,null,2)}let a=this.vault.getAbstractFileByPath(e);a instanceof je.TFile?await this.vault.modify(a,s):(await this.ensureParentFolders(e),await this.vault.create(e,s)),this.isThread&&this.parentSession&&this.threadAnchorId&&await this.parentSession.upsertThreadIndex(this.threadAnchorId,{path:e,createdAt:this.parentSession.threadIndex[this.threadAnchorId]?.createdAt??t,messageCount:this.messages.length,lastActive:t})}async upsertThreadIndex(t,e){this.threadIndex[t]=e,await this.persist()}async ensureParentFolders(t){let e=t.lastIndexOf("/");if(e<=0)return;let a=t.slice(0,e).split("/"),n="";for(let i of a)if(n=n?`${n}/${i}`:i,!this.vault.getAbstractFileByPath(n))try{await this.vault.createFolder(n)}catch(o){if(!(o instanceof Error?o.message:String(o)).includes("already exists"))throw o}}async clearPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);e instanceof je.TFile&&await this.vault.delete(e),this.messages=[],this.claudeSessionId=null,this.basePromptSent=!1}getChatFilePath(){if(this.threadAnchorId&&this.parentSession)return this.parentSession.getThreadFilePath(this.threadAnchorId);if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=fe(this.conversationId)||"conversation";return(0,je.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.inAppConversationId&&this.inAppConversationId!=="default"){let e=fe(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let a=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${a}/conversations/${e}.json`)}let s=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${s}/${this.agent.name}-conversations/${e}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${t}/${this.agent.name}-chat.json`)}getThreadFilePath(t){let e=this.getParentChatFilePath(),s=e.replace(/\/[^/]+$/,""),a=e.slice(s.length+1).replace(/\.json$/,"");return(0,je.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=fe(this.conversationId)||"conversation";return(0,je.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.inAppConversationId&&this.inAppConversationId!=="default"){let e=fe(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let a=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${a}/conversations/${e}.json`)}let s=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${s}/${this.agent.name}-conversations/${e}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,je.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,je.normalizePath)(`${t}/${this.agent.name}-chat.json`)}async ensureProcess(){if(this.process&&this.isProcessAlive)return;this.refreshAgent();let t=["--input-format","stream-json","--output-format","stream-json","--verbose"];this.claudeSessionId&&(t.push("--resume",this.claudeSessionId),this.basePromptSent=!0);let e=Bt(null,this.agent,this.settings);As(e.value)&&t.push("--model",e.value);let s=this.agent.permissionMode?.trim();s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions"),this.agent.effort&&t.push("--effort",this.agent.effort);let a=this.agent.cwd??this.repository.getVaultBasePath()??".";this.settingsState=Ps(a,this.agent);let n=pt(this.settings.claudeCliPath,t,{cwd:a,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.process=n,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:i=>this.handleStdout(i),onStderr:()=>{},onError:i=>this.handleProcessError(i),onClose:()=>this.handleProcessClose()},n.stdout.on("data",this.processListeners.onStdout),n.stderr.on("data",this.processListeners.onStderr),n.on("error",this.processListeners.onError),n.on("close",this.processListeners.onClose)}detachProcessListeners(){this.process&&this.processListeners&&(this.process.stdout?.removeListener("data",this.processListeners.onStdout),this.process.stderr?.removeListener("data",this.processListeners.onStderr),this.process.removeListener("error",this.processListeners.onError),this.process.removeListener("close",this.processListeners.onClose)),this.processListeners=null}handleStdout(t){this.isStreaming&&this.armWatchdog(),this.stdoutBuffer+=t.toString();let e=me(this.stdoutBuffer);this.stdoutBuffer=e.pop()??"";for(let s of e){let a=s.trim();if(a)try{let n=JSON.parse(a);this.handleEvent(n)}catch{}}}handleEvent(t){if(typeof t.session_id=="string"&&(this.claudeSessionId=t.session_id),this.updateStatsFromEvent(t),t.type==="system"&&t.subtype==="compact_boundary"){let s=t.compact_metadata,a=s&&typeof s.pre_tokens=="number"?s.pre_tokens:0,n=s&&typeof s.post_tokens=="number"?s.post_tokens:0;n>0&&(this.stats.contextTokensUsed=n),this.stats.lastCompact={preTokens:a,postTokens:n},this.emitStats(),this.needsCompactBeforeNextTurn=!1,this.activeOnEvent?.({type:"compacted",content:"",compact:{preTokens:a,postTokens:n}});return}if(t.type==="result"){if(t.is_error===!0||typeof t.api_error_status=="string"&&t.api_error_status){let s=this.describeResultError(t);this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd();return}let e=this.parseStreamEvent(t);if(e){if(e.type==="text"){let s=this.turnResponseText.length===0;this.turnResponseText+=e.content,this.setCurrentTool(void 0),s&&this.turnResponseText.length>0&&this.emitActivity()}else e.type==="tool_use"&&e.toolName&&(this.turnToolCalls.push({name:e.toolName,command:e.content||void 0}),this.setCurrentTool(e.toolName));this.activeOnEvent?.(e)}}get hasCurrentTurnText(){return this.turnResponseText.length>0}describeResultError(t){let e=[],s=typeof t.api_error_status=="string"?t.api_error_status:"",a=typeof t.subtype=="string"?t.subtype:"",n=typeof t.result=="string"?t.result:"";return s?e.push(`API ${s}`):a?e.push(a.replace(/_/g," ")):e.push("unknown error"),n&&e.push(`\u2014 ${n}`),e.join(" ")}updateStatsFromEvent(t){let e=!1,s=typeof t.model=="string"?t.model:void 0,a=t.message,n=a&&typeof a.model=="string"?a.model:void 0,i=s||n;if(i&&i!==this.stats.concreteModel&&(this.stats.concreteModel=i,e=!0),t.type==="rate_limit_event"){let o=t.rate_limit_info;o&&(this.stats.rateLimit={type:typeof o.rateLimitType=="string"?o.rateLimitType:"unknown",resetsAt:typeof o.resetsAt=="number"?o.resetsAt:void 0,status:typeof o.status=="string"?o.status:void 0,isUsingOverage:typeof o.isUsingOverage=="boolean"?o.isUsingOverage:void 0},e=!0)}if(t.type==="assistant"&&a){let o=a.usage;if(o){let c=typeof o.input_tokens=="number"?o.input_tokens:0,l=typeof o.cache_read_input_tokens=="number"?o.cache_read_input_tokens:0,d=typeof o.cache_creation_input_tokens=="number"?o.cache_creation_input_tokens:0,h=c+l+d;h>0&&h!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=h,e=!0)}}if(t.type==="result"){let o=typeof t.total_cost_usd=="number"?t.total_cost_usd:0;o>0&&(this.stats.costTotalUsd+=o,e=!0);let c=t.modelUsage;if(c)for(let l of Object.values(c)){let d=l;typeof d.contextWindow=="number"&&d.contextWindow!==this.stats.contextWindow&&(this.stats.contextWindow=d.contextWindow,e=!0)}this.stats.turnCount+=1,e=!0}t.type==="result"&&this.evaluateAutoCompact(),e&&this.emitStats()}evaluateAutoCompact(){let t=this.agent.autoCompactThreshold??0;if(t<=0||t>=100)return;let e=this.stats.contextWindow,s=this.stats.contextTokensUsed;!e||!s||s/e*1000?[...this.turnToolCalls]:void 0});let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};if(this.activeOnEvent?.({type:"result",content:"",toolCalls:[...this.turnToolCalls]}),this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns--,this.pendingTurns<=0){this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),Et(this.settingsState),this.settingsState=null;let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,Et(this.settingsState),this.settingsState=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:Fs(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],this.clearWatchdog(),this.setStreaming(!1),this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}async sendMessage(t,e,s,a){this.lastActiveAt=Date.now(),this.messages.push({id:Fs(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:a&&a.length>0?a:void 0});let n=s??t;this.basePromptSent||(n=`${await this.buildBasePrompt()} ## Task ${n}`,this.basePromptSent=!0),await this.ensureProcess();let i=this.needsCompactBeforeNextTurn;i&&(this.needsCompactBeforeNextTurn=!1,this.lastCompactTriggerAt=Date.now()),this.stats.lastCompact&&(this.stats.lastCompact=void 0,this.emitStats()),this.activeOnEvent=e,this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=i?2:1,this.setStreaming(!0),this.armWatchdog();let o=c=>{let l=JSON.stringify({type:"user",message:{role:"user",content:c}});this.process.stdin.write(l+` @@ -11816,10 +11816,10 @@ ${n.join(` `&&(e+=1),t.push({kind:"code",text:r.slice(a,e)}),a=e,s=!1):(e>a&&t.push({kind:"prose",text:r.slice(a,e)}),a=e,s=!0,e+=3):e+=1;return a{if(s.isCode)return s.text;let a=s.text;return a=a.replace(/&/g,"&").replace(//g,">"),a=a.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(n,i,o)=>`<${o}|${i}>`),a=a.replace(/\*\*([^*]+)\*\*/g,"*$1*"),a=a.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),a}).join("")}function Tr(r){let t=[],e=/`([^`\n]+)`/g,s=0,a;for(;a=e.exec(r);)a.index>s&&t.push({text:r.slice(s,a.index),isCode:!1}),t.push({text:a[0],isCode:!0}),s=a.index+a[0].length;return st;){let a=s.slice(0,t),n=_r(a)%2===1,i;if(n){let o=Ar(a);if(o>0)i=o;else{e.push(a+"\n```"),s="```\n"+s.slice(t);continue}}else{if(i=a.lastIndexOf(` `),it/2?i=o:i=t}i<=0&&(i=t)}e.push(s.slice(0,i)),s=s.slice(i).replace(/^\n+/,"")}return s.length>0&&e.push(s),e}function _r(r){let t=0,e=0;for(;(e=r.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function Ar(r){let t=0,e=0,s=0;for(;sDate.now());let e=t.getSettings();this.rateLimiter=new Ns({maxPerWindow:Math.max(1,e.channelRateLimitPerConversation),windowMs:Math.max(1e3,e.channelRateLimitWindowMinutes*6e4),now:this.now})}adapters=new Map;adapterConfigs=new Map;sessions=new Map;conversationLocks=new Map;rateLimiter;metrics=new Map;statusListeners=new Set;adapterUnsubscribes=new Map;now;threadBindings=new Map;hibernationInterval=null;started=!1;async start(t){if(!this.started){this.started=!0;for(let e of t.channels)await this.bringUpChannel(e,t);this.hibernationInterval=setInterval(()=>{this.runHibernationSweep()},6e4)}}async stop(){if(!this.started)return;this.started=!1,this.hibernationInterval&&(clearInterval(this.hibernationInterval),this.hibernationInterval=null);let t=Array.from(this.adapters.values());await Promise.all(t.map(async e=>{try{await e.stop()}catch(s){console.warn(`Agent Fleet: channel adapter ${e.config.name} stop() failed`,s)}})),this.adapters.clear(),this.adapterConfigs.clear();for(let e of this.adapterUnsubscribes.values())for(let s of e)s();this.adapterUnsubscribes.clear(),this.conversationLocks.size>0&&await Promise.allSettled(Array.from(this.conversationLocks.values()));for(let e of this.sessions.values())try{e.session.isStreaming?e.session.abort():e.session.hibernate()}catch{}this.sessions.clear(),this.conversationLocks.clear(),this.conversationLockGen.clear(),this.rateLimiter.resetAll()}async reconcile(t){if(!this.started)return;let e=new Map;for(let s of t.channels)e.set(s.name,s);for(let[s,a]of this.adapters){let n=e.get(s),i=n&&this.isChannelRuntimeValid(n,t);if(!n||!n.enabled||!i){await this.tearDownChannel(s);continue}let o=this.adapterConfigs.get(s);o&&this.requiresRestart(o,n)?(await this.tearDownChannel(s),await this.bringUpChannel(n,t)):(this.adapterConfigs.set(s,n),a.config=n)}for(let[s,a]of e)!this.adapters.has(s)&&a.enabled&&this.isChannelRuntimeValid(a,t)&&await this.bringUpChannel(a,t);this.notifyStatusListeners()}getCredentials(){return this.deps.getChannelCredentials?.()??this.deps.getSettings().channelCredentials??{}}isChannelRuntimeValid(t,e){let s=e.agents.find(n=>n.name===t.defaultAgent);if(!s||s.approvalRequired.length>0)return!1;let a=this.getCredentials()[t.credentialRef];return!(!a||a.type!==t.type)}async bringUpChannel(t,e){if(!t.enabled||!this.isChannelRuntimeValid(t,e))return;let s=this.getCredentials()[t.credentialRef];if(!s)return;let a;try{a=this.deps.adapterFactory(t,s)}catch(i){console.error(`Agent Fleet: failed to build adapter for channel ${t.name}`,i);return}let n=[];n.push(a.onInbound(i=>{this.handleInbound(a,i)})),n.push(a.onStatusChange(()=>this.notifyStatusListeners())),a.onAgentSwitch&&n.push(a.onAgentSwitch((i,o,c)=>{let l=`${t.name}:${i}`;this.threadBindings.set(l,o),this.persistBindings(t.name)})),this.adapters.set(t.name,a),this.adapterConfigs.set(t.name,t),this.adapterUnsubscribes.set(t.name,n),this.ensureMetrics(t.name),await this.loadBindings(t.name);try{await a.start()}catch(i){console.error(`Agent Fleet: channel adapter ${t.name} start() failed`,i)}this.notifyStatusListeners()}async tearDownChannel(t){let e=this.adapters.get(t);if(!e)return;try{await e.stop()}catch(n){console.warn(`Agent Fleet: channel adapter ${t} stop() failed`,n)}let s=this.adapterUnsubscribes.get(t);if(s)for(let n of s)n();this.adapterUnsubscribes.delete(t),this.adapters.delete(t),this.adapterConfigs.delete(t);let a=`${t}:`;for(let[n,i]of this.sessions)if(n.startsWith(a)){try{i.session.isStreaming?i.session.abort():i.session.hibernate()}catch{}this.sessions.delete(n)}}requiresRestart(t,e){return t.type!==e.type||t.credentialRef!==e.credentialRef||JSON.stringify(t.transport)!==JSON.stringify(e.transport)}async handleInbound(t,e){let s=t.config,a=`${s.name}:${e.conversationId}`;if(!(s.allowedUsers.length>0&&(!e.externalUserId||!s.allowedUsers.includes(e.externalUserId)))){if(!this.rateLimiter.tryConsume(a)){console.warn(`Agent Fleet: rate-limited message from ${e.externalUserId} on ${s.name} (conversation ${e.conversationId})`);try{await t.send(e.conversationId,"_Rate limit exceeded. Please slow down and try again in a few minutes._")}catch(n){console.warn(`Agent Fleet: rate-limit reply failed on ${s.name}`,n)}return}await this.withConversationLock(a,async()=>{let n=this.ensureMetrics(s.name);n.messagesReceived+=1,n.lastMessageAt=this.now();let i=this.resolveAllowedAgents(s),o=Er(e.text,i),c,l;if(o){c=o.agent,l=o.rest;let h=this.threadBindings.get(a);if(this.threadBindings.set(a,c),this.persistBindings(s.name),h!==c)try{await t.setThreadTitle?.(e.conversationId,c)}catch{}if(!l){try{await t.send(e.conversationId,`_Now chatting with *${c}*. Send your next message to start._`)}catch{}return}}else{let h=`${s.name}:${e.conversationId.replace(/:thread:[^:]+$/,`:user:${e.externalUserId}`)}`;c=this.threadBindings.get(a)??this.threadBindings.get(h)??s.defaultAgent,l=e.text}try{await t.setTyping(e.conversationId,!0)}catch{}if(e.images&&e.images.length>0){let h=await this.saveInboundImages(e.images);h&&(l=h+(l||"Please analyze this image."))}let d="";try{let u=await(await this.getOrCreateSession(s,e.conversationId,c)).sendMessage(l,()=>{});if(d=u.text.trim(),u.toolCalls.length>0){let p=Dr(u.toolCalls);p&&(d+=` +`);o>t/2?i=o:i=t}i<=0&&(i=t)}e.push(s.slice(0,i)),s=s.slice(i).replace(/^\n+/,"")}return s.length>0&&e.push(s),e}function _r(r){let t=0,e=0;for(;(e=r.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function Ar(r){let t=0,e=0,s=0;for(;sDate.now());let e=t.getSettings();this.rateLimiter=new Ns({maxPerWindow:Math.max(1,e.channelRateLimitPerConversation),windowMs:Math.max(1e3,e.channelRateLimitWindowMinutes*6e4),now:this.now})}adapters=new Map;adapterConfigs=new Map;sessions=new Map;conversationLocks=new Map;rateLimiter;metrics=new Map;statusListeners=new Set;adapterUnsubscribes=new Map;now;threadBindings=new Map;hibernationInterval=null;started=!1;async start(t){if(!this.started){this.started=!0;for(let e of t.channels)await this.bringUpChannel(e,t);this.hibernationInterval=setInterval(()=>{this.runHibernationSweep()},6e4)}}async stop(){if(!this.started)return;this.started=!1,this.hibernationInterval&&(clearInterval(this.hibernationInterval),this.hibernationInterval=null);let t=Array.from(this.adapters.values());await Promise.all(t.map(async e=>{try{await e.stop()}catch(s){console.warn(`Agent Fleet: channel adapter ${e.config.name} stop() failed`,s)}})),this.adapters.clear(),this.adapterConfigs.clear();for(let e of this.adapterUnsubscribes.values())for(let s of e)s();this.adapterUnsubscribes.clear(),this.conversationLocks.size>0&&await Promise.allSettled(Array.from(this.conversationLocks.values()));for(let e of this.sessions.values())try{e.session.isStreaming?e.session.abort():e.session.hibernate()}catch{}this.sessions.clear(),this.conversationLocks.clear(),this.conversationLockGen.clear(),this.rateLimiter.resetAll()}async reconcile(t){if(!this.started)return;let e=new Map;for(let s of t.channels)e.set(s.name,s);for(let[s,a]of this.adapters){let n=e.get(s),i=n&&this.isChannelRuntimeValid(n,t);if(!n||!n.enabled||!i){await this.tearDownChannel(s);continue}let o=this.adapterConfigs.get(s);o&&this.requiresRestart(o,n)?(await this.tearDownChannel(s),await this.bringUpChannel(n,t)):(this.adapterConfigs.set(s,n),a.config=n)}for(let[s,a]of e)!this.adapters.has(s)&&a.enabled&&this.isChannelRuntimeValid(a,t)&&await this.bringUpChannel(a,t);this.notifyStatusListeners()}getCredentials(){return this.deps.getChannelCredentials?.()??this.deps.getSettings().channelCredentials??{}}isChannelRuntimeValid(t,e){let s=e.agents.find(n=>n.name===t.defaultAgent);if(!s||s.approvalRequired.length>0)return!1;let a=this.getCredentials()[t.credentialRef];return!(!a||a.type!==t.type)}async bringUpChannel(t,e){if(!t.enabled||!this.isChannelRuntimeValid(t,e))return;let s=this.getCredentials()[t.credentialRef];if(!s)return;let a;try{a=this.deps.adapterFactory(t,s)}catch(i){console.error(`Agent Fleet: failed to build adapter for channel ${t.name}`,i);return}let n=[];n.push(a.onInbound(i=>{this.handleInbound(a,i)})),n.push(a.onStatusChange(()=>this.notifyStatusListeners())),a.onAgentSwitch&&n.push(a.onAgentSwitch((i,o,c)=>{let l=`${t.name}:${i}`;this.threadBindings.set(l,o),this.persistBindings(t.name)})),this.adapters.set(t.name,a),this.adapterConfigs.set(t.name,t),this.adapterUnsubscribes.set(t.name,n),this.ensureMetrics(t.name),await this.loadBindings(t.name);try{await a.start()}catch(i){console.error(`Agent Fleet: channel adapter ${t.name} start() failed`,i)}this.notifyStatusListeners()}async tearDownChannel(t){let e=this.adapters.get(t);if(!e)return;try{await e.stop()}catch(n){console.warn(`Agent Fleet: channel adapter ${t} stop() failed`,n)}let s=this.adapterUnsubscribes.get(t);if(s)for(let n of s)n();this.adapterUnsubscribes.delete(t),this.adapters.delete(t),this.adapterConfigs.delete(t);let a=`${t}:`;for(let[n,i]of this.sessions)if(n.startsWith(a)){try{i.session.isStreaming?i.session.abort():i.session.hibernate()}catch{}this.sessions.delete(n)}}requiresRestart(t,e){return t.type!==e.type||t.credentialRef!==e.credentialRef||JSON.stringify(t.transport)!==JSON.stringify(e.transport)}async handleInbound(t,e){let s=t.config,a=`${s.name}:${e.conversationId}`;if(!(s.allowedUsers.length>0&&(!e.externalUserId||!s.allowedUsers.includes(e.externalUserId)))){if(!this.rateLimiter.tryConsume(a)){console.warn(`Agent Fleet: rate-limited message from ${e.externalUserId} on ${s.name} (conversation ${e.conversationId})`);try{await t.send(e.conversationId,"_Rate limit exceeded. Please slow down and try again in a few minutes._")}catch(n){console.warn(`Agent Fleet: rate-limit reply failed on ${s.name}`,n)}return}await this.withConversationLock(a,async()=>{let n=this.ensureMetrics(s.name);n.messagesReceived+=1,n.lastMessageAt=this.now();let i=this.resolveAllowedAgents(s),o=Er(e.text,i),c,l;if(o){c=o.agent,l=o.rest;let h=this.threadBindings.get(a);if(this.threadBindings.set(a,c),this.persistBindings(s.name),h!==c)try{await t.setThreadTitle?.(e.conversationId,c)}catch{}if(!l){try{await t.send(e.conversationId,`_Now chatting with *${c}*. Send your next message to start._`)}catch{}return}}else{let h=`${s.name}:${e.conversationId.replace(/:thread:[^:]+$/,`:user:${e.externalUserId}`)}`;c=this.threadBindings.get(a)??this.threadBindings.get(h)??s.defaultAgent,l=e.text}try{await t.setTyping(e.conversationId,!0)}catch{}if(e.images&&e.images.length>0){let h=await this.saveInboundImages(e.images);h&&(l=h+(l||"Please analyze this image."))}let d="";try{let u=await(await this.getOrCreateSession(s,e.conversationId,c)).sendMessage(l,()=>{});if(d=u.text.trim(),u.toolCalls.length>0){let p=Pr(u.toolCalls);p&&(d+=` _${p}_`)}}catch(h){console.error(`Agent Fleet: channel turn failed on ${s.name}/${e.conversationId}`,h),d=`_Sorry \u2014 the agent run failed. ${h instanceof Error?h.message:String(h)}_`}try{d&&((s.allowedAgents.length>1||s.allowedAgents.length===0&&this.resolveAllowedAgents(s).length>1)&&(d=`*[${c}]* -${d}`),await this.deliverReply(t,e.conversationId,d),n.messagesSent+=1)}catch(h){console.error(`Agent Fleet: reply delivery failed on ${s.name}`,h)}finally{try{await t.setTyping(e.conversationId,!1)}catch{}}this.enforceHardCap()})}}async deliverReply(t,e,s){let a=In(s);for(let n of a)await t.send(e,n)}async saveInboundImages(t){let s=`${this.deps.getSettings().fleetFolder}/chat-images`,a=[];for(let n of t)try{this.deps.vault.getAbstractFileByPath((0,dt.normalizePath)(s))||await this.deps.vault.createFolder((0,dt.normalizePath)(s));let i=(0,dt.normalizePath)(`${s}/${n.filename}`),o=i;if(this.deps.vault.getAbstractFileByPath(i)){let h=n.filename.lastIndexOf("."),u=h>0?n.filename.slice(0,h):n.filename,p=h>0?n.filename.slice(h):"";o=(0,dt.normalizePath)(`${s}/${u}_${Date.now()}${p}`)}await this.deps.vault.createBinary(o,n.data);let l=this.deps.vault.adapter.basePath??"",d=l?`${l}/${o}`:o;a.push(`### Image: ${n.filename} +${d}`),await this.deliverReply(t,e.conversationId,d),n.messagesSent+=1)}catch(h){console.error(`Agent Fleet: reply delivery failed on ${s.name}`,h)}finally{try{await t.setTyping(e.conversationId,!1)}catch{}}this.enforceHardCap()})}}async deliverReply(t,e,s){let a=In(s);for(let n of a)await t.send(e,n)}async saveInboundImages(t){let s=`${this.deps.getSettings().fleetFolder}/chat-images`,a=[];for(let n of t)try{this.deps.vault.getAbstractFileByPath((0,ct.normalizePath)(s))||await this.deps.vault.createFolder((0,ct.normalizePath)(s));let i=(0,ct.normalizePath)(`${s}/${n.filename}`),o=i;if(this.deps.vault.getAbstractFileByPath(i)){let h=n.filename.lastIndexOf("."),u=h>0?n.filename.slice(0,h):n.filename,p=h>0?n.filename.slice(h):"";o=(0,ct.normalizePath)(`${s}/${u}_${Date.now()}${p}`)}await this.deps.vault.createBinary(o,n.data);let l=this.deps.vault.adapter.basePath??"",d=l?`${l}/${o}`:o;a.push(`### Image: ${n.filename} The image file is located at: ${d} Please read and analyze this image.`)}catch(i){console.warn("Agent Fleet: failed to save inbound image",n.filename,i)}return a.length===0?"":`## Attached Images @@ -11829,24 +11829,24 @@ ${a.join(` --- -`}async getOrCreateSession(t,e,s){let a=`${t.name}:${e}:${s}`,n=this.sessions.get(a);if(n)return n.session;let i=this.deps.getRepository(),o=i.getAgentByName(s);if(!o)throw new Error(`Channel ${t.name} bound to missing agent ${s}`);let c=new Ut(o,this.deps.getSettings(),i,this.deps.vault,{channelName:t.name,conversationId:`${e}:${s}`,channelContext:t.channelContext||void 0});try{await c.loadPersistedState()}catch{}return this.sessions.set(a,{session:c,channelName:t.name,conversationId:e,sessionKey:a}),c}resolveAllowedAgents(t){return t.allowedAgents.length>0?t.allowedAgents:this.deps.getRepository().getSnapshot().agents.filter(a=>a.enabled).map(a=>a.name)}async persistBindings(t){let e=`${t}:`,s={};for(let[o,c]of this.threadBindings)o.startsWith(e)&&(s[o.slice(e.length)]=c);let a=this.deps.getSettings(),n=(0,dt.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),i=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof dt.TFile)await this.deps.vault.modify(o,i);else{let c=n.slice(0,n.lastIndexOf("/"));if(!this.deps.vault.getAbstractFileByPath(c))try{await this.deps.vault.createFolder(c)}catch{}await this.deps.vault.create(n,i)}}catch(o){console.warn(`Agent Fleet: failed to persist thread bindings for ${t}`,o)}}async loadBindings(t){let e=this.deps.getSettings(),s=(0,dt.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof dt.TFile))return;let n=await this.deps.vault.cachedRead(a),i=JSON.parse(n);for(let[o,c]of Object.entries(i))typeof c=="string"&&this.threadBindings.set(`${t}:${o}`,c)}catch{}}getThreadAgent(t,e){return this.threadBindings.get(`${t}:${e}`)}enforceHardCap(){let t=Math.max(1,this.deps.getSettings().maxConcurrentChannelSessions),e=Array.from(this.sessions.values()).filter(a=>a.session.isProcessAlive&&!a.session.isStreaming);if(e.length<=t)return;e.sort((a,n)=>a.session.lastActiveAt-n.session.lastActiveAt);let s=e.length-t;for(let a=0;a{a=o}),i=(this.conversationLockGen.get(t)??0)+1;this.conversationLockGen.set(t,i),this.conversationLocks.set(t,s.then(()=>n));try{await s,await e()}finally{a(),this.conversationLockGen.get(t)===i&&(this.conversationLocks.delete(t),this.conversationLockGen.delete(t))}}ensureMetrics(t){let e=this.metrics.get(t);return e||(e={messagesReceived:0,messagesSent:0,lastMessageAt:null},this.metrics.set(t,e)),e}getMetrics(t){return{...this.ensureMetrics(t)}}getChannelStatus(t){let e=this.adapters.get(t);return e?e.getStatus():"disabled"}getConnectedCount(){let t=0;for(let e of this.adapters.values())e.getStatus()==="connected"&&(t+=1);return t}getSessionCount(t){let e=0,s=`${t}:`;for(let a of this.sessions.keys())a.startsWith(s)&&(e+=1);return e}onStatusChange(t){return this.statusListeners.add(t),()=>this.statusListeners.delete(t)}notifyStatusListeners(){for(let t of this.statusListeners)try{t()}catch{}}};function Dr(r){if(r.length===0)return"";let t=new Map;for(let s of r)t.set(s.name,(t.get(s.name)??0)+1);let e=[];for(let[s,a]of t)e.push(a>1?`${s}\xD7${a}`:s);return`Used ${r.length} tool${r.length===1?"":"s"}: ${e.join(", ")}`}function Pr(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function rs(r,t){return`${r}-${Pr(t)}`}var $t="af-channel-cred",$s=class{constructor(t){this.storage=t;t||console.warn("Agent Fleet: SecretStorage unavailable (Obsidian < 1.11.4). Secrets will use plaintext fallback.")}get available(){return!!this.storage}setJson(t,e,s){if(!this.storage)return;let a=rs(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=rs(t,e),a=this.storage.getSecret(s);if(!a)return null;try{return JSON.parse(a)}catch{return null}}setString(t,e,s){if(!this.storage)return;let a=rs(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=rs(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=rs(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var js=class{credentials=new Map;secretStore;persistCallback;setSecretStore(t){this.secretStore=t}loadCredentials(t){if(this.credentials.clear(),this.secretStore?.available){let e=this.secretStore.listByPrefix($t);for(let s of e){let a=$t+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson($t,n);if(i){let o=i._ref??n,c={...i};delete c._ref,this.credentials.set(o,c)}}}if(this.credentials.size===0&&t){for(let[e,s]of Object.entries(t))this.credentials.set(e,s);this.secretStore?.available&&this.credentials.size>0&&this.persistToSecretStore()}}onChanged(t){this.persistCallback=t}get(t){return this.credentials.get(t)}set(t,e){this.credentials.set(t,e),this.persist()}delete(t){this.credentials.delete(t),this.secretStore?.delete($t,t),this.persist()}list(){return Array.from(this.credentials.entries()).map(([t,e])=>({ref:t,entry:e}))}toRecord(){let t={};for(let[e,s]of this.credentials.entries())t[e]=s;return t}persist(){this.persistToSecretStore(),this.persistCallback&&this.persistCallback(this.toRecord())}persistToSecretStore(){if(this.secretStore?.available)for(let[t,e]of this.credentials)this.secretStore.setJson($t,t,{...e,_ref:t})}};var Xo=Ke(Ai(),1),Qo=Ke(Js(),1),Zo=Ke(qt(),1),el=Ke(Oa(),1),tl=Ke(Ua(),1),sl=Ke(za(),1),Oi=Ke(ea(),1),al=Ke(Fi(),1);var Va=Oi.default;var Ni=require("obsidian");var nl="https://slack.com/api";function il(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function rl(r){let t=r.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function ol(r){let t=r.split(":");if(t[3]==="thread"&&t[4])return t[4]}var sa=class{type="slack";config;credential;ws=null;status="stopped";stopping=!1;backoffMs=1e3;reconnectTimer=null;inboundHandlers=new Set;statusHandlers=new Set;agentSwitchHandlers=new Set;sendQueues=new Map;threadContext=new Map;constructor(t,e){if(e.type!=="slack")throw new Error(`SlackAdapter requires a slack credential, got ${e.type}`);this.config=t,this.credential=e}async start(){this.stopping=!1,await this.connect()}async stop(){if(this.stopping=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){try{this.ws.close()}catch{}this.ws=null}this.threadContext.clear(),this.sendQueues.clear(),this.setStatus("stopped")}getStatus(){return this.status}async send(t,e){let s=rl(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=ol(t),n=Bs(e);await this.enqueueSend(s,async()=>{await this.slackApi("chat.postMessage",{channel:s,text:n,...a?{thread_ts:a}:{}})})}async broadcast(t){let e=this.config.allowedUsers[0];if(!e){console.warn(`Agent Fleet: broadcast on ${this.config.name} skipped \u2014 no allowed users configured`);return}try{let a=(await this.slackApi("conversations.open",{users:e})).channel?.id;if(!a){console.warn(`Agent Fleet: broadcast \u2014 conversations.open returned no channel for user ${e}`);return}let n=Bs(t);await this.slackApi("chat.postMessage",{channel:a,text:n})}catch(s){console.error(`Agent Fleet: broadcast failed on ${this.config.name}`,s)}}async setThreadTitle(t,e){let s=this.threadContext.get(t);if(s)try{await this.slackApi("assistant.threads.setTitle",{channel_id:s.channelId,thread_ts:s.threadTs,title:e})}catch(a){console.warn(`Agent Fleet: assistant.threads.setTitle failed on ${this.config.name}`,a)}}async setTyping(t,e){let s=this.threadContext.get(t);if(s)try{await this.slackApi("assistant.threads.setStatus",{channel_id:s.channelId,thread_ts:s.threadTs,status:e?"is thinking...":""})}catch(a){console.warn(`Agent Fleet: assistant.threads.setStatus (${e?"on":"off"}) failed on ${this.config.name}`,a)}}onInbound(t){return this.inboundHandlers.add(t),()=>this.inboundHandlers.delete(t)}onStatusChange(t){return this.statusHandlers.add(t),()=>this.statusHandlers.delete(t)}onAgentSwitch(t){return this.agentSwitchHandlers.add(t),()=>this.agentSwitchHandlers.delete(t)}async connect(){if(this.stopping)return;this.setStatus(this.ws?"reconnecting":"connecting");let t;try{let e=await this.slackApi("apps.connections.open",{},{useAppToken:!0});if(!e.ok||!e.url)throw new Error(e.error??"apps.connections.open returned no URL");t=e.url}catch(e){console.error(`Agent Fleet: Slack apps.connections.open failed for channel ${this.config.name}`,e),this.setStatus("needs-auth"),this.scheduleReconnect();return}try{let e=new Va(t);e.on("open",()=>{}),e.on("message",n=>{this.handleSocketData(n)}),e.on("error",n=>{console.warn(`Agent Fleet: Slack WebSocket error on ${this.config.name}`,n),this.setStatus("error")}),e.on("close",()=>{this.ws=null,this.stopping||this.scheduleReconnect()}),this.ws=e;let s=setTimeout(()=>{if(this.status==="connecting"||this.status==="reconnecting"){console.warn(`Agent Fleet: Slack WebSocket connect timeout on ${this.config.name}`);try{e.close()}catch{}}},3e4);e.on("close",()=>clearTimeout(s));let a=this.onStatusChange(n=>{n==="connected"&&(clearTimeout(s),a())})}catch(e){console.error("Agent Fleet: Slack WebSocket open failed",e),this.setStatus("error"),this.scheduleReconnect();return}}handleSocketData(t){let e;try{e=JSON.parse(t.toString())}catch{return}if(e.type==="hello"){this.backoffMs=1e3,this.setStatus("connected");return}if(e.type==="disconnect"){try{this.ws?.close()}catch{}return}if(e.type==="events_api"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.routeEventPayload(e.payload);return}if(e.type==="slash_commands"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleSlashCommand(e.payload);return}if(e.type==="interactive"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleInteraction(e.payload);return}e.envelope_id&&this.ackEnvelope(e.envelope_id)}async handleSlashCommand(t){if(!t)return;let e=t.command,s=t.channel_id,a=t.user_id;if(!(!e||!s||!a)){if(e==="/agents"){let n=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(n.length===0){await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"No agents configured. Set `allowed_agents` in the channel file."});return}let i=n.map(c=>({type:"button",text:{type:"plain_text",text:c===this.config.defaultAgent?`${c} \u2713`:c,emoji:!0},action_id:`switch_agent_${c}`,value:c})),o=[{type:"section",text:{type:"mrkdwn",text:"*Select an agent to chat with:*"}}];for(let c=0;c\s*/,"").trim(),!a.text))return;let o=il(a),c=a.thread_ts??a.ts;if(a.channel&&c&&(this.threadContext.set(o,{channelId:a.channel,threadTs:c}),this.threadContext.size>500)){let h=this.threadContext.keys().next();h.done||this.threadContext.delete(h.value)}let l={conversationId:o,externalUserId:a.user,text:a.text,timestamp:new Date().toISOString(),meta:{slack_channel:a.channel,slack_ts:a.ts,thread_ts:a.thread_ts}};for(let d of this.inboundHandlers)try{d(l)}catch(h){console.error("Agent Fleet: Slack inbound handler threw",h)}}scheduleReconnect(){if(this.stopping||this.reconnectTimer)return;let t=this.backoffMs;this.backoffMs=Math.min(3e4,this.backoffMs*2),console.warn(`Agent Fleet: Slack channel ${this.config.name} scheduling reconnect in ${t}ms`),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopping&&this.connect()},t)}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}async slackApi(t,e,s={}){let a=s.useAppToken?this.credential.appToken:this.credential.botToken,n=`${nl}/${t}`,i=await(0,Ni.requestUrl)({url:n,method:"POST",contentType:"application/json; charset=utf-8",headers:{Authorization:`Bearer ${a}`},body:JSON.stringify(e),throw:!1});if(i.status===429){let c=Number(i.headers["retry-after"]??"1");return await new Promise(l=>setTimeout(l,Math.max(1e3,c*1e3))),this.slackApi(t,e,s)}if(i.status<200||i.status>=300)throw new Error(`Slack ${t} HTTP ${i.status}`);let o=i.json;if(o.ok===!1)throw new Error(`Slack ${t} error: ${o.error??"unknown"}`);return o}async enqueueSend(t,e){let a=(this.sendQueues.get(t)??Promise.resolve()).then(async()=>{try{await e()}finally{await new Promise(i=>setTimeout(i,1e3))}}),n=a.catch(i=>{console.warn(`Agent Fleet: Slack send queue error for ${t}`,i)});this.sendQueues.set(t,n),await a,this.sendQueues.get(t)===n&&this.sendQueues.delete(t)}};var Ya=require("obsidian"),Bi="https://api.telegram.org",aa=class{type="telegram";config;credential;status="stopped";stopping=!1;pollOffset=0;pollTimer=null;backoffMs=1e3;typingIntervals=new Map;pollAbort=null;inboundHandlers=new Set;statusHandlers=new Set;agentSwitchHandlers=new Set;constructor(t,e){if(e.type!=="telegram")throw new Error(`TelegramAdapter requires a telegram credential, got ${e.type}`);this.config=t,this.credential=e}async start(){this.stopping=!1;try{let e=(await this.tgApi("getUpdates",{offset:-1,limit:1})).result?.[0];e&&(this.pollOffset=e.update_id+1)}catch{}try{await this.tgApi("setMyCommands",{commands:[{command:"agents",description:"List available agents"}]})}catch{}this.setStatus("connected"),this.poll()}async stop(){this.stopping=!0,this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=null),this.pollAbort?.abort(),this.pollAbort=null;for(let[,t]of this.typingIntervals)clearInterval(t);this.typingIntervals.clear(),this.setStatus("stopped")}getStatus(){return this.status}async send(t,e){let s=Ui(t);if(!s)return;let a=$i(t),n=ji(e,4096);for(let i of n)await this.tgApi("sendMessage",{chat_id:s,text:i,parse_mode:"Markdown",...a?{message_thread_id:Number(a)}:{}})}async setTyping(t,e){let s=Ui(t);if(!s)return;let a=$i(t),n={chat_id:s,action:"typing"};if(a&&(n.message_thread_id=Number(a)),e){let i=this.typingIntervals.get(t);i&&clearInterval(i);try{await this.tgApi("sendChatAction",n)}catch(c){console.warn("Agent Fleet: Telegram sendChatAction failed",c)}let o=setInterval(()=>{this.tgApi("sendChatAction",n).catch(()=>{})},4500);this.typingIntervals.set(t,o)}else{let i=this.typingIntervals.get(t);i&&(clearInterval(i),this.typingIntervals.delete(t))}}async setThreadTitle(t,e){}async broadcast(t){let e=this.config.allowedUsers[0];if(e)try{let s=ji(t,4096);for(let a of s)await this.tgApi("sendMessage",{chat_id:e,text:a,parse_mode:"Markdown"})}catch(s){console.error(`Agent Fleet: Telegram broadcast failed on ${this.config.name}`,s)}}onInbound(t){return this.inboundHandlers.add(t),()=>this.inboundHandlers.delete(t)}onStatusChange(t){return this.statusHandlers.add(t),()=>this.statusHandlers.delete(t)}onAgentSwitch(t){return this.agentSwitchHandlers.add(t),()=>this.agentSwitchHandlers.delete(t)}poll(){this.stopping||(async()=>{try{this.pollAbort=new AbortController;let t=await this.tgApi("getUpdates",{offset:this.pollOffset,timeout:30,allowed_updates:["message","callback_query"]},this.pollAbort.signal);if(t.ok&&t.result)for(let e of t.result){if(this.pollOffset=e.update_id+1,e.callback_query){this.handleCallbackQuery(e.callback_query);continue}e.message&&this.routeMessage(e.message)}this.backoffMs=1e3,this.status!=="connected"&&this.setStatus("connected")}catch(t){if(t instanceof DOMException&&t.name==="AbortError")return;if(console.warn(`Agent Fleet: Telegram poll failed on ${this.config.name}`,t),this.status!=="error"&&this.status!=="needs-auth"){let e=t instanceof Error?t.message:String(t);this.setStatus(e.includes("401")||e.includes("Unauthorized")?"needs-auth":"error")}await new Promise(e=>setTimeout(e,this.backoffMs)),this.backoffMs=Math.min(3e4,this.backoffMs*2)}this.stopping||(this.pollTimer=setTimeout(()=>this.poll(),100))})()}routeMessage(t){let e=t.photo&&t.photo.length>0,s=t.document&&this.isImageMime(t.document.mime_type),a=!!t.text;if(!t.from||!a&&!e&&!s)return;let n=t.text??t.caption??"";if(n==="/agents"||n.startsWith("/agents@")){this.handleAgentsCommand(t);return}if(n.startsWith("/"))return;let i=String(t.from.id),o=String(t.chat.id),c=t.message_thread_id?String(t.message_thread_id):void 0,l=c?`tg:${o}:topic:${c}`:`tg:${o}`;this.buildAndEmitMessage(t,n,l,i,o,c)}async buildAndEmitMessage(t,e,s,a,n,i){let o=[];try{if(t.photo&&t.photo.length>0){let l=t.photo[t.photo.length-1],d=await this.downloadFile(l.file_id,`photo_${t.message_id}.jpg`,"image/jpeg");d&&o.push(d)}if(t.document&&this.isImageMime(t.document.mime_type)){let l=t.document.file_name??`doc_${t.message_id}`,d=t.document.mime_type??"image/jpeg",h=await this.downloadFile(t.document.file_id,l,d);h&&o.push(h)}}catch(l){console.warn("Agent Fleet: Telegram image download failed",l)}let c={conversationId:s,externalUserId:a,text:e,timestamp:new Date(t.date*1e3).toISOString(),meta:{telegram_chat_id:n,telegram_message_id:t.message_id,telegram_thread_id:i,chat_type:t.chat.type},...o.length>0?{images:o}:{}};for(let l of this.inboundHandlers)try{l(c)}catch(d){console.error("Agent Fleet: Telegram inbound handler threw",d)}}async downloadFile(t,e,s){let n=(await this.tgApi("getFile",{file_id:t})).result?.file_path;if(!n)return null;let i=`${Bi}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,Ya.requestUrl)({url:i,method:"GET"})).arrayBuffer,filename:e,mimeType:s}}isImageMime(t){return t?t==="image/jpeg"||t==="image/png"||t==="image/gif"||t==="image/webp"||t==="image/bmp"||t==="image/tiff":!1}async handleAgentsCommand(t){let e=String(t.chat.id),s=t.message_thread_id,a=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(a.length===0){await this.tgApi("sendMessage",{chat_id:e,text:"No agents configured. Set `allowed_agents` in the channel file.",...s?{message_thread_id:s}:{}});return}let n=a.map(i=>[{text:i===this.config.defaultAgent?`${i} \u2713`:i,callback_data:`switch:${i}`}]);await this.tgApi("sendMessage",{chat_id:e,text:"*Select an agent to chat with:*",parse_mode:"Markdown",reply_markup:{inline_keyboard:n},...s?{message_thread_id:s}:{}})}async handleCallbackQuery(t){let e=t.data;if(!e?.startsWith("switch:")){await this.tgApi("answerCallbackQuery",{callback_query_id:t.id});return}let s=e.slice(7),a=String(t.from.id),n=String(t.message?.chat?.id??t.from.id),i=t.message?.message_thread_id?String(t.message.message_thread_id):void 0,o=i?`tg:${n}:topic:${i}`:`tg:${n}`;for(let c of this.agentSwitchHandlers)try{c(o,s,a)}catch(l){console.error("Agent Fleet: Telegram agent switch handler threw",l)}if(await this.tgApi("answerCallbackQuery",{callback_query_id:t.id,text:`Switched to ${s}`}),t.message){let l=(this.config.allowedAgents.length>0?this.config.allowedAgents:[]).map(d=>[{text:d===s?`${d} \u2713`:d,callback_data:`switch:${d}`}]);try{await this.tgApi("editMessageText",{chat_id:n,message_id:t.message.message_id,text:`*Active agent: ${s}*`,parse_mode:"Markdown",reply_markup:{inline_keyboard:l}})}catch{}}}async tgApi(t,e,s){if(s?.aborted)throw new DOMException("Aborted","AbortError");let a=`${Bi}/bot${this.credential.botToken}/${t}`,n=(0,Ya.requestUrl)({url:a,method:"POST",contentType:"application/json",body:JSON.stringify(e),throw:!1}),i;if(s?i=await Promise.race([n,new Promise((o,c)=>{s.addEventListener("abort",()=>c(new DOMException("Aborted","AbortError")),{once:!0})})]):i=await n,i.status===401||i.status===403)throw new Error(`Telegram ${t} ${i.status} Unauthorized`);if(i.status===429){let c=i.json?.parameters?.retry_after??1;return await new Promise(l=>setTimeout(l,c*1e3)),this.tgApi(t,e)}if(i.status<200||i.status>=300)throw new Error(`Telegram ${t} HTTP ${i.status}: ${i.text}`);return i.json}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}};function Ui(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function $i(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function ji(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(` +`}async getOrCreateSession(t,e,s){let a=`${t.name}:${e}:${s}`,n=this.sessions.get(a);if(n)return n.session;let i=this.deps.getRepository(),o=i.getAgentByName(s);if(!o)throw new Error(`Channel ${t.name} bound to missing agent ${s}`);let c=new Ut(o,this.deps.getSettings(),i,this.deps.vault,{channelName:t.name,conversationId:`${e}:${s}`,channelContext:t.channelContext||void 0});try{await c.loadPersistedState()}catch{}return this.sessions.set(a,{session:c,channelName:t.name,conversationId:e,sessionKey:a}),c}resolveAllowedAgents(t){return t.allowedAgents.length>0?t.allowedAgents:this.deps.getRepository().getSnapshot().agents.filter(a=>a.enabled).map(a=>a.name)}async persistBindings(t){let e=`${t}:`,s={};for(let[o,c]of this.threadBindings)o.startsWith(e)&&(s[o.slice(e.length)]=c);let a=this.deps.getSettings(),n=(0,ct.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),i=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof ct.TFile)await this.deps.vault.modify(o,i);else{let c=n.slice(0,n.lastIndexOf("/"));if(!this.deps.vault.getAbstractFileByPath(c))try{await this.deps.vault.createFolder(c)}catch{}await this.deps.vault.create(n,i)}}catch(o){console.warn(`Agent Fleet: failed to persist thread bindings for ${t}`,o)}}async loadBindings(t){let e=this.deps.getSettings(),s=(0,ct.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof ct.TFile))return;let n=await this.deps.vault.cachedRead(a),i=JSON.parse(n);for(let[o,c]of Object.entries(i))typeof c=="string"&&this.threadBindings.set(`${t}:${o}`,c)}catch{}}getThreadAgent(t,e){return this.threadBindings.get(`${t}:${e}`)}enforceHardCap(){let t=Math.max(1,this.deps.getSettings().maxConcurrentChannelSessions),e=Array.from(this.sessions.values()).filter(a=>a.session.isProcessAlive&&!a.session.isStreaming);if(e.length<=t)return;e.sort((a,n)=>a.session.lastActiveAt-n.session.lastActiveAt);let s=e.length-t;for(let a=0;a{a=o}),i=(this.conversationLockGen.get(t)??0)+1;this.conversationLockGen.set(t,i),this.conversationLocks.set(t,s.then(()=>n));try{await s,await e()}finally{a(),this.conversationLockGen.get(t)===i&&(this.conversationLocks.delete(t),this.conversationLockGen.delete(t))}}ensureMetrics(t){let e=this.metrics.get(t);return e||(e={messagesReceived:0,messagesSent:0,lastMessageAt:null},this.metrics.set(t,e)),e}getMetrics(t){return{...this.ensureMetrics(t)}}getChannelStatus(t){let e=this.adapters.get(t);return e?e.getStatus():"disabled"}getConnectedCount(){let t=0;for(let e of this.adapters.values())e.getStatus()==="connected"&&(t+=1);return t}getSessionCount(t){let e=0,s=`${t}:`;for(let a of this.sessions.keys())a.startsWith(s)&&(e+=1);return e}onStatusChange(t){return this.statusListeners.add(t),()=>this.statusListeners.delete(t)}notifyStatusListeners(){for(let t of this.statusListeners)try{t()}catch{}}};function Pr(r){if(r.length===0)return"";let t=new Map;for(let s of r)t.set(s.name,(t.get(s.name)??0)+1);let e=[];for(let[s,a]of t)e.push(a>1?`${s}\xD7${a}`:s);return`Used ${r.length} tool${r.length===1?"":"s"}: ${e.join(", ")}`}function Dr(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function rs(r,t){return`${r}-${Dr(t)}`}var $t="af-channel-cred",$s=class{constructor(t){this.storage=t;t||console.warn("Agent Fleet: SecretStorage unavailable (Obsidian < 1.11.4). Secrets will use plaintext fallback.")}get available(){return!!this.storage}setJson(t,e,s){if(!this.storage)return;let a=rs(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=rs(t,e),a=this.storage.getSecret(s);if(!a)return null;try{return JSON.parse(a)}catch{return null}}setString(t,e,s){if(!this.storage)return;let a=rs(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=rs(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=rs(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var js=class{credentials=new Map;secretStore;persistCallback;setSecretStore(t){this.secretStore=t}loadCredentials(t){if(this.credentials.clear(),this.secretStore?.available){let e=this.secretStore.listByPrefix($t);for(let s of e){let a=$t+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson($t,n);if(i){let o=i._ref??n,c={...i};delete c._ref,this.credentials.set(o,c)}}}if(this.credentials.size===0&&t){for(let[e,s]of Object.entries(t))this.credentials.set(e,s);this.secretStore?.available&&this.credentials.size>0&&this.persistToSecretStore()}}onChanged(t){this.persistCallback=t}get(t){return this.credentials.get(t)}set(t,e){this.credentials.set(t,e),this.persist()}delete(t){this.credentials.delete(t),this.secretStore?.delete($t,t),this.persist()}list(){return Array.from(this.credentials.entries()).map(([t,e])=>({ref:t,entry:e}))}toRecord(){let t={};for(let[e,s]of this.credentials.entries())t[e]=s;return t}persist(){this.persistToSecretStore(),this.persistCallback&&this.persistCallback(this.toRecord())}persistToSecretStore(){if(this.secretStore?.available)for(let[t,e]of this.credentials)this.secretStore.setJson($t,t,{...e,_ref:t})}};var Xo=Ke(Ai(),1),Qo=Ke(Js(),1),Zo=Ke(qt(),1),el=Ke(Na(),1),tl=Ke($a(),1),sl=Ke(Ga(),1),Oi=Ke(ea(),1),al=Ke(Fi(),1);var Ya=Oi.default;var Ni=require("obsidian");var nl="https://slack.com/api";function il(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function rl(r){let t=r.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function ol(r){let t=r.split(":");if(t[3]==="thread"&&t[4])return t[4]}var sa=class{type="slack";config;credential;ws=null;status="stopped";stopping=!1;backoffMs=1e3;reconnectTimer=null;inboundHandlers=new Set;statusHandlers=new Set;agentSwitchHandlers=new Set;sendQueues=new Map;threadContext=new Map;constructor(t,e){if(e.type!=="slack")throw new Error(`SlackAdapter requires a slack credential, got ${e.type}`);this.config=t,this.credential=e}async start(){this.stopping=!1,await this.connect()}async stop(){if(this.stopping=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws){try{this.ws.close()}catch{}this.ws=null}this.threadContext.clear(),this.sendQueues.clear(),this.setStatus("stopped")}getStatus(){return this.status}async send(t,e){let s=rl(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=ol(t),n=Bs(e);await this.enqueueSend(s,async()=>{await this.slackApi("chat.postMessage",{channel:s,text:n,...a?{thread_ts:a}:{}})})}async broadcast(t){let e=this.config.allowedUsers[0];if(!e){console.warn(`Agent Fleet: broadcast on ${this.config.name} skipped \u2014 no allowed users configured`);return}try{let a=(await this.slackApi("conversations.open",{users:e})).channel?.id;if(!a){console.warn(`Agent Fleet: broadcast \u2014 conversations.open returned no channel for user ${e}`);return}let n=Bs(t);await this.slackApi("chat.postMessage",{channel:a,text:n})}catch(s){console.error(`Agent Fleet: broadcast failed on ${this.config.name}`,s)}}async setThreadTitle(t,e){let s=this.threadContext.get(t);if(s)try{await this.slackApi("assistant.threads.setTitle",{channel_id:s.channelId,thread_ts:s.threadTs,title:e})}catch(a){console.warn(`Agent Fleet: assistant.threads.setTitle failed on ${this.config.name}`,a)}}async setTyping(t,e){let s=this.threadContext.get(t);if(s)try{await this.slackApi("assistant.threads.setStatus",{channel_id:s.channelId,thread_ts:s.threadTs,status:e?"is thinking...":""})}catch(a){console.warn(`Agent Fleet: assistant.threads.setStatus (${e?"on":"off"}) failed on ${this.config.name}`,a)}}onInbound(t){return this.inboundHandlers.add(t),()=>this.inboundHandlers.delete(t)}onStatusChange(t){return this.statusHandlers.add(t),()=>this.statusHandlers.delete(t)}onAgentSwitch(t){return this.agentSwitchHandlers.add(t),()=>this.agentSwitchHandlers.delete(t)}async connect(){if(this.stopping)return;this.setStatus(this.ws?"reconnecting":"connecting");let t;try{let e=await this.slackApi("apps.connections.open",{},{useAppToken:!0});if(!e.ok||!e.url)throw new Error(e.error??"apps.connections.open returned no URL");t=e.url}catch(e){console.error(`Agent Fleet: Slack apps.connections.open failed for channel ${this.config.name}`,e),this.setStatus("needs-auth"),this.scheduleReconnect();return}try{let e=new Ya(t);e.on("open",()=>{}),e.on("message",n=>{this.handleSocketData(n)}),e.on("error",n=>{console.warn(`Agent Fleet: Slack WebSocket error on ${this.config.name}`,n),this.setStatus("error")}),e.on("close",()=>{this.ws=null,this.stopping||this.scheduleReconnect()}),this.ws=e;let s=setTimeout(()=>{if(this.status==="connecting"||this.status==="reconnecting"){console.warn(`Agent Fleet: Slack WebSocket connect timeout on ${this.config.name}`);try{e.close()}catch{}}},3e4);e.on("close",()=>clearTimeout(s));let a=this.onStatusChange(n=>{n==="connected"&&(clearTimeout(s),a())})}catch(e){console.error("Agent Fleet: Slack WebSocket open failed",e),this.setStatus("error"),this.scheduleReconnect();return}}handleSocketData(t){let e;try{e=JSON.parse(t.toString())}catch{return}if(e.type==="hello"){this.backoffMs=1e3,this.setStatus("connected");return}if(e.type==="disconnect"){try{this.ws?.close()}catch{}return}if(e.type==="events_api"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.routeEventPayload(e.payload);return}if(e.type==="slash_commands"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleSlashCommand(e.payload);return}if(e.type==="interactive"&&e.envelope_id){this.ackEnvelope(e.envelope_id),this.handleInteraction(e.payload);return}e.envelope_id&&this.ackEnvelope(e.envelope_id)}async handleSlashCommand(t){if(!t)return;let e=t.command,s=t.channel_id,a=t.user_id;if(!(!e||!s||!a)){if(e==="/agents"){let n=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(n.length===0){await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"No agents configured. Set `allowed_agents` in the channel file."});return}let i=n.map(c=>({type:"button",text:{type:"plain_text",text:c===this.config.defaultAgent?`${c} \u2713`:c,emoji:!0},action_id:`switch_agent_${c}`,value:c})),o=[{type:"section",text:{type:"mrkdwn",text:"*Select an agent to chat with:*"}}];for(let c=0;c\s*/,"").trim(),!a.text))return;let o=il(a),c=a.thread_ts??a.ts;if(a.channel&&c&&(this.threadContext.set(o,{channelId:a.channel,threadTs:c}),this.threadContext.size>500)){let h=this.threadContext.keys().next();h.done||this.threadContext.delete(h.value)}let l={conversationId:o,externalUserId:a.user,text:a.text,timestamp:new Date().toISOString(),meta:{slack_channel:a.channel,slack_ts:a.ts,thread_ts:a.thread_ts}};for(let d of this.inboundHandlers)try{d(l)}catch(h){console.error("Agent Fleet: Slack inbound handler threw",h)}}scheduleReconnect(){if(this.stopping||this.reconnectTimer)return;let t=this.backoffMs;this.backoffMs=Math.min(3e4,this.backoffMs*2),console.warn(`Agent Fleet: Slack channel ${this.config.name} scheduling reconnect in ${t}ms`),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopping&&this.connect()},t)}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}async slackApi(t,e,s={}){let a=s.useAppToken?this.credential.appToken:this.credential.botToken,n=`${nl}/${t}`,i=await(0,Ni.requestUrl)({url:n,method:"POST",contentType:"application/json; charset=utf-8",headers:{Authorization:`Bearer ${a}`},body:JSON.stringify(e),throw:!1});if(i.status===429){let c=Number(i.headers["retry-after"]??"1");return await new Promise(l=>setTimeout(l,Math.max(1e3,c*1e3))),this.slackApi(t,e,s)}if(i.status<200||i.status>=300)throw new Error(`Slack ${t} HTTP ${i.status}`);let o=i.json;if(o.ok===!1)throw new Error(`Slack ${t} error: ${o.error??"unknown"}`);return o}async enqueueSend(t,e){let a=(this.sendQueues.get(t)??Promise.resolve()).then(async()=>{try{await e()}finally{await new Promise(i=>setTimeout(i,1e3))}}),n=a.catch(i=>{console.warn(`Agent Fleet: Slack send queue error for ${t}`,i)});this.sendQueues.set(t,n),await a,this.sendQueues.get(t)===n&&this.sendQueues.delete(t)}};var Ka=require("obsidian"),Bi="https://api.telegram.org",aa=class{type="telegram";config;credential;status="stopped";stopping=!1;pollOffset=0;pollTimer=null;backoffMs=1e3;typingIntervals=new Map;pollAbort=null;inboundHandlers=new Set;statusHandlers=new Set;agentSwitchHandlers=new Set;constructor(t,e){if(e.type!=="telegram")throw new Error(`TelegramAdapter requires a telegram credential, got ${e.type}`);this.config=t,this.credential=e}async start(){this.stopping=!1;try{let e=(await this.tgApi("getUpdates",{offset:-1,limit:1})).result?.[0];e&&(this.pollOffset=e.update_id+1)}catch{}try{await this.tgApi("setMyCommands",{commands:[{command:"agents",description:"List available agents"}]})}catch{}this.setStatus("connected"),this.poll()}async stop(){this.stopping=!0,this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=null),this.pollAbort?.abort(),this.pollAbort=null;for(let[,t]of this.typingIntervals)clearInterval(t);this.typingIntervals.clear(),this.setStatus("stopped")}getStatus(){return this.status}async send(t,e){let s=Ui(t);if(!s)return;let a=$i(t),n=ji(e,4096);for(let i of n)await this.tgApi("sendMessage",{chat_id:s,text:i,parse_mode:"Markdown",...a?{message_thread_id:Number(a)}:{}})}async setTyping(t,e){let s=Ui(t);if(!s)return;let a=$i(t),n={chat_id:s,action:"typing"};if(a&&(n.message_thread_id=Number(a)),e){let i=this.typingIntervals.get(t);i&&clearInterval(i);try{await this.tgApi("sendChatAction",n)}catch(c){console.warn("Agent Fleet: Telegram sendChatAction failed",c)}let o=setInterval(()=>{this.tgApi("sendChatAction",n).catch(()=>{})},4500);this.typingIntervals.set(t,o)}else{let i=this.typingIntervals.get(t);i&&(clearInterval(i),this.typingIntervals.delete(t))}}async setThreadTitle(t,e){}async broadcast(t){let e=this.config.allowedUsers[0];if(e)try{let s=ji(t,4096);for(let a of s)await this.tgApi("sendMessage",{chat_id:e,text:a,parse_mode:"Markdown"})}catch(s){console.error(`Agent Fleet: Telegram broadcast failed on ${this.config.name}`,s)}}onInbound(t){return this.inboundHandlers.add(t),()=>this.inboundHandlers.delete(t)}onStatusChange(t){return this.statusHandlers.add(t),()=>this.statusHandlers.delete(t)}onAgentSwitch(t){return this.agentSwitchHandlers.add(t),()=>this.agentSwitchHandlers.delete(t)}poll(){this.stopping||(async()=>{try{this.pollAbort=new AbortController;let t=await this.tgApi("getUpdates",{offset:this.pollOffset,timeout:30,allowed_updates:["message","callback_query"]},this.pollAbort.signal);if(t.ok&&t.result)for(let e of t.result){if(this.pollOffset=e.update_id+1,e.callback_query){this.handleCallbackQuery(e.callback_query);continue}e.message&&this.routeMessage(e.message)}this.backoffMs=1e3,this.status!=="connected"&&this.setStatus("connected")}catch(t){if(t instanceof DOMException&&t.name==="AbortError")return;if(console.warn(`Agent Fleet: Telegram poll failed on ${this.config.name}`,t),this.status!=="error"&&this.status!=="needs-auth"){let e=t instanceof Error?t.message:String(t);this.setStatus(e.includes("401")||e.includes("Unauthorized")?"needs-auth":"error")}await new Promise(e=>setTimeout(e,this.backoffMs)),this.backoffMs=Math.min(3e4,this.backoffMs*2)}this.stopping||(this.pollTimer=setTimeout(()=>this.poll(),100))})()}routeMessage(t){let e=t.photo&&t.photo.length>0,s=t.document&&this.isImageMime(t.document.mime_type),a=!!t.text;if(!t.from||!a&&!e&&!s)return;let n=t.text??t.caption??"";if(n==="/agents"||n.startsWith("/agents@")){this.handleAgentsCommand(t);return}if(n.startsWith("/"))return;let i=String(t.from.id),o=String(t.chat.id),c=t.message_thread_id?String(t.message_thread_id):void 0,l=c?`tg:${o}:topic:${c}`:`tg:${o}`;this.buildAndEmitMessage(t,n,l,i,o,c)}async buildAndEmitMessage(t,e,s,a,n,i){let o=[];try{if(t.photo&&t.photo.length>0){let l=t.photo[t.photo.length-1],d=await this.downloadFile(l.file_id,`photo_${t.message_id}.jpg`,"image/jpeg");d&&o.push(d)}if(t.document&&this.isImageMime(t.document.mime_type)){let l=t.document.file_name??`doc_${t.message_id}`,d=t.document.mime_type??"image/jpeg",h=await this.downloadFile(t.document.file_id,l,d);h&&o.push(h)}}catch(l){console.warn("Agent Fleet: Telegram image download failed",l)}let c={conversationId:s,externalUserId:a,text:e,timestamp:new Date(t.date*1e3).toISOString(),meta:{telegram_chat_id:n,telegram_message_id:t.message_id,telegram_thread_id:i,chat_type:t.chat.type},...o.length>0?{images:o}:{}};for(let l of this.inboundHandlers)try{l(c)}catch(d){console.error("Agent Fleet: Telegram inbound handler threw",d)}}async downloadFile(t,e,s){let n=(await this.tgApi("getFile",{file_id:t})).result?.file_path;if(!n)return null;let i=`${Bi}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,Ka.requestUrl)({url:i,method:"GET"})).arrayBuffer,filename:e,mimeType:s}}isImageMime(t){return t?t==="image/jpeg"||t==="image/png"||t==="image/gif"||t==="image/webp"||t==="image/bmp"||t==="image/tiff":!1}async handleAgentsCommand(t){let e=String(t.chat.id),s=t.message_thread_id,a=this.config.allowedAgents.length>0?this.config.allowedAgents:[];if(a.length===0){await this.tgApi("sendMessage",{chat_id:e,text:"No agents configured. Set `allowed_agents` in the channel file.",...s?{message_thread_id:s}:{}});return}let n=a.map(i=>[{text:i===this.config.defaultAgent?`${i} \u2713`:i,callback_data:`switch:${i}`}]);await this.tgApi("sendMessage",{chat_id:e,text:"*Select an agent to chat with:*",parse_mode:"Markdown",reply_markup:{inline_keyboard:n},...s?{message_thread_id:s}:{}})}async handleCallbackQuery(t){let e=t.data;if(!e?.startsWith("switch:")){await this.tgApi("answerCallbackQuery",{callback_query_id:t.id});return}let s=e.slice(7),a=String(t.from.id),n=String(t.message?.chat?.id??t.from.id),i=t.message?.message_thread_id?String(t.message.message_thread_id):void 0,o=i?`tg:${n}:topic:${i}`:`tg:${n}`;for(let c of this.agentSwitchHandlers)try{c(o,s,a)}catch(l){console.error("Agent Fleet: Telegram agent switch handler threw",l)}if(await this.tgApi("answerCallbackQuery",{callback_query_id:t.id,text:`Switched to ${s}`}),t.message){let l=(this.config.allowedAgents.length>0?this.config.allowedAgents:[]).map(d=>[{text:d===s?`${d} \u2713`:d,callback_data:`switch:${d}`}]);try{await this.tgApi("editMessageText",{chat_id:n,message_id:t.message.message_id,text:`*Active agent: ${s}*`,parse_mode:"Markdown",reply_markup:{inline_keyboard:l}})}catch{}}}async tgApi(t,e,s){if(s?.aborted)throw new DOMException("Aborted","AbortError");let a=`${Bi}/bot${this.credential.botToken}/${t}`,n=(0,Ka.requestUrl)({url:a,method:"POST",contentType:"application/json",body:JSON.stringify(e),throw:!1}),i;if(s?i=await Promise.race([n,new Promise((o,c)=>{s.addEventListener("abort",()=>c(new DOMException("Aborted","AbortError")),{once:!0})})]):i=await n,i.status===401||i.status===403)throw new Error(`Telegram ${t} ${i.status} Unauthorized`);if(i.status===429){let c=i.json?.parameters?.retry_after??1;return await new Promise(l=>setTimeout(l,c*1e3)),this.tgApi(t,e)}if(i.status<200||i.status>=300)throw new Error(`Telegram ${t} HTTP ${i.status}: ${i.text}`);return i.json}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}};function Ui(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function $i(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function ji(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(` `,t);as.agents.length},{icon:"columns-3",label:"Tasks Board",page:"kanban"},{icon:"scroll-text",label:"Run History",page:"runs"},{icon:"shield-check",label:"Approvals",page:"approvals",badge:()=>a.pending},{icon:"puzzle",label:"Skills",page:"skills",badge:()=>s.skills.length},{icon:"plug",label:"MCP Servers",page:"mcp",badge:()=>this.plugin.mcpManager.getCachedServers()?.length??0},{icon:"radio",label:"Channels",page:"channels",badge:()=>this.plugin.channelManager?.getConnectedCount()??s.channels.length}];for(let l of i){let d=n.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,ms.setIcon)(h,l.icon),d.createSpan({cls:"af-sidebar-nav-label",text:l.label});let u=l.badge?.();u!==void 0&&u>0&&d.createSpan({cls:"af-sidebar-badge",text:String(u)}),d.setAttribute("role","button"),d.setAttribute("tabindex","0"),d.onclick=()=>void this.plugin.navigateDashboard(l.page),d.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),this.plugin.navigateDashboard(l.page))}}let o=e.createDiv({cls:"af-sidebar-section"});o.createDiv({cls:"af-sidebar-section-header",text:"AGENTS"}),s.agents.length===0&&o.createDiv({cls:"af-sidebar-empty",text:"No agents configured"});for(let l of s.agents){let d=this.plugin.runtime.getAgentState(l.name),h=o.createDiv({cls:"af-sidebar-agent-item"});h.createSpan({cls:`af-sidebar-agent-dot ${this.healthToClass(d.status)}`}),h.createSpan({cls:"af-sidebar-agent-name",text:l.name}),h.setAttribute("role","button"),h.setAttribute("tabindex","0"),h.onclick=()=>void this.plugin.navigateDashboard("agent-detail",l.name),h.onkeydown=u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),this.plugin.navigateDashboard("agent-detail",l.name))}}let c=e.createDiv({cls:"af-sidebar-section"});c.createDiv({cls:"af-sidebar-section-header",text:"QUICK ACTIONS"}),this.renderQuickAction(c,"plus","New Agent",()=>void this.plugin.createAgentTemplate()),this.renderQuickAction(c,"plus","New Task",()=>void this.plugin.openCreateTask()),this.renderQuickAction(c,"plus","New Skill",()=>void this.plugin.createSkillTemplate())}renderQuickAction(e,s,a,n){let i=e.createDiv({cls:"af-sidebar-action-item"}),o=i.createSpan({cls:"af-sidebar-action-icon"});(0,ms.setIcon)(o,s),i.createSpan({text:a}),i.setAttribute("role","button"),i.setAttribute("tabindex","0"),i.onclick=n,i.onkeydown=c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),n())}}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}};var w=require("obsidian");var Kt=require("obsidian"),ll=["bot","brain","shield-check","search","file-text","rocket","wand","sparkles","zap","target","compass","eye","code","terminal","database","globe","mail","message-circle","book","pen-tool","palette","music","camera","chart-bar","clipboard","cpu","server","cloud","lock","key","bell","calendar","clock","heart","star","flag","bookmark"],ia=class extends Kt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,Kt.getIconIds)().sort();let{contentEl:e}=this;e.empty(),e.addClass("af-icon-picker-modal");let s=e.createEl("input",{cls:"af-icon-picker-search",attr:{type:"text",placeholder:"Search icons...",value:this.searchQuery}});this.gridContainer=e.createDiv({cls:"af-icon-picker-scroll"}),this.renderGrid(),s.addEventListener("input",()=>{this.searchQuery=s.value,this.renderGrid()}),setTimeout(()=>s.focus(),0)}renderGrid(){let e=this.gridContainer;e.empty();let s=this.searchQuery.toLowerCase().trim();if(!s)this.renderSection(e,"Popular",ll),this.renderSection(e,"All Icons",this.allIcons);else{let a=this.allIcons.filter(i=>i.includes(s)),n=a.length===0?"No results":`${a.length} result${a.length!==1?"s":""}`;this.renderSection(e,n,a)}}renderSection(e,s,a){let n=e.createDiv({cls:"af-icon-picker-section"});n.createDiv({cls:"af-icon-picker-section-label",text:s});let i=n.createDiv({cls:"af-icon-picker-grid"});for(let o of a)this.renderIconItem(i,o)}renderIconItem(e,s){let a=e.createDiv({cls:`af-icon-picker-item${this.selectedIcon===s?" selected":""}`});a.setAttribute("title",s),a.setAttribute("aria-label",s),(0,Kt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var Hi=require("obsidian");function D(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,Hi.setIcon)(s,t),s}function Ze(r,t={}){let e=document.createElementNS("http://www.w3.org/2000/svg",r);for(let[s,a]of Object.entries(t))e.setAttribute(s,a);return e}function cl(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function qi(r,t){let e=t.length||1,s=1e3,a=6,n=4,i=4,o=s-n-i-a*(e-1),c=Math.floor(o/e),l=140,d=36,h=18,u=h+l+d,p=Math.max(1,...t.map(m=>m.success+m.failure+m.cancelled)),f=Ze("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let m=0;m<=4;m++){let y=h+l-m/4*l;f.appendChild(Ze("line",{x1:String(n),y1:String(y),x2:String(s-i),y2:String(y),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let m=0;m0&&f.appendChild(Ze("rect",{x:String(k),y:String(h+l-g),width:String(c),height:String(Math.max(g,2)),fill:"var(--af-green)",opacity:"0.85"})),y.cancelled>0&&f.appendChild(Ze("rect",{x:String(k),y:String(h+l-g-x),width:String(c),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),y.failure>0&&f.appendChild(Ze("rect",{x:String(k),y:String(h+l-v),width:String(c),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),b===0&&f.appendChild(Ze("rect",{x:String(k),y:String(h+l-3),width:String(c),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),b>0){let R=Ze("text",{x:String(k+c/2),y:String(h+l-v-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});R.textContent=String(b),f.appendChild(R)}let _=Ze("text",{x:String(k+c/2),y:String(h+l+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});_.textContent=cl(y.date),f.appendChild(_)}r.appendChild(f)}function Wi(r,t,e){let c=2*Math.PI*46,l=e>0?t/e:0,d=c*l,h=c-d,u=Ze("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(Ze("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:e>0?"var(--af-red)":"var(--af-text-faint)","stroke-width":String(12),opacity:"0.2"})),l>0&&u.appendChild(Ze("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:"var(--af-green)","stroke-width":String(12),"stroke-dasharray":`${d} ${h}`,"stroke-dashoffset":String(c*.25),"stroke-linecap":"round"}));let p=Ze("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});p.textContent=`${Math.round(l*100)}%`,u.appendChild(p);let f=Ze("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});f.textContent=`${t}/${e} runs`,u.appendChild(f),r.appendChild(u)}function zi(r,t){r.draggable=!0,r.addEventListener("dragstart",e=>{e.dataTransfer?.setData("text/plain",t),r.addClass("af-dragging")}),r.addEventListener("dragend",()=>{r.removeClass("af-dragging")})}function Gi(r,t){r.addEventListener("dragover",e=>{e.preventDefault(),r.addClass("af-drag-over")}),r.addEventListener("dragleave",e=>{let s=e.relatedTarget;(!s||!r.contains(s))&&r.removeClass("af-drag-over")}),r.addEventListener("drop",e=>{e.preventDefault();let s=e.dataTransfer?.getData("text/plain");r.removeClass("af-drag-over"),s&&t(s)})}function Vi(r){let t=/^##\s+Lint\s+(\d{4}-\d{2}-\d{2})\s*$/gm,e,s=-1,a="";for(;(e=t.exec(r))!==null;)e.index>s&&(s=e.index,a=e[1]??"");if(s<0)return null;let n=r.slice(s),i=n.search(/\n##\s+(?!\s*#)/),o=i<0?n:n.slice(0,i);return{date:a,summary:ra(o,"Summary"),autoApplied:ra(o,"Auto-applied"),needsReview:ra(o,"Needs review"),refreshChained:ra(o,"Refresh chained")}}function ra(r,t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^###\\s+${e}\\s*$`,"m").exec(r);if(!a)return[];let n=a.index+a[0].length,i=r.slice(n),o=i.search(/\n###\s+/),l=(o<0?i:i.slice(0,o)).split(/\r?\n/).map(h=>h.trimEnd()),d=[];for(let h of l){let u=h.replace(/^\s+/,"");u.startsWith("- ")?d.push(u.slice(2).trim()):d.length>0&&(h.startsWith(" ")||h.startsWith(" "))&&(d[d.length-1]+=" "+u)}return d}var Yi={dashboard:"Dashboard",agents:"Agents",kanban:"Tasks Board",runs:"Run History",skills:"Skills Library",approvals:"Approvals",mcp:"MCP Servers",channels:"Channels","wiki-keepers":"Wiki Keepers","agent-detail":"Agent Details","task-detail":"Task Details","create-agent":"Create Agent","create-task":"Create Task","create-skill":"Create Skill","edit-agent":"Edit Agent","edit-task":"Edit Task","edit-skill":"Edit Skill","create-channel":"Create Channel","edit-channel":"Edit Channel","add-mcp-server":"Add MCP Server"},dl={dashboard:"layout-dashboard",agents:"bot",kanban:"columns-3",runs:"scroll-text",skills:"puzzle",approvals:"shield-check",mcp:"plug",channels:"radio","wiki-keepers":"library","agent-detail":"bot","task-detail":"circle-dot","create-agent":"plus","create-task":"plus","create-skill":"plus","edit-agent":"edit","edit-task":"edit","edit-skill":"edit","create-channel":"plus","edit-channel":"edit","add-mcp-server":"plus"},hl=["dashboard","agents","kanban","runs","approvals","skills","wiki-keepers","mcp","channels"],fs=class extends w.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return wt}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),this.channelStatusUnsubscribe=this.plugin.channelManager?.onStatusChange(()=>{this.currentPage==="channels"&&this.render()}),await this.render()}async onClose(){this.cleanupStreaming(),this.channelStatusUnsubscribe?.(),this.channelStatusUnsubscribe=void 0,this.plugin.unsubscribeView(this)}navigateTo(e,s){this.currentPage=e,this.detailContext=s,e!=="agent-detail"&&(this.agentDetailTab="overview"),this.render()}async render(){this.cleanupStreaming();let e=this.contentEl;e.empty(),e.addClass("af-root");let a=e.createDiv({cls:"af-app"}).createDiv({cls:"af-main-content"});this.renderTopBar(a),this.renderTabBar(a);let n=a.createDiv({cls:"af-page"});switch(this.currentPage){case"dashboard":this.renderDashboardPage(n);break;case"agents":this.renderAgentsPage(n);break;case"kanban":this.renderKanbanPage(n);break;case"runs":this.renderRunsPage(n);break;case"skills":this.renderSkillsPage(n);break;case"approvals":this.renderApprovalsPage(n);break;case"mcp":this.renderMcpPage(n);break;case"channels":this.renderChannelsPage(n);break;case"wiki-keepers":this.renderWikiKeepersPage(n);break;case"agent-detail":this.renderAgentDetailPage(n);break;case"task-detail":this.renderTaskDetailPage(n);break;case"create-agent":this.renderCreateAgentPage(n);break;case"create-task":this.renderCreateTaskPage(n);break;case"create-skill":this.renderCreateSkillPage(n);break;case"edit-agent":this.renderEditAgentPage(n);break;case"edit-task":this.renderEditTaskPage(n);break;case"edit-skill":this.renderEditSkillPage(n);break;case"create-channel":this.renderCreateChannelPage(n);break;case"edit-channel":this.renderEditChannelPage(n);break;case"add-mcp-server":this.renderAddMcpServerPage(n);break}}navigate(e,s){this.navigateTo(e,s)}cleanupStreaming(){for(let e of this.streamingUnsubscribes)e();this.streamingUnsubscribes=[]}renderTopBar(e){let s=e.createDiv({cls:"af-top-bar"}),a=s.createDiv({cls:"af-top-bar-title"});D(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),i=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(i,"chevron-right");let o=(f,m,y)=>{let k=n.createSpan({cls:m?"af-breadcrumb-link":"",text:f});m&&(k.onclick=()=>this.navigate(m,y))},c=()=>{let f=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(f,"chevron-right")};switch(this.currentPage){case"agent-detail":o("Agents","agents"),c(),o(this.detailContext??"Agent");break;case"task-detail":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task");break;case"edit-agent":o("Agents","agents"),c(),o(this.detailContext??"Agent","agent-detail",this.detailContext),c(),o("Edit");break;case"edit-task":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task","task-detail",this.detailContext),c(),o("Edit");break;case"create-agent":o("Agents","agents"),c(),o("New Agent");break;case"create-task":o("Tasks Board","kanban"),c(),o("New Task");break;case"create-skill":o("Skills Library","skills"),c(),o("New Skill");break;case"edit-skill":o("Skills Library","skills"),c(),o(this.detailContext??"Skill"),c(),o("Edit");break;case"create-channel":o("Channels","channels"),c(),o("New Channel");break;case"edit-channel":o("Channels","channels"),c(),o(this.detailContext??"Channel"),c(),o("Edit");break;case"add-mcp-server":o("MCP Servers","mcp"),c(),o("Add Server");break;default:o(Yi[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let l=s.createDiv({cls:"af-search-wrap"});D(l,"search","af-search-icon");let d=l.createEl("input",{cls:"af-search-input",attr:{type:"text",placeholder:"Search agents, tasks, runs..."}});d.addEventListener("input",()=>{this.handleSearch(d.value,l)}),d.addEventListener("blur",()=>{setTimeout(()=>l.querySelector(".af-search-results")?.remove(),200)});let h=this.plugin.runtime.getFleetStatus(),u=s.createDiv({cls:"af-status-pills"});if(h.running>0){let f=u.createSpan({cls:"af-pill yellow"});f.createSpan({cls:"af-dot pulse"}),f.appendText(` ${h.running} Running`)}if(h.pending>0){let f=u.createSpan({cls:"af-pill blue"});f.createSpan({cls:"af-dot"}),f.appendText(` ${h.pending} Pending`)}let p=u.createSpan({cls:"af-pill green"});p.createSpan({cls:"af-dot"}),p.appendText(` ${h.completedToday} Today`)}renderTabBar(e){let s=e.createDiv({cls:"af-tab-bar"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getFleetStatus();for(let i of hl){let o=this.currentPage===i||i==="agents"&&(this.currentPage==="agent-detail"||this.currentPage==="edit-agent"||this.currentPage==="create-agent")||i==="kanban"&&(this.currentPage==="task-detail"||this.currentPage==="edit-task")||i==="skills"&&(this.currentPage==="edit-skill"||this.currentPage==="create-skill")||i==="mcp"&&this.currentPage==="add-mcp-server",c=s.createEl("button",{cls:`af-tab-item${o?" active":""}`}),l=c.createSpan({cls:"af-tab-icon"});if((0,w.setIcon)(l,dl[i]),c.appendText(i==="dashboard"?"Overview":Yi[i]),i==="agents")c.createSpan({cls:"af-badge",text:String(a.agents.length)});else if(i==="skills")c.createSpan({cls:"af-badge",text:String(a.skills.length)});else if(i==="mcp"){let d=this.plugin.mcpManager.getCachedServers()?.length??0;c.createSpan({cls:"af-badge",text:String(d)})}else i==="approvals"&&n.pending>0&&c.createSpan({cls:"af-badge af-badge-warn",text:String(n.pending)});c.onclick=()=>this.navigate(i)}}renderDashboardPage(e){let s=e.createDiv({cls:"af-dashboard"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),i=this.plugin.runtime.getFleetStatus(),o=n.filter(g=>(g.approvals??[]).some(x=>x.status==="pending"));for(let g of o)for(let x of g.approvals??[])x.status==="pending"&&this.renderApprovalBanner(s,g,x.tool);let c=s.createDiv({cls:"af-dash-grid"}),l=a.agents.filter(g=>g.enabled).length,d=a.agents.length;this.renderStatCard(c,"Active Agents",`${l}`,`/ ${d}`,"bot",`${l} of ${d} enabled`);let h=this.toLocalDateStr(new Date),u=n.filter(g=>this.runToLocalDate(g.started)===h),p=u.filter(g=>g.status==="success").length,f=u.filter(g=>g.status==="failure"||g.status==="timeout").length;this.renderStatCard(c,"Runs Today",String(u.length),"","activity",`${p} passed \xB7 ${f} failed \xB7 ${i.running} running`);let m=u.reduce((g,x)=>g+(x.tokensUsed??0),0),y=u.reduce((g,x)=>g+(x.costUsd??0),0),k=y>0?` \xB7 $${y.toFixed(2)}`:"";this.renderStatCard(c,"Tokens Used",Ka(m),"","zap",`today${k}`);let b=a.tasks.filter(g=>g.enabled&&g.schedule);this.renderStatCard(c,"Scheduled Tasks",String(b.length),"","clock",b.length>0?`Next: ${this.getNextTaskLabel(b)}`:"No scheduled tasks"),this.renderChartsRow(s,n,this.plugin.runtime.getChartRuns()),this.renderStreamingCards(s);let v=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(v,n),this.renderFleetStatusPanel(v,a)}renderChartsRow(e,s,a){let n=e.createDiv({cls:"af-charts-row"}),i=n.createDiv({cls:"af-section-card af-chart-section"}),c=i.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(c,"activity"),c.appendText(" Run Activity (14d)");let l=i.createDiv({cls:"af-chart-body"}),d=this.buildChartData(a,14);d.some(k=>k.success+k.failure+k.cancelled>0)?qi(l,d):this.renderEmptyState(l,"activity","No run data","Run agents to see activity");let h=n.createDiv({cls:"af-section-card af-chart-section"}),p=h.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(p,"target"),p.appendText(" Success Rate");let f=h.createDiv({cls:"af-chart-body af-chart-body-center"}),m=s.length,y=s.filter(k=>k.status==="success").length;m>0?Wi(f,y,m):this.renderEmptyState(f,"target","No data","")}toLocalDateStr(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}`}runToLocalDate(e){return this.toLocalDateStr(new Date(e))}buildChartData(e,s){let a=[],n=new Date;for(let i=s-1;i>=0;i--){let o=new Date(n);o.setDate(o.getDate()-i);let c=this.toLocalDateStr(o),l=e.filter(d=>this.runToLocalDate(d.started)===c);a.push({date:c,success:l.filter(d=>d.status==="success").length,failure:l.filter(d=>d.status==="failure"||d.status==="timeout").length,cancelled:l.filter(d=>d.status==="cancelled").length})}return a}renderStreamingCards(e){let a=this.plugin.runtime.getSnapshot().agents.filter(c=>this.plugin.runtime.getAgentState(c.name).status==="running");if(a.length===0)return;let n=e.createDiv({cls:"af-streaming-section"}),o=n.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(o,"terminal"),o.appendText(" Active Agents");for(let c of a)this.renderStreamingCard(n,c.name)}renderStreamingCard(e,s){let a=e.createDiv({cls:"af-streaming-card"}),n=this.plugin.runtime.getAgentState(s),i=this.plugin.runtime.getSnapshot(),o=n.currentTaskId?i.tasks.find(f=>f.taskId===n.currentTaskId):void 0,c=o?` \u2192 ${o.taskId}`:n.status==="running"?" \u2192 Heartbeat":"",l=a.createDiv({cls:"af-streaming-card-header"});l.createSpan({cls:"af-dot pulse",attr:{style:"background: var(--af-yellow)"}}),l.createSpan({cls:"af-streaming-card-agent",text:` ${s}`}),c&&l.createSpan({cls:"af-streaming-card-task",text:c});let d=a.createDiv({cls:"af-streaming-output"}),h=this.plugin.runtime.getRunOutputBuffer(s),u=fe(h).slice(-4);d.setText(u.join(` -`));let p=this.plugin.runtime.onRunOutput(s,()=>{let f=this.plugin.runtime.getRunOutputBuffer(s),m=fe(f).slice(-4);d.setText(m.join(` -`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(p)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),i=n.createDiv({cls:"af-approval-icon"});(0,w.setIcon)(i,"shield-check");let o=n.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${a}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${a}`});let c=n.createDiv({cls:"af-approval-actions"}),l=c.createEl("button",{cls:"af-btn-approve",text:"Approve"});l.onclick=()=>void this.plugin.runtime.resolveApproval(s,a,"approved").then(()=>this.render());let d=c.createEl("button",{cls:"af-btn-reject",text:"Reject"});d.onclick=()=>void this.plugin.runtime.resolveApproval(s,a,"rejected").then(()=>this.render())}renderStatCard(e,s,a,n,i,o){let c=e.createDiv({cls:"af-stat-card"}),l=c.createDiv({cls:"af-stat-label"});D(l,i,"af-stat-icon"),l.appendText(` ${s}`);let d=c.createDiv({cls:"af-stat-value"});d.appendText(a),n&&d.createSpan({cls:"af-stat-value-suffix",text:n}),c.createDiv({cls:"af-stat-sub",text:o})}renderActivityTimeline(e,s){let a=e.createDiv({cls:"af-section-card"}),i=a.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(i,"inbox"),i.appendText(" Recent Activity");let o=a.createDiv({cls:"af-timeline"}),c=s.slice(0,8);if(c.length===0){this.renderEmptyState(o,"inbox","No runs yet","Run an agent to see activity here");return}for(let l of c)this.renderTimelineItem(o,l)}renderTimelineItem(e,s){let a=e.createDiv({cls:"af-timeline-item"}),n=this.statusToTimelineClass(s.status),i=a.createDiv({cls:`af-tl-icon ${n}`});(0,w.setIcon)(i,this.statusToIconName(s.status));let o=a.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({cls:"af-agent-tag",text:s.agent}),c.appendText(` ${s.task}`),o.createDiv({cls:"af-tl-desc",text:Ot(s.output,100)});let l=o.createDiv({cls:"af-tl-meta"}),d=l.createSpan();if(D(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=l.createSpan();D(h,"zap","af-meta-icon"),h.appendText(` ${s.tokensUsed.toLocaleString()} tokens`)}a.onclick=()=>this.openSlideover(s)}renderFleetStatusPanel(e,s){let a=e.createDiv({cls:"af-section-card"}),n=a.createDiv({cls:"af-section-header"}),i=n.createDiv({cls:"af-section-title"});D(i,"bot"),i.appendText(" Fleet Status");let c=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});D(c,"plus","af-btn-icon"),c.appendText(" New Agent"),c.onclick=()=>void this.plugin.createAgentTemplate();let l=a.createDiv({cls:"af-agent-mini-list"});if(s.agents.length===0){this.renderEmptyState(l,"bot","No agents yet","Create your first agent to get started");return}for(let h of s.agents)this.renderAgentMini(l,h,s.tasks);let d=s.agents.filter(h=>h.enabled);if(d.length>0){let h=a.createDiv({cls:"af-quick-run"}),u=h.createDiv({cls:"af-quick-run-label"});D(u,"zap","af-meta-icon"),u.appendText(" Quick Run");let p=h.createDiv({cls:"af-quick-run-row"}),f=p.createEl("select",{cls:"af-select"});for(let y of d)f.createEl("option",{text:y.name,attr:{value:y.name}});let m=p.createEl("button",{cls:"af-btn-sm primary"});D(m,"play","af-btn-icon"),m.appendText(" Run"),m.onclick=()=>void this.plugin.runAgentPrompt(f.value)}}renderAgentMini(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=a.filter(u=>u.agent===s.name),o=this.healthToClass(n.status),c=e.createDiv({cls:"af-agent-mini"}),l=c.createDiv({cls:`af-agent-avatar ${o}`});s.avatar?.trim()?this.renderAgentAvatar(l,s):l.setText(this.getInitials(s.name));let d=c.createDiv({cls:"af-agent-info"});d.createDiv({cls:"af-agent-name",text:s.name});let h="";if(n.status==="running")h=`Running now \xB7 ${i.length} task${i.length!==1?"s":""}`;else if(!s.enabled)h=`Disabled \xB7 ${i.length} task${i.length!==1?"s":""} paused`;else{let u=i.map(p=>p.nextRun).filter(Boolean).sort()[0];h=u?`Next: ${this.formatNextRun(u)} \xB7 ${i.length} task${i.length!==1?"s":""}`:`${i.length} task${i.length!==1?"s":""}`}d.createDiv({cls:"af-agent-desc",text:h}),c.createDiv({cls:`af-agent-status-dot ${o}`}),c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentsPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Agents"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});D(i,"plus","af-btn-icon"),i.appendText(" New Agent"),i.onclick=()=>void this.plugin.createAgentTemplate();let o=s.createDiv({cls:"af-agents-grid"});if(a.agents.length===0){this.renderEmptyState(o,"bot","No agents configured","Create your first agent to get started");return}for(let c of a.agents)this.renderAgentCard(o,c,a)}renderAgentCard(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=this.plugin.runtime.getRecentRuns().filter(A=>A.agent===s.name),o=a.tasks.filter(A=>A.agent===s.name),c=e.createDiv({cls:`af-agent-card${s.enabled?"":" disabled"}`}),l=c.createDiv({cls:"af-agent-card-header"}),d=s.enabled?this.healthToClass(n.status):"disabled",h=l.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(h,s);let u=l.createDiv({cls:"af-agent-card-titleblock"}),p=u.createDiv({cls:"af-agent-card-name"});if(p.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let A=p.createSpan({cls:"af-heartbeat-indicator"});(0,w.setIcon)(A,"heart-pulse"),A.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let f=l.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});f.onclick=A=>{A.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let m=c.createDiv({cls:"af-agent-card-stats"}),y=i.length,k=i.filter(A=>A.status==="success").length,b=y>0?Math.round(k/y*100):0,v=y>0?Math.round(i.reduce((A,S)=>A+S.durationSeconds,0)/y):0,g=i.reduce((A,S)=>A+(S.tokensUsed??0),0);if(this.renderAgentStat(m,String(y),"Runs"),this.renderAgentStat(m,`${b}%`,"Success"),this.renderAgentStat(m,`${v}s`,"Avg Time"),this.renderAgentStat(m,Ka(g),"Tokens"),s.skills.length>0){let A=c.createDiv({cls:"af-agent-card-skills"});for(let S of s.skills)A.createSpan({cls:"af-skill-tag",text:S})}let x=c.createDiv({cls:"af-agent-card-footer"}),T=[`Model: ${s.model}`];s.approvalRequired.length>0&&T.push(`Approval: ${s.approvalRequired.join(", ")}`),s.memory&&T.push("Memory: on"),s.enabled||T.unshift("Disabled"),x.createSpan({cls:"af-agent-card-meta",text:T.join(" \xB7 ")});let _=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let A=_.createEl("button",{cls:"af-btn-sm",text:"Enable"});A.onclick=S=>{S.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let R=_.createEl("button",{cls:"af-btn-sm"});if(D(R,"edit","af-btn-icon"),R.appendText(" Edit"),R.onclick=A=>{A.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let A=_.createEl("button",{cls:"af-btn-sm primary"});D(A,"play","af-btn-icon"),A.appendText(" Run"),A.onclick=S=>{S.stopPropagation(),this.plugin.runAgentPrompt(s.name)}}c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentStat(e,s,a){let n=e.createDiv({cls:"af-agent-stat"});n.createSpan({cls:"af-agent-stat-value",text:s}),n.createSpan({cls:"af-agent-stat-label",text:a})}renderAgentDetailPage(e){let s=e.createDiv({cls:"af-agent-detail-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"bot","No agent selected","Select an agent from the list");return}let n=this.plugin.runtime.getSnapshot().agents.find(v=>v.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let i=this.plugin.runtime.getAgentState(n.name),o=this.plugin.runtime.getRecentRuns().filter(v=>v.agent===n.name),c=s.createDiv({cls:"af-detail-header"}),l=c.createDiv({cls:"af-detail-header-left"}),d=l.createDiv({cls:`af-agent-card-avatar ${this.healthToClass(i.status)}`});this.renderAgentAvatar(d,n);let h=l.createDiv();h.createDiv({cls:"af-detail-header-name",text:n.name}),h.createDiv({cls:"af-detail-header-desc",text:n.description??"No description"});let u=c.createDiv({cls:"af-detail-header-actions"}),p=u.createEl("button",{cls:"af-btn-sm primary"});if(D(p,"message-circle","af-btn-icon"),p.appendText(" Chat"),p.onclick=()=>this.openChatSlideover(n),n.enabled){let v=u.createEl("button",{cls:"af-btn-sm"});D(v,"play","af-btn-icon"),v.appendText(" Run Now"),v.onclick=()=>void this.plugin.runAgentPrompt(n.name);let g=u.createEl("button",{cls:"af-btn-sm"});D(g,"pause","af-btn-icon"),g.appendText(" Disable"),g.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let v=u.createEl("button",{cls:"af-btn-sm"});D(v,"play","af-btn-icon"),v.appendText(" Enable"),v.onclick=()=>void this.plugin.toggleAgent(n.name,!0)}let f=u.createEl("button",{cls:"af-btn-sm"});D(f,"edit","af-btn-icon"),f.appendText(" Edit"),f.onclick=()=>this.navigate("edit-agent",n.name);let m=u.createEl("button",{cls:"af-btn-sm danger"});D(m,"trash-2","af-btn-icon"),m.appendText(" Delete"),m.onclick=()=>void this.plugin.deleteAgent(n.name);let y=s.createDiv({cls:"af-detail-tabs"}),k=[{id:"overview",label:"Overview",icon:"layout-dashboard"},{id:"config",label:"Config",icon:"settings"},{id:"runs",label:"Runs",icon:"scroll-text"},{id:"memory",label:"Memory",icon:"file-text"}];for(let v of k){let g=y.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===v.id?" active":""}`});D(g,v.icon,"af-tab-icon"),g.appendText(` ${v.label}`),g.onclick=()=>{this.agentDetailTab=v.id,this.render()}}let b=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(b,n,o);break;case"config":this.renderAgentConfigTab(b,n);break;case"runs":this.renderAgentRunsTab(b,o);break;case"memory":this.renderAgentMemoryTab(b,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(v=>v.status==="success").length,c=i>0?Math.round(o/i*100):0,l=i>0?Math.round(a.reduce((v,g)=>v+g.durationSeconds,0)/i):0,d=a.reduce((v,g)=>v+(g.tokensUsed??0),0),h=a.reduce((v,g)=>v+(g.costUsd??0),0),u=h>0?` \xB7 $${h.toFixed(2)}`:"";if(this.renderStatCard(n,"Total Runs",String(i),"","activity","all time"),this.renderStatCard(n,"Success Rate",`${c}%`,"","check-circle-2",`${o}/${i}`),this.renderStatCard(n,"Avg Time",`${l}s`,"","clock","per run"),this.renderStatCard(n,"Total Tokens",Ka(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let v=e.createDiv({cls:"af-section-card"}),g=v.createDiv({cls:"af-section-header"}),x=g.createDiv({cls:"af-section-title"});D(x,"heart-pulse"),x.appendText(" Heartbeat");let _=g.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});_.onclick=async()=>{let S=_.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!S}),await this.plugin.refreshFromVault(),new w.Notice(`Heartbeat ${S?"paused":"enabled"} for ${s.name}`)};let R=v.createDiv({cls:"af-config-form"});this.renderConfigRow(R,"Schedule",ul(s.heartbeatSchedule));let A=this.plugin.runtime.getNextHeartbeat(s.name);A&&s.heartbeatEnabled&&this.renderConfigRow(R,"Next run",this.timeUntil(A)),s.heartbeatChannel&&this.renderConfigRow(R,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"puzzle"),x.appendText(" Skills");let T=v.createDiv({cls:"af-detail-skills-list"});for(let _ of s.skills)T.createSpan({cls:"af-skill-tag",text:_})}let p=s.mcpServers??[];if(p.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"plug"),x.appendText(" MCP Servers");let T=v.createDiv({cls:"af-mcp-overview-list"}),_=this.plugin.mcpManager.getCachedServers();for(let R of p){let A=_?.find(B=>B.name===R),S=T.createDiv({cls:"af-mcp-overview-row"}),L=S.createSpan({cls:`af-mcp-status-dot ${A?A.enabled?A.status:"disabled":"disconnected"}`});L.title=A?A.enabled?A.status:"disabled":"unknown",S.createSpan({cls:"af-mcp-overview-name",text:R});let E=A?.toolDetails.length??A?.tools.length??0;E>0?S.createSpan({cls:"af-mcp-overview-tools",text:`${E} tools`}):A&&!A.enabled?S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):A?.status==="needs-auth"&&S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"needs auth"})}}if(s.permissionRules.allow.length>0||s.permissionRules.deny.length>0||s.permissionMode&&s.permissionMode!=="default"){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(x,"shield-check"),x.appendText(" Permissions");let T=v.createDiv({cls:"af-config-form"});this.renderConfigRow(T,"Mode",s.permissionMode||"default"),s.permissionRules.allow.length>0&&this.renderConfigRow(T,"Allowed",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(T,"Denied",s.permissionRules.deny.join(", "))}let m=e.createDiv({cls:"af-section-card"}),k=m.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(k,"scroll-text"),k.appendText(" Recent Runs");let b=m.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(b,"scroll-text","No runs yet","");else for(let v of a.slice(0,5))this.renderTimelineItem(b,v)}renderAgentConfigTab(e,s){let a=e.createDiv({cls:"af-config-form"});this.renderConfigRow(a,"Name",s.name),this.renderConfigRow(a,"Description",s.description??""),this.renderConfigRow(a,"Model",s.model),this.renderConfigRow(a,"Timeout",`${s.timeout}s`),this.renderConfigRow(a,"Working Directory",s.cwd??"(vault root)"),this.renderConfigRow(a,"Permission Mode",s.permissionMode||"default"),this.renderConfigRow(a,"Approval Required",s.approvalRequired.join(", ")||"none"),s.permissionRules.allow.length>0&&this.renderConfigRow(a,"Allowed Commands",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(a,"Blocked Commands",s.permissionRules.deny.join(", ")),this.renderConfigRow(a,"Memory",s.memory?"Enabled":"Disabled"),this.renderConfigRow(a,"Auto-compact",s.autoCompactThreshold&&s.autoCompactThreshold>0?`at ${s.autoCompactThreshold}% context`:"disabled"),s.wikiReferences&&s.wikiReferences.length>0&&this.renderConfigRow(a,"Wiki access",s.wikiReferences.map(c=>c.agent).join(", ")),this.renderConfigRow(a,"Tags",s.tags.join(", ")||"none");let n=a.createDiv({cls:"af-config-prompt-section"});if(n.createDiv({cls:"af-slideover-section-title",text:"SYSTEM PROMPT"}),n.createDiv({cls:"af-output-block",text:s.body||"(empty)"}),s.heartbeatBody.trim()){let c=a.createDiv({cls:"af-config-prompt-section"});c.createDiv({cls:"af-slideover-section-title",text:"HEARTBEAT INSTRUCTION"}),c.createDiv({cls:"af-output-block",text:s.heartbeatBody})}let o=a.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm primary"});D(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let a of s){let n=e.createDiv({cls:"af-run-list-item"}),i=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,w.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({text:a.task}),c.createSpan({cls:`af-status-badge ${this.statusToBadgeClass(a.status)}`,text:this.statusToBadgeText(a.status)});let l=o.createDiv({cls:"af-tl-meta"});l.createSpan({text:`${this.formatStarted(a.started)} \xB7 ${this.formatDuration(a.durationSeconds)}`}),a.tokensUsed&&l.createSpan({text:`${a.tokensUsed.toLocaleString()} tokens`}),o.createDiv({cls:"af-tl-desc",text:Ot(a.output,120)}),n.onclick=()=>this.openSlideover(a)}}async renderAgentMemoryTab(e,s){if(!s.memory){this.renderEmptyState(e,"file-text","Memory disabled","Enable memory in agent config");return}let a=await this.plugin.repository.getMemory(s.name);if(!a||!a.body.trim()){this.renderEmptyState(e,"file-text","No memories yet","Agent will learn from runs");return}e.createDiv({cls:"af-output-block"}).setText(a.body);let o=e.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm"});D(o,"external-link","af-btn-icon"),o.appendText(" Open in Editor"),o.onclick=()=>void this.plugin.openPath(this.plugin.repository.getMemoryPath(s.name))}timeAgo(e){let s=Math.round((Date.now()-e.getTime())/1e3);if(s<60)return"just now";let a=Math.round(s/60);if(a<60)return`${a}m ago`;let n=Math.round(a/60);return n<24?`${n}h ago`:`${Math.round(n/24)}d ago`}timeUntil(e){let s=Math.round((e.getTime()-Date.now())/1e3);if(s<60)return"< 1m";let a=Math.round(s/60);if(a<60)return`in ${a}m`;let n=Math.round(a/60);return n<24?`in ${n}h`:`in ${Math.round(n/24)}d`}renderKanbanPage(e){let s=e.createDiv({cls:"af-kanban-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),i=s.createDiv({cls:"af-kanban-toolbar"});i.createDiv({cls:"af-page-title",text:"Tasks Board"}),i.createDiv({cls:"af-toolbar-spacer"});let o=i.createEl("button",{cls:"af-btn-sm primary"});D(o,"plus","af-btn-icon"),o.appendText(" New Task"),o.onclick=()=>this.navigate("create-task");let c=s.createDiv({cls:"af-kanban-board"}),l=[],d=[],h=[],u=[],p=[],f=new Set;for(let b of a.agents){let v=this.plugin.runtime.getAgentState(b.name);v.status==="running"&&v.currentTaskId&&f.add(v.currentTaskId)}let m=this.toLocalDateStr(new Date);for(let b of n)b.status==="success"&&this.runToLocalDate(b.started)===m?u.push(b):(b.status==="failure"||b.status==="timeout"||b.status==="cancelled")&&this.runToLocalDate(b.started)===m&&p.push(b);let y=new Set(u.map(b=>b.task)),k=new Set(p.map(b=>b.task));for(let b of a.tasks){let v=y.has(b.taskId)||k.has(b.taskId)||b.lastRun&&this.runToLocalDate(b.lastRun)===m;if(f.has(b.taskId))h.push(b);else{if(v&&!b.schedule)continue;b.schedule&&b.enabled?d.push(b):l.push(b)}}this.renderKanbanColumn(c,"Backlog","inbox",l.length,b=>{for(let v of l)this.renderKanbanTaskCard(b,v,a,!0)},"backlog"),this.renderKanbanColumn(c,"Scheduled","clock",d.length,b=>{for(let v of d)this.renderKanbanTaskCard(b,v,a,!0)},"scheduled"),this.renderKanbanColumn(c,"Running","loader-2",h.length,b=>{for(let v of h)this.renderKanbanRunningCard(b,v)},"running",!1,"running"),this.renderKanbanColumn(c,"Done","check-circle-2",u.length,b=>{for(let v of u.slice(0,10))this.renderKanbanCompletedCard(b,v)},"completed"),this.renderKanbanColumn(c,"Failed","x-circle",p.length,b=>{for(let v of p)this.renderKanbanFailedCard(b,v)},"failed",!1,"failed")}renderKanbanColumn(e,s,a,n,i,o,c=!1,l){let d=e.createDiv({cls:`af-kanban-column${l?` af-kanban-${l}`:""}`});(o==="backlog"||o==="scheduled")&&Gi(d,f=>this.handleTaskDrop(f,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});D(u,a),u.appendText(` ${s} `),u.createSpan({cls:"af-kanban-col-count",text:String(n)});let p=d.createDiv({cls:"af-kanban-col-body"});if(i(p),n===0&&p.createDiv({cls:"af-kanban-empty",text:"No items"}),c){let m=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");D(m,"plus","af-btn-icon"),m.appendText(" Add task"),m.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let a=this.plugin.runtime.getSnapshot().tasks.find(n=>n.taskId===e);if(a){if(s==="backlog")this.setTaskEnabled(a,!1).then(()=>{new w.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new w.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new w.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof w.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);i.enabled=s,await this.plugin.app.vault.modify(a,J(i,o)),await this.plugin.refreshFromVault()}renderKanbanTaskCard(e,s,a,n){let i=e.createDiv({cls:`af-kanban-card af-priority-${s.priority}`});if(n){zi(i,s.taskId);let m=i.createDiv({cls:"af-kanban-card-grip"});(0,w.setIcon)(m,"grip-vertical")}let o=i.createDiv({cls:"af-kanban-card-header"});o.createDiv({cls:"af-kanban-card-title",text:s.taskId});let l=(a.agents.find(m=>m.name===s.agent)?.enabled??!1)&&s.enabled,d=o.createSpan({cls:`af-kanban-card-status ${l?"active":"inactive"}`});d.title=l?"Active":"Inactive";let h=i.createDiv({cls:"af-kanban-card-agent"}),u=h.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(u,"bot"),h.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let f=i.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});l?s.schedule?(D(f,"refresh-cw","af-meta-icon"),f.appendText(` ${this.humanizeCron(s.schedule)}`)):f.appendText(s.runAt??"Manual"):(D(f,"pause","af-meta-icon"),f.appendText(" Paused")),i.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let a=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});a.createDiv({cls:"af-kanban-card-title",text:s.taskId});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=this.plugin.runtime.getSnapshot().agents.find(x=>x.name===s.agent)?.timeout??300,l=this.plugin.runtime.getAgentState(s.agent),d=l.runStarted?new Date(l.runStarted).getTime():Date.now(),p=a.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),f=(Date.now()-d)/1e3,m=Math.min(95,f/c*100);p.style.width=`${m}%`;let y=a.createDiv({cls:"af-kanban-card-footer"}),k=y.createSpan({cls:"af-kanban-card-schedule"});D(k,"loader-2","af-meta-icon");let b=Math.round(f);k.appendText(` ${b}s / ${c}s`);let v=y.createEl("button",{cls:"af-kanban-stop-btn"});(0,w.setIcon)(v,"square"),v.title="Stop task",v.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new w.Notice(`Stopped task "${s.taskId}"`)};let g=setInterval(()=>{let x=(Date.now()-d)/1e3,T=Math.min(95,x/c*100);p.style.width=`${T}%`;let _=Math.round(x);k.textContent="",(0,w.setIcon)(k,"loader-2"),k.appendText(` ${_}s / ${c}s`)},1e3);this.streamingUnsubscribes.push(()=>clearInterval(g))}renderKanbanCompletedCard(e,s){let a=e.createDiv({cls:"af-kanban-card"});a.createDiv({cls:"af-kanban-card-title",text:s.task});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=a.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});D(c,"check-circle-2","af-meta-icon"),c.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),a.onclick=()=>this.openSlideover(s)}renderKanbanFailedCard(e,s){let a=s.status==="cancelled",n=e.createDiv({cls:`af-kanban-card ${a?"af-kanban-card-cancelled":"af-kanban-card-failed"}`});n.createDiv({cls:"af-kanban-card-title",text:s.task});let i=n.createDiv({cls:"af-kanban-card-agent"}),o=i.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(o,"bot"),i.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=a?`Stopped after ${s.durationSeconds}s`:s.status==="timeout"?`Timeout after ${s.durationSeconds}s`:Ot(s.output,60),l=n.createDiv({cls:"af-kanban-card-error"});D(l,a?"square":"alert-triangle","af-meta-icon"),l.appendText(` ${c}`);let d=n.createDiv({cls:"af-kanban-card-footer"}),h=d.createSpan({cls:"af-kanban-card-schedule"});if(D(h,a?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!a){let u=d.createEl("button",{cls:"af-btn-sm"});D(u,"refresh-cw","af-btn-icon"),u.appendText(" Retry"),u.onclick=p=>{p.stopPropagation(),this.plugin.runAgentPrompt(s.agent)}}n.onclick=()=>this.openSlideover(s)}renderRunsPage(e){let s=e.createDiv({cls:"af-runs-page"}),a=this.plugin.runtime.getRecentRuns(),n=s.createDiv({cls:"af-runs-toolbar"});n.createDiv({cls:"af-page-title",text:"Run History"}),n.createDiv({cls:"af-toolbar-spacer"});let i=s.createDiv({cls:"af-runs-table"});if(a.length===0){this.renderEmptyState(i,"scroll-text","No runs yet","Run an agent to see history here");return}let o=i.createEl("table"),l=o.createEl("thead").createEl("tr");for(let h of["Status","Agent","Task","Started","Duration","Tokens","Model"])l.createEl("th",{text:h});let d=o.createEl("tbody");for(let h of a.slice(0,50))this.renderRunRow(d,h)}renderRunRow(e,s){let a=e.createEl("tr"),i=a.createEl("td").createSpan({cls:`af-status-badge ${this.statusToBadgeClass(s.status)}`}),o=i.createSpan();(0,w.setIcon)(o,this.statusToIconName(s.status)),i.appendText(` ${this.statusToBadgeText(s.status)}`);let c=a.createEl("td",{cls:"af-agent-link"});c.setText(s.agent),c.onclick=l=>{l.stopPropagation(),this.navigate("agent-detail",s.agent)},a.createEl("td",{text:s.task}),a.createEl("td",{cls:"af-mono",text:this.formatStarted(s.started)}),a.createEl("td",{cls:"af-mono",text:this.formatDuration(s.durationSeconds)}),a.createEl("td",{cls:"af-mono",text:s.tokensUsed?s.tokensUsed.toLocaleString():"\u2014"}),a.createEl("td",{cls:"af-mono",text:s.model}),a.style.cursor="pointer",a.onclick=()=>this.openSlideover(s)}renderSkillsPage(e){let s=e.createDiv({cls:"af-skills-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Skills Library"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});D(i,"plus","af-btn-icon"),i.appendText(" New Skill"),i.onclick=()=>void this.plugin.createSkillTemplate();let o=s.createDiv({cls:"af-skills-grid"});if(a.skills.length===0){this.renderEmptyState(o,"puzzle","No skills yet","Create skills to give agents specialized abilities");return}for(let c of a.skills)this.renderSkillCard(o,c,a.agents)}renderSkillCard(e,s,a){let n=e.createDiv({cls:"af-skill-card"}),i=n.createDiv({cls:"af-skill-card-header"}),o=i.createDiv({cls:"af-skill-card-icon"});(0,w.setIcon)(o,this.getSkillIcon(s.name));let c=i.createEl("button",{cls:"af-btn-sm af-btn-xs"});D(c,"edit","af-btn-icon"),c.onclick=d=>{d.stopPropagation(),this.navigate("edit-skill",s.name)},n.createDiv({cls:"af-skill-card-name",text:s.name}),n.createDiv({cls:"af-skill-card-desc",text:s.description??"No description"});let l=a.filter(d=>d.skills.includes(s.name));if(l.length>0){let d=n.createDiv({cls:"af-skill-card-agents"});for(let h of l)d.createSpan({cls:"af-skill-card-agent-tag",text:h.name})}}renderChannelsPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Channels"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});D(i,"plus","af-btn-icon"),i.appendText(" New Channel"),i.onclick=()=>this.navigate("create-channel");let o=s.createDiv({cls:"af-agents-grid"});if(a.channels.length===0){this.renderEmptyState(o,"radio","No channels configured","Connect an agent to Slack or another chat platform");return}for(let c of a.channels)this.renderChannelCard(o,c,a.validationIssues)}async renderWikiKeepersPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Wiki Keepers"}),n.createDiv({cls:"af-toolbar-spacer"});let i=a.agents.filter(c=>c.wikiKeeper!==void 0);if(i.length===0){this.renderEmptyState(s,"library","No Wiki Keepers yet","Open Settings \u2192 Agent Fleet \u2192 Wiki Keepers \u2192 + Add to create one.");return}let o=s.createDiv({cls:"af-wk-list"});o.style.display="flex",o.style.flexDirection="column",o.style.gap="16px";for(let c of i)await this.renderWikiKeeperCard(o,c)}async renderWikiKeeperCard(e,s){let a=s.wikiKeeper,n=e.createDiv({cls:"af-card"});n.style.padding="16px",n.style.border="1px solid var(--background-modifier-border)",n.style.borderRadius="8px";let i=n.createDiv();i.style.display="flex",i.style.alignItems="center",i.style.gap="12px",i.style.marginBottom="12px";let o=i.createDiv();o.style.flex="1",o.createEl("strong",{text:s.name});let c=a.scopeRoot||"(whole vault)";o.createEl("div",{text:`Scope: ${c} \xB7 topics: ${a.topicsRoot}/ \xB7 log: ${a.logPath}`,cls:"af-form-hint"});let l=i.createEl("button",{cls:"af-btn-sm"});l.appendText("Open log"),l.onclick=()=>{let y=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,k=this.plugin.app.vault.getAbstractFileByPath(y);k instanceof w.TFile?this.plugin.app.workspace.getLeaf().openFile(k):new w.Notice(`Log file not found: ${y}`)};let d=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,h=this.plugin.app.vault.getAbstractFileByPath(d),u=null;if(h instanceof w.TFile)try{let y=await this.plugin.app.vault.cachedRead(h);u=Vi(y)}catch{u=null}if(!u){n.createDiv({cls:"af-form-hint"}).setText("No lint report yet. Run wiki-lint manually or wait for the weekly task to fire.");return}let p=n.createDiv();if(p.style.display="flex",p.style.alignItems="baseline",p.style.gap="12px",p.style.marginBottom="8px",p.createEl("strong",{text:`Lint ${u.date}`}),p.createSpan({cls:"af-form-hint",text:`${u.summary.length} summary lines \xB7 ${u.autoApplied.length} auto-applied \xB7 ${u.needsReview.length} needs review`}),u.summary.length>0){let y=n.createEl("details");y.createEl("summary",{text:"Summary"});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.summary)k.createEl("li",{text:b})}if(u.autoApplied.length>0){let y=n.createEl("details");y.createEl("summary",{text:`Auto-applied (${u.autoApplied.length})`});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.autoApplied)k.createEl("li",{text:b})}if(u.refreshChained.length>0){let y=n.createEl("details");y.createEl("summary",{text:`Refresh chained (${u.refreshChained.length})`});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.refreshChained)k.createEl("li",{text:b})}let f=n.createDiv();if(f.style.marginTop="12px",f.createEl("strong",{text:`Needs review (${u.needsReview.length})`}),u.needsReview.length===0){f.createDiv({cls:"af-form-hint",text:"All clear. Nothing requires manual review from this lint pass."});return}let m=f.createDiv();m.style.display="flex",m.style.flexDirection="column",m.style.gap="6px",m.style.marginTop="8px";for(let y of u.needsReview){let k=m.createDiv();k.style.display="flex",k.style.alignItems="flex-start",k.style.gap="8px",k.style.padding="8px 10px",k.style.background="var(--background-secondary)",k.style.borderRadius="4px",k.style.fontSize="13px";let b=k.createDiv();b.style.flex="1",b.setText(y);let v=k.createEl("button",{cls:"af-btn-sm",text:"Dismiss"});v.title="Hide this item from the dashboard until the next lint pass (does not modify log.md).",v.onclick=()=>{k.remove()}}}renderChannelCard(e,s,a){let n=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",i=Ki(n),o=s.enabled&&n!=="disabled"?"af-agent-card":"af-agent-card disabled",c=e.createDiv({cls:o});c.style.cursor="default";let l=c.createDiv({cls:"af-agent-card-header"}),d=l.createDiv({cls:`af-agent-card-avatar ${i}`});(0,w.setIcon)(d,"radio");let h=l.createDiv({cls:"af-agent-card-titleblock"});h.createDiv({cls:"af-agent-card-name",text:s.name}),h.createDiv({cls:"af-agent-card-desc",text:`Default: ${s.defaultAgent}`});let u=l.createSpan({cls:`af-pill ${pl(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let T=c.createDiv({cls:"af-agent-card-skills"});for(let _ of s.allowedAgents){let R=T.createSpan({cls:"af-skill-tag",text:_});_===s.defaultAgent&&(R.style.fontWeight="700")}}let p=c.createDiv({cls:"af-agent-card-stats"}),f=this.plugin.channelManager?.getSessionCount(s.name)??0,m=this.plugin.channelManager?.getMetrics(s.name),y=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,y,"Agents"),this.renderAgentStat(p,String(f),"Sessions"),this.renderAgentStat(p,String(m?.messagesReceived??0),"In"),this.renderAgentStat(p,String(m?.messagesSent??0),"Out");let k=c.createDiv({cls:"af-agent-card-footer"}),b=[s.type];s.enabled||b.push("disabled"),s.allowedUsers.length>0?b.push(`${s.allowedUsers.length} user(s)`):b.push("allowlist empty"),k.createSpan({cls:"af-agent-card-meta",text:b.join(" \xB7 ")});let g=k.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});D(g,"edit","af-btn-icon"),g.appendText(" Edit"),g.onclick=T=>{T.stopPropagation(),this.navigate("edit-channel",s.name)};let x=a.filter(T=>T.path===s.filePath);if(x.length>0){let T=c.createDiv({cls:"af-channel-issues"});for(let _ of x)T.createDiv({cls:"af-channel-issue-row",text:_.message})}}renderCreateChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.channelCredentials.list(),i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"plus");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:"Create New Channel"}),l.createDiv({cls:"af-detail-header-desc",text:"Connect an external chat transport to an agent"}),i.createDiv({cls:"af-detail-header-actions"});let d={name:"",type:"slack",defaultAgent:a.agents[0]?.name??"",allowedAgents:[],credentialRef:n[0]?.ref??"",allowedUsers:"",perUserSessions:!0,channelContext:"",enabled:!0,tags:"",body:"",transportJson:""},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),f=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",H=>{d.name=H});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Type"});let y=m.createEl("select",{cls:"af-form-select"});y.createEl("option",{text:"slack",attr:{value:"slack"}}),y.createEl("option",{text:"telegram",attr:{value:"telegram"}}),y.addEventListener("change",()=>{d.type=y.value});let k=u.createDiv({cls:"af-form-row"}),b=k.createDiv({cls:"af-form-label"});b.setText("Credential"),this.addTooltip(b,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let v=k.createEl("select",{cls:"af-form-select"});n.length===0&&v.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let H of n)v.createEl("option",{text:`${H.ref} (${H.entry.type})`,attr:{value:H.ref}});v.addEventListener("change",()=>{d.credentialRef=v.value});let g=u.createDiv({cls:"af-form-row af-form-row-toggle"});g.createDiv({cls:"af-form-label",text:"Enabled"});let x=g.createDiv({cls:"af-agent-card-toggle on"});x.onclick=()=>{let H=x.hasClass("on");x.toggleClass("on",!H),d.enabled=!H};let T=h.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"bot"),_.createSpan({text:"Agent Routing"});let A=T.createDiv({cls:"af-form-row"}),S=A.createDiv({cls:"af-form-label"});S.setText("Default agent"),this.addTooltip(S,"Used when no @agent-name prefix is given in a message");let L=A.createEl("select",{cls:"af-form-select"});for(let H of a.agents)L.createEl("option",{text:H.name,attr:{value:H.name}});L.addEventListener("change",()=>{d.defaultAgent=L.value});let E=T.createDiv({cls:"af-form-row"}),B=E.createDiv({cls:"af-form-label"});B.setText("Allowed agents"),this.addTooltip(B,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let I=E.createDiv({cls:"af-form-checkboxes"});for(let H of a.agents){let we=I.createEl("label",{cls:"af-form-checkbox-label"}),ke=we.createEl("input",{attr:{type:"checkbox",value:H.name}});we.appendText(` ${H.name}`),ke.addEventListener("change",()=>{ke.checked?d.allowedAgents.includes(H.name)||d.allowedAgents.push(H.name):d.allowedAgents=d.allowedAgents.filter(ge=>ge!==H.name)})}let q=T.createDiv({cls:"af-form-row af-form-row-toggle"}),F=q.createDiv({cls:"af-form-label"});F.setText("Per-user sessions"),this.addTooltip(F,"Each external user gets their own isolated Claude session");let $=q.createDiv({cls:"af-agent-card-toggle on"});$.onclick=()=>{let H=$.hasClass("on");$.toggleClass("on",!H),d.perUserSessions=!H};let X=h.createDiv({cls:"af-create-section"}),z=X.createDiv({cls:"af-create-section-header"}),ie=z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ie,"shield-check"),z.createSpan({text:"Access Control"});let te=X.createDiv({cls:"af-form-label"});te.setText("Allowed users"),this.addTooltip(te,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let V=X.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1 -U0BXYZ12345`,rows:"4"}});V.addEventListener("input",()=>{d.allowedUsers=V.value});let W=h.createDiv({cls:"af-create-section"}),Z=W.createDiv({cls:"af-create-section-header"}),de=Z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(de,"message-square");let re=Z.createSpan({text:"Channel Context"});this.addTooltip(re,"Extra instructions appended to the agent's system prompt when reached through this channel");let he=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});he.addEventListener("input",()=>{d.channelContext=he.value}),this.createFormField(u,"Tags","ops, internal","Comma-separated metadata",H=>{d.tags=H});let Le=h.createDiv({cls:"af-create-section"}),Ae=Le.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ne,"settings");let He=Ae.createSpan({text:"Advanced"});this.addTooltip(He,"Markdown body (shown in the channel detail page) and transport-specific overrides");let Ee=Le.createDiv({cls:"af-form-label"});Ee.setText("Body (markdown)"),this.addTooltip(Ee,"Free-form notes for this channel. Shown in the channel detail page; not sent to the agent.");let Ce=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});Ce.addEventListener("input",()=>{d.body=Ce.value});let De=Le.createDiv({cls:"af-form-label"});De.setText("Transport config (JSON)"),this.addTooltip(De,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode, telegram webhook settings). Leave blank for defaults.");let Be=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{ +`,t)),as.agents.length},{icon:"columns-3",label:"Tasks Board",page:"kanban"},{icon:"scroll-text",label:"Run History",page:"runs"},{icon:"shield-check",label:"Approvals",page:"approvals",badge:()=>a.pending},{icon:"puzzle",label:"Skills",page:"skills",badge:()=>s.skills.length},{icon:"plug",label:"MCP Servers",page:"mcp",badge:()=>this.plugin.mcpManager.getCachedServers()?.length??0},{icon:"radio",label:"Channels",page:"channels",badge:()=>this.plugin.channelManager?.getConnectedCount()??s.channels.length}];for(let l of i){let d=n.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,fs.setIcon)(h,l.icon),d.createSpan({cls:"af-sidebar-nav-label",text:l.label});let u=l.badge?.();u!==void 0&&u>0&&d.createSpan({cls:"af-sidebar-badge",text:String(u)}),d.setAttribute("role","button"),d.setAttribute("tabindex","0"),d.onclick=()=>void this.plugin.navigateDashboard(l.page),d.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),this.plugin.navigateDashboard(l.page))}}let o=e.createDiv({cls:"af-sidebar-section"});o.createDiv({cls:"af-sidebar-section-header",text:"AGENTS"}),s.agents.length===0&&o.createDiv({cls:"af-sidebar-empty",text:"No agents configured"});for(let l of s.agents){let d=this.plugin.runtime.getAgentState(l.name),h=o.createDiv({cls:"af-sidebar-agent-item"});h.createSpan({cls:`af-sidebar-agent-dot ${this.healthToClass(d.status)}`}),h.createSpan({cls:"af-sidebar-agent-name",text:l.name}),h.setAttribute("role","button"),h.setAttribute("tabindex","0"),h.onclick=()=>void this.plugin.navigateDashboard("agent-detail",l.name),h.onkeydown=u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),this.plugin.navigateDashboard("agent-detail",l.name))}}let c=e.createDiv({cls:"af-sidebar-section"});c.createDiv({cls:"af-sidebar-section-header",text:"QUICK ACTIONS"}),this.renderQuickAction(c,"plus","New Agent",()=>void this.plugin.createAgentTemplate()),this.renderQuickAction(c,"plus","New Task",()=>void this.plugin.openCreateTask()),this.renderQuickAction(c,"plus","New Skill",()=>void this.plugin.createSkillTemplate())}renderQuickAction(e,s,a,n){let i=e.createDiv({cls:"af-sidebar-action-item"}),o=i.createSpan({cls:"af-sidebar-action-icon"});(0,fs.setIcon)(o,s),i.createSpan({text:a}),i.setAttribute("role","button"),i.setAttribute("tabindex","0"),i.onclick=n,i.onkeydown=c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),n())}}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}};var w=require("obsidian");var Kt=require("obsidian"),ll=["bot","brain","shield-check","search","file-text","rocket","wand","sparkles","zap","target","compass","eye","code","terminal","database","globe","mail","message-circle","book","pen-tool","palette","music","camera","chart-bar","clipboard","cpu","server","cloud","lock","key","bell","calendar","clock","heart","star","flag","bookmark"],ia=class extends Kt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,Kt.getIconIds)().sort();let{contentEl:e}=this;e.empty(),e.addClass("af-icon-picker-modal");let s=e.createEl("input",{cls:"af-icon-picker-search",attr:{type:"text",placeholder:"Search icons...",value:this.searchQuery}});this.gridContainer=e.createDiv({cls:"af-icon-picker-scroll"}),this.renderGrid(),s.addEventListener("input",()=>{this.searchQuery=s.value,this.renderGrid()}),setTimeout(()=>s.focus(),0)}renderGrid(){let e=this.gridContainer;e.empty();let s=this.searchQuery.toLowerCase().trim();if(!s)this.renderSection(e,"Popular",ll),this.renderSection(e,"All Icons",this.allIcons);else{let a=this.allIcons.filter(i=>i.includes(s)),n=a.length===0?"No results":`${a.length} result${a.length!==1?"s":""}`;this.renderSection(e,n,a)}}renderSection(e,s,a){let n=e.createDiv({cls:"af-icon-picker-section"});n.createDiv({cls:"af-icon-picker-section-label",text:s});let i=n.createDiv({cls:"af-icon-picker-grid"});for(let o of a)this.renderIconItem(i,o)}renderIconItem(e,s){let a=e.createDiv({cls:`af-icon-picker-item${this.selectedIcon===s?" selected":""}`});a.setAttribute("title",s),a.setAttribute("aria-label",s),(0,Kt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var Hi=require("obsidian");function P(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,Hi.setIcon)(s,t),s}function Ze(r,t={}){let e=document.createElementNS("http://www.w3.org/2000/svg",r);for(let[s,a]of Object.entries(t))e.setAttribute(s,a);return e}function cl(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function qi(r,t){let e=t.length||1,s=1e3,a=6,n=4,i=4,o=s-n-i-a*(e-1),c=Math.floor(o/e),l=140,d=36,h=18,u=h+l+d,p=Math.max(1,...t.map(f=>f.success+f.failure+f.cancelled)),m=Ze("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let f=0;f<=4;f++){let y=h+l-f/4*l;m.appendChild(Ze("line",{x1:String(n),y1:String(y),x2:String(s-i),y2:String(y),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let f=0;f0&&m.appendChild(Ze("rect",{x:String(k),y:String(h+l-g),width:String(c),height:String(Math.max(g,2)),fill:"var(--af-green)",opacity:"0.85"})),y.cancelled>0&&m.appendChild(Ze("rect",{x:String(k),y:String(h+l-g-x),width:String(c),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),y.failure>0&&m.appendChild(Ze("rect",{x:String(k),y:String(h+l-v),width:String(c),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),b===0&&m.appendChild(Ze("rect",{x:String(k),y:String(h+l-3),width:String(c),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),b>0){let R=Ze("text",{x:String(k+c/2),y:String(h+l-v-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});R.textContent=String(b),m.appendChild(R)}let _=Ze("text",{x:String(k+c/2),y:String(h+l+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});_.textContent=cl(y.date),m.appendChild(_)}r.appendChild(m)}function Wi(r,t,e){let c=2*Math.PI*46,l=e>0?t/e:0,d=c*l,h=c-d,u=Ze("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(Ze("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:e>0?"var(--af-red)":"var(--af-text-faint)","stroke-width":String(12),opacity:"0.2"})),l>0&&u.appendChild(Ze("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:"var(--af-green)","stroke-width":String(12),"stroke-dasharray":`${d} ${h}`,"stroke-dashoffset":String(c*.25),"stroke-linecap":"round"}));let p=Ze("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});p.textContent=`${Math.round(l*100)}%`,u.appendChild(p);let m=Ze("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});m.textContent=`${t}/${e} runs`,u.appendChild(m),r.appendChild(u)}function zi(r,t){r.draggable=!0,r.addEventListener("dragstart",e=>{e.dataTransfer?.setData("text/plain",t),r.addClass("af-dragging")}),r.addEventListener("dragend",()=>{r.removeClass("af-dragging")})}function Gi(r,t){r.addEventListener("dragover",e=>{e.preventDefault(),r.addClass("af-drag-over")}),r.addEventListener("dragleave",e=>{let s=e.relatedTarget;(!s||!r.contains(s))&&r.removeClass("af-drag-over")}),r.addEventListener("drop",e=>{e.preventDefault();let s=e.dataTransfer?.getData("text/plain");r.removeClass("af-drag-over"),s&&t(s)})}function Vi(r){let t=/^##\s+Lint\s+(\d{4}-\d{2}-\d{2})\s*$/gm,e,s=-1,a="";for(;(e=t.exec(r))!==null;)e.index>s&&(s=e.index,a=e[1]??"");if(s<0)return null;let n=r.slice(s),i=n.search(/\n##\s+(?!\s*#)/),o=i<0?n:n.slice(0,i);return{date:a,summary:ra(o,"Summary"),autoApplied:ra(o,"Auto-applied"),needsReview:ra(o,"Needs review"),refreshChained:ra(o,"Refresh chained")}}function ra(r,t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^###\\s+${e}\\s*$`,"m").exec(r);if(!a)return[];let n=a.index+a[0].length,i=r.slice(n),o=i.search(/\n###\s+/),l=(o<0?i:i.slice(0,o)).split(/\r?\n/).map(h=>h.trimEnd()),d=[];for(let h of l){let u=h.replace(/^\s+/,"");u.startsWith("- ")?d.push(u.slice(2).trim()):d.length>0&&(h.startsWith(" ")||h.startsWith(" "))&&(d[d.length-1]+=" "+u)}return d}var Yi={dashboard:"Dashboard",agents:"Agents",kanban:"Tasks Board",runs:"Run History",skills:"Skills Library",approvals:"Approvals",mcp:"MCP Servers",channels:"Channels","wiki-keepers":"Wiki Keepers","agent-detail":"Agent Details","task-detail":"Task Details","create-agent":"Create Agent","create-task":"Create Task","create-skill":"Create Skill","edit-agent":"Edit Agent","edit-task":"Edit Task","edit-skill":"Edit Skill","create-channel":"Create Channel","edit-channel":"Edit Channel","add-mcp-server":"Add MCP Server"},dl={dashboard:"layout-dashboard",agents:"bot",kanban:"columns-3",runs:"scroll-text",skills:"puzzle",approvals:"shield-check",mcp:"plug",channels:"radio","wiki-keepers":"library","agent-detail":"bot","task-detail":"circle-dot","create-agent":"plus","create-task":"plus","create-skill":"plus","edit-agent":"edit","edit-task":"edit","edit-skill":"edit","create-channel":"plus","edit-channel":"edit","add-mcp-server":"plus"},hl=["dashboard","agents","kanban","runs","approvals","skills","wiki-keepers","mcp","channels"],ms=class extends w.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return wt}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),this.channelStatusUnsubscribe=this.plugin.channelManager?.onStatusChange(()=>{this.currentPage==="channels"&&this.render()}),await this.render()}async onClose(){this.cleanupStreaming(),this.channelStatusUnsubscribe?.(),this.channelStatusUnsubscribe=void 0,this.plugin.unsubscribeView(this)}navigateTo(e,s){this.currentPage=e,this.detailContext=s,e!=="agent-detail"&&(this.agentDetailTab="overview"),this.render()}async render(){this.cleanupStreaming();let e=this.contentEl;e.empty(),e.addClass("af-root");let a=e.createDiv({cls:"af-app"}).createDiv({cls:"af-main-content"});this.renderTopBar(a),this.renderTabBar(a);let n=a.createDiv({cls:"af-page"});switch(this.currentPage){case"dashboard":this.renderDashboardPage(n);break;case"agents":this.renderAgentsPage(n);break;case"kanban":this.renderKanbanPage(n);break;case"runs":this.renderRunsPage(n);break;case"skills":this.renderSkillsPage(n);break;case"approvals":this.renderApprovalsPage(n);break;case"mcp":this.renderMcpPage(n);break;case"channels":this.renderChannelsPage(n);break;case"wiki-keepers":this.renderWikiKeepersPage(n);break;case"agent-detail":this.renderAgentDetailPage(n);break;case"task-detail":this.renderTaskDetailPage(n);break;case"create-agent":this.renderCreateAgentPage(n);break;case"create-task":this.renderCreateTaskPage(n);break;case"create-skill":this.renderCreateSkillPage(n);break;case"edit-agent":this.renderEditAgentPage(n);break;case"edit-task":this.renderEditTaskPage(n);break;case"edit-skill":this.renderEditSkillPage(n);break;case"create-channel":this.renderCreateChannelPage(n);break;case"edit-channel":this.renderEditChannelPage(n);break;case"add-mcp-server":this.renderAddMcpServerPage(n);break}}navigate(e,s){this.navigateTo(e,s)}cleanupStreaming(){for(let e of this.streamingUnsubscribes)e();this.streamingUnsubscribes=[]}renderTopBar(e){let s=e.createDiv({cls:"af-top-bar"}),a=s.createDiv({cls:"af-top-bar-title"});P(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),i=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(i,"chevron-right");let o=(m,f,y)=>{let k=n.createSpan({cls:f?"af-breadcrumb-link":"",text:m});f&&(k.onclick=()=>this.navigate(f,y))},c=()=>{let m=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(m,"chevron-right")};switch(this.currentPage){case"agent-detail":o("Agents","agents"),c(),o(this.detailContext??"Agent");break;case"task-detail":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task");break;case"edit-agent":o("Agents","agents"),c(),o(this.detailContext??"Agent","agent-detail",this.detailContext),c(),o("Edit");break;case"edit-task":o("Tasks Board","kanban"),c(),o(this.detailContext??"Task","task-detail",this.detailContext),c(),o("Edit");break;case"create-agent":o("Agents","agents"),c(),o("New Agent");break;case"create-task":o("Tasks Board","kanban"),c(),o("New Task");break;case"create-skill":o("Skills Library","skills"),c(),o("New Skill");break;case"edit-skill":o("Skills Library","skills"),c(),o(this.detailContext??"Skill"),c(),o("Edit");break;case"create-channel":o("Channels","channels"),c(),o("New Channel");break;case"edit-channel":o("Channels","channels"),c(),o(this.detailContext??"Channel"),c(),o("Edit");break;case"add-mcp-server":o("MCP Servers","mcp"),c(),o("Add Server");break;default:o(Yi[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let l=s.createDiv({cls:"af-search-wrap"});P(l,"search","af-search-icon");let d=l.createEl("input",{cls:"af-search-input",attr:{type:"text",placeholder:"Search agents, tasks, runs..."}});d.addEventListener("input",()=>{this.handleSearch(d.value,l)}),d.addEventListener("blur",()=>{setTimeout(()=>l.querySelector(".af-search-results")?.remove(),200)});let h=this.plugin.runtime.getFleetStatus(),u=s.createDiv({cls:"af-status-pills"});if(h.running>0){let m=u.createSpan({cls:"af-pill yellow"});m.createSpan({cls:"af-dot pulse"}),m.appendText(` ${h.running} Running`)}if(h.pending>0){let m=u.createSpan({cls:"af-pill blue"});m.createSpan({cls:"af-dot"}),m.appendText(` ${h.pending} Pending`)}let p=u.createSpan({cls:"af-pill green"});p.createSpan({cls:"af-dot"}),p.appendText(` ${h.completedToday} Today`)}renderTabBar(e){let s=e.createDiv({cls:"af-tab-bar"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getFleetStatus();for(let i of hl){let o=this.currentPage===i||i==="agents"&&(this.currentPage==="agent-detail"||this.currentPage==="edit-agent"||this.currentPage==="create-agent")||i==="kanban"&&(this.currentPage==="task-detail"||this.currentPage==="edit-task")||i==="skills"&&(this.currentPage==="edit-skill"||this.currentPage==="create-skill")||i==="mcp"&&this.currentPage==="add-mcp-server",c=s.createEl("button",{cls:`af-tab-item${o?" active":""}`}),l=c.createSpan({cls:"af-tab-icon"});if((0,w.setIcon)(l,dl[i]),c.appendText(i==="dashboard"?"Overview":Yi[i]),i==="agents")c.createSpan({cls:"af-badge",text:String(a.agents.length)});else if(i==="skills")c.createSpan({cls:"af-badge",text:String(a.skills.length)});else if(i==="mcp"){let d=this.plugin.mcpManager.getCachedServers()?.length??0;c.createSpan({cls:"af-badge",text:String(d)})}else i==="approvals"&&n.pending>0&&c.createSpan({cls:"af-badge af-badge-warn",text:String(n.pending)});c.onclick=()=>this.navigate(i)}}renderDashboardPage(e){let s=e.createDiv({cls:"af-dashboard"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),i=this.plugin.runtime.getFleetStatus(),o=n.filter(g=>(g.approvals??[]).some(x=>x.status==="pending"));for(let g of o)for(let x of g.approvals??[])x.status==="pending"&&this.renderApprovalBanner(s,g,x.tool);let c=s.createDiv({cls:"af-dash-grid"}),l=a.agents.filter(g=>g.enabled).length,d=a.agents.length;this.renderStatCard(c,"Active Agents",`${l}`,`/ ${d}`,"bot",`${l} of ${d} enabled`);let h=this.toLocalDateStr(new Date),u=n.filter(g=>this.runToLocalDate(g.started)===h),p=u.filter(g=>g.status==="success").length,m=u.filter(g=>g.status==="failure"||g.status==="timeout").length;this.renderStatCard(c,"Runs Today",String(u.length),"","activity",`${p} passed \xB7 ${m} failed \xB7 ${i.running} running`);let f=u.reduce((g,x)=>g+(x.tokensUsed??0),0),y=u.reduce((g,x)=>g+(x.costUsd??0),0),k=y>0?` \xB7 $${y.toFixed(2)}`:"";this.renderStatCard(c,"Tokens Used",Ja(f),"","zap",`today${k}`);let b=a.tasks.filter(g=>g.enabled&&g.schedule);this.renderStatCard(c,"Scheduled Tasks",String(b.length),"","clock",b.length>0?`Next: ${this.getNextTaskLabel(b)}`:"No scheduled tasks"),this.renderChartsRow(s,n,this.plugin.runtime.getChartRuns()),this.renderStreamingCards(s);let v=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(v,n),this.renderFleetStatusPanel(v,a)}renderChartsRow(e,s,a){let n=e.createDiv({cls:"af-charts-row"}),i=n.createDiv({cls:"af-section-card af-chart-section"}),c=i.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(c,"activity"),c.appendText(" Run Activity (14d)");let l=i.createDiv({cls:"af-chart-body"}),d=this.buildChartData(a,14);d.some(k=>k.success+k.failure+k.cancelled>0)?qi(l,d):this.renderEmptyState(l,"activity","No run data","Run agents to see activity");let h=n.createDiv({cls:"af-section-card af-chart-section"}),p=h.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(p,"target"),p.appendText(" Success Rate");let m=h.createDiv({cls:"af-chart-body af-chart-body-center"}),f=s.length,y=s.filter(k=>k.status==="success").length;f>0?Wi(m,y,f):this.renderEmptyState(m,"target","No data","")}toLocalDateStr(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}`}runToLocalDate(e){return this.toLocalDateStr(new Date(e))}buildChartData(e,s){let a=[],n=new Date;for(let i=s-1;i>=0;i--){let o=new Date(n);o.setDate(o.getDate()-i);let c=this.toLocalDateStr(o),l=e.filter(d=>this.runToLocalDate(d.started)===c);a.push({date:c,success:l.filter(d=>d.status==="success").length,failure:l.filter(d=>d.status==="failure"||d.status==="timeout").length,cancelled:l.filter(d=>d.status==="cancelled").length})}return a}renderStreamingCards(e){let a=this.plugin.runtime.getSnapshot().agents.filter(c=>this.plugin.runtime.getAgentState(c.name).status==="running");if(a.length===0)return;let n=e.createDiv({cls:"af-streaming-section"}),o=n.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(o,"terminal"),o.appendText(" Active Agents");for(let c of a)this.renderStreamingCard(n,c.name)}renderStreamingCard(e,s){let a=e.createDiv({cls:"af-streaming-card"}),n=this.plugin.runtime.getAgentState(s),i=this.plugin.runtime.getSnapshot(),o=n.currentTaskId?i.tasks.find(m=>m.taskId===n.currentTaskId):void 0,c=o?` \u2192 ${o.taskId}`:n.status==="running"?" \u2192 Heartbeat":"",l=a.createDiv({cls:"af-streaming-card-header"});l.createSpan({cls:"af-dot pulse",attr:{style:"background: var(--af-yellow)"}}),l.createSpan({cls:"af-streaming-card-agent",text:` ${s}`}),c&&l.createSpan({cls:"af-streaming-card-task",text:c});let d=a.createDiv({cls:"af-streaming-output"}),h=this.plugin.runtime.getRunOutputBuffer(s),u=me(h).slice(-4);d.setText(u.join(` +`));let p=this.plugin.runtime.onRunOutput(s,()=>{let m=this.plugin.runtime.getRunOutputBuffer(s),f=me(m).slice(-4);d.setText(f.join(` +`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(p)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),i=n.createDiv({cls:"af-approval-icon"});(0,w.setIcon)(i,"shield-check");let o=n.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${a}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${a}`});let c=n.createDiv({cls:"af-approval-actions"}),l=c.createEl("button",{cls:"af-btn-approve",text:"Approve"});l.onclick=()=>void this.plugin.runtime.resolveApproval(s,a,"approved").then(()=>this.render());let d=c.createEl("button",{cls:"af-btn-reject",text:"Reject"});d.onclick=()=>void this.plugin.runtime.resolveApproval(s,a,"rejected").then(()=>this.render())}renderStatCard(e,s,a,n,i,o){let c=e.createDiv({cls:"af-stat-card"}),l=c.createDiv({cls:"af-stat-label"});P(l,i,"af-stat-icon"),l.appendText(` ${s}`);let d=c.createDiv({cls:"af-stat-value"});d.appendText(a),n&&d.createSpan({cls:"af-stat-value-suffix",text:n}),c.createDiv({cls:"af-stat-sub",text:o})}renderActivityTimeline(e,s){let a=e.createDiv({cls:"af-section-card"}),i=a.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(i,"inbox"),i.appendText(" Recent Activity");let o=a.createDiv({cls:"af-timeline"}),c=s.slice(0,8);if(c.length===0){this.renderEmptyState(o,"inbox","No runs yet","Run an agent to see activity here");return}for(let l of c)this.renderTimelineItem(o,l)}renderTimelineItem(e,s){let a=e.createDiv({cls:"af-timeline-item"}),n=this.statusToTimelineClass(s.status),i=a.createDiv({cls:`af-tl-icon ${n}`});(0,w.setIcon)(i,this.statusToIconName(s.status));let o=a.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({cls:"af-agent-tag",text:s.agent}),c.appendText(` ${s.task}`),o.createDiv({cls:"af-tl-desc",text:Ot(s.output,100)});let l=o.createDiv({cls:"af-tl-meta"}),d=l.createSpan();if(P(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=l.createSpan();P(h,"zap","af-meta-icon"),h.appendText(` ${s.tokensUsed.toLocaleString()} tokens`)}a.onclick=()=>this.openSlideover(s)}renderFleetStatusPanel(e,s){let a=e.createDiv({cls:"af-section-card"}),n=a.createDiv({cls:"af-section-header"}),i=n.createDiv({cls:"af-section-title"});P(i,"bot"),i.appendText(" Fleet Status");let c=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});P(c,"plus","af-btn-icon"),c.appendText(" New Agent"),c.onclick=()=>void this.plugin.createAgentTemplate();let l=a.createDiv({cls:"af-agent-mini-list"});if(s.agents.length===0){this.renderEmptyState(l,"bot","No agents yet","Create your first agent to get started");return}for(let h of s.agents)this.renderAgentMini(l,h,s.tasks);let d=s.agents.filter(h=>h.enabled);if(d.length>0){let h=a.createDiv({cls:"af-quick-run"}),u=h.createDiv({cls:"af-quick-run-label"});P(u,"zap","af-meta-icon"),u.appendText(" Quick Run");let p=h.createDiv({cls:"af-quick-run-row"}),m=p.createEl("select",{cls:"af-select"});for(let y of d)m.createEl("option",{text:y.name,attr:{value:y.name}});let f=p.createEl("button",{cls:"af-btn-sm primary"});P(f,"play","af-btn-icon"),f.appendText(" Run"),f.onclick=()=>void this.plugin.runAgentPrompt(m.value)}}renderAgentMini(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=a.filter(u=>u.agent===s.name),o=this.healthToClass(n.status),c=e.createDiv({cls:"af-agent-mini"}),l=c.createDiv({cls:`af-agent-avatar ${o}`});s.avatar?.trim()?this.renderAgentAvatar(l,s):l.setText(this.getInitials(s.name));let d=c.createDiv({cls:"af-agent-info"});d.createDiv({cls:"af-agent-name",text:s.name});let h="";if(n.status==="running")h=`Running now \xB7 ${i.length} task${i.length!==1?"s":""}`;else if(!s.enabled)h=`Disabled \xB7 ${i.length} task${i.length!==1?"s":""} paused`;else{let u=i.map(p=>p.nextRun).filter(Boolean).sort()[0];h=u?`Next: ${this.formatNextRun(u)} \xB7 ${i.length} task${i.length!==1?"s":""}`:`${i.length} task${i.length!==1?"s":""}`}d.createDiv({cls:"af-agent-desc",text:h}),c.createDiv({cls:`af-agent-status-dot ${o}`}),c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentsPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Agents"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});P(i,"plus","af-btn-icon"),i.appendText(" New Agent"),i.onclick=()=>void this.plugin.createAgentTemplate();let o=s.createDiv({cls:"af-agents-grid"});if(a.agents.length===0){this.renderEmptyState(o,"bot","No agents configured","Create your first agent to get started");return}for(let c of a.agents)this.renderAgentCard(o,c,a)}renderAgentCard(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=this.plugin.runtime.getRecentRuns().filter(A=>A.agent===s.name),o=a.tasks.filter(A=>A.agent===s.name),c=e.createDiv({cls:`af-agent-card${s.enabled?"":" disabled"}`}),l=c.createDiv({cls:"af-agent-card-header"}),d=s.enabled?this.healthToClass(n.status):"disabled",h=l.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(h,s);let u=l.createDiv({cls:"af-agent-card-titleblock"}),p=u.createDiv({cls:"af-agent-card-name"});if(p.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let A=p.createSpan({cls:"af-heartbeat-indicator"});(0,w.setIcon)(A,"heart-pulse"),A.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let m=l.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});m.onclick=A=>{A.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let f=c.createDiv({cls:"af-agent-card-stats"}),y=i.length,k=i.filter(A=>A.status==="success").length,b=y>0?Math.round(k/y*100):0,v=y>0?Math.round(i.reduce((A,S)=>A+S.durationSeconds,0)/y):0,g=i.reduce((A,S)=>A+(S.tokensUsed??0),0);if(this.renderAgentStat(f,String(y),"Runs"),this.renderAgentStat(f,`${b}%`,"Success"),this.renderAgentStat(f,`${v}s`,"Avg Time"),this.renderAgentStat(f,Ja(g),"Tokens"),s.skills.length>0){let A=c.createDiv({cls:"af-agent-card-skills"});for(let S of s.skills)A.createSpan({cls:"af-skill-tag",text:S})}let x=c.createDiv({cls:"af-agent-card-footer"}),T=[`Model: ${s.model}`];s.approvalRequired.length>0&&T.push(`Approval: ${s.approvalRequired.join(", ")}`),s.memory&&T.push("Memory: on"),s.enabled||T.unshift("Disabled"),x.createSpan({cls:"af-agent-card-meta",text:T.join(" \xB7 ")});let _=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let A=_.createEl("button",{cls:"af-btn-sm",text:"Enable"});A.onclick=S=>{S.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let R=_.createEl("button",{cls:"af-btn-sm"});if(P(R,"edit","af-btn-icon"),R.appendText(" Edit"),R.onclick=A=>{A.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let A=_.createEl("button",{cls:"af-btn-sm primary"});P(A,"play","af-btn-icon"),A.appendText(" Run"),A.onclick=S=>{S.stopPropagation(),this.plugin.runAgentPrompt(s.name)}}c.onclick=()=>this.navigate("agent-detail",s.name)}renderAgentStat(e,s,a){let n=e.createDiv({cls:"af-agent-stat"});n.createSpan({cls:"af-agent-stat-value",text:s}),n.createSpan({cls:"af-agent-stat-label",text:a})}renderAgentDetailPage(e){let s=e.createDiv({cls:"af-agent-detail-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"bot","No agent selected","Select an agent from the list");return}let n=this.plugin.runtime.getSnapshot().agents.find(v=>v.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let i=this.plugin.runtime.getAgentState(n.name),o=this.plugin.runtime.getRecentRuns().filter(v=>v.agent===n.name),c=s.createDiv({cls:"af-detail-header"}),l=c.createDiv({cls:"af-detail-header-left"}),d=l.createDiv({cls:`af-agent-card-avatar ${this.healthToClass(i.status)}`});this.renderAgentAvatar(d,n);let h=l.createDiv();h.createDiv({cls:"af-detail-header-name",text:n.name}),h.createDiv({cls:"af-detail-header-desc",text:n.description??"No description"});let u=c.createDiv({cls:"af-detail-header-actions"}),p=u.createEl("button",{cls:"af-btn-sm primary"});if(P(p,"message-circle","af-btn-icon"),p.appendText(" Chat"),p.onclick=()=>this.openChatSlideover(n),n.enabled){let v=u.createEl("button",{cls:"af-btn-sm"});P(v,"play","af-btn-icon"),v.appendText(" Run Now"),v.onclick=()=>void this.plugin.runAgentPrompt(n.name);let g=u.createEl("button",{cls:"af-btn-sm"});P(g,"pause","af-btn-icon"),g.appendText(" Disable"),g.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let v=u.createEl("button",{cls:"af-btn-sm"});P(v,"play","af-btn-icon"),v.appendText(" Enable"),v.onclick=()=>void this.plugin.toggleAgent(n.name,!0)}let m=u.createEl("button",{cls:"af-btn-sm"});P(m,"edit","af-btn-icon"),m.appendText(" Edit"),m.onclick=()=>this.navigate("edit-agent",n.name);let f=u.createEl("button",{cls:"af-btn-sm danger"});P(f,"trash-2","af-btn-icon"),f.appendText(" Delete"),f.onclick=()=>void this.plugin.deleteAgent(n.name);let y=s.createDiv({cls:"af-detail-tabs"}),k=[{id:"overview",label:"Overview",icon:"layout-dashboard"},{id:"config",label:"Config",icon:"settings"},{id:"runs",label:"Runs",icon:"scroll-text"},{id:"memory",label:"Memory",icon:"file-text"}];for(let v of k){let g=y.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===v.id?" active":""}`});P(g,v.icon,"af-tab-icon"),g.appendText(` ${v.label}`),g.onclick=()=>{this.agentDetailTab=v.id,this.render()}}let b=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(b,n,o);break;case"config":this.renderAgentConfigTab(b,n);break;case"runs":this.renderAgentRunsTab(b,o);break;case"memory":this.renderAgentMemoryTab(b,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(v=>v.status==="success").length,c=i>0?Math.round(o/i*100):0,l=i>0?Math.round(a.reduce((v,g)=>v+g.durationSeconds,0)/i):0,d=a.reduce((v,g)=>v+(g.tokensUsed??0),0),h=a.reduce((v,g)=>v+(g.costUsd??0),0),u=h>0?` \xB7 $${h.toFixed(2)}`:"";if(this.renderStatCard(n,"Total Runs",String(i),"","activity","all time"),this.renderStatCard(n,"Success Rate",`${c}%`,"","check-circle-2",`${o}/${i}`),this.renderStatCard(n,"Avg Time",`${l}s`,"","clock","per run"),this.renderStatCard(n,"Total Tokens",Ja(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let v=e.createDiv({cls:"af-section-card"}),g=v.createDiv({cls:"af-section-header"}),x=g.createDiv({cls:"af-section-title"});P(x,"heart-pulse"),x.appendText(" Heartbeat");let _=g.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});_.onclick=async()=>{let S=_.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!S}),await this.plugin.refreshFromVault(),new w.Notice(`Heartbeat ${S?"paused":"enabled"} for ${s.name}`)};let R=v.createDiv({cls:"af-config-form"});this.renderConfigRow(R,"Schedule",ul(s.heartbeatSchedule));let A=this.plugin.runtime.getNextHeartbeat(s.name);A&&s.heartbeatEnabled&&this.renderConfigRow(R,"Next run",this.timeUntil(A)),s.heartbeatChannel&&this.renderConfigRow(R,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"puzzle"),x.appendText(" Skills");let T=v.createDiv({cls:"af-detail-skills-list"});for(let _ of s.skills)T.createSpan({cls:"af-skill-tag",text:_})}let p=s.mcpServers??[];if(p.length>0){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"plug"),x.appendText(" MCP Servers");let T=v.createDiv({cls:"af-mcp-overview-list"}),_=this.plugin.mcpManager.getCachedServers();for(let R of p){let A=_?.find(B=>B.name===R),S=T.createDiv({cls:"af-mcp-overview-row"}),L=S.createSpan({cls:`af-mcp-status-dot ${A?A.enabled?A.status:"disabled":"disconnected"}`});L.title=A?A.enabled?A.status:"disabled":"unknown",S.createSpan({cls:"af-mcp-overview-name",text:R});let E=A?.toolDetails.length??A?.tools.length??0;E>0?S.createSpan({cls:"af-mcp-overview-tools",text:`${E} tools`}):A&&!A.enabled?S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):A?.status==="needs-auth"&&S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"needs auth"})}}if(s.permissionRules.allow.length>0||s.permissionRules.deny.length>0||s.permissionMode&&s.permissionMode!=="default"){let v=e.createDiv({cls:"af-section-card"}),x=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"shield-check"),x.appendText(" Permissions");let T=v.createDiv({cls:"af-config-form"});this.renderConfigRow(T,"Mode",s.permissionMode||"default"),s.permissionRules.allow.length>0&&this.renderConfigRow(T,"Allowed",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(T,"Denied",s.permissionRules.deny.join(", "))}let f=e.createDiv({cls:"af-section-card"}),k=f.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(k,"scroll-text"),k.appendText(" Recent Runs");let b=f.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(b,"scroll-text","No runs yet","");else for(let v of a.slice(0,5))this.renderTimelineItem(b,v)}renderAgentConfigTab(e,s){let a=e.createDiv({cls:"af-config-form"});this.renderConfigRow(a,"Name",s.name),this.renderConfigRow(a,"Description",s.description??""),this.renderConfigRow(a,"Model",s.model),this.renderConfigRow(a,"Timeout",`${s.timeout}s`),this.renderConfigRow(a,"Working Directory",s.cwd??"(vault root)"),this.renderConfigRow(a,"Permission Mode",s.permissionMode||"default"),this.renderConfigRow(a,"Approval Required",s.approvalRequired.join(", ")||"none"),s.permissionRules.allow.length>0&&this.renderConfigRow(a,"Allowed Commands",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(a,"Blocked Commands",s.permissionRules.deny.join(", ")),this.renderConfigRow(a,"Memory",s.memory?"Enabled":"Disabled"),this.renderConfigRow(a,"Auto-compact",s.autoCompactThreshold&&s.autoCompactThreshold>0?`at ${s.autoCompactThreshold}% context`:"disabled"),s.wikiReferences&&s.wikiReferences.length>0&&this.renderConfigRow(a,"Wiki access",s.wikiReferences.map(c=>c.agent).join(", ")),this.renderConfigRow(a,"Tags",s.tags.join(", ")||"none");let n=a.createDiv({cls:"af-config-prompt-section"});if(n.createDiv({cls:"af-slideover-section-title",text:"SYSTEM PROMPT"}),n.createDiv({cls:"af-output-block",text:s.body||"(empty)"}),s.heartbeatBody.trim()){let c=a.createDiv({cls:"af-config-prompt-section"});c.createDiv({cls:"af-slideover-section-title",text:"HEARTBEAT INSTRUCTION"}),c.createDiv({cls:"af-output-block",text:s.heartbeatBody})}let o=a.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm primary"});P(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let a of s){let n=e.createDiv({cls:"af-run-list-item"}),i=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,w.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),c=o.createDiv({cls:"af-tl-title"});c.createSpan({text:a.task}),c.createSpan({cls:`af-status-badge ${this.statusToBadgeClass(a.status)}`,text:this.statusToBadgeText(a.status)});let l=o.createDiv({cls:"af-tl-meta"});l.createSpan({text:`${this.formatStarted(a.started)} \xB7 ${this.formatDuration(a.durationSeconds)}`}),a.tokensUsed&&l.createSpan({text:`${a.tokensUsed.toLocaleString()} tokens`}),o.createDiv({cls:"af-tl-desc",text:Ot(a.output,120)}),n.onclick=()=>this.openSlideover(a)}}async renderAgentMemoryTab(e,s){if(!s.memory){this.renderEmptyState(e,"file-text","Memory disabled","Enable memory in agent config");return}let a=await this.plugin.repository.getMemory(s.name);if(!a||!a.body.trim()){this.renderEmptyState(e,"file-text","No memories yet","Agent will learn from runs");return}e.createDiv({cls:"af-output-block"}).setText(a.body);let o=e.createDiv({cls:"af-slideover-actions"}).createEl("button",{cls:"af-btn-sm"});P(o,"external-link","af-btn-icon"),o.appendText(" Open in Editor"),o.onclick=()=>void this.plugin.openPath(this.plugin.repository.getMemoryPath(s.name))}timeAgo(e){let s=Math.round((Date.now()-e.getTime())/1e3);if(s<60)return"just now";let a=Math.round(s/60);if(a<60)return`${a}m ago`;let n=Math.round(a/60);return n<24?`${n}h ago`:`${Math.round(n/24)}d ago`}timeUntil(e){let s=Math.round((e.getTime()-Date.now())/1e3);if(s<60)return"< 1m";let a=Math.round(s/60);if(a<60)return`in ${a}m`;let n=Math.round(a/60);return n<24?`in ${n}h`:`in ${Math.round(n/24)}d`}renderKanbanPage(e){let s=e.createDiv({cls:"af-kanban-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),i=s.createDiv({cls:"af-kanban-toolbar"});i.createDiv({cls:"af-page-title",text:"Tasks Board"}),i.createDiv({cls:"af-toolbar-spacer"});let o=i.createEl("button",{cls:"af-btn-sm primary"});P(o,"plus","af-btn-icon"),o.appendText(" New Task"),o.onclick=()=>this.navigate("create-task");let c=s.createDiv({cls:"af-kanban-board"}),l=[],d=[],h=[],u=[],p=[],m=new Set;for(let b of a.agents){let v=this.plugin.runtime.getAgentState(b.name);v.status==="running"&&v.currentTaskId&&m.add(v.currentTaskId)}let f=this.toLocalDateStr(new Date);for(let b of n)b.status==="success"&&this.runToLocalDate(b.started)===f?u.push(b):(b.status==="failure"||b.status==="timeout"||b.status==="cancelled")&&this.runToLocalDate(b.started)===f&&p.push(b);let y=new Set(u.map(b=>b.task)),k=new Set(p.map(b=>b.task));for(let b of a.tasks){let v=y.has(b.taskId)||k.has(b.taskId)||b.lastRun&&this.runToLocalDate(b.lastRun)===f;if(m.has(b.taskId))h.push(b);else{if(v&&!b.schedule)continue;b.schedule&&b.enabled?d.push(b):l.push(b)}}this.renderKanbanColumn(c,"Backlog","inbox",l.length,b=>{for(let v of l)this.renderKanbanTaskCard(b,v,a,!0)},"backlog"),this.renderKanbanColumn(c,"Scheduled","clock",d.length,b=>{for(let v of d)this.renderKanbanTaskCard(b,v,a,!0)},"scheduled"),this.renderKanbanColumn(c,"Running","loader-2",h.length,b=>{for(let v of h)this.renderKanbanRunningCard(b,v)},"running",!1,"running"),this.renderKanbanColumn(c,"Done","check-circle-2",u.length,b=>{for(let v of u.slice(0,10))this.renderKanbanCompletedCard(b,v)},"completed"),this.renderKanbanColumn(c,"Failed","x-circle",p.length,b=>{for(let v of p)this.renderKanbanFailedCard(b,v)},"failed",!1,"failed")}renderKanbanColumn(e,s,a,n,i,o,c=!1,l){let d=e.createDiv({cls:`af-kanban-column${l?` af-kanban-${l}`:""}`});(o==="backlog"||o==="scheduled")&&Gi(d,m=>this.handleTaskDrop(m,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});P(u,a),u.appendText(` ${s} `),u.createSpan({cls:"af-kanban-col-count",text:String(n)});let p=d.createDiv({cls:"af-kanban-col-body"});if(i(p),n===0&&p.createDiv({cls:"af-kanban-empty",text:"No items"}),c){let f=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");P(f,"plus","af-btn-icon"),f.appendText(" Add task"),f.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let a=this.plugin.runtime.getSnapshot().tasks.find(n=>n.taskId===e);if(a){if(s==="backlog")this.setTaskEnabled(a,!1).then(()=>{new w.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new w.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new w.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof w.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);i.enabled=s,await this.plugin.app.vault.modify(a,J(i,o)),await this.plugin.refreshFromVault()}renderKanbanTaskCard(e,s,a,n){let i=e.createDiv({cls:`af-kanban-card af-priority-${s.priority}`});if(n){zi(i,s.taskId);let f=i.createDiv({cls:"af-kanban-card-grip"});(0,w.setIcon)(f,"grip-vertical")}let o=i.createDiv({cls:"af-kanban-card-header"});o.createDiv({cls:"af-kanban-card-title",text:s.taskId});let l=(a.agents.find(f=>f.name===s.agent)?.enabled??!1)&&s.enabled,d=o.createSpan({cls:`af-kanban-card-status ${l?"active":"inactive"}`});d.title=l?"Active":"Inactive";let h=i.createDiv({cls:"af-kanban-card-agent"}),u=h.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(u,"bot"),h.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let m=i.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});l?s.schedule?(P(m,"refresh-cw","af-meta-icon"),m.appendText(` ${this.humanizeCron(s.schedule)}`)):m.appendText(s.runAt??"Manual"):(P(m,"pause","af-meta-icon"),m.appendText(" Paused")),i.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let a=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});a.createDiv({cls:"af-kanban-card-title",text:s.taskId});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=this.plugin.runtime.getSnapshot().agents.find(x=>x.name===s.agent)?.timeout??300,l=this.plugin.runtime.getAgentState(s.agent),d=l.runStarted?new Date(l.runStarted).getTime():Date.now(),p=a.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),m=(Date.now()-d)/1e3,f=Math.min(95,m/c*100);p.style.width=`${f}%`;let y=a.createDiv({cls:"af-kanban-card-footer"}),k=y.createSpan({cls:"af-kanban-card-schedule"});P(k,"loader-2","af-meta-icon");let b=Math.round(m);k.appendText(` ${b}s / ${c}s`);let v=y.createEl("button",{cls:"af-kanban-stop-btn"});(0,w.setIcon)(v,"square"),v.title="Stop task",v.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new w.Notice(`Stopped task "${s.taskId}"`)};let g=setInterval(()=>{let x=(Date.now()-d)/1e3,T=Math.min(95,x/c*100);p.style.width=`${T}%`;let _=Math.round(x);k.textContent="",(0,w.setIcon)(k,"loader-2"),k.appendText(` ${_}s / ${c}s`)},1e3);this.streamingUnsubscribes.push(()=>clearInterval(g))}renderKanbanCompletedCard(e,s){let a=e.createDiv({cls:"af-kanban-card"});a.createDiv({cls:"af-kanban-card-title",text:s.task});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=a.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});P(c,"check-circle-2","af-meta-icon"),c.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),a.onclick=()=>this.openSlideover(s)}renderKanbanFailedCard(e,s){let a=s.status==="cancelled",n=e.createDiv({cls:`af-kanban-card ${a?"af-kanban-card-cancelled":"af-kanban-card-failed"}`});n.createDiv({cls:"af-kanban-card-title",text:s.task});let i=n.createDiv({cls:"af-kanban-card-agent"}),o=i.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(o,"bot"),i.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let c=a?`Stopped after ${s.durationSeconds}s`:s.status==="timeout"?`Timeout after ${s.durationSeconds}s`:Ot(s.output,60),l=n.createDiv({cls:"af-kanban-card-error"});P(l,a?"square":"alert-triangle","af-meta-icon"),l.appendText(` ${c}`);let d=n.createDiv({cls:"af-kanban-card-footer"}),h=d.createSpan({cls:"af-kanban-card-schedule"});if(P(h,a?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!a){let u=d.createEl("button",{cls:"af-btn-sm"});P(u,"refresh-cw","af-btn-icon"),u.appendText(" Retry"),u.onclick=p=>{p.stopPropagation(),this.plugin.runAgentPrompt(s.agent)}}n.onclick=()=>this.openSlideover(s)}renderRunsPage(e){let s=e.createDiv({cls:"af-runs-page"}),a=this.plugin.runtime.getRecentRuns(),n=s.createDiv({cls:"af-runs-toolbar"});n.createDiv({cls:"af-page-title",text:"Run History"}),n.createDiv({cls:"af-toolbar-spacer"});let i=s.createDiv({cls:"af-runs-table"});if(a.length===0){this.renderEmptyState(i,"scroll-text","No runs yet","Run an agent to see history here");return}let o=i.createEl("table"),l=o.createEl("thead").createEl("tr");for(let h of["Status","Agent","Task","Started","Duration","Tokens","Model"])l.createEl("th",{text:h});let d=o.createEl("tbody");for(let h of a.slice(0,50))this.renderRunRow(d,h)}renderRunRow(e,s){let a=e.createEl("tr"),i=a.createEl("td").createSpan({cls:`af-status-badge ${this.statusToBadgeClass(s.status)}`}),o=i.createSpan();(0,w.setIcon)(o,this.statusToIconName(s.status)),i.appendText(` ${this.statusToBadgeText(s.status)}`);let c=a.createEl("td",{cls:"af-agent-link"});c.setText(s.agent),c.onclick=l=>{l.stopPropagation(),this.navigate("agent-detail",s.agent)},a.createEl("td",{text:s.task}),a.createEl("td",{cls:"af-mono",text:this.formatStarted(s.started)}),a.createEl("td",{cls:"af-mono",text:this.formatDuration(s.durationSeconds)}),a.createEl("td",{cls:"af-mono",text:s.tokensUsed?s.tokensUsed.toLocaleString():"\u2014"}),a.createEl("td",{cls:"af-mono",text:s.model}),a.style.cursor="pointer",a.onclick=()=>this.openSlideover(s)}renderSkillsPage(e){let s=e.createDiv({cls:"af-skills-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Skills Library"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});P(i,"plus","af-btn-icon"),i.appendText(" New Skill"),i.onclick=()=>void this.plugin.createSkillTemplate();let o=s.createDiv({cls:"af-skills-grid"});if(a.skills.length===0){this.renderEmptyState(o,"puzzle","No skills yet","Create skills to give agents specialized abilities");return}for(let c of a.skills)this.renderSkillCard(o,c,a.agents)}renderSkillCard(e,s,a){let n=e.createDiv({cls:"af-skill-card"}),i=n.createDiv({cls:"af-skill-card-header"}),o=i.createDiv({cls:"af-skill-card-icon"});(0,w.setIcon)(o,this.getSkillIcon(s.name));let c=i.createEl("button",{cls:"af-btn-sm af-btn-xs"});P(c,"edit","af-btn-icon"),c.onclick=d=>{d.stopPropagation(),this.navigate("edit-skill",s.name)},n.createDiv({cls:"af-skill-card-name",text:s.name}),n.createDiv({cls:"af-skill-card-desc",text:s.description??"No description"});let l=a.filter(d=>d.skills.includes(s.name));if(l.length>0){let d=n.createDiv({cls:"af-skill-card-agents"});for(let h of l)d.createSpan({cls:"af-skill-card-agent-tag",text:h.name})}}renderChannelsPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Channels"}),n.createDiv({cls:"af-toolbar-spacer"});let i=n.createEl("button",{cls:"af-btn-sm primary"});P(i,"plus","af-btn-icon"),i.appendText(" New Channel"),i.onclick=()=>this.navigate("create-channel");let o=s.createDiv({cls:"af-agents-grid"});if(a.channels.length===0){this.renderEmptyState(o,"radio","No channels configured","Connect an agent to Slack or another chat platform");return}for(let c of a.channels)this.renderChannelCard(o,c,a.validationIssues)}async renderWikiKeepersPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Wiki Keepers"}),n.createDiv({cls:"af-toolbar-spacer"});let i=a.agents.filter(c=>c.wikiKeeper!==void 0);if(i.length===0){this.renderEmptyState(s,"library","No Wiki Keepers yet","Open Settings \u2192 Agent Fleet \u2192 Wiki Keepers \u2192 + Add to create one.");return}let o=s.createDiv({cls:"af-wk-list"});o.style.display="flex",o.style.flexDirection="column",o.style.gap="16px";for(let c of i)await this.renderWikiKeeperCard(o,c)}async renderWikiKeeperCard(e,s){let a=s.wikiKeeper,n=e.createDiv({cls:"af-card"});n.style.padding="16px",n.style.border="1px solid var(--background-modifier-border)",n.style.borderRadius="8px";let i=n.createDiv();i.style.display="flex",i.style.alignItems="center",i.style.gap="12px",i.style.marginBottom="12px";let o=i.createDiv();o.style.flex="1",o.createEl("strong",{text:s.name});let c=a.scopeRoot||"(whole vault)";o.createEl("div",{text:`Scope: ${c} \xB7 topics: ${a.topicsRoot}/ \xB7 log: ${a.logPath}`,cls:"af-form-hint"});let l=i.createEl("button",{cls:"af-btn-sm"});l.appendText("Open log"),l.onclick=()=>{let y=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,k=this.plugin.app.vault.getAbstractFileByPath(y);k instanceof w.TFile?this.plugin.app.workspace.getLeaf().openFile(k):new w.Notice(`Log file not found: ${y}`)};let d=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,h=this.plugin.app.vault.getAbstractFileByPath(d),u=null;if(h instanceof w.TFile)try{let y=await this.plugin.app.vault.cachedRead(h);u=Vi(y)}catch{u=null}if(!u){n.createDiv({cls:"af-form-hint"}).setText("No lint report yet. Run wiki-lint manually or wait for the weekly task to fire.");return}let p=n.createDiv();if(p.style.display="flex",p.style.alignItems="baseline",p.style.gap="12px",p.style.marginBottom="8px",p.createEl("strong",{text:`Lint ${u.date}`}),p.createSpan({cls:"af-form-hint",text:`${u.summary.length} summary lines \xB7 ${u.autoApplied.length} auto-applied \xB7 ${u.needsReview.length} needs review`}),u.summary.length>0){let y=n.createEl("details");y.createEl("summary",{text:"Summary"});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.summary)k.createEl("li",{text:b})}if(u.autoApplied.length>0){let y=n.createEl("details");y.createEl("summary",{text:`Auto-applied (${u.autoApplied.length})`});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.autoApplied)k.createEl("li",{text:b})}if(u.refreshChained.length>0){let y=n.createEl("details");y.createEl("summary",{text:`Refresh chained (${u.refreshChained.length})`});let k=y.createEl("ul");k.style.marginTop="4px";for(let b of u.refreshChained)k.createEl("li",{text:b})}let m=n.createDiv();if(m.style.marginTop="12px",m.createEl("strong",{text:`Needs review (${u.needsReview.length})`}),u.needsReview.length===0){m.createDiv({cls:"af-form-hint",text:"All clear. Nothing requires manual review from this lint pass."});return}let f=m.createDiv();f.style.display="flex",f.style.flexDirection="column",f.style.gap="6px",f.style.marginTop="8px";for(let y of u.needsReview){let k=f.createDiv();k.style.display="flex",k.style.alignItems="flex-start",k.style.gap="8px",k.style.padding="8px 10px",k.style.background="var(--background-secondary)",k.style.borderRadius="4px",k.style.fontSize="13px";let b=k.createDiv();b.style.flex="1",b.setText(y);let v=k.createEl("button",{cls:"af-btn-sm",text:"Dismiss"});v.title="Hide this item from the dashboard until the next lint pass (does not modify log.md).",v.onclick=()=>{k.remove()}}}renderChannelCard(e,s,a){let n=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",i=Ki(n),o=s.enabled&&n!=="disabled"?"af-agent-card":"af-agent-card disabled",c=e.createDiv({cls:o});c.style.cursor="default";let l=c.createDiv({cls:"af-agent-card-header"}),d=l.createDiv({cls:`af-agent-card-avatar ${i}`});(0,w.setIcon)(d,"radio");let h=l.createDiv({cls:"af-agent-card-titleblock"});h.createDiv({cls:"af-agent-card-name",text:s.name}),h.createDiv({cls:"af-agent-card-desc",text:`Default: ${s.defaultAgent}`});let u=l.createSpan({cls:`af-pill ${pl(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let T=c.createDiv({cls:"af-agent-card-skills"});for(let _ of s.allowedAgents){let R=T.createSpan({cls:"af-skill-tag",text:_});_===s.defaultAgent&&(R.style.fontWeight="700")}}let p=c.createDiv({cls:"af-agent-card-stats"}),m=this.plugin.channelManager?.getSessionCount(s.name)??0,f=this.plugin.channelManager?.getMetrics(s.name),y=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,y,"Agents"),this.renderAgentStat(p,String(m),"Sessions"),this.renderAgentStat(p,String(f?.messagesReceived??0),"In"),this.renderAgentStat(p,String(f?.messagesSent??0),"Out");let k=c.createDiv({cls:"af-agent-card-footer"}),b=[s.type];s.enabled||b.push("disabled"),s.allowedUsers.length>0?b.push(`${s.allowedUsers.length} user(s)`):b.push("allowlist empty"),k.createSpan({cls:"af-agent-card-meta",text:b.join(" \xB7 ")});let g=k.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});P(g,"edit","af-btn-icon"),g.appendText(" Edit"),g.onclick=T=>{T.stopPropagation(),this.navigate("edit-channel",s.name)};let x=a.filter(T=>T.path===s.filePath);if(x.length>0){let T=c.createDiv({cls:"af-channel-issues"});for(let _ of x)T.createDiv({cls:"af-channel-issue-row",text:_.message})}}renderCreateChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.channelCredentials.list(),i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"plus");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:"Create New Channel"}),l.createDiv({cls:"af-detail-header-desc",text:"Connect an external chat transport to an agent"}),i.createDiv({cls:"af-detail-header-actions"});let d={name:"",type:"slack",defaultAgent:a.agents[0]?.name??"",allowedAgents:[],credentialRef:n[0]?.ref??"",allowedUsers:"",perUserSessions:!0,channelContext:"",enabled:!0,tags:"",body:"",transportJson:""},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",H=>{d.name=H});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Type"});let y=f.createEl("select",{cls:"af-form-select"});y.createEl("option",{text:"slack",attr:{value:"slack"}}),y.createEl("option",{text:"telegram",attr:{value:"telegram"}}),y.addEventListener("change",()=>{d.type=y.value});let k=u.createDiv({cls:"af-form-row"}),b=k.createDiv({cls:"af-form-label"});b.setText("Credential"),this.addTooltip(b,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let v=k.createEl("select",{cls:"af-form-select"});n.length===0&&v.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let H of n)v.createEl("option",{text:`${H.ref} (${H.entry.type})`,attr:{value:H.ref}});v.addEventListener("change",()=>{d.credentialRef=v.value});let g=u.createDiv({cls:"af-form-row af-form-row-toggle"});g.createDiv({cls:"af-form-label",text:"Enabled"});let x=g.createDiv({cls:"af-agent-card-toggle on"});x.onclick=()=>{let H=x.hasClass("on");x.toggleClass("on",!H),d.enabled=!H};let T=h.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"bot"),_.createSpan({text:"Agent Routing"});let A=T.createDiv({cls:"af-form-row"}),S=A.createDiv({cls:"af-form-label"});S.setText("Default agent"),this.addTooltip(S,"Used when no @agent-name prefix is given in a message");let L=A.createEl("select",{cls:"af-form-select"});for(let H of a.agents)L.createEl("option",{text:H.name,attr:{value:H.name}});L.addEventListener("change",()=>{d.defaultAgent=L.value});let E=T.createDiv({cls:"af-form-row"}),B=E.createDiv({cls:"af-form-label"});B.setText("Allowed agents"),this.addTooltip(B,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let I=E.createDiv({cls:"af-form-checkboxes"});for(let H of a.agents){let we=I.createEl("label",{cls:"af-form-checkbox-label"}),ke=we.createEl("input",{attr:{type:"checkbox",value:H.name}});we.appendText(` ${H.name}`),ke.addEventListener("change",()=>{ke.checked?d.allowedAgents.includes(H.name)||d.allowedAgents.push(H.name):d.allowedAgents=d.allowedAgents.filter(ge=>ge!==H.name)})}let q=T.createDiv({cls:"af-form-row af-form-row-toggle"}),F=q.createDiv({cls:"af-form-label"});F.setText("Per-user sessions"),this.addTooltip(F,"Each external user gets their own isolated Claude session");let $=q.createDiv({cls:"af-agent-card-toggle on"});$.onclick=()=>{let H=$.hasClass("on");$.toggleClass("on",!H),d.perUserSessions=!H};let X=h.createDiv({cls:"af-create-section"}),z=X.createDiv({cls:"af-create-section-header"}),ie=z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ie,"shield-check"),z.createSpan({text:"Access Control"});let te=X.createDiv({cls:"af-form-label"});te.setText("Allowed users"),this.addTooltip(te,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let Y=X.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1 +U0BXYZ12345`,rows:"4"}});Y.addEventListener("input",()=>{d.allowedUsers=Y.value});let W=h.createDiv({cls:"af-create-section"}),Z=W.createDiv({cls:"af-create-section-header"}),de=Z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(de,"message-square");let re=Z.createSpan({text:"Channel Context"});this.addTooltip(re,"Extra instructions appended to the agent's system prompt when reached through this channel");let he=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});he.addEventListener("input",()=>{d.channelContext=he.value}),this.createFormField(u,"Tags","ops, internal","Comma-separated metadata",H=>{d.tags=H});let Le=h.createDiv({cls:"af-create-section"}),Ae=Le.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ne,"settings");let He=Ae.createSpan({text:"Advanced"});this.addTooltip(He,"Markdown body (shown in the channel detail page) and transport-specific overrides");let Ee=Le.createDiv({cls:"af-form-label"});Ee.setText("Body (markdown)"),this.addTooltip(Ee,"Free-form notes for this channel. Shown in the channel detail page; not sent to the agent.");let Ce=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});Ce.addEventListener("input",()=>{d.body=Ce.value});let Pe=Le.createDiv({cls:"af-form-label"});Pe.setText("Transport config (JSON)"),this.addTooltip(Pe,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode, telegram webhook settings). Leave blank for defaults.");let Be=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{ "socket_mode": true -}`,rows:"4"}});Be.addEventListener("input",()=>{d.transportJson=Be.value});let qe=s.createDiv({cls:"af-create-footer"}),G=qe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});G.onclick=()=>this.navigate("channels");let se=qe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(se,"plus","af-btn-icon"),se.appendText(" Create Channel"),se.onclick=async()=>{let H=d.name.trim();if(!H){new w.Notice("Channel name is required.");return}if(!d.credentialRef){new w.Notice("Select a credential.");return}let we;if(d.transportJson.trim())try{let Q=JSON.parse(d.transportJson);if(Q&&typeof Q=="object"&&!Array.isArray(Q))we=Q;else{new w.Notice("Transport config must be a JSON object.");return}}catch(Q){new w.Notice(`Transport JSON is invalid: ${Q instanceof Error?Q.message:String(Q)}`);return}let ke=Q=>Q.split(/[\n,]+/).map(P=>P.trim()).filter(Boolean),ge=Q=>Q.split(",").map(P=>P.trim()).filter(Boolean),Me={name:me(H),type:d.type,default_agent:d.defaultAgent,allowed_agents:d.allowedAgents.length>0?d.allowedAgents:void 0,enabled:d.enabled,credential_ref:d.credentialRef,allowed_users:ke(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0,tags:ge(d.tags).length>0?ge(d.tags):void 0,transport:we};try{let Q=me(H),P=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),Q);await this.plugin.app.vault.create(P,J(Me,d.body.trim())),new w.Notice(`Channel "${Q}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",Q)}catch(Q){let P=Q instanceof Error?Q.message:String(Q);new w.Notice(`Failed to create channel: ${P}`)}}}renderEditChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"radio","No channel selected","");return}let n=this.plugin.runtime.getSnapshot().channels.find(P=>P.name===a);if(!n){this.renderEmptyState(s,"radio","Channel not found",`Channel "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=this.plugin.channelCredentials.list(),c=this.plugin.channelManager?.getChannelStatus(n.name)??"disabled",l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:`af-agent-card-avatar ${Ki(c)}`});(0,w.setIcon)(h,"radio");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:`Edit Channel: ${n.name}`}),u.createDiv({cls:"af-detail-header-desc",text:`Status: ${c}`}),l.createDiv({cls:"af-detail-header-actions"});let p={type:n.type,defaultAgent:n.defaultAgent,allowedAgents:[...n.allowedAgents],credentialRef:n.credentialRef,allowedUsers:n.allowedUsers.join(` -`),perUserSessions:n.perUserSessions,channelContext:n.channelContext,enabled:n.enabled,tags:n.tags.join(", "),body:n.body,transportJson:Object.keys(n.transport).length>0?JSON.stringify(n.transport,null,2):""},f=s.createDiv({cls:"af-create-form"}),m=f.createDiv({cls:"af-create-section"}),y=m.createDiv({cls:"af-create-section-header"}),k=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"radio"),y.createSpan({text:"Channel Details"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Name"});let v=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});v.style.opacity="0.6";let g=m.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Type"});let x=g.createEl("select",{cls:"af-form-select"});for(let P of["slack","telegram"]){let ee=x.createEl("option",{text:P,attr:{value:P}});P===n.type&&(ee.selected=!0)}x.addEventListener("change",()=>{p.type=x.value});let T=m.createDiv({cls:"af-form-row"}),_=T.createDiv({cls:"af-form-label"});_.setText("Credential"),this.addTooltip(_,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let R=T.createEl("select",{cls:"af-form-select"});o.length===0&&R.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let P of o){let ee=R.createEl("option",{text:`${P.ref} (${P.entry.type})`,attr:{value:P.ref}});P.ref===n.credentialRef&&(ee.selected=!0)}R.addEventListener("change",()=>{p.credentialRef=R.value});let A=m.createDiv({cls:"af-form-row af-form-row-toggle"});A.createDiv({cls:"af-form-label",text:"Enabled"});let S=A.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});S.onclick=()=>{let P=S.hasClass("on");S.toggleClass("on",!P),p.enabled=!P};let L=f.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),B=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(B,"bot"),E.createSpan({text:"Agent Routing"});let I=L.createDiv({cls:"af-form-row"}),q=I.createDiv({cls:"af-form-label"});q.setText("Default agent"),this.addTooltip(q,"Used when no @agent-name prefix is given in a message");let F=I.createEl("select",{cls:"af-form-select"});for(let P of i.agents){let ee=F.createEl("option",{text:P.name,attr:{value:P.name}});P.name===n.defaultAgent&&(ee.selected=!0)}F.addEventListener("change",()=>{p.defaultAgent=F.value});let $=L.createDiv({cls:"af-form-row"}),X=$.createDiv({cls:"af-form-label"});X.setText("Allowed agents"),this.addTooltip(X,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let z=$.createDiv({cls:"af-form-checkboxes"});for(let P of i.agents){let ee=z.createEl("label",{cls:"af-form-checkbox-label"}),oe=ee.createEl("input",{attr:{type:"checkbox",value:P.name}});n.allowedAgents.includes(P.name)&&(oe.checked=!0),ee.appendText(` ${P.name}`),oe.addEventListener("change",()=>{oe.checked?p.allowedAgents.includes(P.name)||p.allowedAgents.push(P.name):p.allowedAgents=p.allowedAgents.filter(ue=>ue!==P.name)})}let ie=L.createDiv({cls:"af-form-row af-form-row-toggle"}),te=ie.createDiv({cls:"af-form-label"});te.setText("Per-user sessions"),this.addTooltip(te,"Each external user gets their own isolated Claude session");let V=ie.createDiv({cls:`af-agent-card-toggle${n.perUserSessions?" on":""}`});V.onclick=()=>{let P=V.hasClass("on");V.toggleClass("on",!P),p.perUserSessions=!P};let W=f.createDiv({cls:"af-create-section"}),Z=W.createDiv({cls:"af-create-section-header"}),de=Z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(de,"shield-check"),Z.createSpan({text:"Access Control"});let re=W.createDiv({cls:"af-form-label"});re.setText("Allowed users"),this.addTooltip(re,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let he=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1 +}`,rows:"4"}});Be.addEventListener("input",()=>{d.transportJson=Be.value});let qe=s.createDiv({cls:"af-create-footer"}),G=qe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});G.onclick=()=>this.navigate("channels");let se=qe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(se,"plus","af-btn-icon"),se.appendText(" Create Channel"),se.onclick=async()=>{let H=d.name.trim();if(!H){new w.Notice("Channel name is required.");return}if(!d.credentialRef){new w.Notice("Select a credential.");return}let we;if(d.transportJson.trim())try{let Q=JSON.parse(d.transportJson);if(Q&&typeof Q=="object"&&!Array.isArray(Q))we=Q;else{new w.Notice("Transport config must be a JSON object.");return}}catch(Q){new w.Notice(`Transport JSON is invalid: ${Q instanceof Error?Q.message:String(Q)}`);return}let ke=Q=>Q.split(/[\n,]+/).map(D=>D.trim()).filter(Boolean),ge=Q=>Q.split(",").map(D=>D.trim()).filter(Boolean),Me={name:fe(H),type:d.type,default_agent:d.defaultAgent,allowed_agents:d.allowedAgents.length>0?d.allowedAgents:void 0,enabled:d.enabled,credential_ref:d.credentialRef,allowed_users:ke(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0,tags:ge(d.tags).length>0?ge(d.tags):void 0,transport:we};try{let Q=fe(H),D=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),Q);await this.plugin.app.vault.create(D,J(Me,d.body.trim())),new w.Notice(`Channel "${Q}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",Q)}catch(Q){let D=Q instanceof Error?Q.message:String(Q);new w.Notice(`Failed to create channel: ${D}`)}}}renderEditChannelPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"radio","No channel selected","");return}let n=this.plugin.runtime.getSnapshot().channels.find(D=>D.name===a);if(!n){this.renderEmptyState(s,"radio","Channel not found",`Channel "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=this.plugin.channelCredentials.list(),c=this.plugin.channelManager?.getChannelStatus(n.name)??"disabled",l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:`af-agent-card-avatar ${Ki(c)}`});(0,w.setIcon)(h,"radio");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:`Edit Channel: ${n.name}`}),u.createDiv({cls:"af-detail-header-desc",text:`Status: ${c}`}),l.createDiv({cls:"af-detail-header-actions"});let p={type:n.type,defaultAgent:n.defaultAgent,allowedAgents:[...n.allowedAgents],credentialRef:n.credentialRef,allowedUsers:n.allowedUsers.join(` +`),perUserSessions:n.perUserSessions,channelContext:n.channelContext,enabled:n.enabled,tags:n.tags.join(", "),body:n.body,transportJson:Object.keys(n.transport).length>0?JSON.stringify(n.transport,null,2):""},m=s.createDiv({cls:"af-create-form"}),f=m.createDiv({cls:"af-create-section"}),y=f.createDiv({cls:"af-create-section-header"}),k=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"radio"),y.createSpan({text:"Channel Details"});let b=f.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Name"});let v=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});v.style.opacity="0.6";let g=f.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Type"});let x=g.createEl("select",{cls:"af-form-select"});for(let D of["slack","telegram"]){let ee=x.createEl("option",{text:D,attr:{value:D}});D===n.type&&(ee.selected=!0)}x.addEventListener("change",()=>{p.type=x.value});let T=f.createDiv({cls:"af-form-row"}),_=T.createDiv({cls:"af-form-label"});_.setText("Credential"),this.addTooltip(_,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let R=T.createEl("select",{cls:"af-form-select"});o.length===0&&R.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let D of o){let ee=R.createEl("option",{text:`${D.ref} (${D.entry.type})`,attr:{value:D.ref}});D.ref===n.credentialRef&&(ee.selected=!0)}R.addEventListener("change",()=>{p.credentialRef=R.value});let A=f.createDiv({cls:"af-form-row af-form-row-toggle"});A.createDiv({cls:"af-form-label",text:"Enabled"});let S=A.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});S.onclick=()=>{let D=S.hasClass("on");S.toggleClass("on",!D),p.enabled=!D};let L=m.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),B=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(B,"bot"),E.createSpan({text:"Agent Routing"});let I=L.createDiv({cls:"af-form-row"}),q=I.createDiv({cls:"af-form-label"});q.setText("Default agent"),this.addTooltip(q,"Used when no @agent-name prefix is given in a message");let F=I.createEl("select",{cls:"af-form-select"});for(let D of i.agents){let ee=F.createEl("option",{text:D.name,attr:{value:D.name}});D.name===n.defaultAgent&&(ee.selected=!0)}F.addEventListener("change",()=>{p.defaultAgent=F.value});let $=L.createDiv({cls:"af-form-row"}),X=$.createDiv({cls:"af-form-label"});X.setText("Allowed agents"),this.addTooltip(X,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let z=$.createDiv({cls:"af-form-checkboxes"});for(let D of i.agents){let ee=z.createEl("label",{cls:"af-form-checkbox-label"}),oe=ee.createEl("input",{attr:{type:"checkbox",value:D.name}});n.allowedAgents.includes(D.name)&&(oe.checked=!0),ee.appendText(` ${D.name}`),oe.addEventListener("change",()=>{oe.checked?p.allowedAgents.includes(D.name)||p.allowedAgents.push(D.name):p.allowedAgents=p.allowedAgents.filter(ue=>ue!==D.name)})}let ie=L.createDiv({cls:"af-form-row af-form-row-toggle"}),te=ie.createDiv({cls:"af-form-label"});te.setText("Per-user sessions"),this.addTooltip(te,"Each external user gets their own isolated Claude session");let Y=ie.createDiv({cls:`af-agent-card-toggle${n.perUserSessions?" on":""}`});Y.onclick=()=>{let D=Y.hasClass("on");Y.toggleClass("on",!D),p.perUserSessions=!D};let W=m.createDiv({cls:"af-create-section"}),Z=W.createDiv({cls:"af-create-section-header"}),de=Z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(de,"shield-check"),Z.createSpan({text:"Access Control"});let re=W.createDiv({cls:"af-form-label"});re.setText("Allowed users"),this.addTooltip(re,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let he=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1 U0BXYZ12345`,rows:"4"}});he.value=n.allowedUsers.join(` -`),he.addEventListener("input",()=>{p.allowedUsers=he.value});let Le=f.createDiv({cls:"af-create-section"}),Ae=Le.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ne,"message-square");let He=Ae.createSpan({text:"Channel Context"});this.addTooltip(He,"Extra instructions appended to the agent's system prompt when reached through this channel");let Ee=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});Ee.value=n.channelContext,Ee.addEventListener("input",()=>{p.channelContext=Ee.value}),this.createFormField(m,"Tags","ops, internal","Comma-separated metadata",P=>{p.tags=P},n.tags.join(", "));let Ce=f.createDiv({cls:"af-create-section"}),De=Ce.createDiv({cls:"af-create-section-header"}),Be=De.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Be,"settings");let qe=De.createSpan({text:"Advanced"});this.addTooltip(qe,"Markdown body (shown in the channel detail page) and transport-specific overrides"),Ce.createDiv({cls:"af-form-label"}).setText("Body (markdown)");let se=Ce.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});se.value=n.body,se.addEventListener("input",()=>{p.body=se.value});let H=Ce.createDiv({cls:"af-form-label"});H.setText("Transport config (JSON)"),this.addTooltip(H,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode). Leave blank for defaults.");let we=Ce.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{ +`),he.addEventListener("input",()=>{p.allowedUsers=he.value});let Le=m.createDiv({cls:"af-create-section"}),Ae=Le.createDiv({cls:"af-create-section-header"}),Ne=Ae.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ne,"message-square");let He=Ae.createSpan({text:"Channel Context"});this.addTooltip(He,"Extra instructions appended to the agent's system prompt when reached through this channel");let Ee=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});Ee.value=n.channelContext,Ee.addEventListener("input",()=>{p.channelContext=Ee.value}),this.createFormField(f,"Tags","ops, internal","Comma-separated metadata",D=>{p.tags=D},n.tags.join(", "));let Ce=m.createDiv({cls:"af-create-section"}),Pe=Ce.createDiv({cls:"af-create-section-header"}),Be=Pe.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Be,"settings");let qe=Pe.createSpan({text:"Advanced"});this.addTooltip(qe,"Markdown body (shown in the channel detail page) and transport-specific overrides"),Ce.createDiv({cls:"af-form-label"}).setText("Body (markdown)");let se=Ce.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});se.value=n.body,se.addEventListener("input",()=>{p.body=se.value});let H=Ce.createDiv({cls:"af-form-label"});H.setText("Transport config (JSON)"),this.addTooltip(H,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode). Leave blank for defaults.");let we=Ce.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{ "socket_mode": true -}`,rows:"4"}});we.value=p.transportJson,we.addEventListener("input",()=>{p.transportJson=we.value});let ke=s.createDiv({cls:"af-create-footer"}),ge=ke.createEl("button",{cls:"af-btn-sm danger"});D(ge,"trash-2","af-btn-icon"),ge.appendText(" Delete"),ge.onclick=async()=>{await this.plugin.repository.deleteChannel(n.name),new w.Notice(`Channel "${n.name}" deleted.`),await new Promise(P=>setTimeout(P,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},ke.createDiv({cls:"af-toolbar-spacer"});let Me=ke.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("channels");let Q=ke.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let P=ue=>ue.split(/[\n,]+/).map(et=>et.trim()).filter(Boolean),ee=ue=>ue.split(",").map(et=>et.trim()).filter(Boolean),oe;if(p.transportJson.trim())try{let ue=JSON.parse(p.transportJson);if(ue&&typeof ue=="object"&&!Array.isArray(ue))oe=ue;else{new w.Notice("Transport config must be a JSON object.");return}}catch(ue){new w.Notice(`Transport JSON is invalid: ${ue instanceof Error?ue.message:String(ue)}`);return}else oe={};try{await this.plugin.repository.updateChannel(n.name,{type:p.type,default_agent:p.defaultAgent,allowed_agents:p.allowedAgents.length>0?p.allowedAgents:[],enabled:p.enabled,credential_ref:p.credentialRef,allowed_users:P(p.allowedUsers),per_user_sessions:p.perUserSessions,channel_context:p.channelContext.trim(),tags:ee(p.tags),body:p.body.trim(),transport:oe}),new w.Notice(`Channel "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(ue){let et=ue instanceof Error?ue.message:String(ue);new w.Notice(`Failed to update channel: ${et}`)}}}renderApprovalsPage(e){let s=e.createDiv({cls:"af-approvals-page"}),a=this.plugin.runtime.getRecentRuns(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Approvals"}),n.createDiv({cls:"af-toolbar-spacer"});let i=a.filter(c=>(c.approvals??[]).some(l=>l.status==="pending"));if(i.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(d,"alert-triangle"),d.appendText(` Pending (${i.length})`);let h=c.createDiv({cls:"af-approvals-list"});for(let u of i)this.renderApprovalItem(h,u,!0)}else{let c=s.createDiv({cls:"af-section-card"});this.renderEmptyState(c,"shield-check","No pending approvals","All clear!")}let o=a.filter(c=>(c.approvals??[]).some(l=>l.status!=="pending"));if(o.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(d,"check-circle-2"),d.appendText(" History");let h=c.createDiv({cls:"af-approvals-list"});for(let u of o.slice(0,20))this.renderApprovalItem(h,u,!1)}}renderApprovalItem(e,s,a){for(let n of s.approvals??[]){if(a&&n.status!=="pending"||!a&&n.status==="pending")continue;let i=e.createDiv({cls:"af-approval-item"}),o=i.createDiv({cls:"af-approval-item-icon"});n.status==="pending"?((0,w.setIcon)(o,"shield-check"),o.addClass("pending")):n.status==="approved"?((0,w.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,w.setIcon)(o,"x-circle"),o.addClass("rejected"));let c=i.createDiv({cls:"af-approval-item-body"});if(c.createDiv({cls:"af-approval-item-title",text:`${s.agent} \u2192 ${n.tool}`}),c.createDiv({cls:"af-approval-item-meta",text:`Task: ${s.task} \xB7 ${n.command??"no command"} \xB7 ${this.formatStarted(s.started)}`}),n.reason&&c.createDiv({cls:"af-approval-item-reason",text:`Reason: ${n.reason}`}),a&&n.status==="pending"){let l=i.createDiv({cls:"af-approval-item-actions"}),d=l.createEl("button",{cls:"af-btn-approve"});D(d,"check-circle-2","af-btn-icon"),d.appendText(" Approve"),d.onclick=()=>void this.plugin.runtime.resolveApproval(s,n.tool,"approved").then(()=>this.render());let h=l.createEl("button",{cls:"af-btn-reject"});D(h,"x-circle","af-btn-icon"),h.appendText(" Reject"),h.onclick=()=>void this.plugin.runtime.resolveApproval(s,n.tool,"rejected").then(()=>this.render())}}}renderTaskDetailPage(e){let s=e.createDiv({cls:"af-task-detail-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"circle-dot","No task selected","");return}let n=this.plugin.runtime.getSnapshot().tasks.find(E=>E.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=this.plugin.runtime.getRecentRuns().filter(E=>E.task===a),c=i.agents.find(E=>E.name===n.agent),l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(h,"circle-dot");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:n.taskId}),u.createDiv({cls:"af-detail-header-desc",text:`Agent: ${n.agent}`});let p=l.createDiv({cls:"af-detail-header-actions"}),f=p.createEl("button",{cls:"af-btn-sm"});D(f,"edit","af-btn-icon"),f.appendText(" Edit"),f.onclick=()=>this.navigate("edit-task",n.taskId);let m=p.createEl("button",{cls:"af-btn-sm primary"});D(m,"play","af-btn-icon"),m.appendText(" Run Now"),m.onclick=()=>void this.plugin.runtime.runTaskNow(n);let y=s.createDiv({cls:"af-section-card"}),b=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(b,"file-text"),b.appendText(" Details");let v=y.createDiv({cls:"af-config-form"});this.renderConfigRow(v,"Agent",n.agent),this.renderConfigRow(v,"Priority",n.priority.charAt(0).toUpperCase()+n.priority.slice(1)),this.renderConfigRow(v,"Status",n.enabled?"Enabled":"Disabled");let g=n.schedule?this.humanizeCron(n.schedule):n.runAt??"Manual (run on demand)";this.renderConfigRow(v,"Schedule",g),n.schedule&&this.renderConfigRow(v,"Catch up if missed",n.catchUp?"Yes":"No"),this.renderConfigRow(v,"Created",n.created),this.renderConfigRow(v,"Runs",String(n.runCount)),n.lastRun&&this.renderConfigRow(v,"Last Run",this.formatStarted(n.lastRun));let x=s.createDiv({cls:"af-section-card"}),_=x.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(_,"message-square"),_.appendText(" Instructions"),x.createDiv({cls:"af-output-block",text:n.body||"(empty)"});let R=s.createDiv({cls:"af-section-card"}),S=R.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});D(S,"scroll-text"),S.appendText(" Recent Runs");let L=R.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(L,"scroll-text","No runs yet","");else for(let E of o.slice(0,10))this.renderTimelineItem(L,E)}handleSearch(e,s){if(s.querySelector(".af-search-results")?.remove(),e.length<2)return;let a=e.toLowerCase(),n=this.plugin.runtime.getSnapshot(),i=this.plugin.runtime.getRecentRuns(),o=[];for(let l of n.agents)(l.name.toLowerCase().includes(a)||(l.description?.toLowerCase().includes(a)??!1))&&o.push({label:`Agent: ${l.name}`,icon:"bot",action:()=>this.navigate("agent-detail",l.name)});for(let l of n.tasks)(l.taskId.toLowerCase().includes(a)||l.body.toLowerCase().includes(a))&&o.push({label:`Task: ${l.taskId}`,icon:"circle-dot",action:()=>this.navigate("task-detail",l.taskId)});for(let l of n.skills)l.name.toLowerCase().includes(a)&&o.push({label:`Skill: ${l.name}`,icon:"puzzle",action:()=>this.navigate("edit-skill",l.name)});for(let l of i.slice(0,20))l.output.toLowerCase().includes(a)&&o.push({label:`Run: ${l.agent} / ${l.task}`,icon:"scroll-text",action:()=>this.openSlideover(l)});if(o.length===0)return;let c=s.createDiv({cls:"af-search-results"});for(let l of o.slice(0,10)){let d=c.createDiv({cls:"af-search-result-item"});D(d,l.icon,"af-search-result-icon"),d.createSpan({text:l.label}),d.onclick=()=>{c.remove(),l.action()}}}openChatSlideover(e){this.plugin.openChatView(e.name)}openSlideover(e){this.contentEl.querySelector(".af-slideover-overlay")?.remove();let s=this.contentEl.createDiv({cls:"af-slideover-overlay"}),a=s.createDiv({cls:"af-slideover"}),n=a.createDiv({cls:"af-slideover-header"});n.createDiv({cls:"af-slideover-title",text:"Run Details"});let i=n.createEl("button",{cls:"clickable-icon"});(0,w.setIcon)(i,"cross"),i.onclick=()=>s.remove();let o=a.createDiv({cls:"af-slideover-body"}),c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"METADATA"}),this.renderDetailRow(c,"Run ID",e.runId.slice(0,8)),this.renderDetailRow(c,"Agent",e.agent),this.renderDetailRow(c,"Task",e.task);let l=c.createDiv({cls:"af-detail-row"});l.createSpan({cls:"af-detail-label",text:"Status"});let h=l.createSpan({cls:"af-detail-value"}).createSpan({cls:`af-status-badge ${this.statusToBadgeClass(e.status)}`}),u=h.createSpan();(0,w.setIcon)(u,this.statusToIconName(e.status)),h.appendText(` ${this.statusToBadgeText(e.status)}`),this.renderDetailRow(c,"Started",e.started),this.renderDetailRow(c,"Duration",this.formatDuration(e.durationSeconds)),this.renderDetailRow(c,"Tokens",e.tokensUsed?e.tokensUsed.toLocaleString():"\u2014");{let g={task:"from task override",agent:"from agent",settings:"from settings default","cli-default":"Claude CLI default"},x=e.modelSource?` (${g[e.modelSource]??e.modelSource})`:"",T=e.concreteModel&&e.concreteModel!==e.model?` \u2192 ${e.concreteModel}`:"";this.renderDetailRow(c,"Model",`${e.model}${T}${x}`)}let p=5e4,f=g=>g.length>p?g.slice(0,p)+` +}`,rows:"4"}});we.value=p.transportJson,we.addEventListener("input",()=>{p.transportJson=we.value});let ke=s.createDiv({cls:"af-create-footer"}),ge=ke.createEl("button",{cls:"af-btn-sm danger"});P(ge,"trash-2","af-btn-icon"),ge.appendText(" Delete"),ge.onclick=async()=>{await this.plugin.repository.deleteChannel(n.name),new w.Notice(`Channel "${n.name}" deleted.`),await new Promise(D=>setTimeout(D,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},ke.createDiv({cls:"af-toolbar-spacer"});let Me=ke.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("channels");let Q=ke.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let D=ue=>ue.split(/[\n,]+/).map(et=>et.trim()).filter(Boolean),ee=ue=>ue.split(",").map(et=>et.trim()).filter(Boolean),oe;if(p.transportJson.trim())try{let ue=JSON.parse(p.transportJson);if(ue&&typeof ue=="object"&&!Array.isArray(ue))oe=ue;else{new w.Notice("Transport config must be a JSON object.");return}}catch(ue){new w.Notice(`Transport JSON is invalid: ${ue instanceof Error?ue.message:String(ue)}`);return}else oe={};try{await this.plugin.repository.updateChannel(n.name,{type:p.type,default_agent:p.defaultAgent,allowed_agents:p.allowedAgents.length>0?p.allowedAgents:[],enabled:p.enabled,credential_ref:p.credentialRef,allowed_users:D(p.allowedUsers),per_user_sessions:p.perUserSessions,channel_context:p.channelContext.trim(),tags:ee(p.tags),body:p.body.trim(),transport:oe}),new w.Notice(`Channel "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(ue){let et=ue instanceof Error?ue.message:String(ue);new w.Notice(`Failed to update channel: ${et}`)}}}renderApprovalsPage(e){let s=e.createDiv({cls:"af-approvals-page"}),a=this.plugin.runtime.getRecentRuns(),n=s.createDiv({cls:"af-agents-toolbar"});n.createDiv({cls:"af-page-title",text:"Approvals"}),n.createDiv({cls:"af-toolbar-spacer"});let i=a.filter(c=>(c.approvals??[]).some(l=>l.status==="pending"));if(i.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(d,"alert-triangle"),d.appendText(` Pending (${i.length})`);let h=c.createDiv({cls:"af-approvals-list"});for(let u of i)this.renderApprovalItem(h,u,!0)}else{let c=s.createDiv({cls:"af-section-card"});this.renderEmptyState(c,"shield-check","No pending approvals","All clear!")}let o=a.filter(c=>(c.approvals??[]).some(l=>l.status!=="pending"));if(o.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(d,"check-circle-2"),d.appendText(" History");let h=c.createDiv({cls:"af-approvals-list"});for(let u of o.slice(0,20))this.renderApprovalItem(h,u,!1)}}renderApprovalItem(e,s,a){for(let n of s.approvals??[]){if(a&&n.status!=="pending"||!a&&n.status==="pending")continue;let i=e.createDiv({cls:"af-approval-item"}),o=i.createDiv({cls:"af-approval-item-icon"});n.status==="pending"?((0,w.setIcon)(o,"shield-check"),o.addClass("pending")):n.status==="approved"?((0,w.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,w.setIcon)(o,"x-circle"),o.addClass("rejected"));let c=i.createDiv({cls:"af-approval-item-body"});if(c.createDiv({cls:"af-approval-item-title",text:`${s.agent} \u2192 ${n.tool}`}),c.createDiv({cls:"af-approval-item-meta",text:`Task: ${s.task} \xB7 ${n.command??"no command"} \xB7 ${this.formatStarted(s.started)}`}),n.reason&&c.createDiv({cls:"af-approval-item-reason",text:`Reason: ${n.reason}`}),a&&n.status==="pending"){let l=i.createDiv({cls:"af-approval-item-actions"}),d=l.createEl("button",{cls:"af-btn-approve"});P(d,"check-circle-2","af-btn-icon"),d.appendText(" Approve"),d.onclick=()=>void this.plugin.runtime.resolveApproval(s,n.tool,"approved").then(()=>this.render());let h=l.createEl("button",{cls:"af-btn-reject"});P(h,"x-circle","af-btn-icon"),h.appendText(" Reject"),h.onclick=()=>void this.plugin.runtime.resolveApproval(s,n.tool,"rejected").then(()=>this.render())}}}renderTaskDetailPage(e){let s=e.createDiv({cls:"af-task-detail-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"circle-dot","No task selected","");return}let n=this.plugin.runtime.getSnapshot().tasks.find(E=>E.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=this.plugin.runtime.getRecentRuns().filter(E=>E.task===a),c=i.agents.find(E=>E.name===n.agent),l=s.createDiv({cls:"af-detail-header"}),d=l.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(h,"circle-dot");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:n.taskId}),u.createDiv({cls:"af-detail-header-desc",text:`Agent: ${n.agent}`});let p=l.createDiv({cls:"af-detail-header-actions"}),m=p.createEl("button",{cls:"af-btn-sm"});P(m,"edit","af-btn-icon"),m.appendText(" Edit"),m.onclick=()=>this.navigate("edit-task",n.taskId);let f=p.createEl("button",{cls:"af-btn-sm primary"});P(f,"play","af-btn-icon"),f.appendText(" Run Now"),f.onclick=()=>void this.plugin.runtime.runTaskNow(n);let y=s.createDiv({cls:"af-section-card"}),b=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(b,"file-text"),b.appendText(" Details");let v=y.createDiv({cls:"af-config-form"});this.renderConfigRow(v,"Agent",n.agent),this.renderConfigRow(v,"Priority",n.priority.charAt(0).toUpperCase()+n.priority.slice(1)),this.renderConfigRow(v,"Status",n.enabled?"Enabled":"Disabled");let g=n.schedule?this.humanizeCron(n.schedule):n.runAt??"Manual (run on demand)";this.renderConfigRow(v,"Schedule",g),n.schedule&&this.renderConfigRow(v,"Catch up if missed",n.catchUp?"Yes":"No"),this.renderConfigRow(v,"Created",n.created),this.renderConfigRow(v,"Runs",String(n.runCount)),n.lastRun&&this.renderConfigRow(v,"Last Run",this.formatStarted(n.lastRun));let x=s.createDiv({cls:"af-section-card"}),_=x.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(_,"message-square"),_.appendText(" Instructions"),x.createDiv({cls:"af-output-block",text:n.body||"(empty)"});let R=s.createDiv({cls:"af-section-card"}),S=R.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(S,"scroll-text"),S.appendText(" Recent Runs");let L=R.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(L,"scroll-text","No runs yet","");else for(let E of o.slice(0,10))this.renderTimelineItem(L,E)}handleSearch(e,s){if(s.querySelector(".af-search-results")?.remove(),e.length<2)return;let a=e.toLowerCase(),n=this.plugin.runtime.getSnapshot(),i=this.plugin.runtime.getRecentRuns(),o=[];for(let l of n.agents)(l.name.toLowerCase().includes(a)||(l.description?.toLowerCase().includes(a)??!1))&&o.push({label:`Agent: ${l.name}`,icon:"bot",action:()=>this.navigate("agent-detail",l.name)});for(let l of n.tasks)(l.taskId.toLowerCase().includes(a)||l.body.toLowerCase().includes(a))&&o.push({label:`Task: ${l.taskId}`,icon:"circle-dot",action:()=>this.navigate("task-detail",l.taskId)});for(let l of n.skills)l.name.toLowerCase().includes(a)&&o.push({label:`Skill: ${l.name}`,icon:"puzzle",action:()=>this.navigate("edit-skill",l.name)});for(let l of i.slice(0,20))l.output.toLowerCase().includes(a)&&o.push({label:`Run: ${l.agent} / ${l.task}`,icon:"scroll-text",action:()=>this.openSlideover(l)});if(o.length===0)return;let c=s.createDiv({cls:"af-search-results"});for(let l of o.slice(0,10)){let d=c.createDiv({cls:"af-search-result-item"});P(d,l.icon,"af-search-result-icon"),d.createSpan({text:l.label}),d.onclick=()=>{c.remove(),l.action()}}}openChatSlideover(e){this.plugin.openChatView(e.name)}openSlideover(e){this.contentEl.querySelector(".af-slideover-overlay")?.remove();let s=this.contentEl.createDiv({cls:"af-slideover-overlay"}),a=s.createDiv({cls:"af-slideover"}),n=a.createDiv({cls:"af-slideover-header"});n.createDiv({cls:"af-slideover-title",text:"Run Details"});let i=n.createEl("button",{cls:"clickable-icon"});(0,w.setIcon)(i,"cross"),i.onclick=()=>s.remove();let o=a.createDiv({cls:"af-slideover-body"}),c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"METADATA"}),this.renderDetailRow(c,"Run ID",e.runId.slice(0,8)),this.renderDetailRow(c,"Agent",e.agent),this.renderDetailRow(c,"Task",e.task);let l=c.createDiv({cls:"af-detail-row"});l.createSpan({cls:"af-detail-label",text:"Status"});let h=l.createSpan({cls:"af-detail-value"}).createSpan({cls:`af-status-badge ${this.statusToBadgeClass(e.status)}`}),u=h.createSpan();(0,w.setIcon)(u,this.statusToIconName(e.status)),h.appendText(` ${this.statusToBadgeText(e.status)}`),this.renderDetailRow(c,"Started",e.started),this.renderDetailRow(c,"Duration",this.formatDuration(e.durationSeconds)),this.renderDetailRow(c,"Tokens",e.tokensUsed?e.tokensUsed.toLocaleString():"\u2014");{let g={task:"from task override",agent:"from agent",settings:"from settings default","cli-default":"Claude CLI default"},x=e.modelSource?` (${g[e.modelSource]??e.modelSource})`:"",T=e.concreteModel&&e.concreteModel!==e.model?` \u2192 ${e.concreteModel}`:"";this.renderDetailRow(c,"Model",`${e.model}${T}${x}`)}let p=5e4,m=g=>g.length>p?g.slice(0,p)+` --- -*Truncated (${(g.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:g,m=!!(e.finalResult&&e.finalResult.trim()),y=e.output?.trim()??"",k=m&&y.length>0&&y!==e.finalResult.trim();if(m){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=g.createDiv({cls:"af-output-block af-compact-md"});if(w.MarkdownRenderer.render(this.app,f(e.finalResult),x,"",this.plugin),k){let T=g.createEl("details",{cls:"af-run-transcript"}),_=T.createEl("summary");(0,w.setIcon)(_.createSpan({cls:"af-run-transcript-icon"}),"file-text"),_.createSpan({text:"Show full transcript"}),_.createSpan({cls:"af-run-transcript-meta"}).setText(`${(y.length/1024).toFixed(1)} KB`);let A=T.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});w.MarkdownRenderer.render(this.app,f(y),A,"",this.plugin)}}else if(y){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=g.createDiv({cls:"af-output-block af-compact-md"});w.MarkdownRenderer.render(this.app,f(y),x,"",this.plugin)}if(e.toolsUsed.length>0){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"TOOLS USED"}),g.createDiv({cls:"af-output-block",text:e.toolsUsed.join(` -`)})}let b=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let g=b.createEl("button",{cls:"af-btn-sm"});D(g,"external-link","af-btn-icon"),g.appendText(" Open Run Note"),g.onclick=()=>void this.plugin.openPath(e.filePath)}let v=b.createEl("button",{cls:"af-btn-sm primary"});D(v,"refresh-cw","af-btn-icon"),v.appendText(" Re-run Task"),v.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=g=>{g.target===s&&s.remove()}}renderDetailRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderEmptyState(e,s,a,n){let i=e.createDiv({cls:"af-empty-state"}),o=i.createDiv({cls:"af-empty-icon"});(0,w.setIcon)(o,s),i.createDiv({cls:"af-empty-label",text:a}),n&&i.createDiv({cls:"af-empty-sublabel",text:n})}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}statusToTimelineClass(e){switch(e){case"success":return"success";case"failure":case"timeout":return"error";case"cancelled":return"warning";case"pending_approval":return"pending";default:return"running"}}statusToIconName(e){switch(e){case"success":return"check-circle-2";case"failure":return"x-circle";case"timeout":return"clock";case"pending_approval":return"shield-check";case"cancelled":return"square";default:return"loader-2"}}statusToBadgeClass(e){switch(e){case"success":return"success";case"failure":return"failure";case"timeout":return"timeout";case"pending_approval":return"pending";case"cancelled":return"cancelled";default:return"running"}}statusToBadgeText(e){switch(e){case"success":return"Success";case"failure":return"Failed";case"timeout":return"Timeout";case"pending_approval":return"Pending";case"cancelled":return"Cancelled";case"interrupted":return"Interrupted";default:return e}}formatDuration(e){if(e<60)return`${e}s`;let s=Math.floor(e/60),a=e%60;return a>0?`${s}m ${a}s`:`${s}m`}formatStarted(e){try{let s=new Date(e),a=new Date;if(s.toDateString()===a.toDateString())return s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});let n=new Date(a);return n.setDate(n.getDate()-1),s.toDateString()===n.toDateString()?`Yesterday ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:s.toLocaleDateString([],{month:"short",day:"numeric"})+` ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`}catch{return e}}formatNextRun(e){try{let s=new Date(e),a=new Date,n=s.getTime()-a.getTime();if(n<0)return"overdue";let i=Math.round(n/6e4);if(i<60)return`${i}m`;let o=Math.round(i/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let a=e.map(i=>i.nextRun).filter(Boolean).sort()[0];return a?`${e.find(i=>i.nextRun===a)?.agent??"unknown"} in ${this.formatNextRun(a)}`:"none"}getInitials(e){return e.split("-").map(s=>s[0]?.toUpperCase()??"").slice(0,2).join("")}renderAgentAvatar(e,s){let a=s.avatar?.trim();if(!a){(0,w.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,w.setIcon)(e,a):e.setText(a)}getSkillIcon(e){return e.includes("git")?"settings":e.includes("summarize")||e.includes("log")?"activity":e.includes("review")||e.includes("check")?"check-circle-2":e.includes("vault")||e.includes("note")?"file-text":"puzzle"}renderInlineSchedule(e,s){let a=this.parseCronComponents(s.schedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["daily","Daily"],["weekdays","Weekdays"],["weekly","Weekly"],["monthly","Monthly"]];for(let[g,x]of o){let T=i.createEl("option",{text:x,attr:{value:g}});g===a.freq&&(T.selected=!0)}let c=e.createDiv({cls:"af-form-row af-schedule-time-row"});c.createDiv({cls:"af-form-label",text:"Time"});let l=c.createDiv({cls:"af-schedule-time-selects"}),d=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=0;g<24;g++){let x=g>=12?"PM":"AM",T=g===0?12:g>12?g-12:g,_=d.createEl("option",{text:`${T} ${x}`,attr:{value:String(g)}});g===a.hour&&(_.selected=!0)}l.createSpan({cls:"af-schedule-colon",text:":"});let h=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=0;g<60;g+=5){let x=h.createEl("option",{text:String(g).padStart(2,"0"),attr:{value:String(g)}});g===a.minute&&(x.selected=!0)}let u=e.createDiv({cls:"af-form-row af-schedule-day-row"});u.createDiv({cls:"af-form-label",text:"Day"});let p=u.createDiv({cls:"af-schedule-day-buttons"}),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=new Set(a.days);for(let g=0;g<7;g++){let x=p.createEl("button",{cls:`af-schedule-day-btn${m.has(g)?" active":""}`,text:f[g]});x.onclick=()=>{m.has(g)?m.delete(g):m.add(g),x.toggleClass("active",m.has(g)),v()}}let y=e.createDiv({cls:"af-form-row af-schedule-dom-row"});y.createDiv({cls:"af-form-label",text:"Day of month"});let k=y.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=1;g<=28;g++){let x=k.createEl("option",{text:String(g),attr:{value:String(g)}});g===a.dayOfMonth&&(x.selected=!0)}let b=()=>{let g=i.value,x=["daily","weekdays","weekly","monthly"].includes(g),T=g==="weekly",_=g==="monthly";c.style.display=x?"":"none",u.style.display=T?"":"none",y.style.display=_?"":"none"},v=()=>{let g=i.value,x=d.value,T=h.value,_="";switch(g){case"every_5m":_="*/5 * * * *";break;case"every_15m":_="*/15 * * * *";break;case"every_30m":_="*/30 * * * *";break;case"every_hour":_="0 * * * *";break;case"every_2h":_="0 */2 * * *";break;case"daily":_=`${T} ${x} * * *`;break;case"weekdays":_=`${T} ${x} * * 1-5`;break;case"weekly":{let R=Array.from(m).sort().join(",")||"1";_=`${T} ${x} * * ${R}`;break}case"monthly":_=`${T} ${x} ${k.value} * *`;break}s.schedule=_,s.type="recurring"};i.addEventListener("change",()=>{b(),v()}),d.addEventListener("change",v),h.addEventListener("change",v),k.addEventListener("change",v),b()}renderHeartbeatSchedule(e,s){let a=this.parseCronComponents(s.heartbeatSchedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["every_4h","Every 4 hours"],["every_6h","Every 6 hours"],["every_12h","Every 12 hours"],["daily","Once a day"]],c="every_hour",l={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h","0 */4 * * *":"every_4h","0 */6 * * *":"every_6h","0 */12 * * *":"every_12h"};l[s.heartbeatSchedule]?c=l[s.heartbeatSchedule]:(a.freq==="daily"||a.freq==="weekdays")&&(c="daily");for(let[y,k]of o){let b=i.createEl("option",{text:k,attr:{value:y}});y===c&&(b.selected=!0)}let d=e.createDiv({cls:"af-form-row af-schedule-time-row"});d.createDiv({cls:"af-form-label",text:"Time"});let h=d.createDiv({cls:"af-schedule-time-selects"}),u=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<24;y++){let k=y>=12?"PM":"AM",b=y===0?12:y>12?y-12:y,v=u.createEl("option",{text:`${b} ${k}`,attr:{value:String(y)}});y===a.hour&&(v.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let p=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<60;y+=5){let k=p.createEl("option",{text:String(y).padStart(2,"0"),attr:{value:String(y)}});y===a.minute&&(k.selected=!0)}let f=()=>{d.style.display=i.value==="daily"?"":"none"},m=()=>{let y=i.value,k=u.value,b=p.value;switch(y){case"every_5m":s.heartbeatSchedule="*/5 * * * *";break;case"every_15m":s.heartbeatSchedule="*/15 * * * *";break;case"every_30m":s.heartbeatSchedule="*/30 * * * *";break;case"every_hour":s.heartbeatSchedule="0 * * * *";break;case"every_2h":s.heartbeatSchedule="0 */2 * * *";break;case"every_4h":s.heartbeatSchedule="0 */4 * * *";break;case"every_6h":s.heartbeatSchedule="0 */6 * * *";break;case"every_12h":s.heartbeatSchedule="0 */12 * * *";break;case"daily":s.heartbeatSchedule=`${b} ${k} * * *`;break}};i.addEventListener("change",()=>{f(),m()}),u.addEventListener("change",m),p.addEventListener("change",m),f()}parseCronComponents(e){let s={freq:"daily",hour:9,minute:0,days:[1],dayOfMonth:1};if(!e?.trim())return s;let a={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h"};if(a[e])return{...s,freq:a[e]};let n=e.trim().split(/\s+/);if(n.length!==5)return s;let[i,o,c,,l]=n,d=parseInt(o??"9",10),h=parseInt(i??"0",10);if(c==="*"&&l==="*")return{...s,freq:"daily",hour:d,minute:h};if(c==="*"&&l==="1-5")return{...s,freq:"weekdays",hour:d,minute:h};if(c==="*"&&l!=="*"){let u=(l??"1").split(",").map(p=>parseInt(p,10));return{...s,freq:"weekly",hour:d,minute:h,days:u}}return l==="*"&&c!=="*"?{...s,freq:"monthly",hour:d,minute:h,dayOfMonth:parseInt(c??"1",10)}:{...s,hour:d,minute:h}}humanizeCron(e){let s={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours"};if(s[e])return s[e];let a=e.toLowerCase().trim();if(a.startsWith("every ")||a.startsWith("daily ")||a==="daily")return e;if(a.startsWith("hourly"))return"Every hour";if(a.startsWith("weekdays")||a.startsWith("weekly")||a.startsWith("monthly"))return e;let n=e.trim().split(/\s+/);if(n.length!==5)return e;let[i,o,c,,l]=n,d=(p,f)=>{let m=parseInt(p??"0",10),y=parseInt(f??"0",10),k=m>=12?"PM":"AM",b=m===0?12:m>12?m-12:m;return y===0?`${b} ${k}`:`${b}:${String(y).padStart(2,"0")} ${k}`},h=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u=p=>p==="1-5"?"weekdays":p==="0,6"?"weekends":p.split(",").map(m=>parseInt(m,10)).map(m=>h[m]??m).join(", ");return o==="*"&&c==="*"&&l==="*"?i==="*"?"Every minute":`Every hour at :${String(i).padStart(2,"0")}`:c==="*"&&l==="*"&&o!=="*"?`Daily at ${d(o??"0",i??"0")}`:c==="*"&&l==="1-5"&&o!=="*"?`Weekdays at ${d(o??"0",i??"0")}`:c==="*"&&l!=="*"&&o!=="*"?`${u(l??"1")} at ${d(o??"0",i??"0")}`:l==="*"&&c!=="*"&&o!=="*"?`Monthly on the ${c} at ${d(o??"0",i??"0")}`:e}getTagClass(e){return e==="monitoring"?"monitoring":e==="devops"?"devops":e==="sample"?"sample":"default"}renderCreateAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Agent"}),o.createDiv({cls:"af-detail-header-desc",text:"Configure a new agent for your fleet"});let c=a.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",avatar:"",tags:"",systemPrompt:"",model:"default",adapter:"claude-code",cwd:"",timeout:300,permissionMode:"bypassPermissions",effort:"",selectedSkills:new Set,selectedMcpServers:new Set,skillsBody:"",contextBody:"",approvalRequired:"",memory:!0,enabled:!0,allowedCommands:"",blockedCommands:"",heartbeatEnabled:!1,heartbeatSchedule:"0 */6 * * *",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:"",autoCompactThreshold:85,wikiReferences:[]},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features. +*Truncated (${(g.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:g,f=!!(e.finalResult&&e.finalResult.trim()),y=e.output?.trim()??"",k=f&&y.length>0&&y!==e.finalResult.trim();if(f){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=g.createDiv({cls:"af-output-block af-compact-md"});if(w.MarkdownRenderer.render(this.app,m(e.finalResult),x,"",this.plugin),k){let T=g.createEl("details",{cls:"af-run-transcript"}),_=T.createEl("summary");(0,w.setIcon)(_.createSpan({cls:"af-run-transcript-icon"}),"file-text"),_.createSpan({text:"Show full transcript"}),_.createSpan({cls:"af-run-transcript-meta"}).setText(`${(y.length/1024).toFixed(1)} KB`);let A=T.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});w.MarkdownRenderer.render(this.app,m(y),A,"",this.plugin)}}else if(y){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=g.createDiv({cls:"af-output-block af-compact-md"});w.MarkdownRenderer.render(this.app,m(y),x,"",this.plugin)}if(e.toolsUsed.length>0){let g=o.createDiv({cls:"af-slideover-section"});g.createDiv({cls:"af-slideover-section-title",text:"TOOLS USED"}),g.createDiv({cls:"af-output-block",text:e.toolsUsed.join(` +`)})}let b=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let g=b.createEl("button",{cls:"af-btn-sm"});P(g,"external-link","af-btn-icon"),g.appendText(" Open Run Note"),g.onclick=()=>void this.plugin.openPath(e.filePath)}let v=b.createEl("button",{cls:"af-btn-sm primary"});P(v,"refresh-cw","af-btn-icon"),v.appendText(" Re-run Task"),v.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=g=>{g.target===s&&s.remove()}}renderDetailRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderEmptyState(e,s,a,n){let i=e.createDiv({cls:"af-empty-state"}),o=i.createDiv({cls:"af-empty-icon"});(0,w.setIcon)(o,s),i.createDiv({cls:"af-empty-label",text:a}),n&&i.createDiv({cls:"af-empty-sublabel",text:n})}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}statusToTimelineClass(e){switch(e){case"success":return"success";case"failure":case"timeout":return"error";case"cancelled":return"warning";case"pending_approval":return"pending";default:return"running"}}statusToIconName(e){switch(e){case"success":return"check-circle-2";case"failure":return"x-circle";case"timeout":return"clock";case"pending_approval":return"shield-check";case"cancelled":return"square";default:return"loader-2"}}statusToBadgeClass(e){switch(e){case"success":return"success";case"failure":return"failure";case"timeout":return"timeout";case"pending_approval":return"pending";case"cancelled":return"cancelled";default:return"running"}}statusToBadgeText(e){switch(e){case"success":return"Success";case"failure":return"Failed";case"timeout":return"Timeout";case"pending_approval":return"Pending";case"cancelled":return"Cancelled";case"interrupted":return"Interrupted";default:return e}}formatDuration(e){if(e<60)return`${e}s`;let s=Math.floor(e/60),a=e%60;return a>0?`${s}m ${a}s`:`${s}m`}formatStarted(e){try{let s=new Date(e),a=new Date;if(s.toDateString()===a.toDateString())return s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});let n=new Date(a);return n.setDate(n.getDate()-1),s.toDateString()===n.toDateString()?`Yesterday ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:s.toLocaleDateString([],{month:"short",day:"numeric"})+` ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`}catch{return e}}formatNextRun(e){try{let s=new Date(e),a=new Date,n=s.getTime()-a.getTime();if(n<0)return"overdue";let i=Math.round(n/6e4);if(i<60)return`${i}m`;let o=Math.round(i/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let a=e.map(i=>i.nextRun).filter(Boolean).sort()[0];return a?`${e.find(i=>i.nextRun===a)?.agent??"unknown"} in ${this.formatNextRun(a)}`:"none"}getInitials(e){return e.split("-").map(s=>s[0]?.toUpperCase()??"").slice(0,2).join("")}renderAgentAvatar(e,s){let a=s.avatar?.trim();if(!a){(0,w.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,w.setIcon)(e,a):e.setText(a)}getSkillIcon(e){return e.includes("git")?"settings":e.includes("summarize")||e.includes("log")?"activity":e.includes("review")||e.includes("check")?"check-circle-2":e.includes("vault")||e.includes("note")?"file-text":"puzzle"}renderInlineSchedule(e,s){let a=this.parseCronComponents(s.schedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["daily","Daily"],["weekdays","Weekdays"],["weekly","Weekly"],["monthly","Monthly"]];for(let[g,x]of o){let T=i.createEl("option",{text:x,attr:{value:g}});g===a.freq&&(T.selected=!0)}let c=e.createDiv({cls:"af-form-row af-schedule-time-row"});c.createDiv({cls:"af-form-label",text:"Time"});let l=c.createDiv({cls:"af-schedule-time-selects"}),d=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=0;g<24;g++){let x=g>=12?"PM":"AM",T=g===0?12:g>12?g-12:g,_=d.createEl("option",{text:`${T} ${x}`,attr:{value:String(g)}});g===a.hour&&(_.selected=!0)}l.createSpan({cls:"af-schedule-colon",text:":"});let h=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=0;g<60;g+=5){let x=h.createEl("option",{text:String(g).padStart(2,"0"),attr:{value:String(g)}});g===a.minute&&(x.selected=!0)}let u=e.createDiv({cls:"af-form-row af-schedule-day-row"});u.createDiv({cls:"af-form-label",text:"Day"});let p=u.createDiv({cls:"af-schedule-day-buttons"}),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],f=new Set(a.days);for(let g=0;g<7;g++){let x=p.createEl("button",{cls:`af-schedule-day-btn${f.has(g)?" active":""}`,text:m[g]});x.onclick=()=>{f.has(g)?f.delete(g):f.add(g),x.toggleClass("active",f.has(g)),v()}}let y=e.createDiv({cls:"af-form-row af-schedule-dom-row"});y.createDiv({cls:"af-form-label",text:"Day of month"});let k=y.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let g=1;g<=28;g++){let x=k.createEl("option",{text:String(g),attr:{value:String(g)}});g===a.dayOfMonth&&(x.selected=!0)}let b=()=>{let g=i.value,x=["daily","weekdays","weekly","monthly"].includes(g),T=g==="weekly",_=g==="monthly";c.style.display=x?"":"none",u.style.display=T?"":"none",y.style.display=_?"":"none"},v=()=>{let g=i.value,x=d.value,T=h.value,_="";switch(g){case"every_5m":_="*/5 * * * *";break;case"every_15m":_="*/15 * * * *";break;case"every_30m":_="*/30 * * * *";break;case"every_hour":_="0 * * * *";break;case"every_2h":_="0 */2 * * *";break;case"daily":_=`${T} ${x} * * *`;break;case"weekdays":_=`${T} ${x} * * 1-5`;break;case"weekly":{let R=Array.from(f).sort().join(",")||"1";_=`${T} ${x} * * ${R}`;break}case"monthly":_=`${T} ${x} ${k.value} * *`;break}s.schedule=_,s.type="recurring"};i.addEventListener("change",()=>{b(),v()}),d.addEventListener("change",v),h.addEventListener("change",v),k.addEventListener("change",v),b()}renderHeartbeatSchedule(e,s){let a=this.parseCronComponents(s.heartbeatSchedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["every_4h","Every 4 hours"],["every_6h","Every 6 hours"],["every_12h","Every 12 hours"],["daily","Once a day"]],c="every_hour",l={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h","0 */4 * * *":"every_4h","0 */6 * * *":"every_6h","0 */12 * * *":"every_12h"};l[s.heartbeatSchedule]?c=l[s.heartbeatSchedule]:(a.freq==="daily"||a.freq==="weekdays")&&(c="daily");for(let[y,k]of o){let b=i.createEl("option",{text:k,attr:{value:y}});y===c&&(b.selected=!0)}let d=e.createDiv({cls:"af-form-row af-schedule-time-row"});d.createDiv({cls:"af-form-label",text:"Time"});let h=d.createDiv({cls:"af-schedule-time-selects"}),u=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<24;y++){let k=y>=12?"PM":"AM",b=y===0?12:y>12?y-12:y,v=u.createEl("option",{text:`${b} ${k}`,attr:{value:String(y)}});y===a.hour&&(v.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let p=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<60;y+=5){let k=p.createEl("option",{text:String(y).padStart(2,"0"),attr:{value:String(y)}});y===a.minute&&(k.selected=!0)}let m=()=>{d.style.display=i.value==="daily"?"":"none"},f=()=>{let y=i.value,k=u.value,b=p.value;switch(y){case"every_5m":s.heartbeatSchedule="*/5 * * * *";break;case"every_15m":s.heartbeatSchedule="*/15 * * * *";break;case"every_30m":s.heartbeatSchedule="*/30 * * * *";break;case"every_hour":s.heartbeatSchedule="0 * * * *";break;case"every_2h":s.heartbeatSchedule="0 */2 * * *";break;case"every_4h":s.heartbeatSchedule="0 */4 * * *";break;case"every_6h":s.heartbeatSchedule="0 */6 * * *";break;case"every_12h":s.heartbeatSchedule="0 */12 * * *";break;case"daily":s.heartbeatSchedule=`${b} ${k} * * *`;break}};i.addEventListener("change",()=>{m(),f()}),u.addEventListener("change",f),p.addEventListener("change",f),m()}parseCronComponents(e){let s={freq:"daily",hour:9,minute:0,days:[1],dayOfMonth:1};if(!e?.trim())return s;let a={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h"};if(a[e])return{...s,freq:a[e]};let n=e.trim().split(/\s+/);if(n.length!==5)return s;let[i,o,c,,l]=n,d=parseInt(o??"9",10),h=parseInt(i??"0",10);if(c==="*"&&l==="*")return{...s,freq:"daily",hour:d,minute:h};if(c==="*"&&l==="1-5")return{...s,freq:"weekdays",hour:d,minute:h};if(c==="*"&&l!=="*"){let u=(l??"1").split(",").map(p=>parseInt(p,10));return{...s,freq:"weekly",hour:d,minute:h,days:u}}return l==="*"&&c!=="*"?{...s,freq:"monthly",hour:d,minute:h,dayOfMonth:parseInt(c??"1",10)}:{...s,hour:d,minute:h}}humanizeCron(e){let s={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours"};if(s[e])return s[e];let a=e.toLowerCase().trim();if(a.startsWith("every ")||a.startsWith("daily ")||a==="daily")return e;if(a.startsWith("hourly"))return"Every hour";if(a.startsWith("weekdays")||a.startsWith("weekly")||a.startsWith("monthly"))return e;let n=e.trim().split(/\s+/);if(n.length!==5)return e;let[i,o,c,,l]=n,d=(p,m)=>{let f=parseInt(p??"0",10),y=parseInt(m??"0",10),k=f>=12?"PM":"AM",b=f===0?12:f>12?f-12:f;return y===0?`${b} ${k}`:`${b}:${String(y).padStart(2,"0")} ${k}`},h=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u=p=>p==="1-5"?"weekdays":p==="0,6"?"weekends":p.split(",").map(f=>parseInt(f,10)).map(f=>h[f]??f).join(", ");return o==="*"&&c==="*"&&l==="*"?i==="*"?"Every minute":`Every hour at :${String(i).padStart(2,"0")}`:c==="*"&&l==="*"&&o!=="*"?`Daily at ${d(o??"0",i??"0")}`:c==="*"&&l==="1-5"&&o!=="*"?`Weekdays at ${d(o??"0",i??"0")}`:c==="*"&&l!=="*"&&o!=="*"?`${u(l??"1")} at ${d(o??"0",i??"0")}`:l==="*"&&c!=="*"&&o!=="*"?`Monthly on the ${c} at ${d(o??"0",i??"0")}`:e}getTagClass(e){return e==="monitoring"?"monitoring":e==="devops"?"devops":e==="sample"?"sample":"default"}renderCreateAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Agent"}),o.createDiv({cls:"af-detail-header-desc",text:"Configure a new agent for your fleet"});let c=a.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",avatar:"",tags:"",systemPrompt:"",model:"default",adapter:"claude-code",cwd:"",timeout:300,permissionMode:"bypassPermissions",effort:"",selectedSkills:new Set,selectedMcpServers:new Set,skillsBody:"",contextBody:"",approvalRequired:"",memory:!0,enabled:!0,allowedCommands:"",blockedCommands:"",heartbeatEnabled:!1,heartbeatSchedule:"0 */6 * * *",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:"",autoCompactThreshold:85,wikiReferences:[]},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features. Follow existing code conventions. Write clean, well-tested code. If something is unclear, ask for clarification instead of guessing.`},monitor:{label:"Monitor",prompt:`You are a monitoring agent. Check system status, alert on failures, and report on health metrics. Be concise and factual. Highlight anomalies clearly. @@ -11854,13 +11854,13 @@ Include timestamps and relevant context in all reports.`},briefing:{label:"Brief Prioritize recent and important changes. Keep summaries concise. End with explicit next actions if they exist.`},reviewer:{label:"Code Reviewer",prompt:`You are a code review agent. Analyze pull requests, suggest improvements, and flag potential issues. Focus on correctness, security, and maintainability. -Be specific \u2014 reference file names and line numbers.`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),f=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",U=>{l.name=U}),this.createFormField(u,"Description","Monitors deployments and reports status","",U=>{l.description=U});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Avatar"});let y=m.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});y.addEventListener("input",()=>{l.avatar=y.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",U=>{l.tags=U});let k=u.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let b=k.createDiv({cls:"af-agent-card-toggle on"});b.onclick=()=>{let U=b.hasClass("on");b.toggleClass("on",!U),l.enabled=!U};let v=h.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),g.createSpan({text:"System Prompt"});let T=v.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let _=T.createEl("select",{cls:"af-form-select"});for(let[U,{label:K}]of Object.entries(d))_.createEl("option",{text:K,attr:{value:U}});let R=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});R.addEventListener("input",()=>{l.systemPrompt=R.value}),_.addEventListener("change",()=>{let U=d[_.value];U&&_.value!=="none"&&(l.systemPrompt=U.prompt,R.value=U.prompt)});let A=h.createDiv({cls:"af-create-section"}),S=A.createDiv({cls:"af-create-section-header"}),L=S.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(L,"settings"),S.createSpan({text:"Runtime Config"});let E=A.createDiv({cls:"af-create-config-grid"}),B=E.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Adapter"});let I=B.createEl("select",{cls:"af-form-select"}),q=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[U,K,Ue]of q){let xe=I.createEl("option",{text:K,attr:{value:U,...Ue?{disabled:"true"}:{}}});U==="claude-code"&&(xe.selected=!0)}let F=E.createDiv({cls:"af-form-row"}),$=F.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip($,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let X=F.createDiv({cls:"af-form-field-wrap"});At(X,{value:l.model,onChange:U=>{l.model=U}}),I.addEventListener("change",()=>{l.adapter=I.value});let z=E.createDiv({cls:"af-form-row"});z.createDiv({cls:"af-form-label",text:"Working Dir"});let ie=z.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});ie.addEventListener("input",()=>{l.cwd=ie.value});let te=E.createDiv({cls:"af-form-row"});te.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let V=te.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});V.addEventListener("input",()=>{let U=parseInt(V.value,10);!isNaN(U)&&U>0&&(l.timeout=U)});let W=E.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Permission Mode"});let Z=W.createEl("select",{cls:"af-form-select"}),de=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[U,K]of de)Z.createEl("option",{text:K,attr:{value:U}});Z.addEventListener("change",()=>{l.permissionMode=Z.value});let re=E.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});Z.addEventListener("change",()=>{let U=de.find(([K])=>K===Z.value)?.[2]??"";re.textContent=U});let he=E.createDiv({cls:"af-form-row"});he.createDiv({cls:"af-form-label",text:"Effort Level"});let Le=he.createEl("select",{cls:"af-form-select"});for(let[U,K]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Le.createEl("option",{text:K,attr:{value:U}});Le.addEventListener("change",()=>{l.effort=Le.value}),E.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ae=E.createDiv({cls:"af-form-row"}),Ne=Ae.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ne,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let He=Ae.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(l.autoCompactThreshold)}});He.addEventListener("input",()=>{let U=parseInt(He.value,10);!isNaN(U)&&U>=0&&U<=100&&(l.autoCompactThreshold=U)}),E.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let U=this.plugin.runtime.getSnapshot().agents.filter(K=>K.wikiKeeper!==void 0);if(U.length>0){let K=E.createDiv({cls:"af-form-row af-form-row-toggle"}),Ue=K.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(Ue,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let xe=K.createDiv({cls:"af-form-field-wrap"});for(let ye of U){let Fe=xe.createEl("label",{cls:"af-form-checkbox-row"}),pe=Fe.createEl("input",{attr:{type:"checkbox"}});Fe.createSpan({text:` ${ye.name}`,cls:"af-form-checkbox-label"}),pe.addEventListener("change",()=>{pe.checked?l.wikiReferences.includes(ye.name)||l.wikiReferences.push(ye.name):l.wikiReferences=l.wikiReferences.filter(Ve=>Ve!==ye.name)})}}}{let U=h.createDiv({cls:"af-create-section"}),K=U.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"heart-pulse");let xe=K.createSpan({text:"Heartbeat"});this.addTooltip(xe,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ye=U.createDiv({cls:"af-form-row af-form-row-toggle"});ye.createDiv({cls:"af-form-label",text:"Enabled"});let Fe=ye.createDiv({cls:"af-agent-card-toggle"}),pe=U.createDiv();pe.style.display="none",Fe.onclick=()=>{let tt=Fe.hasClass("on");Fe.toggleClass("on",!tt),l.heartbeatEnabled=!tt,pe.style.display=tt?"none":""},this.renderHeartbeatSchedule(pe,l);let Ve=pe.createDiv({cls:"af-form-row af-form-row-toggle"}),O=Ve.createDiv({cls:"af-form-label"});O.setText("Notify"),this.addTooltip(O,"Show an Obsidian notice when the heartbeat completes");let j=Ve.createDiv({cls:"af-agent-card-toggle on"});j.onclick=()=>{let tt=j.hasClass("on");j.toggleClass("on",!tt),l.heartbeatNotify=!tt};let ae=this.plugin.runtime.getSnapshot(),_e=pe.createDiv({cls:"af-form-row"}),Pe=_e.createDiv({cls:"af-form-label"});Pe.setText("Post to channel"),this.addTooltip(Pe,"Heartbeat results are posted to this Slack channel when the run completes");let Oe=_e.createEl("select",{cls:"af-form-select"});Oe.createEl("option",{text:"(none)",attr:{value:""}});for(let tt of ae.channels)Oe.createEl("option",{text:tt.name,attr:{value:tt.name}});Oe.addEventListener("change",()=>{l.heartbeatChannel=Oe.value});let Re=pe.createDiv({cls:"af-form-label"});Re.style.width="auto",Re.style.marginTop="12px",Re.setText("Instruction"),this.addTooltip(Re,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let ot=pe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});ot.addEventListener("input",()=>{l.heartbeatBody=ot.value})}let Ee=h.createDiv({cls:"af-create-section"}),Ce=Ee.createDiv({cls:"af-create-section-header"}),De=Ce.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(De,"puzzle"),Ce.createSpan({text:"Skills"});let Be=this.plugin.runtime.getSnapshot();if(Be.skills.length>0){Ee.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let U=Ee.createDiv({cls:"af-create-skills-grid"});for(let K of Be.skills){let Ue=U.createDiv({cls:"af-create-skill-item"}),xe=Ue.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});xe.addEventListener("change",()=>{xe.checked?l.selectedSkills.add(K.name):l.selectedSkills.delete(K.name)});let ye=Ue.createDiv({cls:"af-create-skill-label"});ye.createSpan({cls:"af-create-skill-name",text:K.name}),K.description&&ye.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${K.description}`})}}let qe=Ee.createDiv({cls:"af-form-sublabel"});qe.setText("Agent-specific skills"),this.addTooltip(qe,"Custom skills/instructions only for this agent, not shared with others");let G=Ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});G.addEventListener("input",()=>{l.skillsBody=G.value});{let U=h.createDiv({cls:"af-create-section"}),K=U.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"plug");let xe=K.createSpan({text:"MCP Servers"});this.addTooltip(xe,"Grant agent access to MCP servers");let ye=this.plugin.mcpManager.getCachedServers();if(ye===null){let Fe=U.createDiv({cls:"af-form-hint"});Fe.appendText("MCP servers not loaded. ");let pe=Fe.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});pe.onclick=Ve=>{Ve.preventDefault(),this.navigate("mcp")}}else if(ye.length===0)U.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Fe=U.createDiv({cls:"af-create-skills-grid"});for(let pe of ye){let Ve=Fe.createDiv({cls:"af-mcp-agent-item"}),O=Ve.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});O.addEventListener("change",()=>{O.checked?l.selectedMcpServers.add(pe.name):l.selectedMcpServers.delete(pe.name)});let ae=Ve.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),_e=ae.createSpan({cls:`af-mcp-status-dot ${pe.enabled?pe.status:"disabled"}`});_e.title=pe.enabled?pe.status:"disabled",ae.createSpan({cls:"af-create-skill-name",text:pe.name});let Pe=pe.toolDetails.length||pe.tools.length;Pe>0?ae.createSpan({cls:"af-mcp-agent-tool-count",text:`${Pe} tools`}):pe.enabled?pe.status==="needs-auth"&&ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let se=h.createDiv({cls:"af-create-section"}),H=se.createDiv({cls:"af-create-section-header"}),we=H.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(we,"file-text");let ke=H.createSpan({text:"Context"});this.addTooltip(ke,"Project-specific context included in every run");let ge=se.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});ge.addEventListener("input",()=>{l.contextBody=ge.value});let Me=h.createDiv({cls:"af-create-section"}),Q=Me.createDiv({cls:"af-create-section-header"}),P=Q.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(P,"shield-check"),Q.createSpan({text:"Permissions"}),this.createFormField(Me,"Approval required","git_push, file_delete","Comma-separated tool names",U=>{l.approvalRequired=U});let ee=Me.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Allowed Commands"});let oe=ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *) +Be specific \u2014 reference file names and line numbers.`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",U=>{l.name=U}),this.createFormField(u,"Description","Monitors deployments and reports status","",U=>{l.description=U});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Avatar"});let y=f.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});y.addEventListener("input",()=>{l.avatar=y.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",U=>{l.tags=U});let k=u.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let b=k.createDiv({cls:"af-agent-card-toggle on"});b.onclick=()=>{let U=b.hasClass("on");b.toggleClass("on",!U),l.enabled=!U};let v=h.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),g.createSpan({text:"System Prompt"});let T=v.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let _=T.createEl("select",{cls:"af-form-select"});for(let[U,{label:K}]of Object.entries(d))_.createEl("option",{text:K,attr:{value:U}});let R=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});R.addEventListener("input",()=>{l.systemPrompt=R.value}),_.addEventListener("change",()=>{let U=d[_.value];U&&_.value!=="none"&&(l.systemPrompt=U.prompt,R.value=U.prompt)});let A=h.createDiv({cls:"af-create-section"}),S=A.createDiv({cls:"af-create-section-header"}),L=S.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(L,"settings"),S.createSpan({text:"Runtime Config"});let E=A.createDiv({cls:"af-create-config-grid"}),B=E.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Adapter"});let I=B.createEl("select",{cls:"af-form-select"}),q=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[U,K,Ue]of q){let xe=I.createEl("option",{text:K,attr:{value:U,...Ue?{disabled:"true"}:{}}});U==="claude-code"&&(xe.selected=!0)}let F=E.createDiv({cls:"af-form-row"}),$=F.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip($,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let X=F.createDiv({cls:"af-form-field-wrap"});At(X,{value:l.model,onChange:U=>{l.model=U}}),I.addEventListener("change",()=>{l.adapter=I.value});let z=E.createDiv({cls:"af-form-row"});z.createDiv({cls:"af-form-label",text:"Working Dir"});let ie=z.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});ie.addEventListener("input",()=>{l.cwd=ie.value});let te=E.createDiv({cls:"af-form-row"});te.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let Y=te.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});Y.addEventListener("input",()=>{let U=parseInt(Y.value,10);!isNaN(U)&&U>0&&(l.timeout=U)});let W=E.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Permission Mode"});let Z=W.createEl("select",{cls:"af-form-select"}),de=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[U,K]of de)Z.createEl("option",{text:K,attr:{value:U}});Z.addEventListener("change",()=>{l.permissionMode=Z.value});let re=E.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});Z.addEventListener("change",()=>{let U=de.find(([K])=>K===Z.value)?.[2]??"";re.textContent=U});let he=E.createDiv({cls:"af-form-row"});he.createDiv({cls:"af-form-label",text:"Effort Level"});let Le=he.createEl("select",{cls:"af-form-select"});for(let[U,K]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Le.createEl("option",{text:K,attr:{value:U}});Le.addEventListener("change",()=>{l.effort=Le.value}),E.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ae=E.createDiv({cls:"af-form-row"}),Ne=Ae.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ne,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let He=Ae.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(l.autoCompactThreshold)}});He.addEventListener("input",()=>{let U=parseInt(He.value,10);!isNaN(U)&&U>=0&&U<=100&&(l.autoCompactThreshold=U)}),E.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let U=this.plugin.runtime.getSnapshot().agents.filter(K=>K.wikiKeeper!==void 0);if(U.length>0){let K=E.createDiv({cls:"af-form-row af-form-row-toggle"}),Ue=K.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(Ue,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let xe=K.createDiv({cls:"af-form-field-wrap"});for(let ye of U){let Fe=xe.createEl("label",{cls:"af-form-checkbox-row"}),pe=Fe.createEl("input",{attr:{type:"checkbox"}});Fe.createSpan({text:` ${ye.name}`,cls:"af-form-checkbox-label"}),pe.addEventListener("change",()=>{pe.checked?l.wikiReferences.includes(ye.name)||l.wikiReferences.push(ye.name):l.wikiReferences=l.wikiReferences.filter(Ve=>Ve!==ye.name)})}}}{let U=h.createDiv({cls:"af-create-section"}),K=U.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"heart-pulse");let xe=K.createSpan({text:"Heartbeat"});this.addTooltip(xe,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ye=U.createDiv({cls:"af-form-row af-form-row-toggle"});ye.createDiv({cls:"af-form-label",text:"Enabled"});let Fe=ye.createDiv({cls:"af-agent-card-toggle"}),pe=U.createDiv();pe.style.display="none",Fe.onclick=()=>{let tt=Fe.hasClass("on");Fe.toggleClass("on",!tt),l.heartbeatEnabled=!tt,pe.style.display=tt?"none":""},this.renderHeartbeatSchedule(pe,l);let Ve=pe.createDiv({cls:"af-form-row af-form-row-toggle"}),O=Ve.createDiv({cls:"af-form-label"});O.setText("Notify"),this.addTooltip(O,"Show an Obsidian notice when the heartbeat completes");let j=Ve.createDiv({cls:"af-agent-card-toggle on"});j.onclick=()=>{let tt=j.hasClass("on");j.toggleClass("on",!tt),l.heartbeatNotify=!tt};let ae=this.plugin.runtime.getSnapshot(),_e=pe.createDiv({cls:"af-form-row"}),De=_e.createDiv({cls:"af-form-label"});De.setText("Post to channel"),this.addTooltip(De,"Heartbeat results are posted to this Slack channel when the run completes");let Oe=_e.createEl("select",{cls:"af-form-select"});Oe.createEl("option",{text:"(none)",attr:{value:""}});for(let tt of ae.channels)Oe.createEl("option",{text:tt.name,attr:{value:tt.name}});Oe.addEventListener("change",()=>{l.heartbeatChannel=Oe.value});let Re=pe.createDiv({cls:"af-form-label"});Re.style.width="auto",Re.style.marginTop="12px",Re.setText("Instruction"),this.addTooltip(Re,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let rt=pe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});rt.addEventListener("input",()=>{l.heartbeatBody=rt.value})}let Ee=h.createDiv({cls:"af-create-section"}),Ce=Ee.createDiv({cls:"af-create-section-header"}),Pe=Ce.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Pe,"puzzle"),Ce.createSpan({text:"Skills"});let Be=this.plugin.runtime.getSnapshot();if(Be.skills.length>0){Ee.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let U=Ee.createDiv({cls:"af-create-skills-grid"});for(let K of Be.skills){let Ue=U.createDiv({cls:"af-create-skill-item"}),xe=Ue.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});xe.addEventListener("change",()=>{xe.checked?l.selectedSkills.add(K.name):l.selectedSkills.delete(K.name)});let ye=Ue.createDiv({cls:"af-create-skill-label"});ye.createSpan({cls:"af-create-skill-name",text:K.name}),K.description&&ye.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${K.description}`})}}let qe=Ee.createDiv({cls:"af-form-sublabel"});qe.setText("Agent-specific skills"),this.addTooltip(qe,"Custom skills/instructions only for this agent, not shared with others");let G=Ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});G.addEventListener("input",()=>{l.skillsBody=G.value});{let U=h.createDiv({cls:"af-create-section"}),K=U.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ue,"plug");let xe=K.createSpan({text:"MCP Servers"});this.addTooltip(xe,"Grant agent access to MCP servers");let ye=this.plugin.mcpManager.getCachedServers();if(ye===null){let Fe=U.createDiv({cls:"af-form-hint"});Fe.appendText("MCP servers not loaded. ");let pe=Fe.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});pe.onclick=Ve=>{Ve.preventDefault(),this.navigate("mcp")}}else if(ye.length===0)U.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Fe=U.createDiv({cls:"af-create-skills-grid"});for(let pe of ye){let Ve=Fe.createDiv({cls:"af-mcp-agent-item"}),O=Ve.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});O.addEventListener("change",()=>{O.checked?l.selectedMcpServers.add(pe.name):l.selectedMcpServers.delete(pe.name)});let ae=Ve.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),_e=ae.createSpan({cls:`af-mcp-status-dot ${pe.enabled?pe.status:"disabled"}`});_e.title=pe.enabled?pe.status:"disabled",ae.createSpan({cls:"af-create-skill-name",text:pe.name});let De=pe.toolDetails.length||pe.tools.length;De>0?ae.createSpan({cls:"af-mcp-agent-tool-count",text:`${De} tools`}):pe.enabled?pe.status==="needs-auth"&&ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let se=h.createDiv({cls:"af-create-section"}),H=se.createDiv({cls:"af-create-section-header"}),we=H.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(we,"file-text");let ke=H.createSpan({text:"Context"});this.addTooltip(ke,"Project-specific context included in every run");let ge=se.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});ge.addEventListener("input",()=>{l.contextBody=ge.value});let Me=h.createDiv({cls:"af-create-section"}),Q=Me.createDiv({cls:"af-create-section-header"}),D=Q.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(D,"shield-check"),Q.createSpan({text:"Permissions"}),this.createFormField(Me,"Approval required","git_push, file_delete","Comma-separated tool names",U=>{l.approvalRequired=U});let ee=Me.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Allowed Commands"});let oe=ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *) Bash(python3 *) Read Edit Write`,rows:"4"}});oe.addEventListener("input",()=>{l.allowedCommands=oe.value});let ue=Me.createDiv({cls:"af-form-row"});ue.createDiv({cls:"af-form-label",text:"Blocked Commands"});let et=ue.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *) Bash(rm -rf *) -Bash(sudo *)`,rows:"4"}});et.addEventListener("input",()=>{l.blockedCommands=et.value});let bt=Me.createDiv({cls:"af-form-row"});bt.createDiv({cls:"af-form-label",text:"Memory enabled"});let Ft=bt.createDiv({cls:"af-agent-card-toggle on"});Ft.onclick=()=>{let U=Ft.hasClass("on");Ft.toggleClass("on",!U),l.memory=!U};let gs=s.createDiv({cls:"af-create-footer"}),ys=gs.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ys.onclick=()=>this.navigate("agents");let St=gs.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(St,"plus","af-btn-icon"),St.appendText(" Create Agent"),St.onclick=async()=>{let U=l.name.trim();if(!U){new w.Notice("Agent name is required.");return}let K=me(U);if(this.plugin.repository.getAgentByName(K)){new w.Notice(`Agent "${K}" already exists.`);return}let Ue=xe=>xe.split(",").map(ye=>ye.trim()).filter(Boolean);try{let xe=ye=>fe(ye).map(Fe=>Fe.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:K,description:l.description.trim(),avatar:l.avatar.trim(),tags:Ue(l.tags),systemPrompt:l.systemPrompt.trim(),model:l.model.trim()||"default",adapter:l.adapter,cwd:l.cwd.trim(),timeout:l.timeout,permissionMode:l.permissionMode,effort:l.effort||void 0,approvalRequired:Ue(l.approvalRequired),memory:l.memory,memoryMaxEntries:100,skills:Array.from(l.selectedSkills),mcpServers:Array.from(l.selectedMcpServers),skillsBody:l.skillsBody.trim(),contextBody:l.contextBody.trim(),enabled:l.enabled,permissionRules:{allow:xe(l.allowedCommands),deny:xe(l.blockedCommands)},autoCompactThreshold:l.autoCompactThreshold,wikiReferences:l.wikiReferences}),l.heartbeatEnabled&&l.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(K,{enabled:l.heartbeatEnabled,schedule:l.heartbeatSchedule.trim(),notify:l.heartbeatNotify,channel:l.heartbeatChannel,body:l.heartbeatBody.trim()}),new w.Notice(`Agent "${K}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",K)}catch(xe){let ye=xe instanceof Error?xe.message:String(xe);new w.Notice(`Failed to create agent: ${ye}`)}}}renderCreateSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Skill"}),o.createDiv({cls:"af-detail-header-desc",text:"Define a reusable skill for your agents"});let c=a.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",tags:"",body:"",toolsBody:"",referencesBody:"",examplesBody:""},d={none:{label:"None",prompt:""},cli:{label:"CLI Tool Wrapper",prompt:`You are using the {{tool}} CLI. All operations go through the wrapper script. +Bash(sudo *)`,rows:"4"}});et.addEventListener("input",()=>{l.blockedCommands=et.value});let bt=Me.createDiv({cls:"af-form-row"});bt.createDiv({cls:"af-form-label",text:"Memory enabled"});let Ft=bt.createDiv({cls:"af-agent-card-toggle on"});Ft.onclick=()=>{let U=Ft.hasClass("on");Ft.toggleClass("on",!U),l.memory=!U};let gs=s.createDiv({cls:"af-create-footer"}),ys=gs.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ys.onclick=()=>this.navigate("agents");let St=gs.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(St,"plus","af-btn-icon"),St.appendText(" Create Agent"),St.onclick=async()=>{let U=l.name.trim();if(!U){new w.Notice("Agent name is required.");return}let K=fe(U);if(this.plugin.repository.getAgentByName(K)){new w.Notice(`Agent "${K}" already exists.`);return}let Ue=xe=>xe.split(",").map(ye=>ye.trim()).filter(Boolean);try{let xe=ye=>me(ye).map(Fe=>Fe.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:K,description:l.description.trim(),avatar:l.avatar.trim(),tags:Ue(l.tags),systemPrompt:l.systemPrompt.trim(),model:l.model.trim()||"default",adapter:l.adapter,cwd:l.cwd.trim(),timeout:l.timeout,permissionMode:l.permissionMode,effort:l.effort||void 0,approvalRequired:Ue(l.approvalRequired),memory:l.memory,memoryMaxEntries:100,skills:Array.from(l.selectedSkills),mcpServers:Array.from(l.selectedMcpServers),skillsBody:l.skillsBody.trim(),contextBody:l.contextBody.trim(),enabled:l.enabled,permissionRules:{allow:xe(l.allowedCommands),deny:xe(l.blockedCommands)},autoCompactThreshold:l.autoCompactThreshold,wikiReferences:l.wikiReferences}),l.heartbeatEnabled&&l.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(K,{enabled:l.heartbeatEnabled,schedule:l.heartbeatSchedule.trim(),notify:l.heartbeatNotify,channel:l.heartbeatChannel,body:l.heartbeatBody.trim()}),new w.Notice(`Agent "${K}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",K)}catch(xe){let ye=xe instanceof Error?xe.message:String(xe);new w.Notice(`Failed to create agent: ${ye}`)}}}renderCreateSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Skill"}),o.createDiv({cls:"af-detail-header-desc",text:"Define a reusable skill for your agents"});let c=a.createDiv({cls:"af-detail-header-actions"}),l={name:"",description:"",tags:"",body:"",toolsBody:"",referencesBody:"",examplesBody:""},d={none:{label:"None",prompt:""},cli:{label:"CLI Tool Wrapper",prompt:`You are using the {{tool}} CLI. All operations go through the wrapper script. Requirements: - Ensure required environment variables are set @@ -11897,7 +11897,7 @@ Key behaviors: - Use tables and charts where appropriate - Always state the time range and filters applied - Flag anomalies and outliers explicitly -- End with actionable insights, not just observations`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),f=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"puzzle"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",W=>{l.name=W}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",W=>{l.description=W}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",W=>{l.tags=W});let m=h.createDiv({cls:"af-create-section"}),y=m.createDiv({cls:"af-create-section-header"}),k=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"file-text"),y.createSpan({text:"Core Instructions"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Template"});let v=b.createEl("select",{cls:"af-form-select"});for(let[W,{label:Z}]of Object.entries(d))v.createEl("option",{text:Z,attr:{value:W}});let g=m.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions \u2014 what does this skill do and how should agents use it?",rows:"10"}});g.addEventListener("input",()=>{l.body=g.value}),v.addEventListener("change",()=>{let W=d[v.value];W&&v.value!=="none"&&(l.body=W.prompt,g.value=W.prompt)});let x=h.createDiv({cls:"af-create-section"}),T=x.createDiv({cls:"af-create-section-header"}),_=T.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(_,"wrench");let R=T.createSpan({text:"Tools"});this.addTooltip(R,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let A=x.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands +- End with actionable insights, not just observations`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),p=u.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"puzzle"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",W=>{l.name=W}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",W=>{l.description=W}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",W=>{l.tags=W});let f=h.createDiv({cls:"af-create-section"}),y=f.createDiv({cls:"af-create-section-header"}),k=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"file-text"),y.createSpan({text:"Core Instructions"});let b=f.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Template"});let v=b.createEl("select",{cls:"af-form-select"});for(let[W,{label:Z}]of Object.entries(d))v.createEl("option",{text:Z,attr:{value:W}});let g=f.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions \u2014 what does this skill do and how should agents use it?",rows:"10"}});g.addEventListener("input",()=>{l.body=g.value}),v.addEventListener("change",()=>{let W=d[v.value];W&&v.value!=="none"&&(l.body=W.prompt,g.value=W.prompt)});let x=h.createDiv({cls:"af-create-section"}),T=x.createDiv({cls:"af-create-section-header"}),_=T.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(_,"wrench");let R=T.createSpan({text:"Tools"});this.addTooltip(R,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let A=x.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands ### list Usage: tool list [--filter ] @@ -11905,9 +11905,9 @@ Usage: tool list [--filter ] User: Show me my tasks for today -Agent: ...`,rows:"6"}});z.addEventListener("input",()=>{l.examplesBody=z.value});let ie=s.createDiv({cls:"af-create-footer"}),te=ie.createEl("button",{cls:"af-btn-sm",text:"Cancel"});te.onclick=()=>this.navigate("skills");let V=ie.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(V,"plus","af-btn-icon"),V.appendText(" Create Skill"),V.onclick=async()=>{let W=l.name.trim();if(!W){new w.Notice("Skill name is required.");return}let Z=me(W);if(this.plugin.repository.getSkillByName(Z)){new w.Notice(`Skill "${Z}" already exists.`);return}let de=re=>re.split(",").map(he=>he.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:Z,description:l.description.trim(),tags:de(l.tags),body:l.body.trim(),toolsBody:l.toolsBody.trim(),referencesBody:l.referencesBody.trim(),examplesBody:l.examplesBody.trim()}),new w.Notice(`Skill "${Z}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let he=re instanceof Error?re.message:String(re);new w.Notice(`Failed to create skill: ${he}`)}}}renderEditAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"bot","No agent selected","");return}let n=this.plugin.runtime.getSnapshot().agents.find(O=>O.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Agent: ${n.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify agent configuration"});let d=i.createDiv({cls:"af-detail-header-actions"}),h={name:n.name,description:n.description??"",avatar:n.avatar,tags:n.tags.join(", "),systemPrompt:n.body,model:n.model,adapter:n.adapter,cwd:n.cwd??"",timeout:n.timeout,permissionMode:n.permissionMode,effort:n.effort??"",selectedSkills:new Set(n.skills),selectedMcpServers:new Set(n.mcpServers??[]),skillsBody:n.skillsBody,contextBody:n.contextBody,approvalRequired:n.approvalRequired.join(", "),memory:n.memory,enabled:n.enabled,allowedCommands:n.permissionRules.allow.join(` +Agent: ...`,rows:"6"}});z.addEventListener("input",()=>{l.examplesBody=z.value});let ie=s.createDiv({cls:"af-create-footer"}),te=ie.createEl("button",{cls:"af-btn-sm",text:"Cancel"});te.onclick=()=>this.navigate("skills");let Y=ie.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(Y,"plus","af-btn-icon"),Y.appendText(" Create Skill"),Y.onclick=async()=>{let W=l.name.trim();if(!W){new w.Notice("Skill name is required.");return}let Z=fe(W);if(this.plugin.repository.getSkillByName(Z)){new w.Notice(`Skill "${Z}" already exists.`);return}let de=re=>re.split(",").map(he=>he.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:Z,description:l.description.trim(),tags:de(l.tags),body:l.body.trim(),toolsBody:l.toolsBody.trim(),referencesBody:l.referencesBody.trim(),examplesBody:l.examplesBody.trim()}),new w.Notice(`Skill "${Z}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let he=re instanceof Error?re.message:String(re);new w.Notice(`Failed to create skill: ${he}`)}}}renderEditAgentPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"bot","No agent selected","");return}let n=this.plugin.runtime.getSnapshot().agents.find(O=>O.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Agent: ${n.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify agent configuration"});let d=i.createDiv({cls:"af-detail-header-actions"}),h={name:n.name,description:n.description??"",avatar:n.avatar,tags:n.tags.join(", "),systemPrompt:n.body,model:n.model,adapter:n.adapter,cwd:n.cwd??"",timeout:n.timeout,permissionMode:n.permissionMode,effort:n.effort??"",selectedSkills:new Set(n.skills),selectedMcpServers:new Set(n.mcpServers??[]),skillsBody:n.skillsBody,contextBody:n.contextBody,approvalRequired:n.approvalRequired.join(", "),memory:n.memory,enabled:n.enabled,allowedCommands:n.permissionRules.allow.join(` `),blockedCommands:n.permissionRules.deny.join(` -`),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel,autoCompactThreshold:n.autoCompactThreshold??85,wikiReferences:(n.wikiReferences??[]).map(O=>O.agent)},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),f=p.createDiv({cls:"af-create-section-header"}),m=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"user"),f.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let k=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Monitors deployments and reports status","",O=>{h.description=O},n.description??"");let b=p.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Avatar"});let v=b.createEl("button",{cls:"af-avatar-picker-btn"}),g=v.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(g,{...n,avatar:h.avatar??n.avatar}),v.createSpan({cls:"af-avatar-picker-label",text:h.avatar||n.avatar||"Pick icon\u2026"}),v.addEventListener("click",()=>{new ia(this.app,h.avatar??n.avatar,O=>{h.avatar=O,g.empty(),(0,w.setIcon)(g,O),v.querySelector(".af-avatar-picker-label")?.setText(O)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",O=>{h.tags=O},n.tags.join(", "));let x=p.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Enabled"});let T=x.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});T.onclick=()=>{let O=T.hasClass("on");T.toggleClass("on",!O),h.enabled=!O};let _=u.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"message-square"),R.createSpan({text:"System Prompt"});let S=_.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});S.value=n.body,S.addEventListener("input",()=>{h.systemPrompt=S.value});let L=u.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),B=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(B,"settings"),E.createSpan({text:"Runtime Config"});let I=L.createDiv({cls:"af-create-config-grid"}),q=I.createDiv({cls:"af-form-row"});q.createDiv({cls:"af-form-label",text:"Adapter"});let F=q.createEl("select",{cls:"af-form-select"}),$=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[O,j,ae]of $){let _e=F.createEl("option",{text:j,attr:{value:O,...ae?{disabled:"true"}:{}}});O===n.adapter&&(_e.selected=!0)}let X=I.createDiv({cls:"af-form-row"}),z=X.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(z,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let ie=X.createDiv({cls:"af-form-field-wrap"});At(ie,{value:h.model,onChange:O=>{h.model=O}}),F.addEventListener("change",()=>{h.adapter=F.value});let te=I.createDiv({cls:"af-form-row"});te.createDiv({cls:"af-form-label",text:"Working Dir"});let V=te.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});V.addEventListener("input",()=>{h.cwd=V.value});let W=I.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let Z=W.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});Z.addEventListener("input",()=>{let O=parseInt(Z.value,10);!isNaN(O)&&O>0&&(h.timeout=O)});let de=I.createDiv({cls:"af-form-row"});de.createDiv({cls:"af-form-label",text:"Permission Mode"});let re=de.createEl("select",{cls:"af-form-select"}),he=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[O,j]of he){let ae=re.createEl("option",{text:j,attr:{value:O}});O===n.permissionMode&&(ae.selected=!0)}re.addEventListener("change",()=>{h.permissionMode=re.value});let Le=I.createDiv({cls:"af-form-hint",text:he.find(([O])=>O===n.permissionMode)?.[2]??""});re.addEventListener("change",()=>{let O=he.find(([j])=>j===re.value)?.[2]??"";Le.textContent=O});let Ae=I.createDiv({cls:"af-form-row"});Ae.createDiv({cls:"af-form-label",text:"Effort Level"});let Ne=Ae.createEl("select",{cls:"af-form-select"});for(let[O,j]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let ae=Ne.createEl("option",{text:j,attr:{value:O}});O===(n.effort??"")&&(ae.selected=!0)}Ne.addEventListener("change",()=>{h.effort=Ne.value}),I.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let He=I.createDiv({cls:"af-form-row"}),Ee=He.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ee,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let Ce=He.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});Ce.addEventListener("input",()=>{let O=parseInt(Ce.value,10);!isNaN(O)&&O>=0&&O<=100&&(h.autoCompactThreshold=O)}),I.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let O=this.plugin.runtime.getSnapshot().agents.filter(j=>j.wikiKeeper!==void 0);if(O.length>0){let j=I.createDiv({cls:"af-form-row af-form-row-toggle"}),ae=j.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(ae,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let _e=j.createDiv({cls:"af-form-field-wrap"});for(let Pe of O){let Oe=_e.createEl("label",{cls:"af-form-checkbox-row"}),Re=Oe.createEl("input",{attr:{type:"checkbox"}});h.wikiReferences.includes(Pe.name)&&(Re.checked=!0),Oe.createSpan({text:` ${Pe.name}`,cls:"af-form-checkbox-label"}),Re.addEventListener("change",()=>{Re.checked?h.wikiReferences.includes(Pe.name)||h.wikiReferences.push(Pe.name):h.wikiReferences=h.wikiReferences.filter(ot=>ot!==Pe.name)})}}}if(n.isFolder){let O=u.createDiv({cls:"af-create-section"}),j=O.createDiv({cls:"af-create-section-header"}),ae=j.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ae,"heart-pulse");let _e=j.createSpan({text:"Heartbeat"});this.addTooltip(_e,"Autonomous periodic run \u2014 what the agent does when no one is asking");let Pe=O.createDiv({cls:"af-form-row af-form-row-toggle"});Pe.createDiv({cls:"af-form-label",text:"Enabled"});let Oe=Pe.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Re=O.createDiv();Re.style.display=h.heartbeatEnabled?"":"none",Oe.onclick=()=>{let lt=Oe.hasClass("on");Oe.toggleClass("on",!lt),h.heartbeatEnabled=!lt,Re.style.display=lt?"none":""},this.renderHeartbeatSchedule(Re,h);let ot=Re.createDiv({cls:"af-form-row af-form-row-toggle"}),tt=ot.createDiv({cls:"af-form-label"});tt.setText("Notify"),this.addTooltip(tt,"Show an Obsidian notice when the heartbeat completes");let la=ot.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});la.onclick=()=>{let lt=la.hasClass("on");la.toggleClass("on",!lt),h.heartbeatNotify=!lt};let Xi=this.plugin.runtime.getSnapshot(),Xa=Re.createDiv({cls:"af-form-row"}),Qa=Xa.createDiv({cls:"af-form-label"});Qa.setText("Post to channel"),this.addTooltip(Qa,"Heartbeat results are posted to this Slack channel when the run completes");let vs=Xa.createEl("select",{cls:"af-form-select"});vs.createEl("option",{text:"(none)",attr:{value:""}});for(let lt of Xi.channels){let Qi=vs.createEl("option",{text:lt.name,attr:{value:lt.name}});lt.name===h.heartbeatChannel&&(Qi.selected=!0)}vs.addEventListener("change",()=>{h.heartbeatChannel=vs.value});let bs=Re.createDiv({cls:"af-form-label"});bs.style.width="auto",bs.style.marginTop="12px",bs.setText("Instruction"),this.addTooltip(bs,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let ca=Re.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});ca.value=h.heartbeatBody,ca.addEventListener("input",()=>{h.heartbeatBody=ca.value})}let De=u.createDiv({cls:"af-create-section"}),Be=De.createDiv({cls:"af-create-section-header"}),qe=Be.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(qe,"puzzle"),Be.createSpan({text:"Skills"});let G=this.plugin.runtime.getSnapshot();if(G.skills.length>0){De.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let O=De.createDiv({cls:"af-create-skills-grid"});for(let j of G.skills){let ae=O.createDiv({cls:"af-create-skill-item"}),_e=ae.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedSkills.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedSkills.add(j.name):h.selectedSkills.delete(j.name)});let Pe=ae.createDiv({cls:"af-create-skill-label"});Pe.createSpan({cls:"af-create-skill-name",text:j.name}),j.description&&Pe.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${j.description}`})}}let se=De.createDiv({cls:"af-form-sublabel"});se.setText("Agent-specific skills"),this.addTooltip(se,"Custom skills/instructions only for this agent, not shared with others");let H=De.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});H.value=n.skillsBody,H.addEventListener("input",()=>{h.skillsBody=H.value});let we=u.createDiv({cls:"af-create-section"}),ke=we.createDiv({cls:"af-create-section-header"}),ge=ke.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ge,"plug");let Me=ke.createSpan({text:"MCP Servers"});this.addTooltip(Me,"Grant agent access to MCP servers");let Q=this.plugin.mcpManager.getCachedServers();if(Q===null){let O=we.createDiv({cls:"af-form-hint"});O.appendText("MCP servers not loaded. ");let j=O.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});j.onclick=ae=>{ae.preventDefault(),this.navigate("mcp")}}else if(Q.length===0)we.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let O=we.createDiv({cls:"af-create-skills-grid"});for(let j of Q){let ae=O.createDiv({cls:"af-mcp-agent-item"}),_e=ae.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedMcpServers.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedMcpServers.add(j.name):h.selectedMcpServers.delete(j.name)});let Oe=ae.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Re=Oe.createSpan({cls:`af-mcp-status-dot ${j.enabled?j.status:"disabled"}`});Re.title=j.enabled?j.status:"disabled",Oe.createSpan({cls:"af-create-skill-name",text:j.name});let ot=j.toolDetails.length||j.tools.length;ot>0?Oe.createSpan({cls:"af-mcp-agent-tool-count",text:`${ot} tools`}):j.enabled?j.status==="needs-auth"&&Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let P=u.createDiv({cls:"af-create-section"}),ee=P.createDiv({cls:"af-create-section-header"}),oe=ee.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(oe,"file-text");let ue=ee.createSpan({text:"Context"});this.addTooltip(ue,"Project-specific context included in every run");let et=P.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});et.value=n.contextBody,et.addEventListener("input",()=>{h.contextBody=et.value});let bt=u.createDiv({cls:"af-create-section"}),Ft=bt.createDiv({cls:"af-create-section-header"}),gs=Ft.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(gs,"shield-check"),Ft.createSpan({text:"Permissions"}),this.createFormField(bt,"Approval required","git_push, file_delete","Comma-separated tool names",O=>{h.approvalRequired=O},n.approvalRequired.join(", "));let ys=bt.createDiv({cls:"af-form-row"});ys.createDiv({cls:"af-form-label",text:"Allowed Commands"});let St=ys.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *) +`),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel,autoCompactThreshold:n.autoCompactThreshold??85,wikiReferences:(n.wikiReferences??[]).map(O=>O.agent)},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),m=p.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"user"),m.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let k=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Monitors deployments and reports status","",O=>{h.description=O},n.description??"");let b=p.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Avatar"});let v=b.createEl("button",{cls:"af-avatar-picker-btn"}),g=v.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(g,{...n,avatar:h.avatar??n.avatar}),v.createSpan({cls:"af-avatar-picker-label",text:h.avatar||n.avatar||"Pick icon\u2026"}),v.addEventListener("click",()=>{new ia(this.app,h.avatar??n.avatar,O=>{h.avatar=O,g.empty(),(0,w.setIcon)(g,O),v.querySelector(".af-avatar-picker-label")?.setText(O)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",O=>{h.tags=O},n.tags.join(", "));let x=p.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Enabled"});let T=x.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});T.onclick=()=>{let O=T.hasClass("on");T.toggleClass("on",!O),h.enabled=!O};let _=u.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"message-square"),R.createSpan({text:"System Prompt"});let S=_.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});S.value=n.body,S.addEventListener("input",()=>{h.systemPrompt=S.value});let L=u.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),B=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(B,"settings"),E.createSpan({text:"Runtime Config"});let I=L.createDiv({cls:"af-create-config-grid"}),q=I.createDiv({cls:"af-form-row"});q.createDiv({cls:"af-form-label",text:"Adapter"});let F=q.createEl("select",{cls:"af-form-select"}),$=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[O,j,ae]of $){let _e=F.createEl("option",{text:j,attr:{value:O,...ae?{disabled:"true"}:{}}});O===n.adapter&&(_e.selected=!0)}let X=I.createDiv({cls:"af-form-row"}),z=X.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(z,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let ie=X.createDiv({cls:"af-form-field-wrap"});At(ie,{value:h.model,onChange:O=>{h.model=O}}),F.addEventListener("change",()=>{h.adapter=F.value});let te=I.createDiv({cls:"af-form-row"});te.createDiv({cls:"af-form-label",text:"Working Dir"});let Y=te.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});Y.addEventListener("input",()=>{h.cwd=Y.value});let W=I.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let Z=W.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});Z.addEventListener("input",()=>{let O=parseInt(Z.value,10);!isNaN(O)&&O>0&&(h.timeout=O)});let de=I.createDiv({cls:"af-form-row"});de.createDiv({cls:"af-form-label",text:"Permission Mode"});let re=de.createEl("select",{cls:"af-form-select"}),he=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[O,j]of he){let ae=re.createEl("option",{text:j,attr:{value:O}});O===n.permissionMode&&(ae.selected=!0)}re.addEventListener("change",()=>{h.permissionMode=re.value});let Le=I.createDiv({cls:"af-form-hint",text:he.find(([O])=>O===n.permissionMode)?.[2]??""});re.addEventListener("change",()=>{let O=he.find(([j])=>j===re.value)?.[2]??"";Le.textContent=O});let Ae=I.createDiv({cls:"af-form-row"});Ae.createDiv({cls:"af-form-label",text:"Effort Level"});let Ne=Ae.createEl("select",{cls:"af-form-select"});for(let[O,j]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let ae=Ne.createEl("option",{text:j,attr:{value:O}});O===(n.effort??"")&&(ae.selected=!0)}Ne.addEventListener("change",()=>{h.effort=Ne.value}),I.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let He=I.createDiv({cls:"af-form-row"}),Ee=He.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ee,"Percent of context window at which the chat auto-invokes /compact before the next message. 85% is a good default. Set 0 to disable.");let Ce=He.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});Ce.addEventListener("input",()=>{let O=parseInt(Ce.value,10);!isNaN(O)&&O>=0&&O<=100&&(h.autoCompactThreshold=O)}),I.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let O=this.plugin.runtime.getSnapshot().agents.filter(j=>j.wikiKeeper!==void 0);if(O.length>0){let j=I.createDiv({cls:"af-form-row af-form-row-toggle"}),ae=j.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(ae,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let _e=j.createDiv({cls:"af-form-field-wrap"});for(let De of O){let Oe=_e.createEl("label",{cls:"af-form-checkbox-row"}),Re=Oe.createEl("input",{attr:{type:"checkbox"}});h.wikiReferences.includes(De.name)&&(Re.checked=!0),Oe.createSpan({text:` ${De.name}`,cls:"af-form-checkbox-label"}),Re.addEventListener("change",()=>{Re.checked?h.wikiReferences.includes(De.name)||h.wikiReferences.push(De.name):h.wikiReferences=h.wikiReferences.filter(rt=>rt!==De.name)})}}}if(n.isFolder){let O=u.createDiv({cls:"af-create-section"}),j=O.createDiv({cls:"af-create-section-header"}),ae=j.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ae,"heart-pulse");let _e=j.createSpan({text:"Heartbeat"});this.addTooltip(_e,"Autonomous periodic run \u2014 what the agent does when no one is asking");let De=O.createDiv({cls:"af-form-row af-form-row-toggle"});De.createDiv({cls:"af-form-label",text:"Enabled"});let Oe=De.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Re=O.createDiv();Re.style.display=h.heartbeatEnabled?"":"none",Oe.onclick=()=>{let ot=Oe.hasClass("on");Oe.toggleClass("on",!ot),h.heartbeatEnabled=!ot,Re.style.display=ot?"none":""},this.renderHeartbeatSchedule(Re,h);let rt=Re.createDiv({cls:"af-form-row af-form-row-toggle"}),tt=rt.createDiv({cls:"af-form-label"});tt.setText("Notify"),this.addTooltip(tt,"Show an Obsidian notice when the heartbeat completes");let ca=rt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});ca.onclick=()=>{let ot=ca.hasClass("on");ca.toggleClass("on",!ot),h.heartbeatNotify=!ot};let Xi=this.plugin.runtime.getSnapshot(),Xa=Re.createDiv({cls:"af-form-row"}),Qa=Xa.createDiv({cls:"af-form-label"});Qa.setText("Post to channel"),this.addTooltip(Qa,"Heartbeat results are posted to this Slack channel when the run completes");let vs=Xa.createEl("select",{cls:"af-form-select"});vs.createEl("option",{text:"(none)",attr:{value:""}});for(let ot of Xi.channels){let Qi=vs.createEl("option",{text:ot.name,attr:{value:ot.name}});ot.name===h.heartbeatChannel&&(Qi.selected=!0)}vs.addEventListener("change",()=>{h.heartbeatChannel=vs.value});let bs=Re.createDiv({cls:"af-form-label"});bs.style.width="auto",bs.style.marginTop="12px",bs.setText("Instruction"),this.addTooltip(bs,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let da=Re.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});da.value=h.heartbeatBody,da.addEventListener("input",()=>{h.heartbeatBody=da.value})}let Pe=u.createDiv({cls:"af-create-section"}),Be=Pe.createDiv({cls:"af-create-section-header"}),qe=Be.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(qe,"puzzle"),Be.createSpan({text:"Skills"});let G=this.plugin.runtime.getSnapshot();if(G.skills.length>0){Pe.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let O=Pe.createDiv({cls:"af-create-skills-grid"});for(let j of G.skills){let ae=O.createDiv({cls:"af-create-skill-item"}),_e=ae.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedSkills.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedSkills.add(j.name):h.selectedSkills.delete(j.name)});let De=ae.createDiv({cls:"af-create-skill-label"});De.createSpan({cls:"af-create-skill-name",text:j.name}),j.description&&De.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${j.description}`})}}let se=Pe.createDiv({cls:"af-form-sublabel"});se.setText("Agent-specific skills"),this.addTooltip(se,"Custom skills/instructions only for this agent, not shared with others");let H=Pe.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});H.value=n.skillsBody,H.addEventListener("input",()=>{h.skillsBody=H.value});let we=u.createDiv({cls:"af-create-section"}),ke=we.createDiv({cls:"af-create-section-header"}),ge=ke.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ge,"plug");let Me=ke.createSpan({text:"MCP Servers"});this.addTooltip(Me,"Grant agent access to MCP servers");let Q=this.plugin.mcpManager.getCachedServers();if(Q===null){let O=we.createDiv({cls:"af-form-hint"});O.appendText("MCP servers not loaded. ");let j=O.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});j.onclick=ae=>{ae.preventDefault(),this.navigate("mcp")}}else if(Q.length===0)we.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let O=we.createDiv({cls:"af-create-skills-grid"});for(let j of Q){let ae=O.createDiv({cls:"af-mcp-agent-item"}),_e=ae.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedMcpServers.has(j.name),_e.addEventListener("change",()=>{_e.checked?h.selectedMcpServers.add(j.name):h.selectedMcpServers.delete(j.name)});let Oe=ae.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Re=Oe.createSpan({cls:`af-mcp-status-dot ${j.enabled?j.status:"disabled"}`});Re.title=j.enabled?j.status:"disabled",Oe.createSpan({cls:"af-create-skill-name",text:j.name});let rt=j.toolDetails.length||j.tools.length;rt>0?Oe.createSpan({cls:"af-mcp-agent-tool-count",text:`${rt} tools`}):j.enabled?j.status==="needs-auth"&&Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Oe.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let D=u.createDiv({cls:"af-create-section"}),ee=D.createDiv({cls:"af-create-section-header"}),oe=ee.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(oe,"file-text");let ue=ee.createSpan({text:"Context"});this.addTooltip(ue,"Project-specific context included in every run");let et=D.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});et.value=n.contextBody,et.addEventListener("input",()=>{h.contextBody=et.value});let bt=u.createDiv({cls:"af-create-section"}),Ft=bt.createDiv({cls:"af-create-section-header"}),gs=Ft.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(gs,"shield-check"),Ft.createSpan({text:"Permissions"}),this.createFormField(bt,"Approval required","git_push, file_delete","Comma-separated tool names",O=>{h.approvalRequired=O},n.approvalRequired.join(", "));let ys=bt.createDiv({cls:"af-form-row"});ys.createDiv({cls:"af-form-label",text:"Allowed Commands"});let St=ys.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *) Bash(python3 *) Read Edit @@ -11915,18 +11915,18 @@ Write`,rows:"4"}});St.value=n.permissionRules.allow.join(` `),St.addEventListener("input",()=>{h.allowedCommands=St.value});let U=bt.createDiv({cls:"af-form-row"});U.createDiv({cls:"af-form-label",text:"Blocked Commands"});let K=U.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *) Bash(rm -rf *) Bash(sudo *)`,rows:"4"}});K.value=n.permissionRules.deny.join(` -`),K.addEventListener("input",()=>{h.blockedCommands=K.value});let Ue=bt.createDiv({cls:"af-form-row"});Ue.createDiv({cls:"af-form-label",text:"Memory enabled"});let xe=Ue.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});xe.onclick=()=>{let O=xe.hasClass("on");xe.toggleClass("on",!O),h.memory=!O};let ye=s.createDiv({cls:"af-create-footer"}),Fe=ye.createEl("button",{cls:"af-btn-sm danger"});D(Fe,"trash-2","af-btn-icon"),Fe.appendText(" Delete"),Fe.onclick=()=>void this.plugin.deleteAgent(n.name),ye.createDiv({cls:"af-toolbar-spacer"});let pe=ye.createEl("button",{cls:"af-btn-sm",text:"Cancel"});pe.onclick=()=>this.navigate("agent-detail",n.name);let Ve=ye.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Ve,"check","af-btn-icon"),Ve.appendText(" Save Changes"),Ve.onclick=async()=>{let O=j=>j.split(",").map(ae=>ae.trim()).filter(Boolean);try{let j=ae=>fe(ae).map(_e=>_e.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:O(h.tags),systemPrompt:h.systemPrompt.trim(),model:h.model.trim()||"default",adapter:h.adapter,cwd:h.cwd.trim(),timeout:h.timeout,permissionMode:h.permissionMode,effort:h.effort||void 0,approvalRequired:O(h.approvalRequired),memory:h.memory,skills:Array.from(h.selectedSkills),mcpServers:Array.from(h.selectedMcpServers),skillsBody:h.skillsBody.trim(),contextBody:h.contextBody.trim(),enabled:h.enabled,permissionRules:{allow:j(h.allowedCommands),deny:j(h.blockedCommands)},autoCompactThreshold:h.autoCompactThreshold,wikiReferences:h.wikiReferences}),n.isFolder&&await this.plugin.repository.updateHeartbeat(n.name,{enabled:h.heartbeatEnabled,schedule:h.heartbeatSchedule.trim(),notify:h.heartbeatNotify,channel:h.heartbeatChannel,body:h.heartbeatBody.trim()}),new w.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(j){let ae=j instanceof Error?j.message:String(j);new w.Notice(`Failed to update agent: ${ae}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-detail-header"}),i=n.createDiv({cls:"af-detail-header-left"}),o=i.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(o,"plus");let c=i.createDiv();c.createDiv({cls:"af-detail-header-name",text:"Create New Task"}),c.createDiv({cls:"af-detail-header-desc",text:"Configure a new task for your fleet"}),n.createDiv({cls:"af-detail-header-actions"});let l={title:"",agent:a.agents[0]?.name??"",priority:"medium",tags:"",body:"",scheduleEnabled:!1,scheduleMode:"recurring",schedule:"0 9 * * *",runAt:"",type:"immediate",enabled:!0,catchUp:!0,effort:"",model:""},d=s.createDiv({cls:"af-create-form"}),h=d.createDiv({cls:"af-create-section"}),u=h.createDiv({cls:"af-create-section-header"}),p=u.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",G=>{l.title=G});let f=h.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Agent"});let m=f.createEl("select",{cls:"af-form-select"});for(let G of a.agents)m.createEl("option",{text:G.name,attr:{value:G.name}});m.addEventListener("change",()=>{l.agent=m.value});let y=h.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Priority"});let k=y.createEl("select",{cls:"af-form-select"}),b=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[G,se]of b){let H=k.createEl("option",{text:se,attr:{value:G}});G==="medium"&&(H.selected=!0)}k.addEventListener("change",()=>{l.priority=k.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",G=>{l.tags=G});let v=d.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),g.createSpan({text:"Instructions"});let T=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});T.addEventListener("input",()=>{l.body=T.value});let _=d.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"clock"),R.createSpan({text:"Schedule"});let S=_.createDiv({cls:"af-form-row af-form-row-toggle"});S.createDiv({cls:"af-form-label",text:"Enable schedule"});let L=S.createDiv({cls:"af-agent-card-toggle"}),E=_.createDiv({cls:"af-schedule-body"});E.style.display="none",L.onclick=()=>{let G=L.hasClass("on");L.toggleClass("on",!G),l.scheduleEnabled=!G,E.style.display=G?"none":"",G?l.type="immediate":l.type=l.scheduleMode==="once"?"once":"recurring"};let B=E.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Mode"});let I=B.createEl("select",{cls:"af-form-select"});for(let[G,se]of[["recurring","Recurring"],["once","One-time"]])I.createEl("option",{text:se,attr:{value:G}});let q=E.createDiv(),F=E.createDiv();F.style.display="none",this.renderInlineSchedule(q,l);let $=F.createDiv({cls:"af-form-row"});$.createDiv({cls:"af-form-label",text:"Run at"});let X=$.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});l.runAt=new Date(X.value).toISOString(),X.addEventListener("input",()=>{l.runAt=X.value?new Date(X.value).toISOString():""}),I.addEventListener("change",()=>{l.scheduleMode=I.value,q.style.display=l.scheduleMode==="recurring"?"":"none",F.style.display=l.scheduleMode==="once"?"":"none",l.scheduleEnabled&&(l.type=l.scheduleMode==="once"?"once":"recurring")});let z=E.createDiv({cls:"af-form-row af-form-row-toggle"});z.createDiv({cls:"af-form-label",text:"Enabled"});let ie=z.createDiv({cls:"af-agent-card-toggle on"});ie.onclick=()=>{let G=ie.hasClass("on");ie.toggleClass("on",!G),l.enabled=!G};let te=E.createDiv({cls:"af-form-row af-form-row-toggle"});te.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let W=te.createDiv({cls:`af-agent-card-toggle${l.catchUp?" on":""}`});W.onclick=()=>{let G=W.hasClass("on");W.toggleClass("on",!G),l.catchUp=!G};let Z=d.createDiv({cls:"af-create-section"}),de=Z.createDiv({cls:"af-create-section-header"}),re=de.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(re,"gauge"),de.createSpan({text:"Execution"});let he=Z.createDiv({cls:"af-form-row"}),Le=he.createDiv({cls:"af-form-label",text:"Model"}),Ae=he.createDiv({cls:"af-form-field-wrap"}),Ne=G=>{Ae.empty();let se=a.agents.find(H=>H.name===G);At(Ae,{value:l.model,onChange:H=>{l.model=H},allowInherit:!0,inheritPlaceholder:se?`Inherit from ${se.name}${se.model?` (${se.model})`:""}`:"Inherit from agent"})};Ne(l.agent),m.addEventListener("change",()=>Ne(m.value)),this.addTooltip(Le,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let He=Z.createDiv({cls:"af-form-row"}),Ee=He.createDiv({cls:"af-form-label",text:"Effort"}),Ce=He.createEl("select",{cls:"af-form-select"});for(let[G,se]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let H=Ce.createEl("option",{text:se,attr:{value:G}});G===l.effort&&(H.selected=!0)}Ce.addEventListener("change",()=>{l.effort=Ce.value}),this.addTooltip(Ee,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let De=s.createDiv({cls:"af-create-footer"}),Be=De.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Be.onclick=()=>this.navigate("kanban");let qe=De.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(qe,"plus","af-btn-icon"),qe.appendText(" Create Task"),qe.onclick=async()=>{let G=l.title.trim();if(!G){new w.Notice("Task title is required.");return}let se=me(G),H=ge=>ge.split(",").map(Me=>Me.trim()).filter(Boolean),we=l.scheduleEnabled?l.scheduleMode==="once"?"once":"recurring":"immediate",ke={task_id:se,agent:l.agent,type:we,priority:l.priority,enabled:l.enabled,created:this.toLocalISO(new Date),run_count:0,catch_up:l.catchUp,effort:l.effort||void 0,model:l.model||void 0,tags:H(l.tags)};if(we==="recurring")ke.schedule=l.schedule.trim()||"0 9 * * *";else if(we==="once"){if(!l.runAt){new w.Notice("Pick a date/time for the one-time run.");return}ke.run_at=l.runAt}try{let ge=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),se);await this.plugin.app.vault.create(ge,J(ke,l.body.trim()||"Describe the task here.")),new w.Notice(`Task "${se}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",se)}catch(ge){let Me=ge instanceof Error?ge.message:String(ge);new w.Notice(`Failed to create task: ${Me}`)}}}toLocalISO(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}:${s(e.getSeconds())}`}toDatetimeLocal(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}`}renderEditTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"circle-dot","No task selected","");return}let n=this.plugin.runtime.getSnapshot().tasks.find(P=>P.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=s.createDiv({cls:"af-detail-header"}),c=o.createDiv({cls:"af-detail-header-left"}),l=c.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(l,"edit");let d=c.createDiv();d.createDiv({cls:"af-detail-header-name",text:`Edit Task: ${n.taskId}`}),d.createDiv({cls:"af-detail-header-desc",text:"Modify task configuration"}),o.createDiv({cls:"af-detail-header-actions"});let h=!!(n.schedule||n.runAt),u={agent:n.agent,type:n.type,priority:n.priority,schedule:n.schedule??"0 9 * * *",runAt:n.runAt??"",scheduleEnabled:h,scheduleMode:n.type==="once"?"once":"recurring",enabled:n.enabled,catchUp:n.catchUp,effort:n.effort??"",model:n.model??"",tags:n.tags.join(", "),body:n.body},p=s.createDiv({cls:"af-create-form"}),f=p.createDiv({cls:"af-create-section"}),m=f.createDiv({cls:"af-create-section-header"}),y=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(y,"file-text"),m.createSpan({text:"Task Details"});let k=f.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let b=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});b.style.opacity="0.6";let v=f.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Agent"});let g=v.createEl("select",{cls:"af-form-select"});for(let P of i.agents){let ee=g.createEl("option",{text:P.name,attr:{value:P.name}});P.name===n.agent&&(ee.selected=!0)}g.addEventListener("change",()=>{u.agent=g.value});let x=f.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Priority"});let T=x.createEl("select",{cls:"af-form-select"}),_=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[P,ee]of _){let oe=T.createEl("option",{text:ee,attr:{value:P}});P===n.priority&&(oe.selected=!0)}T.addEventListener("change",()=>{u.priority=T.value}),this.createFormField(f,"Tags","monitoring, critical","Comma-separated",P=>{u.tags=P},n.tags.join(", "));let R=p.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"message-square"),A.createSpan({text:"Instructions"});let L=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});L.value=n.body,L.addEventListener("input",()=>{u.body=L.value});let E=p.createDiv({cls:"af-create-section"}),B=E.createDiv({cls:"af-create-section-header"}),I=B.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(I,"clock"),B.createSpan({text:"Schedule"});let q=E.createDiv({cls:"af-form-row af-form-row-toggle"});q.createDiv({cls:"af-form-label",text:"Enable schedule"});let F=q.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),$=E.createDiv({cls:"af-schedule-body"});$.style.display=h?"":"none",F.onclick=()=>{let P=F.hasClass("on");F.toggleClass("on",!P),u.scheduleEnabled=!P,$.style.display=P?"none":"",P?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let X=$.createDiv({cls:"af-form-row"});X.createDiv({cls:"af-form-label",text:"Mode"});let z=X.createEl("select",{cls:"af-form-select"});for(let[P,ee]of[["recurring","Recurring"],["once","One-time"]]){let oe=z.createEl("option",{text:ee,attr:{value:P}});P===u.scheduleMode&&(oe.selected=!0)}let ie=$.createDiv(),te=$.createDiv();ie.style.display=u.scheduleMode==="recurring"?"":"none",te.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(ie,u);let V=te.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Run at"});let W=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),Z=V.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:W}});u.runAt||(u.runAt=new Date(Z.value).toISOString()),Z.addEventListener("input",()=>{u.runAt=Z.value?new Date(Z.value).toISOString():""}),z.addEventListener("change",()=>{u.scheduleMode=z.value,ie.style.display=u.scheduleMode==="recurring"?"":"none",te.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let de=$.createDiv({cls:"af-form-row af-form-row-toggle"});de.createDiv({cls:"af-form-label",text:"Enabled"});let re=de.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});re.onclick=()=>{let P=re.hasClass("on");re.toggleClass("on",!P),u.enabled=!P};let he=$.createDiv({cls:"af-form-row af-form-row-toggle"});he.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Ae=he.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});Ae.onclick=()=>{let P=Ae.hasClass("on");Ae.toggleClass("on",!P),u.catchUp=!P};let Ne=p.createDiv({cls:"af-create-section"}),He=Ne.createDiv({cls:"af-create-section-header"}),Ee=He.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ee,"gauge"),He.createSpan({text:"Execution"});let Ce=Ne.createDiv({cls:"af-form-row"}),De=Ce.createDiv({cls:"af-form-label",text:"Effort"}),Be=Ce.createEl("select",{cls:"af-form-select"}),qe=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[P,ee]of qe){let oe=Be.createEl("option",{text:ee,attr:{value:P}});P===u.effort&&(oe.selected=!0)}Be.addEventListener("change",()=>{u.effort=Be.value}),this.addTooltip(De,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let G=Ne.createDiv({cls:"af-form-row"}),se=G.createDiv({cls:"af-form-label",text:"Model"}),H=G.createDiv({cls:"af-form-field-wrap"}),we=P=>{H.empty();let ee=i.agents.find(oe=>oe.name===P);At(H,{value:u.model,onChange:oe=>{u.model=oe},allowInherit:!0,inheritPlaceholder:ee?`Inherit from ${ee.name}${ee.model?` (${ee.model})`:""}`:"Inherit from agent"})};we(u.agent),g.addEventListener("change",()=>we(g.value)),this.addTooltip(se,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let ke=s.createDiv({cls:"af-create-footer"}),ge=ke.createEl("button",{cls:"af-btn-sm danger"});D(ge,"trash-2","af-btn-icon"),ge.appendText(" Delete"),ge.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new w.Notice(`Task "${n.taskId}" deleted.`),await new Promise(P=>setTimeout(P,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},ke.createDiv({cls:"af-toolbar-spacer"});let Me=ke.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("task-detail",n.taskId);let Q=ke.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let P=oe=>oe.split(",").map(ue=>ue.trim()).filter(Boolean),ee=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(ee==="once"&&!u.runAt){new w.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:ee,priority:u.priority,schedule:ee==="recurring"?u.schedule.trim():"",runAt:ee==="once"?u.runAt:"",enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:P(u.tags),body:u.body.trim()}),new w.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(oe){let ue=oe instanceof Error?oe.message:String(oe);new w.Notice(`Failed to update task: ${ue}`)}}}renderEditSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"puzzle","No skill selected","");return}let n=this.plugin.runtime.getSnapshot().skills.find(de=>de.name===a);if(!n){this.renderEmptyState(s,"puzzle","Skill not found",`Skill "${a}" was not found`);return}let i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Skill: ${n.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify skill definition"});let d=i.createDiv({cls:"af-detail-header-actions"}),h={description:n.description??"",tags:n.tags.join(", "),body:n.body,toolsBody:n.toolsBody,referencesBody:n.referencesBody,examplesBody:n.examplesBody},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),f=p.createDiv({cls:"af-create-section-header"}),m=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"puzzle"),f.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let k=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Manage tasks and projects via CLI","",de=>{h.description=de},n.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",de=>{h.tags=de},n.tags.join(", "));let b=u.createDiv({cls:"af-create-section"}),v=b.createDiv({cls:"af-create-section-header"}),g=v.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(g,"file-text"),v.createSpan({text:"Core Instructions"});let x=b.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});x.value=n.body,x.addEventListener("input",()=>{h.body=x.value});let T=u.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"wrench");let A=_.createSpan({text:"Tools"});this.addTooltip(A,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let S=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands +`),K.addEventListener("input",()=>{h.blockedCommands=K.value});let Ue=bt.createDiv({cls:"af-form-row"});Ue.createDiv({cls:"af-form-label",text:"Memory enabled"});let xe=Ue.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});xe.onclick=()=>{let O=xe.hasClass("on");xe.toggleClass("on",!O),h.memory=!O};let ye=s.createDiv({cls:"af-create-footer"}),Fe=ye.createEl("button",{cls:"af-btn-sm danger"});P(Fe,"trash-2","af-btn-icon"),Fe.appendText(" Delete"),Fe.onclick=()=>void this.plugin.deleteAgent(n.name),ye.createDiv({cls:"af-toolbar-spacer"});let pe=ye.createEl("button",{cls:"af-btn-sm",text:"Cancel"});pe.onclick=()=>this.navigate("agent-detail",n.name);let Ve=ye.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(Ve,"check","af-btn-icon"),Ve.appendText(" Save Changes"),Ve.onclick=async()=>{let O=j=>j.split(",").map(ae=>ae.trim()).filter(Boolean);try{let j=ae=>me(ae).map(_e=>_e.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:O(h.tags),systemPrompt:h.systemPrompt.trim(),model:h.model.trim()||"default",adapter:h.adapter,cwd:h.cwd.trim(),timeout:h.timeout,permissionMode:h.permissionMode,effort:h.effort||void 0,approvalRequired:O(h.approvalRequired),memory:h.memory,skills:Array.from(h.selectedSkills),mcpServers:Array.from(h.selectedMcpServers),skillsBody:h.skillsBody.trim(),contextBody:h.contextBody.trim(),enabled:h.enabled,permissionRules:{allow:j(h.allowedCommands),deny:j(h.blockedCommands)},autoCompactThreshold:h.autoCompactThreshold,wikiReferences:h.wikiReferences}),n.isFolder&&await this.plugin.repository.updateHeartbeat(n.name,{enabled:h.heartbeatEnabled,schedule:h.heartbeatSchedule.trim(),notify:h.heartbeatNotify,channel:h.heartbeatChannel,body:h.heartbeatBody.trim()}),new w.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(j){let ae=j instanceof Error?j.message:String(j);new w.Notice(`Failed to update agent: ${ae}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-detail-header"}),i=n.createDiv({cls:"af-detail-header-left"}),o=i.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(o,"plus");let c=i.createDiv();c.createDiv({cls:"af-detail-header-name",text:"Create New Task"}),c.createDiv({cls:"af-detail-header-desc",text:"Configure a new task for your fleet"}),n.createDiv({cls:"af-detail-header-actions"});let l={title:"",agent:a.agents[0]?.name??"",priority:"medium",tags:"",body:"",scheduleEnabled:!1,scheduleMode:"recurring",schedule:"0 9 * * *",runAt:"",type:"immediate",enabled:!0,catchUp:!0,effort:"",model:""},d=s.createDiv({cls:"af-create-form"}),h=d.createDiv({cls:"af-create-section"}),u=h.createDiv({cls:"af-create-section-header"}),p=u.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",G=>{l.title=G});let m=h.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Agent"});let f=m.createEl("select",{cls:"af-form-select"});for(let G of a.agents)f.createEl("option",{text:G.name,attr:{value:G.name}});f.addEventListener("change",()=>{l.agent=f.value});let y=h.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Priority"});let k=y.createEl("select",{cls:"af-form-select"}),b=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[G,se]of b){let H=k.createEl("option",{text:se,attr:{value:G}});G==="medium"&&(H.selected=!0)}k.addEventListener("change",()=>{l.priority=k.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",G=>{l.tags=G});let v=d.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),g.createSpan({text:"Instructions"});let T=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});T.addEventListener("input",()=>{l.body=T.value});let _=d.createDiv({cls:"af-create-section"}),R=_.createDiv({cls:"af-create-section-header"}),A=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"clock"),R.createSpan({text:"Schedule"});let S=_.createDiv({cls:"af-form-row af-form-row-toggle"});S.createDiv({cls:"af-form-label",text:"Enable schedule"});let L=S.createDiv({cls:"af-agent-card-toggle"}),E=_.createDiv({cls:"af-schedule-body"});E.style.display="none",L.onclick=()=>{let G=L.hasClass("on");L.toggleClass("on",!G),l.scheduleEnabled=!G,E.style.display=G?"none":"",G?l.type="immediate":l.type=l.scheduleMode==="once"?"once":"recurring"};let B=E.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Mode"});let I=B.createEl("select",{cls:"af-form-select"});for(let[G,se]of[["recurring","Recurring"],["once","One-time"]])I.createEl("option",{text:se,attr:{value:G}});let q=E.createDiv(),F=E.createDiv();F.style.display="none",this.renderInlineSchedule(q,l);let $=F.createDiv({cls:"af-form-row"});$.createDiv({cls:"af-form-label",text:"Run at"});let X=$.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});l.runAt=new Date(X.value).toISOString(),X.addEventListener("input",()=>{l.runAt=X.value?new Date(X.value).toISOString():""}),I.addEventListener("change",()=>{l.scheduleMode=I.value,q.style.display=l.scheduleMode==="recurring"?"":"none",F.style.display=l.scheduleMode==="once"?"":"none",l.scheduleEnabled&&(l.type=l.scheduleMode==="once"?"once":"recurring")});let z=E.createDiv({cls:"af-form-row af-form-row-toggle"});z.createDiv({cls:"af-form-label",text:"Enabled"});let ie=z.createDiv({cls:"af-agent-card-toggle on"});ie.onclick=()=>{let G=ie.hasClass("on");ie.toggleClass("on",!G),l.enabled=!G};let te=E.createDiv({cls:"af-form-row af-form-row-toggle"});te.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let W=te.createDiv({cls:`af-agent-card-toggle${l.catchUp?" on":""}`});W.onclick=()=>{let G=W.hasClass("on");W.toggleClass("on",!G),l.catchUp=!G};let Z=d.createDiv({cls:"af-create-section"}),de=Z.createDiv({cls:"af-create-section-header"}),re=de.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(re,"gauge"),de.createSpan({text:"Execution"});let he=Z.createDiv({cls:"af-form-row"}),Le=he.createDiv({cls:"af-form-label",text:"Model"}),Ae=he.createDiv({cls:"af-form-field-wrap"}),Ne=G=>{Ae.empty();let se=a.agents.find(H=>H.name===G);At(Ae,{value:l.model,onChange:H=>{l.model=H},allowInherit:!0,inheritPlaceholder:se?`Inherit from ${se.name}${se.model?` (${se.model})`:""}`:"Inherit from agent"})};Ne(l.agent),f.addEventListener("change",()=>Ne(f.value)),this.addTooltip(Le,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let He=Z.createDiv({cls:"af-form-row"}),Ee=He.createDiv({cls:"af-form-label",text:"Effort"}),Ce=He.createEl("select",{cls:"af-form-select"});for(let[G,se]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let H=Ce.createEl("option",{text:se,attr:{value:G}});G===l.effort&&(H.selected=!0)}Ce.addEventListener("change",()=>{l.effort=Ce.value}),this.addTooltip(Ee,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let Pe=s.createDiv({cls:"af-create-footer"}),Be=Pe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Be.onclick=()=>this.navigate("kanban");let qe=Pe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(qe,"plus","af-btn-icon"),qe.appendText(" Create Task"),qe.onclick=async()=>{let G=l.title.trim();if(!G){new w.Notice("Task title is required.");return}let se=fe(G),H=ge=>ge.split(",").map(Me=>Me.trim()).filter(Boolean),we=l.scheduleEnabled?l.scheduleMode==="once"?"once":"recurring":"immediate",ke={task_id:se,agent:l.agent,type:we,priority:l.priority,enabled:l.enabled,created:this.toLocalISO(new Date),run_count:0,catch_up:l.catchUp,effort:l.effort||void 0,model:l.model||void 0,tags:H(l.tags)};if(we==="recurring")ke.schedule=l.schedule.trim()||"0 9 * * *";else if(we==="once"){if(!l.runAt){new w.Notice("Pick a date/time for the one-time run.");return}ke.run_at=l.runAt}try{let ge=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),se);await this.plugin.app.vault.create(ge,J(ke,l.body.trim()||"Describe the task here.")),new w.Notice(`Task "${se}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",se)}catch(ge){let Me=ge instanceof Error?ge.message:String(ge);new w.Notice(`Failed to create task: ${Me}`)}}}toLocalISO(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}:${s(e.getSeconds())}`}toDatetimeLocal(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}T${s(e.getHours())}:${s(e.getMinutes())}`}renderEditTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"circle-dot","No task selected","");return}let n=this.plugin.runtime.getSnapshot().tasks.find(D=>D.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let i=this.plugin.runtime.getSnapshot(),o=s.createDiv({cls:"af-detail-header"}),c=o.createDiv({cls:"af-detail-header-left"}),l=c.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(l,"edit");let d=c.createDiv();d.createDiv({cls:"af-detail-header-name",text:`Edit Task: ${n.taskId}`}),d.createDiv({cls:"af-detail-header-desc",text:"Modify task configuration"}),o.createDiv({cls:"af-detail-header-actions"});let h=!!(n.schedule||n.runAt),u={agent:n.agent,type:n.type,priority:n.priority,schedule:n.schedule??"0 9 * * *",runAt:n.runAt??"",scheduleEnabled:h,scheduleMode:n.type==="once"?"once":"recurring",enabled:n.enabled,catchUp:n.catchUp,effort:n.effort??"",model:n.model??"",tags:n.tags.join(", "),body:n.body},p=s.createDiv({cls:"af-create-form"}),m=p.createDiv({cls:"af-create-section"}),f=m.createDiv({cls:"af-create-section-header"}),y=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(y,"file-text"),f.createSpan({text:"Task Details"});let k=m.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let b=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});b.style.opacity="0.6";let v=m.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Agent"});let g=v.createEl("select",{cls:"af-form-select"});for(let D of i.agents){let ee=g.createEl("option",{text:D.name,attr:{value:D.name}});D.name===n.agent&&(ee.selected=!0)}g.addEventListener("change",()=>{u.agent=g.value});let x=m.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Priority"});let T=x.createEl("select",{cls:"af-form-select"}),_=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[D,ee]of _){let oe=T.createEl("option",{text:ee,attr:{value:D}});D===n.priority&&(oe.selected=!0)}T.addEventListener("change",()=>{u.priority=T.value}),this.createFormField(m,"Tags","monitoring, critical","Comma-separated",D=>{u.tags=D},n.tags.join(", "));let R=p.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"message-square"),A.createSpan({text:"Instructions"});let L=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});L.value=n.body,L.addEventListener("input",()=>{u.body=L.value});let E=p.createDiv({cls:"af-create-section"}),B=E.createDiv({cls:"af-create-section-header"}),I=B.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(I,"clock"),B.createSpan({text:"Schedule"});let q=E.createDiv({cls:"af-form-row af-form-row-toggle"});q.createDiv({cls:"af-form-label",text:"Enable schedule"});let F=q.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),$=E.createDiv({cls:"af-schedule-body"});$.style.display=h?"":"none",F.onclick=()=>{let D=F.hasClass("on");F.toggleClass("on",!D),u.scheduleEnabled=!D,$.style.display=D?"none":"",D?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let X=$.createDiv({cls:"af-form-row"});X.createDiv({cls:"af-form-label",text:"Mode"});let z=X.createEl("select",{cls:"af-form-select"});for(let[D,ee]of[["recurring","Recurring"],["once","One-time"]]){let oe=z.createEl("option",{text:ee,attr:{value:D}});D===u.scheduleMode&&(oe.selected=!0)}let ie=$.createDiv(),te=$.createDiv();ie.style.display=u.scheduleMode==="recurring"?"":"none",te.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(ie,u);let Y=te.createDiv({cls:"af-form-row"});Y.createDiv({cls:"af-form-label",text:"Run at"});let W=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),Z=Y.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:W}});u.runAt||(u.runAt=new Date(Z.value).toISOString()),Z.addEventListener("input",()=>{u.runAt=Z.value?new Date(Z.value).toISOString():""}),z.addEventListener("change",()=>{u.scheduleMode=z.value,ie.style.display=u.scheduleMode==="recurring"?"":"none",te.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let de=$.createDiv({cls:"af-form-row af-form-row-toggle"});de.createDiv({cls:"af-form-label",text:"Enabled"});let re=de.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});re.onclick=()=>{let D=re.hasClass("on");re.toggleClass("on",!D),u.enabled=!D};let he=$.createDiv({cls:"af-form-row af-form-row-toggle"});he.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Ae=he.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});Ae.onclick=()=>{let D=Ae.hasClass("on");Ae.toggleClass("on",!D),u.catchUp=!D};let Ne=p.createDiv({cls:"af-create-section"}),He=Ne.createDiv({cls:"af-create-section-header"}),Ee=He.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Ee,"gauge"),He.createSpan({text:"Execution"});let Ce=Ne.createDiv({cls:"af-form-row"}),Pe=Ce.createDiv({cls:"af-form-label",text:"Effort"}),Be=Ce.createEl("select",{cls:"af-form-select"}),qe=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[D,ee]of qe){let oe=Be.createEl("option",{text:ee,attr:{value:D}});D===u.effort&&(oe.selected=!0)}Be.addEventListener("change",()=>{u.effort=Be.value}),this.addTooltip(Pe,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let G=Ne.createDiv({cls:"af-form-row"}),se=G.createDiv({cls:"af-form-label",text:"Model"}),H=G.createDiv({cls:"af-form-field-wrap"}),we=D=>{H.empty();let ee=i.agents.find(oe=>oe.name===D);At(H,{value:u.model,onChange:oe=>{u.model=oe},allowInherit:!0,inheritPlaceholder:ee?`Inherit from ${ee.name}${ee.model?` (${ee.model})`:""}`:"Inherit from agent"})};we(u.agent),g.addEventListener("change",()=>we(g.value)),this.addTooltip(se,"Override the agent\u2019s model for this task only. Useful for routing simple runs to haiku while the agent stays on opus for heavier work.");let ke=s.createDiv({cls:"af-create-footer"}),ge=ke.createEl("button",{cls:"af-btn-sm danger"});P(ge,"trash-2","af-btn-icon"),ge.appendText(" Delete"),ge.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new w.Notice(`Task "${n.taskId}" deleted.`),await new Promise(D=>setTimeout(D,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},ke.createDiv({cls:"af-toolbar-spacer"});let Me=ke.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("task-detail",n.taskId);let Q=ke.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let D=oe=>oe.split(",").map(ue=>ue.trim()).filter(Boolean),ee=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(ee==="once"&&!u.runAt){new w.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:ee,priority:u.priority,schedule:ee==="recurring"?u.schedule.trim():"",runAt:ee==="once"?u.runAt:"",enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:D(u.tags),body:u.body.trim()}),new w.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(oe){let ue=oe instanceof Error?oe.message:String(oe);new w.Notice(`Failed to update task: ${ue}`)}}}renderEditSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.detailContext;if(!a){this.renderEmptyState(s,"puzzle","No skill selected","");return}let n=this.plugin.runtime.getSnapshot().skills.find(de=>de.name===a);if(!n){this.renderEmptyState(s,"puzzle","Skill not found",`Skill "${a}" was not found`);return}let i=s.createDiv({cls:"af-detail-header"}),o=i.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(c,"edit");let l=o.createDiv();l.createDiv({cls:"af-detail-header-name",text:`Edit Skill: ${n.name}`}),l.createDiv({cls:"af-detail-header-desc",text:"Modify skill definition"});let d=i.createDiv({cls:"af-detail-header-actions"}),h={description:n.description??"",tags:n.tags.join(", "),body:n.body,toolsBody:n.toolsBody,referencesBody:n.referencesBody,examplesBody:n.examplesBody},u=s.createDiv({cls:"af-create-form"}),p=u.createDiv({cls:"af-create-section"}),m=p.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"puzzle"),m.createSpan({text:"Identity"});let y=p.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Name"});let k=y.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(p,"Description","Manage tasks and projects via CLI","",de=>{h.description=de},n.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",de=>{h.tags=de},n.tags.join(", "));let b=u.createDiv({cls:"af-create-section"}),v=b.createDiv({cls:"af-create-section-header"}),g=v.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(g,"file-text"),v.createSpan({text:"Core Instructions"});let x=b.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});x.value=n.body,x.addEventListener("input",()=>{h.body=x.value});let T=u.createDiv({cls:"af-create-section"}),_=T.createDiv({cls:"af-create-section-header"}),R=_.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"wrench");let A=_.createSpan({text:"Tools"});this.addTooltip(A,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let S=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands ### list ...`,rows:"8"}});S.value=n.toolsBody,S.addEventListener("input",()=>{h.toolsBody=S.value});let L=u.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),B=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(B,"book-open");let I=E.createSpan({text:"References"});this.addTooltip(I,"Background docs, conventions, cheat sheets");let q=L.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});q.value=n.referencesBody,q.addEventListener("input",()=>{h.referencesBody=q.value});let F=u.createDiv({cls:"af-create-section"}),$=F.createDiv({cls:"af-create-section-header"}),X=$.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(X,"message-circle");let z=$.createSpan({text:"Examples"});this.addTooltip(z,"Example prompts and ideal outputs showing how to use this skill");let ie=F.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks -...`,rows:"6"}});ie.value=n.examplesBody,ie.addEventListener("input",()=>{h.examplesBody=ie.value});let te=s.createDiv({cls:"af-create-footer"}),V=te.createEl("button",{cls:"af-btn-sm danger"});D(V,"trash-2","af-btn-icon"),V.appendText(" Delete"),V.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new w.Notice(`Skill "${n.name}" deleted.`),await new Promise(de=>setTimeout(de,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},te.createDiv({cls:"af-toolbar-spacer"});let W=te.createEl("button",{cls:"af-btn-sm",text:"Cancel"});W.onclick=()=>this.navigate("skills");let Z=te.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(Z,"check","af-btn-icon"),Z.appendText(" Save Changes"),Z.onclick=async()=>{let de=re=>re.split(",").map(he=>he.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:de(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new w.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let he=re instanceof Error?re.message:String(re);new w.Notice(`Failed to update skill: ${he}`)}}}renderMcpPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"MCP Servers"}),a.createDiv({cls:"af-toolbar-spacer"});let n=a.createEl("button",{cls:"af-btn-sm primary"});D(n,"plus","af-btn-icon"),n.appendText(" Add Server"),n.onclick=()=>this.navigate("add-mcp-server");let i=a.createEl("button",{cls:"af-btn-sm"});D(i,"refresh-cw","af-btn-icon"),i.appendText(" Refresh"),i.onclick=()=>{this.plugin.mcpManager.invalidateCache(),this.render()};let o=this.plugin.mcpManager.getCachedServers();if(o===null){let l=s.createDiv({cls:"af-mcp-progress"}),d=l.createDiv({cls:"af-mcp-progress-header"}),h=d.createDiv({cls:"af-mcp-spinner"});for(let k=0;k<3;k++)h.createSpan();let u=d.createSpan({cls:"af-mcp-progress-label",text:"Discovering MCP servers\u2026"}),f=l.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});f.style.width="15%";let m=l.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),y=this.plugin.mcpManager.onProgress(k=>{switch(k.phase){case"list":u.setText("Scanning servers\u2026"),m.setText(k.message),f.style.width="20%";break;case"details":u.setText(`Checking server ${k.current}/${k.total}\u2026`),m.setText(k.serverName),f.style.width=`${20+k.current/k.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),m.setText(k.message),f.style.width="60%",f.addClass("af-mcp-progress-fill-slow");break;case"done":f.style.width="100%",u.setText("Done"),m.setText(`${k.serverCount} server${k.serverCount!==1?"s":""}, ${k.toolCount} tool${k.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(y),this.plugin.mcpManager.getServers().then(()=>void this.render()).catch(()=>void this.render());return}if(o.length===0){this.renderEmptyState(s,"plug","No MCP servers found","Click 'Add Server' above or use 'claude mcp add' in the terminal");return}let c=s.createDiv({cls:"af-agents-grid"});for(let l of o)this.renderMcpCard(c,l)}renderMcpCard(e,s){let a=e.createDiv({cls:`af-mcp-card${s.enabled?"":" af-mcp-card-disabled"}`}),n=a.createDiv({cls:"af-agent-card-header"}),i=s.enabled?s.status==="connected"?"idle":s.status==="needs-auth"?"pending":"error":"disabled",o=n.createDiv({cls:`af-agent-card-avatar ${i}`});(0,w.setIcon)(o,"plug");let c=n.createDiv({cls:"af-agent-card-titleblock"});c.createDiv({cls:"af-agent-card-name",text:s.name});let l=c.createDiv({cls:"af-agent-card-desc af-mcp-meta"});l.createSpan({cls:"af-mcp-type-badge",text:s.type}),s.scope!=="unknown"&&l.createSpan({cls:"af-badge",text:s.scope});let d=n.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});d.onclick=v=>{v.stopPropagation(),this.plugin.mcpManager.toggleServerEnabled(s.name,!s.enabled).then(()=>{this.plugin.mcpManager.invalidateCache(),this.render()})};let h=a.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),u=h.createSpan();if(s.enabled?((0,w.setIcon)(u,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),h.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,w.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let v=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:v})}let p=s.url??s.command??"";p&&a.createDiv({cls:"af-mcp-command",text:Ot(p,60)});let f=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",m=a.createDiv({cls:"af-mcp-tool-footer"}),y=m.createDiv({cls:"af-mcp-tool-count"}),k=y.createSpan();(0,w.setIcon)(k,"wrench"),y.createSpan({text:` ${f}`});let b=s.toolDetails.length>0?s.toolDetails.map(v=>v.name):s.tools;if(b.length>0){let v=m.createDiv({cls:"af-mcp-tool-chips"}),g=b.slice(0,4);for(let x of g)v.createSpan({cls:"af-mcp-tool-chip",text:x});b.length>4&&v.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${b.length-4}`})}if(this.authenticatingServers.has(s.name)){let g=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary",attr:{disabled:"true"}}),x=g.createSpan({cls:"af-spin"});(0,w.setIcon)(x,"loader-2"),g.appendText(" Authenticating\u2026")}else if(s.enabled&&s.status==="needs-auth"){let g=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary"}),x=g.createSpan();(0,w.setIcon)(x,"key"),g.appendText(" Authenticate"),g.onclick=T=>{T.stopPropagation(),this.authenticateMcpServer(s)}}else if(s.enabled&&s.status==="connected"&&s.type!=="stdio"&&s.toolDetails.length===0){let v=a.createDiv({cls:"af-mcp-hint-row"});v.createSpan({text:"Tools available to agents via Claude \u2014 "});let g=v.createEl("a",{cls:"af-link",text:"discover tools"});g.onclick=x=>{x.stopPropagation(),x.preventDefault(),this.authenticateMcpServer(s)}}a.onclick=()=>this.openMcpDetailSlideover(s)}async authenticateMcpServer(e){if(!e.url){new w.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new w.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new w.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new w.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=fe(e)[0]??e,n=a.split(/(?<=[.!?])\s/)[0]??a,i=n.lengths.remove(),s.onclick=f=>{f.target===s&&s.remove()};let o=a.createDiv({cls:"af-slideover-body"});if(e.description){let f=o.createDiv({cls:"af-slideover-section"});f.createDiv({cls:"af-slideover-section-title",text:"DESCRIPTION"}),f.createDiv({cls:"af-mcp-detail-description",text:e.description})}let c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"SERVER INFO"}),this.renderDetailRow(c,"Name",e.name),this.renderDetailRow(c,"Type",e.type),this.renderDetailRow(c,"Status",e.status),this.renderDetailRow(c,"Scope",e.scope),e.url&&this.renderDetailRow(c,"URL",e.url),e.command&&this.renderDetailRow(c,"Command",e.command),e.args&&this.renderDetailRow(c,"Args",e.args);let l=o.createDiv({cls:"af-slideover-section"}),d=e.toolDetails.length||e.tools.length;if(l.createDiv({cls:"af-slideover-section-title",text:`AVAILABLE TOOLS (${d})`}),e.toolDetails.length>0)for(let f of e.toolDetails){let m=l.createDiv({cls:"af-mcp-tool-detail"}),y=m.createDiv({cls:"af-mcp-tool-detail-header"}),k=y.createSpan({cls:"af-mcp-tool-detail-name"}),b=k.createSpan();if((0,w.setIcon)(b,"wrench"),k.createSpan({text:` ${f.name}`}),f.inputSchema){let v=f.inputSchema.required??[];v.length>0&&y.createSpan({cls:"af-mcp-tool-param-count",text:`${v.length} param${v.length!==1?"s":""}`})}if(f.description){let v=fe(f.description).filter(T=>T.trim()),g=v.slice(0,2).join(" ").trim();if(v.length>2){let T=m.createEl("details",{cls:"af-mcp-tool-detail-desc"});T.createEl("summary",{text:this.truncateDescription(g,200)}),T.createDiv({cls:"af-mcp-tool-detail-full",text:f.description})}else m.createDiv({cls:"af-mcp-tool-detail-desc",text:g})}if(f.inputSchema){let v=f.inputSchema.properties,g=new Set(f.inputSchema.required??[]);if(v&&Object.keys(v).length>0){let x=m.createDiv({cls:"af-mcp-tool-params"});for(let[T,_]of Object.entries(v)){let R=x.createDiv({cls:"af-mcp-tool-param"});R.createSpan({cls:"af-mcp-tool-param-name",text:T}),_.type&&R.createSpan({cls:"af-mcp-tool-param-type",text:_.type}),g.has(T)&&R.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),_.description&&R.createSpan({cls:"af-mcp-tool-param-desc",text:Ot(_.description,80)})}}}}else if(e.tools.length>0)for(let f of e.tools)l.createDiv({cls:"af-mcp-tool-item",text:f});else l.createDiv({cls:"af-form-hint",text:e.status==="connected"?"No tools reported by this server.":"Connect to this server to discover available tools."});let h=o.createDiv({cls:"af-slideover-section"});if(h.createDiv({cls:"af-slideover-section-title",text:"ACTIONS"}),e.enabled&&e.status==="needs-auth"&&e.url){let f=h.createEl("button",{cls:"af-btn-sm primary"}),m=f.createSpan();(0,w.setIcon)(m,"key"),f.appendText(" Authenticate"),f.onclick=()=>{s.remove(),this.authenticateMcpServer(e)}}let u=h.createEl("button",{cls:"af-btn-sm danger"}),p=u.createSpan();(0,w.setIcon)(p,"trash-2"),u.appendText(" Remove Server"),u.onclick=async()=>{try{let f=e.scope==="user"?"user":void 0;await this.plugin.mcpManager.removeServer(e.name,f),new w.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(f){let m=f instanceof Error?f.message:String(f);new w.Notice(`Failed to remove server: ${m}`)}}}renderAddMcpServerPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Add MCP Server"}),o.createDiv({cls:"af-detail-header-desc",text:"Register a new MCP server for agents to use"}),a.createDiv({cls:"af-detail-header-actions"});let c={name:"",transport:"stdio",scope:"user",command:"",args:"",envVars:"",url:"",headers:""},l=s.createDiv({cls:"af-create-form"}),d=l.createDiv({cls:"af-create-section"}),h=d.createDiv({cls:"af-create-section-header"}),u=h.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",$=>{c.name=$});let p=d.createDiv({cls:"af-form-row"}),f=p.createDiv({cls:"af-form-label"});f.setText("Transport"),this.addTooltip(f,"stdio: local process, http/sse: remote server");let m=p.createEl("select",{cls:"af-form-select"});m.createEl("option",{text:"stdio",attr:{value:"stdio"}}),m.createEl("option",{text:"http",attr:{value:"http"}}),m.createEl("option",{text:"sse",attr:{value:"sse"}});let y=d.createDiv({cls:"af-form-row"}),k=y.createDiv({cls:"af-form-label"});k.setText("Scope"),this.addTooltip(k,"local: this project only, user: available across all projects");let b=y.createEl("select",{cls:"af-form-select"});b.createEl("option",{text:"user",attr:{value:"user"}}),b.createEl("option",{text:"local",attr:{value:"local"}}),b.addEventListener("change",()=>{c.scope=b.value});let v=l.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"terminal"),g.createSpan({text:"Process Configuration"}),this.createFormField(v,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",$=>{c.command=$}),this.createFormField(v,"Arguments","--port 3000","Space-separated arguments (optional)",$=>{c.args=$});let T=v.createDiv({cls:"af-form-label"});T.setText("Environment variables"),this.addTooltip(T,"One KEY=VALUE per line");let _=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-... -DEBUG=true`,rows:"3"}});_.addEventListener("input",()=>{c.envVars=_.value});let R=l.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"globe"),A.createSpan({text:"Remote Server Configuration"}),this.createFormField(R,"URL","https://mcp.example.com/sse","Server endpoint URL",$=>{c.url=$});let L=R.createDiv({cls:"af-form-label"});L.setText("Custom headers"),this.addTooltip(L,"One Header: Value per line (optional)");let E=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});E.addEventListener("input",()=>{c.headers=E.value});let B=()=>{v.style.display=c.transport==="stdio"?"":"none",R.style.display=c.transport!=="stdio"?"":"none"};m.addEventListener("change",()=>{c.transport=m.value,B()}),B();let I=s.createDiv({cls:"af-create-footer"}),q=I.createEl("button",{cls:"af-btn-sm",text:"Cancel"});q.onclick=()=>this.navigate("mcp");let F=I.createEl("button",{cls:"af-btn-sm primary af-create-submit"});D(F,"plus","af-btn-icon"),F.appendText(" Add Server"),F.onclick=async()=>{let $=c.name.trim();if(!$){new w.Notice("Server name is required.");return}if(c.transport==="stdio"){if(!c.command.trim()){new w.Notice("Command is required for stdio servers.");return}}else if(!c.url.trim()){new w.Notice("URL is required for HTTP/SSE servers.");return}let X={};if(c.envVars.trim())for(let te of fe(c.envVars)){let V=te.trim();if(!V)continue;let W=V.indexOf("=");if(W<=0){new w.Notice(`Invalid env var: ${V}`);return}X[V.slice(0,W)]=V.slice(W+1)}let z={};if(c.headers.trim())for(let te of fe(c.headers)){let V=te.trim();if(!V)continue;let W=V.indexOf(":");if(W<=0){new w.Notice(`Invalid header: ${V}`);return}z[V.slice(0,W).trim()]=V.slice(W+1).trim()}let ie=c.args.trim()?c.args.trim().split(/\s+/):void 0;F.disabled=!0,F.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:$,transport:c.transport,scope:c.scope,command:c.transport==="stdio"?c.command.trim():void 0,args:c.transport==="stdio"?ie:void 0,envVars:c.transport==="stdio"&&Object.keys(X).length>0?X:void 0,url:c.transport!=="stdio"?c.url.trim():void 0,headers:c.transport!=="stdio"&&Object.keys(z).length>0?z:void 0}),new w.Notice(`Server "${$}" added successfully.`),this.navigate("mcp")}catch(te){let V=te instanceof Error?te.message:String(te);new w.Notice(`Failed to add server: ${V}`),F.disabled=!1,F.setText(""),D(F,"plus","af-btn-icon"),F.appendText(" Add Server")}}}createFormField(e,s,a,n,i,o){let c=e.createDiv({cls:"af-form-row"}),l=c.createDiv({cls:"af-form-label"});l.setText(s),n&&this.addTooltip(l,n);let d=c.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:a}});o!==void 0&&(d.value=o),d.addEventListener("input",()=>i(d.value))}addTooltip(e,s){let a=e.createSpan({cls:"af-form-tooltip"});(0,w.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function Ki(r){switch(r){case"connected":return"idle";case"connecting":case"reconnecting":return"pending";case"needs-auth":case"error":return"error";case"stopped":case"disabled":default:return"disabled"}}function Ka(r){return r>=1e6?`${(r/1e6).toFixed(1)}M`:r>=1e4?`${Math.round(r/1e3)}K`:r>=1e3?`${(r/1e3).toFixed(1)}K`:String(r)}function ul(r){if(!r?.trim())return"not set";let t={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours","0 */4 * * *":"Every 4 hours","0 */6 * * *":"Every 6 hours","0 */12 * * *":"Every 12 hours"};if(t[r])return t[r];let e=r.trim().split(/\s+/);if(e.length===5){let[s,a,n,,i]=e;if(n==="*"&&a&&s){let o=Number(a),c=Number(s);if(!isNaN(o)&&!isNaN(c)){let l=o>=12?"PM":"AM",h=`${o===0?12:o>12?o-12:o}:${String(c).padStart(2,"0")} ${l}`;return i==="*"?`Daily at ${h}`:i==="1-5"?`Weekdays at ${h}`:`${h} on days ${i}`}}}return r}function pl(r){switch(r){case"connected":return"green";case"connecting":case"reconnecting":return"blue";case"needs-auth":case"error":return"red";case"stopped":case"disabled":default:return""}}var Y=require("obsidian");function Ja(r,t){return`${r}::${t}`}var it="default";function ml(){return Math.random().toString(16).slice(2,10)}var Xt=class r extends Y.ItemView{constructor(e,s){super(e);this.plugin=s}selectedAgentName=null;selectedConversationId=it;sessions=new Map;conversationsCache=[];conversationSelect=null;renameBtn=null;deleteBtn=null;headerEl;agentSelect;messagesEl;messagesInner;textarea;sendBtn;attachStopBtn;isInStopMode=!1;attachedFiles=[];attachedImages=[];pillsRow;activityEl=null;streamingDot=null;activityUnsub=null;statsEl;statsUnsub=null;statsSourceSession=null;threadExpanded=new Map;getViewType(){return st}getDisplayText(){if(!this.selectedAgentName)return"Agent Chat";let e=this.getCurrentConversationName();return!e||this.selectedConversationId===it&&this.conversationsCache.length<=1?`Chat: ${this.selectedAgentName}`:`Chat: ${this.selectedAgentName} \xB7 ${e}`}getIcon(){return"message-circle"}getState(){return{agentName:this.selectedAgentName??null,conversationId:this.selectedConversationId}}async setState(e,s){await super.setState(e,s);let a=typeof e?.conversationId=="string"&&e.conversationId?e.conversationId:it;e?.agentName&&typeof e.agentName=="string"&&this.selectAgent(e.agentName,a)}getCurrentConversationName(){return this.conversationsCache.find(s=>s.id===this.selectedConversationId)?.name??""}async onOpen(){this.plugin.subscribeView(this),this.buildShell(),await this.render()}async onClose(){for(let{session:e}of this.sessions.values())e.abort();this.sessions.clear(),this.statsUnsub?.(),this.statsUnsub=null,this.activityUnsub?.(),this.activityUnsub=null,this.plugin.unsubscribeView(this)}selectAgent(e,s=it){let a=this.plugin.app.workspace.getLeavesOfType(st);for(let n of a)if(n.view!==this&&n.view instanceof r&&n.view.selectedAgentName===e&&n.view.selectedConversationId===s){this.plugin.app.workspace.revealLeaf(n);return}this.selectedAgentName=e,this.selectedConversationId=s,this.agentSelect&&(this.agentSelect.value=e),this.leaf.updateHeader(),this.switchToAgent(e,s)}buildShell(){let e=this.contentEl;e.empty(),e.addClass("af-root");let s=e.createDiv({cls:"af-chat-view-container"});this.headerEl=s.createDiv({cls:"af-chat-view-header"}),this.agentSelect=this.headerEl.createEl("select",{cls:"af-chat-view-agent-select"});let a=this.headerEl.createEl("button",{cls:"af-btn-sm af-chat-view-new-btn"});D(a,"plus","af-btn-icon"),a.appendText(" New Chat"),a.onclick=()=>void this.handleNewChat();let n=s.createDiv({cls:"af-chat-view-convo-row"});n.style.display="none",this.conversationSelect=n.createEl("select",{cls:"af-chat-view-convo-select"}),this.conversationSelect.onchange=()=>void this.handleConversationPicked(),this.renameBtn=n.createEl("button",{cls:"af-btn-sm",text:"Rename"}),this.renameBtn.onclick=()=>void this.handleRenameConversation(),this.deleteBtn=n.createEl("button",{cls:"af-btn-sm af-btn-danger",text:"Delete"}),this.deleteBtn.onclick=()=>void this.handleDeleteConversation(),this.agentSelect.onchange=()=>{let l=this.agentSelect.value;if(l){let d=this.plugin.app.workspace.getLeavesOfType(st);for(let h of d)if(h.view!==this&&h.view instanceof r&&h.view.selectedAgentName===l&&h.view.selectedConversationId===it){this.plugin.app.workspace.revealLeaf(h),this.agentSelect.value=this.selectedAgentName??"";return}this.selectedConversationId=it,this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(l,it)}},this.messagesEl=s.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let i=s.createDiv({cls:"af-chat-input-area"});this.pillsRow=i.createDiv({cls:"af-chat-pills-row"}),this.pillsRow.style.display="none";let o=i.createDiv({cls:"af-chat-input-row"});this.attachStopBtn=o.createEl("button",{cls:"af-chat-attach-btn"}),D(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.onclick=()=>{this.isInStopMode?this.handleStop():this.attachActiveDocument()},this.textarea=o.createEl("textarea",{cls:"af-chat-input",attr:{placeholder:"Message the agent\u2026 (Ctrl+Enter to send)",rows:"1"}}),this.sendBtn=o.createEl("button",{cls:"af-chat-send-btn"}),D(this.sendBtn,"arrow-up","af-btn-icon"),this.sendBtn.style.display="none";let c=()=>{this.textarea.style.height="auto";let l=Math.min(this.textarea.scrollHeight,160);this.textarea.style.height=`${l}px`,this.textarea.style.overflowY=this.textarea.scrollHeight>160?"auto":"hidden",this.sendBtn.style.display=this.textarea.value.trim()?"flex":"none"};this.textarea.addEventListener("input",c),this.textarea.addEventListener("focus",()=>{let l=this.getCurrentSession();l&&this.setStatsSource(l.session)}),this.sendBtn.onclick=()=>void this.handleSend(),this.textarea.onkeydown=l=>{l.key==="Enter"&&(l.ctrlKey||l.metaKey)&&(l.preventDefault(),this.handleSend())},this.textarea.addEventListener("paste",l=>{let d=l.clipboardData?.items;if(d)for(let h=0;h{l.preventDefault(),l.stopPropagation(),i.addClass("af-chat-input-dragover")}),i.addEventListener("dragleave",()=>{i.removeClass("af-chat-input-dragover")}),i.addEventListener("drop",l=>{l.preventDefault(),l.stopPropagation(),i.removeClass("af-chat-input-dragover");let d=l.dataTransfer?.files;if(d)for(let h=0;hthis.renderStats(s)))}renderStats(e){if(this.statsEl.empty(),!e||!e.concreteModel){this.statsEl.createSpan({cls:"af-chat-stats-muted",text:"\xA0"});return}let s=this.statsEl.createSpan({cls:"af-chat-stats-line"});if(s.createSpan({cls:"af-chat-stats-model",text:e.concreteModel}),e.contextWindow&&e.contextTokensUsed){let a=Math.min(100,Math.round(e.contextTokensUsed/e.contextWindow*100)),n=10,i=Math.min(n,Math.max(0,Math.round(a/100*n))),o="\u2593".repeat(i)+"\u2591".repeat(n-i),c=s.createSpan({cls:`af-chat-stats-ctx${a>=80?" warn":""}`});c.createSpan({cls:"af-chat-stats-bar",text:o}),c.createSpan({cls:"af-chat-stats-pct",text:`${a}%`}),c.title=`Context: ${Jt(e.contextTokensUsed)} / ${Jt(e.contextWindow)} tokens (${a}%) +...`,rows:"6"}});ie.value=n.examplesBody,ie.addEventListener("input",()=>{h.examplesBody=ie.value});let te=s.createDiv({cls:"af-create-footer"}),Y=te.createEl("button",{cls:"af-btn-sm danger"});P(Y,"trash-2","af-btn-icon"),Y.appendText(" Delete"),Y.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new w.Notice(`Skill "${n.name}" deleted.`),await new Promise(de=>setTimeout(de,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},te.createDiv({cls:"af-toolbar-spacer"});let W=te.createEl("button",{cls:"af-btn-sm",text:"Cancel"});W.onclick=()=>this.navigate("skills");let Z=te.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(Z,"check","af-btn-icon"),Z.appendText(" Save Changes"),Z.onclick=async()=>{let de=re=>re.split(",").map(he=>he.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:de(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new w.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let he=re instanceof Error?re.message:String(re);new w.Notice(`Failed to update skill: ${he}`)}}}renderMcpPage(e){let s=e.createDiv({cls:"af-agents-page"}),a=s.createDiv({cls:"af-agents-toolbar"});a.createDiv({cls:"af-page-title",text:"MCP Servers"}),a.createDiv({cls:"af-toolbar-spacer"});let n=a.createEl("button",{cls:"af-btn-sm primary"});P(n,"plus","af-btn-icon"),n.appendText(" Add Server"),n.onclick=()=>this.navigate("add-mcp-server");let i=a.createEl("button",{cls:"af-btn-sm"});P(i,"refresh-cw","af-btn-icon"),i.appendText(" Refresh"),i.onclick=()=>{this.plugin.mcpManager.invalidateCache(),this.render()};let o=this.plugin.mcpManager.getCachedServers();if(o===null){let l=s.createDiv({cls:"af-mcp-progress"}),d=l.createDiv({cls:"af-mcp-progress-header"}),h=d.createDiv({cls:"af-mcp-spinner"});for(let k=0;k<3;k++)h.createSpan();let u=d.createSpan({cls:"af-mcp-progress-label",text:"Discovering MCP servers\u2026"}),m=l.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});m.style.width="15%";let f=l.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),y=this.plugin.mcpManager.onProgress(k=>{switch(k.phase){case"list":u.setText("Scanning servers\u2026"),f.setText(k.message),m.style.width="20%";break;case"details":u.setText(`Checking server ${k.current}/${k.total}\u2026`),f.setText(k.serverName),m.style.width=`${20+k.current/k.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),f.setText(k.message),m.style.width="60%",m.addClass("af-mcp-progress-fill-slow");break;case"done":m.style.width="100%",u.setText("Done"),f.setText(`${k.serverCount} server${k.serverCount!==1?"s":""}, ${k.toolCount} tool${k.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(y),this.plugin.mcpManager.getServers().then(()=>void this.render()).catch(()=>void this.render());return}if(o.length===0){this.renderEmptyState(s,"plug","No MCP servers found","Click 'Add Server' above or use 'claude mcp add' in the terminal");return}let c=s.createDiv({cls:"af-agents-grid"});for(let l of o)this.renderMcpCard(c,l)}renderMcpCard(e,s){let a=e.createDiv({cls:`af-mcp-card${s.enabled?"":" af-mcp-card-disabled"}`}),n=a.createDiv({cls:"af-agent-card-header"}),i=s.enabled?s.status==="connected"?"idle":s.status==="needs-auth"?"pending":"error":"disabled",o=n.createDiv({cls:`af-agent-card-avatar ${i}`});(0,w.setIcon)(o,"plug");let c=n.createDiv({cls:"af-agent-card-titleblock"});c.createDiv({cls:"af-agent-card-name",text:s.name});let l=c.createDiv({cls:"af-agent-card-desc af-mcp-meta"});l.createSpan({cls:"af-mcp-type-badge",text:s.type}),s.scope!=="unknown"&&l.createSpan({cls:"af-badge",text:s.scope});let d=n.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});d.onclick=v=>{v.stopPropagation(),this.plugin.mcpManager.toggleServerEnabled(s.name,!s.enabled).then(()=>{this.plugin.mcpManager.invalidateCache(),this.render()})};let h=a.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),u=h.createSpan();if(s.enabled?((0,w.setIcon)(u,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),h.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,w.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let v=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:v})}let p=s.url??s.command??"";p&&a.createDiv({cls:"af-mcp-command",text:Ot(p,60)});let m=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",f=a.createDiv({cls:"af-mcp-tool-footer"}),y=f.createDiv({cls:"af-mcp-tool-count"}),k=y.createSpan();(0,w.setIcon)(k,"wrench"),y.createSpan({text:` ${m}`});let b=s.toolDetails.length>0?s.toolDetails.map(v=>v.name):s.tools;if(b.length>0){let v=f.createDiv({cls:"af-mcp-tool-chips"}),g=b.slice(0,4);for(let x of g)v.createSpan({cls:"af-mcp-tool-chip",text:x});b.length>4&&v.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${b.length-4}`})}if(this.authenticatingServers.has(s.name)){let g=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary",attr:{disabled:"true"}}),x=g.createSpan({cls:"af-spin"});(0,w.setIcon)(x,"loader-2"),g.appendText(" Authenticating\u2026")}else if(s.enabled&&s.status==="needs-auth"){let g=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary"}),x=g.createSpan();(0,w.setIcon)(x,"key"),g.appendText(" Authenticate"),g.onclick=T=>{T.stopPropagation(),this.authenticateMcpServer(s)}}else if(s.enabled&&s.status==="connected"&&s.type!=="stdio"&&s.toolDetails.length===0){let v=a.createDiv({cls:"af-mcp-hint-row"});v.createSpan({text:"Tools available to agents via Claude \u2014 "});let g=v.createEl("a",{cls:"af-link",text:"discover tools"});g.onclick=x=>{x.stopPropagation(),x.preventDefault(),this.authenticateMcpServer(s)}}a.onclick=()=>this.openMcpDetailSlideover(s)}async authenticateMcpServer(e){if(!e.url){new w.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new w.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new w.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new w.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=me(e)[0]??e,n=a.split(/(?<=[.!?])\s/)[0]??a,i=n.lengths.remove(),s.onclick=m=>{m.target===s&&s.remove()};let o=a.createDiv({cls:"af-slideover-body"});if(e.description){let m=o.createDiv({cls:"af-slideover-section"});m.createDiv({cls:"af-slideover-section-title",text:"DESCRIPTION"}),m.createDiv({cls:"af-mcp-detail-description",text:e.description})}let c=o.createDiv({cls:"af-slideover-section"});c.createDiv({cls:"af-slideover-section-title",text:"SERVER INFO"}),this.renderDetailRow(c,"Name",e.name),this.renderDetailRow(c,"Type",e.type),this.renderDetailRow(c,"Status",e.status),this.renderDetailRow(c,"Scope",e.scope),e.url&&this.renderDetailRow(c,"URL",e.url),e.command&&this.renderDetailRow(c,"Command",e.command),e.args&&this.renderDetailRow(c,"Args",e.args);let l=o.createDiv({cls:"af-slideover-section"}),d=e.toolDetails.length||e.tools.length;if(l.createDiv({cls:"af-slideover-section-title",text:`AVAILABLE TOOLS (${d})`}),e.toolDetails.length>0)for(let m of e.toolDetails){let f=l.createDiv({cls:"af-mcp-tool-detail"}),y=f.createDiv({cls:"af-mcp-tool-detail-header"}),k=y.createSpan({cls:"af-mcp-tool-detail-name"}),b=k.createSpan();if((0,w.setIcon)(b,"wrench"),k.createSpan({text:` ${m.name}`}),m.inputSchema){let v=m.inputSchema.required??[];v.length>0&&y.createSpan({cls:"af-mcp-tool-param-count",text:`${v.length} param${v.length!==1?"s":""}`})}if(m.description){let v=me(m.description).filter(T=>T.trim()),g=v.slice(0,2).join(" ").trim();if(v.length>2){let T=f.createEl("details",{cls:"af-mcp-tool-detail-desc"});T.createEl("summary",{text:this.truncateDescription(g,200)}),T.createDiv({cls:"af-mcp-tool-detail-full",text:m.description})}else f.createDiv({cls:"af-mcp-tool-detail-desc",text:g})}if(m.inputSchema){let v=m.inputSchema.properties,g=new Set(m.inputSchema.required??[]);if(v&&Object.keys(v).length>0){let x=f.createDiv({cls:"af-mcp-tool-params"});for(let[T,_]of Object.entries(v)){let R=x.createDiv({cls:"af-mcp-tool-param"});R.createSpan({cls:"af-mcp-tool-param-name",text:T}),_.type&&R.createSpan({cls:"af-mcp-tool-param-type",text:_.type}),g.has(T)&&R.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),_.description&&R.createSpan({cls:"af-mcp-tool-param-desc",text:Ot(_.description,80)})}}}}else if(e.tools.length>0)for(let m of e.tools)l.createDiv({cls:"af-mcp-tool-item",text:m});else l.createDiv({cls:"af-form-hint",text:e.status==="connected"?"No tools reported by this server.":"Connect to this server to discover available tools."});let h=o.createDiv({cls:"af-slideover-section"});if(h.createDiv({cls:"af-slideover-section-title",text:"ACTIONS"}),e.enabled&&e.status==="needs-auth"&&e.url){let m=h.createEl("button",{cls:"af-btn-sm primary"}),f=m.createSpan();(0,w.setIcon)(f,"key"),m.appendText(" Authenticate"),m.onclick=()=>{s.remove(),this.authenticateMcpServer(e)}}let u=h.createEl("button",{cls:"af-btn-sm danger"}),p=u.createSpan();(0,w.setIcon)(p,"trash-2"),u.appendText(" Remove Server"),u.onclick=async()=>{try{let m=e.scope==="user"?"user":void 0;await this.plugin.mcpManager.removeServer(e.name,m),new w.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(m){let f=m instanceof Error?m.message:String(m);new w.Notice(`Failed to remove server: ${f}`)}}}renderAddMcpServerPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Add MCP Server"}),o.createDiv({cls:"af-detail-header-desc",text:"Register a new MCP server for agents to use"}),a.createDiv({cls:"af-detail-header-actions"});let c={name:"",transport:"stdio",scope:"user",command:"",args:"",envVars:"",url:"",headers:""},l=s.createDiv({cls:"af-create-form"}),d=l.createDiv({cls:"af-create-section"}),h=d.createDiv({cls:"af-create-section-header"}),u=h.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",$=>{c.name=$});let p=d.createDiv({cls:"af-form-row"}),m=p.createDiv({cls:"af-form-label"});m.setText("Transport"),this.addTooltip(m,"stdio: local process, http/sse: remote server");let f=p.createEl("select",{cls:"af-form-select"});f.createEl("option",{text:"stdio",attr:{value:"stdio"}}),f.createEl("option",{text:"http",attr:{value:"http"}}),f.createEl("option",{text:"sse",attr:{value:"sse"}});let y=d.createDiv({cls:"af-form-row"}),k=y.createDiv({cls:"af-form-label"});k.setText("Scope"),this.addTooltip(k,"local: this project only, user: available across all projects");let b=y.createEl("select",{cls:"af-form-select"});b.createEl("option",{text:"user",attr:{value:"user"}}),b.createEl("option",{text:"local",attr:{value:"local"}}),b.addEventListener("change",()=>{c.scope=b.value});let v=l.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"terminal"),g.createSpan({text:"Process Configuration"}),this.createFormField(v,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",$=>{c.command=$}),this.createFormField(v,"Arguments","--port 3000","Space-separated arguments (optional)",$=>{c.args=$});let T=v.createDiv({cls:"af-form-label"});T.setText("Environment variables"),this.addTooltip(T,"One KEY=VALUE per line");let _=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-... +DEBUG=true`,rows:"3"}});_.addEventListener("input",()=>{c.envVars=_.value});let R=l.createDiv({cls:"af-create-section"}),A=R.createDiv({cls:"af-create-section-header"}),S=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(S,"globe"),A.createSpan({text:"Remote Server Configuration"}),this.createFormField(R,"URL","https://mcp.example.com/sse","Server endpoint URL",$=>{c.url=$});let L=R.createDiv({cls:"af-form-label"});L.setText("Custom headers"),this.addTooltip(L,"One Header: Value per line (optional)");let E=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});E.addEventListener("input",()=>{c.headers=E.value});let B=()=>{v.style.display=c.transport==="stdio"?"":"none",R.style.display=c.transport!=="stdio"?"":"none"};f.addEventListener("change",()=>{c.transport=f.value,B()}),B();let I=s.createDiv({cls:"af-create-footer"}),q=I.createEl("button",{cls:"af-btn-sm",text:"Cancel"});q.onclick=()=>this.navigate("mcp");let F=I.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(F,"plus","af-btn-icon"),F.appendText(" Add Server"),F.onclick=async()=>{let $=c.name.trim();if(!$){new w.Notice("Server name is required.");return}if(c.transport==="stdio"){if(!c.command.trim()){new w.Notice("Command is required for stdio servers.");return}}else if(!c.url.trim()){new w.Notice("URL is required for HTTP/SSE servers.");return}let X={};if(c.envVars.trim())for(let te of me(c.envVars)){let Y=te.trim();if(!Y)continue;let W=Y.indexOf("=");if(W<=0){new w.Notice(`Invalid env var: ${Y}`);return}X[Y.slice(0,W)]=Y.slice(W+1)}let z={};if(c.headers.trim())for(let te of me(c.headers)){let Y=te.trim();if(!Y)continue;let W=Y.indexOf(":");if(W<=0){new w.Notice(`Invalid header: ${Y}`);return}z[Y.slice(0,W).trim()]=Y.slice(W+1).trim()}let ie=c.args.trim()?c.args.trim().split(/\s+/):void 0;F.disabled=!0,F.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:$,transport:c.transport,scope:c.scope,command:c.transport==="stdio"?c.command.trim():void 0,args:c.transport==="stdio"?ie:void 0,envVars:c.transport==="stdio"&&Object.keys(X).length>0?X:void 0,url:c.transport!=="stdio"?c.url.trim():void 0,headers:c.transport!=="stdio"&&Object.keys(z).length>0?z:void 0}),new w.Notice(`Server "${$}" added successfully.`),this.navigate("mcp")}catch(te){let Y=te instanceof Error?te.message:String(te);new w.Notice(`Failed to add server: ${Y}`),F.disabled=!1,F.setText(""),P(F,"plus","af-btn-icon"),F.appendText(" Add Server")}}}createFormField(e,s,a,n,i,o){let c=e.createDiv({cls:"af-form-row"}),l=c.createDiv({cls:"af-form-label"});l.setText(s),n&&this.addTooltip(l,n);let d=c.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:a}});o!==void 0&&(d.value=o),d.addEventListener("input",()=>i(d.value))}addTooltip(e,s){let a=e.createSpan({cls:"af-form-tooltip"});(0,w.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function Ki(r){switch(r){case"connected":return"idle";case"connecting":case"reconnecting":return"pending";case"needs-auth":case"error":return"error";case"stopped":case"disabled":default:return"disabled"}}function Ja(r){return r>=1e6?`${(r/1e6).toFixed(1)}M`:r>=1e4?`${Math.round(r/1e3)}K`:r>=1e3?`${(r/1e3).toFixed(1)}K`:String(r)}function ul(r){if(!r?.trim())return"not set";let t={"*/5 * * * *":"Every 5 minutes","*/10 * * * *":"Every 10 minutes","*/15 * * * *":"Every 15 minutes","*/30 * * * *":"Every 30 minutes","0 * * * *":"Every hour","0 */2 * * *":"Every 2 hours","0 */4 * * *":"Every 4 hours","0 */6 * * *":"Every 6 hours","0 */12 * * *":"Every 12 hours"};if(t[r])return t[r];let e=r.trim().split(/\s+/);if(e.length===5){let[s,a,n,,i]=e;if(n==="*"&&a&&s){let o=Number(a),c=Number(s);if(!isNaN(o)&&!isNaN(c)){let l=o>=12?"PM":"AM",h=`${o===0?12:o>12?o-12:o}:${String(c).padStart(2,"0")} ${l}`;return i==="*"?`Daily at ${h}`:i==="1-5"?`Weekdays at ${h}`:`${h} on days ${i}`}}}return r}function pl(r){switch(r){case"connected":return"green";case"connecting":case"reconnecting":return"blue";case"needs-auth":case"error":return"red";case"stopped":case"disabled":default:return""}}var V=require("obsidian");function oa(r,t){return`${r}::${t}`}var ht="default";function fl(){return Math.random().toString(16).slice(2,10)}function ml(r){let t=new Date(r).getTime();if(!Number.isFinite(t))return"";let e=Date.now()-t,s=60*1e3,a=60*s,n=24*a;return es.id===this.selectedConversationId)?.name??""}async onOpen(){this.plugin.subscribeView(this),this.buildShell(),await this.render(),this.observeContainerWidth()}async onClose(){for(let{session:e}of this.sessions.values())e.abort();this.sessions.clear(),this.statsUnsub?.(),this.statsUnsub=null,this.activityUnsub?.(),this.activityUnsub=null,this.resizeObserver?.disconnect(),this.resizeObserver=null,this.plugin.unsubscribeView(this)}observeContainerWidth(){typeof ResizeObserver>"u"||(this.resizeObserver=new ResizeObserver(e=>{if(!this.userToggledCollapse)for(let s of e){let n=s.contentRect.widththis.toggleConvoPanel(),this.agentSelect=this.headerEl.createEl("select",{cls:"af-chat-view-agent-select"});let a=this.headerEl.createEl("button",{cls:"af-btn-sm af-chat-view-new-btn"});P(a,"plus","af-btn-icon"),a.appendText(" New Chat"),a.onclick=()=>void this.handleNewChat();let n=s.createDiv({cls:"af-chat-view-body"});this.convoPanelEl=n.createDiv({cls:"af-chat-convo-panel"}),this.convoPanelCollapsed&&this.convoPanelEl.addClass("collapsed");let i=n.createDiv({cls:"af-chat-main"});this.agentSelect.onchange=()=>{let d=this.agentSelect.value;if(d){let h=this.plugin.app.workspace.getLeavesOfType(st);for(let u of h)if(u.view!==this&&u.view instanceof r&&u.view.selectedAgentName===d&&u.view.selectedConversationId===ht){this.plugin.app.workspace.revealLeaf(u),this.agentSelect.value=this.selectedAgentName??"";return}this.selectedConversationId=ht,this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(d,ht)}},this.messagesEl=i.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let o=i.createDiv({cls:"af-chat-input-area"});this.pillsRow=o.createDiv({cls:"af-chat-pills-row"}),this.pillsRow.style.display="none";let c=o.createDiv({cls:"af-chat-input-row"});this.attachStopBtn=c.createEl("button",{cls:"af-chat-attach-btn"}),P(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.onclick=()=>{this.isInStopMode?this.handleStop():this.attachActiveDocument()},this.textarea=c.createEl("textarea",{cls:"af-chat-input",attr:{placeholder:"Message the agent\u2026 (Ctrl+Enter to send)",rows:"1"}}),this.sendBtn=c.createEl("button",{cls:"af-chat-send-btn"}),P(this.sendBtn,"arrow-up","af-btn-icon"),this.sendBtn.style.display="none";let l=()=>{this.textarea.style.height="auto";let d=Math.min(this.textarea.scrollHeight,160);this.textarea.style.height=`${d}px`,this.textarea.style.overflowY=this.textarea.scrollHeight>160?"auto":"hidden",this.sendBtn.style.display=this.textarea.value.trim()?"flex":"none"};this.textarea.addEventListener("input",l),this.textarea.addEventListener("focus",()=>{let d=this.getCurrentSession();d&&this.setStatsSource(d.session)}),this.sendBtn.onclick=()=>void this.handleSend(),this.textarea.onkeydown=d=>{d.key==="Enter"&&(d.ctrlKey||d.metaKey)&&(d.preventDefault(),this.handleSend())},this.textarea.addEventListener("paste",d=>{let h=d.clipboardData?.items;if(h)for(let u=0;u{d.preventDefault(),d.stopPropagation(),o.addClass("af-chat-input-dragover")}),o.addEventListener("dragleave",()=>{o.removeClass("af-chat-input-dragover")}),o.addEventListener("drop",d=>{d.preventDefault(),d.stopPropagation(),o.removeClass("af-chat-input-dragover");let h=d.dataTransfer?.files;if(h)for(let u=0;uthis.renderStats(s)))}renderStats(e){if(this.statsEl.empty(),!e||!e.concreteModel){this.statsEl.createSpan({cls:"af-chat-stats-muted",text:"\xA0"});return}let s=this.statsEl.createSpan({cls:"af-chat-stats-line"});if(s.createSpan({cls:"af-chat-stats-model",text:e.concreteModel}),e.contextWindow&&e.contextTokensUsed){let a=Math.min(100,Math.round(e.contextTokensUsed/e.contextWindow*100)),n=10,i=Math.min(n,Math.max(0,Math.round(a/100*n))),o="\u2593".repeat(i)+"\u2591".repeat(n-i),c=s.createSpan({cls:`af-chat-stats-ctx${a>=80?" warn":""}`});c.createSpan({cls:"af-chat-stats-bar",text:o}),c.createSpan({cls:"af-chat-stats-pct",text:`${a}%`}),c.title=`Context: ${Jt(e.contextTokensUsed)} / ${Jt(e.contextWindow)} tokens (${a}%) Includes the agent's system prompt, attached skills, tool schemas, memory, and prior turns. A fresh chat starts non-zero because all of that is loaded on turn 1. -To get 1M context on Opus, set the agent's model to claude-opus-4-7[1m] (or us.anthropic.claude-opus-4-7[1m] on Bedrock).`}if(e.lastCompact){let{preTokens:a,postTokens:n}=e.lastCompact,i=s.createSpan({cls:"af-chat-stats-compact"});i.setText(`compacted ${Jt(a)} \u2192 ${Jt(n)}`),i.title=`Conversation was summarized to free up context. ${Jt(a)} tokens reduced to ${Jt(n)}.`}}populateAgentDropdown(){let e=this.plugin.runtime.getSnapshot().agents,s=this.agentSelect.value;if(this.agentSelect.empty(),e.length===0){let n=this.agentSelect.createEl("option",{text:"No agents available",attr:{value:"",disabled:"true"}});n.selected=!0,this.textarea.disabled=!0,this.showEmptyState();return}if(!this.selectedAgentName){let n=this.agentSelect.createEl("option",{text:"Select agent\u2026",attr:{value:"",disabled:"true"}});n.selected=!0}for(let n of e){let i=n.avatar?.trim(),o=i&&!/^[a-z][a-z0-9-]*$/.test(i)?`${i} `:"";this.agentSelect.createEl("option",{text:`${o}${n.name}`,attr:{value:n.name}})}if(this.selectedAgentName&&e.some(n=>n.name===this.selectedAgentName))this.agentSelect.value=this.selectedAgentName,this.textarea.disabled=!1;else if(s&&e.some(n=>n.name===s))this.agentSelect.value=s,this.selectedAgentName=s,this.textarea.disabled=!1;else{this.selectedAgentName=null,this.leaf.updateHeader(),this.textarea.disabled=!0,this.textarea.placeholder="Select an agent to start chatting\u2026",this.showEmptyState();return}this.leaf.updateHeader(),this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)";let a=this.messagesInner.querySelector(".af-chat-bubble")!==null;this.selectedAgentName&&!a&&this.switchToAgent(this.selectedAgentName)}showEmptyState(){this.messagesInner.empty();let e=this.messagesInner.createDiv({cls:"af-chat-view-empty"}),s=e.createDiv({cls:"af-chat-view-empty-icon"});this.plugin.runtime.getSnapshot().agents.length===0?((0,Y.setIcon)(s,"bot"),e.createDiv({cls:"af-chat-view-empty-text",text:"No agents available"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Create an agent to start chatting"})):((0,Y.setIcon)(s,"message-circle"),e.createDiv({cls:"af-chat-view-empty-text",text:"Select an agent to start"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Choose an agent from the dropdown above"}))}async switchToAgent(e,s=it){let n=this.plugin.runtime.getSnapshot().agents.find(l=>l.name===e);if(!n)return;this.selectedAgentName=e,this.selectedConversationId=s,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear(),await this.refreshConversationsList(n);let i=Ja(e,s),o=this.sessions.get(i);if(!o){let l=new Ut(n,this.plugin.settings,this.plugin.repository,this.app.vault,{inAppConversationId:s});o={session:l},this.sessions.set(i,o),await l.loadPersistedState()}this.setStatsSource(o.session);for(let l of o.session.messages)if(l.role==="user")this.addBubble("user",l.content,l.attachments);else{let d=this.addBubble("assistant");if(this.renderMarkdownBubble(d,l.content),d._setRawText?.(l.content),this.attachThreadAffordance(d,l.id,o.session),l.toolCalls&&l.toolCalls.length>0){let h=this.getOrCreateAffordancesRow(d);this.buildToolSummary(l.toolCalls,h)}}this.activityUnsub?.();let c=o.session;this.activityUnsub=c.onActivityChange(()=>{this.getCurrentSession()?.session===c&&this.renderIndicators(c)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(Ja(this.selectedAgentName,this.selectedConversationId))}async refreshConversationsList(e){this.conversationsCache=await this.plugin.repository.listConversations(e),this.conversationsCache.length===0&&this.conversationsCache.push({id:it,name:"Main chat",lastActive:new Date().toISOString(),messageCount:0,isLegacy:!0}),this.renderConversationPicker()}renderConversationPicker(){if(!this.conversationSelect)return;let e=this.conversationSelect.parentElement;if(!this.selectedAgentName||this.conversationsCache.length<=1){e&&(e.style.display="none"),this.leaf.updateHeader();return}e&&(e.style.display=""),this.conversationSelect.empty();for(let s of this.conversationsCache){let a=s.messageCount>0?`${s.name} (${s.messageCount})`:s.name;this.conversationSelect.createEl("option",{text:a,attr:{value:s.id}})}this.conversationSelect.value=this.selectedConversationId,this.deleteBtn&&(this.deleteBtn.disabled=this.selectedConversationId===it),this.leaf.updateHeader()}async handleConversationPicked(){if(!this.conversationSelect||!this.selectedAgentName)return;let e=this.conversationSelect.value;if(!e||e===this.selectedConversationId)return;let s=this.plugin.app.workspace.getLeavesOfType(st);for(let a of s)if(a.view!==this&&a.view instanceof r&&a.view.selectedAgentName===this.selectedAgentName&&a.view.selectedConversationId===e){this.plugin.app.workspace.revealLeaf(a),this.conversationSelect.value=this.selectedConversationId;return}await this.switchToAgent(this.selectedAgentName,e)}async handleRenameConversation(){if(!this.selectedAgentName)return;let s=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===this.selectedAgentName);if(!s)return;let a=this.conversationsCache.find(o=>o.id===this.selectedConversationId),n=window.prompt("Rename conversation:",a?.name??"");if(n===null)return;let i=n.trim();if(!(!i||i===a?.name))try{await this.plugin.repository.renameConversation(s,this.selectedConversationId,i);let o=this.getCurrentSession();o&&await o.session.setConversationName(i),await this.refreshConversationsList(s)}catch(o){new Y.Notice(`Couldn't rename: ${o instanceof Error?o.message:String(o)}`)}}async handleDeleteConversation(){if(!this.selectedAgentName||this.selectedConversationId===it)return;let s=this.plugin.runtime.getSnapshot().agents.find(c=>c.name===this.selectedAgentName);if(!s)return;let a=this.conversationsCache.find(c=>c.id===this.selectedConversationId);if(!window.confirm(`Delete conversation "${a?.name??this.selectedConversationId}"? +To get 1M context on Opus, set the agent's model to claude-opus-4-7[1m] (or us.anthropic.claude-opus-4-7[1m] on Bedrock).`}if(e.lastCompact){let{preTokens:a,postTokens:n}=e.lastCompact,i=s.createSpan({cls:"af-chat-stats-compact"});i.setText(`compacted ${Jt(a)} \u2192 ${Jt(n)}`),i.title=`Conversation was summarized to free up context. ${Jt(a)} tokens reduced to ${Jt(n)}.`}}populateAgentDropdown(){let e=this.plugin.runtime.getSnapshot().agents,s=this.agentSelect.value;if(this.agentSelect.empty(),e.length===0){let n=this.agentSelect.createEl("option",{text:"No agents available",attr:{value:"",disabled:"true"}});n.selected=!0,this.textarea.disabled=!0,this.showEmptyState();return}if(!this.selectedAgentName){let n=this.agentSelect.createEl("option",{text:"Select agent\u2026",attr:{value:"",disabled:"true"}});n.selected=!0}for(let n of e){let i=n.avatar?.trim(),o=i&&!/^[a-z][a-z0-9-]*$/.test(i)?`${i} `:"";this.agentSelect.createEl("option",{text:`${o}${n.name}`,attr:{value:n.name}})}if(this.selectedAgentName&&e.some(n=>n.name===this.selectedAgentName))this.agentSelect.value=this.selectedAgentName,this.textarea.disabled=!1;else if(s&&e.some(n=>n.name===s))this.agentSelect.value=s,this.selectedAgentName=s,this.textarea.disabled=!1;else{this.selectedAgentName=null,this.leaf.updateHeader(),this.textarea.disabled=!0,this.textarea.placeholder="Select an agent to start chatting\u2026",this.showEmptyState();return}this.leaf.updateHeader(),this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)";let a=this.messagesInner.querySelector(".af-chat-bubble")!==null;this.selectedAgentName&&!a&&this.switchToAgent(this.selectedAgentName)}showEmptyState(){this.messagesInner.empty();let e=this.messagesInner.createDiv({cls:"af-chat-view-empty"}),s=e.createDiv({cls:"af-chat-view-empty-icon"});this.plugin.runtime.getSnapshot().agents.length===0?((0,V.setIcon)(s,"bot"),e.createDiv({cls:"af-chat-view-empty-text",text:"No agents available"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Create an agent to start chatting"})):((0,V.setIcon)(s,"message-circle"),e.createDiv({cls:"af-chat-view-empty-text",text:"Select an agent to start"}),e.createDiv({cls:"af-chat-view-empty-hint",text:"Choose an agent from the dropdown above"}))}async switchToAgent(e,s=ht){let n=this.plugin.runtime.getSnapshot().agents.find(l=>l.name===e);if(!n)return;this.selectedAgentName=e,this.selectedConversationId=s,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear(),await this.refreshConversationsList(n);let i=oa(e,s),o=this.sessions.get(i);if(!o){let l=new Ut(n,this.plugin.settings,this.plugin.repository,this.app.vault,{inAppConversationId:s});o={session:l},this.sessions.set(i,o),await l.loadPersistedState()}this.setStatsSource(o.session);for(let l of o.session.messages)if(l.role==="user")this.addBubble("user",l.content,l.attachments);else{let d=this.addBubble("assistant");if(this.renderMarkdownBubble(d,l.content),d._setRawText?.(l.content),this.attachThreadAffordance(d,l.id,o.session),l.toolCalls&&l.toolCalls.length>0){let h=this.getOrCreateAffordancesRow(d);this.buildToolSummary(l.toolCalls,h)}}this.activityUnsub?.();let c=o.session;this.activityUnsub=c.onActivityChange(()=>{this.getCurrentSession()?.session===c&&this.renderIndicators(c)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(oa(this.selectedAgentName,this.selectedConversationId))}async refreshConversationsList(e){this.conversationsCache=await this.plugin.repository.listConversations(e),this.conversationsCache.length===0&&this.conversationsCache.push({id:ht,name:"Main chat",lastActive:new Date().toISOString(),messageCount:0,isLegacy:!0}),this.renderConvoPanel(),this.leaf.updateHeader()}renderConvoPanel(){if(!this.convoPanelEl||(this.convoPanelEl.empty(),!this.selectedAgentName))return;this.convoPanelEl.createDiv({cls:"af-chat-convo-header",text:"CONVERSATIONS"});let e=this.convoPanelEl.createDiv({cls:"af-chat-convo-new"}),s=e.createSpan({cls:"af-chat-convo-new-icon"});(0,V.setIcon)(s,"plus"),e.createSpan({cls:"af-chat-convo-new-label",text:"New chat"}),e.onclick=()=>void this.handleNewChat();let a=this.convoPanelEl.createDiv({cls:"af-chat-convo-list"});for(let n of this.conversationsCache)this.renderConvoRow(a,n)}renderConvoRow(e,s){let a=s.id===this.selectedConversationId,n=e.createDiv({cls:`af-chat-convo-item${a?" active":""}`}),i=n.createDiv({cls:"af-chat-convo-name",text:s.name});i.title=s.name,i.ondblclick=l=>{l.stopPropagation(),this.beginInlineRename(i,s)};let o=n.createDiv({cls:"af-chat-convo-meta"}),c=s.messageCount===1?"1 msg":`${s.messageCount} msgs`;if(o.appendText(`${c} \xB7 ${ml(s.lastActive)}`),!s.isLegacy){let l=n.createEl("button",{cls:"af-chat-convo-trash",attr:{title:"Delete conversation","aria-label":"Delete conversation"}});(0,V.setIcon)(l,"trash-2"),l.onclick=d=>{d.stopPropagation(),this.handleDeleteConversation(s)}}n.onclick=()=>{s.id!==this.selectedConversationId&&this.handleConvoSelected(s.id)}}beginInlineRename(e,s){let a=s.name,n=document.createElement("input");n.type="text",n.value=a,n.className="af-chat-convo-name-input",e.replaceWith(n),n.focus(),n.select();let i=!1,o=l=>{let d=document.createElement("div");return d.className="af-chat-convo-name",d.textContent=l??a,d.title=l??a,d.ondblclick=h=>{h.stopPropagation();let u=this.conversationsCache.find(p=>p.id===s.id)??s;this.beginInlineRename(d,u)},n.replaceWith(d),d},c=async()=>{if(i)return;i=!0;let l=n.value.trim();if(!l||l===a){o(null);return}try{await this.saveConversationName(s.id,l)}catch(d){new V.Notice(`Couldn't rename: ${d instanceof Error?d.message:String(d)}`),o(null);return}this.renderConvoPanel(),this.leaf.updateHeader()};n.addEventListener("keydown",l=>{l.key==="Enter"?(l.preventDefault(),c()):l.key==="Escape"&&(l.preventDefault(),i=!0,o(null))}),n.addEventListener("blur",()=>{c()})}async saveConversationName(e,s){if(!this.selectedAgentName)return;let n=this.plugin.runtime.getSnapshot().agents.find(l=>l.name===this.selectedAgentName);if(!n)return;try{await this.plugin.repository.renameConversation(n,e,s)}catch{}let i=oa(this.selectedAgentName,e),o=this.sessions.get(i);o&&await o.session.setConversationName(s);let c=this.conversationsCache.findIndex(l=>l.id===e);c>=0&&(this.conversationsCache[c]={...this.conversationsCache[c],name:s})}async handleConvoSelected(e){if(!this.selectedAgentName)return;let s=this.plugin.app.workspace.getLeavesOfType(st);for(let a of s)if(a.view!==this&&a.view instanceof r&&a.view.selectedAgentName===this.selectedAgentName&&a.view.selectedConversationId===e){this.plugin.app.workspace.revealLeaf(a);return}await this.switchToAgent(this.selectedAgentName,e)}async handleDeleteConversation(e){if(!this.selectedAgentName||e.isLegacy)return;let a=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===this.selectedAgentName);if(!a||!window.confirm(`Delete conversation "${e.name}"? -This removes its message history. The agent and its other conversations are untouched.`))return;let i=Ja(this.selectedAgentName,this.selectedConversationId);this.sessions.get(i)?.session.abort(),this.sessions.delete(i);try{await this.plugin.repository.deleteConversation(s,this.selectedConversationId)}catch(c){new Y.Notice(`Couldn't delete: ${c instanceof Error?c.message:String(c)}`);return}await this.refreshConversationsList(s);let o=this.conversationsCache[0]?.id??it;await this.switchToAgent(this.selectedAgentName,o)}renderMarkdownBubble(e,s){let a=e.querySelector(".af-chat-copy-btn"),n=a?.parentNode?.removeChild(a)??null;e.empty(),e.addClass("af-compact-md"),Y.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),this.wireBubbleLinks(e),e.querySelectorAll("pre").forEach(i=>{i.querySelector(".copy-code-button")?.remove();let o=i.querySelector("code");if(!o)return;let c=document.createElement("button");c.className="af-code-copy-btn",c.setAttribute("aria-label","Copy code"),(0,Y.setIcon)(c,"copy"),c.onclick=l=>{l.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{c.addClass("copied"),(0,Y.setIcon)(c,"check"),setTimeout(()=>{c.removeClass("copied"),(0,Y.setIcon)(c,"copy")},1500)})},i.style.position="relative",i.appendChild(c)})})}wireBubbleLinks(e){e.addEventListener("click",s=>{let a=s.target.closest("a");if(!a)return;if(a.classList.contains("internal-link")){s.preventDefault(),s.stopPropagation();let i=a.getAttribute("data-href")||a.getAttribute("href")||a.textContent||"";if(!i)return;let o=s.shiftKey?"split":"tab";this.app.workspace.openLinkText(i,"",o);return}let n=a.getAttribute("href")||"";if(n.startsWith("obsidian://")){s.preventDefault(),s.stopPropagation(),window.location.href=n;return}if(a.classList.contains("external-link")||/^https?:\/\//.test(n)){s.preventDefault(),s.stopPropagation(),window.open(n,"_blank");return}})}addCopyBtn(e,s){let a=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,Y.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,Y.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,Y.setIcon)(a,"copy")},1500)})}}addBubble(e,s,a){if(e==="user"&&a&&a.length>0){let i=this.messagesInner.createDiv({cls:"af-chat-bubble-attachments"});for(let o of a){let c=i.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),l=c.createSpan({cls:"af-chat-pill-icon"}),d=/\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(o);(0,Y.setIcon)(l,d?"image":"file-text"),c.createSpan({cls:"af-chat-pill-name",text:o})}}let n=this.messagesInner.createDiv({cls:`af-chat-bubble af-chat-bubble-${e}`});if(s&&(e==="assistant"?this.renderMarkdownBubble(n,s):n.setText(s)),e==="assistant"){let i=s??"";this.addCopyBtn(n,()=>i),n._setRawText=o=>{i=o}}return this.messagesEl.scrollTop=this.messagesEl.scrollHeight,n}getOrCreateAffordancesRow(e){let s=e.parentElement;if(!s)throw new Error("bubble has no parent");let a=e.nextElementSibling;if(a&&a.classList.contains("af-chat-affordances"))return a;let n=document.createElement("div");return n.className="af-chat-affordances",s.insertBefore(n,e.nextSibling),n}attachThreadAffordance(e,s,a){let n=e.parentElement;if(!n)return;let i=this.getOrCreateAffordancesRow(e);if(i.querySelector(".af-thread-badge"))return;let o=document.createElement("div");o.className="af-thread-badge",o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i.appendChild(o),(0,Y.setIcon)(o,"message-circle");let c=o.createSpan({cls:"af-thread-badge-label"}),l=document.createElement("div");l.className="af-thread-container",l.style.display="none",n.insertBefore(l,i.nextSibling);let d=()=>{let m=a.getThreadIndex()[s]?.messageCount??0;m<=0?c.setText("Thread"):c.setText(`${m} ${m===1?"reply":"replies"}`),m>0&&o.addClass("has-replies")};d();let h=!1,u=async()=>{if(this.threadExpanded.get(s)===!0){l.style.display="none",this.threadExpanded.set(s,!1),o.removeClass("expanded"),this.setStatsSource(a);return}if(this.threadExpanded.set(s,!0),l.style.display="",o.addClass("expanded"),!h)try{let f=await a.openOrCreateThread(s);this.renderThreadContainer(l,f,a,d),h=!0}catch(f){l.setText(`Failed to open thread: ${f instanceof Error?f.message:String(f)}`)}};o.onclick=()=>void u(),o.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),u())}}renderThreadContainer(e,s,a,n){e.empty();let i=e.createDiv({cls:"af-thread-wrap"}),o=i.createDiv({cls:"af-thread-messages"}),c=null,l=null,d=S=>{S?(c||(c=o.createDiv({cls:"af-chat-activity"})),c.setText(`Working\u2026 (${S})`)):c&&(c.remove(),c=null)},h=S=>{if(S&&!l){l=o.createDiv({cls:"af-chat-streaming-dot"});for(let L=0;L<3;L++)l.createSpan()}else!S&&l&&(l.remove(),l=null)},u=(S,L,E)=>{if(S==="user"&&E&&E.length>0){let I=o.createDiv({cls:"af-chat-bubble-attachments"});for(let q of E){let F=I.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),$=F.createSpan({cls:"af-chat-pill-icon"});(0,Y.setIcon)($,q.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),F.createSpan({cls:"af-chat-pill-name",text:q})}}let B=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${S}`});return S==="assistant"?this.renderMarkdownBubble(B,L):B.setText(L),B};for(let S of s.messages)u(S.role,S.content,S.attachments);let p=[],f=[],m=i.createDiv({cls:"af-thread-composer-wrap"}),y=m.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});y.style.display="none";let k=()=>{if(y.empty(),p.length===0&&f.length===0){y.style.display="none";return}y.style.display="flex";for(let S of p){let L=y.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,Y.setIcon)(E,"file-text"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,Y.setIcon)(B,"x"),B.onclick=I=>{I.stopPropagation();let q=p.findIndex(F=>F.path===S.path);q>=0&&p.splice(q,1),k()}}for(let S of f){let L=y.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,Y.setIcon)(E,"image"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,Y.setIcon)(B,"x"),B.onclick=I=>{I.stopPropagation();let q=f.findIndex(F=>F.path===S.path);q>=0&&f.splice(q,1),k()}}},b=()=>{let S=this.app.workspace.getActiveFile();if(!S){new Y.Notice("No active document to attach");return}if(p.some(E=>E.path===S.path)){new Y.Notice(`"${S.name}" is already attached`);return}if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(S.extension.toLowerCase())){new Y.Notice(`Can't attach "${S.name}" \u2014 only text files are supported`);return}p.push(S),k()},v=async S=>{let L=S.type.split("/")[1]?.replace("jpeg","jpg")??"png",E=S.name&&S.name!=="image"?S.name:`pasted-${Date.now()}.${L}`;if(f.some(I=>I.name===E)){new Y.Notice(`"${E}" is already attached`);return}let B=await this.saveImageBlobToVault(S);B&&(f.push(B),k())},g=m.createDiv({cls:"af-chat-input-row af-thread-composer"}),x=g.createEl("button",{cls:"af-chat-attach-btn"});D(x,"plus","af-btn-icon"),x.title="Attach active document",x.onclick=S=>{S.preventDefault(),b()};let T=g.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}});T.addEventListener("paste",S=>{let L=S.clipboardData?.items;if(L)for(let E=0;E{S.preventDefault(),S.stopPropagation(),m.addClass("af-chat-input-dragover")}),m.addEventListener("dragleave",()=>{m.removeClass("af-chat-input-dragover")}),m.addEventListener("drop",S=>{S.preventDefault(),S.stopPropagation(),m.removeClass("af-chat-input-dragover");let L=S.dataTransfer?.files;if(L)for(let E=0;E{T.style.height="auto",T.style.height=`${Math.min(T.scrollHeight,120)}px`,_.style.display=T.value.trim()?"flex":"none"};T.addEventListener("input",R),T.addEventListener("focus",()=>this.setStatsSource(s));let A=async()=>{let S=T.value.trim();if(!S||s.isStreaming)return;let L=await this.buildAttachmentContextFor(p,f),E=[...p.map(F=>F.name),...f.map(F=>F.name)],B=L?`${L}${S}`:void 0;T.value="",R(),p.length=0,f.length=0,k(),u("user",S,E.length>0?E:void 0),h(!0);let I=null,q="";try{await s.sendMessage(S,F=>{if(F.type==="text"){I||(h(!1),d(),I=u("assistant",""),I.empty()),q+=F.content;let $=I.querySelector(".af-chat-stream-text");$||($=I.createDiv({cls:"af-chat-stream-text"})),$.setText(q)}else F.type==="tool_use"?d(F.toolName):F.type==="result"&&(d(),h(!1),I&&this.renderMarkdownBubble(I,q))},B,E.length>0?E:void 0),n()}catch(F){h(!1),d();let $=F instanceof Error?F.message:String(F);o.createDiv({cls:"af-thread-error",text:`Error: ${$}`})}};_.onclick=()=>void A(),T.onkeydown=S=>{S.key==="Enter"&&(S.ctrlKey||S.metaKey)&&(S.preventDefault(),A())}}buildToolSummary(e,s){let n=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),i=n.createEl("details"),o=i.createEl("summary"),c=new Map;for(let h of e){let u=c.get(h.name)??[];h.command&&u.push(h.command),c.set(h.name,u)}let l=o.createSpan({cls:"af-chat-tool-icon"});(0,Y.setIcon)(l,"wrench"),o.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let d=i.createDiv({cls:"af-chat-tool-list"});for(let[h,u]of c){let p=u.length||(c.get(h)?.length??1),f=d.createDiv({cls:"af-chat-tool-item"}),m=p>1?`${h} (\xD7${p})`:h;f.createSpan({cls:"af-chat-tool-name",text:m}),u.length===1&&u[0]&&f.createSpan({cls:"af-chat-tool-cmd",text:u[0]})}return n}renderIndicators(e){let s=e.isStreaming,a=e.currentToolName,n=e.hasCurrentTurnText,i=!!this.messagesInner.querySelector(".af-chat-stream-text"),o=null;if(s&&a?o=`Working\u2026 (${a})`:s&&n&&!i&&(o="Replying\u2026"),o?(this.activityEl?(this.activityEl.parentElement!==this.messagesInner||this.activityEl.nextElementSibling!==null)&&this.messagesInner.appendChild(this.activityEl):this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"}),this.activityEl.textContent!==o&&this.activityEl.setText(o)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null),s&&!a&&!n)if(this.streamingDot)(this.streamingDot.parentElement!==this.messagesInner||this.streamingDot.nextElementSibling!==null)&&this.messagesInner.appendChild(this.streamingDot);else{this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let l=0;l<3;l++)this.streamingDot.createSpan()}else this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(s)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(D(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(D(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.removeClass("af-chat-stop-mode")))}handleStop(){let e=this.getCurrentSession();e&&(e.session.abort(),this.addBubble("error","Generation stopped"))}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new Y.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new Y.Notice(`"${e.name}" is already attached`);return}let s=e.extension.toLowerCase();if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(s)){new Y.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async saveImageBlobToVault(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",a=Date.now(),n=e.name&&e.name!=="image"?e.name:`pasted-${a}.${s}`,i=`${this.plugin.settings.fleetFolder}/chat-images`,o=`${i}/${a}-${n}`;try{this.app.vault.getAbstractFileByPath(i)||await this.app.vault.createFolder(i);let c=await e.arrayBuffer();await this.app.vault.createBinary(o,c);let d=this.app.vault.adapter.getBasePath?.()??"";return{name:n,path:`${d}/${o}`}}catch(c){let l=c instanceof Error?c.message:String(c);return new Y.Notice(`Failed to save image: ${l}`),null}}async buildAttachmentContextFor(e,s){if(e.length===0&&s.length===0)return"";let a=[];for(let n of e)try{let i=await this.app.vault.cachedRead(n);a.push(`### ${n.name} +This removes its message history. The agent and its other conversations are untouched.`))return;let i=oa(this.selectedAgentName,e.id);this.sessions.get(i)?.session.abort(),this.sessions.delete(i);try{await this.plugin.repository.deleteConversation(a,e.id)}catch(o){new V.Notice(`Couldn't delete: ${o instanceof Error?o.message:String(o)}`);return}if(await this.refreshConversationsList(a),e.id===this.selectedConversationId){let o=this.conversationsCache[0]?.id??ht;await this.switchToAgent(this.selectedAgentName,o)}}toggleConvoPanel(){this.convoPanelCollapsed=!this.convoPanelCollapsed,this.userToggledCollapse=!0,this.applyCollapsedClass(),this.leaf.updateHeader()}applyCollapsedClass(){this.convoPanelEl&&(this.convoPanelCollapsed?this.convoPanelEl.addClass("collapsed"):this.convoPanelEl.removeClass("collapsed")),this.collapseBtn&&(this.collapseBtn.empty(),(0,V.setIcon)(this.collapseBtn,this.convoPanelCollapsed?"panel-left-open":"panel-left-close"))}renderMarkdownBubble(e,s){let a=e.querySelector(".af-chat-copy-btn"),n=a?.parentNode?.removeChild(a)??null;e.empty(),e.addClass("af-compact-md"),V.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),this.wireBubbleLinks(e),e.querySelectorAll("pre").forEach(i=>{i.querySelector(".copy-code-button")?.remove();let o=i.querySelector("code");if(!o)return;let c=document.createElement("button");c.className="af-code-copy-btn",c.setAttribute("aria-label","Copy code"),(0,V.setIcon)(c,"copy"),c.onclick=l=>{l.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{c.addClass("copied"),(0,V.setIcon)(c,"check"),setTimeout(()=>{c.removeClass("copied"),(0,V.setIcon)(c,"copy")},1500)})},i.style.position="relative",i.appendChild(c)})})}wireBubbleLinks(e){e.addEventListener("click",s=>{let a=s.target.closest("a");if(!a)return;if(a.classList.contains("internal-link")){s.preventDefault(),s.stopPropagation();let i=a.getAttribute("data-href")||a.getAttribute("href")||a.textContent||"";if(!i)return;let o=s.shiftKey?"split":"tab";this.app.workspace.openLinkText(i,"",o);return}let n=a.getAttribute("href")||"";if(n.startsWith("obsidian://")){s.preventDefault(),s.stopPropagation(),window.location.href=n;return}if(a.classList.contains("external-link")||/^https?:\/\//.test(n)){s.preventDefault(),s.stopPropagation(),window.open(n,"_blank");return}})}addCopyBtn(e,s){let a=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,V.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,V.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,V.setIcon)(a,"copy")},1500)})}}addBubble(e,s,a){if(e==="user"&&a&&a.length>0){let i=this.messagesInner.createDiv({cls:"af-chat-bubble-attachments"});for(let o of a){let c=i.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),l=c.createSpan({cls:"af-chat-pill-icon"}),d=/\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(o);(0,V.setIcon)(l,d?"image":"file-text"),c.createSpan({cls:"af-chat-pill-name",text:o})}}let n=this.messagesInner.createDiv({cls:`af-chat-bubble af-chat-bubble-${e}`});if(s&&(e==="assistant"?this.renderMarkdownBubble(n,s):n.setText(s)),e==="assistant"){let i=s??"";this.addCopyBtn(n,()=>i),n._setRawText=o=>{i=o}}return this.messagesEl.scrollTop=this.messagesEl.scrollHeight,n}getOrCreateAffordancesRow(e){let s=e.parentElement;if(!s)throw new Error("bubble has no parent");let a=e.nextElementSibling;if(a&&a.classList.contains("af-chat-affordances"))return a;let n=document.createElement("div");return n.className="af-chat-affordances",s.insertBefore(n,e.nextSibling),n}attachThreadAffordance(e,s,a){let n=e.parentElement;if(!n)return;let i=this.getOrCreateAffordancesRow(e);if(i.querySelector(".af-thread-badge"))return;let o=document.createElement("div");o.className="af-thread-badge",o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i.appendChild(o),(0,V.setIcon)(o,"message-circle");let c=o.createSpan({cls:"af-thread-badge-label"}),l=document.createElement("div");l.className="af-thread-container",l.style.display="none",n.insertBefore(l,i.nextSibling);let d=()=>{let f=a.getThreadIndex()[s]?.messageCount??0;f<=0?c.setText("Thread"):c.setText(`${f} ${f===1?"reply":"replies"}`),f>0&&o.addClass("has-replies")};d();let h=!1,u=async()=>{if(this.threadExpanded.get(s)===!0){l.style.display="none",this.threadExpanded.set(s,!1),o.removeClass("expanded"),this.setStatsSource(a);return}if(this.threadExpanded.set(s,!0),l.style.display="",o.addClass("expanded"),!h)try{let m=await a.openOrCreateThread(s);this.renderThreadContainer(l,m,a,d),h=!0}catch(m){l.setText(`Failed to open thread: ${m instanceof Error?m.message:String(m)}`)}};o.onclick=()=>void u(),o.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),u())}}renderThreadContainer(e,s,a,n){e.empty();let i=e.createDiv({cls:"af-thread-wrap"}),o=i.createDiv({cls:"af-thread-messages"}),c=null,l=null,d=S=>{S?(c||(c=o.createDiv({cls:"af-chat-activity"})),c.setText(`Working\u2026 (${S})`)):c&&(c.remove(),c=null)},h=S=>{if(S&&!l){l=o.createDiv({cls:"af-chat-streaming-dot"});for(let L=0;L<3;L++)l.createSpan()}else!S&&l&&(l.remove(),l=null)},u=(S,L,E)=>{if(S==="user"&&E&&E.length>0){let I=o.createDiv({cls:"af-chat-bubble-attachments"});for(let q of E){let F=I.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),$=F.createSpan({cls:"af-chat-pill-icon"});(0,V.setIcon)($,q.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),F.createSpan({cls:"af-chat-pill-name",text:q})}}let B=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${S}`});return S==="assistant"?this.renderMarkdownBubble(B,L):B.setText(L),B};for(let S of s.messages)u(S.role,S.content,S.attachments);let p=[],m=[],f=i.createDiv({cls:"af-thread-composer-wrap"}),y=f.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});y.style.display="none";let k=()=>{if(y.empty(),p.length===0&&m.length===0){y.style.display="none";return}y.style.display="flex";for(let S of p){let L=y.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,V.setIcon)(E,"file-text"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,V.setIcon)(B,"x"),B.onclick=I=>{I.stopPropagation();let q=p.findIndex(F=>F.path===S.path);q>=0&&p.splice(q,1),k()}}for(let S of m){let L=y.createDiv({cls:"af-chat-pill"}),E=L.createSpan({cls:"af-chat-pill-icon"});(0,V.setIcon)(E,"image"),L.createSpan({cls:"af-chat-pill-name",text:S.name});let B=L.createSpan({cls:"af-chat-pill-remove"});(0,V.setIcon)(B,"x"),B.onclick=I=>{I.stopPropagation();let q=m.findIndex(F=>F.path===S.path);q>=0&&m.splice(q,1),k()}}},b=()=>{let S=this.app.workspace.getActiveFile();if(!S){new V.Notice("No active document to attach");return}if(p.some(E=>E.path===S.path)){new V.Notice(`"${S.name}" is already attached`);return}if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(S.extension.toLowerCase())){new V.Notice(`Can't attach "${S.name}" \u2014 only text files are supported`);return}p.push(S),k()},v=async S=>{let L=S.type.split("/")[1]?.replace("jpeg","jpg")??"png",E=S.name&&S.name!=="image"?S.name:`pasted-${Date.now()}.${L}`;if(m.some(I=>I.name===E)){new V.Notice(`"${E}" is already attached`);return}let B=await this.saveImageBlobToVault(S);B&&(m.push(B),k())},g=f.createDiv({cls:"af-chat-input-row af-thread-composer"}),x=g.createEl("button",{cls:"af-chat-attach-btn"});P(x,"plus","af-btn-icon"),x.title="Attach active document",x.onclick=S=>{S.preventDefault(),b()};let T=g.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}});T.addEventListener("paste",S=>{let L=S.clipboardData?.items;if(L)for(let E=0;E{S.preventDefault(),S.stopPropagation(),f.addClass("af-chat-input-dragover")}),f.addEventListener("dragleave",()=>{f.removeClass("af-chat-input-dragover")}),f.addEventListener("drop",S=>{S.preventDefault(),S.stopPropagation(),f.removeClass("af-chat-input-dragover");let L=S.dataTransfer?.files;if(L)for(let E=0;E{T.style.height="auto",T.style.height=`${Math.min(T.scrollHeight,120)}px`,_.style.display=T.value.trim()?"flex":"none"};T.addEventListener("input",R),T.addEventListener("focus",()=>this.setStatsSource(s));let A=async()=>{let S=T.value.trim();if(!S||s.isStreaming)return;let L=await this.buildAttachmentContextFor(p,m),E=[...p.map(F=>F.name),...m.map(F=>F.name)],B=L?`${L}${S}`:void 0;T.value="",R(),p.length=0,m.length=0,k(),u("user",S,E.length>0?E:void 0),h(!0);let I=null,q="";try{await s.sendMessage(S,F=>{if(F.type==="text"){I||(h(!1),d(),I=u("assistant",""),I.empty()),q+=F.content;let $=I.querySelector(".af-chat-stream-text");$||($=I.createDiv({cls:"af-chat-stream-text"})),$.setText(q)}else F.type==="tool_use"?d(F.toolName):F.type==="result"&&(d(),h(!1),I&&this.renderMarkdownBubble(I,q))},B,E.length>0?E:void 0),n()}catch(F){h(!1),d();let $=F instanceof Error?F.message:String(F);o.createDiv({cls:"af-thread-error",text:`Error: ${$}`})}};_.onclick=()=>void A(),T.onkeydown=S=>{S.key==="Enter"&&(S.ctrlKey||S.metaKey)&&(S.preventDefault(),A())}}buildToolSummary(e,s){let n=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),i=n.createEl("details"),o=i.createEl("summary"),c=new Map;for(let h of e){let u=c.get(h.name)??[];h.command&&u.push(h.command),c.set(h.name,u)}let l=o.createSpan({cls:"af-chat-tool-icon"});(0,V.setIcon)(l,"wrench"),o.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let d=i.createDiv({cls:"af-chat-tool-list"});for(let[h,u]of c){let p=u.length||(c.get(h)?.length??1),m=d.createDiv({cls:"af-chat-tool-item"}),f=p>1?`${h} (\xD7${p})`:h;m.createSpan({cls:"af-chat-tool-name",text:f}),u.length===1&&u[0]&&m.createSpan({cls:"af-chat-tool-cmd",text:u[0]})}return n}renderIndicators(e){let s=e.isStreaming,a=e.currentToolName,n=e.hasCurrentTurnText,i=!!this.messagesInner.querySelector(".af-chat-stream-text"),o=null;if(s&&a?o=`Working\u2026 (${a})`:s&&n&&!i&&(o="Replying\u2026"),o?(this.activityEl?(this.activityEl.parentElement!==this.messagesInner||this.activityEl.nextElementSibling!==null)&&this.messagesInner.appendChild(this.activityEl):this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"}),this.activityEl.textContent!==o&&this.activityEl.setText(o)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null),s&&!a&&!n)if(this.streamingDot)(this.streamingDot.parentElement!==this.messagesInner||this.streamingDot.nextElementSibling!==null)&&this.messagesInner.appendChild(this.streamingDot);else{this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let l=0;l<3;l++)this.streamingDot.createSpan()}else this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(s)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(P(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(P(this.attachStopBtn,"plus","af-btn-icon"),this.attachStopBtn.title="Attach active document",this.attachStopBtn.removeClass("af-chat-stop-mode")))}handleStop(){let e=this.getCurrentSession();e&&(e.session.abort(),this.addBubble("error","Generation stopped"))}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new V.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new V.Notice(`"${e.name}" is already attached`);return}let s=e.extension.toLowerCase();if(!new Set(["md","txt","json","yaml","yml","toml","csv","xml","html","css","js","ts","py","sh","sql","env","cfg","ini","log"]).has(s)){new V.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async saveImageBlobToVault(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",a=Date.now(),n=e.name&&e.name!=="image"?e.name:`pasted-${a}.${s}`,i=`${this.plugin.settings.fleetFolder}/chat-images`,o=`${i}/${a}-${n}`;try{this.app.vault.getAbstractFileByPath(i)||await this.app.vault.createFolder(i);let c=await e.arrayBuffer();await this.app.vault.createBinary(o,c);let d=this.app.vault.adapter.getBasePath?.()??"";return{name:n,path:`${d}/${o}`}}catch(c){let l=c instanceof Error?c.message:String(c);return new V.Notice(`Failed to save image: ${l}`),null}}async buildAttachmentContextFor(e,s){if(e.length===0&&s.length===0)return"";let a=[];for(let n of e)try{let i=await this.app.vault.cachedRead(n);a.push(`### ${n.name} \`\`\` ${i} \`\`\``)}catch{a.push(`### ${n.name} @@ -11940,6 +11940,6 @@ ${a.join(` --- -`}async attachImageBlob(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",a=e.name&&e.name!=="image"?e.name:`pasted-${Date.now()}.${s}`;if(this.attachedImages.some(i=>i.name===a)){new Y.Notice(`"${a}" is already attached`);return}let n=await this.saveImageBlobToVault(e);n&&(this.attachedImages.push(n),this.renderPills())}removeAttachment(e){this.attachedFiles=this.attachedFiles.filter(s=>s.path!==e),this.attachedImages=this.attachedImages.filter(s=>s.path!==e),this.renderPills()}renderPills(){if(this.pillsRow.empty(),this.attachedFiles.length+this.attachedImages.length===0){this.pillsRow.style.display="none";return}this.pillsRow.style.display="flex";for(let s of this.attachedFiles){let a=this.pillsRow.createDiv({cls:"af-chat-pill"}),n=a.createSpan({cls:"af-chat-pill-icon"});(0,Y.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,Y.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}for(let s of this.attachedImages){let a=this.pillsRow.createDiv({cls:"af-chat-pill"}),n=a.createSpan({cls:"af-chat-pill-icon"});(0,Y.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,Y.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}}buildAttachmentContext(){return this.buildAttachmentContextFor(this.attachedFiles,this.attachedImages)}async handleSend(){let e=this.getCurrentSession();if(!e)return;let s=this.textarea.value.trim();if(!s)return;let a=await this.buildAttachmentContext(),n=[...this.attachedFiles.map(u=>u.name),...this.attachedImages.map(u=>u.name)];this.textarea.value="",this.textarea.style.height="auto",this.sendBtn.style.display="none",this.attachedFiles=[],this.attachedImages=[],this.renderPills();let i=a?`${a}${s}`:void 0;if(this.addBubble("user",s,n.length>0?n:void 0),e.session.isStreaming){e.session.injectMessage(s,i,n.length>0?n:void 0);return}let o=null,c="",l=!1,d=e.session,h=()=>this.getCurrentSession()?.session===d;try{await e.session.sendMessage(s,u=>{if(!h()){o=null,l=!1,c="";return}if(u.type==="text"){(!l||!o||!o.isConnected)&&(o=this.addBubble("assistant"),l=!0),c+=u.content;let p=o.querySelector(".af-chat-stream-text");p||(p=o.createDiv({cls:"af-chat-stream-text"})),p.setText(c)}else if(u.type==="error"){let p=u.errorMessage?.trim()||"The agent's run ended with an error.";this.addBubble("error",`Error: ${p}`)}else if(u.type!=="compacted"){if(u.type==="result"){if(l&&o&&o.isConnected){this.renderMarkdownBubble(o,c),o._setRawText?.(c);let p=e.session.messages[e.session.messages.length-1];if(p&&p.role==="assistant"&&(this.attachThreadAffordance(o,p.id,e.session),u.toolCalls&&u.toolCalls.length>0)){let f=this.getOrCreateAffordancesRow(o);this.buildToolSummary(u.toolCalls,f)}}else u.toolCalls&&u.toolCalls.length>0&&this.buildToolSummary(u.toolCalls);c="",l=!1,o=null}}},i,n.length>0?n:void 0)}catch(u){let p=u instanceof Error?u.message:String(u);p!=="Aborted"&&this.addBubble("error",`Error: ${p}`)}}async handleNewChat(){if(!this.selectedAgentName)return;let s=this.plugin.runtime.getSnapshot().agents.find(i=>i.name===this.selectedAgentName);if(!s)return;let a=ml(),n=`New chat ${new Date().toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}`;try{await this.plugin.repository.createConversation(s,a,n)}catch(i){new Y.Notice(`Couldn't create conversation: ${i instanceof Error?i.message:String(i)}`);return}await this.switchToAgent(this.selectedAgentName,a)}startFreshIntro(e){let s="",a=null,n=!1;e.sendMessage("Please introduce yourself and briefly describe your capabilities and what you can help with.",i=>{i.type==="text"&&(n||(a=this.addBubble("assistant"),n=!0),s+=i.content,a.setText(s))}).then(i=>{n&&a?(this.renderMarkdownBubble(a,s),a._setRawText?.(s)):i.text.trim()&&(a=this.addBubble("assistant"),this.renderMarkdownBubble(a,i.text),a._setRawText?.(i.text)),i.toolCalls.length>0&&this.buildToolSummary(i.toolCalls),this.textarea.focus()}).catch(i=>{let o=i instanceof Error?i.message:String(i);o!=="Aborted"&&this.addBubble("error",`Error: ${o}`)})}};function Jt(r){return r>=1e3?`${(r/1e3).toFixed(r>=1e4?0:1)}k`:`${r}`}var oa=class extends Se.Plugin{settings={...ct};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new Ms;channelCredentials=new js;channelManager;secretStore;statusBarEl;subscribedViews=new Set;vaultChangeTimer;suppressVaultEvents=!1;suppressTimer;runtimeUnsubscribe;async onload(){await this.loadSettings(),this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),this.repository=new ts(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new is(this.repository,this.settings),this.registerView(wt,a=>new fs(a,this)),this.registerView(Ct,a=>new na(a,this)),this.registerView(st,a=>new Xt(a,this)),this.addSettingTab(new Ts(this)),await this.repository.ensureFleetStructure()&&await this.repository.ensureSamples();let e=await this.repository.updateDefaults(this.settings.defaultFileHashes??{});JSON.stringify(e)!==JSON.stringify(this.settings.defaultFileHashes??{})&&(this.settings.defaultFileHashes=e,await this.saveData(this.settings)),await this.runtime.initialize(),await this.verifyClaudeCli(!1),this.addRibbonIcon("bot","Agent Fleet Dashboard",()=>void this.activateDashboardView()),this.addRibbonIcon("message-circle","Agent Chat",()=>{let a=this.app.workspace.getLeavesOfType(st);a.length>0?this.app.workspace.revealLeaf(a[0]):this.openChatView()}),this.addCommands(),this.registerVaultHandlers(),this.registerRuntimeListeners();let s=this.app.secretStorage;this.secretStore=new $s(s),this.channelCredentials.setSecretStore(this.secretStore),!this.settings.secretsMigrated&&this.secretStore.available?(this.channelCredentials.loadCredentials(this.settings.channelCredentials??{}),this.settings.mcpTokens={},this.settings.mcpApiKeys={},this.settings.channelCredentials={},this.settings.secretsMigrated=!0,await this.saveData(this.settings)):this.channelCredentials.loadCredentials(this.secretStore.available?void 0:this.settings.channelCredentials??{}),this.secretStore.available||this.channelCredentials.onChanged(a=>{this.settings.channelCredentials=a,this.saveSettings()}),this.channelManager=new Us({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(a,n)=>{if(a.type==="slack")return new sa(a,n);if(a.type==="telegram")return new aa(a,n);throw new Error(`Channel type \`${a.type}\` is not yet supported in this version.`)}});try{await this.channelManager.start(this.runtime.getSnapshot())}catch(a){console.error("Agent Fleet: channel manager failed to start",a),new Se.Notice("Agent Fleet: channel manager failed to start \u2014 check console.")}this.runtime.onHeartbeatResult((a,n,i)=>{this.channelManager?.broadcastToChannel(n,`*Heartbeat \u2014 ${a}* +`}async attachImageBlob(e){let s=e.type.split("/")[1]?.replace("jpeg","jpg")??"png",a=e.name&&e.name!=="image"?e.name:`pasted-${Date.now()}.${s}`;if(this.attachedImages.some(i=>i.name===a)){new V.Notice(`"${a}" is already attached`);return}let n=await this.saveImageBlobToVault(e);n&&(this.attachedImages.push(n),this.renderPills())}removeAttachment(e){this.attachedFiles=this.attachedFiles.filter(s=>s.path!==e),this.attachedImages=this.attachedImages.filter(s=>s.path!==e),this.renderPills()}renderPills(){if(this.pillsRow.empty(),this.attachedFiles.length+this.attachedImages.length===0){this.pillsRow.style.display="none";return}this.pillsRow.style.display="flex";for(let s of this.attachedFiles){let a=this.pillsRow.createDiv({cls:"af-chat-pill"}),n=a.createSpan({cls:"af-chat-pill-icon"});(0,V.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,V.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}for(let s of this.attachedImages){let a=this.pillsRow.createDiv({cls:"af-chat-pill"}),n=a.createSpan({cls:"af-chat-pill-icon"});(0,V.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,V.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}}buildAttachmentContext(){return this.buildAttachmentContextFor(this.attachedFiles,this.attachedImages)}async handleSend(){let e=this.getCurrentSession();if(!e)return;let s=this.textarea.value.trim();if(!s)return;let a=await this.buildAttachmentContext(),n=[...this.attachedFiles.map(u=>u.name),...this.attachedImages.map(u=>u.name)];this.textarea.value="",this.textarea.style.height="auto",this.sendBtn.style.display="none",this.attachedFiles=[],this.attachedImages=[],this.renderPills();let i=a?`${a}${s}`:void 0;if(this.addBubble("user",s,n.length>0?n:void 0),e.session.isStreaming){e.session.injectMessage(s,i,n.length>0?n:void 0);return}let o=null,c="",l=!1,d=e.session,h=()=>this.getCurrentSession()?.session===d;try{await e.session.sendMessage(s,u=>{if(!h()){o=null,l=!1,c="";return}if(u.type==="text"){(!l||!o||!o.isConnected)&&(o=this.addBubble("assistant"),l=!0),c+=u.content;let p=o.querySelector(".af-chat-stream-text");p||(p=o.createDiv({cls:"af-chat-stream-text"})),p.setText(c)}else if(u.type==="error"){let p=u.errorMessage?.trim()||"The agent's run ended with an error.";this.addBubble("error",`Error: ${p}`)}else if(u.type!=="compacted"){if(u.type==="result"){if(l&&o&&o.isConnected){this.renderMarkdownBubble(o,c),o._setRawText?.(c);let p=e.session.messages[e.session.messages.length-1];if(p&&p.role==="assistant"&&(this.attachThreadAffordance(o,p.id,e.session),u.toolCalls&&u.toolCalls.length>0)){let m=this.getOrCreateAffordancesRow(o);this.buildToolSummary(u.toolCalls,m)}}else u.toolCalls&&u.toolCalls.length>0&&this.buildToolSummary(u.toolCalls);c="",l=!1,o=null}}},i,n.length>0?n:void 0)}catch(u){let p=u instanceof Error?u.message:String(u);p!=="Aborted"&&this.addBubble("error",`Error: ${p}`)}}async handleNewChat(){if(!this.selectedAgentName)return;let s=this.plugin.runtime.getSnapshot().agents.find(i=>i.name===this.selectedAgentName);if(!s)return;let a=fl(),n=`New chat ${new Date().toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}`;try{await this.plugin.repository.createConversation(s,a,n)}catch(i){new V.Notice(`Couldn't create conversation: ${i instanceof Error?i.message:String(i)}`);return}await this.switchToAgent(this.selectedAgentName,a)}startFreshIntro(e){let s="",a=null,n=!1;e.sendMessage("Please introduce yourself and briefly describe your capabilities and what you can help with.",i=>{i.type==="text"&&(n||(a=this.addBubble("assistant"),n=!0),s+=i.content,a.setText(s))}).then(i=>{n&&a?(this.renderMarkdownBubble(a,s),a._setRawText?.(s)):i.text.trim()&&(a=this.addBubble("assistant"),this.renderMarkdownBubble(a,i.text),a._setRawText?.(i.text)),i.toolCalls.length>0&&this.buildToolSummary(i.toolCalls),this.textarea.focus()}).catch(i=>{let o=i instanceof Error?i.message:String(i);o!=="Aborted"&&this.addBubble("error",`Error: ${o}`)})}};function Jt(r){return r>=1e3?`${(r/1e3).toFixed(r>=1e4?0:1)}k`:`${r}`}var la=class extends Se.Plugin{settings={...lt};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new Ms;channelCredentials=new js;channelManager;secretStore;statusBarEl;subscribedViews=new Set;vaultChangeTimer;suppressVaultEvents=!1;suppressTimer;runtimeUnsubscribe;async onload(){await this.loadSettings(),this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),this.repository=new ts(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new is(this.repository,this.settings),this.registerView(wt,a=>new ms(a,this)),this.registerView(Ct,a=>new na(a,this)),this.registerView(st,a=>new Xt(a,this)),this.addSettingTab(new Ts(this)),await this.repository.ensureFleetStructure()&&await this.repository.ensureSamples();let e=await this.repository.updateDefaults(this.settings.defaultFileHashes??{});JSON.stringify(e)!==JSON.stringify(this.settings.defaultFileHashes??{})&&(this.settings.defaultFileHashes=e,await this.saveData(this.settings)),await this.runtime.initialize(),await this.verifyClaudeCli(!1),this.addRibbonIcon("bot","Agent Fleet Dashboard",()=>void this.activateDashboardView()),this.addRibbonIcon("message-circle","Agent Chat",()=>{let a=this.app.workspace.getLeavesOfType(st);a.length>0?this.app.workspace.revealLeaf(a[0]):this.openChatView()}),this.addCommands(),this.registerVaultHandlers(),this.registerRuntimeListeners();let s=this.app.secretStorage;this.secretStore=new $s(s),this.channelCredentials.setSecretStore(this.secretStore),!this.settings.secretsMigrated&&this.secretStore.available?(this.channelCredentials.loadCredentials(this.settings.channelCredentials??{}),this.settings.mcpTokens={},this.settings.mcpApiKeys={},this.settings.channelCredentials={},this.settings.secretsMigrated=!0,await this.saveData(this.settings)):this.channelCredentials.loadCredentials(this.secretStore.available?void 0:this.settings.channelCredentials??{}),this.secretStore.available||this.channelCredentials.onChanged(a=>{this.settings.channelCredentials=a,this.saveSettings()}),this.channelManager=new Us({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(a,n)=>{if(a.type==="slack")return new sa(a,n);if(a.type==="telegram")return new aa(a,n);throw new Error(`Channel type \`${a.type}\` is not yet supported in this version.`)}});try{await this.channelManager.start(this.runtime.getSnapshot())}catch(a){console.error("Agent Fleet: channel manager failed to start",a),new Se.Notice("Agent Fleet: channel manager failed to start \u2014 check console.")}this.runtime.onHeartbeatResult((a,n,i)=>{this.channelManager?.broadcastToChannel(n,`*Heartbeat \u2014 ${a}* -${i}`).catch(o=>{console.warn(`Agent Fleet: heartbeat channel post failed for ${a}`,o)})}),this.refreshStatusBar(),this.mcpManager.setAuthManager(this.mcpAuth),this.mcpManager.getServers().then(()=>{this.notifyViews()}),this.registerInterval(window.setInterval(()=>void this.mcpManager.refreshProbeTokens(),30*6e4)),new Se.Notice("Agent Fleet loaded.")}onunload(){this.runtimeUnsubscribe?.(),this.runtimeUnsubscribe=void 0,this.vaultChangeTimer&&(clearTimeout(this.vaultChangeTimer),this.vaultChangeTimer=void 0),this.suppressTimer&&(clearTimeout(this.suppressTimer),this.suppressTimer=void 0),this.app.workspace.detachLeavesOfType(wt),this.app.workspace.detachLeavesOfType(Ct),this.app.workspace.detachLeavesOfType(st),this.runtime?.shutdown(),this.channelManager?.stop()}async loadSettings(){this.settings={...ct,...await this.loadData()}}async saveSettings(){this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),await this.saveData(this.settings),this.repository&&this.runtime&&(this.runtime.shutdown(),this.repository=new ts(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new is(this.repository,this.settings),await this.repository.ensureFleetStructure(),await this.runtime.initialize(),this.registerRuntimeListeners(),this.notifyViews(),this.refreshStatusBar(),this.channelCredentials.loadCredentials(this.secretStore.available?void 0:this.settings.channelCredentials??{}),this.channelManager?.reconcile(this.runtime.getSnapshot()))}subscribeView(t){this.subscribedViews.add(t)}unsubscribeView(t){this.subscribedViews.delete(t)}async activateDashboardView(){let t=this.app.workspace.getLeavesOfType(wt);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:wt,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let a=this.app.workspace.getLeavesOfType(wt)[0];if(a){let n=a.view;n instanceof fs&&n.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(Ct,"left");await t.setViewState({type:Ct,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(st);for(let a of s)if(a.view instanceof Xt&&a.view.selectedAgentName===t){this.app.workspace.revealLeaf(a);return}}let e=this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0);await e.setViewState({type:st,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof Xt&&e.view.selectAgent(t)}async refreshFromVault(){this.suppressVaultEvents=!0;try{await this.runtime.refreshFromVault(),this.notifyViews(),this.refreshStatusBar(),this.channelManager?.reconcile(this.runtime.getSnapshot())}finally{this.suppressTimer&&clearTimeout(this.suppressTimer),this.suppressTimer=setTimeout(()=>{this.suppressTimer=void 0,this.suppressVaultEvents=!1},500)}}refreshStatusBar(){if(!this.settings.showStatusBar){this.statusBarEl?.detach(),this.statusBarEl=void 0;return}this.statusBarEl||(this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.onclick=()=>void this.activateDashboardView());let t=this.runtime.getFleetStatus();this.statusBarEl.setText(`\u{1F916} ${t.running} running \xB7 ${t.pending} pending \xB7 ${t.completedToday} completed today`)}async verifyClaudeCli(t=!0){let e=await this.resolveClaudeCliPath(this.settings.claudeCliPath);return this.settings.claudeCliPath=e,await new Promise(s=>{let a=pt(e,["--version"]),n="";a.stderr.on("data",i=>{n+=i.toString()}),a.on("close",i=>{let o=i===0;o||console.error("Agent Fleet: Claude CLI verification failed",n),t&&new Se.Notice(o?"Claude CLI available.":"Claude CLI verification failed \u2014 check Claude CLI Path in settings."),s(o)}),a.on("error",i=>{console.error("Agent Fleet: Claude CLI verification error",i),t&&new Se.Notice("Claude CLI verification failed \u2014 check Claude CLI Path in settings."),s(!1)})})}async openPath(t){let e=this.app.vault.getAbstractFileByPath((0,Se.normalizePath)(t));e instanceof Se.TFile&&await this.app.workspace.getLeaf(!0).openFile(e)}async createAgentTemplate(){await this.navigateDashboard("create-agent")}async createSkillTemplate(){await this.navigateDashboard("create-skill")}async openCreateTask(){await this.navigateDashboard("create-task")}async runAgentPrompt(t){let e=this.repository.getAgentByName(t);if(!e){new Se.Notice(`Unknown agent: ${t}`);return}await this.runtime.runAgentNow(e,"Run now and summarize the current state.")}async chatWithAgent(t){if(!this.repository.getAgentByName(t)){new Se.Notice(`Unknown agent: ${t}`);return}await this.openChatView(t)}async deleteAgent(t){if(!this.repository.getAgentByName(t)){new Se.Notice(`Unknown agent: ${t}`);return}let s=this.repository.getTasksForAgent(t),a=this.runtime.getRecentRuns().filter(o=>o.agent===t),n=this.repository.getMemoryPath(t),i=!!this.app.vault.getAbstractFileByPath(n);new Ss(this.app,{agentName:t,taskCount:s.length,runCount:a.length,hasMemory:i},async o=>{let c=await this.repository.deleteAgent(t,o);await new Promise(l=>setTimeout(l,200)),await this.refreshFromVault(),new Se.Notice(`Deleted agent "${t}" (${c.trashedFiles.length} files moved to trash)`),await this.navigateDashboard("agents")}).open()}async toggleAgent(t,e){let s=this.repository.getAgentByName(t);if(!s)return;let a=this.app.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof Se.TFile))return;let n=await this.app.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);i.enabled=e,await this.app.vault.modify(a,J(i,o)),await this.refreshFromVault()}addCommands(){this.addCommand({id:"open-dashboard",name:"Open Dashboard",callback:()=>void this.activateDashboardView()}),this.addCommand({id:"open-agents-panel",name:"Open Agents Panel",callback:()=>void this.activateAgentsView()}),this.addCommand({id:"open-chat",name:"Open Agent Chat",callback:()=>{let t=this.app.workspace.getLeavesOfType(st);t.length>0?this.app.workspace.revealLeaf(t[0]):this.openChatView()}}),this.addCommand({id:"new-chat-tab",name:"New Chat Tab",callback:()=>void this.openChatView()}),this.addCommand({id:"new-agent",name:"New Agent",callback:()=>void this.createAgentTemplate()}),this.addCommand({id:"new-skill",name:"New Skill",callback:()=>void this.createSkillTemplate()}),this.addCommand({id:"new-task",name:"New Task",callback:()=>void this.openCreateTask()}),this.addCommand({id:"run-agent-now",name:"Run Agent Now",callback:()=>{let t=this.runtime.getSnapshot().agents[0];t?this.runAgentPrompt(t.name):new Se.Notice("No agents configured.")}}),this.addCommand({id:"pause-all",name:"Pause All",callback:()=>{this.runtime.scheduler.pauseAll(),new Se.Notice("Agent Fleet paused.")}}),this.addCommand({id:"resume-all",name:"Resume All",callback:()=>{this.runtime.scheduler.resumeAll(),new Se.Notice("Agent Fleet resumed.")}}),this.addCommand({id:"view-fleet-status",name:"View Fleet Status",callback:()=>{let t=this.runtime.getFleetStatus();new Se.Notice(`${t.running} running \xB7 ${t.pending} pending \xB7 ${t.completedToday} completed today`)}})}debouncedVaultRefresh(){this.suppressVaultEvents||(this.vaultChangeTimer&&clearTimeout(this.vaultChangeTimer),this.vaultChangeTimer=setTimeout(()=>{this.suppressVaultEvents||this.refreshFromVault()},500))}registerVaultHandlers(){this.registerEvent(this.app.vault.on("create",t=>{t instanceof Se.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof Se.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("rename",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("delete",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()}))}registerRuntimeListeners(){this.runtimeUnsubscribe?.(),this.runtimeUnsubscribe=this.runtime.subscribe(()=>{this.notifyViews(),this.refreshStatusBar()})}notifyViews(){for(let t of this.subscribedViews)t.render()}async resolveClaudeCliPath(t){let e=rn(t);for(let s of e)if(pa(s)&&(0,Ji.existsSync)(s)||!pa(s)&&await new Promise(n=>{let i=pt(s,["--version"]);i.on("close",o=>n(o===0)),i.on("error",()=>n(!1))}))return s;return t}getLeafForView(t,e){let s=this.app.workspace.getLeavesOfType(t)[0];return s||(e==="right"?this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0):this.app.workspace.getLeftLeaf(!1)??this.app.workspace.getLeaf(!1))}}; +${i}`).catch(o=>{console.warn(`Agent Fleet: heartbeat channel post failed for ${a}`,o)})}),this.refreshStatusBar(),this.mcpManager.setAuthManager(this.mcpAuth),this.mcpManager.getServers().then(()=>{this.notifyViews()}),this.registerInterval(window.setInterval(()=>void this.mcpManager.refreshProbeTokens(),30*6e4)),new Se.Notice("Agent Fleet loaded.")}onunload(){this.runtimeUnsubscribe?.(),this.runtimeUnsubscribe=void 0,this.vaultChangeTimer&&(clearTimeout(this.vaultChangeTimer),this.vaultChangeTimer=void 0),this.suppressTimer&&(clearTimeout(this.suppressTimer),this.suppressTimer=void 0),this.app.workspace.detachLeavesOfType(wt),this.app.workspace.detachLeavesOfType(Ct),this.app.workspace.detachLeavesOfType(st),this.runtime?.shutdown(),this.channelManager?.stop()}async loadSettings(){this.settings={...lt,...await this.loadData()}}async saveSettings(){this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),await this.saveData(this.settings),this.repository&&this.runtime&&(this.runtime.shutdown(),this.repository=new ts(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new is(this.repository,this.settings),await this.repository.ensureFleetStructure(),await this.runtime.initialize(),this.registerRuntimeListeners(),this.notifyViews(),this.refreshStatusBar(),this.channelCredentials.loadCredentials(this.secretStore.available?void 0:this.settings.channelCredentials??{}),this.channelManager?.reconcile(this.runtime.getSnapshot()))}subscribeView(t){this.subscribedViews.add(t)}unsubscribeView(t){this.subscribedViews.delete(t)}async activateDashboardView(){let t=this.app.workspace.getLeavesOfType(wt);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:wt,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let a=this.app.workspace.getLeavesOfType(wt)[0];if(a){let n=a.view;n instanceof ms&&n.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(Ct,"left");await t.setViewState({type:Ct,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(st);for(let a of s)if(a.view instanceof Xt&&a.view.selectedAgentName===t){this.app.workspace.revealLeaf(a);return}}let e=this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0);await e.setViewState({type:st,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof Xt&&e.view.selectAgent(t)}async refreshFromVault(){this.suppressVaultEvents=!0;try{await this.runtime.refreshFromVault(),this.notifyViews(),this.refreshStatusBar(),this.channelManager?.reconcile(this.runtime.getSnapshot())}finally{this.suppressTimer&&clearTimeout(this.suppressTimer),this.suppressTimer=setTimeout(()=>{this.suppressTimer=void 0,this.suppressVaultEvents=!1},500)}}refreshStatusBar(){if(!this.settings.showStatusBar){this.statusBarEl?.detach(),this.statusBarEl=void 0;return}this.statusBarEl||(this.statusBarEl=this.addStatusBarItem(),this.statusBarEl.onclick=()=>void this.activateDashboardView());let t=this.runtime.getFleetStatus();this.statusBarEl.setText(`\u{1F916} ${t.running} running \xB7 ${t.pending} pending \xB7 ${t.completedToday} completed today`)}async verifyClaudeCli(t=!0){let e=await this.resolveClaudeCliPath(this.settings.claudeCliPath);return this.settings.claudeCliPath=e,await new Promise(s=>{let a=pt(e,["--version"]),n="";a.stderr.on("data",i=>{n+=i.toString()}),a.on("close",i=>{let o=i===0;o||console.error("Agent Fleet: Claude CLI verification failed",n),t&&new Se.Notice(o?"Claude CLI available.":"Claude CLI verification failed \u2014 check Claude CLI Path in settings."),s(o)}),a.on("error",i=>{console.error("Agent Fleet: Claude CLI verification error",i),t&&new Se.Notice("Claude CLI verification failed \u2014 check Claude CLI Path in settings."),s(!1)})})}async openPath(t){let e=this.app.vault.getAbstractFileByPath((0,Se.normalizePath)(t));e instanceof Se.TFile&&await this.app.workspace.getLeaf(!0).openFile(e)}async createAgentTemplate(){await this.navigateDashboard("create-agent")}async createSkillTemplate(){await this.navigateDashboard("create-skill")}async openCreateTask(){await this.navigateDashboard("create-task")}async runAgentPrompt(t){let e=this.repository.getAgentByName(t);if(!e){new Se.Notice(`Unknown agent: ${t}`);return}await this.runtime.runAgentNow(e,"Run now and summarize the current state.")}async chatWithAgent(t){if(!this.repository.getAgentByName(t)){new Se.Notice(`Unknown agent: ${t}`);return}await this.openChatView(t)}async deleteAgent(t){if(!this.repository.getAgentByName(t)){new Se.Notice(`Unknown agent: ${t}`);return}let s=this.repository.getTasksForAgent(t),a=this.runtime.getRecentRuns().filter(o=>o.agent===t),n=this.repository.getMemoryPath(t),i=!!this.app.vault.getAbstractFileByPath(n);new Ss(this.app,{agentName:t,taskCount:s.length,runCount:a.length,hasMemory:i},async o=>{let c=await this.repository.deleteAgent(t,o);await new Promise(l=>setTimeout(l,200)),await this.refreshFromVault(),new Se.Notice(`Deleted agent "${t}" (${c.trashedFiles.length} files moved to trash)`),await this.navigateDashboard("agents")}).open()}async toggleAgent(t,e){let s=this.repository.getAgentByName(t);if(!s)return;let a=this.app.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof Se.TFile))return;let n=await this.app.vault.cachedRead(a),{frontmatter:i,body:o}=ne(n);i.enabled=e,await this.app.vault.modify(a,J(i,o)),await this.refreshFromVault()}addCommands(){this.addCommand({id:"open-dashboard",name:"Open Dashboard",callback:()=>void this.activateDashboardView()}),this.addCommand({id:"open-agents-panel",name:"Open Agents Panel",callback:()=>void this.activateAgentsView()}),this.addCommand({id:"open-chat",name:"Open Agent Chat",callback:()=>{let t=this.app.workspace.getLeavesOfType(st);t.length>0?this.app.workspace.revealLeaf(t[0]):this.openChatView()}}),this.addCommand({id:"new-chat-tab",name:"New Chat Tab",callback:()=>void this.openChatView()}),this.addCommand({id:"new-agent",name:"New Agent",callback:()=>void this.createAgentTemplate()}),this.addCommand({id:"new-skill",name:"New Skill",callback:()=>void this.createSkillTemplate()}),this.addCommand({id:"new-task",name:"New Task",callback:()=>void this.openCreateTask()}),this.addCommand({id:"run-agent-now",name:"Run Agent Now",callback:()=>{let t=this.runtime.getSnapshot().agents[0];t?this.runAgentPrompt(t.name):new Se.Notice("No agents configured.")}}),this.addCommand({id:"pause-all",name:"Pause All",callback:()=>{this.runtime.scheduler.pauseAll(),new Se.Notice("Agent Fleet paused.")}}),this.addCommand({id:"resume-all",name:"Resume All",callback:()=>{this.runtime.scheduler.resumeAll(),new Se.Notice("Agent Fleet resumed.")}}),this.addCommand({id:"view-fleet-status",name:"View Fleet Status",callback:()=>{let t=this.runtime.getFleetStatus();new Se.Notice(`${t.running} running \xB7 ${t.pending} pending \xB7 ${t.completedToday} completed today`)}})}debouncedVaultRefresh(){this.suppressVaultEvents||(this.vaultChangeTimer&&clearTimeout(this.vaultChangeTimer),this.vaultChangeTimer=setTimeout(()=>{this.suppressVaultEvents||this.refreshFromVault()},500))}registerVaultHandlers(){this.registerEvent(this.app.vault.on("create",t=>{t instanceof Se.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof Se.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("rename",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("delete",t=>{t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()}))}registerRuntimeListeners(){this.runtimeUnsubscribe?.(),this.runtimeUnsubscribe=this.runtime.subscribe(()=>{this.notifyViews(),this.refreshStatusBar()})}notifyViews(){for(let t of this.subscribedViews)t.render()}async resolveClaudeCliPath(t){let e=rn(t);for(let s of e)if(fa(s)&&(0,Ji.existsSync)(s)||!fa(s)&&await new Promise(n=>{let i=pt(s,["--version"]);i.on("close",o=>n(o===0)),i.on("error",()=>n(!1))}))return s;return t}getLeafForView(t,e){let s=this.app.workspace.getLeavesOfType(t)[0];return s||(e==="right"?this.app.workspace.getRightLeaf(!1)??this.app.workspace.getLeaf(!0):this.app.workspace.getLeftLeaf(!1)??this.app.workspace.getLeaf(!1))}}; diff --git a/styles.css b/styles.css index b14f56a..0da805f 100644 --- a/styles.css +++ b/styles.css @@ -4526,3 +4526,211 @@ color: var(--af-text-secondary); margin-bottom: 6px; } + +/* ─── Chat: conversations side rail ───────────────────────────────────── */ +/* Adjacent to the messages column inside the chat view, this rail lists all + * parallel conversations for the currently-selected agent. Styled to match + * the plugin-level left sidebar (af-sidebar-*) so it reads as a first-class + * part of the plugin, not a bolted-on widget. */ + +.af-chat-view-body { + display: flex; + flex: 1; + min-height: 0; + overflow: hidden; +} + +.af-chat-main { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; + overflow: hidden; +} + +.af-chat-convo-panel { + width: 200px; + flex-shrink: 0; + border-right: 1px solid var(--af-border); + background: var(--af-bg-secondary); + display: flex; + flex-direction: column; + overflow-y: auto; + overflow-x: hidden; +} + +/* Collapsed state — hidden from layout entirely. The toggle button in the + * header reveals it again. */ +.af-chat-convo-panel.collapsed { + display: none; +} + +.af-chat-convo-header { + padding: 12px 14px 6px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.8px; + color: var(--af-text-faint); + text-transform: uppercase; +} + +/* "+ New chat" — sits at the top of the rail, styled as a list row (not a + * button) so it belongs with the conversation list visually. */ +.af-chat-convo-new { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + margin: 2px 6px 6px; + border-radius: 4px; + cursor: pointer; + color: var(--af-text-secondary); + transition: background var(--af-transition), color var(--af-transition); + font-size: 12px; +} + +.af-chat-convo-new:hover { + background: var(--background-modifier-hover); + color: var(--af-accent); +} + +.af-chat-convo-new-icon { + display: inline-flex; + align-items: center; +} + +.af-chat-convo-new-icon svg { + width: 14px; + height: 14px; +} + +.af-chat-convo-list { + display: flex; + flex-direction: column; + padding: 0 0 12px; +} + +/* Conversation row — two-line layout (name on top, "12 msgs · 2h ago" below) + * mirroring the way Slack/ChatGPT list DMs/chats. Hover reveals the trash. */ +.af-chat-convo-item { + position: relative; + padding: 6px 10px; + margin: 1px 6px; + border-radius: 4px; + cursor: pointer; + transition: background var(--af-transition); + /* Reserve a fixed gutter for the trash button so the row doesn't shift + * width when it appears on hover. */ + padding-right: 30px; +} + +.af-chat-convo-item:hover { + background: var(--background-modifier-hover); +} + +.af-chat-convo-item.active { + background: var(--af-accent-bg); +} + +.af-chat-convo-item.active .af-chat-convo-name { + color: var(--af-accent); + font-weight: 600; +} + +.af-chat-convo-name { + font-size: 12px; + color: var(--af-text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 1.4; +} + +/* Inline-rename input — replaces the name span in place. Styled to match + * the row's existing typography so the swap is visually quiet. */ +.af-chat-convo-name-input { + font-size: 12px; + line-height: 1.4; + padding: 1px 4px; + margin: -1px -4px; + border: 1px solid var(--af-accent); + border-radius: 3px; + background: var(--af-card-bg); + color: var(--af-text-primary); + width: 100%; + outline: none; +} + +.af-chat-convo-meta { + font-size: 10px; + color: var(--af-text-faint); + margin-top: 1px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Trash button — fades in on row hover. `.active` rows show it on focus + * too so keyboard users can reach it via Tab. */ +.af-chat-convo-trash { + position: absolute; + top: 50%; + right: 6px; + transform: translateY(-50%); + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background: transparent; + border: none; + border-radius: 4px; + color: var(--af-text-faint); + cursor: pointer; + opacity: 0; + transition: opacity var(--af-transition), color var(--af-transition), background var(--af-transition); +} + +.af-chat-convo-item:hover .af-chat-convo-trash, +.af-chat-convo-trash:focus-visible { + opacity: 1; +} + +.af-chat-convo-trash:hover { + color: var(--af-red); + background: var(--af-red-bg, rgba(243, 139, 168, 0.12)); +} + +.af-chat-convo-trash svg { + width: 13px; + height: 13px; +} + +/* Collapse toggle in the chat-view header — small icon button, no border, + * intentionally lower-emphasis than the agent dropdown next to it. */ +.af-chat-convo-collapse-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + padding: 0; + background: transparent; + border: none; + border-radius: 4px; + color: var(--af-text-muted); + cursor: pointer; + transition: background var(--af-transition), color var(--af-transition); + flex-shrink: 0; +} + +.af-chat-convo-collapse-btn:hover { + background: var(--background-modifier-hover); + color: var(--af-text-primary); +} + +.af-chat-convo-collapse-btn svg { + width: 16px; + height: 16px; +}