obsidian-agent-fleet 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/plugin/main.js CHANGED
@@ -1,10 +1,10 @@
1
- "use strict";var Wi=Object.create;var vs=Object.defineProperty;var zi=Object.getOwnPropertyDescriptor;var Gi=Object.getOwnPropertyNames;var Vi=Object.getPrototypeOf,Yi=Object.prototype.hasOwnProperty;var Ge=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),Ki=(r,t)=>{for(var e in t)vs(r,e,{get:t[e],enumerable:!0})},Ya=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Gi(t))!Yi.call(r,a)&&a!==e&&vs(r,a,{get:()=>t[a],enumerable:!(s=zi(t,a))||s.enumerable});return r};var Ve=(r,t,e)=>(e=r!=null?Wi(Vi(r)):{},Ya(t||!r||!r.__esModule?vs(e,"default",{value:r,enumerable:!0}):e,r)),Ji=r=>Ya(vs({},"__esModule",{value:!0}),r);var ft=Ge((rc,An)=>{"use strict";var Cn=["nodebuffer","arraybuffer","fragments"],_n=typeof Blob<"u";_n&&Cn.push("blob");An.exports={BINARY_TYPES:Cn,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:_n,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var is=Ge((oc,Us)=>{"use strict";var{EMPTY_BUFFER:kr}=ft(),xa=Buffer[Symbol.species];function xr(r,t){if(r.length===0)return kr;if(r.length===1)return r[0];let e=Buffer.allocUnsafe(t),s=0;for(let a=0;a<r.length;a++){let n=r[a];e.set(n,s),s+=n.length}return s<t?new xa(e.buffer,e.byteOffset,s):e}function En(r,t,e,s,a){for(let n=0;n<a;n++)e[s+n]=r[n]^t[n&3]}function Pn(r,t){for(let e=0;e<r.length;e++)r[e]^=t[e&3]}function Sr(r){return r.length===r.buffer.byteLength?r.buffer:r.buffer.slice(r.byteOffset,r.byteOffset+r.length)}function Sa(r){if(Sa.readOnly=!0,Buffer.isBuffer(r))return r;let t;return r instanceof ArrayBuffer?t=new xa(r):ArrayBuffer.isView(r)?t=new xa(r.buffer,r.byteOffset,r.byteLength):(t=Buffer.from(r),Sa.readOnly=!1),t}Us.exports={concat:xr,mask:En,toArrayBuffer:Sr,toBuffer:Sa,unmask:Pn};if(!process.env.WS_NO_BUFFER_UTIL)try{let r=require("bufferutil");Us.exports.mask=function(t,e,s,a,n){n<48?En(t,e,s,a,n):r.mask(t,e,s,a,n)},Us.exports.unmask=function(t,e){t.length<32?Pn(t,e):r.unmask(t,e)}}catch{}});var In=Ge((lc,Rn)=>{"use strict";var Dn=Symbol("kDone"),Ta=Symbol("kRun"),Ca=class{constructor(t){this[Dn]=()=>{this.pending--,this[Ta]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Ta]()}[Ta](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[Dn])}}};Rn.exports=Ca});var jt=Ge((cc,On)=>{"use strict";var rs=require("zlib"),Ln=is(),Tr=In(),{kStatusCode:Mn}=ft(),Cr=Buffer[Symbol.species],_r=Buffer.from([0,0,255,255]),js=Symbol("permessage-deflate"),mt=Symbol("total-length"),Ut=Symbol("callback"),wt=Symbol("buffers"),$t=Symbol("error"),$s,_a=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,!$s){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;$s=new Tr(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[Ut];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){$s.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){$s.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"?rs.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=rs.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[js]=this,this._inflate[mt]=0,this._inflate[wt]=[],this._inflate.on("error",Er),this._inflate.on("data",Fn)}this._inflate[Ut]=s,this._inflate.write(t),e&&this._inflate.write(_r),this._inflate.flush(()=>{let n=this._inflate[$t];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=Ln.concat(this._inflate[wt],this._inflate[mt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[mt]=0,this._inflate[wt]=[],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"?rs.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=rs.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[mt]=0,this._deflate[wt]=[],this._deflate.on("data",Ar)}this._deflate[Ut]=s,this._deflate.write(t),this._deflate.flush(rs.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=Ln.concat(this._deflate[wt],this._deflate[mt]);e&&(n=new Cr(n.buffer,n.byteOffset,n.length-4)),this._deflate[Ut]=null,this._deflate[mt]=0,this._deflate[wt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};On.exports=_a;function Ar(r){this[wt].push(r),this[mt]+=r.length}function Fn(r){if(this[mt]+=r.length,this[js]._maxPayload<1||this[mt]<=this[js]._maxPayload){this[wt].push(r);return}this[$t]=new RangeError("Max payload size exceeded"),this[$t].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[$t][Mn]=1009,this.removeListener("data",Fn),this.reset()}function Er(r){if(this[js]._inflate=null,this[$t]){this[Ut](this[$t]);return}r[Mn]=1007,this[Ut](r)}});var Ht=Ge((dc,Hs)=>{"use strict";var{isUtf8:Nn}=require("buffer"),{hasBlob:Pr}=ft(),Dr=[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 Rr(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Aa(r){let t=r.length,e=0;for(;e<t;)if((r[e]&128)===0)e++;else if((r[e]&224)===192){if(e+1===t||(r[e+1]&192)!==128||(r[e]&254)===192)return!1;e+=2}else if((r[e]&240)===224){if(e+2>=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 Ir(r){return Pr&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}Hs.exports={isBlob:Ir,isValidStatusCode:Rr,isValidUTF8:Aa,tokenChars:Dr};if(Nn)Hs.exports.isValidUTF8=function(r){return r.length<24?Aa(r):Nn(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");Hs.exports.isValidUTF8=function(t){return t.length<32?Aa(t):r(t)}}catch{}});var Ia=Ge((hc,Wn)=>{"use strict";var{Writable:Lr}=require("stream"),Bn=jt(),{BINARY_TYPES:Mr,EMPTY_BUFFER:Un,kStatusCode:Fr,kWebSocket:Or}=ft(),{concat:Ea,toArrayBuffer:Nr,unmask:Br}=is(),{isValidStatusCode:Ur,isValidUTF8:$n}=Ht(),qs=Buffer[Symbol.species],st=0,jn=1,Hn=2,qn=3,Pa=4,Da=5,Ws=6,Ra=class extends Lr{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Mr[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Or]=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=st}_write(t,e,s){if(this._opcode===8&&this._state==st)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<this._buffers[0].length){let s=this._buffers[0];return this._buffers[0]=new qs(s.buffer,s.byteOffset+t,s.length-t),new qs(s.buffer,s.byteOffset,t)}let e=Buffer.allocUnsafe(t);do{let s=this._buffers[0],a=e.length-t;t>=s.length?e.set(this._buffers.shift(),a):(e.set(new Uint8Array(s.buffer,s.byteOffset,t),a),this._buffers[0]=new qs(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 st:this.getInfo(t);break;case jn:this.getPayloadLength16(t);break;case Hn:this.getPayloadLength64(t);break;case qn:this.getMask();break;case Pa:this.getData(t);break;case Da:case Ws: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[Bn.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=jn:this._payloadLength===127?this._state=Hn: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=qn:this._state=Pa}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=Pa}getData(t){let e=Un;if(this._payloadLength){if(this._bufferedBytes<this._payloadLength){this._loop=!1;return}e=this.consume(this._payloadLength),this._masked&&(this._mask[0]|this._mask[1]|this._mask[2]|this._mask[3])!==0&&Br(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=Da,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Bn.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===st&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=st;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=Ea(s,e):this._binaryType==="arraybuffer"?a=Nr(Ea(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=st):(this._state=Ws,setImmediate(()=>{this.emit("message",a,!0),this._state=st,this.startLoop(t)}))}else{let a=Ea(s,e);if(!this._skipUTF8Validation&&!$n(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===Da||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=st):(this._state=Ws,setImmediate(()=>{this.emit("message",a,!1),this._state=st,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Un),this.end();else{let s=t.readUInt16BE(0);if(!Ur(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new qs(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!$n(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=st;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=st):(this._state=Ws,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=st,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[Fr]=a,i}};Wn.exports=Ra});var Fa=Ge((pc,Vn)=>{"use strict";var{Duplex:uc}=require("stream"),{randomFillSync:$r}=require("crypto"),zn=jt(),{EMPTY_BUFFER:jr,kWebSocket:Hr,NOOP:qr}=ft(),{isBlob:qt,isValidStatusCode:Wr}=Ht(),{mask:Gn,toBuffer:Et}=is(),at=Symbol("kByteLength"),zr=Buffer.alloc(4),zs=8*1024,Pt,Wt=zs,it=0,Gr=1,Vr=2,La=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=qr,this[Hr]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||zr,e.generateMask?e.generateMask(s):(Wt===zs&&(Pt===void 0&&(Pt=Buffer.alloc(zs)),$r(Pt,0,zs),Wt=0),s[0]=Pt[Wt++],s[1]=Pt[Wt++],s[2]=Pt[Wt++],s[3]=Pt[Wt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[at]!==void 0?o=e[at]:(t=Buffer.from(t),o=t.length):(o=t.length,a=e.mask&&e.readOnly&&!i);let l=o;o>=65536?(n+=8,l=127):o>125&&(n+=2,l=126);let c=Buffer.allocUnsafe(a?o+n:n);return c[0]=e.fin?e.opcode|128:e.opcode,e.rsv1&&(c[0]|=64),c[1]=l,l===126?c.writeUInt16BE(o,2):l===127&&(c[2]=c[3]=0,c.writeUIntBE(o,4,6)),e.mask?(c[1]|=128,c[n-4]=s[0],c[n-3]=s[1],c[n-2]=s[2],c[n-1]=s[3],i?[c,t]:a?(Gn(t,s,c,n,o),[c]):(Gn(t,s,t,0,o),[c,t])):[c,t]}close(t,e,s,a){let n;if(t===void 0)n=jr;else{if(typeof t!="number"||!Wr(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={[at]: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):qt(t)?(a=t.size,n=!1):(t=Et(t),a=t.length,n=Et.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[at]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};qt(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):qt(t)?(a=t.size,n=!1):(t=Et(t),a=t.length,n=Et.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[at]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};qt(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,l;typeof t=="string"?(o=Buffer.byteLength(t),l=!1):qt(t)?(o=t.size,l=!1):(t=Et(t),o=t.length,l=Et.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 c={[at]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:l,rsv1:i};qt(t)?this._state!==it?this.enqueue([this.getBlobData,t,this._compress,c,s]):this.getBlobData(t,this._compress,c,s):this._state!==it?this.enqueue([this.dispatch,t,this._compress,c,s]):this.dispatch(t,this._compress,c,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[at],this._state=Vr,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(Ma,this,o,a);return}this._bufferedBytes-=s[at];let i=Et(n);e?this.dispatch(i,e,s,a):(this._state=it,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(Yr,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[at],this._state=Gr,n.compress(t,s.fin,(i,o)=>{if(this._socket.destroyed){let l=new Error("The socket was closed while data was being compressed");Ma(this,l,a);return}this._bufferedBytes-=s[at],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][at],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][at],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)}};Vn.exports=La;function Ma(r,t,e){typeof e=="function"&&e(t);for(let s=0;s<r._queue.length;s++){let a=r._queue[s],n=a[a.length-1];typeof n=="function"&&n(t)}}function Yr(r,t,e){Ma(r,t,e),r.onerror(t)}});var si=Ge((fc,ti)=>{"use strict";var{kForOnEventAttribute:os,kListener:Oa}=ft(),Yn=Symbol("kCode"),Kn=Symbol("kData"),Jn=Symbol("kError"),Xn=Symbol("kMessage"),Qn=Symbol("kReason"),zt=Symbol("kTarget"),Zn=Symbol("kType"),ei=Symbol("kWasClean"),gt=class{constructor(t){this[zt]=null,this[Zn]=t}get target(){return this[zt]}get type(){return this[Zn]}};Object.defineProperty(gt.prototype,"target",{enumerable:!0});Object.defineProperty(gt.prototype,"type",{enumerable:!0});var Dt=class extends gt{constructor(t,e={}){super(t),this[Yn]=e.code===void 0?0:e.code,this[Qn]=e.reason===void 0?"":e.reason,this[ei]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[Yn]}get reason(){return this[Qn]}get wasClean(){return this[ei]}};Object.defineProperty(Dt.prototype,"code",{enumerable:!0});Object.defineProperty(Dt.prototype,"reason",{enumerable:!0});Object.defineProperty(Dt.prototype,"wasClean",{enumerable:!0});var Gt=class extends gt{constructor(t,e={}){super(t),this[Jn]=e.error===void 0?null:e.error,this[Xn]=e.message===void 0?"":e.message}get error(){return this[Jn]}get message(){return this[Xn]}};Object.defineProperty(Gt.prototype,"error",{enumerable:!0});Object.defineProperty(Gt.prototype,"message",{enumerable:!0});var ls=class extends gt{constructor(t,e={}){super(t),this[Kn]=e.data===void 0?null:e.data}get data(){return this[Kn]}};Object.defineProperty(ls.prototype,"data",{enumerable:!0});var Kr={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[os]&&a[Oa]===t&&!a[os])return;let s;if(r==="message")s=function(n,i){let o=new ls("message",{data:i?n:n.toString()});o[zt]=this,Gs(t,this,o)};else if(r==="close")s=function(n,i){let o=new Dt("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[zt]=this,Gs(t,this,o)};else if(r==="error")s=function(n){let i=new Gt("error",{error:n,message:n.message});i[zt]=this,Gs(t,this,i)};else if(r==="open")s=function(){let n=new gt("open");n[zt]=this,Gs(t,this,n)};else return;s[os]=!!e[os],s[Oa]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[Oa]===t&&!e[os]){this.removeListener(r,e);break}}};ti.exports={CloseEvent:Dt,ErrorEvent:Gt,Event:gt,EventTarget:Kr,MessageEvent:ls};function Gs(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var Vs=Ge((mc,ai)=>{"use strict";var{tokenChars:cs}=Ht();function dt(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function Jr(r){let t=Object.create(null),e=Object.create(null),s=!1,a=!1,n=!1,i,o,l=-1,c=-1,d=-1,h=0;for(;h<r.length;h++)if(c=r.charCodeAt(h),i===void 0)if(d===-1&&cs[c]===1)l===-1&&(l=h);else if(h!==0&&(c===32||c===9))d===-1&&l!==-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=r.slice(l,d);c===44?(dt(t,p,e),e=Object.create(null)):i=p,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);else if(o===void 0)if(d===-1&&cs[c]===1)l===-1&&(l=h);else if(c===32||c===9)d===-1&&l!==-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h),dt(e,r.slice(l,d),!0),c===44&&(dt(t,i,e),e=Object.create(null),i=void 0),l=d=-1}else if(c===61&&l!==-1&&d===-1)o=r.slice(l,h),l=d=-1;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(a){if(cs[c]!==1)throw new SyntaxError(`Unexpected character at index ${h}`);l===-1?l=h:s||(s=!0),a=!1}else if(n)if(cs[c]===1)l===-1&&(l=h);else if(c===34&&l!==-1)n=!1,d=h;else if(c===92)a=!0;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(c===34&&r.charCodeAt(h-1)===61)n=!0;else if(d===-1&&cs[c]===1)l===-1&&(l=h);else if(l!==-1&&(c===32||c===9))d===-1&&(d=h);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=r.slice(l,d);s&&(p=p.replace(/\\/g,""),s=!1),dt(e,o,p),c===44&&(dt(t,i,e),e=Object.create(null),i=void 0),o=void 0,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);if(l===-1||n||c===32||c===9)throw new SyntaxError("Unexpected end of input");d===-1&&(d=h);let u=r.slice(l,d);return i===void 0?dt(t,u,e):(o===void 0?dt(e,u,!0):s?dt(e,o,u.replace(/\\/g,"")):dt(e,o,u),dt(t,i,e)),t}function Xr(r){return Object.keys(r).map(t=>{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(", ")}ai.exports={format:Xr,parse:Jr}});var Xs=Ge((vc,mi)=>{"use strict";var Qr=require("events"),Zr=require("https"),eo=require("http"),ri=require("net"),to=require("tls"),{randomBytes:so,createHash:ao}=require("crypto"),{Duplex:gc,Readable:yc}=require("stream"),{URL:Na}=require("url"),kt=jt(),no=Ia(),io=Fa(),{isBlob:ro}=Ht(),{BINARY_TYPES:ni,CLOSE_TIMEOUT:oo,EMPTY_BUFFER:Ys,GUID:lo,kForOnEventAttribute:Ba,kListener:co,kStatusCode:ho,kWebSocket:Ie,NOOP:oi}=ft(),{EventTarget:{addEventListener:uo,removeEventListener:po}}=si(),{format:fo,parse:mo}=Vs(),{toBuffer:go}=is(),li=Symbol("kAborted"),Ua=[8,13],yt=["CONNECTING","OPEN","CLOSING","CLOSED"],yo=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,ye=class r extends Qr{constructor(t,e,s){super(),this._binaryType=ni[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Ys,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]),ci(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){ni.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 no({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new io(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",wo),a.on("drain",ko),a.on("error",xo),a.on("message",So),a.on("ping",To),a.on("pong",Co),n.onerror=_o,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",ui),t.on("data",Js),t.on("end",pi),t.on("error",fi),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[kt.extensionName]&&this._extensions[kt.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){Je(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())}),hi(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){$a(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Ys,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){$a(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Ys,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){$a(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[kt.extensionName]||(a.compress=!1),this._sender.send(t||Ys,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Je(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(ye,"CONNECTING",{enumerable:!0,value:yt.indexOf("CONNECTING")});Object.defineProperty(ye.prototype,"CONNECTING",{enumerable:!0,value:yt.indexOf("CONNECTING")});Object.defineProperty(ye,"OPEN",{enumerable:!0,value:yt.indexOf("OPEN")});Object.defineProperty(ye.prototype,"OPEN",{enumerable:!0,value:yt.indexOf("OPEN")});Object.defineProperty(ye,"CLOSING",{enumerable:!0,value:yt.indexOf("CLOSING")});Object.defineProperty(ye.prototype,"CLOSING",{enumerable:!0,value:yt.indexOf("CLOSING")});Object.defineProperty(ye,"CLOSED",{enumerable:!0,value:yt.indexOf("CLOSED")});Object.defineProperty(ye.prototype,"CLOSED",{enumerable:!0,value:yt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(ye.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(ye.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[Ba])return t[co];return null},set(t){for(let e of this.listeners(r))if(e[Ba]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[Ba]:!0})}})});ye.prototype.addEventListener=uo;ye.prototype.removeEventListener=po;mi.exports=ye;function ci(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:oo,protocolVersion:Ua[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,!Ua.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${Ua.join(", ")})`);let n;if(t instanceof Na)n=t;else try{n=new Na(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:",l;if(n.protocol!=="ws:"&&!i&&!o?l=`The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`:o&&!n.pathname?l="The URL's pathname is empty":n.hash&&(l="The URL contains a fragment identifier"),l){let f=new SyntaxError(l);if(r._redirects===0)throw f;Ks(r,f);return}let c=i?443:80,d=so(16).toString("base64"),h=i?Zr.request:eo.request,u=new Set,p;if(a.createConnection=a.createConnection||(i?bo:vo),a.defaultPort=a.defaultPort||c,a.port=n.port||c,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 kt({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=fo({[kt.extensionName]:p.offer()})),e.length){for(let f of e){if(typeof f!="string"||!yo.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[v,k]of Object.entries(f))s.headers[v.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",()=>{Je(r,m,"Opening handshake has timed out")}),m.on("error",f=>{m===null||m[li]||(m=r._req=null,Ks(r,f))}),m.on("response",f=>{let v=f.headers.location,k=f.statusCode;if(v&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){Je(r,m,"Maximum redirects exceeded");return}m.abort();let w;try{w=new Na(v,t)}catch{let g=new SyntaxError(`Invalid URL: ${v}`);Ks(r,g);return}ci(r,w,e,s)}else r.emit("unexpected-response",m,f)||Je(r,m,`Unexpected server response: ${f.statusCode}`)}),m.on("upgrade",(f,v,k)=>{if(r.emit("upgrade",f),r.readyState!==ye.CONNECTING)return;m=r._req=null;let w=f.headers.upgrade;if(w===void 0||w.toLowerCase()!=="websocket"){Je(r,v,"Invalid Upgrade header");return}let y=ao("sha1").update(d+lo).digest("base64");if(f.headers["sec-websocket-accept"]!==y){Je(r,v,"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){Je(r,v,x);return}g&&(r._protocol=g);let T=f.headers["sec-websocket-extensions"];if(T!==void 0){if(!p){Je(r,v,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let C;try{C=mo(T)}catch{Je(r,v,"Invalid Sec-WebSocket-Extensions header");return}let L=Object.keys(C);if(L.length!==1||L[0]!==kt.extensionName){Je(r,v,"Server indicated an extension that was not requested");return}try{p.accept(C[kt.extensionName])}catch{Je(r,v,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[kt.extensionName]=p}r.setSocket(v,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(m,r):m.end()}function Ks(r,t){r._readyState=ye.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function vo(r){return r.path=r.socketPath,ri.connect(r)}function bo(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=ri.isIP(r.host)?"":r.host),to.connect(r)}function Je(r,t,e){r._readyState=ye.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Je),t.setHeader?(t[li]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Ks,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function $a(r,t,e){if(t){let s=ro(t)?t.size:go(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${yt[r.readyState]})`);process.nextTick(e,s)}}function wo(r,t){let e=this[Ie];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[Ie]!==void 0&&(e._socket.removeListener("data",Js),process.nextTick(di,e._socket),r===1005?e.close():e.close(r,t))}function ko(){let r=this[Ie];r.isPaused||r._socket.resume()}function xo(r){let t=this[Ie];t._socket[Ie]!==void 0&&(t._socket.removeListener("data",Js),process.nextTick(di,t._socket),t.close(r[ho])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function ii(){this[Ie].emitClose()}function So(r,t){this[Ie].emit("message",r,t)}function To(r){let t=this[Ie];t._autoPong&&t.pong(r,!this._isServer,oi),t.emit("ping",r)}function Co(r){this[Ie].emit("pong",r)}function di(r){r.resume()}function _o(r){let t=this[Ie];t.readyState!==ye.CLOSED&&(t.readyState===ye.OPEN&&(t._readyState=ye.CLOSING,hi(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function hi(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function ui(){let r=this[Ie];if(this.removeListener("close",ui),this.removeListener("data",Js),this.removeListener("end",pi),r._readyState=ye.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",ii),r._receiver.on("finish",ii))}function Js(r){this[Ie]._receiver.write(r)||this.pause()}function pi(){let r=this[Ie];r._readyState=ye.CLOSING,r._receiver.end(),this.end()}function fi(){let r=this[Ie];this.removeListener("error",fi),this.on("error",oi),r&&(r._readyState=ye.CLOSING,this.destroy())}});var bi=Ge((wc,vi)=>{"use strict";var bc=Xs(),{Duplex:Ao}=require("stream");function gi(r){r.emit("close")}function Eo(){!this.destroyed&&this._writableState.finished&&this.destroy()}function yi(r){this.removeListener("error",yi),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function Po(r,t){let e=!0,s=new Ao({...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(gi,s);return}let i=!1;r.once("error",function(l){i=!0,n(l)}),r.once("close",function(){i||n(a),process.nextTick(gi,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",Eo),s.on("error",yi),s}vi.exports=Po});var ja=Ge((kc,wi)=>{"use strict";var{tokenChars:Do}=Ht();function Ro(r){let t=new Set,e=-1,s=-1,a=0;for(a;a<r.length;a++){let i=r.charCodeAt(a);if(s===-1&&Do[i]===1)e===-1&&(e=a);else if(a!==0&&(i===32||i===9))s===-1&&e!==-1&&(s=a);else if(i===44){if(e===-1)throw new SyntaxError(`Unexpected character at index ${a}`);s===-1&&(s=a);let o=r.slice(e,s);if(t.has(o))throw new SyntaxError(`The "${o}" subprotocol is duplicated`);t.add(o),e=s=-1}else throw new SyntaxError(`Unexpected character at index ${a}`)}if(e===-1||s!==-1)throw new SyntaxError("Unexpected end of input");let n=r.slice(e,a);if(t.has(n))throw new SyntaxError(`The "${n}" subprotocol is duplicated`);return t.add(n),t}wi.exports={parse:Ro}});var Ai=Ge((Sc,_i)=>{"use strict";var Io=require("events"),Qs=require("http"),{Duplex:xc}=require("stream"),{createHash:Lo}=require("crypto"),ki=Vs(),Rt=jt(),Mo=ja(),Fo=Xs(),{CLOSE_TIMEOUT:Oo,GUID:No,kWebSocket:Bo}=ft(),Uo=/^[+/0-9A-Za-z]{22}==$/,xi=0,Si=1,Ci=2,Ha=class extends Io{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Oo,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:Fo,...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=Qs.createServer((s,a)=>{let n=Qs.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=$o(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=xi}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===Ci){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(ds,this);return}if(t&&this.once("close",t),this._state!==Si)if(this._state=Si,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(ds,this):process.nextTick(ds,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{ds(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",Ti);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){It(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){It(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Uo.test(n)){It(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){It(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){hs(e,400);return}let l=t.headers["sec-websocket-protocol"],c=new Set;if(l!==void 0)try{c=Mo.parse(l)}catch{It(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 Rt({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=ki.parse(d);p[Rt.extensionName]&&(u.accept(p[Rt.extensionName]),h[Rt.extensionName]=u)}catch{It(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,v)=>{if(!p)return hs(e,m||401,f,v);this.completeUpgrade(h,n,c,t,e,s,a)});return}if(!this.options.verifyClient(u))return hs(e,401)}this.completeUpgrade(h,n,c,t,e,s,a)}completeUpgrade(t,e,s,a,n,i,o){if(!n.readable||!n.writable)return n.destroy();if(n[Bo])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>xi)return hs(n,503);let c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${Lo("sha1").update(e+No).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&&(c.push(`Sec-WebSocket-Protocol: ${h}`),d._protocol=h)}if(t[Rt.extensionName]){let h=t[Rt.extensionName].params,u=ki.format({[Rt.extensionName]:[h]});c.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",c,a),n.write(c.concat(`\r
1
+ "use strict";var Mr=Object.create;var Ps=Object.defineProperty;var Fr=Object.getOwnPropertyDescriptor;var Or=Object.getOwnPropertyNames;var Nr=Object.getPrototypeOf,Br=Object.prototype.hasOwnProperty;var Ke=(i,t)=>()=>(t||i((t={exports:{}}).exports,t),t.exports),Ur=(i,t)=>{for(var e in t)Ps(i,e,{get:t[e],enumerable:!0})},wn=(i,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Or(t))!Br.call(i,a)&&a!==e&&Ps(i,a,{get:()=>t[a],enumerable:!(s=Fr(t,a))||s.enumerable});return i};var Je=(i,t,e)=>(e=i!=null?Mr(Nr(i)):{},wn(t||!i||!i.__esModule?Ps(e,"default",{value:i,enumerable:!0}):e,i)),$r=i=>wn(Ps({},"__esModule",{value:!0}),i);var vt=Ke((Nd,hi)=>{"use strict";var ci=["nodebuffer","arraybuffer","fragments"],di=typeof Blob<"u";di&&ci.push("blob");hi.exports={BINARY_TYPES:ci,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:di,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var fs=Ke((Bd,ea)=>{"use strict";var{EMPTY_BUFFER:Mo}=vt(),Va=Buffer[Symbol.species];function Fo(i,t){if(i.length===0)return Mo;if(i.length===1)return i[0];let e=Buffer.allocUnsafe(t),s=0;for(let a=0;a<i.length;a++){let n=i[a];e.set(n,s),s+=n.length}return s<t?new Va(e.buffer,e.byteOffset,s):e}function ui(i,t,e,s,a){for(let n=0;n<a;n++)e[s+n]=i[n]^t[n&3]}function pi(i,t){for(let e=0;e<i.length;e++)i[e]^=t[e&3]}function Oo(i){return i.length===i.buffer.byteLength?i.buffer:i.buffer.slice(i.byteOffset,i.byteOffset+i.length)}function Ya(i){if(Ya.readOnly=!0,Buffer.isBuffer(i))return i;let t;return i instanceof ArrayBuffer?t=new Va(i):ArrayBuffer.isView(i)?t=new Va(i.buffer,i.byteOffset,i.byteLength):(t=Buffer.from(i),Ya.readOnly=!1),t}ea.exports={concat:Fo,mask:ui,toArrayBuffer:Oo,toBuffer:Ya,unmask:pi};if(!process.env.WS_NO_BUFFER_UTIL)try{let i=require("bufferutil");ea.exports.mask=function(t,e,s,a,n){n<48?ui(t,e,s,a,n):i.mask(t,e,s,a,n)},ea.exports.unmask=function(t,e){t.length<32?pi(t,e):i.unmask(t,e)}}catch{}});var gi=Ke((Ud,fi)=>{"use strict";var mi=Symbol("kDone"),Ka=Symbol("kRun"),Ja=class{constructor(t){this[mi]=()=>{this.pending--,this[Ka]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Ka]()}[Ka](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[mi])}}};fi.exports=Ja});var zt=Ke(($d,wi)=>{"use strict";var gs=require("zlib"),yi=fs(),No=gi(),{kStatusCode:vi}=vt(),Bo=Buffer[Symbol.species],Uo=Buffer.from([0,0,255,255]),sa=Symbol("permessage-deflate"),bt=Symbol("total-length"),qt=Symbol("callback"),St=Symbol("buffers"),Wt=Symbol("error"),ta,Xa=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,!ta){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;ta=new No(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[qt];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){ta.add(a=>{this._decompress(t,e,(n,r)=>{a(),s(n,r)})})}compress(t,e,s){ta.add(a=>{this._compress(t,e,(n,r)=>{a(),s(n,r)})})}_decompress(t,e,s){let a=this._isServer?"client":"server";if(!this._inflate){let n=`${a}_max_window_bits`,r=typeof this.params[n]!="number"?gs.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=gs.createInflateRaw({...this._options.zlibInflateOptions,windowBits:r}),this._inflate[sa]=this,this._inflate[bt]=0,this._inflate[St]=[],this._inflate.on("error",jo),this._inflate.on("data",bi)}this._inflate[qt]=s,this._inflate.write(t),e&&this._inflate.write(Uo),this._inflate.flush(()=>{let n=this._inflate[Wt];if(n){this._inflate.close(),this._inflate=null,s(n);return}let r=yi.concat(this._inflate[St],this._inflate[bt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[bt]=0,this._inflate[St]=[],e&&this.params[`${a}_no_context_takeover`]&&this._inflate.reset()),s(null,r)})}_compress(t,e,s){let a=this._isServer?"server":"client";if(!this._deflate){let n=`${a}_max_window_bits`,r=typeof this.params[n]!="number"?gs.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=gs.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:r}),this._deflate[bt]=0,this._deflate[St]=[],this._deflate.on("data",$o)}this._deflate[qt]=s,this._deflate.write(t),this._deflate.flush(gs.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=yi.concat(this._deflate[St],this._deflate[bt]);e&&(n=new Bo(n.buffer,n.byteOffset,n.length-4)),this._deflate[qt]=null,this._deflate[bt]=0,this._deflate[St]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};wi.exports=Xa;function $o(i){this[St].push(i),this[bt]+=i.length}function bi(i){if(this[bt]+=i.length,this[sa]._maxPayload<1||this[bt]<=this[sa]._maxPayload){this[St].push(i);return}this[Wt]=new RangeError("Max payload size exceeded"),this[Wt].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[Wt][vi]=1009,this.removeListener("data",bi),this.reset()}function jo(i){if(this[sa]._inflate=null,this[Wt]){this[qt](this[Wt]);return}i[vi]=1007,this[qt](i)}});var Gt=Ke((jd,aa)=>{"use strict";var{isUtf8:ki}=require("buffer"),{hasBlob:Ho}=vt(),qo=[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 Wo(i){return i>=1e3&&i<=1014&&i!==1004&&i!==1005&&i!==1006||i>=3e3&&i<=4999}function Qa(i){let t=i.length,e=0;for(;e<t;)if((i[e]&128)===0)e++;else if((i[e]&224)===192){if(e+1===t||(i[e+1]&192)!==128||(i[e]&254)===192)return!1;e+=2}else if((i[e]&240)===224){if(e+2>=t||(i[e+1]&192)!==128||(i[e+2]&192)!==128||i[e]===224&&(i[e+1]&224)===128||i[e]===237&&(i[e+1]&224)===160)return!1;e+=3}else if((i[e]&248)===240){if(e+3>=t||(i[e+1]&192)!==128||(i[e+2]&192)!==128||(i[e+3]&192)!==128||i[e]===240&&(i[e+1]&240)===128||i[e]===244&&i[e+1]>143||i[e]>244)return!1;e+=4}else return!1;return!0}function zo(i){return Ho&&typeof i=="object"&&typeof i.arrayBuffer=="function"&&typeof i.type=="string"&&typeof i.stream=="function"&&(i[Symbol.toStringTag]==="Blob"||i[Symbol.toStringTag]==="File")}aa.exports={isBlob:zo,isValidStatusCode:Wo,isValidUTF8:Qa,tokenChars:qo};if(ki)aa.exports.isValidUTF8=function(i){return i.length<24?Qa(i):ki(i)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let i=require("utf-8-validate");aa.exports.isValidUTF8=function(t){return t.length<32?Qa(t):i(t)}}catch{}});var an=Ke((Hd,Ei)=>{"use strict";var{Writable:Go}=require("stream"),xi=zt(),{BINARY_TYPES:Vo,EMPTY_BUFFER:Si,kStatusCode:Yo,kWebSocket:Ko}=vt(),{concat:Za,toArrayBuffer:Jo,unmask:Xo}=fs(),{isValidStatusCode:Qo,isValidUTF8:Ci}=Gt(),na=Buffer[Symbol.species],rt=0,Ti=1,_i=2,Ai=3,en=4,tn=5,ia=6,sn=class extends Go{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Vo[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Ko]=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=rt}_write(t,e,s){if(this._opcode===8&&this._state==rt)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<this._buffers[0].length){let s=this._buffers[0];return this._buffers[0]=new na(s.buffer,s.byteOffset+t,s.length-t),new na(s.buffer,s.byteOffset,t)}let e=Buffer.allocUnsafe(t);do{let s=this._buffers[0],a=e.length-t;t>=s.length?e.set(this._buffers.shift(),a):(e.set(new Uint8Array(s.buffer,s.byteOffset,t),a),this._buffers[0]=new na(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 rt:this.getInfo(t);break;case Ti:this.getPayloadLength16(t);break;case _i:this.getPayloadLength64(t);break;case Ai:this.getMask();break;case en:this.getData(t);break;case tn:case ia: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[xi.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=Ti:this._payloadLength===127?this._state=_i: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=Ai:this._state=en}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=en}getData(t){let e=Si;if(this._payloadLength){if(this._bufferedBytes<this._payloadLength){this._loop=!1;return}e=this.consume(this._payloadLength),this._masked&&(this._mask[0]|this._mask[1]|this._mask[2]|this._mask[3])!==0&&Xo(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=tn,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[xi.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 r=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(r);return}this._fragments.push(n)}this.dataMessage(e),this._state===rt&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=rt;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=Za(s,e):this._binaryType==="arraybuffer"?a=Jo(Za(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=rt):(this._state=ia,setImmediate(()=>{this.emit("message",a,!0),this._state=rt,this.startLoop(t)}))}else{let a=Za(s,e);if(!this._skipUTF8Validation&&!Ci(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===tn||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=rt):(this._state=ia,setImmediate(()=>{this.emit("message",a,!1),this._state=rt,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Si),this.end();else{let s=t.readUInt16BE(0);if(!Qo(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new na(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!Ci(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=rt;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=rt):(this._state=ia,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=rt,this.startLoop(e)}))}createError(t,e,s,a,n){this._loop=!1,this._errored=!0;let r=new t(s?`Invalid WebSocket frame: ${e}`:e);return Error.captureStackTrace(r,this.createError),r.code=n,r[Yo]=a,r}};Ei.exports=sn});var on=Ke((Wd,Ri)=>{"use strict";var{Duplex:qd}=require("stream"),{randomFillSync:Zo}=require("crypto"),Pi=zt(),{EMPTY_BUFFER:el,kWebSocket:tl,NOOP:sl}=vt(),{isBlob:Vt,isValidStatusCode:al}=Gt(),{mask:Di,toBuffer:It}=fs(),ot=Symbol("kByteLength"),nl=Buffer.alloc(4),ra=8*1024,Lt,Yt=ra,ct=0,il=1,rl=2,nn=class i{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=ct,this.onerror=sl,this[tl]=void 0}static frame(t,e){let s,a=!1,n=2,r=!1;e.mask&&(s=e.maskBuffer||nl,e.generateMask?e.generateMask(s):(Yt===ra&&(Lt===void 0&&(Lt=Buffer.alloc(ra)),Zo(Lt,0,ra),Yt=0),s[0]=Lt[Yt++],s[1]=Lt[Yt++],s[2]=Lt[Yt++],s[3]=Lt[Yt++]),r=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||r)&&e[ot]!==void 0?o=e[ot]:(t=Buffer.from(t),o=t.length):(o=t.length,a=e.mask&&e.readOnly&&!r);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],r?[l,t]:a?(Di(t,s,l,n,o),[l]):(Di(t,s,t,0,o),[l,t])):[l,t]}close(t,e,s,a){let n;if(t===void 0)n=el;else{if(typeof t!="number"||!al(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 r={[ot]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==ct?this.enqueue([this.dispatch,n,!1,r,a]):this.sendFrame(i.frame(n,r),a)}ping(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):Vt(t)?(a=t.size,n=!1):(t=It(t),a=t.length,n=It.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let r={[ot]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};Vt(t)?this._state!==ct?this.enqueue([this.getBlobData,t,!1,r,s]):this.getBlobData(t,!1,r,s):this._state!==ct?this.enqueue([this.dispatch,t,!1,r,s]):this.sendFrame(i.frame(t,r),s)}pong(t,e,s){let a,n;if(typeof t=="string"?(a=Buffer.byteLength(t),n=!1):Vt(t)?(a=t.size,n=!1):(t=It(t),a=t.length,n=It.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let r={[ot]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};Vt(t)?this._state!==ct?this.enqueue([this.getBlobData,t,!1,r,s]):this.getBlobData(t,!1,r,s):this._state!==ct?this.enqueue([this.dispatch,t,!1,r,s]):this.sendFrame(i.frame(t,r),s)}send(t,e,s){let a=this._extensions[Pi.extensionName],n=e.binary?2:1,r=e.compress,o,c;typeof t=="string"?(o=Buffer.byteLength(t),c=!1):Vt(t)?(o=t.size,c=!1):(t=It(t),o=t.length,c=It.readOnly),this._firstFragment?(this._firstFragment=!1,r&&a&&a.params[a._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(r=o>=a._threshold),this._compress=r):(r=!1,n=0),e.fin&&(this._firstFragment=!0);let l={[ot]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:c,rsv1:r};Vt(t)?this._state!==ct?this.enqueue([this.getBlobData,t,this._compress,l,s]):this.getBlobData(t,this._compress,l,s):this._state!==ct?this.enqueue([this.dispatch,t,this._compress,l,s]):this.dispatch(t,this._compress,l,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[ot],this._state=rl,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(rn,this,o,a);return}this._bufferedBytes-=s[ot];let r=It(n);e?this.dispatch(r,e,s,a):(this._state=ct,this.sendFrame(i.frame(r,s),a),this.dequeue())}).catch(n=>{process.nextTick(ol,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(i.frame(t,s),a);return}let n=this._extensions[Pi.extensionName];this._bufferedBytes+=s[ot],this._state=il,n.compress(t,s.fin,(r,o)=>{if(this._socket.destroyed){let c=new Error("The socket was closed while data was being compressed");rn(this,c,a);return}this._bufferedBytes-=s[ot],this._state=ct,s.readOnly=!1,this.sendFrame(i.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===ct&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][ot],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][ot],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)}};Ri.exports=nn;function rn(i,t,e){typeof e=="function"&&e(t);for(let s=0;s<i._queue.length;s++){let a=i._queue[s],n=a[a.length-1];typeof n=="function"&&n(t)}}function ol(i,t,e){rn(i,t,e),i.onerror(t)}});var $i=Ke((zd,Ui)=>{"use strict";var{kForOnEventAttribute:ys,kListener:ln}=vt(),Ii=Symbol("kCode"),Li=Symbol("kData"),Mi=Symbol("kError"),Fi=Symbol("kMessage"),Oi=Symbol("kReason"),Kt=Symbol("kTarget"),Ni=Symbol("kType"),Bi=Symbol("kWasClean"),wt=class{constructor(t){this[Kt]=null,this[Ni]=t}get target(){return this[Kt]}get type(){return this[Ni]}};Object.defineProperty(wt.prototype,"target",{enumerable:!0});Object.defineProperty(wt.prototype,"type",{enumerable:!0});var Mt=class extends wt{constructor(t,e={}){super(t),this[Ii]=e.code===void 0?0:e.code,this[Oi]=e.reason===void 0?"":e.reason,this[Bi]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[Ii]}get reason(){return this[Oi]}get wasClean(){return this[Bi]}};Object.defineProperty(Mt.prototype,"code",{enumerable:!0});Object.defineProperty(Mt.prototype,"reason",{enumerable:!0});Object.defineProperty(Mt.prototype,"wasClean",{enumerable:!0});var Jt=class extends wt{constructor(t,e={}){super(t),this[Mi]=e.error===void 0?null:e.error,this[Fi]=e.message===void 0?"":e.message}get error(){return this[Mi]}get message(){return this[Fi]}};Object.defineProperty(Jt.prototype,"error",{enumerable:!0});Object.defineProperty(Jt.prototype,"message",{enumerable:!0});var vs=class extends wt{constructor(t,e={}){super(t),this[Li]=e.data===void 0?null:e.data}get data(){return this[Li]}};Object.defineProperty(vs.prototype,"data",{enumerable:!0});var ll={addEventListener(i,t,e={}){for(let a of this.listeners(i))if(!e[ys]&&a[ln]===t&&!a[ys])return;let s;if(i==="message")s=function(n,r){let o=new vs("message",{data:r?n:n.toString()});o[Kt]=this,oa(t,this,o)};else if(i==="close")s=function(n,r){let o=new Mt("close",{code:n,reason:r.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Kt]=this,oa(t,this,o)};else if(i==="error")s=function(n){let r=new Jt("error",{error:n,message:n.message});r[Kt]=this,oa(t,this,r)};else if(i==="open")s=function(){let n=new wt("open");n[Kt]=this,oa(t,this,n)};else return;s[ys]=!!e[ys],s[ln]=t,e.once?this.once(i,s):this.on(i,s)},removeEventListener(i,t){for(let e of this.listeners(i))if(e[ln]===t&&!e[ys]){this.removeListener(i,e);break}}};Ui.exports={CloseEvent:Mt,ErrorEvent:Jt,Event:wt,EventTarget:ll,MessageEvent:vs};function oa(i,t,e){typeof i=="object"&&i.handleEvent?i.handleEvent.call(i,e):i.call(t,e)}});var la=Ke((Gd,ji)=>{"use strict";var{tokenChars:bs}=Gt();function pt(i,t,e){i[t]===void 0?i[t]=[e]:i[t].push(e)}function cl(i){let t=Object.create(null),e=Object.create(null),s=!1,a=!1,n=!1,r,o,c=-1,l=-1,d=-1,h=0;for(;h<i.length;h++)if(l=i.charCodeAt(h),r===void 0)if(d===-1&&bs[l]===1)c===-1&&(c=h);else if(h!==0&&(l===32||l===9))d===-1&&c!==-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=i.slice(c,d);l===44?(pt(t,p,e),e=Object.create(null)):r=p,c=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);else if(o===void 0)if(d===-1&&bs[l]===1)c===-1&&(c=h);else if(l===32||l===9)d===-1&&c!==-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h),pt(e,i.slice(c,d),!0),l===44&&(pt(t,r,e),e=Object.create(null),r=void 0),c=d=-1}else if(l===61&&c!==-1&&d===-1)o=i.slice(c,h),c=d=-1;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(a){if(bs[l]!==1)throw new SyntaxError(`Unexpected character at index ${h}`);c===-1?c=h:s||(s=!0),a=!1}else if(n)if(bs[l]===1)c===-1&&(c=h);else if(l===34&&c!==-1)n=!1,d=h;else if(l===92)a=!0;else throw new SyntaxError(`Unexpected character at index ${h}`);else if(l===34&&i.charCodeAt(h-1)===61)n=!0;else if(d===-1&&bs[l]===1)c===-1&&(c=h);else if(c!==-1&&(l===32||l===9))d===-1&&(d=h);else if(l===59||l===44){if(c===-1)throw new SyntaxError(`Unexpected character at index ${h}`);d===-1&&(d=h);let p=i.slice(c,d);s&&(p=p.replace(/\\/g,""),s=!1),pt(e,o,p),l===44&&(pt(t,r,e),e=Object.create(null),r=void 0),o=void 0,c=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);if(c===-1||n||l===32||l===9)throw new SyntaxError("Unexpected end of input");d===-1&&(d=h);let u=i.slice(c,d);return r===void 0?pt(t,u,e):(o===void 0?pt(e,u,!0):s?pt(e,o,u.replace(/\\/g,"")):pt(e,o,u),pt(t,r,e)),t}function dl(i){return Object.keys(i).map(t=>{let e=i[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(r=>r===!0?a:`${a}=${r}`).join("; ")})).join("; ")).join(", ")}).join(", ")}ji.exports={format:dl,parse:cl}});var ua=Ke((Kd,Zi)=>{"use strict";var hl=require("events"),ul=require("https"),pl=require("http"),Wi=require("net"),ml=require("tls"),{randomBytes:fl,createHash:gl}=require("crypto"),{Duplex:Vd,Readable:Yd}=require("stream"),{URL:cn}=require("url"),Ct=zt(),yl=an(),vl=on(),{isBlob:bl}=Gt(),{BINARY_TYPES:Hi,CLOSE_TIMEOUT:wl,EMPTY_BUFFER:ca,GUID:kl,kForOnEventAttribute:dn,kListener:xl,kStatusCode:Sl,kWebSocket:Oe,NOOP:zi}=vt(),{EventTarget:{addEventListener:Cl,removeEventListener:Tl}}=$i(),{format:_l,parse:Al}=la(),{toBuffer:El}=fs(),Gi=Symbol("kAborted"),hn=[8,13],kt=["CONNECTING","OPEN","CLOSING","CLOSED"],Pl=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,xe=class i extends hl{constructor(t,e,s){super(),this._binaryType=Hi[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=ca,this._closeTimer=null,this._errorEmitted=!1,this._extensions={},this._paused=!1,this._protocol="",this._readyState=i.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]),Vi(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){Hi.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 yl({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new vl(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[Oe]=this,n[Oe]=this,t[Oe]=this,a.on("conclude",Il),a.on("drain",Ll),a.on("error",Ml),a.on("message",Fl),a.on("ping",Ol),a.on("pong",Nl),n.onerror=Bl,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",Ji),t.on("data",ha),t.on("end",Xi),t.on("error",Qi),this._readyState=i.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=i.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[Ct.extensionName]&&this._extensions[Ct.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=i.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(t,e){if(this.readyState!==i.CLOSED){if(this.readyState===i.CONNECTING){et(this,this._req,"WebSocket was closed before the connection was established");return}if(this.readyState===i.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=i.CLOSING,this._sender.close(t,e,!this._isServer,s=>{s||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),Ki(this)}}pause(){this.readyState===i.CONNECTING||this.readyState===i.CLOSED||(this._paused=!0,this._socket.pause())}ping(t,e,s){if(this.readyState===i.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!==i.OPEN){un(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||ca,e,s)}pong(t,e,s){if(this.readyState===i.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!==i.OPEN){un(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||ca,e,s)}resume(){this.readyState===i.CONNECTING||this.readyState===i.CLOSED||(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(t,e,s){if(this.readyState===i.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!==i.OPEN){un(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[Ct.extensionName]||(a.compress=!1),this._sender.send(t||ca,a,s)}terminate(){if(this.readyState!==i.CLOSED){if(this.readyState===i.CONNECTING){et(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=i.CLOSING,this._socket.destroy())}}};Object.defineProperty(xe,"CONNECTING",{enumerable:!0,value:kt.indexOf("CONNECTING")});Object.defineProperty(xe.prototype,"CONNECTING",{enumerable:!0,value:kt.indexOf("CONNECTING")});Object.defineProperty(xe,"OPEN",{enumerable:!0,value:kt.indexOf("OPEN")});Object.defineProperty(xe.prototype,"OPEN",{enumerable:!0,value:kt.indexOf("OPEN")});Object.defineProperty(xe,"CLOSING",{enumerable:!0,value:kt.indexOf("CLOSING")});Object.defineProperty(xe.prototype,"CLOSING",{enumerable:!0,value:kt.indexOf("CLOSING")});Object.defineProperty(xe,"CLOSED",{enumerable:!0,value:kt.indexOf("CLOSED")});Object.defineProperty(xe.prototype,"CLOSED",{enumerable:!0,value:kt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(i=>{Object.defineProperty(xe.prototype,i,{enumerable:!0})});["open","error","close","message"].forEach(i=>{Object.defineProperty(xe.prototype,`on${i}`,{enumerable:!0,get(){for(let t of this.listeners(i))if(t[dn])return t[xl];return null},set(t){for(let e of this.listeners(i))if(e[dn]){this.removeListener(i,e);break}typeof t=="function"&&this.addEventListener(i,t,{[dn]:!0})}})});xe.prototype.addEventListener=Cl;xe.prototype.removeEventListener=Tl;Zi.exports=xe;function Vi(i,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:wl,protocolVersion:hn[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(i._autoPong=a.autoPong,i._closeTimeout=a.closeTimeout,!hn.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${hn.join(", ")})`);let n;if(t instanceof cn)n=t;else try{n=new cn(t)}catch{throw new SyntaxError(`Invalid URL: ${t}`)}n.protocol==="http:"?n.protocol="ws:":n.protocol==="https:"&&(n.protocol="wss:"),i._url=n.href;let r=n.protocol==="wss:",o=n.protocol==="ws+unix:",c;if(n.protocol!=="ws:"&&!r&&!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(i._redirects===0)throw f;da(i,f);return}let l=r?443:80,d=fl(16).toString("base64"),h=r?ul.request:pl.request,u=new Set,p;if(a.createConnection=a.createConnection||(r?Rl:Dl),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 Ct({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=_l({[Ct.extensionName]:p.offer()})),e.length){for(let f of e){if(typeof f!="string"||!Pl.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(i._redirects===0){i._originalIpc=o,i._originalSecure=r,i._originalHostOrSocketPath=o?a.socketPath:n.host;let f=s&&s.headers;if(s={...s,headers:{}},f)for(let[g,b]of Object.entries(f))s.headers[g.toLowerCase()]=b}else if(i.listenerCount("redirect")===0){let f=o?i._originalIpc?a.socketPath===i._originalHostOrSocketPath:!1:i._originalIpc?!1:n.host===i._originalHostOrSocketPath;(!f||i._originalSecure&&!r)&&(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=i._req=h(a),i._redirects&&i.emit("redirect",i.url,m)}else m=i._req=h(a);a.timeout&&m.on("timeout",()=>{et(i,m,"Opening handshake has timed out")}),m.on("error",f=>{m===null||m[Gi]||(m=i._req=null,da(i,f))}),m.on("response",f=>{let g=f.headers.location,b=f.statusCode;if(g&&a.followRedirects&&b>=300&&b<400){if(++i._redirects>a.maxRedirects){et(i,m,"Maximum redirects exceeded");return}m.abort();let w;try{w=new cn(g,t)}catch{let y=new SyntaxError(`Invalid URL: ${g}`);da(i,y);return}Vi(i,w,e,s)}else i.emit("unexpected-response",m,f)||et(i,m,`Unexpected server response: ${f.statusCode}`)}),m.on("upgrade",(f,g,b)=>{if(i.emit("upgrade",f),i.readyState!==xe.CONNECTING)return;m=i._req=null;let w=f.headers.upgrade;if(w===void 0||w.toLowerCase()!=="websocket"){et(i,g,"Invalid Upgrade header");return}let v=gl("sha1").update(d+kl).digest("base64");if(f.headers["sec-websocket-accept"]!==v){et(i,g,"Invalid Sec-WebSocket-Accept header");return}let y=f.headers["sec-websocket-protocol"],x;if(y!==void 0?u.size?u.has(y)||(x="Server sent an invalid subprotocol"):x="Server sent a subprotocol but none was requested":u.size&&(x="Server sent no subprotocol"),x){et(i,g,x);return}y&&(i._protocol=y);let S=f.headers["sec-websocket-extensions"];if(S!==void 0){if(!p){et(i,g,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let _;try{_=Al(S)}catch{et(i,g,"Invalid Sec-WebSocket-Extensions header");return}let M=Object.keys(_);if(M.length!==1||M[0]!==Ct.extensionName){et(i,g,"Server indicated an extension that was not requested");return}try{p.accept(_[Ct.extensionName])}catch{et(i,g,"Invalid Sec-WebSocket-Extensions header");return}i._extensions[Ct.extensionName]=p}i.setSocket(g,b,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(m,i):m.end()}function da(i,t){i._readyState=xe.CLOSING,i._errorEmitted=!0,i.emit("error",t),i.emitClose()}function Dl(i){return i.path=i.socketPath,Wi.connect(i)}function Rl(i){return i.path=void 0,!i.servername&&i.servername!==""&&(i.servername=Wi.isIP(i.host)?"":i.host),ml.connect(i)}function et(i,t,e){i._readyState=xe.CLOSING;let s=new Error(e);Error.captureStackTrace(s,et),t.setHeader?(t[Gi]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(da,i,s)):(t.destroy(s),t.once("error",i.emit.bind(i,"error")),t.once("close",i.emitClose.bind(i)))}function un(i,t,e){if(t){let s=bl(t)?t.size:El(t).length;i._socket?i._sender._bufferedBytes+=s:i._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${i.readyState} (${kt[i.readyState]})`);process.nextTick(e,s)}}function Il(i,t){let e=this[Oe];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=i,e._socket[Oe]!==void 0&&(e._socket.removeListener("data",ha),process.nextTick(Yi,e._socket),i===1005?e.close():e.close(i,t))}function Ll(){let i=this[Oe];i.isPaused||i._socket.resume()}function Ml(i){let t=this[Oe];t._socket[Oe]!==void 0&&(t._socket.removeListener("data",ha),process.nextTick(Yi,t._socket),t.close(i[Sl])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",i))}function qi(){this[Oe].emitClose()}function Fl(i,t){this[Oe].emit("message",i,t)}function Ol(i){let t=this[Oe];t._autoPong&&t.pong(i,!this._isServer,zi),t.emit("ping",i)}function Nl(i){this[Oe].emit("pong",i)}function Yi(i){i.resume()}function Bl(i){let t=this[Oe];t.readyState!==xe.CLOSED&&(t.readyState===xe.OPEN&&(t._readyState=xe.CLOSING,Ki(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",i)))}function Ki(i){i._closeTimer=setTimeout(i._socket.destroy.bind(i._socket),i._closeTimeout)}function Ji(){let i=this[Oe];if(this.removeListener("close",Ji),this.removeListener("data",ha),this.removeListener("end",Xi),i._readyState=xe.CLOSING,!this._readableState.endEmitted&&!i._closeFrameReceived&&!i._receiver._writableState.errorEmitted&&this._readableState.length!==0){let t=this.read(this._readableState.length);i._receiver.write(t)}i._receiver.end(),this[Oe]=void 0,clearTimeout(i._closeTimer),i._receiver._writableState.finished||i._receiver._writableState.errorEmitted?i.emitClose():(i._receiver.on("error",qi),i._receiver.on("finish",qi))}function ha(i){this[Oe]._receiver.write(i)||this.pause()}function Xi(){let i=this[Oe];i._readyState=xe.CLOSING,i._receiver.end(),this.end()}function Qi(){let i=this[Oe];this.removeListener("error",Qi),this.on("error",zi),i&&(i._readyState=xe.CLOSING,this.destroy())}});var ar=Ke((Xd,sr)=>{"use strict";var Jd=ua(),{Duplex:Ul}=require("stream");function er(i){i.emit("close")}function $l(){!this.destroyed&&this._writableState.finished&&this.destroy()}function tr(i){this.removeListener("error",tr),this.destroy(),this.listenerCount("error")===0&&this.emit("error",i)}function jl(i,t){let e=!0,s=new Ul({...t,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return i.on("message",function(n,r){let o=!r&&s._readableState.objectMode?n.toString():n;s.push(o)||i.pause()}),i.once("error",function(n){s.destroyed||(e=!1,s.destroy(n))}),i.once("close",function(){s.destroyed||s.push(null)}),s._destroy=function(a,n){if(i.readyState===i.CLOSED){n(a),process.nextTick(er,s);return}let r=!1;i.once("error",function(c){r=!0,n(c)}),i.once("close",function(){r||n(a),process.nextTick(er,s)}),e&&i.terminate()},s._final=function(a){if(i.readyState===i.CONNECTING){i.once("open",function(){s._final(a)});return}i._socket!==null&&(i._socket._writableState.finished?(a(),s._readableState.endEmitted&&s.destroy()):(i._socket.once("finish",function(){a()}),i.close()))},s._read=function(){i.isPaused&&i.resume()},s._write=function(a,n,r){if(i.readyState===i.CONNECTING){i.once("open",function(){s._write(a,n,r)});return}i.send(a,r)},s.on("end",$l),s.on("error",tr),s}sr.exports=jl});var pn=Ke((Qd,nr)=>{"use strict";var{tokenChars:Hl}=Gt();function ql(i){let t=new Set,e=-1,s=-1,a=0;for(a;a<i.length;a++){let r=i.charCodeAt(a);if(s===-1&&Hl[r]===1)e===-1&&(e=a);else if(a!==0&&(r===32||r===9))s===-1&&e!==-1&&(s=a);else if(r===44){if(e===-1)throw new SyntaxError(`Unexpected character at index ${a}`);s===-1&&(s=a);let o=i.slice(e,s);if(t.has(o))throw new SyntaxError(`The "${o}" subprotocol is duplicated`);t.add(o),e=s=-1}else throw new SyntaxError(`Unexpected character at index ${a}`)}if(e===-1||s!==-1)throw new SyntaxError("Unexpected end of input");let n=i.slice(e,a);if(t.has(n))throw new SyntaxError(`The "${n}" subprotocol is duplicated`);return t.add(n),t}nr.exports={parse:ql}});var hr=Ke((eh,dr)=>{"use strict";var Wl=require("events"),pa=require("http"),{Duplex:Zd}=require("stream"),{createHash:zl}=require("crypto"),ir=la(),Ft=zt(),Gl=pn(),Vl=ua(),{CLOSE_TIMEOUT:Yl,GUID:Kl,kWebSocket:Jl}=vt(),Xl=/^[+/0-9A-Za-z]{22}==$/,rr=0,or=1,cr=2,mn=class extends Wl{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Yl,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:Vl,...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=pa.createServer((s,a)=>{let n=pa.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=Ql(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(a,n,r)=>{this.handleUpgrade(a,n,r,s)}})}t.perMessageDeflate===!0&&(t.perMessageDeflate={}),t.clientTracking&&(this.clients=new Set,this._shouldEmitClose=!1),this.options=t,this._state=rr}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===cr){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(ws,this);return}if(t&&this.once("close",t),this._state!==or)if(this._state=or,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(ws,this):process.nextTick(ws,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{ws(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",lr);let n=t.headers["sec-websocket-key"],r=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){Ot(this,t,e,405,"Invalid HTTP method");return}if(r===void 0||r.toLowerCase()!=="websocket"){Ot(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Xl.test(n)){Ot(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){Ot(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){ks(e,400);return}let c=t.headers["sec-websocket-protocol"],l=new Set;if(c!==void 0)try{l=Gl.parse(c)}catch{Ot(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 Ft({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let p=ir.parse(d);p[Ft.extensionName]&&(u.accept(p[Ft.extensionName]),h[Ft.extensionName]=u)}catch{Ot(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,g)=>{if(!p)return ks(e,m||401,f,g);this.completeUpgrade(h,n,l,t,e,s,a)});return}if(!this.options.verifyClient(u))return ks(e,401)}this.completeUpgrade(h,n,l,t,e,s,a)}completeUpgrade(t,e,s,a,n,r,o){if(!n.readable||!n.writable)return n.destroy();if(n[Jl])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>rr)return ks(n,503);let l=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${zl("sha1").update(e+Kl).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[Ft.extensionName]){let h=t[Ft.extensionName].params,u=ir.format({[Ft.extensionName]:[h]});l.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",l,a),n.write(l.concat(`\r
2
2
  `).join(`\r
3
- `)),n.removeListener("error",Ti),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(ds,this)})),o(d,a)}};_i.exports=Ha;function $o(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 ds(r){r._state=Ci,r.emit("close")}function Ti(){this.destroy()}function hs(r,t,e,s){e=e||Qs.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} ${Qs.STATUS_CODES[t]}\r
3
+ `)),n.removeListener("error",lr),d.setSocket(n,r,{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(ws,this)})),o(d,a)}};dr.exports=mn;function Ql(i,t){for(let e of Object.keys(t))i.on(e,t[e]);return function(){for(let s of Object.keys(t))i.removeListener(s,t[s])}}function ws(i){i._state=cr,i.emit("close")}function lr(){this.destroy()}function ks(i,t,e,s){e=e||pa.STATUS_CODES[t],s={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(e),...s},i.once("finish",i.destroy),i.end(`HTTP/1.1 ${t} ${pa.STATUS_CODES[t]}\r
4
4
  `+Object.keys(s).map(a=>`${a}: ${s[a]}`).join(`\r
5
5
  `)+`\r
6
6
  \r
7
- `+e)}function It(r,t,e,s,a,n){if(r.listenerCount("wsClientError")){let i=new Error(a);Error.captureStackTrace(i,It),r.emit("wsClientError",i,e,t)}else hs(e,s,a,n)}});var nl={};Ki(nl,{default:()=>aa});module.exports=Ji(nl);var ji=require("fs"),Se=require("obsidian");var St="agent-fleet-agents";var bt="agent-fleet-dashboard",nt="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,defaultFileHashes:{}},Ka=["agents","skills","tasks","runs","memory","channels"];var _=require("obsidian");var ra=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`---
7
+ `+e)}function Ot(i,t,e,s,a,n){if(i.listenerCount("wsClientError")){let r=new Error(a);Error.captureStackTrace(r,Ot),i.emit("wsClientError",r,e,t)}else ks(e,s,a,n)}});var vc={};Ur(vc,{default:()=>xa});module.exports=$r(vc);var Rr=require("fs"),Te=require("obsidian");var _t="agent-fleet-agents";var xt="agent-fleet-dashboard",nt="agent-fleet-chat",lt={fleetFolder:"_fleet",claudeCliPath:"claude",codexCliPath:"codex",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:{}},kn=["agents","skills","tasks","runs","memory","channels"];var T=require("obsidian");var Ta=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`---
8
8
  {}
9
9
  ---
10
10
 
@@ -558,7 +558,7 @@ When the CLI returns an error result (context overflow, rate limit, auth issue),
558
558
  - Renders a red error bubble in the chat: \`Error: <reason>\`
559
559
  - Flips \`isStreaming\` back to false so the stop button clears and the user can send another message
560
560
 
561
- A 5-minute watchdog additionally protects against CLI subprocess hangs: if no stream events arrive while streaming, the turn is auto-rejected, the process is killed, and a timeout error is surfaced in the chat. This guarantees the chat never gets permanently stuck.
561
+ A configurable watchdog (default 10 minutes; tunable via plugin Settings \u2192 "Chat watchdog timeout") additionally protects against CLI subprocess hangs: if no stream events arrive while streaming, the turn is auto-rejected, the process is killed, and a timeout error is surfaced in the chat. This guarantees the chat never gets permanently stuck.
562
562
  `},{path:"skills/agent-fleet-system/skill.md",content:`---
563
563
  name: agent-fleet-system
564
564
  description: Complete knowledge of the Agent Fleet plugin \u2014 file structures, schemas, configuration, and management operations
@@ -637,16 +637,20 @@ who it is, how it behaves, what it does.
637
637
  ### config.md \u2014 Runtime Configuration
638
638
  \`\`\`yaml
639
639
  ---
640
- model: opus # Aliases work everywhere: opus / sonnet / haiku / opusplan.
640
+ model: opus # Claude aliases work everywhere: opus / sonnet / haiku / opusplan.
641
641
  # Or "default" (let CLI pick), or a pinned ID like
642
642
  # "claude-opus-4-7" (direct), "us.anthropic.claude-opus-4-7" (Bedrock),
643
- # "claude-opus-4-7@20251101" (Vertex). Backend-agnostic aliases are preferred.
644
- adapter: claude-code # Currently the only supported adapter
643
+ # "claude-opus-4-7@20251101" (Vertex). For Codex agents use slugs
644
+ # like "gpt-5.5". Backend-agnostic aliases are preferred.
645
+ adapter: claude-code # "claude-code" (default) or "codex" (OpenAI Codex CLI)
645
646
  timeout: 300 # Seconds before kill
646
647
  max_retries: 1
647
648
  cwd: "" # Working directory (empty = vault root)
648
- permission_mode: bypassPermissions # "bypassPermissions", "dontAsk", "acceptEdits", "plan"
649
+ permission_mode: bypassPermissions # Claude: "bypassPermissions", "dontAsk", "acceptEdits", "plan".
650
+ # Codex: "bypassPermissions" (no sandbox), "workspace-write", "read-only".
651
+ # Claude-style values map to the nearest Codex sandbox automatically.
649
652
  effort: "" # Reasoning effort: "low", "medium", "high", "max", or "" for default
653
+ # (Codex maps "max" to its "xhigh" level)
650
654
  approval_required: []
651
655
  allowed_tools: []
652
656
  blocked_tools: []
@@ -683,6 +687,15 @@ Rules use Claude Code's native format:
683
687
  - With \`bypassPermissions\` mode: everything runs EXCEPT deny list
684
688
  - With \`dontAsk\` mode: only allow list runs, everything else blocked
685
689
 
690
+ Both adapters enforce these rules. \`claude-code\` applies them directly. \`codex\`
691
+ translates \`Bash(cmd args *)\` command patterns into execpolicy rules (deny \u2192
692
+ forbidden, allow \u2192 allow) injected via a per-agent \`CODEX_HOME\` overlay. Codex
693
+ limitations: only command-prefix \`Bash(...)\` patterns translate \u2014 tool-name
694
+ rules (\`Read\`/\`Write\`/\`Edit\`) and mid-pattern wildcards are ignored, and file/
695
+ network access is governed by the sandbox level (\`permission_mode\`:
696
+ bypassPermissions / workspace-write / read-only) instead. Requires a Codex build
697
+ with execpolicy support; otherwise the agent falls back to sandbox-only.
698
+
686
699
  ### SKILLS.md \u2014 Agent-Specific Skills (optional)
687
700
  \`\`\`markdown
688
701
  Additional instructions specific to this agent that aren't reusable as shared skills.
@@ -10968,167 +10981,232 @@ python scripts/with_server.py \\
10968
10981
  - Raises \`RuntimeError\` if a server fails to start within the timeout
10969
10982
  - Always cleans up server processes in the \`finally\` block
10970
10983
  - Exits with the return code of the executed command
10971
- `},{path:"skills/wiki-ingest/skill.md",content:'---\nname: wiki-ingest\ndescription: "Ingest new sources into a scoped wiki. Two modes: inbox (destructive \u2014 summarize and archive) and watched (non-destructive \u2014 extract durable claims into topic pages on mtime change)."\ntags:\n - wiki-keeper\n - knowledge\n - ingest\n---\n\n# wiki-ingest\n\nYou are running the **ingestion phase** of a scoped Wiki Keeper. Use this skill when the user or a heartbeat asks you to ingest, or when the prompt references new files in `_sources/inbox/` or changes in watched folders.\n\n## Scope resolution\n\nYour scope config is in the calling agent\'s `config.md` under the `wiki_keeper:` block. Read it **first**. Resolve every path in this skill relative to `scope_root`:\n\n- `inbox_path` (default `_sources/inbox`)\n- `archive_path` (default `_sources/archive`)\n- `topics_root` (default `_topics`)\n- `index_path` (default `index.md`)\n- `log_path` (default `log.md`)\n- `state_file` (default `.wiki-keeper-state.json`)\n- `watched_folders` \u2014 list, vault-relative\n- `exclude_patterns` \u2014 glob list, vault-relative\n- `watched_since` \u2014 optional ISO date (YYYY-MM-DD). In watched mode, skip any file whose mtime is strictly older. Missing or empty = no cutoff.\n\n(Lint runs on its own schedule via a sibling `*-lint` task \u2014 do NOT run wiki-lint as part of this skill.)\n\n**Never write outside `scope_root`.** If scope_root is empty, treat the whole vault as the scope.\n\n## Two modes \u2014 run both in one invocation\n\nRun **inbox mode** first, then **watched mode**.\n\n---\n\n### Inbox mode (destructive)\n\n1. List every file under `<scope_root>/<inbox_path>/` excluding `exclude_patterns`.\n2. For each file, in order:\n 1. Read the content. For PDFs use the `pdf` skill; for docx/xlsx use those skills.\n 2. Identify the **subjects** it covers (entities, concepts, events).\n 3. For each subject, grep `<scope_root>/<topics_root>/` for an existing page. If missing, create `<topics_root>/<slug>.md` with appropriate frontmatter (see the agent\'s `CONTEXT.md` for page-type schema).\n 4. Write a **summary page** at `<topics_root>/summaries/YYYY-MM-DD-<source-slug>.md` with frontmatter `{ type: summary, source: <original filename>, date: <today> }` and a concise summary of the source.\n 5. For each subject, append a dated sub-entry to that topic page: `- YYYY-MM-DD: from [[summaries/YYYY-MM-DD-<source-slug>]]: <one-sentence claim>`.\n 6. Update `<index_path>` inside the fenced `<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->` block \u2014 add a one-line entry for each new topic page, alphabetized, under the appropriate heading.\n 7. **Move** (not copy) the source from `<inbox_path>/` to `<archive_path>/YYYY/MM/<original filename>`. Use Bash `mv`, not Write \u2014 Write would leave the original in the inbox and cause re-processing on the next cycle. First run `mkdir -p <archive_path>/YYYY/MM/` to ensure the target folder exists, then `mv <inbox_path>/<file> <archive_path>/YYYY/MM/<file>`. Both `YYYY` and `MM` are zero-padded (e.g. `2026/04`).\n3. If any single file fails, leave it in the inbox and log the error; continue with the rest.\n4. Append a log entry to `<log_path>` (also fenced): `- YYYY-MM-DD HH:MM inbox: processed N files; created [[x]], [[y]]; updated [[a]], [[b]]`.\n\n### Watched mode (non-destructive)\n\n1. Load `<scope_root>/<state_file>`. Expected shape:\n ```json\n {\n "mtimes": { "<path>": "<ISO8601>" },\n "lastIngest": "<ISO8601>"\n }\n ```\n If the file is missing, treat every watched file as new.\n2. For each path in `watched_folders` (resolved relative to vault root, not scope_root \u2014 watched folders may sit anywhere):\n 1. List every file in the folder, recursively, excluding `exclude_patterns`.\n 2. For each file, read its mtime. If `watched_since` is set and the file\'s mtime is strictly older than that date (midnight local), **skip it** \u2014 do not process or record in the state file. Otherwise compare to `mtimes[<path>]`. Process only files whose mtime is newer (or unknown).\n3. For each file to process:\n 1. Read it.\n 2. Identify every **distinct subject** the file touches \u2014 each person, org, product, project, concept, meeting, decision, or event mentioned is its own subject. A single daily note or meeting transcript typically yields 3\u201310 subjects, not one.\n 3. For **each subject**, locate or create its own topic page under `<topics_root>/` (following the page-type conventions in the agent\'s `CONTEXT.md` \u2014 entity / concept / event). If a page for that subject already exists, update it; if not, create a new one with appropriate frontmatter.\n 4. Extract ONLY **durable claims** about each subject \u2014 decisions made, commitments, key facts, relationships to other entities, concept definitions. Skip small talk, noise, procedural details, and anything not worth remembering in a week.\n 5. Append each claim as a dated sub-entry **to the topic page of the subject it\'s about**, with a forward wikilink back to the source:\n `- YYYY-MM-DD: from [[path/to/source-file|source-file]]: <claim>`\n 6. **Do NOT** create a summary page for this file.\n 7. **Do NOT** open the source file for write. Only read.\n4. Update `<state_file>` with the new mtimes and `lastIngest`.\n5. Append one consolidated log entry: `- YYYY-MM-DD HH:MM watched: processed N files across M folders; updated [[x]], [[y]]`.\n\n#### Anti-patterns for watched mode\n\n- \u274C **One dump page per source.** Creating a single topic page (e.g. `daily-2026-04-21.md` or `standup-notes.md`) and listing everything from the source in it. Topic pages are organized by *subject*, not by *source*. A Monday standup mentioning Alice, Project X, and a pricing decision produces entries on **three different pages** (`alice.md`, `project-x.md`, `pricing-decision-2026-04-21.md`), each linking back to the standup note.\n- \u274C **One catch-all concept page** with everything filed under `type: concept`. Concept pages are for specific ideas/techniques/patterns (e.g. `event-sourcing.md`, `blue-green-deploy.md`), not for "things from yesterday\'s meeting."\n- \u274C **Rewriting the source.** Watched files are read-only; never modify, move, or delete them.\n- \u274C **Creating a summary page** (`type: summary`). Those are inbox-mode only. If you\'re tempted to write "here\'s what this source said," you\'re in the wrong mode \u2014 just extract the claims and distribute them.\n\n---\n\n## Rules that apply to both modes\n\n- **Preserve user-authored content.** When updating a page, find the append zone (end of file, or a specific `## Claims` / `## Contradictions` section) and add there. Never rewrite sections you didn\'t create.\n- **Wikilinks only.** All internal links are `[[path|Display]]`.\n- **Contradictions.** When a new claim contradicts an existing one on the same topic page, add it under a `## Contradictions` section with a dated entry. Do NOT silently overwrite. Flag in the log entry: `- CONFLICT: [[topic]] \u2014 new source contradicts earlier claim`.\n- **Fenced writes.** `<index_path>` and `<log_path>` may contain user-authored content outside our fenced blocks. Only modify within `<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->`. If the file doesn\'t yet have those markers, create them and place all your content between them, preserving any pre-existing user content above and below.\n- **Token budget.** The `max_tokens_per_ingest` config field caps how much you spend in one run. If you approach the limit, stop processing new files, log what was skipped, and exit. The next cycle resumes.\n- **Frontmatter conventions.** See the agent\'s `CONTEXT.md` for page types. Always include `type:` on new pages.\n\n## Output\n\nOn success: produce a short summary of what was ingested (counts + main topic names). This is what the heartbeat will broadcast to Slack if a channel is configured.\n\nOn failure: list what succeeded, what failed, and log the failures.\n\n## What NOT to do\n\n- Never delete user-authored pages.\n- Never modify a watched source file.\n- Never move a watched source file.\n- Never write outside `scope_root` (except reading watched folders).\n- Never summarize training-data knowledge \u2014 stick to what\'s in the actual sources.\n- Never create per-day summary pages for watched-source daily notes \u2014 that would flood the wiki. Use topic-page updates only.\n'},{path:"skills/wiki-lint/skill.md",content:`---
10972
- name: wiki-lint
10973
- description: "Weekly health check for a scoped wiki. Finds orphans, stale pages, missing cross-links, contradictions, schema violations, and index drift. Proposes fixes; auto-applies only deterministic ones."
10984
+ `},{path:"skills/wiki-ingest/skill.md",content:'---\nname: wiki-ingest\ndescription: "Ingest new sources into a scoped wiki. Two modes: inbox (destructive \u2014 summarize and archive) and watched (non-destructive \u2014 extract durable claims into topic pages on hash change). Ends by invoking wiki-refresh on touched pages."\ntags:\n - wiki-keeper\n - knowledge\n - ingest\n---\n\n# wiki-ingest\n\nYou are running the **ingestion phase** of a scoped Wiki Keeper. Use this skill when the user or a heartbeat asks you to ingest, or when the prompt references new files in `_sources/inbox/` or changes in watched folders.\n\n## Scope resolution\n\nYour scope config is in the calling agent\'s `config.md` under the `wiki_keeper:` block. Read it **first**. Resolve every path in this skill relative to `scope_root`:\n\n- `inbox_path` (default `_sources/inbox`)\n- `archive_path` (default `_sources/archive`)\n- `failed_path` (default `_sources/failed`)\n- `topics_root` (default `_topics`)\n- `index_path` (default `index.md`)\n- `log_path` (default `log.md`)\n- `state_file` (default `.wiki-keeper-state.json`)\n- `watched_folders` \u2014 list, vault-relative\n- `exclude_patterns` \u2014 glob list, vault-relative\n- `watched_since` \u2014 optional ISO date (YYYY-MM-DD). In watched mode, skip any file whose mtime is strictly older. Missing or empty = no cutoff.\n- `max_tokens_per_ingest` \u2014 hard cap on this run\n- `max_tokens_per_refresh` (default 30000) \u2014 separate budget for the end-of-run refresh phase\n- `index_split_threshold` (default 100) \u2014 when total topic pages exceeds this, split index into per-type sub-MOCs\n\n(Lint runs on its own schedule via a sibling `*-lint` task \u2014 do NOT run wiki-lint as part of this skill.)\n\n**Never write outside `scope_root`.** If scope_root is empty, treat the whole vault as the scope.\n\n## Touched-pages ledger\n\nMaintain an in-memory list `touched_pages: Set<string>` for this run. Every time you create or append to a topic page (in either mode), record its path. The end-of-run **Refresh phase** consumes this list.\n\n## State file shape\n\nThe state file lives at `<scope_root>/<state_file>` and tracks both watched-mode mtimes and content hashes plus inbox-mode failure counts:\n\n```json\n{\n "mtimes": { "<vault-relative path>": "<ISO8601>" },\n "hashes": { "<vault-relative path>": "<sha1-hex>" },\n "lastIngest": "<ISO8601>",\n "failures": {\n "<inbox-relative filename>": { "count": 2, "lastError": "...", "lastAttempt": "<ISO8601>" }\n }\n}\n```\n\nIf the file is missing, all maps are empty.\n\n## Three phases \u2014 run in order\n\n1. **Inbox mode** (destructive)\n2. **Watched mode** (non-destructive, hash-gated)\n3. **Refresh phase** (synthesis refresh on touched pages)\n\n---\n\n### Phase 1 \u2014 Inbox mode (destructive)\n\n1. List every file under `<scope_root>/<inbox_path>/` excluding `exclude_patterns`.\n2. For each file, in order:\n 1. Check `state.failures[<filename>]`. If `count >= 3`, **quarantine** the file: `mv` it to `<failed_path>/<filename>` and write a sidecar `<failed_path>/<filename>.error.md` containing `count`, `lastError`, `lastAttempt`. Skip processing. Continue.\n 2. Read the content. For PDFs use the `pdf` skill; for docx/xlsx use those skills. **Verify** these skills are attached to the agent before invoking \u2014 if missing, treat as a failure (see step 7) with error "missing skill: pdf".\n 3. Identify the **subjects** the file covers (entities, concepts, events).\n 4. Pick a **source-type tag** for bullets emitted from this file: `[email]` (`.eml`/`.msg`), `[doc]` (`.pdf`/`.docx`/`.xlsx`), `[note]` (`.md`), `[web]` (clipped HTML or URL drop), `[other]` otherwise. The tag rides on every dated bullet you append (see the bullet format below).\n 5. For each subject, grep `<scope_root>/<topics_root>/` for an existing page. If missing, **create** `<topics_root>/<slug>.md` with:\n - Frontmatter: `type:` (entity / concept / event), plus `summary_refreshed: ""`, `claims_at_refresh: 0`, plus type-specific fields per `CONTEXT.md`.\n - A fenced summary block right after frontmatter:\n ```\n <!-- wiki-keeper:summary:begin -->\n <!-- wiki-keeper:summary:end -->\n ```\n - A `## Claims` section header.\n - Record the page in `touched_pages`.\n 6. Write a **summary page** at `<topics_root>/summaries/YYYY-MM-DD-<source-slug>.md` with frontmatter `{ type: summary, source: <original filename>, date: <today> }` and a concise summary of the source. This is *not* a refreshable topic page \u2014 it has no fenced summary block.\n 7. For each subject, append a dated sub-entry to that topic page\'s `## Claims` section:\n ```\n - YYYY-MM-DD [doc]: from [[summaries/YYYY-MM-DD-<source-slug>]]: <one-sentence claim>\n ```\n The `[doc]` (or other) tag is the source-type. Add the page to `touched_pages` if not already present.\n 8. Update `<index_path>` inside the fenced `<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->` block \u2014 see **Index maintenance** below for handling growing indexes.\n 9. **Move** (not copy) the source from `<inbox_path>/` to `<archive_path>/YYYY/MM/<original filename>`. Use Bash `mv`, not Write. First run `mkdir -p <archive_path>/YYYY/MM/`, then `mv <inbox_path>/<file> <archive_path>/YYYY/MM/<file>`. Both `YYYY` and `MM` are zero-padded.\n 10. On success: clear `state.failures[<filename>]`.\n3. **On per-file failure** (any step throws or returns an error): increment `state.failures[<filename>].count`, set `lastError` to the error message and `lastAttempt` to now ISO. Leave the source in the inbox. Do NOT abort the whole run; continue with the next file. The 3-strikes rule in step 2.1 quarantines persistently failing sources.\n4. Append a log entry to `<log_path>` (fenced):\n ```\n - YYYY-MM-DD HH:MM inbox: processed N files; created [[x]], [[y]]; updated [[a]], [[b]]; quarantined K\n ```\n\n### Phase 2 \u2014 Watched mode (non-destructive, hash-gated)\n\n1. Load the state file (see shape above).\n2. For each path in `watched_folders` (resolved relative to vault root, not scope_root \u2014 watched folders may sit anywhere):\n 1. List every file in the folder, recursively, excluding `exclude_patterns`.\n 2. For each file, read its mtime. Apply the `watched_since` filter (skip strictly older).\n 3. **Hash gate.** Compute `sha1` of the normalized content (strip trailing whitespace per line, normalize line endings to `\\n`). If `state.hashes[<path>] == new_hash`, skip \u2014 file content is unchanged even if mtime moved (handles iCloud/Obsidian Sync touch-without-edit). If different or missing, queue for processing.\n3. For each file to process:\n 1. Read it.\n 2. Pick the source-type tag for the file\'s folder (e.g. `meetings/` \u2192 `[meeting]`; `daily-notes/` \u2192 `[note]`; transcripts \u2192 `[meeting]`; otherwise `[note]`).\n 3. Identify every **distinct subject** the file touches \u2014 each person, org, product, project, concept, meeting, decision, or event mentioned is its own subject. A single daily note or meeting transcript typically yields 3\u201310 subjects, not one.\n 4. For **each subject**, locate or create its own topic page under `<topics_root>/` (per `CONTEXT.md`). New pages get the same scaffolding as inbox-mode (frontmatter + fenced summary block + `## Claims` section). Add to `touched_pages`.\n 5. Extract ONLY **durable claims** about each subject \u2014 decisions, commitments, key facts, entity relationships, concept definitions. Skip small talk, noise, procedural details, and anything not worth remembering in a week.\n 6. **Idempotency check.** Before appending, grep the target page\'s `## Claims` section for an existing line `- YYYY-MM-DD <tag>: from [[<source-path>...` matching today\'s date AND this source path. If present and the claim text is substantively the same, **skip** \u2014 don\'t append a duplicate. (This is the dedup safety net for re-runs after a hash false-positive or partial failure.)\n 7. Append each claim as a dated sub-entry **to the topic page of the subject it\'s about**, with the source-type tag and forward wikilink back to the source:\n ```\n - YYYY-MM-DD [meeting]: from [[meetings/2026-04-18-vendor-sync|vendor-sync]]: Vendor X raised prices 15%\n ```\n 8. **Do NOT** create a summary page for this file.\n 9. **Do NOT** open the source file for write. Only read.\n4. Update `state.mtimes[<path>]` and `state.hashes[<path>]` for every successfully processed file. Update `state.lastIngest`.\n5. Append one consolidated log entry: `- YYYY-MM-DD HH:MM watched: processed N files across M folders; updated [[x]], [[y]]`.\n\n#### Anti-patterns for watched mode\n\n- \u274C **One dump page per source.** Topic pages are organized by *subject*, not by *source*. A Monday standup mentioning Alice, Project X, and a pricing decision produces entries on **three different pages**, each linking back to the standup note.\n- \u274C **One catch-all concept page.** Concept pages are for specific ideas/techniques/patterns, not for "things from yesterday\'s meeting."\n- \u274C **Rewriting the source.** Watched files are read-only; never modify, move, or delete them.\n- \u274C **Creating a summary page** (`type: summary`). Those are inbox-mode only.\n\n### Phase 3 \u2014 Refresh phase (synthesis)\n\nAfter both ingest phases finish, **invoke the `wiki-refresh` skill** with the `touched_pages` set as input.\n\n- Refresh threshold from `wiki-refresh` applies (\u22655 new claims, or 30-day stale, or new contradictions). Caller-supplied list bypasses the staleness check but still respects the per-page delta.\n- Refresh has its **own** token budget (`max_tokens_per_refresh`). It does not share the ingest budget.\n- If `wiki-refresh` is not attached to this agent, log a warning and skip \u2014 don\'t fail the ingest run.\n\nThe refresh phase is what keeps every cited topic page query-cheap. Do not skip it routinely.\n\n---\n\n## Index maintenance\n\nThe default `index.md` carries a single fenced block listing every topic page alphabetically. As the wiki grows this becomes unwieldy.\n\n**Sub-MOC split.** When the count of pages directly under `<topics_root>/` exceeds `index_split_threshold` (default 100), or when any single `type:` exceeds 30 pages, switch to a hub-of-hubs layout:\n\n- `index.md` becomes a thin top-level hub with one section per page-type, each pointing at a sub-MOC: `[[index/entities]]`, `[[index/concepts]]`, `[[index/events]]`.\n- Per-type indexes live at `<topics_root>/index/<type>.md` (or, if `topics_root != "_topics"`, at `<scope_root>/index/<type>.md`). Each is its own fenced-block MOC listing pages of that type.\n- Once split, every new topic page goes into the matching sub-MOC, not the root index.\n\nDetect the split state by checking for `<scope_root>/index/` existing. Once present, never write topic-page entries directly into the root `index.md`\'s fenced block \u2014 only into the relevant sub-MOC.\n\n---\n\n## Rules that apply to all phases\n\n- **Preserve user-authored content.** When updating a page, find the append zone (`## Claims`, `## Contradictions`, end of file). Never rewrite sections you didn\'t create.\n- **Wikilinks only.** All internal links are `[[path|Display]]`.\n- **Contradictions.** When a new claim contradicts an existing one on the same topic page, add it under `## Contradictions` with a dated entry. Do NOT silently overwrite. Flag in the log: `- CONFLICT: [[topic]] \u2014 new source contradicts earlier claim`.\n- **Fenced writes.** `<index_path>` and `<log_path>` may contain user-authored content outside our fenced blocks. Only modify within `<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->`. If the file doesn\'t yet have those markers, create them and place all your content between them, preserving any pre-existing user content above and below.\n- **Token budget.** `max_tokens_per_ingest` caps inbox + watched. If you approach the limit, stop processing new files, log what was skipped, and proceed to the Refresh phase (which has its own budget). The next cycle resumes.\n- **Frontmatter conventions.** See `CONTEXT.md` for page types. Always include `type:` on new pages, plus the empty `summary_refreshed`/`claims_at_refresh` slots.\n\n## Output\n\nOn success: short summary \u2014 counts per phase, main topic names, refresh count. The heartbeat broadcasts this if a channel is configured.\n\nOn failure: list what succeeded, what failed, what\'s quarantined, and log the failures.\n\n## What NOT to do\n\n- Never delete user-authored pages.\n- Never modify a watched source file.\n- Never move a watched source file.\n- Never write outside `scope_root` (except *reading* watched folders).\n- Never summarize training-data knowledge \u2014 stick to actual sources.\n- Never create per-day summary pages for watched-source daily notes \u2014 that floods the wiki. Use topic-page updates only.\n- Never write the `## Summary` block by hand \u2014 that\'s `wiki-refresh`\'s job. Leave the fenced block empty on new pages; refresh fills it.\n'},{path:"skills/wiki-lint/skill.md",content:'---\nname: wiki-lint\ndescription: "Weekly health check for a scoped wiki. Finds orphans, stale pages, missing cross-links (bidirectional), contradictions, schema violations, index drift, dedup candidates, and stale summaries. Proposes fixes; auto-applies only deterministic ones; can chain wiki-refresh for stale summaries."\ntags:\n - wiki-keeper\n - knowledge\n - lint\n---\n\n# wiki-lint\n\nYou are running the **weekly lint pass** on a scoped wiki. Your job is to find problems, propose fixes, and auto-apply ONLY deterministic ones. Judgment calls go in a "Needs review" list for the user.\n\n## Scope resolution\n\nRead `wiki_keeper:` from the calling agent\'s `config.md`:\n- `scope_root`\n- `topics_root`, `index_path`, `log_path`\n- `state_file`, `failed_path`\n- `watched_folders`\n- `dedup_similarity_threshold` (default 0.82) \u2014 Levenshtein-similarity ratio above which two page titles get flagged as a possible merge.\n- `summary_stale_days` (default 30) \u2014 pages whose `summary_refreshed` is older than this with claims since are stale.\n\nAll paths resolve relative to `scope_root` unless otherwise noted.\n\n## Checks to run\n\n### 1. Orphan topic pages\nFor each page under `<scope_root>/<topics_root>/`, check its inbound backlink count via Grep. Flag pages with zero inbound links.\n\n**Exempt:** `<topics_root>/syntheses/` pages whose frontmatter is `type: synthesis` AND that have at least one outbound forward-link to a topic page they cite. Syntheses earn their place by linking out, not in. (If a synthesis has zero outbound links AND zero inbound links, flag it \u2014 it\'s truly an orphan.)\n\n### 2. Missing forward-links from summaries\nFor each page under `<topics_root>/summaries/`, read its content. Case-insensitively match against slugs of existing topic pages. If the summary mentions a topic without a `[[wikilink]]` to it, flag \u2014 suggest adding the link.\n\n### 3. Bidirectional cross-link gaps (NEW)\nBuild the wikilink graph: parse every `[[link]]` in every topic page (excluding the `## Claims` history \u2014 claim-source links are unidirectional by design, watched-source files don\'t link back). For each edge `A \u2192 B` in topic-summary-or-prose space, check whether `B \u2192 A` exists somewhere on `B` (summary, prose, or `## Claims`). When asymmetric:\n\n- If `A` is referenced in `B`\'s `## Claims` already \u2014 it counts as bidirectional, no flag.\n- Otherwise, propose adding a forward-link from `B` to `A` in `B`\'s summary block \u2014 but **don\'t auto-apply**: the summary block is `wiki-refresh`\'s territory, not ours. Add to `Needs review` with the proposed wording, OR mark for `wiki-refresh` to consider on its next pass.\n\n### 4. Contradictions >30 days old\nGrep for `## Contradictions` sections across `<topics_root>/`. Within each, find dated entries older than 30 days. Flag for user review.\n\n### 5. Stale events\nFor each page with frontmatter `type: event` and `date: YYYY-MM-DD`, compute age. If >12 months AND no other page links forward to it in the last 90 days, flag as stale. Don\'t auto-archive \u2014 just flag.\n\n### 6. Index drift\nCompare `<index_path>` content (within our fenced block) \u2014 and any sub-MOCs under `<scope_root>/index/<type>.md` if the split layout is in use \u2014 to the actual list of pages under `<topics_root>/`. Flag:\n- Pages in the topics root but missing from index (suggest adding)\n- Index entries whose target file no longer exists (suggest removing)\n- Index split threshold reached but no split performed (suggest running `wiki-ingest` to trigger the split, or split manually).\n\n### 7. Schema violations\nFor each page under `<topics_root>/`, verify required frontmatter per `CONTEXT.md`:\n- `type:` field present (entity | concept | event | summary | synthesis)\n- `type: event` pages must have `date:`\n- `type: summary` pages must have `source:`\n- Topic pages of types entity/concept/event SHOULD have `summary_refreshed` and `claims_at_refresh` slots (deterministic auto-fix: add empty values).\n\n**Auto-apply:** add missing `type:` when the path makes it deterministic (`<topics_root>/summaries/X.md` \u2192 `type: summary`; `<topics_root>/syntheses/X.md` \u2192 `type: synthesis`); add empty `summary_refreshed: ""` and `claims_at_refresh: 0` slots when missing on entity/concept/event pages. Everything else is "Needs review".\n\n### 8. Watched-source drift\nRead `<state_file>`. For each entry whose source file no longer exists (user deleted or renamed it), find topic-page entries that forward-link back to that dead source path. Flag \u2014 suggest updating the entry. **Auto-apply:** strip the dead entry from `state.mtimes` and `state.hashes` so the source isn\'t tracked further.\n\n### 9. Stale summaries (NEW)\nFor each topic page with `summary_refreshed:` set, compute age in days. Flag pages where:\n- `summary_refreshed` is older than `summary_stale_days` AND `claim_count_now > claims_at_refresh` (claims accrued since last refresh), OR\n- `summary_refreshed` is missing AND the page has \u22651 claim.\n\nIf `wiki-refresh` is attached to this agent, **chain it** at the end of the lint run with the stale-page list (subject to `max_tokens_per_refresh`). Otherwise log the list under `Needs review` for the user to trigger manually.\n\n### 10. Dedup candidates (NEW)\nFor each pair of topic pages of the same `type:` whose slug similarity exceeds `dedup_similarity_threshold` (Levenshtein-ratio over normalized slugs), AND whose claim sources overlap by \u22651 source path, propose a merge.\n\n**Never auto-merge.** Always `Needs review`. Output the proposal in the form:\n- *"Possible merge: [[vendor-x]] and [[vendor-X]] (similarity 0.94, 3 shared sources). Suggested canonical: `vendor-x` (older). Manual merge needed \u2014 keep one, delete the other, redirect inbound links."*\n\n### 11. Failed-source review (NEW)\nList anything in `<failed_path>/` (the inbox quarantine). For each, surface filename, attempt count, and last error. Flag for user review \u2014 broken sources are a quality signal worth seeing weekly.\n\n## Reporting\n\nWrite ONE dated report to `<log_path>` under a `## Lint YYYY-MM-DD` heading (inside the fenced `<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->` block):\n\n```markdown\n## Lint 2026-04-26\n\n### Summary\n- Orphans: 2 (1 exempt synthesis)\n- Missing forward-links from summaries: 1\n- Bidirectional gaps: 4\n- Old contradictions: 0\n- Stale events: 3\n- Index drift: 1 addition / 0 removals (split threshold NOT reached)\n- Schema violations: 4 (3 auto-fixed)\n- Watched-source drift: 1 (auto-stripped from state)\n- Stale summaries: 7 (refresh chained: 5 covered, 2 deferred for budget)\n- Dedup candidates: 2\n- Failed sources in quarantine: 1\n\n### Auto-applied\n- Added `type: summary` to [[_topics/summaries/2026-04-10-vendor-brief]]\n- Added `summary_refreshed: ""` slot to [[_topics/vendor-x]]\n- Stripped dead path `meetings/2026-02-08-old-sync.md` from state file\n\n### Needs review\n- Orphan: [[_topics/old-proposal]] has no inbound links. Consider linking from [[index]] or archiving.\n- Bidirectional: [[vendor-x]] mentions [[procurement]] in summary, but [[procurement]] has no link back. Propose adding to procurement\'s summary.\n- Stale event: [[_topics/events/q3-kickoff]] (date: 2025-09-10) with no recent references. Archive?\n- Contradiction in [[_topics/vendor-x]] from 2026-03-12 still unresolved (45 days old).\n- Possible merge: [[vendor-x]] and [[vendor-X]] (similarity 0.94, 3 shared sources). Suggested canonical: `vendor-x`.\n- Failed source: `bad-pdf.pdf` (3 attempts, last error: "missing skill: pdf"). Attach the `pdf` skill or remove the file.\n\n### Refresh chained\n- Refreshed: [[vendor-x]], [[pricing-q3]], [[alice]], [[project-acme]], [[vendor-y]]\n- Deferred (budget): [[concept-event-sourcing]], [[meeting-cadence]]\n```\n\n## Rules\n\n- **Only auto-apply deterministic fixes.** Adding missing `type:` inferable from path = deterministic. Merging two topic pages = judgment call \u2192 "Needs review" only.\n- **Never delete user content.** Flagging an orphan is fine; deleting the orphan is not.\n- **Never auto-rename pages.** That breaks every inbound link.\n- **Never modify watched source files.**\n- **Never modify summary blocks.** That\'s `wiki-refresh`\'s territory.\n- **One lint report per run.** If called twice in the same day, the second run replaces the day\'s report.\n\n## Optional broadcast\n\nIf the agent\'s heartbeat channel is set, output a short one-paragraph summary of the lint findings suitable for Slack/Telegram. Include a link to the full report in the log.\n\n## What NOT to do\n\n- Never restructure the topic taxonomy as a lint action.\n- Never silently fix something that required judgment \u2014 log it.\n- Never suggest fixes outside `scope_root`.\n- Never auto-merge dedup candidates.\n- Never overwrite `## Summary` blocks \u2014 that\'s `wiki-refresh`.\n'},{path:"skills/wiki-query/skill.md",content:`---
10985
+ name: wiki-query
10986
+ description: "Answer a question strictly from wiki content. Works in two modes: keeper (queries your own scope, enforces isolation, compounds substantive answers back into the wiki) and consumer (queries one or more referenced scopes via wiki_references). Every factual claim cites a vault page. Refuses to hallucinate."
10974
10987
  tags:
10975
10988
  - wiki-keeper
10976
10989
  - knowledge
10977
- - lint
10990
+ - query
10978
10991
  ---
10979
10992
 
10980
- # wiki-lint
10981
-
10982
- You are running the **weekly lint pass** on a scoped wiki. Your job is to find problems, propose fixes, and auto-apply ONLY deterministic ones. Judgment calls go in a "Needs review" list for the user.
10983
-
10984
- ## Scope resolution
10985
-
10986
- Read \`wiki_keeper:\` from the calling agent's \`config.md\`:
10987
- - \`scope_root\`
10988
- - \`topics_root\`, \`index_path\`, \`log_path\`
10989
- - \`state_file\`
10990
- - \`watched_folders\`
10991
-
10992
- All paths resolve relative to \`scope_root\` unless otherwise noted.
10993
-
10994
- ## Checks to run
10995
-
10996
- ### 1. Orphan topic pages
10997
- For each page under \`<scope_root>/<topics_root>/\`, check its inbound backlink count via Grep. Flag pages with zero inbound links.
10993
+ # wiki-query
10998
10994
 
10999
- ### 2. Missing forward-links from summaries
11000
- For each page under \`<topics_root>/summaries/\`, read its content. Case-insensitively match against slugs of existing topic pages. If the summary mentions a topic without a \`[[wikilink]]\` to it, flag \u2014 suggest adding the link.
10995
+ You answer questions against the **current state of one or more wikis**. Your answer is grounded strictly in the wiki pages you have access to \u2014 never in training-data knowledge.
11001
10996
 
11002
- ### 3. Contradictions >30 days old
11003
- Grep for \`## Contradictions\` sections across \`<topics_root>/\`. Within each, find dated entries older than 30 days. Flag for user review.
10997
+ You operate in one of two modes, chosen by what's in your agent's \`config.md\`.
11004
10998
 
11005
- ### 4. Stale events
11006
- For each page with frontmatter \`type: event\` and \`date: YYYY-MM-DD\`, compute age. If >12 months AND no other page links forward to it in the last 90 days, flag as stale. Don't auto-archive \u2014 just flag.
10999
+ ## Mode A \u2014 Keeper (your own scope)
11007
11000
 
11008
- ### 5. Index drift
11009
- Compare \`<index_path>\` content (within our fenced block) to the actual list of pages under \`<topics_root>/\`. Flag:
11010
- - Pages in the topics root but missing from index (suggest adding)
11011
- - Index entries whose target file no longer exists (suggest removing)
11001
+ Triggered when the agent has a \`wiki_keeper:\` block.
11012
11002
 
11013
- ### 6. Schema violations
11014
- For each page under \`<topics_root>/\`, verify it has required frontmatter per the agent's \`CONTEXT.md\`. Specifically:
11015
- - \`type:\` field must be present (entity | concept | event | summary | synthesis)
11016
- - \`type: event\` pages must have \`date:\`
11017
- - \`type: summary\` pages must have \`source:\`
11003
+ - Resolve paths relative to \`wiki_keeper.scope_root\`.
11004
+ - Search \`<scope_root>/<topics_root>/\` only. Never cross into another scope.
11005
+ - If the question seems to relate to a different scope, reply: *"This is outside the \`<scope_root>\` scope. Ask \`@wiki-keeper-<other-scope>\` for their take."*
11006
+ - Respect \`file_substantive_answers\` (default \`true\`) and \`obsidian_url_scheme\`.
11018
11007
 
11019
- Auto-apply fix: add missing \`type:\` when the page path or filename makes it deterministic (e.g., \`<topics_root>/summaries/X.md\` \u2192 \`type: summary\`). Everything else is "Needs review".
11008
+ ## Mode B \u2014 Consumer (reference one or more scopes)
11020
11009
 
11021
- ### 7. Watched-source drift
11022
- Read \`<state_file>\`. For each entry whose source file no longer exists (user deleted or renamed it), find topic-page entries that forward-link back to that dead source path. Flag \u2014 suggest updating the entry.
11010
+ Triggered when the agent has a \`wiki_references:\` block (typically without a \`wiki_keeper:\` block).
11023
11011
 
11024
- ## Reporting
11012
+ - The prompt-build layer has injected a \`## Wiki Access\` section listing every wiki you can read, with each one's scope root, topics path, index path, and inbox path. **That section is your source of truth.**
11013
+ - When answering, **name which scope each citation belongs to** if more than one wiki is referenced.
11014
+ - When the user shares a durable claim that isn't in any referenced wiki yet, **write a short markdown file to the relevant wiki's inbox** at \`<inbox>/YYYY-MM-DD-<slug>.md\`. The keeper files it canonically on its next ingest. Do NOT write to \`<topics-path>/\` directly.
11015
+ - Never answer from training-data knowledge.
11025
11016
 
11026
- Write ONE dated report to \`<log_path>\` under a \`## Lint YYYY-MM-DD\` heading (inside the fenced \`<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->\` block):
11017
+ ## Shared procedure (both modes)
11027
11018
 
11028
- \`\`\`markdown
11029
- ## Lint 2026-04-26
11019
+ 1. Parse the question. Identify key entities, concepts, timeframes.
11020
+ 2. **Search** the topics path(s) using Grep with keyword variants. Weight results by inbound backlink count \u2014 central pages are more authoritative.
11021
+ 3. **Read** the top N candidate pages (default 5, cap 10 for broad questions). Prefer reading just the \`## Summary\` block first if present \u2014 it's the curated synthesis. Only fall back to \`## Claims\` history when the summary doesn't answer the question.
11022
+ 4. **Follow one hop** \u2014 for each candidate, check \`[[wikilinks]]\` in the summary and optionally read one linked page per candidate if clearly relevant.
11023
+ 5. **Synthesize** the answer. Every factual claim must be followed by a citation \`[[path|Display]]\`. Multiple citations per claim are welcome.
11024
+ 6. **Provenance line.** For each cited page, note its \`summary_refreshed\` (or file mtime if no summary). Add a one-line provenance footer at the end of your answer:
11025
+ > _Sources: [[vendor-x]] (refreshed 2026-04-25), [[pricing-q3]] (mtime 2026-03-12)_
11026
+ This is the user's signal that a cited page is stale.
11027
+ 7. If the wiki doesn't contain the answer, say so **explicitly**: *"I don't see this in the \`<scope>\` wiki. Last ingest was <lastIngest if available>. You may want to add relevant sources to \`<inbox>/\`."* Do NOT fabricate.
11028
+ 8. For external-channel replies (Slack/Telegram) when \`obsidian_url_scheme\` is on, convert \`[[topic-path|Display]]\` citations to \`obsidian://open?vault=<vault>&file=<full-path>\` URLs.
11029
+
11030
+ ## Compounding \u2014 Mode A only (Keeper)
11031
+
11032
+ Karpathy's central claim: valuable answers should file themselves back into the wiki rather than disappear into chat. When \`file_substantive_answers: true\` (default) AND the synthesized answer is **substantive** (>500 tokens of unique synthesis, OR introduces a connection between topics that isn't yet in their pages, OR resolves an open contradiction), do BOTH:
11033
+
11034
+ 1. **Synthesis page.** Write \`<topics_root>/syntheses/YYYY-MM-DD-<question-slug>.md\` with frontmatter \`{ type: synthesis, question: <q>, refreshed: <today> }\` and the answer body. Forward-link from this page to every cited topic. Forward-link from \`index.md\` (or the \`index/syntheses.md\` sub-MOC if split) so this synthesis isn't an orphan on next lint.
11035
+ 2. **Topic-page bullets.** For each cited topic page, append a dated bullet to its \`## Claims\` section:
11036
+ \`\`\`
11037
+ - YYYY-MM-DD [synthesis]: from [[syntheses/YYYY-MM-DD-<question-slug>|<short label>]]: <one-sentence takeaway specific to this topic>
11038
+ \`\`\`
11039
+ This is what makes the wiki compound from queries \u2014 every substantive Q&A leaves a per-topic trace, not just a sidecar synthesis.
11030
11040
 
11031
- ### Summary
11032
- - Orphans: 2
11033
- - Missing forward-links: 1
11034
- - Old contradictions: 0
11035
- - Stale events: 3
11036
- - Index drift: 1 addition / 0 removals
11037
- - Schema violations: 4 (2 auto-fixed)
11038
- - Watched-source drift: 1
11041
+ After compounding writes, the next end-of-ingest refresh phase will pick up the touched topic pages naturally (their claim count delta crosses the threshold). No need to invoke \`wiki-refresh\` directly from this skill.
11039
11042
 
11040
- ### Auto-applied
11041
- - Added \`type: summary\` to [[_topics/summaries/2026-04-10-vendor-brief]]
11042
- - Added \`type: summary\` to [[_topics/summaries/2026-04-12-planning-meeting]]
11043
+ **Skip compounding** when:
11044
+ - The answer is short (<500 tokens) and just restates an existing summary.
11045
+ - The user prefixed the question with \`/quick\` or asked a yes/no.
11046
+ - \`file_substantive_answers: false\`.
11043
11047
 
11044
- ### Needs review
11045
- - Orphan: [[_topics/old-proposal]] has no inbound links. Consider linking from [[index]] or archiving.
11046
- - Stale event: [[_topics/events/q3-kickoff]] (date: 2025-09-10) with no recent references. Archive?
11047
- - Contradiction in [[_topics/vendor-x]] from 2026-03-12 still unresolved (36 days old).
11048
- \`\`\`
11048
+ In Mode B (Consumer), compounding looks different: drop a markdown file into the relevant wiki's inbox per the consumer-mode rules. Do NOT write to \`<topics-path>/\` directly \u2014 that's the keeper's job.
11049
11049
 
11050
11050
  ## Rules
11051
11051
 
11052
- - **Only auto-apply deterministic fixes.** Adding a missing \`type:\` inferable from path = deterministic. Merging two topic pages = judgment call \u2192 "Needs review" only.
11053
- - **Never delete user content.** Flagging an orphan is fine; deleting the orphan is not.
11054
- - **Never auto-rename pages.** That breaks every inbound link.
11055
- - **Never modify watched source files.**
11056
- - **One lint report per run.** If called twice in the same day, the second run replaces the day's report.
11052
+ - **Wiki-grounded only.** If a claim isn't in any accessible wiki page, you don't know it.
11053
+ - **Cite everything.** Un-cited claims are a bug.
11054
+ - **Provenance footer.** Every answer ends with the sources line including refresh/mtime dates.
11055
+ - **Name the scope** in consumer mode when citations come from more than one wiki.
11056
+ - **Be concise.** Long prose with citations beats exhaustive prose. Group related claims under one citation rather than repeating it.
11057
+ - **Compounding is in Keeper mode only.** Consumer mode files claims to the inbox; the keeper synthesizes.
11057
11058
 
11058
- ## Optional broadcast
11059
+ ## Output shape
11059
11060
 
11060
- If the agent's \`heartbeatChannel\` is set, output a short one-paragraph summary of the lint findings suitable for Slack/Telegram. Include a link to the full report in the log.
11061
+ 1. **TL;DR** \u2014 one or two sentences that actually answer, with citations.
11062
+ 2. **Details** \u2014 bullets of specific claims, each cited.
11063
+ 3. **What the wiki is missing** (if relevant) \u2014 explicit note about gaps.
11064
+ 4. **Sources** (provenance footer) \u2014 \`[[page]] (refreshed YYYY-MM-DD)\` per citation, one line.
11065
+ 5. **Filed back** (Keeper compounding only, when triggered) \u2014 list the synthesis page path and the topic pages bulleted.
11066
+ 6. **Suggested inbox drop** (Consumer mode only, when the user shared new durable claims mid-answer) \u2014 list the file(s) you wrote to the inbox.
11061
11067
 
11062
11068
  ## What NOT to do
11063
11069
 
11064
- - Never restructure the topic taxonomy as a lint action.
11065
- - Never silently fix something that required judgment \u2014 log it.
11066
- - Never suggest fixes outside \`scope_root\`.
11067
- `},{path:"skills/wiki-query/skill.md",content:`---
11068
- name: wiki-query
11069
- description: "Answer a question strictly from wiki content. Works in two modes: keeper (queries your own scope, enforces isolation) and consumer (queries one or more referenced scopes listed in wiki_references). Every factual claim cites a vault page. Refuses to hallucinate."
11070
+ - Never answer from general knowledge.
11071
+ - Never cite external URLs (only vault pages).
11072
+ - Never rewrite topic page summary blocks (\`<!-- wiki-keeper:summary:* -->\`). Refresh owns those.
11073
+ - Never modify \`## Claims\` history.
11074
+ - In Keeper mode, only append to \`## Claims\` (compounding bullets) and only write under \`<topics_root>/syntheses/\`. Never edit other parts of topic pages.
11075
+ - In Consumer mode, never write outside \`<inbox>/\`.
11076
+ - Never cross-reference between scopes unless the user asked a cross-scope question \u2014 and even then, be explicit about which scope each page belongs to.
11077
+ `},{path:"skills/wiki-refresh/skill.md",content:`---
11078
+ name: wiki-refresh
11079
+ description: "Regenerate the \`## Summary\` block at the top of topic pages whose append-only claim list has outgrown the existing summary. Bounded by token budget; never touches user prose or claim history."
11070
11080
  tags:
11071
11081
  - wiki-keeper
11072
11082
  - knowledge
11073
- - query
11083
+ - refresh
11074
11084
  ---
11075
11085
 
11076
- # wiki-query
11086
+ # wiki-refresh
11077
11087
 
11078
- You answer questions against the **current state of one or more wikis**. Your answer is grounded strictly in the wiki pages you have access to \u2014 never in training-data knowledge.
11088
+ You are running the **synthesis refresh phase** of a scoped Wiki Keeper. Topic pages accrue dated bullets forever in \`## Claims\` (and sometimes \`## Contradictions\`); without a refresh, the top-of-page synthesis goes stale and every wiki-query read pays the full claim-history token cost. This skill rewrites a small, fenced summary block at the top of each candidate page so query reads stay cheap and the page actually summarizes what it knows.
11079
11089
 
11080
- You operate in one of two modes, chosen by what's in your agent's \`config.md\`.
11090
+ ## Scope resolution
11081
11091
 
11082
- ## Mode A \u2014 Keeper (your own scope)
11092
+ Read \`wiki_keeper:\` from the calling agent's \`config.md\`:
11093
+ - \`scope_root\`, \`topics_root\`, \`log_path\`
11094
+ - \`max_tokens_per_refresh\` (default 30000) \u2014 hard cap on this run
11095
+ - \`exclude_patterns\`
11083
11096
 
11084
- Triggered when the agent has a \`wiki_keeper:\` block.
11097
+ All paths resolve relative to \`scope_root\` unless otherwise noted. **Never write outside \`scope_root\`.**
11085
11098
 
11086
- - Resolve paths relative to \`wiki_keeper.scope_root\`.
11087
- - Search \`<scope_root>/<topics_root>/\` only. Never cross into another scope.
11088
- - If the question seems to relate to a different scope, reply with: *"This is outside the \`<scope_root>\` scope. Ask \`@wiki-keeper-<other-scope>\` for their take."*
11089
- - Respect \`file_substantive_answers\` and \`obsidian_url_scheme\`.
11099
+ ## When to run
11090
11100
 
11091
- ## Mode B \u2014 Consumer (reference one or more scopes)
11101
+ Three triggers, all valid:
11092
11102
 
11093
- Triggered when the agent has a \`wiki_references:\` block (typically without a \`wiki_keeper:\` block \u2014 consumer agents like PM agents, research agents, etc.).
11103
+ 1. **End-of-ingest (default).** \`wiki-ingest\` calls you with the list of pages it touched. Refresh only those that meet the threshold (see below).
11104
+ 2. **Manual.** User says "refresh \`[[topic]]\`", "refresh stale", or "refresh all". \`all\` ignores thresholds; \`stale\` uses them.
11105
+ 3. **Weekly via lint.** \`wiki-lint\` flags pages with stale summaries; the lint task can chain this skill.
11094
11106
 
11095
- - The prompt-build layer has injected a \`## Wiki Access\` section listing every wiki you can read, including each one's scope root, topics path, index path, and inbox path. **That section is your source of truth** for where to look.
11096
- - When answering, **name which scope each citation belongs to** if more than one wiki is referenced. Example: *"Per the \`acme\` wiki: [[projects/acme/_topics/pricing]] \u2026"*.
11097
- - When the user shares a durable claim that isn't in any referenced wiki yet (a decision, new entity, competitor update, meeting outcome), **write a short markdown file to the relevant wiki's inbox** at \`<inbox>/YYYY-MM-DD-<slug>.md\`. The wiki keeper will file it canonically on its next ingest. Do NOT write to \`<topics-path>/\` directly.
11098
- - Never answer from training-data knowledge. If no referenced wiki covers the question, say so.
11107
+ ## Refresh threshold
11099
11108
 
11100
- ## Shared procedure (both modes)
11109
+ A page is a refresh **candidate** when ANY of:
11110
+ - \`claim_count_now - frontmatter.claims_at_refresh \u2265 5\`
11111
+ - \`summary_refreshed\` is missing or older than 30 days, AND the page has \u22651 claim
11112
+ - \`## Contradictions\` section was added or extended since \`summary_refreshed\`
11113
+ - The caller explicitly passed the page in (end-of-ingest mode skips the threshold for caller-supplied pages but still respects token budget)
11101
11114
 
11102
- 1. Parse the question. Identify key entities, concepts, timeframes.
11103
- 2. **Search** the topics path(s) you're allowed to read, using Grep with keyword variants. Weight results by inbound backlink count \u2014 central pages are more authoritative.
11104
- 3. **Read** the top N candidate pages (default 5, cap 10 for broad questions).
11105
- 4. **Follow one hop** \u2014 for each candidate, check \`[[wikilinks]]\` and optionally read one linked page per candidate if it's clearly relevant.
11106
- 5. **Synthesize** an answer. Every factual claim must be followed by a citation \`[[path|Display]]\`. Multiple citations per claim are welcome.
11107
- 6. If the wiki does not contain the answer, say so **explicitly**: *"I don't see this in the \`<scope>\` wiki. Last ingest was <lastIngest if available>. You may want to add relevant sources to \`<inbox>/\`."* Do NOT fabricate.
11108
- 7. For external-channel replies (Slack/Telegram) when \`obsidian_url_scheme\` is on, convert \`[[topic-path|Display]]\` citations to \`obsidian://open?vault=<vault>&file=<full-path>\` URLs.
11115
+ Pages with zero claims and no user prose are **skipped** \u2014 there's nothing to summarize.
11116
+
11117
+ ## The summary block convention
11118
+
11119
+ Every topic page has a fenced summary block immediately after frontmatter, before any user content or \`## Claims\`. Two HTML comments mark the bounds:
11120
+
11121
+ \`\`\`markdown
11122
+ ---
11123
+ type: entity
11124
+ name: Vendor X
11125
+ summary_refreshed: 2026-04-26
11126
+ claims_at_refresh: 14
11127
+ ---
11128
+
11129
+ <!-- wiki-keeper:summary:begin -->
11130
+ ## Summary
11131
+
11132
+ - Vendor X is the incumbent CRM provider for the Acme team since 2024.
11133
+ - Pricing increased 15% in Q1 2026; renewal negotiation owned by [[procurement]].
11134
+ - Reliability has been stable but support response degraded over Q4 2025
11135
+ (see [[_topics/contradictions/vendor-x-uptime]]).
11136
+ - Active alternatives evaluated: [[vendor-y]], [[vendor-z]].
11137
+ <!-- wiki-keeper:summary:end -->
11138
+
11139
+ ## Claims
11140
+
11141
+ - 2026-04-18: from [[meetings/2026-04-18-vendor-sync|vendor-sync]]: Vendor X raised prices 15%
11142
+ - 2026-04-12: from [[summaries/2026-04-12-renewal-brief|renewal-brief]]: contract auto-renews 2026-12-01
11143
+ - ...
11144
+ \`\`\`
11145
+
11146
+ You write **only** within the fenced block. The \`## Claims\` section, \`## Contradictions\` section, and any user-authored prose elsewhere on the page are **read-only** to you.
11147
+
11148
+ If a candidate page does not yet have the fenced block, create it: insert immediately after the closing \`---\` of frontmatter (or at top if no frontmatter), with one trailing blank line before the next section.
11149
+
11150
+ ## Procedure
11151
+
11152
+ 1. **Candidate list.**
11153
+ - If caller passed pages: use those (still apply token budget).
11154
+ - If "refresh all": every page under \`<topics_root>/\` (excluding \`exclude_patterns\` and \`<topics_root>/syntheses/\` \u2014 those are already syntheses).
11155
+ - If "refresh stale" or invoked from lint: scan all pages, keep only those meeting the threshold above.
11156
+ 2. **Order by churn.** Sort candidates by \`claim_count_now - claims_at_refresh\` descending so the most-changed pages refresh first under a tight token budget.
11157
+ 3. **For each candidate, until budget exhausted:**
11158
+ 1. Read the full page.
11159
+ 2. Parse: frontmatter, summary block (if present), \`## Claims\` bullets, \`## Contradictions\` bullets, any user prose outside our blocks.
11160
+ 3. Synthesize a fresh summary:
11161
+ - **3\u20137 bullets**, each one sentence.
11162
+ - Cover: what this page is, the most durable facts, current status, key relationships (forward-link to other topics with \`[[\u2026]]\`), unresolved contradictions if any.
11163
+ - Prefer the most recent claims when older claims have been superseded \u2014 if a 2026-04 claim contradicts a 2025-09 claim, the summary reflects 2026-04 unless \`## Contradictions\` flags a still-open dispute.
11164
+ - Cite supporting claims by date inline only when ambiguity helps (e.g., "as of 2026-04\u2026"). Don't repeat the claim history.
11165
+ - Preserve user-authored prose elsewhere on the page; do NOT pull it into the summary unless it's clearly a fact about the entity. When in doubt, leave it where the user wrote it.
11166
+ 4. Write back: replace the block contents (or insert a new fenced block at the correct position).
11167
+ 5. Update frontmatter:
11168
+ - \`summary_refreshed: <today ISO>\`
11169
+ - \`claims_at_refresh: <current claim count>\`
11170
+ - Preserve all other frontmatter fields exactly.
11171
+ 6. Track tokens spent on this page; if running close to budget, finish the current page cleanly then stop.
11172
+ 4. **Log.** Append one consolidated entry to \`<log_path>\` (inside the fenced \`<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->\` block):
11173
+ \`\`\`
11174
+ - YYYY-MM-DD HH:MM refresh: regenerated N summaries (covered: [[a]], [[b]], \u2026); skipped M for budget
11175
+ \`\`\`
11176
+
11177
+ ## Counting claims
11178
+
11179
+ A "claim" is one bullet under \`## Claims\` or \`## Contradictions\` matching the dated pattern \`- YYYY-MM-DD ...\`. Lines without a leading date prefix don't count (they're prose, not append-mode entries).
11109
11180
 
11110
11181
  ## Rules
11111
11182
 
11112
- - **Wiki-grounded only.** If a claim isn't in any accessible wiki page, you don't know it.
11113
- - **Cite everything.** Un-cited claims are a bug.
11114
- - **Name the scope** in consumer mode when citations come from more than one wiki.
11115
- - **Provenance helps.** When a page is clearly stale (last edit >12 months and no fresh references), flag that in your answer.
11116
- - **Be concise.** Long prose with citations beats exhaustive prose. Group related claims under one citation rather than repeating it.
11183
+ - **Only the summary block is yours.** Never edit \`## Claims\`, \`## Contradictions\`, or user-authored sections.
11184
+ - **No new claims.** This skill never invents facts not already present in the page's claim history or its forward-linked pages. If the claim history is sparse, the summary is sparse \u2014 that's fine.
11185
+ - **No external knowledge.** No training-data facts. The summary is a *synthesis of what's on the page*, nothing else.
11186
+ - **Idempotent.** Running refresh twice in a row on an unchanged page should produce the same summary (modulo trivial wording). Don't introduce churn for its own sake \u2014 if \`claims_at_refresh\` matches the current count and the existing summary is reasonable, skip and don't bump \`summary_refreshed\`.
11187
+ - **Forward-links earn their place.** Any \`[[\u2026]]\` in the summary must point to a page that actually exists in the scope. Verify before writing.
11188
+ - **Wiki-keeper marker.** Pages refreshed by this skill carry the \`wiki-keeper\` tag (already set by ingest); don't re-add it.
11117
11189
 
11118
- ## Output shape
11190
+ ## Token budget
11119
11191
 
11120
- 1. **TL;DR** \u2014 one or two sentences that actually answer, with citations.
11121
- 2. **Details** \u2014 bullets of specific claims, each cited.
11122
- 3. **What the wiki is missing** (if relevant) \u2014 explicit note about gaps.
11123
- 4. **Suggested inbox drop** (consumer mode only, when the user shared new durable claims mid-answer) \u2014 list the file(s) you wrote to the inbox, one line each, so the user has a record.
11192
+ - Hard cap: \`max_tokens_per_refresh\` from config (default 30000).
11193
+ - When approaching the cap, finish the in-flight page, log how many candidates were skipped, and stop. The next ingest cycle will pick them up.
11194
+ - Per-page soft cap: ~3000 tokens of read + write. Pages with a huge claim history (>200 claims) get a *capped read* of the most recent 100 claims plus the full \`## Contradictions\` section \u2014 older claims are assumed already reflected in the prior summary.
11195
+
11196
+ ## Output
11197
+
11198
+ Short summary of the refresh pass: count refreshed, count skipped, list of pages touched. The heartbeat broadcasts this if a channel is configured.
11124
11199
 
11125
11200
  ## What NOT to do
11126
11201
 
11127
- - Never answer from general knowledge.
11128
- - Never cite external URLs (only vault pages).
11129
- - Never rewrite or update topic pages (\`<topics-path>/\`). That's the wiki keeper's job.
11130
- - Never write to \`<inbox>/\` in keeper mode unless \`file_substantive_answers: true\` (and even then, only to \`<topics-path>/syntheses/\`, not the inbox).
11131
- - In consumer mode, never cross-reference between scopes unless the user asked a cross-scope question \u2014 and even then, be explicit about which scope each page belongs to.
11202
+ - Never modify \`## Claims\` history.
11203
+ - Never modify \`## Contradictions\` content.
11204
+ - Never modify user-authored prose.
11205
+ - Never change frontmatter fields other than \`summary_refreshed\` and \`claims_at_refresh\`.
11206
+ - Never delete a topic page.
11207
+ - Never reformat or restructure the page beyond writing your fenced block.
11208
+ - Never run this on \`<topics_root>/syntheses/\` pages \u2014 those are already syntheses; double-summarizing them adds nothing.
11209
+ - Never invoke this skill from a consumer agent. It's keeper-mode only; only the keeper that owns the scope refreshes it.
11132
11210
  `},{path:"skills/xlsx/skill.md",content:`---
11133
11211
  name: xlsx
11134
11212
  description: "Create, read, edit, and clean spreadsheet files (.xlsx, .csv, .tsv) \u2014 formulas, charts, formatting, data cleanup."
@@ -11594,19 +11672,19 @@ python scripts/office/validate.py <path> [--original <original_file>] [--auto-re
11594
11672
 
11595
11673
  - \`paraId\`/\`durableId\` values that exceed OOXML limits
11596
11674
  - Missing \`xml:space="preserve"\` on \`w:t\` elements with whitespace
11597
- `}];var bs=require("obsidian");function ie(r){let t=r.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);if(!t)return{frontmatter:{},body:r.trim()};let e=t[1]??"",s=t[2]??"",a;try{a=(0,bs.parseYaml)(e)??{}}catch(n){console.warn("Agent Fleet: malformed YAML frontmatter, treating as empty",n),a={}}return{frontmatter:a,body:s.trim()}}function J(r,t){let e=(0,bs.stringifyYaml)(r).trim(),s=t.trim();return`---
11675
+ `}];var Ds=require("obsidian");function ae(i){let t=i.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);if(!t)return{frontmatter:{},body:i.trim()};let e=t[1]??"",s=t[2]??"",a;try{a=(0,Ds.parseYaml)(e)??{}}catch(n){console.warn("Agent Fleet: malformed YAML frontmatter, treating as empty",n),a={}}return{frontmatter:a,body:s.trim()}}function K(i,t){let e=(0,Ds.stringifyYaml)(i).trim(),s=t.trim();return`---
11598
11676
  ${e}
11599
11677
  ---
11600
11678
 
11601
11679
  ${s}
11602
- `}function ke(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Mt(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}var ht=require("child_process"),Xa=require("fs"),Jt=require("os"),Tt=require("path");function oa(){return(0,Jt.homedir)()}function Xt(){return(0,Tt.join)((0,Jt.homedir)(),".claude")}function Qt(){return(0,Tt.join)((0,Jt.homedir)(),".claude.json")}function Qa(){if(process.platform==="darwin")return"/bin/zsh";for(let r of["/bin/bash","/bin/zsh","/bin/sh"])if((0,Xa.existsSync)(r))return r;return"/bin/sh"}function Xi(r){return`'${r.replace(/'/g,"'\\''")}'`}function ut(r,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,ht.spawn)(r,t,s);let a=Qa(),n=[r,...t].map(Xi).join(" ");return(0,ht.spawn)(a,["-l","-c",n],s)}function la(r,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,ht.spawn)(r,[],{...e,shell:!0});let s=Qa();return(0,ht.spawn)(s,["-l","-c",r],e)}function Za(r){try{require("electron").shell.openExternal(r)}catch{switch(process.platform){case"darwin":(0,ht.spawn)("open",[r],{stdio:"ignore"});break;case"win32":(0,ht.spawn)("cmd.exe",["/c","start","",r.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,ht.spawn)("xdg-open",[r],{stdio:"ignore"});break}}}function pe(r){return r.split(/\r?\n/)}function en(r){let t=(0,Jt.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&&Ja(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&&Ja(s))}function Ja(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 ca(r){return!!(r.includes("/")||r.includes("\\"))}function ws(r){return typeof r=="object"&&r!==null}function M(r){return typeof r=="string"?r:void 0}function Ye(r,t){return typeof r=="boolean"?r:t}function et(r,t){return typeof r=="number"&&Number.isFinite(r)?r:t}function xe(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}function tn(r){let t=0;for(let e=0;e<r.length;e++){let s=r.charCodeAt(e);t=(t<<5)-t+s|0}return t.toString(36)}var Zt=class{constructor(t,e){this.vault=t;this.settings=e}agents=new Map;skills=new Map;tasks=new Map;channels=new Map;validationIssues=new Map;channelCredentialGetter;warnedFolderAgentModelConflict=new Set;setChannelCredentialGetter(t){this.channelCredentialGetter=t}getVaultBasePath(){let t=this.vault.adapter;return t instanceof _.FileSystemAdapter?t.getBasePath():void 0}getFleetRoot(){return(0,_.normalizePath)(this.settings.fleetFolder)}getSubfolder(t){return(0,_.normalizePath)(`${this.getFleetRoot()}/${t}`)}async ensureFleetStructure(){let t=this.getFleetRoot(),e=!this.vault.getAbstractFileByPath(t);await this.ensureFolder(t);for(let s of Ka)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of ra){let s=(0,_.normalizePath)(`${t}/${e.path}`),a=s.substring(0,s.lastIndexOf("/"));await this.ensureFolder(a),await this.createFileIfMissing(s,e.content)}}async updateDefaults(t){let e=this.getFleetRoot(),s={...t};for(let a of ra){let n=(0,_.normalizePath)(`${e}/${a.path}`),i=tn(a.content),o=t[a.path];if(o===i)continue;let l=this.vault.getAbstractFileByPath(n);if(!(l instanceof _.TFile)){let h=n.substring(0,n.lastIndexOf("/"));await this.ensureFolder(h),await this.createFileIfMissing(n,a.content),s[a.path]=i;continue}let c=await this.vault.cachedRead(l),d=tn(c);(!o||d===o)&&(await this.vault.modify(l,a.content),s[a.path]=i)}return s}async loadAll(){this.agents.clear(),this.skills.clear(),this.tasks.clear(),this.channels.clear(),this.validationIssues.clear(),await this.loadFolderAgents(),await this.loadFolderSkills();let t=this.vault.getMarkdownFiles().filter(e=>e.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 _.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),l=this.parseChannelFile(e.path,o);l&&this.channels.set(e.path,l)}return}let a=await this.vault.cachedRead(e),n=this.parseFile(e.path,a);n&&("taskId"in n?this.tasks.set(e.path,n):"model"in n?this.agents.set(e.path,n):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,_.normalizePath)(`${e}${a}`),i=(0,_.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof _.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof _.TFile))return;let c=await this.loadFolderAgent(n,l);c&&this.agents.set(i,c)}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,_.normalizePath)(`${e}${a}`),i=(0,_.normalizePath)(`${n}/skill.md`);if(this.skills.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof _.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof _.TFile))return;let c=await this.loadFolderSkill(n,l);c&&this.skills.set(i,c)}async loadFolderSkills(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("skills"));if(t instanceof _.TFolder)for(let e of t.children){if(!(e instanceof _.TFolder))continue;let s=(0,_.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof _.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}=ie(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async l=>{let c=(0,_.normalizePath)(`${t}/${l}`),d=this.vault.getAbstractFileByPath(c);if(!(d instanceof _.TFile))return"";let h=await this.vault.cachedRead(d);return ie(h).body};return{filePath:e.path,name:i,description:M(a.description),tags:xe(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 _.TFolder)for(let e of t.children){if(!(e instanceof _.TFolder))continue;let s=(0,_.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof _.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}=ie(s),i=M(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},l=(0,_.normalizePath)(`${t}/config.md`),c=this.vault.getAbstractFileByPath(l);if(c instanceof _.TFile){let O=await this.vault.cachedRead(c);o=ie(O).frontmatter}let d={allow:[],deny:[]},h=(0,_.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof _.TFile)try{let O=await this.vault.cachedRead(u),R=JSON.parse(O);d={allow:xe(R.allow),deny:xe(R.deny)}}catch{}let p="",m=(0,_.normalizePath)(`${t}/SKILLS.md`),f=this.vault.getAbstractFileByPath(m);if(f instanceof _.TFile){let O=await this.vault.cachedRead(f);p=ie(O).body}let v="",k=(0,_.normalizePath)(`${t}/CONTEXT.md`),w=this.vault.getAbstractFileByPath(k);if(w instanceof _.TFile){let O=await this.vault.cachedRead(w);v=ie(O).body}let y=!1,g="",x="",T=!0,C="",L=(0,_.normalizePath)(`${t}/HEARTBEAT.md`),E=this.vault.getAbstractFileByPath(L);if(E instanceof _.TFile){let O=await this.vault.cachedRead(E),R=ie(O);y=Ye(R.frontmatter.enabled,!1),g=M(R.frontmatter.schedule)??"",T=Ye(R.frontmatter.notify,!0),C=M(R.frontmatter.channel)??"",x=R.body}let S=M(a.model),I=M(o.model);S&&I&&S!==I&&(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 "${I}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let A=I??S??this.settings.defaultModel;return{filePath:e.path,name:i,description:M(a.description),model:A,adapter:M(o.adapter)??"claude-code",permissionMode:M(o.permission_mode)??"bypassPermissions",effort:M(o.effort),maxRetries:et(o.max_retries,1),skills:xe(a.skills),mcpServers:xe(a.mcp_servers),allowedTools:xe(o.allowed_tools),blockedTools:xe(o.blocked_tools),cwd:M(o.cwd)||M(a.cwd),enabled:Ye(a.enabled,!0),timeout:et(o.timeout,et(a.timeout,300)),approvalRequired:xe(o.approval_required),memory:Ye(o.memory,Ye(a.memory,!1)),memoryMaxEntries:et(o.memory_max_entries,100),autoCompactThreshold:et(o.auto_compact_threshold??a.auto_compact_threshold,85),tags:xe(a.tags),avatar:M(a.avatar)??"",body:n,contextBody:v,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:y,heartbeatSchedule:g,heartbeatBody:x,heartbeatNotify:T,heartbeatChannel:C,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",topicsRoot:M(e.topics_root)??"_topics",indexPath:M(e.index_path)??"index.md",logPath:M(e.log_path)??"log.md",watchedFolders:xe(e.watched_folders),excludePatterns:xe(e.exclude_patterns),watchedSince:M(e.watched_since)??"",fileSubstantiveAnswers:Ye(e.file_substantive_answers,!1),obsidianUrlScheme:Ye(e.obsidian_url_scheme,!0),maxTokensPerIngest:et(e.max_tokens_per_ingest,6e4),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,_.normalizePath)(`${this.getSubfolder("memory")}/${ke(t)}.md`)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof _.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ie(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(`
11603
- `);if(a instanceof _.TFile){let l=`${(await this.getMemory(t))?.body.trim()||"## Learned Context"}
11680
+ `}function ge(i){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Bt(i,t){return i.length<=t?i:`${i.slice(0,t-1)}\u2026`}var gt=require("child_process"),xn=require("fs"),Ut=require("os"),ft=require("path");function _a(){return(0,Ut.homedir)()}function es(){return(0,ft.join)((0,Ut.homedir)(),".claude")}function ts(){return(0,ft.join)((0,Ut.homedir)(),".claude.json")}function Sn(){if(process.platform==="darwin")return"/bin/zsh";for(let i of["/bin/bash","/bin/zsh","/bin/sh"])if((0,xn.existsSync)(i))return i;return"/bin/sh"}function jr(i){return`'${i.replace(/'/g,"'\\''")}'`}function Ge(i,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,gt.spawn)(i,t,s);let a=Sn(),n=[i,...t].map(jr).join(" ");return(0,gt.spawn)(a,["-l","-c",n],s)}function Aa(i,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,gt.spawn)(i,[],{...e,shell:!0});let s=Sn();return(0,gt.spawn)(s,["-l","-c",i],e)}function Cn(i){try{require("electron").shell.openExternal(i)}catch{switch(process.platform){case"darwin":(0,gt.spawn)("open",[i],{stdio:"ignore"});break;case"win32":(0,gt.spawn)("cmd.exe",["/c","start","",i.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,gt.spawn)("xdg-open",[i],{stdio:"ignore"});break}}}function re(i){return i.split(/\r?\n/)}function Tn(i){let t=(0,Ut.homedir)();if(process.platform==="win32")return[i,(0,ft.join)(process.env.APPDATA??"","Claude","claude.exe"),(0,ft.join)(process.env.LOCALAPPDATA??"","Claude","claude.exe"),(0,ft.join)(t,".local","bin","claude.exe"),"claude.exe","claude"].filter(s=>!!s&&Rs(s));let e=[i,(0,ft.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&&Rs(s))}function Ea(i){let t=(0,Ut.homedir)();if(process.platform==="win32")return[i,(0,ft.join)(t,".local","bin","codex.exe"),"codex.exe","codex"].filter(s=>!!s&&Rs(s));let e=[i,(0,ft.join)(t,".local","bin","codex")];return process.platform==="darwin"&&e.push("/opt/homebrew/bin/codex"),e.push("/usr/local/bin/codex","/usr/bin/codex","codex"),e.filter(s=>!!s&&Rs(s))}function Rs(i){return!i||/[\n\r\0]/.test(i)?!1:i.startsWith("/")?/^[\w/.@+-]+$/.test(i):i.startsWith("~")?/^~[\w/.@+-]*$/.test(i):/^[a-zA-Z]:[\\/]/.test(i)?/^[a-zA-Z]:[\\/][\w\\/. @+-]+$/.test(i):i.startsWith("\\\\")?/^\\\\[\w\\/. @+-]+$/.test(i):!i.includes("/")&&!i.includes("\\")?/^[\w.@+-]+$/.test(i):!1}function Pa(i){return!!(i.includes("/")||i.includes("\\"))}function Is(i){return typeof i=="object"&&i!==null}function R(i){return typeof i=="string"?i:void 0}function Xe(i,t){return typeof i=="boolean"?i:t}function je(i,t){return typeof i=="number"&&Number.isFinite(i)?i:t}function we(i){return Array.isArray(i)?i.filter(t=>typeof t=="string"):[]}function _n(i){return(0,T.normalizePath)(i.replace(/\.md$/,".permissions.json"))}function An(i){let t=0;for(let e=0;e<i.length;e++){let s=i.charCodeAt(e);t=(t<<5)-t+s|0}return t.toString(36)}var ss=class{constructor(t,e){this.vault=t;this.settings=e}agents=new Map;skills=new Map;tasks=new Map;channels=new Map;validationIssues=new Map;channelCredentialGetter;warnedFolderAgentModelConflict=new Set;warnedLegacyPerms=new Set;setChannelCredentialGetter(t){this.channelCredentialGetter=t}getVaultBasePath(){let t=this.vault.adapter;return t instanceof T.FileSystemAdapter?t.getBasePath():void 0}getFleetRoot(){return(0,T.normalizePath)(this.settings.fleetFolder)}getSubfolder(t){return(0,T.normalizePath)(`${this.getFleetRoot()}/${t}`)}async ensureFleetStructure(){let t=this.getFleetRoot(),e=!this.vault.getAbstractFileByPath(t);await this.ensureFolder(t);for(let s of kn)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of Ta){let s=(0,T.normalizePath)(`${t}/${e.path}`),a=s.substring(0,s.lastIndexOf("/"));await this.ensureFolder(a),await this.createFileIfMissing(s,e.content)}}async updateDefaults(t){let e=this.getFleetRoot(),s={...t};for(let a of Ta){let n=(0,T.normalizePath)(`${e}/${a.path}`),r=An(a.content),o=t[a.path];if(o===r)continue;let c=this.vault.getAbstractFileByPath(n);if(!(c instanceof T.TFile)){let h=n.substring(0,n.lastIndexOf("/"));await this.ensureFolder(h),await this.createFileIfMissing(n,a.content),s[a.path]=r;continue}let l=await this.vault.cachedRead(c),d=An(l);(!o||d===o)&&(await this.vault.modify(c,a.content),s[a.path]=r)}return s}async loadAll(){this.agents.clear(),this.skills.clear(),this.tasks.clear(),this.channels.clear(),this.validationIssues.clear(),await this.loadFolderAgents(),await this.loadFolderSkills();let t=this.vault.getMarkdownFiles().filter(e=>e.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 T.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 r=_n(e.path),o=this.vault.getAbstractFileByPath(r);if(o instanceof T.TFile)try{let c=await this.vault.cachedRead(o),l=JSON.parse(c);n.permissionRules={allow:we(l.allow),deny:we(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,T.normalizePath)(`${e}${a}`),r=(0,T.normalizePath)(`${n}/agent.md`);if(this.agents.delete(r),!(this.vault.getAbstractFileByPath(n)instanceof T.TFolder))return;let c=this.vault.getAbstractFileByPath(r);if(!(c instanceof T.TFile))return;let l=await this.loadFolderAgent(n,c);l&&this.agents.set(r,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,T.normalizePath)(`${e}${a}`),r=(0,T.normalizePath)(`${n}/skill.md`);if(this.skills.delete(r),!(this.vault.getAbstractFileByPath(n)instanceof T.TFolder))return;let c=this.vault.getAbstractFileByPath(r);if(!(c instanceof T.TFile))return;let l=await this.loadFolderSkill(n,c);l&&this.skills.set(r,l)}async loadFolderSkills(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("skills"));if(t instanceof T.TFolder)for(let e of t.children){if(!(e instanceof T.TFolder))continue;let s=(0,T.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof T.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}=ae(s),r=R(a.name);if(!r)return this.setIssue(e.path,"Folder skill skill.md requires string field `name`."),null;let o=async c=>{let l=(0,T.normalizePath)(`${t}/${c}`),d=this.vault.getAbstractFileByPath(l);if(!(d instanceof T.TFile))return"";let h=await this.vault.cachedRead(d);return ae(h).body};return{filePath:e.path,name:r,description:R(a.description),tags:we(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 T.TFolder)for(let e of t.children){if(!(e instanceof T.TFolder))continue;let s=(0,T.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof T.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}=ae(s),r=R(a.name);if(!r)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},c=(0,T.normalizePath)(`${t}/config.md`),l=this.vault.getAbstractFileByPath(c);if(l instanceof T.TFile){let B=await this.vault.cachedRead(l);o=ae(B).frontmatter}let d={allow:[],deny:[]},h=(0,T.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof T.TFile)try{let B=await this.vault.cachedRead(u),L=JSON.parse(B);d={allow:we(L.allow),deny:we(L.deny)}}catch{}if(d.allow.length===0&&d.deny.length===0){let B=we(o.allowed_tools),L=we(o.blocked_tools);(B.length>0||L.length>0)&&(d={allow:B,deny:L},this.warnedLegacyPerms.has(r)||(this.warnedLegacyPerms.add(r),console.warn(`Agent Fleet: "${r}" 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,T.normalizePath)(`${t}/SKILLS.md`),f=this.vault.getAbstractFileByPath(m);if(f instanceof T.TFile){let B=await this.vault.cachedRead(f);p=ae(B).body}let g="",b=(0,T.normalizePath)(`${t}/CONTEXT.md`),w=this.vault.getAbstractFileByPath(b);if(w instanceof T.TFile){let B=await this.vault.cachedRead(w);g=ae(B).body}let v=!1,y="",x="",S=!0,_="",M=(0,T.normalizePath)(`${t}/HEARTBEAT.md`),D=this.vault.getAbstractFileByPath(M);if(D instanceof T.TFile){let B=await this.vault.cachedRead(D),L=ae(B);v=Xe(L.frontmatter.enabled,!1),y=R(L.frontmatter.schedule)??"",S=Xe(L.frontmatter.notify,!0),_=R(L.frontmatter.channel)??"",x=L.body}let C=R(a.model),I=R(o.model);C&&I&&C!==I&&(this.warnedFolderAgentModelConflict.has(r)||(this.warnedFolderAgentModelConflict.add(r),console.warn(`Agent Fleet: "${r}" has conflicting model fields \u2014 agent.md says "${C}", config.md says "${I}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let P=I??C??this.settings.defaultModel;return{filePath:e.path,name:r,description:R(a.description),model:P,adapter:R(o.adapter)??"claude-code",permissionMode:R(o.permission_mode)??"bypassPermissions",effort:R(o.effort),maxRetries:je(o.max_retries,1),skills:we(a.skills),mcpServers:we(a.mcp_servers),cwd:R(o.cwd)||R(a.cwd),enabled:Xe(a.enabled,!0),timeout:je(o.timeout,je(a.timeout,300)),approvalRequired:we(o.approval_required),memory:Xe(o.memory,Xe(a.memory,!1)),memoryMaxEntries:je(o.memory_max_entries,100),autoCompactThreshold:je(o.auto_compact_threshold??a.auto_compact_threshold,85),tags:we(a.tags),avatar:R(a.avatar)??"",body:n,contextBody:g,skillsBody:p,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:v,heartbeatSchedule:y,heartbeatBody:x,heartbeatNotify:S,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:R(e.scope_root)??"",inboxPath:R(e.inbox_path)??"_sources/inbox",archivePath:R(e.archive_path)??"_sources/archive",failedPath:R(e.failed_path)??"_sources/failed",topicsRoot:R(e.topics_root)??"_topics",indexPath:R(e.index_path)??"index.md",logPath:R(e.log_path)??"log.md",watchedFolders:we(e.watched_folders),excludePatterns:we(e.exclude_patterns),watchedSince:R(e.watched_since)??"",fileSubstantiveAnswers:Xe(e.file_substantive_answers,!0),obsidianUrlScheme:Xe(e.obsidian_url_scheme,!0),maxTokensPerIngest:je(e.max_tokens_per_ingest,6e4),maxTokensPerRefresh:je(e.max_tokens_per_refresh,3e4),indexSplitThreshold:je(e.index_split_threshold,100),dedupSimilarityThreshold:je(e.dedup_similarity_threshold,.82),summaryStaleDays:je(e.summary_stale_days,30),stateFile:R(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,T.normalizePath)(`${this.getSubfolder("memory")}/${ge(t)}.md`)}getConversationsDir(t){if(t.isFolder){let s=t.filePath.replace(/\/agent\.md$/,"");return(0,T.normalizePath)(`${s}/conversations`)}let e=this.getMemoryPath(t.name).replace(/\/[^/]+$/,"");return(0,T.normalizePath)(`${e}/${t.name}-conversations`)}getConversationPath(t,e){let s=this.getConversationsDir(t),a=ge(e)||"conversation";return(0,T.normalizePath)(`${s}/${a}.json`)}async listConversations(t){let e=[],s=this.getConversationsDir(t),a=this.vault.getAbstractFileByPath(s);if(a instanceof T.TFolder)for(let n of a.children){if(!(n instanceof T.TFile)||n.extension!=="json")continue;let r=n.basename,o=await this.readConversationMeta(n,r);o&&e.push(o)}return e.sort((n,r)=>r.lastActive.localeCompare(n.lastActive)),e}async readConversationMeta(t,e){try{let s=await this.vault.cachedRead(t),a=JSON.parse(s);return{id:e,name:a.name?.trim()||"Untitled",lastActive:a.lastActive??new Date(t.stat.mtime).toISOString(),messageCount:Array.isArray(a.messages)?a.messages.length:0}}catch{return null}}async createConversation(t,e,s){let a=this.getConversationPath(t,e);if(this.vault.getAbstractFileByPath(a)instanceof T.TFile)return;let r=a.replace(/\/[^/]+$/,"");await this.ensureFolder(r);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 T.TFile))return;let r=await this.vault.cachedRead(n),o;try{o=JSON.parse(r)}catch{return}o.name=s.trim(),await this.vault.modify(n,JSON.stringify(o,null,2))}async deleteConversation(t,e){let s=this.getConversationPath(t,e),a=this.vault.getAbstractFileByPath(s);a instanceof T.TFile&&await this.vault.delete(a);let n=s.replace(/\.json$/,".threads"),r=this.vault.getAbstractFileByPath(n);r instanceof T.TFolder&&await this.vault.delete(r,!0);let o=this.getConversationsDir(t),c=this.vault.getAbstractFileByPath(o);c instanceof T.TFolder&&c.children.length===0&&await this.vault.delete(c,!0)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof T.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:r}=ae(a);return{filePath:e,agent:R(n.agent)??t,lastUpdated:R(n.last_updated),body:r}}async appendMemory(t,e){if(e.length===0)return;let s=this.getMemoryPath(t),a=this.vault.getAbstractFileByPath(s),n=new Date().toISOString(),r=e.map(o=>`- ${o.trim()}`).join(`
11681
+ `);if(a instanceof T.TFile){let c=`${(await this.getMemory(t))?.body.trim()||"## Learned Context"}
11604
11682
 
11605
- ${i}`.trim();await this.vault.modify(a,J({agent:t,last_updated:n},l));return}await this.createFileIfMissing(s,J({agent:t,last_updated:n},`## Learned Context
11683
+ ${r}`.trim();await this.vault.modify(a,K({agent:t,last_updated:n},c));return}await this.createFileIfMissing(s,K({agent:t,last_updated:n},`## Learned Context
11606
11684
 
11607
- ${i}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof _.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 readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:a}=ie(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|$)/),l=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:et(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 c=M(s.model_source);if(c==="task"||c==="agent"||c==="settings"||c==="cli-default")return c})(),concreteModel:M(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:xe(s.tags),prompt:n?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:i?.[1]?.trim()||void 0,toolsUsed:l?.[1]?pe(l[1]).map(c=>c.replace(/^- /,"").trim()).filter(Boolean):[],approvals:this.parseApprovals(s.approvals)}}async writeRunLog(t){let e=new Date(t.started),s=(0,_.normalizePath)(`${this.getRunsRoot()}/${e.toISOString().slice(0,10)}`);await this.ensureFolder(s);let a=`${e.toISOString().slice(11,19).replace(/:/g,"")}-${ke(t.agent)}-${ke(t.task)}.md`,n=(0,_.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(l=>`- ${l}`):["- none"],...t.stderr?["","## STDERR","",t.stderr.trim()]:[]].join(`
11608
- `)),o=this.vault.getAbstractFileByPath(n);return o instanceof _.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 _.TFile))return;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=ie(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 _.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ie(n),l=(this.parseApprovals(i.approvals)??[]).map(c=>c.tool===e?{...c,status:s,resolvedAt:new Date().toISOString()}:c);await this.vault.modify(a,J({...i,approvals:l},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),ke(t)),s=`---
11609
- name: ${ke(t)}
11685
+ ${r}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof T.TFolder))return[];let s=[];this.collectMarkdownChildren(e,s),s.sort((r,o)=>o.path.localeCompare(r.path));let a=s.slice(0,t),n=[];for(let r of a){let o=await this.readRunLog(r);o&&n.push(o)}return n.sort((r,o)=>o.started.localeCompare(r.started))}async listRunsSince(t){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof T.TFolder))return[];let s=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,a=[];for(let r of e.children)r instanceof T.TFolder&&(r.name<s||this.collectMarkdownChildren(r,a));let n=[];for(let r of a){let o=await this.readRunLog(r);o&&n.push(o)}return n.sort((r,o)=>o.started.localeCompare(r.started))}async readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:a}=ae(e),n=a.match(/## Prompt\n([\s\S]*?)(?:\n## Result\n|\n## Output\n|$)/),r=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:R(s.run_id)??t.basename,agent:R(s.agent)??"unknown",task:R(s.task)??"unknown",status:R(s.status)??"failure",started:R(s.started)??new Date(t.stat.ctime).toISOString(),completed:R(s.completed),durationSeconds:je(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:R(s.model)??lt.defaultModel,modelSource:(()=>{let l=R(s.model_source);if(l==="task"||l==="agent"||l==="settings"||l==="cli-default")return l})(),concreteModel:R(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:we(s.tags),prompt:n?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:r?.[1]?.trim()||void 0,toolsUsed:c?.[1]?re(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,T.normalizePath)(`${this.getRunsRoot()}/${e.toISOString().slice(0,10)}`);await this.ensureFolder(s);let a=`${e.toISOString().slice(11,19).replace(/:/g,"")}-${ge(t.agent)}-${ge(t.task)}.md`,n=(0,T.normalizePath)(`${s}/${a}`),r=K({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(`
11686
+ `)),o=this.vault.getAbstractFileByPath(n);return o instanceof T.TFile?await this.vault.modify(o,r):await this.vault.create(n,r),n}async updateTaskRunMetadata(t,e){let s=this.vault.getAbstractFileByPath(t.filePath);if(!(s instanceof T.TFile))return;let a=await this.vault.cachedRead(s),{frontmatter:n,body:r}=ae(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,K(o,r)),await this.loadFile(s)}async setApprovalDecision(t,e,s){let a=this.vault.getAbstractFileByPath(t);if(!(a instanceof T.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n),c=(this.parseApprovals(r.approvals)??[]).map(l=>l.tool===e?{...l,status:s,resolvedAt:new Date().toISOString()}:l);await this.vault.modify(a,K({...r,approvals:c},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),ge(t)),s=`---
11687
+ name: ${ge(t)}
11610
11688
  description:
11611
11689
  enabled: true
11612
11690
  skills: []
@@ -11614,64 +11692,77 @@ tags: []
11614
11692
  ---
11615
11693
 
11616
11694
  Agent instructions go here.
11617
- `;return await this.vault.create(e,s)}async createAgentFolder(t){let e=ke(t.name),s=(0,_.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,_.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,allowed_tools:[],blocked_tools:[],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,_.normalizePath)(`${s}/config.md`);await this.vault.create(o,J(i,""));let l=(0,_.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(l,J({},t.skillsBody||""));let c=(0,_.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(c,J({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,_.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+`
11618
- `)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),ke(t)),s=`---
11619
- name: ${ke(t)}
11695
+ `;return await this.vault.create(e,s)}async createAgentFolder(t){let e=ge(t.name),s=(0,T.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,T.normalizePath)(`${s}/agent.md`);await this.vault.create(n,K(a,t.systemPrompt||""));let r={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"&&(r.auto_compact_threshold=t.autoCompactThreshold),t.wikiReferences&&t.wikiReferences.length>0&&(r.wiki_references=t.wikiReferences.map(h=>({agent:h})));let o=(0,T.normalizePath)(`${s}/config.md`);await this.vault.create(o,K(r,""));let c=(0,T.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(c,K({},t.skillsBody||""));let l=(0,T.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(l,K({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,T.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+`
11696
+ `)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),ge(t)),s=`---
11697
+ name: ${ge(t)}
11620
11698
  description:
11621
11699
  tags: []
11622
11700
  ---
11623
11701
 
11624
11702
  Skill instructions go here.
11625
- `;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,_.normalizePath)(`${this.getSubfolder("skills")}/${ke(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,_.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(a,J(s,t.body||"Skill instructions go here.")),t.toolsBody){let n=(0,_.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(n,`# Tools
11703
+ `;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,T.normalizePath)(`${this.getSubfolder("skills")}/${ge(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,T.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(a,K(s,t.body||"Skill instructions go here.")),t.toolsBody){let n=(0,T.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(n,`# Tools
11626
11704
 
11627
- ${t.toolsBody}`)}if(t.referencesBody){let n=(0,_.normalizePath)(`${e}/references.md`);await this.createFileIfMissing(n,`# References
11705
+ ${t.toolsBody}`)}if(t.referencesBody){let n=(0,T.normalizePath)(`${e}/references.md`);await this.createFileIfMissing(n,`# References
11628
11706
 
11629
- ${t.referencesBody}`)}if(t.examplesBody){let n=(0,_.normalizePath)(`${e}/examples.md`);await this.createFileIfMissing(n,`# Examples
11707
+ ${t.referencesBody}`)}if(t.examplesBody){let n=(0,T.normalizePath)(`${e}/examples.md`);await this.createFileIfMissing(n,`# Examples
11630
11708
 
11631
- ${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let a=(0,_.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof _.TFile){let l=await this.vault.cachedRead(n),{frontmatter:c,body:d}=ie(l);e.description!==void 0&&(c.description=e.description||void 0),e.avatar!==void 0&&(c.avatar=e.avatar||void 0),e.tags!==void 0&&(c.tags=e.tags),e.skills!==void 0&&(c.skills=e.skills),e.mcpServers!==void 0&&(c.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(c.enabled=e.enabled),e.model!==void 0&&e.model!=="default"&&(c.model=e.model);let h=e.systemPrompt!==void 0?e.systemPrompt:d;await this.vault.modify(n,J(c,h))}let i=(0,_.normalizePath)(`${a}/config.md`),o=this.vault.getAbstractFileByPath(i);if(o instanceof _.TFile){let l=await this.vault.cachedRead(o),{frontmatter:c,body:d}=ie(l);e.model!==void 0&&(c.model=e.model),e.adapter!==void 0&&(c.adapter=e.adapter),e.timeout!==void 0&&(c.timeout=e.timeout),e.cwd!==void 0&&(c.cwd=e.cwd),e.permissionMode!==void 0&&(c.permission_mode=e.permissionMode),e.effort!==void 0&&(c.effort=e.effort||void 0),e.approvalRequired!==void 0&&(c.approval_required=e.approvalRequired),e.memory!==void 0&&(c.memory=e.memory),e.autoCompactThreshold!==void 0&&(c.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(c.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(h=>({agent:h})):void 0),await this.vault.modify(o,J(c,d))}if(e.skillsBody!==void 0){let l=(0,_.normalizePath)(`${a}/SKILLS.md`),c=this.vault.getAbstractFileByPath(l);c instanceof _.TFile?await this.vault.modify(c,J({},e.skillsBody)):await this.vault.create(l,J({},e.skillsBody))}if(e.contextBody!==void 0){let l=(0,_.normalizePath)(`${a}/CONTEXT.md`),c=this.vault.getAbstractFileByPath(l);c instanceof _.TFile?await this.vault.modify(c,J({},e.contextBody)):await this.vault.create(l,J({},e.contextBody))}if(e.permissionRules!==void 0){let l=(0,_.normalizePath)(`${a}/permissions.json`),c=this.vault.getAbstractFileByPath(l),d=e.permissionRules;if(d.allow.length>0||d.deny.length>0){let h=JSON.stringify(d,null,2)+`
11632
- `;c instanceof _.TFile?await this.vault.modify(c,h):await this.vault.create(l,h)}else c instanceof _.TFile&&await this.vault.trash(c,!0)}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof _.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ie(n);e.description!==void 0&&(i.description=e.description||void 0),e.avatar!==void 0&&(i.avatar=e.avatar||void 0),e.tags!==void 0&&(i.tags=e.tags),e.skills!==void 0&&(i.skills=e.skills),e.mcpServers!==void 0&&(i.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(i.enabled=e.enabled),e.model!==void 0&&(i.model=e.model),e.adapter!==void 0&&(i.adapter=e.adapter),e.timeout!==void 0&&(i.timeout=e.timeout),e.cwd!==void 0&&(i.cwd=e.cwd),e.permissionMode!==void 0&&(i.permission_mode=e.permissionMode),e.effort!==void 0&&(i.effort=e.effort||void 0),e.approvalRequired!==void 0&&(i.approval_required=e.approvalRequired),e.memory!==void 0&&(i.memory=e.memory),e.autoCompactThreshold!==void 0&&(i.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(i.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(c=>({agent:c})):void 0);let l=e.systemPrompt!==void 0?e.systemPrompt:o;await this.vault.modify(a,J(i,l))}}async updateTask(t,e){let s=this.getTaskById(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof _.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ie(n);e.agent!==void 0&&(i.agent=e.agent),e.type!==void 0&&(i.type=e.type),e.schedule!==void 0&&(i.schedule=e.schedule||void 0),e.runAt!==void 0&&(i.run_at=e.runAt||void 0),e.enabled!==void 0&&(i.enabled=e.enabled),e.priority!==void 0&&(i.priority=e.priority),e.catch_up!==void 0&&(i.catch_up=e.catch_up),e.effort!==void 0&&(i.effort=e.effort||void 0),e.model!==void 0&&(i.model=e.model||void 0),e.tags!==void 0&&(i.tags=e.tags);let l=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,l))}async updateSkill(t,e){let s=this.getSkillByName(t);if(s)if(s.isFolder){let a=(0,_.normalizePath)(s.filePath.replace(/\/skill\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof _.TFile){let i=await this.vault.cachedRead(n),{frontmatter:o,body:l}=ie(i);e.description!==void 0&&(o.description=e.description||void 0),e.tags!==void 0&&(o.tags=e.tags.length>0?e.tags:void 0);let c=e.body!==void 0?e.body:l;await this.vault.modify(n,J(o,c))}if(e.toolsBody!==void 0){let i=(0,_.normalizePath)(`${a}/tools.md`),o=this.vault.getAbstractFileByPath(i);e.toolsBody&&(o instanceof _.TFile?await this.vault.modify(o,`# Tools
11709
+ ${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let a=(0,T.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof T.TFile){let c=await this.vault.cachedRead(n),{frontmatter:l,body:d}=ae(c);e.description!==void 0&&(l.description=e.description||void 0),e.avatar!==void 0&&(l.avatar=e.avatar||void 0),e.tags!==void 0&&(l.tags=e.tags),e.skills!==void 0&&(l.skills=e.skills),e.mcpServers!==void 0&&(l.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(l.enabled=e.enabled),e.model!==void 0&&e.model!=="default"&&(l.model=e.model);let h=e.systemPrompt!==void 0?e.systemPrompt:d;await this.vault.modify(n,K(l,h))}let r=(0,T.normalizePath)(`${a}/config.md`),o=this.vault.getAbstractFileByPath(r);if(o instanceof T.TFile){let c=await this.vault.cachedRead(o),{frontmatter:l,body:d}=ae(c);e.model!==void 0&&(l.model=e.model),e.adapter!==void 0&&(l.adapter=e.adapter),e.timeout!==void 0&&(l.timeout=e.timeout),e.cwd!==void 0&&(l.cwd=e.cwd),e.permissionMode!==void 0&&(l.permission_mode=e.permissionMode),e.effort!==void 0&&(l.effort=e.effort||void 0),e.approvalRequired!==void 0&&(l.approval_required=e.approvalRequired),e.memory!==void 0&&(l.memory=e.memory),e.autoCompactThreshold!==void 0&&(l.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(l.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(h=>({agent:h})):void 0),delete l.allowed_tools,delete l.blocked_tools,await this.vault.modify(o,K(l,d))}if(e.skillsBody!==void 0){let c=(0,T.normalizePath)(`${a}/SKILLS.md`),l=this.vault.getAbstractFileByPath(c);l instanceof T.TFile?await this.vault.modify(l,K({},e.skillsBody)):await this.vault.create(c,K({},e.skillsBody))}if(e.contextBody!==void 0){let c=(0,T.normalizePath)(`${a}/CONTEXT.md`),l=this.vault.getAbstractFileByPath(c);l instanceof T.TFile?await this.vault.modify(l,K({},e.contextBody)):await this.vault.create(c,K({},e.contextBody))}if(e.permissionRules!==void 0){let c=(0,T.normalizePath)(`${a}/permissions.json`),l=this.vault.getAbstractFileByPath(c),d=e.permissionRules;if(d.allow.length>0||d.deny.length>0){let h=JSON.stringify(d,null,2)+`
11710
+ `;l instanceof T.TFile?await this.vault.modify(l,h):await this.vault.create(c,h)}else l instanceof T.TFile&&await this.vault.trash(l,!0)}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof T.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);e.description!==void 0&&(r.description=e.description||void 0),e.avatar!==void 0&&(r.avatar=e.avatar||void 0),e.tags!==void 0&&(r.tags=e.tags),e.skills!==void 0&&(r.skills=e.skills),e.mcpServers!==void 0&&(r.mcp_servers=e.mcpServers.length>0?e.mcpServers:void 0),e.enabled!==void 0&&(r.enabled=e.enabled),e.model!==void 0&&(r.model=e.model),e.adapter!==void 0&&(r.adapter=e.adapter),e.timeout!==void 0&&(r.timeout=e.timeout),e.cwd!==void 0&&(r.cwd=e.cwd),e.permissionMode!==void 0&&(r.permission_mode=e.permissionMode),e.effort!==void 0&&(r.effort=e.effort||void 0),e.approvalRequired!==void 0&&(r.approval_required=e.approvalRequired),e.memory!==void 0&&(r.memory=e.memory),e.autoCompactThreshold!==void 0&&(r.auto_compact_threshold=e.autoCompactThreshold),e.wikiReferences!==void 0&&(r.wiki_references=e.wikiReferences.length>0?e.wikiReferences.map(l=>({agent:l})):void 0),delete r.allowed_tools,delete r.blocked_tools;let c=e.systemPrompt!==void 0?e.systemPrompt:o;if(await this.vault.modify(a,K(r,c)),e.permissionRules!==void 0){let l=_n(s.filePath),d=this.vault.getAbstractFileByPath(l),h=e.permissionRules;if(h.allow.length>0||h.deny.length>0){let u=JSON.stringify(h,null,2)+`
11711
+ `;d instanceof T.TFile?await this.vault.modify(d,u):await this.vault.create(l,u)}else d instanceof T.TFile&&await this.vault.trash(d,!0)}}}async updateTask(t,e){let s=this.getTaskById(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof T.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);e.agent!==void 0&&(r.agent=e.agent),e.type!==void 0&&(r.type=e.type),e.schedule!==void 0&&(r.schedule=e.schedule||void 0),e.runAt!==void 0&&(r.run_at=e.runAt||void 0),e.enabled!==void 0&&(r.enabled=e.enabled),e.priority!==void 0&&(r.priority=e.priority),e.catch_up!==void 0&&(r.catch_up=e.catch_up),e.effort!==void 0&&(r.effort=e.effort||void 0),e.model!==void 0&&(r.model=e.model||void 0),e.tags!==void 0&&(r.tags=e.tags);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,K(r,c))}async updateSkill(t,e){let s=this.getSkillByName(t);if(s)if(s.isFolder){let a=(0,T.normalizePath)(s.filePath.replace(/\/skill\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof T.TFile){let r=await this.vault.cachedRead(n),{frontmatter:o,body:c}=ae(r);e.description!==void 0&&(o.description=e.description||void 0),e.tags!==void 0&&(o.tags=e.tags.length>0?e.tags:void 0);let l=e.body!==void 0?e.body:c;await this.vault.modify(n,K(o,l))}if(e.toolsBody!==void 0){let r=(0,T.normalizePath)(`${a}/tools.md`),o=this.vault.getAbstractFileByPath(r);e.toolsBody&&(o instanceof T.TFile?await this.vault.modify(o,`# Tools
11633
11712
 
11634
- ${e.toolsBody}`):await this.vault.create(i,`# Tools
11713
+ ${e.toolsBody}`):await this.vault.create(r,`# Tools
11635
11714
 
11636
- ${e.toolsBody}`))}if(e.referencesBody!==void 0){let i=(0,_.normalizePath)(`${a}/references.md`),o=this.vault.getAbstractFileByPath(i);e.referencesBody&&(o instanceof _.TFile?await this.vault.modify(o,`# References
11715
+ ${e.toolsBody}`))}if(e.referencesBody!==void 0){let r=(0,T.normalizePath)(`${a}/references.md`),o=this.vault.getAbstractFileByPath(r);e.referencesBody&&(o instanceof T.TFile?await this.vault.modify(o,`# References
11637
11716
 
11638
- ${e.referencesBody}`):await this.vault.create(i,`# References
11717
+ ${e.referencesBody}`):await this.vault.create(r,`# References
11639
11718
 
11640
- ${e.referencesBody}`))}if(e.examplesBody!==void 0){let i=(0,_.normalizePath)(`${a}/examples.md`),o=this.vault.getAbstractFileByPath(i);e.examplesBody&&(o instanceof _.TFile?await this.vault.modify(o,`# Examples
11719
+ ${e.referencesBody}`))}if(e.examplesBody!==void 0){let r=(0,T.normalizePath)(`${a}/examples.md`),o=this.vault.getAbstractFileByPath(r);e.examplesBody&&(o instanceof T.TFile?await this.vault.modify(o,`# Examples
11641
11720
 
11642
- ${e.examplesBody}`):await this.vault.create(i,`# Examples
11721
+ ${e.examplesBody}`):await this.vault.create(r,`# Examples
11643
11722
 
11644
- ${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof _.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ie(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 l=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,l))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,_.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof _.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 _.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=ie(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 l=e.body!==void 0?e.body:o;await this.vault.modify(a,J(i,l))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,_.normalizePath)(`${this.getSubfolder("channels")}/${ke(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof _.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,_.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,_.normalizePath)(`${a}/HEARTBEAT.md`),i=this.vault.getAbstractFileByPath(n);if(i instanceof _.TFile){let o=await this.vault.cachedRead(i),{frontmatter:l,body:c}=ie(o);e.enabled!==void 0&&(l.enabled=e.enabled),e.schedule!==void 0&&(l.schedule=e.schedule||void 0),e.notify!==void 0&&(l.notify=e.notify),e.channel!==void 0&&(l.channel=e.channel||void 0);let d=e.body!==void 0?e.body:c;await this.vault.modify(i,J(l,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 l=e.body??"";await this.vault.create(n,J(o,l))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let i=(0,_.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(i);if(o instanceof _.TFolder){let l=[];this.collectMarkdownChildren(o,l);for(let c of l)s.push(c.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,_.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 _.TFile&&s.extension==="md"&&e.push(s),s instanceof _.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}=ie(e);if(!ws(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=M(s.name),i=M(s.model)??this.settings.defaultModel;return!n||!i?(this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null):{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:et(s.max_retries,1),skills:xe(s.skills),mcpServers:xe(s.mcp_servers),allowedTools:xe(s.allowed_tools),blockedTools:xe(s.blocked_tools),cwd:M(s.cwd),enabled:Ye(s.enabled,!0),timeout:et(s.timeout,300),approvalRequired:xe(s.approval_required),memory:Ye(s.memory,!1),memoryMaxEntries:et(s.memory_max_entries,100),autoCompactThreshold:et(s.auto_compact_threshold,85),tags:xe(s.tags),avatar:M(s.avatar)??"",body:a,contextBody:"",skillsBody:"",env:this.parseEnvMap(s.env),permissionRules:{allow:[],deny:[]},isFolder:!1,heartbeatEnabled:!1,heartbeatSchedule:"",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""}}parseSkill(t,e){let{frontmatter:s,body:a}=ie(e),n=M(s.name);return n?{filePath:t,name:n,description:M(s.description),tags:xe(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}=ie(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 l=M(s.priority),d=l&&["low","medium","high","critical"].includes(l)?l:"medium";return{filePath:t,taskId:n,agent:i,schedule:M(s.schedule),runAt:M(s.run_at),type:o,priority:d,enabled:Ye(s.enabled,!0),created:M(s.created)??new Date().toISOString(),lastRun:M(s.last_run),nextRun:M(s.next_run),runCount:et(s.run_count,0),catchUp:Ye(s.catch_up,this.settings.catchUpMissedTasks),effort:M(s.effort),model:M(s.model),tags:xe(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=ie(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 l=i,c=M(s.default_agent)??M(s.agent);if(!c)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=xe(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=ws(s.transport)?s.transport:{};return{filePath:t,name:n,type:l,defaultAgent:c,allowedAgents:d,enabled:Ye(s.enabled,!0),credentialRef:h,allowedUsers:xe(s.allowed_users),perUserSessions:Ye(s.per_user_sessions,!0),channelContext:M(s.channel_context)??"",transport:u,tags:xe(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 l=n[i.credentialRef];l?l.type!==i.type&&this.setIssue(i.filePath,`Channel \`${i.name}\` is type \`${i.type}\` but credential \`${i.credentialRef}\` is type \`${l.type}\`.`):this.setIssue(i.filePath,`Channel \`${i.name}\` references missing credential \`${i.credentialRef}\`. Add it under Settings \u2192 Channel Credentials.`)}}parseEnvMap(t){if(!ws(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(!ws(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 Ct=require("obsidian"),ks=class extends Ct.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,Ct.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 Ct.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"}),l=o.createEl("button",{text:"Cancel"});l.onclick=()=>this.close();let c=o.createEl("button",{cls:"af-delete-confirm-btn",text:"Delete Agent"}),d=c.createSpan({cls:"af-delete-btn-icon"});(0,Ct.setIcon)(d,"trash-2"),c.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var U=require("obsidian");var sn=[{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"}],xs="__custom__";function Qi(r){let t=r.trim();return!t||t==="default"||t==="subscription"?"inherit":sn.some(e=>e.value===t)?"alias":"custom"}function _t(r,t){r.empty(),r.addClass("af-model-picker");let e=Qi(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 sn)n.createEl("option",{text:o.label,attr:{value:o.value}});s.createEl("option",{text:"Custom\u2026",attr:{value:xs}});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=xs,i.value=t.value.trim(),i.style.display=""),s.addEventListener("change",()=>{s.value===xs?(i.style.display="",i.focus(),t.onChange(i.value.trim())):(i.style.display="none",t.onChange(s.value))}),i.addEventListener("input",()=>{s.value===xs&&t.onChange(i.value.trim())})}var fe=require("obsidian");function Zi(){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 er="0 3 * * *",tr="0 9 * * 0";function da(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:Zi(),heartbeatChannel:"",fileSubstantiveAnswers:!1,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,stateFile:".wiki-keeper-state.json"}}var nn=da();function ha(r){let t=ke(r).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function sr(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"],tags:t?["wiki-keeper",`scope:${ke(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).
11723
+ ${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof T.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);e.description!==void 0&&(r.description=e.description||void 0),e.tags!==void 0&&(r.tags=e.tags.length>0?e.tags:void 0);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,K(r,c))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,T.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof T.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 T.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);e.default_agent!==void 0&&(r.default_agent=e.default_agent,delete r.agent),e.allowed_agents!==void 0&&(r.allowed_agents=e.allowed_agents),e.enabled!==void 0&&(r.enabled=e.enabled),e.credential_ref!==void 0&&(r.credential_ref=e.credential_ref),e.allowed_users!==void 0&&(r.allowed_users=e.allowed_users),e.per_user_sessions!==void 0&&(r.per_user_sessions=e.per_user_sessions),e.channel_context!==void 0&&(r.channel_context=e.channel_context||void 0),e.tags!==void 0&&(r.tags=e.tags),e.type!==void 0&&(r.type=e.type),e.transport!==void 0&&(r.transport=e.transport);let c=e.body!==void 0?e.body:o;await this.vault.modify(a,K(r,c))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,T.normalizePath)(`${this.getSubfolder("channels")}/${ge(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof T.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,T.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,T.normalizePath)(`${a}/HEARTBEAT.md`),r=this.vault.getAbstractFileByPath(n);if(r instanceof T.TFile){let o=await this.vault.cachedRead(r),{frontmatter:c,body:l}=ae(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(r,K(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,K(o,c))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let r=(0,T.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(r);if(o instanceof T.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 r=this.getTasksForAgent(t);for(let o of r)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,T.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 T.TFile&&s.extension==="md"&&e.push(s),s instanceof T.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}=ae(e);if(!Is(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=R(s.name),r=R(s.model)??this.settings.defaultModel;if(!n||!r)return this.setIssue(t,"Agent requires string field `name` and a valid model or default model setting."),null;let o=we(s.allowed_tools),c=we(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 <name>.permissions.json beside the .md. Open Edit and Save to migrate.`)),{filePath:t,name:n,description:R(s.description),model:r,adapter:R(s.adapter)??"claude-code",permissionMode:R(s.permission_mode)??"bypassPermissions",effort:R(s.effort),maxRetries:je(s.max_retries,1),skills:we(s.skills),mcpServers:we(s.mcp_servers),cwd:R(s.cwd),enabled:Xe(s.enabled,!0),timeout:je(s.timeout,300),approvalRequired:we(s.approval_required),memory:Xe(s.memory,!1),memoryMaxEntries:je(s.memory_max_entries,100),autoCompactThreshold:je(s.auto_compact_threshold,85),tags:we(s.tags),avatar:R(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}=ae(e),n=R(s.name);return n?{filePath:t,name:n,description:R(s.description),tags:we(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}=ae(e),n=R(s.task_id),r=R(s.agent),o=R(s.type);if(!n||!r||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!R(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!R(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let c=R(s.priority),d=c&&["low","medium","high","critical"].includes(c)?c:"medium";return{filePath:t,taskId:n,agent:r,schedule:R(s.schedule),runAt:R(s.run_at),type:o,priority:d,enabled:Xe(s.enabled,!0),created:R(s.created)??new Date().toISOString(),lastRun:R(s.last_run),nextRun:R(s.next_run),runCount:je(s.run_count,0),catchUp:Xe(s.catch_up,this.settings.catchUpMissedTasks),effort:R(s.effort),model:R(s.model),tags:we(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=ae(e),n=R(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let r=R(s.type),o=["slack","telegram"];if(!r||!o.includes(r))return this.setIssue(t,`Channel \`${n}\` requires \`type\` to be one of: ${o.join(", ")}.`),null;let c=r,l=R(s.default_agent)??R(s.agent);if(!l)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=we(s.allowed_agents),h=R(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=Is(s.transport)?s.transport:{};return{filePath:t,name:n,type:c,defaultAgent:l,allowedAgents:d,enabled:Xe(s.enabled,!0),credentialRef:h,allowedUsers:we(s.allowed_users),perUserSessions:Xe(s.per_user_sessions,!0),channelContext:R(s.channel_context)??"",transport:u,tags:we(s.tags),body:a}}validateReferences(){let t=new Set;for(let r of this.skills.values())t.has(r.name)&&this.setIssue(r.filePath,`Duplicate skill name \`${r.name}\`.`),t.add(r.name);let e=new Set;for(let r of this.agents.values()){e.has(r.name)&&this.setIssue(r.filePath,`Duplicate agent name \`${r.name}\`.`),e.add(r.name);for(let o of r.skills)t.has(o)||this.setIssue(r.filePath,`Agent references missing skill \`${o}\`.`)}for(let r of this.tasks.values())e.has(r.agent)||this.setIssue(r.filePath,`Task references missing agent \`${r.agent}\`.`);let s=new Set,a=new Map;for(let r of this.agents.values())a.set(r.name,r);let n=this.channelCredentialGetter?.()??this.settings.channelCredentials??{};for(let r of this.channels.values()){s.has(r.name)&&this.setIssue(r.filePath,`Duplicate channel name \`${r.name}\`.`),s.add(r.name);let o=a.get(r.defaultAgent);o?o.approvalRequired.length>0&&this.setIssue(r.filePath,`Channel \`${r.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(r.filePath,`Channel \`${r.name}\` references missing agent \`${r.defaultAgent}\`.`);let c=n[r.credentialRef];c?c.type!==r.type&&this.setIssue(r.filePath,`Channel \`${r.name}\` is type \`${r.type}\` but credential \`${r.credentialRef}\` is type \`${c.type}\`.`):this.setIssue(r.filePath,`Channel \`${r.name}\` references missing credential \`${r.credentialRef}\`. Add it under Settings \u2192 Channel Credentials.`)}}parseEnvMap(t){if(!Is(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(!Is(e)||!R(e.tool))return[];let s=R(e.tool);return s?[{tool:s,command:R(e.command),reason:R(e.reason),status:R(e.status)??"pending",resolvedAt:R(e.resolvedAt),note:R(e.note)}]:[]})}};var At=require("obsidian"),Ls=class extends At.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,At.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 r=n.createEl("ul",{cls:"af-delete-impact-list"});r.createEl("li",{text:"Move the agent definition to trash"}),this.info.hasMemory&&r.createEl("li",{text:"Move the agent's memory file to trash"}),this.info.taskCount>0&&r.createEl("li").setText(`${this.info.taskCount} task${this.info.taskCount!==1?"s":""} reference this agent`),this.info.runCount>0&&r.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 At.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,At.setIcon)(d,"trash-2"),l.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var O=require("obsidian");var Fs=[{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"}],Os=[{value:"gpt-5.5",label:"gpt-5.5 \u2014 frontier"},{value:"gpt-5.4",label:"gpt-5.4"},{value:"gpt-5.4-mini",label:"gpt-5.4-mini \u2014 fast"},{value:"gpt-5.3-codex",label:"gpt-5.3-codex"}],Ms="__custom__";function En(i){let t=(i??"").trim().toLowerCase();return t==="codex"||t==="openai-codex"}function Pn(i){return En(i)?Os:Fs}function Hr(i,t){let e=i.trim();return!e||e==="default"||e==="subscription"?"inherit":Pn(t).some(s=>s.value===e)?"alias":"custom"}function Et(i,t){i.empty(),i.addClass("af-model-picker");let e=En(t.adapter),s=Pn(t.adapter),a=Hr(t.value,t.adapter),n=i.createEl("select",{cls:"af-form-select af-mp-select"}),r=t.allowInherit?t.inheritPlaceholder??"Inherit from agent":e?"Default (let Codex pick)":"Default (let Claude Code pick)";n.createEl("option",{text:r,attr:{value:""}});let o=n.createEl("optgroup",{attr:{label:e?"Codex models":"Aliases (any backend)"}});for(let l of s)o.createEl("option",{text:l.label,attr:{value:l.value}});n.createEl("option",{text:"Custom\u2026",attr:{value:Ms}});let c=i.createEl("input",{cls:"af-form-input af-mp-custom-input",attr:{type:"text",placeholder:e?"e.g. gpt-5.5 \xB7 gpt-5.4-mini":"e.g. claude-opus-4-7 \xB7 us.anthropic.claude-opus-4-7 \xB7 claude-opus-4-7@20251101",spellcheck:"false"}});a==="inherit"?(n.value="",c.value="",c.style.display="none"):a==="alias"?(n.value=t.value.trim(),c.value="",c.style.display="none"):(n.value=Ms,c.value=t.value.trim(),c.style.display=""),n.addEventListener("change",()=>{n.value===Ms?(c.style.display="",c.focus(),t.onChange(c.value.trim())):(c.style.display="none",t.onChange(n.value))}),c.addEventListener("input",()=>{n.value===Ms&&t.onChange(c.value.trim())})}var ce=require("obsidian");function qr(){let i=new Date,t=i.getFullYear(),e=String(i.getMonth()+1).padStart(2,"0"),s=String(i.getDate()).padStart(2,"0");return`${t}-${e}-${s}`}var Wr="0 3 * * *",zr="0 9 * * 0";function Da(){return{scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],watchedSince:qr(),heartbeatChannel:"",fileSubstantiveAnswers:!0,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,maxTokensPerRefresh:3e4,indexSplitThreshold:100,dedupSimilarityThreshold:.82,summaryStaleDays:30,failedPath:"_sources/failed",stateFile:".wiki-keeper-state.json"}}var In=Da();function Ra(i){let t=ge(i).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function Gr(i,t){let e=t||"the whole vault",s={name:i,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:${ge(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).
11645
11724
 
11646
11725
  ## Scope isolation
11647
11726
 
11648
- Your knowledge is bounded to the \`scope_root\` in your config. You do NOT read or write files outside this scope. If a question requires another scope, say you don't know and suggest asking that scope's keeper.
11727
+ Your knowledge is bounded to the \`scope_root\` in your config. You do NOT read or write files outside this scope. **Every Write/Edit/Bash(mv) target path must begin with \`<scope_root>/\`** (or be inside watched folders for *reading* only). If a question requires another scope, say you don't know and suggest asking that scope's keeper. If a tool call would target an out-of-scope path, refuse and explain why instead of attempting it.
11649
11728
 
11650
- ## Two ingestion modes
11729
+ ## Four skills
11651
11730
 
11652
- - **Inbox** (\`_sources/inbox/\`): one-off drops. Process \u2192 summarize \u2192 archive.
11653
- - **Watched**: folders listed in \`wiki_keeper.watched_folders\`. Read-only sources. Extract durable claims; never modify or move.
11731
+ - **\`wiki-ingest\`** \u2014 runs inbox mode + watched mode + chained refresh phase. The default heartbeat invokes this.
11732
+ - **\`wiki-query\`** \u2014 answers questions, cites every claim, compounds substantive answers back into the wiki.
11733
+ - **\`wiki-lint\`** \u2014 weekly health check; bidirectional cross-refs, dedup proposals, stale summaries, schema violations. Chains \`wiki-refresh\` for stale summaries.
11734
+ - **\`wiki-refresh\`** \u2014 regenerates the fenced \`## Summary\` block at the top of topic pages whose claim history has grown. Called automatically at end of ingest and from lint; can also be invoked manually.
11654
11735
 
11655
11736
  ## Conventions
11656
11737
 
11657
11738
  - Cross-links are Obsidian wikilinks within this scope.
11658
11739
  - Topic pages under \`_topics/\` unless a subfolder exists.
11659
- - Preserve user-authored content; only revise/extend inside fenced blocks.
11740
+ - Topic pages have a fenced \`## Summary\` block at top, then \`## Claims\` (append-only history), then optional \`## Contradictions\`. The summary block is auto-managed by \`wiki-refresh\`; everything else preserves user-authored content.
11741
+ - Bullets in \`## Claims\` carry a source-type tag: \`[doc]\`, \`[meeting]\`, \`[email]\`, \`[note]\`, \`[web]\`, \`[synthesis]\`, \`[other]\`.
11742
+ - Preserve user-authored content; only revise/extend inside fenced blocks (\`<!-- wiki-keeper:begin -->\`, \`<!-- wiki-keeper:summary:begin -->\`).
11660
11743
  - Log every action to \`log.md\`.
11661
11744
 
11662
11745
  ## When invoked
11663
11746
 
11664
- - If prompt names a file in the inbox or mentions watched-folder changes \u2192 use \`wiki-ingest\`.
11665
- - If prompt is a question \u2192 use \`wiki-query\`.
11666
- - If prompt is "lint" or a periodic \`lint\` heartbeat \u2192 use \`wiki-lint\`.
11747
+ - If prompt names a file in the inbox or mentions watched-folder changes \u2192 \`wiki-ingest\`.
11748
+ - If prompt is a question \u2192 \`wiki-query\` (compounding writes apply automatically when the answer is substantive).
11749
+ - If prompt says "lint" or a periodic lint heartbeat fires \u2192 \`wiki-lint\`.
11750
+ - If prompt says "refresh [[topic]]", "refresh stale", or "refresh all" \u2192 \`wiki-refresh\` directly.
11667
11751
  - Otherwise ask what the user wants.
11668
11752
 
11753
+ ## Memory vs. wiki
11754
+
11755
+ Use \`memory\` for procedural learning ("user prefers concept pages under sub-folders", "user wants Sunday lint reports broadcast to Slack"). Topical content always goes into \`_topics/\` \u2014 never into memory. Memory is for how-to-work; the wiki is what-we-know.
11756
+
11669
11757
  ## Non-goals
11670
11758
 
11671
11759
  - Never delete user-authored pages.
11672
11760
  - Never rewrite source files.
11673
11761
  - Never auto-apply judgment-call lint fixes.
11674
- `;return J(s,a)}function ar(r){let t={model:"opus",adapter:"claude-code",timeout:900,max_retries:1,cwd:"",permission_mode:"acceptEdits",approval_required:["Write"],allowed_tools:["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],blocked_tools:["Bash(rm -rf *)","Bash(git push *)"],memory:!0,memory_max_entries:200,wiki_keeper:{scope_root:r.scopeRoot,inbox_path:r.inboxPath,archive_path:r.archivePath,topics_root:r.topicsRoot,index_path:r.indexPath,log_path:r.logPath,watched_folders:r.watchedFolders,exclude_patterns:r.excludePatterns,watched_since:r.watchedSince,file_substantive_answers:r.fileSubstantiveAnswers,obsidian_url_scheme:r.obsidianUrlScheme,max_tokens_per_ingest:r.maxTokensPerIngest,state_file:r.stateFile}};return J(t,"")}function nr(r){let t={enabled:!0,schedule:er,notify:!0};return r.heartbeatChannel&&(t.channel=r.heartbeatChannel),J(t,`Run wiki-ingest in both modes:
11762
+ - Never write outside the scope root.
11763
+ - Never edit \`## Claims\` history (append only).
11764
+ - Never edit \`## Summary\` blocks by hand \u2014 \`wiki-refresh\` owns them.
11765
+ `;return K(s,a)}function Vr(i){let t={model:"opus",adapter:"claude-code",timeout:900,max_retries:1,cwd:"",permission_mode:"acceptEdits",approval_required:["Write"],memory:!0,memory_max_entries:200,wiki_keeper:{scope_root:i.scopeRoot,inbox_path:i.inboxPath,archive_path:i.archivePath,failed_path:i.failedPath,topics_root:i.topicsRoot,index_path:i.indexPath,log_path:i.logPath,watched_folders:i.watchedFolders,exclude_patterns:i.excludePatterns,watched_since:i.watchedSince,file_substantive_answers:i.fileSubstantiveAnswers,obsidian_url_scheme:i.obsidianUrlScheme,max_tokens_per_ingest:i.maxTokensPerIngest,max_tokens_per_refresh:i.maxTokensPerRefresh,index_split_threshold:i.indexSplitThreshold,dedup_similarity_threshold:i.dedupSimilarityThreshold,summary_stale_days:i.summaryStaleDays,state_file:i.stateFile}};return K(t,"")}function Yr(i){let t={enabled:!0,schedule:Wr,notify:!0};return i.heartbeatChannel&&(t.channel=i.heartbeatChannel),K(t,`Run wiki-ingest in both modes:
11675
11766
 
11676
11767
  1. Drain every unprocessed file in the configured inbox (inbox mode).
11677
11768
  2. Diff watched folders against the state file; process changed or new files (watched mode).
@@ -11679,70 +11770,58 @@ Your knowledge is bounded to the \`scope_root\` in your config. You do NOT read
11679
11770
  Lint runs on its own schedule via the sibling \`*-lint\` task.
11680
11771
 
11681
11772
  Change the schedule by editing this file's \`schedule:\` frontmatter directly, or via the agent editor in the dashboard.
11682
- `)}function rn(r){let e={task_id:`${r}-lint`,agent:r,type:"recurring",schedule:tr,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 ua(r,t){return(0,fe.normalizePath)(`${r}/tasks/${t}-lint.md`)}var ir=`# Wiki Schema
11683
-
11684
- ## Page types
11685
-
11686
- - **Entity pages** \u2014 people, orgs, products. Frontmatter: \`type: entity\`.
11687
- - **Concept pages** \u2014 ideas, techniques, patterns. \`type: concept\`.
11688
- - **Event pages** \u2014 meetings, releases, decisions. \`type: event, date: YYYY-MM-DD\`.
11689
- - **Summary pages** (inbox-mode only) \u2014 one per ingested inbox source. \`type: summary, source: <filename>\`.
11690
- - **Synthesis pages** (optional, from wiki-query) \u2014 filed answers. \`type: synthesis, question: <q>\`.
11691
-
11692
- ## Naming
11693
-
11694
- - Slug-case filenames: \`vendor-x.md\`, not \`Vendor X.md\`.
11695
- - Group by type under \`_topics/<type>/\` when there are >5 pages of a type.
11696
-
11697
- ## Links
11698
-
11699
- - Every entity/concept page MUST have \u22651 inbound link from \`index.md\` or a sibling.
11700
- - Summary pages MUST forward-link to every entity/concept they mention.
11701
- - Watched-mode extractions append dated entries to topic pages; forward-link to the source file path so readers can find the raw note.
11702
-
11703
- ## Conflict resolution
11704
-
11705
- - New claim contradicts existing? Add a \`## Contradictions\` section at the bottom of the contested page with a dated entry. Do NOT overwrite.
11706
- - Flag in \`log.md\` for user review.
11707
- `,rr=JSON.stringify({allow:["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],deny:["Bash(rm -rf *)","Bash(git push *)"]},null,2);async function on(r,t,e){let s=ha(e.scopeSlug||e.scopeRoot),a=(0,fe.normalizePath)(`${t}/agents/${s}`);if(await Ft(r,a),r.getAbstractFileByPath((0,fe.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,fe.normalizePath)(`${a}/agent.md`),sr(s,e.scopeRoot)),await r.create((0,fe.normalizePath)(`${a}/config.md`),ar(e)),await r.create((0,fe.normalizePath)(`${a}/HEARTBEAT.md`),nr(e)),await r.create((0,fe.normalizePath)(`${a}/CONTEXT.md`),ir),await r.create((0,fe.normalizePath)(`${a}/permissions.json`),rr);let i=ua(t,s);r.getAbstractFileByPath(i)||(await Ft(r,(0,fe.normalizePath)(`${t}/tasks`)),await r.create(i,rn(s)));let o=e.scopeRoot.trim(),l=o?`${o}/`:"";return await Ft(r,(0,fe.normalizePath)(`${l}${e.inboxPath}`)),await Ft(r,(0,fe.normalizePath)(`${l}${e.topicsRoot}`)),await an(r,(0,fe.normalizePath)(`${l}${e.indexPath}`),`# Index
11773
+ `)}function Ln(i){let e={task_id:`${i}-lint`,agent:i,type:"recurring",schedule:zr,priority:"low",enabled:!0,created:new Date().toISOString(),run_count:0,catch_up:!1,tags:["wiki-keeper","lint"]};return K(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 Ia(i,t){return(0,ce.normalizePath)(`${i}/tasks/${t}-lint.md`)}var Kr="# 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: <filename>`.\n- **Synthesis pages** \u2014 filed answers from `wiki-query` substantive responses. `type: synthesis, question: <q>, refreshed: <ISO>`. Live under `_topics/syntheses/`.\n\nTopic pages of type entity/concept/event SHOULD also carry:\n- `summary_refreshed: <ISO>` \u2014 last time `wiki-refresh` regenerated this page's summary block. Empty string on creation.\n- `claims_at_refresh: <int>` \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<!-- wiki-keeper:summary:begin -->\n## Summary\n\n- 3-7 bullet synthesis maintained by wiki-refresh.\n- Forward-links to other topics with [[wikilinks]].\n<!-- wiki-keeper:summary:end -->\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/<type>/` 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 `<scope>/index/<type>.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 `<failed_path>/` (default `_sources/failed/`) with a sidecar `.error.md`. Lint surfaces the quarantine count weekly.\n",Jr=["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],Xr=["Bash(rm -rf *)","Bash(git push *)","Bash(rm -rf /*)","Bash(mv * /*)","Bash(cp -r * /*)"];async function Mn(i,t){let e=(0,ce.normalizePath)(`${t}/permissions.json`),s=i.getAbstractFileByPath(e),a={allow:[],deny:[]};if(s instanceof ce.TFile)try{let o=await i.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,Jr),deny:Dn(a.deny,Xr)},r=JSON.stringify(n,null,2)+`
11774
+ `;return s instanceof ce.TFile?await i.modify(s,r):await i.create(e,r),n}function Dn(i,t){let e=new Set,s=[];for(let a of i)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(i,t,e){let s=Ra(e.scopeSlug||e.scopeRoot),a=(0,ce.normalizePath)(`${t}/agents/${s}`);if(await $t(i,a),i.getAbstractFileByPath((0,ce.normalizePath)(`${a}/agent.md`)))throw new Error(`Wiki Keeper agent already exists at ${a}. Delete it first or choose a different scope slug.`);await i.create((0,ce.normalizePath)(`${a}/agent.md`),Gr(s,e.scopeRoot)),await i.create((0,ce.normalizePath)(`${a}/config.md`),Vr(e)),await i.create((0,ce.normalizePath)(`${a}/HEARTBEAT.md`),Yr(e)),await i.create((0,ce.normalizePath)(`${a}/CONTEXT.md`),Kr),await Mn(i,a);let r=Ia(t,s);i.getAbstractFileByPath(r)||(await $t(i,(0,ce.normalizePath)(`${t}/tasks`)),await i.create(r,Ln(s)));let o=e.scopeRoot.trim(),c=o?`${o}/`:"";return await $t(i,(0,ce.normalizePath)(`${c}${e.inboxPath}`)),await $t(i,(0,ce.normalizePath)(`${c}${e.topicsRoot}`)),await Rn(i,(0,ce.normalizePath)(`${c}${e.indexPath}`),`# Index
11708
11775
 
11709
11776
  <!-- wiki-keeper:begin -->
11710
11777
  <!-- wiki-keeper:end -->
11711
- `),await an(r,(0,fe.normalizePath)(`${l}${e.logPath}`),`# Log
11778
+ `),await Rn(i,(0,ce.normalizePath)(`${c}${e.logPath}`),`# Log
11712
11779
 
11713
11780
  <!-- wiki-keeper:begin -->
11714
11781
  <!-- wiki-keeper:end -->
11715
- `),{path:a,name:s}}async function Ft(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 an(r,t,e){if(r.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await Ft(r,s),await r.create(t,e)}async function ln(r,t,e,s){let a=(0,fe.normalizePath)(`${t}/agents/${e}`),n=(0,fe.normalizePath)(`${a}/config.md`),i=r.getAbstractFileByPath(n);if(!(i instanceof fe.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await r.cachedRead(i),{frontmatter:l,body:c}=ie(o),d=l.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,l.wiki_keeper=d,l.allowed_tools=["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],await r.modify(i,J(l,c));let h=(0,fe.normalizePath)(`${a}/HEARTBEAT.md`),u=r.getAbstractFileByPath(h);if(u instanceof fe.TFile){let f=await r.cachedRead(u),{frontmatter:v,body:k}=ie(f);s.heartbeatChannel?v.channel=s.heartbeatChannel:delete v.channel,await r.modify(u,J(v,k))}let p=ua(t,e);r.getAbstractFileByPath(p)instanceof fe.TFile||(await Ft(r,(0,fe.normalizePath)(`${t}/tasks`)),await r.create(p,rn(e)))}async function cn(r,t,e){let s=(0,fe.normalizePath)(`${t}/agents/${e}`),a=r.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof fe.TFolder))throw new Error(`Expected folder at ${s}`);await r.adapter.rmdir(s,!0);let n=ua(t,e),i=r.getAbstractFileByPath(n);i instanceof fe.TFile&&await r.delete(i)}var Ss=class extends U.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 U.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 U.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 U.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();_t(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||lt.defaultModel,await this.plugin.saveSettings()}}),new U.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 U.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 U.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 U.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 U.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 U.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 U.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 U.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 U.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(c=>c.setLimits(1,20,1).setValue(this.plugin.settings.maxConcurrentChannelSessions).setDynamicTooltip().onChange(async d=>{this.plugin.settings.maxConcurrentChannelSessions=d,await this.plugin.saveSettings()})),new U.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(c=>c.setLimits(1,120,1).setValue(this.plugin.settings.channelIdleTimeoutMinutes).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelIdleTimeoutMinutes=d,await this.plugin.saveSettings()})),new U.Setting(e).setName("Rate limit per conversation").setDesc("Maximum messages allowed per external conversation within the rolling window.").addSlider(c=>c.setLimits(1,100,1).setValue(this.plugin.settings.channelRateLimitPerConversation).setDynamicTooltip().onChange(async d=>{this.plugin.settings.channelRateLimitPerConversation=d,await this.plugin.saveSettings()})),new U.Setting(e).setName("Rate limit window (minutes)").addSlider(c=>c.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 U.Setting(n).setName("Reference name").setDesc("Used by `credential_ref` in _fleet/channels/*.md files.").addText(c=>c.setPlaceholder("my-creds").onChange(d=>{i.ref=d.trim()})),new U.Setting(n).setName("Type").addDropdown(c=>c.addOption("slack","Slack").addOption("telegram","Telegram").setValue("slack").onChange(d=>{i.type=d,o.style.display=d==="slack"?"":"none",l.style.display=d==="telegram"?"":"none"}));let o=n.createDiv();new U.Setting(o).setName("Bot token (xoxb-...)").addText(c=>{c.inputEl.type="password",c.setPlaceholder("xoxb-...").onChange(d=>{i.botToken=d.trim()})}),new U.Setting(o).setName("App-level token (xapp-...)").setDesc("Generated in your Slack app's Basic Information \u2192 App-Level Tokens.").addText(c=>{c.inputEl.type="password",c.setPlaceholder("xapp-...").onChange(d=>{i.appToken=d.trim()})});let l=n.createDiv();l.style.display="none",new U.Setting(l).setName("Bot token").setDesc("From @BotFather on Telegram.").addText(c=>{c.inputEl.type="password",c.setPlaceholder("123456:ABC-DEF1234...").onChange(d=>{i.botToken=d.trim()})}),new U.Setting(n).addButton(c=>c.setButtonText("Add credential").setCta().onClick(async()=>{if(!i.ref||!i.botToken){new U.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 U.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 U.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 ${lr(or(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let l=i.createEl("button",{text:"Remove"});l.onclick=()=>{this.plugin.channelCredentials.delete(a),new U.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 l=i.createDiv({cls:"af-wk-row"}),c=l.createDiv({cls:"af-wk-row-left"});c.createSpan({cls:"af-wk-name",text:o.name});let d=o.wikiKeeper.scopeRoot||"(whole vault)";c.createSpan({cls:"af-wk-scope",text:`scope: ${d}`});let h=l.createDiv({cls:"af-wk-row-actions"}),u=h.createEl("button",{text:"Edit",cls:"af-wk-row-btn"});u.onclick=()=>{new fa(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}"?
11782
+ `),{path:a,name:s}}async function $t(i,t){if(i.getAbstractFileByPath(t))return;let e=t.split("/"),s="";for(let a of e)if(s=s?`${s}/${a}`:a,!i.getAbstractFileByPath(s))try{await i.createFolder(s)}catch(n){if(!(n instanceof Error?n.message:String(n)).includes("already exists"))throw n}}async function Rn(i,t,e){if(i.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await $t(i,s),await i.create(t,e)}async function On(i,t,e,s){let a=(0,ce.normalizePath)(`${t}/agents/${e}`),n=(0,ce.normalizePath)(`${a}/config.md`),r=i.getAbstractFileByPath(n);if(!(r instanceof ce.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await i.cachedRead(r),{frontmatter:c,body:l}=ae(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 i.modify(r,K(c,l)),await Mn(i,a);let h=(0,ce.normalizePath)(`${a}/HEARTBEAT.md`),u=i.getAbstractFileByPath(h);if(u instanceof ce.TFile){let f=await i.cachedRead(u),{frontmatter:g,body:b}=ae(f);s.heartbeatChannel?g.channel=s.heartbeatChannel:delete g.channel,await i.modify(u,K(g,b))}let p=Ia(t,e);i.getAbstractFileByPath(p)instanceof ce.TFile||(await $t(i,(0,ce.normalizePath)(`${t}/tasks`)),await i.create(p,Ln(e)))}async function Nn(i,t,e){let s=(0,ce.normalizePath)(`${t}/agents/${e}`),a=i.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof ce.TFolder))throw new Error(`Expected folder at ${s}`);await i.adapter.rmdir(s,!0);let n=Ia(t,e),r=i.getAbstractFileByPath(n);r instanceof ce.TFile&&await i.delete(r)}var Ns=class extends O.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 O.Setting(e).setName("Fleet folder").addText(n=>n.setValue(this.plugin.settings.fleetFolder).onChange(async r=>{this.plugin.settings.fleetFolder=r.trim()||lt.fleetFolder,await this.plugin.saveSettings()})),new O.Setting(e).setName("Claude CLI path").addText(n=>n.setValue(this.plugin.settings.claudeCliPath).onChange(async r=>{this.plugin.settings.claudeCliPath=r.trim()||lt.claudeCliPath,await this.plugin.saveSettings()})),new O.Setting(e).setName("Codex CLI path").setDesc("Used by agents with adapter \u201Ccodex\u201D. Install via `npm i -g @openai/codex` and authenticate with `codex login` in a terminal first.").addText(n=>n.setValue(this.plugin.settings.codexCliPath).onChange(async r=>{this.plugin.settings.codexCliPath=r.trim()||lt.codexCliPath,await this.plugin.saveSettings()}));let a=new O.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();Et(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||lt.defaultModel,await this.plugin.saveSettings()}}),new O.Setting(e).setName("AWS region").addText(n=>n.setValue(this.plugin.settings.awsRegion).onChange(async r=>{this.plugin.settings.awsRegion=r.trim()||lt.awsRegion,await this.plugin.saveSettings()})),new O.Setting(e).setName("Max concurrent runs").addSlider(n=>n.setLimits(1,10,1).setValue(this.plugin.settings.maxConcurrentRuns).setDynamicTooltip().onChange(async r=>{this.plugin.settings.maxConcurrentRuns=r,await this.plugin.saveSettings()})),new O.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 r=>{this.plugin.settings.runLogRetentionDays=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Catch up missed tasks").addToggle(n=>n.setValue(this.plugin.settings.catchUpMissedTasks).onChange(async r=>{this.plugin.settings.catchUpMissedTasks=r,await this.plugin.saveSettings()})),new O.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 r=>{this.plugin.settings.notificationLevel=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Status bar").addToggle(n=>n.setValue(this.plugin.settings.showStatusBar).onChange(async r=>{this.plugin.settings.showStatusBar=r,await this.plugin.saveSettings(),this.plugin.refreshStatusBar()})),new O.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 r=>{this.plugin.settings.chatWatchdogMinutes=r,await this.plugin.saveSettings()})),new O.Setting(e).setName("Verify Claude CLI").setDesc("Checks that the configured binary is reachable.").addButton(n=>n.setButtonText("Verify").onClick(async()=>{let r=await this.plugin.verifyClaudeCli();new O.Notice(r?"Claude CLI detected.":"Claude CLI check failed. See console for details.")})),new O.Setting(e).setName("Verify Codex CLI").setDesc("Checks that the configured Codex binary is reachable.").addButton(n=>n.setButtonText("Verify").onClick(async()=>{let r=await this.plugin.verifyCodexCli();new O.Notice(r?"Codex CLI detected.":"Codex 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 O.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 O.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 O.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 O.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 r={ref:"",type:"slack",botToken:"",appToken:""};new O.Setting(n).setName("Reference name").setDesc("Used by `credential_ref` in _fleet/channels/*.md files.").addText(l=>l.setPlaceholder("my-creds").onChange(d=>{r.ref=d.trim()})),new O.Setting(n).setName("Type").addDropdown(l=>l.addOption("slack","Slack").addOption("telegram","Telegram").setValue("slack").onChange(d=>{r.type=d,o.style.display=d==="slack"?"":"none",c.style.display=d==="telegram"?"":"none"}));let o=n.createDiv();new O.Setting(o).setName("Bot token (xoxb-...)").addText(l=>{l.inputEl.type="password",l.setPlaceholder("xoxb-...").onChange(d=>{r.botToken=d.trim()})}),new O.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=>{r.appToken=d.trim()})});let c=n.createDiv();c.style.display="none",new O.Setting(c).setName("Bot token").setDesc("From @BotFather on Telegram.").addText(l=>{l.inputEl.type="password",l.setPlaceholder("123456:ABC-DEF1234...").onChange(d=>{r.botToken=d.trim()})}),new O.Setting(n).addButton(l=>l.setButtonText("Add credential").setCta().onClick(async()=>{if(!r.ref||!r.botToken){new O.Notice("Fill in the reference name and bot token.");return}let d;if(r.type==="telegram")d={type:"telegram",botToken:r.botToken};else{if(!r.appToken){new O.Notice("Slack requires both bot token and app-level token.");return}d={type:"slack",botToken:r.botToken,appToken:r.appToken}}this.plugin.channelCredentials.set(r.ref,d),new O.Notice(`Added credential \`${r.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 r=e.createDiv({cls:"af-channel-credential-row"});r.style.display="flex",r.style.justifyContent="space-between",r.style.alignItems="center",r.style.padding="8px 12px",r.style.border="1px solid var(--background-modifier-border)",r.style.borderRadius="6px",r.style.marginBottom="6px";let o=r.createDiv();o.createEl("strong",{text:a}),o.createEl("span",{text:` \xB7 ${n.type} \xB7 ${Zr(Qr(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let c=r.createEl("button",{text:"Remove"});c.onclick=()=>{this.plugin.channelCredentials.delete(a),new O.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),r=e.createDiv({cls:"af-wk-list"});if(n.length===0)r.createDiv({cls:"af-wk-empty",text:"No Wiki Keepers yet. Click Add to create one."});else for(let o of n){let c=r.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 Ma(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}"?
11716
11783
 
11717
11784
  This removes the agent folder at _fleet/agents/${o.name}/.
11718
- Your scope's inbox, topics, index, and log are NOT deleted.`))try{await cn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),new U.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(f){let v=f instanceof Error?f.message:String(f);new U.Notice(`Failed to delete: ${v}`)}}}new U.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new pa(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},pa=class extends U.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=da()}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 U.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 U.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 U.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 U.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 U.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 U.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 U.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 U.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 U.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 U.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()||nn[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"}),l=o.createEl("button",{text:"Cancel"});l.onclick=()=>this.close();let c=o.createEl("button",{text:"Create",cls:"mod-cta"});c.onclick=async()=>{c.setText("Creating\u2026"),c.disabled=!0;try{let{name:d}=await on(this.app.vault,this.plugin.settings.fleetFolder,this.input);this.close(),new U.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new U.Notice(`Failed to create Wiki Keeper: ${h}`),c.setText("Create"),c.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: ${ha(s)}`)}onClose(){this.contentEl.empty()}},fa=class extends U.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}}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 U.Setting(e).setName("Watched folders").setDesc("Comma- or newline-separated vault-relative paths.").addTextArea(c=>c.setValue(this.edit.watchedFolders.join(", ")).onChange(d=>{this.edit.watchedFolders=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new U.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(c=>c.setValue(this.edit.excludePatterns.join(", ")).onChange(d=>{this.edit.excludePatterns=d.split(/[,\n]/).map(h=>h.trim()).filter(Boolean)})),new U.Setting(e).setName("Watch files modified since").setDesc("Watched mode skips files older than this date. Clear to process everything.").addText(c=>{c.inputEl.type="date",c.setValue(this.edit.watchedSince).onChange(d=>{this.edit.watchedSince=d.trim()})});let n=this.plugin.runtime.getSnapshot().channels.map(c=>c.name);new U.Setting(e).setName("Heartbeat channel").addDropdown(c=>{c.addOption("","(none)");for(let d of n)c.addOption(d,d);c.setValue(this.edit.heartbeatChannel).onChange(d=>{this.edit.heartbeatChannel=d})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new U.Setting(e).setName("Max tokens per ingest").addText(c=>c.setValue(String(this.edit.maxTokensPerIngest)).onChange(d=>{let h=parseInt(d,10);!isNaN(h)&&h>0&&(this.edit.maxTokensPerIngest=h)})),new U.Setting(e).setName("File substantive answers").setDesc("If on, wiki-query files long answers under _topics/syntheses/.").addToggle(c=>c.setValue(this.edit.fileSubstantiveAnswers).onChange(d=>{this.edit.fileSubstantiveAnswers=d})),new U.Setting(e).setName("Obsidian URL scheme for citations").setDesc("Rewrite [[wikilinks]] as obsidian:// URLs when replying via Slack/Telegram.").addToggle(c=>c.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 l=i.createEl("button",{text:"Save",cls:"mod-cta"});l.onclick=async()=>{l.setText("Saving\u2026"),l.disabled=!0;try{await ln(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),this.close(),new U.Notice("Saved."),this.onSaved()}catch(c){let d=c instanceof Error?c.message:String(c);new U.Notice(`Failed to save: ${d}`),l.setText("Save"),l.disabled=!1}}}onClose(){this.contentEl.empty()}};function or(r){return r.type==="slack",r.botToken}function lr(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var kn=require("crypto");function He(r,t,e,s,a,n,i,o){return He.fromTZ(He.tp(r,t,e,s,a,n,i),o)}He.fromTZISO=(r,t,e)=>He.fromTZ(cr(r,t),e);He.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=ma(r.tz,e),a=new Date(e.getTime()-s),n=ma(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=ma(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}};He.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}};He.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function ma(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 cr(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("+")?He.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):He.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}He.minitz=He;function dr(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 Ce(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new Ce(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 ga=32,es=31|ga,hn=[1,2,4,8,16];function We(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()}We.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 Ce(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,es),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};We.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;o<i.length;o++)this.partToArray(r,i[o],e,s);else t.indexOf("-")!==-1&&t.indexOf("/")!==-1?this.handleRangeWithStepping(t,r,e,s):t.indexOf("-")!==-1?this.handleRange(t,r,e,s):t.indexOf("/")!==-1?this.handleStepping(t,r,e,s):t!==""&&this.handleNumber(t,r,e,s)};We.prototype.throwAtIllegalCharacters=function(r){for(let t=0;t<r.length;t++)if((t===5?/[^/*0-9,\-#L]+/:/[^/*0-9,-]+/).test(r[t]))throw new TypeError("CronPattern: configuration entry "+t+" ("+r[t]+") contains illegal characters.")};We.prototype.handleNumber=function(r,t,e,s){let a=this.extractNth(r,t),n=parseInt(a[0],10)+e;if(isNaN(n))throw new TypeError("CronPattern: "+t+" is not a number: '"+r+"'");this.setPart(t,n,a[1]||s)};We.prototype.setPart=function(r,t,e){if(!Object.prototype.hasOwnProperty.call(this,r))throw new TypeError("CronPattern: Invalid part specified: "+r);if(r==="dayOfWeek"){if(t===7&&(t=0),(t<0||t>6)&&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};We.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,l]=n;if(i=parseInt(i,10)+e,o=parseInt(o,10)+e,l=parseInt(l,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(l))throw new TypeError("CronPattern: Syntax error, illegal stepping: (NaN)");if(l===0)throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");if(l>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 c=i;c<=o;c+=l)this.setPart(t,c,a[1]||s)};We.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]};We.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 l=i;l<=o;l++)this.setPart(t,l,a[1]||s)};We.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 l=i;l<this[t].length;l+=o)this.setPart(t,l,a[1]||s)};We.prototype.replaceAlphaDays=function(r){return r.replace(/-sun/gi,"-7").replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};We.prototype.replaceAlphaMonths=function(r){return r.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};We.prototype.handleNicknames=function(r){let t=r.trim().toLowerCase();return t==="@yearly"||t==="@annually"?"0 0 1 1 *":t==="@monthly"?"0 0 1 * *":t==="@weekly"?"0 0 * * 0":t==="@daily"?"0 0 * * *":t==="@hourly"?"0 * * * *":r};We.prototype.setNthWeekdayOfMonth=function(r,t){if(t==="L")this.dayOfWeek[r]=this.dayOfWeek[r]|ga;else if(t<6&&t>0)this.dayOfWeek[r]=this.dayOfWeek[r]|hn[t-1];else if(t===es)this.dayOfWeek[r]=es;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var un=[31,28,31,30,31,30,31,31,30,31,30,31],pt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function Ce(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 Ce)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}Ce.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&es&&hn[i-1]&s)return!0;if(s&ga){let o=new Date(Date.UTC(r,t+1,0)).getUTCDate();for(let l=e+1;l<=o;l++)if(new Date(Date.UTC(r,t,l)).getUTCDay()===n)return!1;return!0}return!1};Ce.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=He.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()};Ce.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};Ce.prototype.apply=function(){if(this.month>11||this.day>un[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};Ce.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=He.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(He.fromTZISO(r,this.tz))};Ce.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=un[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;o<e[t].length;o++){let l=e[t][o];if(t==="day"&&e.lastDayOfMonth&&o-s==n&&(l=!0),t==="day"&&!e.starDOW){let c=e.dayOfWeek[(i+(o-s-1))%7];if(c&&c&es)c=this.isNthWeekdayOfMonth(this.year,this.month,o-s,c);else if(c)throw new Error(`CronDate: Invalid value for dayOfWeek encountered. ${c}`);r.legacyMode&&!e.starDOM?l=l||c:l=l&&c}if(l)return this[t]=o-s,a!==this[t]?2:1}return 3};Ce.prototype.recurse=function(r,t,e){let s=this.findNext(t,pt[e][0],r,pt[e][2]);if(s>1){let a=e+1;for(;a<pt.length;)this[pt[a][0]]=-pt[a][2],a++;if(s===3)return this[pt[e][1]]++,this[pt[e][0]]=-pt[e][2],this.apply(),this.recurse(r,t,0);if(this.apply())return this.recurse(r,t,e-1)}return e+=1,e>=pt.length?this:this.year>=3e3?null:this.recurse(r,t,e)};Ce.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)};Ce.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)):He(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};Ce.prototype.getTime=function(){return this.getDate().getTime()};function Ts(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function hr(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var dn=30*1e3,ts=[];function ce(r,t,e){if(!(this instanceof ce))return new ce(r,t,e);let s,a;if(Ts(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(Ts(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=dr(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 Ce(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new We(r,this.options.timezone),this.name){if(ts.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");ts.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=ts.indexOf(this);r>=0&&ts.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>dn&&(t=dn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&hr(this._states.currentTimeout),this)};ce.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new Ce(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){Ts(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new Ce(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&&Ts(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 Ce(r,this.options.timezone||this.options.utcOffset),this.options.startAt&&r&&r.getTime()<this.options.startAt.getTime()&&(r=this.options.startAt);let s=this._states.once||new Ce(r,this.options.timezone||this.options.utcOffset);return!e&&s!==this._states.once&&(s=s.increment(this._states.pattern,this.options,t)),this._states.once&&this._states.once.getTime()<=r.getTime()||s===null||this._states.maxRuns<=0||this._states.kill||this.options.stopAt&&s.getTime()>=this.options.stopAt.getTime()?null:s};ce.prototype._calculatePreviousRun=function(r,t){let e=new Ce(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 Ce(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=ts;var xn=require("obsidian");var mn=require("crypto"),Ke=require("fs"),ba=require("path");var pn=new Set(["","default","subscription"]);function Ot(r,t,e){let s=ya(r?.model);if(s)return{value:va(s),source:"task"};let a=ya(t.model);if(a)return{value:va(a),source:"agent"};let n=ya(e.defaultModel);return n?{value:va(n),source:"settings"}:{value:"",source:"cli-default"}}function Cs(r){return!pn.has(r.trim())}function ya(r){if(!r)return"";let t=r.trim();return t||""}function va(r){return pn.has(r)?"":r}function _s(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,l=o.scopeRoot?`${o.scopeRoot.replace(/\/+$/,"")}/`:"";s.push({agentName:i.name,scopeRoot:o.scopeRoot||"(whole vault)",topicsPath:`${l}${o.topicsRoot}`,inboxPath:`${l}${o.inboxPath}`,indexPath:`${l}${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 `[[<topics-path>/<page>]]`. 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 `<inbox>/YYYY-MM-DD-<slug>.md` with a one-line note + the source. The wiki keeper files it canonically on its next ingest."),a.push("- **Do NOT write to `<topics-path>/` 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(`
11719
- `)}function gn(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(`
11720
- `);if(r&&typeof r=="object"){for(let t of["output","result","text","message"])if(t in r)return gn(r[t])}}function wa(r,t=[]){if(Array.isArray(r)){for(let i of r)wa(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))wa(i,t);return t}function yn(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,l=a+n+i+o;if(l>0)return l}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=yn(a);if(typeof n=="number")return n}}function vn(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=vn(e);if(typeof s=="number")return s}}function fn(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 ka(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=ka(s);if(a)return a}}function bn(r){let t=r.matchAll(/\[REMEMBER\]([\s\S]*?)\[\/REMEMBER\]/g);return Array.from(t).map(e=>e[1]?.trim()??"").filter(Boolean)}var As=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,l=o?.command??o?.content??"";a.push(`
11721
- \u25B8 ${i}${l?`: ${String(l).slice(0,200)}`:""}
11722
- `)}if(a.length>0)return a.join("")}}if(e==="result"){let s=typeof t.result=="string"?t.result:null;if(s)return`
11723
- ${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 l=[o.body.trim()];o.toolsBody.trim()&&l.push(`### Tools
11724
- ${o.toolsBody.trim()}`),o.referencesBody.trim()&&l.push(`### References
11725
- ${o.referencesBody.trim()}`),o.examplesBody.trim()&&l.push(`### Examples
11785
+ Your scope's inbox, topics, index, and log are NOT deleted.`))try{await Nn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),await this.plugin.refreshFromVault(),new O.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(f){let g=f instanceof Error?f.message:String(f);new O.Notice(`Failed to delete: ${g}`)}}}new O.Setting(e).setName("Add Wiki Keeper").setDesc("Create a new scoped wiki agent. All fields optional.").addButton(o=>o.setButtonText("+ Add").onClick(()=>{new La(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},La=class extends O.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input=Da()}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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 r=(d,h,u)=>{new O.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()||In[u]}))};r("Inbox path","Where one-off source drops land.","inboxPath"),r("Archive path","Where processed inbox files are moved after summarization.","archivePath"),r("Topics root","Folder under scope_root that holds the generated topic pages.","topicsRoot"),r("Index path","Relative file path for the curated index.","indexPath"),r("Log path","Relative file path for the keeper's activity log.","logPath"),r("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 O.Notice(`Wiki Keeper "${d}" created.`),this.onCreated()}catch(d){let h=d instanceof Error?d.message:String(d);new O.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: ${Ra(s)}`)}onClose(){this.contentEl.empty()}},Ma=class extends O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 O.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 r=e.createDiv({cls:"af-wk-modal-footer"}),o=r.createEl("button",{text:"Cancel"});o.onclick=()=>this.close();let c=r.createEl("button",{text:"Save",cls:"mod-cta"});c.onclick=async()=>{c.setText("Saving\u2026"),c.disabled=!0;try{await On(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),await this.plugin.refreshFromVault(),this.close(),new O.Notice("Saved."),this.onSaved()}catch(l){let d=l instanceof Error?l.message:String(l);new O.Notice(`Failed to save: ${d}`),c.setText("Save"),c.disabled=!1}}}onClose(){this.contentEl.empty()}};function Qr(i){return i.type==="slack",i.botToken}function Zr(i){return i.length<=10?"***":`${i.slice(0,6)}\u2026${i.slice(-4)}`}var ii=require("crypto");function We(i,t,e,s,a,n,r,o){return We.fromTZ(We.tp(i,t,e,s,a,n,r),o)}We.fromTZISO=(i,t,e)=>We.fromTZ(eo(i,t),e);We.fromTZ=function(i,t){let e=new Date(Date.UTC(i.y,i.m-1,i.d,i.h,i.i,i.s)),s=Fa(i.tz,e),a=new Date(e.getTime()-s),n=Fa(i.tz,a);if(n-s===0)return a;{let r=new Date(e.getTime()-n),o=Fa(i.tz,r);if(o-n===0)return r;if(!t&&o-n>0)return r;if(t)throw new Error("Invalid date passed to fromTZ()");return a}};We.toTZ=function(i,t){let e=i.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=(i,t,e,s,a,n,r)=>({y:i,m:t,d:e,h:s,i:a,s:n,tz:r});function Fa(i,t=new Date){let e=t.toLocaleString("en-US",{timeZone:i,timeZoneName:"shortOffset"}).split(" ").slice(-1)[0],s=t.toLocaleString("en-US").replace(/[\u202f]/," ");return Date.parse(`${s} GMT`)-Date.parse(`${s} ${e}`)}function eo(i,t){let e=new Date(Date.parse(i));if(isNaN(e))throw new Error("minitz: Invalid ISO8601 passed to parser.");let s=i.substring(9);return i.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 to(i){if(i===void 0&&(i={}),delete i.name,i.legacyMode=i.legacyMode===void 0?!0:i.legacyMode,i.paused=i.paused===void 0?!1:i.paused,i.maxRuns=i.maxRuns===void 0?1/0:i.maxRuns,i.catch=i.catch===void 0?!1:i.catch,i.interval=i.interval===void 0?0:parseInt(i.interval,10),i.utcOffset=i.utcOffset===void 0?void 0:parseInt(i.utcOffset,10),i.unref=i.unref===void 0?!1:i.unref,i.startAt&&(i.startAt=new Ae(i.startAt,i.timezone)),i.stopAt&&(i.stopAt=new Ae(i.stopAt,i.timezone)),i.interval!==null){if(isNaN(i.interval))throw new Error("CronOptions: Supplied value for interval is not a number");if(i.interval<0)throw new Error("CronOptions: Supplied value for interval can not be negative")}if(i.utcOffset!==void 0){if(isNaN(i.utcOffset))throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");if(i.utcOffset<-870||i.utcOffset>870)throw new Error("CronOptions: utcOffset out of bounds.");if(i.utcOffset!==void 0&&i.timezone)throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.")}if(i.unref!==!0&&i.unref!==!1)throw new Error("CronOptions: Unref should be either true, false or undefined(false).");return i}var Oa=32,as=31|Oa,Un=[1,2,4,8,16];function Ve(i,t){this.pattern=i,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()}Ve.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 i=this.pattern.replace(/\s+/g," ").split(" ");if(i.length<5||i.length>6)throw new TypeError("CronPattern: invalid configuration format ('"+this.pattern+"'), exactly five or six space separated parts are required.");if(i.length===5&&i.unshift("0"),i[3].indexOf("L")>=0&&(i[3]=i[3].replace("L",""),this.lastDayOfMonth=!0),i[3]=="*"&&(this.starDOM=!0),i[4].length>=3&&(i[4]=this.replaceAlphaMonths(i[4])),i[5].length>=3&&(i[5]=this.replaceAlphaDays(i[5])),i[5]=="*"&&(this.starDOW=!0),this.pattern.indexOf("?")>=0){let t=new Ae(new Date,this.timezone).getDate(!0);i[0]=i[0].replace("?",t.getSeconds()),i[1]=i[1].replace("?",t.getMinutes()),i[2]=i[2].replace("?",t.getHours()),this.starDOM||(i[3]=i[3].replace("?",t.getDate())),i[4]=i[4].replace("?",t.getMonth()+1),this.starDOW||(i[5]=i[5].replace("?",t.getDay()))}this.throwAtIllegalCharacters(i),this.partToArray("second",i[0],0,1),this.partToArray("minute",i[1],0,1),this.partToArray("hour",i[2],0,1),this.partToArray("day",i[3],-1,1),this.partToArray("month",i[4],-1,1),this.partToArray("dayOfWeek",i[5],0,as),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};Ve.prototype.partToArray=function(i,t,e,s){let a=this[i],n=i==="day"&&this.lastDayOfMonth;if(t===""&&!n)throw new TypeError("CronPattern: configuration entry "+i+" ("+t+") is empty, check for trailing spaces.");if(t==="*")return a.fill(s);let r=t.split(",");if(r.length>1)for(let o=0;o<r.length;o++)this.partToArray(i,r[o],e,s);else t.indexOf("-")!==-1&&t.indexOf("/")!==-1?this.handleRangeWithStepping(t,i,e,s):t.indexOf("-")!==-1?this.handleRange(t,i,e,s):t.indexOf("/")!==-1?this.handleStepping(t,i,e,s):t!==""&&this.handleNumber(t,i,e,s)};Ve.prototype.throwAtIllegalCharacters=function(i){for(let t=0;t<i.length;t++)if((t===5?/[^/*0-9,\-#L]+/:/[^/*0-9,-]+/).test(i[t]))throw new TypeError("CronPattern: configuration entry "+t+" ("+i[t]+") contains illegal characters.")};Ve.prototype.handleNumber=function(i,t,e,s){let a=this.extractNth(i,t),n=parseInt(a[0],10)+e;if(isNaN(n))throw new TypeError("CronPattern: "+t+" is not a number: '"+i+"'");this.setPart(t,n,a[1]||s)};Ve.prototype.setPart=function(i,t,e){if(!Object.prototype.hasOwnProperty.call(this,i))throw new TypeError("CronPattern: Invalid part specified: "+i);if(i==="dayOfWeek"){if(t===7&&(t=0),(t<0||t>6)&&t!=="L")throw new RangeError("CronPattern: Invalid value for dayOfWeek: "+t);this.setNthWeekdayOfMonth(t,e);return}if(i==="second"||i==="minute"){if(t<0||t>=60)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="hour"){if(t<0||t>=24)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="day"){if(t<0||t>=31)throw new RangeError("CronPattern: Invalid value for "+i+": "+t)}else if(i==="month"&&(t<0||t>=12))throw new RangeError("CronPattern: Invalid value for "+i+": "+t);this[i][t]=e};Ve.prototype.handleRangeWithStepping=function(i,t,e,s){let a=this.extractNth(i,t),n=a[0].match(/^(\d+)-(\d+)\/(\d+)$/);if(n===null)throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '"+i+"'");let[,r,o,c]=n;if(r=parseInt(r,10)+e,o=parseInt(o,10)+e,c=parseInt(c,10),isNaN(r))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(r>o)throw new TypeError("CronPattern: From value is larger than to value: '"+i+"'");for(let l=r;l<=o;l+=c)this.setPart(t,l,a[1]||s)};Ve.prototype.extractNth=function(i,t){let e=i,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]};Ve.prototype.handleRange=function(i,t,e,s){let a=this.extractNth(i,t),n=a[0].split("-");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal range: '"+i+"'");let r=parseInt(n[0],10)+e,o=parseInt(n[1],10)+e;if(isNaN(r))throw new TypeError("CronPattern: Syntax error, illegal lower range (NaN)");if(isNaN(o))throw new TypeError("CronPattern: Syntax error, illegal upper range (NaN)");if(r>o)throw new TypeError("CronPattern: From value is larger than to value: '"+i+"'");for(let c=r;c<=o;c++)this.setPart(t,c,a[1]||s)};Ve.prototype.handleStepping=function(i,t,e,s){let a=this.extractNth(i,t),n=a[0].split("/");if(n.length!==2)throw new TypeError("CronPattern: Syntax error, illegal stepping: '"+i+"'");let r=0;n[0]!=="*"&&(r=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=r;c<this[t].length;c+=o)this.setPart(t,c,a[1]||s)};Ve.prototype.replaceAlphaDays=function(i){return i.replace(/-sun/gi,"-7").replace(/sun/gi,"0").replace(/mon/gi,"1").replace(/tue/gi,"2").replace(/wed/gi,"3").replace(/thu/gi,"4").replace(/fri/gi,"5").replace(/sat/gi,"6")};Ve.prototype.replaceAlphaMonths=function(i){return i.replace(/jan/gi,"1").replace(/feb/gi,"2").replace(/mar/gi,"3").replace(/apr/gi,"4").replace(/may/gi,"5").replace(/jun/gi,"6").replace(/jul/gi,"7").replace(/aug/gi,"8").replace(/sep/gi,"9").replace(/oct/gi,"10").replace(/nov/gi,"11").replace(/dec/gi,"12")};Ve.prototype.handleNicknames=function(i){let t=i.trim().toLowerCase();return t==="@yearly"||t==="@annually"?"0 0 1 1 *":t==="@monthly"?"0 0 1 * *":t==="@weekly"?"0 0 * * 0":t==="@daily"?"0 0 * * *":t==="@hourly"?"0 * * * *":i};Ve.prototype.setNthWeekdayOfMonth=function(i,t){if(t==="L")this.dayOfWeek[i]=this.dayOfWeek[i]|Oa;else if(t<6&&t>0)this.dayOfWeek[i]=this.dayOfWeek[i]|Un[t-1];else if(t===as)this.dayOfWeek[i]=as;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var $n=[31,28,31,30,31,30,31,31,30,31,30,31],yt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function Ae(i,t){if(this.tz=t,i&&i instanceof Date)if(!isNaN(i))this.fromDate(i);else throw new TypeError("CronDate: Invalid date passed to CronDate constructor");else if(i===void 0)this.fromDate(new Date);else if(i&&typeof i=="string")this.fromString(i);else if(i instanceof Ae)this.fromCronDate(i);else throw new TypeError("CronDate: Invalid type ("+typeof i+") passed to CronDate constructor")}Ae.prototype.isNthWeekdayOfMonth=function(i,t,e,s){let n=new Date(Date.UTC(i,t,e)).getUTCDay(),r=0;for(let o=1;o<=e;o++)new Date(Date.UTC(i,t,o)).getUTCDay()===n&&r++;if(s&as&&Un[r-1]&s)return!0;if(s&Oa){let o=new Date(Date.UTC(i,t+1,0)).getUTCDate();for(let c=e+1;c<=o;c++)if(new Date(Date.UTC(i,t,c)).getUTCDay()===n)return!1;return!0}return!1};Ae.prototype.fromDate=function(i){if(this.tz!==void 0)if(typeof this.tz=="number")this.ms=i.getUTCMilliseconds(),this.second=i.getUTCSeconds(),this.minute=i.getUTCMinutes()+this.tz,this.hour=i.getUTCHours(),this.day=i.getUTCDate(),this.month=i.getUTCMonth(),this.year=i.getUTCFullYear(),this.apply();else{let t=We.toTZ(i,this.tz);this.ms=i.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=i.getMilliseconds(),this.second=i.getSeconds(),this.minute=i.getMinutes(),this.hour=i.getHours(),this.day=i.getDate(),this.month=i.getMonth(),this.year=i.getFullYear()};Ae.prototype.fromCronDate=function(i){this.tz=i.tz,this.year=i.year,this.month=i.month,this.day=i.day,this.hour=i.hour,this.minute=i.minute,this.second=i.second,this.ms=i.ms};Ae.prototype.apply=function(){if(this.month>11||this.day>$n[this.month]||this.hour>59||this.minute>59||this.second>59||this.hour<0||this.minute<0||this.second<0){let i=new Date(Date.UTC(this.year,this.month,this.day,this.hour,this.minute,this.second,this.ms));return this.ms=i.getUTCMilliseconds(),this.second=i.getUTCSeconds(),this.minute=i.getUTCMinutes(),this.hour=i.getUTCHours(),this.day=i.getUTCDate(),this.month=i.getUTCMonth(),this.year=i.getUTCFullYear(),!0}else return!1};Ae.prototype.fromString=function(i){if(typeof this.tz=="number"){let t=We.fromTZISO(i);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(i,this.tz))};Ae.prototype.findNext=function(i,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=$n[this.month]:n=new Date(Date.UTC(this.year,this.month+1,0,0,0,0,0)).getUTCDate());let r=!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;o<e[t].length;o++){let c=e[t][o];if(t==="day"&&e.lastDayOfMonth&&o-s==n&&(c=!0),t==="day"&&!e.starDOW){let l=e.dayOfWeek[(r+(o-s-1))%7];if(l&&l&as)l=this.isNthWeekdayOfMonth(this.year,this.month,o-s,l);else if(l)throw new Error(`CronDate: Invalid value for dayOfWeek encountered. ${l}`);i.legacyMode&&!e.starDOM?c=c||l:c=c&&l}if(c)return this[t]=o-s,a!==this[t]?2:1}return 3};Ae.prototype.recurse=function(i,t,e){let s=this.findNext(t,yt[e][0],i,yt[e][2]);if(s>1){let a=e+1;for(;a<yt.length;)this[yt[a][0]]=-yt[a][2],a++;if(s===3)return this[yt[e][1]]++,this[yt[e][0]]=-yt[e][2],this.apply(),this.recurse(i,t,0);if(this.apply())return this.recurse(i,t,e-1)}return e+=1,e>=yt.length?this:this.year>=3e3?null:this.recurse(i,t,e)};Ae.prototype.increment=function(i,t,e){return this.second+=t.interval>1&&e?t.interval:1,this.ms=0,this.apply(),this.recurse(i,t,0)};Ae.prototype.getDate=function(i){return i||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)};Ae.prototype.getTime=function(){return this.getDate().getTime()};function Bs(i){return Object.prototype.toString.call(i)==="[object Function]"||typeof i=="function"||i instanceof Function}function so(i){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(i):i&&typeof i.unref<"u"&&i.unref()}var Bn=30*1e3,ns=[];function de(i,t,e){if(!(this instanceof de))return new de(i,t,e);let s,a;if(Bs(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(Bs(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=to(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},i&&(i instanceof Date||typeof i=="string"&&i.indexOf(":")>0)?this._states.once=new Ae(i,this.options.timezone||this.options.utcOffset):this._states.pattern=new Ve(i,this.options.timezone),this.name){if(ns.find(r=>r.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");ns.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}de.prototype.nextRun=function(i){let t=this._next(i);return t?t.getDate():null};de.prototype.nextRuns=function(i,t){i>this._states.maxRuns&&(i=this._states.maxRuns);let e=[],s=t||this._states.currentRun;for(;i--&&(s=this.nextRun(s));)e.push(s);return e};de.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};de.prototype.isRunning=function(){let i=this.nextRun(this._states.currentRun),t=!this._states.paused,e=this.fn!==void 0,s=!this._states.kill;return t&&e&&s&&i!==null};de.prototype.isStopped=function(){return this._states.kill};de.prototype.isBusy=function(){return this._states.blocking};de.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};de.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};de.prototype.msToNext=function(i){i=i||new Date;let t=this._next(i);return t?t.getTime()-i.getTime():null};de.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let i=ns.indexOf(this);i>=0&&ns.splice(i,1)};de.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};de.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};de.prototype.schedule=function(i){if(i&&this.fn)throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");i&&(this.fn=i);let t=this.msToNext(),e=this.nextRun(this._states.currentRun);return t==null||isNaN(t)||e===null?this:(t>Bn&&(t=Bn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&so(this._states.currentTimeout),this)};de.prototype._trigger=async function(i){if(this._states.blocking=!0,this._states.currentRun=new Ae(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){Bs(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new Ae(i,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};de.prototype.trigger=async function(){await this._trigger()};de.prototype._checkTrigger=function(i){let t=new Date,e=!this._states.paused&&t.getTime()>=i,s=this._states.blocking&&this.options.protect;e&&!s?(this._states.maxRuns--,this._trigger()):e&&s&&Bs(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};de.prototype._next=function(i){let t=!!(i||this._states.currentRun),e=!1;!i&&this.options.startAt&&this.options.interval&&([i,t]=this._calculatePreviousRun(i,t),e=!i),i=new Ae(i,this.options.timezone||this.options.utcOffset),this.options.startAt&&i&&i.getTime()<this.options.startAt.getTime()&&(i=this.options.startAt);let s=this._states.once||new Ae(i,this.options.timezone||this.options.utcOffset);return!e&&s!==this._states.once&&(s=s.increment(this._states.pattern,this.options,t)),this._states.once&&this._states.once.getTime()<=i.getTime()||s===null||this._states.maxRuns<=0||this._states.kill||this.options.stopAt&&s.getTime()>=this.options.stopAt.getTime()?null:s};de.prototype._calculatePreviousRun=function(i,t){let e=new Ae(void 0,this.options.timezone||this.options.utcOffset);if(this.options.startAt.getTime()<=e.getTime()){i=this.options.startAt;let s=i.getTime()+this.options.interval*1e3;for(;s<=e.getTime();)i=new Ae(i,this.options.timezone||this.options.utcOffset).increment(this._states.pattern,this.options,!0),s=i.getTime()+this.options.interval*1e3;t=!0}return[i,t]};de.Cron=de;de.scheduledJobs=ns;var ri=require("obsidian");var si=require("crypto");var Qe=require("fs"),Na=require("path");function Us(i,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,Na.join)(i,".claude"),c=(0,Na.join)(o,"settings.local.json");(0,Qe.existsSync)(o)||(0,Qe.mkdirSync)(o,{recursive:!0});let l=null;if((0,Qe.existsSync)(c))try{l=(0,Qe.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,Qe.writeFileSync)(c,JSON.stringify(d,null,2)+`
11786
+ `,"utf-8"),{path:c,backupContent:l}}function Pt(i){if(i)try{i.backupContent!==null?(0,Qe.writeFileSync)(i.path,i.backupContent,"utf-8"):(0,Qe.existsSync)(i.path)&&(0,Qe.unlinkSync)(i.path)}catch{}}function jn(i){if(typeof i=="string")return i;if(Array.isArray(i))return i.map(e=>typeof e=="string"?e:e&&typeof e=="object"&&"text"in e&&typeof e.text=="string"?e.text:"").filter(Boolean).join(`
11787
+ `);if(i&&typeof i=="object"){for(let t of["output","result","text","message"])if(t in i)return jn(i[t])}}function Ba(i,t=[]){if(Array.isArray(i)){for(let r of i)Ba(r,t);return t}if(!i||typeof i!="object")return t;let e=i,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(r=>r in e)&&t.push({tool:s,command:a,reason:n});for(let r of Object.values(e))Ba(r,t);return t}function Hn(i){if(!i||typeof i!="object")return;let t=i,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,r=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+r+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 r=n;a+=typeof r.inputTokens=="number"?r.inputTokens:0,a+=typeof r.outputTokens=="number"?r.outputTokens:0,a+=typeof r.cacheReadInputTokens=="number"?r.cacheReadInputTokens:0,a+=typeof r.cacheCreationInputTokens=="number"?r.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=Hn(a);if(typeof n=="number")return n}}function qn(i){if(!i||typeof i!="object")return;let t=i;if(typeof t.total_cost_usd=="number")return t.total_cost_usd;for(let e of Object.values(t)){let s=qn(e);if(typeof s=="number")return s}}function Ua(i){if(!i||typeof i!="object")return;let t=i;if(t.type==="result"&&typeof t.result=="string"&&t.result.trim())return t.result}function $s(i){if(!i||typeof i!="object")return;let t=i;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=$s(s);if(a)return a}}function ao(i){return/^gpt-|codex/i.test(i.trim())}var Wn={id:"claude-code",label:"Claude Code",cliPath(i){return i.claudeCliPath},buildExec(i){let t=["-p",i.prompt,"--output-format",i.streaming?"stream-json":"json"];i.streaming&&t.push("--verbose");let e=i.modelSource==="settings"&&ao(i.model);i.model&&!e&&t.push("--model",i.model),i.effort&&t.push("--effort",i.effort);let s=i.agent.permissionMode?.trim();return s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions"),Promise.resolve({cliPath:i.settings.claudeCliPath,args:t})},parseExecOutput(i,t,e){let s=i.trim(),a;if(e){let c=re(s);for(let l=c.length-1;l>=0;l--){let d=c[l]?.trim();if(d)try{let h=JSON.parse(d);if(h&&typeof h=="object"){a=h;break}}catch{}}}else if(s.startsWith("{")||s.startsWith("["))try{a=JSON.parse(s)}catch{a=void 0}let n=jn(a)??"";if(!n&&e){let c=[];for(let l of re(s)){let d=l.trim();if(d)try{let h=JSON.parse(d);if(h.type==="assistant"&&h.message?.content)for(let u of h.message.content)u.type==="text"&&u.text&&c.push(u.text);else h.type==="result"&&typeof h.result=="string"&&c.push(h.result)}catch{}}n=c.join(`
11788
+ `).trim()}n||(n=t.trim()||"(no output)");let r=$s(a),o=Ua(a);if((!r||!o)&&e)for(let c of re(s)){let l=c.trim();if(l)try{let d=JSON.parse(l);if(!r){let h=$s(d);h&&(r=h)}if(!o){let h=Ua(d);h&&(o=h)}if(r&&o)break}catch{}}return{outputText:n,finalResult:o,tokensUsed:Hn(a),costUsd:qn(a),toolsUsed:Ba(a),concreteModel:r,rawJson:a}},extractStreamChunk(i){let t=i.trim();if(!t)return null;let e;try{e=JSON.parse(t)}catch{return null}let s=e.type;if(s==="assistant"){let a=e.message;if(a?.content&&Array.isArray(a.content)){let n=[];for(let r of a.content)if(r.type==="text"&&typeof r.text=="string")n.push(r.text);else if(r.type==="tool_use"){let o=String(r.name??"tool"),c=r.input,l=c?.command??c?.content??"";n.push(`
11789
+ \u25B8 ${o}${l?`: ${String(l).slice(0,200)}`:""}
11790
+ `)}if(n.length>0)return n.join("")}}if(s==="result"){let a=typeof e.result=="string"?e.result:null;if(a)return`
11791
+ ${a}`}return null},setupPermissions(i,t,e){let s=Us(i,t);return s?Promise.resolve({restore:()=>Pt(s)}):Promise.resolve(null)}};var ne=require("fs"),Ha=require("crypto"),rs=require("os"),Ze=require("path");var no=/^[A-Z][A-Za-z]*$/;function io(i){let t=i.trim();if(!t)return{error:"empty Bash() matches every command \u2014 use the read-only sandbox instead"};let e=t.split(/\s+/).filter(Boolean),s=[];for(let a=0;a<e.length;a++){let n=e[a],r=a===e.length-1;if(n==="*"){if(r)continue;return{error:"a wildcard in the middle of a command can't be expressed as a prefix rule"}}if(n.includes("*"))return{error:"embedded wildcards aren't supported (execpolicy matches argv prefixes only)"};s.push(n)}return s.length===0?{error:"a bare wildcard matches every command \u2014 use the read-only sandbox instead"}:{tokens:s}}function ro(i,t){return`prefix_rule(pattern=[${i.map(s=>JSON.stringify(s)).join(", ")}], decision="${t}", justification="agent-fleet")`}function oo(i){let t=[],e=[],s=(n,r)=>{let o=n.trim();if(!o||o.startsWith("mcp__"))return;let c=o.match(/^Bash\((.*)\)$/s);if(!c){e.push({rule:o,reason:no.test(o)?"tool-name rules are governed by the sandbox, not execpolicy":"not a Bash(...) command pattern"});return}let l=io(c[1]);if("error"in l){e.push({rule:o,reason:l.error});return}t.push(ro(l.tokens,r))};for(let n of i.permissionRules.deny)s(n,"forbidden");for(let n of i.permissionRules.allow)s(n,"allow");return t.length===0?{rulesText:null,dropped:e,emitted:0}:{rulesText:`${`# Generated by Agent Fleet for agent "${i.name}". Do not edit \u2014 regenerated each run.
11792
+ # Source: this agent's permissionRules (allow/deny), translated to Codex execpolicy.
11793
+ `}${t.join(`
11794
+ `)}
11795
+ `,dropped:e,emitted:t.length}}function lo(){let i=process.env.CODEX_HOME?.trim();return i||(0,Ze.join)((0,rs.homedir)(),".codex")}var Gn=(0,Ze.join)((0,rs.tmpdir)(),"agent-fleet-codex");function co(i){let t=i.name.replace(/[^A-Za-z0-9_-]+/g,"-").replace(/^-+|-+$/g,"")||"agent",e=(0,Ha.createHash)("sha1").update(i.filePath).digest("hex").slice(0,8);return(0,Ze.join)(Gn,`${t}-${e}`)}function ho(i,t){let e=`${i}.${process.pid}.${Date.now()}.tmp`;(0,ne.writeFileSync)(e,t,"utf-8"),(0,ne.renameSync)(e,i)}function uo(i,t){try{let e=(0,ne.lstatSync)(t);if(e.isSymbolicLink()){try{if((0,ne.readlinkSync)(t)===i)return}catch{}(0,ne.unlinkSync)(t)}else e.isDirectory()?(0,ne.rmSync)(t,{recursive:!0,force:!0}):(0,ne.unlinkSync)(t)}catch{}(0,ne.symlinkSync)(i,t)}function po(i,t,e=lo()){if(!(0,ne.existsSync)(e))return null;let s=co(i);(0,ne.mkdirSync)(s,{recursive:!0});for(let o of(0,ne.readdirSync)(e))o!=="rules"&&uo((0,Ze.join)(e,o),(0,Ze.join)(s,o));let a=(0,Ze.join)(s,"rules");(0,ne.mkdirSync)(a,{recursive:!0});let n=(0,Ze.join)(e,"rules");if((0,ne.existsSync)(n)){for(let o of(0,ne.readdirSync)(n))if(o.endsWith(".rules"))try{(0,ne.copyFileSync)((0,Ze.join)(n,o),(0,Ze.join)(a,`user-${o}`))}catch{}}let r=(0,Ze.join)(a,"agent-fleet.rules");return ho(r,t),{codexHome:s,rulesPath:r}}function Vn(){try{(0,ne.rmSync)(Gn,{recursive:!0,force:!0})}catch{}}var $a=new Map,ja=new Map;function Yn(){$a.clear(),ja.clear()}function Kn(i,t,e){return new Promise(s=>{let a=!1,n=r=>{a||(a=!0,s(r))};try{let r=Ge(i,["execpolicy","check","--rules",t,...e]),o=setTimeout(()=>{try{r.kill()}catch{}n(null)},8e3);r.on("error",()=>{clearTimeout(o),n(null)}),r.on("close",c=>{clearTimeout(o),n(c)})}catch{n(null)}})}function mo(i){let t=$a.get(i);if(t)return t;let e=(async()=>{let s=null;try{s=(0,ne.mkdtempSync)((0,Ze.join)((0,rs.tmpdir)(),"af-codex-probe-"));let a=(0,Ze.join)(s,"probe.rules");return(0,ne.writeFileSync)(a,`prefix_rule(pattern=["ls"], decision="allow")
11796
+ `,"utf-8"),await Kn(i,a,["ls"])===0}catch{return!1}finally{if(s)try{(0,ne.rmSync)(s,{recursive:!0,force:!0})}catch{}}})();return $a.set(i,e),e}async function fo(i,t,e){let s=`${i}:${(0,Ha.createHash)("sha1").update(e).digest("hex")}`,a=ja.get(s);if(a!==void 0)return a;let r=await Kn(i,t,["true"])===0;return ja.set(s,r),r}var zn=new Set;function is(i,t){zn.has(i)||(zn.add(i),console.warn(`Agent Fleet: ${t}`))}async function Jn(i,t){if(!(i.permissionRules.allow.length>0||i.permissionRules.deny.length>0))return null;let{rulesText:s,dropped:a}=oo(i);if(a.length>0&&is(`dropped:${i.name}:${a.map(l=>l.rule).join("|")}`,`agent "${i.name}": ${a.length} permission rule(s) can't be enforced by Codex and were ignored \u2014 ${a.map(l=>`"${l.rule}" (${l.reason})`).join("; ")}. File/network access is governed by the sandbox (Permission Mode).`),!s)return null;let n=t.codexCliPath;if(!await mo(n))return is(`unsupported:${n}`,`this Codex build doesn't support execpolicy rules; agent "${i.name}" runs with sandbox-only enforcement. Update Codex to enforce command allow/deny rules.`),null;let o;try{o=po(i,s)}catch(l){return is(`overlay-fail:${i.name}`,`couldn't build the Codex permission overlay for agent "${i.name}" (${l instanceof Error?l.message:String(l)}); falling back to sandbox-only (symlinks may be unavailable on this platform).`),null}return o?await fo(n,o.rulesPath,s)?{restore:()=>{},env:{CODEX_HOME:o.codexHome}}:(is(`invalid-rules:${i.name}`,`generated Codex rules for agent "${i.name}" failed validation; falling back to sandbox-only.`),null):(is("no-codex-home",`Codex home (~/.codex) not found; agent "${i.name}" runs with sandbox-only enforcement. Run \`codex login\` first.`),null)}function go(i){let t=i.trim();return/^(opus|sonnet|haiku|opusplan)$/i.test(t)||/claude|anthropic/i.test(t)}function yo(i){let t=i.trim().toLowerCase();return t?t==="max"?"xhigh":["low","medium","high","xhigh"].includes(t)?t:"":""}function vo(i){switch((i??"").trim()){case"plan":case"read-only":return["--sandbox","read-only"];case"acceptEdits":case"default":case"workspace-write":return["--sandbox","workspace-write"];case"danger-full-access":return["--sandbox","danger-full-access"];default:return["--dangerously-bypass-approvals-and-sandbox"]}}function bo(i){return/^[A-Za-z0-9_-]+$/.test(i)?i:`"${i.replace(/"/g,'\\"')}"`}function wo(i,t){let e=["exec","--json","--skip-git-repo-check"],s=i.modelSource==="settings"&&go(i.model);i.model&&!s&&e.push("-m",i.model);let a=yo(i.effort);a&&e.push("-c",`model_reasoning_effort="${a}"`),e.push(...vo(i.agent.permissionMode));let n=i.agent.mcpServers??[];if(n.length>0&&t&&t.length>0){let r=new Set(n.map(o=>o.trim().toLowerCase()));for(let o of t){let c=r.has(o.trim().toLowerCase());e.push("-c",`mcp_servers.${bo(o)}.enabled=${c}`)}}return i.resumeSessionId&&e.push("resume",i.resumeSessionId),e.push("-"),{args:e,stdinPayload:i.prompt}}var ko=5*6e4,os=null;async function xo(i){if(os&&os.cliPath===i&&Date.now()-os.at<ko)return os.names;let t=await new Promise(e=>{let s=!1,a=n=>{s||(s=!0,e(n))};try{let n=Ge(i,["mcp","list","--json"]),r="",o=setTimeout(()=>{try{n.kill()}catch{}a(null)},1e4);n.stdout?.on("data",c=>{r+=c.toString()}),n.on("error",()=>{clearTimeout(o),a(null)}),n.on("close",c=>{if(clearTimeout(o),c!==0)return a(null);a(So(r))})}catch{a(null)}});return os={at:Date.now(),cliPath:i,names:t},t}function So(i){try{let t=JSON.parse(i.trim()),e=s=>s.map(a=>a&&typeof a=="object"&&typeof a.name=="string"?a.name:typeof a=="string"?a:"").filter(Boolean);if(Array.isArray(t))return e(t);if(t&&typeof t=="object"){let s=t;return Array.isArray(s.servers)?e(s.servers):Object.keys(s)}}catch{}return null}function Xn(i){let t=i.trim();if(!t)return null;try{let e=JSON.parse(t);return e&&typeof e=="object"&&!Array.isArray(e)?e:null}catch{return null}}function js(i){let t=i.item;return t&&typeof t=="object"?t:null}function qa(i){let t=typeof i.type=="string"?i.type:"";if(t==="command_execution")return{tool:"shell",command:typeof i.command=="string"?i.command:void 0};if(t==="mcp_tool_call"){let e=typeof i.server=="string"?i.server:"mcp",s=typeof i.tool=="string"?i.tool:"tool";return{tool:`mcp__${e}__${s}`}}return t==="web_search"?{tool:"web_search",command:typeof i.query=="string"?i.query:void 0}:t==="file_change"?{tool:"file_change",command:(Array.isArray(i.changes)?i.changes:[]).map(a=>`${typeof a.kind=="string"?a.kind:"update"} ${typeof a.path=="string"?a.path:""}`.trim()).filter(Boolean).join(", ")||void 0}:null}function Qn(i){let t=i.usage;if(!t||typeof t!="object")return 0;let e=typeof t.input_tokens=="number"?t.input_tokens:0,s=typeof t.output_tokens=="number"?t.output_tokens:0;return e+s}function Co(i){let t=i.usage;return!t||typeof t!="object"?0:typeof t.input_tokens=="number"?t.input_tokens:0}function Wa(){return{emittedTextLengths:new Map}}function Zn(i,t){let e=typeof i.type=="string"?i.type:"",s=[];if(e==="thread.started"&&typeof i.thread_id=="string"&&i.thread_id)return s.push({kind:"session",sessionId:i.thread_id}),s;if(e==="turn.completed")return s.push({kind:"usage",contextTokens:Co(i),totalTokens:Qn(i)}),s;if(e==="turn.failed"){let a=i.error,n=a&&typeof a.message=="string"?a.message:"turn failed";return s.push({kind:"turn-failed",message:n}),s}if(e==="error"){let a=typeof i.message=="string"?i.message:"stream error";return s.push({kind:"error",message:a}),s}if(e==="item.started"||e==="item.updated"||e==="item.completed"){let a=js(i);if(!a)return s;let n=typeof a.type=="string"?a.type:"";if(n==="agent_message"){let r=typeof a.text=="string"?a.text:"",o=typeof a.id=="string"?a.id:"__single__",c=t.emittedTextLengths.get(o)??0;return r.length>c&&(s.push({kind:"text",text:r.slice(c)}),t.emittedTextLengths.set(o,r.length)),s}if(n==="error"){let r=typeof a.message=="string"?a.message:"item error";return s.push({kind:"error",message:r}),s}if(e==="item.started"){let r=qa(a);r&&s.push({kind:"tool",toolName:r.tool,command:r.command})}return s}return s}var ls={id:"codex",label:"Codex",cliPath(i){return i.codexCliPath},async buildExec(i){let e=(i.agent.mcpServers?.length??0)>0?await xo(i.settings.codexCliPath):null,{args:s,stdinPayload:a}=wo(i,e);return{cliPath:i.settings.codexCliPath,args:s,stdinPayload:a}},parseExecOutput(i,t,e){let s=[],a=[],n=[],r=0,o,c;for(let h of re(i)){let u=Xn(h);if(!u)continue;let p=typeof u.type=="string"?u.type:"";if(p==="thread.started"&&typeof u.thread_id=="string")o=u.thread_id;else if(p==="turn.completed")r+=Qn(u),c=u;else if(p==="turn.failed"){let m=u.error;n.push(m&&typeof m.message=="string"?m.message:"turn failed")}else if(p==="error")n.push(typeof u.message=="string"?u.message:"stream error");else if(p==="item.completed"){let m=js(u);if(!m)continue;let f=typeof m.type=="string"?m.type:"";if(f==="agent_message"&&typeof m.text=="string"&&m.text.trim())s.push(m.text);else if(f==="error"&&typeof m.message=="string")n.push(m.message);else{let g=qa(m);g&&a.push(g)}}}let l=s.join(`
11797
+
11798
+ `).trim();l||(l=n.join(`
11799
+ `).trim()),l||(l=t.trim()||"(no output)");let d=s[s.length-1];return{outputText:l,finalResult:d?.trim()?d:void 0,tokensUsed:r>0?r:void 0,costUsd:void 0,toolsUsed:a,concreteModel:void 0,rawJson:c,sessionId:o}},extractStreamChunk(i){let t=Xn(i);if(!t)return null;let e=typeof t.type=="string"?t.type:"";if(e==="item.completed"){let s=js(t);return s&&s.type==="agent_message"&&typeof s.text=="string"&&s.text.trim()?s.text:null}if(e==="item.started"){let s=js(t);if(!s)return null;let a=qa(s);if(a){let n=a.command?`: ${a.command.slice(0,200)}`:"";return`
11800
+ \u25B8 ${a.tool}${n}
11801
+ `}return null}if(e==="turn.failed"){let s=t.error;return`
11802
+ \u2716 ${s&&typeof s.message=="string"?s.message:"turn failed"}
11803
+ `}return null},setupPermissions(i,t,e){return Jn(t,e)}};function cs(i){let t=(i??"").trim().toLowerCase();return t==="codex"||t==="openai-codex"?"codex":"claude-code"}function ei(i){return cs(i)==="codex"?ls:Wn}var ti=new Set(["","default","subscription"]);function Dt(i,t,e){let s=za(i?.model);if(s)return{value:Ga(s),source:"task"};let a=za(t.model);if(a)return{value:Ga(a),source:"agent"};let n=za(e.defaultModel);return n?{value:Ga(n),source:"settings"}:{value:"",source:"cli-default"}}function ds(i){return!ti.has(i.trim())}function za(i){if(!i)return"";let t=i.trim();return t||""}function Ga(i){return ti.has(i)?"":i}function Hs(i,t){let e=i.wikiReferences;if(!e||e.length===0)return null;let s=[];for(let n of e){let r=t.getAgentByName(n.agent);if(!r||!r.wikiKeeper)continue;let o=r.wikiKeeper,c=o.scopeRoot?`${o.scopeRoot.replace(/\/+$/,"")}/`:"";s.push({agentName:r.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 `[[<topics-path>/<page>]]`. 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 `<inbox>/YYYY-MM-DD-<slug>.md` with a one-line note + the source. The wiki keeper files it canonically on its next ingest."),a.push("- **Do NOT write to `<topics-path>/` 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(`
11804
+ `)}function ai(i){let t=i.matchAll(/\[REMEMBER\]([\s\S]*?)\[\/REMEMBER\]/g);return Array.from(t).map(e=>e[1]?.trim()??"").filter(Boolean)}var qs=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}async buildPrompt(t,e,s){let a=[t.body.trim()];for(let r of t.skills){let o=this.repository.getSkillByName(r);if(o){let c=[o.body.trim()];o.toolsBody.trim()&&c.push(`### Tools
11805
+ ${o.toolsBody.trim()}`),o.referencesBody.trim()&&c.push(`### References
11806
+ ${o.referencesBody.trim()}`),o.examplesBody.trim()&&c.push(`### Examples
11726
11807
  ${o.examplesBody.trim()}`),a.push(`## Skill: ${o.name}
11727
- ${l.join(`
11808
+ ${c.join(`
11728
11809
 
11729
11810
  `)}`)}}if(t.skillsBody.trim()&&a.push(`## Agent Skills
11730
11811
  ${t.skillsBody.trim()}`),t.contextBody.trim()&&a.push(`## Agent Context
11731
- ${t.contextBody.trim()}`),t.memory){let i=await this.repository.getMemory(t.name);i?.body.trim()&&a.push(`## Agent Memory
11732
- ${i.body.trim()}`)}let n=_s(t,this.repository);return n&&a.push(n),a.push(`## Task
11812
+ ${t.contextBody.trim()}`),t.memory){let r=await this.repository.getMemory(t.name);r?.body.trim()&&a.push(`## Agent Memory
11813
+ ${r.body.trim()}`)}let n=Hs(t,this.repository);return n&&a.push(n),a.push(`## Task
11733
11814
  ${(s??e.body).trim()}`),a.filter(Boolean).join(`
11734
11815
 
11735
- `)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),i=(0,mn.randomUUID)(),o=Ot(e,t,this.settings),l=a!=null,c=["-p",n,"--output-format",l?"stream-json":"json"];l&&c.push("--verbose"),Cs(o.value)&&c.push("--model",o.value);let d=e.effort||t.effort;d&&c.push("--effort",d);let h=t.cwd??this.repository.getVaultBasePath()??".",u=t.permissionRules.allow.length>0||t.permissionRules.deny.length>0,p=t.permissionMode?.trim(),m=!!p&&p!=="default",f=(t.mcpServers?.length??0)>0,v=u||m||f,k=null,w=null;if(v){let x=(0,ba.join)(h,".claude");if(k=(0,ba.join)(x,"settings.local.json"),(0,Ke.existsSync)(x)||(0,Ke.mkdirSync)(x,{recursive:!0}),(0,Ke.existsSync)(k))try{w=(0,Ke.readFileSync)(k,"utf-8")}catch{w=null}let T={};if(m&&(T.defaultMode=p),u||f){let C=[...t.permissionRules.allow];if(f)for(let L of t.mcpServers??[]){let E=L.replace(/[\s.]+/g,"_");C.push(`mcp__${E}`)}T.permissions={allow:C,deny:t.permissionRules.deny}}(0,Ke.writeFileSync)(k,JSON.stringify(T,null,2)+`
11736
- `,"utf-8")}let y=Date.now(),g=()=>{if(k)try{w!==null?(0,Ke.writeFileSync)(k,w,"utf-8"):(0,Ke.existsSync)(k)&&(0,Ke.unlinkSync)(k)}catch{}};try{return await new Promise((x,T)=>{let C=ut(this.settings.claudeCliPath,c,{cwd:h,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.runningProcesses.set(t.name,C);let L="",E="",S=!1,I=setTimeout(()=>{S=!0,C.kill()},t.timeout*1e3);C.stdout.on("data",A=>{let O=A.toString();if(L+=O,l&&a)for(let R of pe(O)){let z=R.trim();if(z)try{let N=JSON.parse(z),j=this.extractStreamContent(N);j&&a(j)}catch{}}}),C.stderr.on("data",A=>{E+=A.toString()}),C.on("error",A=>{clearTimeout(I),T(A)}),C.on("close",A=>{clearTimeout(I),this.runningProcesses.delete(t.name);let O=L.trim(),R;if(l){let V=pe(O);for(let G=V.length-1;G>=0;G--){let $=V[G]?.trim();if($)try{let H=JSON.parse($);if(H&&typeof H=="object"){R=H;break}}catch{}}}else if(O.startsWith("{")||O.startsWith("["))try{R=JSON.parse(O)}catch{R=void 0}let z=gn(R)??"";if(!z&&l){let V=[];for(let G of pe(O)){let $=G.trim();if($)try{let H=JSON.parse($);if(H.type==="assistant"&&H.message?.content)for(let se of H.message.content)se.type==="text"&&se.text&&V.push(se.text);else H.type==="result"&&typeof H.result=="string"&&V.push(H.result)}catch{}}z=V.join(`
11737
- `).trim()}z||(z=E.trim()||"(no output)");let N=wa(R),j=yn(R),oe=vn(R),te=ka(R),ee=fn(R);if((!te||!ee)&&l)for(let V of pe(O)){let G=V.trim();if(G)try{let $=JSON.parse(G);if(!te){let H=ka($);H&&(te=H)}if(!ee){let H=fn($);H&&(ee=H)}if(te&&ee)break}catch{}}x({runId:i,prompt:n,exitCode:A,durationSeconds:Math.max(1,Math.round((Date.now()-y)/1e3)),stdout:L,stderr:E,outputText:z,rawJson:R,tokensUsed:j,costUsd:oe,toolsUsed:N,timedOut:S,resolvedModel:o.value,modelSource:o.source,concreteModel:te,finalResult:ee})})})}finally{g()}}};var ss=Ve(require("crypto")),wn=Ve(require("https")),Es=Ve(require("http")),qe=Ve(require("fs")),At=Ve(require("path"));var Ps=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=At.join(Xt(),"settings.local.json"),a={};try{let o=qe.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),qe.writeFileSync(s,JSON.stringify(a,null,2)),this.cache){let o=this.cache.find(l=>l.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 l=await this.startOAuthCallbackServer(),c=`http://localhost:${l.port}/callback`;try{let d=await this.registerOAuthClient(n.registration_endpoint,c),h=ss.randomBytes(32).toString("base64url"),u=ss.createHash("sha256").update(h).digest("base64url"),p=ss.randomBytes(16).toString("hex"),m=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:c,state:p,resource:o}),f=i??n.scopes_supported;f?.length&&m.set("scope",f.join(" "));let v=new URL(n.authorization_endpoint);for(let[g,x]of m)v.searchParams.set(g,x);let k=v.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",k),Za(k);let w=await l.waitForCode(p,18e4),y=await this.exchangeOAuthCode(n.token_endpoint,w,c,d,h);this.injectTokenIntoClaudeConfig(t,y.access_token),this.authManager&&this.authManager.storeOAuthToken(t,{accessToken:y.access_token,refreshToken:y.refresh_token,expiresAt:y.expires_in?Date.now()+y.expires_in*1e3:void 0,tokenEndpoint:n.token_endpoint,clientId:d,resource:o}),this.clearNeedsAuthCache(t),this.enableServerInClaudeConfig(t),this.invalidateCache()}finally{l.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),l=o.access_token;l&&(this.authManager.storeOAuthToken(s,{accessToken:l,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,l),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 l=new URL(n),c=l.pathname==="/"?"":l.pathname,d=`${l.origin}/.well-known/oauth-authorization-server${c}`;try{let h=await this.oauthFetch(d,"GET");if(h.status===200)return{metadata:JSON.parse(h.body),resourceScopes:i,resource:o}}catch{}if(c){let h=`${l.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=Es.createServer((n,i)=>{let o=new URL(n.url??"/","http://localhost");if(o.pathname!=="/callback"){i.writeHead(404),i.end();return}let l=o.searchParams.get("error");if(l){let h=o.searchParams.get("error_description")??l;i.writeHead(200,{"Content-Type":"text/html"}),i.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2>Authorization Failed</h2><p>"+h+"</p><p style='color:#888'>You can close this tab.</p></body></html>"),e?.(new Error(`OAuth denied: ${h}`));return}let c=o.searchParams.get("code"),d=o.searchParams.get("state");if(!c||!d){i.writeHead(400),i.end("Missing code or state");return}i.writeHead(200,{"Content-Type":"text/html"}),i.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2 style='color:#22c55e'>Authenticated!</h2><p>You can close this tab and return to Obsidian.</p><script>setTimeout(()=>window.close(),2000)</script></body></html>"),t?.(c,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,l)=>{let c=setTimeout(()=>{l(new Error("Authentication timed out \u2014 complete authorization in your browser and try again."))},i);t=(d,h)=>{clearTimeout(c),h!==n?l(new Error("OAuth state mismatch \u2014 possible CSRF attack")):o(d)},e=d=>{clearTimeout(c),l(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 l=JSON.parse(o.body);if(!l.access_token)throw new Error("Token response missing access_token");return l}readTokenFromClaudeConfig(t){let e=Qt();try{let s=qe.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=Qt();try{let a=qe.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}`,qe.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=At.join(Xt(),"mcp-needs-auth-cache.json");try{let s=qe.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&&qe.writeFileSync(e,JSON.stringify(a))}catch{}}oauthFetch(t,e,s,a){return new Promise((n,i)=>{let o=new URL(t),l=o.protocol==="https:",c={Accept:"application/json"};s&&(c["Content-Type"]=a??"application/json",c["Content-Length"]=String(Buffer.byteLength(s)));let d={hostname:o.hostname,port:o.port||(l?443:80),path:o.pathname+o.search,method:e,headers:c},u=(l?wn:Es).request(d,m=>{let f="";m.on("data",v=>{f+=v.toString()}),m.on("end",()=>{let v={};for(let[k,w]of Object.entries(m.headers))typeof w=="string"?v[k]=w:Array.isArray(w)&&(v[k]=w.join(", "));n({status:m.statusCode??0,body:f,headers:v})})});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;i<e.length;i++){let o=e[i];this.emitProgress({phase:"details",current:i+1,total:e.length,serverName:o.name});try{let l=await this.runCliArgs(["mcp","get",o.name]),c=this.mergeGetOutput(o,l);c.description||(c.description=this.getPluginDescription(c.name)),s.push(c)}catch{s.push(o)}}if(this.authManager)for(let i of s)i.status==="needs-auth"&&this.authManager.hasToken(i.name)&&(i.status="connected");let a=s.filter(i=>i.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 l=await this.probeStdioServer(i.command,i.args);l.description&&!i.description&&(i.description=l.description),o=l.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(l=>l.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=At.join(Xt(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=qe.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=la(a,{env:{...process.env}}),i="",o,l=[],c=!1,d=!1,h=!1,u=()=>{c||(c=!0,n.kill(),s({description:o,tools:l}))},p=setTimeout(u,1e4);n.stdout.on("data",m=>{i+=m.toString();let f=pe(i);i=f.pop()??"";for(let v of f){let k=v.trim();if(k){try{let w=JSON.parse(k);if(w.id===1&&w.result){o=w.result.instructions??w.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+`
11816
+ `)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),r=(0,si.randomUUID)(),o=Dt(e,t,this.settings),c=a!=null,l=ei(t.adapter),d=await l.buildExec({prompt:n,model:ds(o.value)?o.value:"",modelSource:o.source,effort:e.effort||t.effort||"",agent:t,settings:this.settings,streaming:c}),h=t.cwd??this.repository.getVaultBasePath()??".",u=await l.setupPermissions(h,t,this.settings),p=Date.now();try{return await new Promise((m,f)=>{let g=Ge(d.cliPath,d.args,{cwd:h,env:{...process.env,AWS_REGION:this.settings.awsRegion,...u?.env??{}}});if(d.stdinPayload!==void 0)try{g.stdin?.write(d.stdinPayload),g.stdin?.end()}catch(x){g.kill(),f(x instanceof Error?x:new Error(String(x)));return}this.runningProcesses.set(t.name,g);let b="",w="",v=!1,y=setTimeout(()=>{v=!0,g.kill()},t.timeout*1e3);g.stdout.on("data",x=>{let S=x.toString();if(b+=S,c&&a)for(let _ of re(S)){let M=l.extractStreamChunk(_);M&&a(M)}}),g.stderr.on("data",x=>{w+=x.toString()}),g.on("error",x=>{clearTimeout(y),f(x)}),g.on("close",x=>{clearTimeout(y),this.runningProcesses.delete(t.name);let S=l.parseExecOutput(b,w,c);m({runId:r,prompt:n,exitCode:x,durationSeconds:Math.max(1,Math.round((Date.now()-p)/1e3)),stdout:b,stderr:w,outputText:S.outputText,rawJson:S.rawJson,tokensUsed:S.tokensUsed,costUsd:S.costUsd,toolsUsed:S.toolsUsed,timedOut:v,resolvedModel:o.value,modelSource:o.source,concreteModel:S.concreteModel,finalResult:S.finalResult})})})}finally{u?.restore()}}};var hs=Je(require("crypto")),ni=Je(require("https")),Ws=Je(require("http")),ze=Je(require("fs")),Rt=Je(require("path"));var zs=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=Rt.join(es(),"settings.local.json"),a={};try{let o=ze.readFileSync(s,"utf8");a=JSON.parse(o)}catch{}let n=a.disabledMcpjsonServers??[],r=this.toInternalName(t);if(e?(a.disabledMcpjsonServers=n.filter(o=>o!==r),this.enableServerInClaudeConfig(t)):n.includes(r)||(n.push(r),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:r,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=hs.randomBytes(32).toString("base64url"),u=hs.createHash("sha256").update(h).digest("base64url"),p=hs.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=r??n.scopes_supported;f?.length&&m.set("scope",f.join(" "));let g=new URL(n.authorization_endpoint);for(let[y,x]of m)g.searchParams.set(y,x);let b=g.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",b),Cn(b);let w=await c.waitForCode(p,18e4),v=await this.exchangeOAuthCode(n.token_endpoint,w,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(),r=await this.oauthFetch(a.tokenEndpoint,"POST",n,"application/x-www-form-urlencoded");if(r.status===200){let o=JSON.parse(r.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,r,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)&&(r=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:r,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:r,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=Ws.createServer((n,r)=>{let o=new URL(n.url??"/","http://localhost");if(o.pathname!=="/callback"){r.writeHead(404),r.end();return}let c=o.searchParams.get("error");if(c){let h=o.searchParams.get("error_description")??c;r.writeHead(200,{"Content-Type":"text/html"}),r.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2>Authorization Failed</h2><p>"+h+"</p><p style='color:#888'>You can close this tab.</p></body></html>"),e?.(new Error(`OAuth denied: ${h}`));return}let l=o.searchParams.get("code"),d=o.searchParams.get("state");if(!l||!d){r.writeHead(400),r.end("Missing code or state");return}r.writeHead(200,{"Content-Type":"text/html"}),r.end("<html><body style='font-family:system-ui;text-align:center;padding:60px'><h2 style='color:#22c55e'>Authenticated!</h2><p>You can close this tab and return to Obsidian.</p><script>setTimeout(()=>window.close(),2000)</script></body></html>"),t?.(l,d)});return{port:await new Promise((n,r)=>{s.listen(0,"127.0.0.1",()=>{let o=s.address();if(!o||typeof o=="string"){r(new Error("Failed to bind callback server"));return}n(o.port)}),s.on("error",r)}),waitForCode:(n,r)=>new Promise((o,c)=>{let l=setTimeout(()=>{c(new Error("Authentication timed out \u2014 complete authorization in your browser and try again."))},r);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 r=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:s,client_id:a,code_verifier:n}).toString(),o=await this.oauthFetch(t,"POST",r,"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=ts();try{let s=ze.readFileSync(e,"utf8"),r=JSON.parse(s).mcpServers?.[t]?.headers,o=r?.Authorization??r?.authorization;if(o?.startsWith("Bearer "))return o.slice(7)}catch{}}injectTokenIntoClaudeConfig(t,e){let s=ts();try{let a=ze.readFileSync(s,"utf8"),n=JSON.parse(a),r=n.mcpServers;if(r?.[t]){let o=r[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=Rt.join(es(),"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 r=`claude.ai ${t}`;r in a&&(delete a[r],n=!0),n&&ze.writeFileSync(e,JSON.stringify(a))}catch{}}oauthFetch(t,e,s,a){return new Promise((n,r)=>{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?ni:Ws).request(d,m=>{let f="";m.on("data",g=>{f+=g.toString()}),m.on("end",()=>{let g={};for(let[b,w]of Object.entries(m.headers))typeof w=="string"?g[b]=w:Array.isArray(w)&&(g[b]=w.join(", "));n({status:m.statusCode??0,body:f,headers:g})})});u.on("error",r);let p=setTimeout(()=>{u.destroy(),r(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 r=0;r<e.length;r++){let o=e[r];this.emitProgress({phase:"details",current:r+1,total:e.length,serverName:o.name});try{let c=await this.runCliArgs(["mcp","get",o.name]),l=this.mergeGetOutput(o,c);l.description||(l.description=this.getPluginDescription(l.name)),s.push(l)}catch{s.push(o)}}if(this.authManager)for(let r of s)r.status==="needs-auth"&&this.authManager.hasToken(r.name)&&(r.status="connected");let a=s.filter(r=>r.enabled&&(r.status==="connected"||r.status==="needs-auth"&&(r.type==="http"||r.type==="sse")&&r.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 r=>{try{let o=[];if(r.type==="stdio"&&r.command){let c=await this.probeStdioServer(r.command,r.args);c.description&&!r.description&&(r.description=c.description),o=c.tools}else(r.type==="http"||r.type==="sse")&&r.url&&(o=await this.probeHttpServer(r));o.length>0&&(r.toolDetails=o,r.tools=o.map(c=>c.name),r.status==="needs-auth"&&(r.status="connected"))}catch(o){console.warn(`McpManager: probe failed for ${r.name}:`,o)}})));let n=s.reduce((r,o)=>r+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=Rt.join(es(),"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=Aa(a,{env:{...process.env}}),r="",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=>{r+=m.toString();let f=re(r);r=f.pop()??"";for(let g of f){let b=g.trim();if(b){try{let w=JSON.parse(b);if(w.id===1&&w.result){o=w.result.instructions??w.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+`
11738
11817
  `),n.stdin.write(JSON.stringify({jsonrpc:"2.0",id:2,method:"tools/list"})+`
11739
- `)}catch{clearTimeout(p),u();return}}else if(w.id===2&&w.result){for(let y of w.result.tools??[])l.push({name:y.name,description:y.description,inputSchema:y.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"}}})+`
11740
- `)}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=[At.join(oa(),".openclaw","workspace",".env"),At.join(oa(),".env")];for(let i of n)try{let o=qe.readFileSync(i,"utf8");for(let l of pe(o)){let c=l.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(c){let d=c[1],h=c[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),l=new URL(t),c=l.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:l.hostname,port:l.port||(c?443:80),path:l.pathname+l.search,method:"POST",headers:d},p=(c?wn:Es).request(h,f=>{let v="";f.on("data",k=>{v+=k.toString()}),f.on("end",()=>{let k=f.headers["mcp-session-id"];if((f.headers["content-type"]??"").includes("text/event-stream")){for(let y of pe(v))if(y.startsWith("data: "))try{let g=JSON.parse(y.slice(6));k&&(g._sessionId=k),n(g);return}catch{}n(null)}else try{let y=JSON.parse(v);k&&(y._sessionId=k),n(y)}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=la(a,{env:{...process.env}}),i="",o="";n.stdout.on("data",l=>{i+=l.toString()}),n.stderr.on("data",l=>{o+=l.toString()}),n.on("error",s),n.on("close",l=>{l!==0&&!i.trim()?s(new Error(o||`Process exited with code ${l}`)):e(i)})})}runCliArgs(t){return new Promise((e,s)=>{let a=ut(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=At.join(Xt(),"settings.local.json");try{let s=qe.readFileSync(e,"utf8"),a=JSON.parse(s);for(let n of a.disabledMcpjsonServers??[])t.add(n)}catch{}try{let s=qe.readFileSync(Qt(),"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=Qt();try{let s=qe.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 l=o.disabledMcpServers.indexOf(t);l!==-1&&(o.disabledMcpServers.splice(l,1),i=!0)}i&&qe.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of pe(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(),l=n.slice(i+2),c=l.lastIndexOf(" - ");if(c===-1)continue;let d=l.slice(0,c).trim(),h=l.slice(c+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 w=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=w.endsWith("/sse")?"sse":"http",m=w}else d&&(p="stdio",f=d);let v=this.toInternalName(o),k=!s.has(v);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 pe(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 ur={"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 * *"},Ds=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 ur[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()<e&&await this.enqueue({task:s,reason:"catch-up"})}async enqueue(t){this.queue.push(t),await this.processQueue()}pauseAll(){this.paused=!0,this.jobs.forEach(t=>t.pause())}resumeAll(){this.paused=!1,this.jobs.forEach(t=>t.resume()),this.processQueue()}getQueueSize(){return this.queue.length}async processQueue(){if(!this.paused)for(;this.activeRuns<this.maxConcurrentRuns&&this.queue.length>0;){let t=this.queue.shift();if(!t)return;this.activeRuns+=1,this.callbacks.onTaskTriggered(t).finally(async()=>{this.activeRuns-=1,await this.processQueue()})}}};var as=class{constructor(t,e){this.repository=t;this.settings=e;this.executor=new As(e,t),this.mcpManager=new Ps(e),this.scheduler=new Ds(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=[];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(),this.recentRuns=await this.repository.listRecentRuns();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(),this.recentRuns=await this.repository.listRecentRuns(),this.emitStatusChange()}getSnapshot(){return this.snapshot}getRecentRuns(){return this.recentRuns}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 l=new Date(o.started);return`${l.getFullYear()}-${e(l.getMonth()+1)}-${e(l.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(),this.recentRuns=await this.repository.listRecentRuns(),this.emitStatusChange()}async handleVaultDelete(t){this.repository.removeFile(t),this.snapshot=this.repository.getSnapshot(),await this.rebuildSchedules(),this.recentRuns=await this.repository.listRecentRuns(),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),this.recentRuns=await this.repository.listRecentRuns(),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()<t&&await this.repository.trashFile(s.filePath)}async rebuildSchedules(){this.scheduler.pauseAll();for(let t of this.snapshot.tasks)this.scheduler.unregisterTask(t.taskId);this.scheduler.setMaxConcurrentRuns(this.settings.maxConcurrentRuns),await this.scheduler.loadTasks(this.snapshot.tasks.filter(t=>this.repository.getAgentByName(t.agent)?.enabled!==!1)),this.scheduler.resumeAll(),this.registerHeartbeats()}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),l=i?"cancelled":this.resolveRunStatus(n,o),c={runId:n.runId,agent:s.name,task:t.taskId,status:l,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(c);if(await this.repository.updateTaskRunMetadata(t,{lastRun:a,runCount:t.runCount+1}),s.memory){let u=bn(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&&c.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,c.output)}catch(u){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,u)}i&&(c.output="Task was manually stopped."),this.recentRuns=await this.repository.listRecentRuns(),this.runtimeState.set(s.name,{status:i||l==="success"?"idle":l==="pending_approval"?"pending":"error",currentRunId:n.runId,lastRun:{...c,filePath:d}}),i||this.notify(c)}catch(n){let i=this.consumeAborted(s.name),o=i?"cancelled":"failure",l=Ot(t,s,this.settings),c={runId:(0,kn.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:l.value||s.model,modelSource:l.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(c);this.recentRuns=await this.repository.listRecentRuns(),this.runtimeState.set(s.name,{status:i?"idle":"error",lastRun:{...c,filePath:d}}),i||this.notify(c)}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=(pe(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 xn.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Rs=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-s<t&&e.set(a,n);return e}};var ct=require("obsidian");var Ls=require("crypto"),tt=require("obsidian");function Is(){return(0,Ls.randomUUID)()}function Sn(r){let t=`${r.timestamp}|${r.content.slice(0,80)}`;return(0,Ls.createHash)("sha1").update(t).digest("hex").slice(0,16)}var Nt=class r{constructor(t,e,s,a,n){this.agent=t;this.settings=e;this.repository=s;this.vault=a,this.channelName=n?.channelName,this.conversationId=n?.conversationId,this.channelContext=n?.channelContext,this.threadAnchorId=n?.threadAnchorId,this.parentSession=n?.parentSession}messages=[];isStreaming=!1;isProcessAlive=!1;get pendingTurnCount(){return this.pendingTurns}lastActiveAt=Date.now();process=null;claudeSessionId=null;vault;stdoutBuffer="";processListeners=null;basePromptSent=!1;channelName;conversationId;channelContext;threadAnchorId;parentSession;threadAnchorIndex;threads=new Map;threadIndex={};activeOnEvent=null;turnResponseText="";turnToolCalls=[];pendingTurns=0;turnResolve=null;turnReject=null;needsCompactBeforeNextTurn=!1;lastCompactTriggerAt=0;static WATCHDOG_MS=300*1e3;watchdogTimer=null;armWatchdog(){this.clearWatchdog(),this.watchdogTimer=setTimeout(()=>{if(this.watchdogTimer=null,!this.isStreaming)return;this.activeOnEvent?.({type:"error",content:"",errorMessage:`no response from the CLI for ${Math.round(r.WATCHDOG_MS/6e4)} minutes \u2014 giving up`});let t=new Error("Watchdog timeout");this.handleProcessError(t);try{this.process?.kill()}catch{}},r.WATCHDOG_MS)}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)}get isThread(){return!!this.threadAnchorId}async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof tt.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:Sn(i)}),this.claudeSessionId=n.sessionId??null,this.threadAnchorIndex=n.anchorIndex,this.claudeSessionId&&(this.basePromptSent=!0),!0):!1}let a=JSON.parse(s);if(a.messages?.length>0)return this.messages=a.messages.map(n=>n.id?n:{...n,id:Sn(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,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 tt.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 tt.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=ke(this.conversationId)||"conversation";return(0,tt.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,tt.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,tt.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,tt.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=ke(this.conversationId)||"conversation";return(0,tt.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,tt.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,tt.normalizePath)(`${t}/${this.agent.name}-chat.json`)}async ensureProcess(){if(this.process&&this.isProcessAlive)return;let t=["--input-format","stream-json","--output-format","stream-json","--verbose"];this.claudeSessionId&&(t.push("--resume",this.claudeSessionId),this.basePromptSent=!0);let e=Ot(null,this.agent,this.settings);Cs(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()??".",n=ut(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=pe(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 l=typeof o.input_tokens=="number"?o.input_tokens:0,c=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=l+c+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 l=t.modelUsage;if(l)for(let c of Object.values(l)){let d=c;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*100<t||Date.now()-this.lastCompactTriggerAt<3e4||(this.needsCompactBeforeNextTurn=!0)}handleTurnEnd(){this.lastActiveAt=Date.now(),this.turnResponseText.trim()&&this.messages.push({id:Is(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...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);let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:Is(),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:Is(),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()}
11818
+ `)}catch{clearTimeout(p),u();return}}else if(w.id===2&&w.result){for(let v of w.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"}}})+`
11819
+ `)}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 r=await this.httpRequest(s,e,{jsonrpc:"2.0",id:2,method:"tools/list"},n),o=[],d=r?.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 r=this.authManager.getToken(t.name);if(r)return r}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 r of a){let o=process.env[r];if(o)return o}let n=[Rt.join(_a(),".openclaw","workspace",".env"),Rt.join(_a(),".env")];for(let r of n)try{let o=ze.readFileSync(r,"utf8");for(let c of re(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,r)=>{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?ni:Ws).request(h,f=>{let g="";f.on("data",b=>{g+=b.toString()}),f.on("end",()=>{let b=f.headers["mcp-session-id"];if((f.headers["content-type"]??"").includes("text/event-stream")){for(let v of re(g))if(v.startsWith("data: "))try{let y=JSON.parse(v.slice(6));b&&(y._sessionId=b),n(y);return}catch{}n(null)}else try{let v=JSON.parse(g);b&&(v._sessionId=b),n(v)}catch{n(null)}})});p.on("error",r);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=Aa(a,{env:{...process.env}}),r="",o="";n.stdout.on("data",c=>{r+=c.toString()}),n.stderr.on("data",c=>{o+=c.toString()}),n.on("error",s),n.on("close",c=>{c!==0&&!r.trim()?s(new Error(o||`Process exited with code ${c}`)):e(r)})})}runCliArgs(t){return new Promise((e,s)=>{let a=Ge(this.settings.claudeCliPath,t,{env:{...process.env}}),n="",r="";a.stdout.on("data",o=>{n+=o.toString()}),a.stderr.on("data",o=>{r+=o.toString()}),a.on("error",s),a.on("close",o=>{o!==0&&!n.trim()?s(new Error(r||`Process exited with code ${o}`)):e(n)})})}getDisabledServers(){let t=new Set,e=Rt.join(es(),"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(ts(),"utf8"),n=JSON.parse(s).projects;if(n){for(let r of Object.values(n))if(r&&Array.isArray(r.disabledMcpServers))for(let o of r.disabledMcpServers)t.add(o)}}catch{}return t}enableServerInClaudeConfig(t){let e=ts();try{let s=ze.readFileSync(e,"utf8"),a=JSON.parse(s),n=a.projects;if(!n)return;let r=!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),r=!0)}r&&ze.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of re(t)){let n=a.trim();if(!n||n.startsWith("Checking"))continue;let r=n.indexOf(": ");if(r===-1)continue;let o=n.slice(0,r).trim(),c=n.slice(r+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 w=d.replace(/\s+\(\w+\)\s*$/,"").trim();p=w.endsWith("/sse")?"sse":"http",m=w}else d&&(p="stdio",f=d);let g=this.toInternalName(o),b=!s.has(g);e.push({name:o,type:p,status:u,scope:"unknown",enabled:b,url:m,command:f,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of re(e)){let n=a.trim();if(n.startsWith("Type:")){let r=n.slice(5).trim().toLowerCase();r==="stdio"?s.type="stdio":r==="http"?s.type="http":r==="sse"&&(s.type="sse")}else if(n.startsWith("Scope:")){let r=n.slice(6).trim().toLowerCase();r.includes("user")?s.scope="user":r.includes("project")&&(s.scope="project")}else if(n.startsWith("Command:")){let r=n.slice(8).trim();r&&(s.command=r)}else if(n.startsWith("Args:")){let r=n.slice(5).trim();r&&(s.args=r)}}return s}};var To={"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 * *"},Gs=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 To[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 de(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 de(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()<e&&await this.enqueue({task:s,reason:"catch-up"})}async enqueue(t){this.queue.push(t),await this.processQueue()}pauseAll(){this.paused=!0,this.jobs.forEach(t=>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.activeRuns<this.maxConcurrentRuns&&this.queue.length>0;){let t=this.queue.shift();if(!t)return;this.activeRuns+=1,this.callbacks.onTaskTriggered(t).finally(async()=>{this.activeRuns-=1,await this.processQueue()})}}};var us=class i{constructor(t,e){this.repository=t;this.settings=e;this.executor=new qs(e,t),this.mcpManager=new zs(e),this.scheduler=new Gs(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()-(i.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,r=this.recentRuns.flatMap(o=>o.approvals??[]).filter(o=>o.status==="pending").length;return{running:n,pending:r,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()<t&&await this.repository.trashFile(s.filePath)}async rebuildSchedules(){this.scheduler.pauseAll();for(let t of this.snapshot.tasks)this.scheduler.unregisterTask(t.taskId);this.scheduler.setMaxConcurrentRuns(this.settings.maxConcurrentRuns),await this.scheduler.loadTasks(this.snapshot.tasks.filter(t=>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 de(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)}),r=this.consumeAborted(s.name),o=r?[]:this.buildApprovals(s,n.toolsUsed),c=r?"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=ai(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")&&!r&&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)}r&&(l.output="Task was manually stopped."),await this.refreshRunCaches(),this.runtimeState.set(s.name,{status:r||c==="success"?"idle":c==="pending_approval"?"pending":"error",currentRunId:n.runId,lastRun:{...l,filePath:d}}),r||this.notify(l)}catch(n){let r=this.consumeAborted(s.name),o=r?"cancelled":"failure",c=Dt(t,s,this.settings),l={runId:(0,ii.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:r?-1:1,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:e??t.body,output:r?"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:r?"idle":"error",lastRun:{...l,filePath:d}}),r||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=(re(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 ri.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Vs=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-s<t&&e.set(a,n);return e}};var ut=require("obsidian");var Ys=require("crypto"),it=require("obsidian");function ps(){return(0,Ys.randomUUID)()}function oi(i){let t=`${i.timestamp}|${i.content.slice(0,80)}`;return(0,Ys.createHash)("sha1").update(t).digest("hex").slice(0,16)}var jt=class i{constructor(t,e,s,a,n){this.agent=t;this.settings=e;this.repository=s;this.vault=a,this.channelName=n?.channelName,this.conversationId=n?.conversationId,this.channelContext=n?.channelContext,this.inAppConversationId=n?.inAppConversationId,this.threadAnchorId=n?.threadAnchorId,this.parentSession=n?.parentSession}messages=[];isStreaming=!1;isProcessAlive=!1;settingsState=null;get pendingTurnCount(){return this.pendingTurns}lastActiveAt=Date.now();process=null;claudeSessionId=null;vault;stdoutBuffer="";processListeners=null;basePromptSent=!1;codexTurnState=null;codexQueue=[];codexResumeAttempted=!1;codexTurnErrors=[];codexStderr="";codexPermState=null;get isCodex(){return cs(this.agent.adapter)==="codex"}channelName;conversationId;channelContext;inAppConversationId;conversationName="";threadAnchorId;parentSession;threadAnchorIndex;threads=new Map;threadIndex={};activeOnEvent=null;turnResponseText="";turnToolCalls=[];pendingTurns=0;turnResolve=null;turnReject=null;needsCompactBeforeNextTurn=!1;lastCompactTriggerAt=0;static WATCHDOG_FALLBACK_MINUTES=10;watchdogTimer=null;getWatchdogMs(){let t=this.settings.chatWatchdogMinutes;return(typeof t=="number"&&t>0?t:i.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 it.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(r=>r.id?r:{...r,id:oi(r)}),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:oi(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 it.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 r of a)if(n=n?`${n}/${r}`:r,!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 it.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 s=this.settings.fleetFolder,a=ge(this.conversationId)||"conversation";return(0,it.normalizePath)(`${s}/channels/${this.channelName}/sessions/${a}.json`)}if(!this.inAppConversationId)throw new Error("ChatSession requires inAppConversationId (or channel options) to resolve a file path");let t=ge(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let s=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,it.normalizePath)(`${s}/conversations/${t}.json`)}let e=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,it.normalizePath)(`${e}/${this.agent.name}-conversations/${t}.json`)}getThreadFilePath(t){let e=this.getParentChatFilePath(),s=e.replace(/\/[^/]+$/,""),a=e.slice(s.length+1).replace(/\.json$/,"");return(0,it.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let t=this.settings.fleetFolder,e=ge(this.conversationId)||"conversation";return(0,it.normalizePath)(`${t}/channels/${this.channelName}/sessions/${e}.json`)}if(this.inAppConversationId){let t=ge(this.inAppConversationId)||"conversation";if(this.agent.isFolder){let s=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,it.normalizePath)(`${s}/conversations/${t}.json`)}let e=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,it.normalizePath)(`${e}/${this.agent.name}-conversations/${t}.json`)}throw new Error("ChatSession requires inAppConversationId (or channel options) to resolve a parent file path")}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=Dt(null,this.agent,this.settings);ds(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=Us(a,this.agent);let n=Ge(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:r=>this.handleStdout(r),onStderr:()=>{},onError:r=>this.handleProcessError(r),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=re(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(this.isCodex){this.handleCodexEvent(t);return}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);e&&this.dispatchStreamEvent(e)}dispatchStreamEvent(t){if(t.type==="text"){let e=this.turnResponseText.length===0;this.turnResponseText+=t.content,this.setCurrentTool(void 0),e&&this.turnResponseText.length>0&&this.emitActivity()}else t.type==="tool_use"&&t.toolName&&(this.turnToolCalls.push({name:t.toolName,command:t.content||void 0}),this.setCurrentTool(t.toolName));this.activeOnEvent?.(t)}handleCodexEvent(t){this.codexTurnState||(this.codexTurnState=Wa());let e=Zn(t,this.codexTurnState);for(let s of e)switch(s.kind){case"session":this.claudeSessionId=s.sessionId;break;case"text":this.dispatchStreamEvent({type:"text",content:s.text});break;case"tool":this.dispatchStreamEvent({type:"tool_use",content:s.command?s.command.slice(0,150):"",toolName:s.toolName});break;case"usage":{s.contextTokens>0&&s.contextTokens!==this.stats.contextTokensUsed&&(this.stats.contextTokensUsed=s.contextTokens),this.stats.turnCount+=1,this.emitStats();break}case"turn-failed":case"error":this.codexTurnErrors.push(s.message),this.activeOnEvent?.({type:"error",content:"",errorMessage:s.message});break}}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,r=s||n;if(r&&r!==this.stats.concreteModel&&(this.stats.concreteModel=r,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(){if(this.isCodex)return;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*100<t||Date.now()-this.lastCompactTriggerAt<3e4||(this.needsCompactBeforeNextTurn=!0)}handleTurnEnd(){this.lastActiveAt=Date.now(),this.turnResponseText.trim()&&this.messages.push({id:ps(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...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.isCodex){let e=this.codexQueue.shift();if(e!==void 0){this.armWatchdog(),this.startCodexTurn(e).catch(s=>{this.handleProcessError(s instanceof Error?s:new Error(String(s)))});return}this.pendingTurns=0}if(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.codexQueue=[],this.clearWatchdog(),this.setStreaming(!1),Pt(this.settingsState),this.settingsState=null,this.codexPermState?.restore(),this.codexPermState=null;let e=this.turnReject;this.turnResolve=null,this.turnReject=null,e?.(t)}handleProcessClose(){if(this.isProcessAlive=!1,this.process=null,Pt(this.settingsState),this.settingsState=null,this.turnResolve){let t={text:this.turnResponseText,toolCalls:[...this.turnToolCalls]};this.turnResponseText.trim()&&this.messages.push({id:ps(),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:ps(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:a&&a.length>0?a:void 0});let n=s??t;if(this.basePromptSent||(n=`${await this.buildBasePrompt()}
11741
11820
 
11742
11821
  ## Task
11743
- ${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=l=>{let c=JSON.stringify({type:"user",message:{role:"user",content:l}});this.process.stdin.write(c+`
11744
- `)};try{i&&o("/compact"),o(n)}catch(l){throw this.pendingTurns=0,this.setStreaming(!1),new Error(`Failed to write to Claude process stdin: ${l instanceof Error?l.message:String(l)}`)}return new Promise((l,c)=>{this.turnResolve=l,this.turnReject=c})}scheduleCompact(){this.needsCompactBeforeNextTurn=!0}injectMessage(t,e,s){if(!this.process||!this.isProcessAlive)return;this.messages.push({id:Is(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:s&&s.length>0?s:void 0});let n=JSON.stringify({type:"user",message:{role:"user",content:e??t}});try{this.process.stdin.write(n+`
11745
- `)}catch(i){console.warn("Agent Fleet: injectMessage stdin write failed",i);return}this.pendingTurns++}abort(){this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="",this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1);let t=this.turnReject;this.turnResolve=null,this.turnReject=null,t?.(new Error("Aborted"))}hibernate(){this.isStreaming||this.pendingTurns>0||(this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="")}clearSessionId(){this.claudeSessionId=null,this.basePromptSent=!1}async buildBasePrompt(){let t=[this.agent.body.trim()];for(let s of this.agent.skills){let a=this.repository.getSkillByName(s);if(a){let n=[a.body.trim()];a.toolsBody.trim()&&n.push(`### Tools
11822
+ ${n}`,this.basePromptSent=!0),this.isCodex){this.stats.lastCompact&&(this.stats.lastCompact=void 0,this.emitStats()),this.activeOnEvent=e,this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=1,this.setStreaming(!0),this.armWatchdog();try{await this.startCodexTurn(n)}catch(c){throw this.pendingTurns=0,this.clearWatchdog(),this.setStreaming(!1),new Error(`Failed to start Codex process: ${c instanceof Error?c.message:String(c)}`)}return new Promise((c,l)=>{this.turnResolve=c,this.turnReject=l})}await this.ensureProcess();let r=this.needsCompactBeforeNextTurn;r&&(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=r?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+`
11823
+ `)};try{r&&o("/compact"),o(n)}catch(c){throw this.pendingTurns=0,this.setStreaming(!1),new Error(`Failed to write to Claude process stdin: ${c instanceof Error?c.message:String(c)}`)}return new Promise((c,l)=>{this.turnResolve=c,this.turnReject=l})}scheduleCompact(){this.isCodex||(this.needsCompactBeforeNextTurn=!0)}async startCodexTurn(t){this.refreshAgent();let e=Dt(null,this.agent,this.settings),s=await ls.buildExec({prompt:t,model:ds(e.value)?e.value:"",modelSource:e.source,effort:this.agent.effort??"",agent:this.agent,settings:this.settings,streaming:!0,resumeSessionId:this.claudeSessionId});e.value&&this.stats.concreteModel!==e.value&&(this.stats.concreteModel=e.value,this.emitStats()),this.codexTurnState=Wa(),this.codexResumeAttempted=!!this.claudeSessionId,this.codexTurnErrors=[],this.codexStderr="";let a=this.agent.cwd??this.repository.getVaultBasePath()??".";this.codexPermState=await ls.setupPermissions(a,this.agent,this.settings);let n=Ge(s.cliPath,s.args,{cwd:a,env:{...process.env,AWS_REGION:this.settings.awsRegion,...this.codexPermState?.env??{}}});this.process=n,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:r=>this.handleStdout(r),onStderr:r=>{this.codexStderr+=r.toString()},onError:r=>this.handleProcessError(r),onClose:r=>this.handleCodexProcessClose(r)},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);try{n.stdin.write(s.stdinPayload??t),n.stdin.end()}catch(r){try{n.kill()}catch{}throw r instanceof Error?r:new Error(String(r))}}handleCodexProcessClose(t){if(this.detachProcessListeners(),this.isProcessAlive=!1,this.process=null,this.stdoutBuffer="",this.codexPermState?.restore(),this.codexPermState=null,t!==0&&!this.turnResponseText.trim()){let s=this.codexTurnErrors.join("; ")||this.codexStderr.trim().slice(-500)||`Codex CLI exited with code ${t??"unknown"}`;this.codexResumeAttempted&&this.clearSessionId(),this.codexQueue=[],this.activeOnEvent?.({type:"error",content:"",errorMessage:s})}this.handleTurnEnd()}injectMessage(t,e,s){if(this.isCodex){if(!this.isStreaming&&this.pendingTurns===0)return;this.messages.push({id:ps(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:s&&s.length>0?s:void 0}),this.codexQueue.push(e??t),this.pendingTurns++;return}if(!this.process||!this.isProcessAlive)return;this.messages.push({id:ps(),role:"user",content:t,timestamp:new Date().toISOString(),attachments:s&&s.length>0?s:void 0});let n=JSON.stringify({type:"user",message:{role:"user",content:e??t}});try{this.process.stdin.write(n+`
11824
+ `)}catch(r){console.warn("Agent Fleet: injectMessage stdin write failed",r);return}this.pendingTurns++}abort(){this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="",this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=0,this.codexQueue=[],this.clearWatchdog(),this.setStreaming(!1),Pt(this.settingsState),this.settingsState=null,this.codexPermState?.restore(),this.codexPermState=null;let t=this.turnReject;this.turnResolve=null,this.turnReject=null,t?.(new Error("Aborted"))}dispose(){for(let t of this.threads.values())t.abort();this.threads.clear(),this.threadIndex={},this.abort()}hibernate(){this.isStreaming||this.pendingTurns>0||(this.detachProcessListeners(),this.process&&(this.process.kill(),this.process=null),this.isProcessAlive=!1,this.stdoutBuffer="",Pt(this.settingsState),this.settingsState=null)}clearSessionId(){this.claudeSessionId=null,this.basePromptSent=!1}async buildBasePrompt(){let t=[this.agent.body.trim()];for(let s of this.agent.skills){let a=this.repository.getSkillByName(s);if(a){let n=[a.body.trim()];a.toolsBody.trim()&&n.push(`### Tools
11746
11825
  ${a.toolsBody.trim()}`),a.referencesBody.trim()&&n.push(`### References
11747
11826
  ${a.referencesBody.trim()}`),a.examplesBody.trim()&&n.push(`### Examples
11748
11827
  ${a.examplesBody.trim()}`),t.push(`## Skill: ${a.name}
@@ -11752,22 +11831,22 @@ ${n.join(`
11752
11831
  ${this.agent.skillsBody.trim()}`),this.agent.contextBody.trim()&&t.push(`## Agent Context
11753
11832
  ${this.agent.contextBody.trim()}`),this.agent.memory){let s=await this.repository.getMemory(this.agent.name);s?.body.trim()&&t.push(`## Agent Memory
11754
11833
  ${s.body.trim()}`)}this.channelContext&&this.channelContext.trim()&&t.push(`## Channel Context
11755
- ${this.channelContext.trim()}`);let e=_s(this.agent,this.repository);if(e&&t.push(e),this.isThread&&this.parentSession&&this.threadAnchorIndex!==void 0){let s="You are continuing a side thread from this conversation. The user is following up on one of your earlier replies and wants to explore something specific without adding to the main thread. Your answers here stay in this thread only and will NOT be added back to the main conversation.",a=this.parentSession.messages.slice(0,this.threadAnchorIndex+1),n=["## Conversation so far"];for(let i of a){let o=i.role==="user"?"User":"Assistant";n.push(`${o}: ${i.content.trim()}`)}t.push(`## Thread Mode
11834
+ ${this.channelContext.trim()}`);let e=Hs(this.agent,this.repository);if(e&&t.push(e),this.isThread&&this.parentSession&&this.threadAnchorIndex!==void 0){let s="You are continuing a side thread from this conversation. The user is following up on one of your earlier replies and wants to explore something specific without adding to the main thread. Your answers here stay in this thread only and will NOT be added back to the main conversation.",a=this.parentSession.messages.slice(0,this.threadAnchorIndex+1),n=["## Conversation so far"];for(let r of a){let o=r.role==="user"?"User":"Assistant";n.push(`${o}: ${r.content.trim()}`)}t.push(`## Thread Mode
11756
11835
  ${s}
11757
11836
 
11758
11837
  ${n.join(`
11759
11838
  `)}`)}return t.filter(Boolean).join(`
11760
11839
 
11761
- `)}async openOrCreateThread(t){if(this.isThread)throw new Error("Nested threads are not supported.");let e=this.threads.get(t);if(e)return e;let s=this.messages.findIndex(i=>i.id===t);if(s<0)throw new Error(`Thread anchor message "${t}" not found in parent.`);let a=new r(this.agent,this.settings,this.repository,this.vault,{threadAnchorId:t,parentSession:this});return a.threadAnchorIndex=s,await a.loadPersistedState()||(a.threadAnchorIndex=s),this.threads.set(t,a),a}closeThread(t){let e=this.threads.get(t);e&&(e.abort(),this.threads.delete(t))}hibernateIdleThreads(t){let e=Date.now();for(let s of this.threads.values())s.isProcessAlive&&e-s.lastActiveAt>t&&s.hibernate()}parseStreamEvent(t){let e=t.type;if(e==="assistant"){let s=t.message;if(s?.content&&Array.isArray(s.content))for(let a of s.content){if(a.type==="text"&&typeof a.text=="string")return{type:"text",content:a.text};if(a.type==="tool_use"){let n=String(a.name??"tool"),i=a.input,o=i?.command??i?.content??i?.file_path??i?.path??"";return{type:"tool_use",content:o?String(o).slice(0,150):"",toolName:n}}}}if(e==="content_block_delta"){let s=t.delta;if(s?.type==="text_delta"&&typeof s.text=="string")return{type:"text",content:s.text}}return null}};var Ms=class{constructor(t){this.config=t;this.now=t.now??Date.now}buckets=new Map;now;tryConsume(t){let e=this.now(),s=e-this.config.windowMs,n=(this.buckets.get(t)??[]).filter(i=>i>s);return n.length>=this.config.maxPerWindow?(this.buckets.set(t,n),!1):(n.push(e),this.buckets.set(t,n),!0)}currentCount(t){let s=this.now()-this.config.windowMs;return(this.buckets.get(t)??[]).filter(n=>n>s).length}reset(t){this.buckets.delete(t)}resetAll(){this.buckets.clear()}};function Fs(r){let t=pr(r),e=[];for(let s of t)if(s.kind==="code"){let a=s.text.replace(/^```[^\n]*\n/,"```\n");e.push(a)}else e.push(fr(s.text));return e.join("")}function pr(r){let t=[],e=0,s=!1,a=0;for(;e<r.length;)r.startsWith("```",e)?s?(e+=3,r[e]===`
11762
- `&&(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<r.length&&t.push({kind:s?"code":"prose",text:r.slice(a)}),t}function fr(r){return mr(r).map(s=>{if(s.isCode)return s.text;let a=s.text;return a=a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),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 mr(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 s<r.length&&t.push({text:r.slice(s),isCode:!1}),t}function Tn(r,t=3e3){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.slice(0,t),n=gr(a)%2===1,i;if(n){let o=yr(a);if(o>0)i=o;else{e.push(a+"\n```"),s="```\n"+s.slice(t);continue}}else{if(i=a.lastIndexOf(`
11840
+ `)}async openOrCreateThread(t){if(this.isThread)throw new Error("Nested threads are not supported.");let e=this.threads.get(t);if(e)return e;let s=this.messages.findIndex(r=>r.id===t);if(s<0)throw new Error(`Thread anchor message "${t}" not found in parent.`);let a=new i(this.agent,this.settings,this.repository,this.vault,{threadAnchorId:t,parentSession:this});return a.threadAnchorIndex=s,await a.loadPersistedState()||(a.threadAnchorIndex=s),this.threads.set(t,a),a}closeThread(t){let e=this.threads.get(t);e&&(e.abort(),this.threads.delete(t))}hibernateIdleThreads(t){let e=Date.now();for(let s of this.threads.values())s.isProcessAlive&&e-s.lastActiveAt>t&&s.hibernate()}parseStreamEvent(t){let e=t.type;if(e==="assistant"){let s=t.message;if(s?.content&&Array.isArray(s.content))for(let a of s.content){if(a.type==="text"&&typeof a.text=="string")return{type:"text",content:a.text};if(a.type==="tool_use"){let n=String(a.name??"tool"),r=a.input,o=r?.command??r?.content??r?.file_path??r?.path??"";return{type:"tool_use",content:o?String(o).slice(0,150):"",toolName:n}}}}if(e==="content_block_delta"){let s=t.delta;if(s?.type==="text_delta"&&typeof s.text=="string")return{type:"text",content:s.text}}return null}};var Ks=class{constructor(t){this.config=t;this.now=t.now??Date.now}buckets=new Map;now;tryConsume(t){let e=this.now(),s=e-this.config.windowMs,n=(this.buckets.get(t)??[]).filter(r=>r>s);return n.length>=this.config.maxPerWindow?(this.buckets.set(t,n),!1):(n.push(e),this.buckets.set(t,n),!0)}currentCount(t){let s=this.now()-this.config.windowMs;return(this.buckets.get(t)??[]).filter(n=>n>s).length}reset(t){this.buckets.delete(t)}resetAll(){this.buckets.clear()}};function Js(i){let t=_o(i),e=[];for(let s of t)if(s.kind==="code"){let a=s.text.replace(/^```[^\n]*\n/,"```\n");e.push(a)}else e.push(Ao(s.text));return e.join("")}function _o(i){let t=[],e=0,s=!1,a=0;for(;e<i.length;)i.startsWith("```",e)?s?(e+=3,i[e]===`
11841
+ `&&(e+=1),t.push({kind:"code",text:i.slice(a,e)}),a=e,s=!1):(e>a&&t.push({kind:"prose",text:i.slice(a,e)}),a=e,s=!0,e+=3):e+=1;return a<i.length&&t.push({kind:s?"code":"prose",text:i.slice(a)}),t}function Ao(i){return Eo(i).map(s=>{if(s.isCode)return s.text;let a=s.text;return a=a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),a=a.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(n,r,o)=>`<${o}|${r}>`),a=a.replace(/\*\*([^*]+)\*\*/g,"*$1*"),a=a.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),a}).join("")}function Eo(i){let t=[],e=/`([^`\n]+)`/g,s=0,a;for(;a=e.exec(i);)a.index>s&&t.push({text:i.slice(s,a.index),isCode:!1}),t.push({text:a[0],isCode:!0}),s=a.index+a[0].length;return s<i.length&&t.push({text:i.slice(s),isCode:!1}),t}function li(i,t=3e3){if(i.length<=t)return[i];let e=[],s=i;for(;s.length>t;){let a=s.slice(0,t),n=Po(a)%2===1,r;if(n){let o=Do(a);if(o>0)r=o;else{e.push(a+"\n```"),s="```\n"+s.slice(t);continue}}else{if(r=a.lastIndexOf(`
11763
11842
 
11764
- `),i<t/2){let o=a.lastIndexOf(`
11765
- `);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 gr(r){let t=0,e=0;for(;(e=r.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function yr(r){let t=0,e=0,s=0;for(;s<r.length;){let a=r.indexOf("```",s);if(a===-1)break;t+=1,s=a+3,t%2===0&&(e=s)}return e}function vr(r,t){let e=r.trim(),s=e.toLowerCase();for(let a of t){let n=a.toLowerCase(),i=[`use ${n}:`,`use ${n}`,`@${n}:`,`@${n}`,`${n}:`];for(let o of i)if(s.startsWith(o)){let l=e.slice(o.length).trim();return{agent:a,rest:l}}}return null}var Os=class{constructor(t){this.deps=t;this.now=t.now??(()=>Date.now());let e=t.getSettings();this.rateLimiter=new Ms({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,l)=>{let c=`${t.name}:${i}`;this.threadBindings.set(c,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=vr(e.text,i),l,c;if(o){l=o.agent,c=o.rest;let h=this.threadBindings.get(a);if(this.threadBindings.set(a,l),this.persistBindings(s.name),h!==l)try{await t.setThreadTitle?.(e.conversationId,l)}catch{}if(!c){try{await t.send(e.conversationId,`_Now chatting with *${l}*. Send your next message to start._`)}catch{}return}}else{let h=`${s.name}:${e.conversationId.replace(/:thread:[^:]+$/,`:user:${e.externalUserId}`)}`;l=this.threadBindings.get(a)??this.threadBindings.get(h)??s.defaultAgent,c=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&&(c=h+(c||"Please analyze this image."))}let d="";try{let u=await(await this.getOrCreateSession(s,e.conversationId,l)).sendMessage(c,()=>{});if(d=u.text.trim(),u.toolCalls.length>0){let p=br(u.toolCalls);p&&(d+=`
11843
+ `),r<t/2){let o=a.lastIndexOf(`
11844
+ `);o>t/2?r=o:r=t}r<=0&&(r=t)}e.push(s.slice(0,r)),s=s.slice(r).replace(/^\n+/,"")}return s.length>0&&e.push(s),e}function Po(i){let t=0,e=0;for(;(e=i.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function Do(i){let t=0,e=0,s=0;for(;s<i.length;){let a=i.indexOf("```",s);if(a===-1)break;t+=1,s=a+3,t%2===0&&(e=s)}return e}function Ro(i,t){let e=i.trim(),s=e.toLowerCase();for(let a of t){let n=a.toLowerCase(),r=[`use ${n}:`,`use ${n}`,`@${n}:`,`@${n}`,`${n}:`];for(let o of r)if(s.startsWith(o)){let c=e.slice(o.length).trim();return{agent:a,rest:c}}}return null}var Xs=class{constructor(t){this.deps=t;this.now=t.now??(()=>Date.now());let e=t.getSettings();this.rateLimiter=new Ks({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),r=n&&this.isChannelRuntimeValid(n,t);if(!n||!n.enabled||!r){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(r){console.error(`Agent Fleet: failed to build adapter for channel ${t.name}`,r);return}let n=[];n.push(a.onInbound(r=>{this.handleInbound(a,r)})),n.push(a.onStatusChange(()=>this.notifyStatusListeners())),a.onAgentSwitch&&n.push(a.onAgentSwitch((r,o,c)=>{let l=`${t.name}:${r}`;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(r){console.error(`Agent Fleet: channel adapter ${t.name} start() failed`,r)}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,r]of this.sessions)if(n.startsWith(a)){try{r.session.isStreaming?r.session.abort():r.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 r=this.resolveAllowedAgents(s),o=Ro(e.text,r),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=Io(u.toolCalls);p&&(d+=`
11766
11845
 
11767
- _${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=`*[${l}]*
11768
- ${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=Tn(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 c=this.deps.vault.adapter.basePath??"",d=c?`${c}/${o}`:o;a.push(`### Image: ${n.filename}
11846
+ _${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}]*
11847
+ ${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=li(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,ut.normalizePath)(s))||await this.deps.vault.createFolder((0,ut.normalizePath)(s));let r=(0,ut.normalizePath)(`${s}/${n.filename}`),o=r;if(this.deps.vault.getAbstractFileByPath(r)){let h=n.filename.lastIndexOf("."),u=h>0?n.filename.slice(0,h):n.filename,p=h>0?n.filename.slice(h):"";o=(0,ut.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}
11769
11848
  The image file is located at: ${d}
11770
- 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
11849
+ Please read and analyze this image.`)}catch(r){console.warn("Agent Fleet: failed to save inbound image",n.filename,r)}return a.length===0?"":`## Attached Images
11771
11850
 
11772
11851
  ${a.join(`
11773
11852
 
@@ -11775,24 +11854,24 @@ ${a.join(`
11775
11854
 
11776
11855
  ---
11777
11856
 
11778
- `}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 l=new Nt(o,this.deps.getSettings(),i,this.deps.vault,{channelName:t.name,conversationId:`${e}:${s}`,channelContext:t.channelContext||void 0});try{await l.loadPersistedState()}catch{}return this.sessions.set(a,{session:l,channelName:t.name,conversationId:e,sessionKey:a}),l}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,l]of this.threadBindings)o.startsWith(e)&&(s[o.slice(e.length)]=l);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 l=n.slice(0,n.lastIndexOf("/"));if(!this.deps.vault.getAbstractFileByPath(l))try{await this.deps.vault.createFolder(l)}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,l]of Object.entries(i))typeof l=="string"&&this.threadBindings.set(`${t}:${o}`,l)}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<s;a+=1){let n=e[a];if(!n)break;try{n.session.hibernate()}catch{}}}async runHibernationSweep(){let t=Math.max(6e4,this.deps.getSettings().channelIdleTimeoutMinutes*6e4),e=this.now()-t;for(let s of this.sessions.values())if(s.session.isProcessAlive&&!s.session.isStreaming&&s.session.lastActiveAt<e)try{s.session.hibernate()}catch{}}async broadcastToChannel(t,e){let s=this.adapters.get(t);if(!s){console.warn(`Agent Fleet: broadcastToChannel \u2014 no adapter for channel ${t}`);return}if(!s.broadcast){console.warn(`Agent Fleet: broadcastToChannel \u2014 adapter ${t} does not support broadcast`);return}await s.broadcast(e)}conversationLockGen=new Map;async withConversationLock(t,e){let s=this.conversationLocks.get(t)??Promise.resolve(),a,n=new Promise(o=>{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 br(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 wr(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function ns(r,t){return`${r}-${wr(t)}`}var Bt="af-channel-cred",Ns=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=ns(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=ns(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=ns(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=ns(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=ns(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var Bs=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(Bt);for(let s of e){let a=Bt+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson(Bt,n);if(i){let o=i._ref??n,l={...i};delete l._ref,this.credentials.set(o,l)}}}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(Bt,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(Bt,t,{...e,_ref:t})}};var jo=Ve(bi(),1),Ho=Ve(Vs(),1),qo=Ve(jt(),1),Wo=Ve(Ia(),1),zo=Ve(Fa(),1),Go=Ve(ja(),1),Ei=Ve(Xs(),1),Vo=Ve(Ai(),1);var qa=Ei.default;var Pi=require("obsidian");var Yo="https://slack.com/api";function Ko(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function Jo(r){let t=r.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function Xo(r){let t=r.split(":");if(t[3]==="thread"&&t[4])return t[4]}var Zs=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=Jo(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=Xo(t),n=Fs(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=Fs(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 qa(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(l=>({type:"button",text:{type:"plain_text",text:l===this.config.defaultAgent?`${l} \u2713`:l,emoji:!0},action_id:`switch_agent_${l}`,value:l})),o=[{type:"section",text:{type:"mrkdwn",text:"*Select an agent to chat with:*"}}];for(let l=0;l<i.length;l+=5)o.push({type:"actions",elements:i.slice(l,l+5)});try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"Select an agent",blocks:o})}catch(l){console.error("Agent Fleet: /agents response failed",l)}return}try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:`Unknown command: ${e}`})}catch(n){console.error(`Agent Fleet: slash command response failed for ${e}`,n)}}}async handleInteraction(t){if(!t||t.type!=="block_actions")return;let s=t.actions,a=t.user,n=t.channel,i=t.message;if(!s?.length||!a||!n)return;let o=s[0];if(!o)return;let l=o.action_id,c=o.value;if(!l?.startsWith("switch_agent_")||!c)return;let d=a.id,h=n.id;if(!d||!h)return;let p=`slack:${t.team?.id??"unknown"}:${h}:user:${d}`;for(let m of this.agentSwitchHandlers)try{m(p,c,d)}catch(f){console.error("Agent Fleet: agent switch handler threw",f)}try{await this.slackApi("chat.postEphemeral",{channel:h,user:d,text:`Switched to *${c}*. Send your next message to start.`})}catch(m){console.warn("Agent Fleet: agent switch confirmation failed",m)}try{await this.setThreadTitle(p,c)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==qa.OPEN))try{this.ws.send(JSON.stringify({envelope_id:t}))}catch(e){console.warn("Agent Fleet: Slack envelope ACK failed",e)}}routeEventPayload(t){if(!t)return;let e=t.event;if(!e)return;let s=e.type;if(s==="assistant_thread_started"){let d=e.assistant_thread;d?.channel_id&&d.thread_ts&&console.debug(`Agent Fleet: assistant thread started on ${this.config.name} (channel=${d.channel_id}, thread_ts=${d.thread_ts}, user=${d.user_id})`);return}if(s==="assistant_thread_context_changed")return;let a=e,n=s==="message"&&a.subtype===void 0,i=s==="app_mention";if(!n&&!i||a.bot_id||!a.user||!a.text||i&&(a.text=a.text.replace(/^<@[A-Z0-9]+>\s*/,"").trim(),!a.text))return;let o=Ko(a),l=a.thread_ts??a.ts;if(a.channel&&l&&(this.threadContext.set(o,{channelId:a.channel,threadTs:l}),this.threadContext.size>500)){let h=this.threadContext.keys().next();h.done||this.threadContext.delete(h.value)}let c={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(c)}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=`${Yo}/${t}`,i=await(0,Pi.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 l=Number(i.headers["retry-after"]??"1");return await new Promise(c=>setTimeout(c,Math.max(1e3,l*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 Wa=require("obsidian"),Di="https://api.telegram.org",ea=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=Ri(t);if(!s)return;let a=Ii(t),n=Li(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=Ri(t);if(!s)return;let a=Ii(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(l){console.warn("Agent Fleet: Telegram sendChatAction failed",l)}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=Li(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),l=t.message_thread_id?String(t.message_thread_id):void 0,c=l?`tg:${o}:topic:${l}`:`tg:${o}`;this.buildAndEmitMessage(t,n,c,i,o,l)}async buildAndEmitMessage(t,e,s,a,n,i){let o=[];try{if(t.photo&&t.photo.length>0){let c=t.photo[t.photo.length-1],d=await this.downloadFile(c.file_id,`photo_${t.message_id}.jpg`,"image/jpeg");d&&o.push(d)}if(t.document&&this.isImageMime(t.document.mime_type)){let c=t.document.file_name??`doc_${t.message_id}`,d=t.document.mime_type??"image/jpeg",h=await this.downloadFile(t.document.file_id,c,d);h&&o.push(h)}}catch(c){console.warn("Agent Fleet: Telegram image download failed",c)}let l={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 c of this.inboundHandlers)try{c(l)}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=`${Di}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,Wa.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 l of this.agentSwitchHandlers)try{l(o,s,a)}catch(c){console.error("Agent Fleet: Telegram agent switch handler threw",c)}if(await this.tgApi("answerCallbackQuery",{callback_query_id:t.id,text:`Switched to ${s}`}),t.message){let c=(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:c}})}catch{}}}async tgApi(t,e,s){if(s?.aborted)throw new DOMException("Aborted","AbortError");let a=`${Di}/bot${this.credential.botToken}/${t}`,n=(0,Wa.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,l)=>{s.addEventListener("abort",()=>l(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 l=i.json?.parameters?.retry_after??1;return await new Promise(c=>setTimeout(c,l*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 Ri(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function Ii(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function Li(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(`
11857
+ `}async getOrCreateSession(t,e,s){let a=`${t.name}:${e}:${s}`,n=this.sessions.get(a);if(n)return n.session;let r=this.deps.getRepository(),o=r.getAgentByName(s);if(!o)throw new Error(`Channel ${t.name} bound to missing agent ${s}`);let c=new jt(o,this.deps.getSettings(),r,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,ut.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),r=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof ut.TFile)await this.deps.vault.modify(o,r);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,r)}}catch(o){console.warn(`Agent Fleet: failed to persist thread bindings for ${t}`,o)}}async loadBindings(t){let e=this.deps.getSettings(),s=(0,ut.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof ut.TFile))return;let n=await this.deps.vault.cachedRead(a),r=JSON.parse(n);for(let[o,c]of Object.entries(r))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<s;a+=1){let n=e[a];if(!n)break;try{n.session.hibernate()}catch{}}}async runHibernationSweep(){let t=Math.max(6e4,this.deps.getSettings().channelIdleTimeoutMinutes*6e4),e=this.now()-t;for(let s of this.sessions.values())if(s.session.isProcessAlive&&!s.session.isStreaming&&s.session.lastActiveAt<e)try{s.session.hibernate()}catch{}}async broadcastToChannel(t,e){let s=this.adapters.get(t);if(!s){console.warn(`Agent Fleet: broadcastToChannel \u2014 no adapter for channel ${t}`);return}if(!s.broadcast){console.warn(`Agent Fleet: broadcastToChannel \u2014 adapter ${t} does not support broadcast`);return}await s.broadcast(e)}conversationLockGen=new Map;async withConversationLock(t,e){let s=this.conversationLocks.get(t)??Promise.resolve(),a,n=new Promise(o=>{a=o}),r=(this.conversationLockGen.get(t)??0)+1;this.conversationLockGen.set(t,r),this.conversationLocks.set(t,s.then(()=>n));try{await s,await e()}finally{a(),this.conversationLockGen.get(t)===r&&(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 Io(i){if(i.length===0)return"";let t=new Map;for(let s of i)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 ${i.length} tool${i.length===1?"":"s"}: ${e.join(", ")}`}function Lo(i){return i.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function ms(i,t){return`${i}-${Lo(t)}`}var Ht="af-channel-cred",Qs=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=ms(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=ms(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=ms(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=ms(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=ms(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var Zs=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(Ht);for(let s of e){let a=Ht+"-",n=s.startsWith(a)?s.slice(a.length):s,r=this.secretStore.getJson(Ht,n);if(r){let o=r._ref??n,c={...r};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(Ht,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(Ht,t,{...e,_ref:t})}};var Zl=Je(ar(),1),ec=Je(la(),1),tc=Je(zt(),1),sc=Je(an(),1),ac=Je(on(),1),nc=Je(pn(),1),ur=Je(ua(),1),ic=Je(hr(),1);var fn=ur.default;var pr=require("obsidian");var rc="https://slack.com/api";function oc(i){let t=i.team??"unknown",e=i.channel??"unknown",s=i.thread_ts??i.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function lc(i){let t=i.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function cc(i){let t=i.split(":");if(t[3]==="thread"&&t[4])return t[4]}var ma=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=lc(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=cc(t),n=Js(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=Js(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 fn(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 r=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<r.length;c+=5)o.push({type:"actions",elements:r.slice(c,c+5)});try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:"Select an agent",blocks:o})}catch(c){console.error("Agent Fleet: /agents response failed",c)}return}try{await this.slackApi("chat.postEphemeral",{channel:s,user:a,text:`Unknown command: ${e}`})}catch(n){console.error(`Agent Fleet: slash command response failed for ${e}`,n)}}}async handleInteraction(t){if(!t||t.type!=="block_actions")return;let s=t.actions,a=t.user,n=t.channel,r=t.message;if(!s?.length||!a||!n)return;let o=s[0];if(!o)return;let c=o.action_id,l=o.value;if(!c?.startsWith("switch_agent_")||!l)return;let d=a.id,h=n.id;if(!d||!h)return;let p=`slack:${t.team?.id??"unknown"}:${h}:user:${d}`;for(let m of this.agentSwitchHandlers)try{m(p,l,d)}catch(f){console.error("Agent Fleet: agent switch handler threw",f)}try{await this.slackApi("chat.postEphemeral",{channel:h,user:d,text:`Switched to *${l}*. Send your next message to start.`})}catch(m){console.warn("Agent Fleet: agent switch confirmation failed",m)}try{await this.setThreadTitle(p,l)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==fn.OPEN))try{this.ws.send(JSON.stringify({envelope_id:t}))}catch(e){console.warn("Agent Fleet: Slack envelope ACK failed",e)}}routeEventPayload(t){if(!t)return;let e=t.event;if(!e)return;let s=e.type;if(s==="assistant_thread_started"){let d=e.assistant_thread;d?.channel_id&&d.thread_ts&&console.debug(`Agent Fleet: assistant thread started on ${this.config.name} (channel=${d.channel_id}, thread_ts=${d.thread_ts}, user=${d.user_id})`);return}if(s==="assistant_thread_context_changed")return;let a=e,n=s==="message"&&a.subtype===void 0,r=s==="app_mention";if(!n&&!r||a.bot_id||!a.user||!a.text||r&&(a.text=a.text.replace(/^<@[A-Z0-9]+>\s*/,"").trim(),!a.text))return;let o=oc(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=`${rc}/${t}`,r=await(0,pr.requestUrl)({url:n,method:"POST",contentType:"application/json; charset=utf-8",headers:{Authorization:`Bearer ${a}`},body:JSON.stringify(e),throw:!1});if(r.status===429){let c=Number(r.headers["retry-after"]??"1");return await new Promise(l=>setTimeout(l,Math.max(1e3,c*1e3))),this.slackApi(t,e,s)}if(r.status<200||r.status>=300)throw new Error(`Slack ${t} HTTP ${r.status}`);let o=r.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(r=>setTimeout(r,1e3))}}),n=a.catch(r=>{console.warn(`Agent Fleet: Slack send queue error for ${t}`,r)});this.sendQueues.set(t,n),await a,this.sendQueues.get(t)===n&&this.sendQueues.delete(t)}};var gn=require("obsidian"),mr="https://api.telegram.org",fa=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=fr(t);if(!s)return;let a=gr(t),n=yr(e,4096);for(let r of n)await this.tgApi("sendMessage",{chat_id:s,text:r,parse_mode:"Markdown",...a?{message_thread_id:Number(a)}:{}})}async setTyping(t,e){let s=fr(t);if(!s)return;let a=gr(t),n={chat_id:s,action:"typing"};if(a&&(n.message_thread_id=Number(a)),e){let r=this.typingIntervals.get(t);r&&clearInterval(r);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 r=this.typingIntervals.get(t);r&&(clearInterval(r),this.typingIntervals.delete(t))}}async setThreadTitle(t,e){}async broadcast(t){let e=this.config.allowedUsers[0];if(e)try{let s=yr(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 r=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,r,o,c)}async buildAndEmitMessage(t,e,s,a,n,r){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:r,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 r=`${mr}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,gn.requestUrl)({url:r,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(r=>[{text:r===this.config.defaultAgent?`${r} \u2713`:r,callback_data:`switch:${r}`}]);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),r=t.message?.message_thread_id?String(t.message.message_thread_id):void 0,o=r?`tg:${n}:topic:${r}`:`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=`${mr}/bot${this.credential.botToken}/${t}`,n=(0,gn.requestUrl)({url:a,method:"POST",contentType:"application/json",body:JSON.stringify(e),throw:!1}),r;if(s?r=await Promise.race([n,new Promise((o,c)=>{s.addEventListener("abort",()=>c(new DOMException("Aborted","AbortError")),{once:!0})})]):r=await n,r.status===401||r.status===403)throw new Error(`Telegram ${t} ${r.status} Unauthorized`);if(r.status===429){let c=r.json?.parameters?.retry_after??1;return await new Promise(l=>setTimeout(l,c*1e3)),this.tgApi(t,e)}if(r.status<200||r.status>=300)throw new Error(`Telegram ${t} HTTP ${r.status}: ${r.text}`);return r.json}setStatus(t){if(this.status!==t){this.status=t;for(let e of this.statusHandlers)try{e(t)}catch{}}}};function fr(i){return i.startsWith("tg:")?i.split(":")[1]??null:null}function gr(i){let t=i.split(":");if(t[2]==="topic"&&t[3])return t[3]}function yr(i,t){if(i.length<=t)return[i];let e=[],s=i;for(;s.length>t;){let a=s.lastIndexOf(`
11779
11858
 
11780
11859
  `,t);a<t/2&&(a=s.lastIndexOf(`
11781
- `,t)),a<t/2&&(a=t),e.push(s.slice(0,a)),s=s.slice(a).replace(/^\n+/,"")}return s&&e.push(s),e}var us=require("obsidian");var ta=class extends us.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return St}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),await this.render()}async onClose(){this.plugin.unsubscribeView(this)}async render(){let e=this.contentEl;e.empty(),e.addClass("af-sidebar");let s=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getFleetStatus(),n=e.createDiv({cls:"af-sidebar-section"});n.createDiv({cls:"af-sidebar-section-header",text:"AGENT FLEET"});let i=[{icon:"layout-dashboard",label:"Dashboard",page:"dashboard"},{icon:"bot",label:"Agents",page:"agents",badge:()=>s.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 c of i){let d=n.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,us.setIcon)(h,c.icon),d.createSpan({cls:"af-sidebar-nav-label",text:c.label});let u=c.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(c.page),d.onkeydown=p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),this.plugin.navigateDashboard(c.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 c of s.agents){let d=this.plugin.runtime.getAgentState(c.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:c.name}),h.setAttribute("role","button"),h.setAttribute("tabindex","0"),h.onclick=()=>void this.plugin.navigateDashboard("agent-detail",c.name),h.onkeydown=u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),this.plugin.navigateDashboard("agent-detail",c.name))}}let l=e.createDiv({cls:"af-sidebar-section"});l.createDiv({cls:"af-sidebar-section-header",text:"QUICK ACTIONS"}),this.renderQuickAction(l,"plus","New Agent",()=>void this.plugin.createAgentTemplate()),this.renderQuickAction(l,"plus","New Task",()=>void this.plugin.openCreateTask()),this.renderQuickAction(l,"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,us.setIcon)(o,s),i.createSpan({text:a}),i.setAttribute("role","button"),i.setAttribute("tabindex","0"),i.onclick=n,i.onkeydown=l=>{(l.key==="Enter"||l.key===" ")&&(l.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 b=require("obsidian");var Vt=require("obsidian"),Qo=["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"],sa=class extends Vt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,Vt.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",Qo),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,Vt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var Mi=require("obsidian");function P(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,Mi.setIcon)(s,t),s}function Xe(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 Zo(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function Fi(r,t){let e=t.length||1,s=1e3,a=6,n=4,i=4,o=s-n-i-a*(e-1),l=Math.floor(o/e),c=140,d=36,h=18,u=h+c+d,p=Math.max(1,...t.map(f=>f.success+f.failure+f.cancelled)),m=Xe("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let f=0;f<=4;f++){let v=h+c-f/4*c;m.appendChild(Xe("line",{x1:String(n),y1:String(v),x2:String(s-i),y2:String(v),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let f=0;f<t.length;f++){let v=t[f],k=n+f*(l+a),w=v.success+v.failure+v.cancelled,y=w/p*c,g=v.success/p*c,x=v.cancelled/p*c,T=v.failure/p*c;if(v.success>0&&m.appendChild(Xe("rect",{x:String(k),y:String(h+c-g),width:String(l),height:String(Math.max(g,2)),fill:"var(--af-green)",opacity:"0.85"})),v.cancelled>0&&m.appendChild(Xe("rect",{x:String(k),y:String(h+c-g-x),width:String(l),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),v.failure>0&&m.appendChild(Xe("rect",{x:String(k),y:String(h+c-y),width:String(l),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),w===0&&m.appendChild(Xe("rect",{x:String(k),y:String(h+c-3),width:String(l),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),w>0){let L=Xe("text",{x:String(k+l/2),y:String(h+c-y-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});L.textContent=String(w),m.appendChild(L)}let C=Xe("text",{x:String(k+l/2),y:String(h+c+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});C.textContent=Zo(v.date),m.appendChild(C)}r.appendChild(m)}function Oi(r,t,e){let l=2*Math.PI*46,c=e>0?t/e:0,d=l*c,h=l-d,u=Xe("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(Xe("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"})),c>0&&u.appendChild(Xe("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(l*.25),"stroke-linecap":"round"}));let p=Xe("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(c*100)}%`,u.appendChild(p);let m=Xe("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 Ni(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 Bi(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)})}var Ui={dashboard:"Dashboard",agents:"Agents",kanban:"Tasks Board",runs:"Run History",skills:"Skills Library",approvals:"Approvals",mcp:"MCP Servers",channels:"Channels","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"},el={dashboard:"layout-dashboard",agents:"bot",kanban:"columns-3",runs:"scroll-text",skills:"puzzle",approvals:"shield-check",mcp:"plug",channels:"radio","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"},tl=["dashboard","agents","kanban","runs","approvals","skills","mcp","channels"],ps=class extends b.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return bt}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"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,b.setIcon)(i,"chevron-right");let o=(m,f,v)=>{let k=n.createSpan({cls:f?"af-breadcrumb-link":"",text:m});f&&(k.onclick=()=>this.navigate(f,v))},l=()=>{let m=n.createSpan({cls:"af-breadcrumb-sep"});(0,b.setIcon)(m,"chevron-right")};switch(this.currentPage){case"agent-detail":o("Agents","agents"),l(),o(this.detailContext??"Agent");break;case"task-detail":o("Tasks Board","kanban"),l(),o(this.detailContext??"Task");break;case"edit-agent":o("Agents","agents"),l(),o(this.detailContext??"Agent","agent-detail",this.detailContext),l(),o("Edit");break;case"edit-task":o("Tasks Board","kanban"),l(),o(this.detailContext??"Task","task-detail",this.detailContext),l(),o("Edit");break;case"create-agent":o("Agents","agents"),l(),o("New Agent");break;case"create-task":o("Tasks Board","kanban"),l(),o("New Task");break;case"create-skill":o("Skills Library","skills"),l(),o("New Skill");break;case"edit-skill":o("Skills Library","skills"),l(),o(this.detailContext??"Skill"),l(),o("Edit");break;case"create-channel":o("Channels","channels"),l(),o("New Channel");break;case"edit-channel":o("Channels","channels"),l(),o(this.detailContext??"Channel"),l(),o("Edit");break;case"add-mcp-server":o("MCP Servers","mcp"),l(),o("Add Server");break;default:o(Ui[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let c=s.createDiv({cls:"af-search-wrap"});P(c,"search","af-search-icon");let d=c.createEl("input",{cls:"af-search-input",attr:{type:"text",placeholder:"Search agents, tasks, runs..."}});d.addEventListener("input",()=>{this.handleSearch(d.value,c)}),d.addEventListener("blur",()=>{setTimeout(()=>c.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 tl){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",l=s.createEl("button",{cls:`af-tab-item${o?" active":""}`}),c=l.createSpan({cls:"af-tab-icon"});if((0,b.setIcon)(c,el[i]),l.appendText(i==="dashboard"?"Overview":Ui[i]),i==="agents")l.createSpan({cls:"af-badge",text:String(a.agents.length)});else if(i==="skills")l.createSpan({cls:"af-badge",text:String(a.skills.length)});else if(i==="mcp"){let d=this.plugin.mcpManager.getCachedServers()?.length??0;l.createSpan({cls:"af-badge",text:String(d)})}else i==="approvals"&&n.pending>0&&l.createSpan({cls:"af-badge af-badge-warn",text:String(n.pending)});l.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 l=s.createDiv({cls:"af-dash-grid"}),c=a.agents.filter(g=>g.enabled).length,d=a.agents.length;this.renderStatCard(l,"Active Agents",`${c}`,`/ ${d}`,"bot",`${c} 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(l,"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),v=u.reduce((g,x)=>g+(x.costUsd??0),0),k=v>0?` \xB7 $${v.toFixed(2)}`:"";this.renderStatCard(l,"Tokens Used",za(f),"","zap",`today${k}`);let w=a.tasks.filter(g=>g.enabled&&g.schedule);this.renderStatCard(l,"Scheduled Tasks",String(w.length),"","clock",w.length>0?`Next: ${this.getNextTaskLabel(w)}`:"No scheduled tasks"),this.renderChartsRow(s,n),this.renderStreamingCards(s);let y=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(y,n),this.renderFleetStatusPanel(y,a)}renderChartsRow(e,s){let a=e.createDiv({cls:"af-charts-row"}),n=a.createDiv({cls:"af-section-card af-chart-section"}),o=n.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(o,"activity"),o.appendText(" Run Activity (14d)");let l=n.createDiv({cls:"af-chart-body"}),c=this.buildChartData(s,14);c.some(v=>v.success+v.failure+v.cancelled>0)?Fi(l,c):this.renderEmptyState(l,"activity","No run data","Run agents to see activity");let d=a.createDiv({cls:"af-section-card af-chart-section"}),u=d.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(u,"target"),u.appendText(" Success Rate");let p=d.createDiv({cls:"af-chart-body af-chart-body-center"}),m=s.length,f=s.filter(v=>v.status==="success").length;m>0?Oi(p,f,m):this.renderEmptyState(p,"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 l=this.toLocalDateStr(o),c=e.filter(d=>this.runToLocalDate(d.started)===l);a.push({date:l,success:c.filter(d=>d.status==="success").length,failure:c.filter(d=>d.status==="failure"||d.status==="timeout").length,cancelled:c.filter(d=>d.status==="cancelled").length})}return a}renderStreamingCards(e){let a=this.plugin.runtime.getSnapshot().agents.filter(l=>this.plugin.runtime.getAgentState(l.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 l of a)this.renderStreamingCard(n,l.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,l=o?` \u2192 ${o.taskId}`:n.status==="running"?" \u2192 Heartbeat":"",c=a.createDiv({cls:"af-streaming-card-header"});c.createSpan({cls:"af-dot pulse",attr:{style:"background: var(--af-yellow)"}}),c.createSpan({cls:"af-streaming-card-agent",text:` ${s}`}),l&&c.createSpan({cls:"af-streaming-card-task",text:l});let d=a.createDiv({cls:"af-streaming-output"}),h=this.plugin.runtime.getRunOutputBuffer(s),u=pe(h).slice(-4);d.setText(u.join(`
11782
- `));let p=this.plugin.runtime.onRunOutput(s,()=>{let m=this.plugin.runtime.getRunOutputBuffer(s),f=pe(m).slice(-4);d.setText(f.join(`
11783
- `)),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,b.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 l=n.createDiv({cls:"af-approval-actions"}),c=l.createEl("button",{cls:"af-btn-approve",text:"Approve"});c.onclick=()=>void this.plugin.runtime.resolveApproval(s,a,"approved").then(()=>this.render());let d=l.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 l=e.createDiv({cls:"af-stat-card"}),c=l.createDiv({cls:"af-stat-label"});P(c,i,"af-stat-icon"),c.appendText(` ${s}`);let d=l.createDiv({cls:"af-stat-value"});d.appendText(a),n&&d.createSpan({cls:"af-stat-value-suffix",text:n}),l.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"}),l=s.slice(0,8);if(l.length===0){this.renderEmptyState(o,"inbox","No runs yet","Run an agent to see activity here");return}for(let c of l)this.renderTimelineItem(o,c)}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,b.setIcon)(i,this.statusToIconName(s.status));let o=a.createDiv({cls:"af-tl-body"}),l=o.createDiv({cls:"af-tl-title"});l.createSpan({cls:"af-agent-tag",text:s.agent}),l.appendText(` ${s.task}`),o.createDiv({cls:"af-tl-desc",text:Mt(s.output,100)});let c=o.createDiv({cls:"af-tl-meta"}),d=c.createSpan();if(P(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=c.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 l=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});P(l,"plus","af-btn-icon"),l.appendText(" New Agent"),l.onclick=()=>void this.plugin.createAgentTemplate();let c=a.createDiv({cls:"af-agent-mini-list"});if(s.agents.length===0){this.renderEmptyState(c,"bot","No agents yet","Create your first agent to get started");return}for(let h of s.agents)this.renderAgentMini(c,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 v of d)m.createEl("option",{text:v.name,attr:{value:v.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),l=e.createDiv({cls:"af-agent-mini"}),c=l.createDiv({cls:`af-agent-avatar ${o}`});s.avatar?.trim()?this.renderAgentAvatar(c,s):c.setText(this.getInitials(s.name));let d=l.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}),l.createDiv({cls:`af-agent-status-dot ${o}`}),l.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 l of a.agents)this.renderAgentCard(o,l,a)}renderAgentCard(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=this.plugin.runtime.getRecentRuns().filter(E=>E.agent===s.name),o=a.tasks.filter(E=>E.agent===s.name),l=e.createDiv({cls:`af-agent-card${s.enabled?"":" disabled"}`}),c=l.createDiv({cls:"af-agent-card-header"}),d=s.enabled?this.healthToClass(n.status):"disabled",h=c.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(h,s);let u=c.createDiv({cls:"af-agent-card-titleblock"}),p=u.createDiv({cls:"af-agent-card-name"});if(p.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let E=p.createSpan({cls:"af-heartbeat-indicator"});(0,b.setIcon)(E,"heart-pulse"),E.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let m=c.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});m.onclick=E=>{E.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let f=l.createDiv({cls:"af-agent-card-stats"}),v=i.length,k=i.filter(E=>E.status==="success").length,w=v>0?Math.round(k/v*100):0,y=v>0?Math.round(i.reduce((E,S)=>E+S.durationSeconds,0)/v):0,g=i.reduce((E,S)=>E+(S.tokensUsed??0),0);if(this.renderAgentStat(f,String(v),"Runs"),this.renderAgentStat(f,`${w}%`,"Success"),this.renderAgentStat(f,`${y}s`,"Avg Time"),this.renderAgentStat(f,za(g),"Tokens"),s.skills.length>0){let E=l.createDiv({cls:"af-agent-card-skills"});for(let S of s.skills)E.createSpan({cls:"af-skill-tag",text:S})}let x=l.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 C=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let E=C.createEl("button",{cls:"af-btn-sm",text:"Enable"});E.onclick=S=>{S.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let L=C.createEl("button",{cls:"af-btn-sm"});if(P(L,"edit","af-btn-icon"),L.appendText(" Edit"),L.onclick=E=>{E.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let E=C.createEl("button",{cls:"af-btn-sm primary"});P(E,"play","af-btn-icon"),E.appendText(" Run"),E.onclick=S=>{S.stopPropagation(),this.plugin.runAgentPrompt(s.name)}}l.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(y=>y.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(y=>y.agent===n.name),l=s.createDiv({cls:"af-detail-header"}),c=l.createDiv({cls:"af-detail-header-left"}),d=c.createDiv({cls:`af-agent-card-avatar ${this.healthToClass(i.status)}`});this.renderAgentAvatar(d,n);let h=c.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=l.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 y=u.createEl("button",{cls:"af-btn-sm"});P(y,"play","af-btn-icon"),y.appendText(" Run Now"),y.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 y=u.createEl("button",{cls:"af-btn-sm"});P(y,"play","af-btn-icon"),y.appendText(" Enable"),y.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 v=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 y of k){let g=v.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===y.id?" active":""}`});P(g,y.icon,"af-tab-icon"),g.appendText(` ${y.label}`),g.onclick=()=>{this.agentDetailTab=y.id,this.render()}}let w=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(w,n,o);break;case"config":this.renderAgentConfigTab(w,n);break;case"runs":this.renderAgentRunsTab(w,o);break;case"memory":this.renderAgentMemoryTab(w,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(y=>y.status==="success").length,l=i>0?Math.round(o/i*100):0,c=i>0?Math.round(a.reduce((y,g)=>y+g.durationSeconds,0)/i):0,d=a.reduce((y,g)=>y+(g.tokensUsed??0),0),h=a.reduce((y,g)=>y+(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",`${l}%`,"","check-circle-2",`${o}/${i}`),this.renderStatCard(n,"Avg Time",`${c}s`,"","clock","per run"),this.renderStatCard(n,"Total Tokens",za(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let y=e.createDiv({cls:"af-section-card"}),g=y.createDiv({cls:"af-section-header"}),x=g.createDiv({cls:"af-section-title"});P(x,"heart-pulse"),x.appendText(" Heartbeat");let C=g.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});C.onclick=async()=>{let S=C.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!S}),await this.plugin.refreshFromVault(),new b.Notice(`Heartbeat ${S?"paused":"enabled"} for ${s.name}`)};let L=y.createDiv({cls:"af-config-form"});this.renderConfigRow(L,"Schedule",sl(s.heartbeatSchedule));let E=this.plugin.runtime.getNextHeartbeat(s.name);E&&s.heartbeatEnabled&&this.renderConfigRow(L,"Next run",this.timeUntil(E)),s.heartbeatChannel&&this.renderConfigRow(L,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let y=e.createDiv({cls:"af-section-card"}),x=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"puzzle"),x.appendText(" Skills");let T=y.createDiv({cls:"af-detail-skills-list"});for(let C of s.skills)T.createSpan({cls:"af-skill-tag",text:C})}let p=s.mcpServers??[];if(p.length>0){let y=e.createDiv({cls:"af-section-card"}),x=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"plug"),x.appendText(" MCP Servers");let T=y.createDiv({cls:"af-mcp-overview-list"}),C=this.plugin.mcpManager.getCachedServers();for(let L of p){let E=C?.find(O=>O.name===L),S=T.createDiv({cls:"af-mcp-overview-row"}),I=S.createSpan({cls:`af-mcp-status-dot ${E?E.enabled?E.status:"disabled":"disconnected"}`});I.title=E?E.enabled?E.status:"disabled":"unknown",S.createSpan({cls:"af-mcp-overview-name",text:L});let A=E?.toolDetails.length??E?.tools.length??0;A>0?S.createSpan({cls:"af-mcp-overview-tools",text:`${A} tools`}):E&&!E.enabled?S.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):E?.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 y=e.createDiv({cls:"af-section-card"}),x=y.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(x,"shield-check"),x.appendText(" Permissions");let T=y.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 w=f.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(w,"scroll-text","No runs yet","");else for(let y of a.slice(0,5))this.renderTimelineItem(w,y)}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(l=>l.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 l=a.createDiv({cls:"af-config-prompt-section"});l.createDiv({cls:"af-slideover-section-title",text:"HEARTBEAT INSTRUCTION"}),l.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,b.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),l=o.createDiv({cls:"af-tl-title"});l.createSpan({text:a.task}),l.createSpan({cls:`af-status-badge ${this.statusToBadgeClass(a.status)}`,text:this.statusToBadgeText(a.status)});let c=o.createDiv({cls:"af-tl-meta"});c.createSpan({text:`${this.formatStarted(a.started)} \xB7 ${this.formatDuration(a.durationSeconds)}`}),a.tokensUsed&&c.createSpan({text:`${a.tokensUsed.toLocaleString()} tokens`}),o.createDiv({cls:"af-tl-desc",text:Mt(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 l=s.createDiv({cls:"af-kanban-board"}),c=[],d=[],h=[],u=[],p=[],m=new Set;for(let w of a.agents){let y=this.plugin.runtime.getAgentState(w.name);y.status==="running"&&y.currentTaskId&&m.add(y.currentTaskId)}let f=this.toLocalDateStr(new Date);for(let w of n)w.status==="success"&&this.runToLocalDate(w.started)===f?u.push(w):(w.status==="failure"||w.status==="timeout"||w.status==="cancelled")&&this.runToLocalDate(w.started)===f&&p.push(w);let v=new Set(u.map(w=>w.task)),k=new Set(p.map(w=>w.task));for(let w of a.tasks){let y=v.has(w.taskId)||k.has(w.taskId)||w.lastRun&&this.runToLocalDate(w.lastRun)===f;if(m.has(w.taskId))h.push(w);else{if(y&&!w.schedule)continue;w.schedule&&w.enabled?d.push(w):c.push(w)}}this.renderKanbanColumn(l,"Backlog","inbox",c.length,w=>{for(let y of c)this.renderKanbanTaskCard(w,y,a,!0)},"backlog"),this.renderKanbanColumn(l,"Scheduled","clock",d.length,w=>{for(let y of d)this.renderKanbanTaskCard(w,y,a,!0)},"scheduled"),this.renderKanbanColumn(l,"Running","loader-2",h.length,w=>{for(let y of h)this.renderKanbanRunningCard(w,y)},"running",!1,"running"),this.renderKanbanColumn(l,"Done","check-circle-2",u.length,w=>{for(let y of u.slice(0,10))this.renderKanbanCompletedCard(w,y)},"completed"),this.renderKanbanColumn(l,"Failed","x-circle",p.length,w=>{for(let y of p)this.renderKanbanFailedCard(w,y)},"failed",!1,"failed")}renderKanbanColumn(e,s,a,n,i,o,l=!1,c){let d=e.createDiv({cls:`af-kanban-column${c?` af-kanban-${c}`:""}`});(o==="backlog"||o==="scheduled")&&Bi(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"}),l){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 b.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new b.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new b.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof b.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=ie(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){Ni(i,s.taskId);let f=i.createDiv({cls:"af-kanban-card-grip"});(0,b.setIcon)(f,"grip-vertical")}let o=i.createDiv({cls:"af-kanban-card-header"});o.createDiv({cls:"af-kanban-card-title",text:s.taskId});let c=(a.agents.find(f=>f.name===s.agent)?.enabled??!1)&&s.enabled,d=o.createSpan({cls:`af-kanban-card-status ${c?"active":"inactive"}`});d.title=c?"Active":"Inactive";let h=i.createDiv({cls:"af-kanban-card-agent"}),u=h.createSpan({cls:"af-kanban-card-agent-icon"});(0,b.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"});c?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,b.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=this.plugin.runtime.getSnapshot().agents.find(x=>x.name===s.agent)?.timeout??300,c=this.plugin.runtime.getAgentState(s.agent),d=c.runStarted?new Date(c.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/l*100);p.style.width=`${f}%`;let v=a.createDiv({cls:"af-kanban-card-footer"}),k=v.createSpan({cls:"af-kanban-card-schedule"});P(k,"loader-2","af-meta-icon");let w=Math.round(m);k.appendText(` ${w}s / ${l}s`);let y=v.createEl("button",{cls:"af-kanban-stop-btn"});(0,b.setIcon)(y,"square"),y.title="Stop task",y.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new b.Notice(`Stopped task "${s.taskId}"`)};let g=setInterval(()=>{let x=(Date.now()-d)/1e3,T=Math.min(95,x/l*100);p.style.width=`${T}%`;let C=Math.round(x);k.textContent="",(0,b.setIcon)(k,"loader-2"),k.appendText(` ${C}s / ${l}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,b.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=a.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});P(l,"check-circle-2","af-meta-icon"),l.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,b.setIcon)(o,"bot"),i.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=a?`Stopped after ${s.durationSeconds}s`:s.status==="timeout"?`Timeout after ${s.durationSeconds}s`:Mt(s.output,60),c=n.createDiv({cls:"af-kanban-card-error"});P(c,a?"square":"alert-triangle","af-meta-icon"),c.appendText(` ${l}`);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"),c=o.createEl("thead").createEl("tr");for(let h of["Status","Agent","Task","Started","Duration","Tokens","Model"])c.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,b.setIcon)(o,this.statusToIconName(s.status)),i.appendText(` ${this.statusToBadgeText(s.status)}`);let l=a.createEl("td",{cls:"af-agent-link"});l.setText(s.agent),l.onclick=c=>{c.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 l of a.skills)this.renderSkillCard(o,l,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,b.setIcon)(o,this.getSkillIcon(s.name));let l=i.createEl("button",{cls:"af-btn-sm af-btn-xs"});P(l,"edit","af-btn-icon"),l.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 c=a.filter(d=>d.skills.includes(s.name));if(c.length>0){let d=n.createDiv({cls:"af-skill-card-agents"});for(let h of c)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 l of a.channels)this.renderChannelCard(o,l,a.validationIssues)}renderChannelCard(e,s,a){let n=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",i=$i(n),o=s.enabled&&n!=="disabled"?"af-agent-card":"af-agent-card disabled",l=e.createDiv({cls:o});l.style.cursor="default";let c=l.createDiv({cls:"af-agent-card-header"}),d=c.createDiv({cls:`af-agent-card-avatar ${i}`});(0,b.setIcon)(d,"radio");let h=c.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=c.createSpan({cls:`af-pill ${al(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let T=l.createDiv({cls:"af-agent-card-skills"});for(let C of s.allowedAgents){let L=T.createSpan({cls:"af-skill-tag",text:C});C===s.defaultAgent&&(L.style.fontWeight="700")}}let p=l.createDiv({cls:"af-agent-card-stats"}),m=this.plugin.channelManager?.getSessionCount(s.name)??0,f=this.plugin.channelManager?.getMetrics(s.name),v=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,v,"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=l.createDiv({cls:"af-agent-card-footer"}),w=[s.type];s.enabled||w.push("disabled"),s.allowedUsers.length>0?w.push(`${s.allowedUsers.length} user(s)`):w.push("allowlist empty"),k.createSpan({cls:"af-agent-card-meta",text:w.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=l.createDiv({cls:"af-channel-issues"});for(let C of x)T.createDiv({cls:"af-channel-issue-row",text:C.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"}),l=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(l,"plus");let c=o.createDiv();c.createDiv({cls:"af-detail-header-name",text:"Create New Channel"}),c.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,b.setIcon)(m,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",W=>{d.name=W});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Type"});let v=f.createEl("select",{cls:"af-form-select"});v.createEl("option",{text:"slack",attr:{value:"slack"}}),v.createEl("option",{text:"telegram",attr:{value:"telegram"}}),v.addEventListener("change",()=>{d.type=v.value});let k=u.createDiv({cls:"af-form-row"}),w=k.createDiv({cls:"af-form-label"});w.setText("Credential"),this.addTooltip(w,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let y=k.createEl("select",{cls:"af-form-select"});n.length===0&&y.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let W of n)y.createEl("option",{text:`${W.ref} (${W.entry.type})`,attr:{value:W.ref}});y.addEventListener("change",()=>{d.credentialRef=y.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 W=x.hasClass("on");x.toggleClass("on",!W),d.enabled=!W};let T=h.createDiv({cls:"af-create-section"}),C=T.createDiv({cls:"af-create-section-header"}),L=C.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(L,"bot"),C.createSpan({text:"Agent Routing"});let E=T.createDiv({cls:"af-form-row"}),S=E.createDiv({cls:"af-form-label"});S.setText("Default agent"),this.addTooltip(S,"Used when no @agent-name prefix is given in a message");let I=E.createEl("select",{cls:"af-form-select"});for(let W of a.agents)I.createEl("option",{text:W.name,attr:{value:W.name}});I.addEventListener("change",()=>{d.defaultAgent=I.value});let A=T.createDiv({cls:"af-form-row"}),O=A.createDiv({cls:"af-form-label"});O.setText("Allowed agents"),this.addTooltip(O,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let R=A.createDiv({cls:"af-form-checkboxes"});for(let W of a.agents){let ve=R.createEl("label",{cls:"af-form-checkbox-label"}),be=ve.createEl("input",{attr:{type:"checkbox",value:W.name}});ve.appendText(` ${W.name}`),be.addEventListener("change",()=>{be.checked?d.allowedAgents.includes(W.name)||d.allowedAgents.push(W.name):d.allowedAgents=d.allowedAgents.filter(me=>me!==W.name)})}let z=T.createDiv({cls:"af-form-row af-form-row-toggle"}),N=z.createDiv({cls:"af-form-label"});N.setText("Per-user sessions"),this.addTooltip(N,"Each external user gets their own isolated Claude session");let j=z.createDiv({cls:"af-agent-card-toggle on"});j.onclick=()=>{let W=j.hasClass("on");j.toggleClass("on",!W),d.perUserSessions=!W};let oe=h.createDiv({cls:"af-create-section"}),te=oe.createDiv({cls:"af-create-section-header"}),ee=te.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ee,"shield-check"),te.createSpan({text:"Access Control"});let V=oe.createDiv({cls:"af-form-label"});V.setText("Allowed users"),this.addTooltip(V,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let G=oe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
11784
- U0BXYZ12345`,rows:"4"}});G.addEventListener("input",()=>{d.allowedUsers=G.value});let $=h.createDiv({cls:"af-create-section"}),H=$.createDiv({cls:"af-create-section-header"}),se=H.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(se,"message-square");let re=H.createSpan({text:"Channel Context"});this.addTooltip(re,"Extra instructions appended to the agent's system prompt when reached through this channel");let de=$.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});de.addEventListener("input",()=>{d.channelContext=de.value}),this.createFormField(u,"Tags","ops, internal","Comma-separated metadata",W=>{d.tags=W});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,b.setIcon)(Ne,"settings");let $e=Ae.createSpan({text:"Advanced"});this.addTooltip($e,"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 Te=Le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});Te.addEventListener("input",()=>{d.body=Te.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:`{
11860
+ `,t)),a<t/2&&(a=t),e.push(s.slice(0,a)),s=s.slice(a).replace(/^\n+/,"")}return s&&e.push(s),e}var xs=require("obsidian");var ga=class extends xs.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return _t}getDisplayText(){return"Agent Fleet"}getIcon(){return"bot"}async onOpen(){this.plugin.subscribeView(this),await this.render()}async onClose(){this.plugin.unsubscribeView(this)}async render(){let e=this.contentEl;e.empty(),e.addClass("af-sidebar");let s=this.plugin.runtime.getSnapshot(),a=this.plugin.runtime.getFleetStatus(),n=e.createDiv({cls:"af-sidebar-section"});n.createDiv({cls:"af-sidebar-section-header",text:"AGENT FLEET"});let r=[{icon:"layout-dashboard",label:"Dashboard",page:"dashboard"},{icon:"bot",label:"Agents",page:"agents",badge:()=>s.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 r){let d=n.createDiv({cls:"af-sidebar-nav-item"}),h=d.createSpan({cls:"af-sidebar-nav-icon"});(0,xs.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 r=e.createDiv({cls:"af-sidebar-action-item"}),o=r.createSpan({cls:"af-sidebar-action-icon"});(0,xs.setIcon)(o,s),r.createSpan({text:a}),r.setAttribute("role","button"),r.setAttribute("tabindex","0"),r.onclick=n,r.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 k=require("obsidian");var Xt=require("obsidian"),dc=["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"],ya=class extends Xt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,Xt.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",dc),this.renderSection(e,"All Icons",this.allIcons);else{let a=this.allIcons.filter(r=>r.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 r=n.createDiv({cls:"af-icon-picker-grid"});for(let o of a)this.renderIconItem(r,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,Xt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var vr=require("obsidian");function A(i,t,e){let s=i.createSpan({cls:e??"af-icon"});return(0,vr.setIcon)(s,t),s}function tt(i,t={}){let e=document.createElementNS("http://www.w3.org/2000/svg",i);for(let[s,a]of Object.entries(t))e.setAttribute(s,a);return e}function hc(i){try{return new Date(i+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return i.slice(5)}}function br(i,t){let e=t.length||1,s=1e3,a=6,n=4,r=4,o=s-n-r-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=tt("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let f=0;f<=4;f++){let g=h+l-f/4*l;m.appendChild(tt("line",{x1:String(n),y1:String(g),x2:String(s-r),y2:String(g),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let f=0;f<t.length;f++){let g=t[f],b=n+f*(c+a),w=g.success+g.failure+g.cancelled,v=w/p*l,y=g.success/p*l,x=g.cancelled/p*l,S=g.failure/p*l;if(g.success>0&&m.appendChild(tt("rect",{x:String(b),y:String(h+l-y),width:String(c),height:String(Math.max(y,2)),fill:"var(--af-green)",opacity:"0.85"})),g.cancelled>0&&m.appendChild(tt("rect",{x:String(b),y:String(h+l-y-x),width:String(c),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),g.failure>0&&m.appendChild(tt("rect",{x:String(b),y:String(h+l-v),width:String(c),height:String(Math.max(S,2)),fill:"var(--af-red)",opacity:"0.85"})),w===0&&m.appendChild(tt("rect",{x:String(b),y:String(h+l-3),width:String(c),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),w>0){let M=tt("text",{x:String(b+c/2),y:String(h+l-v-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});M.textContent=String(w),m.appendChild(M)}let _=tt("text",{x:String(b+c/2),y:String(h+l+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});_.textContent=hc(g.date),m.appendChild(_)}i.appendChild(m)}function wr(i,t,e){let c=2*Math.PI*46,l=e>0?t/e:0,d=c*l,h=c-d,u=tt("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(tt("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(tt("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=tt("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=tt("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),i.appendChild(u)}function kr(i,t){i.draggable=!0,i.addEventListener("dragstart",e=>{e.dataTransfer?.setData("text/plain",t),i.addClass("af-dragging")}),i.addEventListener("dragend",()=>{i.removeClass("af-dragging")})}function xr(i,t){i.addEventListener("dragover",e=>{e.preventDefault(),i.addClass("af-drag-over")}),i.addEventListener("dragleave",e=>{let s=e.relatedTarget;(!s||!i.contains(s))&&i.removeClass("af-drag-over")}),i.addEventListener("drop",e=>{e.preventDefault();let s=e.dataTransfer?.getData("text/plain");i.removeClass("af-drag-over"),s&&t(s)})}function Sr(i){let t=/^##\s+Lint\s+(\d{4}-\d{2}-\d{2})\s*$/gm,e,s=-1,a="";for(;(e=t.exec(i))!==null;)e.index>s&&(s=e.index,a=e[1]??"");if(s<0)return null;let n=i.slice(s),r=n.search(/\n##\s+(?!\s*#)/),o=r<0?n:n.slice(0,r);return{date:a,summary:va(o,"Summary"),autoApplied:va(o,"Auto-applied"),needsReview:va(o,"Needs review"),refreshChained:va(o,"Refresh chained")}}function va(i,t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^###\\s+${e}\\s*$`,"m").exec(i);if(!a)return[];let n=a.index+a[0].length,r=i.slice(n),o=r.search(/\n###\s+/),l=(o<0?r:r.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 Cr={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"},uc={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"},pc=["dashboard","agents","kanban","runs","approvals","skills","wiki-keepers","mcp","channels"],Tr=[["claude-code","Claude Code",!1],["codex","Codex",!1],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]],Ar=[["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"]],Er=[["bypassPermissions","Bypass (no sandbox)","No sandbox, auto-approve everything"],["workspace-write","Workspace Write","Sandboxed: writes only inside the working dir"],["read-only","Read Only","Sandboxed: no writes or side-effect commands"]];function Ss(i){let t=i.trim().toLowerCase();return t==="codex"||t==="openai-codex"}function ba(i){return Ss(i)?Er:Ar}function wa(i,t){if(Ss(t))switch(i){case"acceptEdits":case"default":return"workspace-write";case"plan":return"read-only";case"dontAsk":return"bypassPermissions";default:return Er.some(([e])=>e===i)?i:"bypassPermissions"}switch(i){case"workspace-write":return"acceptEdits";case"read-only":return"plan";case"danger-full-access":return"bypassPermissions";default:return Ar.some(([e])=>e===i)?i:"bypassPermissions"}}var Cs=class extends k.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return xt}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"});A(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),r=n.createSpan({cls:"af-breadcrumb-sep"});(0,k.setIcon)(r,"chevron-right");let o=(m,f,g)=>{let b=n.createSpan({cls:f?"af-breadcrumb-link":"",text:m});f&&(b.onclick=()=>this.navigate(f,g))},c=()=>{let m=n.createSpan({cls:"af-breadcrumb-sep"});(0,k.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(Cr[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let l=s.createDiv({cls:"af-search-wrap"});A(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 r of pc){let o=this.currentPage===r||r==="agents"&&(this.currentPage==="agent-detail"||this.currentPage==="edit-agent"||this.currentPage==="create-agent")||r==="kanban"&&(this.currentPage==="task-detail"||this.currentPage==="edit-task")||r==="skills"&&(this.currentPage==="edit-skill"||this.currentPage==="create-skill")||r==="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,k.setIcon)(l,uc[r]),c.appendText(r==="dashboard"?"Overview":Cr[r]),r==="agents")c.createSpan({cls:"af-badge",text:String(a.agents.length)});else if(r==="skills")c.createSpan({cls:"af-badge",text:String(a.skills.length)});else if(r==="mcp"){let d=this.plugin.mcpManager.getCachedServers()?.length??0;c.createSpan({cls:"af-badge",text:String(d)})}else r==="approvals"&&n.pending>0&&c.createSpan({cls:"af-badge af-badge-warn",text:String(n.pending)});c.onclick=()=>this.navigate(r)}}renderDashboardPage(e){let s=e.createDiv({cls:"af-dashboard"}),a=this.plugin.runtime.getSnapshot(),n=this.plugin.runtime.getRecentRuns(),r=this.plugin.runtime.getFleetStatus(),o=n.filter(y=>(y.approvals??[]).some(x=>x.status==="pending"));for(let y of o)for(let x of y.approvals??[])x.status==="pending"&&this.renderApprovalBanner(s,y,x.tool);let c=s.createDiv({cls:"af-dash-grid"}),l=a.agents.filter(y=>y.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(y=>this.runToLocalDate(y.started)===h),p=u.filter(y=>y.status==="success").length,m=u.filter(y=>y.status==="failure"||y.status==="timeout").length;this.renderStatCard(c,"Runs Today",String(u.length),"","activity",`${p} passed \xB7 ${m} failed \xB7 ${r.running} running`);let f=u.reduce((y,x)=>y+(x.tokensUsed??0),0),g=u.reduce((y,x)=>y+(x.costUsd??0),0),b=g>0?` \xB7 $${g.toFixed(2)}`:"";this.renderStatCard(c,"Tokens Used",yn(f),"","zap",`today${b}`);let w=a.tasks.filter(y=>y.enabled&&y.schedule);this.renderStatCard(c,"Scheduled Tasks",String(w.length),"","clock",w.length>0?`Next: ${this.getNextTaskLabel(w)}`:"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"}),r=n.createDiv({cls:"af-section-card af-chart-section"}),c=r.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(c,"activity"),c.appendText(" Run Activity (14d)");let l=r.createDiv({cls:"af-chart-body"}),d=this.buildChartData(a,14);d.some(b=>b.success+b.failure+b.cancelled>0)?br(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"});A(p,"target"),p.appendText(" Success Rate");let m=h.createDiv({cls:"af-chart-body af-chart-body-center"}),f=s.length,g=s.filter(b=>b.status==="success").length;f>0?wr(m,g,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 r=s-1;r>=0;r--){let o=new Date(n);o.setDate(o.getDate()-r);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"});A(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),r=this.plugin.runtime.getSnapshot(),o=n.currentTaskId?r.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=re(h).slice(-4);d.setText(u.join(`
11861
+ `));let p=this.plugin.runtime.onRunOutput(s,()=>{let m=this.plugin.runtime.getRunOutputBuffer(s),f=re(m).slice(-4);d.setText(f.join(`
11862
+ `)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(p)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),r=n.createDiv({cls:"af-approval-icon"});(0,k.setIcon)(r,"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,r,o){let c=e.createDiv({cls:"af-stat-card"}),l=c.createDiv({cls:"af-stat-label"});A(l,r,"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"}),r=a.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(r,"inbox"),r.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),r=a.createDiv({cls:`af-tl-icon ${n}`});(0,k.setIcon)(r,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:Bt(s.output,100)});let l=o.createDiv({cls:"af-tl-meta"}),d=l.createSpan();if(A(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=l.createSpan();A(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"}),r=n.createDiv({cls:"af-section-title"});A(r,"bot"),r.appendText(" Fleet Status");let c=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});A(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"});A(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 g of d)m.createEl("option",{text:g.name,attr:{value:g.name}});let f=p.createEl("button",{cls:"af-btn-sm primary"});A(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),r=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 ${r.length} task${r.length!==1?"s":""}`;else if(!s.enabled)h=`Disabled \xB7 ${r.length} task${r.length!==1?"s":""} paused`;else{let u=r.map(p=>p.nextRun).filter(Boolean).sort()[0];h=u?`Next: ${this.formatNextRun(u)} \xB7 ${r.length} task${r.length!==1?"s":""}`:`${r.length} task${r.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 r=n.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Agent"),r.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),r=this.plugin.runtime.getRecentRuns().filter(D=>D.agent===s.name),o=a.tasks.filter(D=>D.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 D=p.createSpan({cls:"af-heartbeat-indicator"});(0,k.setIcon)(D,"heart-pulse"),D.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=D=>{D.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let f=c.createDiv({cls:"af-agent-card-stats"}),g=r.length,b=r.filter(D=>D.status==="success").length,w=g>0?Math.round(b/g*100):0,v=g>0?Math.round(r.reduce((D,C)=>D+C.durationSeconds,0)/g):0,y=r.reduce((D,C)=>D+(C.tokensUsed??0),0);if(this.renderAgentStat(f,String(g),"Runs"),this.renderAgentStat(f,`${w}%`,"Success"),this.renderAgentStat(f,`${v}s`,"Avg Time"),this.renderAgentStat(f,yn(y),"Tokens"),s.skills.length>0){let D=c.createDiv({cls:"af-agent-card-skills"});for(let C of s.skills)D.createSpan({cls:"af-skill-tag",text:C})}let x=c.createDiv({cls:"af-agent-card-footer"}),S=[`Model: ${s.model}`];s.approvalRequired.length>0&&S.push(`Approval: ${s.approvalRequired.join(", ")}`),s.memory&&S.push("Memory: on"),s.enabled||S.unshift("Disabled"),x.createSpan({cls:"af-agent-card-meta",text:S.join(" \xB7 ")});let _=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let D=_.createEl("button",{cls:"af-btn-sm",text:"Enable"});D.onclick=C=>{C.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let M=_.createEl("button",{cls:"af-btn-sm"});if(A(M,"edit","af-btn-icon"),M.appendText(" Edit"),M.onclick=D=>{D.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let D=_.createEl("button",{cls:"af-btn-sm primary"});A(D,"play","af-btn-icon"),D.appendText(" Run"),D.onclick=C=>{C.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 r=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(r.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(A(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"});A(v,"play","af-btn-icon"),v.appendText(" Run Now"),v.onclick=()=>void this.plugin.runAgentPrompt(n.name);let y=u.createEl("button",{cls:"af-btn-sm"});A(y,"pause","af-btn-icon"),y.appendText(" Disable"),y.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let v=u.createEl("button",{cls:"af-btn-sm"});A(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"});A(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"});A(f,"trash-2","af-btn-icon"),f.appendText(" Delete"),f.onclick=()=>void this.plugin.deleteAgent(n.name);let g=s.createDiv({cls:"af-detail-tabs"}),b=[{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 b){let y=g.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===v.id?" active":""}`});A(y,v.icon,"af-tab-icon"),y.appendText(` ${v.label}`),y.onclick=()=>{this.agentDetailTab=v.id,this.render()}}let w=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(w,n,o);break;case"config":this.renderAgentConfigTab(w,n);break;case"runs":this.renderAgentRunsTab(w,o);break;case"memory":this.renderAgentMemoryTab(w,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),r=a.length,o=a.filter(v=>v.status==="success").length,c=r>0?Math.round(o/r*100):0,l=r>0?Math.round(a.reduce((v,y)=>v+y.durationSeconds,0)/r):0,d=a.reduce((v,y)=>v+(y.tokensUsed??0),0),h=a.reduce((v,y)=>v+(y.costUsd??0),0),u=h>0?` \xB7 $${h.toFixed(2)}`:"";if(this.renderStatCard(n,"Total Runs",String(r),"","activity","all time"),this.renderStatCard(n,"Success Rate",`${c}%`,"","check-circle-2",`${o}/${r}`),this.renderStatCard(n,"Avg Time",`${l}s`,"","clock","per run"),this.renderStatCard(n,"Total Tokens",yn(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let v=e.createDiv({cls:"af-section-card"}),y=v.createDiv({cls:"af-section-header"}),x=y.createDiv({cls:"af-section-title"});A(x,"heart-pulse"),x.appendText(" Heartbeat");let _=y.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});_.onclick=async()=>{let C=_.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!C}),await this.plugin.refreshFromVault(),new k.Notice(`Heartbeat ${C?"paused":"enabled"} for ${s.name}`)};let M=v.createDiv({cls:"af-config-form"});this.renderConfigRow(M,"Schedule",mc(s.heartbeatSchedule));let D=this.plugin.runtime.getNextHeartbeat(s.name);D&&s.heartbeatEnabled&&this.renderConfigRow(M,"Next run",this.timeUntil(D)),s.heartbeatChannel&&this.renderConfigRow(M,"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"});A(x,"puzzle"),x.appendText(" Skills");let S=v.createDiv({cls:"af-detail-skills-list"});for(let _ of s.skills)S.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"});A(x,"plug"),x.appendText(" MCP Servers");let S=v.createDiv({cls:"af-mcp-overview-list"}),_=this.plugin.mcpManager.getCachedServers();for(let M of p){let D=_?.find(B=>B.name===M),C=S.createDiv({cls:"af-mcp-overview-row"}),I=C.createSpan({cls:`af-mcp-status-dot ${D?D.enabled?D.status:"disabled":"disconnected"}`});I.title=D?D.enabled?D.status:"disabled":"unknown",C.createSpan({cls:"af-mcp-overview-name",text:M});let P=D?.toolDetails.length??D?.tools.length??0;P>0?C.createSpan({cls:"af-mcp-overview-tools",text:`${P} tools`}):D&&!D.enabled?C.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):D?.status==="needs-auth"&&C.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"});A(x,"shield-check"),x.appendText(" Permissions");let S=v.createDiv({cls:"af-config-form"});this.renderConfigRow(S,"Mode",s.permissionMode||"default"),s.permissionRules.allow.length>0&&this.renderConfigRow(S,"Allowed",s.permissionRules.allow.join(", ")),s.permissionRules.deny.length>0&&this.renderConfigRow(S,"Denied",s.permissionRules.deny.join(", "))}let f=e.createDiv({cls:"af-section-card"}),b=f.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(b,"scroll-text"),b.appendText(" Recent Runs");let w=f.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(w,"scroll-text","No runs yet","");else for(let v of a.slice(0,5))this.renderTimelineItem(w,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"});A(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"}),r=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,k.setIcon)(r,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:Bt(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"});A(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(),r=s.createDiv({cls:"af-kanban-toolbar"});r.createDiv({cls:"af-page-title",text:"Tasks Board"}),r.createDiv({cls:"af-toolbar-spacer"});let o=r.createEl("button",{cls:"af-btn-sm primary"});A(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 w of a.agents){let v=this.plugin.runtime.getAgentState(w.name);v.status==="running"&&v.currentTaskId&&m.add(v.currentTaskId)}let f=this.toLocalDateStr(new Date);for(let w of n)w.status==="success"&&this.runToLocalDate(w.started)===f?u.push(w):(w.status==="failure"||w.status==="timeout"||w.status==="cancelled")&&this.runToLocalDate(w.started)===f&&p.push(w);let g=new Set(u.map(w=>w.task)),b=new Set(p.map(w=>w.task));for(let w of a.tasks){let v=g.has(w.taskId)||b.has(w.taskId)||w.lastRun&&this.runToLocalDate(w.lastRun)===f;if(m.has(w.taskId))h.push(w);else{if(v&&!w.schedule)continue;w.schedule&&w.enabled?d.push(w):l.push(w)}}this.renderKanbanColumn(c,"Backlog","inbox",l.length,w=>{for(let v of l)this.renderKanbanTaskCard(w,v,a,!0)},"backlog"),this.renderKanbanColumn(c,"Scheduled","clock",d.length,w=>{for(let v of d)this.renderKanbanTaskCard(w,v,a,!0)},"scheduled"),this.renderKanbanColumn(c,"Running","loader-2",h.length,w=>{for(let v of h)this.renderKanbanRunningCard(w,v)},"running",!1,"running"),this.renderKanbanColumn(c,"Done","check-circle-2",u.length,w=>{for(let v of u.slice(0,10))this.renderKanbanCompletedCard(w,v)},"completed"),this.renderKanbanColumn(c,"Failed","x-circle",p.length,w=>{for(let v of p)this.renderKanbanFailedCard(w,v)},"failed",!1,"failed")}renderKanbanColumn(e,s,a,n,r,o,c=!1,l){let d=e.createDiv({cls:`af-kanban-column${l?` af-kanban-${l}`:""}`});(o==="backlog"||o==="scheduled")&&xr(d,m=>this.handleTaskDrop(m,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});A(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(r(p),n===0&&p.createDiv({cls:"af-kanban-empty",text:"No items"}),c){let f=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");A(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 k.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new k.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new k.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof k.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);r.enabled=s,await this.plugin.app.vault.modify(a,K(r,o)),await this.plugin.refreshFromVault()}renderKanbanTaskCard(e,s,a,n){let r=e.createDiv({cls:`af-kanban-card af-priority-${s.priority}`});if(n){kr(r,s.taskId);let f=r.createDiv({cls:"af-kanban-card-grip"});(0,k.setIcon)(f,"grip-vertical")}let o=r.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=r.createDiv({cls:"af-kanban-card-agent"}),u=h.createSpan({cls:"af-kanban-card-agent-icon"});(0,k.setIcon)(u,"bot"),h.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let m=r.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});l?s.schedule?(A(m,"refresh-cw","af-meta-icon"),m.appendText(` ${this.humanizeCron(s.schedule)}`)):m.appendText(s.runAt??"Manual"):(A(m,"pause","af-meta-icon"),m.appendText(" Paused")),r.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"}),r=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,k.setIcon)(r,"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 g=a.createDiv({cls:"af-kanban-card-footer"}),b=g.createSpan({cls:"af-kanban-card-schedule"});A(b,"loader-2","af-meta-icon");let w=Math.round(m);b.appendText(` ${w}s / ${c}s`);let v=g.createEl("button",{cls:"af-kanban-stop-btn"});(0,k.setIcon)(v,"square"),v.title="Stop task",v.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new k.Notice(`Stopped task "${s.taskId}"`)};let y=setInterval(()=>{let x=(Date.now()-d)/1e3,S=Math.min(95,x/c*100);p.style.width=`${S}%`;let _=Math.round(x);b.textContent="",(0,k.setIcon)(b,"loader-2"),b.appendText(` ${_}s / ${c}s`)},1e3);this.streamingUnsubscribes.push(()=>clearInterval(y))}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"}),r=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,k.setIcon)(r,"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"});A(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 r=n.createDiv({cls:"af-kanban-card-agent"}),o=r.createSpan({cls:"af-kanban-card-agent-icon"});(0,k.setIcon)(o,"bot"),r.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`:Bt(s.output,60),l=n.createDiv({cls:"af-kanban-card-error"});A(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(A(h,a?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!a){let u=d.createEl("button",{cls:"af-btn-sm"});A(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 r=s.createDiv({cls:"af-runs-table"});if(a.length===0){this.renderEmptyState(r,"scroll-text","No runs yet","Run an agent to see history here");return}let o=r.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"),r=a.createEl("td").createSpan({cls:`af-status-badge ${this.statusToBadgeClass(s.status)}`}),o=r.createSpan();(0,k.setIcon)(o,this.statusToIconName(s.status)),r.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 r=n.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Skill"),r.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"}),r=n.createDiv({cls:"af-skill-card-header"}),o=r.createDiv({cls:"af-skill-card-icon"});(0,k.setIcon)(o,this.getSkillIcon(s.name));let c=r.createEl("button",{cls:"af-btn-sm af-btn-xs"});A(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 r=n.createEl("button",{cls:"af-btn-sm primary"});A(r,"plus","af-btn-icon"),r.appendText(" New Channel"),r.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 r=a.agents.filter(c=>c.wikiKeeper!==void 0);if(r.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 r)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 r=n.createDiv();r.style.display="flex",r.style.alignItems="center",r.style.gap="12px",r.style.marginBottom="12px";let o=r.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=r.createEl("button",{cls:"af-btn-sm"});l.appendText("Open log"),l.onclick=()=>{let g=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,b=this.plugin.app.vault.getAbstractFileByPath(g);b instanceof k.TFile?this.plugin.app.workspace.getLeaf().openFile(b):new k.Notice(`Log file not found: ${g}`)};let d=a.scopeRoot?`${a.scopeRoot}/${a.logPath}`:a.logPath,h=this.plugin.app.vault.getAbstractFileByPath(d),u=null;if(h instanceof k.TFile)try{let g=await this.plugin.app.vault.cachedRead(h);u=Sr(g)}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 g=n.createEl("details");g.createEl("summary",{text:"Summary"});let b=g.createEl("ul");b.style.marginTop="4px";for(let w of u.summary)b.createEl("li",{text:w})}if(u.autoApplied.length>0){let g=n.createEl("details");g.createEl("summary",{text:`Auto-applied (${u.autoApplied.length})`});let b=g.createEl("ul");b.style.marginTop="4px";for(let w of u.autoApplied)b.createEl("li",{text:w})}if(u.refreshChained.length>0){let g=n.createEl("details");g.createEl("summary",{text:`Refresh chained (${u.refreshChained.length})`});let b=g.createEl("ul");b.style.marginTop="4px";for(let w of u.refreshChained)b.createEl("li",{text:w})}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 g of u.needsReview){let b=f.createDiv();b.style.display="flex",b.style.alignItems="flex-start",b.style.gap="8px",b.style.padding="8px 10px",b.style.background="var(--background-secondary)",b.style.borderRadius="4px",b.style.fontSize="13px";let w=b.createDiv();w.style.flex="1",w.setText(g);let v=b.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=()=>{b.remove()}}}renderChannelCard(e,s,a){let n=this.plugin.channelManager?.getChannelStatus(s.name)??"disabled",r=_r(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 ${r}`});(0,k.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 ${fc(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let S=c.createDiv({cls:"af-agent-card-skills"});for(let _ of s.allowedAgents){let M=S.createSpan({cls:"af-skill-tag",text:_});_===s.defaultAgent&&(M.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),g=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(p,g,"Agents"),this.renderAgentStat(p,String(m),"Sessions"),this.renderAgentStat(p,String(f?.messagesReceived??0),"In"),this.renderAgentStat(p,String(f?.messagesSent??0),"Out");let b=c.createDiv({cls:"af-agent-card-footer"}),w=[s.type];s.enabled||w.push("disabled"),s.allowedUsers.length>0?w.push(`${s.allowedUsers.length} user(s)`):w.push("allowlist empty"),b.createSpan({cls:"af-agent-card-meta",text:w.join(" \xB7 ")});let y=b.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});A(y,"edit","af-btn-icon"),y.appendText(" Edit"),y.onclick=S=>{S.stopPropagation(),this.navigate("edit-channel",s.name)};let x=a.filter(S=>S.path===s.filePath);if(x.length>0){let S=c.createDiv({cls:"af-channel-issues"});for(let _ of x)S.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(),r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,k.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"}),r.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,k.setIcon)(m,"radio"),p.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",j=>{d.name=j});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Type"});let g=f.createEl("select",{cls:"af-form-select"});g.createEl("option",{text:"slack",attr:{value:"slack"}}),g.createEl("option",{text:"telegram",attr:{value:"telegram"}}),g.addEventListener("change",()=>{d.type=g.value});let b=u.createDiv({cls:"af-form-row"}),w=b.createDiv({cls:"af-form-label"});w.setText("Credential"),this.addTooltip(w,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let v=b.createEl("select",{cls:"af-form-select"});n.length===0&&v.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let j of n)v.createEl("option",{text:`${j.ref} (${j.entry.type})`,attr:{value:j.ref}});v.addEventListener("change",()=>{d.credentialRef=v.value});let y=u.createDiv({cls:"af-form-row af-form-row-toggle"});y.createDiv({cls:"af-form-label",text:"Enabled"});let x=y.createDiv({cls:"af-agent-card-toggle on"});x.onclick=()=>{let j=x.hasClass("on");x.toggleClass("on",!j),d.enabled=!j};let S=h.createDiv({cls:"af-create-section"}),_=S.createDiv({cls:"af-create-section-header"}),M=_.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(M,"bot"),_.createSpan({text:"Agent Routing"});let D=S.createDiv({cls:"af-form-row"}),C=D.createDiv({cls:"af-form-label"});C.setText("Default agent"),this.addTooltip(C,"Used when no @agent-name prefix is given in a message");let I=D.createEl("select",{cls:"af-form-select"});for(let j of a.agents)I.createEl("option",{text:j.name,attr:{value:j.name}});I.addEventListener("change",()=>{d.defaultAgent=I.value});let P=S.createDiv({cls:"af-form-row"}),B=P.createDiv({cls:"af-form-label"});B.setText("Allowed agents"),this.addTooltip(B,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let L=P.createDiv({cls:"af-form-checkboxes"});for(let j of a.agents){let ke=L.createEl("label",{cls:"af-form-checkbox-label"}),Se=ke.createEl("input",{attr:{type:"checkbox",value:j.name}});ke.appendText(` ${j.name}`),Se.addEventListener("change",()=>{Se.checked?d.allowedAgents.includes(j.name)||d.allowedAgents.push(j.name):d.allowedAgents=d.allowedAgents.filter(ye=>ye!==j.name)})}let V=S.createDiv({cls:"af-form-row af-form-row-toggle"}),U=V.createDiv({cls:"af-form-label"});U.setText("Per-user sessions"),this.addTooltip(U,"Each external user gets their own isolated Claude session");let H=V.createDiv({cls:"af-agent-card-toggle on"});H.onclick=()=>{let j=H.hasClass("on");H.toggleClass("on",!j),d.perUserSessions=!j};let he=h.createDiv({cls:"af-create-section"}),me=he.createDiv({cls:"af-create-section-header"}),ue=me.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(ue,"shield-check"),me.createSpan({text:"Access Control"});let Q=he.createDiv({cls:"af-form-label"});Q.setText("Allowed users"),this.addTooltip(Q,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let Y=he.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
11863
+ U0BXYZ12345`,rows:"4"}});Y.addEventListener("input",()=>{d.allowedUsers=Y.value});let q=h.createDiv({cls:"af-create-section"}),ee=q.createDiv({cls:"af-create-section-header"}),te=ee.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(te,"message-square");let oe=ee.createSpan({text:"Channel Context"});this.addTooltip(oe,"Extra instructions appended to the agent's system prompt when reached through this channel");let ie=q.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});ie.addEventListener("input",()=>{d.channelContext=ie.value}),this.createFormField(u,"Tags","ops, internal","Comma-separated metadata",j=>{d.tags=j});let Ee=h.createDiv({cls:"af-create-section"}),Pe=Ee.createDiv({cls:"af-create-section-header"}),Ue=Pe.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Ue,"settings");let He=Pe.createSpan({text:"Advanced"});this.addTooltip(He,"Markdown body (shown in the channel detail page) and transport-specific overrides");let De=Ee.createDiv({cls:"af-form-label"});De.setText("Body (markdown)"),this.addTooltip(De,"Free-form notes for this channel. Shown in the channel detail page; not sent to the agent.");let _e=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});_e.addEventListener("input",()=>{d.body=_e.value});let Re=Ee.createDiv({cls:"af-form-label"});Re.setText("Transport config (JSON)"),this.addTooltip(Re,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode, telegram webhook settings). Leave blank for defaults.");let $e=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{
11785
11864
  "socket_mode": true
11786
- }`,rows:"4"}});Be.addEventListener("input",()=>{d.transportJson=Be.value});let je=s.createDiv({cls:"af-create-footer"}),Y=je.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Y.onclick=()=>this.navigate("channels");let ae=je.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(ae,"plus","af-btn-icon"),ae.appendText(" Create Channel"),ae.onclick=async()=>{let W=d.name.trim();if(!W){new b.Notice("Channel name is required.");return}if(!d.credentialRef){new b.Notice("Select a credential.");return}let ve;if(d.transportJson.trim())try{let X=JSON.parse(d.transportJson);if(X&&typeof X=="object"&&!Array.isArray(X))ve=X;else{new b.Notice("Transport config must be a JSON object.");return}}catch(X){new b.Notice(`Transport JSON is invalid: ${X instanceof Error?X.message:String(X)}`);return}let be=X=>X.split(/[\n,]+/).map(D=>D.trim()).filter(Boolean),me=X=>X.split(",").map(D=>D.trim()).filter(Boolean),Me={name:ke(W),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:be(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0,tags:me(d.tags).length>0?me(d.tags):void 0,transport:ve};try{let X=ke(W),D=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),X);await this.plugin.app.vault.create(D,J(Me,d.body.trim())),new b.Notice(`Channel "${X}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",X)}catch(X){let D=X instanceof Error?X.message:String(X);new b.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(),l=this.plugin.channelManager?.getChannelStatus(n.name)??"disabled",c=s.createDiv({cls:"af-detail-header"}),d=c.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:`af-agent-card-avatar ${$i(l)}`});(0,b.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: ${l}`}),c.createDiv({cls:"af-detail-header-actions"});let p={type:n.type,defaultAgent:n.defaultAgent,allowedAgents:[...n.allowedAgents],credentialRef:n.credentialRef,allowedUsers:n.allowedUsers.join(`
11787
- `),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"}),v=f.createDiv({cls:"af-create-section-header"}),k=v.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(k,"radio"),v.createSpan({text:"Channel Details"});let w=f.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Name"});let y=w.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});y.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 Z=x.createEl("option",{text:D,attr:{value:D}});D===n.type&&(Z.selected=!0)}x.addEventListener("change",()=>{p.type=x.value});let T=f.createDiv({cls:"af-form-row"}),C=T.createDiv({cls:"af-form-label"});C.setText("Credential"),this.addTooltip(C,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let L=T.createEl("select",{cls:"af-form-select"});o.length===0&&L.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let D of o){let Z=L.createEl("option",{text:`${D.ref} (${D.entry.type})`,attr:{value:D.ref}});D.ref===n.credentialRef&&(Z.selected=!0)}L.addEventListener("change",()=>{p.credentialRef=L.value});let E=f.createDiv({cls:"af-form-row af-form-row-toggle"});E.createDiv({cls:"af-form-label",text:"Enabled"});let S=E.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});S.onclick=()=>{let D=S.hasClass("on");S.toggleClass("on",!D),p.enabled=!D};let I=m.createDiv({cls:"af-create-section"}),A=I.createDiv({cls:"af-create-section-header"}),O=A.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(O,"bot"),A.createSpan({text:"Agent Routing"});let R=I.createDiv({cls:"af-form-row"}),z=R.createDiv({cls:"af-form-label"});z.setText("Default agent"),this.addTooltip(z,"Used when no @agent-name prefix is given in a message");let N=R.createEl("select",{cls:"af-form-select"});for(let D of i.agents){let Z=N.createEl("option",{text:D.name,attr:{value:D.name}});D.name===n.defaultAgent&&(Z.selected=!0)}N.addEventListener("change",()=>{p.defaultAgent=N.value});let j=I.createDiv({cls:"af-form-row"}),oe=j.createDiv({cls:"af-form-label"});oe.setText("Allowed agents"),this.addTooltip(oe,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let te=j.createDiv({cls:"af-form-checkboxes"});for(let D of i.agents){let Z=te.createEl("label",{cls:"af-form-checkbox-label"}),le=Z.createEl("input",{attr:{type:"checkbox",value:D.name}});n.allowedAgents.includes(D.name)&&(le.checked=!0),Z.appendText(` ${D.name}`),le.addEventListener("change",()=>{le.checked?p.allowedAgents.includes(D.name)||p.allowedAgents.push(D.name):p.allowedAgents=p.allowedAgents.filter(he=>he!==D.name)})}let ee=I.createDiv({cls:"af-form-row af-form-row-toggle"}),V=ee.createDiv({cls:"af-form-label"});V.setText("Per-user sessions"),this.addTooltip(V,"Each external user gets their own isolated Claude session");let G=ee.createDiv({cls:`af-agent-card-toggle${n.perUserSessions?" on":""}`});G.onclick=()=>{let D=G.hasClass("on");G.toggleClass("on",!D),p.perUserSessions=!D};let $=m.createDiv({cls:"af-create-section"}),H=$.createDiv({cls:"af-create-section-header"}),se=H.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(se,"shield-check"),H.createSpan({text:"Access Control"});let re=$.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 de=$.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
11788
- U0BXYZ12345`,rows:"4"}});de.value=n.allowedUsers.join(`
11789
- `),de.addEventListener("input",()=>{p.allowedUsers=de.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,b.setIcon)(Ne,"message-square");let $e=Ae.createSpan({text:"Channel Context"});this.addTooltip($e,"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 Te=m.createDiv({cls:"af-create-section"}),Pe=Te.createDiv({cls:"af-create-section-header"}),Be=Pe.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Be,"settings");let je=Pe.createSpan({text:"Advanced"});this.addTooltip(je,"Markdown body (shown in the channel detail page) and transport-specific overrides"),Te.createDiv({cls:"af-form-label"}).setText("Body (markdown)");let ae=Te.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});ae.value=n.body,ae.addEventListener("input",()=>{p.body=ae.value});let W=Te.createDiv({cls:"af-form-label"});W.setText("Transport config (JSON)"),this.addTooltip(W,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode). Leave blank for defaults.");let ve=Te.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{
11865
+ }`,rows:"4"}});$e.addEventListener("input",()=>{d.transportJson=$e.value});let qe=s.createDiv({cls:"af-create-footer"}),z=qe.createEl("button",{cls:"af-btn-sm",text:"Cancel"});z.onclick=()=>this.navigate("channels");let Z=qe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(Z,"plus","af-btn-icon"),Z.appendText(" Create Channel"),Z.onclick=async()=>{let j=d.name.trim();if(!j){new k.Notice("Channel name is required.");return}if(!d.credentialRef){new k.Notice("Select a credential.");return}let ke;if(d.transportJson.trim())try{let J=JSON.parse(d.transportJson);if(J&&typeof J=="object"&&!Array.isArray(J))ke=J;else{new k.Notice("Transport config must be a JSON object.");return}}catch(J){new k.Notice(`Transport JSON is invalid: ${J instanceof Error?J.message:String(J)}`);return}let Se=J=>J.split(/[\n,]+/).map(E=>E.trim()).filter(Boolean),ye=J=>J.split(",").map(E=>E.trim()).filter(Boolean),Ie={name:ge(j),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:Se(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0,tags:ye(d.tags).length>0?ye(d.tags):void 0,transport:ke};try{let J=ge(j),E=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),J);await this.plugin.app.vault.create(E,K(Ie,d.body.trim())),new k.Notice(`Channel "${J}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",J)}catch(J){let E=J instanceof Error?J.message:String(J);new k.Notice(`Failed to create channel: ${E}`)}}}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(E=>E.name===a);if(!n){this.renderEmptyState(s,"radio","Channel not found",`Channel "${a}" was not found`);return}let r=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 ${_r(c)}`});(0,k.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(`
11866
+ `),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"}),g=f.createDiv({cls:"af-create-section-header"}),b=g.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(b,"radio"),g.createSpan({text:"Channel Details"});let w=f.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Name"});let v=w.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});v.style.opacity="0.6";let y=f.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Type"});let x=y.createEl("select",{cls:"af-form-select"});for(let E of["slack","telegram"]){let X=x.createEl("option",{text:E,attr:{value:E}});E===n.type&&(X.selected=!0)}x.addEventListener("change",()=>{p.type=x.value});let S=f.createDiv({cls:"af-form-row"}),_=S.createDiv({cls:"af-form-label"});_.setText("Credential"),this.addTooltip(_,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let M=S.createEl("select",{cls:"af-form-select"});o.length===0&&M.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let E of o){let X=M.createEl("option",{text:`${E.ref} (${E.entry.type})`,attr:{value:E.ref}});E.ref===n.credentialRef&&(X.selected=!0)}M.addEventListener("change",()=>{p.credentialRef=M.value});let D=f.createDiv({cls:"af-form-row af-form-row-toggle"});D.createDiv({cls:"af-form-label",text:"Enabled"});let C=D.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});C.onclick=()=>{let E=C.hasClass("on");C.toggleClass("on",!E),p.enabled=!E};let I=m.createDiv({cls:"af-create-section"}),P=I.createDiv({cls:"af-create-section-header"}),B=P.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(B,"bot"),P.createSpan({text:"Agent Routing"});let L=I.createDiv({cls:"af-form-row"}),V=L.createDiv({cls:"af-form-label"});V.setText("Default agent"),this.addTooltip(V,"Used when no @agent-name prefix is given in a message");let U=L.createEl("select",{cls:"af-form-select"});for(let E of r.agents){let X=U.createEl("option",{text:E.name,attr:{value:E.name}});E.name===n.defaultAgent&&(X.selected=!0)}U.addEventListener("change",()=>{p.defaultAgent=U.value});let H=I.createDiv({cls:"af-form-row"}),he=H.createDiv({cls:"af-form-label"});he.setText("Allowed agents"),this.addTooltip(he,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let me=H.createDiv({cls:"af-form-checkboxes"});for(let E of r.agents){let X=me.createEl("label",{cls:"af-form-checkbox-label"}),le=X.createEl("input",{attr:{type:"checkbox",value:E.name}});n.allowedAgents.includes(E.name)&&(le.checked=!0),X.appendText(` ${E.name}`),le.addEventListener("change",()=>{le.checked?p.allowedAgents.includes(E.name)||p.allowedAgents.push(E.name):p.allowedAgents=p.allowedAgents.filter(pe=>pe!==E.name)})}let ue=I.createDiv({cls:"af-form-row af-form-row-toggle"}),Q=ue.createDiv({cls:"af-form-label"});Q.setText("Per-user sessions"),this.addTooltip(Q,"Each external user gets their own isolated Claude session");let Y=ue.createDiv({cls:`af-agent-card-toggle${n.perUserSessions?" on":""}`});Y.onclick=()=>{let E=Y.hasClass("on");Y.toggleClass("on",!E),p.perUserSessions=!E};let q=m.createDiv({cls:"af-create-section"}),ee=q.createDiv({cls:"af-create-section-header"}),te=ee.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(te,"shield-check"),ee.createSpan({text:"Access Control"});let oe=q.createDiv({cls:"af-form-label"});oe.setText("Allowed users"),this.addTooltip(oe,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let ie=q.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
11867
+ U0BXYZ12345`,rows:"4"}});ie.value=n.allowedUsers.join(`
11868
+ `),ie.addEventListener("input",()=>{p.allowedUsers=ie.value});let Ee=m.createDiv({cls:"af-create-section"}),Pe=Ee.createDiv({cls:"af-create-section-header"}),Ue=Pe.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Ue,"message-square");let He=Pe.createSpan({text:"Channel Context"});this.addTooltip(He,"Extra instructions appended to the agent's system prompt when reached through this channel");let De=Ee.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});De.value=n.channelContext,De.addEventListener("input",()=>{p.channelContext=De.value}),this.createFormField(f,"Tags","ops, internal","Comma-separated metadata",E=>{p.tags=E},n.tags.join(", "));let _e=m.createDiv({cls:"af-create-section"}),Re=_e.createDiv({cls:"af-create-section-header"}),$e=Re.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)($e,"settings");let qe=Re.createSpan({text:"Advanced"});this.addTooltip(qe,"Markdown body (shown in the channel detail page) and transport-specific overrides"),_e.createDiv({cls:"af-form-label"}).setText("Body (markdown)");let Z=_e.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Notes, runbook snippets, escalation contacts\u2026",rows:"4"}});Z.value=n.body,Z.addEventListener("input",()=>{p.body=Z.value});let j=_e.createDiv({cls:"af-form-label"});j.setText("Transport config (JSON)"),this.addTooltip(j,"Optional JSON object for transport-specific overrides (e.g. Slack socket_mode). Leave blank for defaults.");let ke=_e.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`{
11790
11869
  "socket_mode": true
11791
- }`,rows:"4"}});ve.value=p.transportJson,ve.addEventListener("input",()=>{p.transportJson=ve.value});let be=s.createDiv({cls:"af-create-footer"}),me=be.createEl("button",{cls:"af-btn-sm danger"});P(me,"trash-2","af-btn-icon"),me.appendText(" Delete"),me.onclick=async()=>{await this.plugin.repository.deleteChannel(n.name),new b.Notice(`Channel "${n.name}" deleted.`),await new Promise(D=>setTimeout(D,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},be.createDiv({cls:"af-toolbar-spacer"});let Me=be.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("channels");let X=be.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(X,"check","af-btn-icon"),X.appendText(" Save Changes"),X.onclick=async()=>{let D=he=>he.split(/[\n,]+/).map(Qe=>Qe.trim()).filter(Boolean),Z=he=>he.split(",").map(Qe=>Qe.trim()).filter(Boolean),le;if(p.transportJson.trim())try{let he=JSON.parse(p.transportJson);if(he&&typeof he=="object"&&!Array.isArray(he))le=he;else{new b.Notice("Transport config must be a JSON object.");return}}catch(he){new b.Notice(`Transport JSON is invalid: ${he instanceof Error?he.message:String(he)}`);return}else le={};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:Z(p.tags),body:p.body.trim(),transport:le}),new b.Notice(`Channel "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(he){let Qe=he instanceof Error?he.message:String(he);new b.Notice(`Failed to update channel: ${Qe}`)}}}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(l=>(l.approvals??[]).some(c=>c.status==="pending"));if(i.length>0){let l=s.createDiv({cls:"af-section-card"}),d=l.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(d,"alert-triangle"),d.appendText(` Pending (${i.length})`);let h=l.createDiv({cls:"af-approvals-list"});for(let u of i)this.renderApprovalItem(h,u,!0)}else{let l=s.createDiv({cls:"af-section-card"});this.renderEmptyState(l,"shield-check","No pending approvals","All clear!")}let o=a.filter(l=>(l.approvals??[]).some(c=>c.status!=="pending"));if(o.length>0){let l=s.createDiv({cls:"af-section-card"}),d=l.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(d,"check-circle-2"),d.appendText(" History");let h=l.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,b.setIcon)(o,"shield-check"),o.addClass("pending")):n.status==="approved"?((0,b.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,b.setIcon)(o,"x-circle"),o.addClass("rejected"));let l=i.createDiv({cls:"af-approval-item-body"});if(l.createDiv({cls:"af-approval-item-title",text:`${s.agent} \u2192 ${n.tool}`}),l.createDiv({cls:"af-approval-item-meta",text:`Task: ${s.task} \xB7 ${n.command??"no command"} \xB7 ${this.formatStarted(s.started)}`}),n.reason&&l.createDiv({cls:"af-approval-item-reason",text:`Reason: ${n.reason}`}),a&&n.status==="pending"){let c=i.createDiv({cls:"af-approval-item-actions"}),d=c.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=c.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(A=>A.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(A=>A.task===a),l=i.agents.find(A=>A.name===n.agent),c=s.createDiv({cls:"af-detail-header"}),d=c.createDiv({cls:"af-detail-header-left"}),h=d.createDiv({cls:"af-agent-card-avatar idle"});(0,b.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=c.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 v=s.createDiv({cls:"af-section-card"}),w=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(w,"file-text"),w.appendText(" Details");let y=v.createDiv({cls:"af-config-form"});this.renderConfigRow(y,"Agent",n.agent),this.renderConfigRow(y,"Priority",n.priority.charAt(0).toUpperCase()+n.priority.slice(1)),this.renderConfigRow(y,"Status",n.enabled?"Enabled":"Disabled");let g=n.schedule?this.humanizeCron(n.schedule):n.runAt??"Manual (run on demand)";this.renderConfigRow(y,"Schedule",g),n.schedule&&this.renderConfigRow(y,"Catch up if missed",n.catchUp?"Yes":"No"),this.renderConfigRow(y,"Created",n.created),this.renderConfigRow(y,"Runs",String(n.runCount)),n.lastRun&&this.renderConfigRow(y,"Last Run",this.formatStarted(n.lastRun));let x=s.createDiv({cls:"af-section-card"}),C=x.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(C,"message-square"),C.appendText(" Instructions"),x.createDiv({cls:"af-output-block",text:n.body||"(empty)"});let L=s.createDiv({cls:"af-section-card"}),S=L.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});P(S,"scroll-text"),S.appendText(" Recent Runs");let I=L.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(I,"scroll-text","No runs yet","");else for(let A of o.slice(0,10))this.renderTimelineItem(I,A)}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 c of n.agents)(c.name.toLowerCase().includes(a)||(c.description?.toLowerCase().includes(a)??!1))&&o.push({label:`Agent: ${c.name}`,icon:"bot",action:()=>this.navigate("agent-detail",c.name)});for(let c of n.tasks)(c.taskId.toLowerCase().includes(a)||c.body.toLowerCase().includes(a))&&o.push({label:`Task: ${c.taskId}`,icon:"circle-dot",action:()=>this.navigate("task-detail",c.taskId)});for(let c of n.skills)c.name.toLowerCase().includes(a)&&o.push({label:`Skill: ${c.name}`,icon:"puzzle",action:()=>this.navigate("edit-skill",c.name)});for(let c of i.slice(0,20))c.output.toLowerCase().includes(a)&&o.push({label:`Run: ${c.agent} / ${c.task}`,icon:"scroll-text",action:()=>this.openSlideover(c)});if(o.length===0)return;let l=s.createDiv({cls:"af-search-results"});for(let c of o.slice(0,10)){let d=l.createDiv({cls:"af-search-result-item"});P(d,c.icon,"af-search-result-icon"),d.createSpan({text:c.label}),d.onclick=()=>{l.remove(),c.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,b.setIcon)(i,"cross"),i.onclick=()=>s.remove();let o=a.createDiv({cls:"af-slideover-body"}),l=o.createDiv({cls:"af-slideover-section"});l.createDiv({cls:"af-slideover-section-title",text:"METADATA"}),this.renderDetailRow(l,"Run ID",e.runId.slice(0,8)),this.renderDetailRow(l,"Agent",e.agent),this.renderDetailRow(l,"Task",e.task);let c=l.createDiv({cls:"af-detail-row"});c.createSpan({cls:"af-detail-label",text:"Status"});let h=c.createSpan({cls:"af-detail-value"}).createSpan({cls:`af-status-badge ${this.statusToBadgeClass(e.status)}`}),u=h.createSpan();(0,b.setIcon)(u,this.statusToIconName(e.status)),h.appendText(` ${this.statusToBadgeText(e.status)}`),this.renderDetailRow(l,"Started",e.started),this.renderDetailRow(l,"Duration",this.formatDuration(e.durationSeconds)),this.renderDetailRow(l,"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(l,"Model",`${e.model}${T}${x}`)}let p=5e4,m=g=>g.length>p?g.slice(0,p)+`
11870
+ }`,rows:"4"}});ke.value=p.transportJson,ke.addEventListener("input",()=>{p.transportJson=ke.value});let Se=s.createDiv({cls:"af-create-footer"}),ye=Se.createEl("button",{cls:"af-btn-sm danger"});A(ye,"trash-2","af-btn-icon"),ye.appendText(" Delete"),ye.onclick=async()=>{await this.plugin.repository.deleteChannel(n.name),new k.Notice(`Channel "${n.name}" deleted.`),await new Promise(E=>setTimeout(E,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},Se.createDiv({cls:"af-toolbar-spacer"});let Ie=Se.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Ie.onclick=()=>this.navigate("channels");let J=Se.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(J,"check","af-btn-icon"),J.appendText(" Save Changes"),J.onclick=async()=>{let E=pe=>pe.split(/[\n,]+/).map(st=>st.trim()).filter(Boolean),X=pe=>pe.split(",").map(st=>st.trim()).filter(Boolean),le;if(p.transportJson.trim())try{let pe=JSON.parse(p.transportJson);if(pe&&typeof pe=="object"&&!Array.isArray(pe))le=pe;else{new k.Notice("Transport config must be a JSON object.");return}}catch(pe){new k.Notice(`Transport JSON is invalid: ${pe instanceof Error?pe.message:String(pe)}`);return}else le={};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:E(p.allowedUsers),per_user_sessions:p.perUserSessions,channel_context:p.channelContext.trim(),tags:X(p.tags),body:p.body.trim(),transport:le}),new k.Notice(`Channel "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(pe){let st=pe instanceof Error?pe.message:String(pe);new k.Notice(`Failed to update channel: ${st}`)}}}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 r=a.filter(c=>(c.approvals??[]).some(l=>l.status==="pending"));if(r.length>0){let c=s.createDiv({cls:"af-section-card"}),d=c.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(d,"alert-triangle"),d.appendText(` Pending (${r.length})`);let h=c.createDiv({cls:"af-approvals-list"});for(let u of r)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"});A(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 r=e.createDiv({cls:"af-approval-item"}),o=r.createDiv({cls:"af-approval-item-icon"});n.status==="pending"?((0,k.setIcon)(o,"shield-check"),o.addClass("pending")):n.status==="approved"?((0,k.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,k.setIcon)(o,"x-circle"),o.addClass("rejected"));let c=r.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=r.createDiv({cls:"af-approval-item-actions"}),d=l.createEl("button",{cls:"af-btn-approve"});A(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"});A(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(P=>P.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let r=this.plugin.runtime.getSnapshot(),o=this.plugin.runtime.getRecentRuns().filter(P=>P.task===a),c=r.agents.find(P=>P.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,k.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"});A(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"});A(f,"play","af-btn-icon"),f.appendText(" Run Now"),f.onclick=()=>void this.plugin.runtime.runTaskNow(n);let g=s.createDiv({cls:"af-section-card"}),w=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(w,"file-text"),w.appendText(" Details");let v=g.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 y=n.schedule?this.humanizeCron(n.schedule):n.runAt??"Manual (run on demand)";this.renderConfigRow(v,"Schedule",y),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"});A(_,"message-square"),_.appendText(" Instructions"),x.createDiv({cls:"af-output-block",text:n.body||"(empty)"});let M=s.createDiv({cls:"af-section-card"}),C=M.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});A(C,"scroll-text"),C.appendText(" Recent Runs");let I=M.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(I,"scroll-text","No runs yet","");else for(let P of o.slice(0,10))this.renderTimelineItem(I,P)}handleSearch(e,s){if(s.querySelector(".af-search-results")?.remove(),e.length<2)return;let a=e.toLowerCase(),n=this.plugin.runtime.getSnapshot(),r=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 r.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"});A(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 r=n.createEl("button",{cls:"clickable-icon"});(0,k.setIcon)(r,"cross"),r.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,k.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 y={task:"from task override",agent:"from agent",settings:"from settings default","cli-default":"CLI default"},x=e.modelSource?` (${y[e.modelSource]??e.modelSource})`:"",S=e.concreteModel&&e.concreteModel!==e.model?` \u2192 ${e.concreteModel}`:"";this.renderDetailRow(c,"Model",`${e.model}${S}${x}`)}let p=5e4,m=y=>y.length>p?y.slice(0,p)+`
11792
11871
 
11793
11872
  ---
11794
- *Truncated (${(g.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:g,f=!!(e.finalResult&&e.finalResult.trim()),v=e.output?.trim()??"",k=f&&v.length>0&&v!==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(b.MarkdownRenderer.render(this.app,m(e.finalResult),x,"",this.plugin),k){let T=g.createEl("details",{cls:"af-run-transcript"}),C=T.createEl("summary");(0,b.setIcon)(C.createSpan({cls:"af-run-transcript-icon"}),"file-text"),C.createSpan({text:"Show full transcript"}),C.createSpan({cls:"af-run-transcript-meta"}).setText(`${(v.length/1024).toFixed(1)} KB`);let E=T.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});b.MarkdownRenderer.render(this.app,m(v),E,"",this.plugin)}}else if(v){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"});b.MarkdownRenderer.render(this.app,m(v),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(`
11795
- `)})}let w=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let g=w.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 y=w.createEl("button",{cls:"af-btn-sm primary"});P(y,"refresh-cw","af-btn-icon"),y.appendText(" Re-run Task"),y.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,b.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,b.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,b.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 l=e.createDiv({cls:"af-form-row af-schedule-time-row"});l.createDiv({cls:"af-form-label",text:"Time"});let c=l.createDiv({cls:"af-schedule-time-selects"}),d=c.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,C=d.createEl("option",{text:`${T} ${x}`,attr:{value:String(g)}});g===a.hour&&(C.selected=!0)}c.createSpan({cls:"af-schedule-colon",text:":"});let h=c.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)),y()}}let v=e.createDiv({cls:"af-form-row af-schedule-dom-row"});v.createDiv({cls:"af-form-label",text:"Day of month"});let k=v.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 w=()=>{let g=i.value,x=["daily","weekdays","weekly","monthly"].includes(g),T=g==="weekly",C=g==="monthly";l.style.display=x?"":"none",u.style.display=T?"":"none",v.style.display=C?"":"none"},y=()=>{let g=i.value,x=d.value,T=h.value,C="";switch(g){case"every_5m":C="*/5 * * * *";break;case"every_15m":C="*/15 * * * *";break;case"every_30m":C="*/30 * * * *";break;case"every_hour":C="0 * * * *";break;case"every_2h":C="0 */2 * * *";break;case"daily":C=`${T} ${x} * * *`;break;case"weekdays":C=`${T} ${x} * * 1-5`;break;case"weekly":{let L=Array.from(f).sort().join(",")||"1";C=`${T} ${x} * * ${L}`;break}case"monthly":C=`${T} ${x} ${k.value} * *`;break}s.schedule=C,s.type="recurring"};i.addEventListener("change",()=>{w(),y()}),d.addEventListener("change",y),h.addEventListener("change",y),k.addEventListener("change",y),w()}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"]],l="every_hour",c={"*/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"};c[s.heartbeatSchedule]?l=c[s.heartbeatSchedule]:(a.freq==="daily"||a.freq==="weekdays")&&(l="daily");for(let[v,k]of o){let w=i.createEl("option",{text:k,attr:{value:v}});v===l&&(w.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 v=0;v<24;v++){let k=v>=12?"PM":"AM",w=v===0?12:v>12?v-12:v,y=u.createEl("option",{text:`${w} ${k}`,attr:{value:String(v)}});v===a.hour&&(y.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let p=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=0;v<60;v+=5){let k=p.createEl("option",{text:String(v).padStart(2,"0"),attr:{value:String(v)}});v===a.minute&&(k.selected=!0)}let m=()=>{d.style.display=i.value==="daily"?"":"none"},f=()=>{let v=i.value,k=u.value,w=p.value;switch(v){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=`${w} ${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,l,,c]=n,d=parseInt(o??"9",10),h=parseInt(i??"0",10);if(l==="*"&&c==="*")return{...s,freq:"daily",hour:d,minute:h};if(l==="*"&&c==="1-5")return{...s,freq:"weekdays",hour:d,minute:h};if(l==="*"&&c!=="*"){let u=(c??"1").split(",").map(p=>parseInt(p,10));return{...s,freq:"weekly",hour:d,minute:h,days:u}}return c==="*"&&l!=="*"?{...s,freq:"monthly",hour:d,minute:h,dayOfMonth:parseInt(l??"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,l,,c]=n,d=(p,m)=>{let f=parseInt(p??"0",10),v=parseInt(m??"0",10),k=f>=12?"PM":"AM",w=f===0?12:f>12?f-12:f;return v===0?`${w} ${k}`:`${w}:${String(v).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==="*"&&l==="*"&&c==="*"?i==="*"?"Every minute":`Every hour at :${String(i).padStart(2,"0")}`:l==="*"&&c==="*"&&o!=="*"?`Daily at ${d(o??"0",i??"0")}`:l==="*"&&c==="1-5"&&o!=="*"?`Weekdays at ${d(o??"0",i??"0")}`:l==="*"&&c!=="*"&&o!=="*"?`${u(c??"1")} at ${d(o??"0",i??"0")}`:c==="*"&&l!=="*"&&o!=="*"?`Monthly on the ${l} 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,b.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 l=a.createDiv({cls:"af-detail-header-actions"}),c={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.
11873
+ *Truncated (${(y.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:y,f=!!(e.finalResult&&e.finalResult.trim()),g=e.output?.trim()??"",b=f&&g.length>0&&g!==e.finalResult.trim();if(f){let y=o.createDiv({cls:"af-slideover-section"});y.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=y.createDiv({cls:"af-output-block af-compact-md"});if(k.MarkdownRenderer.render(this.app,m(e.finalResult),x,"",this.plugin),b){let S=y.createEl("details",{cls:"af-run-transcript"}),_=S.createEl("summary");(0,k.setIcon)(_.createSpan({cls:"af-run-transcript-icon"}),"file-text"),_.createSpan({text:"Show full transcript"}),_.createSpan({cls:"af-run-transcript-meta"}).setText(`${(g.length/1024).toFixed(1)} KB`);let D=S.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});k.MarkdownRenderer.render(this.app,m(g),D,"",this.plugin)}}else if(g){let y=o.createDiv({cls:"af-slideover-section"});y.createDiv({cls:"af-slideover-section-title",text:"OUTPUT"});let x=y.createDiv({cls:"af-output-block af-compact-md"});k.MarkdownRenderer.render(this.app,m(g),x,"",this.plugin)}if(e.toolsUsed.length>0){let y=o.createDiv({cls:"af-slideover-section"});y.createDiv({cls:"af-slideover-section-title",text:"TOOLS USED"}),y.createDiv({cls:"af-output-block",text:e.toolsUsed.join(`
11874
+ `)})}let w=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let y=w.createEl("button",{cls:"af-btn-sm"});A(y,"external-link","af-btn-icon"),y.appendText(" Open Run Note"),y.onclick=()=>void this.plugin.openPath(e.filePath)}let v=w.createEl("button",{cls:"af-btn-sm primary"});A(v,"refresh-cw","af-btn-icon"),v.appendText(" Re-run Task"),v.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=y=>{y.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 r=e.createDiv({cls:"af-empty-state"}),o=r.createDiv({cls:"af-empty-icon"});(0,k.setIcon)(o,s),r.createDiv({cls:"af-empty-label",text:a}),n&&r.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 r=Math.round(n/6e4);if(r<60)return`${r}m`;let o=Math.round(r/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let a=e.map(r=>r.nextRun).filter(Boolean).sort()[0];return a?`${e.find(r=>r.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,k.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,k.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 r=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[y,x]of o){let S=r.createEl("option",{text:x,attr:{value:y}});y===a.freq&&(S.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 y=0;y<24;y++){let x=y>=12?"PM":"AM",S=y===0?12:y>12?y-12:y,_=d.createEl("option",{text:`${S} ${x}`,attr:{value:String(y)}});y===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 y=0;y<60;y+=5){let x=h.createEl("option",{text:String(y).padStart(2,"0"),attr:{value:String(y)}});y===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 y=0;y<7;y++){let x=p.createEl("button",{cls:`af-schedule-day-btn${f.has(y)?" active":""}`,text:m[y]});x.onclick=()=>{f.has(y)?f.delete(y):f.add(y),x.toggleClass("active",f.has(y)),v()}}let g=e.createDiv({cls:"af-form-row af-schedule-dom-row"});g.createDiv({cls:"af-form-label",text:"Day of month"});let b=g.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=1;y<=28;y++){let x=b.createEl("option",{text:String(y),attr:{value:String(y)}});y===a.dayOfMonth&&(x.selected=!0)}let w=()=>{let y=r.value,x=["daily","weekdays","weekly","monthly"].includes(y),S=y==="weekly",_=y==="monthly";c.style.display=x?"":"none",u.style.display=S?"":"none",g.style.display=_?"":"none"},v=()=>{let y=r.value,x=d.value,S=h.value,_="";switch(y){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":_=`${S} ${x} * * *`;break;case"weekdays":_=`${S} ${x} * * 1-5`;break;case"weekly":{let M=Array.from(f).sort().join(",")||"1";_=`${S} ${x} * * ${M}`;break}case"monthly":_=`${S} ${x} ${b.value} * *`;break}s.schedule=_,s.type="recurring"};r.addEventListener("change",()=>{w(),v()}),d.addEventListener("change",v),h.addEventListener("change",v),b.addEventListener("change",v),w()}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 r=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[g,b]of o){let w=r.createEl("option",{text:b,attr:{value:g}});g===c&&(w.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 g=0;g<24;g++){let b=g>=12?"PM":"AM",w=g===0?12:g>12?g-12:g,v=u.createEl("option",{text:`${w} ${b}`,attr:{value:String(g)}});g===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 g=0;g<60;g+=5){let b=p.createEl("option",{text:String(g).padStart(2,"0"),attr:{value:String(g)}});g===a.minute&&(b.selected=!0)}let m=()=>{d.style.display=r.value==="daily"?"":"none"},f=()=>{let g=r.value,b=u.value,w=p.value;switch(g){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=`${w} ${b} * * *`;break}};r.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[r,o,c,,l]=n,d=parseInt(o??"9",10),h=parseInt(r??"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[r,o,c,,l]=n,d=(p,m)=>{let f=parseInt(p??"0",10),g=parseInt(m??"0",10),b=f>=12?"PM":"AM",w=f===0?12:f>12?f-12:f;return g===0?`${w} ${b}`:`${w}:${String(g).padStart(2,"0")} ${b}`},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==="*"?r==="*"?"Every minute":`Every hour at :${String(r).padStart(2,"0")}`:c==="*"&&l==="*"&&o!=="*"?`Daily at ${d(o??"0",r??"0")}`:c==="*"&&l==="1-5"&&o!=="*"?`Weekdays at ${d(o??"0",r??"0")}`:c==="*"&&l!=="*"&&o!=="*"?`${u(l??"1")} at ${d(o??"0",r??"0")}`:l==="*"&&c!=="*"&&o!=="*"?`Monthly on the ${c} at ${d(o??"0",r??"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"}),r=n.createDiv({cls:"af-agent-card-avatar idle"});(0,k.setIcon)(r,"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.
11796
11875
  Follow existing code conventions. Write clean, well-tested code.
11797
11876
  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.
11798
11877
  Be concise and factual. Highlight anomalies clearly.
@@ -11800,13 +11879,13 @@ Include timestamps and relevant context in all reports.`},briefing:{label:"Brief
11800
11879
  Prioritize recent and important changes. Keep summaries concise.
11801
11880
  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.
11802
11881
  Focus on correctness, security, and maintainability.
11803
- 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,b.setIcon)(m,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",B=>{c.name=B}),this.createFormField(u,"Description","Monitors deployments and reports status","",B=>{c.description=B});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Avatar"});let v=f.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});v.addEventListener("input",()=>{c.avatar=v.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",B=>{c.tags=B});let k=u.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let w=k.createDiv({cls:"af-agent-card-toggle on"});w.onclick=()=>{let B=w.hasClass("on");w.toggleClass("on",!B),c.enabled=!B};let y=h.createDiv({cls:"af-create-section"}),g=y.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(x,"message-square"),g.createSpan({text:"System Prompt"});let T=y.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let C=T.createEl("select",{cls:"af-form-select"});for(let[B,{label:K}]of Object.entries(d))C.createEl("option",{text:K,attr:{value:B}});let L=y.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});L.addEventListener("input",()=>{c.systemPrompt=L.value}),C.addEventListener("change",()=>{let B=d[C.value];B&&C.value!=="none"&&(c.systemPrompt=B.prompt,L.value=B.prompt)});let E=h.createDiv({cls:"af-create-section"}),S=E.createDiv({cls:"af-create-section-header"}),I=S.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(I,"settings"),S.createSpan({text:"Runtime Config"});let A=E.createDiv({cls:"af-create-config-grid"}),O=A.createDiv({cls:"af-form-row"});O.createDiv({cls:"af-form-label",text:"Adapter"});let R=O.createEl("select",{cls:"af-form-select"}),z=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[B,K,Ue]of z){let we=R.createEl("option",{text:K,attr:{value:B,...Ue?{disabled:"true"}:{}}});B==="claude-code"&&(we.selected=!0)}let N=A.createDiv({cls:"af-form-row"}),j=N.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(j,`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 oe=N.createDiv({cls:"af-form-field-wrap"});_t(oe,{value:c.model,onChange:B=>{c.model=B}}),R.addEventListener("change",()=>{c.adapter=R.value});let te=A.createDiv({cls:"af-form-row"});te.createDiv({cls:"af-form-label",text:"Working Dir"});let ee=te.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});ee.addEventListener("input",()=>{c.cwd=ee.value});let V=A.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let G=V.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});G.addEventListener("input",()=>{let B=parseInt(G.value,10);!isNaN(B)&&B>0&&(c.timeout=B)});let $=A.createDiv({cls:"af-form-row"});$.createDiv({cls:"af-form-label",text:"Permission Mode"});let H=$.createEl("select",{cls:"af-form-select"}),se=[["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[B,K]of se)H.createEl("option",{text:K,attr:{value:B}});H.addEventListener("change",()=>{c.permissionMode=H.value});let re=A.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});H.addEventListener("change",()=>{let B=se.find(([K])=>K===H.value)?.[2]??"";re.textContent=B});let de=A.createDiv({cls:"af-form-row"});de.createDiv({cls:"af-form-label",text:"Effort Level"});let Le=de.createEl("select",{cls:"af-form-select"});for(let[B,K]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Le.createEl("option",{text:K,attr:{value:B}});Le.addEventListener("change",()=>{c.effort=Le.value}),A.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Ae=A.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 $e=Ae.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(c.autoCompactThreshold)}});$e.addEventListener("input",()=>{let B=parseInt($e.value,10);!isNaN(B)&&B>=0&&B<=100&&(c.autoCompactThreshold=B)}),A.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let B=this.plugin.runtime.getSnapshot().agents.filter(K=>K.wikiKeeper!==void 0);if(B.length>0){let K=A.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 we=K.createDiv({cls:"af-form-field-wrap"});for(let ge of B){let Fe=we.createEl("label",{cls:"af-form-checkbox-row"}),ue=Fe.createEl("input",{attr:{type:"checkbox"}});Fe.createSpan({text:` ${ge.name}`,cls:"af-form-checkbox-label"}),ue.addEventListener("change",()=>{ue.checked?c.wikiReferences.includes(ge.name)||c.wikiReferences.push(ge.name):c.wikiReferences=c.wikiReferences.filter(ze=>ze!==ge.name)})}}}{let B=h.createDiv({cls:"af-create-section"}),K=B.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ue,"heart-pulse");let we=K.createSpan({text:"Heartbeat"});this.addTooltip(we,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ge=B.createDiv({cls:"af-form-row af-form-row-toggle"});ge.createDiv({cls:"af-form-label",text:"Enabled"});let Fe=ge.createDiv({cls:"af-agent-card-toggle"}),ue=B.createDiv();ue.style.display="none",Fe.onclick=()=>{let Ze=Fe.hasClass("on");Fe.toggleClass("on",!Ze),c.heartbeatEnabled=!Ze,ue.style.display=Ze?"none":""},this.renderHeartbeatSchedule(ue,c);let ze=ue.createDiv({cls:"af-form-row af-form-row-toggle"}),F=ze.createDiv({cls:"af-form-label"});F.setText("Notify"),this.addTooltip(F,"Show an Obsidian notice when the heartbeat completes");let q=ze.createDiv({cls:"af-agent-card-toggle on"});q.onclick=()=>{let Ze=q.hasClass("on");q.toggleClass("on",!Ze),c.heartbeatNotify=!Ze};let ne=this.plugin.runtime.getSnapshot(),_e=ue.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 Ze of ne.channels)Oe.createEl("option",{text:Ze.name,attr:{value:Ze.name}});Oe.addEventListener("change",()=>{c.heartbeatChannel=Oe.value});let Re=ue.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=ue.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});rt.addEventListener("input",()=>{c.heartbeatBody=rt.value})}let Ee=h.createDiv({cls:"af-create-section"}),Te=Ee.createDiv({cls:"af-create-section-header"}),Pe=Te.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Pe,"puzzle"),Te.createSpan({text:"Skills"});let Be=this.plugin.runtime.getSnapshot();if(Be.skills.length>0){Ee.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let B=Ee.createDiv({cls:"af-create-skills-grid"});for(let K of Be.skills){let Ue=B.createDiv({cls:"af-create-skill-item"}),we=Ue.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});we.addEventListener("change",()=>{we.checked?c.selectedSkills.add(K.name):c.selectedSkills.delete(K.name)});let ge=Ue.createDiv({cls:"af-create-skill-label"});ge.createSpan({cls:"af-create-skill-name",text:K.name}),K.description&&ge.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${K.description}`})}}let je=Ee.createDiv({cls:"af-form-sublabel"});je.setText("Agent-specific skills"),this.addTooltip(je,"Custom skills/instructions only for this agent, not shared with others");let Y=Ee.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});Y.addEventListener("input",()=>{c.skillsBody=Y.value});{let B=h.createDiv({cls:"af-create-section"}),K=B.createDiv({cls:"af-create-section-header"}),Ue=K.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ue,"plug");let we=K.createSpan({text:"MCP Servers"});this.addTooltip(we,"Grant agent access to MCP servers");let ge=this.plugin.mcpManager.getCachedServers();if(ge===null){let Fe=B.createDiv({cls:"af-form-hint"});Fe.appendText("MCP servers not loaded. ");let ue=Fe.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});ue.onclick=ze=>{ze.preventDefault(),this.navigate("mcp")}}else if(ge.length===0)B.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Fe=B.createDiv({cls:"af-create-skills-grid"});for(let ue of ge){let ze=Fe.createDiv({cls:"af-mcp-agent-item"}),F=ze.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});F.addEventListener("change",()=>{F.checked?c.selectedMcpServers.add(ue.name):c.selectedMcpServers.delete(ue.name)});let ne=ze.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),_e=ne.createSpan({cls:`af-mcp-status-dot ${ue.enabled?ue.status:"disabled"}`});_e.title=ue.enabled?ue.status:"disabled",ne.createSpan({cls:"af-create-skill-name",text:ue.name});let De=ue.toolDetails.length||ue.tools.length;De>0?ne.createSpan({cls:"af-mcp-agent-tool-count",text:`${De} tools`}):ue.enabled?ue.status==="needs-auth"&&ne.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):ne.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let ae=h.createDiv({cls:"af-create-section"}),W=ae.createDiv({cls:"af-create-section-header"}),ve=W.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ve,"file-text");let be=W.createSpan({text:"Context"});this.addTooltip(be,"Project-specific context included in every run");let me=ae.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});me.addEventListener("input",()=>{c.contextBody=me.value});let Me=h.createDiv({cls:"af-create-section"}),X=Me.createDiv({cls:"af-create-section-header"}),D=X.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(D,"shield-check"),X.createSpan({text:"Permissions"}),this.createFormField(Me,"Approval required","git_push, file_delete","Comma-separated tool names",B=>{c.approvalRequired=B});let Z=Me.createDiv({cls:"af-form-row"});Z.createDiv({cls:"af-form-label",text:"Allowed Commands"});let le=Z.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
11882
+ 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,k.setIcon)(m,"user"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",N=>{l.name=N}),this.createFormField(u,"Description","Monitors deployments and reports status","",N=>{l.description=N});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Avatar"});let g=f.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});g.addEventListener("input",()=>{l.avatar=g.value}),this.createFormField(u,"Tags","devops, monitoring","Comma-separated",N=>{l.tags=N});let b=u.createDiv({cls:"af-form-row af-form-row-toggle"});b.createDiv({cls:"af-form-label",text:"Enabled"});let w=b.createDiv({cls:"af-agent-card-toggle on"});w.onclick=()=>{let N=w.hasClass("on");w.toggleClass("on",!N),l.enabled=!N};let v=h.createDiv({cls:"af-create-section"}),y=v.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(x,"message-square"),y.createSpan({text:"System Prompt"});let S=v.createDiv({cls:"af-form-row"});S.createDiv({cls:"af-form-label",text:"Template"});let _=S.createEl("select",{cls:"af-form-select"});for(let[N,{label:G}]of Object.entries(d))_.createEl("option",{text:G,attr:{value:N}});let M=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});M.addEventListener("input",()=>{l.systemPrompt=M.value}),_.addEventListener("change",()=>{let N=d[_.value];N&&_.value!=="none"&&(l.systemPrompt=N.prompt,M.value=N.prompt)});let D=h.createDiv({cls:"af-create-section"}),C=D.createDiv({cls:"af-create-section-header"}),I=C.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(I,"settings"),C.createSpan({text:"Runtime Config"});let P=D.createDiv({cls:"af-create-config-grid"}),B=P.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Adapter"});let L=B.createEl("select",{cls:"af-form-select"});for(let[N,G,Le]of Tr){let ve=L.createEl("option",{text:G,attr:{value:N,...Le?{disabled:"true"}:{}}});N==="claude-code"&&(ve.selected=!0)}let V=P.createDiv({cls:"af-form-row"}),U=V.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(U,`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||"CLI default"}).`);let H=V.createDiv({cls:"af-form-field-wrap"}),he=()=>{Et(H,{value:l.model,adapter:l.adapter,onChange:N=>{l.model=N}})};he();let me=()=>{};L.addEventListener("change",()=>{l.adapter=L.value,(Ss(l.adapter)?Fs:Os).some(G=>G.value===l.model.trim())&&(l.model=""),he(),l.permissionMode=wa(l.permissionMode,l.adapter),me()});let ue=P.createDiv({cls:"af-form-row"});ue.createDiv({cls:"af-form-label",text:"Working Dir"});let Q=ue.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});Q.addEventListener("input",()=>{l.cwd=Q.value});let Y=P.createDiv({cls:"af-form-row"});Y.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let q=Y.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});q.addEventListener("input",()=>{let N=parseInt(q.value,10);!isNaN(N)&&N>0&&(l.timeout=N)});let ee=P.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Permission Mode"});let te=ee.createEl("select",{cls:"af-form-select"}),oe=P.createDiv({cls:"af-form-hint",text:""});me=()=>{l.permissionMode=wa(l.permissionMode,l.adapter);let N=ba(l.adapter);te.empty();for(let[G,Le]of N){let ve=te.createEl("option",{text:Le,attr:{value:G}});G===l.permissionMode&&(ve.selected=!0)}oe.textContent=N.find(([G])=>G===te.value)?.[2]??""},me(),te.addEventListener("change",()=>{l.permissionMode=te.value,oe.textContent=ba(l.adapter).find(([N])=>N===te.value)?.[2]??""});let ie=P.createDiv({cls:"af-form-row"});ie.createDiv({cls:"af-form-label",text:"Effort Level"});let Ee=ie.createEl("select",{cls:"af-form-select"});for(let[N,G]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Ee.createEl("option",{text:G,attr:{value:N}});Ee.addEventListener("change",()=>{l.effort=Ee.value}),P.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let Pe=P.createDiv({cls:"af-form-row"}),Ue=Pe.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(Ue,"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=Pe.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 N=parseInt(He.value,10);!isNaN(N)&&N>=0&&N<=100&&(l.autoCompactThreshold=N)}),P.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let N=this.plugin.runtime.getSnapshot().agents.filter(G=>G.wikiKeeper!==void 0);if(N.length>0){let G=P.createDiv({cls:"af-form-row af-form-row-toggle"}),Le=G.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(Le,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let ve=G.createDiv({cls:"af-form-field-wrap"});for(let be of N){let Ne=ve.createEl("label",{cls:"af-form-checkbox-row"}),fe=Ne.createEl("input",{attr:{type:"checkbox"}});Ne.createSpan({text:` ${be.name}`,cls:"af-form-checkbox-label"}),fe.addEventListener("change",()=>{fe.checked?l.wikiReferences.includes(be.name)||l.wikiReferences.push(be.name):l.wikiReferences=l.wikiReferences.filter(Ye=>Ye!==be.name)})}}}{let N=h.createDiv({cls:"af-create-section"}),G=N.createDiv({cls:"af-create-section-header"}),Le=G.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Le,"heart-pulse");let ve=G.createSpan({text:"Heartbeat"});this.addTooltip(ve,"Autonomous periodic run \u2014 what the agent does when no one is asking");let be=N.createDiv({cls:"af-form-row af-form-row-toggle"});be.createDiv({cls:"af-form-label",text:"Enabled"});let Ne=be.createDiv({cls:"af-agent-card-toggle"}),fe=N.createDiv();fe.style.display="none",Ne.onclick=()=>{let at=Ne.hasClass("on");Ne.toggleClass("on",!at),l.heartbeatEnabled=!at,fe.style.display=at?"none":""},this.renderHeartbeatSchedule(fe,l);let Ye=fe.createDiv({cls:"af-form-row af-form-row-toggle"}),F=Ye.createDiv({cls:"af-form-label"});F.setText("Notify"),this.addTooltip(F,"Show an Obsidian notice when the heartbeat completes");let $=Ye.createDiv({cls:"af-agent-card-toggle on"});$.onclick=()=>{let at=$.hasClass("on");$.toggleClass("on",!at),l.heartbeatNotify=!at};let se=this.plugin.runtime.getSnapshot(),Ce=fe.createDiv({cls:"af-form-row"}),Me=Ce.createDiv({cls:"af-form-label"});Me.setText("Post to channel"),this.addTooltip(Me,"Heartbeat results are posted to this Slack channel when the run completes");let Be=Ce.createEl("select",{cls:"af-form-select"});Be.createEl("option",{text:"(none)",attr:{value:""}});for(let at of se.channels)Be.createEl("option",{text:at.name,attr:{value:at.name}});Be.addEventListener("change",()=>{l.heartbeatChannel=Be.value});let Fe=fe.createDiv({cls:"af-form-label"});Fe.style.width="auto",Fe.style.marginTop="12px",Fe.setText("Instruction"),this.addTooltip(Fe,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let dt=fe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});dt.addEventListener("input",()=>{l.heartbeatBody=dt.value})}let De=h.createDiv({cls:"af-create-section"}),_e=De.createDiv({cls:"af-create-section-header"}),Re=_e.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Re,"puzzle"),_e.createSpan({text:"Skills"});let $e=this.plugin.runtime.getSnapshot();if($e.skills.length>0){De.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let N=De.createDiv({cls:"af-create-skills-grid"});for(let G of $e.skills){let Le=N.createDiv({cls:"af-create-skill-item"}),ve=Le.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});ve.addEventListener("change",()=>{ve.checked?l.selectedSkills.add(G.name):l.selectedSkills.delete(G.name)});let be=Le.createDiv({cls:"af-create-skill-label"});be.createSpan({cls:"af-create-skill-name",text:G.name}),G.description&&be.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${G.description}`})}}let qe=De.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 z=De.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});z.addEventListener("input",()=>{l.skillsBody=z.value});{let N=h.createDiv({cls:"af-create-section"}),G=N.createDiv({cls:"af-create-section-header"}),Le=G.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Le,"plug");let ve=G.createSpan({text:"MCP Servers"});this.addTooltip(ve,"Grant agent access to MCP servers"),N.createDiv({cls:"af-form-hint",text:"Codex agents read servers from Codex's own config (`codex mcp add`); selections here are matched to those servers by name."});let be=this.plugin.mcpManager.getCachedServers();if(be===null){let Ne=N.createDiv({cls:"af-form-hint"});Ne.appendText("MCP servers not loaded. ");let fe=Ne.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});fe.onclick=Ye=>{Ye.preventDefault(),this.navigate("mcp")}}else if(be.length===0)N.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Ne=N.createDiv({cls:"af-create-skills-grid"});for(let fe of be){let Ye=Ne.createDiv({cls:"af-mcp-agent-item"}),F=Ye.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});F.addEventListener("change",()=>{F.checked?l.selectedMcpServers.add(fe.name):l.selectedMcpServers.delete(fe.name)});let se=Ye.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Ce=se.createSpan({cls:`af-mcp-status-dot ${fe.enabled?fe.status:"disabled"}`});Ce.title=fe.enabled?fe.status:"disabled",se.createSpan({cls:"af-create-skill-name",text:fe.name});let Me=fe.toolDetails.length||fe.tools.length;Me>0?se.createSpan({cls:"af-mcp-agent-tool-count",text:`${Me} tools`}):fe.enabled?fe.status==="needs-auth"&&se.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):se.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let Z=h.createDiv({cls:"af-create-section"}),j=Z.createDiv({cls:"af-create-section-header"}),ke=j.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(ke,"file-text");let Se=j.createSpan({text:"Context"});this.addTooltip(Se,"Project-specific context included in every run");let ye=Z.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});ye.addEventListener("input",()=>{l.contextBody=ye.value});let Ie=h.createDiv({cls:"af-create-section"}),J=Ie.createDiv({cls:"af-create-section-header"}),E=J.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(E,"shield-check"),J.createSpan({text:"Permissions"}),this.createFormField(Ie,"Approval required","git_push, file_delete","Comma-separated tool names",N=>{l.approvalRequired=N});let X=Ie.createDiv({cls:"af-form-row"});X.createDiv({cls:"af-form-label",text:"Allowed Commands"});let le=X.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
11804
11883
  Bash(python3 *)
11805
11884
  Read
11806
11885
  Edit
11807
- Write`,rows:"4"}});le.addEventListener("input",()=>{c.allowedCommands=le.value});let he=Me.createDiv({cls:"af-form-row"});he.createDiv({cls:"af-form-label",text:"Blocked Commands"});let Qe=he.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
11886
+ Write`,rows:"4"}});le.addEventListener("input",()=>{l.allowedCommands=le.value});let pe=Ie.createDiv({cls:"af-form-row"});pe.createDiv({cls:"af-form-label",text:"Blocked Commands"});let st=pe.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
11808
11887
  Bash(rm -rf *)
11809
- Bash(sudo *)`,rows:"4"}});Qe.addEventListener("input",()=>{c.blockedCommands=Qe.value});let vt=Me.createDiv({cls:"af-form-row"});vt.createDiv({cls:"af-form-label",text:"Memory enabled"});let Lt=vt.createDiv({cls:"af-agent-card-toggle on"});Lt.onclick=()=>{let B=Lt.hasClass("on");Lt.toggleClass("on",!B),c.memory=!B};let fs=s.createDiv({cls:"af-create-footer"}),ms=fs.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ms.onclick=()=>this.navigate("agents");let xt=fs.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(xt,"plus","af-btn-icon"),xt.appendText(" Create Agent"),xt.onclick=async()=>{let B=c.name.trim();if(!B){new b.Notice("Agent name is required.");return}let K=ke(B);if(this.plugin.repository.getAgentByName(K)){new b.Notice(`Agent "${K}" already exists.`);return}let Ue=we=>we.split(",").map(ge=>ge.trim()).filter(Boolean);try{let we=ge=>pe(ge).map(Fe=>Fe.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:K,description:c.description.trim(),avatar:c.avatar.trim(),tags:Ue(c.tags),systemPrompt:c.systemPrompt.trim(),model:c.model.trim()||"default",adapter:c.adapter,cwd:c.cwd.trim(),timeout:c.timeout,permissionMode:c.permissionMode,effort:c.effort||void 0,approvalRequired:Ue(c.approvalRequired),memory:c.memory,memoryMaxEntries:100,skills:Array.from(c.selectedSkills),mcpServers:Array.from(c.selectedMcpServers),skillsBody:c.skillsBody.trim(),contextBody:c.contextBody.trim(),enabled:c.enabled,permissionRules:{allow:we(c.allowedCommands),deny:we(c.blockedCommands)},autoCompactThreshold:c.autoCompactThreshold,wikiReferences:c.wikiReferences}),c.heartbeatEnabled&&c.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(K,{enabled:c.heartbeatEnabled,schedule:c.heartbeatSchedule.trim(),notify:c.heartbeatNotify,channel:c.heartbeatChannel,body:c.heartbeatBody.trim()}),new b.Notice(`Agent "${K}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",K)}catch(we){let ge=we instanceof Error?we.message:String(we);new b.Notice(`Failed to create agent: ${ge}`)}}}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,b.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 l=a.createDiv({cls:"af-detail-header-actions"}),c={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.
11888
+ Bash(sudo *)`,rows:"4"}});st.addEventListener("input",()=>{l.blockedCommands=st.value}),Ie.createDiv({cls:"af-form-hint",text:"On Codex agents these become execpolicy command rules \u2014 only Bash(cmd args *) prefixes are enforced; tool-name rules (Read/Write) and mid-pattern wildcards are ignored, and file/network access is governed by Permission Mode (the sandbox)."});let mt=Ie.createDiv({cls:"af-form-row"});mt.createDiv({cls:"af-form-label",text:"Memory enabled"});let Nt=mt.createDiv({cls:"af-agent-card-toggle on"});Nt.onclick=()=>{let N=Nt.hasClass("on");Nt.toggleClass("on",!N),l.memory=!N};let Ts=s.createDiv({cls:"af-create-footer"}),_s=Ts.createEl("button",{cls:"af-btn-sm",text:"Cancel"});_s.onclick=()=>this.navigate("agents");let Tt=Ts.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(Tt,"plus","af-btn-icon"),Tt.appendText(" Create Agent"),Tt.onclick=async()=>{let N=l.name.trim();if(!N){new k.Notice("Agent name is required.");return}let G=ge(N);if(this.plugin.repository.getAgentByName(G)){new k.Notice(`Agent "${G}" already exists.`);return}let Le=ve=>ve.split(",").map(be=>be.trim()).filter(Boolean);try{let ve=be=>re(be).map(Ne=>Ne.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:G,description:l.description.trim(),avatar:l.avatar.trim(),tags:Le(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:Le(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:ve(l.allowedCommands),deny:ve(l.blockedCommands)},autoCompactThreshold:l.autoCompactThreshold,wikiReferences:l.wikiReferences}),l.heartbeatEnabled&&l.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(G,{enabled:l.heartbeatEnabled,schedule:l.heartbeatSchedule.trim(),notify:l.heartbeatNotify,channel:l.heartbeatChannel,body:l.heartbeatBody.trim()}),new k.Notice(`Agent "${G}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",G)}catch(ve){let be=ve instanceof Error?ve.message:String(ve);new k.Notice(`Failed to create agent: ${be}`)}}}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"}),r=n.createDiv({cls:"af-agent-card-avatar idle"});(0,k.setIcon)(r,"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.
11810
11889
 
11811
11890
  Requirements:
11812
11891
  - Ensure required environment variables are set
@@ -11843,36 +11922,38 @@ Key behaviors:
11843
11922
  - Use tables and charts where appropriate
11844
11923
  - Always state the time range and filters applied
11845
11924
  - Flag anomalies and outliers explicitly
11846
- - 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,b.setIcon)(m,"puzzle"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",$=>{c.name=$}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",$=>{c.description=$}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",$=>{c.tags=$});let f=h.createDiv({cls:"af-create-section"}),v=f.createDiv({cls:"af-create-section-header"}),k=v.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(k,"file-text"),v.createSpan({text:"Core Instructions"});let w=f.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Template"});let y=w.createEl("select",{cls:"af-form-select"});for(let[$,{label:H}]of Object.entries(d))y.createEl("option",{text:H,attr:{value:$}});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",()=>{c.body=g.value}),y.addEventListener("change",()=>{let $=d[y.value];$&&y.value!=="none"&&(c.body=$.prompt,g.value=$.prompt)});let x=h.createDiv({cls:"af-create-section"}),T=x.createDiv({cls:"af-create-section-header"}),C=T.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(C,"wrench");let L=T.createSpan({text:"Tools"});this.addTooltip(L,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let E=x.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
11925
+ - 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,k.setIcon)(m,"puzzle"),p.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",q=>{l.name=q}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",q=>{l.description=q}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",q=>{l.tags=q});let f=h.createDiv({cls:"af-create-section"}),g=f.createDiv({cls:"af-create-section-header"}),b=g.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(b,"file-text"),g.createSpan({text:"Core Instructions"});let w=f.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Template"});let v=w.createEl("select",{cls:"af-form-select"});for(let[q,{label:ee}]of Object.entries(d))v.createEl("option",{text:ee,attr:{value:q}});let y=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"}});y.addEventListener("input",()=>{l.body=y.value}),v.addEventListener("change",()=>{let q=d[v.value];q&&v.value!=="none"&&(l.body=q.prompt,y.value=q.prompt)});let x=h.createDiv({cls:"af-create-section"}),S=x.createDiv({cls:"af-create-section-header"}),_=S.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(_,"wrench");let M=S.createSpan({text:"Tools"});this.addTooltip(M,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let D=x.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
11847
11926
 
11848
11927
  ### list
11849
11928
  Usage: tool list [--filter <query>]
11850
- ...`,rows:"8"}});E.addEventListener("input",()=>{c.toolsBody=E.value});let S=h.createDiv({cls:"af-create-section"}),I=S.createDiv({cls:"af-create-section-header"}),A=I.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(A,"book-open");let O=I.createSpan({text:"References"});this.addTooltip(O,"Background docs, conventions, cheat sheets");let R=S.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});R.addEventListener("input",()=>{c.referencesBody=R.value});let z=h.createDiv({cls:"af-create-section"}),N=z.createDiv({cls:"af-create-section-header"}),j=N.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(j,"message-circle");let oe=N.createSpan({text:"Examples"});this.addTooltip(oe,"Example prompts and ideal outputs showing how to use this skill");let te=z.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
11929
+ ...`,rows:"8"}});D.addEventListener("input",()=>{l.toolsBody=D.value});let C=h.createDiv({cls:"af-create-section"}),I=C.createDiv({cls:"af-create-section-header"}),P=I.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(P,"book-open");let B=I.createSpan({text:"References"});this.addTooltip(B,"Background docs, conventions, cheat sheets");let L=C.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});L.addEventListener("input",()=>{l.referencesBody=L.value});let V=h.createDiv({cls:"af-create-section"}),U=V.createDiv({cls:"af-create-section-header"}),H=U.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(H,"message-circle");let he=U.createSpan({text:"Examples"});this.addTooltip(he,"Example prompts and ideal outputs showing how to use this skill");let me=V.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
11851
11930
 
11852
11931
  User: Show me my tasks for today
11853
11932
 
11854
- Agent: ...`,rows:"6"}});te.addEventListener("input",()=>{c.examplesBody=te.value});let ee=s.createDiv({cls:"af-create-footer"}),V=ee.createEl("button",{cls:"af-btn-sm",text:"Cancel"});V.onclick=()=>this.navigate("skills");let G=ee.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(G,"plus","af-btn-icon"),G.appendText(" Create Skill"),G.onclick=async()=>{let $=c.name.trim();if(!$){new b.Notice("Skill name is required.");return}let H=ke($);if(this.plugin.repository.getSkillByName(H)){new b.Notice(`Skill "${H}" already exists.`);return}let se=re=>re.split(",").map(de=>de.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:H,description:c.description.trim(),tags:se(c.tags),body:c.body.trim(),toolsBody:c.toolsBody.trim(),referencesBody:c.referencesBody.trim(),examplesBody:c.examplesBody.trim()}),new b.Notice(`Skill "${H}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let de=re instanceof Error?re.message:String(re);new b.Notice(`Failed to create skill: ${de}`)}}}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(F=>F.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"}),l=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(l,"edit");let c=o.createDiv();c.createDiv({cls:"af-detail-header-name",text:`Edit Agent: ${n.name}`}),c.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(`
11933
+ Agent: ...`,rows:"6"}});me.addEventListener("input",()=>{l.examplesBody=me.value});let ue=s.createDiv({cls:"af-create-footer"}),Q=ue.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Q.onclick=()=>this.navigate("skills");let Y=ue.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(Y,"plus","af-btn-icon"),Y.appendText(" Create Skill"),Y.onclick=async()=>{let q=l.name.trim();if(!q){new k.Notice("Skill name is required.");return}let ee=ge(q);if(this.plugin.repository.getSkillByName(ee)){new k.Notice(`Skill "${ee}" already exists.`);return}let te=oe=>oe.split(",").map(ie=>ie.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:ee,description:l.description.trim(),tags:te(l.tags),body:l.body.trim(),toolsBody:l.toolsBody.trim(),referencesBody:l.referencesBody.trim(),examplesBody:l.examplesBody.trim()}),new k.Notice(`Skill "${ee}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(oe){let ie=oe instanceof Error?oe.message:String(oe);new k.Notice(`Failed to create skill: ${ie}`)}}}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(F=>F.name===a);if(!n){this.renderEmptyState(s,"bot","Agent not found",`Agent "${a}" was not found`);return}let r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,k.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=r.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(`
11855
11934
  `),blockedCommands:n.permissionRules.deny.join(`
11856
- `),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel,autoCompactThreshold:n.autoCompactThreshold??85,wikiReferences:(n.wikiReferences??[]).map(F=>F.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,b.setIcon)(f,"user"),m.createSpan({text:"Identity"});let v=p.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Name"});let k=v.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","",F=>{h.description=F},n.description??"");let w=p.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Avatar"});let y=w.createEl("button",{cls:"af-avatar-picker-btn"}),g=y.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(g,{...n,avatar:h.avatar??n.avatar}),y.createSpan({cls:"af-avatar-picker-label",text:h.avatar||n.avatar||"Pick icon\u2026"}),y.addEventListener("click",()=>{new sa(this.app,h.avatar??n.avatar,F=>{h.avatar=F,g.empty(),(0,b.setIcon)(g,F),y.querySelector(".af-avatar-picker-label")?.setText(F)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",F=>{h.tags=F},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 F=T.hasClass("on");T.toggleClass("on",!F),h.enabled=!F};let C=u.createDiv({cls:"af-create-section"}),L=C.createDiv({cls:"af-create-section-header"}),E=L.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(E,"message-square"),L.createSpan({text:"System Prompt"});let S=C.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 I=u.createDiv({cls:"af-create-section"}),A=I.createDiv({cls:"af-create-section-header"}),O=A.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(O,"settings"),A.createSpan({text:"Runtime Config"});let R=I.createDiv({cls:"af-create-config-grid"}),z=R.createDiv({cls:"af-form-row"});z.createDiv({cls:"af-form-label",text:"Adapter"});let N=z.createEl("select",{cls:"af-form-select"}),j=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[F,q,ne]of j){let _e=N.createEl("option",{text:q,attr:{value:F,...ne?{disabled:"true"}:{}}});F===n.adapter&&(_e.selected=!0)}let oe=R.createDiv({cls:"af-form-row"}),te=oe.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(te,`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 ee=oe.createDiv({cls:"af-form-field-wrap"});_t(ee,{value:h.model,onChange:F=>{h.model=F}}),N.addEventListener("change",()=>{h.adapter=N.value});let V=R.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Working Dir"});let G=V.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});G.addEventListener("input",()=>{h.cwd=G.value});let $=R.createDiv({cls:"af-form-row"});$.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let H=$.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});H.addEventListener("input",()=>{let F=parseInt(H.value,10);!isNaN(F)&&F>0&&(h.timeout=F)});let se=R.createDiv({cls:"af-form-row"});se.createDiv({cls:"af-form-label",text:"Permission Mode"});let re=se.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[F,q]of de){let ne=re.createEl("option",{text:q,attr:{value:F}});F===n.permissionMode&&(ne.selected=!0)}re.addEventListener("change",()=>{h.permissionMode=re.value});let Le=R.createDiv({cls:"af-form-hint",text:de.find(([F])=>F===n.permissionMode)?.[2]??""});re.addEventListener("change",()=>{let F=de.find(([q])=>q===re.value)?.[2]??"";Le.textContent=F});let Ae=R.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[F,q]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let ne=Ne.createEl("option",{text:q,attr:{value:F}});F===(n.effort??"")&&(ne.selected=!0)}Ne.addEventListener("change",()=>{h.effort=Ne.value}),R.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let $e=R.createDiv({cls:"af-form-row"}),Ee=$e.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 Te=$e.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});Te.addEventListener("input",()=>{let F=parseInt(Te.value,10);!isNaN(F)&&F>=0&&F<=100&&(h.autoCompactThreshold=F)}),R.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let F=this.plugin.runtime.getSnapshot().agents.filter(q=>q.wikiKeeper!==void 0);if(F.length>0){let q=R.createDiv({cls:"af-form-row af-form-row-toggle"}),ne=q.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(ne,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let _e=q.createDiv({cls:"af-form-field-wrap"});for(let De of F){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 F=u.createDiv({cls:"af-create-section"}),q=F.createDiv({cls:"af-create-section-header"}),ne=q.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(ne,"heart-pulse");let _e=q.createSpan({text:"Heartbeat"});this.addTooltip(_e,"Autonomous periodic run \u2014 what the agent does when no one is asking");let De=F.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=F.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"}),Ze=rt.createDiv({cls:"af-form-label"});Ze.setText("Notify"),this.addTooltip(Ze,"Show an Obsidian notice when the heartbeat completes");let na=rt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});na.onclick=()=>{let ot=na.hasClass("on");na.toggleClass("on",!ot),h.heartbeatNotify=!ot};let Hi=this.plugin.runtime.getSnapshot(),Ga=Re.createDiv({cls:"af-form-row"}),Va=Ga.createDiv({cls:"af-form-label"});Va.setText("Post to channel"),this.addTooltip(Va,"Heartbeat results are posted to this Slack channel when the run completes");let gs=Ga.createEl("select",{cls:"af-form-select"});gs.createEl("option",{text:"(none)",attr:{value:""}});for(let ot of Hi.channels){let qi=gs.createEl("option",{text:ot.name,attr:{value:ot.name}});ot.name===h.heartbeatChannel&&(qi.selected=!0)}gs.addEventListener("change",()=>{h.heartbeatChannel=gs.value});let ys=Re.createDiv({cls:"af-form-label"});ys.style.width="auto",ys.style.marginTop="12px",ys.setText("Instruction"),this.addTooltip(ys,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let ia=Re.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});ia.value=h.heartbeatBody,ia.addEventListener("input",()=>{h.heartbeatBody=ia.value})}let Pe=u.createDiv({cls:"af-create-section"}),Be=Pe.createDiv({cls:"af-create-section-header"}),je=Be.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(je,"puzzle"),Be.createSpan({text:"Skills"});let Y=this.plugin.runtime.getSnapshot();if(Y.skills.length>0){Pe.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let F=Pe.createDiv({cls:"af-create-skills-grid"});for(let q of Y.skills){let ne=F.createDiv({cls:"af-create-skill-item"}),_e=ne.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedSkills.has(q.name),_e.addEventListener("change",()=>{_e.checked?h.selectedSkills.add(q.name):h.selectedSkills.delete(q.name)});let De=ne.createDiv({cls:"af-create-skill-label"});De.createSpan({cls:"af-create-skill-name",text:q.name}),q.description&&De.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${q.description}`})}}let ae=Pe.createDiv({cls:"af-form-sublabel"});ae.setText("Agent-specific skills"),this.addTooltip(ae,"Custom skills/instructions only for this agent, not shared with others");let W=Pe.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});W.value=n.skillsBody,W.addEventListener("input",()=>{h.skillsBody=W.value});let ve=u.createDiv({cls:"af-create-section"}),be=ve.createDiv({cls:"af-create-section-header"}),me=be.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(me,"plug");let Me=be.createSpan({text:"MCP Servers"});this.addTooltip(Me,"Grant agent access to MCP servers");let X=this.plugin.mcpManager.getCachedServers();if(X===null){let F=ve.createDiv({cls:"af-form-hint"});F.appendText("MCP servers not loaded. ");let q=F.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});q.onclick=ne=>{ne.preventDefault(),this.navigate("mcp")}}else if(X.length===0)ve.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let F=ve.createDiv({cls:"af-create-skills-grid"});for(let q of X){let ne=F.createDiv({cls:"af-mcp-agent-item"}),_e=ne.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedMcpServers.has(q.name),_e.addEventListener("change",()=>{_e.checked?h.selectedMcpServers.add(q.name):h.selectedMcpServers.delete(q.name)});let Oe=ne.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Re=Oe.createSpan({cls:`af-mcp-status-dot ${q.enabled?q.status:"disabled"}`});Re.title=q.enabled?q.status:"disabled",Oe.createSpan({cls:"af-create-skill-name",text:q.name});let rt=q.toolDetails.length||q.tools.length;rt>0?Oe.createSpan({cls:"af-mcp-agent-tool-count",text:`${rt} tools`}):q.enabled?q.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"}),Z=D.createDiv({cls:"af-create-section-header"}),le=Z.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(le,"file-text");let he=Z.createSpan({text:"Context"});this.addTooltip(he,"Project-specific context included in every run");let Qe=D.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});Qe.value=n.contextBody,Qe.addEventListener("input",()=>{h.contextBody=Qe.value});let vt=u.createDiv({cls:"af-create-section"}),Lt=vt.createDiv({cls:"af-create-section-header"}),fs=Lt.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(fs,"shield-check"),Lt.createSpan({text:"Permissions"}),this.createFormField(vt,"Approval required","git_push, file_delete","Comma-separated tool names",F=>{h.approvalRequired=F},n.approvalRequired.join(", "));let ms=vt.createDiv({cls:"af-form-row"});ms.createDiv({cls:"af-form-label",text:"Allowed Commands"});let xt=ms.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
11935
+ `),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel,autoCompactThreshold:n.autoCompactThreshold??85,wikiReferences:(n.wikiReferences??[]).map(F=>F.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,k.setIcon)(f,"user"),m.createSpan({text:"Identity"});let g=p.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Name"});let b=g.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});b.style.opacity="0.6",this.createFormField(p,"Description","Monitors deployments and reports status","",F=>{h.description=F},n.description??"");let w=p.createDiv({cls:"af-form-row"});w.createDiv({cls:"af-form-label",text:"Avatar"});let v=w.createEl("button",{cls:"af-avatar-picker-btn"}),y=v.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(y,{...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 ya(this.app,h.avatar??n.avatar,F=>{h.avatar=F,y.empty(),(0,k.setIcon)(y,F),v.querySelector(".af-avatar-picker-label")?.setText(F)}).open()}),this.createFormField(p,"Tags","devops, monitoring","Comma-separated",F=>{h.tags=F},n.tags.join(", "));let x=p.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Enabled"});let S=x.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});S.onclick=()=>{let F=S.hasClass("on");S.toggleClass("on",!F),h.enabled=!F};let _=u.createDiv({cls:"af-create-section"}),M=_.createDiv({cls:"af-create-section-header"}),D=M.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(D,"message-square"),M.createSpan({text:"System Prompt"});let C=_.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});C.value=n.body,C.addEventListener("input",()=>{h.systemPrompt=C.value});let I=u.createDiv({cls:"af-create-section"}),P=I.createDiv({cls:"af-create-section-header"}),B=P.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(B,"settings"),P.createSpan({text:"Runtime Config"});let L=I.createDiv({cls:"af-create-config-grid"}),V=L.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Adapter"});let U=V.createEl("select",{cls:"af-form-select"});for(let[F,$,se]of Tr){let Ce=U.createEl("option",{text:$,attr:{value:F,...se?{disabled:"true"}:{}}});(F===n.adapter||Ss(n.adapter)&&F==="codex")&&(Ce.selected=!0)}let H=L.createDiv({cls:"af-form-row"}),he=H.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(he,`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||"CLI default"}).`);let me=H.createDiv({cls:"af-form-field-wrap"}),ue=()=>{Et(me,{value:h.model,adapter:h.adapter,onChange:F=>{h.model=F}})};ue();let Q=()=>{};U.addEventListener("change",()=>{h.adapter=U.value,(Ss(h.adapter)?Fs:Os).some($=>$.value===h.model.trim())&&(h.model=""),ue(),h.permissionMode=wa(h.permissionMode,h.adapter),Q()});let Y=L.createDiv({cls:"af-form-row"});Y.createDiv({cls:"af-form-label",text:"Working Dir"});let q=Y.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});q.addEventListener("input",()=>{h.cwd=q.value});let ee=L.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let te=ee.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});te.addEventListener("input",()=>{let F=parseInt(te.value,10);!isNaN(F)&&F>0&&(h.timeout=F)});let oe=L.createDiv({cls:"af-form-row"});oe.createDiv({cls:"af-form-label",text:"Permission Mode"});let ie=oe.createEl("select",{cls:"af-form-select"}),Ee=L.createDiv({cls:"af-form-hint",text:""});Q=()=>{h.permissionMode=wa(h.permissionMode,h.adapter);let F=ba(h.adapter);ie.empty();for(let[$,se]of F){let Ce=ie.createEl("option",{text:se,attr:{value:$}});$===h.permissionMode&&(Ce.selected=!0)}Ee.textContent=F.find(([$])=>$===ie.value)?.[2]??""},Q(),ie.addEventListener("change",()=>{h.permissionMode=ie.value,Ee.textContent=ba(h.adapter).find(([F])=>F===ie.value)?.[2]??""});let Pe=L.createDiv({cls:"af-form-row"});Pe.createDiv({cls:"af-form-label",text:"Effort Level"});let Ue=Pe.createEl("select",{cls:"af-form-select"});for(let[F,$]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let se=Ue.createEl("option",{text:$,attr:{value:F}});F===(n.effort??"")&&(se.selected=!0)}Ue.addEventListener("change",()=>{h.effort=Ue.value}),L.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});let He=L.createDiv({cls:"af-form-row"}),De=He.createDiv({cls:"af-form-label",text:"Auto-compact at"});this.addTooltip(De,"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 _e=He.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",min:"0",max:"100",value:String(h.autoCompactThreshold)}});_e.addEventListener("input",()=>{let F=parseInt(_e.value,10);!isNaN(F)&&F>=0&&F<=100&&(h.autoCompactThreshold=F)}),L.createDiv({cls:"af-form-hint",text:"0 disables auto-compact"});{let F=this.plugin.runtime.getSnapshot().agents.filter($=>$.wikiKeeper!==void 0);if(F.length>0){let $=L.createDiv({cls:"af-form-row af-form-row-toggle"}),se=$.createDiv({cls:"af-form-label",text:"Wiki access"});this.addTooltip(se,"Lets this agent read + cite from the selected Wiki Keeper scopes (requires the wiki-query skill).");let Ce=$.createDiv({cls:"af-form-field-wrap"});for(let Me of F){let Be=Ce.createEl("label",{cls:"af-form-checkbox-row"}),Fe=Be.createEl("input",{attr:{type:"checkbox"}});h.wikiReferences.includes(Me.name)&&(Fe.checked=!0),Be.createSpan({text:` ${Me.name}`,cls:"af-form-checkbox-label"}),Fe.addEventListener("change",()=>{Fe.checked?h.wikiReferences.includes(Me.name)||h.wikiReferences.push(Me.name):h.wikiReferences=h.wikiReferences.filter(dt=>dt!==Me.name)})}}}if(n.isFolder){let F=u.createDiv({cls:"af-create-section"}),$=F.createDiv({cls:"af-create-section-header"}),se=$.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(se,"heart-pulse");let Ce=$.createSpan({text:"Heartbeat"});this.addTooltip(Ce,"Autonomous periodic run \u2014 what the agent does when no one is asking");let Me=F.createDiv({cls:"af-form-row af-form-row-toggle"});Me.createDiv({cls:"af-form-label",text:"Enabled"});let Be=Me.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Fe=F.createDiv();Fe.style.display=h.heartbeatEnabled?"":"none",Be.onclick=()=>{let ht=Be.hasClass("on");Be.toggleClass("on",!ht),h.heartbeatEnabled=!ht,Fe.style.display=ht?"none":""},this.renderHeartbeatSchedule(Fe,h);let dt=Fe.createDiv({cls:"af-form-row af-form-row-toggle"}),at=dt.createDiv({cls:"af-form-label"});at.setText("Notify"),this.addTooltip(at,"Show an Obsidian notice when the heartbeat completes");let Sa=dt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});Sa.onclick=()=>{let ht=Sa.hasClass("on");Sa.toggleClass("on",!ht),h.heartbeatNotify=!ht};let Ir=this.plugin.runtime.getSnapshot(),vn=Fe.createDiv({cls:"af-form-row"}),bn=vn.createDiv({cls:"af-form-label"});bn.setText("Post to channel"),this.addTooltip(bn,"Heartbeat results are posted to this Slack channel when the run completes");let As=vn.createEl("select",{cls:"af-form-select"});As.createEl("option",{text:"(none)",attr:{value:""}});for(let ht of Ir.channels){let Lr=As.createEl("option",{text:ht.name,attr:{value:ht.name}});ht.name===h.heartbeatChannel&&(Lr.selected=!0)}As.addEventListener("change",()=>{h.heartbeatChannel=As.value});let Es=Fe.createDiv({cls:"af-form-label"});Es.style.width="auto",Es.style.marginTop="12px",Es.setText("Instruction"),this.addTooltip(Es,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let Ca=Fe.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 Re=u.createDiv({cls:"af-create-section"}),$e=Re.createDiv({cls:"af-create-section-header"}),qe=$e.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(qe,"puzzle"),$e.createSpan({text:"Skills"});let z=this.plugin.runtime.getSnapshot();if(z.skills.length>0){Re.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let F=Re.createDiv({cls:"af-create-skills-grid"});for(let $ of z.skills){let se=F.createDiv({cls:"af-create-skill-item"}),Ce=se.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});Ce.checked=h.selectedSkills.has($.name),Ce.addEventListener("change",()=>{Ce.checked?h.selectedSkills.add($.name):h.selectedSkills.delete($.name)});let Me=se.createDiv({cls:"af-create-skill-label"});Me.createSpan({cls:"af-create-skill-name",text:$.name}),$.description&&Me.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${$.description}`})}}let Z=Re.createDiv({cls:"af-form-sublabel"});Z.setText("Agent-specific skills"),this.addTooltip(Z,"Custom skills/instructions only for this agent, not shared with others");let j=Re.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});j.value=n.skillsBody,j.addEventListener("input",()=>{h.skillsBody=j.value});let ke=u.createDiv({cls:"af-create-section"}),Se=ke.createDiv({cls:"af-create-section-header"}),ye=Se.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(ye,"plug");let Ie=Se.createSpan({text:"MCP Servers"});this.addTooltip(Ie,"Grant agent access to MCP servers"),ke.createDiv({cls:"af-form-hint",text:"Codex agents read servers from Codex's own config (`codex mcp add`); selections here are matched to those servers by name."});let J=this.plugin.mcpManager.getCachedServers();if(J===null){let F=ke.createDiv({cls:"af-form-hint"});F.appendText("MCP servers not loaded. ");let $=F.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});$.onclick=se=>{se.preventDefault(),this.navigate("mcp")}}else if(J.length===0)ke.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let F=ke.createDiv({cls:"af-create-skills-grid"});for(let $ of J){let se=F.createDiv({cls:"af-mcp-agent-item"}),Ce=se.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});Ce.checked=h.selectedMcpServers.has($.name),Ce.addEventListener("change",()=>{Ce.checked?h.selectedMcpServers.add($.name):h.selectedMcpServers.delete($.name)});let Be=se.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Fe=Be.createSpan({cls:`af-mcp-status-dot ${$.enabled?$.status:"disabled"}`});Fe.title=$.enabled?$.status:"disabled",Be.createSpan({cls:"af-create-skill-name",text:$.name});let dt=$.toolDetails.length||$.tools.length;dt>0?Be.createSpan({cls:"af-mcp-agent-tool-count",text:`${dt} tools`}):$.enabled?$.status==="needs-auth"&&Be.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Be.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let E=u.createDiv({cls:"af-create-section"}),X=E.createDiv({cls:"af-create-section-header"}),le=X.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(le,"file-text");let pe=X.createSpan({text:"Context"});this.addTooltip(pe,"Project-specific context included in every run");let st=E.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});st.value=n.contextBody,st.addEventListener("input",()=>{h.contextBody=st.value});let mt=u.createDiv({cls:"af-create-section"}),Nt=mt.createDiv({cls:"af-create-section-header"}),Ts=Nt.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(Ts,"shield-check"),Nt.createSpan({text:"Permissions"}),this.createFormField(mt,"Approval required","git_push, file_delete","Comma-separated tool names",F=>{h.approvalRequired=F},n.approvalRequired.join(", "));let _s=mt.createDiv({cls:"af-form-row"});_s.createDiv({cls:"af-form-label",text:"Allowed Commands"});let Tt=_s.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
11857
11936
  Bash(python3 *)
11858
11937
  Read
11859
11938
  Edit
11860
- Write`,rows:"4"}});xt.value=n.permissionRules.allow.join(`
11861
- `),xt.addEventListener("input",()=>{h.allowedCommands=xt.value});let B=vt.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Blocked Commands"});let K=B.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
11939
+ Write`,rows:"4"}});Tt.value=n.permissionRules.allow.join(`
11940
+ `),Tt.addEventListener("input",()=>{h.allowedCommands=Tt.value});let N=mt.createDiv({cls:"af-form-row"});N.createDiv({cls:"af-form-label",text:"Blocked Commands"});let G=N.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
11862
11941
  Bash(rm -rf *)
11863
- Bash(sudo *)`,rows:"4"}});K.value=n.permissionRules.deny.join(`
11864
- `),K.addEventListener("input",()=>{h.blockedCommands=K.value});let Ue=vt.createDiv({cls:"af-form-row"});Ue.createDiv({cls:"af-form-label",text:"Memory enabled"});let we=Ue.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});we.onclick=()=>{let F=we.hasClass("on");we.toggleClass("on",!F),h.memory=!F};let ge=s.createDiv({cls:"af-create-footer"}),Fe=ge.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),ge.createDiv({cls:"af-toolbar-spacer"});let ue=ge.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ue.onclick=()=>this.navigate("agent-detail",n.name);let ze=ge.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(ze,"check","af-btn-icon"),ze.appendText(" Save Changes"),ze.onclick=async()=>{let F=q=>q.split(",").map(ne=>ne.trim()).filter(Boolean);try{let q=ne=>pe(ne).map(_e=>_e.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:F(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:F(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:q(h.allowedCommands),deny:q(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 b.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(q){let ne=q instanceof Error?q.message:String(q);new b.Notice(`Failed to update agent: ${ne}`)}}}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,b.setIcon)(o,"plus");let l=i.createDiv();l.createDiv({cls:"af-detail-header-name",text:"Create New Task"}),l.createDiv({cls:"af-detail-header-desc",text:"Configure a new task for your fleet"}),n.createDiv({cls:"af-detail-header-actions"});let c={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,b.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",Y=>{c.title=Y});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 Y of a.agents)f.createEl("option",{text:Y.name,attr:{value:Y.name}});f.addEventListener("change",()=>{c.agent=f.value});let v=h.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Priority"});let k=v.createEl("select",{cls:"af-form-select"}),w=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[Y,ae]of w){let W=k.createEl("option",{text:ae,attr:{value:Y}});Y==="medium"&&(W.selected=!0)}k.addEventListener("change",()=>{c.priority=k.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",Y=>{c.tags=Y});let y=d.createDiv({cls:"af-create-section"}),g=y.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(x,"message-square"),g.createSpan({text:"Instructions"});let T=y.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});T.addEventListener("input",()=>{c.body=T.value});let C=d.createDiv({cls:"af-create-section"}),L=C.createDiv({cls:"af-create-section-header"}),E=L.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(E,"clock"),L.createSpan({text:"Schedule"});let S=C.createDiv({cls:"af-form-row af-form-row-toggle"});S.createDiv({cls:"af-form-label",text:"Enable schedule"});let I=S.createDiv({cls:"af-agent-card-toggle"}),A=C.createDiv({cls:"af-schedule-body"});A.style.display="none",I.onclick=()=>{let Y=I.hasClass("on");I.toggleClass("on",!Y),c.scheduleEnabled=!Y,A.style.display=Y?"none":"",Y?c.type="immediate":c.type=c.scheduleMode==="once"?"once":"recurring"};let O=A.createDiv({cls:"af-form-row"});O.createDiv({cls:"af-form-label",text:"Mode"});let R=O.createEl("select",{cls:"af-form-select"});for(let[Y,ae]of[["recurring","Recurring"],["once","One-time"]])R.createEl("option",{text:ae,attr:{value:Y}});let z=A.createDiv(),N=A.createDiv();N.style.display="none",this.renderInlineSchedule(z,c);let j=N.createDiv({cls:"af-form-row"});j.createDiv({cls:"af-form-label",text:"Run at"});let oe=j.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});c.runAt=new Date(oe.value).toISOString(),oe.addEventListener("input",()=>{c.runAt=oe.value?new Date(oe.value).toISOString():""}),R.addEventListener("change",()=>{c.scheduleMode=R.value,z.style.display=c.scheduleMode==="recurring"?"":"none",N.style.display=c.scheduleMode==="once"?"":"none",c.scheduleEnabled&&(c.type=c.scheduleMode==="once"?"once":"recurring")});let te=A.createDiv({cls:"af-form-row af-form-row-toggle"});te.createDiv({cls:"af-form-label",text:"Enabled"});let ee=te.createDiv({cls:"af-agent-card-toggle on"});ee.onclick=()=>{let Y=ee.hasClass("on");ee.toggleClass("on",!Y),c.enabled=!Y};let V=A.createDiv({cls:"af-form-row af-form-row-toggle"});V.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let $=V.createDiv({cls:`af-agent-card-toggle${c.catchUp?" on":""}`});$.onclick=()=>{let Y=$.hasClass("on");$.toggleClass("on",!Y),c.catchUp=!Y};let H=d.createDiv({cls:"af-create-section"}),se=H.createDiv({cls:"af-create-section-header"}),re=se.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(re,"gauge"),se.createSpan({text:"Execution"});let de=H.createDiv({cls:"af-form-row"}),Le=de.createDiv({cls:"af-form-label",text:"Model"}),Ae=de.createDiv({cls:"af-form-field-wrap"}),Ne=Y=>{Ae.empty();let ae=a.agents.find(W=>W.name===Y);_t(Ae,{value:c.model,onChange:W=>{c.model=W},allowInherit:!0,inheritPlaceholder:ae?`Inherit from ${ae.name}${ae.model?` (${ae.model})`:""}`:"Inherit from agent"})};Ne(c.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 $e=H.createDiv({cls:"af-form-row"}),Ee=$e.createDiv({cls:"af-form-label",text:"Effort"}),Te=$e.createEl("select",{cls:"af-form-select"});for(let[Y,ae]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let W=Te.createEl("option",{text:ae,attr:{value:Y}});Y===c.effort&&(W.selected=!0)}Te.addEventListener("change",()=>{c.effort=Te.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 je=Pe.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(je,"plus","af-btn-icon"),je.appendText(" Create Task"),je.onclick=async()=>{let Y=c.title.trim();if(!Y){new b.Notice("Task title is required.");return}let ae=ke(Y),W=me=>me.split(",").map(Me=>Me.trim()).filter(Boolean),ve=c.scheduleEnabled?c.scheduleMode==="once"?"once":"recurring":"immediate",be={task_id:ae,agent:c.agent,type:ve,priority:c.priority,enabled:c.enabled,created:this.toLocalISO(new Date),run_count:0,catch_up:c.catchUp,effort:c.effort||void 0,model:c.model||void 0,tags:W(c.tags)};if(ve==="recurring")be.schedule=c.schedule.trim()||"0 9 * * *";else if(ve==="once"){if(!c.runAt){new b.Notice("Pick a date/time for the one-time run.");return}be.run_at=c.runAt}try{let me=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),ae);await this.plugin.app.vault.create(me,J(be,c.body.trim()||"Describe the task here.")),new b.Notice(`Task "${ae}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",ae)}catch(me){let Me=me instanceof Error?me.message:String(me);new b.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"}),l=o.createDiv({cls:"af-detail-header-left"}),c=l.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(c,"edit");let d=l.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"}),v=f.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(v,"file-text"),f.createSpan({text:"Task Details"});let k=m.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let w=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});w.style.opacity="0.6";let y=m.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Agent"});let g=y.createEl("select",{cls:"af-form-select"});for(let D of i.agents){let Z=g.createEl("option",{text:D.name,attr:{value:D.name}});D.name===n.agent&&(Z.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"}),C=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[D,Z]of C){let le=T.createEl("option",{text:Z,attr:{value:D}});D===n.priority&&(le.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 L=p.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),S=E.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(S,"message-square"),E.createSpan({text:"Instructions"});let I=L.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});I.value=n.body,I.addEventListener("input",()=>{u.body=I.value});let A=p.createDiv({cls:"af-create-section"}),O=A.createDiv({cls:"af-create-section-header"}),R=O.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(R,"clock"),O.createSpan({text:"Schedule"});let z=A.createDiv({cls:"af-form-row af-form-row-toggle"});z.createDiv({cls:"af-form-label",text:"Enable schedule"});let N=z.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),j=A.createDiv({cls:"af-schedule-body"});j.style.display=h?"":"none",N.onclick=()=>{let D=N.hasClass("on");N.toggleClass("on",!D),u.scheduleEnabled=!D,j.style.display=D?"none":"",D?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let oe=j.createDiv({cls:"af-form-row"});oe.createDiv({cls:"af-form-label",text:"Mode"});let te=oe.createEl("select",{cls:"af-form-select"});for(let[D,Z]of[["recurring","Recurring"],["once","One-time"]]){let le=te.createEl("option",{text:Z,attr:{value:D}});D===u.scheduleMode&&(le.selected=!0)}let ee=j.createDiv(),V=j.createDiv();ee.style.display=u.scheduleMode==="recurring"?"":"none",V.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(ee,u);let G=V.createDiv({cls:"af-form-row"});G.createDiv({cls:"af-form-label",text:"Run at"});let $=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),H=G.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:$}});u.runAt||(u.runAt=new Date(H.value).toISOString()),H.addEventListener("input",()=>{u.runAt=H.value?new Date(H.value).toISOString():""}),te.addEventListener("change",()=>{u.scheduleMode=te.value,ee.style.display=u.scheduleMode==="recurring"?"":"none",V.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let se=j.createDiv({cls:"af-form-row af-form-row-toggle"});se.createDiv({cls:"af-form-label",text:"Enabled"});let re=se.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});re.onclick=()=>{let D=re.hasClass("on");re.toggleClass("on",!D),u.enabled=!D};let de=j.createDiv({cls:"af-form-row af-form-row-toggle"});de.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Ae=de.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"}),$e=Ne.createDiv({cls:"af-create-section-header"}),Ee=$e.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(Ee,"gauge"),$e.createSpan({text:"Execution"});let Te=Ne.createDiv({cls:"af-form-row"}),Pe=Te.createDiv({cls:"af-form-label",text:"Effort"}),Be=Te.createEl("select",{cls:"af-form-select"}),je=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[D,Z]of je){let le=Be.createEl("option",{text:Z,attr:{value:D}});D===u.effort&&(le.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 Y=Ne.createDiv({cls:"af-form-row"}),ae=Y.createDiv({cls:"af-form-label",text:"Model"}),W=Y.createDiv({cls:"af-form-field-wrap"}),ve=D=>{W.empty();let Z=i.agents.find(le=>le.name===D);_t(W,{value:u.model,onChange:le=>{u.model=le},allowInherit:!0,inheritPlaceholder:Z?`Inherit from ${Z.name}${Z.model?` (${Z.model})`:""}`:"Inherit from agent"})};ve(u.agent),g.addEventListener("change",()=>ve(g.value)),this.addTooltip(ae,"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 be=s.createDiv({cls:"af-create-footer"}),me=be.createEl("button",{cls:"af-btn-sm danger"});P(me,"trash-2","af-btn-icon"),me.appendText(" Delete"),me.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new b.Notice(`Task "${n.taskId}" deleted.`),await new Promise(D=>setTimeout(D,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},be.createDiv({cls:"af-toolbar-spacer"});let Me=be.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Me.onclick=()=>this.navigate("task-detail",n.taskId);let X=be.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(X,"check","af-btn-icon"),X.appendText(" Save Changes"),X.onclick=async()=>{let D=le=>le.split(",").map(he=>he.trim()).filter(Boolean),Z=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(Z==="once"&&!u.runAt){new b.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:Z,priority:u.priority,schedule:Z==="recurring"?u.schedule.trim():"",runAt:Z==="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 b.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(le){let he=le instanceof Error?le.message:String(le);new b.Notice(`Failed to update task: ${he}`)}}}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(se=>se.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"}),l=o.createDiv({cls:"af-agent-card-avatar idle"});(0,b.setIcon)(l,"edit");let c=o.createDiv();c.createDiv({cls:"af-detail-header-name",text:`Edit Skill: ${n.name}`}),c.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,b.setIcon)(f,"puzzle"),m.createSpan({text:"Identity"});let v=p.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Name"});let k=v.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","",se=>{h.description=se},n.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",se=>{h.tags=se},n.tags.join(", "));let w=u.createDiv({cls:"af-create-section"}),y=w.createDiv({cls:"af-create-section-header"}),g=y.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(g,"file-text"),y.createSpan({text:"Core Instructions"});let x=w.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"}),C=T.createDiv({cls:"af-create-section-header"}),L=C.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(L,"wrench");let E=C.createSpan({text:"Tools"});this.addTooltip(E,"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
11942
+ Bash(sudo *)`,rows:"4"}});G.value=n.permissionRules.deny.join(`
11943
+ `),G.addEventListener("input",()=>{h.blockedCommands=G.value}),mt.createDiv({cls:"af-form-hint",text:"On Codex agents these become execpolicy command rules \u2014 only Bash(cmd args *) prefixes are enforced; tool-name rules (Read/Write) and mid-pattern wildcards are ignored, and file/network access is governed by Permission Mode (the sandbox)."});let Le=mt.createDiv({cls:"af-form-row"});Le.createDiv({cls:"af-form-label",text:"Memory enabled"});let ve=Le.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});ve.onclick=()=>{let F=ve.hasClass("on");ve.toggleClass("on",!F),h.memory=!F};let be=s.createDiv({cls:"af-create-footer"}),Ne=be.createEl("button",{cls:"af-btn-sm danger"});A(Ne,"trash-2","af-btn-icon"),Ne.appendText(" Delete"),Ne.onclick=()=>void this.plugin.deleteAgent(n.name),be.createDiv({cls:"af-toolbar-spacer"});let fe=be.createEl("button",{cls:"af-btn-sm",text:"Cancel"});fe.onclick=()=>this.navigate("agent-detail",n.name);let Ye=be.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(Ye,"check","af-btn-icon"),Ye.appendText(" Save Changes"),Ye.onclick=async()=>{let F=$=>$.split(",").map(se=>se.trim()).filter(Boolean);try{let $=se=>re(se).map(Ce=>Ce.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:F(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:F(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:$(h.allowedCommands),deny:$(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 k.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch($){let se=$ instanceof Error?$.message:String($);new k.Notice(`Failed to update agent: ${se}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-detail-header"}),r=n.createDiv({cls:"af-detail-header-left"}),o=r.createDiv({cls:"af-agent-card-avatar idle"});(0,k.setIcon)(o,"plus");let c=r.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,k.setIcon)(p,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",z=>{l.title=z});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 z of a.agents)f.createEl("option",{text:z.name,attr:{value:z.name}});f.addEventListener("change",()=>{l.agent=f.value});let g=h.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Priority"});let b=g.createEl("select",{cls:"af-form-select"}),w=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[z,Z]of w){let j=b.createEl("option",{text:Z,attr:{value:z}});z==="medium"&&(j.selected=!0)}b.addEventListener("change",()=>{l.priority=b.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",z=>{l.tags=z});let v=d.createDiv({cls:"af-create-section"}),y=v.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(x,"message-square"),y.createSpan({text:"Instructions"});let S=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});S.addEventListener("input",()=>{l.body=S.value});let _=d.createDiv({cls:"af-create-section"}),M=_.createDiv({cls:"af-create-section-header"}),D=M.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(D,"clock"),M.createSpan({text:"Schedule"});let C=_.createDiv({cls:"af-form-row af-form-row-toggle"});C.createDiv({cls:"af-form-label",text:"Enable schedule"});let I=C.createDiv({cls:"af-agent-card-toggle"}),P=_.createDiv({cls:"af-schedule-body"});P.style.display="none",I.onclick=()=>{let z=I.hasClass("on");I.toggleClass("on",!z),l.scheduleEnabled=!z,P.style.display=z?"none":"",z?l.type="immediate":l.type=l.scheduleMode==="once"?"once":"recurring"};let B=P.createDiv({cls:"af-form-row"});B.createDiv({cls:"af-form-label",text:"Mode"});let L=B.createEl("select",{cls:"af-form-select"});for(let[z,Z]of[["recurring","Recurring"],["once","One-time"]])L.createEl("option",{text:Z,attr:{value:z}});let V=P.createDiv(),U=P.createDiv();U.style.display="none",this.renderInlineSchedule(V,l);let H=U.createDiv({cls:"af-form-row"});H.createDiv({cls:"af-form-label",text:"Run at"});let he=H.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:this.toDatetimeLocal(new Date(Date.now()+36e5))}});l.runAt=new Date(he.value).toISOString(),he.addEventListener("input",()=>{l.runAt=he.value?new Date(he.value).toISOString():""}),L.addEventListener("change",()=>{l.scheduleMode=L.value,V.style.display=l.scheduleMode==="recurring"?"":"none",U.style.display=l.scheduleMode==="once"?"":"none",l.scheduleEnabled&&(l.type=l.scheduleMode==="once"?"once":"recurring")});let me=P.createDiv({cls:"af-form-row af-form-row-toggle"});me.createDiv({cls:"af-form-label",text:"Enabled"});let ue=me.createDiv({cls:"af-agent-card-toggle on"});ue.onclick=()=>{let z=ue.hasClass("on");ue.toggleClass("on",!z),l.enabled=!z};let Q=P.createDiv({cls:"af-form-row af-form-row-toggle"});Q.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let q=Q.createDiv({cls:`af-agent-card-toggle${l.catchUp?" on":""}`});q.onclick=()=>{let z=q.hasClass("on");q.toggleClass("on",!z),l.catchUp=!z};let ee=d.createDiv({cls:"af-create-section"}),te=ee.createDiv({cls:"af-create-section-header"}),oe=te.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(oe,"gauge"),te.createSpan({text:"Execution"});let ie=ee.createDiv({cls:"af-form-row"}),Ee=ie.createDiv({cls:"af-form-label",text:"Model"}),Pe=ie.createDiv({cls:"af-form-field-wrap"}),Ue=z=>{Pe.empty();let Z=a.agents.find(j=>j.name===z);Et(Pe,{value:l.model,adapter:Z?.adapter,onChange:j=>{l.model=j},allowInherit:!0,inheritPlaceholder:Z?`Inherit from ${Z.name}${Z.model?` (${Z.model})`:""}`:"Inherit from agent"})};Ue(l.agent),f.addEventListener("change",()=>Ue(f.value)),this.addTooltip(Ee,"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=ee.createDiv({cls:"af-form-row"}),De=He.createDiv({cls:"af-form-label",text:"Effort"}),_e=He.createEl("select",{cls:"af-form-select"});for(let[z,Z]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let j=_e.createEl("option",{text:Z,attr:{value:z}});z===l.effort&&(j.selected=!0)}_e.addEventListener("change",()=>{l.effort=_e.value}),this.addTooltip(De,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let Re=s.createDiv({cls:"af-create-footer"}),$e=Re.createEl("button",{cls:"af-btn-sm",text:"Cancel"});$e.onclick=()=>this.navigate("kanban");let qe=Re.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(qe,"plus","af-btn-icon"),qe.appendText(" Create Task"),qe.onclick=async()=>{let z=l.title.trim();if(!z){new k.Notice("Task title is required.");return}let Z=ge(z),j=ye=>ye.split(",").map(Ie=>Ie.trim()).filter(Boolean),ke=l.scheduleEnabled?l.scheduleMode==="once"?"once":"recurring":"immediate",Se={task_id:Z,agent:l.agent,type:ke,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:j(l.tags)};if(ke==="recurring")Se.schedule=l.schedule.trim()||"0 9 * * *";else if(ke==="once"){if(!l.runAt){new k.Notice("Pick a date/time for the one-time run.");return}Se.run_at=l.runAt}try{let ye=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),Z);await this.plugin.app.vault.create(ye,K(Se,l.body.trim()||"Describe the task here.")),new k.Notice(`Task "${Z}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",Z)}catch(ye){let Ie=ye instanceof Error?ye.message:String(ye);new k.Notice(`Failed to create task: ${Ie}`)}}}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(E=>E.taskId===a);if(!n){this.renderEmptyState(s,"circle-dot","Task not found",`Task "${a}" was not found`);return}let r=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,k.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"}),g=f.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(g,"file-text"),f.createSpan({text:"Task Details"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Title"});let w=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});w.style.opacity="0.6";let v=m.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Agent"});let y=v.createEl("select",{cls:"af-form-select"});for(let E of r.agents){let X=y.createEl("option",{text:E.name,attr:{value:E.name}});E.name===n.agent&&(X.selected=!0)}y.addEventListener("change",()=>{u.agent=y.value});let x=m.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Priority"});let S=x.createEl("select",{cls:"af-form-select"}),_=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[E,X]of _){let le=S.createEl("option",{text:X,attr:{value:E}});E===n.priority&&(le.selected=!0)}S.addEventListener("change",()=>{u.priority=S.value}),this.createFormField(m,"Tags","monitoring, critical","Comma-separated",E=>{u.tags=E},n.tags.join(", "));let M=p.createDiv({cls:"af-create-section"}),D=M.createDiv({cls:"af-create-section-header"}),C=D.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(C,"message-square"),D.createSpan({text:"Instructions"});let I=M.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});I.value=n.body,I.addEventListener("input",()=>{u.body=I.value});let P=p.createDiv({cls:"af-create-section"}),B=P.createDiv({cls:"af-create-section-header"}),L=B.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(L,"clock"),B.createSpan({text:"Schedule"});let V=P.createDiv({cls:"af-form-row af-form-row-toggle"});V.createDiv({cls:"af-form-label",text:"Enable schedule"});let U=V.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),H=P.createDiv({cls:"af-schedule-body"});H.style.display=h?"":"none",U.onclick=()=>{let E=U.hasClass("on");U.toggleClass("on",!E),u.scheduleEnabled=!E,H.style.display=E?"none":"",E?u.type="immediate":u.type=u.scheduleMode==="once"?"once":"recurring"};let he=H.createDiv({cls:"af-form-row"});he.createDiv({cls:"af-form-label",text:"Mode"});let me=he.createEl("select",{cls:"af-form-select"});for(let[E,X]of[["recurring","Recurring"],["once","One-time"]]){let le=me.createEl("option",{text:X,attr:{value:E}});E===u.scheduleMode&&(le.selected=!0)}let ue=H.createDiv(),Q=H.createDiv();ue.style.display=u.scheduleMode==="recurring"?"":"none",Q.style.display=u.scheduleMode==="once"?"":"none",this.renderInlineSchedule(ue,u);let Y=Q.createDiv({cls:"af-form-row"});Y.createDiv({cls:"af-form-label",text:"Run at"});let q=u.runAt?this.toDatetimeLocal(new Date(u.runAt)):this.toDatetimeLocal(new Date(Date.now()+36e5)),ee=Y.createEl("input",{cls:"af-form-input",attr:{type:"datetime-local",value:q}});u.runAt||(u.runAt=new Date(ee.value).toISOString()),ee.addEventListener("input",()=>{u.runAt=ee.value?new Date(ee.value).toISOString():""}),me.addEventListener("change",()=>{u.scheduleMode=me.value,ue.style.display=u.scheduleMode==="recurring"?"":"none",Q.style.display=u.scheduleMode==="once"?"":"none",u.scheduleEnabled&&(u.type=u.scheduleMode==="once"?"once":"recurring")});let te=H.createDiv({cls:"af-form-row af-form-row-toggle"});te.createDiv({cls:"af-form-label",text:"Enabled"});let oe=te.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});oe.onclick=()=>{let E=oe.hasClass("on");oe.toggleClass("on",!E),u.enabled=!E};let ie=H.createDiv({cls:"af-form-row af-form-row-toggle"});ie.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let Pe=ie.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});Pe.onclick=()=>{let E=Pe.hasClass("on");Pe.toggleClass("on",!E),u.catchUp=!E};let Ue=p.createDiv({cls:"af-create-section"}),He=Ue.createDiv({cls:"af-create-section-header"}),De=He.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(De,"gauge"),He.createSpan({text:"Execution"});let _e=Ue.createDiv({cls:"af-form-row"}),Re=_e.createDiv({cls:"af-form-label",text:"Effort"}),$e=_e.createEl("select",{cls:"af-form-select"}),qe=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[E,X]of qe){let le=$e.createEl("option",{text:X,attr:{value:E}});E===u.effort&&(le.selected=!0)}$e.addEventListener("change",()=>{u.effort=$e.value}),this.addTooltip(Re,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let z=Ue.createDiv({cls:"af-form-row"}),Z=z.createDiv({cls:"af-form-label",text:"Model"}),j=z.createDiv({cls:"af-form-field-wrap"}),ke=E=>{j.empty();let X=r.agents.find(le=>le.name===E);Et(j,{value:u.model,adapter:X?.adapter,onChange:le=>{u.model=le},allowInherit:!0,inheritPlaceholder:X?`Inherit from ${X.name}${X.model?` (${X.model})`:""}`:"Inherit from agent"})};ke(u.agent),y.addEventListener("change",()=>ke(y.value)),this.addTooltip(Z,"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 Se=s.createDiv({cls:"af-create-footer"}),ye=Se.createEl("button",{cls:"af-btn-sm danger"});A(ye,"trash-2","af-btn-icon"),ye.appendText(" Delete"),ye.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new k.Notice(`Task "${n.taskId}" deleted.`),await new Promise(E=>setTimeout(E,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},Se.createDiv({cls:"af-toolbar-spacer"});let Ie=Se.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Ie.onclick=()=>this.navigate("task-detail",n.taskId);let J=Se.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(J,"check","af-btn-icon"),J.appendText(" Save Changes"),J.onclick=async()=>{let E=le=>le.split(",").map(pe=>pe.trim()).filter(Boolean),X=u.scheduleEnabled?u.scheduleMode==="once"?"once":"recurring":"immediate";if(X==="once"&&!u.runAt){new k.Notice("Pick a date/time for the one-time run.");return}try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:X,priority:u.priority,schedule:X==="recurring"?u.schedule.trim():"",runAt:X==="once"?u.runAt:"",enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:E(u.tags),body:u.body.trim()}),new k.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(le){let pe=le instanceof Error?le.message:String(le);new k.Notice(`Failed to update task: ${pe}`)}}}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(te=>te.name===a);if(!n){this.renderEmptyState(s,"puzzle","Skill not found",`Skill "${a}" was not found`);return}let r=s.createDiv({cls:"af-detail-header"}),o=r.createDiv({cls:"af-detail-header-left"}),c=o.createDiv({cls:"af-agent-card-avatar idle"});(0,k.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=r.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,k.setIcon)(f,"puzzle"),m.createSpan({text:"Identity"});let g=p.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Name"});let b=g.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});b.style.opacity="0.6",this.createFormField(p,"Description","Manage tasks and projects via CLI","",te=>{h.description=te},n.description??""),this.createFormField(p,"Tags","productivity, tasks","Comma-separated",te=>{h.tags=te},n.tags.join(", "));let w=u.createDiv({cls:"af-create-section"}),v=w.createDiv({cls:"af-create-section-header"}),y=v.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(y,"file-text"),v.createSpan({text:"Core Instructions"});let x=w.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 S=u.createDiv({cls:"af-create-section"}),_=S.createDiv({cls:"af-create-section-header"}),M=_.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(M,"wrench");let D=_.createSpan({text:"Tools"});this.addTooltip(D,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let C=S.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
11865
11944
 
11866
11945
  ### list
11867
- ...`,rows:"8"}});S.value=n.toolsBody,S.addEventListener("input",()=>{h.toolsBody=S.value});let I=u.createDiv({cls:"af-create-section"}),A=I.createDiv({cls:"af-create-section-header"}),O=A.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(O,"book-open");let R=A.createSpan({text:"References"});this.addTooltip(R,"Background docs, conventions, cheat sheets");let z=I.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});z.value=n.referencesBody,z.addEventListener("input",()=>{h.referencesBody=z.value});let N=u.createDiv({cls:"af-create-section"}),j=N.createDiv({cls:"af-create-section-header"}),oe=j.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(oe,"message-circle");let te=j.createSpan({text:"Examples"});this.addTooltip(te,"Example prompts and ideal outputs showing how to use this skill");let ee=N.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
11868
- ...`,rows:"6"}});ee.value=n.examplesBody,ee.addEventListener("input",()=>{h.examplesBody=ee.value});let V=s.createDiv({cls:"af-create-footer"}),G=V.createEl("button",{cls:"af-btn-sm danger"});P(G,"trash-2","af-btn-icon"),G.appendText(" Delete"),G.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new b.Notice(`Skill "${n.name}" deleted.`),await new Promise(se=>setTimeout(se,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},V.createDiv({cls:"af-toolbar-spacer"});let $=V.createEl("button",{cls:"af-btn-sm",text:"Cancel"});$.onclick=()=>this.navigate("skills");let H=V.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(H,"check","af-btn-icon"),H.appendText(" Save Changes"),H.onclick=async()=>{let se=re=>re.split(",").map(de=>de.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:se(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new b.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(re){let de=re instanceof Error?re.message:String(re);new b.Notice(`Failed to update skill: ${de}`)}}}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 c=s.createDiv({cls:"af-mcp-progress"}),d=c.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=c.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});m.style.width="15%";let f=c.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),v=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(v),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 l=s.createDiv({cls:"af-agents-grid"});for(let c of o)this.renderMcpCard(l,c)}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,b.setIcon)(o,"plug");let l=n.createDiv({cls:"af-agent-card-titleblock"});l.createDiv({cls:"af-agent-card-name",text:s.name});let c=l.createDiv({cls:"af-agent-card-desc af-mcp-meta"});c.createSpan({cls:"af-mcp-type-badge",text:s.type}),s.scope!=="unknown"&&c.createSpan({cls:"af-badge",text:s.scope});let d=n.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});d.onclick=y=>{y.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,b.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,b.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let y=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:y})}let p=s.url??s.command??"";p&&a.createDiv({cls:"af-mcp-command",text:Mt(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"}),v=f.createDiv({cls:"af-mcp-tool-count"}),k=v.createSpan();(0,b.setIcon)(k,"wrench"),v.createSpan({text:` ${m}`});let w=s.toolDetails.length>0?s.toolDetails.map(y=>y.name):s.tools;if(w.length>0){let y=f.createDiv({cls:"af-mcp-tool-chips"}),g=w.slice(0,4);for(let x of g)y.createSpan({cls:"af-mcp-tool-chip",text:x});w.length>4&&y.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${w.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,b.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,b.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 y=a.createDiv({cls:"af-mcp-hint-row"});y.createSpan({text:"Tools available to agents via Claude \u2014 "});let g=y.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 b.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new b.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 b.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new b.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=pe(e)[0]??e,n=a.split(/(?<=[.!?])\s/)[0]??a,i=n.length<a.length?n:a;return i.length<=s?i:i.slice(0,s-1)+"\u2026"}openMcpDetailSlideover(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:e.name});let i=n.createEl("button",{cls:"clickable-icon"});(0,b.setIcon)(i,"cross"),i.onclick=()=>s.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 l=o.createDiv({cls:"af-slideover-section"});l.createDiv({cls:"af-slideover-section-title",text:"SERVER INFO"}),this.renderDetailRow(l,"Name",e.name),this.renderDetailRow(l,"Type",e.type),this.renderDetailRow(l,"Status",e.status),this.renderDetailRow(l,"Scope",e.scope),e.url&&this.renderDetailRow(l,"URL",e.url),e.command&&this.renderDetailRow(l,"Command",e.command),e.args&&this.renderDetailRow(l,"Args",e.args);let c=o.createDiv({cls:"af-slideover-section"}),d=e.toolDetails.length||e.tools.length;if(c.createDiv({cls:"af-slideover-section-title",text:`AVAILABLE TOOLS (${d})`}),e.toolDetails.length>0)for(let m of e.toolDetails){let f=c.createDiv({cls:"af-mcp-tool-detail"}),v=f.createDiv({cls:"af-mcp-tool-detail-header"}),k=v.createSpan({cls:"af-mcp-tool-detail-name"}),w=k.createSpan();if((0,b.setIcon)(w,"wrench"),k.createSpan({text:` ${m.name}`}),m.inputSchema){let y=m.inputSchema.required??[];y.length>0&&v.createSpan({cls:"af-mcp-tool-param-count",text:`${y.length} param${y.length!==1?"s":""}`})}if(m.description){let y=pe(m.description).filter(T=>T.trim()),g=y.slice(0,2).join(" ").trim();if(y.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 y=m.inputSchema.properties,g=new Set(m.inputSchema.required??[]);if(y&&Object.keys(y).length>0){let x=f.createDiv({cls:"af-mcp-tool-params"});for(let[T,C]of Object.entries(y)){let L=x.createDiv({cls:"af-mcp-tool-param"});L.createSpan({cls:"af-mcp-tool-param-name",text:T}),C.type&&L.createSpan({cls:"af-mcp-tool-param-type",text:C.type}),g.has(T)&&L.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),C.description&&L.createSpan({cls:"af-mcp-tool-param-desc",text:Mt(C.description,80)})}}}}else if(e.tools.length>0)for(let m of e.tools)c.createDiv({cls:"af-mcp-tool-item",text:m});else c.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,b.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,b.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 b.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(m){let f=m instanceof Error?m.message:String(m);new b.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,b.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 l={name:"",transport:"stdio",scope:"user",command:"",args:"",envVars:"",url:"",headers:""},c=s.createDiv({cls:"af-create-form"}),d=c.createDiv({cls:"af-create-section"}),h=d.createDiv({cls:"af-create-section-header"}),u=h.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",j=>{l.name=j});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 v=d.createDiv({cls:"af-form-row"}),k=v.createDiv({cls:"af-form-label"});k.setText("Scope"),this.addTooltip(k,"local: this project only, user: available across all projects");let w=v.createEl("select",{cls:"af-form-select"});w.createEl("option",{text:"user",attr:{value:"user"}}),w.createEl("option",{text:"local",attr:{value:"local"}}),w.addEventListener("change",()=>{l.scope=w.value});let y=c.createDiv({cls:"af-create-section"}),g=y.createDiv({cls:"af-create-section-header"}),x=g.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(x,"terminal"),g.createSpan({text:"Process Configuration"}),this.createFormField(y,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",j=>{l.command=j}),this.createFormField(y,"Arguments","--port 3000","Space-separated arguments (optional)",j=>{l.args=j});let T=y.createDiv({cls:"af-form-label"});T.setText("Environment variables"),this.addTooltip(T,"One KEY=VALUE per line");let C=y.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-...
11869
- DEBUG=true`,rows:"3"}});C.addEventListener("input",()=>{l.envVars=C.value});let L=c.createDiv({cls:"af-create-section"}),E=L.createDiv({cls:"af-create-section-header"}),S=E.createSpan({cls:"af-create-section-icon"});(0,b.setIcon)(S,"globe"),E.createSpan({text:"Remote Server Configuration"}),this.createFormField(L,"URL","https://mcp.example.com/sse","Server endpoint URL",j=>{l.url=j});let I=L.createDiv({cls:"af-form-label"});I.setText("Custom headers"),this.addTooltip(I,"One Header: Value per line (optional)");let A=L.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});A.addEventListener("input",()=>{l.headers=A.value});let O=()=>{y.style.display=l.transport==="stdio"?"":"none",L.style.display=l.transport!=="stdio"?"":"none"};f.addEventListener("change",()=>{l.transport=f.value,O()}),O();let R=s.createDiv({cls:"af-create-footer"}),z=R.createEl("button",{cls:"af-btn-sm",text:"Cancel"});z.onclick=()=>this.navigate("mcp");let N=R.createEl("button",{cls:"af-btn-sm primary af-create-submit"});P(N,"plus","af-btn-icon"),N.appendText(" Add Server"),N.onclick=async()=>{let j=l.name.trim();if(!j){new b.Notice("Server name is required.");return}if(l.transport==="stdio"){if(!l.command.trim()){new b.Notice("Command is required for stdio servers.");return}}else if(!l.url.trim()){new b.Notice("URL is required for HTTP/SSE servers.");return}let oe={};if(l.envVars.trim())for(let V of pe(l.envVars)){let G=V.trim();if(!G)continue;let $=G.indexOf("=");if($<=0){new b.Notice(`Invalid env var: ${G}`);return}oe[G.slice(0,$)]=G.slice($+1)}let te={};if(l.headers.trim())for(let V of pe(l.headers)){let G=V.trim();if(!G)continue;let $=G.indexOf(":");if($<=0){new b.Notice(`Invalid header: ${G}`);return}te[G.slice(0,$).trim()]=G.slice($+1).trim()}let ee=l.args.trim()?l.args.trim().split(/\s+/):void 0;N.disabled=!0,N.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:j,transport:l.transport,scope:l.scope,command:l.transport==="stdio"?l.command.trim():void 0,args:l.transport==="stdio"?ee:void 0,envVars:l.transport==="stdio"&&Object.keys(oe).length>0?oe:void 0,url:l.transport!=="stdio"?l.url.trim():void 0,headers:l.transport!=="stdio"&&Object.keys(te).length>0?te:void 0}),new b.Notice(`Server "${j}" added successfully.`),this.navigate("mcp")}catch(V){let G=V instanceof Error?V.message:String(V);new b.Notice(`Failed to add server: ${G}`),N.disabled=!1,N.setText(""),P(N,"plus","af-btn-icon"),N.appendText(" Add Server")}}}createFormField(e,s,a,n,i,o){let l=e.createDiv({cls:"af-form-row"}),c=l.createDiv({cls:"af-form-label"});c.setText(s),n&&this.addTooltip(c,n);let d=l.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,b.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function $i(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 za(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 sl(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),l=Number(s);if(!isNaN(o)&&!isNaN(l)){let c=o>=12?"PM":"AM",h=`${o===0?12:o>12?o-12:o}:${String(l).padStart(2,"0")} ${c}`;return i==="*"?`Daily at ${h}`:i==="1-5"?`Weekdays at ${h}`:`${h} on days ${i}`}}}return r}function al(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 Q=require("obsidian");var Kt=class r extends Q.ItemView{constructor(e,s){super(e);this.plugin=s}selectedAgentName=null;sessions=new Map;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 nt}getDisplayText(){return this.selectedAgentName?`Chat: ${this.selectedAgentName}`:"Agent Chat"}getIcon(){return"message-circle"}getState(){return{agentName:this.selectedAgentName??null}}async setState(e,s){await super.setState(e,s),e?.agentName&&typeof e.agentName=="string"&&this.selectAgent(e.agentName)}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){let s=this.plugin.app.workspace.getLeavesOfType(nt);for(let a of s)if(a.view!==this&&a.view instanceof r&&a.view.selectedAgentName===e){this.plugin.app.workspace.revealLeaf(a);return}this.selectedAgentName=e,this.agentSelect&&(this.agentSelect.value=e),this.leaf.updateHeader(),this.switchToAgent(e)}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"});P(a,"plus","af-btn-icon"),a.appendText(" New Chat"),a.onclick=()=>void this.handleNewChat(),this.agentSelect.onchange=()=>{let l=this.agentSelect.value;if(l){let c=this.plugin.app.workspace.getLeavesOfType(nt);for(let d of c)if(d.view!==this&&d.view instanceof r&&d.view.selectedAgentName===l){this.plugin.app.workspace.revealLeaf(d),this.agentSelect.value=this.selectedAgentName??"";return}this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(l)}},this.messagesEl=s.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let n=s.createDiv({cls:"af-chat-input-area"});this.pillsRow=n.createDiv({cls:"af-chat-pills-row"}),this.pillsRow.style.display="none";let i=n.createDiv({cls:"af-chat-input-row"});this.attachStopBtn=i.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=i.createEl("textarea",{cls:"af-chat-input",attr:{placeholder:"Message the agent\u2026 (Ctrl+Enter to send)",rows:"1"}}),this.sendBtn=i.createEl("button",{cls:"af-chat-send-btn"}),P(this.sendBtn,"arrow-up","af-btn-icon"),this.sendBtn.style.display="none";let o=()=>{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",o),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 c=l.clipboardData?.items;if(c)for(let d=0;d<c.length;d++){let h=c[d];if(h.type.startsWith("image/")){l.preventDefault();let u=h.getAsFile();u&&this.attachImageBlob(u);return}}}),n.addEventListener("dragover",l=>{l.preventDefault(),l.stopPropagation(),n.addClass("af-chat-input-dragover")}),n.addEventListener("dragleave",()=>{n.removeClass("af-chat-input-dragover")}),n.addEventListener("drop",l=>{l.preventDefault(),l.stopPropagation(),n.removeClass("af-chat-input-dragover");let c=l.dataTransfer?.files;if(c)for(let d=0;d<c.length;d++){let h=c[d];h.type.startsWith("image/")&&this.attachImageBlob(h)}}),this.statsEl=n.createDiv({cls:"af-chat-stats"}),this.renderStats(null)}async render(){this.populateAgentDropdown()}setStatsSource(e){this.statsSourceSession!==e&&(this.statsUnsub?.(),this.statsSourceSession=e,this.statsUnsub=e.onStatsChange(s=>this.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),l=s.createSpan({cls:`af-chat-stats-ctx${a>=80?" warn":""}`});l.createSpan({cls:"af-chat-stats-bar",text:o}),l.createSpan({cls:"af-chat-stats-pct",text:`${a}%`}),l.title=`Context: ${Yt(e.contextTokensUsed)} / ${Yt(e.contextWindow)} tokens (${a}%)
11946
+ ...`,rows:"8"}});C.value=n.toolsBody,C.addEventListener("input",()=>{h.toolsBody=C.value});let I=u.createDiv({cls:"af-create-section"}),P=I.createDiv({cls:"af-create-section-header"}),B=P.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(B,"book-open");let L=P.createSpan({text:"References"});this.addTooltip(L,"Background docs, conventions, cheat sheets");let V=I.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});V.value=n.referencesBody,V.addEventListener("input",()=>{h.referencesBody=V.value});let U=u.createDiv({cls:"af-create-section"}),H=U.createDiv({cls:"af-create-section-header"}),he=H.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(he,"message-circle");let me=H.createSpan({text:"Examples"});this.addTooltip(me,"Example prompts and ideal outputs showing how to use this skill");let ue=U.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
11947
+ ...`,rows:"6"}});ue.value=n.examplesBody,ue.addEventListener("input",()=>{h.examplesBody=ue.value});let Q=s.createDiv({cls:"af-create-footer"}),Y=Q.createEl("button",{cls:"af-btn-sm danger"});A(Y,"trash-2","af-btn-icon"),Y.appendText(" Delete"),Y.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new k.Notice(`Skill "${n.name}" deleted.`),await new Promise(te=>setTimeout(te,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},Q.createDiv({cls:"af-toolbar-spacer"});let q=Q.createEl("button",{cls:"af-btn-sm",text:"Cancel"});q.onclick=()=>this.navigate("skills");let ee=Q.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(ee,"check","af-btn-icon"),ee.appendText(" Save Changes"),ee.onclick=async()=>{let te=oe=>oe.split(",").map(ie=>ie.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:te(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new k.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(oe){let ie=oe instanceof Error?oe.message:String(oe);new k.Notice(`Failed to update skill: ${ie}`)}}}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"});A(n,"plus","af-btn-icon"),n.appendText(" Add Server"),n.onclick=()=>this.navigate("add-mcp-server");let r=a.createEl("button",{cls:"af-btn-sm"});A(r,"refresh-cw","af-btn-icon"),r.appendText(" Refresh"),r.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 b=0;b<3;b++)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"}),g=this.plugin.mcpManager.onProgress(b=>{switch(b.phase){case"list":u.setText("Scanning servers\u2026"),f.setText(b.message),m.style.width="20%";break;case"details":u.setText(`Checking server ${b.current}/${b.total}\u2026`),f.setText(b.serverName),m.style.width=`${20+b.current/b.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),f.setText(b.message),m.style.width="60%",m.addClass("af-mcp-progress-fill-slow");break;case"done":m.style.width="100%",u.setText("Done"),f.setText(`${b.serverCount} server${b.serverCount!==1?"s":""}, ${b.toolCount} tool${b.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(g),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"}),r=s.enabled?s.status==="connected"?"idle":s.status==="needs-auth"?"pending":"error":"disabled",o=n.createDiv({cls:`af-agent-card-avatar ${r}`});(0,k.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,k.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,k.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:Bt(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"}),g=f.createDiv({cls:"af-mcp-tool-count"}),b=g.createSpan();(0,k.setIcon)(b,"wrench"),g.createSpan({text:` ${m}`});let w=s.toolDetails.length>0?s.toolDetails.map(v=>v.name):s.tools;if(w.length>0){let v=f.createDiv({cls:"af-mcp-tool-chips"}),y=w.slice(0,4);for(let x of y)v.createSpan({cls:"af-mcp-tool-chip",text:x});w.length>4&&v.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${w.length-4}`})}if(this.authenticatingServers.has(s.name)){let y=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary",attr:{disabled:"true"}}),x=y.createSpan({cls:"af-spin"});(0,k.setIcon)(x,"loader-2"),y.appendText(" Authenticating\u2026")}else if(s.enabled&&s.status==="needs-auth"){let y=a.createDiv({cls:"af-mcp-auth-row"}).createEl("button",{cls:"af-btn-sm primary"}),x=y.createSpan();(0,k.setIcon)(x,"key"),y.appendText(" Authenticate"),y.onclick=S=>{S.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 y=v.createEl("a",{cls:"af-link",text:"discover tools"});y.onclick=x=>{x.stopPropagation(),x.preventDefault(),this.authenticateMcpServer(s)}}a.onclick=()=>this.openMcpDetailSlideover(s)}async authenticateMcpServer(e){if(!e.url){new k.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new k.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 k.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new k.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=re(e)[0]??e,n=a.split(/(?<=[.!?])\s/)[0]??a,r=n.length<a.length?n:a;return r.length<=s?r:r.slice(0,s-1)+"\u2026"}openMcpDetailSlideover(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:e.name});let r=n.createEl("button",{cls:"clickable-icon"});(0,k.setIcon)(r,"cross"),r.onclick=()=>s.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"}),g=f.createDiv({cls:"af-mcp-tool-detail-header"}),b=g.createSpan({cls:"af-mcp-tool-detail-name"}),w=b.createSpan();if((0,k.setIcon)(w,"wrench"),b.createSpan({text:` ${m.name}`}),m.inputSchema){let v=m.inputSchema.required??[];v.length>0&&g.createSpan({cls:"af-mcp-tool-param-count",text:`${v.length} param${v.length!==1?"s":""}`})}if(m.description){let v=re(m.description).filter(S=>S.trim()),y=v.slice(0,2).join(" ").trim();if(v.length>2){let S=f.createEl("details",{cls:"af-mcp-tool-detail-desc"});S.createEl("summary",{text:this.truncateDescription(y,200)}),S.createDiv({cls:"af-mcp-tool-detail-full",text:m.description})}else f.createDiv({cls:"af-mcp-tool-detail-desc",text:y})}if(m.inputSchema){let v=m.inputSchema.properties,y=new Set(m.inputSchema.required??[]);if(v&&Object.keys(v).length>0){let x=f.createDiv({cls:"af-mcp-tool-params"});for(let[S,_]of Object.entries(v)){let M=x.createDiv({cls:"af-mcp-tool-param"});M.createSpan({cls:"af-mcp-tool-param-name",text:S}),_.type&&M.createSpan({cls:"af-mcp-tool-param-type",text:_.type}),y.has(S)&&M.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),_.description&&M.createSpan({cls:"af-mcp-tool-param-desc",text:Bt(_.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,k.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,k.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 k.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(m){let f=m instanceof Error?m.message:String(m);new k.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"}),r=n.createDiv({cls:"af-agent-card-avatar idle"});(0,k.setIcon)(r,"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,k.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",H=>{c.name=H});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 g=d.createDiv({cls:"af-form-row"}),b=g.createDiv({cls:"af-form-label"});b.setText("Scope"),this.addTooltip(b,"local: this project only, user: available across all projects");let w=g.createEl("select",{cls:"af-form-select"});w.createEl("option",{text:"user",attr:{value:"user"}}),w.createEl("option",{text:"local",attr:{value:"local"}}),w.addEventListener("change",()=>{c.scope=w.value});let v=l.createDiv({cls:"af-create-section"}),y=v.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(x,"terminal"),y.createSpan({text:"Process Configuration"}),this.createFormField(v,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",H=>{c.command=H}),this.createFormField(v,"Arguments","--port 3000","Space-separated arguments (optional)",H=>{c.args=H});let S=v.createDiv({cls:"af-form-label"});S.setText("Environment variables"),this.addTooltip(S,"One KEY=VALUE per line");let _=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-...
11948
+ DEBUG=true`,rows:"3"}});_.addEventListener("input",()=>{c.envVars=_.value});let M=l.createDiv({cls:"af-create-section"}),D=M.createDiv({cls:"af-create-section-header"}),C=D.createSpan({cls:"af-create-section-icon"});(0,k.setIcon)(C,"globe"),D.createSpan({text:"Remote Server Configuration"}),this.createFormField(M,"URL","https://mcp.example.com/sse","Server endpoint URL",H=>{c.url=H});let I=M.createDiv({cls:"af-form-label"});I.setText("Custom headers"),this.addTooltip(I,"One Header: Value per line (optional)");let P=M.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});P.addEventListener("input",()=>{c.headers=P.value});let B=()=>{v.style.display=c.transport==="stdio"?"":"none",M.style.display=c.transport!=="stdio"?"":"none"};f.addEventListener("change",()=>{c.transport=f.value,B()}),B();let L=s.createDiv({cls:"af-create-footer"}),V=L.createEl("button",{cls:"af-btn-sm",text:"Cancel"});V.onclick=()=>this.navigate("mcp");let U=L.createEl("button",{cls:"af-btn-sm primary af-create-submit"});A(U,"plus","af-btn-icon"),U.appendText(" Add Server"),U.onclick=async()=>{let H=c.name.trim();if(!H){new k.Notice("Server name is required.");return}if(c.transport==="stdio"){if(!c.command.trim()){new k.Notice("Command is required for stdio servers.");return}}else if(!c.url.trim()){new k.Notice("URL is required for HTTP/SSE servers.");return}let he={};if(c.envVars.trim())for(let Q of re(c.envVars)){let Y=Q.trim();if(!Y)continue;let q=Y.indexOf("=");if(q<=0){new k.Notice(`Invalid env var: ${Y}`);return}he[Y.slice(0,q)]=Y.slice(q+1)}let me={};if(c.headers.trim())for(let Q of re(c.headers)){let Y=Q.trim();if(!Y)continue;let q=Y.indexOf(":");if(q<=0){new k.Notice(`Invalid header: ${Y}`);return}me[Y.slice(0,q).trim()]=Y.slice(q+1).trim()}let ue=c.args.trim()?c.args.trim().split(/\s+/):void 0;U.disabled=!0,U.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:H,transport:c.transport,scope:c.scope,command:c.transport==="stdio"?c.command.trim():void 0,args:c.transport==="stdio"?ue:void 0,envVars:c.transport==="stdio"&&Object.keys(he).length>0?he:void 0,url:c.transport!=="stdio"?c.url.trim():void 0,headers:c.transport!=="stdio"&&Object.keys(me).length>0?me:void 0}),new k.Notice(`Server "${H}" added successfully.`),this.navigate("mcp")}catch(Q){let Y=Q instanceof Error?Q.message:String(Q);new k.Notice(`Failed to add server: ${Y}`),U.disabled=!1,U.setText(""),A(U,"plus","af-btn-icon"),U.appendText(" Add Server")}}}createFormField(e,s,a,n,r,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",()=>r(d.value))}addTooltip(e,s){let a=e.createSpan({cls:"af-form-tooltip"});(0,k.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function _r(i){switch(i){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 yn(i){return i>=1e6?`${(i/1e6).toFixed(1)}M`:i>=1e4?`${Math.round(i/1e3)}K`:i>=1e3?`${(i/1e3).toFixed(1)}K`:String(i)}function mc(i){if(!i?.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[i])return t[i];let e=i.trim().split(/\s+/);if(e.length===5){let[s,a,n,,r]=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 r==="*"?`Daily at ${h}`:r==="1-5"?`Weekdays at ${h}`:`${h} on days ${r}`}}}return i}function fc(i){switch(i){case"connected":return"green";case"connecting":case"reconnecting":return"blue";case"needs-auth":case"error":return"red";case"stopped":case"disabled":default:return""}}var W=require("obsidian");function ka(i,t){return`${i}::${t}`}function Pr(){return Math.random().toString(16).slice(2,10)}function Dr(){return`New chat ${new Date().toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}`}function gc(i){let t=new Date(i).getTime();if(!Number.isFinite(t))return"";let e=Date.now()-t,s=60*1e3,a=60*s,n=24*a;return e<s?"just now":e<a?`${Math.floor(e/s)}m`:e<n?`${Math.floor(e/a)}h`:e<2*n?"yesterday":e<7*n?`${Math.floor(e/n)}d`:new Date(t).toLocaleDateString(void 0,{month:"short",day:"numeric"})}var yc=480,Zt=class i extends W.ItemView{constructor(e,s){super(e);this.plugin=s}selectedAgentName=null;selectedConversationId="";sessions=new Map;conversationsCache=[];convoPanelEl=null;collapseBtn=null;convoPanelCollapsed=!1;userToggledCollapse=!1;resizeObserver=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 nt}getDisplayText(){if(!this.selectedAgentName)return"Agent Chat";let e=this.getCurrentConversationName();return!e||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,convoPanelCollapsed:this.convoPanelCollapsed}}async setState(e,s){await super.setState(e,s);let a=typeof e?.conversationId=="string"&&e.conversationId?e.conversationId:void 0;typeof e?.convoPanelCollapsed=="boolean"&&(this.convoPanelCollapsed=e.convoPanelCollapsed,this.userToggledCollapse=!0,this.applyCollapsedClass()),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(),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.width<yc;n!==this.convoPanelCollapsed&&(this.convoPanelCollapsed=n,this.applyCollapsedClass())}}),this.resizeObserver.observe(this.contentEl))}selectAgent(e,s){if(s){let a=this.plugin.app.workspace.getLeavesOfType(nt);for(let n of a)if(n.view!==this&&n.view instanceof i&&n.view.selectedAgentName===e&&n.view.selectedConversationId===s){this.plugin.app.workspace.revealLeaf(n);return}}this.selectedAgentName=e,s&&(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.collapseBtn=this.headerEl.createEl("button",{cls:"af-chat-convo-collapse-btn",attr:{title:"Toggle conversations panel","aria-label":"Toggle conversations panel"}}),(0,W.setIcon)(this.collapseBtn,this.convoPanelCollapsed?"panel-left-open":"panel-left-close"),this.collapseBtn.onclick=()=>this.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"});A(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 r=n.createDiv({cls:"af-chat-main"});this.agentSelect.onchange=()=>{let d=this.agentSelect.value;d&&(this.selectedConversationId="",this.textarea.disabled=!1,this.textarea.placeholder="Message the agent\u2026 (Ctrl+Enter to send)",this.switchToAgent(d))},this.messagesEl=r.createDiv({cls:"af-chat-messages"}),this.messagesInner=this.messagesEl.createDiv({cls:"af-chat-messages-inner"});let o=r.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"}),A(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"}),A(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<h.length;u++){let p=h[u];if(p.type.startsWith("image/")){d.preventDefault();let m=p.getAsFile();m&&this.attachImageBlob(m);return}}}),o.addEventListener("dragover",d=>{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;u<h.length;u++){let p=h[u];p.type.startsWith("image/")&&this.attachImageBlob(p)}}),this.statsEl=o.createDiv({cls:"af-chat-stats"}),this.renderStats(null)}async render(){this.populateAgentDropdown()}setStatsSource(e){this.statsSourceSession!==e&&(this.statsUnsub?.(),this.statsSourceSession=e,this.statsUnsub=e.onStatsChange(s=>this.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,r=Math.min(n,Math.max(0,Math.round(a/100*n))),o="\u2593".repeat(r)+"\u2591".repeat(n-r),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: ${Qt(e.contextTokensUsed)} / ${Qt(e.contextWindow)} tokens (${a}%)
11870
11949
 
11871
11950
  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.
11872
11951
 
11873
- 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 ${Yt(a)} \u2192 ${Yt(n)}`),i.title=`Conversation was summarized to free up context. ${Yt(a)} tokens reduced to ${Yt(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,Q.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,Q.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){let a=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===e);if(!a)return;this.selectedAgentName=e,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear();let n=this.sessions.get(e);if(!n){let o=new Nt(a,this.plugin.settings,this.plugin.repository,this.app.vault);n={session:o},this.sessions.set(e,n),await o.loadPersistedState()}this.setStatsSource(n.session);for(let o of n.session.messages)if(o.role==="user")this.addBubble("user",o.content,o.attachments);else{let l=this.addBubble("assistant");if(this.renderMarkdownBubble(l,o.content),l._setRawText?.(o.content),this.attachThreadAffordance(l,o.id,n.session),o.toolCalls&&o.toolCalls.length>0){let c=this.getOrCreateAffordancesRow(l);this.buildToolSummary(o.toolCalls,c)}}this.activityUnsub?.();let i=n.session;this.activityUnsub=i.onActivityChange(()=>{this.getCurrentSession()?.session===i&&this.renderIndicators(i)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(this.selectedAgentName)}renderMarkdownBubble(e,s){let a=e.querySelector(".af-chat-copy-btn"),n=a?.parentNode?.removeChild(a)??null;e.empty(),e.addClass("af-compact-md"),Q.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 l=document.createElement("button");l.className="af-code-copy-btn",l.setAttribute("aria-label","Copy code"),(0,Q.setIcon)(l,"copy"),l.onclick=c=>{c.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{l.addClass("copied"),(0,Q.setIcon)(l,"check"),setTimeout(()=>{l.removeClass("copied"),(0,Q.setIcon)(l,"copy")},1500)})},i.style.position="relative",i.appendChild(l)})})}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,Q.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,Q.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,Q.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 l=i.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),c=l.createSpan({cls:"af-chat-pill-icon"}),d=/\.(png|jpe?g|gif|webp|svg|bmp)$/i.test(o);(0,Q.setIcon)(c,d?"image":"file-text"),l.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,Q.setIcon)(o,"message-circle");let l=o.createSpan({cls:"af-thread-badge-label"}),c=document.createElement("div");c.className="af-thread-container",c.style.display="none",n.insertBefore(c,i.nextSibling);let d=()=>{let f=a.getThreadIndex()[s]?.messageCount??0;f<=0?l.setText("Thread"):l.setText(`${f} ${f===1?"reply":"replies"}`),f>0&&o.addClass("has-replies")};d();let h=!1,u=async()=>{if(this.threadExpanded.get(s)===!0){c.style.display="none",this.threadExpanded.set(s,!1),o.removeClass("expanded"),this.setStatsSource(a);return}if(this.threadExpanded.set(s,!0),c.style.display="",o.addClass("expanded"),!h)try{let m=await a.openOrCreateThread(s);this.renderThreadContainer(c,m,a,d),h=!0}catch(m){c.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"}),l=null,c=null,d=S=>{S?(l||(l=o.createDiv({cls:"af-chat-activity"})),l.setText(`Working\u2026 (${S})`)):l&&(l.remove(),l=null)},h=S=>{if(S&&!c){c=o.createDiv({cls:"af-chat-streaming-dot"});for(let I=0;I<3;I++)c.createSpan()}else!S&&c&&(c.remove(),c=null)},u=(S,I,A)=>{if(S==="user"&&A&&A.length>0){let R=o.createDiv({cls:"af-chat-bubble-attachments"});for(let z of A){let N=R.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),j=N.createSpan({cls:"af-chat-pill-icon"});(0,Q.setIcon)(j,z.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),N.createSpan({cls:"af-chat-pill-name",text:z})}}let O=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${S}`});return S==="assistant"?this.renderMarkdownBubble(O,I):O.setText(I),O};for(let S of s.messages)u(S.role,S.content,S.attachments);let p=[],m=[],f=i.createDiv({cls:"af-thread-composer-wrap"}),v=f.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});v.style.display="none";let k=()=>{if(v.empty(),p.length===0&&m.length===0){v.style.display="none";return}v.style.display="flex";for(let S of p){let I=v.createDiv({cls:"af-chat-pill"}),A=I.createSpan({cls:"af-chat-pill-icon"});(0,Q.setIcon)(A,"file-text"),I.createSpan({cls:"af-chat-pill-name",text:S.name});let O=I.createSpan({cls:"af-chat-pill-remove"});(0,Q.setIcon)(O,"x"),O.onclick=R=>{R.stopPropagation();let z=p.findIndex(N=>N.path===S.path);z>=0&&p.splice(z,1),k()}}for(let S of m){let I=v.createDiv({cls:"af-chat-pill"}),A=I.createSpan({cls:"af-chat-pill-icon"});(0,Q.setIcon)(A,"image"),I.createSpan({cls:"af-chat-pill-name",text:S.name});let O=I.createSpan({cls:"af-chat-pill-remove"});(0,Q.setIcon)(O,"x"),O.onclick=R=>{R.stopPropagation();let z=m.findIndex(N=>N.path===S.path);z>=0&&m.splice(z,1),k()}}},w=()=>{let S=this.app.workspace.getActiveFile();if(!S){new Q.Notice("No active document to attach");return}if(p.some(A=>A.path===S.path)){new Q.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 Q.Notice(`Can't attach "${S.name}" \u2014 only text files are supported`);return}p.push(S),k()},y=async S=>{let I=S.type.split("/")[1]?.replace("jpeg","jpg")??"png",A=S.name&&S.name!=="image"?S.name:`pasted-${Date.now()}.${I}`;if(m.some(R=>R.name===A)){new Q.Notice(`"${A}" is already attached`);return}let O=await this.saveImageBlobToVault(S);O&&(m.push(O),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(),w()};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 I=S.clipboardData?.items;if(I)for(let A=0;A<I.length;A++){let O=I[A];if(O.type.startsWith("image/")){S.preventDefault();let R=O.getAsFile();R&&y(R);return}}}),f.addEventListener("dragover",S=>{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 I=S.dataTransfer?.files;if(I)for(let A=0;A<I.length;A++){let O=I[A];O.type.startsWith("image/")&&y(O)}});let C=g.createEl("button",{cls:"af-chat-send-btn"});P(C,"arrow-up","af-btn-icon"),C.style.display="none";let L=()=>{T.style.height="auto",T.style.height=`${Math.min(T.scrollHeight,120)}px`,C.style.display=T.value.trim()?"flex":"none"};T.addEventListener("input",L),T.addEventListener("focus",()=>this.setStatsSource(s));let E=async()=>{let S=T.value.trim();if(!S||s.isStreaming)return;let I=await this.buildAttachmentContextFor(p,m),A=[...p.map(N=>N.name),...m.map(N=>N.name)],O=I?`${I}${S}`:void 0;T.value="",L(),p.length=0,m.length=0,k(),u("user",S,A.length>0?A:void 0),h(!0);let R=null,z="";try{await s.sendMessage(S,N=>{if(N.type==="text"){R||(h(!1),d(),R=u("assistant",""),R.empty()),z+=N.content;let j=R.querySelector(".af-chat-stream-text");j||(j=R.createDiv({cls:"af-chat-stream-text"})),j.setText(z)}else N.type==="tool_use"?d(N.toolName):N.type==="result"&&(d(),h(!1),R&&this.renderMarkdownBubble(R,z))},O,A.length>0?A:void 0),n()}catch(N){h(!1),d();let j=N instanceof Error?N.message:String(N);o.createDiv({cls:"af-thread-error",text:`Error: ${j}`})}};C.onclick=()=>void E(),T.onkeydown=S=>{S.key==="Enter"&&(S.ctrlKey||S.metaKey)&&(S.preventDefault(),E())}}buildToolSummary(e,s){let n=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),i=n.createEl("details"),o=i.createEl("summary"),l=new Map;for(let h of e){let u=l.get(h.name)??[];h.command&&u.push(h.command),l.set(h.name,u)}let c=o.createSpan({cls:"af-chat-tool-icon"});(0,Q.setIcon)(c,"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 l){let p=u.length||(l.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 c=0;c<3;c++)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 Q.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new Q.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 Q.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 l=await e.arrayBuffer();await this.app.vault.createBinary(o,l);let d=this.app.vault.adapter.getBasePath?.()??"";return{name:n,path:`${d}/${o}`}}catch(l){let c=l instanceof Error?l.message:String(l);return new Q.Notice(`Failed to save image: ${c}`),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}
11952
+ 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,r=s.createSpan({cls:"af-chat-stats-compact"});r.setText(`compacted ${Qt(a)} \u2192 ${Qt(n)}`),r.title=`Conversation was summarized to free up context. ${Qt(a)} tokens reduced to ${Qt(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 r=n.avatar?.trim(),o=r&&!/^[a-z][a-z0-9-]*$/.test(r)?`${r} `:"";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,this.selectedConversationId||void 0)}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,W.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,W.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){let n=this.plugin.runtime.getSnapshot().agents.find(d=>d.name===e);if(!n)return;await this.loadConversations(n);let r;if(s&&this.conversationsCache.some(d=>d.id===s))r=s;else if(this.conversationsCache.length>0)r=this.conversationsCache[0].id;else{r=Pr();try{await this.plugin.repository.createConversation(n,r,Dr())}catch(d){new W.Notice(`Couldn't create conversation: ${d instanceof Error?d.message:String(d)}`);return}await this.loadConversations(n)}if(!s||s!==r){let d=this.plugin.app.workspace.getLeavesOfType(nt);for(let h of d)if(h.view!==this&&h.view instanceof i&&h.view.selectedAgentName===e&&h.view.selectedConversationId===r){this.plugin.app.workspace.revealLeaf(h);return}}this.selectedAgentName=e,this.selectedConversationId=r,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),this.threadExpanded.clear(),this.renderConvoPanel();let o=ka(e,r),c=this.sessions.get(o);if(!c){let d=new jt(n,this.plugin.settings,this.plugin.repository,this.app.vault,{inAppConversationId:r});c={session:d},this.sessions.set(o,c),await d.loadPersistedState()}this.setStatsSource(c.session);for(let d of c.session.messages)if(d.role==="user")this.addBubble("user",d.content,d.attachments);else{let h=this.addBubble("assistant");if(this.renderMarkdownBubble(h,d.content),h._setRawText?.(d.content),this.attachThreadAffordance(h,d.id,c.session),d.toolCalls&&d.toolCalls.length>0){let u=this.getOrCreateAffordancesRow(h);this.buildToolSummary(d.toolCalls,u)}}this.activityUnsub?.();let l=c.session;this.activityUnsub=l.onActivityChange(()=>{this.getCurrentSession()?.session===l&&this.renderIndicators(l)}),this.textarea.disabled=!1,this.textarea.focus()}getCurrentSession(){if(this.selectedAgentName)return this.sessions.get(ka(this.selectedAgentName,this.selectedConversationId))}async loadConversations(e){this.conversationsCache=await this.plugin.repository.listConversations(e)}async refreshConversationsList(e){await this.loadConversations(e),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,W.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":""}`}),r=n.createDiv({cls:"af-chat-convo-name",text:s.name});r.title=s.name,r.ondblclick=d=>{d.stopPropagation(),this.beginInlineRename(r,s)};let o=n.createDiv({cls:"af-chat-convo-meta"}),c=s.messageCount===1?"1 msg":`${s.messageCount} msgs`;o.appendText(`${c} \xB7 ${gc(s.lastActive)}`);let l=n.createEl("button",{cls:"af-chat-convo-trash",attr:{title:"Delete conversation","aria-label":"Delete conversation"}});(0,W.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 r=!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(r)return;r=!0;let l=n.value.trim();if(!l||l===a){o(null);return}try{await this.saveConversationName(s.id,l)}catch(d){new W.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(),r=!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 r=ka(this.selectedAgentName,e),o=this.sessions.get(r);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(nt);for(let a of s)if(a.view!==this&&a.view instanceof i&&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)return;let a=this.plugin.runtime.getSnapshot().agents.find(o=>o.name===this.selectedAgentName);if(!a||!window.confirm(`Delete conversation "${e.name}"?
11953
+
11954
+ This removes its message history. The agent and its other conversations are untouched.`))return;let r=ka(this.selectedAgentName,e.id);this.sessions.get(r)?.session.dispose(),this.sessions.delete(r);try{await this.plugin.repository.deleteConversation(a,e.id)}catch(o){new W.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;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,W.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"),W.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),this.wireBubbleLinks(e),e.querySelectorAll("pre").forEach(r=>{r.querySelector(".copy-code-button")?.remove();let o=r.querySelector("code");if(!o)return;let c=document.createElement("button");c.className="af-code-copy-btn",c.setAttribute("aria-label","Copy code"),(0,W.setIcon)(c,"copy"),c.onclick=l=>{l.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{c.addClass("copied"),(0,W.setIcon)(c,"check"),setTimeout(()=>{c.removeClass("copied"),(0,W.setIcon)(c,"copy")},1500)})},r.style.position="relative",r.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 r=a.getAttribute("data-href")||a.getAttribute("href")||a.textContent||"";if(!r)return;let o=s.shiftKey?"split":"tab";this.app.workspace.openLinkText(r,"",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,W.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,W.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,W.setIcon)(a,"copy")},1500)})}}addBubble(e,s,a){if(e==="user"&&a&&a.length>0){let r=this.messagesInner.createDiv({cls:"af-chat-bubble-attachments"});for(let o of a){let c=r.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,W.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 r=s??"";this.addCopyBtn(n,()=>r),n._setRawText=o=>{r=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 r=this.getOrCreateAffordancesRow(e);if(r.querySelector(".af-thread-badge"))return;let o=document.createElement("div");o.className="af-thread-badge",o.setAttribute("role","button"),o.setAttribute("tabindex","0"),r.appendChild(o),(0,W.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,r.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 r=e.createDiv({cls:"af-thread-wrap"}),o=r.createDiv({cls:"af-thread-messages"}),c=null,l=null,d=C=>{C?(c||(c=o.createDiv({cls:"af-chat-activity"})),c.setText(`Working\u2026 (${C})`)):c&&(c.remove(),c=null)},h=C=>{if(C&&!l){l=o.createDiv({cls:"af-chat-streaming-dot"});for(let I=0;I<3;I++)l.createSpan()}else!C&&l&&(l.remove(),l=null)},u=(C,I,P)=>{if(C==="user"&&P&&P.length>0){let L=o.createDiv({cls:"af-chat-bubble-attachments"});for(let V of P){let U=L.createSpan({cls:"af-chat-pill af-chat-pill-inline"}),H=U.createSpan({cls:"af-chat-pill-icon"});(0,W.setIcon)(H,V.match(/\.(png|jpe?g|gif|webp|svg)$/i)?"image":"file-text"),U.createSpan({cls:"af-chat-pill-name",text:V})}}let B=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${C}`});return C==="assistant"?this.renderMarkdownBubble(B,I):B.setText(I),B};for(let C of s.messages)u(C.role,C.content,C.attachments);let p=[],m=[],f=r.createDiv({cls:"af-thread-composer-wrap"}),g=f.createDiv({cls:"af-chat-pills-row af-thread-pills-row"});g.style.display="none";let b=()=>{if(g.empty(),p.length===0&&m.length===0){g.style.display="none";return}g.style.display="flex";for(let C of p){let I=g.createDiv({cls:"af-chat-pill"}),P=I.createSpan({cls:"af-chat-pill-icon"});(0,W.setIcon)(P,"file-text"),I.createSpan({cls:"af-chat-pill-name",text:C.name});let B=I.createSpan({cls:"af-chat-pill-remove"});(0,W.setIcon)(B,"x"),B.onclick=L=>{L.stopPropagation();let V=p.findIndex(U=>U.path===C.path);V>=0&&p.splice(V,1),b()}}for(let C of m){let I=g.createDiv({cls:"af-chat-pill"}),P=I.createSpan({cls:"af-chat-pill-icon"});(0,W.setIcon)(P,"image"),I.createSpan({cls:"af-chat-pill-name",text:C.name});let B=I.createSpan({cls:"af-chat-pill-remove"});(0,W.setIcon)(B,"x"),B.onclick=L=>{L.stopPropagation();let V=m.findIndex(U=>U.path===C.path);V>=0&&m.splice(V,1),b()}}},w=()=>{let C=this.app.workspace.getActiveFile();if(!C){new W.Notice("No active document to attach");return}if(p.some(P=>P.path===C.path)){new W.Notice(`"${C.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(C.extension.toLowerCase())){new W.Notice(`Can't attach "${C.name}" \u2014 only text files are supported`);return}p.push(C),b()},v=async C=>{let I=C.type.split("/")[1]?.replace("jpeg","jpg")??"png",P=C.name&&C.name!=="image"?C.name:`pasted-${Date.now()}.${I}`;if(m.some(L=>L.name===P)){new W.Notice(`"${P}" is already attached`);return}let B=await this.saveImageBlobToVault(C);B&&(m.push(B),b())},y=f.createDiv({cls:"af-chat-input-row af-thread-composer"}),x=y.createEl("button",{cls:"af-chat-attach-btn"});A(x,"plus","af-btn-icon"),x.title="Attach active document",x.onclick=C=>{C.preventDefault(),w()};let S=y.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}});S.addEventListener("paste",C=>{let I=C.clipboardData?.items;if(I)for(let P=0;P<I.length;P++){let B=I[P];if(B.type.startsWith("image/")){C.preventDefault();let L=B.getAsFile();L&&v(L);return}}}),f.addEventListener("dragover",C=>{C.preventDefault(),C.stopPropagation(),f.addClass("af-chat-input-dragover")}),f.addEventListener("dragleave",()=>{f.removeClass("af-chat-input-dragover")}),f.addEventListener("drop",C=>{C.preventDefault(),C.stopPropagation(),f.removeClass("af-chat-input-dragover");let I=C.dataTransfer?.files;if(I)for(let P=0;P<I.length;P++){let B=I[P];B.type.startsWith("image/")&&v(B)}});let _=y.createEl("button",{cls:"af-chat-send-btn"});A(_,"arrow-up","af-btn-icon"),_.style.display="none";let M=()=>{S.style.height="auto",S.style.height=`${Math.min(S.scrollHeight,120)}px`,_.style.display=S.value.trim()?"flex":"none"};S.addEventListener("input",M),S.addEventListener("focus",()=>this.setStatsSource(s));let D=async()=>{let C=S.value.trim();if(!C||s.isStreaming)return;let I=await this.buildAttachmentContextFor(p,m),P=[...p.map(U=>U.name),...m.map(U=>U.name)],B=I?`${I}${C}`:void 0;S.value="",M(),p.length=0,m.length=0,b(),u("user",C,P.length>0?P:void 0),h(!0);let L=null,V="";try{await s.sendMessage(C,U=>{if(U.type==="text"){L||(h(!1),d(),L=u("assistant",""),L.empty()),V+=U.content;let H=L.querySelector(".af-chat-stream-text");H||(H=L.createDiv({cls:"af-chat-stream-text"})),H.setText(V)}else U.type==="tool_use"?d(U.toolName):U.type==="result"&&(d(),h(!1),L&&this.renderMarkdownBubble(L,V))},B,P.length>0?P:void 0),n()}catch(U){h(!1),d();let H=U instanceof Error?U.message:String(U);o.createDiv({cls:"af-thread-error",text:`Error: ${H}`})}};_.onclick=()=>void D(),S.onkeydown=C=>{C.key==="Enter"&&(C.ctrlKey||C.metaKey)&&(C.preventDefault(),D())}}buildToolSummary(e,s){let n=(s??this.messagesInner).createDiv({cls:"af-chat-tool-summary"}),r=n.createEl("details"),o=r.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,W.setIcon)(l,"wrench"),o.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let d=r.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,r=!!this.messagesInner.querySelector(".af-chat-stream-text"),o=null;if(s&&a?o=`Working\u2026 (${a})`:s&&n&&!r&&(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?(A(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(A(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 W.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new W.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 W.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}`,r=`${this.plugin.settings.fleetFolder}/chat-images`,o=`${r}/${a}-${n}`;try{this.app.vault.getAbstractFileByPath(r)||await this.app.vault.createFolder(r);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 W.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 r=await this.app.vault.cachedRead(n);a.push(`### ${n.name}
11874
11955
  \`\`\`
11875
- ${i}
11956
+ ${r}
11876
11957
  \`\`\``)}catch{a.push(`### ${n.name}
11877
11958
  (Could not read file)`)}for(let n of s)a.push(`### Image: ${n.name}
11878
11959
  The image file is located at: ${n.path}
@@ -11884,6 +11965,6 @@ ${a.join(`
11884
11965
 
11885
11966
  ---
11886
11967
 
11887
- `}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 Q.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,Q.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,Q.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,Q.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,Q.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,l="",c=!1,d=e.session,h=()=>this.getCurrentSession()?.session===d;try{await e.session.sendMessage(s,u=>{if(!h()){o=null,c=!1,l="";return}if(u.type==="text"){(!c||!o||!o.isConnected)&&(o=this.addBubble("assistant"),c=!0),l+=u.content;let p=o.querySelector(".af-chat-stream-text");p||(p=o.createDiv({cls:"af-chat-stream-text"})),p.setText(l)}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(c&&o&&o.isConnected){this.renderMarkdownBubble(o,l),o._setRawText?.(l);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);l="",c=!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(){let e=this.getCurrentSession();!e||!this.selectedAgentName||(e.session.abort(),await e.session.clearPersistedState(),this.sessions.delete(this.selectedAgentName),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty(),await this.switchToAgent(this.selectedAgentName))}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 Yt(r){return r>=1e3?`${(r/1e3).toFixed(r>=1e4?0:1)}k`:`${r}`}var aa=class extends Se.Plugin{settings={...lt};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new Rs;channelCredentials=new Bs;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 Zt(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new as(this.repository,this.settings),this.registerView(bt,a=>new ps(a,this)),this.registerView(St,a=>new ta(a,this)),this.registerView(nt,a=>new Kt(a,this)),this.addSettingTab(new Ss(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(nt);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 Ns(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 Os({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(a,n)=>{if(a.type==="slack")return new Zs(a,n);if(a.type==="telegram")return new ea(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}*
11968
+ `}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(r=>r.name===a)){new W.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,W.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let r=a.createSpan({cls:"af-chat-pill-remove"});(0,W.setIcon)(r,"x"),r.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,W.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let r=a.createSpan({cls:"af-chat-pill-remove"});(0,W.setIcon)(r,"x"),r.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 r=a?`${a}${s}`:void 0;if(this.addBubble("user",s,n.length>0?n:void 0),e.session.isStreaming){e.session.injectMessage(s,r,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}}},r,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(n=>n.name===this.selectedAgentName);if(!s)return;let a=Pr();try{await this.plugin.repository.createConversation(s,a,Dr())}catch(n){new W.Notice(`Couldn't create conversation: ${n instanceof Error?n.message:String(n)}`);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.",r=>{r.type==="text"&&(n||(a=this.addBubble("assistant"),n=!0),s+=r.content,a.setText(s))}).then(r=>{n&&a?(this.renderMarkdownBubble(a,s),a._setRawText?.(s)):r.text.trim()&&(a=this.addBubble("assistant"),this.renderMarkdownBubble(a,r.text),a._setRawText?.(r.text)),r.toolCalls.length>0&&this.buildToolSummary(r.toolCalls),this.textarea.focus()}).catch(r=>{let o=r instanceof Error?r.message:String(r);o!=="Aborted"&&this.addBubble("error",`Error: ${o}`)})}};function Qt(i){return i>=1e3?`${(i/1e3).toFixed(i>=1e4?0:1)}k`:`${i}`}var xa=class extends Te.Plugin{settings={...lt};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new Vs;channelCredentials=new Zs;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 ss(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new us(this.repository,this.settings),this.registerView(xt,a=>new Cs(a,this)),this.registerView(_t,a=>new ga(a,this)),this.registerView(nt,a=>new Zt(a,this)),this.addSettingTab(new Ns(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),await this.maybeResolveCodexCliPath(),this.addRibbonIcon("bot","Agent Fleet Dashboard",()=>void this.activateDashboardView()),this.addRibbonIcon("message-circle","Agent Chat",()=>{let a=this.app.workspace.getLeavesOfType(nt);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 Qs(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 Xs({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(a,n)=>{if(a.type==="slack")return new ma(a,n);if(a.type==="telegram")return new fa(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 Te.Notice("Agent Fleet: channel manager failed to start \u2014 check console.")}this.runtime.onHeartbeatResult((a,n,r)=>{this.channelManager?.broadcastToChannel(n,`*Heartbeat \u2014 ${a}*
11888
11969
 
11889
- ${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(bt),this.app.workspace.detachLeavesOfType(St),this.app.workspace.detachLeavesOfType(nt),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.repository=new Zt(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new as(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(bt);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:bt,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let a=this.app.workspace.getLeavesOfType(bt)[0];if(a){let n=a.view;n instanceof ps&&n.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(St,"left");await t.setViewState({type:St,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(nt);for(let a of s)if(a.view instanceof Kt&&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:nt,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof Kt&&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=ut(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 ks(this.app,{agentName:t,taskCount:s.length,runCount:a.length,hasMemory:i},async o=>{let l=await this.repository.deleteAgent(t,o);await new Promise(c=>setTimeout(c,200)),await this.refreshFromVault(),new Se.Notice(`Deleted agent "${t}" (${l.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}=ie(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(nt);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=en(t);for(let s of e)if(ca(s)&&(0,ji.existsSync)(s)||!ca(s)&&await new Promise(n=>{let i=ut(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))}};
11970
+ ${r}`).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 Te.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(xt),this.app.workspace.detachLeavesOfType(_t),this.app.workspace.detachLeavesOfType(nt),this.runtime?.shutdown(),this.channelManager?.stop(),Vn()}async loadSettings(){this.settings={...lt,...await this.loadData()}}async saveSettings(){this.settings.claudeCliPath=await this.resolveClaudeCliPath(this.settings.claudeCliPath),await this.maybeResolveCodexCliPath(),Yn(),await this.saveData(this.settings),this.repository&&this.runtime&&(this.runtime.shutdown(),this.repository=new ss(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new us(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(xt);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:xt,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let a=this.app.workspace.getLeavesOfType(xt)[0];if(a){let n=a.view;n instanceof Cs&&n.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(_t,"left");await t.setViewState({type:_t,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(nt);for(let a of s)if(a.view instanceof Zt&&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:nt,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof Zt&&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 this.verifyCliBinary(e,"Claude",t)}async verifyCodexCli(t=!0){let e=await this.resolveCliPathFrom(Ea(this.settings.codexCliPath),this.settings.codexCliPath);return this.settings.codexCliPath=e,await this.verifyCliBinary(e,"Codex",t)}async verifyCliBinary(t,e,s){return await new Promise(a=>{let n=Ge(t,["--version"]),r="";n.stderr.on("data",o=>{r+=o.toString()}),n.on("close",o=>{let c=o===0;c||console.error(`Agent Fleet: ${e} CLI verification failed`,r),s&&new Te.Notice(c?`${e} CLI available.`:`${e} CLI verification failed \u2014 check ${e} CLI Path in settings.`),a(c)}),n.on("error",o=>{console.error(`Agent Fleet: ${e} CLI verification error`,o),s&&new Te.Notice(`${e} CLI verification failed \u2014 check ${e} CLI Path in settings.`),a(!1)})})}async openPath(t){let e=this.app.vault.getAbstractFileByPath((0,Te.normalizePath)(t));e instanceof Te.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 Te.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 Te.Notice(`Unknown agent: ${t}`);return}await this.openChatView(t)}async deleteAgent(t){if(!this.repository.getAgentByName(t)){new Te.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),r=!!this.app.vault.getAbstractFileByPath(n);new Ls(this.app,{agentName:t,taskCount:s.length,runCount:a.length,hasMemory:r},async o=>{let c=await this.repository.deleteAgent(t,o);await new Promise(l=>setTimeout(l,200)),await this.refreshFromVault(),new Te.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 Te.TFile))return;let n=await this.app.vault.cachedRead(a),{frontmatter:r,body:o}=ae(n);r.enabled=e,await this.app.vault.modify(a,K(r,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(nt);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 Te.Notice("No agents configured.")}}),this.addCommand({id:"pause-all",name:"Pause All",callback:()=>{this.runtime.scheduler.pauseAll(),new Te.Notice("Agent Fleet paused.")}}),this.addCommand({id:"resume-all",name:"Resume All",callback:()=>{this.runtime.scheduler.resumeAll(),new Te.Notice("Agent Fleet resumed.")}}),this.addCommand({id:"view-fleet-status",name:"View Fleet Status",callback:()=>{let t=this.runtime.getFleetStatus();new Te.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 Te.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof Te.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){return this.resolveCliPathFrom(Tn(t),t)}async maybeResolveCodexCliPath(){this.runtime?.getSnapshot().agents.some(e=>cs(e.adapter)==="codex")&&(this.settings.codexCliPath=await this.resolveCliPathFrom(Ea(this.settings.codexCliPath),this.settings.codexCliPath))}async resolveCliPathFrom(t,e){for(let s of t)if(Pa(s)&&(0,Rr.existsSync)(s)||!Pa(s)&&await new Promise(n=>{let r=Ge(s,["--version"]);r.on("close",o=>n(o===0)),r.on("error",()=>n(!1))}))return s;return e}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))}};