obsidian-agent-fleet 0.7.1 → 0.9.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/LICENSE +21 -0
- package/README.md +14 -8
- package/bin/cli.js +23 -0
- package/package.json +5 -2
- package/plugin/main.js +472 -124
- package/plugin/manifest.json +1 -1
- package/plugin/styles.css +476 -2
package/plugin/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var ni=Object.create;var as=Object.defineProperty;var ii=Object.getOwnPropertyDescriptor;var ri=Object.getOwnPropertyNames;var oi=Object.getPrototypeOf,li=Object.prototype.hasOwnProperty;var Ie=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),ci=(r,t)=>{for(var e in t)as(r,e,{get:t[e],enumerable:!0})},ba=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ri(t))!li.call(r,a)&&a!==e&&as(r,a,{get:()=>t[a],enumerable:!(s=ii(t,a))||s.enumerable});return r};var Le=(r,t,e)=>(e=r!=null?ni(oi(r)):{},ba(t||!r||!r.__esModule?as(e,"default",{value:r,enumerable:!0}):e,r)),di=r=>ba(as({},"__esModule",{value:!0}),r);var et=Ie((Go,Na)=>{"use strict";var Oa=["nodebuffer","arraybuffer","fragments"],Ba=typeof Blob<"u";Ba&&Oa.push("blob");Na.exports={BINARY_TYPES:Oa,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:Ba,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var $t=Ie((Vo,ws)=>{"use strict";var{EMPTY_BUFFER:Ci}=et(),Gs=Buffer[Symbol.species];function _i(r,t){if(r.length===0)return Ci;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 Gs(e.buffer,e.byteOffset,s):e}function Ua(r,t,e,s,a){for(let n=0;n<a;n++)e[s+n]=r[n]^t[n&3]}function ja(r,t){for(let e=0;e<r.length;e++)r[e]^=t[e&3]}function Ei(r){return r.length===r.buffer.byteLength?r.buffer:r.buffer.slice(r.byteOffset,r.byteOffset+r.length)}function Vs(r){if(Vs.readOnly=!0,Buffer.isBuffer(r))return r;let t;return r instanceof ArrayBuffer?t=new Gs(r):ArrayBuffer.isView(r)?t=new Gs(r.buffer,r.byteOffset,r.byteLength):(t=Buffer.from(r),Vs.readOnly=!1),t}ws.exports={concat:_i,mask:Ua,toArrayBuffer:Ei,toBuffer:Vs,unmask:ja};if(!process.env.WS_NO_BUFFER_UTIL)try{let r=require("bufferutil");ws.exports.mask=function(t,e,s,a,n){n<48?Ua(t,e,s,a,n):r.mask(t,e,s,a,n)},ws.exports.unmask=function(t,e){t.length<32?ja(t,e):r.unmask(t,e)}}catch{}});var qa=Ie((Yo,Ha)=>{"use strict";var $a=Symbol("kDone"),Ys=Symbol("kRun"),Ks=class{constructor(t){this[$a]=()=>{this.pending--,this[Ys]()},this.concurrency=t||1/0,this.jobs=[],this.pending=0}add(t){this.jobs.push(t),this[Ys]()}[Ys](){if(this.pending!==this.concurrency&&this.jobs.length){let t=this.jobs.shift();this.pending++,t(this[$a])}}};Ha.exports=Ks});var _t=Ie((Ko,Va)=>{"use strict";var Ht=require("zlib"),za=$t(),Ai=qa(),{kStatusCode:Wa}=et(),Pi=Buffer[Symbol.species],Di=Buffer.from([0,0,255,255]),xs=Symbol("permessage-deflate"),tt=Symbol("total-length"),Tt=Symbol("callback"),lt=Symbol("buffers"),Ct=Symbol("error"),ks,Xs=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,!ks){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;ks=new Ai(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[Tt];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){ks.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){ks.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"?Ht.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=Ht.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[xs]=this,this._inflate[tt]=0,this._inflate[lt]=[],this._inflate.on("error",Ii),this._inflate.on("data",Ga)}this._inflate[Tt]=s,this._inflate.write(t),e&&this._inflate.write(Di),this._inflate.flush(()=>{let n=this._inflate[Ct];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=za.concat(this._inflate[lt],this._inflate[tt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[tt]=0,this._inflate[lt]=[],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"?Ht.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=Ht.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[tt]=0,this._deflate[lt]=[],this._deflate.on("data",Ri)}this._deflate[Tt]=s,this._deflate.write(t),this._deflate.flush(Ht.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=za.concat(this._deflate[lt],this._deflate[tt]);e&&(n=new Pi(n.buffer,n.byteOffset,n.length-4)),this._deflate[Tt]=null,this._deflate[tt]=0,this._deflate[lt]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};Va.exports=Xs;function Ri(r){this[lt].push(r),this[tt]+=r.length}function Ga(r){if(this[tt]+=r.length,this[xs]._maxPayload<1||this[tt]<=this[xs]._maxPayload){this[lt].push(r);return}this[Ct]=new RangeError("Max payload size exceeded"),this[Ct].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[Ct][Wa]=1009,this.removeListener("data",Ga),this.reset()}function Ii(r){if(this[xs]._inflate=null,this[Ct]){this[Tt](this[Ct]);return}r[Wa]=1007,this[Tt](r)}});var Et=Ie((Xo,Ss)=>{"use strict";var{isUtf8:Ya}=require("buffer"),{hasBlob:Li}=et(),Fi=[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 Mi(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Js(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 Oi(r){return Li&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}Ss.exports={isBlob:Oi,isValidStatusCode:Mi,isValidUTF8:Js,tokenChars:Fi};if(Ya)Ss.exports.isValidUTF8=function(r){return r.length<24?Js(r):Ya(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");Ss.exports.isValidUTF8=function(t){return t.length<32?Js(t):r(t)}}catch{}});var sa=Ie((Jo,tn)=>{"use strict";var{Writable:Bi}=require("stream"),Ka=_t(),{BINARY_TYPES:Ni,EMPTY_BUFFER:Xa,kStatusCode:Ui,kWebSocket:ji}=et(),{concat:Qs,toArrayBuffer:$i,unmask:Hi}=$t(),{isValidStatusCode:qi,isValidUTF8:Ja}=Et(),Ts=Buffer[Symbol.species],Ue=0,Qa=1,Za=2,en=3,Zs=4,ea=5,Cs=6,ta=class extends Bi{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Ni[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[ji]=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=Ue}_write(t,e,s){if(this._opcode===8&&this._state==Ue)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 Ts(s.buffer,s.byteOffset+t,s.length-t),new Ts(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 Ts(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 Ue:this.getInfo(t);break;case Qa:this.getPayloadLength16(t);break;case Za:this.getPayloadLength64(t);break;case en:this.getMask();break;case Zs:this.getData(t);break;case ea:case Cs: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[Ka.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=Qa:this._payloadLength===127?this._state=Za: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=en:this._state=Zs}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=Zs}getData(t){let e=Xa;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&&Hi(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=ea,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Ka.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===Ue&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=Ue;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=Qs(s,e):this._binaryType==="arraybuffer"?a=$i(Qs(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=Ue):(this._state=Cs,setImmediate(()=>{this.emit("message",a,!0),this._state=Ue,this.startLoop(t)}))}else{let a=Qs(s,e);if(!this._skipUTF8Validation&&!Ja(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===ea||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=Ue):(this._state=Cs,setImmediate(()=>{this.emit("message",a,!1),this._state=Ue,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Xa),this.end();else{let s=t.readUInt16BE(0);if(!qi(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new Ts(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!Ja(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=Ue;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=Ue):(this._state=Cs,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=Ue,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[Ui]=a,i}};tn.exports=ta});var ia=Ie((Zo,nn)=>{"use strict";var{Duplex:Qo}=require("stream"),{randomFillSync:zi}=require("crypto"),sn=_t(),{EMPTY_BUFFER:Wi,kWebSocket:Gi,NOOP:Vi}=et(),{isBlob:At,isValidStatusCode:Yi}=Et(),{mask:an,toBuffer:pt}=$t(),je=Symbol("kByteLength"),Ki=Buffer.alloc(4),_s=8*1024,mt,Pt=_s,We=0,Xi=1,Ji=2,aa=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=We,this.onerror=Vi,this[Gi]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||Ki,e.generateMask?e.generateMask(s):(Pt===_s&&(mt===void 0&&(mt=Buffer.alloc(_s)),zi(mt,0,_s),Pt=0),s[0]=mt[Pt++],s[1]=mt[Pt++],s[2]=mt[Pt++],s[3]=mt[Pt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[je]!==void 0?o=e[je]:(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?(an(t,s,c,n,o),[c]):(an(t,s,t,0,o),[c,t])):[c,t]}close(t,e,s,a){let n;if(t===void 0)n=Wi;else{if(typeof t!="number"||!Yi(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={[je]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==We?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):At(t)?(a=t.size,n=!1):(t=pt(t),a=t.length,n=pt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[je]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};At(t)?this._state!==We?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==We?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):At(t)?(a=t.size,n=!1):(t=pt(t),a=t.length,n=pt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[je]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};At(t)?this._state!==We?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==We?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}send(t,e,s){let a=this._extensions[sn.extensionName],n=e.binary?2:1,i=e.compress,o,l;typeof t=="string"?(o=Buffer.byteLength(t),l=!1):At(t)?(o=t.size,l=!1):(t=pt(t),o=t.length,l=pt.readOnly),this._firstFragment?(this._firstFragment=!1,i&&a&&a.params[a._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(i=o>=a._threshold),this._compress=i):(i=!1,n=0),e.fin&&(this._firstFragment=!0);let c={[je]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:l,rsv1:i};At(t)?this._state!==We?this.enqueue([this.getBlobData,t,this._compress,c,s]):this.getBlobData(t,this._compress,c,s):this._state!==We?this.enqueue([this.dispatch,t,this._compress,c,s]):this.dispatch(t,this._compress,c,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[je],this._state=Ji,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(na,this,o,a);return}this._bufferedBytes-=s[je];let i=pt(n);e?this.dispatch(i,e,s,a):(this._state=We,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(Qi,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(r.frame(t,s),a);return}let n=this._extensions[sn.extensionName];this._bufferedBytes+=s[je],this._state=Xi,n.compress(t,s.fin,(i,o)=>{if(this._socket.destroyed){let l=new Error("The socket was closed while data was being compressed");na(this,l,a);return}this._bufferedBytes-=s[je],this._state=We,s.readOnly=!1,this.sendFrame(r.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===We&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][je],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][je],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)}};nn.exports=aa;function na(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 Qi(r,t,e){na(r,t,e),r.onerror(t)}});var mn=Ie((el,pn)=>{"use strict";var{kForOnEventAttribute:qt,kListener:ra}=et(),rn=Symbol("kCode"),on=Symbol("kData"),ln=Symbol("kError"),cn=Symbol("kMessage"),dn=Symbol("kReason"),Dt=Symbol("kTarget"),un=Symbol("kType"),hn=Symbol("kWasClean"),st=class{constructor(t){this[Dt]=null,this[un]=t}get target(){return this[Dt]}get type(){return this[un]}};Object.defineProperty(st.prototype,"target",{enumerable:!0});Object.defineProperty(st.prototype,"type",{enumerable:!0});var ft=class extends st{constructor(t,e={}){super(t),this[rn]=e.code===void 0?0:e.code,this[dn]=e.reason===void 0?"":e.reason,this[hn]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[rn]}get reason(){return this[dn]}get wasClean(){return this[hn]}};Object.defineProperty(ft.prototype,"code",{enumerable:!0});Object.defineProperty(ft.prototype,"reason",{enumerable:!0});Object.defineProperty(ft.prototype,"wasClean",{enumerable:!0});var Rt=class extends st{constructor(t,e={}){super(t),this[ln]=e.error===void 0?null:e.error,this[cn]=e.message===void 0?"":e.message}get error(){return this[ln]}get message(){return this[cn]}};Object.defineProperty(Rt.prototype,"error",{enumerable:!0});Object.defineProperty(Rt.prototype,"message",{enumerable:!0});var zt=class extends st{constructor(t,e={}){super(t),this[on]=e.data===void 0?null:e.data}get data(){return this[on]}};Object.defineProperty(zt.prototype,"data",{enumerable:!0});var Zi={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[qt]&&a[ra]===t&&!a[qt])return;let s;if(r==="message")s=function(n,i){let o=new zt("message",{data:i?n:n.toString()});o[Dt]=this,Es(t,this,o)};else if(r==="close")s=function(n,i){let o=new ft("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Dt]=this,Es(t,this,o)};else if(r==="error")s=function(n){let i=new Rt("error",{error:n,message:n.message});i[Dt]=this,Es(t,this,i)};else if(r==="open")s=function(){let n=new st("open");n[Dt]=this,Es(t,this,n)};else return;s[qt]=!!e[qt],s[ra]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[ra]===t&&!e[qt]){this.removeListener(r,e);break}}};pn.exports={CloseEvent:ft,ErrorEvent:Rt,Event:st,EventTarget:Zi,MessageEvent:zt};function Es(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var As=Ie((tl,fn)=>{"use strict";var{tokenChars:Wt}=Et();function Xe(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function er(r){let t=Object.create(null),e=Object.create(null),s=!1,a=!1,n=!1,i,o,l=-1,c=-1,d=-1,u=0;for(;u<r.length;u++)if(c=r.charCodeAt(u),i===void 0)if(d===-1&&Wt[c]===1)l===-1&&(l=u);else if(u!==0&&(c===32||c===9))d===-1&&l!==-1&&(d=u);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${u}`);d===-1&&(d=u);let m=r.slice(l,d);c===44?(Xe(t,m,e),e=Object.create(null)):i=m,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${u}`);else if(o===void 0)if(d===-1&&Wt[c]===1)l===-1&&(l=u);else if(c===32||c===9)d===-1&&l!==-1&&(d=u);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${u}`);d===-1&&(d=u),Xe(e,r.slice(l,d),!0),c===44&&(Xe(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,u),l=d=-1;else throw new SyntaxError(`Unexpected character at index ${u}`);else if(a){if(Wt[c]!==1)throw new SyntaxError(`Unexpected character at index ${u}`);l===-1?l=u:s||(s=!0),a=!1}else if(n)if(Wt[c]===1)l===-1&&(l=u);else if(c===34&&l!==-1)n=!1,d=u;else if(c===92)a=!0;else throw new SyntaxError(`Unexpected character at index ${u}`);else if(c===34&&r.charCodeAt(u-1)===61)n=!0;else if(d===-1&&Wt[c]===1)l===-1&&(l=u);else if(l!==-1&&(c===32||c===9))d===-1&&(d=u);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${u}`);d===-1&&(d=u);let m=r.slice(l,d);s&&(m=m.replace(/\\/g,""),s=!1),Xe(e,o,m),c===44&&(Xe(t,i,e),e=Object.create(null),i=void 0),o=void 0,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${u}`);if(l===-1||n||c===32||c===9)throw new SyntaxError("Unexpected end of input");d===-1&&(d=u);let h=r.slice(l,d);return i===void 0?Xe(t,h,e):(o===void 0?Xe(e,h,!0):s?Xe(e,o,h.replace(/\\/g,"")):Xe(e,o,h),Xe(t,i,e)),t}function tr(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(", ")}fn.exports={format:tr,parse:er}});var Is=Ie((nl,En)=>{"use strict";var sr=require("events"),ar=require("https"),nr=require("http"),vn=require("net"),ir=require("tls"),{randomBytes:rr,createHash:or}=require("crypto"),{Duplex:sl,Readable:al}=require("stream"),{URL:oa}=require("url"),ct=_t(),lr=sa(),cr=ia(),{isBlob:dr}=Et(),{BINARY_TYPES:gn,CLOSE_TIMEOUT:ur,EMPTY_BUFFER:Ps,GUID:hr,kForOnEventAttribute:la,kListener:pr,kStatusCode:mr,kWebSocket:be,NOOP:bn}=et(),{EventTarget:{addEventListener:fr,removeEventListener:gr}}=mn(),{format:yr,parse:vr}=As(),{toBuffer:br}=$t(),wn=Symbol("kAborted"),ca=[8,13],at=["CONNECTING","OPEN","CLOSING","CLOSED"],wr=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,oe=class r extends sr{constructor(t,e,s){super(),this._binaryType=gn[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Ps,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]),kn(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){gn.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 lr({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new cr(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[be]=this,n[be]=this,t[be]=this,a.on("conclude",Sr),a.on("drain",Tr),a.on("error",Cr),a.on("message",_r),a.on("ping",Er),a.on("pong",Ar),n.onerror=Pr,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",Tn),t.on("data",Rs),t.on("end",Cn),t.on("error",_n),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[ct.extensionName]&&this._extensions[ct.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){Me(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())}),Sn(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){da(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Ps,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){da(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Ps,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){da(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||Ps,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){Me(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(oe,"CONNECTING",{enumerable:!0,value:at.indexOf("CONNECTING")});Object.defineProperty(oe.prototype,"CONNECTING",{enumerable:!0,value:at.indexOf("CONNECTING")});Object.defineProperty(oe,"OPEN",{enumerable:!0,value:at.indexOf("OPEN")});Object.defineProperty(oe.prototype,"OPEN",{enumerable:!0,value:at.indexOf("OPEN")});Object.defineProperty(oe,"CLOSING",{enumerable:!0,value:at.indexOf("CLOSING")});Object.defineProperty(oe.prototype,"CLOSING",{enumerable:!0,value:at.indexOf("CLOSING")});Object.defineProperty(oe,"CLOSED",{enumerable:!0,value:at.indexOf("CLOSED")});Object.defineProperty(oe.prototype,"CLOSED",{enumerable:!0,value:at.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(oe.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(oe.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[la])return t[pr];return null},set(t){for(let e of this.listeners(r))if(e[la]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[la]:!0})}})});oe.prototype.addEventListener=fr;oe.prototype.removeEventListener=gr;En.exports=oe;function kn(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:ur,protocolVersion:ca[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,!ca.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${ca.join(", ")})`);let n;if(t instanceof oa)n=t;else try{n=new oa(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 p=new SyntaxError(l);if(r._redirects===0)throw p;Ds(r,p);return}let c=i?443:80,d=rr(16).toString("base64"),u=i?ar.request:nr.request,h=new Set,m;if(a.createConnection=a.createConnection||(i?xr:kr),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&&(m=new ct({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=yr({[ct.extensionName]:m.offer()})),e.length){for(let p of e){if(typeof p!="string"||!wr.test(p)||h.has(p))throw new SyntaxError("An invalid or duplicated subprotocol was specified");h.add(p)}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 p=a.path.split(":");a.socketPath=p[0],a.path=p[1]}let f;if(a.followRedirects){if(r._redirects===0){r._originalIpc=o,r._originalSecure=i,r._originalHostOrSocketPath=o?a.socketPath:n.host;let p=s&&s.headers;if(s={...s,headers:{}},p)for(let[b,k]of Object.entries(p))s.headers[b.toLowerCase()]=k}else if(r.listenerCount("redirect")===0){let p=o?r._originalIpc?a.socketPath===r._originalHostOrSocketPath:!1:r._originalIpc?!1:n.host===r._originalHostOrSocketPath;(!p||r._originalSecure&&!i)&&(delete a.headers.authorization,delete a.headers.cookie,p||delete a.headers.host,a.auth=void 0)}a.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(a.auth).toString("base64")),f=r._req=u(a),r._redirects&&r.emit("redirect",r.url,f)}else f=r._req=u(a);a.timeout&&f.on("timeout",()=>{Me(r,f,"Opening handshake has timed out")}),f.on("error",p=>{f===null||f[wn]||(f=r._req=null,Ds(r,p))}),f.on("response",p=>{let b=p.headers.location,k=p.statusCode;if(b&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){Me(r,f,"Maximum redirects exceeded");return}f.abort();let v;try{v=new oa(b,t)}catch{let y=new SyntaxError(`Invalid URL: ${b}`);Ds(r,y);return}kn(r,v,e,s)}else r.emit("unexpected-response",f,p)||Me(r,f,`Unexpected server response: ${p.statusCode}`)}),f.on("upgrade",(p,b,k)=>{if(r.emit("upgrade",p),r.readyState!==oe.CONNECTING)return;f=r._req=null;let v=p.headers.upgrade;if(v===void 0||v.toLowerCase()!=="websocket"){Me(r,b,"Invalid Upgrade header");return}let g=or("sha1").update(d+hr).digest("base64");if(p.headers["sec-websocket-accept"]!==g){Me(r,b,"Invalid Sec-WebSocket-Accept header");return}let y=p.headers["sec-websocket-protocol"],x;if(y!==void 0?h.size?h.has(y)||(x="Server sent an invalid subprotocol"):x="Server sent a subprotocol but none was requested":h.size&&(x="Server sent no subprotocol"),x){Me(r,b,x);return}y&&(r._protocol=y);let T=p.headers["sec-websocket-extensions"];if(T!==void 0){if(!m){Me(r,b,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let C;try{C=vr(T)}catch{Me(r,b,"Invalid Sec-WebSocket-Extensions header");return}let A=Object.keys(C);if(A.length!==1||A[0]!==ct.extensionName){Me(r,b,"Server indicated an extension that was not requested");return}try{m.accept(C[ct.extensionName])}catch{Me(r,b,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[ct.extensionName]=m}r.setSocket(b,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(f,r):f.end()}function Ds(r,t){r._readyState=oe.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function kr(r){return r.path=r.socketPath,vn.connect(r)}function xr(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=vn.isIP(r.host)?"":r.host),ir.connect(r)}function Me(r,t,e){r._readyState=oe.CLOSING;let s=new Error(e);Error.captureStackTrace(s,Me),t.setHeader?(t[wn]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Ds,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function da(r,t,e){if(t){let s=dr(t)?t.size:br(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${at[r.readyState]})`);process.nextTick(e,s)}}function Sr(r,t){let e=this[be];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[be]!==void 0&&(e._socket.removeListener("data",Rs),process.nextTick(xn,e._socket),r===1005?e.close():e.close(r,t))}function Tr(){let r=this[be];r.isPaused||r._socket.resume()}function Cr(r){let t=this[be];t._socket[be]!==void 0&&(t._socket.removeListener("data",Rs),process.nextTick(xn,t._socket),t.close(r[mr])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function yn(){this[be].emitClose()}function _r(r,t){this[be].emit("message",r,t)}function Er(r){let t=this[be];t._autoPong&&t.pong(r,!this._isServer,bn),t.emit("ping",r)}function Ar(r){this[be].emit("pong",r)}function xn(r){r.resume()}function Pr(r){let t=this[be];t.readyState!==oe.CLOSED&&(t.readyState===oe.OPEN&&(t._readyState=oe.CLOSING,Sn(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function Sn(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function Tn(){let r=this[be];if(this.removeListener("close",Tn),this.removeListener("data",Rs),this.removeListener("end",Cn),r._readyState=oe.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[be]=void 0,clearTimeout(r._closeTimer),r._receiver._writableState.finished||r._receiver._writableState.errorEmitted?r.emitClose():(r._receiver.on("error",yn),r._receiver.on("finish",yn))}function Rs(r){this[be]._receiver.write(r)||this.pause()}function Cn(){let r=this[be];r._readyState=oe.CLOSING,r._receiver.end(),this.end()}function _n(){let r=this[be];this.removeListener("error",_n),this.on("error",bn),r&&(r._readyState=oe.CLOSING,this.destroy())}});var Rn=Ie((rl,Dn)=>{"use strict";var il=Is(),{Duplex:Dr}=require("stream");function An(r){r.emit("close")}function Rr(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Pn(r){this.removeListener("error",Pn),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function Ir(r,t){let e=!0,s=new Dr({...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(An,s);return}let i=!1;r.once("error",function(l){i=!0,n(l)}),r.once("close",function(){i||n(a),process.nextTick(An,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",Rr),s.on("error",Pn),s}Dn.exports=Ir});var ua=Ie((ol,In)=>{"use strict";var{tokenChars:Lr}=Et();function Fr(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&&Lr[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}In.exports={parse:Fr}});var Un=Ie((cl,Nn)=>{"use strict";var Mr=require("events"),Ls=require("http"),{Duplex:ll}=require("stream"),{createHash:Or}=require("crypto"),Ln=As(),gt=_t(),Br=ua(),Nr=Is(),{CLOSE_TIMEOUT:Ur,GUID:jr,kWebSocket:$r}=et(),Hr=/^[+/0-9A-Za-z]{22}==$/,Fn=0,Mn=1,Bn=2,ha=class extends Mr{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Ur,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:Nr,...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=Ls.createServer((s,a)=>{let n=Ls.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=qr(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=Fn}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===Bn){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(Gt,this);return}if(t&&this.once("close",t),this._state!==Mn)if(this._state=Mn,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(Gt,this):process.nextTick(Gt,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{Gt(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",On);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){yt(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){yt(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Hr.test(n)){yt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){yt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){Vt(e,400);return}let l=t.headers["sec-websocket-protocol"],c=new Set;if(l!==void 0)try{c=Br.parse(l)}catch{yt(this,t,e,400,"Invalid Sec-WebSocket-Protocol header");return}let d=t.headers["sec-websocket-extensions"],u={};if(this.options.perMessageDeflate&&d!==void 0){let h=new gt({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let m=Ln.parse(d);m[gt.extensionName]&&(h.accept(m[gt.extensionName]),u[gt.extensionName]=h)}catch{yt(this,t,e,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let h={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(h,(m,f,p,b)=>{if(!m)return Vt(e,f||401,p,b);this.completeUpgrade(u,n,c,t,e,s,a)});return}if(!this.options.verifyClient(h))return Vt(e,401)}this.completeUpgrade(u,n,c,t,e,s,a)}completeUpgrade(t,e,s,a,n,i,o){if(!n.readable||!n.writable)return n.destroy();if(n[$r])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>Fn)return Vt(n,503);let c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${Or("sha1").update(e+jr).digest("base64")}`],d=new this.options.WebSocket(null,void 0,this.options);if(s.size){let u=this.options.handleProtocols?this.options.handleProtocols(s,a):s.values().next().value;u&&(c.push(`Sec-WebSocket-Protocol: ${u}`),d._protocol=u)}if(t[gt.extensionName]){let u=t[gt.extensionName].params,h=Ln.format({[gt.extensionName]:[u]});c.push(`Sec-WebSocket-Extensions: ${h}`),d._extensions=t}this.emit("headers",c,a),n.write(c.concat(`\r
|
|
1
|
+
"use strict";var ji=Object.create;var gs=Object.defineProperty;var Hi=Object.getOwnPropertyDescriptor;var qi=Object.getOwnPropertyNames;var Wi=Object.getPrototypeOf,zi=Object.prototype.hasOwnProperty;var Oe=(r,t)=>()=>(t||r((t={exports:{}}).exports,t),t.exports),Gi=(r,t)=>{for(var e in t)gs(r,e,{get:t[e],enumerable:!0})},za=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of qi(t))!zi.call(r,a)&&a!==e&&gs(r,a,{get:()=>t[a],enumerable:!(s=Hi(t,a))||s.enumerable});return r};var Ne=(r,t,e)=>(e=r!=null?ji(Wi(r)):{},za(t||!r||!r.__esModule?gs(e,"default",{value:r,enumerable:!0}):e,r)),Vi=r=>za(gs({},"__esModule",{value:!0}),r);var it=Oe((oc,Sn)=>{"use strict";var kn=["nodebuffer","arraybuffer","fragments"],xn=typeof Blob<"u";xn&&kn.push("blob");Sn.exports={BINARY_TYPES:kn,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:xn,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var es=Oe((lc,Ns)=>{"use strict";var{EMPTY_BUFFER:wr}=it(),ba=Buffer[Symbol.species];function kr(r,t){if(r.length===0)return wr;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 ba(e.buffer,e.byteOffset,s):e}function Tn(r,t,e,s,a){for(let n=0;n<a;n++)e[s+n]=r[n]^t[n&3]}function Cn(r,t){for(let e=0;e<r.length;e++)r[e]^=t[e&3]}function xr(r){return r.length===r.buffer.byteLength?r.buffer:r.buffer.slice(r.byteOffset,r.byteOffset+r.length)}function wa(r){if(wa.readOnly=!0,Buffer.isBuffer(r))return r;let t;return r instanceof ArrayBuffer?t=new ba(r):ArrayBuffer.isView(r)?t=new ba(r.buffer,r.byteOffset,r.byteLength):(t=Buffer.from(r),wa.readOnly=!1),t}Ns.exports={concat:kr,mask:Tn,toArrayBuffer:xr,toBuffer:wa,unmask:Cn};if(!process.env.WS_NO_BUFFER_UTIL)try{let r=require("bufferutil");Ns.exports.mask=function(t,e,s,a,n){n<48?Tn(t,e,s,a,n):r.mask(t,e,s,a,n)},Ns.exports.unmask=function(t,e){t.length<32?Cn(t,e):r.unmask(t,e)}}catch{}});var An=Oe((cc,En)=>{"use strict";var _n=Symbol("kDone"),ka=Symbol("kRun"),xa=class{constructor(t){this[_n]=()=>{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[_n])}}};En.exports=xa});var Ft=Oe((dc,In)=>{"use strict";var ts=require("zlib"),Pn=es(),Sr=An(),{kStatusCode:Dn}=it(),Tr=Buffer[Symbol.species],Cr=Buffer.from([0,0,255,255]),Us=Symbol("permessage-deflate"),rt=Symbol("total-length"),Lt=Symbol("callback"),ut=Symbol("buffers"),Mt=Symbol("error"),Bs,Sa=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,!Bs){let e=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;Bs=new Sr(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[Lt];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){Bs.add(a=>{this._decompress(t,e,(n,i)=>{a(),s(n,i)})})}compress(t,e,s){Bs.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"?ts.Z_DEFAULT_WINDOWBITS:this.params[n];this._inflate=ts.createInflateRaw({...this._options.zlibInflateOptions,windowBits:i}),this._inflate[Us]=this,this._inflate[rt]=0,this._inflate[ut]=[],this._inflate.on("error",Er),this._inflate.on("data",Rn)}this._inflate[Lt]=s,this._inflate.write(t),e&&this._inflate.write(Cr),this._inflate.flush(()=>{let n=this._inflate[Mt];if(n){this._inflate.close(),this._inflate=null,s(n);return}let i=Pn.concat(this._inflate[ut],this._inflate[rt]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[rt]=0,this._inflate[ut]=[],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"?ts.Z_DEFAULT_WINDOWBITS:this.params[n];this._deflate=ts.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:i}),this._deflate[rt]=0,this._deflate[ut]=[],this._deflate.on("data",_r)}this._deflate[Lt]=s,this._deflate.write(t),this._deflate.flush(ts.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let n=Pn.concat(this._deflate[ut],this._deflate[rt]);e&&(n=new Tr(n.buffer,n.byteOffset,n.length-4)),this._deflate[Lt]=null,this._deflate[rt]=0,this._deflate[ut]=[],e&&this.params[`${a}_no_context_takeover`]&&this._deflate.reset(),s(null,n)})}};In.exports=Sa;function _r(r){this[ut].push(r),this[rt]+=r.length}function Rn(r){if(this[rt]+=r.length,this[Us]._maxPayload<1||this[rt]<=this[Us]._maxPayload){this[ut].push(r);return}this[Mt]=new RangeError("Max payload size exceeded"),this[Mt].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[Mt][Dn]=1009,this.removeListener("data",Rn),this.reset()}function Er(r){if(this[Us]._inflate=null,this[Mt]){this[Lt](this[Mt]);return}r[Dn]=1007,this[Lt](r)}});var Ot=Oe((hc,$s)=>{"use strict";var{isUtf8:Ln}=require("buffer"),{hasBlob:Ar}=it(),Pr=[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 Dr(r){return r>=1e3&&r<=1014&&r!==1004&&r!==1005&&r!==1006||r>=3e3&&r<=4999}function Ta(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 Rr(r){return Ar&&typeof r=="object"&&typeof r.arrayBuffer=="function"&&typeof r.type=="string"&&typeof r.stream=="function"&&(r[Symbol.toStringTag]==="Blob"||r[Symbol.toStringTag]==="File")}$s.exports={isBlob:Rr,isValidStatusCode:Dr,isValidUTF8:Ta,tokenChars:Pr};if(Ln)$s.exports.isValidUTF8=function(r){return r.length<24?Ta(r):Ln(r)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let r=require("utf-8-validate");$s.exports.isValidUTF8=function(t){return t.length<32?Ta(t):r(t)}}catch{}});var Pa=Oe((uc,$n)=>{"use strict";var{Writable:Ir}=require("stream"),Mn=Ft(),{BINARY_TYPES:Lr,EMPTY_BUFFER:Fn,kStatusCode:Mr,kWebSocket:Fr}=it(),{concat:Ca,toArrayBuffer:Or,unmask:Nr}=es(),{isValidStatusCode:Br,isValidUTF8:On}=Ot(),js=Buffer[Symbol.species],ze=0,Nn=1,Bn=2,Un=3,_a=4,Ea=5,Hs=6,Aa=class extends Ir{constructor(t={}){super(),this._allowSynchronousEvents=t.allowSynchronousEvents!==void 0?t.allowSynchronousEvents:!0,this._binaryType=t.binaryType||Lr[0],this._extensions=t.extensions||{},this._isServer=!!t.isServer,this._maxPayload=t.maxPayload|0,this._skipUTF8Validation=!!t.skipUTF8Validation,this[Fr]=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=ze}_write(t,e,s){if(this._opcode===8&&this._state==ze)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 js(s.buffer,s.byteOffset+t,s.length-t),new js(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 js(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 ze:this.getInfo(t);break;case Nn:this.getPayloadLength16(t);break;case Bn:this.getPayloadLength64(t);break;case Un:this.getMask();break;case _a:this.getData(t);break;case Ea:case Hs: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[Mn.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=Nn:this._payloadLength===127?this._state=Bn: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=Un:this._state=_a}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=_a}getData(t){let e=Fn;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&&Nr(e,this._mask)}if(this._opcode>7){this.controlMessage(e,t);return}if(this._compressed){this._state=Ea,this.decompress(e,t);return}e.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(e)),this.dataMessage(t)}decompress(t,e){this._extensions[Mn.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===ze&&this.startLoop(e)})}dataMessage(t){if(!this._fin){this._state=ze;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=Ca(s,e):this._binaryType==="arraybuffer"?a=Or(Ca(s,e)):this._binaryType==="blob"?a=new Blob(s):a=s,this._allowSynchronousEvents?(this.emit("message",a,!0),this._state=ze):(this._state=Hs,setImmediate(()=>{this.emit("message",a,!0),this._state=ze,this.startLoop(t)}))}else{let a=Ca(s,e);if(!this._skipUTF8Validation&&!On(a)){let n=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");t(n);return}this._state===Ea||this._allowSynchronousEvents?(this.emit("message",a,!1),this._state=ze):(this._state=Hs,setImmediate(()=>{this.emit("message",a,!1),this._state=ze,this.startLoop(t)}))}}controlMessage(t,e){if(this._opcode===8){if(t.length===0)this._loop=!1,this.emit("conclude",1005,Fn),this.end();else{let s=t.readUInt16BE(0);if(!Br(s)){let n=this.createError(RangeError,`invalid status code ${s}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");e(n);return}let a=new js(t.buffer,t.byteOffset+2,t.length-2);if(!this._skipUTF8Validation&&!On(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=ze;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",t),this._state=ze):(this._state=Hs,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",t),this._state=ze,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[Mr]=a,i}};$n.exports=Aa});var Ia=Oe((fc,qn)=>{"use strict";var{Duplex:pc}=require("stream"),{randomFillSync:Ur}=require("crypto"),jn=Ft(),{EMPTY_BUFFER:$r,kWebSocket:jr,NOOP:Hr}=it(),{isBlob:Nt,isValidStatusCode:qr}=Ot(),{mask:Hn,toBuffer:wt}=es(),Ge=Symbol("kByteLength"),Wr=Buffer.alloc(4),qs=8*1024,kt,Bt=qs,Ke=0,zr=1,Gr=2,Da=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=Ke,this.onerror=Hr,this[jr]=void 0}static frame(t,e){let s,a=!1,n=2,i=!1;e.mask&&(s=e.maskBuffer||Wr,e.generateMask?e.generateMask(s):(Bt===qs&&(kt===void 0&&(kt=Buffer.alloc(qs)),Ur(kt,0,qs),Bt=0),s[0]=kt[Bt++],s[1]=kt[Bt++],s[2]=kt[Bt++],s[3]=kt[Bt++]),i=(s[0]|s[1]|s[2]|s[3])===0,n=6);let o;typeof t=="string"?(!e.mask||i)&&e[Ge]!==void 0?o=e[Ge]:(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?(Hn(t,s,c,n,o),[c]):(Hn(t,s,t,0,o),[c,t])):[c,t]}close(t,e,s,a){let n;if(t===void 0)n=$r;else{if(typeof t!="number"||!qr(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={[Ge]:n.length,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==Ke?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):Nt(t)?(a=t.size,n=!1):(t=wt(t),a=t.length,n=wt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[Ge]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:9,readOnly:n,rsv1:!1};Nt(t)?this._state!==Ke?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==Ke?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):Nt(t)?(a=t.size,n=!1):(t=wt(t),a=t.length,n=wt.readOnly),a>125)throw new RangeError("The data size must not be greater than 125 bytes");let i={[Ge]:a,fin:!0,generateMask:this._generateMask,mask:e,maskBuffer:this._maskBuffer,opcode:10,readOnly:n,rsv1:!1};Nt(t)?this._state!==Ke?this.enqueue([this.getBlobData,t,!1,i,s]):this.getBlobData(t,!1,i,s):this._state!==Ke?this.enqueue([this.dispatch,t,!1,i,s]):this.sendFrame(r.frame(t,i),s)}send(t,e,s){let a=this._extensions[jn.extensionName],n=e.binary?2:1,i=e.compress,o,l;typeof t=="string"?(o=Buffer.byteLength(t),l=!1):Nt(t)?(o=t.size,l=!1):(t=wt(t),o=t.length,l=wt.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={[Ge]:o,fin:e.fin,generateMask:this._generateMask,mask:e.mask,maskBuffer:this._maskBuffer,opcode:n,readOnly:l,rsv1:i};Nt(t)?this._state!==Ke?this.enqueue([this.getBlobData,t,this._compress,c,s]):this.getBlobData(t,this._compress,c,s):this._state!==Ke?this.enqueue([this.dispatch,t,this._compress,c,s]):this.dispatch(t,this._compress,c,s)}getBlobData(t,e,s,a){this._bufferedBytes+=s[Ge],this._state=Gr,t.arrayBuffer().then(n=>{if(this._socket.destroyed){let o=new Error("The socket was closed while the blob was being read");process.nextTick(Ra,this,o,a);return}this._bufferedBytes-=s[Ge];let i=wt(n);e?this.dispatch(i,e,s,a):(this._state=Ke,this.sendFrame(r.frame(i,s),a),this.dequeue())}).catch(n=>{process.nextTick(Vr,this,n,a)})}dispatch(t,e,s,a){if(!e){this.sendFrame(r.frame(t,s),a);return}let n=this._extensions[jn.extensionName];this._bufferedBytes+=s[Ge],this._state=zr,n.compress(t,s.fin,(i,o)=>{if(this._socket.destroyed){let l=new Error("The socket was closed while data was being compressed");Ra(this,l,a);return}this._bufferedBytes-=s[Ge],this._state=Ke,s.readOnly=!1,this.sendFrame(r.frame(o,s),a),this.dequeue()})}dequeue(){for(;this._state===Ke&&this._queue.length;){let t=this._queue.shift();this._bufferedBytes-=t[3][Ge],Reflect.apply(t[0],this,t.slice(1))}}enqueue(t){this._bufferedBytes+=t[3][Ge],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)}};qn.exports=Da;function Ra(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 Vr(r,t,e){Ra(r,t,e),r.onerror(t)}});var Qn=Oe((mc,Jn)=>{"use strict";var{kForOnEventAttribute:ss,kListener:La}=it(),Wn=Symbol("kCode"),zn=Symbol("kData"),Gn=Symbol("kError"),Vn=Symbol("kMessage"),Yn=Symbol("kReason"),Ut=Symbol("kTarget"),Kn=Symbol("kType"),Xn=Symbol("kWasClean"),ot=class{constructor(t){this[Ut]=null,this[Kn]=t}get target(){return this[Ut]}get type(){return this[Kn]}};Object.defineProperty(ot.prototype,"target",{enumerable:!0});Object.defineProperty(ot.prototype,"type",{enumerable:!0});var xt=class extends ot{constructor(t,e={}){super(t),this[Wn]=e.code===void 0?0:e.code,this[Yn]=e.reason===void 0?"":e.reason,this[Xn]=e.wasClean===void 0?!1:e.wasClean}get code(){return this[Wn]}get reason(){return this[Yn]}get wasClean(){return this[Xn]}};Object.defineProperty(xt.prototype,"code",{enumerable:!0});Object.defineProperty(xt.prototype,"reason",{enumerable:!0});Object.defineProperty(xt.prototype,"wasClean",{enumerable:!0});var $t=class extends ot{constructor(t,e={}){super(t),this[Gn]=e.error===void 0?null:e.error,this[Vn]=e.message===void 0?"":e.message}get error(){return this[Gn]}get message(){return this[Vn]}};Object.defineProperty($t.prototype,"error",{enumerable:!0});Object.defineProperty($t.prototype,"message",{enumerable:!0});var as=class extends ot{constructor(t,e={}){super(t),this[zn]=e.data===void 0?null:e.data}get data(){return this[zn]}};Object.defineProperty(as.prototype,"data",{enumerable:!0});var Yr={addEventListener(r,t,e={}){for(let a of this.listeners(r))if(!e[ss]&&a[La]===t&&!a[ss])return;let s;if(r==="message")s=function(n,i){let o=new as("message",{data:i?n:n.toString()});o[Ut]=this,Ws(t,this,o)};else if(r==="close")s=function(n,i){let o=new xt("close",{code:n,reason:i.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});o[Ut]=this,Ws(t,this,o)};else if(r==="error")s=function(n){let i=new $t("error",{error:n,message:n.message});i[Ut]=this,Ws(t,this,i)};else if(r==="open")s=function(){let n=new ot("open");n[Ut]=this,Ws(t,this,n)};else return;s[ss]=!!e[ss],s[La]=t,e.once?this.once(r,s):this.on(r,s)},removeEventListener(r,t){for(let e of this.listeners(r))if(e[La]===t&&!e[ss]){this.removeListener(r,e);break}}};Jn.exports={CloseEvent:xt,ErrorEvent:$t,Event:ot,EventTarget:Yr,MessageEvent:as};function Ws(r,t,e){typeof r=="object"&&r.handleEvent?r.handleEvent.call(r,e):r.call(t,e)}});var zs=Oe((gc,Zn)=>{"use strict";var{tokenChars:ns}=Ot();function tt(r,t,e){r[t]===void 0?r[t]=[e]:r[t].push(e)}function Kr(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&&ns[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 f=r.slice(l,d);c===44?(tt(t,f,e),e=Object.create(null)):i=f,l=d=-1}else throw new SyntaxError(`Unexpected character at index ${h}`);else if(o===void 0)if(d===-1&&ns[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),tt(e,r.slice(l,d),!0),c===44&&(tt(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(ns[c]!==1)throw new SyntaxError(`Unexpected character at index ${h}`);l===-1?l=h:s||(s=!0),a=!1}else if(n)if(ns[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&&ns[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 f=r.slice(l,d);s&&(f=f.replace(/\\/g,""),s=!1),tt(e,o,f),c===44&&(tt(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?tt(t,u,e):(o===void 0?tt(e,u,!0):s?tt(e,o,u.replace(/\\/g,"")):tt(e,o,u),tt(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(", ")}Zn.exports={format:Xr,parse:Kr}});var Ks=Oe((bc,hi)=>{"use strict";var Jr=require("events"),Qr=require("https"),Zr=require("http"),si=require("net"),eo=require("tls"),{randomBytes:to,createHash:so}=require("crypto"),{Duplex:yc,Readable:vc}=require("stream"),{URL:Ma}=require("url"),pt=Ft(),ao=Pa(),no=Ia(),{isBlob:io}=Ot(),{BINARY_TYPES:ei,CLOSE_TIMEOUT:ro,EMPTY_BUFFER:Gs,GUID:oo,kForOnEventAttribute:Fa,kListener:lo,kStatusCode:co,kWebSocket:Ee,NOOP:ai}=it(),{EventTarget:{addEventListener:ho,removeEventListener:uo}}=Qn(),{format:po,parse:fo}=zs(),{toBuffer:mo}=es(),ni=Symbol("kAborted"),Oa=[8,13],lt=["CONNECTING","OPEN","CLOSING","CLOSED"],go=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,pe=class r extends Jr{constructor(t,e,s){super(),this._binaryType=ei[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=Gs,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]),ii(this,t,e,s)):(this._autoPong=s.autoPong,this._closeTimeout=s.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(t){ei.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 ao({allowSynchronousEvents:s.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxPayload:s.maxPayload,skipUTF8Validation:s.skipUTF8Validation}),n=new no(t,this._extensions,s.generateMask);this._receiver=a,this._sender=n,this._socket=t,a[Ee]=this,n[Ee]=this,t[Ee]=this,a.on("conclude",bo),a.on("drain",wo),a.on("error",ko),a.on("message",xo),a.on("ping",So),a.on("pong",To),n.onerror=Co,t.setTimeout&&t.setTimeout(0),t.setNoDelay&&t.setNoDelay(),e.length>0&&t.unshift(e),t.on("close",li),t.on("data",Ys),t.on("end",ci),t.on("error",di),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[pt.extensionName]&&this._extensions[pt.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){$e(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())}),oi(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){Na(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.ping(t||Gs,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){Na(this,t,s);return}e===void 0&&(e=!this._isServer),this._sender.pong(t||Gs,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){Na(this,t,s);return}let a={binary:typeof t!="string",mask:!this._isServer,compress:!0,fin:!0,...e};this._extensions[pt.extensionName]||(a.compress=!1),this._sender.send(t||Gs,a,s)}terminate(){if(this.readyState!==r.CLOSED){if(this.readyState===r.CONNECTING){$e(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=r.CLOSING,this._socket.destroy())}}};Object.defineProperty(pe,"CONNECTING",{enumerable:!0,value:lt.indexOf("CONNECTING")});Object.defineProperty(pe.prototype,"CONNECTING",{enumerable:!0,value:lt.indexOf("CONNECTING")});Object.defineProperty(pe,"OPEN",{enumerable:!0,value:lt.indexOf("OPEN")});Object.defineProperty(pe.prototype,"OPEN",{enumerable:!0,value:lt.indexOf("OPEN")});Object.defineProperty(pe,"CLOSING",{enumerable:!0,value:lt.indexOf("CLOSING")});Object.defineProperty(pe.prototype,"CLOSING",{enumerable:!0,value:lt.indexOf("CLOSING")});Object.defineProperty(pe,"CLOSED",{enumerable:!0,value:lt.indexOf("CLOSED")});Object.defineProperty(pe.prototype,"CLOSED",{enumerable:!0,value:lt.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(r=>{Object.defineProperty(pe.prototype,r,{enumerable:!0})});["open","error","close","message"].forEach(r=>{Object.defineProperty(pe.prototype,`on${r}`,{enumerable:!0,get(){for(let t of this.listeners(r))if(t[Fa])return t[lo];return null},set(t){for(let e of this.listeners(r))if(e[Fa]){this.removeListener(r,e);break}typeof t=="function"&&this.addEventListener(r,t,{[Fa]:!0})}})});pe.prototype.addEventListener=ho;pe.prototype.removeEventListener=uo;hi.exports=pe;function ii(r,t,e,s){let a={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:ro,protocolVersion:Oa[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,!Oa.includes(a.protocolVersion))throw new RangeError(`Unsupported protocol version: ${a.protocolVersion} (supported versions: ${Oa.join(", ")})`);let n;if(t instanceof Ma)n=t;else try{n=new Ma(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 m=new SyntaxError(l);if(r._redirects===0)throw m;Vs(r,m);return}let c=i?443:80,d=to(16).toString("base64"),h=i?Qr.request:Zr.request,u=new Set,f;if(a.createConnection=a.createConnection||(i?vo:yo),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&&(f=new pt({...a.perMessageDeflate,isServer:!1,maxPayload:a.maxPayload}),a.headers["Sec-WebSocket-Extensions"]=po({[pt.extensionName]:f.offer()})),e.length){for(let m of e){if(typeof m!="string"||!go.test(m)||u.has(m))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(m)}a.headers["Sec-WebSocket-Protocol"]=e.join(",")}if(a.origin&&(a.protocolVersion<13?a.headers["Sec-WebSocket-Origin"]=a.origin:a.headers.Origin=a.origin),(n.username||n.password)&&(a.auth=`${n.username}:${n.password}`),o){let m=a.path.split(":");a.socketPath=m[0],a.path=m[1]}let p;if(a.followRedirects){if(r._redirects===0){r._originalIpc=o,r._originalSecure=i,r._originalHostOrSocketPath=o?a.socketPath:n.host;let m=s&&s.headers;if(s={...s,headers:{}},m)for(let[v,k]of Object.entries(m))s.headers[v.toLowerCase()]=k}else if(r.listenerCount("redirect")===0){let m=o?r._originalIpc?a.socketPath===r._originalHostOrSocketPath:!1:r._originalIpc?!1:n.host===r._originalHostOrSocketPath;(!m||r._originalSecure&&!i)&&(delete a.headers.authorization,delete a.headers.cookie,m||delete a.headers.host,a.auth=void 0)}a.auth&&!s.headers.authorization&&(s.headers.authorization="Basic "+Buffer.from(a.auth).toString("base64")),p=r._req=h(a),r._redirects&&r.emit("redirect",r.url,p)}else p=r._req=h(a);a.timeout&&p.on("timeout",()=>{$e(r,p,"Opening handshake has timed out")}),p.on("error",m=>{p===null||p[ni]||(p=r._req=null,Vs(r,m))}),p.on("response",m=>{let v=m.headers.location,k=m.statusCode;if(v&&a.followRedirects&&k>=300&&k<400){if(++r._redirects>a.maxRedirects){$e(r,p,"Maximum redirects exceeded");return}p.abort();let b;try{b=new Ma(v,t)}catch{let y=new SyntaxError(`Invalid URL: ${v}`);Vs(r,y);return}ii(r,b,e,s)}else r.emit("unexpected-response",p,m)||$e(r,p,`Unexpected server response: ${m.statusCode}`)}),p.on("upgrade",(m,v,k)=>{if(r.emit("upgrade",m),r.readyState!==pe.CONNECTING)return;p=r._req=null;let b=m.headers.upgrade;if(b===void 0||b.toLowerCase()!=="websocket"){$e(r,v,"Invalid Upgrade header");return}let g=so("sha1").update(d+oo).digest("base64");if(m.headers["sec-websocket-accept"]!==g){$e(r,v,"Invalid Sec-WebSocket-Accept header");return}let y=m.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){$e(r,v,x);return}y&&(r._protocol=y);let S=m.headers["sec-websocket-extensions"];if(S!==void 0){if(!f){$e(r,v,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let T;try{T=fo(S)}catch{$e(r,v,"Invalid Sec-WebSocket-Extensions header");return}let P=Object.keys(T);if(P.length!==1||P[0]!==pt.extensionName){$e(r,v,"Server indicated an extension that was not requested");return}try{f.accept(T[pt.extensionName])}catch{$e(r,v,"Invalid Sec-WebSocket-Extensions header");return}r._extensions[pt.extensionName]=f}r.setSocket(v,k,{allowSynchronousEvents:a.allowSynchronousEvents,generateMask:a.generateMask,maxPayload:a.maxPayload,skipUTF8Validation:a.skipUTF8Validation})}),a.finishRequest?a.finishRequest(p,r):p.end()}function Vs(r,t){r._readyState=pe.CLOSING,r._errorEmitted=!0,r.emit("error",t),r.emitClose()}function yo(r){return r.path=r.socketPath,si.connect(r)}function vo(r){return r.path=void 0,!r.servername&&r.servername!==""&&(r.servername=si.isIP(r.host)?"":r.host),eo.connect(r)}function $e(r,t,e){r._readyState=pe.CLOSING;let s=new Error(e);Error.captureStackTrace(s,$e),t.setHeader?(t[ni]=!0,t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),process.nextTick(Vs,r,s)):(t.destroy(s),t.once("error",r.emit.bind(r,"error")),t.once("close",r.emitClose.bind(r)))}function Na(r,t,e){if(t){let s=io(t)?t.size:mo(t).length;r._socket?r._sender._bufferedBytes+=s:r._bufferedAmount+=s}if(e){let s=new Error(`WebSocket is not open: readyState ${r.readyState} (${lt[r.readyState]})`);process.nextTick(e,s)}}function bo(r,t){let e=this[Ee];e._closeFrameReceived=!0,e._closeMessage=t,e._closeCode=r,e._socket[Ee]!==void 0&&(e._socket.removeListener("data",Ys),process.nextTick(ri,e._socket),r===1005?e.close():e.close(r,t))}function wo(){let r=this[Ee];r.isPaused||r._socket.resume()}function ko(r){let t=this[Ee];t._socket[Ee]!==void 0&&(t._socket.removeListener("data",Ys),process.nextTick(ri,t._socket),t.close(r[co])),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r))}function ti(){this[Ee].emitClose()}function xo(r,t){this[Ee].emit("message",r,t)}function So(r){let t=this[Ee];t._autoPong&&t.pong(r,!this._isServer,ai),t.emit("ping",r)}function To(r){this[Ee].emit("pong",r)}function ri(r){r.resume()}function Co(r){let t=this[Ee];t.readyState!==pe.CLOSED&&(t.readyState===pe.OPEN&&(t._readyState=pe.CLOSING,oi(t)),this._socket.end(),t._errorEmitted||(t._errorEmitted=!0,t.emit("error",r)))}function oi(r){r._closeTimer=setTimeout(r._socket.destroy.bind(r._socket),r._closeTimeout)}function li(){let r=this[Ee];if(this.removeListener("close",li),this.removeListener("data",Ys),this.removeListener("end",ci),r._readyState=pe.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[Ee]=void 0,clearTimeout(r._closeTimer),r._receiver._writableState.finished||r._receiver._writableState.errorEmitted?r.emitClose():(r._receiver.on("error",ti),r._receiver.on("finish",ti))}function Ys(r){this[Ee]._receiver.write(r)||this.pause()}function ci(){let r=this[Ee];r._readyState=pe.CLOSING,r._receiver.end(),this.end()}function di(){let r=this[Ee];this.removeListener("error",di),this.on("error",ai),r&&(r._readyState=pe.CLOSING,this.destroy())}});var mi=Oe((kc,fi)=>{"use strict";var wc=Ks(),{Duplex:_o}=require("stream");function ui(r){r.emit("close")}function Eo(){!this.destroyed&&this._writableState.finished&&this.destroy()}function pi(r){this.removeListener("error",pi),this.destroy(),this.listenerCount("error")===0&&this.emit("error",r)}function Ao(r,t){let e=!0,s=new _o({...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(ui,s);return}let i=!1;r.once("error",function(l){i=!0,n(l)}),r.once("close",function(){i||n(a),process.nextTick(ui,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",pi),s}fi.exports=Ao});var Ba=Oe((xc,gi)=>{"use strict";var{tokenChars:Po}=Ot();function Do(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&&Po[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}gi.exports={parse:Do}});var Si=Oe((Tc,xi)=>{"use strict";var Ro=require("events"),Xs=require("http"),{Duplex:Sc}=require("stream"),{createHash:Io}=require("crypto"),yi=zs(),St=Ft(),Lo=Ba(),Mo=Ks(),{CLOSE_TIMEOUT:Fo,GUID:Oo,kWebSocket:No}=it(),Bo=/^[+/0-9A-Za-z]{22}==$/,vi=0,bi=1,ki=2,Ua=class extends Ro{constructor(t,e){if(super(),t={allowSynchronousEvents:!0,autoPong:!0,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Fo,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:Mo,...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=Xs.createServer((s,a)=>{let n=Xs.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=Uo(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=vi}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===ki){t&&this.once("close",()=>{t(new Error("The server is not running"))}),process.nextTick(is,this);return}if(t&&this.once("close",t),this._state!==bi)if(this._state=bi,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(is,this):process.nextTick(is,this);else{let e=this._server;this._removeListeners(),this._removeListeners=this._server=null,e.close(()=>{is(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",wi);let n=t.headers["sec-websocket-key"],i=t.headers.upgrade,o=+t.headers["sec-websocket-version"];if(t.method!=="GET"){Tt(this,t,e,405,"Invalid HTTP method");return}if(i===void 0||i.toLowerCase()!=="websocket"){Tt(this,t,e,400,"Invalid Upgrade header");return}if(n===void 0||!Bo.test(n)){Tt(this,t,e,400,"Missing or invalid Sec-WebSocket-Key header");return}if(o!==13&&o!==8){Tt(this,t,e,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(t)){rs(e,400);return}let l=t.headers["sec-websocket-protocol"],c=new Set;if(l!==void 0)try{c=Lo.parse(l)}catch{Tt(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 St({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let f=yi.parse(d);f[St.extensionName]&&(u.accept(f[St.extensionName]),h[St.extensionName]=u)}catch{Tt(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,(f,p,m,v)=>{if(!f)return rs(e,p||401,m,v);this.completeUpgrade(h,n,c,t,e,s,a)});return}if(!this.options.verifyClient(u))return rs(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[No])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>vi)return rs(n,503);let c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${Io("sha1").update(e+Oo).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[St.extensionName]){let h=t[St.extensionName].params,u=yi.format({[St.extensionName]:[h]});c.push(`Sec-WebSocket-Extensions: ${u}`),d._extensions=t}this.emit("headers",c,a),n.write(c.concat(`\r
|
|
2
2
|
`).join(`\r
|
|
3
|
-
`)),n.removeListener("error",
|
|
3
|
+
`)),n.removeListener("error",wi),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(is,this)})),o(d,a)}};xi.exports=Ua;function Uo(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 is(r){r._state=ki,r.emit("close")}function wi(){this.destroy()}function rs(r,t,e,s){e=e||Xs.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} ${Xs.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
|
|
7
|
+
`+e)}function Tt(r,t,e,s,a,n){if(r.listenerCount("wsClientError")){let i=new Error(a);Error.captureStackTrace(i,Tt),r.emit("wsClientError",i,e,t)}else rs(e,s,a,n)}});var al={};Gi(al,{default:()=>ta});module.exports=Vi(al);var Bi=require("fs"),ve=require("obsidian");var mt="agent-fleet-agents";var ht="agent-fleet-dashboard",Ye="agent-fleet-chat",Qe={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:{}},Ga=["agents","skills","tasks","runs","memory","channels"];var C=require("obsidian");var na=[{path:"agents/fleet-orchestrator/CONTEXT.md",content:`---
|
|
8
8
|
{}
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
You operate inside an Obsidian vault with the Agent Fleet plugin installed.
|
|
14
14
|
The fleet data lives in the \`_fleet/\` folder at the vault root.
|
|
15
15
|
You can read and write files in this folder to manage the fleet.
|
|
16
|
+
|
|
17
|
+
The plugin runs on macOS, Windows, and Linux. Agent processes are spawned
|
|
18
|
+
via the Claude Code CLI, which must be installed separately. Credentials
|
|
19
|
+
for channels and MCP servers are stored securely in the OS keychain
|
|
20
|
+
(macOS Keychain, Windows Credential Manager, Linux Secret Service).
|
|
16
21
|
`},{path:"agents/fleet-orchestrator/SKILLS.md",content:`---
|
|
17
22
|
{}
|
|
18
23
|
---
|
|
@@ -30,7 +35,6 @@ tags:
|
|
|
30
35
|
- orchestration
|
|
31
36
|
skills:
|
|
32
37
|
- agent-fleet-system
|
|
33
|
-
model: claude-opus-4-6
|
|
34
38
|
---
|
|
35
39
|
|
|
36
40
|
You are the Agent Fleet Orchestrator \u2014 the system administrator for this Obsidian Agent Fleet installation.
|
|
@@ -41,10 +45,12 @@ You have deep knowledge of:
|
|
|
41
45
|
- How to create, modify, and configure agents, tasks, skills, and channels
|
|
42
46
|
- The scheduling system (cron expressions, task types, heartbeat schedules)
|
|
43
47
|
- Heartbeat configuration \u2014 autonomous periodic agent runs via HEARTBEAT.md
|
|
44
|
-
- Channels \u2014 connecting agents to external chat platforms (Slack,
|
|
45
|
-
- Multi-agent routing via @agent-name prefix
|
|
48
|
+
- Channels \u2014 connecting agents to external chat platforms (Slack, Telegram)
|
|
49
|
+
- Multi-agent routing via @agent-name prefix, /agents command, and Telegram inline keyboard
|
|
50
|
+
- MCP server management \u2014 assigning servers to agents via mcp_servers field
|
|
46
51
|
- Permission modes and security rules
|
|
47
52
|
- The folder structure and file formats
|
|
53
|
+
- Cross-platform support (macOS, Windows, Linux)
|
|
48
54
|
|
|
49
55
|
When asked to create a new agent, task, skill, or channel:
|
|
50
56
|
1. Create the proper folder structure and files
|
|
@@ -59,9 +65,14 @@ When asked to set up a heartbeat:
|
|
|
59
65
|
|
|
60
66
|
When asked to set up a channel:
|
|
61
67
|
1. Create a channel file in _fleet/channels/
|
|
62
|
-
2. Explain required Slack app
|
|
68
|
+
2. Explain required external setup (Slack app with Socket Mode, or Telegram bot via BotFather)
|
|
63
69
|
3. Set up the allowed agents for multi-agent routing if needed
|
|
64
70
|
|
|
71
|
+
When asked about MCP servers:
|
|
72
|
+
1. Explain that MCP servers are managed from the dashboard (add/remove/authenticate)
|
|
73
|
+
2. Show how to assign servers to agents via the mcp_servers field in agent.md
|
|
74
|
+
3. Explain OAuth authentication flow for HTTP/SSE servers
|
|
75
|
+
|
|
65
76
|
When asked to troubleshoot:
|
|
66
77
|
1. Check the relevant files in _fleet/
|
|
67
78
|
2. Validate frontmatter and configuration
|
|
@@ -69,7 +80,7 @@ When asked to troubleshoot:
|
|
|
69
80
|
|
|
70
81
|
Always explain what you're doing and why. You are the expert \u2014 help users get the most out of their agent fleet.
|
|
71
82
|
`},{path:"agents/fleet-orchestrator/config.md",content:`---
|
|
72
|
-
model:
|
|
83
|
+
model: opus
|
|
73
84
|
adapter: claude-code
|
|
74
85
|
timeout: 300
|
|
75
86
|
max_retries: 1
|
|
@@ -191,7 +202,36 @@ channel_context: |
|
|
|
191
202
|
---
|
|
192
203
|
\`\`\`
|
|
193
204
|
|
|
194
|
-
Then explains: "You need to set up a Slack app at api.slack.com with Socket Mode enabled, and add the bot token + app token as a credential named \`slack-creds\` in Settings \u2192 Agent Fleet \u2192 Channel Credentials. Once configured, send a DM to the bot from Slack \u2014 use \`@agent-name: message\` to switch between agents."
|
|
205
|
+
Then explains: "You need to set up a Slack app at api.slack.com with Socket Mode enabled, and add the bot token + app token as a credential named \`slack-creds\` in Settings \u2192 Agent Fleet \u2192 Channel Credentials. Credentials are stored securely in your OS keychain. Once configured, send a DM to the bot from Slack \u2014 use \`@agent-name: message\` to switch between agents, or type \`/agents\` to see available agents."
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Example 3b: Set up a Telegram channel
|
|
210
|
+
|
|
211
|
+
**User:** I want to chat with my agents from Telegram.
|
|
212
|
+
|
|
213
|
+
**Agent:** Creates a channel binding file:
|
|
214
|
+
|
|
215
|
+
**_fleet/channels/my-telegram.md:**
|
|
216
|
+
\`\`\`yaml
|
|
217
|
+
---
|
|
218
|
+
name: my-telegram
|
|
219
|
+
type: telegram
|
|
220
|
+
default_agent: fleet-orchestrator
|
|
221
|
+
allowed_agents:
|
|
222
|
+
- fleet-orchestrator
|
|
223
|
+
- site-monitor
|
|
224
|
+
enabled: true
|
|
225
|
+
credential_ref: telegram-creds
|
|
226
|
+
allowed_users:
|
|
227
|
+
- "110810710"
|
|
228
|
+
per_user_sessions: true
|
|
229
|
+
channel_context: |
|
|
230
|
+
You are being contacted via Telegram. Keep replies concise.
|
|
231
|
+
---
|
|
232
|
+
\`\`\`
|
|
233
|
+
|
|
234
|
+
Then explains: "Create a bot via @BotFather on Telegram \u2014 use \`/newbot\`, pick a name and username, and copy the token. Add the token as a credential named \`telegram-creds\` in Settings \u2192 Agent Fleet \u2192 Channel Credentials (type: Telegram). Then message the bot in Telegram. Use \`/agents\` to get interactive buttons for switching agents."
|
|
195
235
|
|
|
196
236
|
---
|
|
197
237
|
|
|
@@ -259,6 +299,28 @@ allowed_agents:
|
|
|
259
299
|
\`\`\`
|
|
260
300
|
|
|
261
301
|
Explains: "Users can now type \`@code-reviewer: review this\` to switch agents mid-conversation. Each agent gets its own isolated session. Type \`/agents\` in Slack to see the full list."
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Example 8: Assign MCP servers to an agent
|
|
306
|
+
|
|
307
|
+
**User:** I want my assistant agent to use the Todoist and Linear MCP servers.
|
|
308
|
+
|
|
309
|
+
**Agent:** Updates \`_fleet/agents/assistant/agent.md\` frontmatter to include \`mcp_servers\`:
|
|
310
|
+
|
|
311
|
+
\`\`\`yaml
|
|
312
|
+
---
|
|
313
|
+
name: assistant
|
|
314
|
+
description: Personal assistant with task and project management
|
|
315
|
+
mcp_servers:
|
|
316
|
+
- todoist
|
|
317
|
+
- linear
|
|
318
|
+
skills:
|
|
319
|
+
- agent-fleet-system
|
|
320
|
+
---
|
|
321
|
+
\`\`\`
|
|
322
|
+
|
|
323
|
+
Explains: "The agent will now have access to Todoist and Linear tools during its runs. Make sure these MCP servers are added and authenticated in the dashboard (MCP Servers page). For HTTP/SSE servers that need OAuth, click 'Authenticate' on the server card \u2014 the plugin handles the browser-based auth flow and injects tokens into the CLI automatically."
|
|
262
324
|
`},{path:"skills/agent-fleet-system/references.md",content:`# References
|
|
263
325
|
|
|
264
326
|
## Permission Modes
|
|
@@ -297,15 +359,14 @@ The plugin spawns Claude Code with:
|
|
|
297
359
|
claude -p "<prompt>" --output-format stream-json --verbose [--model <model>]
|
|
298
360
|
\`\`\`
|
|
299
361
|
|
|
300
|
-
|
|
362
|
+
On macOS/Linux, commands run through a login shell (\`/bin/zsh -l -c\` or \`/bin/bash -l -c\`) so shell profile environment variables are available. On Windows, commands spawn directly \u2014 Windows inherits environment variables from the system without a shell wrapper.
|
|
301
363
|
|
|
302
364
|
## Environment Variables
|
|
303
365
|
|
|
304
|
-
API tokens and secrets should be set in
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
\`\`\`
|
|
366
|
+
API tokens and secrets should be set in your shell profile:
|
|
367
|
+
- **macOS:** \`~/.zshenv\` or \`~/.zprofile\`
|
|
368
|
+
- **Linux:** \`~/.bashrc\` or \`~/.profile\`
|
|
369
|
+
- **Windows:** System Environment Variables (Settings \u2192 System \u2192 Advanced \u2192 Environment Variables)
|
|
309
370
|
|
|
310
371
|
These are inherited by all agent processes. Never store tokens in vault files.
|
|
311
372
|
|
|
@@ -314,15 +375,17 @@ These are inherited by all agent processes. Never store tokens in vault files.
|
|
|
314
375
|
| Type | Transport | Status |
|
|
315
376
|
|---|---|---|
|
|
316
377
|
| slack | Socket Mode WebSocket + Assistants API | Supported |
|
|
317
|
-
| telegram | Long-poll via getUpdates |
|
|
378
|
+
| telegram | Long-poll via HTTPS (getUpdates) | Supported |
|
|
318
379
|
| discord | Gateway WebSocket | Coming soon |
|
|
319
380
|
|
|
320
381
|
**Slack requirements:** Slack app with Socket Mode enabled, bot token (xoxb-) + app-level token (xapp-), scopes: chat:write, im:history, im:read, im:write, app_mentions:read, assistant:write, commands.
|
|
321
382
|
|
|
383
|
+
**Telegram requirements:** Bot created via @BotFather, bot token. Optional: disable privacy mode for group access, enable threaded mode for forum topics.
|
|
384
|
+
|
|
322
385
|
**Channel constraints:**
|
|
323
386
|
- Agents with \`approval_required\` cannot be bound to channels (would deadlock)
|
|
324
|
-
- Credentials
|
|
325
|
-
- \`allowed_users\` is checked against
|
|
387
|
+
- Credentials are stored in the OS keychain via Obsidian's SecretStorage API (macOS Keychain, Windows Credential Manager, Linux Secret Service)
|
|
388
|
+
- \`allowed_users\` is checked against the platform's verified sender field
|
|
326
389
|
|
|
327
390
|
## Heartbeat vs Tasks
|
|
328
391
|
|
|
@@ -421,11 +484,12 @@ who it is, how it behaves, what it does.
|
|
|
421
484
|
\`\`\`yaml
|
|
422
485
|
---
|
|
423
486
|
model: default # "default", "claude-sonnet-4-6", "claude-opus-4-6", etc.
|
|
424
|
-
adapter: claude-code #
|
|
487
|
+
adapter: claude-code # Currently the only supported adapter
|
|
425
488
|
timeout: 300 # Seconds before kill
|
|
426
489
|
max_retries: 1
|
|
427
490
|
cwd: "" # Working directory (empty = vault root)
|
|
428
491
|
permission_mode: bypassPermissions # "bypassPermissions", "dontAsk", "acceptEdits", "plan"
|
|
492
|
+
effort: "" # Reasoning effort: "low", "medium", "high", "max", or "" for default
|
|
429
493
|
approval_required: []
|
|
430
494
|
allowed_tools: []
|
|
431
495
|
blocked_tools: []
|
|
@@ -496,33 +560,57 @@ a one-line "all clear". Use [REMEMBER] to track trends across heartbeats.
|
|
|
496
560
|
|
|
497
561
|
## Creating a Channel
|
|
498
562
|
|
|
499
|
-
Channels connect agents to external chat platforms. Create a markdown file in \`_fleet/channels/<name>.md
|
|
563
|
+
Channels connect agents to external chat platforms. Create a markdown file in \`_fleet/channels/<name>.md\`.
|
|
500
564
|
|
|
565
|
+
### Slack Channel
|
|
501
566
|
\`\`\`yaml
|
|
502
567
|
---
|
|
503
|
-
name: my-slack
|
|
504
|
-
type: slack
|
|
505
|
-
default_agent: fleet-orchestrator
|
|
506
|
-
allowed_agents:
|
|
568
|
+
name: my-slack
|
|
569
|
+
type: slack
|
|
570
|
+
default_agent: fleet-orchestrator
|
|
571
|
+
allowed_agents:
|
|
507
572
|
- fleet-orchestrator
|
|
508
573
|
- site-monitor
|
|
509
574
|
enabled: true
|
|
510
|
-
credential_ref: my-slack-creds
|
|
511
|
-
allowed_users:
|
|
575
|
+
credential_ref: my-slack-creds
|
|
576
|
+
allowed_users:
|
|
512
577
|
- U0AQW6P37N1
|
|
513
|
-
per_user_sessions: true
|
|
514
|
-
channel_context: |
|
|
578
|
+
per_user_sessions: true
|
|
579
|
+
channel_context: |
|
|
515
580
|
You are being contacted via Slack. Keep replies concise.
|
|
516
581
|
---
|
|
517
582
|
\`\`\`
|
|
518
583
|
|
|
584
|
+
Slack uses Socket Mode (outbound WebSocket) with the Assistants API for native "is thinking..." indicators and thread titles.
|
|
585
|
+
|
|
586
|
+
### Telegram Channel
|
|
587
|
+
\`\`\`yaml
|
|
588
|
+
---
|
|
589
|
+
name: my-telegram
|
|
590
|
+
type: telegram
|
|
591
|
+
default_agent: fleet-orchestrator
|
|
592
|
+
allowed_agents:
|
|
593
|
+
- fleet-orchestrator
|
|
594
|
+
- site-monitor
|
|
595
|
+
enabled: true
|
|
596
|
+
credential_ref: my-telegram-creds
|
|
597
|
+
allowed_users:
|
|
598
|
+
- "110810710"
|
|
599
|
+
per_user_sessions: true
|
|
600
|
+
channel_context: |
|
|
601
|
+
You are being contacted via Telegram. Keep replies concise.
|
|
602
|
+
---
|
|
603
|
+
\`\`\`
|
|
604
|
+
|
|
605
|
+
Telegram uses long-poll HTTPS (no WebSocket, no SDK). Features: typing indicators, inline keyboard agent picker via \`/agents\`, slash command autocomplete, group chat and forum topic support.
|
|
606
|
+
|
|
519
607
|
**Important notes:**
|
|
520
608
|
- \`credential_ref\` must match a credential name in Settings \u2192 Agent Fleet \u2192 Channel Credentials
|
|
521
|
-
- Credentials
|
|
522
|
-
- \`allowed_users
|
|
609
|
+
- Credentials are stored securely in the OS keychain via Obsidian's SecretStorage API
|
|
610
|
+
- \`allowed_users\`: Slack user IDs (start with U) or Telegram user IDs (numeric)
|
|
523
611
|
- Agents with \`approval_required\` set cannot be bound to a channel
|
|
524
|
-
- Multi-agent routing:
|
|
525
|
-
-
|
|
612
|
+
- Multi-agent routing: type \`@agent-name: message\` to switch agents, or use \`/agents\` for interactive picker
|
|
613
|
+
- Obsidian must be running for channels to work \u2014 when closed, bots go offline
|
|
526
614
|
|
|
527
615
|
## Creating a Task
|
|
528
616
|
|
|
@@ -538,6 +626,7 @@ enabled: true
|
|
|
538
626
|
created: 2026-03-29T10:00:00
|
|
539
627
|
run_count: 0
|
|
540
628
|
catch_up: false # Run missed executions on startup
|
|
629
|
+
effort: "" # Override agent effort: "low", "medium", "high", "max", or "" for agent default
|
|
541
630
|
tags:
|
|
542
631
|
- monitoring
|
|
543
632
|
---
|
|
@@ -582,6 +671,42 @@ Core skill instructions go here.
|
|
|
582
671
|
### references.md \u2014 Background Docs (optional)
|
|
583
672
|
### examples.md \u2014 Few-Shot Examples (optional)
|
|
584
673
|
|
|
674
|
+
## MCP Servers
|
|
675
|
+
|
|
676
|
+
MCP (Model Context Protocol) servers give agents access to external tools and services. Servers are managed from the dashboard \u2014 add, remove, authenticate, and inspect servers without touching the terminal.
|
|
677
|
+
|
|
678
|
+
### Assigning MCP Servers to Agents
|
|
679
|
+
|
|
680
|
+
Add the \`mcp_servers\` field to the agent's \`agent.md\` frontmatter:
|
|
681
|
+
|
|
682
|
+
\`\`\`yaml
|
|
683
|
+
---
|
|
684
|
+
name: my-agent
|
|
685
|
+
description: Agent with MCP access
|
|
686
|
+
mcp_servers:
|
|
687
|
+
- todoist
|
|
688
|
+
- linear
|
|
689
|
+
- github
|
|
690
|
+
---
|
|
691
|
+
\`\`\`
|
|
692
|
+
|
|
693
|
+
At runtime, the plugin writes a temporary \`settings.local.json\` in the agent's working directory that maps these server names to Claude Code's \`mcp__<name>\` allow entries. The file is restored after the run.
|
|
694
|
+
|
|
695
|
+
### Server Types
|
|
696
|
+
|
|
697
|
+
- **stdio** \u2014 local process spawned by Claude CLI (e.g., \`npx @some/mcp-server\`)
|
|
698
|
+
- **HTTP / SSE** \u2014 remote server accessed via URL, often with OAuth authentication
|
|
699
|
+
|
|
700
|
+
### OAuth Authentication
|
|
701
|
+
|
|
702
|
+
HTTP/SSE servers that require OAuth can be authenticated from the dashboard:
|
|
703
|
+
1. Click "Authenticate" on the server card
|
|
704
|
+
2. Plugin discovers OAuth endpoints automatically (RFC 8414 / RFC 9728)
|
|
705
|
+
3. Registers via Dynamic Client Registration
|
|
706
|
+
4. Opens browser for PKCE authorization flow
|
|
707
|
+
5. Tokens stored in OS keychain and injected into Claude CLI config
|
|
708
|
+
6. Background token refresh keeps agents authenticated
|
|
709
|
+
|
|
585
710
|
## Modifying Agents, Tasks, Skills, or Channels
|
|
586
711
|
|
|
587
712
|
To modify any entity, read the file, change the frontmatter or body, and write it back. The plugin watches the \`_fleet/\` folder and picks up changes automatically.
|
|
@@ -10587,6 +10712,167 @@ python scripts/with_server.py \\
|
|
|
10587
10712
|
- Raises \`RuntimeError\` if a server fails to start within the timeout
|
|
10588
10713
|
- Always cleans up server processes in the \`finally\` block
|
|
10589
10714
|
- Exits with the return code of the executed command
|
|
10715
|
+
`},{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\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. 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. Extract ONLY **durable claims** \u2014 decisions, entity mentions, action items, concept introductions. Ignore small talk, noise, repeated context.\n 3. For each durable claim, update the relevant topic page by **appending** a dated sub-entry. Include a forward wikilink back to the source file:\n `- YYYY-MM-DD: from [[path/to/source-file|source-file]]: <claim>`\n 4. **Do NOT** create a summary page for this file.\n 5. **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---\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:`---
|
|
10716
|
+
name: wiki-lint
|
|
10717
|
+
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."
|
|
10718
|
+
tags:
|
|
10719
|
+
- wiki-keeper
|
|
10720
|
+
- knowledge
|
|
10721
|
+
- lint
|
|
10722
|
+
---
|
|
10723
|
+
|
|
10724
|
+
# wiki-lint
|
|
10725
|
+
|
|
10726
|
+
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.
|
|
10727
|
+
|
|
10728
|
+
## Scope resolution
|
|
10729
|
+
|
|
10730
|
+
Read \`wiki_keeper:\` from the calling agent's \`config.md\`:
|
|
10731
|
+
- \`scope_root\`
|
|
10732
|
+
- \`topics_root\`, \`index_path\`, \`log_path\`
|
|
10733
|
+
- \`state_file\`
|
|
10734
|
+
- \`watched_folders\`
|
|
10735
|
+
|
|
10736
|
+
All paths resolve relative to \`scope_root\` unless otherwise noted.
|
|
10737
|
+
|
|
10738
|
+
## Checks to run
|
|
10739
|
+
|
|
10740
|
+
### 1. Orphan topic pages
|
|
10741
|
+
For each page under \`<scope_root>/<topics_root>/\`, check its inbound backlink count via Grep. Flag pages with zero inbound links.
|
|
10742
|
+
|
|
10743
|
+
### 2. Missing forward-links from summaries
|
|
10744
|
+
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.
|
|
10745
|
+
|
|
10746
|
+
### 3. Contradictions >30 days old
|
|
10747
|
+
Grep for \`## Contradictions\` sections across \`<topics_root>/\`. Within each, find dated entries older than 30 days. Flag for user review.
|
|
10748
|
+
|
|
10749
|
+
### 4. Stale events
|
|
10750
|
+
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.
|
|
10751
|
+
|
|
10752
|
+
### 5. Index drift
|
|
10753
|
+
Compare \`<index_path>\` content (within our fenced block) to the actual list of pages under \`<topics_root>/\`. Flag:
|
|
10754
|
+
- Pages in the topics root but missing from index (suggest adding)
|
|
10755
|
+
- Index entries whose target file no longer exists (suggest removing)
|
|
10756
|
+
|
|
10757
|
+
### 6. Schema violations
|
|
10758
|
+
For each page under \`<topics_root>/\`, verify it has required frontmatter per the agent's \`CONTEXT.md\`. Specifically:
|
|
10759
|
+
- \`type:\` field must be present (entity | concept | event | summary | synthesis)
|
|
10760
|
+
- \`type: event\` pages must have \`date:\`
|
|
10761
|
+
- \`type: summary\` pages must have \`source:\`
|
|
10762
|
+
|
|
10763
|
+
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".
|
|
10764
|
+
|
|
10765
|
+
### 7. Watched-source drift
|
|
10766
|
+
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.
|
|
10767
|
+
|
|
10768
|
+
## Reporting
|
|
10769
|
+
|
|
10770
|
+
Write ONE dated report to \`<log_path>\` under a \`## Lint YYYY-MM-DD\` heading (inside the fenced \`<!-- wiki-keeper:begin --> ... <!-- wiki-keeper:end -->\` block):
|
|
10771
|
+
|
|
10772
|
+
\`\`\`markdown
|
|
10773
|
+
## Lint 2026-04-26
|
|
10774
|
+
|
|
10775
|
+
### Summary
|
|
10776
|
+
- Orphans: 2
|
|
10777
|
+
- Missing forward-links: 1
|
|
10778
|
+
- Old contradictions: 0
|
|
10779
|
+
- Stale events: 3
|
|
10780
|
+
- Index drift: 1 addition / 0 removals
|
|
10781
|
+
- Schema violations: 4 (2 auto-fixed)
|
|
10782
|
+
- Watched-source drift: 1
|
|
10783
|
+
|
|
10784
|
+
### Auto-applied
|
|
10785
|
+
- Added \`type: summary\` to [[_topics/summaries/2026-04-10-vendor-brief]]
|
|
10786
|
+
- Added \`type: summary\` to [[_topics/summaries/2026-04-12-planning-meeting]]
|
|
10787
|
+
|
|
10788
|
+
### Needs review
|
|
10789
|
+
- Orphan: [[_topics/old-proposal]] has no inbound links. Consider linking from [[index]] or archiving.
|
|
10790
|
+
- Stale event: [[_topics/events/q3-kickoff]] (date: 2025-09-10) with no recent references. Archive?
|
|
10791
|
+
- Contradiction in [[_topics/vendor-x]] from 2026-03-12 still unresolved (36 days old).
|
|
10792
|
+
\`\`\`
|
|
10793
|
+
|
|
10794
|
+
## Rules
|
|
10795
|
+
|
|
10796
|
+
- **Only auto-apply deterministic fixes.** Adding a missing \`type:\` inferable from path = deterministic. Merging two topic pages = judgment call \u2192 "Needs review" only.
|
|
10797
|
+
- **Never delete user content.** Flagging an orphan is fine; deleting the orphan is not.
|
|
10798
|
+
- **Never auto-rename pages.** That breaks every inbound link.
|
|
10799
|
+
- **Never modify watched source files.**
|
|
10800
|
+
- **One lint report per run.** If called twice in the same day, the second run replaces the day's report.
|
|
10801
|
+
|
|
10802
|
+
## Optional broadcast
|
|
10803
|
+
|
|
10804
|
+
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.
|
|
10805
|
+
|
|
10806
|
+
## What NOT to do
|
|
10807
|
+
|
|
10808
|
+
- Never restructure the topic taxonomy as a lint action.
|
|
10809
|
+
- Never silently fix something that required judgment \u2014 log it.
|
|
10810
|
+
- Never suggest fixes outside \`scope_root\`.
|
|
10811
|
+
`},{path:"skills/wiki-query/skill.md",content:`---
|
|
10812
|
+
name: wiki-query
|
|
10813
|
+
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."
|
|
10814
|
+
tags:
|
|
10815
|
+
- wiki-keeper
|
|
10816
|
+
- knowledge
|
|
10817
|
+
- query
|
|
10818
|
+
---
|
|
10819
|
+
|
|
10820
|
+
# wiki-query
|
|
10821
|
+
|
|
10822
|
+
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.
|
|
10823
|
+
|
|
10824
|
+
You operate in one of two modes, chosen by what's in your agent's \`config.md\`.
|
|
10825
|
+
|
|
10826
|
+
## Mode A \u2014 Keeper (your own scope)
|
|
10827
|
+
|
|
10828
|
+
Triggered when the agent has a \`wiki_keeper:\` block.
|
|
10829
|
+
|
|
10830
|
+
- Resolve paths relative to \`wiki_keeper.scope_root\`.
|
|
10831
|
+
- Search \`<scope_root>/<topics_root>/\` only. Never cross into another scope.
|
|
10832
|
+
- 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."*
|
|
10833
|
+
- Respect \`file_substantive_answers\` and \`obsidian_url_scheme\`.
|
|
10834
|
+
|
|
10835
|
+
## Mode B \u2014 Consumer (reference one or more scopes)
|
|
10836
|
+
|
|
10837
|
+
Triggered when the agent has a \`wiki_references:\` block (typically without a \`wiki_keeper:\` block \u2014 consumer agents like PM agents, research agents, etc.).
|
|
10838
|
+
|
|
10839
|
+
- 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.
|
|
10840
|
+
- 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"*.
|
|
10841
|
+
- 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.
|
|
10842
|
+
- Never answer from training-data knowledge. If no referenced wiki covers the question, say so.
|
|
10843
|
+
|
|
10844
|
+
## Shared procedure (both modes)
|
|
10845
|
+
|
|
10846
|
+
1. Parse the question. Identify key entities, concepts, timeframes.
|
|
10847
|
+
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.
|
|
10848
|
+
3. **Read** the top N candidate pages (default 5, cap 10 for broad questions).
|
|
10849
|
+
4. **Follow one hop** \u2014 for each candidate, check \`[[wikilinks]]\` and optionally read one linked page per candidate if it's clearly relevant.
|
|
10850
|
+
5. **Synthesize** an answer. Every factual claim must be followed by a citation \`[[path|Display]]\`. Multiple citations per claim are welcome.
|
|
10851
|
+
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.
|
|
10852
|
+
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.
|
|
10853
|
+
|
|
10854
|
+
## Rules
|
|
10855
|
+
|
|
10856
|
+
- **Wiki-grounded only.** If a claim isn't in any accessible wiki page, you don't know it.
|
|
10857
|
+
- **Cite everything.** Un-cited claims are a bug.
|
|
10858
|
+
- **Name the scope** in consumer mode when citations come from more than one wiki.
|
|
10859
|
+
- **Provenance helps.** When a page is clearly stale (last edit >12 months and no fresh references), flag that in your answer.
|
|
10860
|
+
- **Be concise.** Long prose with citations beats exhaustive prose. Group related claims under one citation rather than repeating it.
|
|
10861
|
+
|
|
10862
|
+
## Output shape
|
|
10863
|
+
|
|
10864
|
+
1. **TL;DR** \u2014 one or two sentences that actually answer, with citations.
|
|
10865
|
+
2. **Details** \u2014 bullets of specific claims, each cited.
|
|
10866
|
+
3. **What the wiki is missing** (if relevant) \u2014 explicit note about gaps.
|
|
10867
|
+
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.
|
|
10868
|
+
|
|
10869
|
+
## What NOT to do
|
|
10870
|
+
|
|
10871
|
+
- Never answer from general knowledge.
|
|
10872
|
+
- Never cite external URLs (only vault pages).
|
|
10873
|
+
- Never rewrite or update topic pages (\`<topics-path>/\`). That's the wiki keeper's job.
|
|
10874
|
+
- Never write to \`<inbox>/\` in keeper mode unless \`file_substantive_answers: true\` (and even then, only to \`<topics-path>/syntheses/\`, not the inbox).
|
|
10875
|
+
- 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.
|
|
10590
10876
|
`},{path:"skills/xlsx/skill.md",content:`---
|
|
10591
10877
|
name: xlsx
|
|
10592
10878
|
description: "Create, read, edit, and clean spreadsheet files (.xlsx, .csv, .tsv) \u2014 formulas, charts, formatting, data cleanup."
|
|
@@ -11052,20 +11338,19 @@ python scripts/office/validate.py <path> [--original <original_file>] [--auto-re
|
|
|
11052
11338
|
|
|
11053
11339
|
- \`paraId\`/\`durableId\` values that exceed OOXML limits
|
|
11054
11340
|
- Missing \`xml:space="preserve"\` on \`w:t\` elements with whitespace
|
|
11055
|
-
`}];var
|
|
11341
|
+
`}];var ys=require("obsidian");function J(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,ys.parseYaml)(e)??{}}catch(n){console.warn("Agent Fleet: malformed YAML frontmatter, treating as empty",n),a={}}return{frontmatter:a,body:s.trim()}}function Y(r,t){let e=(0,ys.stringifyYaml)(r).trim(),s=t.trim();return`---
|
|
11056
11342
|
${e}
|
|
11057
11343
|
---
|
|
11058
11344
|
|
|
11059
11345
|
${s}
|
|
11060
|
-
`}function ve(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function kt(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function is(r){return typeof r=="object"&&r!==null}function L(r){return typeof r=="string"?r:void 0}function qe(r,t){return typeof r=="boolean"?r:t}function Qe(r,t){return typeof r=="number"&&Number.isFinite(r)?r:t}function pe(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}function ka(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 Mt=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;setChannelCredentialGetter(t){this.channelCredentialGetter=t}getVaultBasePath(){let t=this.vault.adapter;return t instanceof S.FileSystemAdapter?t.getBasePath():void 0}getFleetRoot(){return(0,S.normalizePath)(this.settings.fleetFolder)}getSubfolder(t){return(0,S.normalizePath)(`${this.getFleetRoot()}/${t}`)}async ensureFleetStructure(){let t=this.getFleetRoot(),e=!this.vault.getAbstractFileByPath(t);await this.ensureFolder(t);for(let s of wa)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of $s){let s=(0,S.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 $s){let n=(0,S.normalizePath)(`${e}/${a.path}`),i=ka(a.content),o=t[a.path];if(o===i)continue;let l=this.vault.getAbstractFileByPath(n);if(!(l instanceof S.TFile)){let u=n.substring(0,n.lastIndexOf("/"));await this.ensureFolder(u),await this.createFileIfMissing(n,a.content),s[a.path]=i;continue}let c=await this.vault.cachedRead(l),d=ka(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 S.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,S.normalizePath)(`${e}${a}`),i=(0,S.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof S.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof S.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,S.normalizePath)(`${e}${a}`),i=(0,S.normalizePath)(`${n}/skill.md`);if(this.skills.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof S.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof S.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 S.TFolder)for(let e of t.children){if(!(e instanceof S.TFolder))continue;let s=(0,S.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof S.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}=se(s),i=L(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,S.normalizePath)(`${t}/${l}`),d=this.vault.getAbstractFileByPath(c);if(!(d instanceof S.TFile))return"";let u=await this.vault.cachedRead(d);return se(u).body};return{filePath:e.path,name:i,description:L(a.description),tags:pe(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 S.TFolder)for(let e of t.children){if(!(e instanceof S.TFolder))continue;let s=(0,S.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof S.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}=se(s),i=L(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},l=(0,S.normalizePath)(`${t}/config.md`),c=this.vault.getAbstractFileByPath(l);if(c instanceof S.TFile){let U=await this.vault.cachedRead(c);o=se(U).frontmatter}let d={allow:[],deny:[]},u=(0,S.normalizePath)(`${t}/permissions.json`),h=this.vault.getAbstractFileByPath(u);if(h instanceof S.TFile)try{let U=await this.vault.cachedRead(h),D=JSON.parse(U);d={allow:pe(D.allow),deny:pe(D.deny)}}catch{}let m="",f=(0,S.normalizePath)(`${t}/SKILLS.md`),p=this.vault.getAbstractFileByPath(f);if(p instanceof S.TFile){let U=await this.vault.cachedRead(p);m=se(U).body}let b="",k=(0,S.normalizePath)(`${t}/CONTEXT.md`),v=this.vault.getAbstractFileByPath(k);if(v instanceof S.TFile){let U=await this.vault.cachedRead(v);b=se(U).body}let g=!1,y="",x="",T=!0,C="",A=(0,S.normalizePath)(`${t}/HEARTBEAT.md`),E=this.vault.getAbstractFileByPath(A);if(E instanceof S.TFile){let U=await this.vault.cachedRead(E),D=se(U);g=qe(D.frontmatter.enabled,!1),y=L(D.frontmatter.schedule)??"",T=qe(D.frontmatter.notify,!0),C=L(D.frontmatter.channel)??"",x=D.body}let R=L(a.model)??L(o.model)??this.settings.defaultModel;return{filePath:e.path,name:i,description:L(a.description),model:R,adapter:L(o.adapter)??"claude-code",permissionMode:L(o.permission_mode)??"bypassPermissions",maxRetries:Qe(o.max_retries,1),skills:pe(a.skills),mcpServers:pe(a.mcp_servers),allowedTools:pe(o.allowed_tools),blockedTools:pe(o.blocked_tools),cwd:L(o.cwd)||L(a.cwd),enabled:qe(a.enabled,!0),timeout:Qe(o.timeout,Qe(a.timeout,300)),approvalRequired:pe(o.approval_required),memory:qe(o.memory,qe(a.memory,!1)),memoryMaxEntries:Qe(o.memory_max_entries,100),tags:pe(a.tags),avatar:L(a.avatar)??"",body:n,contextBody:b,skillsBody:m,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:g,heartbeatSchedule:y,heartbeatBody:x,heartbeatNotify:T,heartbeatChannel:C}}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,S.normalizePath)(`${this.getSubfolder("memory")}/${ve(t)}.md`)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof S.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=se(a);return{filePath:e,agent:L(n.agent)??t,lastUpdated:L(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(`
|
|
11061
|
-
`);if(a instanceof
|
|
11346
|
+
`}function ge(r){return r.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")}function Et(r,t){return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}var st=require("child_process"),Ya=require("fs"),Wt=require("os"),gt=require("path");function ia(){return(0,Wt.homedir)()}function zt(){return(0,gt.join)((0,Wt.homedir)(),".claude")}function Gt(){return(0,gt.join)((0,Wt.homedir)(),".claude.json")}function Ka(){if(process.platform==="darwin")return"/bin/zsh";for(let r of["/bin/bash","/bin/zsh","/bin/sh"])if((0,Ya.existsSync)(r))return r;return"/bin/sh"}function Yi(r){return`'${r.replace(/'/g,"'\\''")}'`}function at(r,t,e){let s={cwd:e?.cwd,env:e?.env};if(process.platform==="win32")return(0,st.spawn)(r,t,s);let a=Ka(),n=[r,...t].map(Yi).join(" ");return(0,st.spawn)(a,["-l","-c",n],s)}function ra(r,t){let e={cwd:t?.cwd,env:t?.env,stdio:["pipe","pipe","pipe"]};if(process.platform==="win32")return(0,st.spawn)(r,[],{...e,shell:!0});let s=Ka();return(0,st.spawn)(s,["-l","-c",r],e)}function Xa(r){try{require("electron").shell.openExternal(r)}catch{switch(process.platform){case"darwin":(0,st.spawn)("open",[r],{stdio:"ignore"});break;case"win32":(0,st.spawn)("cmd.exe",["/c","start","",r.replace(/&/g,"^&")],{stdio:"ignore"});break;default:(0,st.spawn)("xdg-open",[r],{stdio:"ignore"});break}}}function ce(r){return r.split(/\r?\n/)}function Ja(r){let t=(0,Wt.homedir)();if(process.platform==="win32")return[r,(0,gt.join)(process.env.APPDATA??"","Claude","claude.exe"),(0,gt.join)(process.env.LOCALAPPDATA??"","Claude","claude.exe"),(0,gt.join)(t,".local","bin","claude.exe"),"claude.exe","claude"].filter(s=>!!s&&Va(s));let e=[r,(0,gt.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&&Va(s))}function Va(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 oa(r){return!!(r.includes("/")||r.includes("\\"))}function vs(r){return typeof r=="object"&&r!==null}function A(r){return typeof r=="string"?r:void 0}function Be(r,t){return typeof r=="boolean"?r:t}function Ze(r,t){return typeof r=="number"&&Number.isFinite(r)?r:t}function ye(r){return Array.isArray(r)?r.filter(t=>typeof t=="string"):[]}function Ki(r){if(!r)return;let e={sunday:0,monday:1,tuesday:2,wednesday:3,thursday:4,friday:5,saturday:6}[r.toLowerCase()];return e===void 0?void 0:`0 9 * * ${e}`}function Qa(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 Vt=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 C.FileSystemAdapter?t.getBasePath():void 0}getFleetRoot(){return(0,C.normalizePath)(this.settings.fleetFolder)}getSubfolder(t){return(0,C.normalizePath)(`${this.getFleetRoot()}/${t}`)}async ensureFleetStructure(){let t=this.getFleetRoot(),e=!this.vault.getAbstractFileByPath(t);await this.ensureFolder(t);for(let s of Ga)await this.ensureFolder(this.getSubfolder(s));return e}async ensureSamples(){let t=this.getFleetRoot();for(let e of na){let s=(0,C.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 na){let n=(0,C.normalizePath)(`${e}/${a.path}`),i=Qa(a.content),o=t[a.path];if(o===i)continue;let l=this.vault.getAbstractFileByPath(n);if(!(l instanceof C.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=Qa(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 C.TFile)||e.extension!=="md")return;if(this.isInsideAgentFolder(e.path)){await this.reloadFolderAgentContaining(e.path);return}if(this.isInsideSkillFolder(e.path)){await this.reloadFolderSkillContaining(e.path);return}this.clearStoredFile(e.path);let s=`${this.getSubfolder("channels")}/`;if(e.path.startsWith(s)){if(!e.path.slice(s.length).includes("/")){let o=await this.vault.cachedRead(e),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,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/agent.md`);if(this.agents.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof C.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,C.normalizePath)(`${e}${a}`),i=(0,C.normalizePath)(`${n}/skill.md`);if(this.skills.delete(i),!(this.vault.getAbstractFileByPath(n)instanceof C.TFolder))return;let l=this.vault.getAbstractFileByPath(i);if(!(l instanceof C.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 C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/skill.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderSkill(e.path,a);n&&this.skills.set(s,n)}}async loadFolderSkill(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=J(s),i=A(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,C.normalizePath)(`${t}/${l}`),d=this.vault.getAbstractFileByPath(c);if(!(d instanceof C.TFile))return"";let h=await this.vault.cachedRead(d);return J(h).body};return{filePath:e.path,name:i,description:A(a.description),tags:ye(a.tags),body:n,toolsBody:await o("tools.md"),referencesBody:await o("references.md"),examplesBody:await o("examples.md"),isFolder:!0}}async loadFolderAgents(){let t=this.vault.getAbstractFileByPath(this.getSubfolder("agents"));if(t instanceof C.TFolder)for(let e of t.children){if(!(e instanceof C.TFolder))continue;let s=(0,C.normalizePath)(`${e.path}/agent.md`),a=this.vault.getAbstractFileByPath(s);if(!(a instanceof C.TFile))continue;let n=await this.loadFolderAgent(e.path,a);n&&this.agents.set(s,n)}}async loadFolderAgent(t,e){let s=await this.vault.cachedRead(e),{frontmatter:a,body:n}=J(s),i=A(a.name);if(!i)return this.setIssue(e.path,"Folder agent agent.md requires string field `name`."),null;let o={},l=(0,C.normalizePath)(`${t}/config.md`),c=this.vault.getAbstractFileByPath(l);if(c instanceof C.TFile){let q=await this.vault.cachedRead(c);o=J(q).frontmatter}let d={allow:[],deny:[]},h=(0,C.normalizePath)(`${t}/permissions.json`),u=this.vault.getAbstractFileByPath(h);if(u instanceof C.TFile)try{let q=await this.vault.cachedRead(u),L=JSON.parse(q);d={allow:ye(L.allow),deny:ye(L.deny)}}catch{}let f="",p=(0,C.normalizePath)(`${t}/SKILLS.md`),m=this.vault.getAbstractFileByPath(p);if(m instanceof C.TFile){let q=await this.vault.cachedRead(m);f=J(q).body}let v="",k=(0,C.normalizePath)(`${t}/CONTEXT.md`),b=this.vault.getAbstractFileByPath(k);if(b instanceof C.TFile){let q=await this.vault.cachedRead(b);v=J(q).body}let g=!1,y="",x="",S=!0,T="",P=(0,C.normalizePath)(`${t}/HEARTBEAT.md`),E=this.vault.getAbstractFileByPath(P);if(E instanceof C.TFile){let q=await this.vault.cachedRead(E),L=J(q);g=Be(L.frontmatter.enabled,!1),y=A(L.frontmatter.schedule)??"",S=Be(L.frontmatter.notify,!0),T=A(L.frontmatter.channel)??"",x=L.body}let R=A(a.model),W=A(o.model);R&&W&&R!==W&&(this.warnedFolderAgentModelConflict.has(i)||(this.warnedFolderAgentModelConflict.add(i),console.warn(`Agent Fleet: "${i}" has conflicting model fields \u2014 agent.md says "${R}", config.md says "${W}". config.md wins. Remove agent.md's model field or sync the values to silence this warning.`)));let I=W??R??this.settings.defaultModel;return{filePath:e.path,name:i,description:A(a.description),model:I,adapter:A(o.adapter)??"claude-code",permissionMode:A(o.permission_mode)??"bypassPermissions",effort:A(o.effort),maxRetries:Ze(o.max_retries,1),skills:ye(a.skills),mcpServers:ye(a.mcp_servers),allowedTools:ye(o.allowed_tools),blockedTools:ye(o.blocked_tools),cwd:A(o.cwd)||A(a.cwd),enabled:Be(a.enabled,!0),timeout:Ze(o.timeout,Ze(a.timeout,300)),approvalRequired:ye(o.approval_required),memory:Be(o.memory,Be(a.memory,!1)),memoryMaxEntries:Ze(o.memory_max_entries,100),tags:ye(a.tags),avatar:A(a.avatar)??"",body:n,contextBody:v,skillsBody:f,env:this.parseEnvMap(o.env),permissionRules:d,isFolder:!0,heartbeatEnabled:g,heartbeatSchedule:y,heartbeatBody:x,heartbeatNotify:S,heartbeatChannel:T,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:A(e.scope_root)??"",inboxPath:A(e.inbox_path)??"_sources/inbox",archivePath:A(e.archive_path)??"_sources/archive",topicsRoot:A(e.topics_root)??"_topics",indexPath:A(e.index_path)??"index.md",logPath:A(e.log_path)??"log.md",watchedFolders:ye(e.watched_folders),excludePatterns:ye(e.exclude_patterns),ingestSchedule:A(e.ingest_schedule)??"0 3 * * *",lintSchedule:A(e.lint_schedule)??Ki(A(e.lint_day))??"0 9 * * 0",fileSubstantiveAnswers:Be(e.file_substantive_answers,!1),obsidianUrlScheme:Be(e.obsidian_url_scheme,!0),maxTokensPerIngest:Ze(e.max_tokens_per_ingest,6e4),stateFile:A(e.state_file)??".wiki-keeper-state.json"}}removeFile(t){this.clearStoredFile(t)}getSnapshot(){return{agents:Array.from(this.agents.values()).sort((t,e)=>t.name.localeCompare(e.name)),skills:Array.from(this.skills.values()).sort((t,e)=>t.name.localeCompare(e.name)),tasks:Array.from(this.tasks.values()).sort((t,e)=>t.taskId.localeCompare(e.taskId)),channels:Array.from(this.channels.values()).sort((t,e)=>t.name.localeCompare(e.name)),validationIssues:Array.from(this.validationIssues.values()).flat()}}getAgentByName(t){return Array.from(this.agents.values()).find(e=>e.name===t)}getSkillByName(t){return Array.from(this.skills.values()).find(e=>e.name===t)}getTaskById(t){return Array.from(this.tasks.values()).find(e=>e.taskId===t)}getTasksForAgent(t){return Array.from(this.tasks.values()).filter(e=>e.agent===t)}getChannelByName(t){return Array.from(this.channels.values()).find(e=>e.name===t)}getChannelsForAgent(t){return Array.from(this.channels.values()).filter(e=>e.defaultAgent===t)}getRunsRoot(){return this.getSubfolder("runs")}getMemoryPath(t){return(0,C.normalizePath)(`${this.getSubfolder("memory")}/${ge(t)}.md`)}async getMemory(t){let e=this.getMemoryPath(t),s=this.vault.getAbstractFileByPath(e);if(!(s instanceof C.TFile))return null;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=J(a);return{filePath:e,agent:A(n.agent)??t,lastUpdated:A(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(`
|
|
11347
|
+
`);if(a instanceof C.TFile){let l=`${(await this.getMemory(t))?.body.trim()||"## Learned Context"}
|
|
11062
11348
|
|
|
11063
|
-
${i}`.trim();await this.vault.modify(a,
|
|
11349
|
+
${i}`.trim();await this.vault.modify(a,Y({agent:t,last_updated:n},l));return}await this.createFileIfMissing(s,Y({agent:t,last_updated:n},`## Learned Context
|
|
11064
11350
|
|
|
11065
|
-
${i}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof
|
|
11066
|
-
`).
|
|
11067
|
-
|
|
11068
|
-
name: ${ve(t)}
|
|
11351
|
+
${i}`))}async listRecentRuns(t=50){let e=this.vault.getAbstractFileByPath(this.getRunsRoot());if(!(e instanceof C.TFolder))return[];let s=[];this.collectMarkdownChildren(e,s),s.sort((i,o)=>o.path.localeCompare(i.path));let a=s.slice(0,t),n=[];for(let i of a){let o=await this.readRunLog(i);o&&n.push(o)}return n.sort((i,o)=>o.started.localeCompare(i.started))}async readRunLog(t){let e=await this.vault.cachedRead(t),{frontmatter:s,body:a}=J(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:A(s.run_id)??t.basename,agent:A(s.agent)??"unknown",task:A(s.task)??"unknown",status:A(s.status)??"failure",started:A(s.started)??new Date(t.stat.ctime).toISOString(),completed:A(s.completed),durationSeconds:Ze(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:A(s.model)??Qe.defaultModel,modelSource:(()=>{let c=A(s.model_source);if(c==="task"||c==="agent"||c==="settings"||c==="cli-default")return c})(),concreteModel:A(s.resolved_concrete_model),exitCode:typeof s.exit_code=="number"?s.exit_code:null,tags:ye(s.tags),prompt:n?.[1]?.trim()??"",output:o?.[1]?.trim()??"",finalResult:i?.[1]?.trim()||void 0,toolsUsed:l?.[1]?ce(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,C.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,C.normalizePath)(`${s}/${a}`),i=Y({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(`
|
|
11352
|
+
`)),o=this.vault.getAbstractFileByPath(n);return o instanceof C.TFile?await this.vault.modify(o,i):await this.vault.create(n,i),n}async updateTaskRunMetadata(t,e){let s=this.vault.getAbstractFileByPath(t.filePath);if(!(s instanceof C.TFile))return;let a=await this.vault.cachedRead(s),{frontmatter:n,body:i}=J(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,Y(o,i)),await this.loadFile(s)}async setApprovalDecision(t,e,s){let a=this.vault.getAbstractFileByPath(t);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=J(n),l=(this.parseApprovals(i.approvals)??[]).map(c=>c.tool===e?{...c,status:s,resolvedAt:new Date().toISOString()}:c);await this.vault.modify(a,Y({...i,approvals:l},o))}async createAgentTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("agents"),ge(t)),s=`---
|
|
11353
|
+
name: ${ge(t)}
|
|
11069
11354
|
description:
|
|
11070
11355
|
enabled: true
|
|
11071
11356
|
skills: []
|
|
@@ -11073,89 +11358,156 @@ tags: []
|
|
|
11073
11358
|
---
|
|
11074
11359
|
|
|
11075
11360
|
Agent instructions go here.
|
|
11076
|
-
`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=
|
|
11077
|
-
`)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),
|
|
11078
|
-
name: ${
|
|
11361
|
+
`;return await this.vault.create(e,s)}async createAgentFolder(t){let e=ge(t.name),s=(0,C.normalizePath)(`${this.getSubfolder("agents")}/${e}`);await this.ensureFolder(s);let a={name:t.name,description:t.description||void 0,avatar:t.avatar||void 0,enabled:t.enabled??!0,tags:t.tags,skills:t.skills,mcp_servers:t.mcpServers?.length?t.mcpServers:void 0};t.model&&t.model!=="default"&&(a.model=t.model);let n=(0,C.normalizePath)(`${s}/agent.md`);await this.vault.create(n,Y(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},o=(0,C.normalizePath)(`${s}/config.md`);await this.vault.create(o,Y(i,""));let l=(0,C.normalizePath)(`${s}/SKILLS.md`);await this.vault.create(l,Y({},t.skillsBody||""));let c=(0,C.normalizePath)(`${s}/CONTEXT.md`);await this.vault.create(c,Y({},t.contextBody||""));let d=t.permissionRules;if(d&&(d.allow.length>0||d.deny.length>0)){let h=(0,C.normalizePath)(`${s}/permissions.json`);await this.vault.create(h,JSON.stringify(d,null,2)+`
|
|
11362
|
+
`)}return n}async createSkillTemplate(t){let e=await this.getAvailablePath(this.getSubfolder("skills"),ge(t)),s=`---
|
|
11363
|
+
name: ${ge(t)}
|
|
11079
11364
|
description:
|
|
11080
11365
|
tags: []
|
|
11081
11366
|
---
|
|
11082
11367
|
|
|
11083
11368
|
Skill instructions go here.
|
|
11084
|
-
`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,
|
|
11369
|
+
`;return await this.vault.create(e,s)}async createSkillFolder(t){let e=(0,C.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,C.normalizePath)(`${e}/skill.md`);if(await this.createFileIfMissing(a,Y(s,t.body||"Skill instructions go here.")),t.toolsBody){let n=(0,C.normalizePath)(`${e}/tools.md`);await this.createFileIfMissing(n,`# Tools
|
|
11085
11370
|
|
|
11086
|
-
${t.toolsBody}`)}if(t.referencesBody){let n=(0,
|
|
11371
|
+
${t.toolsBody}`)}if(t.referencesBody){let n=(0,C.normalizePath)(`${e}/references.md`);await this.createFileIfMissing(n,`# References
|
|
11087
11372
|
|
|
11088
|
-
${t.referencesBody}`)}if(t.examplesBody){let n=(0,
|
|
11373
|
+
${t.referencesBody}`)}if(t.examplesBody){let n=(0,C.normalizePath)(`${e}/examples.md`);await this.createFileIfMissing(n,`# Examples
|
|
11089
11374
|
|
|
11090
|
-
${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let a=(0,
|
|
11091
|
-
`;c instanceof
|
|
11375
|
+
${t.examplesBody}`)}}async updateAgent(t,e){let s=this.getAgentByName(t);if(s)if(s.isFolder){let a=(0,C.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof C.TFile){let l=await this.vault.cachedRead(n),{frontmatter:c,body:d}=J(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,Y(c,h))}let i=(0,C.normalizePath)(`${a}/config.md`),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFile){let l=await this.vault.cachedRead(o),{frontmatter:c,body:d}=J(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),await this.vault.modify(o,Y(c,d))}if(e.skillsBody!==void 0){let l=(0,C.normalizePath)(`${a}/SKILLS.md`),c=this.vault.getAbstractFileByPath(l);c instanceof C.TFile?await this.vault.modify(c,Y({},e.skillsBody)):await this.vault.create(l,Y({},e.skillsBody))}if(e.contextBody!==void 0){let l=(0,C.normalizePath)(`${a}/CONTEXT.md`),c=this.vault.getAbstractFileByPath(l);c instanceof C.TFile?await this.vault.modify(c,Y({},e.contextBody)):await this.vault.create(l,Y({},e.contextBody))}if(e.permissionRules!==void 0){let l=(0,C.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)+`
|
|
11376
|
+
`;c instanceof C.TFile?await this.vault.modify(c,h):await this.vault.create(l,h)}else c instanceof C.TFile&&await this.vault.trash(c,!0)}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=J(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);let l=e.systemPrompt!==void 0?e.systemPrompt:o;await this.vault.modify(a,Y(i,l))}}async updateTask(t,e){let s=this.getTaskById(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=J(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,Y(i,l))}async updateSkill(t,e){let s=this.getSkillByName(t);if(s)if(s.isFolder){let a=(0,C.normalizePath)(s.filePath.replace(/\/skill\.md$/,"")),n=this.vault.getAbstractFileByPath(s.filePath);if(n instanceof C.TFile){let i=await this.vault.cachedRead(n),{frontmatter:o,body:l}=J(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,Y(o,c))}if(e.toolsBody!==void 0){let i=(0,C.normalizePath)(`${a}/tools.md`),o=this.vault.getAbstractFileByPath(i);e.toolsBody&&(o instanceof C.TFile?await this.vault.modify(o,`# Tools
|
|
11092
11377
|
|
|
11093
11378
|
${e.toolsBody}`):await this.vault.create(i,`# Tools
|
|
11094
11379
|
|
|
11095
|
-
${e.toolsBody}`))}if(e.referencesBody!==void 0){let i=(0,
|
|
11380
|
+
${e.toolsBody}`))}if(e.referencesBody!==void 0){let i=(0,C.normalizePath)(`${a}/references.md`),o=this.vault.getAbstractFileByPath(i);e.referencesBody&&(o instanceof C.TFile?await this.vault.modify(o,`# References
|
|
11096
11381
|
|
|
11097
11382
|
${e.referencesBody}`):await this.vault.create(i,`# References
|
|
11098
11383
|
|
|
11099
|
-
${e.referencesBody}`))}if(e.examplesBody!==void 0){let i=(0,
|
|
11384
|
+
${e.referencesBody}`))}if(e.examplesBody!==void 0){let i=(0,C.normalizePath)(`${a}/examples.md`),o=this.vault.getAbstractFileByPath(i);e.examplesBody&&(o instanceof C.TFile?await this.vault.modify(o,`# Examples
|
|
11100
11385
|
|
|
11101
11386
|
${e.examplesBody}`):await this.vault.create(i,`# Examples
|
|
11102
11387
|
|
|
11103
|
-
${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof S.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=se(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,ee(i,l))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,S.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof S.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 S.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=se(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,ee(i,l))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,S.normalizePath)(`${this.getSubfolder("channels")}/${ve(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof S.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,S.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,S.normalizePath)(`${a}/HEARTBEAT.md`),i=this.vault.getAbstractFileByPath(n);if(i instanceof S.TFile){let o=await this.vault.cachedRead(i),{frontmatter:l,body:c}=se(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,ee(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,ee(o,l))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let i=(0,S.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(i);if(o instanceof S.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,S.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 S.TFile&&s.extension==="md"&&e.push(s),s instanceof S.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}=se(e);if(!is(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=L(s.name),i=L(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:L(s.description),model:i,adapter:L(s.adapter)??"claude-code",permissionMode:L(s.permission_mode)??"bypassPermissions",maxRetries:Qe(s.max_retries,1),skills:pe(s.skills),mcpServers:pe(s.mcp_servers),allowedTools:pe(s.allowed_tools),blockedTools:pe(s.blocked_tools),cwd:L(s.cwd),enabled:qe(s.enabled,!0),timeout:Qe(s.timeout,300),approvalRequired:pe(s.approval_required),memory:qe(s.memory,!1),memoryMaxEntries:Qe(s.memory_max_entries,100),tags:pe(s.tags),avatar:L(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}=se(e),n=L(s.name);return n?{filePath:t,name:n,description:L(s.description),tags:pe(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}=se(e),n=L(s.task_id),i=L(s.agent),o=L(s.type);if(!n||!i||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!L(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!L(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let l=L(s.priority),d=l&&["low","medium","high","critical"].includes(l)?l:"medium";return{filePath:t,taskId:n,agent:i,schedule:L(s.schedule),runAt:L(s.run_at),type:o,priority:d,enabled:qe(s.enabled,!0),created:L(s.created)??new Date().toISOString(),lastRun:L(s.last_run),nextRun:L(s.next_run),runCount:Qe(s.run_count,0),catchUp:qe(s.catch_up,this.settings.catchUpMissedTasks),tags:pe(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=se(e),n=L(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let i=L(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=L(s.default_agent)??L(s.agent);if(!c)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=pe(s.allowed_agents),u=L(s.credential_ref);if(!u)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let h=is(s.transport)?s.transport:{};return{filePath:t,name:n,type:l,defaultAgent:c,allowedAgents:d,enabled:qe(s.enabled,!0),credentialRef:u,allowedUsers:pe(s.allowed_users),perUserSessions:qe(s.per_user_sessions,!0),channelContext:L(s.channel_context)??"",transport:h,tags:pe(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(!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)||!L(e.tool))return[];let s=L(e.tool);return s?[{tool:s,command:L(e.command),reason:L(e.reason),status:L(e.status)??"pending",resolvedAt:L(e.resolvedAt),note:L(e.note)}]:[]})}};var ht=require("obsidian"),rs=class extends ht.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,ht.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 ht.Setting(e).setName("Also delete associated tasks").setDesc(`Delete ${this.info.taskCount} task${this.info.taskCount!==1?"s":""} that reference this agent`).addToggle(u=>{u.setValue(this.deleteTasks),u.onChange(h=>{this.deleteTasks=h})});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,ht.setIcon)(d,"trash-2"),c.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var te=require("obsidian");var os=class extends te.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 te.Setting(e).setName("Fleet folder").addText(s=>s.setValue(this.plugin.settings.fleetFolder).onChange(async a=>{this.plugin.settings.fleetFolder=a.trim()||Ye.fleetFolder,await this.plugin.saveSettings()})),new te.Setting(e).setName("Claude CLI path").addText(s=>s.setValue(this.plugin.settings.claudeCliPath).onChange(async a=>{this.plugin.settings.claudeCliPath=a.trim()||Ye.claudeCliPath,await this.plugin.saveSettings()})),new te.Setting(e).setName("Default model").addText(s=>s.setValue(this.plugin.settings.defaultModel).onChange(async a=>{this.plugin.settings.defaultModel=a.trim()||Ye.defaultModel,await this.plugin.saveSettings()})),new te.Setting(e).setName("AWS region").addText(s=>s.setValue(this.plugin.settings.awsRegion).onChange(async a=>{this.plugin.settings.awsRegion=a.trim()||Ye.awsRegion,await this.plugin.saveSettings()})),new te.Setting(e).setName("Max concurrent runs").addSlider(s=>s.setLimits(1,10,1).setValue(this.plugin.settings.maxConcurrentRuns).setDynamicTooltip().onChange(async a=>{this.plugin.settings.maxConcurrentRuns=a,await this.plugin.saveSettings()})),new te.Setting(e).setName("Run log retention").setDesc("Days to keep run logs before auto-prune.").addSlider(s=>s.setLimits(1,365,1).setValue(this.plugin.settings.runLogRetentionDays).setDynamicTooltip().onChange(async a=>{this.plugin.settings.runLogRetentionDays=a,await this.plugin.saveSettings()})),new te.Setting(e).setName("Catch up missed tasks").addToggle(s=>s.setValue(this.plugin.settings.catchUpMissedTasks).onChange(async a=>{this.plugin.settings.catchUpMissedTasks=a,await this.plugin.saveSettings()})),new te.Setting(e).setName("Notification level").addDropdown(s=>s.addOption("all","All").addOption("failures-only","Failures only").addOption("none","None").setValue(this.plugin.settings.notificationLevel).onChange(async a=>{this.plugin.settings.notificationLevel=a,await this.plugin.saveSettings()})),new te.Setting(e).setName("Status bar").addToggle(s=>s.setValue(this.plugin.settings.showStatusBar).onChange(async a=>{this.plugin.settings.showStatusBar=a,await this.plugin.saveSettings(),this.plugin.refreshStatusBar()})),new te.Setting(e).setName("Verify Claude CLI").setDesc("Checks that the configured binary is reachable.").addButton(s=>s.setButtonText("Verify").onClick(async()=>{let a=await this.plugin.verifyClaudeCli();new te.Notice(a?"Claude CLI detected.":"Claude CLI check failed. See console for details.")})),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 te.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 te.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 te.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 te.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 te.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 te.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 te.Setting(o).setName("Bot token (xoxb-...)").addText(c=>{c.inputEl.type="password",c.setPlaceholder("xoxb-...").onChange(d=>{i.botToken=d.trim()})}),new te.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 te.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 te.Setting(n).addButton(c=>c.setButtonText("Add credential").setCta().onClick(async()=>{if(!i.ref||!i.botToken){new te.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 te.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 te.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 ${hi(ui(n))}`,cls:"af-muted"}).style.color="var(--text-muted)";let l=i.createEl("button",{text:"Remove"});l.onclick=()=>{this.plugin.channelCredentials.delete(a),new te.Notice(`Removed credential \`${a}\`.`),this.display()}}}};function ui(r){return r.type==="slack",r.botToken}function hi(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var Ia=require("crypto");function xe(r,t,e,s,a,n,i,o){return xe.fromTZ(xe.tp(r,t,e,s,a,n,i),o)}xe.fromTZISO=(r,t,e)=>xe.fromTZ(pi(r,t),e);xe.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=Hs(r.tz,e),a=new Date(e.getTime()-s),n=Hs(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=Hs(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}};xe.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}};xe.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function Hs(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 pi(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("+")?xe.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):xe.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}xe.minitz=xe;function mi(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 me(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new me(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 qs=32,Ot=31|qs,Sa=[1,2,4,8,16];function De(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()}De.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 me(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,Ot),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};De.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)};De.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.")};De.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)};De.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};De.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)};De.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]};De.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)};De.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)};De.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")};De.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")};De.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};De.prototype.setNthWeekdayOfMonth=function(r,t){if(t==="L")this.dayOfWeek[r]=this.dayOfWeek[r]|qs;else if(t<6&&t>0)this.dayOfWeek[r]=this.dayOfWeek[r]|Sa[t-1];else if(t===Ot)this.dayOfWeek[r]=Ot;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var Ta=[31,28,31,30,31,30,31,31,30,31,30,31],Ze=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function me(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 me)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}me.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&Ot&&Sa[i-1]&s)return!0;if(s&qs){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};me.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=xe.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()};me.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};me.prototype.apply=function(){if(this.month>11||this.day>Ta[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};me.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=xe.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(xe.fromTZISO(r,this.tz))};me.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=Ta[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&Ot)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};me.prototype.recurse=function(r,t,e){let s=this.findNext(t,Ze[e][0],r,Ze[e][2]);if(s>1){let a=e+1;for(;a<Ze.length;)this[Ze[a][0]]=-Ze[a][2],a++;if(s===3)return this[Ze[e][1]]++,this[Ze[e][0]]=-Ze[e][2],this.apply(),this.recurse(r,t,0);if(this.apply())return this.recurse(r,t,e-1)}return e+=1,e>=Ze.length?this:this.year>=3e3?null:this.recurse(r,t,e)};me.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)};me.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)):xe(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};me.prototype.getTime=function(){return this.getDate().getTime()};function ls(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function fi(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var xa=30*1e3,Bt=[];function ae(r,t,e){if(!(this instanceof ae))return new ae(r,t,e);let s,a;if(ls(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(ls(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=mi(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 me(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new De(r,this.options.timezone),this.name){if(Bt.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");Bt.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}ae.prototype.nextRun=function(r){let t=this._next(r);return t?t.getDate():null};ae.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};ae.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};ae.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};ae.prototype.isStopped=function(){return this._states.kill};ae.prototype.isBusy=function(){return this._states.blocking};ae.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};ae.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};ae.prototype.msToNext=function(r){r=r||new Date;let t=this._next(r);return t?t.getTime()-r.getTime():null};ae.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let r=Bt.indexOf(this);r>=0&&Bt.splice(r,1)};ae.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};ae.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};ae.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>xa&&(t=xa),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&fi(this._states.currentTimeout),this)};ae.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new me(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){ls(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new me(r,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};ae.prototype.trigger=async function(){await this._trigger()};ae.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&&ls(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};ae.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 me(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 me(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};ae.prototype._calculatePreviousRun=function(r,t){let e=new me(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 me(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]};ae.Cron=ae;ae.scheduledJobs=Bt;var La=require("obsidian");var Ca=require("child_process"),_a=require("crypto"),Fe=require("fs"),zs=require("path");function Ea(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(`
|
|
11104
|
-
|
|
11388
|
+
${e.examplesBody}`))}}else{let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=J(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,Y(i,l))}}async deleteSkill(t){let e=this.getSkillByName(t);if(e)if(e.isFolder){let s=(0,C.normalizePath)(e.filePath.replace(/\/skill\.md$/,"")),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}else await this.trashFile(e.filePath)}async deleteTask(t){let e=this.getTaskById(t);e&&await this.trashFile(e.filePath)}async updateChannel(t,e){let s=this.getChannelByName(t);if(!s)return;let a=this.vault.getAbstractFileByPath(s.filePath);if(!(a instanceof C.TFile))return;let n=await this.vault.cachedRead(a),{frontmatter:i,body:o}=J(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,Y(i,l))}async deleteChannel(t){let e=this.getChannelByName(t);if(!e)return;await this.trashFile(e.filePath);let s=(0,C.normalizePath)(`${this.getSubfolder("channels")}/${ge(t)}/sessions`),a=this.vault.getAbstractFileByPath(s);a instanceof C.TFolder&&await this.vault.trash(a,!0)}async updateHeartbeat(t,e){let s=this.getAgentByName(t);if(!s||!s.isFolder)return;let a=(0,C.normalizePath)(s.filePath.replace(/\/agent\.md$/,"")),n=(0,C.normalizePath)(`${a}/HEARTBEAT.md`),i=this.vault.getAbstractFileByPath(n);if(i instanceof C.TFile){let o=await this.vault.cachedRead(i),{frontmatter:l,body:c}=J(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,Y(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,Y(o,l))}}async deleteAgent(t,e){let s=[],a=this.getAgentByName(t);if(!a)return{trashedFiles:s};if(a.isFolder){let i=(0,C.normalizePath)(a.filePath.replace(/\/agent\.md$/,"")),o=this.vault.getAbstractFileByPath(i);if(o instanceof C.TFolder){let 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,C.normalizePath)(`${t}/${e}${a}.md`);if(!this.vault.getAbstractFileByPath(n))return n;s+=1}}async createFileIfMissing(t,e){if(!this.vault.getAbstractFileByPath(t))try{await this.vault.create(t,e)}catch(s){if(!(s instanceof Error?s.message:String(s)).includes("File already exists"))throw s}}collectMarkdownChildren(t,e){for(let s of t.children)s instanceof C.TFile&&s.extension==="md"&&e.push(s),s instanceof C.TFolder&&this.collectMarkdownChildren(s,e)}clearStoredFile(t){this.agents.delete(t),this.skills.delete(t),this.tasks.delete(t),this.channels.delete(t),this.validationIssues.delete(t)}setIssue(t,e){let s=this.validationIssues.get(t)??[];s.push({path:t,message:e}),this.validationIssues.set(t,s)}parseFile(t,e){return t.startsWith(`${this.getSubfolder("agents")}/`)?this.parseAgent(t,e):t.startsWith(`${this.getSubfolder("skills")}/`)?this.parseSkill(t,e):t.startsWith(`${this.getSubfolder("tasks")}/`)?this.parseTask(t,e):null}parseAgent(t,e){let{frontmatter:s,body:a}=J(e);if(!vs(s))return this.setIssue(t,"Invalid frontmatter."),null;let n=A(s.name),i=A(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:A(s.description),model:i,adapter:A(s.adapter)??"claude-code",permissionMode:A(s.permission_mode)??"bypassPermissions",effort:A(s.effort),maxRetries:Ze(s.max_retries,1),skills:ye(s.skills),mcpServers:ye(s.mcp_servers),allowedTools:ye(s.allowed_tools),blockedTools:ye(s.blocked_tools),cwd:A(s.cwd),enabled:Be(s.enabled,!0),timeout:Ze(s.timeout,300),approvalRequired:ye(s.approval_required),memory:Be(s.memory,!1),memoryMaxEntries:Ze(s.memory_max_entries,100),tags:ye(s.tags),avatar:A(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}=J(e),n=A(s.name);return n?{filePath:t,name:n,description:A(s.description),tags:ye(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}=J(e),n=A(s.task_id),i=A(s.agent),o=A(s.type);if(!n||!i||!o)return this.setIssue(t,"Task requires `task_id`, `agent`, and `type`."),null;if(o==="recurring"&&!A(s.schedule))return this.setIssue(t,"Recurring task requires `schedule`."),null;if(o==="once"&&!A(s.run_at))return this.setIssue(t,"One-time task requires `run_at`."),null;let l=A(s.priority),d=l&&["low","medium","high","critical"].includes(l)?l:"medium";return{filePath:t,taskId:n,agent:i,schedule:A(s.schedule),runAt:A(s.run_at),type:o,priority:d,enabled:Be(s.enabled,!0),created:A(s.created)??new Date().toISOString(),lastRun:A(s.last_run),nextRun:A(s.next_run),runCount:Ze(s.run_count,0),catchUp:Be(s.catch_up,this.settings.catchUpMissedTasks),effort:A(s.effort),model:A(s.model),tags:ye(s.tags),body:a}}parseChannelFile(t,e){let{frontmatter:s,body:a}=J(e),n=A(s.name);if(!n)return this.setIssue(t,"Channel requires string field `name`."),null;let i=A(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=A(s.default_agent)??A(s.agent);if(!c)return this.setIssue(t,`Channel \`${n}\` requires \`default_agent\` (or \`agent\`).`),null;let d=ye(s.allowed_agents),h=A(s.credential_ref);if(!h)return this.setIssue(t,`Channel \`${n}\` requires \`credential_ref\` pointing at a configured credential.`),null;let u=vs(s.transport)?s.transport:{};return{filePath:t,name:n,type:l,defaultAgent:c,allowedAgents:d,enabled:Be(s.enabled,!0),credentialRef:h,allowedUsers:ye(s.allowed_users),perUserSessions:Be(s.per_user_sessions,!0),channelContext:A(s.channel_context)??"",transport:u,tags:ye(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(!vs(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(!vs(e)||!A(e.tool))return[];let s=A(e.tool);return s?[{tool:s,command:A(e.command),reason:A(e.reason),status:A(e.status)??"pending",resolvedAt:A(e.resolvedAt),note:A(e.note)}]:[]})}};var yt=require("obsidian"),bs=class extends yt.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,yt.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 yt.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,yt.setIcon)(d,"trash-2"),c.onclick=()=>{this.onConfirm(this.deleteTasks),this.close()}}};var U=require("obsidian");var Za=[{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"}],ws="__custom__";function Xi(r){let t=r.trim();return!t||t==="default"||t==="subscription"?"inherit":Za.some(e=>e.value===t)?"alias":"custom"}function vt(r,t){r.empty(),r.addClass("af-model-picker");let e=Xi(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 Za)n.createEl("option",{text:o.label,attr:{value:o.value}});s.createEl("option",{text:"Custom\u2026",attr:{value:ws}});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=ws,i.value=t.value.trim(),i.style.display=""),s.addEventListener("change",()=>{s.value===ws?(i.style.display="",i.focus(),t.onChange(i.value.trim())):(i.style.display="none",t.onChange(s.value))}),i.addEventListener("input",()=>{s.value===ws&&t.onChange(i.value.trim())})}var Ji={"*/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"},Qi=[["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"],["weekly","Once a week"]],Zi=[[0,"Sunday"],[1,"Monday"],[2,"Tuesday"],[3,"Wednesday"],[4,"Thursday"],[5,"Friday"],[6,"Saturday"]];function er(r){let t={freq:"daily",hour:3,minute:0,dayOfWeek:0},e=r?.trim();if(!e)return t;let s=Ji[e];if(s)return{...t,freq:s};let a=e.split(/\s+/);if(a.length>=5){let n=parseInt(a[0],10),i=parseInt(a[1],10),o=a[2],l=a[4];if(!isNaN(n)&&!isNaN(i))return o==="*"&&/^[0-6]$/.test(l)?{freq:"weekly",hour:i,minute:n,dayOfWeek:parseInt(l,10)}:{...t,freq:"daily",hour:i,minute:n}}return t}function Yt(r,t){r.addClass("af-schedule-picker");let e=er(t.value),s=r.createDiv({cls:"af-form-row"});s.createDiv({cls:"af-form-label",text:"Frequency"});let a=s.createEl("select",{cls:"af-form-select"});for(let[p,m]of Qi){let v=a.createEl("option",{text:m,attr:{value:p}});p===e.freq&&(v.selected=!0)}let n=r.createDiv({cls:"af-form-row af-schedule-day-row"});n.createDiv({cls:"af-form-label",text:"Day"});let i=n.createEl("select",{cls:"af-form-select"});for(let[p,m]of Zi){let v=i.createEl("option",{text:m,attr:{value:String(p)}});p===e.dayOfWeek&&(v.selected=!0)}let o=r.createDiv({cls:"af-form-row af-schedule-time-row"});o.createDiv({cls:"af-form-label",text:"Time"});let l=o.createDiv({cls:"af-schedule-time-selects"}),c=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let p=0;p<24;p++){let m=p>=12?"PM":"AM",v=p===0?12:p>12?p-12:p,k=c.createEl("option",{text:`${v} ${m}`,attr:{value:String(p)}});p===e.hour&&(k.selected=!0)}l.createSpan({cls:"af-schedule-colon",text:":"});let d=l.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let p=0;p<60;p+=5){let m=d.createEl("option",{text:String(p).padStart(2,"0"),attr:{value:String(p)}});p===e.minute&&(m.selected=!0)}let h=()=>{let p=a.value,m=p==="daily"||p==="weekly",v=p==="weekly";o.style.display=m?"":"none",n.style.display=v?"":"none"},u=()=>{let p=a.value,m=c.value,v=d.value,k=i.value,b="";switch(p){case"every_5m":b="*/5 * * * *";break;case"every_15m":b="*/15 * * * *";break;case"every_30m":b="*/30 * * * *";break;case"every_hour":b="0 * * * *";break;case"every_2h":b="0 */2 * * *";break;case"every_4h":b="0 */4 * * *";break;case"every_6h":b="0 */6 * * *";break;case"every_12h":b="0 */12 * * *";break;case"daily":b=`${v} ${m} * * *`;break;case"weekly":b=`${v} ${m} * * ${k}`;break}t.onChange(b)},f=()=>{h(),u()};return a.addEventListener("change",f),c.addEventListener("change",u),d.addEventListener("change",u),i.addEventListener("change",u),h(),()=>{a.removeEventListener("change",f),c.removeEventListener("change",u),d.removeEventListener("change",u),i.removeEventListener("change",u)}}var de=require("obsidian");var Pt={scopeSlug:"",scopeRoot:"",inboxPath:"_sources/inbox",archivePath:"_sources/archive",topicsRoot:"_topics",indexPath:"index.md",logPath:"log.md",watchedFolders:[],excludePatterns:[],ingestSchedule:"0 3 * * *",lintSchedule:"0 9 * * 0",heartbeatChannel:"",fileSubstantiveAnswers:!1,obsidianUrlScheme:!0,maxTokensPerIngest:6e4,stateFile:".wiki-keeper-state.json"};function la(r){let t=ge(r).trim();return t?`wiki-keeper-${t}`:"wiki-keeper"}function tr(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:${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).
|
|
11389
|
+
|
|
11390
|
+
## Scope isolation
|
|
11391
|
+
|
|
11392
|
+
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.
|
|
11393
|
+
|
|
11394
|
+
## Two ingestion modes
|
|
11395
|
+
|
|
11396
|
+
- **Inbox** (\`_sources/inbox/\`): one-off drops. Process \u2192 summarize \u2192 archive.
|
|
11397
|
+
- **Watched**: folders listed in \`wiki_keeper.watched_folders\`. Read-only sources. Extract durable claims; never modify or move.
|
|
11398
|
+
|
|
11399
|
+
## Conventions
|
|
11400
|
+
|
|
11401
|
+
- Cross-links are Obsidian wikilinks within this scope.
|
|
11402
|
+
- Topic pages under \`_topics/\` unless a subfolder exists.
|
|
11403
|
+
- Preserve user-authored content; only revise/extend inside fenced blocks.
|
|
11404
|
+
- Log every action to \`log.md\`.
|
|
11405
|
+
|
|
11406
|
+
## When invoked
|
|
11407
|
+
|
|
11408
|
+
- If prompt names a file in the inbox or mentions watched-folder changes \u2192 use \`wiki-ingest\`.
|
|
11409
|
+
- If prompt is a question \u2192 use \`wiki-query\`.
|
|
11410
|
+
- If prompt is "lint" or a periodic \`lint\` heartbeat \u2192 use \`wiki-lint\`.
|
|
11411
|
+
- Otherwise ask what the user wants.
|
|
11412
|
+
|
|
11413
|
+
## Non-goals
|
|
11414
|
+
|
|
11415
|
+
- Never delete user-authored pages.
|
|
11416
|
+
- Never rewrite source files.
|
|
11417
|
+
- Never auto-apply judgment-call lint fixes.
|
|
11418
|
+
`;return Y(s,a)}function sr(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,ingest_schedule:r.ingestSchedule,lint_schedule:r.lintSchedule,file_substantive_answers:r.fileSubstantiveAnswers,obsidian_url_scheme:r.obsidianUrlScheme,max_tokens_per_ingest:r.maxTokensPerIngest,state_file:r.stateFile}};return Y(t,"")}function ar(r){let t={enabled:!0,schedule:r.ingestSchedule,notify:!0};return r.heartbeatChannel&&(t.channel=r.heartbeatChannel),Y(t,`Run wiki-ingest in both modes:
|
|
11419
|
+
|
|
11420
|
+
1. Drain every unprocessed file in the configured inbox (inbox mode).
|
|
11421
|
+
2. Diff watched folders against the state file; process changed or new files (watched mode).
|
|
11422
|
+
|
|
11423
|
+
Lint runs on its own schedule via the sibling \`*-lint\` task.
|
|
11424
|
+
`)}function tn(r,t){let s={task_id:`${r}-lint`,agent:r,type:"recurring",schedule:t,priority:"low",enabled:!0,created:new Date().toISOString(),run_count:0,catch_up:!1,tags:["wiki-keeper","lint"]};return Y(s,"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 ca(r,t){return(0,de.normalizePath)(`${r}/tasks/${t}-lint.md`)}var nr=`# Wiki Schema
|
|
11425
|
+
|
|
11426
|
+
## Page types
|
|
11427
|
+
|
|
11428
|
+
- **Entity pages** \u2014 people, orgs, products. Frontmatter: \`type: entity\`.
|
|
11429
|
+
- **Concept pages** \u2014 ideas, techniques, patterns. \`type: concept\`.
|
|
11430
|
+
- **Event pages** \u2014 meetings, releases, decisions. \`type: event, date: YYYY-MM-DD\`.
|
|
11431
|
+
- **Summary pages** (inbox-mode only) \u2014 one per ingested inbox source. \`type: summary, source: <filename>\`.
|
|
11432
|
+
- **Synthesis pages** (optional, from wiki-query) \u2014 filed answers. \`type: synthesis, question: <q>\`.
|
|
11433
|
+
|
|
11434
|
+
## Naming
|
|
11435
|
+
|
|
11436
|
+
- Slug-case filenames: \`vendor-x.md\`, not \`Vendor X.md\`.
|
|
11437
|
+
- Group by type under \`_topics/<type>/\` when there are >5 pages of a type.
|
|
11438
|
+
|
|
11439
|
+
## Links
|
|
11440
|
+
|
|
11441
|
+
- Every entity/concept page MUST have \u22651 inbound link from \`index.md\` or a sibling.
|
|
11442
|
+
- Summary pages MUST forward-link to every entity/concept they mention.
|
|
11443
|
+
- Watched-mode extractions append dated entries to topic pages; forward-link to the source file path so readers can find the raw note.
|
|
11444
|
+
|
|
11445
|
+
## Conflict resolution
|
|
11446
|
+
|
|
11447
|
+
- New claim contradicts existing? Add a \`## Contradictions\` section at the bottom of the contested page with a dated entry. Do NOT overwrite.
|
|
11448
|
+
- Flag in \`log.md\` for user review.
|
|
11449
|
+
`,ir=JSON.stringify({allow:["Read","Write","Edit","Glob","Grep","Bash(mv *)","Bash(mkdir *)"],deny:["Bash(rm -rf *)","Bash(git push *)"]},null,2);async function sn(r,t,e){let s=la(e.scopeSlug||e.scopeRoot),a=(0,de.normalizePath)(`${t}/agents/${s}`);if(await At(r,a),r.getAbstractFileByPath((0,de.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,de.normalizePath)(`${a}/agent.md`),tr(s,e.scopeRoot)),await r.create((0,de.normalizePath)(`${a}/config.md`),sr(e)),await r.create((0,de.normalizePath)(`${a}/HEARTBEAT.md`),ar(e)),await r.create((0,de.normalizePath)(`${a}/CONTEXT.md`),nr),await r.create((0,de.normalizePath)(`${a}/permissions.json`),ir);let i=ca(t,s);r.getAbstractFileByPath(i)||(await At(r,(0,de.normalizePath)(`${t}/tasks`)),await r.create(i,tn(s,e.lintSchedule)));let o=e.scopeRoot.trim(),l=o?`${o}/`:"";return await At(r,(0,de.normalizePath)(`${l}${e.inboxPath}`)),await At(r,(0,de.normalizePath)(`${l}${e.topicsRoot}`)),await en(r,(0,de.normalizePath)(`${l}${e.indexPath}`),`# Index
|
|
11450
|
+
|
|
11451
|
+
<!-- wiki-keeper:begin -->
|
|
11452
|
+
<!-- wiki-keeper:end -->
|
|
11453
|
+
`),await en(r,(0,de.normalizePath)(`${l}${e.logPath}`),`# Log
|
|
11454
|
+
|
|
11455
|
+
<!-- wiki-keeper:begin -->
|
|
11456
|
+
<!-- wiki-keeper:end -->
|
|
11457
|
+
`),{path:a,name:s}}async function At(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 en(r,t,e){if(r.getAbstractFileByPath(t))return;let s=t.replace(/\/[^/]+$/,"");s&&s!==t&&await At(r,s),await r.create(t,e)}async function an(r,t,e,s){let a=(0,de.normalizePath)(`${t}/agents/${e}`),n=(0,de.normalizePath)(`${a}/config.md`),i=r.getAbstractFileByPath(n);if(!(i instanceof de.TFile))throw new Error(`Config file not found for ${e} at ${n}`);let o=await r.cachedRead(i),{frontmatter:l,body:c}=J(o),d=l.wiki_keeper??{};d.watched_folders=s.watchedFolders,d.exclude_patterns=s.excludePatterns,d.ingest_schedule=s.ingestSchedule,d.lint_schedule=s.lintSchedule,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,Y(l,c));let h=(0,de.normalizePath)(`${a}/HEARTBEAT.md`),u=r.getAbstractFileByPath(h);if(u instanceof de.TFile){let m=await r.cachedRead(u),{frontmatter:v,body:k}=J(m);v.schedule=s.ingestSchedule,s.heartbeatChannel?v.channel=s.heartbeatChannel:delete v.channel,await r.modify(u,Y(v,k))}let f=ca(t,e),p=r.getAbstractFileByPath(f);if(p instanceof de.TFile){let m=await r.cachedRead(p),{frontmatter:v,body:k}=J(m);v.schedule=s.lintSchedule,await r.modify(p,Y(v,k))}else await At(r,(0,de.normalizePath)(`${t}/tasks`)),await r.create(f,tn(e,s.lintSchedule))}async function nn(r,t,e){let s=(0,de.normalizePath)(`${t}/agents/${e}`),a=r.getAbstractFileByPath(s);if(!a)return;if(!(a instanceof de.TFolder))throw new Error(`Expected folder at ${s}`);await r.adapter.rmdir(s,!0);let n=ca(t,e),i=r.getAbstractFileByPath(n);i instanceof de.TFile&&await r.delete(i)}var ks=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()||Qe.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()||Qe.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();vt(a,{value:this.plugin.settings.defaultModel,onChange:async n=>{this.plugin.settings.defaultModel=n||Qe.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()||Qe.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 ${or(rr(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 ha(this.plugin,o,()=>{this.scheduleRerender()}).open()};let f=h.createEl("button",{text:"Delete",cls:"af-wk-row-btn af-wk-row-btn-danger"});f.onclick=async()=>{if(confirm(`Delete Wiki Keeper "${o.name}"?
|
|
11458
|
+
|
|
11459
|
+
This removes the agent folder at _fleet/agents/${o.name}/.
|
|
11460
|
+
Your scope's inbox, topics, index, and log are NOT deleted.`))try{await nn(this.plugin.app.vault,this.plugin.settings.fleetFolder,o.name),new U.Notice(`Wiki Keeper "${o.name}" deleted.`),this.scheduleRerender()}catch(m){let v=m instanceof Error?m.message:String(m);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 da(this.plugin,()=>{this.scheduleRerender()}).open()}))}scheduleRerender(){window.setTimeout(async()=>{try{await this.plugin.refreshFromVault()}catch{}this.display()},600)}},da=class extends U.Modal{constructor(e,s){super(e.app);this.plugin=e;this.onCreated=s;this.input={...Pt}}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(u=>u.setPlaceholder("(empty = whole vault)").setValue(this.input.scopeRoot).onChange(f=>{this.input.scopeRoot=f.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(u=>u.setPlaceholder("(auto)").setValue(this.input.scopeSlug).onChange(f=>{this.input.scopeSlug=f.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(u=>u.setPlaceholder("(empty = inbox-only)").setValue(this.input.watchedFolders.join(", ")).onChange(f=>{this.input.watchedFolders=f.split(/[,\n]/).map(p=>p.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(u=>u.setPlaceholder("(empty = no excludes)").setValue(this.input.excludePatterns.join(", ")).onChange(f=>{this.input.excludePatterns=f.split(/[,\n]/).map(p=>p.trim()).filter(Boolean)})),e.createEl("h3",{text:"Schedule",cls:"af-wk-section-h"});let a=e.createDiv({cls:"af-wk-schedule-block"});a.createDiv({cls:"af-form-hint",text:"Ingest schedule \u2014 when the keeper wakes up to process new sources."});let n=a.createDiv();Yt(n,{value:this.input.ingestSchedule,onChange:u=>{this.input.ingestSchedule=u||Pt.ingestSchedule}});let i=e.createDiv({cls:"af-wk-schedule-block"});i.createDiv({cls:"af-form-hint",text:"Lint schedule \u2014 when the keeper audits the wiki for orphans, stale claims, and schema drift."});let o=i.createDiv();Yt(o,{value:this.input.lintSchedule,onChange:u=>{this.input.lintSchedule=u||Pt.lintSchedule}});let l=this.plugin.runtime.getSnapshot().channels.map(u=>u.name);new U.Setting(e).setName("Heartbeat channel").setDesc("Optional. Slack/Telegram channel for ingest + lint summaries. Leave as (none) to disable.").addDropdown(u=>{u.addOption("","(none)");for(let f of l)u.addOption(f,f);u.setValue(this.input.heartbeatChannel).onChange(f=>{this.input.heartbeatChannel=f})}),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(u=>u.setValue(String(this.input.maxTokensPerIngest)).onChange(f=>{let p=parseInt(f,10);!isNaN(p)&&p>0&&(this.input.maxTokensPerIngest=p)}));let c=e.createDiv({cls:"af-wk-modal-footer"}),d=c.createEl("button",{text:"Cancel"});d.onclick=()=>this.close();let h=c.createEl("button",{text:"Create",cls:"mod-cta"});h.onclick=async()=>{h.setText("Creating\u2026"),h.disabled=!0;try{let{name:u}=await sn(this.app.vault,this.plugin.settings.fleetFolder,this.input);this.close(),new U.Notice(`Wiki Keeper "${u}" created.`),this.onCreated()}catch(u){let f=u instanceof Error?u.message:String(u);new U.Notice(`Failed to create Wiki Keeper: ${f}`),h.setText("Create"),h.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: ${la(s)}`)}onClose(){this.contentEl.empty()}},ha=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],ingestSchedule:n.ingestSchedule,lintSchedule:n.lintSchedule,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(f=>f.setValue(this.edit.watchedFolders.join(", ")).onChange(p=>{this.edit.watchedFolders=p.split(/[,\n]/).map(m=>m.trim()).filter(Boolean)})),new U.Setting(e).setName("Exclude patterns").setDesc("Glob patterns to skip.").addTextArea(f=>f.setValue(this.edit.excludePatterns.join(", ")).onChange(p=>{this.edit.excludePatterns=p.split(/[,\n]/).map(m=>m.trim()).filter(Boolean)})),e.createEl("h3",{text:"Schedule",cls:"af-wk-section-h"});let n=e.createDiv({cls:"af-wk-schedule-block"});n.createDiv({cls:"af-form-hint",text:"Ingest schedule \u2014 when the keeper wakes up to process new sources."});let i=n.createDiv();Yt(i,{value:this.edit.ingestSchedule,onChange:f=>{this.edit.ingestSchedule=f||Pt.ingestSchedule}});let o=e.createDiv({cls:"af-wk-schedule-block"});o.createDiv({cls:"af-form-hint",text:"Lint schedule \u2014 when the keeper audits the wiki for orphans, stale claims, and schema drift."});let l=o.createDiv();Yt(l,{value:this.edit.lintSchedule,onChange:f=>{this.edit.lintSchedule=f||Pt.lintSchedule}});let c=this.plugin.runtime.getSnapshot().channels.map(f=>f.name);new U.Setting(e).setName("Heartbeat channel").addDropdown(f=>{f.addOption("","(none)");for(let p of c)f.addOption(p,p);f.setValue(this.edit.heartbeatChannel).onChange(p=>{this.edit.heartbeatChannel=p})}),e.createEl("h3",{text:"Advanced",cls:"af-wk-section-h"}),new U.Setting(e).setName("Max tokens per ingest").addText(f=>f.setValue(String(this.edit.maxTokensPerIngest)).onChange(p=>{let m=parseInt(p,10);!isNaN(m)&&m>0&&(this.edit.maxTokensPerIngest=m)}));let d=e.createDiv({cls:"af-wk-modal-footer"}),h=d.createEl("button",{text:"Cancel"});h.onclick=()=>this.close();let u=d.createEl("button",{text:"Save",cls:"mod-cta"});u.onclick=async()=>{u.setText("Saving\u2026"),u.disabled=!0;try{await an(this.app.vault,this.plugin.settings.fleetFolder,this.agent.name,this.edit),this.close(),new U.Notice("Saved."),this.onSaved()}catch(f){let p=f instanceof Error?f.message:String(f);new U.Notice(`Failed to save: ${p}`),u.setText("Save"),u.disabled=!1}}}onClose(){this.contentEl.empty()}};function rr(r){return r.type==="slack",r.botToken}function or(r){return r.length<=10?"***":`${r.slice(0,6)}\u2026${r.slice(-4)}`}var yn=require("crypto");function Pe(r,t,e,s,a,n,i,o){return Pe.fromTZ(Pe.tp(r,t,e,s,a,n,i),o)}Pe.fromTZISO=(r,t,e)=>Pe.fromTZ(lr(r,t),e);Pe.fromTZ=function(r,t){let e=new Date(Date.UTC(r.y,r.m-1,r.d,r.h,r.i,r.s)),s=ua(r.tz,e),a=new Date(e.getTime()-s),n=ua(r.tz,a);if(n-s===0)return a;{let i=new Date(e.getTime()-n),o=ua(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}};Pe.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}};Pe.tp=(r,t,e,s,a,n,i)=>({y:r,m:t,d:e,h:s,i:a,s:n,tz:i});function ua(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 lr(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("+")?Pe.tp(e.getUTCFullYear(),e.getUTCMonth()+1,e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),"Etc/UTC"):Pe.tp(e.getFullYear(),e.getMonth()+1,e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),t)}Pe.minitz=Pe;function cr(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 we(r.startAt,r.timezone)),r.stopAt&&(r.stopAt=new we(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 pa=32,Kt=31|pa,on=[1,2,4,8,16];function Fe(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()}Fe.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 we(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,Kt),this.dayOfWeek[7]&&(this.dayOfWeek[0]=this.dayOfWeek[7])};Fe.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)};Fe.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.")};Fe.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)};Fe.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};Fe.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)};Fe.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]};Fe.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)};Fe.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)};Fe.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")};Fe.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")};Fe.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};Fe.prototype.setNthWeekdayOfMonth=function(r,t){if(t==="L")this.dayOfWeek[r]=this.dayOfWeek[r]|pa;else if(t<6&&t>0)this.dayOfWeek[r]=this.dayOfWeek[r]|on[t-1];else if(t===Kt)this.dayOfWeek[r]=Kt;else throw new TypeError(`CronPattern: nth weekday of of range, should be 1-5 or L. Value: ${t}`)};var ln=[31,28,31,30,31,30,31,31,30,31,30,31],nt=[["month","year",0],["day","month",-1],["hour","day",0],["minute","hour",0],["second","minute",0]];function we(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 we)this.fromCronDate(r);else throw new TypeError("CronDate: Invalid type ("+typeof r+") passed to CronDate constructor")}we.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&Kt&&on[i-1]&s)return!0;if(s&pa){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};we.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=Pe.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()};we.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};we.prototype.apply=function(){if(this.month>11||this.day>ln[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};we.prototype.fromString=function(r){if(typeof this.tz=="number"){let t=Pe.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(Pe.fromTZISO(r,this.tz))};we.prototype.findNext=function(r,t,e,s){let a=this[t],n;e.lastDayOfMonth&&(this.month!==1?n=ln[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&Kt)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};we.prototype.recurse=function(r,t,e){let s=this.findNext(t,nt[e][0],r,nt[e][2]);if(s>1){let a=e+1;for(;a<nt.length;)this[nt[a][0]]=-nt[a][2],a++;if(s===3)return this[nt[e][1]]++,this[nt[e][0]]=-nt[e][2],this.apply(),this.recurse(r,t,0);if(this.apply())return this.recurse(r,t,e-1)}return e+=1,e>=nt.length?this:this.year>=3e3?null:this.recurse(r,t,e)};we.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)};we.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)):Pe(this.year,this.month+1,this.day,this.hour,this.minute,this.second,this.tz)};we.prototype.getTime=function(){return this.getDate().getTime()};function xs(r){return Object.prototype.toString.call(r)==="[object Function]"||typeof r=="function"||r instanceof Function}function dr(r){typeof Deno<"u"&&typeof Deno.unrefTimer<"u"?Deno.unrefTimer(r):r&&typeof r.unref<"u"&&r.unref()}var rn=30*1e3,Xt=[];function re(r,t,e){if(!(this instanceof re))return new re(r,t,e);let s,a;if(xs(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(xs(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=cr(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 we(r,this.options.timezone||this.options.utcOffset):this._states.pattern=new Fe(r,this.options.timezone),this.name){if(Xt.find(i=>i.name===this.name))throw new Error("Cron: Tried to initialize new named job '"+this.name+"', but name already taken.");Xt.push(this)}return a!==void 0&&(this.fn=a,this.schedule()),this}re.prototype.nextRun=function(r){let t=this._next(r);return t?t.getDate():null};re.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};re.prototype.getPattern=function(){return this._states.pattern?this._states.pattern.pattern:void 0};re.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};re.prototype.isStopped=function(){return this._states.kill};re.prototype.isBusy=function(){return this._states.blocking};re.prototype.currentRun=function(){return this._states.currentRun?this._states.currentRun.getDate():null};re.prototype.previousRun=function(){return this._states.previousRun?this._states.previousRun.getDate():null};re.prototype.msToNext=function(r){r=r||new Date;let t=this._next(r);return t?t.getTime()-r.getTime():null};re.prototype.stop=function(){this._states.kill=!0,this._states.currentTimeout&&clearTimeout(this._states.currentTimeout);let r=Xt.indexOf(this);r>=0&&Xt.splice(r,1)};re.prototype.pause=function(){return this._states.paused=!0,!this._states.kill};re.prototype.resume=function(){return this._states.paused=!1,!this._states.kill};re.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>rn&&(t=rn),this._states.currentTimeout=setTimeout(()=>this._checkTrigger(e),t),this._states.currentTimeout&&this.options.unref&&dr(this._states.currentTimeout),this)};re.prototype._trigger=async function(r){if(this._states.blocking=!0,this._states.currentRun=new we(void 0,this.options.timezone||this.options.utcOffset),this.options.catch)try{await this.fn(this,this.options.context)}catch(t){xs(this.options.catch)&&this.options.catch(t,this)}else await this.fn(this,this.options.context);this._states.previousRun=new we(r,this.options.timezone||this.options.utcOffset),this._states.blocking=!1};re.prototype.trigger=async function(){await this._trigger()};re.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&&xs(this.options.protect)&&setTimeout(()=>this.options.protect(this),0),this.schedule()};re.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 we(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 we(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};re.prototype._calculatePreviousRun=function(r,t){let e=new we(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 we(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]};re.Cron=re;re.scheduledJobs=Xt;var vn=require("obsidian");var hn=require("crypto"),Ue=require("fs"),ga=require("path");var cn=new Set(["","default","subscription"]);function Dt(r,t,e){let s=fa(r?.model);if(s)return{value:ma(s),source:"task"};let a=fa(t.model);if(a)return{value:ma(a),source:"agent"};let n=fa(e.defaultModel);return n?{value:ma(n),source:"settings"}:{value:"",source:"cli-default"}}function Ss(r){return!cn.has(r.trim())}function fa(r){if(!r)return"";let t=r.trim();return t||""}function ma(r){return cn.has(r)?"":r}function Ts(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(`
|
|
11461
|
+
`)}function un(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(`
|
|
11462
|
+
`);if(r&&typeof r=="object"){for(let t of["output","result","text","message"])if(t in r)return un(r[t])}}function ya(r,t=[]){if(Array.isArray(r)){for(let i of r)ya(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))ya(i,t);return t}function pn(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=pn(a);if(typeof n=="number")return n}}function fn(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=fn(e);if(typeof s=="number")return s}}function dn(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 va(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=va(s);if(a)return a}}function mn(r){let t=r.matchAll(/\[REMEMBER\]([\s\S]*?)\[\/REMEMBER\]/g);return Array.from(t).map(e=>e[1]?.trim()??"").filter(Boolean)}var Cs=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(`
|
|
11105
11463
|
\u25B8 ${i}${l?`: ${String(l).slice(0,200)}`:""}
|
|
11106
11464
|
`)}if(a.length>0)return a.join("")}}if(e==="result"){let s=typeof t.result=="string"?t.result:null;if(s)return`
|
|
11107
|
-
${s}`}return null}async buildPrompt(t,e,s){let a=[t.body.trim()];for(let
|
|
11108
|
-
${
|
|
11109
|
-
${
|
|
11110
|
-
${
|
|
11111
|
-
${
|
|
11465
|
+
${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
|
|
11466
|
+
${o.toolsBody.trim()}`),o.referencesBody.trim()&&l.push(`### References
|
|
11467
|
+
${o.referencesBody.trim()}`),o.examplesBody.trim()&&l.push(`### Examples
|
|
11468
|
+
${o.examplesBody.trim()}`),a.push(`## Skill: ${o.name}
|
|
11469
|
+
${l.join(`
|
|
11112
11470
|
|
|
11113
11471
|
`)}`)}}if(t.skillsBody.trim()&&a.push(`## Agent Skills
|
|
11114
11472
|
${t.skillsBody.trim()}`),t.contextBody.trim()&&a.push(`## Agent Context
|
|
11115
|
-
${t.contextBody.trim()}`),t.memory){let
|
|
11116
|
-
${
|
|
11473
|
+
${t.contextBody.trim()}`),t.memory){let i=await this.repository.getMemory(t.name);i?.body.trim()&&a.push(`## Agent Memory
|
|
11474
|
+
${i.body.trim()}`)}let n=Ts(t,this.repository);return n&&a.push(n),a.push(`## Task
|
|
11117
11475
|
${(s??e.body).trim()}`),a.filter(Boolean).join(`
|
|
11118
11476
|
|
|
11119
|
-
`)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),i=(0,
|
|
11120
|
-
`,"utf-8")}let g=Date.now(),y=()=>{if(k)try{
|
|
11121
|
-
`)){let j=V.trim();if(j)try{let $=JSON.parse(j),W=this.extractStreamContent($);W&&a(W)}catch{}}}),A.stderr.on("data",K=>{R+=K.toString()}),A.on("error",K=>{clearTimeout(D),T(K)}),A.on("close",K=>{clearTimeout(D),this.runningProcesses.delete(t.name);let q=E.trim(),V;if(c){let H=q.split(`
|
|
11122
|
-
`);for(let P=H.length-1;P>=0;P--){let M=H[P]?.trim();if(M)try{let I=JSON.parse(M);if(I&&typeof I=="object"){V=I;break}}catch{}}}else if(q.startsWith("{")||q.startsWith("["))try{V=JSON.parse(q)}catch{V=void 0}let j=Ea(V)??"";if(!j&&c){let H=[];for(let P of q.split(`
|
|
11123
|
-
`)){let M=P.trim();if(M)try{let I=JSON.parse(M);if(I.type==="assistant"&&I.message?.content)for(let Q of I.message.content)Q.type==="text"&&Q.text&&H.push(Q.text);else I.type==="result"&&typeof I.result=="string"&&H.push(I.result)}catch{}}j=H.join(`
|
|
11124
|
-
`).trim()}j||(j=R.trim()||"(no output)");let $=Ws(V),W=Aa(V),X=Pa(V);x({runId:i,prompt:n,exitCode:K,durationSeconds:Math.max(1,Math.round((Date.now()-g)/1e3)),stdout:E,stderr:R,outputText:j,rawJson:V,tokensUsed:W,costUsd:X,toolsUsed:$,timedOut:U})})})}finally{y()}}};var ds=require("child_process"),Nt=Le(require("crypto")),Ra=Le(require("https")),us=Le(require("http")),Se=Le(require("fs")),ze=Le(require("path")),hs=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=ze.join(process.env.HOME??"",".claude","settings.local.json"),a={};try{let o=Se.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),Se.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,"_")}shellEscape(t){return`'${t.replace(/'/g,"'\\''")}'`}async addServer(t){let e=["mcp","add"],s=t.scope??"user";if(e.push("-s",s),t.transport==="stdio"){if(t.envVars)for(let[a,n]of Object.entries(t.envVars))e.push("-e",`${a}=${n}`);e.push(this.shellEscape(t.name)),e.push("--"),t.command&&e.push(t.command),t.args&&e.push(...t.args)}else if(e.push("-t",t.transport),e.push(this.shellEscape(t.name)),t.url&&e.push(this.shellEscape(t.url)),t.headers)for(let[a,n]of Object.entries(t.headers))e.push("-H",this.shellEscape(`${a}: ${n}`));await this.runCli(e.join(" ")),this.enableServerInClaudeConfig(t.name),this.invalidateCache()}async removeServer(t,e){let s=["mcp","remove"];e&&s.push("-s",e),s.push(this.shellEscape(t)),await this.runCli(s.join(" ")),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),u=Nt.randomBytes(32).toString("base64url"),h=Nt.createHash("sha256").update(u).digest("base64url"),m=Nt.randomBytes(16).toString("hex"),f=new URLSearchParams({response_type:"code",client_id:d,code_challenge:h,code_challenge_method:"S256",redirect_uri:c,state:m,resource:o}),p=i??n.scopes_supported;p?.length&&f.set("scope",p.join(" "));let b=`${n.authorization_endpoint}?${f.toString()}`;(0,ds.spawn)("open",[b],{stdio:"ignore"});let k=await l.waitForCode(m,18e4),v=await this.exchangeOAuthCode(n.token_endpoint,k,c,d,u);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{l.close()}}async extractTokenFromCli(t){try{return(await this.runCli(`mcp get ${this.shellEscape(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 u=await this.oauthFetch(e,"POST","{}","application/json");u.status===401&&(a=u.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 u=await this.oauthFetch(a,"GET");if(u.status===200){let h=JSON.parse(u.body),m=h.authorization_servers;m?.[0]&&(n=m[0]),Array.isArray(h.scopes_supported)&&(i=h.scopes_supported),typeof h.resource=="string"&&(o=h.resource)}}catch{}let l=new URL(n),c=l.pathname==="/"?"":l.pathname,d=`${l.origin}/.well-known/oauth-authorization-server${c}`;try{let u=await this.oauthFetch(d,"GET");if(u.status===200)return{metadata:JSON.parse(u.body),resourceScopes:i,resource:o}}catch{}if(c){let u=`${l.origin}/.well-known/oauth-authorization-server`;try{let h=await this.oauthFetch(u,"GET");if(h.status===200)return{metadata:JSON.parse(h.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=us.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 u=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>"+u+"</p><p style='color:#888'>You can close this tab.</p></body></html>"),e?.(new Error(`OAuth denied: ${u}`));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,u)=>{clearTimeout(c),u!==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=ze.join(process.env.HOME??"",".claude.json");try{let s=Se.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=ze.join(process.env.HOME??"",".claude.json");try{let a=Se.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}`,Se.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=ze.join(process.env.HOME??"",".claude","mcp-needs-auth-cache.json");try{let s=Se.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&&Se.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},h=(l?Ra:us).request(d,f=>{let p="";f.on("data",b=>{p+=b.toString()}),f.on("end",()=>{let b={};for(let[k,v]of Object.entries(f.headers))typeof v=="string"?b[k]=v:Array.isArray(v)&&(b[k]=v.join(", "));n({status:f.statusCode??0,body:p,headers:b})})});h.on("error",i);let m=setTimeout(()=>{h.destroy(),i(new Error("OAuth request timed out"))},15e3);h.on("close",()=>clearTimeout(m)),s&&h.write(s),h.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.runCli(`mcp get '${o.name.replace(/'/g,"'\\''")}'`),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=ze.join(process.env.HOME??"",".claude","plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=Se.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=(0,ds.spawn)("/bin/zsh",["-l","-c",a],{env:{...process.env},stdio:["pipe","pipe","pipe"]}),i="",o,l=[],c=!1,d=!1,u=!1,h=()=>{c||(c=!0,n.kill(),s({description:o,tools:l}))},m=setTimeout(h,1e4);n.stdout.on("data",f=>{i+=f.toString();let p=i.split(`
|
|
11125
|
-
`);i=p.pop()??"";for(let b of p){let k=b.trim();if(k){try{let v=JSON.parse(k);if(v.id===1&&v.result){o=v.result.instructions??v.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+`
|
|
11477
|
+
`)}async execute(t,e,s,a){let n=await this.buildPrompt(t,e,s),i=(0,hn.randomUUID)(),o=Dt(e,t,this.settings),l=a!=null,c=["-p",n,"--output-format",l?"stream-json":"json"];l&&c.push("--verbose"),Ss(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,f=t.permissionMode?.trim(),p=!!f&&f!=="default",m=(t.mcpServers?.length??0)>0,v=u||p||m,k=null,b=null;if(v){let x=(0,ga.join)(h,".claude");if(k=(0,ga.join)(x,"settings.local.json"),(0,Ue.existsSync)(x)||(0,Ue.mkdirSync)(x,{recursive:!0}),(0,Ue.existsSync)(k))try{b=(0,Ue.readFileSync)(k,"utf-8")}catch{b=null}let S={};if(p&&(S.defaultMode=f),u||m){let T=[...t.permissionRules.allow];if(m)for(let P of t.mcpServers??[]){let E=P.replace(/[\s.]+/g,"_");T.push(`mcp__${E}`)}S.permissions={allow:T,deny:t.permissionRules.deny}}(0,Ue.writeFileSync)(k,JSON.stringify(S,null,2)+`
|
|
11478
|
+
`,"utf-8")}let g=Date.now(),y=()=>{if(k)try{b!==null?(0,Ue.writeFileSync)(k,b,"utf-8"):(0,Ue.existsSync)(k)&&(0,Ue.unlinkSync)(k)}catch{}};try{return await new Promise((x,S)=>{let T=at(this.settings.claudeCliPath,c,{cwd:h,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.runningProcesses.set(t.name,T);let P="",E="",R=!1,W=setTimeout(()=>{R=!0,T.kill()},t.timeout*1e3);T.stdout.on("data",I=>{let q=I.toString();if(P+=q,l&&a)for(let L of ce(q)){let K=L.trim();if(K)try{let V=JSON.parse(K),z=this.extractStreamContent(V);z&&a(z)}catch{}}}),T.stderr.on("data",I=>{E+=I.toString()}),T.on("error",I=>{clearTimeout(W),S(I)}),T.on("close",I=>{clearTimeout(W),this.runningProcesses.delete(t.name);let q=P.trim(),L;if(l){let G=ce(q);for(let B=G.length-1;B>=0;B--){let F=G[B]?.trim();if(F)try{let $=JSON.parse(F);if($&&typeof $=="object"){L=$;break}}catch{}}}else if(q.startsWith("{")||q.startsWith("["))try{L=JSON.parse(q)}catch{L=void 0}let K=un(L)??"";if(!K&&l){let G=[];for(let B of ce(q)){let F=B.trim();if(F)try{let $=JSON.parse(F);if($.type==="assistant"&&$.message?.content)for(let ee of $.message.content)ee.type==="text"&&ee.text&&G.push(ee.text);else $.type==="result"&&typeof $.result=="string"&&G.push($.result)}catch{}}K=G.join(`
|
|
11479
|
+
`).trim()}K||(K=E.trim()||"(no output)");let V=ya(L),z=pn(L),ie=fn(L),Q=va(L),te=dn(L);if((!Q||!te)&&l)for(let G of ce(q)){let B=G.trim();if(B)try{let F=JSON.parse(B);if(!Q){let $=va(F);$&&(Q=$)}if(!te){let $=dn(F);$&&(te=$)}if(Q&&te)break}catch{}}x({runId:i,prompt:n,exitCode:I,durationSeconds:Math.max(1,Math.round((Date.now()-g)/1e3)),stdout:P,stderr:E,outputText:K,rawJson:L,tokensUsed:z,costUsd:ie,toolsUsed:V,timedOut:R,resolvedModel:o.value,modelSource:o.source,concreteModel:Q,finalResult:te})})})}finally{y()}}};var Jt=Ne(require("crypto")),gn=Ne(require("https")),_s=Ne(require("http")),De=Ne(require("fs")),bt=Ne(require("path"));var Es=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=bt.join(zt(),"settings.local.json"),a={};try{let o=De.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),De.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=Jt.randomBytes(32).toString("base64url"),u=Jt.createHash("sha256").update(h).digest("base64url"),f=Jt.randomBytes(16).toString("hex"),p=new URLSearchParams({response_type:"code",client_id:d,code_challenge:u,code_challenge_method:"S256",redirect_uri:c,state:f,resource:o}),m=i??n.scopes_supported;m?.length&&p.set("scope",m.join(" "));let v=new URL(n.authorization_endpoint);for(let[y,x]of p)v.searchParams.set(y,x);let k=v.toString();console.log("McpManager: OAuth DCR client_id:",d),console.log("McpManager: OAuth auth URL:",k),Xa(k);let b=await l.waitForCode(f,18e4),g=await this.exchangeOAuthCode(n.token_endpoint,b,c,d,h);this.injectTokenIntoClaudeConfig(t,g.access_token),this.authManager&&this.authManager.storeOAuthToken(t,{accessToken:g.access_token,refreshToken:g.refresh_token,expiresAt:g.expires_in?Date.now()+g.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),f=u.authorization_servers;f?.[0]&&(n=f[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=_s.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=Gt();try{let s=De.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=Gt();try{let a=De.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}`,De.writeFileSync(s,JSON.stringify(n,null,2))}}catch(a){console.warn("McpManager: failed to inject token into ~/.claude.json:",a)}}clearNeedsAuthCache(t){let e=bt.join(zt(),"mcp-needs-auth-cache.json");try{let s=De.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&&De.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?gn:_s).request(d,p=>{let m="";p.on("data",v=>{m+=v.toString()}),p.on("end",()=>{let v={};for(let[k,b]of Object.entries(p.headers))typeof b=="string"?v[k]=b:Array.isArray(b)&&(v[k]=b.join(", "));n({status:p.statusCode??0,body:m,headers:v})})});u.on("error",i);let f=setTimeout(()=>{u.destroy(),i(new Error("OAuth request timed out"))},15e3);u.on("close",()=>clearTimeout(f)),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=bt.join(zt(),"plugins","marketplaces","claude-plugins-official","external_plugins",e,".claude-plugin","plugin.json");try{let a=De.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=ra(a,{env:{...process.env}}),i="",o,l=[],c=!1,d=!1,h=!1,u=()=>{c||(c=!0,n.kill(),s({description:o,tools:l}))},f=setTimeout(u,1e4);n.stdout.on("data",p=>{i+=p.toString();let m=ce(i);i=m.pop()??"";for(let v of m){let k=v.trim();if(k){try{let b=JSON.parse(k);if(b.id===1&&b.result){o=b.result.instructions??b.result.serverInfo?.description,d=!0;try{n.stdin.write(JSON.stringify({jsonrpc:"2.0",method:"notifications/initialized"})+`
|
|
11126
11480
|
`),n.stdin.write(JSON.stringify({jsonrpc:"2.0",id:2,method:"tools/list"})+`
|
|
11127
|
-
`)}catch{clearTimeout(
|
|
11128
|
-
`)}catch{clearTimeout(m),h()}})}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 u of d)o.push({name:u.name,description:u.description,inputSchema:u.inputSchema});return o}catch(a){return console.warn(`McpManager: HTTP probe failed for ${t.name}:`,a),[]}}async findServerToken(t){if(this.authManager){let o=this.authManager.getToken(t.name);if(o)return o}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 o of a){let l=process.env[o];if(l)return l}let n=process.env.HOME??"",i=[ze.join(n,".openclaw","workspace",".env"),ze.join(n,".env")];for(let o of i)try{let l=Se.readFileSync(o,"utf8");for(let c of l.split(`
|
|
11129
|
-
`)){let d=c.trim().match(/^(?:export\s+)?([A-Za-z_]\w*)=(.*)$/);if(d){let u=d[1],h=d[2].replace(/^["']|["']$/g,"");if(a.includes(u))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 u={hostname:l.hostname,port:l.port||(c?443:80),path:l.pathname+l.search,method:"POST",headers:d},m=(c?Ra:us).request(u,p=>{let b="";p.on("data",k=>{b+=k.toString()}),p.on("end",()=>{let k=p.headers["mcp-session-id"];if((p.headers["content-type"]??"").includes("text/event-stream")){for(let g of b.split(`
|
|
11130
|
-
`))if(g.startsWith("data: "))try{let y=JSON.parse(g.slice(6));k&&(y._sessionId=k),n(y);return}catch{}n(null)}else try{let g=JSON.parse(b);k&&(g._sessionId=k),n(g)}catch{n(null)}})});m.on("error",i);let f=setTimeout(()=>{m.destroy(),n(null)},15e3);m.on("close",()=>clearTimeout(f)),m.write(o),m.end()})}runCli(t){return new Promise((e,s)=>{let a=`${this.settings.claudeCliPath} ${t}`,n=(0,ds.spawn)("/bin/zsh",["-l","-c",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)})})}getDisabledServers(){let t=new Set,e=ze.join(process.env.HOME??"",".claude","settings.local.json");try{let a=Se.readFileSync(e,"utf8"),n=JSON.parse(a);for(let i of n.disabledMcpjsonServers??[])t.add(i)}catch{}let s=ze.join(process.env.HOME??"",".claude.json");try{let a=Se.readFileSync(s,"utf8"),i=JSON.parse(a).projects;if(i){for(let o of Object.values(i))if(o&&Array.isArray(o.disabledMcpServers))for(let l of o.disabledMcpServers)t.add(l)}}catch{}return t}enableServerInClaudeConfig(t){let e=ze.join(process.env.HOME??"",".claude.json");try{let s=Se.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&&Se.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of t.split(`
|
|
11131
|
-
`)){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(),u=l.slice(c+3).trim(),h="disconnected";u.includes("Connected")?h="connected":u.includes("authentication")?h="needs-auth":u.toLowerCase().includes("error")&&(h="error");let m="unknown",f,p;if(d.startsWith("http://")||d.startsWith("https://")){let v=d.replace(/\s+\(\w+\)\s*$/,"").trim();m=v.endsWith("/sse")?"sse":"http",f=v}else d&&(m="stdio",p=d);let b=this.toInternalName(o),k=!s.has(b);e.push({name:o,type:m,status:h,scope:"unknown",enabled:k,url:f,command:p,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of e.split(`
|
|
11132
|
-
`)){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 gi={"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 * *"},ps=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 gi[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 ae(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 ae(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 Ut=class{constructor(t,e){this.repository=t;this.settings=e;this.executor=new cs(e,t),this.mcpManager=new hs(e),this.scheduler=new ps(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 ae(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,h=>{let m=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,m+h);let f=this.runOutputListeners.get(s.name);if(f)for(let p of f)p(h)}),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:s.model,exitCode:n.exitCode,tags:Array.from(new Set([...s.tags,...t.tags])),prompt:n.prompt,output:n.outputText,toolsUsed:n.toolsUsed.map(h=>`${h.tool}${h.command?`: ${h.command}`:""}`),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 h=Da(n.outputText);try{await this.repository.appendMemory(s.name,h)}catch(m){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,m)}}if(t.tags.includes("heartbeat")&&!i&&s.heartbeatChannel&&c.output.trim())try{this.heartbeatResultHandler?.(s.name,s.heartbeatChannel,c.output)}catch(h){console.warn(`Agent Fleet: heartbeat channel delivery failed for ${s.name}`,h)}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={runId:(0,Ia.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:s.model,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:[]},c=await this.repository.writeRunLog(l);this.recentRuns=await this.repository.listRecentRuns(),this.runtimeState.set(s.name,{status:i?"idle":"error",lastRun:{...l,filePath:c}}),i||this.notify(l)}finally{this.runOutputBuffers.delete(s.name),this.runOutputListeners.delete(s.name),this.emitStatusChange()}}buildApprovals(t,e){let s=e.filter(a=>t.approvalRequired.includes(a.tool)).map(a=>({tool:a.tool,command:a.command,reason:a.reason,status:"pending"}));return s.length>0?s:void 0}resolveRunStatus(t,e){return e?.length?"pending_approval":t.timedOut?"timeout":t.exitCode===0?"success":"failure"}notify(t){if(this.settings.notificationLevel==="none"||this.settings.notificationLevel==="failures-only"&&t.status==="success")return;let s=(t.output.split(`
|
|
11133
|
-
`).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 La.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var ms=class{tokens=new Map;oauthTokens=new Map;storeProbeToken(t,e){this.tokens.set(t,e)}storeOAuthToken(t,e){this.oauthTokens.set(t,e),this.tokens.set(t,e.accessToken)}getToken(t){return this.tokens.get(t)}getOAuthToken(t){return this.oauthTokens.get(t)}hasToken(t){return this.tokens.has(t)}removeToken(t){this.tokens.delete(t),this.oauthTokens.delete(t)}getExpiringTokens(t=5*6e4){let e=new Map,s=Date.now();for(let[a,n]of this.oauthTokens)n.refreshToken&&n.expiresAt&&n.expiresAt-s<t&&e.set(a,n);return e}};var Ke=require("obsidian");var Fa=require("child_process"),ot=require("obsidian");var xt=class{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}messages=[];isStreaming=!1;isProcessAlive=!1;lastActiveAt=Date.now();process=null;claudeSessionId=null;vault;stdoutBuffer="";processListeners=null;basePromptSent=!1;channelName;conversationId;channelContext;activeOnEvent=null;turnResponseText="";turnToolCalls=[];pendingTurns=0;turnResolve=null;turnReject=null;async loadPersistedState(){let t=this.getChatFilePath(),e=this.vault.getAbstractFileByPath(t);if(!(e instanceof ot.TFile))return!1;try{let s=await this.vault.cachedRead(e),a=JSON.parse(s);if(a.messages?.length>0)return this.messages=a.messages,this.claudeSessionId=a.sessionId??null,this.claudeSessionId&&(this.basePromptSent=!0),!0}catch{}return!1}async persist(){let t=this.getChatFilePath(),e={sessionId:this.claudeSessionId,messages:this.messages,lastActive:new Date().toISOString()},s=JSON.stringify(e,null,2),a=this.vault.getAbstractFileByPath(t);if(a instanceof ot.TFile){await this.vault.modify(a,s);return}await this.ensureParentFolders(t),await this.vault.create(t,s)}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 ot.TFile&&await this.vault.delete(e),this.messages=[],this.claudeSessionId=null,this.basePromptSent=!1}getChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=ve(this.conversationId)||"conversation";return(0,ot.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,ot.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,ot.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=this.agent.model.trim();e&&e!=="default"&&e!=="subscription"&&t.push("--model",e);let s=this.agent.permissionMode?.trim();s&&s!=="default"?t.push("--permission-mode",s):t.push("--permission-mode","bypassPermissions");let a=[this.settings.claudeCliPath,...t].map(o=>`'${o.replace(/'/g,"'\\''")}'`).join(" "),n=this.agent.cwd??this.repository.getVaultBasePath()??".",i=(0,Fa.spawn)("/bin/zsh",["-l","-c",a],{cwd:n,env:{...process.env,AWS_REGION:this.settings.awsRegion}});this.process=i,this.isProcessAlive=!0,this.stdoutBuffer="",this.processListeners={onStdout:o=>this.handleStdout(o),onStderr:()=>{},onError:o=>this.handleProcessError(o),onClose:()=>this.handleProcessClose()},i.stdout.on("data",this.processListeners.onStdout),i.stderr.on("data",this.processListeners.onStderr),i.on("error",this.processListeners.onError),i.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.stdoutBuffer+=t.toString();let e=this.stdoutBuffer.split(`
|
|
11134
|
-
`);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),t.type==="result"){this.handleTurnEnd();return}let e=this.parseStreamEvent(t);e&&(e.type==="text"?this.turnResponseText+=e.content:e.type==="tool_use"&&e.toolName&&this.turnToolCalls.push({name:e.toolName,command:e.content||void 0}),this.activeOnEvent?.(e))}handleTurnEnd(){this.lastActiveAt=Date.now(),this.turnResponseText.trim()&&this.messages.push({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.isStreaming=!1,this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.isStreaming=!1,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[];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({role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.isStreaming=!1,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],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({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()}
|
|
11481
|
+
`)}catch{clearTimeout(f),u();return}}else if(b.id===2&&b.result){for(let g of b.result.tools??[])l.push({name:g.name,description:g.description,inputSchema:g.inputSchema});h=!0}}catch{}d&&h&&(clearTimeout(f),u())}}}),n.on("error",()=>{clearTimeout(f),u()}),n.on("close",()=>{clearTimeout(f),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"}}})+`
|
|
11482
|
+
`)}catch{clearTimeout(f),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=[bt.join(ia(),".openclaw","workspace",".env"),bt.join(ia(),".env")];for(let i of n)try{let o=De.readFileSync(i,"utf8");for(let l of ce(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},f=(c?gn:_s).request(h,m=>{let v="";m.on("data",k=>{v+=k.toString()}),m.on("end",()=>{let k=m.headers["mcp-session-id"];if((m.headers["content-type"]??"").includes("text/event-stream")){for(let g of ce(v))if(g.startsWith("data: "))try{let y=JSON.parse(g.slice(6));k&&(y._sessionId=k),n(y);return}catch{}n(null)}else try{let g=JSON.parse(v);k&&(g._sessionId=k),n(g)}catch{n(null)}})});f.on("error",i);let p=setTimeout(()=>{f.destroy(),n(null)},15e3);f.on("close",()=>clearTimeout(p)),f.write(o),f.end()})}runCli(t){return new Promise((e,s)=>{let a=`${this.settings.claudeCliPath} ${t}`,n=ra(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=at(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=bt.join(zt(),"settings.local.json");try{let s=De.readFileSync(e,"utf8"),a=JSON.parse(s);for(let n of a.disabledMcpjsonServers??[])t.add(n)}catch{}try{let s=De.readFileSync(Gt(),"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=Gt();try{let s=De.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&&De.writeFileSync(e,JSON.stringify(a,null,2))}catch{}}parseListOutput(t){let e=[],s=this.getDisabledServers();for(let a of ce(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 f="unknown",p,m;if(d.startsWith("http://")||d.startsWith("https://")){let b=d.replace(/\s+\(\w+\)\s*$/,"").trim();f=b.endsWith("/sse")?"sse":"http",p=b}else d&&(f="stdio",m=d);let v=this.toInternalName(o),k=!s.has(v);e.push({name:o,type:f,status:u,scope:"unknown",enabled:k,url:p,command:m,tools:[],toolDetails:[]})}return e}mergeGetOutput(t,e){let s={...t};for(let a of ce(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 hr={"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 * *"},As=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 hr[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 re(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 re(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 Qt=class{constructor(t,e){this.repository=t;this.settings=e;this.executor=new Cs(e,t),this.mcpManager=new Es(e),this.scheduler=new As(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 re(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 f=this.runOutputBuffers.get(s.name)??"";this.runOutputBuffers.set(s.name,f+u);let p=this.runOutputListeners.get(s.name);if(p)for(let m of p)m(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=mn(n.outputText);try{await this.repository.appendMemory(s.name,u)}catch(f){console.warn(`Agent Fleet: failed to append memory for "${s.name}"`,f)}}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=Dt(t,s,this.settings),c={runId:(0,yn.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=(ce(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 vn.Notice(a,t.status==="success"?5e3:0)}emitStatusChange(){for(let t of this.statusChangeListeners)t()}};var Ps=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 et=require("obsidian");var Rs=require("crypto"),We=require("obsidian");function Ds(){return(0,Rs.randomUUID)()}function bn(r){let t=`${r.timestamp}|${r.content.slice(0,80)}`;return(0,Rs.createHash)("sha1").update(t).digest("hex").slice(0,16)}var Rt=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;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 We.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:bn(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:bn(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 We.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 We.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=ge(this.conversationId)||"conversation";return(0,We.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,We.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,We.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,We.normalizePath)(`${s}/${a}.threads/${t}.json`)}getParentChatFilePath(){if(this.channelName&&this.conversationId){let e=this.settings.fleetFolder,s=ge(this.conversationId)||"conversation";return(0,We.normalizePath)(`${e}/channels/${this.channelName}/sessions/${s}.json`)}if(this.agent.isFolder){let e=this.agent.filePath.replace(/\/agent\.md$/,"");return(0,We.normalizePath)(`${e}/chat.json`)}let t=this.repository.getMemoryPath(this.agent.name).replace(/\/[^/]+$/,"");return(0,We.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=Dt(null,this.agent,this.settings);Ss(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=at(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.stdoutBuffer+=t.toString();let e=ce(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==="result"){this.handleTurnEnd();return}let e=this.parseStreamEvent(t);e&&(e.type==="text"?this.turnResponseText+=e.content:e.type==="tool_use"&&e.toolName&&this.turnToolCalls.push({name:e.toolName,command:e.content||void 0}),this.activeOnEvent?.(e))}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}e&&this.emitStats()}handleTurnEnd(){this.lastActiveAt=Date.now(),this.turnResponseText.trim()&&this.messages.push({id:Ds(),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.isStreaming=!1,this.persist();let e=this.turnResolve;this.turnResolve=null,this.turnReject=null,e?.(t)}}handleProcessError(t){this.isProcessAlive=!1,this.process=null,this.isStreaming=!1,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[];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:Ds(),role:"assistant",content:this.turnResponseText,timestamp:new Date().toISOString(),toolCalls:this.turnToolCalls.length>0?[...this.turnToolCalls]:void 0}),this.isStreaming=!1,this.pendingTurns=0,this.turnResponseText="",this.turnToolCalls=[],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:Ds(),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()}
|
|
11135
11483
|
|
|
11136
11484
|
## Task
|
|
11137
11485
|
${n}`,this.basePromptSent=!0),await this.ensureProcess(),this.activeOnEvent=e,this.isStreaming=!0,this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=1;let i=JSON.stringify({type:"user",message:{role:"user",content:n}});try{this.process.stdin.write(i+`
|
|
11138
|
-
`)}catch(o){throw this.isStreaming=!1,this.pendingTurns=0,new Error(`Failed to write to Claude process stdin: ${o instanceof Error?o.message:String(o)}`)}return new Promise((o,l)=>{this.turnResolve=o,this.turnReject=l})}injectMessage(t,e,s){if(!this.process||!this.isProcessAlive)return;this.messages.push({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+`
|
|
11139
|
-
`)}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.isStreaming=!1,this.stdoutBuffer="",this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=0;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
|
|
11140
|
-
${
|
|
11141
|
-
${
|
|
11142
|
-
${
|
|
11143
|
-
${
|
|
11486
|
+
`)}catch(o){throw this.isStreaming=!1,this.pendingTurns=0,new Error(`Failed to write to Claude process stdin: ${o instanceof Error?o.message:String(o)}`)}return new Promise((o,l)=>{this.turnResolve=o,this.turnReject=l})}injectMessage(t,e,s){if(!this.process||!this.isProcessAlive)return;this.messages.push({id:Ds(),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+`
|
|
11487
|
+
`)}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.isStreaming=!1,this.stdoutBuffer="",this.turnResponseText="",this.turnToolCalls=[],this.pendingTurns=0;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
|
|
11488
|
+
${a.toolsBody.trim()}`),a.referencesBody.trim()&&n.push(`### References
|
|
11489
|
+
${a.referencesBody.trim()}`),a.examplesBody.trim()&&n.push(`### Examples
|
|
11490
|
+
${a.examplesBody.trim()}`),t.push(`## Skill: ${a.name}
|
|
11491
|
+
${n.join(`
|
|
11144
11492
|
|
|
11145
11493
|
`)}`)}}if(this.agent.skillsBody.trim()&&t.push(`## Agent Skills
|
|
11146
11494
|
${this.agent.skillsBody.trim()}`),this.agent.contextBody.trim()&&t.push(`## Agent Context
|
|
11147
|
-
${this.agent.contextBody.trim()}`),this.agent.memory){let
|
|
11148
|
-
${
|
|
11149
|
-
${this.channelContext.trim()}`),t.
|
|
11495
|
+
${this.agent.contextBody.trim()}`),this.agent.memory){let s=await this.repository.getMemory(this.agent.name);s?.body.trim()&&t.push(`## Agent Memory
|
|
11496
|
+
${s.body.trim()}`)}this.channelContext&&this.channelContext.trim()&&t.push(`## Channel Context
|
|
11497
|
+
${this.channelContext.trim()}`);let e=Ts(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
|
|
11498
|
+
${s}
|
|
11499
|
+
|
|
11500
|
+
${n.join(`
|
|
11501
|
+
`)}`)}return t.filter(Boolean).join(`
|
|
11150
11502
|
|
|
11151
|
-
`)}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
|
|
11152
|
-
`&&(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
|
|
11503
|
+
`)}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 Is=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 Ls(r){let t=ur(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(pr(s.text));return e.join("")}function ur(r){let t=[],e=0,s=!1,a=0;for(;e<r.length;)r.startsWith("```",e)?s?(e+=3,r[e]===`
|
|
11504
|
+
`&&(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 pr(r){return fr(r).map(s=>{if(s.isCode)return s.text;let a=s.text;return a=a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),a=a.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(n,i,o)=>`<${o}|${i}>`),a=a.replace(/\*\*([^*]+)\*\*/g,"*$1*"),a=a.replace(/^#{1,6}\s+(.+)$/gm,"*$1*"),a}).join("")}function fr(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 wn(r,t=3e3){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.slice(0,t),n=mr(a)%2===1,i;if(n){let o=gr(a);if(o>0)i=o;else{e.push(a+"\n```"),s="```\n"+s.slice(t);continue}}else{if(i=a.lastIndexOf(`
|
|
11153
11505
|
|
|
11154
11506
|
`),i<t/2){let o=a.lastIndexOf(`
|
|
11155
|
-
`);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
|
|
11507
|
+
`);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 mr(r){let t=0,e=0;for(;(e=r.indexOf("```",e))!==-1;)t+=1,e+=3;return t}function gr(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 yr(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 Ms=class{constructor(t){this.deps=t;this.now=t.now??(()=>Date.now());let e=t.getSettings();this.rateLimiter=new Is({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=yr(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 f=vr(u.toolCalls);f&&(d+=`
|
|
11156
11508
|
|
|
11157
|
-
_${
|
|
11158
|
-
${d}`),await this.deliverReply(t,e.conversationId,d),n.messagesSent+=1)}catch(
|
|
11509
|
+
_${f}_`)}}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}]*
|
|
11510
|
+
${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=wn(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,et.normalizePath)(s))||await this.deps.vault.createFolder((0,et.normalizePath)(s));let i=(0,et.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,f=h>0?n.filename.slice(h):"";o=(0,et.normalizePath)(`${s}/${u}_${Date.now()}${f}`)}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}
|
|
11159
11511
|
The image file is located at: ${d}
|
|
11160
11512
|
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
|
|
11161
11513
|
|
|
@@ -11165,22 +11517,20 @@ ${a.join(`
|
|
|
11165
11517
|
|
|
11166
11518
|
---
|
|
11167
11519
|
|
|
11168
|
-
`}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 xt(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,Ke.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),i=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof Ke.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,Ke.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof Ke.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 Si(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 Ti(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function jt(r,t){return`${r}-${Ti(t)}`}var St="af-channel-cred",vs=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=jt(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=jt(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=jt(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=jt(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=jt(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(St);for(let s of e){let a=St+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson(St,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(St,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(St,t,{...e,_ref:t})}};var zr=Le(Rn(),1),Wr=Le(As(),1),Gr=Le(_t(),1),Vr=Le(sa(),1),Yr=Le(ia(),1),Kr=Le(ua(),1),jn=Le(Is(),1),Xr=Le(Un(),1);var pa=jn.default;var $n=require("obsidian");var Jr="https://slack.com/api";function Qr(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function Zr(r){let t=r.split(":");return t.length>=3&&t[0]==="slack"?t[2]??null:null}function eo(r){let t=r.split(":");if(t[3]==="thread"&&t[4])return t[4]}var Fs=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=Zr(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=eo(t),n=gs(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=gs(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 pa(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,u=n.id;if(!d||!u)return;let m=`slack:${t.team?.id??"unknown"}:${u}:user:${d}`;for(let f of this.agentSwitchHandlers)try{f(m,c,d)}catch(p){console.error("Agent Fleet: agent switch handler threw",p)}try{await this.slackApi("chat.postEphemeral",{channel:u,user:d,text:`Switched to *${c}*. Send your next message to start.`})}catch(f){console.warn("Agent Fleet: agent switch confirmation failed",f)}try{await this.setThreadTitle(m,c)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==pa.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=Qr(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 u=this.threadContext.keys().next();u.done||this.threadContext.delete(u.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(u){console.error("Agent Fleet: Slack inbound handler threw",u)}}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=`${Jr}/${t}`,i=await(0,$n.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 ma=require("obsidian"),Hn="https://api.telegram.org",Ms=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=qn(t);if(!s)return;let a=zn(t),n=Wn(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=qn(t);if(!s)return;let a=zn(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=Wn(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",u=await this.downloadFile(t.document.file_id,c,d);u&&o.push(u)}}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=`${Hn}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,ma.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=`${Hn}/bot${this.credential.botToken}/${t}`,n=(0,ma.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 qn(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function zn(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function Wn(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(`
|
|
11520
|
+
`}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 Rt(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,et.normalizePath)(`${a.fleetFolder}/channels/${t}/bindings.json`),i=JSON.stringify(s,null,2);try{let o=this.deps.vault.getAbstractFileByPath(n);if(o instanceof et.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,et.normalizePath)(`${e.fleetFolder}/channels/${t}/bindings.json`);try{let a=this.deps.vault.getAbstractFileByPath(s);if(!(a instanceof et.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 vr(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 br(r){return r.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-{2,}/g,"-").replace(/^-|-$/g,"")}function Zt(r,t){return`${r}-${br(t)}`}var It="af-channel-cred",Fs=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=Zt(t,e);this.storage.setSecret(a,JSON.stringify(s))}getJson(t,e){if(!this.storage)return null;let s=Zt(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=Zt(t,e);this.storage.setSecret(a,s)}getString(t,e){if(!this.storage)return null;let s=Zt(t,e);return this.storage.getSecret(s)||null}delete(t,e){if(!this.storage)return;let s=Zt(t,e);this.storage.setSecret(s,"")}listByPrefix(t){return this.storage?this.storage.listSecrets().filter(e=>e.startsWith(t+"-")):[]}};var Os=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(It);for(let s of e){let a=It+"-",n=s.startsWith(a)?s.slice(a.length):s,i=this.secretStore.getJson(It,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(It,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(It,t,{...e,_ref:t})}};var $o=Ne(mi(),1),jo=Ne(zs(),1),Ho=Ne(Ft(),1),qo=Ne(Pa(),1),Wo=Ne(Ia(),1),zo=Ne(Ba(),1),Ti=Ne(Ks(),1),Go=Ne(Si(),1);var $a=Ti.default;var Ci=require("obsidian");var Vo="https://slack.com/api";function Yo(r){let t=r.team??"unknown",e=r.channel??"unknown",s=r.thread_ts??r.ts??"unknown";return`slack:${t}:${e}:thread:${s}`}function Ko(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 Js=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=Ko(t);if(!s){console.warn(`Agent Fleet: could not extract channel id from ${t}`);return}let a=Xo(t),n=Ls(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=Ls(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 $a(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 f=`slack:${t.team?.id??"unknown"}:${h}:user:${d}`;for(let p of this.agentSwitchHandlers)try{p(f,c,d)}catch(m){console.error("Agent Fleet: agent switch handler threw",m)}try{await this.slackApi("chat.postEphemeral",{channel:h,user:d,text:`Switched to *${c}*. Send your next message to start.`})}catch(p){console.warn("Agent Fleet: agent switch confirmation failed",p)}try{await this.setThreadTitle(f,c)}catch{}}ackEnvelope(t){if(!(!this.ws||this.ws.readyState!==$a.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=Yo(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=`${Vo}/${t}`,i=await(0,Ci.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 ja=require("obsidian"),_i="https://api.telegram.org",Qs=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=Ei(t);if(!s)return;let a=Ai(t),n=Pi(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=Ei(t);if(!s)return;let a=Ai(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=Pi(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=`${_i}/file/bot${this.credential.botToken}/${n}`;return{data:(await(0,ja.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=`${_i}/bot${this.credential.botToken}/${t}`,n=(0,ja.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 Ei(r){return r.startsWith("tg:")?r.split(":")[1]??null:null}function Ai(r){let t=r.split(":");if(t[2]==="topic"&&t[3])return t[3]}function Pi(r,t){if(r.length<=t)return[r];let e=[],s=r;for(;s.length>t;){let a=s.lastIndexOf(`
|
|
11169
11521
|
|
|
11170
11522
|
`,t);a<t/2&&(a=s.lastIndexOf(`
|
|
11171
|
-
`,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 Yt=require("obsidian");var Os=class extends Yt.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return ut}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"}),u=d.createSpan({cls:"af-sidebar-nav-icon"});(0,Yt.setIcon)(u,c.icon),d.createSpan({cls:"af-sidebar-nav-label",text:c.label});let h=c.badge?.();h!==void 0&&h>0&&d.createSpan({cls:"af-sidebar-badge",text:String(h)}),d.setAttribute("role","button"),d.setAttribute("tabindex","0"),d.onclick=()=>void this.plugin.navigateDashboard(c.page),d.onkeydown=m=>{(m.key==="Enter"||m.key===" ")&&(m.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),u=o.createDiv({cls:"af-sidebar-agent-item"});u.createSpan({cls:`af-sidebar-agent-dot ${this.healthToClass(d.status)}`}),u.createSpan({cls:"af-sidebar-agent-name",text:c.name}),u.setAttribute("role","button"),u.setAttribute("tabindex","0"),u.onclick=()=>void this.plugin.navigateDashboard("agent-detail",c.name),u.onkeydown=h=>{(h.key==="Enter"||h.key===" ")&&(h.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,Yt.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 w=require("obsidian");var It=require("obsidian"),to=["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"],Bs=class extends It.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,It.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",to),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,It.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var Gn=require("obsidian");function _(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,Gn.setIcon)(s,t),s}function Oe(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 so(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function Vn(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,u=18,h=u+c+d,m=Math.max(1,...t.map(p=>p.success+p.failure+p.cancelled)),f=Oe("svg",{viewBox:`0 0 ${s} ${h}`,width:"100%",height:String(h),class:"af-chart-bar"});for(let p=0;p<=4;p++){let b=u+c-p/4*c;f.appendChild(Oe("line",{x1:String(n),y1:String(b),x2:String(s-i),y2:String(b),stroke:"var(--af-text-faint)","stroke-opacity":"0.15","stroke-width":"1"}))}for(let p=0;p<t.length;p++){let b=t[p],k=n+p*(l+a),v=b.success+b.failure+b.cancelled,g=v/m*c,y=b.success/m*c,x=b.cancelled/m*c,T=b.failure/m*c;if(b.success>0&&f.appendChild(Oe("rect",{x:String(k),y:String(u+c-y),width:String(l),height:String(Math.max(y,2)),fill:"var(--af-green)",opacity:"0.85"})),b.cancelled>0&&f.appendChild(Oe("rect",{x:String(k),y:String(u+c-y-x),width:String(l),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),b.failure>0&&f.appendChild(Oe("rect",{x:String(k),y:String(u+c-g),width:String(l),height:String(Math.max(T,2)),fill:"var(--af-red)",opacity:"0.85"})),v===0&&f.appendChild(Oe("rect",{x:String(k),y:String(u+c-3),width:String(l),height:"3",rx:"1.5",fill:"var(--af-text-faint)",opacity:"0.2"})),v>0){let A=Oe("text",{x:String(k+l/2),y:String(u+c-g-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});A.textContent=String(v),f.appendChild(A)}let C=Oe("text",{x:String(k+l/2),y:String(u+c+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});C.textContent=so(b.date),f.appendChild(C)}r.appendChild(f)}function Yn(r,t,e){let l=2*Math.PI*46,c=e>0?t/e:0,d=l*c,u=l-d,h=Oe("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});h.appendChild(Oe("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&&h.appendChild(Oe("circle",{cx:String(65),cy:String(65),r:String(46),fill:"none",stroke:"var(--af-green)","stroke-width":String(12),"stroke-dasharray":`${d} ${u}`,"stroke-dashoffset":String(l*.25),"stroke-linecap":"round"}));let m=Oe("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});m.textContent=`${Math.round(c*100)}%`,h.appendChild(m);let f=Oe("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});f.textContent=`${t}/${e} runs`,h.appendChild(f),r.appendChild(h)}function Kn(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 Xn(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 Jn={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"},ao={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"},no=["dashboard","agents","kanban","runs","approvals","skills","mcp","channels"],Qn={"claude-code":[{value:"default",label:"Default (subscription model)"},{value:"claude-sonnet-4-6",label:"Sonnet 4.6 (Anthropic)"},{value:"claude-opus-4-6",label:"Opus 4.6 (Anthropic)"},{value:"claude-sonnet-4-5-20250929",label:"Sonnet 4.5 (Anthropic)"},{value:"claude-opus-4-5-20251101",label:"Opus 4.5 (Anthropic)"},{value:"claude-haiku-4-5-20251001",label:"Haiku 4.5 (Anthropic)"},{value:"us.anthropic.claude-sonnet-4-6",label:"Sonnet 4.6 (Bedrock)"},{value:"us.anthropic.claude-opus-4-6-v1",label:"Opus 4.6 (Bedrock)"},{value:"us.anthropic.claude-sonnet-4-5-20250929-v1:0",label:"Sonnet 4.5 (Bedrock)"},{value:"us.anthropic.claude-opus-4-5-20251101-v1:0",label:"Opus 4.5 (Bedrock)"},{value:"us.anthropic.claude-sonnet-4-20250514-v1:0",label:"Sonnet 4 (Bedrock)"},{value:"us.anthropic.claude-opus-4-1-20250805-v1:0",label:"Opus 4.1 (Bedrock)"},{value:"us.anthropic.claude-haiku-4-5-20251001-v1:0",label:"Haiku 4.5 (Bedrock)"},{value:"us.anthropic.claude-3-5-haiku-20241022-v1:0",label:"Haiku 3.5 (Bedrock)"}],codex:[{value:"default",label:"Default (config.toml)"},{value:"gpt-5.4",label:"GPT-5.4"},{value:"o3",label:"o3"},{value:"o4-mini",label:"o4-mini"}],process:[{value:"default",label:"Default"}],http:[{value:"default",label:"Default"}]},Kt=class extends w.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return rt}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"});_(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),i=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(i,"chevron-right");let o=(f,p,b)=>{let k=n.createSpan({cls:p?"af-breadcrumb-link":"",text:f});p&&(k.onclick=()=>this.navigate(p,b))},l=()=>{let f=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(f,"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(Jn[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let c=s.createDiv({cls:"af-search-wrap"});_(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 u=this.plugin.runtime.getFleetStatus(),h=s.createDiv({cls:"af-status-pills"});if(u.running>0){let f=h.createSpan({cls:"af-pill yellow"});f.createSpan({cls:"af-dot pulse"}),f.appendText(` ${u.running} Running`)}if(u.pending>0){let f=h.createSpan({cls:"af-pill blue"});f.createSpan({cls:"af-dot"}),f.appendText(` ${u.pending} Pending`)}let m=h.createSpan({cls:"af-pill green"});m.createSpan({cls:"af-dot"}),m.appendText(` ${u.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 no){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,w.setIcon)(c,ao[i]),l.appendText(i==="dashboard"?"Overview":Jn[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(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 l=s.createDiv({cls:"af-dash-grid"}),c=a.agents.filter(y=>y.enabled).length,d=a.agents.length;this.renderStatCard(l,"Active Agents",`${c}`,`/ ${d}`,"bot",`${c} of ${d} enabled`);let u=this.toLocalDateStr(new Date),h=n.filter(y=>this.runToLocalDate(y.started)===u),m=h.filter(y=>y.status==="success").length,f=h.filter(y=>y.status==="failure"||y.status==="timeout").length;this.renderStatCard(l,"Runs Today",String(h.length),"","activity",`${m} passed \xB7 ${f} failed \xB7 ${i.running} running`);let p=h.reduce((y,x)=>y+(x.tokensUsed??0),0),b=h.reduce((y,x)=>y+(x.costUsd??0),0),k=b>0?` \xB7 $${b.toFixed(2)}`:"";this.renderStatCard(l,"Tokens Used",fa(p),"","zap",`today${k}`);let v=a.tasks.filter(y=>y.enabled&&y.schedule);this.renderStatCard(l,"Scheduled Tasks",String(v.length),"","clock",v.length>0?`Next: ${this.getNextTaskLabel(v)}`:"No scheduled tasks"),this.renderChartsRow(s,n),this.renderStreamingCards(s);let g=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(g,n),this.renderFleetStatusPanel(g,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"});_(o,"activity"),o.appendText(" Run Activity (14d)");let l=n.createDiv({cls:"af-chart-body"}),c=this.buildChartData(s,14);c.some(b=>b.success+b.failure+b.cancelled>0)?Vn(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"}),h=d.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(h,"target"),h.appendText(" Success Rate");let m=d.createDiv({cls:"af-chart-body af-chart-body-center"}),f=s.length,p=s.filter(b=>b.status==="success").length;f>0?Yn(m,p,f):this.renderEmptyState(m,"target","No data","")}toLocalDateStr(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}`}runToLocalDate(e){return this.toLocalDateStr(new Date(e))}buildChartData(e,s){let a=[],n=new Date;for(let i=s-1;i>=0;i--){let o=new Date(n);o.setDate(o.getDate()-i);let 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"});_(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(f=>f.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).split(`
|
|
11172
|
-
`).slice(-4);d.setText(
|
|
11173
|
-
`));let m=this.plugin.runtime.onRunOutput(s,()=>{let p=this.plugin.runtime.getRunOutputBuffer(s).split(`
|
|
11174
|
-
`).
|
|
11175
|
-
`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(m)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),i=n.createDiv({cls:"af-approval-icon"});(0,w.setIcon)(i,"shield-check");let o=n.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${a}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${a}`});let 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"});_(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"});_(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,w.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:kt(s.output,100)});let c=o.createDiv({cls:"af-tl-meta"}),d=c.createSpan();if(_(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let u=c.createSpan();_(u,"zap","af-meta-icon"),u.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"});_(i,"bot"),i.appendText(" Fleet Status");let l=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});_(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 u of s.agents)this.renderAgentMini(c,u,s.tasks);let d=s.agents.filter(u=>u.enabled);if(d.length>0){let u=a.createDiv({cls:"af-quick-run"}),h=u.createDiv({cls:"af-quick-run-label"});_(h,"zap","af-meta-icon"),h.appendText(" Quick Run");let m=u.createDiv({cls:"af-quick-run-row"}),f=m.createEl("select",{cls:"af-select"});for(let b of d)f.createEl("option",{text:b.name,attr:{value:b.name}});let p=m.createEl("button",{cls:"af-btn-sm primary"});_(p,"play","af-btn-icon"),p.appendText(" Run"),p.onclick=()=>void this.plugin.runAgentPrompt(f.value)}}renderAgentMini(e,s,a){let n=this.plugin.runtime.getAgentState(s.name),i=a.filter(h=>h.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 u="";if(n.status==="running")u=`Running now \xB7 ${i.length} task${i.length!==1?"s":""}`;else if(!s.enabled)u=`Disabled \xB7 ${i.length} task${i.length!==1?"s":""} paused`;else{let h=i.map(m=>m.nextRun).filter(Boolean).sort()[0];u=h?`Next: ${this.formatNextRun(h)} \xB7 ${i.length} task${i.length!==1?"s":""}`:`${i.length} task${i.length!==1?"s":""}`}d.createDiv({cls:"af-agent-desc",text:u}),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"});_(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",u=c.createDiv({cls:`af-agent-card-avatar ${d}`});this.renderAgentAvatar(u,s);let h=c.createDiv({cls:"af-agent-card-titleblock"}),m=h.createDiv({cls:"af-agent-card-name"});if(m.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let E=m.createSpan({cls:"af-heartbeat-indicator"});(0,w.setIcon)(E,"heart-pulse"),E.title=`Heartbeat: ${s.heartbeatSchedule}`}h.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let f=c.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});f.onclick=E=>{E.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let p=l.createDiv({cls:"af-agent-card-stats"}),b=i.length,k=i.filter(E=>E.status==="success").length,v=b>0?Math.round(k/b*100):0,g=b>0?Math.round(i.reduce((E,R)=>E+R.durationSeconds,0)/b):0,y=i.reduce((E,R)=>E+(R.tokensUsed??0),0);if(this.renderAgentStat(p,String(b),"Runs"),this.renderAgentStat(p,`${v}%`,"Success"),this.renderAgentStat(p,`${g}s`,"Avg Time"),this.renderAgentStat(p,fa(y),"Tokens"),s.skills.length>0){let E=l.createDiv({cls:"af-agent-card-skills"});for(let R of s.skills)E.createSpan({cls:"af-skill-tag",text:R})}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=R=>{R.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let A=C.createEl("button",{cls:"af-btn-sm"});if(_(A,"edit","af-btn-icon"),A.appendText(" Edit"),A.onclick=E=>{E.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let E=C.createEl("button",{cls:"af-btn-sm primary"});_(E,"play","af-btn-icon"),E.appendText(" Run"),E.onclick=R=>{R.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(g=>g.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(g=>g.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 u=c.createDiv();u.createDiv({cls:"af-detail-header-name",text:n.name}),u.createDiv({cls:"af-detail-header-desc",text:n.description??"No description"});let h=l.createDiv({cls:"af-detail-header-actions"}),m=h.createEl("button",{cls:"af-btn-sm primary"});if(_(m,"message-circle","af-btn-icon"),m.appendText(" Chat"),m.onclick=()=>this.openChatSlideover(n),n.enabled){let g=h.createEl("button",{cls:"af-btn-sm"});_(g,"play","af-btn-icon"),g.appendText(" Run Now"),g.onclick=()=>void this.plugin.runAgentPrompt(n.name);let y=h.createEl("button",{cls:"af-btn-sm"});_(y,"pause","af-btn-icon"),y.appendText(" Disable"),y.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let g=h.createEl("button",{cls:"af-btn-sm"});_(g,"play","af-btn-icon"),g.appendText(" Enable"),g.onclick=()=>void this.plugin.toggleAgent(n.name,!0)}let f=h.createEl("button",{cls:"af-btn-sm"});_(f,"edit","af-btn-icon"),f.appendText(" Edit"),f.onclick=()=>this.navigate("edit-agent",n.name);let p=h.createEl("button",{cls:"af-btn-sm danger"});_(p,"trash-2","af-btn-icon"),p.appendText(" Delete"),p.onclick=()=>void this.plugin.deleteAgent(n.name);let b=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 g of k){let y=b.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===g.id?" active":""}`});_(y,g.icon,"af-tab-icon"),y.appendText(` ${g.label}`),y.onclick=()=>{this.agentDetailTab=g.id,this.render()}}let v=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(v,n,o);break;case"config":this.renderAgentConfigTab(v,n);break;case"runs":this.renderAgentRunsTab(v,o);break;case"memory":this.renderAgentMemoryTab(v,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(g=>g.status==="success").length,l=i>0?Math.round(o/i*100):0,c=i>0?Math.round(a.reduce((g,y)=>g+y.durationSeconds,0)/i):0,d=a.reduce((g,y)=>g+(y.tokensUsed??0),0),u=a.reduce((g,y)=>g+(y.costUsd??0),0),h=u>0?` \xB7 $${u.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",fa(d),"","zap",`all time${h}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let g=e.createDiv({cls:"af-section-card"}),y=g.createDiv({cls:"af-section-header"}),x=y.createDiv({cls:"af-section-title"});_(x,"heart-pulse"),x.appendText(" Heartbeat");let C=y.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});C.onclick=async()=>{let R=C.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!R}),await this.plugin.refreshFromVault(),new w.Notice(`Heartbeat ${R?"paused":"enabled"} for ${s.name}`)};let A=g.createDiv({cls:"af-config-form"});this.renderConfigRow(A,"Schedule",io(s.heartbeatSchedule));let E=this.plugin.runtime.getNextHeartbeat(s.name);E&&s.heartbeatEnabled&&this.renderConfigRow(A,"Next run",this.timeUntil(E)),s.heartbeatChannel&&this.renderConfigRow(A,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"puzzle"),x.appendText(" Skills");let T=g.createDiv({cls:"af-detail-skills-list"});for(let C of s.skills)T.createSpan({cls:"af-skill-tag",text:C})}let m=s.mcpServers??[];if(m.length>0){let g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"plug"),x.appendText(" MCP Servers");let T=g.createDiv({cls:"af-mcp-overview-list"}),C=this.plugin.mcpManager.getCachedServers();for(let A of m){let E=C?.find(K=>K.name===A),R=T.createDiv({cls:"af-mcp-overview-row"}),U=R.createSpan({cls:`af-mcp-status-dot ${E?E.enabled?E.status:"disabled":"disconnected"}`});U.title=E?E.enabled?E.status:"disabled":"unknown",R.createSpan({cls:"af-mcp-overview-name",text:A});let D=E?.toolDetails.length??E?.tools.length??0;D>0?R.createSpan({cls:"af-mcp-overview-tools",text:`${D} tools`}):E&&!E.enabled?R.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):E?.status==="needs-auth"&&R.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 g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"shield-check"),x.appendText(" Permissions");let T=g.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 p=e.createDiv({cls:"af-section-card"}),k=p.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(k,"scroll-text"),k.appendText(" Recent Runs");let v=p.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(v,"scroll-text","No runs yet","");else for(let g of a.slice(0,5))this.renderTimelineItem(v,g)}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,"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"});_(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let a of s){let n=e.createDiv({cls:"af-run-list-item"}),i=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,w.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),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:kt(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"});_(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"});_(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=[],u=[],h=[],m=[],f=new Set;for(let v of a.agents){let g=this.plugin.runtime.getAgentState(v.name);g.status==="running"&&g.currentTaskId&&f.add(g.currentTaskId)}let p=this.toLocalDateStr(new Date);for(let v of n)v.status==="success"&&this.runToLocalDate(v.started)===p?h.push(v):(v.status==="failure"||v.status==="timeout"||v.status==="cancelled")&&this.runToLocalDate(v.started)===p&&m.push(v);let b=new Set(h.map(v=>v.task)),k=new Set(m.map(v=>v.task));for(let v of a.tasks){let g=b.has(v.taskId)||k.has(v.taskId)||v.lastRun&&this.runToLocalDate(v.lastRun)===p;if(f.has(v.taskId))u.push(v);else{if(g&&!v.schedule)continue;v.schedule&&v.enabled?d.push(v):c.push(v)}}this.renderKanbanColumn(l,"Backlog","inbox",c.length,v=>{for(let g of c)this.renderKanbanTaskCard(v,g,a,!0)},"backlog"),this.renderKanbanColumn(l,"Scheduled","clock",d.length,v=>{for(let g of d)this.renderKanbanTaskCard(v,g,a,!0)},"scheduled"),this.renderKanbanColumn(l,"Running","loader-2",u.length,v=>{for(let g of u)this.renderKanbanRunningCard(v,g)},"running",!1,"running"),this.renderKanbanColumn(l,"Done","check-circle-2",h.length,v=>{for(let g of h.slice(0,10))this.renderKanbanCompletedCard(v,g)},"completed"),this.renderKanbanColumn(l,"Failed","x-circle",m.length,v=>{for(let g of m)this.renderKanbanFailedCard(v,g)},"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")&&Xn(d,f=>this.handleTaskDrop(f,o));let h=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});_(h,a),h.appendText(` ${s} `),h.createSpan({cls:"af-kanban-col-count",text:String(n)});let m=d.createDiv({cls:"af-kanban-col-body"});if(i(m),n===0&&m.createDiv({cls:"af-kanban-empty",text:"No items"}),l){let p=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");_(p,"plus","af-btn-icon"),p.appendText(" Add task"),p.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let a=this.plugin.runtime.getSnapshot().tasks.find(n=>n.taskId===e);if(a){if(s==="backlog")this.setTaskEnabled(a,!1).then(()=>{new w.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new w.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new w.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof w.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=se(n);i.enabled=s,await this.plugin.app.vault.modify(a,ee(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){Kn(i,s.taskId);let p=i.createDiv({cls:"af-kanban-card-grip"});(0,w.setIcon)(p,"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(p=>p.name===s.agent)?.enabled??!1)&&s.enabled,d=o.createSpan({cls:`af-kanban-card-status ${c?"active":"inactive"}`});d.title=c?"Active":"Inactive";let u=i.createDiv({cls:"af-kanban-card-agent"}),h=u.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(h,"bot"),u.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let f=i.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});c?s.schedule?(_(f,"refresh-cw","af-meta-icon"),f.appendText(` ${this.humanizeCron(s.schedule)}`)):f.appendText(s.runAt??"Manual"):(_(f,"pause","af-meta-icon"),f.appendText(" Paused")),i.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let a=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});a.createDiv({cls:"af-kanban-card-title",text:s.taskId});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let 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(),m=a.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),f=(Date.now()-d)/1e3,p=Math.min(95,f/l*100);m.style.width=`${p}%`;let b=a.createDiv({cls:"af-kanban-card-footer"}),k=b.createSpan({cls:"af-kanban-card-schedule"});_(k,"loader-2","af-meta-icon");let v=Math.round(f);k.appendText(` ${v}s / ${l}s`);let g=b.createEl("button",{cls:"af-kanban-stop-btn"});(0,w.setIcon)(g,"square"),g.title="Stop task",g.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new w.Notice(`Stopped task "${s.taskId}"`)};let y=setInterval(()=>{let x=(Date.now()-d)/1e3,T=Math.min(95,x/l*100);m.style.width=`${T}%`;let C=Math.round(x);k.textContent="",(0,w.setIcon)(k,"loader-2"),k.appendText(` ${C}s / ${l}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"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=a.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});_(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,w.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`:kt(s.output,60),c=n.createDiv({cls:"af-kanban-card-error"});_(c,a?"square":"alert-triangle","af-meta-icon"),c.appendText(` ${l}`);let d=n.createDiv({cls:"af-kanban-card-footer"}),u=d.createSpan({cls:"af-kanban-card-schedule"});if(_(u,a?"square":"x-circle","af-meta-icon"),u.appendText(` ${this.formatStarted(s.started)}`),!a){let h=d.createEl("button",{cls:"af-btn-sm"});_(h,"refresh-cw","af-btn-icon"),h.appendText(" Retry"),h.onclick=m=>{m.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 u of["Status","Agent","Task","Started","Duration","Tokens","Model"])c.createEl("th",{text:u});let d=o.createEl("tbody");for(let u of a.slice(0,50))this.renderRunRow(d,u)}renderRunRow(e,s){let a=e.createEl("tr"),i=a.createEl("td").createSpan({cls:`af-status-badge ${this.statusToBadgeClass(s.status)}`}),o=i.createSpan();(0,w.setIcon)(o,this.statusToIconName(s.status)),i.appendText(` ${this.statusToBadgeText(s.status)}`);let 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"});_(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,w.setIcon)(o,this.getSkillIcon(s.name));let l=i.createEl("button",{cls:"af-btn-sm af-btn-xs"});_(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 u of c)d.createSpan({cls:"af-skill-card-agent-tag",text:u.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"});_(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=Zn(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,w.setIcon)(d,"radio");let u=c.createDiv({cls:"af-agent-card-titleblock"});u.createDiv({cls:"af-agent-card-name",text:s.name}),u.createDiv({cls:"af-agent-card-desc",text:`Default: ${s.defaultAgent}`});let h=c.createSpan({cls:`af-pill ${ro(n)}`});if(h.createSpan({cls:"af-dot"}),h.appendText(` ${n}`),s.allowedAgents.length>0){let T=l.createDiv({cls:"af-agent-card-skills"});for(let C of s.allowedAgents){let A=T.createSpan({cls:"af-skill-tag",text:C});C===s.defaultAgent&&(A.style.fontWeight="700")}}let m=l.createDiv({cls:"af-agent-card-stats"}),f=this.plugin.channelManager?.getSessionCount(s.name)??0,p=this.plugin.channelManager?.getMetrics(s.name),b=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(m,b,"Agents"),this.renderAgentStat(m,String(f),"Sessions"),this.renderAgentStat(m,String(p?.messagesReceived??0),"In"),this.renderAgentStat(m,String(p?.messagesSent??0),"Out");let k=l.createDiv({cls:"af-agent-card-footer"}),v=[s.type];s.enabled||v.push("disabled"),s.allowedUsers.length>0?v.push(`${s.allowedUsers.length} user(s)`):v.push("allowlist empty"),k.createSpan({cls:"af-agent-card-meta",text:v.join(" \xB7 ")});let y=k.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});_(y,"edit","af-btn-icon"),y.appendText(" Edit"),y.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,w.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},u=s.createDiv({cls:"af-create-form"}),h=u.createDiv({cls:"af-create-section"}),m=h.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"radio"),m.createSpan({text:"Channel Details"}),this.createFormField(h,"Name","my-slack","Unique identifier for this channel",J=>{d.name=J});let p=h.createDiv({cls:"af-form-row"});p.createDiv({cls:"af-form-label",text:"Type"});let b=p.createEl("select",{cls:"af-form-select"});b.createEl("option",{text:"slack",attr:{value:"slack"}}),b.createEl("option",{text:"telegram",attr:{value:"telegram"}}),b.addEventListener("change",()=>{d.type=b.value});let k=h.createDiv({cls:"af-form-row"}),v=k.createDiv({cls:"af-form-label"});v.setText("Credential"),this.addTooltip(v,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let g=k.createEl("select",{cls:"af-form-select"});n.length===0&&g.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let J of n)g.createEl("option",{text:`${J.ref} (${J.entry.type})`,attr:{value:J.ref}});g.addEventListener("change",()=>{d.credentialRef=g.value});let y=h.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 T=u.createDiv({cls:"af-create-section"}),C=T.createDiv({cls:"af-create-section-header"}),A=C.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"bot"),C.createSpan({text:"Agent Routing"});let E=T.createDiv({cls:"af-form-row"}),R=E.createDiv({cls:"af-form-label"});R.setText("Default agent"),this.addTooltip(R,"Used when no @agent-name prefix is given in a message");let U=E.createEl("select",{cls:"af-form-select"});for(let J of a.agents)U.createEl("option",{text:J.name,attr:{value:J.name}});U.addEventListener("change",()=>{d.defaultAgent=U.value});let D=T.createDiv({cls:"af-form-row"}),K=D.createDiv({cls:"af-form-label"});K.setText("Allowed agents"),this.addTooltip(K,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let q=D.createDiv({cls:"af-form-checkboxes"});for(let J of a.agents){let Ce=q.createEl("label",{cls:"af-form-checkbox-label"}),_e=Ce.createEl("input",{attr:{type:"checkbox",value:J.name}});Ce.appendText(` ${J.name}`),_e.addEventListener("change",()=>{_e.checked?d.allowedAgents.includes(J.name)||d.allowedAgents.push(J.name):d.allowedAgents=d.allowedAgents.filter(he=>he!==J.name)})}let V=T.createDiv({cls:"af-form-row af-form-row-toggle"}),j=V.createDiv({cls:"af-form-label"});j.setText("Per-user sessions"),this.addTooltip(j,"Each external user gets their own isolated Claude session");let $=V.createDiv({cls:"af-agent-card-toggle on"});$.onclick=()=>{let J=$.hasClass("on");$.toggleClass("on",!J),d.perUserSessions=!J};let W=u.createDiv({cls:"af-create-section"}),X=W.createDiv({cls:"af-create-section-header"}),H=X.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(H,"shield-check"),X.createSpan({text:"Access Control"});let P=W.createDiv({cls:"af-form-label"});P.setText("Allowed users"),this.addTooltip(P,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let M=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
11176
|
-
U0BXYZ12345`,rows:"4"}});
|
|
11177
|
-
`),
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
---
|
|
11182
|
-
*Output truncated (${(e.output.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:e.output;w.MarkdownRenderer.render(this.app,v,b,"",this.plugin)}if(e.toolsUsed.length>0){let p=o.createDiv({cls:"af-slideover-section"});p.createDiv({cls:"af-slideover-section-title",text:"TOOLS USED"}),p.createDiv({cls:"af-output-block",text:e.toolsUsed.join(`
|
|
11183
|
-
`)})}let m=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let p=m.createEl("button",{cls:"af-btn-sm"});_(p,"external-link","af-btn-icon"),p.appendText(" Open Run Note"),p.onclick=()=>void this.plugin.openPath(e.filePath)}let f=m.createEl("button",{cls:"af-btn-sm primary"});_(f,"refresh-cw","af-btn-icon"),f.appendText(" Re-run Task"),f.onclick=()=>void this.plugin.runAgentPrompt(e.agent),s.onclick=p=>{p.target===s&&s.remove()}}renderDetailRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderEmptyState(e,s,a,n){let i=e.createDiv({cls:"af-empty-state"}),o=i.createDiv({cls:"af-empty-icon"});(0,w.setIcon)(o,s),i.createDiv({cls:"af-empty-label",text:a}),n&&i.createDiv({cls:"af-empty-sublabel",text:n})}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}statusToTimelineClass(e){switch(e){case"success":return"success";case"failure":case"timeout":return"error";case"cancelled":return"warning";case"pending_approval":return"pending";default:return"running"}}statusToIconName(e){switch(e){case"success":return"check-circle-2";case"failure":return"x-circle";case"timeout":return"clock";case"pending_approval":return"shield-check";case"cancelled":return"square";default:return"loader-2"}}statusToBadgeClass(e){switch(e){case"success":return"success";case"failure":return"failure";case"timeout":return"timeout";case"pending_approval":return"pending";case"cancelled":return"cancelled";default:return"running"}}statusToBadgeText(e){switch(e){case"success":return"Success";case"failure":return"Failed";case"timeout":return"Timeout";case"pending_approval":return"Pending";case"cancelled":return"Cancelled";case"interrupted":return"Interrupted";default:return e}}formatDuration(e){if(e<60)return`${e}s`;let s=Math.floor(e/60),a=e%60;return a>0?`${s}m ${a}s`:`${s}m`}formatStarted(e){try{let s=new Date(e),a=new Date;if(s.toDateString()===a.toDateString())return s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});let n=new Date(a);return n.setDate(n.getDate()-1),s.toDateString()===n.toDateString()?`Yesterday ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:s.toLocaleDateString([],{month:"short",day:"numeric"})+` ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`}catch{return e}}formatNextRun(e){try{let s=new Date(e),a=new Date,n=s.getTime()-a.getTime();if(n<0)return"overdue";let i=Math.round(n/6e4);if(i<60)return`${i}m`;let o=Math.round(i/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let a=e.map(i=>i.nextRun).filter(Boolean).sort()[0];return a?`${e.find(i=>i.nextRun===a)?.agent??"unknown"} in ${this.formatNextRun(a)}`:"none"}getInitials(e){return e.split("-").map(s=>s[0]?.toUpperCase()??"").slice(0,2).join("")}renderAgentAvatar(e,s){let a=s.avatar?.trim();if(!a){(0,w.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,w.setIcon)(e,a):e.setText(a)}getSkillIcon(e){return e.includes("git")?"settings":e.includes("summarize")||e.includes("log")?"activity":e.includes("review")||e.includes("check")?"check-circle-2":e.includes("vault")||e.includes("note")?"file-text":"puzzle"}renderInlineSchedule(e,s){let a=this.parseCronComponents(s.schedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["daily","Daily"],["weekdays","Weekdays"],["weekly","Weekly"],["monthly","Monthly"]];for(let[y,x]of o){let T=i.createEl("option",{text:x,attr:{value:y}});y===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 y=0;y<24;y++){let x=y>=12?"PM":"AM",T=y===0?12:y>12?y-12:y,C=d.createEl("option",{text:`${T} ${x}`,attr:{value:String(y)}});y===a.hour&&(C.selected=!0)}c.createSpan({cls:"af-schedule-colon",text:":"});let u=c.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=0;y<60;y+=5){let x=u.createEl("option",{text:String(y).padStart(2,"0"),attr:{value:String(y)}});y===a.minute&&(x.selected=!0)}let h=e.createDiv({cls:"af-form-row af-schedule-day-row"});h.createDiv({cls:"af-form-label",text:"Day"});let m=h.createDiv({cls:"af-schedule-day-buttons"}),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],p=new Set(a.days);for(let y=0;y<7;y++){let x=m.createEl("button",{cls:`af-schedule-day-btn${p.has(y)?" active":""}`,text:f[y]});x.onclick=()=>{p.has(y)?p.delete(y):p.add(y),x.toggleClass("active",p.has(y)),g()}}let b=e.createDiv({cls:"af-form-row af-schedule-dom-row"});b.createDiv({cls:"af-form-label",text:"Day of month"});let k=b.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let y=1;y<=28;y++){let x=k.createEl("option",{text:String(y),attr:{value:String(y)}});y===a.dayOfMonth&&(x.selected=!0)}let v=()=>{let y=i.value,x=["daily","weekdays","weekly","monthly"].includes(y),T=y==="weekly",C=y==="monthly";l.style.display=x?"":"none",h.style.display=T?"":"none",b.style.display=C?"":"none"},g=()=>{let y=i.value,x=d.value,T=u.value,C="";switch(y){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 A=Array.from(p).sort().join(",")||"1";C=`${T} ${x} * * ${A}`;break}case"monthly":C=`${T} ${x} ${k.value} * *`;break}s.schedule=C,s.type="recurring"};i.addEventListener("change",()=>{v(),g()}),d.addEventListener("change",g),u.addEventListener("change",g),k.addEventListener("change",g),v()}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[b,k]of o){let v=i.createEl("option",{text:k,attr:{value:b}});b===l&&(v.selected=!0)}let d=e.createDiv({cls:"af-form-row af-schedule-time-row"});d.createDiv({cls:"af-form-label",text:"Time"});let u=d.createDiv({cls:"af-schedule-time-selects"}),h=u.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let b=0;b<24;b++){let k=b>=12?"PM":"AM",v=b===0?12:b>12?b-12:b,g=h.createEl("option",{text:`${v} ${k}`,attr:{value:String(b)}});b===a.hour&&(g.selected=!0)}u.createSpan({cls:"af-schedule-colon",text:":"});let m=u.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let b=0;b<60;b+=5){let k=m.createEl("option",{text:String(b).padStart(2,"0"),attr:{value:String(b)}});b===a.minute&&(k.selected=!0)}let f=()=>{d.style.display=i.value==="daily"?"":"none"},p=()=>{let b=i.value,k=h.value,v=m.value;switch(b){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=`${v} ${k} * * *`;break}};i.addEventListener("change",()=>{f(),p()}),h.addEventListener("change",p),m.addEventListener("change",p),f()}parseCronComponents(e){let s={freq:"daily",hour:9,minute:0,days:[1],dayOfMonth:1};if(!e?.trim())return s;let a={"*/5 * * * *":"every_5m","*/15 * * * *":"every_15m","*/30 * * * *":"every_30m","0 * * * *":"every_hour","0 */2 * * *":"every_2h"};if(a[e])return{...s,freq:a[e]};let n=e.trim().split(/\s+/);if(n.length!==5)return s;let[i,o,l,,c]=n,d=parseInt(o??"9",10),u=parseInt(i??"0",10);if(l==="*"&&c==="*")return{...s,freq:"daily",hour:d,minute:u};if(l==="*"&&c==="1-5")return{...s,freq:"weekdays",hour:d,minute:u};if(l==="*"&&c!=="*"){let h=(c??"1").split(",").map(m=>parseInt(m,10));return{...s,freq:"weekly",hour:d,minute:u,days:h}}return c==="*"&&l!=="*"?{...s,freq:"monthly",hour:d,minute:u,dayOfMonth:parseInt(l??"1",10)}:{...s,hour:d,minute:u}}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=(m,f)=>{let p=parseInt(m??"0",10),b=parseInt(f??"0",10),k=p>=12?"PM":"AM",v=p===0?12:p>12?p-12:p;return b===0?`${v} ${k}`:`${v}:${String(b).padStart(2,"0")} ${k}`},u=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],h=m=>m==="1-5"?"weekdays":m==="0,6"?"weekends":m.split(",").map(p=>parseInt(p,10)).map(p=>u[p]??p).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!=="*"?`${h(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,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Agent"}),o.createDiv({cls:"af-detail-header-desc",text:"Configure a new agent for your fleet"});let l=a.createDiv({cls:"af-detail-header-actions"}),c={name:"",description:"",avatar:"",tags:"",systemPrompt:"",model:"default",adapter:"claude-code",cwd:"",timeout:300,permissionMode:"bypassPermissions",selectedSkills:new Set,selectedMcpServers:new Set,skillsBody:"",contextBody:"",approvalRequired:"",memory:!0,enabled:!0,allowedCommands:"",blockedCommands:"",heartbeatEnabled:!1,heartbeatSchedule:"0 */6 * * *",heartbeatBody:"",heartbeatNotify:!0,heartbeatChannel:""},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features.
|
|
11523
|
+
`,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 os=require("obsidian");var Zs=class extends os.ItemView{constructor(e,s){super(e);this.plugin=s}getViewType(){return mt}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,os.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=f=>{(f.key==="Enter"||f.key===" ")&&(f.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,os.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 w=require("obsidian");var jt=require("obsidian"),Jo=["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"],ea=class extends jt.Modal{constructor(e,s,a){super(e);this.onSelect=a;this.selectedIcon=s}searchQuery="";selectedIcon;allIcons=[];gridContainer;onOpen(){this.allIcons=(0,jt.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",Jo),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,jt.setIcon)(a,s),a.addEventListener("click",()=>{this.selectedIcon=s,this.onSelect(s),this.close()})}onClose(){this.contentEl.empty()}};var Di=require("obsidian");function _(r,t,e){let s=r.createSpan({cls:e??"af-icon"});return(0,Di.setIcon)(s,t),s}function je(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 Qo(r){try{return new Date(r+"T12:00:00").toLocaleDateString(void 0,{month:"short",day:"numeric"})}catch{return r.slice(5)}}function Ri(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,f=Math.max(1,...t.map(m=>m.success+m.failure+m.cancelled)),p=je("svg",{viewBox:`0 0 ${s} ${u}`,width:"100%",height:String(u),class:"af-chart-bar"});for(let m=0;m<=4;m++){let v=h+c-m/4*c;p.appendChild(je("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 m=0;m<t.length;m++){let v=t[m],k=n+m*(l+a),b=v.success+v.failure+v.cancelled,g=b/f*c,y=v.success/f*c,x=v.cancelled/f*c,S=v.failure/f*c;if(v.success>0&&p.appendChild(je("rect",{x:String(k),y:String(h+c-y),width:String(l),height:String(Math.max(y,2)),fill:"var(--af-green)",opacity:"0.85"})),v.cancelled>0&&p.appendChild(je("rect",{x:String(k),y:String(h+c-y-x),width:String(l),height:String(Math.max(x,2)),fill:"var(--af-yellow)",opacity:"0.85"})),v.failure>0&&p.appendChild(je("rect",{x:String(k),y:String(h+c-g),width:String(l),height:String(Math.max(S,2)),fill:"var(--af-red)",opacity:"0.85"})),b===0&&p.appendChild(je("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"})),b>0){let P=je("text",{x:String(k+l/2),y:String(h+c-g-5),"text-anchor":"middle","font-size":"10","font-weight":"600",fill:"var(--af-text-secondary)"});P.textContent=String(b),p.appendChild(P)}let T=je("text",{x:String(k+l/2),y:String(h+c+16),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});T.textContent=Qo(v.date),p.appendChild(T)}r.appendChild(p)}function Ii(r,t,e){let l=2*Math.PI*46,c=e>0?t/e:0,d=l*c,h=l-d,u=je("svg",{viewBox:"0 0 130 130",width:String(130),height:String(130),class:"af-chart-donut"});u.appendChild(je("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(je("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 f=je("text",{x:String(65),y:String(61),"text-anchor":"middle","dominant-baseline":"middle","font-size":"24","font-weight":"700",fill:"var(--af-text-primary)"});f.textContent=`${Math.round(c*100)}%`,u.appendChild(f);let p=je("text",{x:String(65),y:String(83),"text-anchor":"middle","font-size":"10",fill:"var(--af-text-muted)"});p.textContent=`${t}/${e} runs`,u.appendChild(p),r.appendChild(u)}function Li(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 Mi(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 Fi={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"},Zo={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"},el=["dashboard","agents","kanban","runs","approvals","skills","mcp","channels"],ls=class extends w.ItemView{constructor(e,s){super(e);this.plugin=s}currentPage="dashboard";detailContext;agentDetailTab="overview";streamingUnsubscribes=[];channelStatusUnsubscribe;authenticatingServers=new Set;getViewType(){return ht}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"});_(a,"bot","af-top-bar-icon"),a.createSpan({text:"Agent Fleet"});let n=s.createDiv({cls:"af-breadcrumb"}),i=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(i,"chevron-right");let o=(p,m,v)=>{let k=n.createSpan({cls:m?"af-breadcrumb-link":"",text:p});m&&(k.onclick=()=>this.navigate(m,v))},l=()=>{let p=n.createSpan({cls:"af-breadcrumb-sep"});(0,w.setIcon)(p,"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(Fi[this.currentPage])}s.createDiv({cls:"af-top-bar-spacer"});let c=s.createDiv({cls:"af-search-wrap"});_(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 p=u.createSpan({cls:"af-pill yellow"});p.createSpan({cls:"af-dot pulse"}),p.appendText(` ${h.running} Running`)}if(h.pending>0){let p=u.createSpan({cls:"af-pill blue"});p.createSpan({cls:"af-dot"}),p.appendText(` ${h.pending} Pending`)}let f=u.createSpan({cls:"af-pill green"});f.createSpan({cls:"af-dot"}),f.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 el){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,w.setIcon)(c,Zo[i]),l.appendText(i==="dashboard"?"Overview":Fi[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(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 l=s.createDiv({cls:"af-dash-grid"}),c=a.agents.filter(y=>y.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(y=>this.runToLocalDate(y.started)===h),f=u.filter(y=>y.status==="success").length,p=u.filter(y=>y.status==="failure"||y.status==="timeout").length;this.renderStatCard(l,"Runs Today",String(u.length),"","activity",`${f} passed \xB7 ${p} failed \xB7 ${i.running} running`);let m=u.reduce((y,x)=>y+(x.tokensUsed??0),0),v=u.reduce((y,x)=>y+(x.costUsd??0),0),k=v>0?` \xB7 $${v.toFixed(2)}`:"";this.renderStatCard(l,"Tokens Used",Ha(m),"","zap",`today${k}`);let b=a.tasks.filter(y=>y.enabled&&y.schedule);this.renderStatCard(l,"Scheduled Tasks",String(b.length),"","clock",b.length>0?`Next: ${this.getNextTaskLabel(b)}`:"No scheduled tasks"),this.renderChartsRow(s,n),this.renderStreamingCards(s);let g=s.createDiv({cls:"af-dash-split"});this.renderActivityTimeline(g,n),this.renderFleetStatusPanel(g,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"});_(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)?Ri(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"});_(u,"target"),u.appendText(" Success Rate");let f=d.createDiv({cls:"af-chart-body af-chart-body-center"}),p=s.length,m=s.filter(v=>v.status==="success").length;p>0?Ii(f,m,p):this.renderEmptyState(f,"target","No data","")}toLocalDateStr(e){let s=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${s(e.getMonth()+1)}-${s(e.getDate())}`}runToLocalDate(e){return this.toLocalDateStr(new Date(e))}buildChartData(e,s){let a=[],n=new Date;for(let i=s-1;i>=0;i--){let o=new Date(n);o.setDate(o.getDate()-i);let 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"});_(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(p=>p.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=ce(h).slice(-4);d.setText(u.join(`
|
|
11524
|
+
`));let f=this.plugin.runtime.onRunOutput(s,()=>{let p=this.plugin.runtime.getRunOutputBuffer(s),m=ce(p).slice(-4);d.setText(m.join(`
|
|
11525
|
+
`)),d.scrollTop=d.scrollHeight});this.streamingUnsubscribes.push(f)}renderApprovalBanner(e,s,a){let n=e.createDiv({cls:"af-approval-banner"}),i=n.createDiv({cls:"af-approval-icon"});(0,w.setIcon)(i,"shield-check");let o=n.createDiv({cls:"af-approval-text"});o.createDiv({cls:"af-approval-title",text:`${s.agent} wants to run: ${a}`}),o.createDiv({cls:"af-approval-desc",text:`Approval required for tool: ${a}`});let 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"});_(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"});_(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,w.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:Et(s.output,100)});let c=o.createDiv({cls:"af-tl-meta"}),d=c.createSpan();if(_(d,"clock","af-meta-icon"),d.appendText(` ${this.formatStarted(s.started)} \xB7 ${this.formatDuration(s.durationSeconds)}`),s.tokensUsed){let h=c.createSpan();_(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"});_(i,"bot"),i.appendText(" Fleet Status");let l=n.createDiv({cls:"af-section-actions"}).createEl("button",{cls:"af-btn-sm primary"});_(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"});_(u,"zap","af-meta-icon"),u.appendText(" Quick Run");let f=h.createDiv({cls:"af-quick-run-row"}),p=f.createEl("select",{cls:"af-select"});for(let v of d)p.createEl("option",{text:v.name,attr:{value:v.name}});let m=f.createEl("button",{cls:"af-btn-sm primary"});_(m,"play","af-btn-icon"),m.appendText(" Run"),m.onclick=()=>void this.plugin.runAgentPrompt(p.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(f=>f.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"});_(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"}),f=u.createDiv({cls:"af-agent-card-name"});if(f.appendText(s.name),s.heartbeatEnabled&&s.heartbeatSchedule){let E=f.createSpan({cls:"af-heartbeat-indicator"});(0,w.setIcon)(E,"heart-pulse"),E.title=`Heartbeat: ${s.heartbeatSchedule}`}u.createDiv({cls:"af-agent-card-desc",text:s.description??"No description"});let p=c.createDiv({cls:`af-agent-card-toggle${s.enabled?" on":""}`});p.onclick=E=>{E.stopPropagation(),this.plugin.toggleAgent(s.name,!s.enabled)};let m=l.createDiv({cls:"af-agent-card-stats"}),v=i.length,k=i.filter(E=>E.status==="success").length,b=v>0?Math.round(k/v*100):0,g=v>0?Math.round(i.reduce((E,R)=>E+R.durationSeconds,0)/v):0,y=i.reduce((E,R)=>E+(R.tokensUsed??0),0);if(this.renderAgentStat(m,String(v),"Runs"),this.renderAgentStat(m,`${b}%`,"Success"),this.renderAgentStat(m,`${g}s`,"Avg Time"),this.renderAgentStat(m,Ha(y),"Tokens"),s.skills.length>0){let E=l.createDiv({cls:"af-agent-card-skills"});for(let R of s.skills)E.createSpan({cls:"af-skill-tag",text:R})}let x=l.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 T=x.createDiv({cls:"af-agent-card-actions"});if(!s.enabled){let E=T.createEl("button",{cls:"af-btn-sm",text:"Enable"});E.onclick=R=>{R.stopPropagation(),this.plugin.toggleAgent(s.name,!0)}}let P=T.createEl("button",{cls:"af-btn-sm"});if(_(P,"edit","af-btn-icon"),P.appendText(" Edit"),P.onclick=E=>{E.stopPropagation(),this.navigate("edit-agent",s.name)},s.enabled){let E=T.createEl("button",{cls:"af-btn-sm primary"});_(E,"play","af-btn-icon"),E.appendText(" Run"),E.onclick=R=>{R.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(g=>g.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(g=>g.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"}),f=u.createEl("button",{cls:"af-btn-sm primary"});if(_(f,"message-circle","af-btn-icon"),f.appendText(" Chat"),f.onclick=()=>this.openChatSlideover(n),n.enabled){let g=u.createEl("button",{cls:"af-btn-sm"});_(g,"play","af-btn-icon"),g.appendText(" Run Now"),g.onclick=()=>void this.plugin.runAgentPrompt(n.name);let y=u.createEl("button",{cls:"af-btn-sm"});_(y,"pause","af-btn-icon"),y.appendText(" Disable"),y.onclick=()=>void this.plugin.toggleAgent(n.name,!1)}else{let g=u.createEl("button",{cls:"af-btn-sm"});_(g,"play","af-btn-icon"),g.appendText(" Enable"),g.onclick=()=>void this.plugin.toggleAgent(n.name,!0)}let p=u.createEl("button",{cls:"af-btn-sm"});_(p,"edit","af-btn-icon"),p.appendText(" Edit"),p.onclick=()=>this.navigate("edit-agent",n.name);let m=u.createEl("button",{cls:"af-btn-sm danger"});_(m,"trash-2","af-btn-icon"),m.appendText(" Delete"),m.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 g of k){let y=v.createEl("button",{cls:`af-detail-tab${this.agentDetailTab===g.id?" active":""}`});_(y,g.icon,"af-tab-icon"),y.appendText(` ${g.label}`),y.onclick=()=>{this.agentDetailTab=g.id,this.render()}}let b=s.createDiv({cls:"af-detail-tab-content"});switch(this.agentDetailTab){case"overview":this.renderAgentOverviewTab(b,n,o);break;case"config":this.renderAgentConfigTab(b,n);break;case"runs":this.renderAgentRunsTab(b,o);break;case"memory":this.renderAgentMemoryTab(b,n);break}}renderAgentOverviewTab(e,s,a){let n=e.createDiv({cls:"af-dash-grid"}),i=a.length,o=a.filter(g=>g.status==="success").length,l=i>0?Math.round(o/i*100):0,c=i>0?Math.round(a.reduce((g,y)=>g+y.durationSeconds,0)/i):0,d=a.reduce((g,y)=>g+(y.tokensUsed??0),0),h=a.reduce((g,y)=>g+(y.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",Ha(d),"","zap",`all time${u}`),s.isFolder&&(s.heartbeatBody.trim()||s.heartbeatEnabled)){let g=e.createDiv({cls:"af-section-card"}),y=g.createDiv({cls:"af-section-header"}),x=y.createDiv({cls:"af-section-title"});_(x,"heart-pulse"),x.appendText(" Heartbeat");let T=y.createDiv({cls:"af-detail-header-actions"}).createDiv({cls:`af-agent-card-toggle${s.heartbeatEnabled?" on":""}`});T.onclick=async()=>{let R=T.hasClass("on");await this.plugin.repository.updateHeartbeat(s.name,{enabled:!R}),await this.plugin.refreshFromVault(),new w.Notice(`Heartbeat ${R?"paused":"enabled"} for ${s.name}`)};let P=g.createDiv({cls:"af-config-form"});this.renderConfigRow(P,"Schedule",tl(s.heartbeatSchedule));let E=this.plugin.runtime.getNextHeartbeat(s.name);E&&s.heartbeatEnabled&&this.renderConfigRow(P,"Next run",this.timeUntil(E)),s.heartbeatChannel&&this.renderConfigRow(P,"Channel",s.heartbeatChannel)}if(s.skills.length>0){let g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"puzzle"),x.appendText(" Skills");let S=g.createDiv({cls:"af-detail-skills-list"});for(let T of s.skills)S.createSpan({cls:"af-skill-tag",text:T})}let f=s.mcpServers??[];if(f.length>0){let g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"plug"),x.appendText(" MCP Servers");let S=g.createDiv({cls:"af-mcp-overview-list"}),T=this.plugin.mcpManager.getCachedServers();for(let P of f){let E=T?.find(q=>q.name===P),R=S.createDiv({cls:"af-mcp-overview-row"}),W=R.createSpan({cls:`af-mcp-status-dot ${E?E.enabled?E.status:"disabled":"disconnected"}`});W.title=E?E.enabled?E.status:"disabled":"unknown",R.createSpan({cls:"af-mcp-overview-name",text:P});let I=E?.toolDetails.length??E?.tools.length??0;I>0?R.createSpan({cls:"af-mcp-overview-tools",text:`${I} tools`}):E&&!E.enabled?R.createSpan({cls:"af-mcp-overview-tools af-muted",text:"disabled"}):E?.status==="needs-auth"&&R.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 g=e.createDiv({cls:"af-section-card"}),x=g.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(x,"shield-check"),x.appendText(" Permissions");let S=g.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 m=e.createDiv({cls:"af-section-card"}),k=m.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(k,"scroll-text"),k.appendText(" Recent Runs");let b=m.createDiv({cls:"af-timeline"});if(a.length===0)this.renderEmptyState(b,"scroll-text","No runs yet","");else for(let g of a.slice(0,5))this.renderTimelineItem(b,g)}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,"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"});_(o,"edit","af-btn-icon"),o.appendText(" Edit Agent"),o.onclick=()=>this.navigate("edit-agent",s.name)}renderConfigRow(e,s,a){let n=e.createDiv({cls:"af-detail-row"});n.createSpan({cls:"af-detail-label",text:s}),n.createSpan({cls:"af-detail-value af-mono",text:a})}renderAgentRunsTab(e,s){if(s.length===0){this.renderEmptyState(e,"scroll-text","No runs yet","Run this agent to see history");return}for(let a of s){let n=e.createDiv({cls:"af-run-list-item"}),i=n.createDiv({cls:`af-tl-icon ${this.statusToTimelineClass(a.status)}`});(0,w.setIcon)(i,this.statusToIconName(a.status));let o=n.createDiv({cls:"af-tl-body"}),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:Et(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"});_(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"});_(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=[],f=[],p=new Set;for(let b of a.agents){let g=this.plugin.runtime.getAgentState(b.name);g.status==="running"&&g.currentTaskId&&p.add(g.currentTaskId)}let m=this.toLocalDateStr(new Date);for(let b of n)b.status==="success"&&this.runToLocalDate(b.started)===m?u.push(b):(b.status==="failure"||b.status==="timeout"||b.status==="cancelled")&&this.runToLocalDate(b.started)===m&&f.push(b);let v=new Set(u.map(b=>b.task)),k=new Set(f.map(b=>b.task));for(let b of a.tasks){let g=v.has(b.taskId)||k.has(b.taskId)||b.lastRun&&this.runToLocalDate(b.lastRun)===m;if(p.has(b.taskId))h.push(b);else{if(g&&!b.schedule)continue;b.schedule&&b.enabled?d.push(b):c.push(b)}}this.renderKanbanColumn(l,"Backlog","inbox",c.length,b=>{for(let g of c)this.renderKanbanTaskCard(b,g,a,!0)},"backlog"),this.renderKanbanColumn(l,"Scheduled","clock",d.length,b=>{for(let g of d)this.renderKanbanTaskCard(b,g,a,!0)},"scheduled"),this.renderKanbanColumn(l,"Running","loader-2",h.length,b=>{for(let g of h)this.renderKanbanRunningCard(b,g)},"running",!1,"running"),this.renderKanbanColumn(l,"Done","check-circle-2",u.length,b=>{for(let g of u.slice(0,10))this.renderKanbanCompletedCard(b,g)},"completed"),this.renderKanbanColumn(l,"Failed","x-circle",f.length,b=>{for(let g of f)this.renderKanbanFailedCard(b,g)},"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")&&Mi(d,p=>this.handleTaskDrop(p,o));let u=d.createDiv({cls:"af-kanban-col-header"}).createDiv({cls:"af-kanban-col-title"});_(u,a),u.appendText(` ${s} `),u.createSpan({cls:"af-kanban-col-count",text:String(n)});let f=d.createDiv({cls:"af-kanban-col-body"});if(i(f),n===0&&f.createDiv({cls:"af-kanban-empty",text:"No items"}),l){let m=d.createDiv({cls:"af-kanban-col-add"}).createEl("button");_(m,"plus","af-btn-icon"),m.appendText(" Add task"),m.onclick=()=>{this.navigate("create-task")}}}handleTaskDrop(e,s){let a=this.plugin.runtime.getSnapshot().tasks.find(n=>n.taskId===e);if(a){if(s==="backlog")this.setTaskEnabled(a,!1).then(()=>{new w.Notice(`Task "${e}" moved to backlog (disabled)`)});else if(s==="scheduled"){if(!a.schedule&&!a.runAt){new w.Notice(`Task "${e}" needs a schedule. Open task details to set one.`),this.navigate("task-detail",e);return}this.setTaskEnabled(a,!0).then(()=>{new w.Notice(`Task "${e}" moved to scheduled (enabled)`)})}}}async setTaskEnabled(e,s){let a=this.plugin.app.vault.getAbstractFileByPath(e.filePath);if(!a||!(a instanceof w.TFile))return;let n=await this.plugin.app.vault.cachedRead(a),{frontmatter:i,body:o}=J(n);i.enabled=s,await this.plugin.app.vault.modify(a,Y(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){Li(i,s.taskId);let m=i.createDiv({cls:"af-kanban-card-grip"});(0,w.setIcon)(m,"grip-vertical")}let o=i.createDiv({cls:"af-kanban-card-header"});o.createDiv({cls:"af-kanban-card-title",text:s.taskId});let c=(a.agents.find(m=>m.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,w.setIcon)(u,"bot"),h.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let p=i.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});c?s.schedule?(_(p,"refresh-cw","af-meta-icon"),p.appendText(` ${this.humanizeCron(s.schedule)}`)):p.appendText(s.runAt??"Manual"):(_(p,"pause","af-meta-icon"),p.appendText(" Paused")),i.onclick=()=>this.navigate("task-detail",s.taskId)}renderKanbanRunningCard(e,s){let a=e.createDiv({cls:"af-kanban-card af-kanban-card-running"});a.createDiv({cls:"af-kanban-card-title",text:s.taskId});let n=a.createDiv({cls:"af-kanban-card-agent"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let 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(),f=a.createDiv({cls:"af-kanban-progress"}).createDiv({cls:"af-kanban-progress-track"}).createDiv({cls:"af-kanban-progress-bar af-kanban-progress-bar-real"}),p=(Date.now()-d)/1e3,m=Math.min(95,p/l*100);f.style.width=`${m}%`;let v=a.createDiv({cls:"af-kanban-card-footer"}),k=v.createSpan({cls:"af-kanban-card-schedule"});_(k,"loader-2","af-meta-icon");let b=Math.round(p);k.appendText(` ${b}s / ${l}s`);let g=v.createEl("button",{cls:"af-kanban-stop-btn"});(0,w.setIcon)(g,"square"),g.title="Stop task",g.onclick=x=>{x.stopPropagation(),this.plugin.runtime.abortAgentRun(s.agent),new w.Notice(`Stopped task "${s.taskId}"`)};let y=setInterval(()=>{let x=(Date.now()-d)/1e3,S=Math.min(95,x/l*100);f.style.width=`${S}%`;let T=Math.round(x);k.textContent="",(0,w.setIcon)(k,"loader-2"),k.appendText(` ${T}s / ${l}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"}),i=n.createSpan({cls:"af-kanban-card-agent-icon"});(0,w.setIcon)(i,"bot"),n.createSpan({cls:"af-kanban-card-agent-name",text:s.agent});let l=a.createDiv({cls:"af-kanban-card-footer"}).createSpan({cls:"af-kanban-card-schedule"});_(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,w.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`:Et(s.output,60),c=n.createDiv({cls:"af-kanban-card-error"});_(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(_(h,a?"square":"x-circle","af-meta-icon"),h.appendText(` ${this.formatStarted(s.started)}`),!a){let u=d.createEl("button",{cls:"af-btn-sm"});_(u,"refresh-cw","af-btn-icon"),u.appendText(" Retry"),u.onclick=f=>{f.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,w.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"});_(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,w.setIcon)(o,this.getSkillIcon(s.name));let l=i.createEl("button",{cls:"af-btn-sm af-btn-xs"});_(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"});_(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=Oi(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,w.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 ${sl(n)}`});if(u.createSpan({cls:"af-dot"}),u.appendText(` ${n}`),s.allowedAgents.length>0){let S=l.createDiv({cls:"af-agent-card-skills"});for(let T of s.allowedAgents){let P=S.createSpan({cls:"af-skill-tag",text:T});T===s.defaultAgent&&(P.style.fontWeight="700")}}let f=l.createDiv({cls:"af-agent-card-stats"}),p=this.plugin.channelManager?.getSessionCount(s.name)??0,m=this.plugin.channelManager?.getMetrics(s.name),v=s.allowedAgents.length>0?String(s.allowedAgents.length):"all";this.renderAgentStat(f,v,"Agents"),this.renderAgentStat(f,String(p),"Sessions"),this.renderAgentStat(f,String(m?.messagesReceived??0),"In"),this.renderAgentStat(f,String(m?.messagesSent??0),"Out");let k=l.createDiv({cls:"af-agent-card-footer"}),b=[s.type];s.enabled||b.push("disabled"),s.allowedUsers.length>0?b.push(`${s.allowedUsers.length} user(s)`):b.push("allowlist empty"),k.createSpan({cls:"af-agent-card-meta",text:b.join(" \xB7 ")});let y=k.createDiv({cls:"af-agent-card-actions"}).createEl("button",{cls:"af-btn-sm"});_(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=l.createDiv({cls:"af-channel-issues"});for(let T of x)S.createDiv({cls:"af-channel-issue-row",text:T.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,w.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},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),f=u.createDiv({cls:"af-create-section-header"}),p=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"radio"),f.createSpan({text:"Channel Details"}),this.createFormField(u,"Name","my-slack","Unique identifier for this channel",D=>{d.name=D});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Type"});let v=m.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"}),b=k.createDiv({cls:"af-form-label"});b.setText("Credential"),this.addTooltip(b,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let g=k.createEl("select",{cls:"af-form-select"});n.length===0&&g.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let D of n)g.createEl("option",{text:`${D.ref} (${D.entry.type})`,attr:{value:D.ref}});g.addEventListener("change",()=>{d.credentialRef=g.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 D=x.hasClass("on");x.toggleClass("on",!D),d.enabled=!D};let S=h.createDiv({cls:"af-create-section"}),T=S.createDiv({cls:"af-create-section-header"}),P=T.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(P,"bot"),T.createSpan({text:"Agent Routing"});let E=S.createDiv({cls:"af-form-row"}),R=E.createDiv({cls:"af-form-label"});R.setText("Default agent"),this.addTooltip(R,"Used when no @agent-name prefix is given in a message");let W=E.createEl("select",{cls:"af-form-select"});for(let D of a.agents)W.createEl("option",{text:D.name,attr:{value:D.name}});W.addEventListener("change",()=>{d.defaultAgent=W.value});let I=S.createDiv({cls:"af-form-row"}),q=I.createDiv({cls:"af-form-label"});q.setText("Allowed agents"),this.addTooltip(q,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let L=I.createDiv({cls:"af-form-checkboxes"});for(let D of a.agents){let Z=L.createEl("label",{cls:"af-form-checkbox-label"}),le=Z.createEl("input",{attr:{type:"checkbox",value:D.name}});Z.appendText(` ${D.name}`),le.addEventListener("change",()=>{le.checked?d.allowedAgents.includes(D.name)||d.allowedAgents.push(D.name):d.allowedAgents=d.allowedAgents.filter(ae=>ae!==D.name)})}let K=S.createDiv({cls:"af-form-row af-form-row-toggle"}),V=K.createDiv({cls:"af-form-label"});V.setText("Per-user sessions"),this.addTooltip(V,"Each external user gets their own isolated Claude session");let z=K.createDiv({cls:"af-agent-card-toggle on"});z.onclick=()=>{let D=z.hasClass("on");z.toggleClass("on",!D),d.perUserSessions=!D};let ie=h.createDiv({cls:"af-create-section"}),Q=ie.createDiv({cls:"af-create-section-header"}),te=Q.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(te,"shield-check"),Q.createSpan({text:"Access Control"});let G=ie.createDiv({cls:"af-form-label"});G.setText("Allowed users"),this.addTooltip(G,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let B=ie.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
11526
|
+
U0BXYZ12345`,rows:"4"}});B.addEventListener("input",()=>{d.allowedUsers=B.value});let F=h.createDiv({cls:"af-create-section"}),$=F.createDiv({cls:"af-create-section-header"}),ee=$.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ee,"message-square");let se=$.createSpan({text:"Channel Context"});this.addTooltip(se,"Extra instructions appended to the agent's system prompt when reached through this channel");let oe=F.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});oe.addEventListener("input",()=>{d.channelContext=oe.value});let Ce=s.createDiv({cls:"af-create-footer"}),Ae=Ce.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Ae.onclick=()=>this.navigate("channels");let Te=Ce.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(Te,"plus","af-btn-icon"),Te.appendText(" Create Channel"),Te.onclick=async()=>{let D=d.name.trim();if(!D){new w.Notice("Channel name is required.");return}if(!d.credentialRef){new w.Notice("Select a credential.");return}let Z=ae=>ae.split(/[\n,]+/).map(ke=>ke.trim()).filter(Boolean),le={name:ge(D),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:Z(d.allowedUsers),per_user_sessions:d.perUserSessions,channel_context:d.channelContext.trim()||void 0};try{let ae=ge(D),ke=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("channels"),ae);await this.plugin.app.vault.create(ke,Y(le,"")),new w.Notice(`Channel "${ae}" created.`),await this.plugin.refreshFromVault(),this.navigate("edit-channel",ae)}catch(ae){let ke=ae instanceof Error?ae.message:String(ae);new w.Notice(`Failed to create channel: ${ke}`)}}}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(j=>j.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 ${Oi(l)}`});(0,w.setIcon)(h,"radio");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:`Edit Channel: ${n.name}`}),u.createDiv({cls:"af-detail-header-desc",text:`Status: ${l}`}),c.createDiv({cls:"af-detail-header-actions"});let f={type:n.type,defaultAgent:n.defaultAgent,allowedAgents:[...n.allowedAgents],credentialRef:n.credentialRef,allowedUsers:n.allowedUsers.join(`
|
|
11527
|
+
`),perUserSessions:n.perUserSessions,channelContext:n.channelContext,enabled:n.enabled},p=s.createDiv({cls:"af-create-form"}),m=p.createDiv({cls:"af-create-section"}),v=m.createDiv({cls:"af-create-section-header"}),k=v.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"radio"),v.createSpan({text:"Channel Details"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Name"});let g=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});g.style.opacity="0.6";let y=m.createDiv({cls:"af-form-row"});y.createDiv({cls:"af-form-label",text:"Type"});let x=y.createEl("select",{cls:"af-form-select"});for(let j of["slack","telegram"]){let N=x.createEl("option",{text:j,attr:{value:j}});j===n.type&&(N.selected=!0)}x.addEventListener("change",()=>{f.type=x.value});let S=m.createDiv({cls:"af-form-row"}),T=S.createDiv({cls:"af-form-label"});T.setText("Credential"),this.addTooltip(T,"Configured in Settings \u2192 Agent Fleet \u2192 Channel Credentials");let P=S.createEl("select",{cls:"af-form-select"});o.length===0&&P.createEl("option",{text:"(no credentials configured)",attr:{value:""}});for(let j of o){let N=P.createEl("option",{text:`${j.ref} (${j.entry.type})`,attr:{value:j.ref}});j.ref===n.credentialRef&&(N.selected=!0)}P.addEventListener("change",()=>{f.credentialRef=P.value});let E=m.createDiv({cls:"af-form-row af-form-row-toggle"});E.createDiv({cls:"af-form-label",text:"Enabled"});let R=E.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});R.onclick=()=>{let j=R.hasClass("on");R.toggleClass("on",!j),f.enabled=!j};let W=p.createDiv({cls:"af-create-section"}),I=W.createDiv({cls:"af-create-section-header"}),q=I.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(q,"bot"),I.createSpan({text:"Agent Routing"});let L=W.createDiv({cls:"af-form-row"}),K=L.createDiv({cls:"af-form-label"});K.setText("Default agent"),this.addTooltip(K,"Used when no @agent-name prefix is given in a message");let V=L.createEl("select",{cls:"af-form-select"});for(let j of i.agents){let N=V.createEl("option",{text:j.name,attr:{value:j.name}});j.name===n.defaultAgent&&(N.selected=!0)}V.addEventListener("change",()=>{f.defaultAgent=V.value});let z=W.createDiv({cls:"af-form-row"}),ie=z.createDiv({cls:"af-form-label"});ie.setText("Allowed agents"),this.addTooltip(ie,"Agents reachable via @prefix. Leave unchecked to allow all agents.");let Q=z.createDiv({cls:"af-form-checkboxes"});for(let j of i.agents){let N=Q.createEl("label",{cls:"af-form-checkbox-label"}),ue=N.createEl("input",{attr:{type:"checkbox",value:j.name}});n.allowedAgents.includes(j.name)&&(ue.checked=!0),N.appendText(` ${j.name}`),ue.addEventListener("change",()=>{ue.checked?f.allowedAgents.includes(j.name)||f.allowedAgents.push(j.name):f.allowedAgents=f.allowedAgents.filter(fe=>fe!==j.name)})}let te=W.createDiv({cls:"af-form-row af-form-row-toggle"}),G=te.createDiv({cls:"af-form-label"});G.setText("Per-user sessions"),this.addTooltip(G,"Each external user gets their own isolated Claude session");let B=te.createDiv({cls:`af-agent-card-toggle${n.perUserSessions?" on":""}`});B.onclick=()=>{let j=B.hasClass("on");B.toggleClass("on",!j),f.perUserSessions=!j};let F=p.createDiv({cls:"af-create-section"}),$=F.createDiv({cls:"af-create-section-header"}),ee=$.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ee,"shield-check"),$.createSpan({text:"Access Control"});let se=F.createDiv({cls:"af-form-label"});se.setText("Allowed users"),this.addTooltip(se,"Slack user IDs (U...), one per line. Only listed users can reach the bot.");let oe=F.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`U0AQW6P37N1
|
|
11528
|
+
U0BXYZ12345`,rows:"4"}});oe.value=n.allowedUsers.join(`
|
|
11529
|
+
`),oe.addEventListener("input",()=>{f.allowedUsers=oe.value});let Ce=p.createDiv({cls:"af-create-section"}),Ae=Ce.createDiv({cls:"af-create-section-header"}),Te=Ae.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Te,"message-square");let D=Ae.createSpan({text:"Channel Context"});this.addTooltip(D,"Extra instructions appended to the agent's system prompt when reached through this channel");let Z=Ce.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are being contacted via Slack. Keep replies concise...",rows:"6"}});Z.value=n.channelContext,Z.addEventListener("input",()=>{f.channelContext=Z.value});let le=s.createDiv({cls:"af-create-footer"}),ae=le.createEl("button",{cls:"af-btn-sm danger"});_(ae,"trash-2","af-btn-icon"),ae.appendText(" Delete"),ae.onclick=async()=>{await this.plugin.repository.deleteChannel(n.name),new w.Notice(`Channel "${n.name}" deleted.`),await new Promise(j=>setTimeout(j,200)),await this.plugin.refreshFromVault(),this.navigate("channels")},le.createDiv({cls:"af-toolbar-spacer"});let ke=le.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ke.onclick=()=>this.navigate("channels");let xe=le.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(xe,"check","af-btn-icon"),xe.appendText(" Save Changes"),xe.onclick=async()=>{let j=N=>N.split(/[\n,]+/).map(ue=>ue.trim()).filter(Boolean);try{await this.plugin.repository.updateChannel(n.name,{type:f.type,default_agent:f.defaultAgent,allowed_agents:f.allowedAgents.length>0?f.allowedAgents:[],enabled:f.enabled,credential_ref:f.credentialRef,allowed_users:j(f.allowedUsers),per_user_sessions:f.perUserSessions,channel_context:f.channelContext.trim()}),new w.Notice(`Channel "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("channels")}catch(N){let ue=N instanceof Error?N.message:String(N);new w.Notice(`Failed to update channel: ${ue}`)}}}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"});_(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"});_(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,w.setIcon)(o,"shield-check"),o.addClass("pending")):n.status==="approved"?((0,w.setIcon)(o,"check-circle-2"),o.addClass("approved")):((0,w.setIcon)(o,"x-circle"),o.addClass("rejected"));let 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"});_(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"});_(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(I=>I.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(I=>I.task===a),l=i.agents.find(I=>I.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,w.setIcon)(h,"circle-dot");let u=d.createDiv();u.createDiv({cls:"af-detail-header-name",text:n.taskId}),u.createDiv({cls:"af-detail-header-desc",text:`Agent: ${n.agent}`});let f=c.createDiv({cls:"af-detail-header-actions"}),p=f.createEl("button",{cls:"af-btn-sm"});_(p,"edit","af-btn-icon"),p.appendText(" Edit"),p.onclick=()=>this.navigate("edit-task",n.taskId);let m=f.createEl("button",{cls:"af-btn-sm primary"});_(m,"play","af-btn-icon"),m.appendText(" Run Now"),m.onclick=()=>void this.plugin.runtime.runTaskNow(n);let v=s.createDiv({cls:"af-section-card"}),b=v.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(b,"file-text"),b.appendText(" Details");let g=v.createDiv({cls:"af-config-form"});this.renderConfigRow(g,"Agent",n.agent),this.renderConfigRow(g,"Priority",n.priority.charAt(0).toUpperCase()+n.priority.slice(1)),this.renderConfigRow(g,"Status",n.enabled?"Enabled":"Disabled");let y=n.schedule?this.humanizeCron(n.schedule):n.runAt??"Manual (run on demand)";this.renderConfigRow(g,"Schedule",y),n.schedule&&this.renderConfigRow(g,"Catch up if missed",n.catchUp?"Yes":"No"),this.renderConfigRow(g,"Created",n.created),this.renderConfigRow(g,"Runs",String(n.runCount)),n.lastRun&&this.renderConfigRow(g,"Last Run",this.formatStarted(n.lastRun));let x=s.createDiv({cls:"af-section-card"}),T=x.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(T,"message-square"),T.appendText(" Instructions"),x.createDiv({cls:"af-output-block",text:n.body||"(empty)"});let P=s.createDiv({cls:"af-section-card"}),R=P.createDiv({cls:"af-section-header"}).createDiv({cls:"af-section-title"});_(R,"scroll-text"),R.appendText(" Recent Runs");let W=P.createDiv({cls:"af-timeline"});if(o.length===0)this.renderEmptyState(W,"scroll-text","No runs yet","");else for(let I of o.slice(0,10))this.renderTimelineItem(W,I)}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"});_(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,w.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,w.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 y={task:"from task override",agent:"from agent",settings:"from settings default","cli-default":"Claude CLI default"},x=e.modelSource?` (${y[e.modelSource]??e.modelSource})`:"",S=e.concreteModel&&e.concreteModel!==e.model?` \u2192 ${e.concreteModel}`:"";this.renderDetailRow(l,"Model",`${e.model}${S}${x}`)}let f=5e4,p=y=>y.length>f?y.slice(0,f)+`
|
|
11530
|
+
|
|
11531
|
+
---
|
|
11532
|
+
*Truncated (${(y.length/1024).toFixed(0)} KB total). Open the run note for full content.*`:y,m=!!(e.finalResult&&e.finalResult.trim()),v=e.output?.trim()??"",k=m&&v.length>0&&v!==e.finalResult.trim();if(m){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(w.MarkdownRenderer.render(this.app,p(e.finalResult),x,"",this.plugin),k){let S=y.createEl("details",{cls:"af-run-transcript"}),T=S.createEl("summary");(0,w.setIcon)(T.createSpan({cls:"af-run-transcript-icon"}),"file-text"),T.createSpan({text:"Show full transcript"}),T.createSpan({cls:"af-run-transcript-meta"}).setText(`${(v.length/1024).toFixed(1)} KB`);let E=S.createDiv({cls:"af-output-block af-compact-md af-run-transcript-body"});w.MarkdownRenderer.render(this.app,p(v),E,"",this.plugin)}}else if(v){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"});w.MarkdownRenderer.render(this.app,p(v),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(`
|
|
11533
|
+
`)})}let b=o.createDiv({cls:"af-slideover-actions"});if(e.filePath){let y=b.createEl("button",{cls:"af-btn-sm"});_(y,"external-link","af-btn-icon"),y.appendText(" Open Run Note"),y.onclick=()=>void this.plugin.openPath(e.filePath)}let g=b.createEl("button",{cls:"af-btn-sm primary"});_(g,"refresh-cw","af-btn-icon"),g.appendText(" Re-run Task"),g.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 i=e.createDiv({cls:"af-empty-state"}),o=i.createDiv({cls:"af-empty-icon"});(0,w.setIcon)(o,s),i.createDiv({cls:"af-empty-label",text:a}),n&&i.createDiv({cls:"af-empty-sublabel",text:n})}healthToClass(e){switch(e){case"running":return"running";case"error":return"error";case"pending":return"pending";case"disabled":return"disabled";default:return"idle"}}statusToTimelineClass(e){switch(e){case"success":return"success";case"failure":case"timeout":return"error";case"cancelled":return"warning";case"pending_approval":return"pending";default:return"running"}}statusToIconName(e){switch(e){case"success":return"check-circle-2";case"failure":return"x-circle";case"timeout":return"clock";case"pending_approval":return"shield-check";case"cancelled":return"square";default:return"loader-2"}}statusToBadgeClass(e){switch(e){case"success":return"success";case"failure":return"failure";case"timeout":return"timeout";case"pending_approval":return"pending";case"cancelled":return"cancelled";default:return"running"}}statusToBadgeText(e){switch(e){case"success":return"Success";case"failure":return"Failed";case"timeout":return"Timeout";case"pending_approval":return"Pending";case"cancelled":return"Cancelled";case"interrupted":return"Interrupted";default:return e}}formatDuration(e){if(e<60)return`${e}s`;let s=Math.floor(e/60),a=e%60;return a>0?`${s}m ${a}s`:`${s}m`}formatStarted(e){try{let s=new Date(e),a=new Date;if(s.toDateString()===a.toDateString())return s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});let n=new Date(a);return n.setDate(n.getDate()-1),s.toDateString()===n.toDateString()?`Yesterday ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:s.toLocaleDateString([],{month:"short",day:"numeric"})+` ${s.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`}catch{return e}}formatNextRun(e){try{let s=new Date(e),a=new Date,n=s.getTime()-a.getTime();if(n<0)return"overdue";let i=Math.round(n/6e4);if(i<60)return`${i}m`;let o=Math.round(i/60);return o<24?`${o}h`:s.toLocaleDateString([],{month:"short",day:"numeric"})}catch{return e}}getNextTaskLabel(e){let a=e.map(i=>i.nextRun).filter(Boolean).sort()[0];return a?`${e.find(i=>i.nextRun===a)?.agent??"unknown"} in ${this.formatNextRun(a)}`:"none"}getInitials(e){return e.split("-").map(s=>s[0]?.toUpperCase()??"").slice(0,2).join("")}renderAgentAvatar(e,s){let a=s.avatar?.trim();if(!a){(0,w.setIcon)(e,"bot");return}/^[a-z][a-z0-9-]*$/.test(a)?(0,w.setIcon)(e,a):e.setText(a)}getSkillIcon(e){return e.includes("git")?"settings":e.includes("summarize")||e.includes("log")?"activity":e.includes("review")||e.includes("check")?"check-circle-2":e.includes("vault")||e.includes("note")?"file-text":"puzzle"}renderInlineSchedule(e,s){let a=this.parseCronComponents(s.schedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["daily","Daily"],["weekdays","Weekdays"],["weekly","Weekly"],["monthly","Monthly"]];for(let[y,x]of o){let S=i.createEl("option",{text:x,attr:{value:y}});y===a.freq&&(S.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 y=0;y<24;y++){let x=y>=12?"PM":"AM",S=y===0?12:y>12?y-12:y,T=d.createEl("option",{text:`${S} ${x}`,attr:{value:String(y)}});y===a.hour&&(T.selected=!0)}c.createSpan({cls:"af-schedule-colon",text:":"});let h=c.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 f=u.createDiv({cls:"af-schedule-day-buttons"}),p=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=new Set(a.days);for(let y=0;y<7;y++){let x=f.createEl("button",{cls:`af-schedule-day-btn${m.has(y)?" active":""}`,text:p[y]});x.onclick=()=>{m.has(y)?m.delete(y):m.add(y),x.toggleClass("active",m.has(y)),g()}}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 y=1;y<=28;y++){let x=k.createEl("option",{text:String(y),attr:{value:String(y)}});y===a.dayOfMonth&&(x.selected=!0)}let b=()=>{let y=i.value,x=["daily","weekdays","weekly","monthly"].includes(y),S=y==="weekly",T=y==="monthly";l.style.display=x?"":"none",u.style.display=S?"":"none",v.style.display=T?"":"none"},g=()=>{let y=i.value,x=d.value,S=h.value,T="";switch(y){case"every_5m":T="*/5 * * * *";break;case"every_15m":T="*/15 * * * *";break;case"every_30m":T="*/30 * * * *";break;case"every_hour":T="0 * * * *";break;case"every_2h":T="0 */2 * * *";break;case"daily":T=`${S} ${x} * * *`;break;case"weekdays":T=`${S} ${x} * * 1-5`;break;case"weekly":{let P=Array.from(m).sort().join(",")||"1";T=`${S} ${x} * * ${P}`;break}case"monthly":T=`${S} ${x} ${k.value} * *`;break}s.schedule=T,s.type="recurring"};i.addEventListener("change",()=>{b(),g()}),d.addEventListener("change",g),h.addEventListener("change",g),k.addEventListener("change",g),b()}renderHeartbeatSchedule(e,s){let a=this.parseCronComponents(s.heartbeatSchedule),n=e.createDiv({cls:"af-form-row"});n.createDiv({cls:"af-form-label",text:"Frequency"});let i=n.createEl("select",{cls:"af-form-select"}),o=[["every_5m","Every 5 minutes"],["every_15m","Every 15 minutes"],["every_30m","Every 30 minutes"],["every_hour","Every hour"],["every_2h","Every 2 hours"],["every_4h","Every 4 hours"],["every_6h","Every 6 hours"],["every_12h","Every 12 hours"],["daily","Once a day"]],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 b=i.createEl("option",{text:k,attr:{value:v}});v===l&&(b.selected=!0)}let d=e.createDiv({cls:"af-form-row af-schedule-time-row"});d.createDiv({cls:"af-form-label",text:"Time"});let h=d.createDiv({cls:"af-schedule-time-selects"}),u=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=0;v<24;v++){let k=v>=12?"PM":"AM",b=v===0?12:v>12?v-12:v,g=u.createEl("option",{text:`${b} ${k}`,attr:{value:String(v)}});v===a.hour&&(g.selected=!0)}h.createSpan({cls:"af-schedule-colon",text:":"});let f=h.createEl("select",{cls:"af-form-select af-form-select-sm"});for(let v=0;v<60;v+=5){let k=f.createEl("option",{text:String(v).padStart(2,"0"),attr:{value:String(v)}});v===a.minute&&(k.selected=!0)}let p=()=>{d.style.display=i.value==="daily"?"":"none"},m=()=>{let v=i.value,k=u.value,b=f.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=`${b} ${k} * * *`;break}};i.addEventListener("change",()=>{p(),m()}),u.addEventListener("change",m),f.addEventListener("change",m),p()}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(f=>parseInt(f,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=(f,p)=>{let m=parseInt(f??"0",10),v=parseInt(p??"0",10),k=m>=12?"PM":"AM",b=m===0?12:m>12?m-12:m;return v===0?`${b} ${k}`:`${b}:${String(v).padStart(2,"0")} ${k}`},h=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],u=f=>f==="1-5"?"weekdays":f==="0,6"?"weekends":f.split(",").map(m=>parseInt(m,10)).map(m=>h[m]??m).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,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Agent"}),o.createDiv({cls:"af-detail-header-desc",text:"Configure a new agent for your fleet"});let 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:""},d={none:{label:"None",prompt:""},coding:{label:"Coding Agent",prompt:`You are a coding agent. Review code, write tests, fix bugs, and implement features.
|
|
11184
11534
|
Follow existing code conventions. Write clean, well-tested code.
|
|
11185
11535
|
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.
|
|
11186
11536
|
Be concise and factual. Highlight anomalies clearly.
|
|
@@ -11188,14 +11538,13 @@ Include timestamps and relevant context in all reports.`},briefing:{label:"Brief
|
|
|
11188
11538
|
Prioritize recent and important changes. Keep summaries concise.
|
|
11189
11539
|
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.
|
|
11190
11540
|
Focus on correctness, security, and maintainability.
|
|
11191
|
-
Be specific \u2014 reference file names and line numbers.`}},u=s.createDiv({cls:"af-create-form"}),h=u.createDiv({cls:"af-create-section"}),m=h.createDiv({cls:"af-create-section-header"}),f=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"user"),m.createSpan({text:"Identity"}),this.createFormField(h,"Name","deploy-watcher","Unique identifier (will be slugified)",N=>{c.name=N}),this.createFormField(h,"Description","Monitors deployments and reports status","",N=>{c.description=N});let p=h.createDiv({cls:"af-form-row"});p.createDiv({cls:"af-form-label",text:"Avatar"});let b=p.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"text",placeholder:"\u{1F6E1}\uFE0F"}});b.addEventListener("input",()=>{c.avatar=b.value}),this.createFormField(h,"Tags","devops, monitoring","Comma-separated",N=>{c.tags=N});let k=h.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let v=k.createDiv({cls:"af-agent-card-toggle on"});v.onclick=()=>{let N=v.hasClass("on");v.toggleClass("on",!N),c.enabled=!N};let g=u.createDiv({cls:"af-create-section"}),y=g.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"System Prompt"});let T=g.createDiv({cls:"af-form-row"});T.createDiv({cls:"af-form-label",text:"Template"});let C=T.createEl("select",{cls:"af-form-select"});for(let[N,{label:Y}]of Object.entries(d))C.createEl("option",{text:Y,attr:{value:N}});let A=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});A.addEventListener("input",()=>{c.systemPrompt=A.value}),C.addEventListener("change",()=>{let N=d[C.value];N&&C.value!=="none"&&(c.systemPrompt=N.prompt,A.value=N.prompt)});let E=u.createDiv({cls:"af-create-section"}),R=E.createDiv({cls:"af-create-section-header"}),U=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(U,"settings"),R.createSpan({text:"Runtime Config"});let D=E.createDiv({cls:"af-create-config-grid"}),K=D.createDiv({cls:"af-form-row"});K.createDiv({cls:"af-form-label",text:"Adapter"});let q=K.createEl("select",{cls:"af-form-select"}),V=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[N,Y,we]of V){let de=q.createEl("option",{text:Y,attr:{value:N,...we?{disabled:"true"}:{}}});N==="claude-code"&&(de.selected=!0)}let j=D.createDiv({cls:"af-form-row"});j.createDiv({cls:"af-form-label",text:"Model"});let $=j.createDiv({cls:"af-form-field-wrap"}),W=$.createEl("select",{cls:"af-form-select"}),X=$.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"custom model name",style:"display:none; margin-top:6px;"}}),H=(N,Y)=>{W.empty();let we=Qn[N]??[{value:"default",label:"Default"}];for(let ie of we)W.createEl("option",{text:ie.label,attr:{value:ie.value}});W.createEl("option",{text:"Custom...",attr:{value:"__custom__"}}),we.find(ie=>ie.value===Y)?(W.value=Y,X.style.display="none"):Y&&Y!=="default"&&(W.value="__custom__",X.style.display="",X.value=Y)};H(c.adapter,c.model),W.addEventListener("change",()=>{W.value==="__custom__"?(X.style.display="",X.focus()):(X.style.display="none",c.model=W.value)}),X.addEventListener("input",()=>{c.model=X.value||"default"}),q.addEventListener("change",()=>{c.adapter=q.value,c.model="default",H(c.adapter,"default"),X.style.display="none"});let P=D.createDiv({cls:"af-form-row"});P.createDiv({cls:"af-form-label",text:"Working Dir"});let M=P.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});M.addEventListener("input",()=>{c.cwd=M.value});let I=D.createDiv({cls:"af-form-row"});I.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let Q=I.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&&(c.timeout=N)});let ne=D.createDiv({cls:"af-form-row"});ne.createDiv({cls:"af-form-label",text:"Permission Mode"});let G=ne.createEl("select",{cls:"af-form-select"}),O=[["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[N,Y]of O)G.createEl("option",{text:Y,attr:{value:N}});G.addEventListener("change",()=>{c.permissionMode=G.value});let fe=D.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});G.addEventListener("change",()=>{let N=O.find(([Y])=>Y===G.value)?.[2]??"";fe.textContent=N});{let N=u.createDiv({cls:"af-create-section"}),Y=N.createDiv({cls:"af-create-section-header"}),we=Y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(we,"heart-pulse");let de=Y.createSpan({text:"Heartbeat"});this.addTooltip(de,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ie=N.createDiv({cls:"af-form-row af-form-row-toggle"});ie.createDiv({cls:"af-form-label",text:"Enabled"});let Ee=ie.createDiv({cls:"af-agent-card-toggle"}),le=N.createDiv();le.style.display="none",Ee.onclick=()=>{let Ne=Ee.hasClass("on");Ee.toggleClass("on",!Ne),c.heartbeatEnabled=!Ne,le.style.display=Ne?"none":""},this.renderHeartbeatSchedule(le,c);let $e=le.createDiv({cls:"af-form-row af-form-row-toggle"}),F=$e.createDiv({cls:"af-form-label"});F.setText("Notify"),this.addTooltip(F,"Show an Obsidian notice when the heartbeat completes");let B=$e.createDiv({cls:"af-agent-card-toggle on"});B.onclick=()=>{let Ne=B.hasClass("on");B.toggleClass("on",!Ne),c.heartbeatNotify=!Ne};let Z=this.plugin.runtime.getSnapshot(),ye=le.createDiv({cls:"af-form-row"}),ke=ye.createDiv({cls:"af-form-label"});ke.setText("Post to channel"),this.addTooltip(ke,"Heartbeat results are posted to this Slack channel when the run completes");let Ae=ye.createEl("select",{cls:"af-form-select"});Ae.createEl("option",{text:"(none)",attr:{value:""}});for(let Ne of Z.channels)Ae.createEl("option",{text:Ne.name,attr:{value:Ne.name}});Ae.addEventListener("change",()=>{c.heartbeatChannel=Ae.value});let Pe=le.createDiv({cls:"af-form-label"});Pe.style.width="auto",Pe.style.marginTop="12px",Pe.setText("Instruction"),this.addTooltip(Pe,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let it=le.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});it.addEventListener("input",()=>{c.heartbeatBody=it.value})}let ce=u.createDiv({cls:"af-create-section"}),Te=ce.createDiv({cls:"af-create-section-header"}),J=Te.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(J,"puzzle"),Te.createSpan({text:"Skills"});let Ce=this.plugin.runtime.getSnapshot();if(Ce.skills.length>0){ce.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let N=ce.createDiv({cls:"af-create-skills-grid"});for(let Y of Ce.skills){let we=N.createDiv({cls:"af-create-skill-item"}),de=we.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});de.addEventListener("change",()=>{de.checked?c.selectedSkills.add(Y.name):c.selectedSkills.delete(Y.name)});let ie=we.createDiv({cls:"af-create-skill-label"});ie.createSpan({cls:"af-create-skill-name",text:Y.name}),Y.description&&ie.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${Y.description}`})}}let _e=ce.createDiv({cls:"af-form-sublabel"});_e.setText("Agent-specific skills"),this.addTooltip(_e,"Custom skills/instructions only for this agent, not shared with others");let he=ce.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});he.addEventListener("input",()=>{c.skillsBody=he.value});{let N=u.createDiv({cls:"af-create-section"}),Y=N.createDiv({cls:"af-create-section-header"}),we=Y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(we,"plug");let de=Y.createSpan({text:"MCP Servers"});this.addTooltip(de,"Grant agent access to MCP servers");let ie=this.plugin.mcpManager.getCachedServers();if(ie===null){let Ee=N.createDiv({cls:"af-form-hint"});Ee.appendText("MCP servers not loaded. ");let le=Ee.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});le.onclick=$e=>{$e.preventDefault(),this.navigate("mcp")}}else if(ie.length===0)N.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Ee=N.createDiv({cls:"af-create-skills-grid"});for(let le of ie){let $e=Ee.createDiv({cls:"af-mcp-agent-item"}),F=$e.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});F.addEventListener("change",()=>{F.checked?c.selectedMcpServers.add(le.name):c.selectedMcpServers.delete(le.name)});let Z=$e.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),ye=Z.createSpan({cls:`af-mcp-status-dot ${le.enabled?le.status:"disabled"}`});ye.title=le.enabled?le.status:"disabled",Z.createSpan({cls:"af-create-skill-name",text:le.name});let ke=le.toolDetails.length||le.tools.length;ke>0?Z.createSpan({cls:"af-mcp-agent-tool-count",text:`${ke} tools`}):le.enabled?le.status==="needs-auth"&&Z.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Z.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}}let Re=u.createDiv({cls:"af-create-section"}),Ge=Re.createDiv({cls:"af-create-section-header"}),z=Ge.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(z,"file-text");let ge=Ge.createSpan({text:"Context"});this.addTooltip(ge,"Project-specific context included in every run");let Be=Re.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});Be.addEventListener("input",()=>{c.contextBody=Be.value});let Je=u.createDiv({cls:"af-create-section"}),vt=Je.createDiv({cls:"af-create-section-header"}),Xt=vt.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Xt,"shield-check"),vt.createSpan({text:"Permissions"}),this.createFormField(Je,"Approval required","git_push, file_delete","Comma-separated tool names",N=>{c.approvalRequired=N});let Ft=Je.createDiv({cls:"af-form-row"});Ft.createDiv({cls:"af-form-label",text:"Allowed Commands"});let Jt=Ft.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11541
|
+
Be specific \u2014 reference file names and line numbers.`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),f=u.createDiv({cls:"af-create-section-header"}),p=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"user"),f.createSpan({text:"Identity"}),this.createFormField(u,"Name","deploy-watcher","Unique identifier (will be slugified)",O=>{c.name=O}),this.createFormField(u,"Description","Monitors deployments and reports status","",O=>{c.description=O});let m=u.createDiv({cls:"af-form-row"});m.createDiv({cls:"af-form-label",text:"Avatar"});let v=m.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",O=>{c.tags=O});let k=u.createDiv({cls:"af-form-row af-form-row-toggle"});k.createDiv({cls:"af-form-label",text:"Enabled"});let b=k.createDiv({cls:"af-agent-card-toggle on"});b.onclick=()=>{let O=b.hasClass("on");b.toggleClass("on",!O),c.enabled=!O};let g=h.createDiv({cls:"af-create-section"}),y=g.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"System Prompt"});let S=g.createDiv({cls:"af-form-row"});S.createDiv({cls:"af-form-label",text:"Template"});let T=S.createEl("select",{cls:"af-form-select"});for(let[O,{label:X}]of Object.entries(d))T.createEl("option",{text:X,attr:{value:O}});let P=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"You are a deployment monitoring agent...",rows:"10"}});P.addEventListener("input",()=>{c.systemPrompt=P.value}),T.addEventListener("change",()=>{let O=d[T.value];O&&T.value!=="none"&&(c.systemPrompt=O.prompt,P.value=O.prompt)});let E=h.createDiv({cls:"af-create-section"}),R=E.createDiv({cls:"af-create-section-header"}),W=R.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(W,"settings"),R.createSpan({text:"Runtime Config"});let I=E.createDiv({cls:"af-create-config-grid"}),q=I.createDiv({cls:"af-form-row"});q.createDiv({cls:"af-form-label",text:"Adapter"});let L=q.createEl("select",{cls:"af-form-select"}),K=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[O,X,Re]of K){let be=L.createEl("option",{text:X,attr:{value:O,...Re?{disabled:"true"}:{}}});O==="claude-code"&&(be.selected=!0)}let V=I.createDiv({cls:"af-form-row"}),z=V.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(z,`Aliases (opus/sonnet/haiku/opusplan) work on any backend. Choose Custom\u2026 for a pinned ID or Bedrock/Vertex/Foundry. Blank = use Settings default (${this.plugin.settings.defaultModel||"Claude CLI default"}).`);let ie=V.createDiv({cls:"af-form-field-wrap"});vt(ie,{value:c.model,onChange:O=>{c.model=O}}),L.addEventListener("change",()=>{c.adapter=L.value});let Q=I.createDiv({cls:"af-form-row"});Q.createDiv({cls:"af-form-label",text:"Working Dir"});let te=Q.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root"}});te.addEventListener("input",()=>{c.cwd=te.value});let G=I.createDiv({cls:"af-form-row"});G.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let B=G.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:"300"}});B.addEventListener("input",()=>{let O=parseInt(B.value,10);!isNaN(O)&&O>0&&(c.timeout=O)});let F=I.createDiv({cls:"af-form-row"});F.createDiv({cls:"af-form-label",text:"Permission Mode"});let $=F.createEl("select",{cls:"af-form-select"}),ee=[["bypassPermissions","Bypass Permissions","Auto-approve everything except deny list"],["dontAsk","Don\u2019t Ask","Auto-approve all tool calls"],["acceptEdits","Accept Edits","Auto-approve file edits, block bash unless allowed"],["plan","Plan","Read-only mode, no writes or commands"],["default","Default","Ask for each tool call"]];for(let[O,X]of ee)$.createEl("option",{text:X,attr:{value:O}});$.addEventListener("change",()=>{c.permissionMode=$.value});let se=I.createDiv({cls:"af-form-hint",text:"Skip all permission checks"});$.addEventListener("change",()=>{let O=ee.find(([X])=>X===$.value)?.[2]??"";se.textContent=O});let oe=I.createDiv({cls:"af-form-row"});oe.createDiv({cls:"af-form-label",text:"Effort Level"});let Ce=oe.createEl("select",{cls:"af-form-select"});for(let[O,X]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]])Ce.createEl("option",{text:X,attr:{value:O}});Ce.addEventListener("change",()=>{c.effort=Ce.value}),I.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"});{let O=h.createDiv({cls:"af-create-section"}),X=O.createDiv({cls:"af-create-section-header"}),Re=X.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Re,"heart-pulse");let be=X.createSpan({text:"Heartbeat"});this.addTooltip(be,"Autonomous periodic run \u2014 what the agent does when no one is asking");let Se=O.createDiv({cls:"af-form-row af-form-row-toggle"});Se.createDiv({cls:"af-form-label",text:"Enabled"});let Ie=Se.createDiv({cls:"af-agent-card-toggle"}),me=O.createDiv();me.style.display="none",Ie.onclick=()=>{let qe=Ie.hasClass("on");Ie.toggleClass("on",!qe),c.heartbeatEnabled=!qe,me.style.display=qe?"none":""},this.renderHeartbeatSchedule(me,c);let Ve=me.createDiv({cls:"af-form-row af-form-row-toggle"}),M=Ve.createDiv({cls:"af-form-label"});M.setText("Notify"),this.addTooltip(M,"Show an Obsidian notice when the heartbeat completes");let H=Ve.createDiv({cls:"af-agent-card-toggle on"});H.onclick=()=>{let qe=H.hasClass("on");H.toggleClass("on",!qe),c.heartbeatNotify=!qe};let ne=this.plugin.runtime.getSnapshot(),_e=me.createDiv({cls:"af-form-row"}),He=_e.createDiv({cls:"af-form-label"});He.setText("Post to channel"),this.addTooltip(He,"Heartbeat results are posted to this Slack channel when the run completes");let Le=_e.createEl("select",{cls:"af-form-select"});Le.createEl("option",{text:"(none)",attr:{value:""}});for(let qe of ne.channels)Le.createEl("option",{text:qe.name,attr:{value:qe.name}});Le.addEventListener("change",()=>{c.heartbeatChannel=Le.value});let Me=me.createDiv({cls:"af-form-label"});Me.style.width="auto",Me.style.marginTop="12px",Me.setText("Instruction"),this.addTooltip(Me,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let dt=me.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});dt.addEventListener("input",()=>{c.heartbeatBody=dt.value})}let Ae=h.createDiv({cls:"af-create-section"}),Te=Ae.createDiv({cls:"af-create-section-header"}),D=Te.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(D,"puzzle"),Te.createSpan({text:"Skills"});let Z=this.plugin.runtime.getSnapshot();if(Z.skills.length>0){Ae.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let O=Ae.createDiv({cls:"af-create-skills-grid"});for(let X of Z.skills){let Re=O.createDiv({cls:"af-create-skill-item"}),be=Re.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});be.addEventListener("change",()=>{be.checked?c.selectedSkills.add(X.name):c.selectedSkills.delete(X.name)});let Se=Re.createDiv({cls:"af-create-skill-label"});Se.createSpan({cls:"af-create-skill-name",text:X.name}),X.description&&Se.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${X.description}`})}}let le=Ae.createDiv({cls:"af-form-sublabel"});le.setText("Agent-specific skills"),this.addTooltip(le,"Custom skills/instructions only for this agent, not shared with others");let ae=Ae.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});ae.addEventListener("input",()=>{c.skillsBody=ae.value});{let O=h.createDiv({cls:"af-create-section"}),X=O.createDiv({cls:"af-create-section-header"}),Re=X.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Re,"plug");let be=X.createSpan({text:"MCP Servers"});this.addTooltip(be,"Grant agent access to MCP servers");let Se=this.plugin.mcpManager.getCachedServers();if(Se===null){let Ie=O.createDiv({cls:"af-form-hint"});Ie.appendText("MCP servers not loaded. ");let me=Ie.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});me.onclick=Ve=>{Ve.preventDefault(),this.navigate("mcp")}}else if(Se.length===0)O.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let Ie=O.createDiv({cls:"af-create-skills-grid"});for(let me of Se){let Ve=Ie.createDiv({cls:"af-mcp-agent-item"}),M=Ve.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});M.addEventListener("change",()=>{M.checked?c.selectedMcpServers.add(me.name):c.selectedMcpServers.delete(me.name)});let ne=Ve.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),_e=ne.createSpan({cls:`af-mcp-status-dot ${me.enabled?me.status:"disabled"}`});_e.title=me.enabled?me.status:"disabled",ne.createSpan({cls:"af-create-skill-name",text:me.name});let He=me.toolDetails.length||me.tools.length;He>0?ne.createSpan({cls:"af-mcp-agent-tool-count",text:`${He} tools`}):me.enabled?me.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 ke=h.createDiv({cls:"af-create-section"}),xe=ke.createDiv({cls:"af-create-section-header"}),j=xe.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(j,"file-text");let N=xe.createSpan({text:"Context"});this.addTooltip(N,"Project-specific context included in every run");let ue=ke.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});ue.addEventListener("input",()=>{c.contextBody=ue.value});let fe=h.createDiv({cls:"af-create-section"}),Xe=fe.createDiv({cls:"af-create-section-header"}),cs=Xe.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(cs,"shield-check"),Xe.createSpan({text:"Permissions"}),this.createFormField(fe,"Approval required","git_push, file_delete","Comma-separated tool names",O=>{c.approvalRequired=O});let qt=fe.createDiv({cls:"af-form-row"});qt.createDiv({cls:"af-form-label",text:"Allowed Commands"});let ds=qt.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11192
11542
|
Bash(python3 *)
|
|
11193
11543
|
Read
|
|
11194
11544
|
Edit
|
|
11195
|
-
Write`,rows:"4"}});
|
|
11545
|
+
Write`,rows:"4"}});ds.addEventListener("input",()=>{c.allowedCommands=ds.value});let hs=fe.createDiv({cls:"af-form-row"});hs.createDiv({cls:"af-form-label",text:"Blocked Commands"});let Ct=hs.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
|
|
11196
11546
|
Bash(rm -rf *)
|
|
11197
|
-
Bash(sudo *)`,rows:"4"}});
|
|
11198
|
-
`).map(Ee=>Ee.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:Y,description:c.description.trim(),avatar:c.avatar.trim(),tags:we(c.tags),systemPrompt:c.systemPrompt.trim(),model:c.model.trim()||"default",adapter:c.adapter,cwd:c.cwd.trim(),timeout:c.timeout,permissionMode:c.permissionMode,approvalRequired:we(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:de(c.allowedCommands),deny:de(c.blockedCommands)}}),c.heartbeatEnabled&&c.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(Y,{enabled:c.heartbeatEnabled,schedule:c.heartbeatSchedule.trim(),notify:c.heartbeatNotify,channel:c.heartbeatChannel,body:c.heartbeatBody.trim()}),new w.Notice(`Agent "${Y}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",Y)}catch(de){let ie=de instanceof Error?de.message:String(de);new w.Notice(`Failed to create agent: ${ie}`)}}}renderCreateSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Skill"}),o.createDiv({cls:"af-detail-header-desc",text:"Define a reusable skill for your agents"});let 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.
|
|
11547
|
+
Bash(sudo *)`,rows:"4"}});Ct.addEventListener("input",()=>{c.blockedCommands=Ct.value});let ct=fe.createDiv({cls:"af-form-row"});ct.createDiv({cls:"af-form-label",text:"Memory enabled"});let _t=ct.createDiv({cls:"af-agent-card-toggle on"});_t.onclick=()=>{let O=_t.hasClass("on");_t.toggleClass("on",!O),c.memory=!O};let us=s.createDiv({cls:"af-create-footer"}),ps=us.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ps.onclick=()=>this.navigate("agents");let ft=us.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(ft,"plus","af-btn-icon"),ft.appendText(" Create Agent"),ft.onclick=async()=>{let O=c.name.trim();if(!O){new w.Notice("Agent name is required.");return}let X=ge(O);if(this.plugin.repository.getAgentByName(X)){new w.Notice(`Agent "${X}" already exists.`);return}let Re=be=>be.split(",").map(Se=>Se.trim()).filter(Boolean);try{let be=Se=>ce(Se).map(Ie=>Ie.trim()).filter(Boolean);await this.plugin.repository.createAgentFolder({name:X,description:c.description.trim(),avatar:c.avatar.trim(),tags:Re(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:Re(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:be(c.allowedCommands),deny:be(c.blockedCommands)}}),c.heartbeatEnabled&&c.heartbeatBody.trim()&&await this.plugin.repository.updateHeartbeat(X,{enabled:c.heartbeatEnabled,schedule:c.heartbeatSchedule.trim(),notify:c.heartbeatNotify,channel:c.heartbeatChannel,body:c.heartbeatBody.trim()}),new w.Notice(`Agent "${X}" created.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",X)}catch(be){let Se=be instanceof Error?be.message:String(be);new w.Notice(`Failed to create agent: ${Se}`)}}}renderCreateSkillPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Create New Skill"}),o.createDiv({cls:"af-detail-header-desc",text:"Define a reusable skill for your agents"});let 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.
|
|
11199
11548
|
|
|
11200
11549
|
Requirements:
|
|
11201
11550
|
- Ensure required environment variables are set
|
|
@@ -11232,35 +11581,34 @@ Key behaviors:
|
|
|
11232
11581
|
- Use tables and charts where appropriate
|
|
11233
11582
|
- Always state the time range and filters applied
|
|
11234
11583
|
- Flag anomalies and outliers explicitly
|
|
11235
|
-
- End with actionable insights, not just observations`}},
|
|
11584
|
+
- End with actionable insights, not just observations`}},h=s.createDiv({cls:"af-create-form"}),u=h.createDiv({cls:"af-create-section"}),f=u.createDiv({cls:"af-create-section-header"}),p=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"puzzle"),f.createSpan({text:"Identity"}),this.createFormField(u,"Name","todoist","Unique identifier (will be slugified)",F=>{c.name=F}),this.createFormField(u,"Description","Manage tasks and projects via CLI","",F=>{c.description=F}),this.createFormField(u,"Tags","productivity, tasks","Comma-separated",F=>{c.tags=F});let m=h.createDiv({cls:"af-create-section"}),v=m.createDiv({cls:"af-create-section-header"}),k=v.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(k,"file-text"),v.createSpan({text:"Core Instructions"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Template"});let g=b.createEl("select",{cls:"af-form-select"});for(let[F,{label:$}]of Object.entries(d))g.createEl("option",{text:$,attr:{value:F}});let y=m.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions \u2014 what does this skill do and how should agents use it?",rows:"10"}});y.addEventListener("input",()=>{c.body=y.value}),g.addEventListener("change",()=>{let F=d[g.value];F&&g.value!=="none"&&(c.body=F.prompt,y.value=F.prompt)});let x=h.createDiv({cls:"af-create-section"}),S=x.createDiv({cls:"af-create-section-header"}),T=S.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(T,"wrench");let P=S.createSpan({text:"Tools"});this.addTooltip(P,"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
|
|
11236
11585
|
|
|
11237
11586
|
### list
|
|
11238
11587
|
Usage: tool list [--filter <query>]
|
|
11239
|
-
...`,rows:"8"}});E.addEventListener("input",()=>{c.toolsBody=E.value});let R=
|
|
11588
|
+
...`,rows:"8"}});E.addEventListener("input",()=>{c.toolsBody=E.value});let R=h.createDiv({cls:"af-create-section"}),W=R.createDiv({cls:"af-create-section-header"}),I=W.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(I,"book-open");let q=W.createSpan({text:"References"});this.addTooltip(q,"Background docs, conventions, cheat sheets");let L=R.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});L.addEventListener("input",()=>{c.referencesBody=L.value});let K=h.createDiv({cls:"af-create-section"}),V=K.createDiv({cls:"af-create-section-header"}),z=V.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(z,"message-circle");let ie=V.createSpan({text:"Examples"});this.addTooltip(ie,"Example prompts and ideal outputs showing how to use this skill");let Q=K.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
|
|
11240
11589
|
|
|
11241
11590
|
User: Show me my tasks for today
|
|
11242
11591
|
|
|
11243
|
-
Agent: ...`,rows:"6"}});
|
|
11592
|
+
Agent: ...`,rows:"6"}});Q.addEventListener("input",()=>{c.examplesBody=Q.value});let te=s.createDiv({cls:"af-create-footer"}),G=te.createEl("button",{cls:"af-btn-sm",text:"Cancel"});G.onclick=()=>this.navigate("skills");let B=te.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(B,"plus","af-btn-icon"),B.appendText(" Create Skill"),B.onclick=async()=>{let F=c.name.trim();if(!F){new w.Notice("Skill name is required.");return}let $=ge(F);if(this.plugin.repository.getSkillByName($)){new w.Notice(`Skill "${$}" already exists.`);return}let ee=se=>se.split(",").map(oe=>oe.trim()).filter(Boolean);try{await this.plugin.repository.createSkillFolder({name:$,description:c.description.trim(),tags:ee(c.tags),body:c.body.trim(),toolsBody:c.toolsBody.trim(),referencesBody:c.referencesBody.trim(),examplesBody:c.examplesBody.trim()}),new w.Notice(`Skill "${$}" created.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(se){let oe=se instanceof Error?se.message:String(se);new w.Notice(`Failed to create skill: ${oe}`)}}}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(M=>M.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,w.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(`
|
|
11244
11593
|
`),blockedCommands:n.permissionRules.deny.join(`
|
|
11245
|
-
`),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel},h=s.createDiv({cls:"af-create-form"}),m=h.createDiv({cls:"af-create-section"}),f=m.createDiv({cls:"af-create-section-header"}),p=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"user"),f.createSpan({text:"Identity"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Name"});let k=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(m,"Description","Monitors deployments and reports status","",F=>{u.description=F},n.description??"");let v=m.createDiv({cls:"af-form-row"});v.createDiv({cls:"af-form-label",text:"Avatar"});let g=v.createEl("button",{cls:"af-avatar-picker-btn"}),y=g.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(y,{...n,avatar:u.avatar??n.avatar}),g.createSpan({cls:"af-avatar-picker-label",text:u.avatar||n.avatar||"Pick icon\u2026"}),g.addEventListener("click",()=>{new Bs(this.app,u.avatar??n.avatar,F=>{u.avatar=F,y.empty(),(0,w.setIcon)(y,F),g.querySelector(".af-avatar-picker-label")?.setText(F)}).open()}),this.createFormField(m,"Tags","devops, monitoring","Comma-separated",F=>{u.tags=F},n.tags.join(", "));let x=m.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),u.enabled=!F};let C=h.createDiv({cls:"af-create-section"}),A=C.createDiv({cls:"af-create-section-header"}),E=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(E,"message-square"),A.createSpan({text:"System Prompt"});let R=C.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});R.value=n.body,R.addEventListener("input",()=>{u.systemPrompt=R.value});let U=h.createDiv({cls:"af-create-section"}),D=U.createDiv({cls:"af-create-section-header"}),K=D.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(K,"settings"),D.createSpan({text:"Runtime Config"});let q=U.createDiv({cls:"af-create-config-grid"}),V=q.createDiv({cls:"af-form-row"});V.createDiv({cls:"af-form-label",text:"Adapter"});let j=V.createEl("select",{cls:"af-form-select"}),$=[["claude-code","Claude Code",!1],["codex","Codex (coming soon)",!0],["process","Process (coming soon)",!0],["http","HTTP (coming soon)",!0]];for(let[F,B,Z]of $){let ye=j.createEl("option",{text:B,attr:{value:F,...Z?{disabled:"true"}:{}}});F===n.adapter&&(ye.selected=!0)}let W=q.createDiv({cls:"af-form-row"});W.createDiv({cls:"af-form-label",text:"Model"});let X=W.createDiv({cls:"af-form-field-wrap"}),H=X.createEl("select",{cls:"af-form-select"}),P=X.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"custom model name",style:"display:none; margin-top:6px;"}}),M=(F,B)=>{H.empty();let Z=Qn[F]??[{value:"default",label:"Default"}];for(let ke of Z)H.createEl("option",{text:ke.label,attr:{value:ke.value}});H.createEl("option",{text:"Custom...",attr:{value:"__custom__"}}),Z.find(ke=>ke.value===B)?(H.value=B,P.style.display="none"):B&&B!=="default"&&(H.value="__custom__",P.style.display="",P.value=B)};M(u.adapter,u.model),H.addEventListener("change",()=>{H.value==="__custom__"?(P.style.display="",P.focus()):(P.style.display="none",u.model=H.value)}),P.addEventListener("input",()=>{u.model=P.value||"default"}),j.addEventListener("change",()=>{u.adapter=j.value,u.model="default",M(u.adapter,"default"),P.style.display="none"});let I=q.createDiv({cls:"af-form-row"});I.createDiv({cls:"af-form-label",text:"Working Dir"});let Q=I.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});Q.addEventListener("input",()=>{u.cwd=Q.value});let ne=q.createDiv({cls:"af-form-row"});ne.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let G=ne.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});G.addEventListener("input",()=>{let F=parseInt(G.value,10);!isNaN(F)&&F>0&&(u.timeout=F)});let O=q.createDiv({cls:"af-form-row"});O.createDiv({cls:"af-form-label",text:"Permission Mode"});let fe=O.createEl("select",{cls:"af-form-select"}),ce=[["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,B]of ce){let Z=fe.createEl("option",{text:B,attr:{value:F}});F===n.permissionMode&&(Z.selected=!0)}fe.addEventListener("change",()=>{u.permissionMode=fe.value});let Te=q.createDiv({cls:"af-form-hint",text:ce.find(([F])=>F===n.permissionMode)?.[2]??""});if(fe.addEventListener("change",()=>{let F=ce.find(([B])=>B===fe.value)?.[2]??"";Te.textContent=F}),n.isFolder){let F=h.createDiv({cls:"af-create-section"}),B=F.createDiv({cls:"af-create-section-header"}),Z=B.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Z,"heart-pulse");let ye=B.createSpan({text:"Heartbeat"});this.addTooltip(ye,"Autonomous periodic run \u2014 what the agent does when no one is asking");let ke=F.createDiv({cls:"af-form-row af-form-row-toggle"});ke.createDiv({cls:"af-form-label",text:"Enabled"});let Ae=ke.createDiv({cls:`af-agent-card-toggle${u.heartbeatEnabled?" on":""}`}),Pe=F.createDiv();Pe.style.display=u.heartbeatEnabled?"":"none",Ae.onclick=()=>{let Ve=Ae.hasClass("on");Ae.toggleClass("on",!Ve),u.heartbeatEnabled=!Ve,Pe.style.display=Ve?"none":""},this.renderHeartbeatSchedule(Pe,u);let it=Pe.createDiv({cls:"af-form-row af-form-row-toggle"}),Ne=it.createDiv({cls:"af-form-label"});Ne.setText("Notify"),this.addTooltip(Ne,"Show an Obsidian notice when the heartbeat completes");let Us=it.createDiv({cls:`af-agent-card-toggle${u.heartbeatNotify?" on":""}`});Us.onclick=()=>{let Ve=Us.hasClass("on");Us.toggleClass("on",!Ve),u.heartbeatNotify=!Ve};let si=this.plugin.runtime.getSnapshot(),ya=Pe.createDiv({cls:"af-form-row"}),va=ya.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 ts=ya.createEl("select",{cls:"af-form-select"});ts.createEl("option",{text:"(none)",attr:{value:""}});for(let Ve of si.channels){let ai=ts.createEl("option",{text:Ve.name,attr:{value:Ve.name}});Ve.name===u.heartbeatChannel&&(ai.selected=!0)}ts.addEventListener("change",()=>{u.heartbeatChannel=ts.value});let ss=Pe.createDiv({cls:"af-form-label"});ss.style.width="auto",ss.style.marginTop="12px",ss.setText("Instruction"),this.addTooltip(ss,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let js=Pe.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});js.value=u.heartbeatBody,js.addEventListener("input",()=>{u.heartbeatBody=js.value})}let J=h.createDiv({cls:"af-create-section"}),Ce=J.createDiv({cls:"af-create-section-header"}),_e=Ce.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(_e,"puzzle"),Ce.createSpan({text:"Skills"});let he=this.plugin.runtime.getSnapshot();if(he.skills.length>0){J.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let F=J.createDiv({cls:"af-create-skills-grid"});for(let B of he.skills){let Z=F.createDiv({cls:"af-create-skill-item"}),ye=Z.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});ye.checked=u.selectedSkills.has(B.name),ye.addEventListener("change",()=>{ye.checked?u.selectedSkills.add(B.name):u.selectedSkills.delete(B.name)});let ke=Z.createDiv({cls:"af-create-skill-label"});ke.createSpan({cls:"af-create-skill-name",text:B.name}),B.description&&ke.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${B.description}`})}}let Re=J.createDiv({cls:"af-form-sublabel"});Re.setText("Agent-specific skills"),this.addTooltip(Re,"Custom skills/instructions only for this agent, not shared with others");let Ge=J.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});Ge.value=n.skillsBody,Ge.addEventListener("input",()=>{u.skillsBody=Ge.value});let z=h.createDiv({cls:"af-create-section"}),ge=z.createDiv({cls:"af-create-section-header"}),Be=ge.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Be,"plug");let Je=ge.createSpan({text:"MCP Servers"});this.addTooltip(Je,"Grant agent access to MCP servers");let vt=this.plugin.mcpManager.getCachedServers();if(vt===null){let F=z.createDiv({cls:"af-form-hint"});F.appendText("MCP servers not loaded. ");let B=F.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});B.onclick=Z=>{Z.preventDefault(),this.navigate("mcp")}}else if(vt.length===0)z.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let F=z.createDiv({cls:"af-create-skills-grid"});for(let B of vt){let Z=F.createDiv({cls:"af-mcp-agent-item"}),ye=Z.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});ye.checked=u.selectedMcpServers.has(B.name),ye.addEventListener("change",()=>{ye.checked?u.selectedMcpServers.add(B.name):u.selectedMcpServers.delete(B.name)});let Ae=Z.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Pe=Ae.createSpan({cls:`af-mcp-status-dot ${B.enabled?B.status:"disabled"}`});Pe.title=B.enabled?B.status:"disabled",Ae.createSpan({cls:"af-create-skill-name",text:B.name});let it=B.toolDetails.length||B.tools.length;it>0?Ae.createSpan({cls:"af-mcp-agent-tool-count",text:`${it} tools`}):B.enabled?B.status==="needs-auth"&&Ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Ae.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let Xt=h.createDiv({cls:"af-create-section"}),Ft=Xt.createDiv({cls:"af-create-section-header"}),Jt=Ft.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Jt,"file-text");let Qt=Ft.createSpan({text:"Context"});this.addTooltip(Qt,"Project-specific context included in every run");let bt=Xt.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});bt.value=n.contextBody,bt.addEventListener("input",()=>{u.contextBody=bt.value});let nt=h.createDiv({cls:"af-create-section"}),wt=nt.createDiv({cls:"af-create-section-header"}),Zt=wt.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(Zt,"shield-check"),wt.createSpan({text:"Permissions"}),this.createFormField(nt,"Approval required","git_push, file_delete","Comma-separated tool names",F=>{u.approvalRequired=F},n.approvalRequired.join(", "));let es=nt.createDiv({cls:"af-form-row"});es.createDiv({cls:"af-form-label",text:"Allowed Commands"});let dt=es.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11594
|
+
`),heartbeatEnabled:n.heartbeatEnabled,heartbeatSchedule:n.heartbeatSchedule,heartbeatBody:n.heartbeatBody,heartbeatNotify:n.heartbeatNotify,heartbeatChannel:n.heartbeatChannel},u=s.createDiv({cls:"af-create-form"}),f=u.createDiv({cls:"af-create-section"}),p=f.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"user"),p.createSpan({text:"Identity"});let v=f.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(f,"Description","Monitors deployments and reports status","",M=>{h.description=M},n.description??"");let b=f.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Avatar"});let g=b.createEl("button",{cls:"af-avatar-picker-btn"}),y=g.createDiv({cls:"af-avatar-picker-preview"});this.renderAgentAvatar(y,{...n,avatar:h.avatar??n.avatar}),g.createSpan({cls:"af-avatar-picker-label",text:h.avatar||n.avatar||"Pick icon\u2026"}),g.addEventListener("click",()=>{new ea(this.app,h.avatar??n.avatar,M=>{h.avatar=M,y.empty(),(0,w.setIcon)(y,M),g.querySelector(".af-avatar-picker-label")?.setText(M)}).open()}),this.createFormField(f,"Tags","devops, monitoring","Comma-separated",M=>{h.tags=M},n.tags.join(", "));let x=f.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 M=S.hasClass("on");S.toggleClass("on",!M),h.enabled=!M};let T=u.createDiv({cls:"af-create-section"}),P=T.createDiv({cls:"af-create-section-header"}),E=P.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(E,"message-square"),P.createSpan({text:"System Prompt"});let R=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"System prompt...",rows:"10"}});R.value=n.body,R.addEventListener("input",()=>{h.systemPrompt=R.value});let W=u.createDiv({cls:"af-create-section"}),I=W.createDiv({cls:"af-create-section-header"}),q=I.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(q,"settings"),I.createSpan({text:"Runtime Config"});let L=W.createDiv({cls:"af-create-config-grid"}),K=L.createDiv({cls:"af-form-row"});K.createDiv({cls:"af-form-label",text:"Adapter"});let V=K.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[M,H,ne]of z){let _e=V.createEl("option",{text:H,attr:{value:M,...ne?{disabled:"true"}:{}}});M===n.adapter&&(_e.selected=!0)}let ie=L.createDiv({cls:"af-form-row"}),Q=ie.createDiv({cls:"af-form-label",text:"Model"});this.addTooltip(Q,`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 te=ie.createDiv({cls:"af-form-field-wrap"});vt(te,{value:h.model,onChange:M=>{h.model=M}}),V.addEventListener("change",()=>{h.adapter=V.value});let G=L.createDiv({cls:"af-form-row"});G.createDiv({cls:"af-form-label",text:"Working Dir"});let B=G.createEl("input",{cls:"af-form-input",attr:{type:"text",placeholder:"Leave empty for vault root",value:n.cwd??""}});B.addEventListener("input",()=>{h.cwd=B.value});let F=L.createDiv({cls:"af-form-row"});F.createDiv({cls:"af-form-label",text:"Timeout (sec)"});let $=F.createEl("input",{cls:"af-form-input af-form-input-sm",attr:{type:"number",value:String(n.timeout)}});$.addEventListener("input",()=>{let M=parseInt($.value,10);!isNaN(M)&&M>0&&(h.timeout=M)});let ee=L.createDiv({cls:"af-form-row"});ee.createDiv({cls:"af-form-label",text:"Permission Mode"});let se=ee.createEl("select",{cls:"af-form-select"}),oe=[["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[M,H]of oe){let ne=se.createEl("option",{text:H,attr:{value:M}});M===n.permissionMode&&(ne.selected=!0)}se.addEventListener("change",()=>{h.permissionMode=se.value});let Ce=L.createDiv({cls:"af-form-hint",text:oe.find(([M])=>M===n.permissionMode)?.[2]??""});se.addEventListener("change",()=>{let M=oe.find(([H])=>H===se.value)?.[2]??"";Ce.textContent=M});let Ae=L.createDiv({cls:"af-form-row"});Ae.createDiv({cls:"af-form-label",text:"Effort Level"});let Te=Ae.createEl("select",{cls:"af-form-select"});for(let[M,H]of[["","Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let ne=Te.createEl("option",{text:H,attr:{value:M}});M===(n.effort??"")&&(ne.selected=!0)}if(Te.addEventListener("change",()=>{h.effort=Te.value}),L.createDiv({cls:"af-form-hint",text:"Controls reasoning depth \u2014 low is fast, max is most thorough"}),n.isFolder){let M=u.createDiv({cls:"af-create-section"}),H=M.createDiv({cls:"af-create-section-header"}),ne=H.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ne,"heart-pulse");let _e=H.createSpan({text:"Heartbeat"});this.addTooltip(_e,"Autonomous periodic run \u2014 what the agent does when no one is asking");let He=M.createDiv({cls:"af-form-row af-form-row-toggle"});He.createDiv({cls:"af-form-label",text:"Enabled"});let Le=He.createDiv({cls:`af-agent-card-toggle${h.heartbeatEnabled?" on":""}`}),Me=M.createDiv();Me.style.display=h.heartbeatEnabled?"":"none",Le.onclick=()=>{let Je=Le.hasClass("on");Le.toggleClass("on",!Je),h.heartbeatEnabled=!Je,Me.style.display=Je?"none":""},this.renderHeartbeatSchedule(Me,h);let dt=Me.createDiv({cls:"af-form-row af-form-row-toggle"}),qe=dt.createDiv({cls:"af-form-label"});qe.setText("Notify"),this.addTooltip(qe,"Show an Obsidian notice when the heartbeat completes");let sa=dt.createDiv({cls:`af-agent-card-toggle${h.heartbeatNotify?" on":""}`});sa.onclick=()=>{let Je=sa.hasClass("on");sa.toggleClass("on",!Je),h.heartbeatNotify=!Je};let Ui=this.plugin.runtime.getSnapshot(),qa=Me.createDiv({cls:"af-form-row"}),Wa=qa.createDiv({cls:"af-form-label"});Wa.setText("Post to channel"),this.addTooltip(Wa,"Heartbeat results are posted to this Slack channel when the run completes");let fs=qa.createEl("select",{cls:"af-form-select"});fs.createEl("option",{text:"(none)",attr:{value:""}});for(let Je of Ui.channels){let $i=fs.createEl("option",{text:Je.name,attr:{value:Je.name}});Je.name===h.heartbeatChannel&&($i.selected=!0)}fs.addEventListener("change",()=>{h.heartbeatChannel=fs.value});let ms=Me.createDiv({cls:"af-form-label"});ms.style.width="auto",ms.style.marginTop="12px",ms.setText("Instruction"),this.addTooltip(ms,'What the agent does on each heartbeat. Also used by the "Run Now" button.');let aa=Me.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Check status, scan for issues, report findings...",rows:"8"}});aa.value=h.heartbeatBody,aa.addEventListener("input",()=>{h.heartbeatBody=aa.value})}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,w.setIcon)(le,"puzzle"),Z.createSpan({text:"Skills"});let ae=this.plugin.runtime.getSnapshot();if(ae.skills.length>0){D.createDiv({cls:"af-form-sublabel",text:"Shared Skills"});let M=D.createDiv({cls:"af-create-skills-grid"});for(let H of ae.skills){let ne=M.createDiv({cls:"af-create-skill-item"}),_e=ne.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedSkills.has(H.name),_e.addEventListener("change",()=>{_e.checked?h.selectedSkills.add(H.name):h.selectedSkills.delete(H.name)});let He=ne.createDiv({cls:"af-create-skill-label"});He.createSpan({cls:"af-create-skill-name",text:H.name}),H.description&&He.createSpan({cls:"af-create-skill-desc",text:` \u2014 ${H.description}`})}}let ke=D.createDiv({cls:"af-form-sublabel"});ke.setText("Agent-specific skills"),this.addTooltip(ke,"Custom skills/instructions only for this agent, not shared with others");let xe=D.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Custom skills/instructions for this agent...",rows:"4"}});xe.value=n.skillsBody,xe.addEventListener("input",()=>{h.skillsBody=xe.value});let j=u.createDiv({cls:"af-create-section"}),N=j.createDiv({cls:"af-create-section-header"}),ue=N.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ue,"plug");let fe=N.createSpan({text:"MCP Servers"});this.addTooltip(fe,"Grant agent access to MCP servers");let Xe=this.plugin.mcpManager.getCachedServers();if(Xe===null){let M=j.createDiv({cls:"af-form-hint"});M.appendText("MCP servers not loaded. ");let H=M.createEl("a",{cls:"af-link",text:"Go to MCP Servers tab to load them."});H.onclick=ne=>{ne.preventDefault(),this.navigate("mcp")}}else if(Xe.length===0)j.createDiv({cls:"af-form-hint",text:"No MCP servers found. Configure them with 'claude mcp add'."});else{let M=j.createDiv({cls:"af-create-skills-grid"});for(let H of Xe){let ne=M.createDiv({cls:"af-mcp-agent-item"}),_e=ne.createEl("input",{cls:"af-form-toggle",attr:{type:"checkbox"}});_e.checked=h.selectedMcpServers.has(H.name),_e.addEventListener("change",()=>{_e.checked?h.selectedMcpServers.add(H.name):h.selectedMcpServers.delete(H.name)});let Le=ne.createDiv({cls:"af-mcp-agent-label"}).createDiv({cls:"af-mcp-agent-name-row"}),Me=Le.createSpan({cls:`af-mcp-status-dot ${H.enabled?H.status:"disabled"}`});Me.title=H.enabled?H.status:"disabled",Le.createSpan({cls:"af-create-skill-name",text:H.name});let dt=H.toolDetails.length||H.tools.length;dt>0?Le.createSpan({cls:"af-mcp-agent-tool-count",text:`${dt} tools`}):H.enabled?H.status==="needs-auth"&&Le.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"needs auth"}):Le.createSpan({cls:"af-mcp-agent-tool-count af-muted",text:"disabled"})}}let cs=u.createDiv({cls:"af-create-section"}),qt=cs.createDiv({cls:"af-create-section-header"}),ds=qt.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ds,"file-text");let hs=qt.createSpan({text:"Context"});this.addTooltip(hs,"Project-specific context included in every run");let Ct=cs.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:"Background info, repo structure, conventions...",rows:"4"}});Ct.value=n.contextBody,Ct.addEventListener("input",()=>{h.contextBody=Ct.value});let ct=u.createDiv({cls:"af-create-section"}),_t=ct.createDiv({cls:"af-create-section-header"}),us=_t.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(us,"shield-check"),_t.createSpan({text:"Permissions"}),this.createFormField(ct,"Approval required","git_push, file_delete","Comma-separated tool names",M=>{h.approvalRequired=M},n.approvalRequired.join(", "));let ps=ct.createDiv({cls:"af-form-row"});ps.createDiv({cls:"af-form-label",text:"Allowed Commands"});let ft=ps.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(curl *)
|
|
11246
11595
|
Bash(python3 *)
|
|
11247
11596
|
Read
|
|
11248
11597
|
Edit
|
|
11249
|
-
Write`,rows:"4"}});
|
|
11250
|
-
`),
|
|
11598
|
+
Write`,rows:"4"}});ft.value=n.permissionRules.allow.join(`
|
|
11599
|
+
`),ft.addEventListener("input",()=>{h.allowedCommands=ft.value});let O=ct.createDiv({cls:"af-form-row"});O.createDiv({cls:"af-form-label",text:"Blocked Commands"});let X=O.createEl("textarea",{cls:"af-create-textarea",attr:{placeholder:`Bash(git push *)
|
|
11251
11600
|
Bash(rm -rf *)
|
|
11252
|
-
Bash(sudo *)`,rows:"4"}});
|
|
11253
|
-
`),Y.addEventListener("input",()=>{u.blockedCommands=Y.value});let we=nt.createDiv({cls:"af-form-row"});we.createDiv({cls:"af-form-label",text:"Memory enabled"});let de=we.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});de.onclick=()=>{let F=de.hasClass("on");de.toggleClass("on",!F),u.memory=!F};let ie=s.createDiv({cls:"af-create-footer"}),Ee=ie.createEl("button",{cls:"af-btn-sm danger"});_(Ee,"trash-2","af-btn-icon"),Ee.appendText(" Delete"),Ee.onclick=()=>void this.plugin.deleteAgent(n.name),ie.createDiv({cls:"af-toolbar-spacer"});let le=ie.createEl("button",{cls:"af-btn-sm",text:"Cancel"});le.onclick=()=>this.navigate("agent-detail",n.name);let $e=ie.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_($e,"check","af-btn-icon"),$e.appendText(" Save Changes"),$e.onclick=async()=>{let F=B=>B.split(",").map(Z=>Z.trim()).filter(Boolean);try{let B=Z=>Z.split(`
|
|
11254
|
-
`).map(ye=>ye.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:u.description.trim(),avatar:u.avatar.trim(),tags:F(u.tags),systemPrompt:u.systemPrompt.trim(),model:u.model.trim()||"default",adapter:u.adapter,cwd:u.cwd.trim(),timeout:u.timeout,permissionMode:u.permissionMode,approvalRequired:F(u.approvalRequired),memory:u.memory,skills:Array.from(u.selectedSkills),mcpServers:Array.from(u.selectedMcpServers),skillsBody:u.skillsBody.trim(),contextBody:u.contextBody.trim(),enabled:u.enabled,permissionRules:{allow:B(u.allowedCommands),deny:B(u.blockedCommands)}}),n.isFolder&&await this.plugin.repository.updateHeartbeat(n.name,{enabled:u.heartbeatEnabled,schedule:u.heartbeatSchedule.trim(),notify:u.heartbeatNotify,channel:u.heartbeatChannel,body:u.heartbeatBody.trim()}),new w.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(B){let Z=B instanceof Error?B.message:String(B);new w.Notice(`Failed to update agent: ${Z}`)}}}renderCreateTaskPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=this.plugin.runtime.getSnapshot(),n=s.createDiv({cls:"af-detail-header"}),i=n.createDiv({cls:"af-detail-header-left"}),o=i.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(o,"plus");let 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,schedule:"0 9 * * *",type:"immediate",enabled:!0,catchUp:!0},d=s.createDiv({cls:"af-create-form"}),u=d.createDiv({cls:"af-create-section"}),h=u.createDiv({cls:"af-create-section-header"}),m=h.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"file-text"),h.createSpan({text:"Task Details"}),this.createFormField(u,"Title","Daily status report","Used as the task identifier",P=>{c.title=P});let f=u.createDiv({cls:"af-form-row"});f.createDiv({cls:"af-form-label",text:"Agent"});let p=f.createEl("select",{cls:"af-form-select"});for(let P of a.agents)p.createEl("option",{text:P.name,attr:{value:P.name}});p.addEventListener("change",()=>{c.agent=p.value});let b=u.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Priority"});let k=b.createEl("select",{cls:"af-form-select"}),v=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[P,M]of v){let I=k.createEl("option",{text:M,attr:{value:P}});P==="medium"&&(I.selected=!0)}k.addEventListener("change",()=>{c.priority=k.value}),this.createFormField(u,"Tags","monitoring, devops","Comma-separated",P=>{c.tags=P});let g=d.createDiv({cls:"af-create-section"}),y=g.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"Instructions"});let T=g.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"}),A=C.createDiv({cls:"af-create-section-header"}),E=A.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(E,"clock"),A.createSpan({text:"Schedule"});let R=C.createDiv({cls:"af-form-row af-form-row-toggle"});R.createDiv({cls:"af-form-label",text:"Enable schedule"});let U=R.createDiv({cls:"af-agent-card-toggle"}),D=C.createDiv({cls:"af-schedule-body"});D.style.display="none",U.onclick=()=>{let P=U.hasClass("on");U.toggleClass("on",!P),c.scheduleEnabled=!P,D.style.display=P?"none":"",P?c.type="immediate":c.type="recurring"},this.renderInlineSchedule(D,c);let K=D.createDiv({cls:"af-form-row af-form-row-toggle"});K.createDiv({cls:"af-form-label",text:"Enabled"});let q=K.createDiv({cls:"af-agent-card-toggle on"});q.onclick=()=>{let P=q.hasClass("on");q.toggleClass("on",!P),c.enabled=!P};let V=D.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 P=$.hasClass("on");$.toggleClass("on",!P),c.catchUp=!P};let W=s.createDiv({cls:"af-create-footer"}),X=W.createEl("button",{cls:"af-btn-sm",text:"Cancel"});X.onclick=()=>this.navigate("kanban");let H=W.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(H,"plus","af-btn-icon"),H.appendText(" Create Task"),H.onclick=async()=>{let P=c.title.trim();if(!P){new w.Notice("Task title is required.");return}let M=ve(P),I=G=>G.split(",").map(O=>O.trim()).filter(Boolean),Q=c.scheduleEnabled?"recurring":"immediate",ne={task_id:M,agent:c.agent,type:Q,priority:c.priority,enabled:c.enabled,created:this.toLocalISO(new Date),run_count:0,catch_up:c.catchUp,tags:I(c.tags)};Q==="recurring"&&(ne.schedule=c.schedule.trim()||"0 9 * * *");try{let G=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),M);await this.plugin.app.vault.create(G,ee(ne,c.body.trim()||"Describe the task here.")),new w.Notice(`Task "${M}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",M)}catch(G){let O=G instanceof Error?G.message:String(G);new w.Notice(`Failed to create task: ${O}`)}}}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())}`}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(O=>O.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,w.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 u=!!(n.schedule||n.runAt),h={agent:n.agent,type:n.type,priority:n.priority,schedule:n.schedule??"0 9 * * *",scheduleEnabled:u,enabled:n.enabled,catchUp:n.catchUp,tags:n.tags.join(", "),body:n.body},m=s.createDiv({cls:"af-create-form"}),f=m.createDiv({cls:"af-create-section"}),p=f.createDiv({cls:"af-create-section-header"}),b=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(b,"file-text"),p.createSpan({text:"Task Details"});let k=f.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let v=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});v.style.opacity="0.6";let g=f.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Agent"});let y=g.createEl("select",{cls:"af-form-select"});for(let O of i.agents){let fe=y.createEl("option",{text:O.name,attr:{value:O.name}});O.name===n.agent&&(fe.selected=!0)}y.addEventListener("change",()=>{h.agent=y.value});let x=f.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Priority"});let T=x.createEl("select",{cls:"af-form-select"}),C=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[O,fe]of C){let ce=T.createEl("option",{text:fe,attr:{value:O}});O===n.priority&&(ce.selected=!0)}T.addEventListener("change",()=>{h.priority=T.value}),this.createFormField(f,"Tags","monitoring, critical","Comma-separated",O=>{h.tags=O},n.tags.join(", "));let A=m.createDiv({cls:"af-create-section"}),E=A.createDiv({cls:"af-create-section-header"}),R=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"message-square"),E.createSpan({text:"Instructions"});let U=A.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});U.value=n.body,U.addEventListener("input",()=>{h.body=U.value});let D=m.createDiv({cls:"af-create-section"}),K=D.createDiv({cls:"af-create-section-header"}),q=K.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(q,"clock"),K.createSpan({text:"Schedule"});let V=D.createDiv({cls:"af-form-row af-form-row-toggle"});V.createDiv({cls:"af-form-label",text:"Enable schedule"});let j=V.createDiv({cls:`af-agent-card-toggle${u?" on":""}`}),$=D.createDiv({cls:"af-schedule-body"});$.style.display=u?"":"none",j.onclick=()=>{let O=j.hasClass("on");j.toggleClass("on",!O),h.scheduleEnabled=!O,$.style.display=O?"none":"",O?h.type="immediate":h.type="recurring"},this.renderInlineSchedule($,h);let W=$.createDiv({cls:"af-form-row af-form-row-toggle"});W.createDiv({cls:"af-form-label",text:"Enabled"});let X=W.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});X.onclick=()=>{let O=X.hasClass("on");X.toggleClass("on",!O),h.enabled=!O};let H=$.createDiv({cls:"af-form-row af-form-row-toggle"});H.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let M=H.createDiv({cls:`af-agent-card-toggle${h.catchUp?" on":""}`});M.onclick=()=>{let O=M.hasClass("on");M.toggleClass("on",!O),h.catchUp=!O};let I=s.createDiv({cls:"af-create-footer"}),Q=I.createEl("button",{cls:"af-btn-sm danger"});_(Q,"trash-2","af-btn-icon"),Q.appendText(" Delete"),Q.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new w.Notice(`Task "${n.taskId}" deleted.`),await new Promise(O=>setTimeout(O,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},I.createDiv({cls:"af-toolbar-spacer"});let ne=I.createEl("button",{cls:"af-btn-sm",text:"Cancel"});ne.onclick=()=>this.navigate("task-detail",n.taskId);let G=I.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(G,"check","af-btn-icon"),G.appendText(" Save Changes"),G.onclick=async()=>{let O=ce=>ce.split(",").map(Te=>Te.trim()).filter(Boolean),fe=h.scheduleEnabled?"recurring":"immediate";try{await this.plugin.repository.updateTask(n.taskId,{agent:h.agent,type:fe,priority:h.priority,schedule:h.scheduleEnabled?h.schedule.trim():void 0,enabled:h.enabled,catch_up:h.catchUp,tags:O(h.tags),body:h.body.trim()}),new w.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(ce){let Te=ce instanceof Error?ce.message:String(ce);new w.Notice(`Failed to update task: ${Te}`)}}}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(ne=>ne.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,w.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"}),u={description:n.description??"",tags:n.tags.join(", "),body:n.body,toolsBody:n.toolsBody,referencesBody:n.referencesBody,examplesBody:n.examplesBody},h=s.createDiv({cls:"af-create-form"}),m=h.createDiv({cls:"af-create-section"}),f=m.createDiv({cls:"af-create-section-header"}),p=f.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(p,"puzzle"),f.createSpan({text:"Identity"});let b=m.createDiv({cls:"af-form-row"});b.createDiv({cls:"af-form-label",text:"Name"});let k=b.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.name,disabled:"true"}});k.style.opacity="0.6",this.createFormField(m,"Description","Manage tasks and projects via CLI","",ne=>{u.description=ne},n.description??""),this.createFormField(m,"Tags","productivity, tasks","Comma-separated",ne=>{u.tags=ne},n.tags.join(", "));let v=h.createDiv({cls:"af-create-section"}),g=v.createDiv({cls:"af-create-section-header"}),y=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(y,"file-text"),g.createSpan({text:"Core Instructions"});let x=v.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});x.value=n.body,x.addEventListener("input",()=>{u.body=x.value});let T=h.createDiv({cls:"af-create-section"}),C=T.createDiv({cls:"af-create-section-header"}),A=C.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(A,"wrench");let E=C.createSpan({text:"Tools"});this.addTooltip(E,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let R=T.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
|
|
11601
|
+
Bash(sudo *)`,rows:"4"}});X.value=n.permissionRules.deny.join(`
|
|
11602
|
+
`),X.addEventListener("input",()=>{h.blockedCommands=X.value});let Re=ct.createDiv({cls:"af-form-row"});Re.createDiv({cls:"af-form-label",text:"Memory enabled"});let be=Re.createDiv({cls:`af-agent-card-toggle${n.memory?" on":""}`});be.onclick=()=>{let M=be.hasClass("on");be.toggleClass("on",!M),h.memory=!M};let Se=s.createDiv({cls:"af-create-footer"}),Ie=Se.createEl("button",{cls:"af-btn-sm danger"});_(Ie,"trash-2","af-btn-icon"),Ie.appendText(" Delete"),Ie.onclick=()=>void this.plugin.deleteAgent(n.name),Se.createDiv({cls:"af-toolbar-spacer"});let me=Se.createEl("button",{cls:"af-btn-sm",text:"Cancel"});me.onclick=()=>this.navigate("agent-detail",n.name);let Ve=Se.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(Ve,"check","af-btn-icon"),Ve.appendText(" Save Changes"),Ve.onclick=async()=>{let M=H=>H.split(",").map(ne=>ne.trim()).filter(Boolean);try{let H=ne=>ce(ne).map(_e=>_e.trim()).filter(Boolean);await this.plugin.repository.updateAgent(n.name,{description:h.description.trim(),avatar:h.avatar.trim(),tags:M(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:M(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(h.allowedCommands),deny:H(h.blockedCommands)}}),n.isFolder&&await this.plugin.repository.updateHeartbeat(n.name,{enabled:h.heartbeatEnabled,schedule:h.heartbeatSchedule.trim(),notify:h.heartbeatNotify,channel:h.heartbeatChannel,body:h.heartbeatBody.trim()}),new w.Notice(`Agent "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("agent-detail",n.name)}catch(H){let ne=H instanceof Error?H.message:String(H);new w.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,w.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,schedule:"0 9 * * *",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"}),f=u.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(f,"file-text"),u.createSpan({text:"Task Details"}),this.createFormField(h,"Title","Daily status report","Used as the task identifier",D=>{c.title=D});let p=h.createDiv({cls:"af-form-row"});p.createDiv({cls:"af-form-label",text:"Agent"});let m=p.createEl("select",{cls:"af-form-select"});for(let D of a.agents)m.createEl("option",{text:D.name,attr:{value:D.name}});m.addEventListener("change",()=>{c.agent=m.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"}),b=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[D,Z]of b){let le=k.createEl("option",{text:Z,attr:{value:D}});D==="medium"&&(le.selected=!0)}k.addEventListener("change",()=>{c.priority=k.value}),this.createFormField(h,"Tags","monitoring, devops","Comma-separated",D=>{c.tags=D});let g=d.createDiv({cls:"af-create-section"}),y=g.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"message-square"),y.createSpan({text:"Instructions"});let S=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});S.addEventListener("input",()=>{c.body=S.value});let T=d.createDiv({cls:"af-create-section"}),P=T.createDiv({cls:"af-create-section-header"}),E=P.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(E,"clock"),P.createSpan({text:"Schedule"});let R=T.createDiv({cls:"af-form-row af-form-row-toggle"});R.createDiv({cls:"af-form-label",text:"Enable schedule"});let W=R.createDiv({cls:"af-agent-card-toggle"}),I=T.createDiv({cls:"af-schedule-body"});I.style.display="none",W.onclick=()=>{let D=W.hasClass("on");W.toggleClass("on",!D),c.scheduleEnabled=!D,I.style.display=D?"none":"",D?c.type="immediate":c.type="recurring"},this.renderInlineSchedule(I,c);let q=I.createDiv({cls:"af-form-row af-form-row-toggle"});q.createDiv({cls:"af-form-label",text:"Enabled"});let L=q.createDiv({cls:"af-agent-card-toggle on"});L.onclick=()=>{let D=L.hasClass("on");L.toggleClass("on",!D),c.enabled=!D};let K=I.createDiv({cls:"af-form-row af-form-row-toggle"});K.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let z=K.createDiv({cls:`af-agent-card-toggle${c.catchUp?" on":""}`});z.onclick=()=>{let D=z.hasClass("on");z.toggleClass("on",!D),c.catchUp=!D};let ie=d.createDiv({cls:"af-create-section"}),Q=ie.createDiv({cls:"af-create-section-header"}),te=Q.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(te,"gauge"),Q.createSpan({text:"Execution"});let G=ie.createDiv({cls:"af-form-row"}),B=G.createDiv({cls:"af-form-label",text:"Model"}),F=G.createDiv({cls:"af-form-field-wrap"}),$=D=>{F.empty();let Z=a.agents.find(le=>le.name===D);vt(F,{value:c.model,onChange:le=>{c.model=le},allowInherit:!0,inheritPlaceholder:Z?`Inherit from ${Z.name}${Z.model?` (${Z.model})`:""}`:"Inherit from agent"})};$(c.agent),m.addEventListener("change",()=>$(m.value)),this.addTooltip(B,"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 ee=ie.createDiv({cls:"af-form-row"}),se=ee.createDiv({cls:"af-form-label",text:"Effort"}),oe=ee.createEl("select",{cls:"af-form-select"});for(let[D,Z]of[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]]){let le=oe.createEl("option",{text:Z,attr:{value:D}});D===c.effort&&(le.selected=!0)}oe.addEventListener("change",()=>{c.effort=oe.value}),this.addTooltip(se,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let Ce=s.createDiv({cls:"af-create-footer"}),Ae=Ce.createEl("button",{cls:"af-btn-sm",text:"Cancel"});Ae.onclick=()=>this.navigate("kanban");let Te=Ce.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(Te,"plus","af-btn-icon"),Te.appendText(" Create Task"),Te.onclick=async()=>{let D=c.title.trim();if(!D){new w.Notice("Task title is required.");return}let Z=ge(D),le=xe=>xe.split(",").map(j=>j.trim()).filter(Boolean),ae=c.scheduleEnabled?"recurring":"immediate",ke={task_id:Z,agent:c.agent,type:ae,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:le(c.tags)};ae==="recurring"&&(ke.schedule=c.schedule.trim()||"0 9 * * *");try{let xe=await this.plugin.repository.getAvailablePath(this.plugin.repository.getSubfolder("tasks"),Z);await this.plugin.app.vault.create(xe,Y(ke,c.body.trim()||"Describe the task here.")),new w.Notice(`Task "${Z}" created.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",Z)}catch(xe){let j=xe instanceof Error?xe.message:String(xe);new w.Notice(`Failed to create task: ${j}`)}}}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())}`}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(N=>N.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,w.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 * * *",scheduleEnabled:h,enabled:n.enabled,catchUp:n.catchUp,effort:n.effort??"",model:n.model??"",tags:n.tags.join(", "),body:n.body},f=s.createDiv({cls:"af-create-form"}),p=f.createDiv({cls:"af-create-section"}),m=p.createDiv({cls:"af-create-section-header"}),v=m.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(v,"file-text"),m.createSpan({text:"Task Details"});let k=p.createDiv({cls:"af-form-row"});k.createDiv({cls:"af-form-label",text:"Title"});let b=k.createEl("input",{cls:"af-form-input",attr:{type:"text",value:n.taskId,disabled:"true"}});b.style.opacity="0.6";let g=p.createDiv({cls:"af-form-row"});g.createDiv({cls:"af-form-label",text:"Agent"});let y=g.createEl("select",{cls:"af-form-select"});for(let N of i.agents){let ue=y.createEl("option",{text:N.name,attr:{value:N.name}});N.name===n.agent&&(ue.selected=!0)}y.addEventListener("change",()=>{u.agent=y.value});let x=p.createDiv({cls:"af-form-row"});x.createDiv({cls:"af-form-label",text:"Priority"});let S=x.createEl("select",{cls:"af-form-select"}),T=[["low","Low"],["medium","Medium"],["high","High"],["critical","Critical"]];for(let[N,ue]of T){let fe=S.createEl("option",{text:ue,attr:{value:N}});N===n.priority&&(fe.selected=!0)}S.addEventListener("change",()=>{u.priority=S.value}),this.createFormField(p,"Tags","monitoring, critical","Comma-separated",N=>{u.tags=N},n.tags.join(", "));let P=f.createDiv({cls:"af-create-section"}),E=P.createDiv({cls:"af-create-section-header"}),R=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"message-square"),E.createSpan({text:"Instructions"});let W=P.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Describe what the agent should do...",rows:"10"}});W.value=n.body,W.addEventListener("input",()=>{u.body=W.value});let I=f.createDiv({cls:"af-create-section"}),q=I.createDiv({cls:"af-create-section-header"}),L=q.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(L,"clock"),q.createSpan({text:"Schedule"});let K=I.createDiv({cls:"af-form-row af-form-row-toggle"});K.createDiv({cls:"af-form-label",text:"Enable schedule"});let V=K.createDiv({cls:`af-agent-card-toggle${h?" on":""}`}),z=I.createDiv({cls:"af-schedule-body"});z.style.display=h?"":"none",V.onclick=()=>{let N=V.hasClass("on");V.toggleClass("on",!N),u.scheduleEnabled=!N,z.style.display=N?"none":"",N?u.type="immediate":u.type="recurring"},this.renderInlineSchedule(z,u);let ie=z.createDiv({cls:"af-form-row af-form-row-toggle"});ie.createDiv({cls:"af-form-label",text:"Enabled"});let Q=ie.createDiv({cls:`af-agent-card-toggle${n.enabled?" on":""}`});Q.onclick=()=>{let N=Q.hasClass("on");Q.toggleClass("on",!N),u.enabled=!N};let te=z.createDiv({cls:"af-form-row af-form-row-toggle"});te.createDiv({cls:"af-form-label"}).setText("Catch up if missed");let B=te.createDiv({cls:`af-agent-card-toggle${u.catchUp?" on":""}`});B.onclick=()=>{let N=B.hasClass("on");B.toggleClass("on",!N),u.catchUp=!N};let F=f.createDiv({cls:"af-create-section"}),$=F.createDiv({cls:"af-create-section-header"}),ee=$.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ee,"gauge"),$.createSpan({text:"Execution"});let se=F.createDiv({cls:"af-form-row"}),oe=se.createDiv({cls:"af-form-label",text:"Effort"}),Ce=se.createEl("select",{cls:"af-form-select"}),Ae=[["","Agent Default"],["low","Low"],["medium","Medium"],["high","High"],["max","Max"]];for(let[N,ue]of Ae){let fe=Ce.createEl("option",{text:ue,attr:{value:N}});N===u.effort&&(fe.selected=!0)}Ce.addEventListener("change",()=>{u.effort=Ce.value}),this.addTooltip(oe,"Overrides the agent\u2019s effort level for this task. Higher effort = more thinking tokens spent.");let Te=F.createDiv({cls:"af-form-row"}),D=Te.createDiv({cls:"af-form-label",text:"Model"}),Z=Te.createDiv({cls:"af-form-field-wrap"}),le=N=>{Z.empty();let ue=i.agents.find(fe=>fe.name===N);vt(Z,{value:u.model,onChange:fe=>{u.model=fe},allowInherit:!0,inheritPlaceholder:ue?`Inherit from ${ue.name}${ue.model?` (${ue.model})`:""}`:"Inherit from agent"})};le(u.agent),y.addEventListener("change",()=>le(y.value)),this.addTooltip(D,"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 ae=s.createDiv({cls:"af-create-footer"}),ke=ae.createEl("button",{cls:"af-btn-sm danger"});_(ke,"trash-2","af-btn-icon"),ke.appendText(" Delete"),ke.onclick=async()=>{await this.plugin.repository.deleteTask(n.taskId),new w.Notice(`Task "${n.taskId}" deleted.`),await new Promise(N=>setTimeout(N,200)),await this.plugin.refreshFromVault(),this.navigate("kanban")},ae.createDiv({cls:"af-toolbar-spacer"});let xe=ae.createEl("button",{cls:"af-btn-sm",text:"Cancel"});xe.onclick=()=>this.navigate("task-detail",n.taskId);let j=ae.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(j,"check","af-btn-icon"),j.appendText(" Save Changes"),j.onclick=async()=>{let N=fe=>fe.split(",").map(Xe=>Xe.trim()).filter(Boolean),ue=u.scheduleEnabled?"recurring":"immediate";try{await this.plugin.repository.updateTask(n.taskId,{agent:u.agent,type:ue,priority:u.priority,schedule:u.scheduleEnabled?u.schedule.trim():void 0,enabled:u.enabled,catch_up:u.catchUp,effort:u.effort||void 0,model:u.model||"",tags:N(u.tags),body:u.body.trim()}),new w.Notice(`Task "${n.taskId}" updated.`),await this.plugin.refreshFromVault(),this.navigate("task-detail",n.taskId)}catch(fe){let Xe=fe instanceof Error?fe.message:String(fe);new w.Notice(`Failed to update task: ${Xe}`)}}}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(ee=>ee.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,w.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"}),f=u.createDiv({cls:"af-create-section"}),p=f.createDiv({cls:"af-create-section-header"}),m=p.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(m,"puzzle"),p.createSpan({text:"Identity"});let v=f.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(f,"Description","Manage tasks and projects via CLI","",ee=>{h.description=ee},n.description??""),this.createFormField(f,"Tags","productivity, tasks","Comma-separated",ee=>{h.tags=ee},n.tags.join(", "));let b=u.createDiv({cls:"af-create-section"}),g=b.createDiv({cls:"af-create-section-header"}),y=g.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(y,"file-text"),g.createSpan({text:"Core Instructions"});let x=b.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"Skill instructions...",rows:"10"}});x.value=n.body,x.addEventListener("input",()=>{h.body=x.value});let S=u.createDiv({cls:"af-create-section"}),T=S.createDiv({cls:"af-create-section-header"}),P=T.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(P,"wrench");let E=T.createSpan({text:"Tools"});this.addTooltip(E,"CLI commands, API endpoints, and tool definitions available to agents using this skill");let R=S.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Commands
|
|
11255
11603
|
|
|
11256
11604
|
### list
|
|
11257
|
-
...`,rows:"8"}});R.value=n.toolsBody,R.addEventListener("input",()=>{
|
|
11258
|
-
...`,rows:"6"}});H.value=n.examplesBody,H.addEventListener("input",()=>{u.examplesBody=H.value});let P=s.createDiv({cls:"af-create-footer"}),M=P.createEl("button",{cls:"af-btn-sm danger"});_(M,"trash-2","af-btn-icon"),M.appendText(" Delete"),M.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new w.Notice(`Skill "${n.name}" deleted.`),await new Promise(ne=>setTimeout(ne,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},P.createDiv({cls:"af-toolbar-spacer"});let I=P.createEl("button",{cls:"af-btn-sm",text:"Cancel"});I.onclick=()=>this.navigate("skills");let Q=P.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(Q,"check","af-btn-icon"),Q.appendText(" Save Changes"),Q.onclick=async()=>{let ne=G=>G.split(",").map(O=>O.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:u.description.trim(),tags:ne(u.tags),body:u.body.trim(),toolsBody:u.toolsBody.trim(),referencesBody:u.referencesBody.trim(),examplesBody:u.examplesBody.trim()}),new w.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(G){let O=G instanceof Error?G.message:String(G);new w.Notice(`Failed to update skill: ${O}`)}}}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"});_(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"});_(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"}),u=d.createDiv({cls:"af-mcp-spinner"});for(let k=0;k<3;k++)u.createSpan();let h=d.createSpan({cls:"af-mcp-progress-label",text:"Discovering MCP servers\u2026"}),f=c.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});f.style.width="15%";let p=c.createDiv({cls:"af-mcp-progress-detail",text:"Scanning for configured servers\u2026"}),b=this.plugin.mcpManager.onProgress(k=>{switch(k.phase){case"list":h.setText("Scanning servers\u2026"),p.setText(k.message),f.style.width="20%";break;case"details":h.setText(`Checking server ${k.current}/${k.total}\u2026`),p.setText(k.serverName),f.style.width=`${20+k.current/k.total*30}%`;break;case"tools":h.setText("Discovering tools\u2026"),p.setText(k.message),f.style.width="60%",f.addClass("af-mcp-progress-fill-slow");break;case"done":f.style.width="100%",h.setText("Done"),p.setText(`${k.serverCount} server${k.serverCount!==1?"s":""}, ${k.toolCount} tool${k.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(b),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,w.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=g=>{g.stopPropagation(),this.plugin.mcpManager.toggleServerEnabled(s.name,!s.enabled).then(()=>{this.plugin.mcpManager.invalidateCache(),this.render()})};let u=a.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),h=u.createSpan();if(s.enabled?((0,w.setIcon)(h,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),u.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,w.setIcon)(h,"pause"),u.createSpan({text:" Disabled"})),s.description){let g=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:g})}let m=s.url??s.command??"";m&&a.createDiv({cls:"af-mcp-command",text:kt(m,60)});let f=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",p=a.createDiv({cls:"af-mcp-tool-footer"}),b=p.createDiv({cls:"af-mcp-tool-count"}),k=b.createSpan();(0,w.setIcon)(k,"wrench"),b.createSpan({text:` ${f}`});let v=s.toolDetails.length>0?s.toolDetails.map(g=>g.name):s.tools;if(v.length>0){let g=p.createDiv({cls:"af-mcp-tool-chips"}),y=v.slice(0,4);for(let x of y)g.createSpan({cls:"af-mcp-tool-chip",text:x});v.length>4&&g.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${v.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,w.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,w.setIcon)(x,"key"),y.appendText(" Authenticate"),y.onclick=T=>{T.stopPropagation(),this.authenticateMcpServer(s)}}else if(s.enabled&&s.status==="connected"&&s.type!=="stdio"&&s.toolDetails.length===0){let g=a.createDiv({cls:"af-mcp-hint-row"});g.createSpan({text:"Tools available to agents via Claude \u2014 "});let y=g.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 w.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new w.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new w.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new w.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=e.split(`
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
`)){let M=P.trim();if(!M)continue;let I=M.indexOf(":");if(I<=0){new w.Notice(`Invalid header: ${M}`);return}X[M.slice(0,I).trim()]=M.slice(I+1).trim()}let H=l.args.trim()?l.args.trim().split(/\s+/):void 0;j.disabled=!0,j.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:$,transport:l.transport,scope:l.scope,command:l.transport==="stdio"?l.command.trim():void 0,args:l.transport==="stdio"?H:void 0,envVars:l.transport==="stdio"&&Object.keys(W).length>0?W:void 0,url:l.transport!=="stdio"?l.url.trim():void 0,headers:l.transport!=="stdio"&&Object.keys(X).length>0?X:void 0}),new w.Notice(`Server "${$}" added successfully.`),this.navigate("mcp")}catch(P){let M=P instanceof Error?P.message:String(P);new w.Notice(`Failed to add server: ${M}`),j.disabled=!1,j.setText(""),_(j,"plus","af-btn-icon"),j.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,w.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function Zn(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 fa(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 io(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",u=`${o===0?12:o>12?o-12:o}:${String(l).padStart(2,"0")} ${c}`;return i==="*"?`Daily at ${u}`:i==="1-5"?`Weekdays at ${u}`:`${u} on days ${i}`}}}return r}function ro(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 re=require("obsidian");var Lt=class r extends re.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;workingIndicator=null;getViewType(){return He}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.plugin.unsubscribeView(this)}selectAgent(e){let s=this.plugin.app.workspace.getLeavesOfType(He);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"});_(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(He);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"}),_(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"}),_(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.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 u=c[d];if(u.type.startsWith("image/")){l.preventDefault();let h=u.getAsFile();h&&this.attachImageBlob(h);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 u=c[d];u.type.startsWith("image/")&&this.attachImageBlob(u)}})}async render(){this.populateAgentDropdown()}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,re.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,re.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(i=>i.name===e);if(!a)return;this.selectedAgentName=e,this.leaf.updateHeader(),this.activityEl=null,this.streamingDot=null,this.messagesInner.empty();let n=this.sessions.get(e);if(!n){let i=new xt(a,this.plugin.settings,this.plugin.repository,this.app.vault);n={session:i},this.sessions.set(e,n),await i.loadPersistedState()}for(let i of n.session.messages)if(i.role==="user")this.addBubble("user",i.content,i.attachments);else{let o=this.addBubble("assistant");this.renderMarkdownBubble(o,i.content),o._setRawText?.(i.content),i.toolCalls&&i.toolCalls.length>0&&this.buildToolSummary(i.toolCalls)}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"),re.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),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,re.setIcon)(l,"copy"),l.onclick=c=>{c.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{l.addClass("copied"),(0,re.setIcon)(l,"check"),setTimeout(()=>{l.removeClass("copied"),(0,re.setIcon)(l,"copy")},1500)})},i.style.position="relative",i.appendChild(l)})})}addCopyBtn(e,s){let a=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,re.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,re.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,re.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,re.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}buildToolSummary(e){let s=this.messagesInner.createDiv({cls:"af-chat-tool-summary"}),a=s.createEl("details"),n=a.createEl("summary"),i=new Map;for(let c of e){let d=i.get(c.name)??[];c.command&&d.push(c.command),i.set(c.name,d)}let o=n.createSpan({cls:"af-chat-tool-icon"});(0,re.setIcon)(o,"wrench"),n.appendText(` ${e.length} tool call${e.length!==1?"s":""}`);let l=a.createDiv({cls:"af-chat-tool-list"});for(let[c,d]of i){let u=d.length||(i.get(c)?.length??1),h=l.createDiv({cls:"af-chat-tool-item"}),m=u>1?`${c} (\xD7${u})`:c;h.createSpan({cls:"af-chat-tool-name",text:m}),d.length===1&&d[0]&&h.createSpan({cls:"af-chat-tool-cmd",text:d[0]})}return s}setActivity(e){e?(this.activityEl||(this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"})),this.activityEl.setText(`Working\u2026 (${e})`)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null)}setStreaming(e){if(e&&!this.streamingDot){this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let s=0;s<3;s++)this.streamingDot.createSpan()}else!e&&this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(e)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(_(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(_(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.setActivity(),this.setStreaming(!1),this.addBubble("error","Generation stopped"))}showWorkingIndicator(e){if(e&&!this.workingIndicator){let s=this.textarea.closest(".af-chat-input-area");s&&(this.workingIndicator=createDiv({cls:"af-chat-working-indicator"}),this.workingIndicator.setText("Agent is working\u2026"),s.insertBefore(this.workingIndicator,s.firstChild))}else!e&&this.workingIndicator&&(this.workingIndicator.remove(),this.workingIndicator=null)}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new re.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new re.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 re.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async attachImageBlob(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}`;if(this.attachedImages.some(l=>l.name===n)){new re.Notice(`"${n}" is already attached`);return}let 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 u=`${this.app.vault.adapter.getBasePath?.()??""}/${o}`;this.attachedImages.push({name:n,path:u}),this.renderPills()}catch(l){let c=l instanceof Error?l.message:String(l);new re.Notice(`Failed to save image: ${c}`)}}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,re.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,re.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,re.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,re.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}}async buildAttachmentContext(){if(this.attachedFiles.length+this.attachedImages.length===0)return"";let s=[];for(let a of this.attachedFiles)try{let n=await this.app.vault.cachedRead(a);s.push(`### ${a.name}
|
|
11605
|
+
...`,rows:"8"}});R.value=n.toolsBody,R.addEventListener("input",()=>{h.toolsBody=R.value});let W=u.createDiv({cls:"af-create-section"}),I=W.createDiv({cls:"af-create-section-header"}),q=I.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(q,"book-open");let L=I.createSpan({text:"References"});this.addTooltip(L,"Background docs, conventions, cheat sheets");let K=W.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"API docs, filter syntax, conventions...",rows:"6"}});K.value=n.referencesBody,K.addEventListener("input",()=>{h.referencesBody=K.value});let V=u.createDiv({cls:"af-create-section"}),z=V.createDiv({cls:"af-create-section-header"}),ie=z.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(ie,"message-circle");let Q=z.createSpan({text:"Examples"});this.addTooltip(Q,"Example prompts and ideal outputs showing how to use this skill");let te=V.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`## Example: List all tasks
|
|
11606
|
+
...`,rows:"6"}});te.value=n.examplesBody,te.addEventListener("input",()=>{h.examplesBody=te.value});let G=s.createDiv({cls:"af-create-footer"}),B=G.createEl("button",{cls:"af-btn-sm danger"});_(B,"trash-2","af-btn-icon"),B.appendText(" Delete"),B.onclick=async()=>{await this.plugin.repository.deleteSkill(n.name),new w.Notice(`Skill "${n.name}" deleted.`),await new Promise(ee=>setTimeout(ee,200)),await this.plugin.refreshFromVault(),this.navigate("skills")},G.createDiv({cls:"af-toolbar-spacer"});let F=G.createEl("button",{cls:"af-btn-sm",text:"Cancel"});F.onclick=()=>this.navigate("skills");let $=G.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_($,"check","af-btn-icon"),$.appendText(" Save Changes"),$.onclick=async()=>{let ee=se=>se.split(",").map(oe=>oe.trim()).filter(Boolean);try{await this.plugin.repository.updateSkill(n.name,{description:h.description.trim(),tags:ee(h.tags),body:h.body.trim(),toolsBody:h.toolsBody.trim(),referencesBody:h.referencesBody.trim(),examplesBody:h.examplesBody.trim()}),new w.Notice(`Skill "${n.name}" updated.`),await this.plugin.refreshFromVault(),this.navigate("skills")}catch(se){let oe=se instanceof Error?se.message:String(se);new w.Notice(`Failed to update skill: ${oe}`)}}}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"});_(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"});_(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"}),p=c.createDiv({cls:"af-mcp-progress-bar"}).createDiv({cls:"af-mcp-progress-fill"});p.style.width="15%";let m=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"),m.setText(k.message),p.style.width="20%";break;case"details":u.setText(`Checking server ${k.current}/${k.total}\u2026`),m.setText(k.serverName),p.style.width=`${20+k.current/k.total*30}%`;break;case"tools":u.setText("Discovering tools\u2026"),m.setText(k.message),p.style.width="60%",p.addClass("af-mcp-progress-fill-slow");break;case"done":p.style.width="100%",u.setText("Done"),m.setText(`${k.serverCount} server${k.serverCount!==1?"s":""}, ${k.toolCount} tool${k.toolCount!==1?"s":""} discovered`);break}});this.streamingUnsubscribes.push(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,w.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=g=>{g.stopPropagation(),this.plugin.mcpManager.toggleServerEnabled(s.name,!s.enabled).then(()=>{this.plugin.mcpManager.invalidateCache(),this.render()})};let h=a.createDiv({cls:`af-mcp-status-badge ${s.enabled?s.status:"disabled"}`}),u=h.createSpan();if(s.enabled?((0,w.setIcon)(u,s.status==="connected"?"check-circle":s.status==="needs-auth"?"alert-circle":"x-circle"),h.createSpan({text:s.status==="connected"?" Connected":s.status==="needs-auth"?" Needs auth":s.status==="error"?" Error":" Disconnected"})):((0,w.setIcon)(u,"pause"),h.createSpan({text:" Disabled"})),s.description){let g=this.truncateDescription(s.description,120);a.createDiv({cls:"af-mcp-description",text:g})}let f=s.url??s.command??"";f&&a.createDiv({cls:"af-mcp-command",text:Et(f,60)});let p=s.toolDetails.length>0?`${s.toolDetails.length} tools`:s.tools.length>0?`${s.tools.length} tools`:"No tools discovered",m=a.createDiv({cls:"af-mcp-tool-footer"}),v=m.createDiv({cls:"af-mcp-tool-count"}),k=v.createSpan();(0,w.setIcon)(k,"wrench"),v.createSpan({text:` ${p}`});let b=s.toolDetails.length>0?s.toolDetails.map(g=>g.name):s.tools;if(b.length>0){let g=m.createDiv({cls:"af-mcp-tool-chips"}),y=b.slice(0,4);for(let x of y)g.createSpan({cls:"af-mcp-tool-chip",text:x});b.length>4&&g.createSpan({cls:"af-mcp-tool-chip af-mcp-tool-chip-more",text:`+${b.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,w.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,w.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 g=a.createDiv({cls:"af-mcp-hint-row"});g.createSpan({text:"Tools available to agents via Claude \u2014 "});let y=g.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 w.Notice("No URL found for this server \u2014 can't authenticate.");return}this.authenticatingServers.add(e.name),this.render(),new w.Notice(`Authenticating ${e.name}\u2026 Complete authorization in your browser.`,1e4);try{let s=e.type==="sse"?"sse":"http";await this.plugin.mcpManager.authenticateServer(e.name,e.url,s),new w.Notice(`${e.name} authenticated successfully!`),await this.plugin.mcpManager.getServers(!0)}catch(s){let a=s instanceof Error?s.message:String(s);new w.Notice(`Authentication failed: ${a}`,8e3)}finally{this.authenticatingServers.delete(e.name),this.render()}}truncateDescription(e,s){let a=ce(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,w.setIcon)(i,"cross"),i.onclick=()=>s.remove(),s.onclick=p=>{p.target===s&&s.remove()};let o=a.createDiv({cls:"af-slideover-body"});if(e.description){let p=o.createDiv({cls:"af-slideover-section"});p.createDiv({cls:"af-slideover-section-title",text:"DESCRIPTION"}),p.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 p of e.toolDetails){let m=c.createDiv({cls:"af-mcp-tool-detail"}),v=m.createDiv({cls:"af-mcp-tool-detail-header"}),k=v.createSpan({cls:"af-mcp-tool-detail-name"}),b=k.createSpan();if((0,w.setIcon)(b,"wrench"),k.createSpan({text:` ${p.name}`}),p.inputSchema){let g=p.inputSchema.required??[];g.length>0&&v.createSpan({cls:"af-mcp-tool-param-count",text:`${g.length} param${g.length!==1?"s":""}`})}if(p.description){let g=ce(p.description).filter(S=>S.trim()),y=g.slice(0,2).join(" ").trim();if(g.length>2){let S=m.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:p.description})}else m.createDiv({cls:"af-mcp-tool-detail-desc",text:y})}if(p.inputSchema){let g=p.inputSchema.properties,y=new Set(p.inputSchema.required??[]);if(g&&Object.keys(g).length>0){let x=m.createDiv({cls:"af-mcp-tool-params"});for(let[S,T]of Object.entries(g)){let P=x.createDiv({cls:"af-mcp-tool-param"});P.createSpan({cls:"af-mcp-tool-param-name",text:S}),T.type&&P.createSpan({cls:"af-mcp-tool-param-type",text:T.type}),y.has(S)&&P.createSpan({cls:"af-mcp-tool-param-required",text:"required"}),T.description&&P.createSpan({cls:"af-mcp-tool-param-desc",text:Et(T.description,80)})}}}}else if(e.tools.length>0)for(let p of e.tools)c.createDiv({cls:"af-mcp-tool-item",text:p});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 p=h.createEl("button",{cls:"af-btn-sm primary"}),m=p.createSpan();(0,w.setIcon)(m,"key"),p.appendText(" Authenticate"),p.onclick=()=>{s.remove(),this.authenticateMcpServer(e)}}let u=h.createEl("button",{cls:"af-btn-sm danger"}),f=u.createSpan();(0,w.setIcon)(f,"trash-2"),u.appendText(" Remove Server"),u.onclick=async()=>{try{let p=e.scope==="user"?"user":void 0;await this.plugin.mcpManager.removeServer(e.name,p),new w.Notice(`Server "${e.name}" removed.`),s.remove(),this.render()}catch(p){let m=p instanceof Error?p.message:String(p);new w.Notice(`Failed to remove server: ${m}`)}}}renderAddMcpServerPage(e){let s=e.createDiv({cls:"af-create-agent-page"}),a=s.createDiv({cls:"af-detail-header"}),n=a.createDiv({cls:"af-detail-header-left"}),i=n.createDiv({cls:"af-agent-card-avatar idle"});(0,w.setIcon)(i,"plus");let o=n.createDiv();o.createDiv({cls:"af-detail-header-name",text:"Add MCP Server"}),o.createDiv({cls:"af-detail-header-desc",text:"Register a new MCP server for agents to use"}),a.createDiv({cls:"af-detail-header-actions"});let 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,w.setIcon)(u,"plug"),h.createSpan({text:"Server Details"}),this.createFormField(d,"Name","my-server","Unique name for this MCP server",z=>{l.name=z});let f=d.createDiv({cls:"af-form-row"}),p=f.createDiv({cls:"af-form-label"});p.setText("Transport"),this.addTooltip(p,"stdio: local process, http/sse: remote server");let m=f.createEl("select",{cls:"af-form-select"});m.createEl("option",{text:"stdio",attr:{value:"stdio"}}),m.createEl("option",{text:"http",attr:{value:"http"}}),m.createEl("option",{text:"sse",attr:{value:"sse"}});let 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 b=v.createEl("select",{cls:"af-form-select"});b.createEl("option",{text:"user",attr:{value:"user"}}),b.createEl("option",{text:"local",attr:{value:"local"}}),b.addEventListener("change",()=>{l.scope=b.value});let g=c.createDiv({cls:"af-create-section"}),y=g.createDiv({cls:"af-create-section-header"}),x=y.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(x,"terminal"),y.createSpan({text:"Process Configuration"}),this.createFormField(g,"Command","npx @anthropic-ai/mcp-server-memory","The command to run",z=>{l.command=z}),this.createFormField(g,"Arguments","--port 3000","Space-separated arguments (optional)",z=>{l.args=z});let S=g.createDiv({cls:"af-form-label"});S.setText("Environment variables"),this.addTooltip(S,"One KEY=VALUE per line");let T=g.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:`API_KEY=sk-...
|
|
11607
|
+
DEBUG=true`,rows:"3"}});T.addEventListener("input",()=>{l.envVars=T.value});let P=c.createDiv({cls:"af-create-section"}),E=P.createDiv({cls:"af-create-section-header"}),R=E.createSpan({cls:"af-create-section-icon"});(0,w.setIcon)(R,"globe"),E.createSpan({text:"Remote Server Configuration"}),this.createFormField(P,"URL","https://mcp.example.com/sse","Server endpoint URL",z=>{l.url=z});let W=P.createDiv({cls:"af-form-label"});W.setText("Custom headers"),this.addTooltip(W,"One Header: Value per line (optional)");let I=P.createEl("textarea",{cls:"af-create-prompt-textarea",attr:{placeholder:"X-Custom-Header: value",rows:"3"}});I.addEventListener("input",()=>{l.headers=I.value});let q=()=>{g.style.display=l.transport==="stdio"?"":"none",P.style.display=l.transport!=="stdio"?"":"none"};m.addEventListener("change",()=>{l.transport=m.value,q()}),q();let L=s.createDiv({cls:"af-create-footer"}),K=L.createEl("button",{cls:"af-btn-sm",text:"Cancel"});K.onclick=()=>this.navigate("mcp");let V=L.createEl("button",{cls:"af-btn-sm primary af-create-submit"});_(V,"plus","af-btn-icon"),V.appendText(" Add Server"),V.onclick=async()=>{let z=l.name.trim();if(!z){new w.Notice("Server name is required.");return}if(l.transport==="stdio"){if(!l.command.trim()){new w.Notice("Command is required for stdio servers.");return}}else if(!l.url.trim()){new w.Notice("URL is required for HTTP/SSE servers.");return}let ie={};if(l.envVars.trim())for(let G of ce(l.envVars)){let B=G.trim();if(!B)continue;let F=B.indexOf("=");if(F<=0){new w.Notice(`Invalid env var: ${B}`);return}ie[B.slice(0,F)]=B.slice(F+1)}let Q={};if(l.headers.trim())for(let G of ce(l.headers)){let B=G.trim();if(!B)continue;let F=B.indexOf(":");if(F<=0){new w.Notice(`Invalid header: ${B}`);return}Q[B.slice(0,F).trim()]=B.slice(F+1).trim()}let te=l.args.trim()?l.args.trim().split(/\s+/):void 0;V.disabled=!0,V.setText("Adding...");try{await this.plugin.mcpManager.addServer({name:z,transport:l.transport,scope:l.scope,command:l.transport==="stdio"?l.command.trim():void 0,args:l.transport==="stdio"?te:void 0,envVars:l.transport==="stdio"&&Object.keys(ie).length>0?ie:void 0,url:l.transport!=="stdio"?l.url.trim():void 0,headers:l.transport!=="stdio"&&Object.keys(Q).length>0?Q:void 0}),new w.Notice(`Server "${z}" added successfully.`),this.navigate("mcp")}catch(G){let B=G instanceof Error?G.message:String(G);new w.Notice(`Failed to add server: ${B}`),V.disabled=!1,V.setText(""),_(V,"plus","af-btn-icon"),V.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,w.setIcon)(a,"info"),a.createSpan({cls:"af-tooltip-text",text:s})}};function Oi(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 Ha(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 tl(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 sl(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 he=require("obsidian");var Ht=class r extends he.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;statsEl;statsUnsub=null;statsSourceSession=null;threadExpanded=new Map;getViewType(){return Ye}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.plugin.unsubscribeView(this)}selectAgent(e){let s=this.plugin.app.workspace.getLeavesOfType(Ye);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"});_(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(Ye);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"}),_(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"}),_(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: ${Ni(e.contextTokensUsed)} / ${Ni(e.contextWindow)} tokens (${a}%)
|
|
11608
|
+
|
|
11609
|
+
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.
|
|
11610
|
+
|
|
11611
|
+
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).`}}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,he.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,he.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(i=>i.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 i=new Rt(a,this.plugin.settings,this.plugin.repository,this.app.vault);n={session:i},this.sessions.set(e,n),await i.loadPersistedState()}this.setStatsSource(n.session);for(let i of n.session.messages)if(i.role==="user")this.addBubble("user",i.content,i.attachments);else{let o=this.addBubble("assistant");if(this.renderMarkdownBubble(o,i.content),o._setRawText?.(i.content),this.attachThreadAffordance(o,i.id,n.session),i.toolCalls&&i.toolCalls.length>0){let l=this.getOrCreateAffordancesRow(o);this.buildToolSummary(i.toolCalls,l)}}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"),he.MarkdownRenderer.render(this.app,s,e,"",this.plugin).then(()=>{n&&e.appendChild(n),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,he.setIcon)(l,"copy"),l.onclick=c=>{c.stopPropagation(),navigator.clipboard.writeText(o.textContent??"").then(()=>{l.addClass("copied"),(0,he.setIcon)(l,"check"),setTimeout(()=>{l.removeClass("copied"),(0,he.setIcon)(l,"copy")},1500)})},i.style.position="relative",i.appendChild(l)})})}addCopyBtn(e,s){let a=e.createEl("button",{cls:"af-chat-copy-btn",attr:{"aria-label":"Copy message"}});(0,he.setIcon)(a,"copy"),a.onclick=n=>{n.stopPropagation(),navigator.clipboard.writeText(s()).then(()=>{a.addClass("copied"),(0,he.setIcon)(a,"check"),setTimeout(()=>{a.removeClass("copied"),(0,he.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,he.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,he.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 m=a.getThreadIndex()[s]?.messageCount??0;m<=0?l.setText("Thread"):l.setText(`${m} ${m===1?"reply":"replies"}`),m>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 p=await a.openOrCreateThread(s);this.renderThreadContainer(c,p,a,d),h=!0}catch(p){c.setText(`Failed to open thread: ${p instanceof Error?p.message:String(p)}`)}};o.onclick=()=>void u(),o.onkeydown=f=>{(f.key==="Enter"||f.key===" ")&&(f.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=g=>{g?(l||(l=o.createDiv({cls:"af-chat-activity"})),l.setText(`Working\u2026 (${g})`)):l&&(l.remove(),l=null)},h=g=>{if(g&&!c){c=o.createDiv({cls:"af-chat-streaming-dot"});for(let y=0;y<3;y++)c.createSpan()}else!g&&c&&(c.remove(),c=null)},u=(g,y)=>{let x=o.createDiv({cls:`af-thread-bubble af-thread-bubble-${g}`});return g==="assistant"?this.renderMarkdownBubble(x,y):x.setText(y),x};for(let g of s.messages)u(g.role,g.content);let f=i.createDiv({cls:"af-chat-input-row af-thread-composer"}),p=f.createEl("button",{cls:"af-chat-attach-btn"});_(p,"plus","af-btn-icon"),p.title="Attachments not yet supported in threads",p.disabled=!0;let m=f.createEl("textarea",{cls:"af-chat-input af-thread-input",attr:{placeholder:"Message in thread\u2026 (Ctrl+Enter to send)",rows:"1"}}),v=f.createEl("button",{cls:"af-chat-send-btn"});_(v,"arrow-up","af-btn-icon"),v.style.display="none";let k=()=>{m.style.height="auto",m.style.height=`${Math.min(m.scrollHeight,120)}px`,v.style.display=m.value.trim()?"flex":"none"};m.addEventListener("input",k),m.addEventListener("focus",()=>this.setStatsSource(s));let b=async()=>{let g=m.value.trim();if(!g||s.isStreaming)return;m.value="",k(),u("user",g),h(!0);let y=null,x="";try{await s.sendMessage(g,S=>{if(S.type==="text"){y||(h(!1),d(),y=u("assistant",""),y.empty()),x+=S.content;let T=y.querySelector(".af-chat-stream-text");T||(T=y.createDiv({cls:"af-chat-stream-text"})),T.setText(x)}else S.type==="tool_use"?d(S.toolName):S.type==="result"&&(d(),h(!1),y&&this.renderMarkdownBubble(y,x))}),n()}catch(S){h(!1),d();let T=S instanceof Error?S.message:String(S);o.createDiv({cls:"af-thread-error",text:`Error: ${T}`})}};v.onclick=()=>void b(),m.onkeydown=g=>{g.key==="Enter"&&(g.ctrlKey||g.metaKey)&&(g.preventDefault(),b())}}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,he.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 f=u.length||(l.get(h)?.length??1),p=d.createDiv({cls:"af-chat-tool-item"}),m=f>1?`${h} (\xD7${f})`:h;p.createSpan({cls:"af-chat-tool-name",text:m}),u.length===1&&u[0]&&p.createSpan({cls:"af-chat-tool-cmd",text:u[0]})}return n}setActivity(e){e?(this.activityEl||(this.activityEl=this.messagesInner.createDiv({cls:"af-chat-activity"})),this.activityEl.setText(`Working\u2026 (${e})`)):this.activityEl&&(this.activityEl.remove(),this.activityEl=null)}setStreaming(e){if(e&&!this.streamingDot){this.streamingDot=this.messagesInner.createDiv({cls:"af-chat-streaming-dot"});for(let s=0;s<3;s++)this.streamingDot.createSpan()}else!e&&this.streamingDot&&(this.streamingDot.remove(),this.streamingDot=null);this.setAttachStopMode(e)}setAttachStopMode(e){e!==this.isInStopMode&&(this.isInStopMode=e,this.attachStopBtn.empty(),e?(_(this.attachStopBtn,"square","af-btn-icon"),this.attachStopBtn.title="Stop generation",this.attachStopBtn.addClass("af-chat-stop-mode")):(_(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.setActivity(),this.setStreaming(!1),this.addBubble("error","Generation stopped"))}attachActiveDocument(){let e=this.app.workspace.getActiveFile();if(!e){new he.Notice("No active document to attach");return}if(this.attachedFiles.some(n=>n.path===e.path)){new he.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 he.Notice(`Can't attach "${e.name}" \u2014 only text files are supported`);return}this.attachedFiles.push(e),this.renderPills()}async attachImageBlob(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}`;if(this.attachedImages.some(l=>l.name===n)){new he.Notice(`"${n}" is already attached`);return}let 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 h=`${this.app.vault.adapter.getBasePath?.()??""}/${o}`;this.attachedImages.push({name:n,path:h}),this.renderPills()}catch(l){let c=l instanceof Error?l.message:String(l);new he.Notice(`Failed to save image: ${c}`)}}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,he.setIcon)(n,"file-text"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,he.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,he.setIcon)(n,"image"),a.createSpan({cls:"af-chat-pill-name",text:s.name});let i=a.createSpan({cls:"af-chat-pill-remove"});(0,he.setIcon)(i,"x"),i.onclick=o=>{o.stopPropagation(),this.removeAttachment(s.path)}}}async buildAttachmentContext(){if(this.attachedFiles.length+this.attachedImages.length===0)return"";let s=[];for(let a of this.attachedFiles)try{let n=await this.app.vault.cachedRead(a);s.push(`### ${a.name}
|
|
11264
11612
|
\`\`\`
|
|
11265
11613
|
${n}
|
|
11266
11614
|
\`\`\``)}catch{s.push(`### ${a.name}
|
|
@@ -11274,6 +11622,6 @@ ${s.join(`
|
|
|
11274
11622
|
|
|
11275
11623
|
---
|
|
11276
11624
|
|
|
11277
|
-
`}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(d=>d.name),...this.attachedImages.map(d=>d.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}this.setStreaming(!0);let o=null,l="",c=!1;try{await e.session.sendMessage(s,d=>{if(d.type==="text"){c||(this.setActivity(),this.setStreaming(!1),o=this.addBubble("assistant"),c=!0),l+=d.content;let
|
|
11625
|
+
`}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(d=>d.name),...this.attachedImages.map(d=>d.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}this.setStreaming(!0);let o=null,l="",c=!1;try{await e.session.sendMessage(s,d=>{if(d.type==="text"){c||(this.setActivity(),this.setStreaming(!1),o=this.addBubble("assistant"),c=!0),l+=d.content;let h=o.querySelector(".af-chat-stream-text");h||(h=o.createDiv({cls:"af-chat-stream-text"})),h.setText(l)}else if(d.type==="tool_use")this.setActivity(d.toolName);else if(d.type==="result"){if(this.setActivity(),this.setStreaming(!1),c&&o){this.renderMarkdownBubble(o,l),o._setRawText?.(l);let h=e.session.messages[e.session.messages.length-1];if(h&&h.role==="assistant"&&(this.attachThreadAffordance(o,h.id,e.session),d.toolCalls&&d.toolCalls.length>0)){let u=this.getOrCreateAffordancesRow(o);this.buildToolSummary(d.toolCalls,u)}}else d.toolCalls&&d.toolCalls.length>0&&this.buildToolSummary(d.toolCalls);l="",c=!1,o=null,e.session.pendingTurnCount>1&&this.setStreaming(!0)}},i,n.length>0?n:void 0),this.setActivity(),this.setStreaming(!1)}catch(d){this.setActivity(),this.setStreaming(!1);let h=d instanceof Error?d.message:String(d);h!=="Aborted"&&this.addBubble("error",`Error: ${h}`)}}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){this.setStreaming(!0);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||(this.setActivity(),this.setStreaming(!1),a=this.addBubble("assistant"),n=!0),s+=i.content,a.setText(s)):i.type==="tool_use"&&this.setActivity(i.toolName)}).then(i=>{this.setActivity(),this.setStreaming(!1),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=>{this.setActivity(),this.setStreaming(!1);let o=i instanceof Error?i.message:String(i);o!=="Aborted"&&this.addBubble("error",`Error: ${o}`)})}};function Ni(r){return r>=1e3?`${(r/1e3).toFixed(r>=1e4?0:1)}k`:`${r}`}var ta=class extends ve.Plugin{settings={...Qe};repository;runtime;get mcpManager(){return this.runtime.mcpManager}mcpAuth=new Ps;channelCredentials=new Os;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 Vt(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new Qt(this.repository,this.settings),this.registerView(ht,a=>new ls(a,this)),this.registerView(mt,a=>new Zs(a,this)),this.registerView(Ye,a=>new Ht(a,this)),this.addSettingTab(new ks(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(Ye);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 Fs(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 Ms({getRepository:()=>this.repository,vault:this.app.vault,getSettings:()=>this.settings,getChannelCredentials:()=>this.channelCredentials.toRecord(),adapterFactory:(a,n)=>{if(a.type==="slack")return new Js(a,n);if(a.type==="telegram")return new Qs(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 ve.Notice("Agent Fleet: channel manager failed to start \u2014 check console.")}this.runtime.onHeartbeatResult((a,n,i)=>{this.channelManager?.broadcastToChannel(n,`*Heartbeat \u2014 ${a}*
|
|
11278
11626
|
|
|
11279
|
-
${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
|
|
11627
|
+
${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 ve.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(ht),this.app.workspace.detachLeavesOfType(mt),this.app.workspace.detachLeavesOfType(Ye),this.channelManager?.stop()}async loadSettings(){this.settings={...Qe,...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 Vt(this.app.vault,this.settings),this.repository.setChannelCredentialGetter(()=>this.channelCredentials.toRecord()),this.runtime=new Qt(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(ht);if(t.length>0){this.app.workspace.revealLeaf(t[0]);return}await this.app.workspace.getLeaf(!0).setViewState({type:ht,active:!0})}async navigateDashboard(t,e){await this.activateDashboardView();let a=this.app.workspace.getLeavesOfType(ht)[0];if(a){let n=a.view;n instanceof ls&&n.navigateTo(t,e)}}async activateAgentsView(){let t=this.getLeafForView(mt,"left");await t.setViewState({type:mt,active:!0}),this.app.workspace.revealLeaf(t)}async openChatView(t){if(t){let s=this.app.workspace.getLeavesOfType(Ye);for(let a of s)if(a.view instanceof Ht&&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:Ye,active:!0,state:t?{agentName:t}:{}}),this.app.workspace.revealLeaf(e),t&&e.view instanceof Ht&&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=at(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 ve.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 ve.Notice("Claude CLI verification failed \u2014 check Claude CLI Path in settings."),s(!1)})})}async openPath(t){let e=this.app.vault.getAbstractFileByPath((0,ve.normalizePath)(t));e instanceof ve.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 ve.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 ve.Notice(`Unknown agent: ${t}`);return}await this.openChatView(t)}async deleteAgent(t){if(!this.repository.getAgentByName(t)){new ve.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 bs(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 ve.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 ve.TFile))return;let n=await this.app.vault.cachedRead(a),{frontmatter:i,body:o}=J(n);i.enabled=e,await this.app.vault.modify(a,Y(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(Ye);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 ve.Notice("No agents configured.")}}),this.addCommand({id:"pause-all",name:"Pause All",callback:()=>{this.runtime.scheduler.pauseAll(),new ve.Notice("Agent Fleet paused.")}}),this.addCommand({id:"resume-all",name:"Resume All",callback:()=>{this.runtime.scheduler.resumeAll(),new ve.Notice("Agent Fleet resumed.")}}),this.addCommand({id:"view-fleet-status",name:"View Fleet Status",callback:()=>{let t=this.runtime.getFleetStatus();new ve.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 ve.TFile&&t.path.startsWith(`${this.settings.fleetFolder}/`)&&this.debouncedVaultRefresh()})),this.registerEvent(this.app.vault.on("modify",t=>{t instanceof ve.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=Ja(t);for(let s of e)if(oa(s)&&(0,Bi.existsSync)(s)||!oa(s)&&await new Promise(n=>{let i=at(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))}};
|